Rename request body min rate APIs (#1901).

This commit is contained in:
Cesar Blum Silveira 2017-06-29 17:16:34 -07:00 committed by GitHub
parent 3ba8c2d3f0
commit f2061ed716
21 changed files with 206 additions and 170 deletions

View File

@ -333,4 +333,7 @@
<data name="NonNegativeTimeSpanRequired" xml:space="preserve">
<value>Value must be a non-negative TimeSpan.</value>
</data>
<data name="MinimumGracePeriodRequired" xml:space="preserve">
<value>The request body rate enforcement grace period must be greater than {heartbeatInterval} second.</value>
</data>
</root>

View File

@ -6,13 +6,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Features
/// <summary>
/// Represents a minimum data rate for the request body of an HTTP request.
/// </summary>
public interface IHttpRequestBodyMinimumDataRateFeature
public interface IHttpMinRequestBodyDataRateFeature
{
/// <summary>
/// The minimum data rate in bytes/second at which the request body should be received.
/// 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.
/// </summary>
MinimumDataRate MinimumDataRate { get; set; }
MinDataRate MinDataRate { get; set; }
}
}

View File

@ -1,41 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Features
{
public class MinimumDataRate
{
/// <summary>
/// Creates a new instance of <see cref="MinimumDataRate"/>.
/// </summary>
/// <param name="rate">The minimum rate in bytes/second at which data should be processed.</param>
/// <param name="gracePeriod">The amount of time to delay enforcement of <paramref name="rate"/>.</param>
public MinimumDataRate(double rate, TimeSpan gracePeriod)
{
if (rate <= 0)
{
throw new ArgumentOutOfRangeException(nameof(rate), CoreStrings.PositiveNumberRequired);
}
if (gracePeriod < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(gracePeriod), CoreStrings.NonNegativeTimeSpanRequired);
}
Rate = rate;
GracePeriod = gracePeriod;
}
/// <summary>
/// The minimum rate in bytes/second at which data should be processed.
/// </summary>
public double Rate { get; }
/// <summary>
/// The amount of time to delay enforcement of <see cref="MinimumDataRate" />.
/// </summary>
public TimeSpan GracePeriod { get; }
}
}

View File

