1
0
mirror of https://github.com/golang/go synced 2024-09-30 10:28:33 -06:00

net/http: fix double-close of req.Body

Add a test and fix for the request body being closed twice.

Fixes #19186

Change-Id: I1e35ad4aebfef68e6099c1dba7986883afdef4d7
Reviewed-on: https://go-review.googlesource.com/37298
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Matt Harden 2017-02-20 20:07:44 -08:00 committed by Brad Fitzpatrick
parent eab99a8d54
commit bd4fcd001c
2 changed files with 38 additions and 5 deletions

View File

@ -498,13 +498,17 @@ func (c *Client) Do(req *Request) (*Response, error) {
reqs []*Request
resp *Response
copyHeaders = c.makeHeadersCopier(req)
reqBodyClosed = false // have we closed the current req.Body?
// Redirect behavior:
redirectMethod string
includeBody bool
)
uerr := func(err error) error {
// the body may have been closed already by c.send()
if !reqBodyClosed {
req.closeBody()
}
method := valueOrDefault(reqs[0].Method, "GET")
var urlStr string
if resp != nil && resp.Request != nil {
@ -596,6 +600,8 @@ func (c *Client) Do(req *Request) (*Response, error) {
var err error
var didTimeout func() bool
if resp, didTimeout, err = c.send(req, deadline); err != nil {
// c.send() always closes req.Body
reqBodyClosed = true
if !deadline.IsZero() && didTimeout() {
err = &httpError{
err: err.Error() + " (Client.Timeout exceeded while awaiting headers)",

View File

@ -1381,3 +1381,30 @@ func testServerUndeclaredTrailers(t *testing.T, h2 bool) {
t.Errorf("Trailer = %#v; want %#v", res.Trailer, want)
}
}
func TestBadResponseAfterReadingBody(t *testing.T) {
defer afterTest(t)
cst := newClientServerTest(t, false, HandlerFunc(func(w ResponseWriter, r *Request) {
_, err := io.Copy(ioutil.Discard, r.Body)
if err != nil {
t.Fatal(err)
}
c, _, err := w.(Hijacker).Hijack()
if err != nil {
t.Fatal(err)
}
defer c.Close()
fmt.Fprintln(c, "some bogus crap")
}))
defer cst.close()
closes := 0
res, err := cst.c.Post(cst.ts.URL, "text/plain", countCloseReader{&closes, strings.NewReader("hello")})
if err == nil {
res.Body.Close()
t.Fatal("expected an error to be returned from Post")
}
if closes != 1 {
t.Errorf("closes = %d; want 1", closes)
}
}