Skip to content

Commit d8ae719

Browse files
mhr3mknyszek
authored andcommitted
[internal-branch.go1.16-vendor] net/http2: Fix handling of expect continue
Recent refactoring in the http2 package broke handling of ExpectContinueTimeout, where the client was always waiting for the timeout to pass before sending the body. For golang/go#49677 Fixes golang/go#49904 Change-Id: I565299e48313bb905b2655bd52084ea49ab742f3 GitHub-Last-Rev: 587b484 GitHub-Pull-Request: #118 Reviewed-on: https://go-review.googlesource.com/c/net/+/363914 Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Trust: Damien Neil <[email protected]> Run-TryBot: Damien Neil <[email protected]> (cherry picked from commit 0a0e4e1) Reviewed-on: https://go-review.googlesource.com/c/net/+/368474 TryBot-Result: Go Bot <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent cc2f99c commit d8ae719

File tree

2 files changed

+96
-6
lines changed

2 files changed

+96
-6
lines changed

http2/transport.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,12 +1165,12 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
11651165
}
11661166

11671167
continueTimeout := cc.t.expectContinueTimeout()
1168-
if continueTimeout != 0 &&
1169-
!httpguts.HeaderValuesContainsToken(
1170-
req.Header["Expect"],
1171-
"100-continue") {
1172-
continueTimeout = 0
1173-
cs.on100 = make(chan struct{}, 1)
1168+
if continueTimeout != 0 {
1169+
if !httpguts.HeaderValuesContainsToken(req.Header["Expect"], "100-continue") {
1170+
continueTimeout = 0
1171+
} else {
1172+
cs.on100 = make(chan struct{}, 1)
1173+
}
11741174
}
11751175

11761176
// Past this point (where we send request headers), it is possible for

http2/transport_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5101,6 +5101,96 @@ func TestTransportServerResetStreamAtHeaders(t *testing.T) {
51015101
res.Body.Close()
51025102
}
51035103

5104+
type trackingReader struct {
5105+
rdr io.Reader
5106+
wasRead uint32
5107+
}
5108+
5109+
func (tr *trackingReader) Read(p []byte) (int, error) {
5110+
atomic.StoreUint32(&tr.wasRead, 1)
5111+
return tr.rdr.Read(p)
5112+
}
5113+
5114+
func (tr *trackingReader) WasRead() bool {
5115+
return atomic.LoadUint32(&tr.wasRead) != 0
5116+
}
5117+
5118+
func TestTransportExpectContinue(t *testing.T) {
5119+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
5120+
switch r.URL.Path {
5121+
case "/reject":
5122+
w.WriteHeader(403)
5123+
default:
5124+
io.Copy(io.Discard, r.Body)
5125+
}
5126+
}, optOnlyServer)
5127+
defer st.Close()
5128+
5129+
tr := &http.Transport{
5130+
TLSClientConfig: tlsConfigInsecure,
5131+
MaxConnsPerHost: 1,
5132+
ExpectContinueTimeout: 10 * time.Second,
5133+
}
5134+
5135+
err := ConfigureTransport(tr)
5136+
if err != nil {
5137+
t.Fatal(err)
5138+
}
5139+
client := &http.Client{
5140+
Transport: tr,
5141+
}
5142+
5143+
testCases := []struct {
5144+
Name string
5145+
Path string
5146+
Body *trackingReader
5147+
ExpectedCode int
5148+
ShouldRead bool
5149+
}{
5150+
{
5151+
Name: "read-all",
5152+
Path: "/",
5153+
Body: &trackingReader{rdr: strings.NewReader("hello")},
5154+
ExpectedCode: 200,
5155+
ShouldRead: true,
5156+
},
5157+
{
5158+
Name: "reject",
5159+
Path: "/reject",
5160+
Body: &trackingReader{rdr: strings.NewReader("hello")},
5161+
ExpectedCode: 403,
5162+
ShouldRead: false,
5163+
},
5164+
}
5165+
5166+
for _, tc := range testCases {
5167+
t.Run(tc.Name, func(t *testing.T) {
5168+
startTime := time.Now()
5169+
5170+
req, err := http.NewRequest("POST", st.ts.URL+tc.Path, tc.Body)
5171+
if err != nil {
5172+
t.Fatal(err)
5173+
}
5174+
req.Header.Set("Expect", "100-continue")
5175+
res, err := client.Do(req)
5176+
if err != nil {
5177+
t.Fatal(err)
5178+
}
5179+
res.Body.Close()
5180+
5181+
if delta := time.Since(startTime); delta >= tr.ExpectContinueTimeout {
5182+
t.Error("Request didn't finish before expect continue timeout")
5183+
}
5184+
if res.StatusCode != tc.ExpectedCode {
5185+
t.Errorf("Unexpected status code, got %d, expected %d", res.StatusCode, tc.ExpectedCode)
5186+
}
5187+
if tc.Body.WasRead() != tc.ShouldRead {
5188+
t.Errorf("Unexpected read status, got %v, expected %v", tc.Body.WasRead(), tc.ShouldRead)
5189+
}
5190+
})
5191+
}
5192+
}
5193+
51045194
type closeChecker struct {
51055195
io.ReadCloser
51065196
closed chan struct{}

0 commit comments

Comments
 (0)