diff options
Diffstat (limited to 'libgo/go/net/http/server.go')
-rw-r--r-- | libgo/go/net/http/server.go | 302 |
1 files changed, 189 insertions, 113 deletions
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index df70a15193b..2fa8ab23d8a 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -75,9 +75,10 @@ var ( // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes // that the effect of the panic was isolated to the active request. // It recovers the panic, logs a stack trace to the server error log, -// and hangs up the connection. To abort a handler so the client sees -// an interrupted response but the server doesn't log an error, panic -// with the value ErrAbortHandler. +// and either closes the network connection or sends an HTTP/2 +// RST_STREAM, depending on the HTTP protocol. To abort a handler so +// the client sees an interrupted response but the server doesn't log +// an error, panic with the value ErrAbortHandler. type Handler interface { ServeHTTP(ResponseWriter, *Request) } @@ -177,6 +178,9 @@ type Hijacker interface { // // The returned bufio.Reader may contain unprocessed buffered // data from the client. + // + // After a call to Hijack, the original Request.Body should + // not be used. Hijack() (net.Conn, *bufio.ReadWriter, error) } @@ -439,9 +443,10 @@ type response struct { handlerDone atomicBool // set true when the handler exits - // Buffers for Date and Content-Length - dateBuf [len(TimeFormat)]byte - clenBuf [10]byte + // Buffers for Date, Content-Length, and status code + dateBuf [len(TimeFormat)]byte + clenBuf [10]byte + statusBuf [3]byte // closeNotifyCh is the channel returned by CloseNotify. // TODO(bradfitz): this is currently (for Go 1.8) always @@ -622,7 +627,6 @@ type connReader struct { mu sync.Mutex // guards following hasByte bool byteBuf [1]byte - bgErr error // non-nil means error happened on background read cond *sync.Cond inRead bool aborted bool // set true before conn.rwc deadline is set to past @@ -731,11 +735,6 @@ func (cr *connReader) Read(p []byte) (n int, err error) { cr.unlock() return 0, io.EOF } - if cr.bgErr != nil { - err = cr.bgErr - cr.unlock() - return 0, err - } if len(p) == 0 { cr.unlock() return 0, nil @@ -839,7 +838,7 @@ func (srv *Server) initialReadLimitSize() int64 { return int64(srv.maxHeaderBytes()) + 4096 // bufio slop } -// wrapper around io.ReaderCloser which on first read, sends an +// wrapper around io.ReadCloser which on first read, sends an // HTTP/1.1 100 Continue header type expectContinueReader struct { resp *response @@ -948,7 +947,7 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) { hosts, haveHost := req.Header["Host"] isH2Upgrade := req.isH2Upgrade() - if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade { + if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade && req.Method != "CONNECT" { return nil, badRequestError("missing required Host header") } if len(hosts) > 1 { @@ -1379,7 +1378,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { } } - w.conn.bufw.WriteString(statusLine(w.req, code)) + writeStatusLine(w.conn.bufw, w.req.ProtoAtLeast(1, 1), code, w.statusBuf[:]) cw.header.WriteSubset(w.conn.bufw, excludeHeader) setHeader.Write(w.conn.bufw) w.conn.bufw.Write(crlf) @@ -1403,49 +1402,25 @@ func foreachHeaderElement(v string, fn func(string)) { } } -// statusLines is a cache of Status-Line strings, keyed by code (for -// HTTP/1.1) or negative code (for HTTP/1.0). This is faster than a -// map keyed by struct of two fields. This map's max size is bounded -// by 2*len(statusText), two protocol types for each known official -// status code in the statusText map. -var ( - statusMu sync.RWMutex - statusLines = make(map[int]string) -) - -// statusLine returns a response Status-Line (RFC 2616 Section 6.1) -// for the given request and response status code. -func statusLine(req *Request, code int) string { - // Fast path: - key := code - proto11 := req.ProtoAtLeast(1, 1) - if !proto11 { - key = -key - } - statusMu.RLock() - line, ok := statusLines[key] - statusMu.RUnlock() - if ok { - return line - } - - // Slow path: - proto := "HTTP/1.0" - if proto11 { - proto = "HTTP/1.1" - } - codestring := fmt.Sprintf("%03d", code) - text, ok := statusText[code] - if !ok { - text = "status code " + codestring +// writeStatusLine writes an HTTP/1.x Status-Line (RFC 2616 Section 6.1) +// to bw. is11 is whether the HTTP request is HTTP/1.1. false means HTTP/1.0. +// code is the response status code. +// scratch is an optional scratch buffer. If it has at least capacity 3, it's used. +func writeStatusLine(bw *bufio.Writer, is11 bool, code int, scratch []byte) { + if is11 { + bw.WriteString("HTTP/1.1 ") + } else { + bw.WriteString("HTTP/1.0 ") } - line = proto + " " + codestring + " " + text + "\r\n" - if ok { - statusMu.Lock() - defer statusMu.Unlock() - statusLines[key] = line + if text, ok := statusText[code]; ok { + bw.Write(strconv.AppendInt(scratch[:0], int64(code), 10)) + bw.WriteByte(' ') + bw.WriteString(text) + bw.WriteString("\r\n") + } else { + // don't worry about performance + fmt.Fprintf(bw, "%03d status code %d\r\n", code, code) } - return line } // bodyAllowed reports whether a Write is allowed for this response type. @@ -1714,6 +1689,7 @@ func isCommonNetReadError(err error) bool { // Serve a new connection. func (c *conn) serve(ctx context.Context) { c.remoteAddr = c.rwc.RemoteAddr().String() + ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr()) defer func() { if err := recover(); err != nil && err != ErrAbortHandler { const size = 64 << 10 @@ -1973,8 +1949,12 @@ func StripPrefix(prefix string, h Handler) Handler { } return HandlerFunc(func(w ResponseWriter, r *Request) { if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { - r.URL.Path = p - h.ServeHTTP(w, r) + r2 := new(Request) + *r2 = *r + r2.URL = new(url.URL) + *r2.URL = *r.URL + r2.URL.Path = p + h.ServeHTTP(w, r2) } else { NotFound(w, r) } @@ -1986,8 +1966,9 @@ func StripPrefix(prefix string, h Handler) Handler { // // The provided code should be in the 3xx range and is usually // StatusMovedPermanently, StatusFound or StatusSeeOther. -func Redirect(w ResponseWriter, r *Request, urlStr string, code int) { - if u, err := url.Parse(urlStr); err == nil { +func Redirect(w ResponseWriter, r *Request, url string, code int) { + // parseURL is just url.Parse (url is shadowed for godoc). + if u, err := parseURL(url); err == nil { // If url was relative, make absolute by // combining with request path. // The browser would probably do this for us, @@ -2011,39 +1992,43 @@ func Redirect(w ResponseWriter, r *Request, urlStr string, code int) { } // no leading http://server - if urlStr == "" || urlStr[0] != '/' { + if url == "" || url[0] != '/' { // make relative path absolute olddir, _ := path.Split(oldpath) - urlStr = olddir + urlStr + url = olddir + url } var query string - if i := strings.Index(urlStr, "?"); i != -1 { - urlStr, query = urlStr[:i], urlStr[i:] + if i := strings.Index(url, "?"); i != -1 { + url, query = url[:i], url[i:] } // clean up but preserve trailing slash - trailing := strings.HasSuffix(urlStr, "/") - urlStr = path.Clean(urlStr) - if trailing && !strings.HasSuffix(urlStr, "/") { - urlStr += "/" + trailing := strings.HasSuffix(url, "/") + url = path.Clean(url) + if trailing && !strings.HasSuffix(url, "/") { + url += "/" } - urlStr += query + url += query } } - w.Header().Set("Location", hexEscapeNonASCII(urlStr)) + w.Header().Set("Location", hexEscapeNonASCII(url)) w.WriteHeader(code) // RFC 2616 recommends that a short note "SHOULD" be included in the // response because older user agents may not understand 301/307. // Shouldn't send the response for POST or HEAD; that leaves GET. if r.Method == "GET" { - note := "<a href=\"" + htmlEscape(urlStr) + "\">" + statusText[code] + "</a>.\n" + note := "<a href=\"" + htmlEscape(url) + "\">" + statusText[code] + "</a>.\n" fmt.Fprintln(w, note) } } +// parseURL is just url.Parse. It exists only so that url.Parse can be called +// in places where url is shadowed for godoc. See https://golang.org/cl/49930. +var parseURL = url.Parse + var htmlReplacer = strings.NewReplacer( "&", "&", "<", "<", @@ -2163,9 +2148,29 @@ func cleanPath(p string) string { return np } -// Find a handler on a handler map given a path string -// Most-specific (longest) pattern wins +// stripHostPort returns h without any trailing ":<port>". +func stripHostPort(h string) string { + // If no port on host, return unchanged + if strings.IndexByte(h, ':') == -1 { + return h + } + host, _, err := net.SplitHostPort(h) + if err != nil { + return h // on error, return unchanged + } + return host +} + +// Find a handler on a handler map given a path string. +// Most-specific (longest) pattern wins. func (mux *ServeMux) match(path string) (h Handler, pattern string) { + // Check for exact match first. + v, ok := mux.m[path] + if ok { + return v.h, v.pattern + } + + // Check for longest valid match. var n = 0 for k, v := range mux.m { if !pathMatch(k, path) { @@ -2184,7 +2189,10 @@ func (mux *ServeMux) match(path string) (h Handler, pattern string) { // consulting r.Method, r.Host, and r.URL.Path. It always returns // a non-nil handler. If the path is not in its canonical form, the // handler will be an internally-generated handler that redirects -// to the canonical path. +// to the canonical path. If the host contains a port, it is ignored +// when matching handlers. +// +// The path and host are used unchanged for CONNECT requests. // // Handler also returns the registered pattern that matches the // request or, in the case of internally-generated redirects, @@ -2193,16 +2201,24 @@ func (mux *ServeMux) match(path string) (h Handler, pattern string) { // If there is no registered handler that applies to the request, // Handler returns a ``page not found'' handler and an empty pattern. func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { - if r.Method != "CONNECT" { - if p := cleanPath(r.URL.Path); p != r.URL.Path { - _, pattern = mux.handler(r.Host, p) - url := *r.URL - url.Path = p - return RedirectHandler(url.String(), StatusMovedPermanently), pattern - } + + // CONNECT requests are not canonicalized. + if r.Method == "CONNECT" { + return mux.handler(r.Host, r.URL.Path) } - return mux.handler(r.Host, r.URL.Path) + // All other requests have any port stripped and path cleaned + // before passing to mux.handler. + host := stripHostPort(r.Host) + path := cleanPath(r.URL.Path) + if path != r.URL.Path { + _, pattern = mux.handler(host, path) + url := *r.URL + url.Path = path + return RedirectHandler(url.String(), StatusMovedPermanently), pattern + } + + return mux.handler(host, r.URL.Path) } // handler is the main implementation of Handler. @@ -2307,12 +2323,27 @@ func Serve(l net.Listener, handler Handler) error { return srv.Serve(l) } +// Serve accepts incoming HTTPS connections on the listener l, +// creating a new service goroutine for each. The service goroutines +// read requests and then call handler to reply to them. +// +// Handler is typically nil, in which case the DefaultServeMux is used. +// +// Additionally, files containing a certificate and matching private key +// for the server must be provided. If the certificate is signed by a +// certificate authority, the certFile should be the concatenation +// of the server's certificate, any intermediates, and the CA's certificate. +func ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error { + srv := &Server{Handler: handler} + return srv.ServeTLS(l, certFile, keyFile) +} + // A Server defines parameters for running an HTTP server. // The zero value for Server is a valid configuration. type Server struct { Addr string // TCP address to listen on, ":http" if empty Handler Handler // handler to invoke, http.DefaultServeMux if nil - TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS + TLSConfig *tls.Config // optional TLS config, used by ServeTLS and ListenAndServeTLS // ReadTimeout is the maximum duration for reading the entire // request, including the body. @@ -2338,7 +2369,7 @@ type Server struct { // IdleTimeout is the maximum amount of time to wait for the // next request when keep-alives are enabled. If IdleTimeout // is zero, the value of ReadTimeout is used. If both are - // zero, there is no timeout. + // zero, ReadHeaderTimeout is used. IdleTimeout time.Duration // MaxHeaderBytes controls the maximum number of bytes the @@ -2379,6 +2410,7 @@ type Server struct { listeners map[net.Listener]struct{} activeConn map[*conn]struct{} doneChan chan struct{} + onShutdown []func() } func (s *Server) getDoneChan() <-chan struct{} { @@ -2441,7 +2473,12 @@ var shutdownPollInterval = 500 * time.Millisecond // listeners, then closing all idle connections, and then waiting // indefinitely for connections to return to idle and then shut down. // If the provided context expires before the shutdown is complete, -// then the context's error is returned. +// Shutdown returns the context's error, otherwise it returns any +// error returned from closing the Server's underlying Listener(s). +// +// When Shutdown is called, Serve, ListenAndServe, and +// ListenAndServeTLS immediately return ErrServerClosed. Make sure the +// program doesn't exit and waits instead for Shutdown to return. // // Shutdown does not attempt to close nor wait for hijacked // connections such as WebSockets. The caller of Shutdown should @@ -2454,6 +2491,9 @@ func (srv *Server) Shutdown(ctx context.Context) error { srv.mu.Lock() lnerr := srv.closeListenersLocked() srv.closeDoneChanLocked() + for _, f := range srv.onShutdown { + go f() + } srv.mu.Unlock() ticker := time.NewTicker(shutdownPollInterval) @@ -2470,6 +2510,17 @@ func (srv *Server) Shutdown(ctx context.Context) error { } } +// RegisterOnShutdown registers a function to call on Shutdown. +// This can be used to gracefully shutdown connections that have +// undergone NPN/ALPN protocol upgrade or that have been hijacked. +// This function should start protocol-specific graceful shutdown, +// but should not wait for shutdown to complete. +func (srv *Server) RegisterOnShutdown(f func()) { + srv.mu.Lock() + srv.onShutdown = append(srv.onShutdown, f) + srv.mu.Unlock() +} + // closeIdleConns closes all idle connections and reports whether the // server is quiescent. func (s *Server) closeIdleConns() bool { @@ -2609,6 +2660,8 @@ func (srv *Server) shouldConfigureHTTP2ForServe() bool { return strSliceContains(srv.TLSConfig.NextProtos, http2NextProtoTLS) } +// ErrServerClosed is returned by the Server's Serve, ServeTLS, ListenAndServe, +// and ListenAndServeTLS methods after a call to Shutdown or Close. var ErrServerClosed = errors.New("http: Server closed") // Serve accepts incoming connections on the Listener l, creating a @@ -2638,7 +2691,6 @@ func (srv *Server) Serve(l net.Listener) error { baseCtx := context.Background() // base is always background, per Issue 16220 ctx := context.WithValue(baseCtx, ServerContextKey, srv) - ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr()) for { rw, e := l.Accept() if e != nil { @@ -2669,6 +2721,49 @@ func (srv *Server) Serve(l net.Listener) error { } } +// ServeTLS accepts incoming connections on the Listener l, creating a +// new service goroutine for each. The service goroutines read requests and +// then call srv.Handler to reply to them. +// +// Additionally, files containing a certificate and matching private key for +// the server must be provided if neither the Server's TLSConfig.Certificates +// nor TLSConfig.GetCertificate are populated.. If the certificate is signed by +// a certificate authority, the certFile should be the concatenation of the +// server's certificate, any intermediates, and the CA's certificate. +// +// For HTTP/2 support, srv.TLSConfig should be initialized to the +// provided listener's TLS Config before calling Serve. If +// srv.TLSConfig is non-nil and doesn't include the string "h2" in +// Config.NextProtos, HTTP/2 support is not enabled. +// +// ServeTLS always returns a non-nil error. After Shutdown or Close, the +// returned error is ErrServerClosed. +func (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) error { + // Setup HTTP/2 before srv.Serve, to initialize srv.TLSConfig + // before we clone it and create the TLS Listener. + if err := srv.setupHTTP2_ServeTLS(); err != nil { + return err + } + + config := cloneTLSConfig(srv.TLSConfig) + if !strSliceContains(config.NextProtos, "http/1.1") { + config.NextProtos = append(config.NextProtos, "http/1.1") + } + + configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil + if !configHasCert || certFile != "" || keyFile != "" { + var err error + config.Certificates = make([]tls.Certificate, 1) + config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return err + } + } + + tlsListener := tls.NewListener(l, config) + return srv.Serve(tlsListener) +} + func (s *Server) trackListener(ln net.Listener, add bool) { s.mu.Lock() defer s.mu.Unlock() @@ -2840,47 +2935,25 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error { addr = ":https" } - // Setup HTTP/2 before srv.Serve, to initialize srv.TLSConfig - // before we clone it and create the TLS Listener. - if err := srv.setupHTTP2_ListenAndServeTLS(); err != nil { - return err - } - - config := cloneTLSConfig(srv.TLSConfig) - if !strSliceContains(config.NextProtos, "http/1.1") { - config.NextProtos = append(config.NextProtos, "http/1.1") - } - - configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil - if !configHasCert || certFile != "" || keyFile != "" { - var err error - config.Certificates = make([]tls.Certificate, 1) - config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return err - } - } - ln, err := net.Listen("tcp", addr) if err != nil { return err } - tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config) - return srv.Serve(tlsListener) + return srv.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, certFile, keyFile) } -// setupHTTP2_ListenAndServeTLS conditionally configures HTTP/2 on +// setupHTTP2_ServeTLS conditionally configures HTTP/2 on // srv and returns whether there was an error setting it up. If it is // not configured for policy reasons, nil is returned. -func (srv *Server) setupHTTP2_ListenAndServeTLS() error { +func (srv *Server) setupHTTP2_ServeTLS() error { srv.nextProtoOnce.Do(srv.onceSetNextProtoDefaults) return srv.nextProtoErr } // setupHTTP2_Serve is called from (*Server).Serve and conditionally // configures HTTP/2 on srv using a more conservative policy than -// setupHTTP2_ListenAndServeTLS because Serve may be called +// setupHTTP2_ServeTLS because Serve may be called // concurrently. // // The tests named TestTransportAutomaticHTTP2* and @@ -2907,7 +2980,10 @@ func (srv *Server) onceSetNextProtoDefaults() { // Enable HTTP/2 by default if the user hasn't otherwise // configured their TLSNextProto map. if srv.TLSNextProto == nil { - srv.nextProtoErr = http2ConfigureServer(srv, nil) + conf := &http2Server{ + NewWriteScheduler: func() http2WriteScheduler { return http2NewPriorityWriteScheduler(nil) }, + } + srv.nextProtoErr = http2ConfigureServer(srv, conf) } } |