diff options
| author | Jed Denlea <jed@fastly.com> | 2015-07-29 18:10:32 -0700 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2015-08-02 09:34:59 +0000 |
| commit | c2db5f4ccc61ba7df96a747e268a277b802cbb87 (patch) | |
| tree | 15954dabb41a075c51ccc98b52e6552d9b280090 /src/net/http/server.go | |
| parent | ec4d06e47010ef5a7a69080046530997169e7666 (diff) | |
| download | go-c2db5f4ccc61ba7df96a747e268a277b802cbb87.tar.xz | |
net/http: close server conn after request body error
HTTP servers attempt to entirely consume a request body before sending a
response. However, when doing so, it previously would ignore any errors
encountered.
Unfortunately, the errors triggered at this stage are indicative of at
least a couple problems: read timeouts and chunked encoding errors.
This means properly crafted and/or timed requests could lead to a
"smuggled" request.
The fix is to inspect the errors created by the response body Reader,
and treat anything other than io.EOF or ErrBodyReadAfterClose as
fatal to the connection.
Fixes #11930
Change-Id: I0bf18006d7d8f6537529823fc450f2e2bdb7c18e
Reviewed-on: https://go-review.googlesource.com/12865
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/net/http/server.go')
| -rw-r--r-- | src/net/http/server.go | 59 |
1 files changed, 46 insertions, 13 deletions
diff --git a/src/net/http/server.go b/src/net/http/server.go index d12f696eaf..905a8b9ad8 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -872,23 +872,56 @@ func (cw *chunkWriter) writeHeader(p []byte) { // don't want to do an unbounded amount of reading here for // DoS reasons, so we only try up to a threshold. if w.req.ContentLength != 0 && !w.closeAfterReply { - ecr, isExpecter := w.req.Body.(*expectContinueReader) - if !isExpecter || ecr.resp.wroteContinue { - var tooBig bool - if reqBody, ok := w.req.Body.(*body); ok && reqBody.unreadDataSize() >= maxPostHandlerReadBytes { + var discard, tooBig bool + + switch bdy := w.req.Body.(type) { + case *expectContinueReader: + if bdy.resp.wroteContinue { + discard = true + } + case *body: + switch { + case bdy.closed: + if !bdy.sawEOF { + // Body was closed in handler with non-EOF error. + w.closeAfterReply = true + } + case bdy.unreadDataSize() >= maxPostHandlerReadBytes: tooBig = true - } else { - n, _ := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1) - tooBig = n >= maxPostHandlerReadBytes + default: + discard = true } - if tooBig { - w.requestTooLarge() - delHeader("Connection") - setHeader.connection = "close" - } else { - w.req.Body.Close() + default: + discard = true + } + + if discard { + _, err := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1) + switch err { + case nil: + // There must be even more data left over. + tooBig = true + case ErrBodyReadAfterClose: + // Body was already consumed and closed. + case io.EOF: + // The remaining body was just consumed, close it. + err = w.req.Body.Close() + if err != nil { + w.closeAfterReply = true + } + default: + // Some other kind of error occured, like a read timeout, or + // corrupt chunked encoding. In any case, whatever remains + // on the wire must not be parsed as another HTTP request. + w.closeAfterReply = true } } + + if tooBig { + w.requestTooLarge() + delHeader("Connection") + setHeader.connection = "close" + } } code := w.status |
