diff --git a/src/codec/framed_read.rs b/src/codec/framed_read.rs index e65b2dad9..be5d5c75b 100644 --- a/src/codec/framed_read.rs +++ b/src/codec/framed_read.rs @@ -253,7 +253,7 @@ fn decode_frame( .into() } Kind::GoAway => { - let res = frame::GoAway::load(&bytes[frame::HEADER_LEN..]); + let res = frame::GoAway::load(head, &bytes[frame::HEADER_LEN..]); res.map_err(|e| { proto_err!(conn: "failed to load GO_AWAY frame; err={:?}", e); Error::library_go_away(Reason::PROTOCOL_ERROR) diff --git a/src/frame/go_away.rs b/src/frame/go_away.rs index 99330e981..8f234645c 100644 --- a/src/frame/go_away.rs +++ b/src/frame/go_away.rs @@ -40,7 +40,17 @@ impl GoAway { &self.debug_data } - pub fn load(payload: &[u8]) -> Result { + pub fn load(head: Head, payload: &[u8]) -> Result { + debug_assert_eq!(head.kind(), crate::frame::Kind::GoAway); + + // The GOAWAY frame applies to the connection, not a specific stream. + // An endpoint MUST treat a GOAWAY frame with a stream identifier + // other than 0x00 as a connection error (Section 5.4.1) of type + // PROTOCOL_ERROR. + if !head.stream_id().is_zero() { + return Err(Error::InvalidStreamId); + } + if payload.len() < 8 { return Err(Error::BadFrameSize); } diff --git a/src/frame/mod.rs b/src/frame/mod.rs index ab8c43c1b..4a0c03036 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -153,8 +153,8 @@ pub enum Error { /// An invalid stream identifier was provided. /// - /// This is returned if a SETTINGS or PING frame is received with a stream - /// identifier other than zero. + /// This is returned if a SETTINGS, PING or GOAWAY frame is received with a + /// stream identifier other than zero. InvalidStreamId, /// A request or response is malformed. diff --git a/tests/h2-tests/tests/codec_read.rs b/tests/h2-tests/tests/codec_read.rs index 489d16daf..98ab122ba 100644 --- a/tests/h2-tests/tests/codec_read.rs +++ b/tests/h2-tests/tests/codec_read.rs @@ -239,3 +239,19 @@ async fn read_goaway_with_debug_data() { assert_closed!(codec); } + +#[tokio::test] +async fn read_goaway_stream_id_not_zero() { + let mut codec = raw_codec! { + read => [ + // head, stream id 1 + 0, 0, 8, 7, 0, 0, 0, 0, 1, + // last_stream_id + 0, 0, 0, 0, + // error_code + 0, 0, 0, 0, + ]; + }; + + poll_err!(codec); +}