Skip to content

Commit 39120d0

Browse files
ashishbhateodeke-em
authored andcommitted
http2: discard DATA frames with higher stream IDs during graceful shutdown
If the server sends a GOAWAY at the same time that a client sends HEADERS+DATA, the server will discard the HEADERS, but error on the DATA frame. Luckily, the server doesn't turn this into a connection error because it's already in a GOAWAY state. It just logs the PROTOCOL_ERROR, but that produces a confusing log message. This change effectively suppresses the log message by discarding the DATA frame rather than erroring on it. Also, we now discard any frames for streams with identifiers higher than the identified last stream. This is done as per section 6.8 of the HTTP2 spec. I also updated some stale documentation while I was trying to understand the logic. This is CL 188360 with a test Fixes golang/go#32112 Co-authored-by: Yunchi Luo <[email protected]> Co-authored-by: Michael Fraenkel <[email protected]> Change-Id: I612c2bd82e37551e813af0961b16a98d14e77c38 Reviewed-on: https://go-review.googlesource.com/c/net/+/237957 Run-TryBot: Agniva De Sarker <[email protected]> Run-TryBot: Emmanuel Odeke <[email protected]> Reviewed-by: Emmanuel Odeke <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Damien Neil <[email protected]>
1 parent 3d97a24 commit 39120d0

File tree

2 files changed

+90
-10
lines changed

2 files changed

+90
-10
lines changed

http2/server.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,9 @@ func (sc *serverConn) startGracefulShutdown() {
12931293
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
12941294
}
12951295

1296-
// After sending GOAWAY, the connection will close after goAwayTimeout.
1296+
// After sending GOAWAY with an error code (non-graceful shutdown), the
1297+
// connection will close after goAwayTimeout.
1298+
//
12971299
// If we close the connection immediately after sending GOAWAY, there may
12981300
// be unsent data in our kernel receive buffer, which will cause the kernel
12991301
// to send a TCP RST on close() instead of a FIN. This RST will abort the
@@ -1629,23 +1631,37 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
16291631

16301632
func (sc *serverConn) processData(f *DataFrame) error {
16311633
sc.serveG.check()
1632-
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
1634+
id := f.Header().StreamID
1635+
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > sc.maxClientStreamID) {
1636+
// Discard all DATA frames if the GOAWAY is due to an
1637+
// error, or:
1638+
//
1639+
// Section 6.8: After sending a GOAWAY frame, the sender
1640+
// can discard frames for streams initiated by the
1641+
// receiver with identifiers higher than the identified
1642+
// last stream.
16331643
return nil
16341644
}
1635-
data := f.Data()
16361645

1637-
// "If a DATA frame is received whose stream is not in "open"
1638-
// or "half closed (local)" state, the recipient MUST respond
1639-
// with a stream error (Section 5.4.2) of type STREAM_CLOSED."
1640-
id := f.Header().StreamID
1646+
data := f.Data()
16411647
state, st := sc.state(id)
16421648
if id == 0 || state == stateIdle {
1649+
// Section 6.1: "DATA frames MUST be associated with a
1650+
// stream. If a DATA frame is received whose stream
1651+
// identifier field is 0x0, the recipient MUST respond
1652+
// with a connection error (Section 5.4.1) of type
1653+
// PROTOCOL_ERROR."
1654+
//
16431655
// Section 5.1: "Receiving any frame other than HEADERS
16441656
// or PRIORITY on a stream in this state MUST be
16451657
// treated as a connection error (Section 5.4.1) of
16461658
// type PROTOCOL_ERROR."
16471659
return ConnectionError(ErrCodeProtocol)
16481660
}
1661+
1662+
// "If a DATA frame is received whose stream is not in "open"
1663+
// or "half closed (local)" state, the recipient MUST respond
1664+
// with a stream error (Section 5.4.2) of type STREAM_CLOSED."
16491665
if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued {
16501666
// This includes sending a RST_STREAM if the stream is
16511667
// in stateHalfClosedLocal (which currently means that

http2/server_test.go

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,37 @@ func stderrv() io.Writer {
4343
return ioutil.Discard
4444
}
4545

46+
type safeBuffer struct {
47+
b bytes.Buffer
48+
m sync.Mutex
49+
}
50+
51+
func (sb *safeBuffer) Write(d []byte) (int, error) {
52+
sb.m.Lock()
53+
defer sb.m.Unlock()
54+
return sb.b.Write(d)
55+
}
56+
57+
func (sb *safeBuffer) Bytes() []byte {
58+
sb.m.Lock()
59+
defer sb.m.Unlock()
60+
return sb.b.Bytes()
61+
}
62+
63+
func (sb *safeBuffer) Len() int {
64+
sb.m.Lock()
65+
defer sb.m.Unlock()
66+
return sb.b.Len()
67+
}
68+
4669
type serverTester struct {
4770
cc net.Conn // client conn
4871
t testing.TB
4972
ts *httptest.Server
5073
fr *Framer
51-
serverLogBuf bytes.Buffer // logger for httptest.Server
52-
logFilter []string // substrings to filter out
53-
scMu sync.Mutex // guards sc
74+
serverLogBuf safeBuffer // logger for httptest.Server
75+
logFilter []string // substrings to filter out
76+
scMu sync.Mutex // guards sc
5477
sc *serverConn
5578
hpackDec *hpack.Decoder
5679
decodedHeaders [][2]string
@@ -4267,3 +4290,44 @@ func TestServerWindowUpdateOnBodyClose(t *testing.T) {
42674290
t.Error(err)
42684291
}
42694292
}
4293+
4294+
func TestNoErrorLoggedOnPostAfterGOAWAY(t *testing.T) {
4295+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {})
4296+
defer st.Close()
4297+
4298+
st.greet()
4299+
4300+
content := "some content"
4301+
st.writeHeaders(HeadersFrameParam{
4302+
StreamID: 1,
4303+
BlockFragment: st.encodeHeader(
4304+
":method", "POST",
4305+
"content-length", strconv.Itoa(len(content)),
4306+
),
4307+
EndStream: false,
4308+
EndHeaders: true,
4309+
})
4310+
st.wantHeaders()
4311+
4312+
st.sc.startGracefulShutdown()
4313+
for {
4314+
f, err := st.readFrame()
4315+
if err == io.EOF {
4316+
st.t.Fatal("got a EOF; want *GoAwayFrame")
4317+
}
4318+
if err != nil {
4319+
t.Fatal(err)
4320+
}
4321+
if gf, ok := f.(*GoAwayFrame); ok && gf.StreamID == 0 {
4322+
break
4323+
}
4324+
}
4325+
4326+
st.writeData(1, true, []byte(content))
4327+
time.Sleep(200 * time.Millisecond)
4328+
st.Close()
4329+
4330+
if bytes.Contains(st.serverLogBuf.Bytes(), []byte("PROTOCOL_ERROR")) {
4331+
t.Error("got protocol error")
4332+
}
4333+
}

0 commit comments

Comments
 (0)