Skip to content

Commit ff9f7bc

Browse files
odeke-embradfitz
authored andcommitted
net/http: make Transport.RoundTrip close body on any invalid request
Fixes #35015 Change-Id: I7a1ed9cfa219ad88014aad033e3a01f9dffc3eb3 Reviewed-on: https://go-review.googlesource.com/c/go/+/202239 Run-TryBot: Emmanuel Odeke <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent a7ce2ca commit ff9f7bc

File tree

2 files changed

+81
-13
lines changed

2 files changed

+81
-13
lines changed

src/net/http/transport.go

+2
Original file line numberDiff line numberDiff line change
@@ -469,10 +469,12 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
469469
if isHTTP {
470470
for k, vv := range req.Header {
471471
if !httpguts.ValidHeaderFieldName(k) {
472+
req.closeBody()
472473
return nil, fmt.Errorf("net/http: invalid header field name %q", k)
473474
}
474475
for _, v := range vv {
475476
if !httpguts.ValidHeaderFieldValue(v) {
477+
req.closeBody()
476478
return nil, fmt.Errorf("net/http: invalid header field value %q for key %v", v, k)
477479
}
478480
}

src/net/http/transport_test.go

+79-13
Original file line numberDiff line numberDiff line change
@@ -5730,21 +5730,87 @@ func (bc *bodyCloser) Read(b []byte) (n int, err error) {
57305730
return 0, io.EOF
57315731
}
57325732

5733-
func TestInvalidMethodClosesBody(t *testing.T) {
5734-
cst := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
5733+
// Issue 35015: ensure that Transport closes the body on any error
5734+
// with an invalid request, as promised by Client.Do docs.
5735+
func TestTransportClosesBodyOnInvalidRequests(t *testing.T) {
5736+
cst := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
5737+
t.Errorf("Should not have been invoked")
5738+
}))
57355739
defer cst.Close()
5736-
var bc bodyCloser
5740+
57375741
u, _ := url.Parse(cst.URL)
5738-
req := &Request{
5739-
Method: " ",
5740-
URL: u,
5741-
Body: &bc,
5742-
}
5743-
_, err := DefaultClient.Do(req)
5744-
if err == nil {
5745-
t.Fatal("Expected an error")
5742+
5743+
tests := []struct {
5744+
name string
5745+
req *Request
5746+
wantErr string
5747+
}{
5748+
{
5749+
name: "invalid method",
5750+
req: &Request{
5751+
Method: " ",
5752+
URL: u,
5753+
},
5754+
wantErr: "invalid method",
5755+
},
5756+
{
5757+
name: "nil URL",
5758+
req: &Request{
5759+
Method: "GET",
5760+
},
5761+
wantErr: "nil Request.URL",
5762+
},
5763+
{
5764+
name: "invalid header key",
5765+
req: &Request{
5766+
Method: "GET",
5767+
Header: Header{"💡": {"emoji"}},
5768+
URL: u,
5769+
},
5770+
wantErr: "invalid header field name",
5771+
},
5772+
{
5773+
name: "invalid header value",
5774+
req: &Request{
5775+
Method: "POST",
5776+
Header: Header{"key": {"\x19"}},
5777+
URL: u,
5778+
},
5779+
wantErr: "invalid header field value",
5780+
},
5781+
{
5782+
name: "non HTTP(s) scheme",
5783+
req: &Request{
5784+
Method: "POST",
5785+
URL: &url.URL{Scheme: "faux"},
5786+
},
5787+
wantErr: "unsupported protocol scheme",
5788+
},
5789+
{
5790+
name: "no Host in URL",
5791+
req: &Request{
5792+
Method: "POST",
5793+
URL: &url.URL{Scheme: "http"},
5794+
},
5795+
wantErr: "no Host",
5796+
},
57465797
}
5747-
if !bc {
5748-
t.Fatal("Expected body to have been closed")
5798+
5799+
for _, tt := range tests {
5800+
t.Run(tt.name, func(t *testing.T) {
5801+
var bc bodyCloser
5802+
req := tt.req
5803+
req.Body = &bc
5804+
_, err := DefaultClient.Do(tt.req)
5805+
if err == nil {
5806+
t.Fatal("Expected an error")
5807+
}
5808+
if !bc {
5809+
t.Fatal("Expected body to have been closed")
5810+
}
5811+
if g, w := err.Error(), tt.wantErr; !strings.Contains(g, w) {
5812+
t.Fatalf("Error mismatch\n\t%q\ndoes not contain\n\t%q", g, w)
5813+
}
5814+
})
57495815
}
57505816
}

0 commit comments

Comments
 (0)