Merge pull request #2952 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
Chris Ross 2018-09-24 17:31:52 -07:00 committed by GitHub
commit 43974bfe5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 317 additions and 118 deletions

View File

@ -15,6 +15,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
private int _headerTableSize = (int)Http2PeerSettings.DefaultHeaderTableSize; private int _headerTableSize = (int)Http2PeerSettings.DefaultHeaderTableSize;
private int _maxFrameSize = (int)Http2PeerSettings.DefaultMaxFrameSize; private int _maxFrameSize = (int)Http2PeerSettings.DefaultMaxFrameSize;
private int _maxRequestHeaderFieldSize = 8192; private int _maxRequestHeaderFieldSize = 8192;
private int _initialConnectionWindowSize = 1024 * 128; // Larger than the default 64kb, and larger than any one single stream.
private int _initialStreamWindowSize = 1024 * 96; // Larger than the default 64kb
/// <summary> /// <summary>
/// Limits the number of concurrent request streams per HTTP/2 connection. Excess streams will be refused. /// Limits the number of concurrent request streams per HTTP/2 connection. Excess streams will be refused.
@ -95,5 +97,49 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
_maxRequestHeaderFieldSize = value; _maxRequestHeaderFieldSize = value;
} }
} }
/// <summary>
/// Indicates how much request body data the server is willing to receive and buffer at a time aggregated across all
/// requests (streams) per connection. Note requests are also limited by <see cref="InitialStreamWindowSize"/>
/// <para>
/// Value must be greater than or equal to 65,535 and less than 2^31, defaults to 128 kb.
/// </para>
/// </summary>
public int InitialConnectionWindowSize
{
get => _initialConnectionWindowSize;
set
{
if (value < Http2PeerSettings.DefaultInitialWindowSize || value > Http2PeerSettings.MaxWindowSize)
{
throw new ArgumentOutOfRangeException(nameof(value), value,
CoreStrings.FormatArgumentOutOfRange(Http2PeerSettings.DefaultInitialWindowSize, Http2PeerSettings.MaxWindowSize));
}
_initialConnectionWindowSize = value;
}
}
/// <summary>
/// Indicates how much request body data the server is willing to receive and buffer at a time per stream.
/// Note connections are also limited by <see cref="InitialConnectionWindowSize"/>
/// <para>
/// Value must be greater than or equal to 65,535 and less than 2^31, defaults to 96 kb.
/// </para>
/// </summary>
public int InitialStreamWindowSize
{
get => _initialStreamWindowSize;
set
{
if (value < Http2PeerSettings.DefaultInitialWindowSize || value > Http2PeerSettings.MaxWindowSize)
{
throw new ArgumentOutOfRangeException(nameof(value), value,
CoreStrings.FormatArgumentOutOfRange(Http2PeerSettings.DefaultInitialWindowSize, Http2PeerSettings.MaxWindowSize));
}
_initialStreamWindowSize = value;
}
}
} }
} }

View File

