diff --git a/http2/server.go b/http2/server.go index 793899169..e111019d5 100644 --- a/http2/server.go +++ b/http2/server.go @@ -1721,6 +1721,13 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { // processing this frame. return nil } + // RFC 7540, sec 5.1: If an endpoint receives additional frames, other than + // WINDOW_UPDATE, PRIORITY, or RST_STREAM, for a stream that is in + // this state, it MUST respond with a stream error (Section 5.4.2) of + // type STREAM_CLOSED. + if st.state == stateHalfClosedRemote { + return streamError(id, ErrCodeStreamClosed) + } return st.processTrailerHeaders(f) } diff --git a/http2/server_test.go b/http2/server_test.go index 9c3e549dc..157388973 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -3841,3 +3841,51 @@ func TestServerHandlerConnectionClose(t *testing.T) { } }) } + +func TestServer_Headers_HalfCloseRemote(t *testing.T) { + var st *serverTester + inHandler := make(chan bool) + writeHeaders := make(chan bool) + leaveHandler := make(chan bool) + st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) { + inHandler <- true + if st.stream(1) == nil { + t.Errorf("nil stream 1 in handler") + } + if got, want := st.streamState(1), stateOpen; got != want { + t.Errorf("in handler, state is %v; want %v", got, want) + } + if n, err := r.Body.Read(make([]byte, 1)); n != 0 || err != io.EOF { + t.Errorf("body read = %d, %v; want 0, EOF", n, err) + } + if got, want := st.streamState(1), stateHalfClosedRemote; got != want { + t.Errorf("in handler, state is %v; want %v", got, want) + } + writeHeaders <- true + + <-leaveHandler + }) + st.greet() + + st.writeHeaders(HeadersFrameParam{ + StreamID: 1, + BlockFragment: st.encodeHeader(), + EndStream: false, // keep it open + EndHeaders: true, + }) + <-inHandler + st.writeData(1, true, nil) + + <-writeHeaders + + st.writeHeaders(HeadersFrameParam{ + StreamID: 1, + BlockFragment: st.encodeHeader(), + EndStream: false, // keep it open + EndHeaders: true, + }) + + defer close(leaveHandler) + + st.wantRSTStream(1, ErrCodeStreamClosed) +}