1
0
mirror of https://github.com/golang/go synced 2024-11-18 00:14:47 -07:00

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 <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Emmanuel T Odeke 2019-10-19 23:50:15 -07:00 committed by Brad Fitzpatrick
parent a7ce2ca52f
commit ff9f7bc9da
2 changed files with 81 additions and 13 deletions

View File

@ -469,10 +469,12 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
if isHTTP { if isHTTP {
for k, vv := range req.Header { for k, vv := range req.Header {
if !httpguts.ValidHeaderFieldName(k) { if !httpguts.ValidHeaderFieldName(k) {
req.closeBody()
return nil, fmt.Errorf("net/http: invalid header field name %q", k) return nil, fmt.Errorf("net/http: invalid header field name %q", k)
} }
for _, v := range vv { for _, v := range vv {
if !httpguts.ValidHeaderFieldValue(v) { if !httpguts.ValidHeaderFieldValue(v) {
req.closeBody()
return nil, fmt.Errorf("net/http: invalid header field value %q for key %v", v, k) return nil, fmt.Errorf("net/http: invalid header field value %q for key %v", v, k)
} }
} }

View File

@ -5730,21 +5730,87 @@ func (bc *bodyCloser) Read(b []byte) (n int, err error) {
return 0, io.EOF return 0, io.EOF
} }
func TestInvalidMethodClosesBody(t *testing.T) { // Issue 35015: ensure that Transport closes the body on any error
cst := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {})) // with an invalid request, as promised by Client.Do docs.
func TestTransportClosesBodyOnInvalidRequests(t *testing.T) {
cst := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
t.Errorf("Should not have been invoked")
}))
defer cst.Close() defer cst.Close()
var bc bodyCloser
u, _ := url.Parse(cst.URL) u, _ := url.Parse(cst.URL)
req := &Request{
Method: " ", tests := []struct {
URL: u, name string
Body: &bc, req *Request
wantErr string
}{
{
name: "invalid method",
req: &Request{
Method: " ",
URL: u,
},
wantErr: "invalid method",
},
{
name: "nil URL",
req: &Request{
Method: "GET",
},
wantErr: "nil Request.URL",
},
{
name: "invalid header key",
req: &Request{
Method: "GET",
Header: Header{"💡": {"emoji"}},
URL: u,
},
wantErr: "invalid header field name",
},
{
name: "invalid header value",
req: &Request{
Method: "POST",
Header: Header{"key": {"\x19"}},
URL: u,
},
wantErr: "invalid header field value",
},
{
name: "non HTTP(s) scheme",
req: &Request{
Method: "POST",
URL: &url.URL{Scheme: "faux"},
},
wantErr: "unsupported protocol scheme",
},
{
name: "no Host in URL",
req: &Request{
Method: "POST",
URL: &url.URL{Scheme: "http"},
},
wantErr: "no Host",
},
} }
_, err := DefaultClient.Do(req)
if err == nil { for _, tt := range tests {
t.Fatal("Expected an error") t.Run(tt.name, func(t *testing.T) {
} var bc bodyCloser
if !bc { req := tt.req
t.Fatal("Expected body to have been closed") req.Body = &bc
_, err := DefaultClient.Do(tt.req)
if err == nil {
t.Fatal("Expected an error")
}
if !bc {
t.Fatal("Expected body to have been closed")
}
if g, w := err.Error(), tt.wantErr; !strings.Contains(g, w) {
t.Fatalf("Error mismatch\n\t%q\ndoes not contain\n\t%q", g, w)
}
})
} }
} }