Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,10 @@ private[http2] class Http2FrameParsing(
val length = reader.readShortBE() << 8 | reader.readByte()
val tpe = reader.readByte()
val flags = new ByteFlag(reader.readByte())
val streamId = reader.readIntBE()
// TODO: assert that reserved bit is 0 by checking if streamId > 0
// RFC 9113 5.1.1: the high bit of the stream identifier is reserved and MUST be ignored when receiving.
// Without masking it a peer that sets it yields a negative stream id, which never matches the stream the
// frame is really for and fails the connection instead.
val streamId = reader.readIntBE() & 0x7FFFFFFF
val payload = reader.take(length)
val maybeframe = FrameType.byId(tpe) match {
case OptionVal.Some(ft) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,14 @@ class Http2ServerSpec extends Http2SpecWithMaterializer("""
trailingResponseHeaders.size should be(1)
trailingResponseHeaders.head should be(("Status", "grpc-status 10"))
})
"ignore the reserved bit of a stream identifier".inAssertAllStagesStopped(
new TestSetup with RequestResponseProbes {
// RFC 9113 5.1.1: the high bit of the stream identifier is reserved and must be ignored when receiving,
// so this is a frame for stream 1 rather than one for an unusable negative stream id
network.sendHEADERS(1 | 0x80000000, endStream = true, network.headersForRequest(Get("/")))

user.expectRequest()
})
"drop a response header whose value contains CRLF".inAssertAllStagesStopped(
new TestSetup with RequestResponseProbes {
val streamId = 1
Expand Down