@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private readonly Http2ConnectionContext _context; private readonly Http2ConnectionContext _context;
private readonly Http2FrameWriter _frameWriter; private readonly Http2FrameWriter _frameWriter;
private readonly HPackDecoder _hpackDecoder; private readonly HPackDecoder _hpackDecoder;
private readonly InputFlowControl _inputFlowControl = new InputFlowControl(Http2PeerSettings.DefaultInitialWindowSize, Http2PeerSettings.DefaultInitialWindowSize / 2); private readonly InputFlowControl _inputFlowControl;
private readonly OutputFlowControl _outputFlowControl = new OutputFlowControl(Http2PeerSettings.DefaultInitialWindowSize); private readonly OutputFlowControl _outputFlowControl = new OutputFlowControl(Http2PeerSettings.DefaultInitialWindowSize);
private readonly Http2PeerSettings _serverSettings = new Http2PeerSettings(); private readonly Http2PeerSettings _serverSettings = new Http2PeerSettings();
@ -96,6 +96,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_serverSettings.HeaderTableSize = (uint)http2Limits.HeaderTableSize; _serverSettings.HeaderTableSize = (uint)http2Limits.HeaderTableSize;
_hpackDecoder = new HPackDecoder(http2Limits.HeaderTableSize, http2Limits.MaxRequestHeaderFieldSize); _hpackDecoder = new HPackDecoder(http2Limits.HeaderTableSize, http2Limits.MaxRequestHeaderFieldSize);
_serverSettings.MaxHeaderListSize = (uint)httpLimits.MaxRequestHeadersTotalSize; _serverSettings.MaxHeaderListSize = (uint)httpLimits.MaxRequestHeadersTotalSize;
_serverSettings.InitialWindowSize = (uint)http2Limits.InitialStreamWindowSize;
var connectionWindow = (uint)http2Limits.InitialConnectionWindowSize;
_inputFlowControl = new InputFlowControl(connectionWindow, connectionWindow / 2);
} }
public string ConnectionId => _context.ConnectionId; public string ConnectionId => _context.ConnectionId;
@ -183,6 +186,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
if (_state != Http2ConnectionState.Closed) if (_state != Http2ConnectionState.Closed)
{ {
await _frameWriter.WriteSettingsAsync(_serverSettings.GetNonProtocolDefaults()); await _frameWriter.WriteSettingsAsync(_serverSettings.GetNonProtocolDefaults());
// Inform the client that the connection window is larger than the default. It can't be lowered here,
// It can only be lowered by not issuing window updates after data is received.
var connectionWindow = _context.ServiceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize;
var diff = connectionWindow - (int)Http2PeerSettings.DefaultInitialWindowSize;
if (diff > 0)
{
await _frameWriter.WriteWindowUpdateAsync(0, diff);
}
} }
while (_state != Http2ConnectionState.Closed) while (_state != Http2ConnectionState.Closed)
@ -541,6 +552,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
RemoteEndPoint = _context.RemoteEndPoint, RemoteEndPoint = _context.RemoteEndPoint,
StreamLifetimeHandler = this, StreamLifetimeHandler = this,
ClientPeerSettings = _clientSettings, ClientPeerSettings = _clientSettings,
ServerPeerSettings = _serverSettings,
FrameWriter = _frameWriter, FrameWriter = _frameWriter,
ConnectionInputFlowControl = _inputFlowControl, ConnectionInputFlowControl = _inputFlowControl,
ConnectionOutputFlowControl = _outputFlowControl, ConnectionOutputFlowControl = _outputFlowControl,

View File

@ -36,13 +36,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_context.StreamId, _context.StreamId,
_context.FrameWriter, _context.FrameWriter,
context.ConnectionInputFlowControl, context.ConnectionInputFlowControl,
Http2PeerSettings.DefaultInitialWindowSize, _context.ServerPeerSettings.InitialWindowSize,
Http2PeerSettings.DefaultInitialWindowSize / 2); _context.ServerPeerSettings.InitialWindowSize / 2);
_outputFlowControl = new StreamOutputFlowControl(context.ConnectionOutputFlowControl, context.ClientPeerSettings.InitialWindowSize); _outputFlowControl = new StreamOutputFlowControl(context.ConnectionOutputFlowControl, context.ClientPeerSettings.InitialWindowSize);
_http2Output = new Http2OutputProducer(context.StreamId, context.FrameWriter, _outputFlowControl, context.TimeoutControl, context.MemoryPool); _http2Output = new Http2OutputProducer(context.StreamId, context.FrameWriter, _outputFlowControl, context.TimeoutControl, context.MemoryPool);
RequestBodyPipe = CreateRequestBodyPipe(); RequestBodyPipe = CreateRequestBodyPipe(_context.ServerPeerSettings.InitialWindowSize);
Output = _http2Output; Output = _http2Output;
} }
@ -446,14 +446,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_inputFlowControl.Abort(); _inputFlowControl.Abort();
} }
private Pipe CreateRequestBodyPipe() private Pipe CreateRequestBodyPipe(uint windowSize)
=> new Pipe(new PipeOptions => new Pipe(new PipeOptions
( (
pool: _context.MemoryPool, pool: _context.MemoryPool,
readerScheduler: ServiceContext.Scheduler, readerScheduler: ServiceContext.Scheduler,
writerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline,
pauseWriterThreshold: Http2PeerSettings.DefaultInitialWindowSize, // Never pause within the window range. Flow control will prevent more data from being added.
resumeWriterThreshold: Http2PeerSettings.DefaultInitialWindowSize, // See the assert in OnDataAsync.
pauseWriterThreshold: windowSize + 1,
resumeWriterThreshold: windowSize + 1,
useSynchronizationContext: false, useSynchronizationContext: false,
minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize
)); ));

View File

@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
public IPEndPoint LocalEndPoint { get; set; } public IPEndPoint LocalEndPoint { get; set; }
public IHttp2StreamLifetimeHandler StreamLifetimeHandler { get; set; } public IHttp2StreamLifetimeHandler StreamLifetimeHandler { get; set; }
public Http2PeerSettings ClientPeerSettings { get; set; } public Http2PeerSettings ClientPeerSettings { get; set; }
public Http2PeerSettings ServerPeerSettings { get; set; }
public Http2FrameWriter FrameWriter { get; set; } public Http2FrameWriter FrameWriter { get; set; }
public InputFlowControl ConnectionInputFlowControl { get; set; } public InputFlowControl ConnectionInputFlowControl { get; set; }
public OutputFlowControl ConnectionOutputFlowControl { get; set; } public OutputFlowControl ConnectionOutputFlowControl { get; set; }

