From b2b15d1230f3b4f669b5dd171c31e1b081bffede Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 19 Jul 2013 12:02:54 +1000 Subject: [PATCH] net/http: document and test Client.Post treating Reader body as ReadCloser R=golang-dev, r CC=golang-dev https://golang.org/cl/11542044 --- src/pkg/net/http/client.go | 7 ++++-- src/pkg/net/http/transport_test.go | 37 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/pkg/net/http/client.go b/src/pkg/net/http/client.go index 828422f84c3..331e8ad90e5 100644 --- a/src/pkg/net/http/client.go +++ b/src/pkg/net/http/client.go @@ -74,8 +74,8 @@ type RoundTripper interface { // authentication, or cookies. // // RoundTrip should not modify the request, except for - // consuming the Body. The request's URL and Header fields - // are guaranteed to be initialized. + // consuming and closing the Body. The request's URL and + // Header fields are guaranteed to be initialized. RoundTrip(*Request) (*Response, error) } @@ -346,6 +346,9 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro // Post issues a POST to the specified URL. // // Caller should close resp.Body when done reading from it. +// +// If the provided body is also an io.Closer, it is closed after the +// body is successfully written to the server. func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) { req, err := NewRequest("POST", url, body) if err != nil { diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go index a34760a0899..48a8c441f72 100644 --- a/src/pkg/net/http/transport_test.go +++ b/src/pkg/net/http/transport_test.go @@ -15,6 +15,7 @@ import ( "io" "io/ioutil" "net" + "net/http" . "net/http" "net/http/httptest" "net/url" @@ -1610,6 +1611,42 @@ func TestIdleConnChannelLeak(t *testing.T) { } } +// Verify the status quo: that the Client.Post function coerces its +// body into a ReadCloser if it's a Closer, and that the Transport +// then closes it. +func TestTransportClosesRequestBody(t *testing.T) { + defer afterTest(t) + ts := httptest.NewServer(http.HandlerFunc(func(w ResponseWriter, r *Request) { + io.Copy(ioutil.Discard, r.Body) + })) + defer ts.Close() + + tr := &Transport{} + defer tr.CloseIdleConnections() + cl := &Client{Transport: tr} + + closes := 0 + + res, err := cl.Post(ts.URL, "text/plain", countCloseReader{&closes, strings.NewReader("hello")}) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + if closes != 1 { + t.Errorf("closes = %d; want 1", closes) + } +} + +type countCloseReader struct { + n *int + io.Reader +} + +func (cr countCloseReader) Close() error { + (*cr.n)++ + return nil +} + // rgz is a gzip quine that uncompresses to itself. var rgz = []byte{ 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,