mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
http2: consider buffered data when doing stream flow control
Fixes golang/go#15930 Change-Id: Ib5d2f57361d52364edb29df25ec9a498c3088781 Reviewed-on: https://go-review.googlesource.com/23812 Reviewed-by: Aaron Jacobs <jacobsa@google.com> Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
@@ -1459,8 +1459,12 @@ func (b transportResponseBody) Read(p []byte) (n int, err error) {
|
||||
cc.inflow.add(connAdd)
|
||||
}
|
||||
if err == nil { // No need to refresh if the stream is over or failed.
|
||||
if v := cs.inflow.available(); v < transportDefaultStreamFlow-transportDefaultStreamMinRefresh {
|
||||
streamAdd = transportDefaultStreamFlow - v
|
||||
// Consider any buffered body data (read from the conn but not
|
||||
// consumed by the client) when computing flow control for this
|
||||
// stream.
|
||||
v := int(cs.inflow.available()) + cs.bufPipe.b.Len()
|
||||
if v < transportDefaultStreamFlow-transportDefaultStreamMinRefresh {
|
||||
streamAdd = int32(transportDefaultStreamFlow - v)
|
||||
cs.inflow.add(streamAdd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1956,3 +1956,58 @@ func TestTransportHandlerBodyClose(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// https://golang.org/issue/15930
|
||||
func TestTransportFlowControl(t *testing.T) {
|
||||
const (
|
||||
total = 100 << 20 // 100MB
|
||||
bufLen = 1 << 16
|
||||
)
|
||||
|
||||
var wrote int64 // updated atomically
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
b := make([]byte, bufLen)
|
||||
for wrote < total {
|
||||
n, err := w.Write(b)
|
||||
atomic.AddInt64(&wrote, int64(n))
|
||||
if err != nil {
|
||||
t.Errorf("ResponseWriter.Write error: %v", err)
|
||||
break
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
}, optOnlyServer)
|
||||
|
||||
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
|
||||
defer tr.CloseIdleConnections()
|
||||
req, err := http.NewRequest("GET", st.ts.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal("NewRequest error:", err)
|
||||
}
|
||||
resp, err := tr.RoundTrip(req)
|
||||
if err != nil {
|
||||
t.Fatal("RoundTrip error:", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var read int64
|
||||
b := make([]byte, bufLen)
|
||||
for {
|
||||
n, err := resp.Body.Read(b)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal("Read error:", err)
|
||||
}
|
||||
read += int64(n)
|
||||
|
||||
const max = transportDefaultStreamFlow
|
||||
if w := atomic.LoadInt64(&wrote); -max > read-w || read-w > max {
|
||||
t.Fatalf("Too much data inflight: server wrote %v bytes but client only received %v", w, read)
|
||||
}
|
||||
|
||||
// Let the server get ahead of the client.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user