@ -285,14 +285,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{
_readTimingElapsedTicks += timestamp - _lastTimestamp;
if (_frame.RequestBodyMinimumDataRate?.Rate > 0 && _readTimingElapsedTicks > _frame.RequestBodyMinimumDataRate.GracePeriod.Ticks)
if (_frame.MinRequestBodyDataRate?.BytesPerSecond > 0 && _readTimingElapsedTicks > _frame.MinRequestBodyDataRate.GracePeriod.Ticks)
{
var elapsedSeconds = (double)_readTimingElapsedTicks / TimeSpan.TicksPerSecond;
var rate = Interlocked.Read(ref _readTimingBytesRead) / elapsedSeconds;
if (rate < _frame.RequestBodyMinimumDataRate.Rate && !Debugger.IsAttached)
if (rate < _frame.MinRequestBodyDataRate.BytesPerSecond && !Debugger.IsAttached)
{
Log.RequestBodyMininumDataRateNotSatisfied(_context.ConnectionId, _frame.TraceIdentifier, _frame.RequestBodyMinimumDataRate.Rate);
Log.RequestBodyMininumDataRateNotSatisfied(_context.ConnectionId, _frame.TraceIdentifier, _frame.MinRequestBodyDataRate.BytesPerSecond);
Timeout();
}
}

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
IHttpRequestLifetimeFeature,
IHttpRequestIdentifierFeature,
IHttpMaxRequestBodySizeFeature,
IHttpRequestBodyMinimumDataRateFeature
IHttpMinRequestBodyDataRateFeature
{
// NOTE: When feature interfaces are added to or removed from this Frame class implementation,
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
@ -229,10 +229,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
}
MinimumDataRate IHttpRequestBodyMinimumDataRateFeature.MinimumDataRate
MinDataRate IHttpMinRequestBodyDataRateFeature.MinDataRate
{
get => RequestBodyMinimumDataRate;
set => RequestBodyMinimumDataRate = value;
get => MinRequestBodyDataRate;
set => MinRequestBodyDataRate = value;
}
object IFeatureCollection.this[Type key]

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private static readonly Type IHttpWebSocketFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpWebSocketFeature);
private static readonly Type ISessionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.ISessionFeature);
private static readonly Type IHttpMaxRequestBodySizeFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature);
private static readonly Type IHttpRequestBodyMinimumDataRateFeatureType = typeof(global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpRequestBodyMinimumDataRateFeature);
private static readonly Type IHttpMinRequestBodyDataRateFeatureType = typeof(global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpMinRequestBodyDataRateFeature);
private static readonly Type IHttpSendFileFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
private object _currentIHttpRequestFeature;
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private object _currentIHttpWebSocketFeature;
private object _currentISessionFeature;
private object _currentIHttpMaxRequestBodySizeFeature;
private object _currentIHttpRequestBodyMinimumDataRateFeature;
private object _currentIHttpMinRequestBodyDataRateFeature;
private object _currentIHttpSendFileFeature;
private void FastReset()
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_currentIHttpRequestLifetimeFeature = this;
_currentIHttpConnectionFeature = this;
_currentIHttpMaxRequestBodySizeFeature = this;
_currentIHttpRequestBodyMinimumDataRateFeature = this;
_currentIHttpMinRequestBodyDataRateFeature = this;
_currentIServiceProvidersFeature = null;
_currentIHttpAuthenticationFeature = null;
@ -135,9 +135,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
return _currentIHttpMaxRequestBodySizeFeature;
}
if (key == IHttpRequestBodyMinimumDataRateFeatureType)
if (key == IHttpMinRequestBodyDataRateFeatureType)
{
return _currentIHttpRequestBodyMinimumDataRateFeature;
return _currentIHttpMinRequestBodyDataRateFeature;
}
if (key == IHttpSendFileFeatureType)
{
@ -230,9 +230,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_currentIHttpMaxRequestBodySizeFeature = feature;
return;
}
if (key == IHttpRequestBodyMinimumDataRateFeatureType)
if (key == IHttpMinRequestBodyDataRateFeatureType)
{
_currentIHttpRequestBodyMinimumDataRateFeature = feature;
_currentIHttpMinRequestBodyDataRateFeature = feature;
return;
}
if (key == IHttpSendFileFeatureType)
@ -309,9 +309,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
yield return new KeyValuePair<Type, object>(IHttpMaxRequestBodySizeFeatureType, _currentIHttpMaxRequestBodySizeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature);
}
if (_currentIHttpRequestBodyMinimumDataRateFeature != null)
if (_currentIHttpMinRequestBodyDataRateFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpRequestBodyMinimumDataRateFeatureType, _currentIHttpRequestBodyMinimumDataRateFeature as global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpRequestBodyMinimumDataRateFeature);
yield return new KeyValuePair<Type, object>(IHttpMinRequestBodyDataRateFeatureType, _currentIHttpMinRequestBodyDataRateFeature as global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpMinRequestBodyDataRateFeature);
}
if (_currentIHttpSendFileFeature != null)
{

View File

@ -13,7 +13,6 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
@ -300,7 +299,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
protected FrameResponseHeaders FrameResponseHeaders { get; } = new FrameResponseHeaders();
public MinimumDataRate RequestBodyMinimumDataRate { get; set; }
public MinDataRate MinRequestBodyDataRate { get; set; }
public void InitializeStreams(MessageBody messageBody)
{
@ -379,7 +378,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_responseBytesWritten = 0;
_requestCount++;
RequestBodyMinimumDataRate = ServerOptions.Limits.RequestBodyMinimumDataRate;
MinRequestBodyDataRate = ServerOptions.Limits.MinRequestBodyDataRate;
}
/// <summary>

View File

@ -255,11 +255,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
/// Gets or sets the request body minimum data rate in bytes/second.
/// 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 can be overridden per-request via <see cref="IHttpRequestBodyMinimumDataRateFeature"/>.
/// This can be overridden per-request via <see cref="IHttpMinRequestBodyDataRateFeature"/>.
/// </summary>
/// <remarks>
/// Defaults to 1 byte/second with a 5 second grace period.
/// </remarks>
public MinimumDataRate RequestBodyMinimumDataRate { get; set; } = new MinimumDataRate(rate: 1, gracePeriod: TimeSpan.FromSeconds(5));
public MinDataRate MinRequestBodyDataRate { get; set; } = new MinDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.FromSeconds(5));
}
}

