HTTP/2: add exception error messages and log them.

This commit is contained in:
Cesar Blum Silveira 2017-10-12 11:23:57 -07:00 committed by GitHub
parent fdb4184dbf
commit deed6c9780
9 changed files with 979 additions and 189 deletions

View File

@ -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) { }
}
}

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -384,4 +384,82 @@
<data name="HPackErrorIncompleteHeaderBlock" xml:space="preserve">
<value>The header block was incomplete and could not be fully decoded.</value>
</data>
</root>
<data name="Http2ErrorStreamIdEven" xml:space="preserve">
<value>The client sent a {frameType} frame with even stream ID {streamId}.</value>
</data>
<data name="Http2ErrorPushPromiseReceived" xml:space="preserve">
<value>The client sent a A PUSH_PROMISE frame.</value>
</data>
<data name="Http2ErrorHeadersInterleaved" xml:space="preserve">
<value>The client sent a {frameType} frame to stream ID {streamId} before signaling of the header block for stream ID {headersStreamId}.</value>
</data>
<data name="Http2ErrorStreamIdZero" xml:space="preserve">
<value>The client sent a {frameType} frame with stream ID 0.</value>
</data>
<data name="Http2ErrorStreamIdNotZero" xml:space="preserve">
<value>The client sent a {frameType} frame with stream ID different than 0.</value>
</data>
<data name="Http2ErrorPaddingTooLong" xml:space="preserve">
<value>The client sent a {frameType} frame with padding longer than or with the same length as the sent data.</value>
</data>
<data name="Http2ErrorStreamClosed" xml:space="preserve">
<value>The client sent a {frameType} frame to closed stream ID {streamId}.</value>
</data>
<data name="Http2ErrorStreamHalfClosedRemote" xml:space="preserve">
<value>The client sent a {frameType} frame to stream ID {streamId} which is in the "half-closed (remote) state".</value>
</data>
<data name="Http2ErrorStreamSelfDependency" xml:space="preserve">
<value>The client sent a {frameType} frame with dependency information that would cause stream ID {streamId} to depend on itself.</value>
</data>
<data name="Http2ErrorUnexpectedFrameLength" xml:space="preserve">
<value>The client sent a {frameType} frame with length different than {expectedLength}.</value>
</data>
<data name="Http2ErrorSettingsLengthNotMultipleOfSix" xml:space="preserve">
<value>The client sent a SETTINGS frame with a length that is not a multiple of 6.</value>
</data>
<data name="Http2ErrorSettingsAckLengthNotZero" xml:space="preserve">
<value>The client sent a SETTINGS frame with ACK set and length different than 0.</value>
</data>
<data name="Http2ErrorSettingsParameterOutOfRange" xml:space="preserve">
<value>The client sent a SETTINGS frame with a value for parameter {parameter} that is out of range.</value>
</data>
<data name="Http2ErrorWindowUpdateIncrementZero" xml:space="preserve">
<value>The client sent a WINDOW_UPDATE frame with a window size increment of 0.</value>
</data>
<data name="Http2ErrorContinuationWithNoHeaders" xml:space="preserve">
<value>The client sent a CONTINUATION frame not preceded by a HEADERS frame.</value>
</data>
<data name="Http2ErrorStreamIdle" xml:space="preserve">
<value>The client sent a {frameType} frame to idle stream ID {streamId}.</value>
</data>
<data name="Http2ErrorTrailersContainPseudoHeaderField" xml:space="preserve">
<value>The client sent trailers containing one or more pseudo-header fields.</value>
</data>
<data name="Http2ErrorHeaderNameUppercase" xml:space="preserve">
<value>The client sent a header with uppercase characters in its name.</value>
</data>
<data name="Http2ErrorTrailerNameUppercase" xml:space="preserve">
<value>The client sent a trailer with uppercase characters in its name.</value>
</data>
<data name="Http2ErrorHeadersWithTrailersNoEndStream" xml:space="preserve">
<value>The client sent a HEADERS frame containing trailers without setting the END_STREAM flag.</value>
</data>
<data name="Http2ErrorMissingMandatoryPseudoHeaderFields" xml:space="preserve">
<value>Request headers missing one or more mandatory pseudo-header fields.</value>
</data>
<data name="Http2ErrorPseudoHeaderFieldAfterRegularHeaders" xml:space="preserve">
<value>Pseudo-header field found in request headers after regular header fields.</value>
</data>
<data name="Http2ErrorUnknownPseudoHeaderField" xml:space="preserve">
<value>Request headers contain unknown pseudo-header field.</value>
</data>
<data name="Http2ErrorResponsePseudoHeaderField" xml:space="preserve">
<value>Request headers contain response-specific pseudo-header field.</value>
</data>
<data name="Http2ErrorDuplicatePseudoHeaderField" xml:space="preserve">
<value>Request headers contain duplicate pseudo-header field.</value>
</data>
<data name="Http2ErrorConnectionSpecificHeaderField" xml:space="preserve">
<value>Request headers contain connection-specific header field.</value>
</data>
</root>