View File

@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_connectionContext.ServiceContext.ServerOptions.Limits.Http2.MaxFrameSize = length; _connectionContext.ServiceContext.ServerOptions.Limits.Http2.MaxFrameSize = length;
_connection = new Http2Connection(_connectionContext); _connection = new Http2Connection(_connectionContext);
await InitializeConnectionAsync(_echoApplication, expectedSettingsCount: 3); await InitializeConnectionAsync(_echoApplication, expectedSettingsCount: 4);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false); await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendDataAsync(1, new byte[length], endStream: true); await SendDataAsync(1, new byte[length], endStream: true);
@ -182,31 +182,44 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public async Task DATA_Received_GreaterThanDefaultInitialWindowSize_ReadByStream() public async Task DATA_Received_GreaterThanInitialWindowSize_ReadByStream()
{ {
// _maxData should be 1/4th of the default initial window size + 1. var initialStreamWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialStreamWindowSize;
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); var framesInStreamWindow = initialStreamWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
var initialConnectionWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize;
var framesInConnectionWindow = initialConnectionWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
// Double the client stream windows to 128KiB so no stream WINDOW_UPDATEs need to be sent. // Grow the client stream windows so no stream WINDOW_UPDATEs need to be sent.
_clientSettings.InitialWindowSize = Http2PeerSettings.DefaultInitialWindowSize * 2; _clientSettings.InitialWindowSize = int.MaxValue;
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
// Double the client connection window to 128KiB. // Grow the client connection windows so no connection WINDOW_UPDATEs need to be sent.
await SendWindowUpdateAsync(0, (int)Http2PeerSettings.DefaultInitialWindowSize); await SendWindowUpdateAsync(0, int.MaxValue - (int)Http2PeerSettings.DefaultInitialWindowSize);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false); await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendDataAsync(1, _maxData, endStream: false);
// Rounds down so we don't go over the half window size and trigger an update
for (var i = 0; i < framesInStreamWindow / 2; i++)
{
await SendDataAsync(1, _maxData, endStream: false);
}
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame1 = await ExpectAsync(Http2FrameType.DATA, var dataFrames = new List<Http2FrameWithPayload>();
withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE, for (var i = 0; i < framesInStreamWindow / 2; i++)
withStreamId: 1); {
var dataFrame1 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1);
dataFrames.Add(dataFrame1);
}
// Writing over half the initial window size induces both a connection-level and stream-level window update. // Writing over half the initial window size induces both a connection-level and stream-level window update.
await SendDataAsync(1, _maxData, endStream: false); await SendDataAsync(1, _maxData, endStream: false);
@ -215,34 +228,43 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withLength: 4, withLength: 4,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
var connectionWindowUpdateFrame1 = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 0);
var dataFrame2 = await ExpectAsync(Http2FrameType.DATA, var dataFrame2 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length, withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
dataFrames.Add(dataFrame2);
// Write a few more frames to get close to the connection window threshold
var additionalFrames = (framesInConnectionWindow / 2) - (framesInStreamWindow / 2) - 1;
for (var i = 0; i < additionalFrames; i++)
{
await SendDataAsync(1, _maxData, endStream: false);
var dataFrame1 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1);
dataFrames.Add(dataFrame1);
}
// Write one more to cross the connection window update threshold
await SendDataAsync(1, _maxData, endStream: false); await SendDataAsync(1, _maxData, endStream: false);
var connectionWindowUpdateFrame1 = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 0);
var dataFrame3 = await ExpectAsync(Http2FrameType.DATA, var dataFrame3 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length, withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
dataFrames.Add(dataFrame3);
await SendDataAsync(1, _maxData, endStream: true); // End
await SendDataAsync(1, new Memory<byte>(), endStream: true);
var connectionWindowUpdateFrame2 = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 0);
var dataFrame4 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
withLength: 0, withLength: 0,
withFlags: (byte)Http2DataFrameFlags.END_STREAM, withFlags: (byte)Http2DataFrameFlags.END_STREAM,
@ -250,13 +272,44 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame1.PayloadSequence.ToArray())); foreach (var frame in dataFrames)
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame2.PayloadSequence.ToArray())); {
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame3.PayloadSequence.ToArray())); Assert.True(_maxData.AsSpan().SequenceEqual(frame.PayloadSequence.ToArray()));
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame4.PayloadSequence.ToArray())); }
Assert.Equal(_maxData.Length * 2, streamWindowUpdateFrame1.WindowUpdateSizeIncrement); var updateSize = ((framesInStreamWindow / 2) + 1) * _maxData.Length;
Assert.Equal(_maxData.Length * 2, connectionWindowUpdateFrame1.WindowUpdateSizeIncrement); Assert.Equal(updateSize, streamWindowUpdateFrame1.WindowUpdateSizeIncrement);
Assert.Equal(_maxData.Length * 2, connectionWindowUpdateFrame2.WindowUpdateSizeIncrement); updateSize = ((framesInConnectionWindow / 2) + 1) * _maxData.Length;
Assert.Equal(updateSize, connectionWindowUpdateFrame1.WindowUpdateSizeIncrement);
}
[Fact]
public async Task DATA_Received_RightAtWindowLimit_DoesNotPausePipe()
{
var initialStreamWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialStreamWindowSize;
var framesInStreamWindow = initialStreamWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
var initialConnectionWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize;
var framesInConnectionWindow = initialConnectionWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
await InitializeConnectionAsync(_waitForAbortApplication);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
// Rounds down so we don't go over the limit
for (var i = 0; i < framesInStreamWindow; i++)
{
await SendDataAsync(1, _maxData, endStream: false);
}
var remainder = initialStreamWindowSize % (int)Http2PeerSettings.DefaultMaxFrameSize;
// Write just to the limit.
// This should not produce a async task from the request body pipe. See the Debug.Assert in Http2Stream.OnDataAsync
await SendDataAsync(1, new Memory<byte>(_maxData, 0, remainder), endStream: false);
// End
await SendDataAsync(1, new Memory<byte>(), endStream: true);
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
} }
[Fact] [Fact]
@ -358,61 +411,77 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public async Task DATA_Received_Multiplexed_GreaterThanDefaultInitialWindowSize_ReadByStream() public async Task DATA_Received_Multiplexed_GreaterThanInitialWindowSize_ReadByStream()
{ {
// _maxData should be 1/4th of the default initial window size + 1. var initialStreamWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialStreamWindowSize;
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); var initialConnectionWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize;
var framesInStreamWindow = initialStreamWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
var framesInConnectionWindow = initialConnectionWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
// Double the client stream windows to 128KiB so no stream WINDOW_UPDATEs need to be sent. // Grow the client stream windows so no stream WINDOW_UPDATEs need to be sent.
_clientSettings.InitialWindowSize = Http2PeerSettings.DefaultInitialWindowSize * 2; _clientSettings.InitialWindowSize = int.MaxValue;
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
// Double the client connection window to 128KiB. // Grow the client connection windows so no connection WINDOW_UPDATEs need to be sent.
await SendWindowUpdateAsync(0, (int)Http2PeerSettings.DefaultInitialWindowSize); await SendWindowUpdateAsync(0, int.MaxValue - (int)Http2PeerSettings.DefaultInitialWindowSize);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false); await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendDataAsync(1, _maxData, endStream: false);
// Rounds down so we don't go over the half window size and trigger an update
for (var i = 0; i < framesInStreamWindow / 2; i++)
{
await SendDataAsync(1, _maxData, endStream: false);
}
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame1 = await ExpectAsync(Http2FrameType.DATA, var dataFrames = new List<Http2FrameWithPayload>();
withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1);
// Writing over half the initial window size induces both a connection-level and stream-level window update. for (var i = 0; i < framesInStreamWindow / 2; i++)
{
var dataFrame1 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1);
dataFrames.Add(dataFrame1);
}
// Writing over half the initial window size induces a stream-level window update.
await SendDataAsync(1, _maxData, endStream: false); await SendDataAsync(1, _maxData, endStream: false);
var streamWindowUpdateFrame = await ExpectAsync(Http2FrameType.WINDOW_UPDATE, var streamWindowUpdateFrame = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4, withLength: 4,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
var connectionWindowUpdateFrame1 = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 0);
var dataFrame2 = await ExpectAsync(Http2FrameType.DATA, var dataFrame2 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length, withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
dataFrames.Add(dataFrame2);
await SendDataAsync(1, _maxData, endStream: false); // No update expected for these
var additionalFrames = (framesInConnectionWindow / 2) - (framesInStreamWindow / 2) - 1;
for (var i = 0; i < additionalFrames; i++)
{
await SendDataAsync(1, _maxData, endStream: false);
var dataFrame3 = await ExpectAsync(Http2FrameType.DATA, var dataFrame3 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length, withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
dataFrames.Add(dataFrame3);
}
// Uploading data to a new stream induces a second connection-level but not stream-level window update. // Uploading data to a new stream induces a second connection-level but not stream-level window update.
await StartStreamAsync(3, _browserRequestHeaders, endStream: false); await StartStreamAsync(3, _browserRequestHeaders, endStream: false);
await SendDataAsync(3, _maxData, endStream: true); await SendDataAsync(3, _maxData, endStream: true);
var connectionWindowUpdateFrame2 = await ExpectAsync(Http2FrameType.WINDOW_UPDATE, var connectionWindowUpdateFrame = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4, withLength: 4,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 0); withStreamId: 0);
@ -426,17 +495,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withLength: _maxData.Length, withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 3); withStreamId: 3);
dataFrames.Add(dataFrame4);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
withLength: 0, withLength: 0,
withFlags: (byte)Http2DataFrameFlags.END_STREAM, withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 3); withStreamId: 3);
// Would trigger a stream window update, except it's the last frame.
await SendDataAsync(1, _maxData, endStream: true); await SendDataAsync(1, _maxData, endStream: true);
var dataFrame5 = await ExpectAsync(Http2FrameType.DATA, var dataFrame5 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length, withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
dataFrames.Add(dataFrame5);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
withLength: 0, withLength: 0,
withFlags: (byte)Http2DataFrameFlags.END_STREAM, withFlags: (byte)Http2DataFrameFlags.END_STREAM,
@ -444,16 +516,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false); await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame1.PayloadSequence.ToArray())); foreach (var frame in dataFrames)
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame2.PayloadSequence.ToArray())); {
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame3.PayloadSequence.ToArray())); Assert.True(_maxData.AsSpan().SequenceEqual(frame.PayloadSequence.ToArray()));
Assert.Equal(_maxData.Length * 2, streamWindowUpdateFrame.WindowUpdateSizeIncrement); }
Assert.Equal(_maxData.Length * 2, connectionWindowUpdateFrame1.WindowUpdateSizeIncrement); var updateSize = ((framesInStreamWindow / 2) + 1) * _maxData.Length;
Assert.Equal(updateSize, streamWindowUpdateFrame.WindowUpdateSizeIncrement);
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame4.PayloadSequence.ToArray())); updateSize = ((framesInConnectionWindow / 2) + 1) * _maxData.Length;
Assert.Equal(_maxData.Length * 2, connectionWindowUpdateFrame2.WindowUpdateSizeIncrement); Assert.Equal(updateSize, connectionWindowUpdateFrame.WindowUpdateSizeIncrement);
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame5.PayloadSequence.ToArray()));
} }
[Fact] [Fact]
@ -557,38 +627,59 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[InlineData(255)] [InlineData(255)]
public async Task DATA_Received_WithPadding_CountsTowardsInputFlowControl(byte padLength) public async Task DATA_Received_WithPadding_CountsTowardsInputFlowControl(byte padLength)
{ {
// _maxData should be 1/4th of the default initial window size + 1. var initialWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialStreamWindowSize;
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); var framesInWindow = initialWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
var maxDataMinusPadding = _maxData.AsMemory(0, _maxData.Length - padLength - 1); var maxDataMinusPadding = _maxData.AsMemory(0, _maxData.Length - padLength - 1);
// Grow the client stream windows so no stream WINDOW_UPDATEs need to be sent.
_clientSettings.InitialWindowSize = int.MaxValue;
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
// Grow the client connection windows so no connection WINDOW_UPDATEs need to be sent.
await SendWindowUpdateAsync(0, int.MaxValue - (int)Http2PeerSettings.DefaultInitialWindowSize);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false); await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendDataWithPaddingAsync(1, maxDataMinusPadding, padLength, endStream: false); var dataSent = 0;
// Rounds down so we don't go over the half window size and trigger an update
for (var i = 0; i < framesInWindow / 2; i++)
{
await SendDataWithPaddingAsync(1, maxDataMinusPadding, padLength, endStream: false);
dataSent += maxDataMinusPadding.Length;
}
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame1 = await ExpectAsync(Http2FrameType.DATA, // The frames come back in various sizes depending on the pipe buffers, and without the padding we sent.
withLength: maxDataMinusPadding.Length, while (dataSent > 0)
withFlags: (byte)Http2DataFrameFlags.NONE, {
withStreamId: 1); var frame = await ReceiveFrameAsync();
Assert.Equal(Http2FrameType.DATA, frame.Type);
Assert.True(dataSent >= frame.PayloadLength);
Assert.Equal(Http2DataFrameFlags.NONE, frame.DataFlags);
Assert.Equal(1, frame.StreamId);
// Writing over half the initial window size induces both a connection-level and stream-level window update. dataSent -= frame.PayloadLength;
await SendDataAsync(1, _maxData, endStream: true); }
// Writing over half the initial window size induces a stream-level window update.
await SendDataAsync(1, _maxData, endStream: false);
var connectionWindowUpdateFrame = await ExpectAsync(Http2FrameType.WINDOW_UPDATE, var connectionWindowUpdateFrame = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4, withLength: 4,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 0); withStreamId: 1);
var dataFrame2 = await ExpectAsync(Http2FrameType.DATA, var dataFrame3 = await ExpectAsync(Http2FrameType.DATA,
withLength: _maxData.Length, withLength: _maxData.Length,
withFlags: (byte)Http2DataFrameFlags.NONE, withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
await SendDataAsync(1, new Memory<byte>(), endStream: true);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
withLength: 0, withLength: 0,
withFlags: (byte)Http2DataFrameFlags.END_STREAM, withFlags: (byte)Http2DataFrameFlags.END_STREAM,
@ -596,22 +687,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
Assert.True(maxDataMinusPadding.Span.SequenceEqual(dataFrame1.PayloadSequence.ToArray())); Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame3.PayloadSequence.ToArray()));
Assert.True(_maxData.AsSpan().SequenceEqual(dataFrame2.PayloadSequence.ToArray()));
Assert.Equal(_maxData.Length * 2, connectionWindowUpdateFrame.WindowUpdateSizeIncrement); var updateSize = ((framesInWindow / 2) + 1) * _maxData.Length;
Assert.Equal(updateSize, connectionWindowUpdateFrame.WindowUpdateSizeIncrement);
} }
[Fact] [Fact]
public async Task DATA_Received_ButNotConsumedByApp_CountsTowardsInputFlowControl() public async Task DATA_Received_ButNotConsumedByApp_CountsTowardsInputFlowControl()
{ {
// _maxData should be 1/4th of the default initial window size + 1. var initialConnectionWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize;
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); var framesConnectionInWindow = initialConnectionWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
await InitializeConnectionAsync(_noopApplication); await InitializeConnectionAsync(_noopApplication);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false); await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendDataAsync(1, _maxData, endStream: false); for (var i = 0; i < framesConnectionInWindow / 2; i++)
{
await SendDataAsync(1, _maxData, endStream: false);
}
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 55, withLength: 55,
@ -622,7 +716,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)Http2DataFrameFlags.END_STREAM, withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1); withStreamId: 1);
// Writing over half the initial window size induces both a connection-level window update. // Writing over half the initial window size induces a connection-level window update.
// But no stream window update since this is the last frame.
await SendDataAsync(1, _maxData, endStream: true); await SendDataAsync(1, _maxData, endStream: true);
var connectionWindowUpdateFrame = await ExpectAsync(Http2FrameType.WINDOW_UPDATE, var connectionWindowUpdateFrame = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
@ -632,7 +727,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
Assert.Equal(_maxData.Length * 2, connectionWindowUpdateFrame.WindowUpdateSizeIncrement); var updateSize = ((framesConnectionInWindow / 2) + 1) * _maxData.Length;
Assert.Equal(updateSize, connectionWindowUpdateFrame.WindowUpdateSizeIncrement);
} }
[Fact] [Fact]
@ -822,17 +918,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact] [Fact]
public async Task DATA_Received_NoStreamWindowSpace_ConnectionError() public async Task DATA_Received_NoStreamWindowSpace_ConnectionError()
{ {
// _maxData should be 1/4th of the default initial window size + 1. var initialWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialStreamWindowSize;
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); var framesInWindow = (initialWindowSize / Http2PeerSettings.DefaultMaxFrameSize) + 1; // Round up to overflow the window
await InitializeConnectionAsync(_waitForAbortApplication); await InitializeConnectionAsync(_waitForAbortApplication);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false); await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendDataAsync(1, _maxData, endStream: false); for (var i = 0; i < framesInWindow; i++)
await SendDataAsync(1, _maxData, endStream: false); {
await SendDataAsync(1, _maxData, endStream: false); await SendDataAsync(1, _maxData, endStream: false);
await SendDataAsync(1, _maxData, endStream: false); }
await WaitForConnectionErrorAsync<Http2ConnectionErrorException>( await WaitForConnectionErrorAsync<Http2ConnectionErrorException>(
ignoreNonGoAwayFrames: false, ignoreNonGoAwayFrames: false,
@ -844,17 +940,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact] [Fact]
public async Task DATA_Received_NoConnectionWindowSpace_ConnectionError() public async Task DATA_Received_NoConnectionWindowSpace_ConnectionError()
{ {
// _maxData should be 1/4th of the default initial window size + 1. var initialWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize;
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); var framesInWindow = initialWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
await InitializeConnectionAsync(_waitForAbortApplication); await InitializeConnectionAsync(_waitForAbortApplication);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false); await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendDataAsync(1, _maxData, endStream: false); for (var i = 0; i < framesInWindow / 2; i++)
await SendDataAsync(1, _maxData, endStream: false); {
await SendDataAsync(1, _maxData, endStream: false);
}
await StartStreamAsync(3, _browserRequestHeaders, endStream: false); await StartStreamAsync(3, _browserRequestHeaders, endStream: false);
await SendDataAsync(3, _maxData, endStream: false); for (var i = 0; i < framesInWindow / 2; i++)
{
await SendDataAsync(3, _maxData, endStream: false);
}
// One extra to overflow the connection window
await SendDataAsync(3, _maxData, endStream: false); await SendDataAsync(3, _maxData, endStream: false);
await WaitForConnectionErrorAsync<Http2ConnectionErrorException>( await WaitForConnectionErrorAsync<Http2ConnectionErrorException>(
@ -1956,14 +2058,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact] [Fact]
public async Task RST_STREAM_Received_ReturnsSpaceToConnectionInputFlowControlWindow() public async Task RST_STREAM_Received_ReturnsSpaceToConnectionInputFlowControlWindow()
{ {
// _maxData should be 1/4th of the default initial window size + 1. var initialConnectionWindowSize = _connectionContext.ServiceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize;
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); var framesInConnectionWindow = initialConnectionWindowSize / Http2PeerSettings.DefaultMaxFrameSize;
await InitializeConnectionAsync(_waitForAbortApplication); await InitializeConnectionAsync(_waitForAbortApplication);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false); await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
await SendDataAsync(1, _maxData, endStream: false);
await SendDataAsync(1, _maxData, endStream: false); // Rounds down so we don't go over the half window size and trigger an update
for (var i = 0; i < framesInConnectionWindow / 2; i++)
{
await SendDataAsync(1, _maxData, endStream: false);
}
// Go over the threshold and trigger an update
await SendDataAsync(1, _maxData, endStream: false); await SendDataAsync(1, _maxData, endStream: false);
await SendRstStreamAsync(1); await SendRstStreamAsync(1);
@ -1977,7 +2085,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
Assert.Contains(1, _abortedStreamIds); Assert.Contains(1, _abortedStreamIds);
Assert.Equal(_maxData.Length * 3, connectionWindowUpdateFrame.WindowUpdateSizeIncrement); var updateSize = ((framesInConnectionWindow / 2) + 1) * _maxData.Length;
Assert.Equal(updateSize, connectionWindowUpdateFrame.WindowUpdateSizeIncrement);
} }
[Fact] [Fact]
@ -2065,22 +2174,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendSettingsAsync(); await SendSettingsAsync();
var frame = await ExpectAsync(Http2FrameType.SETTINGS, var frame = await ExpectAsync(Http2FrameType.SETTINGS,
withLength: Http2FrameReader.SettingSize * 2, withLength: Http2FrameReader.SettingSize * 3,
withFlags: 0, withFlags: 0,
withStreamId: 0); withStreamId: 0);
// Only non protocol defaults are sent // Only non protocol defaults are sent
var settings = Http2FrameReader.ReadSettings(frame.PayloadSequence); var settings = Http2FrameReader.ReadSettings(frame.PayloadSequence);
Assert.Equal(2, settings.Count); Assert.Equal(3, settings.Count);
var setting = settings[0]; var setting = settings[0];
Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, setting.Parameter); Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, setting.Parameter);
Assert.Equal(100u, setting.Value); Assert.Equal(100u, setting.Value);
setting = settings[1]; setting = settings[1];
Assert.Equal(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, setting.Parameter);
Assert.Equal(96 * 1024u, setting.Value);
setting = settings[2];
Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, setting.Parameter); Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, setting.Parameter);
Assert.Equal(32 * 1024u, setting.Value); Assert.Equal(32 * 1024u, setting.Value);
var update = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4,
withFlags: (byte)Http2SettingsFrameFlags.NONE,
withStreamId: 0);
Assert.Equal(1024 * 128 - (int)Http2PeerSettings.DefaultInitialWindowSize, update.WindowUpdateSizeIncrement);
await ExpectAsync(Http2FrameType.SETTINGS, await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0, withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK, withFlags: (byte)Http2SettingsFrameFlags.ACK,
@ -2094,6 +2214,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{ {
_connection.ServerSettings.MaxConcurrentStreams = 1; _connection.ServerSettings.MaxConcurrentStreams = 1;
_connection.ServerSettings.MaxHeaderListSize = 4 * 1024; _connection.ServerSettings.MaxHeaderListSize = 4 * 1024;
_connection.ServerSettings.InitialWindowSize = 1024 * 1024 * 10;
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(_noopApplication)); _connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(_noopApplication));
@ -2101,22 +2222,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendSettingsAsync(); await SendSettingsAsync();
var frame = await ExpectAsync(Http2FrameType.SETTINGS, var frame = await ExpectAsync(Http2FrameType.SETTINGS,
withLength: Http2FrameReader.SettingSize * 2, withLength: Http2FrameReader.SettingSize * 3,
withFlags: 0, withFlags: 0,
withStreamId: 0); withStreamId: 0);
// Only non protocol defaults are sent // Only non protocol defaults are sent
var settings = Http2FrameReader.ReadSettings(frame.PayloadSequence); var settings = Http2FrameReader.ReadSettings(frame.PayloadSequence);
Assert.Equal(2, settings.Count); Assert.Equal(3, settings.Count);
var setting = settings[0]; var setting = settings[0];
Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, setting.Parameter); Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, setting.Parameter);
Assert.Equal(1u, setting.Value); Assert.Equal(1u, setting.Value);
setting = settings[1]; setting = settings[1];
Assert.Equal(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, setting.Parameter);
Assert.Equal(1024 * 1024 * 10u, setting.Value);
setting = settings[2];
Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, setting.Parameter); Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, setting.Parameter);
Assert.Equal(4 * 1024u, setting.Value); Assert.Equal(4 * 1024u, setting.Value);
var update = await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4,
withFlags: (byte)Http2SettingsFrameFlags.NONE,
withStreamId: 0);
Assert.Equal(1024 * 128u - Http2PeerSettings.DefaultInitialWindowSize, (uint)update.WindowUpdateSizeIncrement);
await ExpectAsync(Http2FrameType.SETTINGS, await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0, withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK, withFlags: (byte)Http2SettingsFrameFlags.ACK,
@ -2277,7 +2409,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
context.Response.Headers["A"] = new string('a', headerValueLength); context.Response.Headers["A"] = new string('a', headerValueLength);
context.Response.Headers["B"] = new string('b', headerValueLength); context.Response.Headers["B"] = new string('b', headerValueLength);
return context.Response.Body.WriteAsync(new byte[payloadLength], 0, payloadLength); return context.Response.Body.WriteAsync(new byte[payloadLength], 0, payloadLength);
}, expectedSettingsCount: 3); }, expectedSettingsCount: 4);
// Update client settings // Update client settings
_clientSettings.MaxFrameSize = (uint)payloadLength; _clientSettings.MaxFrameSize = (uint)payloadLength;
@ -2323,7 +2455,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await InitializeConnectionAsync(context => await InitializeConnectionAsync(context =>
{ {
return context.Response.Body.WriteAsync(new byte[clientMaxFrame], 0, clientMaxFrame); return context.Response.Body.WriteAsync(new byte[clientMaxFrame], 0, clientMaxFrame);
}, expectedSettingsCount: 3); }, expectedSettingsCount: 4);
// Start request // Start request
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);