View File

@ -0,0 +1,44 @@
// 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.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core
{
public class MinDataRate
{
/// <summary>
/// Creates a new instance of <see cref="MinDataRate"/>.
/// </summary>
/// <param name="bytesPerSecond">The minimum rate in bytes/second at which data should be processed.</param>
/// <param name="gracePeriod">The amount of time to delay enforcement of <paramref name="bytesPerSecond"/>,
/// starting at the time data is first read or written.</param>
public MinDataRate(double bytesPerSecond, TimeSpan gracePeriod)
{
if (bytesPerSecond < 0)
{
throw new ArgumentOutOfRangeException(nameof(bytesPerSecond), CoreStrings.NonNegativeNumberRequired);
}
if (gracePeriod <= Heartbeat.Interval)
{
throw new ArgumentOutOfRangeException(nameof(gracePeriod), CoreStrings.FormatMinimumGracePeriodRequired(Heartbeat.Interval.TotalSeconds));
}
BytesPerSecond = bytesPerSecond;
GracePeriod = gracePeriod;
}
/// <summary>
/// The minimum rate in bytes/second at which data should be processed.
/// </summary>
public double BytesPerSecond { get; }
/// <summary>
/// The amount of time to delay enforcement of <see cref="MinDataRate" />,
/// starting at the time data is first read or written.
/// </summary>
public TimeSpan GracePeriod { get; }
}
}

View File

