Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Merged PR 21363: [MSRC 69437] Apply MaxResponseHeadersLength limit fo…
Browse files Browse the repository at this point in the history
…r trailing headers
  • Loading branch information
Miha Zupan authored and mmitche committed Apr 13, 2022
1 parent e486c0d commit f9c6475
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ private sealed class ChunkedEncodingReadStream : HttpContentReadStream
/// infinite chunk length is sent. This value is arbitrary and can be changed as needed.
/// </remarks>
private const int MaxChunkBytesAllowed = 16*1024;
/// <summary>How long a trailing header can be. This value is arbitrary and can be changed as needed.</summary>
private const int MaxTrailingHeaderLength = 16*1024;
/// <summary>The number of bytes remaining in the chunk.</summary>
private ulong _chunkBytesRemaining;
/// <summary>The current state of the parsing state machine for the chunked response.</summary>
Expand Down Expand Up @@ -298,6 +296,9 @@ private ReadOnlyMemory<byte> ReadChunkFromConnectionBuffer(int maxBytesToRead, C
else
{
_state = ParsingState.ConsumeTrailers;
// Apply the MaxResponseHeadersLength limit to all trailing headers.
// The limit is applied to regular response headers and trailing headers separately.
_connection._allowedReadLineBytes = _connection.MaxResponseHeadersLength;
goto case ParsingState.ConsumeTrailers;
}

Expand Down Expand Up @@ -345,7 +346,6 @@ private ReadOnlyMemory<byte> ReadChunkFromConnectionBuffer(int maxBytesToRead, C
while (true)
{
// TODO: Consider adding folded trailing header support #35769.
_connection._allowedReadLineBytes = MaxTrailingHeaderLength;
if (!_connection.TryReadNextLine(out currentLine))
{
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ public bool EnsureReadAheadAndPollRead()

public HttpConnectionKind Kind => _pool.Kind;

private int MaxResponseHeadersLength => (int)Math.Min(int.MaxValue, _pool.Settings._maxResponseHeadersLength * 1024L);

private int ReadBufferSize => _readBuffer.Length;

private ReadOnlyMemory<byte> RemainingBuffer => new ReadOnlyMemory<byte>(_readBuffer, _readOffset, _readLength - _readOffset);
Expand Down Expand Up @@ -480,7 +482,7 @@ public async Task<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request,
}

// Start to read response.
_allowedReadLineBytes = (int)Math.Min(int.MaxValue, _pool.Settings._maxResponseHeadersLength * 1024L);
_allowedReadLineBytes = MaxResponseHeadersLength;

// We should not have any buffered data here; if there was, it should have been treated as an error
// by the previous request handling. (Note we do not support HTTP pipelining.)
Expand Down Expand Up @@ -1243,7 +1245,7 @@ private bool TryReadNextLine(out ReadOnlySpan<byte> line)
{
if (_allowedReadLineBytes < buffer.Length)
{
throw new HttpRequestException(SR.Format(SR.net_http_response_headers_exceeded_length, _pool.Settings._maxResponseHeadersLength * 1024L));
throw new HttpRequestException(SR.Format(SR.net_http_response_headers_exceeded_length, MaxResponseHeadersLength));
}

line = default;
Expand Down Expand Up @@ -1354,7 +1356,7 @@ private void ThrowIfExceededAllowedReadLineBytes()
{
if (_allowedReadLineBytes < 0)
{
throw new HttpRequestException(SR.Format(SR.net_http_response_headers_exceeded_length, _pool.Settings._maxResponseHeadersLength * 1024L));
throw new HttpRequestException(SR.Format(SR.net_http_response_headers_exceeded_length, MaxResponseHeadersLength));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,60 @@ public async Task GetAsync_NoTrailingHeaders_EmptyCollection()
}
});
}

[Theory]
[InlineData(1024, 64, false)]
[InlineData(1024, 1024 - 2, false)] // we need at least 2 spare bytes for the next CRLF
[InlineData(1024, 1024 - 1, true)]
[InlineData(1024, 1024, true)]
[InlineData(1024, 1024 + 1, true)]
[InlineData(1024 * 1024, 1024 * 1024 - 2, false)]
[InlineData(1024 * 1024, 1024 * 1024 - 1, true)]
[InlineData(1024 * 1024, 1024 * 1024, true)]
public async Task GetAsync_MaxResponseHeadersLength_EnforcedOnTrailingHeaders(int maxResponseHeadersLength, int trailersLength, bool shouldThrow)
{
await LoopbackServer.CreateClientAndServerAsync(
async uri =>
{
using HttpClientHandler handler = CreateHttpClientHandler();
using HttpClient client = CreateHttpClient(handler);
handler.MaxResponseHeadersLength = maxResponseHeadersLength / 1024;
if (shouldThrow)
{
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(uri));
}
else
{
(await client.GetAsync(uri)).Dispose();
}
},
async server =>
{
try
{
const string TrailerName1 = "My-Trailer-1";
const string TrailerName2 = "My-Trailer-2";
int trailerOneLength = trailersLength / 2;
int trailerTwoLength = trailersLength - trailerOneLength;
await server.AcceptConnectionSendCustomResponseAndCloseAsync(
"HTTP/1.1 200 OK\r\n" +
"Connection: close\r\n" +
"Transfer-Encoding: chunked\r\n" +
"\r\n" +
"4\r\n" +
"data\r\n" +
"0\r\n" +
$"{TrailerName1}: {new string('a', trailerOneLength - TrailerName1.Length - 4)}\r\n" +
$"{TrailerName2}: {new string('b', trailerTwoLength - TrailerName2.Length - 4)}\r\n" +
"\r\n");
}
catch { }
});
}
}

public sealed class SocketsHttpHandler_Http2_TrailingHeaders_Test : SocketsHttpHandler_TrailingHeaders_Test
Expand Down

0 comments on commit f9c6475

Please sign in to comment.