github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/igm/sockjs-go.v2/sockjs/handler.gon-place (about)

     1  package sockjs
     2  
     3  import (
     4  	"errors"
     5  	"net/http"
     6  	"net/url"
     7  	"regexp"
     8  	"strings"
     9  	"sync"
    10  )
    11  
    12  var (
    13  	prefixRegexp   = make(map[string]*regexp.Regexp)
    14  	prefixRegexpMu sync.Mutex // protects prefixRegexp
    15  )
    16  
    17  type handler struct {
    18  	prefix      string
    19  	options     Options
    20  	handlerFunc func(Session)
    21  	mappings    []*mapping
    22  
    23  	sessionsMux sync.Mutex
    24  	sessions    map[string]*session
    25  }
    26  
    27  // NewHandler creates new HTTP handler that conforms to the basic net/http.Handler interface.
    28  // It takes path prefix, options and sockjs handler function as parameters
    29  func NewHandler(prefix string, opts Options, handleFunc func(Session)) http.Handler {
    30  	return newHandler(prefix, opts, handleFunc)
    31  }
    32  
    33  func newHandler(prefix string, opts Options, handlerFunc func(Session)) *handler {
    34  	h := &handler{
    35  		prefix:      prefix,
    36  		options:     opts,
    37  		handlerFunc: handlerFunc,
    38  		sessions:    make(map[string]*session),
    39  	}
    40  
    41  	sessionPrefix := prefix + "/[^/.]+/[^/.]+"
    42  	h.mappings = []*mapping{
    43  		newMapping("GET", prefix+"[/]?$", welcomeHandler),
    44  		newMapping("OPTIONS", prefix+"/info$", opts.cookie, xhrCors, cacheFor, opts.info),
    45  		newMapping("GET", prefix+"/info$", xhrCors, noCache, opts.info),
    46  		// XHR
    47  		newMapping("POST", sessionPrefix+"/xhr_send$", opts.cookie, xhrCors, noCache, h.xhrSend),
    48  		newMapping("OPTIONS", sessionPrefix+"/xhr_send$", opts.cookie, xhrCors, cacheFor, xhrOptions),
    49  		newMapping("POST", sessionPrefix+"/xhr$", opts.cookie, xhrCors, noCache, h.xhrPoll),
    50  		newMapping("OPTIONS", sessionPrefix+"/xhr$", opts.cookie, xhrCors, cacheFor, xhrOptions),
    51  		newMapping("POST", sessionPrefix+"/xhr_streaming$", opts.cookie, xhrCors, noCache, h.xhrStreaming),
    52  		newMapping("OPTIONS", sessionPrefix+"/xhr_streaming$", opts.cookie, xhrCors, cacheFor, xhrOptions),
    53  		// EventStream
    54  		newMapping("GET", sessionPrefix+"/eventsource$", opts.cookie, xhrCors, noCache, h.eventSource),
    55  		// Htmlfile
    56  		newMapping("GET", sessionPrefix+"/htmlfile$", opts.cookie, xhrCors, noCache, h.htmlFile),
    57  		// JsonP
    58  		newMapping("GET", sessionPrefix+"/jsonp$", opts.cookie, xhrCors, noCache, h.jsonp),
    59  		newMapping("OPTIONS", sessionPrefix+"/jsonp$", opts.cookie, xhrCors, cacheFor, xhrOptions),
    60  		newMapping("POST", sessionPrefix+"/jsonp_send$", opts.cookie, xhrCors, noCache, h.jsonpSend),
    61  		// IFrame
    62  		newMapping("GET", prefix+"/iframe[0-9-.a-z_]*.html$", cacheFor, h.iframe),
    63  	}
    64  	if opts.Websocket {
    65  		h.mappings = append(h.mappings, newMapping("GET", sessionPrefix+"/websocket$", h.sockjsWebsocket))
    66  	}
    67  	return h
    68  }
    69  
    70  func (h *handler) Prefix() string { return h.prefix }
    71  
    72  func (h *handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    73  	// iterate over mappings
    74  	allowedMethods := []string{}
    75  	for _, mapping := range h.mappings {
    76  		if match, method := mapping.matches(req); match == fullMatch {
    77  			for _, hf := range mapping.chain {
    78  				hf(rw, req)
    79  			}
    80  			return
    81  		} else if match == pathMatch {
    82  			allowedMethods = append(allowedMethods, method)
    83  		}
    84  	}
    85  	if len(allowedMethods) > 0 {
    86  		rw.Header().Set("allow", strings.Join(allowedMethods, ", "))
    87  		rw.Header().Set("Content-Type", "")
    88  		rw.WriteHeader(http.StatusMethodNotAllowed)
    89  		return
    90  	}
    91  	http.NotFound(rw, req)
    92  }
    93  
    94  func (h *handler) parseSessionID(url *url.URL) (string, error) {
    95  	// cache compiled regexp objects for most used prefixes
    96  	prefixRegexpMu.Lock()
    97  	session, ok := prefixRegexp[h.prefix]
    98  	if !ok {
    99  		session = regexp.MustCompile(h.prefix + "/(?P<server>[^/.]+)/(?P<session>[^/.]+)/.*")
   100  		prefixRegexp[h.prefix] = session
   101  	}
   102  	prefixRegexpMu.Unlock()
   103  
   104  	matches := session.FindStringSubmatch(url.Path)
   105  	if len(matches) == 3 {
   106  		return matches[2], nil
   107  	}
   108  	return "", errors.New("unable to parse URL for session")
   109  }
   110  
   111  func (h *handler) sessionByRequest(req *http.Request) (*session, error) {
   112  	h.sessionsMux.Lock()
   113  	defer h.sessionsMux.Unlock()
   114  	sessionID, err := h.parseSessionID(req.URL)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	sess, exists := h.sessions[sessionID]
   119  	if !exists {
   120  		sess = newSession(req, sessionID, h.options.DisconnectDelay, h.options.HeartbeatDelay)
   121  		h.sessions[sessionID] = sess
   122  		if h.handlerFunc != nil {
   123  			go h.handlerFunc(sess)
   124  		}
   125  		go func() {
   126  			<-sess.closedNotify()
   127  			h.sessionsMux.Lock()
   128  			delete(h.sessions, sessionID)
   129  			h.sessionsMux.Unlock()
   130  		}()
   131  	}
   132  	return sess, nil
   133  }