Skip to content
Closed
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
14 changes: 9 additions & 5 deletions src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,15 @@ private void OnHeaderCore(HeaderType headerType, int? staticTableIndex, ReadOnly

try
{
// https://www.rfc-editor.org/rfc/rfc9113#section-8.2.2
// Connection-specific header fields make a message malformed regardless of how the header name was
// encoded. HPACK's indexed-name representation (e.g. the "transfer-encoding" entry at static table
// index 57) would otherwise let these fields bypass the check below, so validate every header type.
if (IsConnectionSpecificHeaderField(name, value))
{
throw new Http2ConnectionErrorException(CoreStrings.HttpErrorConnectionSpecificHeaderField, Http2ErrorCode.PROTOCOL_ERROR, ConnectionEndReason.InvalidRequestHeaders);
}

if (_requestHeaderParsingState == RequestHeaderParsingState.Trailers)
{
// Just use name + value bytes and do full validation for request trailers.
Expand Down Expand Up @@ -1694,11 +1703,6 @@ public void OnHeadersComplete(bool endStream)

private void ValidateHeaderContent(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
{
if (IsConnectionSpecificHeaderField(name, value))
{
throw new Http2ConnectionErrorException(CoreStrings.HttpErrorConnectionSpecificHeaderField, Http2ErrorCode.PROTOCOL_ERROR, ConnectionEndReason.InvalidRequestHeaders);
}

// http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2
// A request or response containing uppercase header field names MUST be treated as malformed (Section 8.1.2.6).
for (var i = 0; i < name.Length; i++)
Expand Down
14 changes: 9 additions & 5 deletions src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,15 @@ private void OnHeaderCore(HeaderType headerType, int? staticTableIndex, ReadOnly

try
{
// https://www.rfc-editor.org/rfc/rfc9114#section-4.2
// Connection-specific header fields make a message malformed regardless of how the header name was
// encoded. A QPACK indexed-name representation (referencing a dynamic table entry inserted via the
// encoder stream) would otherwise let these fields bypass the check below, so validate every header type.
if (IsConnectionSpecificHeaderField(name, value))
{
throw new Http3StreamErrorException(CoreStrings.HttpErrorConnectionSpecificHeaderField, Http3ErrorCode.MessageError);
}

if (_requestHeaderParsingState == RequestHeaderParsingState.Trailers)
{
// Just use name + value bytes and do full validation for request trailers.
Expand Down Expand Up @@ -378,11 +387,6 @@ private void OnHeaderCore(HeaderType headerType, int? staticTableIndex, ReadOnly

private void ValidateHeaderContent(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
{
if (IsConnectionSpecificHeaderField(name, value))
{
throw new Http3StreamErrorException(CoreStrings.HttpErrorConnectionSpecificHeaderField, Http3ErrorCode.MessageError);
}

// http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2
// A request or response containing uppercase header field names MUST be treated as malformed (Section 8.1.2.6).
for (var i = 0; i < name.Length; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3159,6 +3159,35 @@ public Task HEADERS_Received_HeaderBlockContainsConnectionSpecificHeader_Connect
expectedEndReason: ConnectionEndReason.InvalidRequestHeaders);
}

[Fact]
public async Task HEADERS_Received_TransferEncodingWithHPackIndexedName_ConnectionError()
{
await InitializeConnectionAsync(_noopApplication);

// Reference the "transfer-encoding" name via HPACK's indexed-name representation (static table index 57)
// with a literal "chunked" value. This exercises the OnStaticIndexedHeader(index, value) path, which must
// still reject connection-specific header fields even though the name was not sent as a literal string.
var headerBlock = new byte[]
{
0x82, // :method: GET (indexed, static index 2)
0x84, // :path: / (indexed, static index 4)
0x86, // :scheme: http (indexed, static index 6)
0x0f, 0x2a, // Literal Header Field without Indexing - Indexed Name (static index 57: transfer-encoding)
0x07, // value length: 7
(byte)'c', (byte)'h', (byte)'u', (byte)'n', (byte)'k', (byte)'e', (byte)'d',
};

await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headerBlock);

await WaitForConnectionErrorAsync<Http2ConnectionErrorException>(
ignoreNonGoAwayFrames: false,
expectedLastStreamId: 1,
expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
expectedErrorMessage: CoreStrings.HttpErrorConnectionSpecificHeaderField);

AssertConnectionEndReason(ConnectionEndReason.InvalidRequestHeaders);
}

[Fact]
public async Task HEADERS_Received_HeaderBlockContainsTEHeader_ValueIsTrailers_NoError()
{
Expand Down