From 5d4f7005572804eaf7f5ecdd2473a62557f733ba Mon Sep 17 00:00:00 2001 From: Michael Fraenkel Date: Mon, 27 Jul 2020 23:32:47 -0600 Subject: [PATCH] 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 Reviewed-by: Ian Lance Taylor Run-TryBot: Emmanuel Odeke TryBot-Result: Go Bot Trust: Emmanuel Odeke --- http2/server.go | 1 + http2/server_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/http2/server.go b/http2/server.go index 02714ade6..2aa859f76 100644 --- a/http2/server.go +++ b/http2/server.go @@ -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) { diff --git a/http2/server_test.go b/http2/server_test.go index fa473701c..0a507294a 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -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) + } +}