From 6d375e9027250ec65f186dbd1f05c30fb12a1413 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 13 Nov 2018 11:33:48 -0800 Subject: [PATCH] Wait for reads before advancing clock in Http2TimeoutTests (#3090) Prior to this change, HTTP/2 read rate limit tests would fail when the stalled read started after the test started advancing the clock. --- .../Http2/Http2TestBase.cs | 29 +++++++++++++ .../Http2/Http2TimeoutTests.cs | 43 ++++++++++--------- 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/test/Kestrel.InMemory.FunctionalTests/Http2/Http2TestBase.cs b/test/Kestrel.InMemory.FunctionalTests/Http2/Http2TestBase.cs index 300eece503..9788b554e0 100644 --- a/test/Kestrel.InMemory.FunctionalTests/Http2/Http2TestBase.cs +++ b/test/Kestrel.InMemory.FunctionalTests/Http2/Http2TestBase.cs @@ -100,6 +100,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests new KeyValuePair("g", _4kHeaderValue), }; + protected static IEnumerable> ReadRateRequestHeaders(int expectedBytes) => new[] + { + new KeyValuePair(HeaderNames.Method, "POST"), + new KeyValuePair(HeaderNames.Path, "/" + expectedBytes), + new KeyValuePair(HeaderNames.Scheme, "http"), + new KeyValuePair(HeaderNames.Authority, "localhost:80"), + }; + protected static readonly byte[] _helloBytes = Encoding.ASCII.GetBytes("hello"); protected static readonly byte[] _worldBytes = Encoding.ASCII.GetBytes("world"); protected static readonly byte[] _helloWorldBytes = Encoding.ASCII.GetBytes("hello, world"); @@ -137,6 +145,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests protected readonly RequestDelegate _waitForAbortApplication; protected readonly RequestDelegate _waitForAbortFlushingApplication; protected readonly RequestDelegate _waitForAbortWithDataApplication; + protected readonly RequestDelegate _readRateApplication; protected readonly RequestDelegate _echoMethod; protected readonly RequestDelegate _echoHost; protected readonly RequestDelegate _echoPath; @@ -315,6 +324,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests _runningStreams[streamIdFeature.StreamId].TrySetResult(null); }; + _readRateApplication = async context => + { + var expectedBytes = int.Parse(context.Request.Path.Value.Substring(1)); + + var buffer = new byte[Http2PeerSettings.MinAllowedMaxFrameSize]; + var received = 0; + + while (received < expectedBytes) + { + received += await context.Request.Body.ReadAsync(buffer, 0, buffer.Length); + } + + var stalledReadTask = context.Request.Body.ReadAsync(buffer, 0, buffer.Length); + + // Write to the response so the test knows the app started the stalled read. + await context.Response.Body.WriteAsync(new byte[1], 0, 1); + + await stalledReadTask; + }; + _echoMethod = context => { context.Response.Headers["Method"] = context.Request.Method; diff --git a/test/Kestrel.InMemory.FunctionalTests/Http2/Http2TimeoutTests.cs b/test/Kestrel.InMemory.FunctionalTests/Http2/Http2TimeoutTests.cs index 42e232cbbd..75ed1dfb98 100644 --- a/test/Kestrel.InMemory.FunctionalTests/Http2/Http2TimeoutTests.cs +++ b/test/Kestrel.InMemory.FunctionalTests/Http2/Http2TimeoutTests.cs @@ -563,10 +563,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests _timeoutControl.Initialize(mockSystemClock.UtcNow); - await InitializeConnectionAsync(_echoApplication); + await InitializeConnectionAsync(_readRateApplication); // _helloWorldBytes is 12 bytes, and 12 bytes / 240 bytes/sec = .05 secs which is far below the grace period. - await StartStreamAsync(1, _browserRequestHeaders, endStream: false); + await StartStreamAsync(1, ReadRateRequestHeaders(_helloWorldBytes.Length), endStream: false); await SendDataAsync(1, _helloWorldBytes, endStream: false); await ExpectAsync(Http2FrameType.HEADERS, @@ -575,7 +575,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withStreamId: 1); await ExpectAsync(Http2FrameType.DATA, - withLength: _helloWorldBytes.Length, + withLength: 1, withFlags: (byte)Http2DataFrameFlags.NONE, withStreamId: 1); @@ -612,10 +612,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests _timeoutControl.Initialize(mockSystemClock.UtcNow); - await InitializeConnectionAsync(_echoApplication); + await InitializeConnectionAsync(_readRateApplication); // _maxData is 16 KiB, and 16 KiB / 240 bytes/sec ~= 68 secs which is far above the grace period. - await StartStreamAsync(1, _browserRequestHeaders, endStream: false); + await StartStreamAsync(1, ReadRateRequestHeaders(_maxData.Length), endStream: false); await SendDataAsync(1, _maxData, endStream: false); await ExpectAsync(Http2FrameType.HEADERS, @@ -624,7 +624,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withStreamId: 1); await ExpectAsync(Http2FrameType.DATA, - withLength: _maxData.Length, + withLength: 1, withFlags: (byte)Http2DataFrameFlags.NONE, withStreamId: 1); @@ -665,10 +665,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests _timeoutControl.Initialize(mockSystemClock.UtcNow); - await InitializeConnectionAsync(_echoApplication); + await InitializeConnectionAsync(_readRateApplication); // _maxData is 16 KiB, and 16 KiB / 240 bytes/sec ~= 68 secs which is far above the grace period. - await StartStreamAsync(1, _browserRequestHeaders, endStream: false); + await StartStreamAsync(1, ReadRateRequestHeaders(_maxData.Length), endStream: false); await SendDataAsync(1, _maxData, endStream: false); await ExpectAsync(Http2FrameType.HEADERS, @@ -677,11 +677,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withStreamId: 1); await ExpectAsync(Http2FrameType.DATA, - withLength: _maxData.Length, + withLength: 1, withFlags: (byte)Http2DataFrameFlags.NONE, withStreamId: 1); - await StartStreamAsync(3, _browserRequestHeaders, endStream: false); + await StartStreamAsync(3, ReadRateRequestHeaders(_maxData.Length), endStream: false); await SendDataAsync(3, _maxData, endStream: false); await ExpectAsync(Http2FrameType.HEADERS, @@ -689,7 +689,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withStreamId: 3); await ExpectAsync(Http2FrameType.DATA, - withLength: _maxData.Length, + withLength: 1, withFlags: (byte)Http2DataFrameFlags.NONE, withStreamId: 3); @@ -734,10 +734,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests _timeoutControl.Initialize(mockSystemClock.UtcNow); - await InitializeConnectionAsync(_echoApplication); + await InitializeConnectionAsync(_readRateApplication); // _maxData is 16 KiB, and 16 KiB / 240 bytes/sec ~= 68 secs which is far above the grace period. - await StartStreamAsync(1, _browserRequestHeaders, endStream: false); + await StartStreamAsync(1, ReadRateRequestHeaders(_maxData.Length), endStream: false); await SendDataAsync(1, _maxData, endStream: true); await ExpectAsync(Http2FrameType.HEADERS, @@ -746,7 +746,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withStreamId: 1); await ExpectAsync(Http2FrameType.DATA, - withLength: _maxData.Length, + withLength: 1, withFlags: (byte)Http2DataFrameFlags.NONE, withStreamId: 1); @@ -755,7 +755,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withFlags: (byte)Http2DataFrameFlags.END_STREAM, withStreamId: 1); - await StartStreamAsync(3, _browserRequestHeaders, endStream: false); + await StartStreamAsync(3, ReadRateRequestHeaders(_maxData.Length), endStream: false); await SendDataAsync(3, _maxData, endStream: false); await ExpectAsync(Http2FrameType.HEADERS, @@ -763,7 +763,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withStreamId: 3); await ExpectAsync(Http2FrameType.DATA, - withLength: _maxData.Length, + withLength: 1, withFlags: (byte)Http2DataFrameFlags.NONE, withStreamId: 3); @@ -819,7 +819,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests } else { - await _echoApplication(context); + await _readRateApplication(context); } }); @@ -830,7 +830,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests } await SendDataAsync(1, _maxData, endStream: true); - await StartStreamAsync(3, _browserRequestHeaders, endStream: false); + await StartStreamAsync(3, ReadRateRequestHeaders(_helloWorldBytes.Length), endStream: false); await SendDataAsync(3, _helloWorldBytes, endStream: false); await ExpectAsync(Http2FrameType.HEADERS, @@ -838,7 +838,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withStreamId: 3); await ExpectAsync(Http2FrameType.DATA, - withLength: _helloWorldBytes.Length, + withLength: 1, withFlags: (byte)Http2DataFrameFlags.NONE, withStreamId: 3); @@ -859,11 +859,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withFlags: (byte)Http2DataFrameFlags.END_STREAM, withStreamId: 1); - await ExpectAsync(Http2FrameType.WINDOW_UPDATE, + var updateFrame = await ExpectAsync(Http2FrameType.WINDOW_UPDATE, withLength: 4, withFlags: (byte)Http2DataFrameFlags.NONE, withStreamId: 0); + var expectedUpdateSize = ((framesConnectionInWindow / 2) + 1) * _maxData.Length + _helloWorldBytes.Length; + Assert.Equal(expectedUpdateSize, updateFrame.WindowUpdateSizeIncrement); + AdvanceClock(limits.MinRequestBodyDataRate.GracePeriod); _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny()), Times.Never);