Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Commit

Permalink
Allow 0 bytesPerScond; require gracePeriod to be larger than Hearbeat…
Browse files Browse the repository at this point in the history
….Interval.
  • Loading branch information
Cesar Blum Silveira committed Jun 24, 2017
1 parent 419cccc commit 778a6a6
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 47 deletions.
3 changes: 3 additions & 0 deletions src/Microsoft.AspNetCore.Server.Kestrel.Core/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
12 changes: 7 additions & 5 deletions src/Microsoft.AspNetCore.Server.Kestrel.Core/MinimumDataRate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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
{
Expand All @@ -11,17 +12,18 @@ public class MinimumDataRate
/// Creates a new instance of <see cref="MinimumDataRate"/>.
/// </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"/>.</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 MinimumDataRate(double bytesPerSecond, TimeSpan gracePeriod)
{
if (bytesPerSecond <= 0)
if (bytesPerSecond < 0)
{
throw new ArgumentOutOfRangeException(nameof(bytesPerSecond), CoreStrings.PositiveNumberRequired);
throw new ArgumentOutOfRangeException(nameof(bytesPerSecond), CoreStrings.NonNegativeNumberRequired);
}

if (gracePeriod < TimeSpan.Zero)
if (gracePeriod <= Heartbeat.Interval)
{
throw new ArgumentOutOfRangeException(nameof(gracePeriod), CoreStrings.NonNegativeTimeSpanRequired);
throw new ArgumentOutOfRangeException(nameof(gracePeriod), CoreStrings.FormatMinimumGracePeriodRequired(Heartbeat.Interval.TotalSeconds));
}

BytesPerSecond = bytesPerSecond;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
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;
Expand Down Expand Up @@ -128,7 +127,7 @@ public void RequestBodyMinimumDataRateNotEnforcedDuringGracePeriod()
public void RequestBodyDataRateIsAveragedOverTimeSpentReadingRequestBody()
{
var bytesPerSecond = 100;
var gracePeriod = TimeSpan.FromSeconds(1);
var gracePeriod = TimeSpan.FromSeconds(2);

_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinimumDataRate(bytesPerSecond: bytesPerSecond, gracePeriod: gracePeriod);
Expand All @@ -145,14 +144,14 @@ public void RequestBodyDataRateIsAveragedOverTimeSpentReadingRequestBody()

_frameConnection.StartTimingReads();

// Tick after grace period to start enforcing minimum data rate
// Set base data rate to 200 bytes/second
now += gracePeriod;
_frameConnection.BytesRead(100);
_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
Expand All @@ -162,27 +161,37 @@ public void RequestBodyDataRateIsAveragedOverTimeSpentReadingRequestBody()

// 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>(), 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>(), 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
Expand All @@ -194,11 +203,10 @@ public void RequestBodyDataRateIsAveragedOverTimeSpentReadingRequestBody()
[Fact]
public void RequestBodyDataRateNotComputedOnPausedTime()
{
var requestBodyTimeout = TimeSpan.FromSeconds(5);
var systemClock = new MockSystemClock();

_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinimumDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.Zero);
new MinimumDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;

var mockLogger = new Mock<IKestrelTrace>();
Expand All @@ -212,21 +220,21 @@ public void RequestBodyDataRateNotComputedOnPausedTime()

_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
Expand All @@ -239,7 +247,7 @@ public void RequestBodyDataRateNotComputedOnPausedTime()
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);

Expand All @@ -249,7 +257,7 @@ public void RequestBodyDataRateNotComputedOnPausedTime()
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);

Expand All @@ -266,7 +274,7 @@ public void ReadTimingNotPausedWhenResumeCalledBeforeNextTick()
var systemClock = new MockSystemClock();

_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinimumDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.Zero);
new MinimumDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;

var mockLogger = new Mock<IKestrelTrace>();
Expand All @@ -280,9 +288,9 @@ public void ReadTimingNotPausedWhenResumeCalledBeforeNextTick()

_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
Expand All @@ -291,15 +299,15 @@ public void ReadTimingNotPausedWhenResumeCalledBeforeNextTick()
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);
Expand All @@ -310,7 +318,7 @@ public void ReadTimingNotPausedWhenResumeCalledBeforeNextTick()
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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public void ResetResetsTraceIdentifier()
[Fact]
public void ResetResetsMinRequestBodyDataRate()
{
_frame.MinRequestBodyDataRate = new MinimumDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.Zero);
_frame.MinRequestBodyDataRate = new MinimumDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.MaxValue);

_frame.Reset();

Expand Down Expand Up @@ -881,7 +881,7 @@ public static TheoryData<string> QueryStringWithNullCharData
public static TheoryData<MinimumDataRate> MinRequestBodyDataRateData => new TheoryData<MinimumDataRate>
{
null,
new MinimumDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.Zero)
new MinimumDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.MaxValue)
};

private class RequestHeadersWrapper : IHeaderDictionary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,31 @@
// 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
{
[Theory]
[InlineData(0)]
[InlineData(double.Epsilon)]
[InlineData(double.MaxValue)]
public void BytesPerSecondValid(double value)
{
Assert.Equal(value, new MinimumDataRate(bytesPerSecond: value, gracePeriod: TimeSpan.Zero).BytesPerSecond);
Assert.Equal(value, new MinimumDataRate(bytesPerSecond: value, gracePeriod: TimeSpan.MaxValue).BytesPerSecond);
}

[Theory]
[InlineData(double.MinValue)]
[InlineData(0)]
[InlineData(-double.Epsilon)]
public void BytesPerSecondInvalid(double value)
{
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => new MinimumDataRate(bytesPerSecond: value, gracePeriod: TimeSpan.Zero));
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => new MinimumDataRate(bytesPerSecond: value, gracePeriod: TimeSpan.MaxValue));

Assert.Equal("bytesPerSecond", exception.ParamName);
Assert.StartsWith(CoreStrings.PositiveNumberRequired, exception.Message);
Assert.StartsWith(CoreStrings.NonNegativeNumberRequired, exception.Message);
}

[Theory]
Expand All @@ -42,20 +43,21 @@ public void GracePeriodInvalid(TimeSpan value)
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => new MinimumDataRate(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
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1429,7 +1429,8 @@ public async Task DoesNotEnforceRequestBodyMinimumDataRateOnUpgradedRequest()

using (var server = new TestServer(async context =>
{
context.Features.Get<IHttpMinRequestBodyDataRateFeature>().MinimumDataRate = new MinimumDataRate(bytesPerSecond: double.MaxValue, gracePeriod: TimeSpan.Zero);
context.Features.Get<IHttpMinRequestBodyDataRateFeature>().MinimumDataRate =
new MinimumDataRate(bytesPerSecond: double.MaxValue, gracePeriod: Heartbeat.Interval + TimeSpan.FromTicks(1));
using (var stream = await context.Features.Get<IHttpUpgradeFeature>().UpgradeAsync())
{
Expand Down

0 comments on commit 778a6a6

Please sign in to comment.