Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit f9c6475

Browse files
Miha Zupanmmitche
authored andcommitted
Merged PR 21363: [MSRC 69437] Apply MaxResponseHeadersLength limit for trailing headers
1 parent e486c0d commit f9c6475

File tree

3 files changed

+62
-6
lines changed

3 files changed

+62
-6
lines changed

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingReadStream.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ private sealed class ChunkedEncodingReadStream : HttpContentReadStream
2222
/// infinite chunk length is sent. This value is arbitrary and can be changed as needed.
2323
/// </remarks>
2424
private const int MaxChunkBytesAllowed = 16*1024;
25-
/// <summary>How long a trailing header can be. This value is arbitrary and can be changed as needed.</summary>
26-
private const int MaxTrailingHeaderLength = 16*1024;
2725
/// <summary>The number of bytes remaining in the chunk.</summary>
2826
private ulong _chunkBytesRemaining;
2927
/// <summary>The current state of the parsing state machine for the chunked response.</summary>
@@ -298,6 +296,9 @@ private ReadOnlyMemory<byte> ReadChunkFromConnectionBuffer(int maxBytesToRead, C
298296
else
299297
{
300298
_state = ParsingState.ConsumeTrailers;
299+
// Apply the MaxResponseHeadersLength limit to all trailing headers.
300+
// The limit is applied to regular response headers and trailing headers separately.
301+
_connection._allowedReadLineBytes = _connection.MaxResponseHeadersLength;
301302
goto case ParsingState.ConsumeTrailers;
302303
}
303304

@@ -345,7 +346,6 @@ private ReadOnlyMemory<byte> ReadChunkFromConnectionBuffer(int maxBytesToRead, C
345346
while (true)
346347
{
347348
// TODO: Consider adding folded trailing header support #35769.
348-
_connection._allowedReadLineBytes = MaxTrailingHeaderLength;
349349
if (!_connection.TryReadNextLine(out currentLine))
350350
{
351351
break;

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ public bool EnsureReadAheadAndPollRead()
205205

206206
public HttpConnectionKind Kind => _pool.Kind;
207207

208+
private int MaxResponseHeadersLength => (int)Math.Min(int.MaxValue, _pool.Settings._maxResponseHeadersLength * 1024L);
209+
208210
private int ReadBufferSize => _readBuffer.Length;
209211

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

482484
// Start to read response.
483-
_allowedReadLineBytes = (int)Math.Min(int.MaxValue, _pool.Settings._maxResponseHeadersLength * 1024L);
485+
_allowedReadLineBytes = MaxResponseHeadersLength;
484486

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

12491251
line = default;
@@ -1354,7 +1356,7 @@ private void ThrowIfExceededAllowedReadLineBytes()
13541356
{
13551357
if (_allowedReadLineBytes < 0)
13561358
{
1357-
throw new HttpRequestException(SR.Format(SR.net_http_response_headers_exceeded_length, _pool.Settings._maxResponseHeadersLength * 1024L));
1359+
throw new HttpRequestException(SR.Format(SR.net_http_response_headers_exceeded_length, MaxResponseHeadersLength));
13581360
}
13591361
}
13601362

src/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,60 @@ await TestHelper.WhenAllCompletedOrAnyFailed(
767767
}
768768
});
769769
}
770+
771+
[Theory]
772+
[InlineData(1024, 64, false)]
773+
[InlineData(1024, 1024 - 2, false)] // we need at least 2 spare bytes for the next CRLF
774+
[InlineData(1024, 1024 - 1, true)]
775+
[InlineData(1024, 1024, true)]
776+
[InlineData(1024, 1024 + 1, true)]
777+
[InlineData(1024 * 1024, 1024 * 1024 - 2, false)]
778+
[InlineData(1024 * 1024, 1024 * 1024 - 1, true)]
779+
[InlineData(1024 * 1024, 1024 * 1024, true)]
780+
public async Task GetAsync_MaxResponseHeadersLength_EnforcedOnTrailingHeaders(int maxResponseHeadersLength, int trailersLength, bool shouldThrow)
781+
{
782+
await LoopbackServer.CreateClientAndServerAsync(
783+
async uri =>
784+
{
785+
using HttpClientHandler handler = CreateHttpClientHandler();
786+
using HttpClient client = CreateHttpClient(handler);
787+
788+
handler.MaxResponseHeadersLength = maxResponseHeadersLength / 1024;
789+
790+
if (shouldThrow)
791+
{
792+
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(uri));
793+
}
794+
else
795+
{
796+
(await client.GetAsync(uri)).Dispose();
797+
}
798+
},
799+
async server =>
800+
{
801+
try
802+
{
803+
const string TrailerName1 = "My-Trailer-1";
804+
const string TrailerName2 = "My-Trailer-2";
805+
806+
int trailerOneLength = trailersLength / 2;
807+
int trailerTwoLength = trailersLength - trailerOneLength;
808+
809+
await server.AcceptConnectionSendCustomResponseAndCloseAsync(
810+
"HTTP/1.1 200 OK\r\n" +
811+
"Connection: close\r\n" +
812+
"Transfer-Encoding: chunked\r\n" +
813+
"\r\n" +
814+
"4\r\n" +
815+
"data\r\n" +
816+
"0\r\n" +
817+
$"{TrailerName1}: {new string('a', trailerOneLength - TrailerName1.Length - 4)}\r\n" +
818+
$"{TrailerName2}: {new string('b', trailerTwoLength - TrailerName2.Length - 4)}\r\n" +
819+
"\r\n");
820+
}
821+
catch { }
822+
});
823+
}
770824
}
771825

772826
public sealed class SocketsHttpHandler_Http2_TrailingHeaders_Test : SocketsHttpHandler_TrailingHeaders_Test

0 commit comments

Comments
 (0)