func wsClientHandshake(r *http.Request) (secWSAccept string, err error) { // Check HTTP version if !r.ProtoAtLeast(minProtoMajor, minProtoMinor) { err = errMalformedClientHandshake return } // Check HTTP header identifier for WebSocket if !(strings.EqualFold(r.Header.Get("Upgrade"), "websocket") && strings.EqualFold(r.Header.Get("Connection"), "Upgrade")) { err = errMalformedClientHandshake return } // Check WebSocket version if clientSecWSVersion, errFormat := // TODO: Header.Get() just returns the first value, could be multiple strconv.Atoi(r.Header.Get("Sec-WebSocket-Version")); !(errFormat == nil && clientSecWSVersion == secWSVersion) { err = errMalformedClientHandshake return } // Check Sec-WebSocket-Key secWSKey := strings.TrimSpace(r.Header.Get("Sec-WebSocket-Key")) return validateSecWebSocketKey(secWSKey) }
func (router *MessageRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": CheckServer(w, r) case "POST": data, _ := ioutil.ReadAll(r.Body) msg := &Message{} err := xml.Unmarshal(data, msg) if err != nil { log.Panic("xml unmarshal post data error: %#v", err) } if handler, ok := router.handlers[msg.MessageRoute]; ok { log.Printf("Handle Found: %#v, %v", msg.MessageRoute, handler) handler(w, msg) } else { log.Printf("Handle Not Found: %#v", msg.MessageRoute) if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(http.StatusNotFound) } default: log.Printf("unhandle request method: %s", r.Method) } }
func prepareRequest(req *http.Request) *http.Request { outreq := new(http.Request) *outreq = *req // includes shallow copies of maps, but okay // Pass the Request-URI verbatim without any modifications. // // NOTE: An exception must be made if the Request-URI is a path // beginning with "//" (e.g. "//foo/bar") because then // req.URL.RequestURI() would interpret req.URL.Opaque as being a URI // with the scheme stripped and so generate a URI like scheme:opaque // (e.g. "http://foo/bar") which would be incorrect, see: // https://github.com/golang/go/blob/f75aafd/src/net/url/url.go#L913-L931 // // It is ok to make this exception because the fallback to // req.URL.EscapedPath will generate the correct Request-URI. if !strings.HasPrefix(req.RequestURI, "//") { outreq.URL.Opaque = strings.Split(strings.TrimPrefix(req.RequestURI, req.URL.Scheme+":"), "?")[0] } outreq.URL.Scheme = "http" outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove hop-by-hop headers to the backend. outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) for _, h := range hopHeaders { outreq.Header.Del(h) } // remove the Upgrade header and headers referenced in the Connection // header if HTTP < 1.1 or if Connection header didn't contain "upgrade": // https://tools.ietf.org/html/rfc7230#section-6.7 if !req.ProtoAtLeast(1, 1) || !isConnectionUpgrade(req.Header) { outreq.Header.Del("Upgrade") // Especially important is "Connection" because we want a persistent // connection, regardless of what the client sent to us. outreq.Header.Del("Connection") // A proxy or gateway MUST parse a received Connection header field before a // message is forwarded and, for each connection-option in this field, remove // any header field(s) from the message with the same name as the // connection-option, and then remove the Connection header field itself (or // replace it with the intermediary's own connection options for the // forwarded message): https://tools.ietf.org/html/rfc7230#section-6.1 tokens := strings.Split(req.Header.Get("Connection"), ",") for _, hdr := range tokens { outreq.Header.Del(hdr) } } return outreq }
// ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(http.StatusBadRequest) return } h, _ := mux.Handler(r) h.ServeHTTP(ctx, w, r) }
// ServeHTTP dispatches the request to the handler whose pattern most closely // matches the request URL. ServeHTTP implements the http.Handler interface. // // ServeHTTP also instantiates a request-scoped context.Context that holds the // request specific Params value which can be retrieved using the GetParams function. func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { if req.RequestURI == "*" { if req.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(http.StatusBadRequest) return } h, p, _ := r.Handler(req) ctx := Context(context.Background(), p) h.ServeHTTP(ctx, w, req) }
func (s *StaticServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(http.StatusBadRequest) return } h, _ := s.Handler(r) h = s.interceptHandler(h) h.ServeHTTP(w, r) }
func (mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(http.StatusBadRequest) return } h, pattern := serveMux.Handler(r) // modify r url and cut out the matched pattern trimPrefixFromURL(r.URL, pattern) h.ServeHTTP(w, r) }
func (svc *Service) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if req.RequestURI == "*" { if req.ProtoAtLeast(1, 1) { rw.Header().Set("Connection", "close") } rw.WriteHeader(http.StatusBadRequest) return } originControl(rw) if req.Method == "OPTIONS" { return } ctx := NewContext(rw, req) svc.routes.dispatch(req.URL.Path, ctx) }
func prepareRequest(req *http.Request) *http.Request { outreq := new(http.Request) *outreq = *req // includes shallow copies of maps, but okay // Pass the Request-URI verbatim without any modifications outreq.URL.Opaque = strings.Split(strings.TrimPrefix(req.RequestURI, req.URL.Scheme+":"), "?")[0] outreq.URL.Scheme = "http" outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove hop-by-hop headers to the backend. outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) for _, h := range hopHeaders { outreq.Header.Del(h) } // remove the Upgrade header and headers referenced in the Connection // header if HTTP < 1.1 or if Connection header didn't contain "upgrade": // https://tools.ietf.org/html/rfc7230#section-6.7 if !req.ProtoAtLeast(1, 1) || !isConnectionUpgrade(req.Header) { outreq.Header.Del("Upgrade") // Especially important is "Connection" because we want a persistent // connection, regardless of what the client sent to us. outreq.Header.Del("Connection") // A proxy or gateway MUST parse a received Connection header field before a // message is forwarded and, for each connection-option in this field, remove // any header field(s) from the message with the same name as the // connection-option, and then remove the Connection header field itself (or // replace it with the intermediary's own connection options for the // forwarded message): https://tools.ietf.org/html/rfc7230#section-6.1 tokens := strings.Split(req.Header.Get("Connection"), ",") for _, hdr := range tokens { outreq.Header.Del(hdr) } } return outreq }
// ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(http.StatusBadRequest) return } r.ParseForm() for _, h := range Hooker.UrlRewrite { if !h(r) { return } } h, _ := mux.Handler(r) h.ServeHTTP(w, r) }
func (srv *Server) handler(c net.Conn) { startTime := time.Now() bpe := srv.bufferPool.take(c) defer srv.bufferPool.give(bpe) var closeSentinelChan = make(chan int) go srv.sentinel(c, closeSentinelChan) defer srv.connectionFinished(c, closeSentinelChan) var err error var req *http.Request // no keepalive (for now) reqCount := 0 keepAlive := true for err == nil && keepAlive { if req, err = http.ReadRequest(bpe.br); err == nil { if req.Header.Get("Connection") != "Keep-Alive" { keepAlive = false } request := newRequest(req, c, startTime) reqCount++ var res *http.Response pssInit := new(PipelineStageStat) pssInit.Name = "server.Init" pssInit.StartTime = startTime pssInit.EndTime = time.Now() request.appendPipelineStage(pssInit) // execute the pipeline if res = srv.Pipeline.execute(request); res == nil { res = SimpleResponse(req, 404, nil, "Not Found") } // cleanup request.startPipelineStage("server.ResponseWrite") req.Body.Close() // shutting down? select { case <-srv.stopAccepting: keepAlive = false res.Close = true default: } // The res.Write omits Content-length on 0 length bodies, and by spec, // it SHOULD. While this is not MUST, it's kinda broken. See sec 4.4 // of rfc2616 and a 200 with a zero length does not satisfy any of the // 5 conditions if Connection: keep-alive is set :( // I'm forcing chunked which seems to work because I couldn't get the // content length to write if it was 0. // Specifically, the android http client waits forever if there's no // content-length instead of assuming zero at the end of headers. der. if res.ContentLength == 0 && len(res.TransferEncoding) == 0 && !((res.StatusCode-100 < 100) || res.StatusCode == 204 || res.StatusCode == 304) { res.TransferEncoding = []string{"identity"} } if res.ContentLength < 0 { res.TransferEncoding = []string{"chunked"} } // For HTTP/1.0 and Keep-Alive, sending the Connection: Keep-Alive response header is required // because close is default (opposite of 1.1) if keepAlive && !req.ProtoAtLeast(1, 1) { res.Header.Add("Connection", "Keep-Alive") } // write response if srv.sendfile { res.Write(c) srv.cycleNonBlock(c) } else { wbuf := bufio.NewWriter(c) res.Write(wbuf) wbuf.Flush() } if res.Body != nil { res.Body.Close() } request.finishPipelineStage() request.finishRequest() srv.requestFinished(request) if res.Close { keepAlive = false } // Reset the startTime // this isn't great since there may be lag between requests; but it's the best we've got startTime = time.Now() } else { // EOF is socket closed if nerr, ok := err.(net.Error); err != io.EOF && !(ok && nerr.Timeout()) { Error("%s %v ERROR reading request: <%T %v>", srv.serverLogPrefix(), c.RemoteAddr(), err, err) } } } //Debug("%s Processed %v requests on connection %v", srv.serverLogPrefix(), reqCount, c.RemoteAddr()) }
func (srv *Server) handler(c net.Conn) { var startTime time.Time bpe := srv.bufferPool.Take(c) defer srv.bufferPool.Give(bpe) wbpe := srv.writeBufferPool.Take(c) defer srv.writeBufferPool.Give(wbpe) closeSentinelChan := make(chan struct{}) go srv.sentinel(c, closeSentinelChan) defer srv.connectionFinished(c, closeSentinelChan) var err error var req *http.Request // no keepalive (for now) reqCount := 0 keepAlive := true for err == nil && keepAlive { if _, err := bpe.Br.Peek(1); err == nil { startTime = time.Now() } if req, err = http.ReadRequest(bpe.Br); err == nil { if req.ProtoAtLeast(1, 1) { if req.Header.Get("Connection") == "close" { keepAlive = false } } else if strings.ToLower(req.Header.Get("Connection")) != "keep-alive" { keepAlive = false } request := newRequest(req, c, startTime) reqCount++ pssInit := new(PipelineStageStat) pssInit.Name = "server.Init" pssInit.StartTime = startTime pssInit.EndTime = time.Now() pssInit.Type = PipelineStageTypeOverhead request.appendPipelineStage(pssInit) // execute the pipeline var res = srv.handlerExecutePipeline(request, keepAlive) // shutting down? select { case <-srv.stopAccepting: keepAlive = false res.Close = true default: } // write response err = srv.handlerWriteResponse(request, res, c, wbpe.Br) if err != nil { Error("%s ERROR writing response: <%T %v>", srv.serverLogPrefix(), err, err) } if res.Close { keepAlive = false } } else { // EOF is socket closed if nerr, ok := err.(net.Error); err != io.EOF && !(ok && nerr.Timeout()) { Error("%s %v ERROR reading request: <%T %v>", srv.serverLogPrefix(), c.RemoteAddr(), err, err) } } } //Debug("%s Processed %v requests on connection %v", srv.serverLogPrefix(), reqCount, c.RemoteAddr()) }
// serveLongPoll handles long poll connections when WebSocket is not available // Connection could be without sid or with sid: // - if sid is empty, create session, expect a login in the same request, respond and close // - if sid is not empty and there is an initialized session, payload is optional // - if no payload, perform long poll // - if payload exists, process it and close // - if sid is not empty but there is no session, report an error func serveLongPoll(wrt http.ResponseWriter, req *http.Request) { // Use lowest common denominator - this is a legacy handler after all (otherwise would use application/json) wrt.Header().Set("Content-Type", "text/plain") enc := json.NewEncoder(wrt) if isValid, _ := checkApiKey(getApiKey(req)); !isValid { wrt.WriteHeader(http.StatusForbidden) enc.Encode( &ServerComMessage{Ctrl: &MsgServerCtrl{ Code: http.StatusForbidden, Text: "valid API key is required"}}) return } // TODO(gene): should it be configurable? // Currently any domain is allowed to get data from the chat server wrt.Header().Set("Access-Control-Allow-Origin", "*") // Ensure the response is not cached if req.ProtoAtLeast(1, 1) { wrt.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1 } else { wrt.Header().Set("Pragma", "no-cache") // HTTP 1.0 } wrt.Header().Set("Expires", "0") // Proxies // TODO(gene): respond differently to valious HTTP methods log.Printf("HTTP %s %s?%s from '%s' %d bytes", req.Method, req.URL.Path, req.URL.RawQuery, req.RemoteAddr, req.ContentLength) // Get session id sid := req.FormValue("sid") var sess *Session if sid == "" { // New session sess = globals.sessionStore.Create(wrt, "") log.Println("longPoll: new session created, sid=", sess.sid) } else { // Existing session sess = globals.sessionStore.Get(sid) if sess == nil { log.Println("longPoll: invalid or expired session id ", sid) wrt.WriteHeader(http.StatusForbidden) enc.Encode( &ServerComMessage{Ctrl: &MsgServerCtrl{ Code: http.StatusForbidden, Text: "invalid or expired session id"}}) return } } sess.wrt = wrt sess.remoteAddr = req.RemoteAddr if req.ContentLength > 0 { // Read payload and send it for processing. if !sess.readOnce(req) { // Failed to red, stop return } } // Wait for data, write it to the connection or timeout sess.writeOnce() }