git » go-net » commit 749a502

http2: don't sniff first Request.Body byte in Transport until we have a conn

author Brad Fitzpatrick
2016-09-12 14:44:02 UTC
committer Brad Fitzpatrick
2016-09-12 18:43:37 UTC
parent 57c782092076593634268e699ced8fd6360dbb25

http2: don't sniff first Request.Body byte in Transport until we have a conn

bodyAndLength mutates Request.Body if Request.ContentLength == 0,
reading the first byte to determine whether it's actually empty or
just undeclared. But we did that before we checked whether our
connection was overloaded, which meant the caller could retry the
request on an new or lesser-loaded connection, but then lose the first
byte of the request.

Updates golang/go#17071 (needs bundle into std before fixed)

Change-Id: I996a92ad037b45bc49e7cf0801a2027bbbb3c920
Reviewed-on: https://go-review.googlesource.com/29074
Reviewed-by: Gleb Stepanov <glebstepanov1992@gmail.com>
Reviewed-by: Chris Broadfoot <cbro@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

http2/transport.go +3 -3
http2/transport_test.go +21 -0

diff --git a/http2/transport.go b/http2/transport.go
index 7a8563d..42c73bd 100644
--- a/http2/transport.go
+++ b/http2/transport.go
@@ -656,9 +656,6 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
 	}
 	hasTrailers := trailers != ""
 
-	body, contentLen := bodyAndLength(req)
-	hasBody := body != nil
-
 	cc.mu.Lock()
 	cc.lastActive = time.Now()
 	if cc.closed || !cc.canTakeNewRequestLocked() {
@@ -666,6 +663,9 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
 		return nil, errClientConnUnusable
 	}
 
+	body, contentLen := bodyAndLength(req)
+	hasBody := body != nil
+
 	// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
 	var requestedGzip bool
 	if !cc.t.disableCompression() &&
diff --git a/http2/transport_test.go b/http2/transport_test.go
index e220c91..96d0a08 100644
--- a/http2/transport_test.go
+++ b/http2/transport_test.go
@@ -2597,3 +2597,24 @@ func TestTransportRequestPathPseudo(t *testing.T) {
 	}
 
 }
+
+// golang.org/issue/17071 -- don't sniff the first byte of the request body
+// before we've determined that the ClientConn is usable.
+func TestRoundTripDoesntConsumeRequestBodyEarly(t *testing.T) {
+	const body = "foo"
+	req, _ := http.NewRequest("POST", "http://foo.com/", ioutil.NopCloser(strings.NewReader(body)))
+	cc := &ClientConn{
+		closed: true,
+	}
+	_, err := cc.RoundTrip(req)
+	if err != errClientConnUnusable {
+		t.Fatalf("RoundTrip = %v; want errClientConnUnusable", err)
+	}
+	slurp, err := ioutil.ReadAll(req.Body)
+	if err != nil {
+		t.Errorf("ReadAll = %v", err)
+	}
+	if string(slurp) != body {
+		t.Errorf("Body = %q; want %q", slurp, body)
+	}
+}