HTTP/2: connection error when receiving frames disallowed by stream states.
This commit is contained in:
parent
bb9840a552
commit
1b1137b880
|
|
@ -14,7 +14,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
public class Http1OutputProducer : IHttpOutputProducer
|
||||
{
|
||||
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
||||
private static readonly ArraySegment<byte> _continueBytes = new ArraySegment<byte>(Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n"));
|
||||
private static readonly byte[] _bytesHttpVersion11 = Encoding.ASCII.GetBytes("HTTP/1.1 ");
|
||||
private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n");
|
||||
|
|
@ -71,7 +70,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
|
||||
public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return WriteAsync(_emptyData, cancellationToken);
|
||||
return WriteAsync(Constants.EmptyData, cancellationToken);
|
||||
}
|
||||
|
||||
public void Write<T>(Action<WritableBuffer, T> callback, T state)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
private readonly Http2Frame _incomingFrame = new Http2Frame();
|
||||
|
||||
private Http2Stream _currentHeadersStream;
|
||||
private int _lastStreamId;
|
||||
private int _highestOpenedStreamId;
|
||||
|
||||
private bool _stopping;
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
stream.Abort(error);
|
||||
}
|
||||
|
||||
await _frameWriter.WriteGoAwayAsync(_lastStreamId, errorCode);
|
||||
await _frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, errorCode);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -247,16 +247,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream) && !stream.HasReceivedEndStream)
|
||||
ThrowIfIncomingFrameSentToIdleStream();
|
||||
|
||||
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream) && !stream.EndStreamReceived)
|
||||
{
|
||||
return stream.OnDataAsync(_incomingFrame.DataPayload,
|
||||
endStream: (_incomingFrame.DataFlags & Http2DataFrameFlags.END_STREAM) == Http2DataFrameFlags.END_STREAM);
|
||||
}
|
||||
|
||||
return _frameWriter.WriteRstStreamAsync(_incomingFrame.StreamId, Http2ErrorCode.STREAM_CLOSED);
|
||||
// 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
|
||||
// the stream was closed with RST_STREAM or implicitly, but the spec also says
|
||||
// in http://httpwg.org/specs/rfc7540.html#rfc.section.5.4.1 that
|
||||
//
|
||||
// An endpoint can end a connection at any time. In particular, an endpoint MAY
|
||||
// choose to treat a stream error as a connection error.
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
||||
private Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application)
|
||||
private async Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application)
|
||||
{
|
||||
if (_currentHeadersStream != null)
|
||||
{
|
||||
|
|
@ -278,33 +299,66 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
_currentHeadersStream = new Http2Stream<TContext>(application, new Http2StreamContext
|
||||
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream))
|
||||
{
|
||||
ConnectionId = ConnectionId,
|
||||
StreamId = _incomingFrame.StreamId,
|
||||
ServiceContext = _context.ServiceContext,
|
||||
ConnectionFeatures = _context.ConnectionFeatures,
|
||||
PipeFactory = _context.PipeFactory,
|
||||
LocalEndPoint = _context.LocalEndPoint,
|
||||
RemoteEndPoint = _context.RemoteEndPoint,
|
||||
StreamLifetimeHandler = this,
|
||||
FrameWriter = _frameWriter
|
||||
});
|
||||
_currentHeadersStream.ExpectData = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == 0;
|
||||
_currentHeadersStream.Reset();
|
||||
|
||||
_streams[_incomingFrame.StreamId] = _currentHeadersStream;
|
||||
|
||||
_hpackDecoder.Decode(_incomingFrame.HeadersPayload, _currentHeadersStream.RequestHeaders);
|
||||
|
||||
if ((_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS)
|
||||
{
|
||||
_lastStreamId = _incomingFrame.StreamId;
|
||||
_ = _currentHeadersStream.ProcessRequestsAsync();
|
||||
_currentHeadersStream = null;
|
||||
// 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 (stream.EndStreamReceived)
|
||||
{
|
||||
throw new Http2ConnectionErrorException(Http2ErrorCode.STREAM_CLOSED);
|
||||
}
|
||||
// TODO: trailers
|
||||
}
|
||||
else if (_incomingFrame.StreamId <= _highestOpenedStreamId)
|
||||
{
|
||||
// http://httpwg.org/specs/rfc7540.html#rfc.section.5.1.1
|
||||
//
|
||||
// The first use of a new stream identifier implicitly closes all streams in the "idle"
|
||||
// state that might have been initiated by that peer with a lower-valued stream identifier.
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start a new stream
|
||||
_currentHeadersStream = new Http2Stream<TContext>(application, new Http2StreamContext
|
||||
{
|
||||
ConnectionId = ConnectionId,
|
||||
StreamId = _incomingFrame.StreamId,
|
||||
ServiceContext = _context.ServiceContext,
|
||||
ConnectionFeatures = _context.ConnectionFeatures,
|
||||
PipeFactory = _context.PipeFactory,
|
||||
LocalEndPoint = _context.LocalEndPoint,
|
||||
RemoteEndPoint = _context.RemoteEndPoint,
|
||||
StreamLifetimeHandler = this,
|
||||
FrameWriter = _frameWriter
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
if ((_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == Http2HeadersFrameFlags.END_STREAM)
|
||||
{
|
||||
await _currentHeadersStream.OnDataAsync(Constants.EmptyData, endStream: true);
|
||||
}
|
||||
|
||||
_currentHeadersStream.Reset();
|
||||
|
||||
_streams[_incomingFrame.StreamId] = _currentHeadersStream;
|
||||
|
||||
_hpackDecoder.Decode(_incomingFrame.HeadersPayload, _currentHeadersStream.RequestHeaders);
|
||||
|
||||
if ((_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS)
|
||||
{
|
||||
_highestOpenedStreamId = _incomingFrame.StreamId;
|
||||
_ = _currentHeadersStream.ProcessRequestsAsync();
|
||||
_currentHeadersStream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task ProcessPriorityFrameAsync()
|
||||
|
|
@ -349,14 +403,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
|
||||
}
|
||||
|
||||
ThrowIfIncomingFrameSentToIdleStream();
|
||||
|
||||
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream))
|
||||
{
|
||||
stream.Abort(error: null);
|
||||
}
|
||||
else if (_incomingFrame.StreamId > _lastStreamId)
|
||||
{
|
||||
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
|
@ -449,16 +501,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
|
||||
}
|
||||
|
||||
if (_incomingFrame.StreamId == 0)
|
||||
ThrowIfIncomingFrameSentToIdleStream();
|
||||
|
||||
if (_incomingFrame.WindowUpdateSizeIncrement == 0)
|
||||
{
|
||||
if (_incomingFrame.WindowUpdateSizeIncrement == 0)
|
||||
if (_incomingFrame.StreamId == 0)
|
||||
{
|
||||
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_incomingFrame.WindowUpdateSizeIncrement == 0)
|
||||
else
|
||||
{
|
||||
return _frameWriter.WriteRstStreamAsync(_incomingFrame.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
|
||||
}
|
||||
|
|
@ -478,7 +529,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
if ((_incomingFrame.ContinuationFlags & Http2ContinuationFrameFlags.END_HEADERS) == Http2ContinuationFrameFlags.END_HEADERS)
|
||||
{
|
||||
_lastStreamId = _currentHeadersStream.StreamId;
|
||||
_highestOpenedStreamId = _currentHeadersStream.StreamId;
|
||||
_ = _currentHeadersStream.ProcessRequestsAsync();
|
||||
_currentHeadersStream = null;
|
||||
}
|
||||
|
|
@ -496,6 +547,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ThrowIfIncomingFrameSentToIdleStream()
|
||||
{
|
||||
// http://httpwg.org/specs/rfc7540.html#rfc.section.5.1
|
||||
// 5.1. Stream states
|
||||
// ...
|
||||
// idle:
|
||||
// ...
|
||||
// Receiving any frame other than HEADERS or PRIORITY on a stream in this state MUST be
|
||||
// treated as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
//
|
||||
// If the stream ID in the incoming frame is higher than the highest opened stream ID so
|
||||
// far, then the incoming frame's target stream is in the idle state, which is the implicit
|
||||
// initial state for all streams.
|
||||
if (_incomingFrame.StreamId > _highestOpenedStreamId)
|
||||
{
|
||||
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void IHttp2StreamLifetimeHandler.OnStreamCompleted(int streamId)
|
||||
{
|
||||
_streams.TryRemove(streamId, out _);
|
||||
|
|
|
|||
|
|
@ -7,16 +7,13 @@ using System.IO.Pipelines;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||
{
|
||||
public class Http2FrameWriter : IHttp2FrameWriter
|
||||
{
|
||||
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
||||
|
||||
private readonly Http2Frame _outgoingFrame = new Http2Frame();
|
||||
private readonly object _writeLock = new object();
|
||||
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
|
||||
|
|
@ -48,7 +45,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public Task FlushAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return WriteAsync(_emptyData);
|
||||
return WriteAsync(Constants.EmptyData);
|
||||
}
|
||||
|
||||
public Task Write100ContinueAsync(int streamId)
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
HttpRequestHeaders headers,
|
||||
Http2Stream context)
|
||||
{
|
||||
if (!context.ExpectData)
|
||||
if (context.EndStreamReceived)
|
||||
{
|
||||
return MessageBody.ZeroContentLengthClose;
|
||||
return ZeroContentLengthClose;
|
||||
}
|
||||
|
||||
return new ForHttp2(context);
|
||||
|
|
|
|||
|
|
@ -6,13 +6,12 @@ using System.IO.Pipelines;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||
{
|
||||
public class Http2OutputProducer : IHttpOutputProducer
|
||||
{
|
||||
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
||||
|
||||
private readonly int _streamId;
|
||||
private readonly IHttp2FrameWriter _frameWriter;
|
||||
|
||||
|
|
@ -47,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public Task WriteStreamSuffixAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return _frameWriter.WriteDataAsync(_streamId, _emptyData, endStream: true, cancellationToken: cancellationToken);
|
||||
return _frameWriter.WriteDataAsync(_streamId, Constants.EmptyData, endStream: true, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public void WriteResponseHeaders(int statusCode, string ReasonPhrase, HttpResponseHeaders responseHeaders)
|
||||
|
|
|
|||
|
|
@ -24,12 +24,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public int StreamId => _context.StreamId;
|
||||
|
||||
public bool HasReceivedEndStream { get; private set; }
|
||||
public bool EndStreamReceived { get; private set; }
|
||||
|
||||
protected IHttp2StreamLifetimeHandler StreamLifetimeHandler => _context.StreamLifetimeHandler;
|
||||
|
||||
public bool ExpectData { get; set; }
|
||||
|
||||
public override bool IsUpgradableRequest => false;
|
||||
|
||||
protected override void OnReset()
|
||||
|
|
@ -91,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
if (endStream)
|
||||
{
|
||||
HasReceivedEndStream = true;
|
||||
EndStreamReceived = true;
|
||||
RequestBodyPipe.Writer.Complete();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,5 +32,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
public const string ServerName = "Kestrel";
|
||||
|
||||
public static readonly TimeSpan RequestBodyDrainTimeout = TimeSpan.FromSeconds(5);
|
||||
|
||||
public static readonly ArraySegment<byte> EmptyData = new ArraySegment<byte>(new byte[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -467,33 +467,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DATA_Received_StreamIdle_StreamError()
|
||||
public async Task DATA_Received_StreamIdle_ConnectionError()
|
||||
{
|
||||
await InitializeConnectionAsync(_noopApplication);
|
||||
|
||||
await SendDataAsync(1, _helloWorldBytes, endStream: false);
|
||||
|
||||
await WaitForStreamErrorAsync(expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonRstStreamFrames: false);
|
||||
|
||||
await StopConnectionAsync(expectedLastStreamId: 0, ignoreNonGoAwayFrames: false);
|
||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DATA_Received_StreamHalfClosedRemote_StreamError()
|
||||
public async Task DATA_Received_StreamHalfClosedRemote_ConnectionError()
|
||||
{
|
||||
await InitializeConnectionAsync(_echoWaitForAbortApplication);
|
||||
// Use _waitForAbortApplication so we know the stream will still be active when we send the illegal DATA frame
|
||||
await InitializeConnectionAsync(_waitForAbortApplication);
|
||||
|
||||
await StartStreamAsync(1, _postRequestHeaders, endStream: false);
|
||||
await SendDataAsync(1, _helloBytes, endStream: true);
|
||||
await SendDataAsync(1, _worldBytes, endStream: true);
|
||||
await StartStreamAsync(1, _postRequestHeaders, endStream: true);
|
||||
|
||||
await WaitForStreamErrorAsync(expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonRstStreamFrames: true);
|
||||
await SendDataAsync(1, _helloWorldBytes, endStream: false);
|
||||
|
||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: true);
|
||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DATA_Received_StreamClosed_StreamError()
|
||||
public async Task DATA_Received_StreamClosed_ConnectionError()
|
||||
{
|
||||
await InitializeConnectionAsync(_noopApplication);
|
||||
|
||||
|
|
@ -510,13 +507,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
|
||||
await SendDataAsync(1, _helloWorldBytes, endStream: false);
|
||||
|
||||
await WaitForStreamErrorAsync(expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonRstStreamFrames: false);
|
||||
|
||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DATA_Received_StreamClosedImplicitly_StreamError()
|
||||
public async Task DATA_Received_StreamClosedImplicitly_ConnectionError()
|
||||
{
|
||||
// http://httpwg.org/specs/rfc7540.html#rfc.section.5.1.1
|
||||
//
|
||||
|
|
@ -540,9 +535,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
|
||||
await SendDataAsync(1, _helloWorldBytes, endStream: true);
|
||||
|
||||
await WaitForStreamErrorAsync(expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonRstStreamFrames: false);
|
||||
|
||||
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
|
||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 3, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -655,6 +648,63 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HEADERS_Received_StreamClosed_ConnectionError()
|
||||
{
|
||||
await InitializeConnectionAsync(_noopApplication);
|
||||
|
||||
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
|
||||
|
||||
await ExpectAsync(Http2FrameType.HEADERS,
|
||||
withLength: 55,
|
||||
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
|
||||
withStreamId: 1);
|
||||
await ExpectAsync(Http2FrameType.DATA,
|
||||
withLength: 0,
|
||||
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
||||
withStreamId: 1);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HEADERS_Received_StreamHalfClosedRemote_ConnectionError()
|
||||
{
|
||||
// Use _waitForAbortApplication so we know the stream will still be active when we send the illegal DATA frame
|
||||
await InitializeConnectionAsync(_waitForAbortApplication);
|
||||
|
||||
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
|
||||
|
||||
await SendHeadersAsync(1, Http2HeadersFrameFlags.NONE, _browserRequestHeaders);
|
||||
|
||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HEADERS_Received_StreamClosedImplicitly_ConnectionError()
|
||||
{
|
||||
await InitializeConnectionAsync(_noopApplication);
|
||||
|
||||
await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
|
||||
|
||||
await ExpectAsync(Http2FrameType.HEADERS,
|
||||
withLength: 55,
|
||||
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
|
||||
withStreamId: 3);
|
||||
await ExpectAsync(Http2FrameType.DATA,
|
||||
withLength: 0,
|
||||
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
||||
withStreamId: 3);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
|
|
@ -811,6 +861,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RST_STREAM_Received_StreamIdle_ConnectionError()
|
||||
{
|
||||
await InitializeConnectionAsync(_noopApplication);
|
||||
|
||||
await SendRstStreamAsync(1);
|
||||
|
||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(3)]
|
||||
[InlineData(5)]
|
||||
|
|
@ -1077,6 +1137,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WINDOW_UPDATE_Received_StreamIdle_ConnectionError()
|
||||
{
|
||||
await InitializeConnectionAsync(_waitForAbortApplication);
|
||||
|
||||
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()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue