diff --git a/benchmarks/Kestrel.Performance/Mocks/MockTrace.cs b/benchmarks/Kestrel.Performance/Mocks/MockTrace.cs
index 77290ee718..fef2b970f1 100644
--- a/benchmarks/Kestrel.Performance/Mocks/MockTrace.cs
+++ b/benchmarks/Kestrel.Performance/Mocks/MockTrace.cs
@@ -3,6 +3,8 @@
using System;
using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging;
@@ -40,5 +42,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
public void RequestBodyDone(string connectionId, string traceIdentifier) { }
public void RequestBodyMininumDataRateNotSatisfied(string connectionId, string traceIdentifier, double rate) { }
public void ResponseMininumDataRateNotSatisfied(string connectionId, string traceIdentifier) { }
+ public void Http2ConnectionError(string connectionId, Http2ConnectionErrorException ex) { }
+ public void Http2StreamError(string connectionId, Http2StreamErrorException ex) { }
+ public void HPackDecodingError(string connectionId, int streamId, HPackDecodingException ex) { }
}
}
diff --git a/src/Kestrel.Core/CoreStrings.resx b/src/Kestrel.Core/CoreStrings.resx
index 7127c41905..fdbebb8aaf 100644
--- a/src/Kestrel.Core/CoreStrings.resx
+++ b/src/Kestrel.Core/CoreStrings.resx
@@ -1,17 +1,17 @@
-
@@ -384,4 +384,82 @@
The header block was incomplete and could not be fully decoded.
-
\ No newline at end of file
+
+ The client sent a {frameType} frame with even stream ID {streamId}.
+
+
+ The client sent a A PUSH_PROMISE frame.
+
+
+ The client sent a {frameType} frame to stream ID {streamId} before signaling of the header block for stream ID {headersStreamId}.
+
+
+ The client sent a {frameType} frame with stream ID 0.
+
+
+ The client sent a {frameType} frame with stream ID different than 0.
+
+
+ The client sent a {frameType} frame with padding longer than or with the same length as the sent data.
+
+
+ The client sent a {frameType} frame to closed stream ID {streamId}.
+
+
+ The client sent a {frameType} frame to stream ID {streamId} which is in the "half-closed (remote) state".
+
+
+ The client sent a {frameType} frame with dependency information that would cause stream ID {streamId} to depend on itself.
+
+
+ The client sent a {frameType} frame with length different than {expectedLength}.
+
+
+ The client sent a SETTINGS frame with a length that is not a multiple of 6.
+
+
+ The client sent a SETTINGS frame with ACK set and length different than 0.
+
+
+ The client sent a SETTINGS frame with a value for parameter {parameter} that is out of range.
+
+
+ The client sent a WINDOW_UPDATE frame with a window size increment of 0.
+
+
+ The client sent a CONTINUATION frame not preceded by a HEADERS frame.
+
+
+ The client sent a {frameType} frame to idle stream ID {streamId}.
+
+
+ The client sent trailers containing one or more pseudo-header fields.
+
+
+ The client sent a header with uppercase characters in its name.
+
+
+ The client sent a trailer with uppercase characters in its name.
+
+
+ The client sent a HEADERS frame containing trailers without setting the END_STREAM flag.
+
+
+ Request headers missing one or more mandatory pseudo-header fields.
+
+
+ Pseudo-header field found in request headers after regular header fields.
+
+
+ Request headers contain unknown pseudo-header field.
+
+
+ Request headers contain response-specific pseudo-header field.
+
+
+ Request headers contain duplicate pseudo-header field.
+
+
+ Request headers contain connection-specific header field.
+
+
diff --git a/src/Kestrel.Core/Internal/Http2/Http2Connection.cs b/src/Kestrel.Core/Internal/Http2/Http2Connection.cs
index 50e023196d..40d35ebee3 100644
--- a/src/Kestrel.Core/Internal/Http2/Http2Connection.cs
+++ b/src/Kestrel.Core/Internal/Http2/Http2Connection.cs
@@ -166,28 +166,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
}
}
}
- catch (ConnectionAbortedException ex)
+ catch (ConnectionResetException ex)
{
- // TODO: log
+ // Don't log ECONNRESET errors when there are no active streams on the connection. Browsers like IE will reset connections regularly.
+ if (_streams.Count > 0)
+ {
+ Log.RequestProcessingError(ConnectionId, ex);
+ }
+
error = ex;
}
catch (Http2ConnectionErrorException ex)
{
- // TODO: log
+ Log.Http2ConnectionError(ConnectionId, ex);
error = ex;
errorCode = ex.ErrorCode;
}
catch (HPackDecodingException ex)
{
- // TODO: log
+ Log.HPackDecodingError(ConnectionId, _currentHeadersStream.StreamId, ex);
error = ex;
errorCode = Http2ErrorCode.COMPRESSION_ERROR;
}
catch (Exception ex)
{
- // TODO: log
error = ex;
errorCode = Http2ErrorCode.INTERNAL_ERROR;
+ throw;
}
finally
{
@@ -242,7 +247,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
if (_incomingFrame.StreamId != 0 && (_incomingFrame.StreamId & 1) == 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdEven(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
switch (_incomingFrame.Type)
@@ -258,7 +263,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
case Http2FrameType.SETTINGS:
return ProcessSettingsFrameAsync();
case Http2FrameType.PUSH_PROMISE:
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorPushPromiseReceived, Http2ErrorCode.PROTOCOL_ERROR);
case Http2FrameType.PING:
return ProcessPingFrameAsync();
case Http2FrameType.GOAWAY:
@@ -276,35 +281,39 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.StreamId == 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.DataHasPadding && _incomingFrame.DataPadLength >= _incomingFrame.Length)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorPaddingTooLong(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
ThrowIfIncomingFrameSentToIdleStream();
- if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream) && !stream.EndStreamReceived)
+ if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream))
{
+ if (stream.EndStreamReceived)
+ {
+ // http://httpwg.org/specs/rfc7540.html#rfc.section.5.1
+ //
+ // ...an endpoint that receives any frames after receiving a frame with the
+ // END_STREAM flag set MUST treat that as a connection error (Section 5.4.1)
+ // of type STREAM_CLOSED, unless the frame is permitted as described below.
+ //
+ // (The allowed frame types for this situation are WINDOW_UPDATE, RST_STREAM and PRIORITY)
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamHalfClosedRemote(_incomingFrame.Type, stream.StreamId), Http2ErrorCode.STREAM_CLOSED);
+ }
+
return stream.OnDataAsync(_incomingFrame.DataPayload,
endStream: (_incomingFrame.DataFlags & Http2DataFrameFlags.END_STREAM) == Http2DataFrameFlags.END_STREAM);
}
- // http://httpwg.org/specs/rfc7540.html#rfc.section.5.1
- //
- // ...an endpoint that receives any frames after receiving a frame with the
- // END_STREAM flag set MUST treat that as a connection error (Section 5.4.1)
- // of type STREAM_CLOSED, unless the frame is permitted as described below.
- //
- // (The allowed frame types for this situation are WINDOW_UPDATE, RST_STREAM and PRIORITY)
- //
// If we couldn't find the stream, it was either alive previously but closed with
// END_STREAM or RST_STREAM, or it was implicitly closed when the client opened
// a new stream with a higher ID. Per the spec, we should send RST_STREAM if
@@ -316,29 +325,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
//
// We choose to do that here so we don't have to keep state to track implicitly closed
// streams vs. streams closed with END_STREAM or RST_STREAM.
- throw new Http2ConnectionErrorException(Http2ErrorCode.STREAM_CLOSED);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamClosed(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.STREAM_CLOSED);
}
private async Task ProcessHeadersFrameAsync(IHttpApplication application)
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.StreamId == 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.HeadersHasPadding && _incomingFrame.HeadersPadLength >= _incomingFrame.Length)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorPaddingTooLong(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.HeadersHasPriority && _incomingFrame.HeadersStreamDependency == _incomingFrame.StreamId)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamSelfDependency(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream))
@@ -352,7 +361,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
// (The allowed frame types for this situation are WINDOW_UPDATE, RST_STREAM and PRIORITY)
if (stream.EndStreamReceived)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.STREAM_CLOSED);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamHalfClosedRemote(_incomingFrame.Type, stream.StreamId), Http2ErrorCode.STREAM_CLOSED);
}
// TODO: trailers
@@ -366,7 +375,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
//
// If we couldn't find the stream, it was previously closed (either implicitly or with
// END_STREAM or RST_STREAM).
- throw new Http2ConnectionErrorException(Http2ErrorCode.STREAM_CLOSED);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamClosed(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.STREAM_CLOSED);
}
else
{
@@ -400,22 +409,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.StreamId == 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.PriorityStreamDependency == _incomingFrame.StreamId)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamSelfDependency(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.Length != 5)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(_incomingFrame.Type, 5), Http2ErrorCode.FRAME_SIZE_ERROR);
}
return Task.CompletedTask;
@@ -425,17 +434,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.StreamId == 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.Length != 4)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(_incomingFrame.Type, 4), Http2ErrorCode.FRAME_SIZE_ERROR);
}
ThrowIfIncomingFrameSentToIdleStream();
@@ -452,22 +461,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.StreamId != 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdNotZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
if ((_incomingFrame.SettingsFlags & Http2SettingsFrameFlags.ACK) == Http2SettingsFrameFlags.ACK && _incomingFrame.Length != 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorSettingsAckLengthNotZero, Http2ErrorCode.FRAME_SIZE_ERROR);
}
if (_incomingFrame.Length % 6 != 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorSettingsLengthNotMultipleOfSix, Http2ErrorCode.FRAME_SIZE_ERROR);
}
try
@@ -477,7 +486,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
}
catch (Http2SettingsParameterOutOfRangeException ex)
{
- throw new Http2ConnectionErrorException(ex.Parameter == Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorSettingsParameterOutOfRange(ex.Parameter), ex.Parameter == Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE
? Http2ErrorCode.FLOW_CONTROL_ERROR
: Http2ErrorCode.PROTOCOL_ERROR);
}
@@ -487,21 +496,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.StreamId != 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdNotZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.Length != 8)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(_incomingFrame.Type, 8), Http2ErrorCode.FRAME_SIZE_ERROR);
}
if ((_incomingFrame.PingFlags & Http2PingFrameFlags.ACK) == Http2PingFrameFlags.ACK)
{
+ // TODO: verify that payload is equal to the outgoing PING frame
return Task.CompletedTask;
}
@@ -512,12 +522,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.StreamId != 0)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdNotZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR);
}
Stop();
@@ -528,26 +538,34 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
if (_incomingFrame.Length != 4)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(_incomingFrame.Type, 4), Http2ErrorCode.FRAME_SIZE_ERROR);
}
ThrowIfIncomingFrameSentToIdleStream();
if (_incomingFrame.WindowUpdateSizeIncrement == 0)
{
- if (_incomingFrame.StreamId == 0)
- {
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
- }
- else
- {
- return _frameWriter.WriteRstStreamAsync(_incomingFrame.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
- }
+ // http://httpwg.org/specs/rfc7540.html#rfc.section.6.9
+ // A receiver MUST treat the receipt of a WINDOW_UPDATE
+ // frame with an flow-control window increment of 0 as a
+ // stream error (Section 5.4.2) of type PROTOCOL_ERROR;
+ // errors on the connection flow-control window MUST be
+ // treated as a connection error (Section 5.4.1).
+ //
+ // http://httpwg.org/specs/rfc7540.html#rfc.section.5.4.1
+ // An endpoint can end a connection at any time. In
+ // particular, an endpoint MAY choose to treat a stream
+ // error as a connection error.
+ //
+ // Since server initiated stream resets are not yet properly
+ // implemented and tested, we treat all zero length window
+ // increments as connection errors for now.
+ throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorWindowUpdateIncrementZero, Http2ErrorCode.PROTOCOL_ERROR);
}
return Task.CompletedTask;
@@ -555,9 +573,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private Task ProcessContinuationFrameAsync(IHttpApplication application)
{
- if (_currentHeadersStream == null || _incomingFrame.StreamId != _currentHeadersStream.StreamId)
+ if (_currentHeadersStream == null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorContinuationWithNoHeaders, Http2ErrorCode.PROTOCOL_ERROR);
+ }
+
+ if (_incomingFrame.StreamId != _currentHeadersStream.StreamId)
+ {
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
var endHeaders = (_incomingFrame.ContinuationFlags & Http2ContinuationFrameFlags.END_HEADERS) == Http2ContinuationFrameFlags.END_HEADERS;
@@ -569,7 +592,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (_currentHeadersStream != null)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
return Task.CompletedTask;
@@ -589,6 +612,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
}
catch (Http2StreamErrorException ex)
{
+ Log.Http2StreamError(ConnectionId, ex);
ResetRequestHeaderParsingState();
return _frameWriter.WriteRstStreamAsync(ex.StreamId, ex.ErrorCode);
}
@@ -603,7 +627,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
// All HTTP/2 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header
// fields, unless it is a CONNECT request (Section 8.3). An HTTP request that omits mandatory pseudo-header
// fields is malformed (Section 8.1.2.6).
- throw new Http2StreamErrorException(_currentHeadersStream.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorMissingMandatoryPseudoHeaderFields, Http2ErrorCode.PROTOCOL_ERROR);
}
_streams[_incomingFrame.StreamId] = _currentHeadersStream;
@@ -638,7 +662,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
// initial state for all streams.
if (_incomingFrame.StreamId > _highestOpenedStreamId)
{
- throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdle(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
}
}
@@ -666,7 +690,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
// All pseudo-header fields MUST appear in the header block before regular header fields.
// Any request or response that contains a pseudo-header field that appears in a header
// block after a regular header field MUST be treated as malformed (Section 8.1.2.6).
- throw new Http2StreamErrorException(_currentHeadersStream.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorPseudoHeaderFieldAfterRegularHeaders, Http2ErrorCode.PROTOCOL_ERROR);
}
_requestHeaderParsingState = RequestHeaderParsingState.PseudoHeaderFields;
@@ -675,21 +699,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
// Endpoints MUST treat a request or response that contains undefined or invalid pseudo-header
// fields as malformed (Section 8.1.2.6).
- throw new Http2StreamErrorException(_currentHeadersStream.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorUnknownPseudoHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
}
if (headerField == PseudoHeaderFields.Status)
{
// Pseudo-header fields defined for requests MUST NOT appear in responses; pseudo-header fields
// defined for responses MUST NOT appear in requests.
- throw new Http2StreamErrorException(_currentHeadersStream.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorResponsePseudoHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
}
if ((_parsedPseudoHeaderFields & headerField) == headerField)
{
// http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.3
// All HTTP/2 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header fields
- throw new Http2StreamErrorException(_currentHeadersStream.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorDuplicatePseudoHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
}
if (headerField == PseudoHeaderFields.Method)
@@ -706,7 +730,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
if (IsConnectionSpecificHeaderField(name, value))
{
- throw new Http2StreamErrorException(_currentHeadersStream.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorConnectionSpecificHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
}
// http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2
@@ -715,7 +739,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (name[i] >= 65 && name[i] <= 90)
{
- throw new Http2StreamErrorException(_currentHeadersStream.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
+ throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorHeaderNameUppercase, Http2ErrorCode.PROTOCOL_ERROR);
}
}
}
diff --git a/src/Kestrel.Core/Internal/Http2/Http2ConnectionErrorException.cs b/src/Kestrel.Core/Internal/Http2/Http2ConnectionErrorException.cs
index b1b9136774..dd1314b1a5 100644
--- a/src/Kestrel.Core/Internal/Http2/Http2ConnectionErrorException.cs
+++ b/src/Kestrel.Core/Internal/Http2/Http2ConnectionErrorException.cs
@@ -7,8 +7,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
public class Http2ConnectionErrorException : Exception
{
- public Http2ConnectionErrorException(Http2ErrorCode errorCode)
- : base($"HTTP/2 connection error: {errorCode}")
+ public Http2ConnectionErrorException(string message, Http2ErrorCode errorCode)
+ : base($"HTTP/2 connection error ({errorCode}): {message}")
{
ErrorCode = errorCode;
}
diff --git a/src/Kestrel.Core/Internal/Http2/Http2StreamErrorException.cs b/src/Kestrel.Core/Internal/Http2/Http2StreamErrorException.cs
index d2fcdc6bb0..2f63df1412 100644
--- a/src/Kestrel.Core/Internal/Http2/Http2StreamErrorException.cs
+++ b/src/Kestrel.Core/Internal/Http2/Http2StreamErrorException.cs
@@ -7,8 +7,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
public class Http2StreamErrorException : Exception
{
- public Http2StreamErrorException(int streamId, Http2ErrorCode errorCode)
- : base($"HTTP/2 stream ID {streamId} error: {errorCode}")
+ public Http2StreamErrorException(int streamId, string message, Http2ErrorCode errorCode)
+ : base($"HTTP/2 stream ID {streamId} error ({errorCode}): {message}")
{
StreamId = streamId;
ErrorCode = errorCode;
diff --git a/src/Kestrel.Core/Internal/Infrastructure/IKestrelTrace.cs b/src/Kestrel.Core/Internal/Infrastructure/IKestrelTrace.cs
index 456e132274..46aed5b8ad 100644
--- a/src/Kestrel.Core/Internal/Infrastructure/IKestrelTrace.cs
+++ b/src/Kestrel.Core/Internal/Infrastructure/IKestrelTrace.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
@@ -45,5 +47,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
void RequestBodyMininumDataRateNotSatisfied(string connectionId, string traceIdentifier, double rate);
void ResponseMininumDataRateNotSatisfied(string connectionId, string traceIdentifier);
+
+ void Http2ConnectionError(string connectionId, Http2ConnectionErrorException ex);
+
+ void Http2StreamError(string connectionId, Http2StreamErrorException ex);
+
+ void HPackDecodingError(string connectionId, int streamId, HPackDecodingException ex);
}
-}
\ No newline at end of file
+}
diff --git a/src/Kestrel.Core/Internal/Infrastructure/KestrelTrace.cs b/src/Kestrel.Core/Internal/Infrastructure/KestrelTrace.cs
index ab6eb51293..1c31f4394c 100644
--- a/src/Kestrel.Core/Internal/Infrastructure/KestrelTrace.cs
+++ b/src/Kestrel.Core/Internal/Infrastructure/KestrelTrace.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging;
@@ -66,6 +68,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
private static readonly Action _responseMinimumDataRateNotSatisfied =
LoggerMessage.Define(LogLevel.Information, 28, @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": the connection was closed becuase the response was not read by the client at the specified minimum data rate.");
+ private static readonly Action _http2ConnectionError =
+ LoggerMessage.Define(LogLevel.Information, 29, @"Connection id ""{ConnectionId}"": HTTP/2 connection error.");
+
+ private static readonly Action _http2StreamError =
+ LoggerMessage.Define(LogLevel.Information, 30, @"Connection id ""{ConnectionId}"": HTTP/2 stream error.");
+
+ private static readonly Action _hpackDecodingError =
+ LoggerMessage.Define(LogLevel.Information, 31, @"Connection id ""{ConnectionId}"": HPACK decoding error while decoding headers for stream ID {StreamId}.");
+
protected readonly ILogger _logger;
public KestrelTrace(ILogger logger)
@@ -168,6 +179,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
_responseMinimumDataRateNotSatisfied(_logger, connectionId, traceIdentifier, null);
}
+ public void Http2ConnectionError(string connectionId, Http2ConnectionErrorException ex)
+ {
+ _http2ConnectionError(_logger, connectionId, ex);
+ }
+
+ public void Http2StreamError(string connectionId, Http2StreamErrorException ex)
+ {
+ _http2StreamError(_logger, connectionId, ex);
+ }
+
+ public void HPackDecodingError(string connectionId, int streamId, HPackDecodingException ex)
+ {
+ _hpackDecodingError(_logger, connectionId, streamId, ex);
+ }
+
public virtual void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
=> _logger.Log(logLevel, eventId, state, exception, formatter);
diff --git a/src/Kestrel.Core/Properties/CoreStrings.Designer.cs b/src/Kestrel.Core/Properties/CoreStrings.Designer.cs
index 7084b94b0c..346b18a969 100644
--- a/src/Kestrel.Core/Properties/CoreStrings.Designer.cs
+++ b/src/Kestrel.Core/Properties/CoreStrings.Designer.cs
@@ -1256,6 +1256,370 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
internal static string FormatHPackErrorIncompleteHeaderBlock()
=> GetString("HPackErrorIncompleteHeaderBlock");
+ ///
+ /// The client sent a {frameType} frame with even stream ID {streamId}.
+ ///
+ internal static string Http2ErrorStreamIdEven
+ {
+ get => GetString("Http2ErrorStreamIdEven");
+ }
+
+ ///
+ /// The client sent a {frameType} frame with even stream ID {streamId}.
+ ///
+ internal static string FormatHttp2ErrorStreamIdEven(object frameType, object streamId)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamIdEven", "frameType", "streamId"), frameType, streamId);
+
+ ///
+ /// The client sent a A PUSH_PROMISE frame.
+ ///
+ internal static string Http2ErrorPushPromiseReceived
+ {
+ get => GetString("Http2ErrorPushPromiseReceived");
+ }
+
+ ///
+ /// The client sent a A PUSH_PROMISE frame.
+ ///
+ internal static string FormatHttp2ErrorPushPromiseReceived()
+ => GetString("Http2ErrorPushPromiseReceived");
+
+ ///
+ /// The client sent a {frameType} frame to stream ID {streamId} before signaling of the header block for stream ID {headersStreamId}.
+ ///
+ internal static string Http2ErrorHeadersInterleaved
+ {
+ get => GetString("Http2ErrorHeadersInterleaved");
+ }
+
+ ///
+ /// The client sent a {frameType} frame to stream ID {streamId} before signaling of the header block for stream ID {headersStreamId}.
+ ///
+ internal static string FormatHttp2ErrorHeadersInterleaved(object frameType, object streamId, object headersStreamId)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorHeadersInterleaved", "frameType", "streamId", "headersStreamId"), frameType, streamId, headersStreamId);
+
+ ///
+ /// The client sent a {frameType} frame with stream ID 0.
+ ///
+ internal static string Http2ErrorStreamIdZero
+ {
+ get => GetString("Http2ErrorStreamIdZero");
+ }
+
+ ///
+ /// The client sent a {frameType} frame with stream ID 0.
+ ///
+ internal static string FormatHttp2ErrorStreamIdZero(object frameType)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamIdZero", "frameType"), frameType);
+
+ ///
+ /// The client sent a {frameType} frame with stream ID different than 0.
+ ///
+ internal static string Http2ErrorStreamIdNotZero
+ {
+ get => GetString("Http2ErrorStreamIdNotZero");
+ }
+
+ ///
+ /// The client sent a {frameType} frame with stream ID different than 0.
+ ///
+ internal static string FormatHttp2ErrorStreamIdNotZero(object frameType)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamIdNotZero", "frameType"), frameType);
+
+ ///
+ /// The client sent a {frameType} frame with padding longer than or with the same length as the sent data.
+ ///
+ internal static string Http2ErrorPaddingTooLong
+ {
+ get => GetString("Http2ErrorPaddingTooLong");
+ }
+
+ ///
+ /// The client sent a {frameType} frame with padding longer than or with the same length as the sent data.
+ ///
+ internal static string FormatHttp2ErrorPaddingTooLong(object frameType)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorPaddingTooLong", "frameType"), frameType);
+
+ ///
+ /// The client sent a {frameType} frame to closed stream ID {streamId}.
+ ///
+ internal static string Http2ErrorStreamClosed
+ {
+ get => GetString("Http2ErrorStreamClosed");
+ }
+
+ ///
+ /// The client sent a {frameType} frame to closed stream ID {streamId}.
+ ///
+ internal static string FormatHttp2ErrorStreamClosed(object frameType, object streamId)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamClosed", "frameType", "streamId"), frameType, streamId);
+
+ ///
+ /// The client sent a {frameType} frame to stream ID {streamId} which is in the "half-closed (remote) state".
+ ///
+ internal static string Http2ErrorStreamHalfClosedRemote
+ {
+ get => GetString("Http2ErrorStreamHalfClosedRemote");
+ }
+
+ ///
+ /// The client sent a {frameType} frame to stream ID {streamId} which is in the "half-closed (remote) state".
+ ///
+ internal static string FormatHttp2ErrorStreamHalfClosedRemote(object frameType, object streamId)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamHalfClosedRemote", "frameType", "streamId"), frameType, streamId);
+
+ ///
+ /// The client sent a {frameType} frame with dependency information that would cause stream ID {streamId} to depend on itself.
+ ///
+ internal static string Http2ErrorStreamSelfDependency
+ {
+ get => GetString("Http2ErrorStreamSelfDependency");
+ }
+
+ ///
+ /// The client sent a {frameType} frame with dependency information that would cause stream ID {streamId} to depend on itself.
+ ///
+ internal static string FormatHttp2ErrorStreamSelfDependency(object frameType, object streamId)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamSelfDependency", "frameType", "streamId"), frameType, streamId);
+
+ ///
+ /// The client sent a {frameType} frame with length different than {expectedLength}.
+ ///
+ internal static string Http2ErrorUnexpectedFrameLength
+ {
+ get => GetString("Http2ErrorUnexpectedFrameLength");
+ }
+
+ ///
+ /// The client sent a {frameType} frame with length different than {expectedLength}.
+ ///
+ internal static string FormatHttp2ErrorUnexpectedFrameLength(object frameType, object expectedLength)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorUnexpectedFrameLength", "frameType", "expectedLength"), frameType, expectedLength);
+
+ ///
+ /// The client sent a SETTINGS frame with a length that is not a multiple of 6.
+ ///
+ internal static string Http2ErrorSettingsLengthNotMultipleOfSix
+ {
+ get => GetString("Http2ErrorSettingsLengthNotMultipleOfSix");
+ }
+
+ ///
+ /// The client sent a SETTINGS frame with a length that is not a multiple of 6.
+ ///
+ internal static string FormatHttp2ErrorSettingsLengthNotMultipleOfSix()
+ => GetString("Http2ErrorSettingsLengthNotMultipleOfSix");
+
+ ///
+ /// The client sent a SETTINGS frame with ACK set and length different than 0.
+ ///
+ internal static string Http2ErrorSettingsAckLengthNotZero
+ {
+ get => GetString("Http2ErrorSettingsAckLengthNotZero");
+ }
+
+ ///
+ /// The client sent a SETTINGS frame with ACK set and length different than 0.
+ ///
+ internal static string FormatHttp2ErrorSettingsAckLengthNotZero()
+ => GetString("Http2ErrorSettingsAckLengthNotZero");
+
+ ///
+ /// The client sent a SETTINGS frame with a value for parameter {parameter} that is out of range.
+ ///
+ internal static string Http2ErrorSettingsParameterOutOfRange
+ {
+ get => GetString("Http2ErrorSettingsParameterOutOfRange");
+ }
+
+ ///
+ /// The client sent a SETTINGS frame with a value for parameter {parameter} that is out of range.
+ ///
+ internal static string FormatHttp2ErrorSettingsParameterOutOfRange(object parameter)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorSettingsParameterOutOfRange", "parameter"), parameter);
+
+ ///
+ /// The client sent a WINDOW_UPDATE frame with a window size increment of 0.
+ ///
+ internal static string Http2ErrorWindowUpdateIncrementZero
+ {
+ get => GetString("Http2ErrorWindowUpdateIncrementZero");
+ }
+
+ ///
+ /// The client sent a WINDOW_UPDATE frame with a window size increment of 0.
+ ///
+ internal static string FormatHttp2ErrorWindowUpdateIncrementZero()
+ => GetString("Http2ErrorWindowUpdateIncrementZero");
+
+ ///
+ /// The client sent a CONTINUATION frame not preceded by a HEADERS frame.
+ ///
+ internal static string Http2ErrorContinuationWithNoHeaders
+ {
+ get => GetString("Http2ErrorContinuationWithNoHeaders");
+ }
+
+ ///
+ /// The client sent a CONTINUATION frame not preceded by a HEADERS frame.
+ ///
+ internal static string FormatHttp2ErrorContinuationWithNoHeaders()
+ => GetString("Http2ErrorContinuationWithNoHeaders");
+
+ ///
+ /// The client sent a {frameType} frame to idle stream ID {streamId}.
+ ///
+ internal static string Http2ErrorStreamIdle
+ {
+ get => GetString("Http2ErrorStreamIdle");
+ }
+
+ ///
+ /// The client sent a {frameType} frame to idle stream ID {streamId}.
+ ///
+ internal static string FormatHttp2ErrorStreamIdle(object frameType, object streamId)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamIdle", "frameType", "streamId"), frameType, streamId);
+
+ ///
+ /// The client sent trailers containing one or more pseudo-header fields.
+ ///
+ internal static string Http2ErrorTrailersContainPseudoHeaderField
+ {
+ get => GetString("Http2ErrorTrailersContainPseudoHeaderField");
+ }
+
+ ///
+ /// The client sent trailers containing one or more pseudo-header fields.
+ ///
+ internal static string FormatHttp2ErrorTrailersContainPseudoHeaderField()
+ => GetString("Http2ErrorTrailersContainPseudoHeaderField");
+
+ ///
+ /// The client sent a header with uppercase characters in its name.
+ ///
+ internal static string Http2ErrorHeaderNameUppercase
+ {
+ get => GetString("Http2ErrorHeaderNameUppercase");
+ }
+
+ ///
+ /// The client sent a header with uppercase characters in its name.
+ ///
+ internal static string FormatHttp2ErrorHeaderNameUppercase()
+ => GetString("Http2ErrorHeaderNameUppercase");
+
+ ///
+ /// The client sent a trailer with uppercase characters in its name.
+ ///
+ internal static string Http2ErrorTrailerNameUppercase
+ {
+ get => GetString("Http2ErrorTrailerNameUppercase");
+ }
+
+ ///
+ /// The client sent a trailer with uppercase characters in its name.
+ ///
+ internal static string FormatHttp2ErrorTrailerNameUppercase()
+ => GetString("Http2ErrorTrailerNameUppercase");
+
+ ///
+ /// The client sent a HEADERS frame containing trailers without setting the END_STREAM flag.
+ ///
+ internal static string Http2ErrorHeadersWithTrailersNoEndStream
+ {
+ get => GetString("Http2ErrorHeadersWithTrailersNoEndStream");
+ }
+
+ ///
+ /// The client sent a HEADERS frame containing trailers without setting the END_STREAM flag.
+ ///
+ internal static string FormatHttp2ErrorHeadersWithTrailersNoEndStream()
+ => GetString("Http2ErrorHeadersWithTrailersNoEndStream");
+
+ ///
+ /// Request headers missing one or more mandatory pseudo-header fields.
+ ///
+ internal static string Http2ErrorMissingMandatoryPseudoHeaderFields
+ {
+ get => GetString("Http2ErrorMissingMandatoryPseudoHeaderFields");
+ }
+
+ ///
+ /// Request headers missing one or more mandatory pseudo-header fields.
+ ///
+ internal static string FormatHttp2ErrorMissingMandatoryPseudoHeaderFields()
+ => GetString("Http2ErrorMissingMandatoryPseudoHeaderFields");
+
+ ///
+ /// Pseudo-header field found in request headers after regular header fields.
+ ///
+ internal static string Http2ErrorPseudoHeaderFieldAfterRegularHeaders
+ {
+ get => GetString("Http2ErrorPseudoHeaderFieldAfterRegularHeaders");
+ }
+
+ ///
+ /// Pseudo-header field found in request headers after regular header fields.
+ ///
+ internal static string FormatHttp2ErrorPseudoHeaderFieldAfterRegularHeaders()
+ => GetString("Http2ErrorPseudoHeaderFieldAfterRegularHeaders");
+
+ ///
+ /// Request headers contain unknown pseudo-header field.
+ ///
+ internal static string Http2ErrorUnknownPseudoHeaderField
+ {
+ get => GetString("Http2ErrorUnknownPseudoHeaderField");
+ }
+
+ ///
+ /// Request headers contain unknown pseudo-header field.
+ ///
+ internal static string FormatHttp2ErrorUnknownPseudoHeaderField()
+ => GetString("Http2ErrorUnknownPseudoHeaderField");
+
+ ///
+ /// Request headers contain response-specific pseudo-header field.
+ ///
+ internal static string Http2ErrorResponsePseudoHeaderField
+ {
+ get => GetString("Http2ErrorResponsePseudoHeaderField");
+ }
+
+ ///
+ /// Request headers contain response-specific pseudo-header field.
+ ///
+ internal static string FormatHttp2ErrorResponsePseudoHeaderField()
+ => GetString("Http2ErrorResponsePseudoHeaderField");
+
+ ///
+ /// Request headers contain duplicate pseudo-header field.
+ ///
+ internal static string Http2ErrorDuplicatePseudoHeaderField
+ {
+ get => GetString("Http2ErrorDuplicatePseudoHeaderField");
+ }
+
+ ///
+ /// Request headers contain duplicate pseudo-header field.
+ ///
+ internal static string FormatHttp2ErrorDuplicatePseudoHeaderField()
+ => GetString("Http2ErrorDuplicatePseudoHeaderField");
+
+ ///
+ /// Request headers contain connection-specific header field.
+ ///
+ internal static string Http2ErrorConnectionSpecificHeaderField
+ {
+ get => GetString("Http2ErrorConnectionSpecificHeaderField");
+ }
+
+ ///
+ /// Request headers contain connection-specific header field.
+ ///
+ internal static string FormatHttp2ErrorConnectionSpecificHeaderField()
+ => GetString("Http2ErrorConnectionSpecificHeaderField");
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/test/Kestrel.Core.Tests/Http2ConnectionTests.cs b/test/Kestrel.Core.Tests/Http2ConnectionTests.cs
index 8e7a80aeb4..40fdf47094 100644
--- a/test/Kestrel.Core.Tests/Http2ConnectionTests.cs
+++ b/test/Kestrel.Core.Tests/Http2ConnectionTests.cs
@@ -10,6 +10,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Protocols;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
@@ -77,6 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
private readonly PipeFactory _pipeFactory = new PipeFactory();
private readonly (IPipeConnection Transport, IPipeConnection Application) _pair;
+ private readonly TestApplicationErrorLogger _logger;
private readonly Http2ConnectionContext _connectionContext;
private readonly Http2Connection _connection;
private readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
@@ -215,9 +217,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_hpackDecoder = new HPackDecoder((int)_clientSettings.HeaderTableSize);
+ _logger = new TestApplicationErrorLogger();
+
_connectionContext = new Http2ConnectionContext
{
- ServiceContext = new TestServiceContext(),
+ ServiceContext = new TestServiceContext()
+ {
+ Log = new TestKestrelTrace(_logger)
+ },
PipeFactory = _pipeFactory,
Application = _pair.Application,
Transport = _pair.Transport
@@ -421,7 +428,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(0, _noData, endStream: false);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdZero(Http2FrameType.DATA));
}
[Fact]
@@ -431,7 +442,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(2, _noData, endStream: false);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdEven(Http2FrameType.DATA, streamId: 2));
}
[Fact]
@@ -442,7 +457,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendInvalidDataFrameAsync(1, frameLength: 5, padLength: 5);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: true);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: true,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorPaddingTooLong(Http2FrameType.DATA));
}
[Fact]
@@ -453,7 +472,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendInvalidDataFrameAsync(1, frameLength: 5, padLength: 6);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: true);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: true,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorPaddingTooLong(Http2FrameType.DATA));
}
[Fact]
@@ -464,7 +487,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendInvalidDataFrameAsync(1, frameLength: 0, padLength: 0);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: true);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: true,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorPaddingTooLong(Http2FrameType.DATA));
}
[Fact]
@@ -475,7 +502,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
await SendDataAsync(1, _helloWorldBytes, endStream: true);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.DATA, streamId: 1, headersStreamId: 1));
}
[Fact]
@@ -485,7 +516,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: false);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdle(Http2FrameType.DATA, streamId: 1));
}
[Fact]
@@ -498,7 +533,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: false);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamHalfClosedRemote(Http2FrameType.DATA, streamId: 1));
}
[Fact]
@@ -519,7 +558,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: false);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.DATA, streamId: 1));
}
[Fact]
@@ -547,7 +590,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: true);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 3, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 3,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.DATA, streamId: 1));
}
[Fact]
@@ -647,7 +694,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(0, _browserRequestHeaders, endStream: true);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdZero(Http2FrameType.HEADERS));
}
[Fact]
@@ -657,7 +708,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(2, _browserRequestHeaders, endStream: true);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdEven(Http2FrameType.HEADERS, streamId: 2));
}
[Fact]
@@ -679,7 +734,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Try to re-use the stream ID (http://httpwg.org/specs/rfc7540.html#rfc.section.5.1.1)
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.HEADERS, streamId: 1));
}
[Fact]
@@ -692,7 +751,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamHalfClosedRemote(Http2FrameType.HEADERS, streamId: 1));
}
[Fact]
@@ -714,7 +777,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Stream 1 was implicitly closed by opening stream 3 before (http://httpwg.org/specs/rfc7540.html#rfc.section.5.1.1)
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 3, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 3,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.HEADERS, streamId: 1));
}
[Theory]
@@ -727,7 +794,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendInvalidHeadersFrameAsync(1, frameLength: padLength, padLength: padLength);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: true,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorPaddingTooLong(Http2FrameType.HEADERS));
}
[Theory]
@@ -740,7 +811,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendInvalidHeadersFrameAsync(1, frameLength, padLength);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: true,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorPaddingTooLong(Http2FrameType.HEADERS));
}
[Fact]
@@ -751,7 +826,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
await SendHeadersAsync(3, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.HEADERS, streamId: 3, headersStreamId: 1));
}
[Fact]
@@ -761,7 +840,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersWithPriorityAsync(1, _browserRequestHeaders, priority: 42, streamDependency: 1, endStream: true);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamSelfDependency(Http2FrameType.HEADERS, streamId: 1));
}
[Fact]
@@ -771,7 +854,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendIncompleteHeadersFrameAsync(streamId: 1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.COMPRESSION_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.COMPRESSION_ERROR,
+ expectedErrorMessage: CoreStrings.HPackErrorIncompleteHeaderBlock);
}
[Theory]
@@ -781,11 +868,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await InitializeConnectionAsync(_noopApplication);
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS, headerBlock);
- await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, ignoreNonRstStreamFrames: false);
+ await WaitForStreamErrorAsync(
+ ignoreNonRstStreamFrames: false,
+ expectedStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.Http2ErrorHeaderNameUppercase);
// Verify that the stream ID can't be re-used
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS, _browserRequestHeaders);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.HEADERS, streamId: 1));
}
[Fact]
@@ -799,7 +894,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
new KeyValuePair(":unknown", "0"),
};
- return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
+ return HEADERS_Received_InvalidHeaderFields_StreamError(headers, expectedErrorMessage: CoreStrings.Http2ErrorUnknownPseudoHeaderField);
}
[Fact]
@@ -813,21 +908,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
new KeyValuePair(":status", "200"),
};
- return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
+ return HEADERS_Received_InvalidHeaderFields_StreamError(headers, expectedErrorMessage: CoreStrings.Http2ErrorResponsePseudoHeaderField);
}
[Theory]
[MemberData(nameof(DuplicatePseudoHeaderFieldData))]
public Task HEADERS_Received_HeaderBlockContainsDuplicatePseudoHeaderField_StreamError(IEnumerable> headers)
{
- return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
+ return HEADERS_Received_InvalidHeaderFields_StreamError(headers, expectedErrorMessage: CoreStrings.Http2ErrorDuplicatePseudoHeaderField);
}
[Theory]
[MemberData(nameof(MissingPseudoHeaderFieldData))]
public Task HEADERS_Received_HeaderBlockDoesNotContainMandatoryPseudoHeaderField_StreamError(IEnumerable> headers)
{
- return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
+ return HEADERS_Received_InvalidHeaderFields_StreamError(headers, expectedErrorMessage: CoreStrings.Http2ErrorMissingMandatoryPseudoHeaderFields);
}
[Theory]
@@ -854,23 +949,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[MemberData(nameof(PseudoHeaderFieldAfterRegularHeadersData))]
public Task HEADERS_Received_HeaderBlockContainsPseudoHeaderFieldAfterRegularHeaders_StreamError(IEnumerable> headers)
{
- return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
+ return HEADERS_Received_InvalidHeaderFields_StreamError(headers, expectedErrorMessage: CoreStrings.Http2ErrorPseudoHeaderFieldAfterRegularHeaders);
}
- private async Task HEADERS_Received_InvalidHeaderFields_StreamError(IEnumerable> headers)
+ private async Task HEADERS_Received_InvalidHeaderFields_StreamError(IEnumerable> headers, string expectedErrorMessage)
{
await InitializeConnectionAsync(_noopApplication);
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS, headers);
- await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, ignoreNonRstStreamFrames: false);
+ await WaitForStreamErrorAsync(
+ ignoreNonRstStreamFrames: false,
+ expectedStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: expectedErrorMessage);
// Verify that the stream ID can't be re-used
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS, _browserRequestHeaders);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.HEADERS, streamId: 1));
}
[Fact]
- public Task HEADERS_Received_HeaderBlockContainsConnectionSpecificHeader_StreamError()
+ public Task HEADERS_Received_HeaderBlockContainsConnectionHeader_StreamError()
{
var headers = new[]
{
@@ -880,7 +983,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
new KeyValuePair("connection", "keep-alive")
};
- return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
+ return HEADERS_Received_InvalidHeaderFields_StreamError(headers, CoreStrings.Http2ErrorConnectionSpecificHeaderField);
}
[Fact]
@@ -894,21 +997,34 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
new KeyValuePair("te", "trailers, deflate")
};
- return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
+ return HEADERS_Received_InvalidHeaderFields_StreamError(headers, CoreStrings.Http2ErrorConnectionSpecificHeaderField);
}
[Fact]
- public Task HEADERS_Received_HeaderBlockContainsTEHeader_ValueIsTrailers_NoError()
+ public async Task HEADERS_Received_HeaderBlockContainsTEHeader_ValueIsTrailers_NoError()
{
var headers = new[]
{
new KeyValuePair(":method", "GET"),
new KeyValuePair(":path", "/"),
new KeyValuePair(":scheme", "http"),
- new KeyValuePair("te", "trailers, deflate")
+ new KeyValuePair("te", "trailers")
};
- return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
+ await InitializeConnectionAsync(_noopApplication);
+
+ await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers);
+
+ await ExpectAsync(Http2FrameType.HEADERS,
+ withLength: 55,
+ withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
+ withStreamId: 1);
+ await ExpectAsync(Http2FrameType.DATA,
+ withLength: 0,
+ withFlags: (byte)Http2HeadersFrameFlags.END_STREAM,
+ withStreamId: 1);
+
+ await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
}
[Fact]
@@ -918,7 +1034,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendPriorityAsync(0);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdZero(Http2FrameType.PRIORITY));
}
[Fact]
@@ -928,7 +1048,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendPriorityAsync(2);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdEven(Http2FrameType.PRIORITY, streamId: 2));
}
[Theory]
@@ -940,7 +1064,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendInvalidPriorityFrameAsync(1, length);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(Http2FrameType.PRIORITY, expectedLength: 5));
}
[Fact]
@@ -951,7 +1079,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
await SendPriorityAsync(1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.PRIORITY, streamId: 1, headersStreamId: 1));
}
[Fact]
@@ -961,7 +1093,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendPriorityAsync(1, streamDependency: 1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamSelfDependency(Http2FrameType.PRIORITY, 1));
}
[Fact]
@@ -1007,7 +1143,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendRstStreamAsync(0);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdZero(Http2FrameType.RST_STREAM));
}
[Fact]
@@ -1017,7 +1157,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendRstStreamAsync(2);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdEven(Http2FrameType.RST_STREAM, streamId: 2));
}
[Fact]
@@ -1027,7 +1171,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendRstStreamAsync(1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdle(Http2FrameType.RST_STREAM, streamId: 1));
}
[Theory]
@@ -1042,7 +1190,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendInvalidRstStreamFrameAsync(1, length);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR, ignoreNonGoAwayFrames: true);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: true,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(Http2FrameType.RST_STREAM, expectedLength: 4));
}
[Fact]
@@ -1053,7 +1205,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
await SendRstStreamAsync(1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.RST_STREAM, streamId: 1, headersStreamId: 1));
}
[Fact]
@@ -1065,13 +1221,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
[Fact]
- public async Task SETTINGS_Received_StreamIdNonZero_ConnectionError()
+ public async Task SETTINGS_Received_StreamIdNotZero_ConnectionError()
{
await InitializeConnectionAsync(_noopApplication);
await SendSettingsWithInvalidStreamIdAsync(1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdNotZero(Http2FrameType.SETTINGS));
}
[Theory]
@@ -1090,7 +1250,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendSettingsWithInvalidParameterValueAsync(parameter, value);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: expectedErrorCode, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: expectedErrorCode,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorSettingsParameterOutOfRange(parameter));
}
[Fact]
@@ -1101,7 +1265,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
await SendSettingsAsync();
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.SETTINGS, streamId: 0, headersStreamId: 1));
}
[Theory]
@@ -1113,7 +1281,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendSettingsAckWithInvalidLengthAsync(length);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR,
+ expectedErrorMessage: CoreStrings.Http2ErrorSettingsAckLengthNotZero);
}
[Theory]
@@ -1128,7 +1300,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendSettingsWithInvalidLengthAsync(length);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR,
+ expectedErrorMessage: CoreStrings.Http2ErrorSettingsLengthNotMultipleOfSix);
}
[Fact]
@@ -1138,7 +1314,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendPushPromiseFrameAsync();
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.Http2ErrorPushPromiseReceived);
}
[Fact]
@@ -1173,7 +1353,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
await SendPingAsync(Http2PingFrameFlags.NONE);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.PING, streamId: 0, headersStreamId: 1));
}
[Fact]
@@ -1183,7 +1367,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendPingWithInvalidStreamIdAsync(streamId: 1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdNotZero(Http2FrameType.PING));
}
[Theory]
@@ -1197,7 +1385,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendPingWithInvalidLengthAsync(length);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(Http2FrameType.PING, expectedLength: 8));
}
[Fact]
@@ -1231,13 +1423,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
[Fact]
- public async Task GOAWAY_Received_StreamIdNonZero_ConnectionError()
+ public async Task GOAWAY_Received_StreamIdNotZero_ConnectionError()
{
await InitializeConnectionAsync(_noopApplication);
await SendInvalidGoAwayFrameAsync();
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdNotZero(Http2FrameType.GOAWAY));
}
[Fact]
@@ -1248,7 +1444,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
await SendGoAwayAsync();
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.GOAWAY, streamId: 0, headersStreamId: 1));
}
[Fact]
@@ -1258,7 +1458,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendWindowUpdateAsync(2, sizeIncrement: 42);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdEven(Http2FrameType.WINDOW_UPDATE, streamId: 2));
}
[Fact]
@@ -1269,7 +1473,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
await SendWindowUpdateAsync(1, sizeIncrement: 42);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.WINDOW_UPDATE, streamId: 1, headersStreamId: 1));
}
[Theory]
@@ -1283,7 +1491,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendInvalidWindowUpdateAsync(streamId, sizeIncrement: 42, length: length);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(Http2FrameType.WINDOW_UPDATE, expectedLength: 4));
}
[Fact]
@@ -1293,7 +1505,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendWindowUpdateAsync(0, sizeIncrement: 0);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.Http2ErrorWindowUpdateIncrementZero);
+ }
+
+ [Fact]
+ public async Task WINDOW_UPDATE_Received_OnStream_SizeIncrementZero_ConnectionError()
+ {
+ await InitializeConnectionAsync(_waitForAbortApplication);
+
+ await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
+ await SendWindowUpdateAsync(1, sizeIncrement: 0);
+
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.Http2ErrorWindowUpdateIncrementZero);
}
[Fact]
@@ -1303,20 +1534,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendWindowUpdateAsync(1, sizeIncrement: 1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
- }
-
- [Fact]
- public async Task WINDOW_UPDATE_Received_OnStream_SizeIncrementZero_StreamError()
- {
- await InitializeConnectionAsync(_waitForAbortApplication);
-
- await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
- await SendWindowUpdateAsync(1, sizeIncrement: 0);
-
- await WaitForStreamErrorAsync(expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonRstStreamFrames: true);
-
- await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: true);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdle(Http2FrameType.WINDOW_UPDATE, streamId: 1));
}
[Fact]
@@ -1348,7 +1570,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _oneContinuationRequestHeaders);
await SendContinuationAsync(3, Http2ContinuationFrameFlags.END_HEADERS);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.CONTINUATION, streamId: 3, headersStreamId: 1));
}
[Fact]
@@ -1359,7 +1585,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _postRequestHeaders);
await SendIncompleteContinuationFrameAsync(streamId: 1);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.COMPRESSION_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.COMPRESSION_ERROR,
+ expectedErrorMessage: CoreStrings.HPackErrorIncompleteHeaderBlock);
}
[Theory]
@@ -1371,11 +1601,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.True(await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, headers));
await SendEmptyContinuationFrameAsync(1, Http2ContinuationFrameFlags.END_HEADERS);
- await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, ignoreNonRstStreamFrames: false);
+ await WaitForStreamErrorAsync(
+ ignoreNonRstStreamFrames: false,
+ expectedStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.Http2ErrorMissingMandatoryPseudoHeaderFields);
// Verify that the stream ID can't be re-used
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS, headers);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 1,
+ expectedErrorCode: Http2ErrorCode.STREAM_CLOSED,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.HEADERS, streamId: 1));
}
[Theory]
@@ -1448,7 +1686,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
await InitializeConnectionAsync(_noopApplication);
- await SendUnknownFrameTypeAsync(streamId: 1);
+ await SendUnknownFrameTypeAsync(streamId: 1, frameType: 42);
// Check that the connection is still alive
await SendPingAsync(Http2PingFrameFlags.NONE);
@@ -1466,13 +1704,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await InitializeConnectionAsync(_noopApplication);
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
- await SendUnknownFrameTypeAsync(streamId: 1);
+ await SendUnknownFrameTypeAsync(streamId: 1, frameType: 42);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 0,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(frameType: 42, streamId: 1, headersStreamId: 1));
}
[Fact]
- public async Task ConnectionError_AbortsAllStreams()
+ public async Task ConnectionErrorAbortsAllStreams()
{
await InitializeConnectionAsync(_waitForAbortApplication);
@@ -1484,7 +1726,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Cause a connection error by sending an invalid frame
await SendDataAsync(0, _noData, endStream: false);
- await WaitForConnectionErrorAsync(expectedLastStreamId: 5, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
+ await WaitForConnectionErrorAsync(
+ ignoreNonGoAwayFrames: false,
+ expectedLastStreamId: 5,
+ expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR,
+ expectedErrorMessage: CoreStrings.FormatHttp2ErrorStreamIdZero(Http2FrameType.DATA));
await WaitForAllStreamsAsync();
Assert.Contains(1, _abortedStreamIds);
@@ -1492,6 +1738,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Contains(5, _abortedStreamIds);
}
+ [Fact]
+ public async Task ConnectionResetLoggedWithActiveStreams()
+ {
+ await InitializeConnectionAsync(_waitForAbortApplication);
+
+ await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, _browserRequestHeaders);
+
+ _pair.Application.Output.Complete(new ConnectionResetException(string.Empty));
+
+ var result = await _pair.Application.Input.ReadAsync();
+ Assert.True(result.IsCompleted);
+ Assert.Single(_logger.Messages, m => m.Exception is ConnectionResetException);
+ }
+
+ [Fact]
+ public async Task ConnectionResetNotLoggedWithNoActiveStreams()
+ {
+ await InitializeConnectionAsync(_waitForAbortApplication);
+
+ _pair.Application.Output.Complete(new ConnectionResetException(string.Empty));
+
+ var result = await _pair.Application.Input.ReadAsync();
+ Assert.True(result.IsCompleted);
+ Assert.DoesNotContain(_logger.Messages, m => m.Exception is ConnectionResetException);
+ }
+
private async Task InitializeConnectionAsync(RequestDelegate application)
{
_connectionTask = _connection.ProcessAsync(new DummyApplication(application));
@@ -1931,11 +2203,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
return SendAsync(frame.Raw);
}
- private Task SendUnknownFrameTypeAsync(int streamId)
+ private Task SendUnknownFrameTypeAsync(int streamId, int frameType)
{
var frame = new Http2Frame();
frame.StreamId = streamId;
- frame.Type = (Http2FrameType)42;
+ frame.Type = (Http2FrameType)frameType;
frame.Length = 0;
return SendAsync(frame.Raw);
}
@@ -1996,10 +2268,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
private Task WaitForConnectionStopAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
{
- return WaitForConnectionErrorAsync(expectedLastStreamId, Http2ErrorCode.NO_ERROR, ignoreNonGoAwayFrames);
+ return WaitForConnectionErrorAsync(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR, expectedErrorMessage: null);
}
- private async Task WaitForConnectionErrorAsync(int expectedLastStreamId, Http2ErrorCode expectedErrorCode, bool ignoreNonGoAwayFrames)
+ private async Task WaitForConnectionErrorAsync(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
+ where TException : Exception
{
var frame = await ReceiveFrameAsync();
@@ -2018,11 +2291,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Equal(expectedLastStreamId, frame.GoAwayLastStreamId);
Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
+ if (expectedErrorMessage != null)
+ {
+ var message = Assert.Single(_logger.Messages, m => m.Exception is TException);
+ Assert.Contains(expectedErrorMessage, message.Exception.Message);
+ }
+
await _connectionTask;
_pair.Application.Output.Complete();
}
- private async Task WaitForStreamErrorAsync(int expectedStreamId, Http2ErrorCode expectedErrorCode, bool ignoreNonRstStreamFrames)
+ private async Task WaitForStreamErrorAsync(bool ignoreNonRstStreamFrames, int expectedStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
{
var frame = await ReceiveFrameAsync();
@@ -2039,6 +2318,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Equal(0, frame.Flags);
Assert.Equal(expectedStreamId, frame.StreamId);
Assert.Equal(expectedErrorCode, frame.RstStreamErrorCode);
+
+ if (expectedErrorMessage != null)
+ {
+ var message = Assert.Single(_logger.Messages, m => m.Exception is Http2StreamErrorException);
+ Assert.Contains(expectedErrorMessage, message.Exception.Message);
+ }
}
private void VerifyDecodedRequestHeaders(IEnumerable> expectedHeaders)