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
|
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 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[] _bytesHttpVersion11 = Encoding.ASCII.GetBytes("HTTP/1.1 ");
|
||||||
private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n");
|
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))
|
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)
|
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 readonly Http2Frame _incomingFrame = new Http2Frame();
|
||||||
|
|
||||||
private Http2Stream _currentHeadersStream;
|
private Http2Stream _currentHeadersStream;
|
||||||
private int _lastStreamId;
|
private int _highestOpenedStreamId;
|
||||||
|
|
||||||
private bool _stopping;
|
private bool _stopping;
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
stream.Abort(error);
|
stream.Abort(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _frameWriter.WriteGoAwayAsync(_lastStreamId, errorCode);
|
await _frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, errorCode);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -247,16 +247,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
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,
|
return stream.OnDataAsync(_incomingFrame.DataPayload,
|
||||||
endStream: (_incomingFrame.DataFlags & Http2DataFrameFlags.END_STREAM) == Http2DataFrameFlags.END_STREAM);
|
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)
|
if (_currentHeadersStream != null)
|
||||||
{
|
{
|
||||||
|
|
@ -278,33 +299,66 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentHeadersStream = new Http2Stream<TContext>(application, new Http2StreamContext
|
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream))
|
||||||
{
|
{
|
||||||
ConnectionId = ConnectionId,
|
// http://httpwg.org/specs/rfc7540.html#rfc.section.5.1
|
||||||
StreamId = _incomingFrame.StreamId,
|
//
|
||||||
ServiceContext = _context.ServiceContext,
|
// ...an endpoint that receives any frames after receiving a frame with the
|
||||||
ConnectionFeatures = _context.ConnectionFeatures,
|
// END_STREAM flag set MUST treat that as a connection error (Section 5.4.1)
|
||||||
PipeFactory = _context.PipeFactory,
|
// of type STREAM_CLOSED, unless the frame is permitted as described below.
|
||||||
LocalEndPoint = _context.LocalEndPoint,
|
//
|
||||||
RemoteEndPoint = _context.RemoteEndPoint,
|
// (The allowed frame types for this situation are WINDOW_UPDATE, RST_STREAM and PRIORITY)
|
||||||
StreamLifetimeHandler = this,
|
if (stream.EndStreamReceived)
|
||||||
FrameWriter = _frameWriter
|
{
|
||||||
});
|
throw new Http2ConnectionErrorException(Http2ErrorCode.STREAM_CLOSED);
|
||||||
_currentHeadersStream.ExpectData = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == 0;
|
}
|
||||||
_currentHeadersStream.Reset();
|
// TODO: trailers
|
||||||
|
|
||||||
_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;
|
|
||||||
}
|
}
|
||||||
|
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()
|
private Task ProcessPriorityFrameAsync()
|
||||||
|
|
@ -349,14 +403,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
|
throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowIfIncomingFrameSentToIdleStream();
|
||||||
|
|
||||||
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream))
|
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream))
|
||||||
{
|
{
|
||||||
stream.Abort(error: null);
|
stream.Abort(error: null);
|
||||||
}
|
}
|
||||||
else if (_incomingFrame.StreamId > _lastStreamId)
|
|
||||||
{
|
|
||||||
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
@ -449,16 +501,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
throw new Http2ConnectionErrorException(Http2ErrorCode.FRAME_SIZE_ERROR);
|
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);
|
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_incomingFrame.WindowUpdateSizeIncrement == 0)
|
|
||||||
{
|
{
|
||||||
return _frameWriter.WriteRstStreamAsync(_incomingFrame.StreamId, Http2ErrorCode.PROTOCOL_ERROR);
|
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)
|
if ((_incomingFrame.ContinuationFlags & Http2ContinuationFrameFlags.END_HEADERS) == Http2ContinuationFrameFlags.END_HEADERS)
|
||||||
{
|
{
|
||||||
_lastStreamId = _currentHeadersStream.StreamId;
|
_highestOpenedStreamId = _currentHeadersStream.StreamId;
|
||||||
_ = _currentHeadersStream.ProcessRequestsAsync();
|
_ = _currentHeadersStream.ProcessRequestsAsync();
|
||||||
_currentHeadersStream = null;
|
_currentHeadersStream = null;
|
||||||
}
|
}
|
||||||
|
|
@ -496,6 +547,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
return Task.CompletedTask;
|
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)
|
void IHttp2StreamLifetimeHandler.OnStreamCompleted(int streamId)
|
||||||
{
|
{
|
||||||
_streams.TryRemove(streamId, out _);
|
_streams.TryRemove(streamId, out _);
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,13 @@ using System.IO.Pipelines;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
|
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
|
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
{
|
{
|
||||||
public class Http2FrameWriter : IHttp2FrameWriter
|
public class Http2FrameWriter : IHttp2FrameWriter
|
||||||
{
|
{
|
||||||
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
|
||||||
|
|
||||||
private readonly Http2Frame _outgoingFrame = new Http2Frame();
|
private readonly Http2Frame _outgoingFrame = new Http2Frame();
|
||||||
private readonly object _writeLock = new object();
|
private readonly object _writeLock = new object();
|
||||||
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
|
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
|
||||||
|
|
@ -48,7 +45,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
|
|
||||||
public Task FlushAsync(CancellationToken cancellationToken)
|
public Task FlushAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return WriteAsync(_emptyData);
|
return WriteAsync(Constants.EmptyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Write100ContinueAsync(int streamId)
|
public Task Write100ContinueAsync(int streamId)
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
HttpRequestHeaders headers,
|
HttpRequestHeaders headers,
|
||||||
Http2Stream context)
|
Http2Stream context)
|
||||||
{
|
{
|
||||||
if (!context.ExpectData)
|
if (context.EndStreamReceived)
|
||||||
{
|
{
|
||||||
return MessageBody.ZeroContentLengthClose;
|
return ZeroContentLengthClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ForHttp2(context);
|
return new ForHttp2(context);
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,12 @@ using System.IO.Pipelines;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
{
|
{
|
||||||
public class Http2OutputProducer : IHttpOutputProducer
|
public class Http2OutputProducer : IHttpOutputProducer
|
||||||
{
|
{
|
||||||
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
|
||||||
|
|
||||||
private readonly int _streamId;
|
private readonly int _streamId;
|
||||||
private readonly IHttp2FrameWriter _frameWriter;
|
private readonly IHttp2FrameWriter _frameWriter;
|
||||||
|
|
||||||
|
|
@ -47,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
|
|
||||||
public Task WriteStreamSuffixAsync(CancellationToken cancellationToken)
|
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)
|
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 int StreamId => _context.StreamId;
|
||||||
|
|
||||||
public bool HasReceivedEndStream { get; private set; }
|
public bool EndStreamReceived { get; private set; }
|
||||||
|
|
||||||
protected IHttp2StreamLifetimeHandler StreamLifetimeHandler => _context.StreamLifetimeHandler;
|
protected IHttp2StreamLifetimeHandler StreamLifetimeHandler => _context.StreamLifetimeHandler;
|
||||||
|
|
||||||
public bool ExpectData { get; set; }
|
|
||||||
|
|
||||||
public override bool IsUpgradableRequest => false;
|
public override bool IsUpgradableRequest => false;
|
||||||
|
|
||||||
protected override void OnReset()
|
protected override void OnReset()
|
||||||
|
|
@ -91,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
|
|
||||||
if (endStream)
|
if (endStream)
|
||||||
{
|
{
|
||||||
HasReceivedEndStream = true;
|
EndStreamReceived = true;
|
||||||
RequestBodyPipe.Writer.Complete();
|
RequestBodyPipe.Writer.Complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,5 +32,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
||||||
public const string ServerName = "Kestrel";
|
public const string ServerName = "Kestrel";
|
||||||
|
|
||||||
public static readonly TimeSpan RequestBodyDrainTimeout = TimeSpan.FromSeconds(5);
|
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]
|
[Fact]
|
||||||
public async Task DATA_Received_StreamIdle_StreamError()
|
public async Task DATA_Received_StreamIdle_ConnectionError()
|
||||||
{
|
{
|
||||||
await InitializeConnectionAsync(_noopApplication);
|
await InitializeConnectionAsync(_noopApplication);
|
||||||
|
|
||||||
await SendDataAsync(1, _helloWorldBytes, endStream: false);
|
await SendDataAsync(1, _helloWorldBytes, endStream: false);
|
||||||
|
|
||||||
await WaitForStreamErrorAsync(expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonRstStreamFrames: false);
|
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 0, ignoreNonGoAwayFrames: false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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 StartStreamAsync(1, _postRequestHeaders, endStream: true);
|
||||||
await SendDataAsync(1, _helloBytes, endStream: true);
|
|
||||||
await SendDataAsync(1, _worldBytes, 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]
|
[Fact]
|
||||||
public async Task DATA_Received_StreamClosed_StreamError()
|
public async Task DATA_Received_StreamClosed_ConnectionError()
|
||||||
{
|
{
|
||||||
await InitializeConnectionAsync(_noopApplication);
|
await InitializeConnectionAsync(_noopApplication);
|
||||||
|
|
||||||
|
|
@ -510,13 +507,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
|
|
||||||
await SendDataAsync(1, _helloWorldBytes, endStream: false);
|
await SendDataAsync(1, _helloWorldBytes, endStream: false);
|
||||||
|
|
||||||
await WaitForStreamErrorAsync(expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonRstStreamFrames: false);
|
await WaitForConnectionErrorAsync(expectedLastStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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
|
// 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 SendDataAsync(1, _helloWorldBytes, endStream: true);
|
||||||
|
|
||||||
await WaitForStreamErrorAsync(expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonRstStreamFrames: false);
|
await WaitForConnectionErrorAsync(expectedLastStreamId: 3, expectedErrorCode: Http2ErrorCode.STREAM_CLOSED, ignoreNonGoAwayFrames: false);
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -655,6 +648,63 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
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]
|
[Theory]
|
||||||
[InlineData(0)]
|
[InlineData(0)]
|
||||||
[InlineData(1)]
|
[InlineData(1)]
|
||||||
|
|
@ -811,6 +861,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
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]
|
[Theory]
|
||||||
[InlineData(3)]
|
[InlineData(3)]
|
||||||
[InlineData(5)]
|
[InlineData(5)]
|
||||||
|
|
@ -1077,6 +1137,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
await WaitForConnectionErrorAsync(expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.PROTOCOL_ERROR, ignoreNonGoAwayFrames: false);
|
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]
|
[Fact]
|
||||||
public async Task WINDOW_UPDATE_Received_OnStream_SizeIncrementZero_StreamError()
|
public async Task WINDOW_UPDATE_Received_OnStream_SizeIncrementZero_StreamError()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue