Skip to content

Commit c630100

Browse files
neildgopherbot
authored andcommitted
http2: discard more frames after GOAWAY
After sending a GOAWAY with NO_ERROR, we should discard all frames for streams with larger identifiers than the last stream identifier in the GOAWAY frame. We weren't discarding RST_STREAM frames, which could cause us to incorrectly detect a protocol error when handling a RST_STREAM for a discarded stream. Hoist post-GOAWAY frame discarding higher in the loop rather than handling it on a per-frame-type basis. We are also supposed to count discarded DATA frames against connection-level flow control, possibly sending WINDOW_UPDATE messages to return the flow control. We weren't doing this; this is now fixed. Fixes golang/go#55846 Change-Id: I7603a529c00b8637e648eee9cc4608fb5fa5199b Reviewed-on: https://go-review.googlesource.com/c/net/+/434909 Reviewed-by: Heschi Kreinick <[email protected]> Run-TryBot: Damien Neil <[email protected]> Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: LI ZHEN <[email protected]> Reviewed-by: Antonio Ojea <[email protected]>
1 parent 0c1aede commit c630100

File tree

2 files changed

+52
-22
lines changed

2 files changed

+52
-22
lines changed

http2/server.go

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,22 @@ func (sc *serverConn) processFrame(f Frame) error {
14591459
sc.sawFirstSettings = true
14601460
}
14611461

1462+
// Discard frames for streams initiated after the identified last
1463+
// stream sent in a GOAWAY, or all frames after sending an error.
1464+
// We still need to return connection-level flow control for DATA frames.
1465+
// RFC 9113 Section 6.8.
1466+
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || f.Header().StreamID > sc.maxClientStreamID) {
1467+
1468+
if f, ok := f.(*DataFrame); ok {
1469+
if sc.inflow.available() < int32(f.Length) {
1470+
return sc.countError("data_flow", streamError(f.Header().StreamID, ErrCodeFlowControl))
1471+
}
1472+
sc.inflow.take(int32(f.Length))
1473+
sc.sendWindowUpdate(nil) // conn-level
1474+
}
1475+
return nil
1476+
}
1477+
14621478
switch f := f.(type) {
14631479
case *SettingsFrame:
14641480
return sc.processSettings(f)
@@ -1501,9 +1517,6 @@ func (sc *serverConn) processPing(f *PingFrame) error {
15011517
// PROTOCOL_ERROR."
15021518
return sc.countError("ping_on_stream", ConnectionError(ErrCodeProtocol))
15031519
}
1504-
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
1505-
return nil
1506-
}
15071520
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
15081521
return nil
15091522
}
@@ -1686,16 +1699,6 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
16861699
func (sc *serverConn) processData(f *DataFrame) error {
16871700
sc.serveG.check()
16881701
id := f.Header().StreamID
1689-
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > sc.maxClientStreamID) {
1690-
// Discard all DATA frames if the GOAWAY is due to an
1691-
// error, or:
1692-
//
1693-
// Section 6.8: After sending a GOAWAY frame, the sender
1694-
// can discard frames for streams initiated by the
1695-
// receiver with identifiers higher than the identified
1696-
// last stream.
1697-
return nil
1698-
}
16991702

