Do not enforce timeouts when the debugger is attached

This commit is contained in:
Nate McMaster 2017-06-27 16:56:27 -07:00
parent 3fcd909d90
commit 9b4be69e9d
6 changed files with 88 additions and 16 deletions

View File

@ -44,6 +44,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
// For testing
internal Frame Frame => _frame;
internal IDebugger Debugger { get; set; } = DebuggerWrapper.Singleton;
public bool TimedOut { get; private set; }
@ -263,14 +265,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
// TODO: Use PlatformApis.VolatileRead equivalent again
if (timestamp > Interlocked.Read(ref _timeoutTimestamp))
{
CancelTimeout();
if (_timeoutAction == TimeoutAction.SendTimeoutResponse)
if (!Debugger.IsAttached)
{
SetTimeoutResponse();
}
CancelTimeout();
Timeout();
if (_timeoutAction == TimeoutAction.SendTimeoutResponse)
{
SetTimeoutResponse();
}
Timeout();
}
}
else
{
@ -285,7 +290,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
var elapsedSeconds = (double)_readTimingElapsedTicks / TimeSpan.TicksPerSecond;
var rate = Interlocked.Read(ref _readTimingBytesRead) / elapsedSeconds;
if (rate < _frame.RequestBodyMinimumDataRate.Rate)
if (rate < _frame.RequestBodyMinimumDataRate.Rate && !Debugger.IsAttached)
{
Log.RequestBodyMininumDataRateNotSatisfied(_context.ConnectionId, _frame.TraceIdentifier, _frame.RequestBodyMinimumDataRate.Rate);
Timeout();

View File

@ -3,7 +3,6 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;

View File

@ -0,0 +1,17 @@
// 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.Diagnostics;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
internal sealed class DebuggerWrapper : IDebugger
{
private DebuggerWrapper()
{ }
public static IDebugger Singleton { get; } = new DebuggerWrapper();
public bool IsAttached => Debugger.IsAttached;
}
}

View File

@ -0,0 +1,10 @@
// 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.
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
public interface IDebugger
{
bool IsAttached { get; }
}
}

View File

@ -50,19 +50,60 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_pipeFactory.Dispose();
}
[Fact]
public void DoesNotTimeOutWhenDebuggerIsAttached()
{
var mockDebugger = new Mock<IDebugger>();
mockDebugger.SetupGet(g => g.IsAttached).Returns(true);
_frameConnection.Debugger = mockDebugger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Input.Reader, _frameConnectionContext.Output);
var now = DateTimeOffset.Now;
_frameConnection.Tick(now);
_frameConnection.SetTimeout(1, TimeoutAction.SendTimeoutResponse);
_frameConnection.Tick(now.AddTicks(2).Add(Heartbeat.Interval));
Assert.False(_frameConnection.TimedOut);
}
[Fact]
public void DoesNotTimeOutWhenRequestBodyDoesNotSatisfyMinimumDataRateButDebuggerIsAttached()
{
var mockDebugger = new Mock<IDebugger>();
mockDebugger.SetupGet(g => g.IsAttached).Returns(true);
_frameConnection.Debugger = mockDebugger.Object;
var requestBodyMinimumDataRate = 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);
Assert.False(_frameConnection.TimedOut);
}
[Fact]
public void TimesOutWhenRequestBodyDoesNotSatisfyMinimumDataRate()
{
var requestBodyMinimumDataRate = 100;
var mockLogger = new Mock<IKestrelTrace>();
TickBodyWithMinimumDataRate(mockLogger.Object, requestBodyMinimumDataRate);
// Timed out
Assert.True(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Once);
}
private void TickBodyWithMinimumDataRate(IKestrelTrace logger, int requestBodyMinimumDataRate)
{
var requestBodyGracePeriod = TimeSpan.FromSeconds(5);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.RequestBodyMinimumDataRate =
new MinimumDataRate(rate: requestBodyMinimumDataRate, gracePeriod: requestBodyGracePeriod);
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnectionContext.ServiceContext.Log = logger;
_frameConnection.CreateFrame(new DummyApplication(context => Task.CompletedTask), _frameConnectionContext.Input.Reader, _frameConnectionContext.Output);
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Input.Reader, _frameConnectionContext.Output);
_frameConnection.Frame.Reset();
// Initialize timestamp
@ -75,11 +116,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
now += requestBodyGracePeriod + TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(1);
_frameConnection.Tick(now);
// Timed out
Assert.True(_frameConnection.TimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Once);
}
[Fact]

View File

@ -14,6 +14,11 @@ namespace Microsoft.AspNetCore.Testing
private readonly RequestDelegate _requestDelegate;
private readonly IHttpContextFactory _httpContextFactory;
public DummyApplication()
: this(_ => Task.CompletedTask)
{
}
public DummyApplication(RequestDelegate requestDelegate)
: this(requestDelegate, null)
{