Skip to content

Commit bdb5e9d

Browse files
harshavardhanabradfitz
authored andcommitted
net/http/httputil: fix missing Transfer-Encoding header
Current implementation of httputil.DumpRequestOut incorrectly resets the Request.Body prematurely before Content-Length/Transfer-Encoding detection in newTransferWriter() This fix avoids resetting the Request.Body when Request.ContentLength is set to '0' by the caller and Request.Body is set to a custom reader. To allow newTransferWriter() to treat this situation as 'Transfer-Encoding: chunked'. Fixes #34504 Change-Id: Ieab6bf876ced28c32c084e0f4c8c4432964181f5 Reviewed-on: https://go-review.googlesource.com/c/go/+/197898 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent afe50c1 commit bdb5e9d

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

src/net/http/httputil/dump.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
// It returns an error if the initial slurp of all bytes fails. It does not attempt
2525
// to make the returned ReadClosers have identical error-matching behavior.
2626
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
27-
if b == http.NoBody {
27+
if b == nil || b == http.NoBody {
2828
// No copying needed. Preserve the magic sentinel meaning of NoBody.
2929
return http.NoBody, http.NoBody, nil
3030
}
@@ -60,16 +60,28 @@ func (b neverEnding) Read(p []byte) (n int, err error) {
6060
return len(p), nil
6161
}
6262

63+
// outGoingLength is a copy of the unexported
64+
// (*http.Request).outgoingLength method.
65+
func outgoingLength(req *http.Request) int64 {
66+
if req.Body == nil || req.Body == http.NoBody {
67+
return 0
68+
}
69+
if req.ContentLength != 0 {
70+
return req.ContentLength
71+
}
72+
return -1
73+
}
74+
6375
// DumpRequestOut is like DumpRequest but for outgoing client requests. It
6476
// includes any headers that the standard http.Transport adds, such as
6577
// User-Agent.
6678
func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
6779
save := req.Body
6880
dummyBody := false
69-
if !body || req.Body == nil {
70-
req.Body = nil
71-
if req.ContentLength != 0 {
72-
req.Body = ioutil.NopCloser(io.LimitReader(neverEnding('x'), req.ContentLength))
81+
if !body {
82+
contentLength := outgoingLength(req)
83+
if contentLength != 0 {
84+
req.Body = ioutil.NopCloser(io.LimitReader(neverEnding('x'), contentLength))
7385
dummyBody = true
7486
}
7587
} else {

src/net/http/httputil/dump_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ import (
1717
"testing"
1818
)
1919

20+
type eofReader struct{}
21+
22+
func (n eofReader) Close() error { return nil }
23+
24+
func (n eofReader) Read([]byte) (int, error) { return 0, io.EOF }
25+
2026
type dumpTest struct {
2127
// Either Req or GetReq can be set/nil but not both.
2228
Req *http.Request
@@ -204,6 +210,29 @@ var dumpTests = []dumpTest{
204210
"Content-Length: 0\r\n" +
205211
"Accept-Encoding: gzip\r\n\r\n",
206212
},
213+
214+
// Issue 34504: a non-nil Body without ContentLength set should be chunked
215+
{
216+
Req: &http.Request{
217+
Method: "PUT",
218+
URL: &url.URL{
219+
Scheme: "http",
220+
Host: "post.tld",
221+
Path: "/test",
222+
},
223+
ContentLength: 0,
224+
Proto: "HTTP/1.1",
225+
ProtoMajor: 1,
226+
ProtoMinor: 1,
227+
Body: &eofReader{},
228+
},
229+
NoBody: true,
230+
WantDumpOut: "PUT /test HTTP/1.1\r\n" +
231+
"Host: post.tld\r\n" +
232+
"User-Agent: Go-http-client/1.1\r\n" +
233+
"Transfer-Encoding: chunked\r\n" +
234+
"Accept-Encoding: gzip\r\n\r\n",
235+
},
207236
}
208237

209238
func TestDumpRequest(t *testing.T) {

0 commit comments

Comments
 (0)