net/http2: send WINDOW_UPDATE on a body's write failure

When the body.Write fails during processData, the connection flow
control must be updated to account for the data received. The connection's
WINDOW_UPDATE should reflect the amount of data that was not successfully
written. The stream is about to be closed, so no update is required.

Fixes golang/go#40423

Change-Id: I546597cedf3715e6617babcb3b62140bf1857a27
Reviewed-on: https://go-review.googlesource.com/c/net/+/245158
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Emmanuel Odeke <emm.odeke@gmail.com>
This commit is contained in:
Michael Fraenkel
2020-07-27 23:32:47 -06:00
committed by Emmanuel Odeke
parent 05aa5d4ee3
commit 5d4f700557
2 changed files with 60 additions and 0 deletions

View File

@@ -1694,6 +1694,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
if len(data) > 0 {
wrote, err := st.body.Write(data)
if err != nil {
sc.sendWindowUpdate(nil, int(f.Length)-wrote)
return streamError(id, ErrCodeStreamClosed)
}
if wrote != len(data) {

View File

@@ -4209,3 +4209,62 @@ func TestContentEncodingNoSniffing(t *testing.T) {
})
}
}
func TestServerWindowUpdateOnBodyClose(t *testing.T) {
const content = "12345678"
blockCh := make(chan bool)
errc := make(chan error, 1)
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
buf := make([]byte, 4)
n, err := io.ReadFull(r.Body, buf)
if err != nil {
errc <- err
return
}
if n != len(buf) {
errc <- fmt.Errorf("too few bytes read: %d", n)
return
}
blockCh <- true
<-blockCh
errc <- nil
})
defer st.Close()
st.greet()
st.writeHeaders(HeadersFrameParam{
StreamID: 1, // clients send odd numbers
BlockFragment: st.encodeHeader(
":method", "POST",
"content-length", strconv.Itoa(len(content)),
),
EndStream: false, // to say DATA frames are coming
EndHeaders: true,
})
st.writeData(1, false, []byte(content[:5]))
<-blockCh
st.stream(1).body.CloseWithError(io.EOF)
st.writeData(1, false, []byte(content[5:]))
blockCh <- true
increments := len(content)
for {
f, err := st.readFrame()
if err == io.EOF {
break
}
if err != nil {
t.Fatal(err)
}
if wu, ok := f.(*WindowUpdateFrame); ok && wu.StreamID == 0 {
increments -= int(wu.Increment)
if increments == 0 {
break
}
}
}
if err := <-errc; err != nil {
t.Error(err)
}
}