github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/igm/sockjs-go.v2/sockjs/handler.go (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 }