diff options
Diffstat (limited to 'libgo/go/net/http/server.go')
-rw-r--r-- | libgo/go/net/http/server.go | 133 |
1 files changed, 96 insertions, 37 deletions
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index b74b76298098..ee57e01276da 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -129,7 +129,7 @@ type response struct { // maxBytesReader hits its max size. It is checked in // WriteHeader, to make sure we don't consume the the // remaining request body to try to advance to the next HTTP - // request. Instead, when this is set, we stop doing + // request. Instead, when this is set, we stop reading // subsequent requests on this connection and stop reading // input from it. requestBodyLimitHit bool @@ -287,7 +287,7 @@ func (w *response) WriteHeader(code int) { // Check for a explicit (and valid) Content-Length header. var hasCL bool var contentLength int64 - if clenStr := w.header.Get("Content-Length"); clenStr != "" { + if clenStr := w.header.get("Content-Length"); clenStr != "" { var err error contentLength, err = strconv.ParseInt(clenStr, 10, 64) if err == nil { @@ -303,12 +303,11 @@ func (w *response) WriteHeader(code int) { if !connectionHeaderSet { w.header.Set("Connection", "keep-alive") } - } else if !w.req.ProtoAtLeast(1, 1) { - // Client did not ask to keep connection alive. + } else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() { w.closeAfterReply = true } - if w.header.Get("Connection") == "close" { + if w.header.get("Connection") == "close" { w.closeAfterReply = true } @@ -332,7 +331,7 @@ func (w *response) WriteHeader(code int) { if code == StatusNotModified { // Must not have body. for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} { - if w.header.Get(header) != "" { + if w.header.get(header) != "" { // TODO: return an error if WriteHeader gets a return parameter // or set a flag on w to make future Writes() write an error page? // for now just log and drop the header. @@ -342,7 +341,7 @@ func (w *response) WriteHeader(code int) { } } else { // If no content type, apply sniffing algorithm to body. - if w.header.Get("Content-Type") == "" && w.req.Method != "HEAD" { + if w.header.get("Content-Type") == "" && w.req.Method != "HEAD" { w.needSniff = true } } @@ -351,7 +350,7 @@ func (w *response) WriteHeader(code int) { w.Header().Set("Date", time.Now().UTC().Format(TimeFormat)) } - te := w.header.Get("Transfer-Encoding") + te := w.header.get("Transfer-Encoding") hasTE := te != "" if hasCL && hasTE && te != "identity" { // TODO: return an error if WriteHeader gets a return parameter @@ -391,7 +390,7 @@ func (w *response) WriteHeader(code int) { return } - if w.closeAfterReply && !hasToken(w.header.Get("Connection"), "close") { + if w.closeAfterReply && !hasToken(w.header.get("Connection"), "close") { w.header.Set("Connection", "close") } @@ -518,14 +517,14 @@ func (w *response) finishRequest() { // HTTP/1.0 clients keep their "keep-alive" connections alive, and for // HTTP/1.1 clients is just as good as the alternative: sending a // chunked response and immediately sending the zero-length EOF chunk. - if w.written == 0 && w.header.Get("Content-Length") == "" { + if w.written == 0 && w.header.get("Content-Length") == "" { w.header.Set("Content-Length", "0") } // If this was an HTTP/1.0 request with keep-alive and we sent a // Content-Length back, we can make this a keep-alive response ... if w.req.wantsHttp10KeepAlive() { - sentLength := w.header.Get("Content-Length") != "" - if sentLength && w.header.Get("Connection") == "keep-alive" { + sentLength := w.header.get("Content-Length") != "" + if sentLength && w.header.get("Connection") == "keep-alive" { w.closeAfterReply = false } } @@ -564,18 +563,31 @@ func (w *response) Flush() { w.conn.buf.Flush() } -// Close the connection. -func (c *conn) close() { +func (c *conn) finalFlush() { if c.buf != nil { c.buf.Flush() c.buf = nil } +} + +// Close the connection. +func (c *conn) close() { + c.finalFlush() if c.rwc != nil { c.rwc.Close() c.rwc = nil } } +// closeWrite flushes any outstanding data and sends a FIN packet (if client +// is connected via TCP), signalling that we're done. +func (c *conn) closeWrite() { + c.finalFlush() + if tcp, ok := c.rwc.(*net.TCPConn); ok { + tcp.CloseWrite() + } +} + // Serve a new connection. func (c *conn) serve() { defer func() { @@ -637,7 +649,7 @@ func (c *conn) serve() { break } req.Header.Del("Expect") - } else if req.Header.Get("Expect") != "" { + } else if req.Header.get("Expect") != "" { // TODO(bradfitz): let ServeHTTP handlers handle // requests with non-standard expectation[s]? Seems // theoretical at best, and doesn't fit into the @@ -672,6 +684,20 @@ func (c *conn) serve() { } w.finishRequest() if w.closeAfterReply { + if w.requestBodyLimitHit { + // Flush our response and send a FIN packet and wait a bit + // before closing the connection, so the client has a chance + // to read our response before they possibly get a RST from + // our TCP stack from ignoring their unread body. + // See http://golang.org/issue/3595 + c.closeWrite() + // Now wait a bit for our machine to send the FIN and the client's + // machine's HTTP client to read the request before we close + // the connection, which might send a RST (on BSDs, at least). + // 250ms is somewhat arbitrary (~latency around half the planet), + // but this doesn't need to be a full second probably. + time.Sleep(250 * time.Millisecond) + } break } } @@ -849,13 +875,15 @@ func RedirectHandler(url string, code int) Handler { // redirecting any request containing . or .. elements to an // equivalent .- and ..-free URL. type ServeMux struct { - mu sync.RWMutex - m map[string]muxEntry + mu sync.RWMutex + m map[string]muxEntry + hosts bool // whether any patterns contain hostnames } type muxEntry struct { explicit bool h Handler + pattern string } // NewServeMux allocates and returns a new ServeMux. @@ -896,8 +924,7 @@ func cleanPath(p string) string { // Find a handler on a handler map given a path string // Most-specific (longest) pattern wins -func (mux *ServeMux) match(path string) Handler { - var h Handler +func (mux *ServeMux) match(path string) (h Handler, pattern string) { var n = 0 for k, v := range mux.m { if !pathMatch(k, path) { @@ -906,39 +933,59 @@ func (mux *ServeMux) match(path string) Handler { if h == nil || len(k) > n { n = len(k) h = v.h + pattern = v.pattern + } + } + return +} + +// Handler returns the handler to use for the given request, +// 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. +// +// Handler also returns the registered pattern that matches the +// request or, in the case of internally-generated redirects, +// the pattern that will match after following the redirect. +// +// 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) + return RedirectHandler(p, StatusMovedPermanently), pattern } } - return h + + return mux.handler(r.Host, r.URL.Path) } -// handler returns the handler to use for the request r. -func (mux *ServeMux) handler(r *Request) Handler { +// handler is the main implementation of Handler. +// The path is known to be in canonical form, except for CONNECT methods. +func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { mux.mu.RLock() defer mux.mu.RUnlock() // Host-specific pattern takes precedence over generic ones - h := mux.match(r.Host + r.URL.Path) + if mux.hosts { + h, pattern = mux.match(host + path) + } if h == nil { - h = mux.match(r.URL.Path) + h, pattern = mux.match(path) } if h == nil { - h = NotFoundHandler() + h, pattern = NotFoundHandler(), "" } - return h + return } // ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { - if r.Method != "CONNECT" { - // Clean path to canonical form and redirect. - if p := cleanPath(r.URL.Path); p != r.URL.Path { - w.Header().Set("Location", p) - w.WriteHeader(StatusMovedPermanently) - return - } - } - mux.handler(r).ServeHTTP(w, r) + h, _ := mux.Handler(r) + h.ServeHTTP(w, r) } // Handle registers the handler for the given pattern. @@ -957,14 +1004,26 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) { panic("http: multiple registrations for " + pattern) } - mux.m[pattern] = muxEntry{explicit: true, h: handler} + mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} + + if pattern[0] != '/' { + mux.hosts = true + } // Helpful behavior: // If pattern is /tree/, insert an implicit permanent redirect for /tree. // It can be overridden by an explicit registration. n := len(pattern) if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { - mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(pattern, StatusMovedPermanently)} + // If pattern contains a host name, strip it and use remaining + // path for redirect. + path := pattern + if pattern[0] != '/' { + // In pattern, at least the last character is a '/', so + // strings.Index can't be -1. + path = pattern[strings.Index(pattern, "/"):] + } + mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern} } } |