From: Brad Fitzpatrick Date: Fri, 30 Sep 2016 17:40:58 +0000 (-0700) Subject: net/http: refactor testing of Request.Body on 0 ContentLength X-Git-Tag: go1.8beta1~1093 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=9491e7d65e09644eb7db4e2ed5ff0139571cedf3;p=gostls13.git net/http: refactor testing of Request.Body on 0 ContentLength Code movement only, to look more like the equivalent http2 code, and to make an upcoming fix look more obvious. Updates #16002 (to be fixed once this code is in) Change-Id: Iaa4f965be14e98f9996e7c4624afe6e19bed1a80 Reviewed-on: https://go-review.googlesource.com/30087 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor Reviewed-by: Joe Tsai --- diff --git a/src/net/http/request.go b/src/net/http/request.go index b191d519f2..21e25b08ef 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -1215,3 +1215,42 @@ func (r *Request) isReplayable() bool { } return false } + +// bodyAndLength reports the request's body and content length, with +// the difference from r.ContentLength being that 0 means actually +// zero, and -1 means unknown. +func (r *Request) bodyAndLength() (body io.Reader, contentLen int64) { + body = r.Body + if body == nil { + return nil, 0 + } + if r.ContentLength != 0 { + return body, r.ContentLength + } + // Don't try to sniff the bytes if they're using a custom + // transfer encoding (or specified chunked themselves), and + // don't sniff if they're not using HTTP/1.1 and can't chunk + // anyway. + if len(r.TransferEncoding) != 0 || !r.ProtoAtLeast(1, 1) { + return body, -1 + } + + // Test to see if it's actually zero or just unset. + var buf [1]byte + n, err := io.ReadFull(body, buf[:]) + if err != nil && err != io.EOF { + return errorReader{err}, -1 + } + + if n == 1 { + // Oh, guess there is data in this Body Reader after all. + // The ContentLength field just wasn't set. + // Stich the Body back together again, re-attaching our + // consumed byte. + // TODO(bradfitz): switch to stitchByteAndReader + return io.MultiReader(bytes.NewReader(buf[:]), body), -1 + } + + // Body is actually empty. + return nil, 0 +} diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go index 851469ccbc..9d31b71f32 100644 --- a/src/net/http/transfer.go +++ b/src/net/http/transfer.go @@ -59,37 +59,17 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) { return nil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", rr.ContentLength) } t.Method = valueOrDefault(rr.Method, "GET") - t.Body = rr.Body - t.BodyCloser = rr.Body - t.ContentLength = rr.ContentLength t.Close = rr.Close t.TransferEncoding = rr.TransferEncoding t.Trailer = rr.Trailer atLeastHTTP11 = rr.ProtoAtLeast(1, 1) - if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 { - if t.ContentLength == 0 { - // Test to see if it's actually zero or just unset. - var buf [1]byte - n, rerr := io.ReadFull(t.Body, buf[:]) - if rerr != nil && rerr != io.EOF { - t.ContentLength = -1 - t.Body = errorReader{rerr} - } else if n == 1 { - // Oh, guess there is data in this Body Reader after all. - // The ContentLength field just wasn't set. - // Stich the Body back together again, re-attaching our - // consumed byte. - t.ContentLength = -1 - t.Body = io.MultiReader(bytes.NewReader(buf[:]), t.Body) - } else { - // Body is actually empty. - t.Body = nil - t.BodyCloser = nil - } - } - if t.ContentLength < 0 { - t.TransferEncoding = []string{"chunked"} - } + + t.Body, t.ContentLength = rr.bodyAndLength() + if t.Body != nil { + t.BodyCloser = rr.Body + } + if t.ContentLength < 0 && len(t.TransferEncoding) == 0 && atLeastHTTP11 { + t.TransferEncoding = []string{"chunked"} } case *Response: t.IsResponse = true