Skip to content

Commit 53bc77e

Browse files
committed
Connection error for invalid HTTP/3 frame on request stream
1 parent 45867e0 commit 53bc77e

File tree

6 files changed

+62
-16
lines changed

6 files changed

+62
-16
lines changed

src/Servers/Kestrel/Core/src/CoreStrings.resx

+7-1
Original file line numberDiff line numberDiff line change
@@ -653,4 +653,10 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
653653
<data name="Http3StreamErrorAfterHeaders" xml:space="preserve">
654654
<value>An error occurred after the response headers were sent, a reset is being sent.</value>
655655
</data>
656-
</root>
656+
<data name="Http3ErrorUnsupportedFrameOnRequestStream" xml:space="preserve">
657+
<value>The client sent a {frameType} frame to a request stream which isn't supported.</value>
658+
</data>
659+
<data name="Http3ErrorUnsupportedFrameOnServer" xml:space="preserve">
660+
<value>The client sent a {frameType} frame to a the server which isn't supported.</value>
661+
</data>
662+
</root>

src/Servers/Kestrel/Core/src/Internal/Http3/Frames/Http3RawFrame.Data.cs

+12
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,17 @@ public void PrepareData()
1010
Length = 0;
1111
Type = Http3FrameType.Data;
1212
}
13+
14+
public string FormattedType => Type switch
15+
{
16+
Http3FrameType.Data => "DATA",
17+
Http3FrameType.Headers => "HEADERS",
18+
Http3FrameType.CancelPush => "CANCEL_PUSH",
19+
Http3FrameType.Settings => "SETTINGS",
20+
Http3FrameType.PushPromise => "PUSH_PROMISE",
21+
Http3FrameType.GoAway => "GO_AWAY",
22+
Http3FrameType.MaxPushId => "MAX_PUSH_ID",
23+
_ => Type.ToString()
24+
};
1325
}
1426
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,6 @@ private ValueTask ProcessHttp3ControlStream(in ReadOnlySequence<byte> payload)
245245
{
246246
case Http3FrameType.Data:
247247
case Http3FrameType.Headers:
248-
case Http3FrameType.DuplicatePush:
249248
case Http3FrameType.PushPromise:
250249
throw new Http3ConnectionException("HTTP_FRAME_UNEXPECTED");
251250
case Http3FrameType.Settings:

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

+6-5
Original file line numberDiff line numberDiff line change
@@ -445,14 +445,15 @@ private Task ProcessHttp3Stream<TContext>(IHttpApplication<TContext> application
445445
return ProcessDataFrameAsync(payload);
446446
case Http3FrameType.Headers:
447447
return ProcessHeadersFrameAsync(application, payload);
448-
// need to be on control stream
449-
case Http3FrameType.DuplicatePush:
450-
case Http3FrameType.PushPromise:
451448
case Http3FrameType.Settings:
452-
case Http3FrameType.GoAway:
453449
case Http3FrameType.CancelPush:
450+
case Http3FrameType.GoAway:
454451
case Http3FrameType.MaxPushId:
455-
throw new Http3ConnectionException("HTTP_FRAME_UNEXPECTED");
452+
// These frames need to be on a control stream
453+
throw new Http3StreamErrorException(CoreStrings.FormatHttp3ErrorUnsupportedFrameOnRequestStream(_incomingFrame.FormattedType), Http3ErrorCode.UnexpectedFrame);
454+
case Http3FrameType.PushPromise:
455+
// The server should never receive push promise
456+
throw new Http3StreamErrorException(CoreStrings.FormatHttp3ErrorUnsupportedFrameOnServer(_incomingFrame.FormattedType), Http3ErrorCode.UnexpectedFrame);
456457
default:
457458
return ProcessUnknownFrameAsync();
458459
}

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

+29
Original file line numberDiff line numberDiff line change
@@ -1613,5 +1613,34 @@ public async Task ResetAfterCompleteAsync_POSTWithResponseBodyAndTrailers_Reques
16131613
clientTcs.SetResult(0);
16141614
await appTcs.Task;
16151615
}
1616+
1617+
[Theory]
1618+
[InlineData(nameof(Http3FrameType.MaxPushId))]
1619+
[InlineData(nameof(Http3FrameType.Settings))]
1620+
[InlineData(nameof(Http3FrameType.CancelPush))]
1621+
[InlineData(nameof(Http3FrameType.GoAway))]
1622+
public async Task UnexpectedRequestFrame(string frameType)
1623+
{
1624+
var requestStream = await InitializeConnectionAndStreamsAsync(_echoApplication);
1625+
1626+
var frame = new Http3RawFrame();
1627+
frame.Type = Enum.Parse<Http3FrameType>(frameType);
1628+
await requestStream.SendFrameAsync(frame, Memory<byte>.Empty);
1629+
1630+
await requestStream.WaitForStreamErrorAsync(Http3ErrorCode.UnexpectedFrame, expectedErrorMessage: CoreStrings.FormatHttp3ErrorUnsupportedFrameOnRequestStream(frame.FormattedType));
1631+
}
1632+
1633+
[Theory]
1634+
[InlineData(nameof(Http3FrameType.PushPromise))]
1635+
public async Task UnexpectedServerFrame(string frameType)
1636+
{
1637+
var requestStream = await InitializeConnectionAndStreamsAsync(_echoApplication);
1638+
1639+
var frame = new Http3RawFrame();
1640+
frame.Type = Enum.Parse<Http3FrameType>(frameType);
1641+
await requestStream.SendFrameAsync(frame, Memory<byte>.Empty);
1642+
1643+
await requestStream.WaitForStreamErrorAsync(Http3ErrorCode.UnexpectedFrame, expectedErrorMessage: CoreStrings.FormatHttp3ErrorUnsupportedFrameOnServer(frame.FormattedType));
1644+
}
16161645
}
16171646
}

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

+8-9
Original file line numberDiff line numberDiff line change
@@ -366,24 +366,23 @@ public async Task<bool> SendHeadersAsync(IEnumerable<KeyValuePair<string, string
366366
frame.PrepareHeaders();
367367
var buffer = _headerEncodingBuffer.AsMemory();
368368
var done = _qpackEncoder.BeginEncode(headers, buffer.Span, out var length);
369-
frame.Length = length;
370-
// TODO may want to modify behavior of input frames to mock different client behavior (client can send anything).
371-
Http3FrameWriter.WriteHeader(frame, outputWriter);
372-
await SendAsync(buffer.Span.Slice(0, length));
373369

374-
if (endStream)
375-
{
376-
await _pair.Application.Output.CompleteAsync();
377-
}
370+
// TODO may want to modify behavior of input frames to mock different client behavior (client can send anything).
371+
await SendFrameAsync(frame, buffer.Slice(0, length), endStream);
378372

379373
return done;
380374
}
381375

382376
internal async Task SendDataAsync(Memory<byte> data, bool endStream = false)
383377
{
384-
var outputWriter = _pair.Application.Output;
385378
var frame = new Http3RawFrame();
386379
frame.PrepareData();
380+
await SendFrameAsync(frame, data, endStream);
381+
}
382+
383+
internal async Task SendFrameAsync(Http3RawFrame frame, Memory<byte> data, bool endStream = false)
384+
{
385+
var outputWriter = _pair.Application.Output;
387386
frame.Length = data.Length;
388387
Http3FrameWriter.WriteHeader(frame, outputWriter);
389388
await SendAsync(data.Span);

0 commit comments

Comments
 (0)