Skip to content

Commit 4f3e8e0

Browse files
authored
HTTP/3 input validation and connection abort (#29665)
* Connection error for invalid HTTP/3 frame on request stream * Update src/Servers/Kestrel/Core/src/CoreStrings.resx * Write frame types and errors codes in logs as per spec * Clean up * Add HTTP/3 connection exception with code * Connection abort * More abort connection stuff * More test * More tests * PR feedback
1 parent 2178ea5 commit 4f3e8e0

19 files changed

+440
-161
lines changed

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

+23-2
Original file line numberDiff line numberDiff line change
@@ -654,9 +654,30 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
654654
<value>An error occurred after the response headers were sent, a reset is being sent.</value>
655655
</data>
656656
<data name="Http3StreamErrorDataReceivedBeforeHeaders" xml:space="preserve">
657-
<value>The client sent a DATA frame before the HEADERS frame.</value>
657+
<value>The client sent a DATA frame to a request stream before the HEADERS frame.</value>
658658
</data>
659659
<data name="Http3StreamErrorFrameReceivedAfterTrailers" xml:space="preserve">
660660
<value>The client sent a {frameType} frame after trailing HEADERS.</value>
661661
</data>
662-
</root>
662+
<data name="Http3ErrorUnsupportedFrameOnRequestStream" xml:space="preserve">
663+
<value>The client sent a {frameType} frame to a request stream which isn't supported.</value>
664+
</data>
665+
<data name="Http3ErrorUnsupportedFrameOnServer" xml:space="preserve">
666+
<value>The client sent a {frameType} frame to the server which isn't supported.</value>
667+
</data>
668+
<data name="Http3ErrorUnsupportedFrameOnControlStream" xml:space="preserve">
669+
<value>The client sent a {frameType} frame to a control stream which isn't supported.</value>
670+
</data>
671+
<data name="Http3ErrorControlStreamMultipleSettingsFrames" xml:space="preserve">
672+
<value>The client sent a SETTINGS frame to a control stream that already has settings.</value>
673+
</data>
674+
<data name="Http3ErrorControlStreamFrameReceivedBeforeSettings" xml:space="preserve">
675+
<value>The client sent a {frameType} frame to a control stream before the SETTINGS frame.</value>
676+
</data>
677+
<data name="Http3ErrorControlStreamReservedSetting" xml:space="preserve">
678+
<value>The client sent a reserved setting identifier: {identifier}</value>
679+
</data>
680+
<data name="Http3ControlStreamErrorMultipleInboundStreams" xml:space="preserve">
681+
<value>The client created multiple inbound {streamType} streams for the connection.</value>
682+
</data>
683+
</root>
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3;
5+
46
namespace System.Net.Http
57
{
68
internal partial class Http3RawFrame
@@ -9,9 +11,11 @@ internal partial class Http3RawFrame
911

1012
public Http3FrameType Type { get; internal set; }
1113

14+
public string FormattedType => Http3Formatting.ToFormattedType(Type);
15+
1216
public override string ToString()
1317
{
14-
return $"{Type} Length: {Length}";
18+
return $"{FormattedType} Length: {Length}";
1519
}
1620
}
1721
}

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

+9-6
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ internal class Http3Connection : ITimeoutHandler
3939

4040
private readonly Http3PeerSettings _serverSettings = new Http3PeerSettings();
4141
private readonly StreamCloseAwaitable _streamCompletionAwaitable = new StreamCloseAwaitable();
42-
42+
private readonly IProtocolErrorCodeFeature _errorCodeFeature;
4343

4444
public Http3Connection(Http3ConnectionContext context)
4545
{
@@ -49,6 +49,8 @@ public Http3Connection(Http3ConnectionContext context)
4949
_timeoutControl = new TimeoutControl(this);
5050
_context.TimeoutControl ??= _timeoutControl;
5151

52+
_errorCodeFeature = context.ConnectionFeatures.Get<IProtocolErrorCodeFeature>()!;
53+
5254
var httpLimits = context.ServiceContext.ServerOptions.Limits;
5355

5456
_serverSettings.HeaderTableSize = (uint)httpLimits.Http3.HeaderTableSize;
@@ -76,6 +78,7 @@ internal long HighestStreamId
7678
public Http3ControlStream? ControlStream { get; set; }
7779
public Http3ControlStream? EncoderStream { get; set; }
7880
public Http3ControlStream? DecoderStream { get; set; }
81+
public string ConnectionId => _context.ConnectionId;
7982

8083
public async Task ProcessStreamsAsync<TContext>(IHttpApplication<TContext> httpApplication) where TContext : notnull
8184
{
@@ -163,7 +166,7 @@ private bool TryClose()
163166
return false;
164167
}
165168

166-
public void Abort(ConnectionAbortedException ex)
169+
public void Abort(ConnectionAbortedException ex, Http3ErrorCode errorCode)
167170
{
168171
bool previousState;
169172

@@ -180,6 +183,7 @@ public void Abort(ConnectionAbortedException ex)
180183
SendGoAway(_highestOpenedStreamId);
181184
}
182185

186+
_errorCodeFeature.Error = (long)errorCode;
183187
_multiplexedContext.Abort(ex);
184188
}
185189
}
@@ -326,9 +330,8 @@ internal async Task InnerProcessStreamsAsync<TContext>(IHttpApplication<TContext
326330
Log.RequestProcessingError(_context.ConnectionId, ex);
327331
error = ex;
328332
}
329-
catch (Http3ConnectionException ex)
333+
catch (Http3ConnectionErrorException ex)
330334
{
331-
// TODO Connection error code?
332335
Log.Http3ConnectionError(_context.ConnectionId, ex);
333336
error = ex;
334337
}
@@ -352,7 +355,7 @@ internal async Task InnerProcessStreamsAsync<TContext>(IHttpApplication<TContext
352355

353356
foreach (var stream in _streams.Values)
354357
{
355-
stream.Abort(connectionError);
358+
stream.Abort(connectionError, (Http3ErrorCode)_errorCodeFeature.Error);
356359
}
357360

358361
while (_activeRequestCount > 0)
@@ -364,7 +367,7 @@ internal async Task InnerProcessStreamsAsync<TContext>(IHttpApplication<TContext
364367
}
365368
catch
366369
{
367-
Abort(connectionError);
370+
Abort(connectionError, Http3ErrorCode.NoError);
368371
throw;
369372
}
370373
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Net.Http;
6+
7+
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
8+
{
9+
internal class Http3ConnectionErrorException : Exception
10+
{
11+
public Http3ConnectionErrorException(string message, Http3ErrorCode errorCode)
12+
: base($"HTTP/3 connection error ({errorCode}): {message}")
13+
{
14+
ErrorCode = errorCode;
15+
}
16+
17+
public Http3ErrorCode ErrorCode { get; }
18+
}
19+
}

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

-28
This file was deleted.

0 commit comments

Comments
 (0)