@ -1018,6 +1018,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
internal static string FormatNonNegativeTimeSpanRequired()
=> GetString("NonNegativeTimeSpanRequired");
/// <summary>
/// The request body rate enforcement grace period must be greater than {heartbeatInterval} seconds.
/// </summary>
internal static string MinimumGracePeriodRequired
{
get => GetString("MinimumGracePeriodRequired");
}
/// <summary>
/// The request body rate enforcement grace period must be greater than {heartbeatInterval} seconds.
/// </summary>
internal static string FormatMinimumGracePeriodRequired(object heartbeatInterval)
=> string.Format(CultureInfo.CurrentCulture, GetString("MinimumGracePeriodRequired", "heartbeatInterval"), heartbeatInterval);
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
@ -72,11 +71,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var mockDebugger = new Mock<IDebugger>();
mockDebugger.SetupGet(g => g.IsAttached).Returns(true);
_frameConnection.Debugger = mockDebugger.Object;
var requestBodyMinimumDataRate = 100;
var bytesPerSecond = 100;
var mockLogger = new Mock<IKestrelTrace>();
mockLogger.Setup(l => l.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>())).Throws(new InvalidOperationException("Should not log"));
TickBodyWithMinimumDataRate(mockLogger.Object, requestBodyMinimumDataRate);
TickBodyWithMinimumDataRate(mockLogger.Object, bytesPerSecond);
Assert.False(_frameConnection.TimedOut);
}
@ -84,22 +83,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void TimesOutWhenRequestBodyDoesNotSatisfyMinimumDataRate()
{
var requestBodyMinimumDataRate = 100;
var bytesPerSecond = 100;
var mockLogger = new Mock<IKestrelTrace>();
TickBodyWithMinimumDataRate(mockLogger.Object, requestBodyMinimumDataRate);
TickBodyWithMinimumDataRate(mockLogger.Object, bytesPerSecond);
// Timed out
Assert.True(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Once);
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Once);
}
private void TickBodyWithMinimumDataRate(IKestrelTrace logger, int requestBodyMinimumDataRate)
private void TickBodyWithMinimumDataRate(IKestrelTrace logger, int bytesPerSecond)
{
var requestBodyGracePeriod = TimeSpan.FromSeconds(5);
var gracePeriod = TimeSpan.FromSeconds(5);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.RequestBodyMinimumDataRate =
new MinimumDataRate(rate: requestBodyMinimumDataRate, gracePeriod: requestBodyGracePeriod);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: bytesPerSecond, gracePeriod: gracePeriod);
_frameConnectionContext.ServiceContext.Log = logger;
@ -113,19 +112,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_frameConnection.StartTimingReads();
// Tick after grace period w/ low data rate
now += requestBodyGracePeriod + TimeSpan.FromSeconds(1);
now += gracePeriod + TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(1);
_frameConnection.Tick(now);
}
[Fact]
public void MinimumDataRateNotEnforcedDuringGracePeriod()
public void RequestBodyMinimumDataRateNotEnforcedDuringGracePeriod()
{
var requestBodyMinimumDataRate = 100;
var requestBodyGracePeriod = TimeSpan.FromSeconds(2);
var bytesPerSecond = 100;
var gracePeriod = TimeSpan.FromSeconds(2);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.RequestBodyMinimumDataRate =
new MinimumDataRate(rate: requestBodyMinimumDataRate, gracePeriod: requestBodyGracePeriod);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: bytesPerSecond, gracePeriod: gracePeriod);
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
@ -147,7 +146,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Not timed out
Assert.False(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Never);
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Tick after grace period w/ low data rate
now += TimeSpan.FromSeconds(2);
@ -157,17 +156,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Timed out
Assert.True(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Once);
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Once);
}
[Fact]
public void DataRateIsAveragedOverTimeSpentReadingRequestBody()
public void RequestBodyDataRateIsAveragedOverTimeSpentReadingRequestBody()
{
var requestBodyMinimumDataRate = 100;
var requestBodyGracePeriod = TimeSpan.FromSeconds(1);
var bytesPerSecond = 100;
var gracePeriod = TimeSpan.FromSeconds(2);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.RequestBodyMinimumDataRate =
new MinimumDataRate(rate: requestBodyMinimumDataRate, gracePeriod: requestBodyGracePeriod);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: bytesPerSecond, gracePeriod: gracePeriod);
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
@ -181,60 +180,69 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_frameConnection.StartTimingReads();
// Tick after grace period to start enforcing minimum data rate
now += requestBodyGracePeriod;
_frameConnection.BytesRead(100);
// Set base data rate to 200 bytes/second
now += gracePeriod;
_frameConnection.BytesRead(400);
_frameConnection.Tick(now);
// Data rate: 200 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(300);
_frameConnection.BytesRead(200);
_frameConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Never);
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Data rate: 150 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(50);
_frameConnection.BytesRead(0);
_frameConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Never);
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Data rate: 115 bytes/second
// Data rate: 120 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(10);
_frameConnection.BytesRead(0);
_frameConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Never);
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Data rate: 50 bytes/second
now += TimeSpan.FromSeconds(6);
_frameConnection.BytesRead(40);
// Data rate: 100 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(0);
_frameConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Data rate: ~85 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(0);
_frameConnection.Tick(now);
// Timed out
Assert.True(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Once);
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Once);
}
[Fact]
public void PausedTimeDoesNotCountAgainstRequestBodyTimeout()
public void RequestBodyDataRateNotComputedOnPausedTime()
{
var requestBodyTimeout = TimeSpan.FromSeconds(5);
var systemClock = new MockSystemClock();
_frameConnectionContext.ServiceContext.ServerOptions.Limits.RequestBodyMinimumDataRate =
new MinimumDataRate(rate: 100, gracePeriod: TimeSpan.Zero);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;
var mockLogger = new Mock<IKestrelTrace>();
@ -248,21 +256,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_frameConnection.StartTimingReads();
// Tick at 1s, expected counted time is 1s, expected data rate is 400 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(400);
// Tick at 3s, expected counted time is 3s, expected data rate is 200 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(3);
_frameConnection.BytesRead(600);
_frameConnection.Tick(systemClock.UtcNow);
// Pause at 1.5s
// Pause at 3.5s
systemClock.UtcNow += TimeSpan.FromSeconds(0.5);
_frameConnection.PauseTimingReads();
// Tick at 2s, expected counted time is 2s, expected data rate is 400 bytes/second
// Tick at 4s, expected counted time is 4s (first tick after pause goes through), expected data rate is 150 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(0.5);
_frameConnection.Tick(systemClock.UtcNow);
// Tick at 6s, expected counted time is 2s, expected data rate is 400 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(4);
// Tick at 6s, expected counted time is 4s, expected data rate is 150 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(2);
_frameConnection.Tick(systemClock.UtcNow);
// Not timed out
@ -275,7 +283,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
systemClock.UtcNow += TimeSpan.FromSeconds(0.5);
_frameConnection.ResumeTimingReads();
// Tick at 8s, expected counted time is 4s, expected data rate is 100 bytes/second
// Tick at 9s, expected counted time is 6s, expected data rate is 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(1.5);
_frameConnection.Tick(systemClock.UtcNow);
@ -285,7 +293,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Never);
// Tick at 9s, expected counted time is 9s, expected data rate drops below 100 bytes/second
// Tick at 10s, expected counted time is 7s, expected data rate drops below 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(1);
_frameConnection.Tick(systemClock.UtcNow);
@ -297,12 +305,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
[Fact]
public void NotPausedWhenResumeCalledBeforeNextTick()
public void ReadTimingNotPausedWhenResumeCalledBeforeNextTick()
{
var systemClock = new MockSystemClock();
_frameConnectionContext.ServiceContext.ServerOptions.Limits.RequestBodyMinimumDataRate =
new MinimumDataRate(rate: 100, gracePeriod: TimeSpan.Zero);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;
var mockLogger = new Mock<IKestrelTrace>();
@ -316,9 +324,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_frameConnection.StartTimingReads();
// Tick at 1s, expected counted time is 1s, expected data rate is 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(100);
// Tick at 2s, expected counted time is 2s, expected data rate is 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(2);
_frameConnection.BytesRead(200);
_frameConnection.Tick(systemClock.UtcNow);
// Not timed out
@ -327,15 +335,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Never);
// Pause at 1.25s
// Pause at 2.25s
systemClock.UtcNow += TimeSpan.FromSeconds(0.25);
_frameConnection.PauseTimingReads();
// Resume at 1.5s
// Resume at 2.5s
systemClock.UtcNow += TimeSpan.FromSeconds(0.25);
_frameConnection.ResumeTimingReads();
// Tick at 2s, expected counted time is 2s, expected data rate is 100 bytes/second
// Tick at 3s, expected counted time is 3s, expected data rate is 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(0.5);
_frameConnection.BytesRead(100);
_frameConnection.Tick(systemClock.UtcNow);
@ -346,7 +354,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Never);
// Tick at 3s, expected counted time is 3s, expected data rate drops below 100 bytes/second
// Tick at 4s, expected counted time is 4s, expected data rate drops below 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(1);
_frameConnection.Tick(systemClock.UtcNow);