View File

@ -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<TContext>(IHttpApplication<TContext> 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<TContext>(IHttpApplication<TContext> 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);
}
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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<ILogger, string, string, Exception> _responseMinimumDataRateNotSatisfied =
LoggerMessage.Define<string, string>(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<ILogger, string, Exception> _http2ConnectionError =
LoggerMessage.Define<string>(LogLevel.Information, 29, @"Connection id ""{ConnectionId}"": HTTP/2 connection error.");
private static readonly Action<ILogger, string, Exception> _http2StreamError =
LoggerMessage.Define<string>(LogLevel.Information, 30, @"Connection id ""{ConnectionId}"": HTTP/2 stream error.");
private static readonly Action<ILogger, string, int, Exception> _hpackDecodingError =
LoggerMessage.Define<string, int>(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<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
=> _logger.Log(logLevel, eventId, state, exception, formatter);

View File

@ -1256,6 +1256,370 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
internal static string FormatHPackErrorIncompleteHeaderBlock()
=> GetString("HPackErrorIncompleteHeaderBlock");
/// <summary>
/// The client sent a {frameType} frame with even stream ID {streamId}.
/// </summary>
internal static string Http2ErrorStreamIdEven
{
get => GetString("Http2ErrorStreamIdEven");
}
/// <summary>
/// The client sent a {frameType} frame with even stream ID {streamId}.
/// </summary>
internal static string FormatHttp2ErrorStreamIdEven(object frameType, object streamId)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamIdEven", "frameType", "streamId"), frameType, streamId);
/// <summary>
/// The client sent a A PUSH_PROMISE frame.
/// </summary>
internal static string Http2ErrorPushPromiseReceived
{
get => GetString("Http2ErrorPushPromiseReceived");
}
/// <summary>
/// The client sent a A PUSH_PROMISE frame.
/// </summary>
internal static string FormatHttp2ErrorPushPromiseReceived()
=> GetString("Http2ErrorPushPromiseReceived");
/// <summary>
/// The client sent a {frameType} frame to stream ID {streamId} before signaling of the header block for stream ID {headersStreamId}.
/// </summary>
internal static string Http2ErrorHeadersInterleaved
{
get => GetString("Http2ErrorHeadersInterleaved");
}
/// <summary>
/// The client sent a {frameType} frame to stream ID {streamId} before signaling of the header block for stream ID {headersStreamId}.
/// </summary>
internal static string FormatHttp2ErrorHeadersInterleaved(object frameType, object streamId, object headersStreamId)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorHeadersInterleaved", "frameType", "streamId", "headersStreamId"), frameType, streamId, headersStreamId);
/// <summary>
/// The client sent a {frameType} frame with stream ID 0.
/// </summary>
internal static string Http2ErrorStreamIdZero
{
get => GetString("Http2ErrorStreamIdZero");
}
/// <summary>
/// The client sent a {frameType} frame with stream ID 0.
/// </summary>
internal static string FormatHttp2ErrorStreamIdZero(object frameType)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamIdZero", "frameType"), frameType);
/// <summary>
/// The client sent a {frameType} frame with stream ID different than 0.
/// </summary>
internal static string Http2ErrorStreamIdNotZero
{
get => GetString("Http2ErrorStreamIdNotZero");
}
/// <summary>
/// The client sent a {frameType} frame with stream ID different than 0.
/// </summary>
internal static string FormatHttp2ErrorStreamIdNotZero(object frameType)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamIdNotZero", "frameType"), frameType);
/// <summary>
/// The client sent a {frameType} frame with padding longer than or with the same length as the sent data.
/// </summary>
internal static string Http2ErrorPaddingTooLong
{
get => GetString("Http2ErrorPaddingTooLong");
}
/// <summary>
/// The client sent a {frameType} frame with padding longer than or with the same length as the sent data.
/// </summary>
internal static string FormatHttp2ErrorPaddingTooLong(object frameType)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorPaddingTooLong", "frameType"), frameType);
/// <summary>
/// The client sent a {frameType} frame to closed stream ID {streamId}.
/// </summary>
internal static string Http2ErrorStreamClosed
{
get => GetString("Http2ErrorStreamClosed");
}
/// <summary>
/// The client sent a {frameType} frame to closed stream ID {streamId}.
/// </summary>
internal static string FormatHttp2ErrorStreamClosed(object frameType, object streamId)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamClosed", "frameType", "streamId"), frameType, streamId);
/// <summary>
/// The client sent a {frameType} frame to stream ID {streamId} which is in the "half-closed (remote) state".
/// </summary>
internal static string Http2ErrorStreamHalfClosedRemote
{
get => GetString("Http2ErrorStreamHalfClosedRemote");
}
/// <summary>
/// The client sent a {frameType} frame to stream ID {streamId} which is in the "half-closed (remote) state".
/// </summary>
internal static string FormatHttp2ErrorStreamHalfClosedRemote(object frameType, object streamId)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamHalfClosedRemote", "frameType", "streamId"), frameType, streamId);
/// <summary>
/// The client sent a {frameType} frame with dependency information that would cause stream ID {streamId} to depend on itself.
/// </summary>
internal static string Http2ErrorStreamSelfDependency
{
get => GetString("Http2ErrorStreamSelfDependency");
}
/// <summary>
/// The client sent a {frameType} frame with dependency information that would cause stream ID {streamId} to depend on itself.
/// </summary>
internal static string FormatHttp2ErrorStreamSelfDependency(object frameType, object streamId)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamSelfDependency", "frameType", "streamId"), frameType, streamId);
/// <summary>
/// The client sent a {frameType} frame with length different than {expectedLength}.
/// </summary>
internal static string Http2ErrorUnexpectedFrameLength
{
get => GetString("Http2ErrorUnexpectedFrameLength");
}
/// <summary>
/// The client sent a {frameType} frame with length different than {expectedLength}.
/// </summary>
internal static string FormatHttp2ErrorUnexpectedFrameLength(object frameType, object expectedLength)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorUnexpectedFrameLength", "frameType", "expectedLength"), frameType, expectedLength);
/// <summary>
/// The client sent a SETTINGS frame with a length that is not a multiple of 6.
/// </summary>
internal static string Http2ErrorSettingsLengthNotMultipleOfSix
{
get => GetString("Http2ErrorSettingsLengthNotMultipleOfSix");
}
/// <summary>
/// The client sent a SETTINGS frame with a length that is not a multiple of 6.
/// </summary>
internal static string FormatHttp2ErrorSettingsLengthNotMultipleOfSix()
=> GetString("Http2ErrorSettingsLengthNotMultipleOfSix");
/// <summary>
/// The client sent a SETTINGS frame with ACK set and length different than 0.
/// </summary>
internal static string Http2ErrorSettingsAckLengthNotZero
{
get => GetString("Http2ErrorSettingsAckLengthNotZero");
}
/// <summary>
/// The client sent a SETTINGS frame with ACK set and length different than 0.
/// </summary>
internal static string FormatHttp2ErrorSettingsAckLengthNotZero()
=> GetString("Http2ErrorSettingsAckLengthNotZero");
/// <summary>
/// The client sent a SETTINGS frame with a value for parameter {parameter} that is out of range.
/// </summary>
internal static string Http2ErrorSettingsParameterOutOfRange
{
get => GetString("Http2ErrorSettingsParameterOutOfRange");
}
/// <summary>
/// The client sent a SETTINGS frame with a value for parameter {parameter} that is out of range.
/// </summary>
internal static string FormatHttp2ErrorSettingsParameterOutOfRange(object parameter)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorSettingsParameterOutOfRange", "parameter"), parameter);
/// <summary>
/// The client sent a WINDOW_UPDATE frame with a window size increment of 0.
/// </summary>
internal static string Http2ErrorWindowUpdateIncrementZero
{
get => GetString("Http2ErrorWindowUpdateIncrementZero");
}
/// <summary>
/// The client sent a WINDOW_UPDATE frame with a window size increment of 0.
/// </summary>
internal static string FormatHttp2ErrorWindowUpdateIncrementZero()
=> GetString("Http2ErrorWindowUpdateIncrementZero");
/// <summary>
/// The client sent a CONTINUATION frame not preceded by a HEADERS frame.
/// </summary>
internal static string Http2ErrorContinuationWithNoHeaders
{
get => GetString("Http2ErrorContinuationWithNoHeaders");
}
/// <summary>
/// The client sent a CONTINUATION frame not preceded by a HEADERS frame.
/// </summary>
internal static string FormatHttp2ErrorContinuationWithNoHeaders()
=> GetString("Http2ErrorContinuationWithNoHeaders");
/// <summary>
/// The client sent a {frameType} frame to idle stream ID {streamId}.
/// </summary>
internal static string Http2ErrorStreamIdle
{
get => GetString("Http2ErrorStreamIdle");
}
/// <summary>
/// The client sent a {frameType} frame to idle stream ID {streamId}.
/// </summary>
internal static string FormatHttp2ErrorStreamIdle(object frameType, object streamId)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorStreamIdle", "frameType", "streamId"), frameType, streamId);
/// <summary>
/// The client sent trailers containing one or more pseudo-header fields.
/// </summary>
internal static string Http2ErrorTrailersContainPseudoHeaderField
{
get => GetString("Http2ErrorTrailersContainPseudoHeaderField");
}
/// <summary>
/// The client sent trailers containing one or more pseudo-header fields.
/// </summary>
internal static string FormatHttp2ErrorTrailersContainPseudoHeaderField()
=> GetString("Http2ErrorTrailersContainPseudoHeaderField");
/// <summary>
/// The client sent a header with uppercase characters in its name.
/// </summary>
internal static string Http2ErrorHeaderNameUppercase
{
get => GetString("Http2ErrorHeaderNameUppercase");
}
/// <summary>
/// The client sent a header with uppercase characters in its name.
/// </summary>
internal static string FormatHttp2ErrorHeaderNameUppercase()
=> GetString("Http2ErrorHeaderNameUppercase");
/// <summary>
/// The client sent a trailer with uppercase characters in its name.
/// </summary>
internal static string Http2ErrorTrailerNameUppercase
{
get => GetString("Http2ErrorTrailerNameUppercase");
}
/// <summary>
/// The client sent a trailer with uppercase characters in its name.
/// </summary>
internal static string FormatHttp2ErrorTrailerNameUppercase()
=> GetString("Http2ErrorTrailerNameUppercase");
/// <summary>
/// The client sent a HEADERS frame containing trailers without setting the END_STREAM flag.
/// </summary>
internal static string Http2ErrorHeadersWithTrailersNoEndStream
{
get => GetString("Http2ErrorHeadersWithTrailersNoEndStream");
}
/// <summary>
/// The client sent a HEADERS frame containing trailers without setting the END_STREAM flag.
/// </summary>
internal static string FormatHttp2ErrorHeadersWithTrailersNoEndStream()
=> GetString("Http2ErrorHeadersWithTrailersNoEndStream");
/// <summary>
/// Request headers missing one or more mandatory pseudo-header fields.
/// </summary>
internal static string Http2ErrorMissingMandatoryPseudoHeaderFields
{
get => GetString("Http2ErrorMissingMandatoryPseudoHeaderFields");
}
/// <summary>
/// Request headers missing one or more mandatory pseudo-header fields.
/// </summary>
internal static string FormatHttp2ErrorMissingMandatoryPseudoHeaderFields()
=> GetString("Http2ErrorMissingMandatoryPseudoHeaderFields");
/// <summary>
/// Pseudo-header field found in request headers after regular header fields.
/// </summary>
internal static string Http2ErrorPseudoHeaderFieldAfterRegularHeaders
{
get => GetString("Http2ErrorPseudoHeaderFieldAfterRegularHeaders");
}
/// <summary>
/// Pseudo-header field found in request headers after regular header fields.
/// </summary>
internal static string FormatHttp2ErrorPseudoHeaderFieldAfterRegularHeaders()
=> GetString("Http2ErrorPseudoHeaderFieldAfterRegularHeaders");
/// <summary>
/// Request headers contain unknown pseudo-header field.
/// </summary>
internal static string Http2ErrorUnknownPseudoHeaderField
{
get => GetString("Http2ErrorUnknownPseudoHeaderField");
}
/// <summary>
/// Request headers contain unknown pseudo-header field.
/// </summary>
internal static string FormatHttp2ErrorUnknownPseudoHeaderField()
=> GetString("Http2ErrorUnknownPseudoHeaderField");
/// <summary>
/// Request headers contain response-specific pseudo-header field.
/// </summary>
internal static string Http2ErrorResponsePseudoHeaderField
{
get => GetString("Http2ErrorResponsePseudoHeaderField");
}
/// <summary>
/// Request headers contain response-specific pseudo-header field.
/// </summary>
internal static string FormatHttp2ErrorResponsePseudoHeaderField()
=> GetString("Http2ErrorResponsePseudoHeaderField");
/// <summary>
/// Request headers contain duplicate pseudo-header field.
/// </summary>
internal static string Http2ErrorDuplicatePseudoHeaderField
{
get => GetString("Http2ErrorDuplicatePseudoHeaderField");
}
/// <summary>
/// Request headers contain duplicate pseudo-header field.
/// </summary>
internal static string FormatHttp2ErrorDuplicatePseudoHeaderField()
=> GetString("Http2ErrorDuplicatePseudoHeaderField");
/// <summary>
/// Request headers contain connection-specific header field.
/// </summary>
internal static string Http2ErrorConnectionSpecificHeaderField
{
get => GetString("Http2ErrorConnectionSpecificHeaderField");
}
/// <summary>
/// Request headers contain connection-specific header field.
/// </summary>
internal static string FormatHttp2ErrorConnectionSpecificHeaderField()
=> GetString("Http2ErrorConnectionSpecificHeaderField");
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<HPackDecodingException>(
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<Http2ConnectionErrorException>(
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<string, string>(":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<string, string>(":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<KeyValuePair<string, string>> 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<KeyValuePair<string, string>> 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<KeyValuePair<string, string>> headers)
{
return HEADERS_Received_InvalidHeaderFields_StreamError(headers);
return HEADERS_Received_InvalidHeaderFields_StreamError(headers, expectedErrorMessage: CoreStrings.Http2ErrorPseudoHeaderFieldAfterRegularHeaders);
}
private async Task HEADERS_Received_InvalidHeaderFields_StreamError(IEnumerable<KeyValuePair<string, string>> headers)
private async Task HEADERS_Received_InvalidHeaderFields_StreamError(IEnumerable<KeyValuePair<string, string>> 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<Http2ConnectionErrorException>(
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<string, string>("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<string, string>("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<string, string>(":method", "GET"),
new KeyValuePair<string, string>(":path", "/"),
new KeyValuePair<string, string>(":scheme", "http"),
new KeyValuePair<string, string>("te", "trailers, deflate")
new KeyValuePair<string, string>("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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<HPackDecodingException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Http2ConnectionErrorException>(
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<Exception>(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR, expectedErrorMessage: null);
}
private async Task WaitForConnectionErrorAsync(int expectedLastStreamId, Http2ErrorCode expectedErrorCode, bool ignoreNonGoAwayFrames)
private async Task WaitForConnectionErrorAsync<TException>(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<KeyValuePair<string, string>> expectedHeaders)