View File

@ -310,7 +310,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetAsciiOrUTF8StringNonNullCharacters(); _decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetAsciiOrUTF8StringNonNullCharacters();
} }
protected async Task InitializeConnectionAsync(RequestDelegate application, int expectedSettingsCount = 2) protected async Task InitializeConnectionAsync(RequestDelegate application, int expectedSettingsCount = 3)
{ {
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application)); _connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application));
@ -322,6 +322,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: 0, withFlags: 0,
withStreamId: 0); withStreamId: 0);
await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
withLength: 4,
withFlags: 0,
withStreamId: 0);
await ExpectAsync(Http2FrameType.SETTINGS, await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0, withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK, withFlags: (byte)Http2SettingsFrameFlags.ACK,

View File

@ -40,9 +40,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
// Expect a SETTINGS frame (type 0x4) with default settings // Expect a SETTINGS frame (type 0x4) with default settings
var expected = new byte[] var expected = new byte[]
{ {
0x00, 0x00, 0x0C, // Payload Length (6 * settings count) 0x00, 0x00, 0x12, // Payload Length (6 * settings count)
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // SETTINGS frame (type 0x04) 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // SETTINGS frame (type 0x04)
0x00, 0x03, 0x00, 0x00, 0x00, 0x64, // Connection limit 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, // Connection limit
0x00, 0x04, 0x00, 0x01, 0x80, 0x00, // Initial window size
0x00, 0x06, 0x00, 0x00, 0x80, 0x00 // Header size limit 0x00, 0x06, 0x00, 0x00, 0x80, 0x00 // Header size limit
}; };
var testContext = new TestServiceContext(LoggerFactory); var testContext = new TestServiceContext(LoggerFactory);