View File

@ -141,13 +141,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
[Fact]
public void ResetResetsRequestBodyMinimumDataRate()
public void ResetResetsMinRequestBodyDataRate()
{
_frame.RequestBodyMinimumDataRate = new MinimumDataRate(rate: 1, gracePeriod: TimeSpan.Zero);
_frame.MinRequestBodyDataRate = new MinDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.MaxValue);
_frame.Reset();
Assert.Equal(_serviceContext.ServerOptions.Limits.RequestBodyMinimumDataRate, _frame.RequestBodyMinimumDataRate);
Assert.Equal(_serviceContext.ServerOptions.Limits.MinRequestBodyDataRate, _frame.MinRequestBodyDataRate);
}
[Fact]
@ -254,12 +254,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
[Theory]
[MemberData(nameof(RequestBodyMinimumDataRateData))]
public void ConfiguringRequestBodyMinimumDataRateFeatureSetsRequestBodyMinimumDateRate(MinimumDataRate minimumDataRate)
[MemberData(nameof(MinRequestBodyDataRateData))]
public void ConfiguringIHttpMinRequestBodyDataRateFeatureSetsMinRequestBodyDataRate(MinDataRate minDataRate)
{
((IFeatureCollection)_frame).Get<IHttpRequestBodyMinimumDataRateFeature>().MinimumDataRate = minimumDataRate;
((IFeatureCollection)_frame).Get<IHttpMinRequestBodyDataRateFeature>().MinDataRate = minDataRate;
Assert.Same(minimumDataRate, _frame.RequestBodyMinimumDataRate);
Assert.Same(minDataRate, _frame.MinRequestBodyDataRate);
}
[Fact]
@ -878,10 +878,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
TimeSpan.Zero
};
public static TheoryData<MinimumDataRate> RequestBodyMinimumDataRateData => new TheoryData<MinimumDataRate>
public static TheoryData<MinDataRate> MinRequestBodyDataRateData => new TheoryData<MinDataRate>
{
null,
new MinimumDataRate(rate: 1, gracePeriod: TimeSpan.Zero)
new MinDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.MaxValue)
};
private class RequestHeadersWrapper : IHeaderDictionary

