|
|
@@ -2097,3 +2097,83 @@ func testTransportUsesGoAwayDebugError(t *testing.T, failMidBody bool) {
|
|
|
}
|
|
|
ct.run()
|
|
|
}
|
|
|
+
|
|
|
+// See golang.org/issue/16481
|
|
|
+func TestTransportReturnsUnusedFlowControl(t *testing.T) {
|
|
|
+ ct := newClientTester(t)
|
|
|
+
|
|
|
+ clientClosed := make(chan bool, 1)
|
|
|
+ serverWroteBody := make(chan bool, 1)
|
|
|
+
|
|
|
+ ct.client = func() error {
|
|
|
+ req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
|
|
|
+ res, err := ct.tr.RoundTrip(req)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ <-serverWroteBody
|
|
|
+
|
|
|
+ if n, err := res.Body.Read(make([]byte, 1)); err != nil || n != 1 {
|
|
|
+ return fmt.Errorf("body read = %v, %v; want 1, nil", n, err)
|
|
|
+ }
|
|
|
+ res.Body.Close() // leaving 4999 bytes unread
|
|
|
+ clientClosed <- true
|
|
|
+
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ ct.server = func() error {
|
|
|
+ ct.greet()
|
|
|
+
|
|
|
+ var hf *HeadersFrame
|
|
|
+ for {
|
|
|
+ f, err := ct.fr.ReadFrame()
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("ReadFrame while waiting for Headers: %v", err)
|
|
|
+ }
|
|
|
+ switch f.(type) {
|
|
|
+ case *WindowUpdateFrame, *SettingsFrame:
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ var ok bool
|
|
|
+ hf, ok = f.(*HeadersFrame)
|
|
|
+ if !ok {
|
|
|
+ return fmt.Errorf("Got %T; want HeadersFrame", f)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ var buf bytes.Buffer
|
|
|
+ enc := hpack.NewEncoder(&buf)
|
|
|
+ enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
|
|
|
+ enc.WriteField(hpack.HeaderField{Name: "content-length", Value: "5000"})
|
|
|
+ ct.fr.WriteHeaders(HeadersFrameParam{
|
|
|
+ StreamID: hf.StreamID,
|
|
|
+ EndHeaders: true,
|
|
|
+ EndStream: false,
|
|
|
+ BlockFragment: buf.Bytes(),
|
|
|
+ })
|
|
|
+ ct.fr.WriteData(hf.StreamID, false, make([]byte, 5000)) // without ending stream
|
|
|
+ serverWroteBody <- true
|
|
|
+
|
|
|
+ <-clientClosed
|
|
|
+
|
|
|
+ f, err := ct.fr.ReadFrame()
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("ReadFrame while waiting for RSTStreamFrame: %v", err)
|
|
|
+ }
|
|
|
+ if rf, ok := f.(*RSTStreamFrame); !ok || rf.ErrCode != ErrCodeCancel {
|
|
|
+ return fmt.Errorf("Expected a WindowUpdateFrame with code cancel; got %v", summarizeFrame(f))
|
|
|
+ }
|
|
|
+
|
|
|
+ // And wait for our flow control tokens back:
|
|
|
+ f, err = ct.fr.ReadFrame()
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("ReadFrame while waiting for WindowUpdateFrame: %v", err)
|
|
|
+ }
|
|
|
+ if wuf, ok := f.(*WindowUpdateFrame); !ok || wuf.Increment != 4999 {
|
|
|
+ return fmt.Errorf("Expected WindowUpdateFrame for 4999 bytes; got %v", summarizeFrame(f))
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ ct.run()
|
|
|
+}
|