From 8d83e5352d5dfd6cf07e38a545395be63dd480f8 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Fri, 17 May 2019 18:25:41 -0700 Subject: [PATCH] Disable request body data rate limits per HTTP/2 stream (#10355) --- src/Servers/Kestrel/Core/src/CoreStrings.resx | 3 ++ .../IHttpMinRequestBodyDataRateFeature.cs | 8 +-- .../Http/Http1Connection.FeatureCollection.cs | 8 +-- .../Core/src/Internal/Http/Http1Connection.cs | 3 -- .../src/Internal/Http/Http1MessageBody.cs | 2 +- .../Internal/Http/HttpProtocol.Generated.cs | 2 +- .../Core/src/Internal/Http/HttpProtocol.cs | 2 + .../Core/src/Internal/Http/MessageBody.cs | 8 ++- .../Http/ZeroContentLengthMessageBody.cs | 2 +- .../src/Internal/Http2/Http2MessageBody.cs | 8 +-- .../Http2/Http2Stream.FeatureCollection.cs | 20 ++++++- .../Core/src/Internal/Http2/Http2Stream.cs | 2 +- .../src/Properties/CoreStrings.Designer.cs | 14 +++++ .../Kestrel/Core/test/BodyControlTests.cs | 2 +- .../Kestrel/Core/test/Http1ConnectionTests.cs | 2 +- .../HttpProtocolFeatureCollectionTests.cs | 22 ++++++-- .../Core/test/HttpRequestStreamTests.cs | 2 +- .../Http2/Http2TimeoutTests.cs | 54 +++++++++++++++++++ .../HttpProtocolFeatureCollection.cs | 1 + 19 files changed, 130 insertions(+), 35 deletions(-) diff --git a/src/Servers/Kestrel/Core/src/CoreStrings.resx b/src/Servers/Kestrel/Core/src/CoreStrings.resx index d2b23f176c..4b41f63c44 100644 --- a/src/Servers/Kestrel/Core/src/CoreStrings.resx +++ b/src/Servers/Kestrel/Core/src/CoreStrings.resx @@ -599,4 +599,7 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l Cannot call GetMemory() until response has started. Call HttpResponse.StartAsync() before calling GetMemory(). + + This feature is not supported for HTTP/2 requests except to disable it entirely by setting the rate to null. + \ No newline at end of file diff --git a/src/Servers/Kestrel/Core/src/Features/IHttpMinRequestBodyDataRateFeature.cs b/src/Servers/Kestrel/Core/src/Features/IHttpMinRequestBodyDataRateFeature.cs index 0244621f08..8a8603cfd9 100644 --- a/src/Servers/Kestrel/Core/src/Features/IHttpMinRequestBodyDataRateFeature.cs +++ b/src/Servers/Kestrel/Core/src/Features/IHttpMinRequestBodyDataRateFeature.cs @@ -5,8 +5,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Features { /// /// Feature to set the minimum data rate at which the the request body must be sent by the client. - /// This feature is not available for HTTP/2 requests. Instead, use - /// for server-wide configuration which applies to both HTTP/2 and HTTP/1.x. + /// This feature is not supported for HTTP/2 requests except to disable it entirely by setting to + /// Instead, use for server-wide configuration which applies to both HTTP/2 and HTTP/1.x. /// public interface IHttpMinRequestBodyDataRateFeature { @@ -14,8 +14,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Features /// The minimum data rate in bytes/second at which the request body must be sent by the client. /// Setting this property to null indicates no minimum data rate should be enforced. /// This limit has no effect on upgraded connections which are always unlimited. - /// This feature is not available for HTTP/2 requests. Instead, use - /// for server-wide configuration which applies to both HTTP/2 and HTTP/1.x. + /// This feature is not supported for HTTP/2 requests except to disable it entirely by setting to + /// Instead, use for server-wide configuration which applies to both HTTP/2 and HTTP/1.x. /// MinDataRate MinDataRate { get; set; } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.FeatureCollection.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.FeatureCollection.cs index da4bacf8c0..111cff342c 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.FeatureCollection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.FeatureCollection.cs @@ -1,18 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { internal partial class Http1Connection : IHttpMinRequestBodyDataRateFeature, - IHttpMinResponseDataRateFeature + IHttpMinResponseDataRateFeature { MinDataRate IHttpMinRequestBodyDataRateFeature.MinDataRate { diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs index 7b23196794..d657d70ee0 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs @@ -62,8 +62,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http public bool RequestTimedOut => _requestTimedOut; - public MinDataRate MinRequestBodyDataRate { get; set; } - public MinDataRate MinResponseDataRate { get; set; } public MemoryPool MemoryPool { get; } @@ -542,7 +540,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _remainingRequestHeadersBytesAllowed = ServerOptions.Limits.MaxRequestHeadersTotalSize + 2; _requestCount++; - MinRequestBodyDataRate = ServerOptions.Limits.MinRequestBodyDataRate; MinResponseDataRate = ServerOptions.Limits.MinResponseDataRate; } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1MessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1MessageBody.cs index 743306c9b3..a661946a62 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1MessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1MessageBody.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http protected readonly Http1Connection _context; protected Http1MessageBody(Http1Connection context) - : base(context, context.MinRequestBodyDataRate) + : base(context) { _context = context; } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs index aaeffffe1b..8ba4e4b050 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _currentIHttpRequestLifetimeFeature = this; _currentIHttpConnectionFeature = this; _currentIHttpMaxRequestBodySizeFeature = this; + _currentIHttpMinRequestBodyDataRateFeature = this; _currentIHttpBodyControlFeature = this; _currentIHttpResponseStartFeature = this; _currentIRouteValuesFeature = this; @@ -100,7 +101,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _currentITlsConnectionFeature = null; _currentIHttpWebSocketFeature = null; _currentISessionFeature = null; - _currentIHttpMinRequestBodyDataRateFeature = null; _currentIHttpMinResponseDataRateFeature = null; _currentIHttpSendFileFeature = null; } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs index c9ac239797..78701c5571 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs @@ -101,6 +101,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http public string ConnectionIdFeature { get; set; } public bool HasStartedConsumingRequestBody { get; set; } public long? MaxRequestBodySize { get; set; } + public MinDataRate MinRequestBodyDataRate { get; set; } public bool AllowSynchronousIO { get; set; } /// @@ -340,6 +341,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http HasStartedConsumingRequestBody = false; MaxRequestBodySize = ServerOptions.Limits.MaxRequestBodySize; + MinRequestBodyDataRate = ServerOptions.Limits.MinRequestBodyDataRate; AllowSynchronousIO = ServerOptions.AllowSynchronousIO; TraceIdentifier = null; Method = HttpMethod.None; diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs index e4d38f8c31..8803e9611b 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs @@ -15,7 +15,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private static readonly MessageBody _zeroContentLengthKeepAlive = new ZeroContentLengthMessageBody(keepAlive: true); private readonly HttpProtocol _context; - private readonly MinDataRate _minRequestBodyDataRate; private bool _send100Continue = true; private long _consumedBytes; @@ -25,10 +24,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http protected bool _backpressure; protected long _alreadyTimedBytes; - protected MessageBody(HttpProtocol context, MinDataRate minRequestBodyDataRate) + protected MessageBody(HttpProtocol context) { _context = context; - _minRequestBodyDataRate = minRequestBodyDataRate; } public static MessageBody ZeroContentLengthClose => _zeroContentLengthClose; @@ -98,10 +96,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { Log.RequestBodyStart(_context.ConnectionIdFeature, _context.TraceIdentifier); - if (_minRequestBodyDataRate != null) + if (_context.MinRequestBodyDataRate != null) { _timingEnabled = true; - _context.TimeoutControl.StartRequestBody(_minRequestBodyDataRate); + _context.TimeoutControl.StartRequestBody(_context.MinRequestBodyDataRate); } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/ZeroContentLengthMessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http/ZeroContentLengthMessageBody.cs index d803271b89..9d958671ca 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/ZeroContentLengthMessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/ZeroContentLengthMessageBody.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http internal sealed class ZeroContentLengthMessageBody : MessageBody { public ZeroContentLengthMessageBody(bool keepAlive) - : base(null, null) + : base(null) { RequestKeepAlive = keepAlive; } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs index b2c8f7956d..ac1a243d43 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs @@ -16,8 +16,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 private ReadResult _readResult; private long _alreadyExaminedInNextReadResult; - private Http2MessageBody(Http2Stream context, MinDataRate minRequestBodyDataRate) - : base(context, minRequestBodyDataRate) + private Http2MessageBody(Http2Stream context) + : base(context) { _context = context; } @@ -47,14 +47,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 AddAndCheckConsumedBytes(bytesRead); } - public static MessageBody For(Http2Stream context, MinDataRate minRequestBodyDataRate) + public static MessageBody For(Http2Stream context) { if (context.ReceivedEmptyRequestBody) { return ZeroContentLengthClose; } - return new Http2MessageBody(context, minRequestBodyDataRate); + return new Http2MessageBody(context); } public override void AdvanceTo(SequencePosition consumed) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.FeatureCollection.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.FeatureCollection.cs index 4af0269f4c..7187fc8462 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.FeatureCollection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.FeatureCollection.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; @@ -8,7 +9,10 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { - internal partial class Http2Stream : IHttp2StreamIdFeature, IHttpResponseTrailersFeature + internal partial class Http2Stream : IHttp2StreamIdFeature, + IHttpMinRequestBodyDataRateFeature, + IHttpResponseTrailersFeature + { internal HttpResponseTrailers Trailers { get; set; } private IHeaderDictionary _userTrailers; @@ -30,5 +34,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } int IHttp2StreamIdFeature.StreamId => _context.StreamId; + + MinDataRate IHttpMinRequestBodyDataRateFeature.MinDataRate + { + get => throw new NotSupportedException(CoreStrings.Http2MinDataRateNotSupported); + set + { + if (value != null) + { + throw new NotSupportedException(CoreStrings.Http2MinDataRateNotSupported); + } + + MinRequestBodyDataRate = value; + } + } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index cca143cfbd..dfefd1a3d1 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 => StringUtilities.ConcatAsHexSuffix(ConnectionId, ':', (uint)StreamId); protected override MessageBody CreateMessageBody() - => Http2MessageBody.For(this, ServerOptions.Limits.MinRequestBodyDataRate); + => Http2MessageBody.For(this); // Compare to Http1Connection.OnStartLine protected override bool TryParseRequest(ReadResult result, out bool endConnection) diff --git a/src/Servers/Kestrel/Core/src/Properties/CoreStrings.Designer.cs b/src/Servers/Kestrel/Core/src/Properties/CoreStrings.Designer.cs index 8729db9063..a63a15c259 100644 --- a/src/Servers/Kestrel/Core/src/Properties/CoreStrings.Designer.cs +++ b/src/Servers/Kestrel/Core/src/Properties/CoreStrings.Designer.cs @@ -2254,6 +2254,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core internal static string FormatStartAsyncBeforeGetMemory() => GetString("StartAsyncBeforeGetMemory"); + /// + /// This feature is not supported for HTTP/2 requests except to disable it entirely by setting the rate to null. + /// + internal static string Http2MinDataRateNotSupported + { + get => GetString("Http2MinDataRateNotSupported"); + } + + /// + /// This feature is not supported for HTTP/2 requests except to disable it entirely by setting the rate to null. + /// + internal static string FormatHttp2MinDataRateNotSupported() + => GetString("Http2MinDataRateNotSupported"); + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Servers/Kestrel/Core/test/BodyControlTests.cs b/src/Servers/Kestrel/Core/test/BodyControlTests.cs index 397b92da85..0a2f326e38 100644 --- a/src/Servers/Kestrel/Core/test/BodyControlTests.cs +++ b/src/Servers/Kestrel/Core/test/BodyControlTests.cs @@ -145,7 +145,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests private class MockMessageBody : MessageBody { public MockMessageBody(bool upgradeable = false) - : base(null, null) + : base(null) { RequestUpgrade = upgradeable; } diff --git a/src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs b/src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs index 6ac94c384a..4c3cb758e9 100644 --- a/src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs +++ b/src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs @@ -888,7 +888,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var buffer = new byte[10]; await context.Response.Body.WriteAsync(buffer, 0, 10); }); - var mockMessageBody = new Mock(null, null); + var mockMessageBody = new Mock(null); _http1Connection.NextMessageBody = mockMessageBody.Object; var requestProcessingTask = _http1Connection.ProcessRequestsAsync(httpApplication); diff --git a/src/Servers/Kestrel/Core/test/HttpProtocolFeatureCollectionTests.cs b/src/Servers/Kestrel/Core/test/HttpProtocolFeatureCollectionTests.cs index 2524e88785..c8fb802885 100644 --- a/src/Servers/Kestrel/Core/test/HttpProtocolFeatureCollectionTests.cs +++ b/src/Servers/Kestrel/Core/test/HttpProtocolFeatureCollectionTests.cs @@ -159,12 +159,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests } [Fact] - public void Http2StreamFeatureCollectionDoesNotIncludeMinRateFeatures() + public void Http2StreamFeatureCollectionDoesNotIncludeIHttpMinResponseDataRateFeature() { - Assert.Null(_http2Collection.Get()); Assert.Null(_http2Collection.Get()); - - Assert.NotNull(_collection.Get()); Assert.NotNull(_collection.Get()); } @@ -177,6 +174,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests Assert.False(upgradeFeature.IsUpgradableRequest); } + [Fact] + public void Http2StreamFeatureCollectionDoesIncludeIHttpMinRequestBodyDataRateFeature() + { + var minRateFeature = _http2Collection.Get(); + + Assert.NotNull(minRateFeature); + + Assert.Throws(() => minRateFeature.MinDataRate); + Assert.Throws(() => minRateFeature.MinDataRate = new MinDataRate(1, TimeSpan.FromSeconds(2))); + + // You can set the MinDataRate to null though. + minRateFeature.MinDataRate = null; + + // But you still cannot read the property; + Assert.Throws(() => minRateFeature.MinDataRate); + } + private void CompareGenericGetterToIndexer() { Assert.Same(_collection.Get(), _collection[typeof(IHttpRequestFeature)]); diff --git a/src/Servers/Kestrel/Core/test/HttpRequestStreamTests.cs b/src/Servers/Kestrel/Core/test/HttpRequestStreamTests.cs index 5eaaf242b2..a47949e4d0 100644 --- a/src/Servers/Kestrel/Core/test/HttpRequestStreamTests.cs +++ b/src/Servers/Kestrel/Core/test/HttpRequestStreamTests.cs @@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockBodyControl = new Mock(); mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(() => allowSynchronousIO); - var mockMessageBody = new Mock(null, null); + var mockMessageBody = new Mock(null); mockMessageBody.Setup(m => m.ReadAsync(CancellationToken.None)).Returns(new ValueTask(new ReadResult(default, isCanceled: false, isCompleted: true))); var pipeReader = new HttpRequestPipeReader(); diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TimeoutTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TimeoutTests.cs index 8bb8a5181b..a52db473bc 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TimeoutTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TimeoutTests.cs @@ -798,6 +798,60 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests _mockConnectionContext.VerifyNoOtherCalls(); } + [Fact] + public async Task DATA_Received_SlowlyWhenRateLimitDisabledPerRequest_DoesNotAbortConnection() + { + var mockSystemClock = _serviceContext.MockSystemClock; + var limits = _serviceContext.ServerOptions.Limits; + + // Use non-default value to ensure the min request and response rates aren't mixed up. + limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); + + _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks); + + await InitializeConnectionAsync(context => + { + // Completely disable rate limiting for this stream. + context.Features.Get().MinDataRate = null; + return _readRateApplication(context); + }); + + // _helloWorldBytes is 12 bytes, and 12 bytes / 240 bytes/sec = .05 secs which is far below the grace period. + await StartStreamAsync(1, ReadRateRequestHeaders(_helloWorldBytes.Length), endStream: false); + await SendDataAsync(1, _helloWorldBytes, endStream: false); + + await ExpectAsync(Http2FrameType.HEADERS, + withLength: 37, + withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, + withStreamId: 1); + + await ExpectAsync(Http2FrameType.DATA, + withLength: 1, + withFlags: (byte)Http2DataFrameFlags.NONE, + withStreamId: 1); + + // Don't send any more data and advance just to and then past the grace period. + AdvanceClock(limits.MinRequestBodyDataRate.GracePeriod); + + _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny()), Times.Never); + + AdvanceClock(TimeSpan.FromTicks(1)); + + _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny()), Times.Never); + + await SendDataAsync(1, _helloWorldBytes, endStream: true); + + await ExpectAsync(Http2FrameType.DATA, + withLength: 0, + withFlags: (byte)Http2DataFrameFlags.END_STREAM, + withStreamId: 1); + + await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); + + _mockTimeoutHandler.VerifyNoOtherCalls(); + _mockConnectionContext.VerifyNoOtherCalls(); + } + [Fact] public async Task DATA_Received_SlowlyDueToConnectionFlowControl_DoesNotAbortConnection() { diff --git a/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs b/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs index 1be680d38a..c8bd5f3d3e 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs @@ -71,6 +71,7 @@ namespace CodeGenerator "IHttpRequestLifetimeFeature", "IHttpConnectionFeature", "IHttpMaxRequestBodySizeFeature", + "IHttpMinRequestBodyDataRateFeature", "IHttpBodyControlFeature", "IHttpResponseStartFeature", "IRouteValuesFeature",