View File

@ -15,6 +15,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class HeartbeatTests
{
[Fact]
public void HeartbeatIntervalIsOneSecond()
{
Assert.Equal(TimeSpan.FromSeconds(1), Heartbeat.Interval);
}
[Fact]
public void BlockedHeartbeatDoesntCauseOverlapsAndIsLoggedAsError()
{

View File

@ -293,11 +293,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
[Fact]
public void RequestBodyMinimumDataRateDefault()
public void MinRequestBodyDataRateDefault()
{
Assert.NotNull(new KestrelServerLimits().RequestBodyMinimumDataRate);
Assert.Equal(1, new KestrelServerLimits().RequestBodyMinimumDataRate.Rate);
Assert.Equal(TimeSpan.FromSeconds(5), new KestrelServerLimits().RequestBodyMinimumDataRate.GracePeriod);
Assert.NotNull(new KestrelServerLimits().MinRequestBodyDataRate);
Assert.Equal(1, new KestrelServerLimits().MinRequestBodyDataRate.BytesPerSecond);
Assert.Equal(TimeSpan.FromSeconds(5), new KestrelServerLimits().MinRequestBodyDataRate.GracePeriod);
}
public static TheoryData<TimeSpan> TimeoutValidData => new TheoryData<TimeSpan>

View File

@ -2,60 +2,62 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class MinimumDataRateTests
public class MinDataRateTests
{
[Theory]
[InlineData(0)]
[InlineData(double.Epsilon)]
[InlineData(double.MaxValue)]
public void RateValid(double value)
public void BytesPerSecondValid(double value)
{
Assert.Equal(value, new MinimumDataRate(rate: value, gracePeriod: TimeSpan.Zero).Rate);
Assert.Equal(value, new MinDataRate(bytesPerSecond: value, gracePeriod: TimeSpan.MaxValue).BytesPerSecond);
}
[Theory]
[InlineData(double.MinValue)]
[InlineData(0)]
public void RateInvalid(double value)
[InlineData(-double.Epsilon)]
public void BytesPerSecondInvalid(double value)
{
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => new MinimumDataRate(rate: value, gracePeriod: TimeSpan.Zero));
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => new MinDataRate(bytesPerSecond: value, gracePeriod: TimeSpan.MaxValue));
Assert.Equal("rate", exception.ParamName);
Assert.StartsWith(CoreStrings.PositiveNumberRequired, exception.Message);
Assert.Equal("bytesPerSecond", exception.ParamName);
Assert.StartsWith(CoreStrings.NonNegativeNumberRequired, exception.Message);
}
[Theory]
[MemberData(nameof(GracePeriodValidData))]
public void GracePeriodValid(TimeSpan value)
{
Assert.Equal(value, new MinimumDataRate(rate: 1, gracePeriod: value).GracePeriod);
Assert.Equal(value, new MinDataRate(bytesPerSecond: 1, gracePeriod: value).GracePeriod);
}
[Theory]
[MemberData(nameof(GracePeriodInvalidData))]
public void GracePeriodInvalid(TimeSpan value)
{
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => new MinimumDataRate(rate: 1, gracePeriod: value));
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => new MinDataRate(bytesPerSecond: 1, gracePeriod: value));
Assert.Equal("gracePeriod", exception.ParamName);
Assert.StartsWith(CoreStrings.NonNegativeTimeSpanRequired, exception.Message);
Assert.StartsWith(CoreStrings.FormatMinimumGracePeriodRequired(Heartbeat.Interval.TotalSeconds), exception.Message);
}
public static TheoryData<TimeSpan> GracePeriodValidData => new TheoryData<TimeSpan>
{
TimeSpan.Zero,
TimeSpan.FromTicks(1),
Heartbeat.Interval + TimeSpan.FromTicks(1),
TimeSpan.MaxValue
};
public static TheoryData<TimeSpan> GracePeriodInvalidData => new TheoryData<TimeSpan>
{
TimeSpan.MinValue,
TimeSpan.FromTicks(-1)
TimeSpan.FromTicks(-1),
TimeSpan.Zero,
Heartbeat.Interval
};
}
}

