summaryrefslogtreecommitdiff
path: root/libgo/go/net/http/server.go
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2013-01-29 20:52:43 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2013-01-29 20:52:43 +0000
commit12ebd6294172cc1108bbadab78fea03e890a6da4 (patch)
tree4f2fad1f4b778519bdd5941185c7e1d032af055b /libgo/go/net/http/server.go
parent6effa4dc115122a3a4838de0a302dfcadcefeeca (diff)
downloadgcc-12ebd6294172cc1108bbadab78fea03e890a6da4.tar.gz
libgo: Update Go library to master revision 15489/921e53d4863c.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@195560 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/net/http/server.go')
-rw-r--r--libgo/go/net/http/server.go400
1 files changed, 234 insertions, 166 deletions
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index 89a46f06bb2..434943d49a3 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -113,7 +113,6 @@ type conn struct {
lr *io.LimitedReader // io.LimitReader(sr)
buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
tlsState *tls.ConnectionState // or nil when not using TLS
- body []byte
mu sync.Mutex // guards the following
clientGone bool // if client has disconnected mid-request
@@ -193,18 +192,85 @@ func (sr *switchReader) Read(p []byte) (n int, err error) {
return r.Read(p)
}
+// This should be >= 512 bytes for DetectContentType,
+// but otherwise it's somewhat arbitrary.
+const bufferBeforeChunkingSize = 2048
+
+// chunkWriter writes to a response's conn buffer, and is the writer
+// wrapped by the response.bufw buffered writer.
+//
+// chunkWriter also is responsible for finalizing the Header, including
+// conditionally setting the Content-Type and setting a Content-Length
+// in cases where the handler's final output is smaller than the buffer
+// size. It also conditionally adds chunk headers, when in chunking mode.
+//
+// See the comment above (*response).Write for the entire write flow.
+type chunkWriter struct {
+ res *response
+ header Header // a deep copy of r.Header, once WriteHeader is called
+ wroteHeader bool // whether the header's been sent
+
+ // set by the writeHeader method:
+ chunking bool // using chunked transfer encoding for reply body
+}
+
+var crlf = []byte("\r\n")
+
+func (cw *chunkWriter) Write(p []byte) (n int, err error) {
+ if !cw.wroteHeader {
+ cw.writeHeader(p)
+ }
+ if cw.chunking {
+ _, err = fmt.Fprintf(cw.res.conn.buf, "%x\r\n", len(p))
+ if err != nil {
+ return
+ }
+ }
+ n, err = cw.res.conn.buf.Write(p)
+ if cw.chunking && err == nil {
+ _, err = cw.res.conn.buf.Write(crlf)
+ }
+ return
+}
+
+func (cw *chunkWriter) flush() {
+ if !cw.wroteHeader {
+ cw.writeHeader(nil)
+ }
+ cw.res.conn.buf.Flush()
+}
+
+func (cw *chunkWriter) close() {
+ if !cw.wroteHeader {
+ cw.writeHeader(nil)
+ }
+ if cw.chunking {
+ // zero EOF chunk, trailer key/value pairs (currently
+ // unsupported in Go's server), followed by a blank
+ // line.
+ io.WriteString(cw.res.conn.buf, "0\r\n\r\n")
+ }
+}
+
// A response represents the server side of an HTTP response.
type response struct {
conn *conn
req *Request // request for this response
- chunking bool // using chunked transfer encoding for reply body
- wroteHeader bool // reply header has been written
+ wroteHeader bool // reply header has been (logically) written
wroteContinue bool // 100 Continue response was written
- header Header // reply header parameters
- written int64 // number of bytes written in body
- contentLength int64 // explicitly-declared Content-Length; or -1
- status int // status code passed to WriteHeader
- needSniff bool // need to sniff to find Content-Type
+
+ w *bufio.Writer // buffers output in chunks to chunkWriter
+ cw *chunkWriter
+
+ // handlerHeader is the Header that Handlers get access to,
+ // which may be retained and mutated even after WriteHeader.
+ // handlerHeader is copied into cw.header at WriteHeader
+ // time, and privately mutated thereafter.
+ handlerHeader Header
+
+ written int64 // number of bytes written in body
+ contentLength int64 // explicitly-declared Content-Length; or -1
+ status int // status code passed to WriteHeader
// close connection after this reply. set on request and
// updated after response from handler if there's a
@@ -220,6 +286,8 @@ type response struct {
// subsequent requests on this connection and stop reading
// input from it.
requestBodyLimitHit bool
+
+ handlerDone bool // set true when the handler exits
}
// requestTooLarge is called by maxBytesReader when too much input has
@@ -232,27 +300,46 @@ func (w *response) requestTooLarge() {
}
}
+// needsSniff returns whether a Content-Type still needs to be sniffed.
+func (w *response) needsSniff() bool {
+ return !w.cw.wroteHeader && w.handlerHeader.Get("Content-Type") == "" && w.written < sniffLen
+}
+
type writerOnly struct {
io.Writer
}
func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
- // Call WriteHeader before checking w.chunking if it hasn't
- // been called yet, since WriteHeader is what sets w.chunking.
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
- if !w.chunking && w.bodyAllowed() && !w.needSniff {
- w.Flush()
+
+ if w.needsSniff() {
+ n0, err := io.Copy(writerOnly{w}, io.LimitReader(src, sniffLen))
+ n += n0
+ if err != nil {
+ return n, err
+ }
+ }
+
+ w.w.Flush() // get rid of any previous writes
+ w.cw.flush() // make sure Header is written; flush data to rwc
+
+ // Now that cw has been flushed, its chunking field is guaranteed initialized.
+ if !w.cw.chunking && w.bodyAllowed() {
if rf, ok := w.conn.rwc.(io.ReaderFrom); ok {
- n, err = rf.ReadFrom(src)
- w.written += n
- return
+ n0, err := rf.ReadFrom(src)
+ n += n0
+ w.written += n0
+ return n, err
}
}
+
// Fall back to default io.Copy implementation.
// Use wrapper to hide w.ReadFrom from io.Copy.
- return io.Copy(writerOnly{w}, src)
+ n0, err := io.Copy(writerOnly{w}, src)
+ n += n0
+ return n, err
}
// noLimit is an effective infinite upper bound for io.LimitedReader
@@ -272,7 +359,6 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
c.rwc = newLoggingConn("server", c.rwc)
}
c.sr = switchReader{r: c.rwc}
- c.body = make([]byte, sniffLen)
c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader)
br := bufio.NewReader(c.lr)
bw := bufio.NewWriter(c.rwc)
@@ -343,17 +429,20 @@ func (c *conn) readRequest() (w *response, err error) {
req.RemoteAddr = c.remoteAddr
req.TLS = c.tlsState
- w = new(response)
- w.conn = c
- w.req = req
- w.header = make(Header)
- w.contentLength = -1
- c.body = c.body[:0]
+ w = &response{
+ conn: c,
+ req: req,
+ handlerHeader: make(Header),
+ contentLength: -1,
+ cw: new(chunkWriter),
+ }
+ w.cw.res = w
+ w.w = bufio.NewWriterSize(w.cw, bufferBeforeChunkingSize)
return w, nil
}
func (w *response) Header() Header {
- return w.header
+ return w.handlerHeader
}
// maxPostHandlerReadBytes is the max number of Request.Body bytes not
@@ -379,30 +468,68 @@ func (w *response) WriteHeader(code int) {
w.wroteHeader = true
w.status = code
- // Check for a explicit (and valid) Content-Length header.
- var hasCL bool
- var contentLength int64
- if clenStr := w.header.get("Content-Length"); clenStr != "" {
- var err error
- contentLength, err = strconv.ParseInt(clenStr, 10, 64)
- if err == nil {
- hasCL = true
+ w.cw.header = w.handlerHeader.clone()
+
+ if cl := w.cw.header.get("Content-Length"); cl != "" {
+ v, err := strconv.ParseInt(cl, 10, 64)
+ if err == nil && v >= 0 {
+ w.contentLength = v
} else {
- log.Printf("http: invalid Content-Length of %q sent", clenStr)
- w.header.Del("Content-Length")
+ log.Printf("http: invalid Content-Length of %q", cl)
+ w.cw.header.Del("Content-Length")
+ }
+ }
+}
+
+// writeHeader finalizes the header sent to the client and writes it
+// to cw.res.conn.buf.
+//
+// p is not written by writeHeader, but is the first chunk of the body
+// that will be written. It is sniffed for a Content-Type if none is
+// set explicitly. It's also used to set the Content-Length, if the
+// total body size was small and the handler has already finished
+// running.
+func (cw *chunkWriter) writeHeader(p []byte) {
+ if cw.wroteHeader {
+ return
+ }
+ cw.wroteHeader = true
+
+ w := cw.res
+ code := w.status
+ done := w.handlerDone
+
+ // If the handler is done but never sent a Content-Length
+ // response header and this is our first (and last) write, set
+ // it, even to zero. This helps HTTP/1.0 clients keep their
+ // "keep-alive" connections alive.
+ if done && cw.header.get("Content-Length") == "" && w.req.Method != "HEAD" {
+ w.contentLength = int64(len(p))
+ cw.header.Set("Content-Length", strconv.Itoa(len(p)))
+ }
+
+ // 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 := cw.header.get("Content-Length") != ""
+ if sentLength && cw.header.get("Connection") == "keep-alive" {
+ w.closeAfterReply = false
}
}
+ // Check for a explicit (and valid) Content-Length header.
+ hasCL := w.contentLength != -1
+
if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) {
- _, connectionHeaderSet := w.header["Connection"]
+ _, connectionHeaderSet := cw.header["Connection"]
if !connectionHeaderSet {
- w.header.Set("Connection", "keep-alive")
+ cw.header.Set("Connection", "keep-alive")
}
} else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() {
w.closeAfterReply = true
}
- if w.header.get("Connection") == "close" {
+ if cw.header.get("Connection") == "close" {
w.closeAfterReply = true
}
@@ -416,7 +543,7 @@ func (w *response) WriteHeader(code int) {
n, _ := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1)
if n >= maxPostHandlerReadBytes {
w.requestTooLarge()
- w.header.Set("Connection", "close")
+ cw.header.Set("Connection", "close")
} else {
w.req.Body.Close()
}
@@ -426,69 +553,65 @@ 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) != "" {
- // 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.
- log.Printf("http: StatusNotModified response with header %q defined", header)
- w.header.Del(header)
+ // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
+ if cw.header.get(header) != "" {
+ cw.header.Del(header)
}
}
} else {
// If no content type, apply sniffing algorithm to body.
- if w.header.get("Content-Type") == "" && w.req.Method != "HEAD" {
- w.needSniff = true
+ if cw.header.get("Content-Type") == "" && w.req.Method != "HEAD" {
+ cw.header.Set("Content-Type", DetectContentType(p))
}
}
- if _, ok := w.header["Date"]; !ok {
- w.Header().Set("Date", time.Now().UTC().Format(TimeFormat))
+ if _, ok := cw.header["Date"]; !ok {
+ cw.header.Set("Date", time.Now().UTC().Format(TimeFormat))
}
- te := w.header.get("Transfer-Encoding")
+ te := cw.header.get("Transfer-Encoding")
hasTE := te != ""
if hasCL && hasTE && te != "identity" {
// TODO: return an error if WriteHeader gets a return parameter
// For now just ignore the Content-Length.
log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
- te, contentLength)
- w.header.Del("Content-Length")
+ te, w.contentLength)
+ cw.header.Del("Content-Length")
hasCL = false
}
if w.req.Method == "HEAD" || code == StatusNotModified {
// do nothing
} else if code == StatusNoContent {
- w.header.Del("Transfer-Encoding")
+ cw.header.Del("Transfer-Encoding")
} else if hasCL {
- w.contentLength = contentLength
- w.header.Del("Transfer-Encoding")
+ cw.header.Del("Transfer-Encoding")
} else if w.req.ProtoAtLeast(1, 1) {
// HTTP/1.1 or greater: use chunked transfer encoding
// to avoid closing the connection at EOF.
// TODO: this blows away any custom or stacked Transfer-Encoding they
// might have set. Deal with that as need arises once we have a valid
// use case.
- w.chunking = true
- w.header.Set("Transfer-Encoding", "chunked")
+ cw.chunking = true
+ cw.header.Set("Transfer-Encoding", "chunked")
} else {
// HTTP version < 1.1: cannot do chunked transfer
// encoding and we don't know the Content-Length so
// signal EOF by closing connection.
w.closeAfterReply = true
- w.header.Del("Transfer-Encoding") // in case already set
+ cw.header.Del("Transfer-Encoding") // in case already set
}
// Cannot use Content-Length with non-identity Transfer-Encoding.
- if w.chunking {
- w.header.Del("Content-Length")
+ if cw.chunking {
+ cw.header.Del("Content-Length")
}
if !w.req.ProtoAtLeast(1, 0) {
return
}
- if w.closeAfterReply && !hasToken(w.header.get("Connection"), "close") {
- w.header.Set("Connection", "close")
+ if w.closeAfterReply && !hasToken(cw.header.get("Connection"), "close") {
+ cw.header.Set("Connection", "close")
}
proto := "HTTP/1.0"
@@ -501,37 +624,8 @@ func (w *response) WriteHeader(code int) {
text = "status code " + codestring
}
io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n")
- w.header.Write(w.conn.buf)
-
- // If we need to sniff the body, leave the header open.
- // Otherwise, end it here.
- if !w.needSniff {
- io.WriteString(w.conn.buf, "\r\n")
- }
-}
-
-// sniff uses the first block of written data,
-// stored in w.conn.body, to decide the Content-Type
-// for the HTTP body.
-func (w *response) sniff() {
- if !w.needSniff {
- return
- }
- w.needSniff = false
-
- data := w.conn.body
- fmt.Fprintf(w.conn.buf, "Content-Type: %s\r\n\r\n", DetectContentType(data))
-
- if len(data) == 0 {
- return
- }
- if w.chunking {
- fmt.Fprintf(w.conn.buf, "%x\r\n", len(data))
- }
- _, err := w.conn.buf.Write(data)
- if w.chunking && err == nil {
- io.WriteString(w.conn.buf, "\r\n")
- }
+ cw.header.Write(w.conn.buf)
+ w.conn.buf.Write(crlf)
}
// bodyAllowed returns true if a Write is allowed for this response type.
@@ -543,6 +637,38 @@ func (w *response) bodyAllowed() bool {
return w.status != StatusNotModified && w.req.Method != "HEAD"
}
+// The Life Of A Write is like this:
+//
+// Handler starts. No header has been sent. The handler can either
+// write a header, or just start writing. Writing before sending a header
+// sends an implicity empty 200 OK header.
+//
+// If the handler didn't declare a Content-Length up front, we either
+// go into chunking mode or, if the handler finishes running before
+// the chunking buffer size, we compute a Content-Length and send that
+// in the header instead.
+//
+// Likewise, if the handler didn't set a Content-Type, we sniff that
+// from the initial chunk of output.
+//
+// The Writers are wired together like:
+//
+// 1. *response (the ResponseWriter) ->
+// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes
+// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type)
+// and which writes the chunk headers, if needed.
+// 4. conn.buf, a bufio.Writer of default (4kB) bytes
+// 5. the rwc, the net.Conn.
+//
+// TODO(bradfitz): short-circuit some of the buffering when the
+// initial header contains both a Content-Type and Content-Length.
+// Also short-circuit in (1) when the header's been sent and not in
+// chunking mode, writing directly to (4) instead, if (2) has no
+// buffered data. More generally, we could short-circuit from (1) to
+// (3) even in chunking mode if the write size from (1) is over some
+// threshold and nothing is in (2). The answer might be mostly making
+// bufferBeforeChunkingSize smaller and having bufio's fast-paths deal
+// with this instead.
func (w *response) Write(data []byte) (n int, err error) {
if w.conn.hijacked() {
log.Print("http: response.Write on hijacked connection")
@@ -562,81 +688,20 @@ func (w *response) Write(data []byte) (n int, err error) {
if w.contentLength != -1 && w.written > w.contentLength {
return 0, ErrContentLength
}
-
- var m int
- if w.needSniff {
- // We need to sniff the beginning of the output to
- // determine the content type. Accumulate the
- // initial writes in w.conn.body.
- // Cap m so that append won't allocate.
- m = cap(w.conn.body) - len(w.conn.body)
- if m > len(data) {
- m = len(data)
- }
- w.conn.body = append(w.conn.body, data[:m]...)
- data = data[m:]
- if len(data) == 0 {
- // Copied everything into the buffer.
- // Wait for next write.
- return m, nil
- }
-
- // Filled the buffer; more data remains.
- // Sniff the content (flushes the buffer)
- // and then proceed with the remainder
- // of the data as a normal Write.
- // Calling sniff clears needSniff.
- w.sniff()
- }
-
- // TODO(rsc): if chunking happened after the buffering,
- // then there would be fewer chunk headers.
- // On the other hand, it would make hijacking more difficult.
- if w.chunking {
- fmt.Fprintf(w.conn.buf, "%x\r\n", len(data))
- }
- n, err = w.conn.buf.Write(data)
- if err == nil && w.chunking {
- if n != len(data) {
- err = io.ErrShortWrite
- }
- if err == nil {
- io.WriteString(w.conn.buf, "\r\n")
- }
- }
-
- return m + n, err
+ return w.w.Write(data)
}
func (w *response) finishRequest() {
- // If the handler never wrote any bytes and never sent a Content-Length
- // response header, set the length explicitly to zero. This helps
- // 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") == "" && w.req.Method != "HEAD" {
- 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" {
- w.closeAfterReply = false
- }
- }
+ w.handlerDone = true
+
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
- if w.needSniff {
- w.sniff()
- }
- if w.chunking {
- io.WriteString(w.conn.buf, "0\r\n")
- // trailer key/value pairs, followed by blank line
- io.WriteString(w.conn.buf, "\r\n")
- }
+
+ w.w.Flush()
+ w.cw.close()
w.conn.buf.Flush()
+
// Close the body, unless we're about to close the whole TCP connection
// anyway.
if !w.closeAfterReply {
@@ -646,7 +711,7 @@ func (w *response) finishRequest() {
w.req.MultipartForm.RemoveAll()
}
- if w.contentLength != -1 && w.contentLength != w.written {
+ if w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written {
// Did not write enough. Avoid getting out of sync.
w.closeAfterReply = true
}
@@ -656,8 +721,8 @@ func (w *response) Flush() {
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
- w.sniff()
- w.conn.buf.Flush()
+ w.w.Flush()
+ w.cw.flush()
}
func (c *conn) finalFlush() {
@@ -809,6 +874,9 @@ func (w *response) sendExpectationFailed() {
// Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter
// and a Hijacker.
func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
+ if w.wroteHeader {
+ w.cw.flush()
+ }
return w.conn.hijack()
}
@@ -1148,7 +1216,7 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
}
// Serve accepts incoming HTTP connections on the listener l,
-// creating a new service thread for each. The service threads
+// 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.
func Serve(l net.Listener, handler Handler) error {
@@ -1182,7 +1250,7 @@ func (srv *Server) ListenAndServe() error {
}
// Serve accepts incoming connections on the Listener l, creating a
-// new service thread for each. The service threads read requests and
+// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
@@ -1327,7 +1395,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
// TimeoutHandler returns a Handler that runs h with the given time limit.
//
// The new Handler calls h.ServeHTTP to handle each request, but if a
-// call runs for more than ns nanoseconds, the handler responds with
+// call runs for longer than its time limit, the handler responds with
// a 503 Service Unavailable error and the given message in its body.
// (If msg is empty, a suitable default message will be sent.)
// After such a timeout, writes by h to its ResponseWriter will return