aboutsummaryrefslogtreecommitdiff
path: root/src/net/http/server.go
diff options
context:
space:
mode:
authorJed Denlea <jed@fastly.com>2015-07-29 18:10:32 -0700
committerBrad Fitzpatrick <bradfitz@golang.org>2015-08-02 09:34:59 +0000
commitc2db5f4ccc61ba7df96a747e268a277b802cbb87 (patch)
tree15954dabb41a075c51ccc98b52e6552d9b280090 /src/net/http/server.go
parentec4d06e47010ef5a7a69080046530997169e7666 (diff)
downloadgo-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.go59
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