Skip to content

Commit 82ceb0f

Browse files
committed
Merge
1 parent 33cdc2e commit 82ceb0f

File tree

4 files changed

+35
-21
lines changed

4 files changed

+35
-21
lines changed

src/Servers/Kestrel/Core/src/Internal/Http3/Http3ControlStream.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ private ValueTask ProcessSettingsFrameAsync(ReadOnlySequence<byte> payload)
306306
payload = payload.Slice(consumed);
307307

308308
var value = VariableLengthIntegerHelper.GetInteger(payload, out consumed, out _);
309-
if (id == -1)
309+
if (value == -1)
310310
{
311311
break;
312312
}

src/Servers/Kestrel/Core/src/Internal/Http3/Http3FrameWriter.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ internal class Http3FrameWriter
2323
{
2424
private readonly object _writeLock = new object();
2525

26-
private IEnumerator<KeyValuePair<string, string>>? _headersEnumerator;
26+
private readonly int _maxTotalHeaderSize;
2727
private readonly PipeWriter _outputWriter;
2828
private readonly ConnectionContext _connectionContext;
2929
private readonly ITimeoutControl _timeoutControl;
@@ -36,10 +36,11 @@ internal class Http3FrameWriter
3636
private readonly Http3RawFrame _outgoingFrame;
3737
private readonly TimingPipeFlusher _flusher;
3838

39+
private IEnumerator<KeyValuePair<string, string>>? _headersEnumerator;
40+
private int _headersTotalSize;
3941
// TODO update max frame size
4042
private uint _maxFrameSize = 10000; //Http3PeerSettings.MinAllowedMaxFrameSize;
4143
private byte[] _headerEncodingBuffer;
42-
4344
private long _unflushedBytes;
4445
private bool _completed;
4546
private bool _aborted;
@@ -60,13 +61,12 @@ public Http3FrameWriter(PipeWriter output, ConnectionContext connectionContext,
6061
_outgoingFrame = new Http3RawFrame();
6162
_flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, log);
6263
_headerEncodingBuffer = new byte[_maxFrameSize];
63-
_qpackEncoder = new QPackEncoder();
6464

65-
// Note that QPack encoder doesn't react to settings change during a stream.
65+
// Note that max total header size value doesn't react to settings change during a stream.
6666
// Unlikely to be a problem in practice:
6767
// - Settings rarely change after the start of a connection.
6868
// - Response header size limits are a best-effort requirement in the spec.
69-
_qpackEncoder.MaxTotalHeaderSize = clientPeerSettings.MaxRequestHeaderFieldSectionSize > int.MaxValue
69+
_maxTotalHeaderSize = clientPeerSettings.MaxRequestHeaderFieldSectionSize > int.MaxValue
7070
? int.MaxValue
7171
: (int)clientPeerSettings.MaxRequestHeaderFieldSectionSize;
7272
}
@@ -280,10 +280,11 @@ public ValueTask<FlushResult> WriteResponseTrailersAsync(long streamId, HttpResp
280280
try
281281
{
282282
_headersEnumerator = EnumerateHeaders(headers).GetEnumerator();
283+
_headersTotalSize = 0;
283284

284285
_outgoingFrame.PrepareHeaders();
285286
var buffer = _headerEncodingBuffer.AsSpan();
286-
var done = QPackHeaderWriter.BeginEncode(_headersEnumerator, buffer, out var payloadLength);
287+
var done = QPackHeaderWriter.BeginEncode(_headersEnumerator, buffer, ref _headersTotalSize, out var payloadLength);
287288
FinishWritingHeaders(payloadLength, done);
288289
}
289290
catch (QPackEncodingException ex)
@@ -335,7 +336,7 @@ internal void WriteResponseHeaders(int statusCode, IHeaderDictionary headers)
335336

336337
_outgoingFrame.PrepareHeaders();
337338
var buffer = _headerEncodingBuffer.AsSpan();
338-
var done = QPackHeaderWriter.BeginEncode(statusCode, _headersEnumerator, buffer, out var payloadLength);
339+
var done = QPackHeaderWriter.BeginEncode(statusCode, _headersEnumerator, buffer, ref _headersTotalSize, out var payloadLength);
339340
FinishWritingHeaders(payloadLength, done);
340341
}
341342
catch (QPackEncodingException ex)
@@ -356,12 +357,18 @@ private void FinishWritingHeaders(int payloadLength, bool done)
356357

357358
while (!done)
358359
{
359-
done = QPackHeaderWriter.Encode(_headersEnumerator!, buffer, out payloadLength);
360+
done = QPackHeaderWriter.Encode(_headersEnumerator!, buffer, ref _headersTotalSize, out payloadLength);
360361
_outgoingFrame.Length = payloadLength;
361362

362363
WriteHeaderUnsynchronized();
363364
_outputWriter.Write(buffer.Slice(0, payloadLength));
364365
}
366+
367+
// https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.3
368+
if (_headersTotalSize > _maxTotalHeaderSize)
369+
{
370+
throw new QPackEncodingException($"The encoded HTTP headers length exceeds the limit specified by the peer of {_maxTotalHeaderSize} bytes.");
371+
}
365372
}
366373

367374
public ValueTask CompleteAsync()

src/Servers/Kestrel/Core/src/Internal/Http3/QPackHeaderWriter.cs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,28 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics;
7-
using System.Net.Http.HPack;
87
using System.Net.Http.QPack;
98