17001703
data := f.Data()
17011704
state, st := sc.state(id)
@@ -1847,10 +1850,6 @@ func (st *stream) onWriteTimeout() {
18471850
func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
18481851
sc.serveG.check()
18491852
id := f.StreamID
1850-
if sc.inGoAway {
1851-
// Ignore.
1852-
return nil
1853-
}
18541853
// http://tools.ietf.org/html/rfc7540#section-5.1.1
18551854
// Streams initiated by a client MUST use odd-numbered stream
18561855
// identifiers. [...] An endpoint that receives an unexpected
@@ -2021,9 +2020,6 @@ func (sc *serverConn) checkPriority(streamID uint32, p PriorityParam) error {
20212020
}
20222021

20232022
func (sc *serverConn) processPriority(f *PriorityFrame) error {
2024-
if sc.inGoAway {
2025-
return nil
2026-
}
20272023
if err := sc.checkPriority(f.StreamID, f.PriorityParam); err != nil {
20282024
return err
20292025
}

http2/server_test.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3994,14 +3994,14 @@ func TestServer_Rejects_TooSmall(t *testing.T) {
39943994
// and the connection still completing.
39953995
func TestServerHandlerConnectionClose(t *testing.T) {
39963996
unblockHandler := make(chan bool, 1)
3997-
defer close(unblockHandler) // backup; in case of errors
39983997
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
39993998
w.Header().Set("Connection", "close")
40003999
w.Header().Set("Foo", "bar")
40014000
w.(http.Flusher).Flush()
40024001
<-unblockHandler
40034002
return nil
40044003
}, func(st *serverTester) {
4004+
defer close(unblockHandler) // backup; in case of errors
40054005
st.writeHeaders(HeadersFrameParam{
40064006
StreamID: 1,
40074007
BlockFragment: st.encodeHeader(),
@@ -4010,6 +4010,7 @@ func TestServerHandlerConnectionClose(t *testing.T) {
40104010
})
40114011
var sawGoAway bool
40124012
var sawRes bool
4013+
var sawWindowUpdate bool
40134014
for {
40144015
f, err := st.readFrame()
40154016
if err == io.EOF {
@@ -4021,10 +4022,29 @@ func TestServerHandlerConnectionClose(t *testing.T) {
40214022
switch f := f.(type) {
40224023
case *GoAwayFrame:
40234024
sawGoAway = true
4024-
unblockHandler <- true
40254025
if f.LastStreamID != 1 || f.ErrCode != ErrCodeNo {
40264026
t.Errorf("unexpected GOAWAY frame: %v", summarizeFrame(f))
40274027
}
4028+
// Create a stream and reset it.
4029+
// The server should ignore the stream.
4030+
st.writeHeaders(HeadersFrameParam{
4031+
StreamID: 3,
4032+
BlockFragment: st.encodeHeader(),
4033+
EndStream: false,
4034+
EndHeaders: true,
4035+
})
4036+
st.fr.WriteRSTStream(3, ErrCodeCancel)
4037+
// Create a stream and send data to it.
4038+
// The server should return flow control, even though it
4039+
// does not process the stream.
4040+
st.writeHeaders(HeadersFrameParam{
4041+
StreamID: 5,
4042+
BlockFragment: st.encodeHeader(),
4043+
EndStream: false,
4044+
EndHeaders: true,
4045+
})
4046+
// Write enough data to trigger a window update.
4047+
st.writeData(5, true, make([]byte, 1<<19))
40284048
case *HeadersFrame:
40294049
goth := st.decodeHeader(f.HeaderBlockFragment())
40304050
wanth := [][2]string{
@@ -4039,6 +4059,17 @@ func TestServerHandlerConnectionClose(t *testing.T) {
40394059
if f.StreamID != 1 || !f.StreamEnded() || len(f.Data()) != 0 {
40404060
t.Errorf("unexpected DATA frame: %v", summarizeFrame(f))
40414061
}
4062+
case *WindowUpdateFrame:
4063+
if !sawGoAway {
4064+
t.Errorf("unexpected WINDOW_UPDATE frame: %v", summarizeFrame(f))
4065+
return
4066+
}
4067+
if f.StreamID != 0 {
4068+
st.t.Fatalf("WindowUpdate StreamID = %d; want 5", f.FrameHeader.StreamID)
4069+
return
4070+
}
4071+
sawWindowUpdate = true
4072+
unblockHandler <- true
40424073
default:
40434074
t.Logf("unexpected frame: %v", summarizeFrame(f))
40444075
}
@@ -4049,6 +4080,9 @@ func TestServerHandlerConnectionClose(t *testing.T) {
40494080
if !sawRes {
40504081
t.Errorf("didn't see response")
40514082
}
4083+
if !sawWindowUpdate {
4084+
t.Errorf("didn't see WINDOW_UPDATE")
4085+
}
40524086
})
40534087
}
40544088

0 commit comments

Comments
 (0)