View File

@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
Limits =
{
KeepAliveTimeout = KeepAliveTimeout,
RequestBodyMinimumDataRate = null
MinRequestBodyDataRate = null
}
}
});

View File

@ -274,7 +274,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
options.Limits.MaxRequestHeadersTotalSize = (int)maxRequestBufferSize;
}
options.Limits.RequestBodyMinimumDataRate = null;
options.Limits.MinRequestBodyDataRate = null;
})
.UseContentRoot(Directory.GetCurrentDirectory())
.Configure(app => app.Run(async context =>

View File

@ -17,9 +17,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public class RequestBodyTimeoutTests
{
[Fact]
public async Task RequestTimesOutWhenRequestBodyNotReceivedAtDesiredMinimumRate()
public async Task RequestTimesOutWhenRequestBodyNotReceivedAtSpecifiedMinimumRate()
{
var minimumDataRateGracePeriod = TimeSpan.FromSeconds(5);
var gracePeriod = TimeSpan.FromSeconds(5);
var systemClock = new MockSystemClock();
var serviceContext = new TestServiceContext
{
@ -31,8 +31,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var server = new TestServer(context =>
{
context.Features.Get<IHttpRequestBodyMinimumDataRateFeature>().MinimumDataRate =
new MinimumDataRate(rate: 1, gracePeriod: minimumDataRateGracePeriod);
context.Features.Get<IHttpMinRequestBodyDataRateFeature>().MinDataRate =
new MinDataRate(bytesPerSecond: 1, gracePeriod: gracePeriod);
appRunningEvent.Set();
return context.Request.Body.ReadAsync(new byte[1], 0, 1);
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"");
Assert.True(appRunningEvent.Wait(TimeSpan.FromSeconds(10)));
systemClock.UtcNow += minimumDataRateGracePeriod + TimeSpan.FromSeconds(1);
systemClock.UtcNow += gracePeriod + TimeSpan.FromSeconds(1);
await connection.Receive(
"HTTP/1.1 408 Request Timeout",
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
[Fact]
public async Task RequestTimesWhenNotDrainedWithinDrainTimeoutPeriod()
public async Task RequestTimesOutWhenNotDrainedWithinDrainTimeoutPeriod()
{
// This test requires a real clock since we can't control when the drain timeout is set
var systemClock = new SystemClock();
@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var server = new TestServer(context =>
{
context.Features.Get<IHttpRequestBodyMinimumDataRateFeature>().MinimumDataRate = null;
context.Features.Get<IHttpMinRequestBodyDataRateFeature>().MinDataRate = null;
appRunningEvent.Set();
return Task.CompletedTask;
@ -112,7 +112,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[Fact]
public async Task ConnectionClosedEvenIfAppSwallowsException()
{
var minimumDataRateGracePeriod = TimeSpan.FromSeconds(5);
var gracePeriod = TimeSpan.FromSeconds(5);
var systemClock = new MockSystemClock();
var serviceContext = new TestServiceContext
{
@ -125,8 +125,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var server = new TestServer(async context =>
{
context.Features.Get<IHttpRequestBodyMinimumDataRateFeature>().MinimumDataRate =
new MinimumDataRate(rate: 1, gracePeriod: minimumDataRateGracePeriod);
context.Features.Get<IHttpMinRequestBodyDataRateFeature>().MinDataRate =
new MinDataRate(bytesPerSecond: 1, gracePeriod: gracePeriod);
appRunningEvent.Set();
@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"");
Assert.True(appRunningEvent.Wait(TimeSpan.FromSeconds(10)));
systemClock.UtcNow += minimumDataRateGracePeriod + TimeSpan.FromSeconds(1);
systemClock.UtcNow += gracePeriod + TimeSpan.FromSeconds(1);
Assert.True(exceptionSwallowedEvent.Wait(TimeSpan.FromSeconds(10)));
await connection.Receive(

View File

@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
Limits =
{
RequestHeadersTimeout = RequestHeadersTimeout,
RequestBodyMinimumDataRate = null
MinRequestBodyDataRate = null
}
}
});

View File

@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
.UseKestrel(options =>
{
options.Limits.MaxRequestBodySize = contentLength;
options.Limits.RequestBodyMinimumDataRate = null;
options.Limits.MinRequestBodyDataRate = null;
})
.UseUrls("http://127.0.0.1:0/")
.Configure(app =>
@ -1429,7 +1429,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var server = new TestServer(async context =>
{
context.Features.Get<IHttpRequestBodyMinimumDataRateFeature>().MinimumDataRate = new MinimumDataRate(rate: double.MaxValue, gracePeriod: TimeSpan.Zero);
context.Features.Get<IHttpMinRequestBodyDataRateFeature>().MinDataRate =
new MinDataRate(bytesPerSecond: double.MaxValue, gracePeriod: Heartbeat.Interval + TimeSpan.FromTicks(1));
using (var stream = await context.Features.Get<IHttpUpgradeFeature>().UpgradeAsync())
{

View File

@ -47,7 +47,7 @@ namespace CodeGenerator
typeof(IHttpWebSocketFeature),
typeof(ISessionFeature),
typeof(IHttpMaxRequestBodySizeFeature),
typeof(IHttpRequestBodyMinimumDataRateFeature),
typeof(IHttpMinRequestBodyDataRateFeature),
};
var rareFeatures = new[]
@ -68,7 +68,7 @@ namespace CodeGenerator
typeof(IHttpRequestLifetimeFeature),
typeof(IHttpConnectionFeature),
typeof(IHttpMaxRequestBodySizeFeature),
typeof(IHttpRequestBodyMinimumDataRateFeature),
typeof(IHttpMinRequestBodyDataRateFeature),
};
return $@"// Copyright (c) .NET Foundation. All rights reserved.