109
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
1110
{
1211
internal static class QPackHeaderWriter
1312
{
14-
public static bool BeginEncode(IEnumerator<KeyValuePair<string, string>> enumerator, Span<byte> buffer, out int length)
13+
public static bool BeginEncode(IEnumerator<KeyValuePair<string, string>> enumerator, Span<byte> buffer, ref int totalHeaderSize, out int length)
1514
{
1615
bool hasValue = enumerator.MoveNext();
1716
Debug.Assert(hasValue == true);
1817

1918
buffer[0] = 0;
2019
buffer[1] = 0;
2120

22-
bool doneEncode = Encode(enumerator, buffer.Slice(2), out length);
21+
bool doneEncode = Encode(enumerator, buffer.Slice(2), ref totalHeaderSize, out length);
2322

2423
// Add two for the first two bytes.
2524
length += 2;
2625
return doneEncode;
2726
}
2827

29-
public static bool BeginEncode(int statusCode, IEnumerator<KeyValuePair<string, string>> enumerator, Span<byte> buffer, out int length)
28+
public static bool BeginEncode(int statusCode, IEnumerator<KeyValuePair<string, string>> enumerator, Span<byte> buffer, ref int totalHeaderSize, out int length)
3029
{
3130
bool hasValue = enumerator.MoveNext();
3231
Debug.Assert(hasValue == true);
@@ -36,24 +35,28 @@ public static bool BeginEncode(int statusCode, IEnumerator<KeyValuePair<string,
3635
buffer[1] = 0;
3736

3837
int statusCodeLength = EncodeStatusCode(statusCode, buffer.Slice(2));
39-
bool done = Encode(enumerator, buffer.Slice(statusCodeLength + 2), throwIfNoneEncoded: false, out int headersLength);
38+
totalHeaderSize += 42; // name (:status) + value (xxx) + overhead (32)
39+
40+
bool done = Encode(enumerator, buffer.Slice(statusCodeLength + 2), throwIfNoneEncoded: false, ref totalHeaderSize, out int headersLength);
4041
length = statusCodeLength + headersLength + 2;
4142

4243
return done;
4344
}
4445

45-
public static bool Encode(IEnumerator<KeyValuePair<string, string>> enumerator, Span<byte> buffer, out int length)
46+
public static bool Encode(IEnumerator<KeyValuePair<string, string>> enumerator, Span<byte> buffer, ref int totalHeaderSize, out int length)
4647
{
47-
return Encode(enumerator, buffer, throwIfNoneEncoded: true, out length);
48+
return Encode(enumerator, buffer, throwIfNoneEncoded: true, ref totalHeaderSize, out length);
4849
}
4950

50-
private static bool Encode(IEnumerator<KeyValuePair<string, string>> enumerator, Span<byte> buffer, bool throwIfNoneEncoded, out int length)
51+
private static bool Encode(IEnumerator<KeyValuePair<string, string>> enumerator, Span<byte> buffer, bool throwIfNoneEncoded, ref int totalHeaderSize, out int length)
5152
{
5253
length = 0;
5354

5455
do
5556
{
56-
if (!QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReference(enumerator.Current.Key, enumerator.Current.Value, buffer.Slice(length), out int headerLength))
57+
var current = enumerator.Current;
58+
59+
if (!QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReference(current.Key, current.Value, buffer.Slice(length), out int headerLength))
5760
{
5861
if (length == 0 && throwIfNoneEncoded)
5962
{
@@ -62,6 +65,8 @@ private static bool Encode(IEnumerator<KeyValuePair<string, string>> enumerator,
6265
return false;
6366
}
6467

68+
// https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.3
69+
totalHeaderSize += HeaderField.GetLength(current.Key.Length, current.Value.Length);
6570
length += headerLength;
6671
} while (enumerator.MoveNext());
6772

@@ -87,7 +92,7 @@ private static int EncodeStatusCode(int statusCode, Span<byte> buffer)
8792
buffer[0] = 0b01011111;
8893
buffer[1] = 0b00110000;
8994

90-
ReadOnlySpan<byte> statusBytes = StatusCodes.ToStatusBytes(statusCode);
95+
ReadOnlySpan<byte> statusBytes = System.Net.Http.HPack.StatusCodes.ToStatusBytes(statusCode);
9196
buffer[2] = (byte)statusBytes.Length;
9297
statusBytes.CopyTo(buffer.Slice(3));
9398

src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3TestBase.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,10 +583,12 @@ public Http3RequestStream(Http3TestBase testBase, Http3Connection connection)
583583

584584
public async Task SendHeadersAsync(IEnumerable<KeyValuePair<string, string>> headers, bool endStream = false)
585585
{
586+
var headersTotalSize = 0;
587+
586588
var frame = new Http3RawFrame();
587589
frame.PrepareHeaders();
588590
var buffer = _headerEncodingBuffer.AsMemory();
589-
var done = QPackHeaderWriter.BeginEncode(headers.GetEnumerator(), buffer.Span, out var length);
591+
var done = QPackHeaderWriter.BeginEncode(headers.GetEnumerator(), buffer.Span, ref headersTotalSize, out var length);
590592
Assert.True(done);
591593

592594
await SendFrameAsync(frame, buffer.Slice(0, length), endStream);
@@ -716,7 +718,7 @@ internal async Task<Dictionary<long, long>> ExpectSettingsAsync()
716718
payload = payload.Slice(consumed);
717719

718720
var value = VariableLengthIntegerHelper.GetInteger(payload, out consumed, out _);
719-
if (id == -1)
721+
if (value == -1)
720722
{
721723
break;
722724
}

0 commit comments

Comments
 (0)