Do not enforce timeouts when the debugger is attached
This commit is contained in:
parent
3fcd909d90
commit
9b4be69e9d
|
|
@ -44,6 +44,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
|
|
||||||
// For testing
|
// For testing
|
||||||
internal Frame Frame => _frame;
|
internal Frame Frame => _frame;
|
||||||
|
internal IDebugger Debugger { get; set; } = DebuggerWrapper.Singleton;
|
||||||
|
|
||||||
|
|
||||||
public bool TimedOut { get; private set; }
|
public bool TimedOut { get; private set; }
|
||||||
|
|
||||||
|
|
@ -263,14 +265,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
// TODO: Use PlatformApis.VolatileRead equivalent again
|
// TODO: Use PlatformApis.VolatileRead equivalent again
|
||||||
if (timestamp > Interlocked.Read(ref _timeoutTimestamp))
|
if (timestamp > Interlocked.Read(ref _timeoutTimestamp))
|
||||||
{
|
{
|
||||||
CancelTimeout();
|
if (!Debugger.IsAttached)
|
||||||
|
|
||||||
if (_timeoutAction == TimeoutAction.SendTimeoutResponse)
|
|
||||||
{
|
{
|
||||||
SetTimeoutResponse();
|
CancelTimeout();
|
||||||
}
|
|
||||||
|
|
||||||
Timeout();
|
if (_timeoutAction == TimeoutAction.SendTimeoutResponse)
|
||||||
|
{
|
||||||
|
SetTimeoutResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timeout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -285,7 +290,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
var elapsedSeconds = (double)_readTimingElapsedTicks / TimeSpan.TicksPerSecond;
|
var elapsedSeconds = (double)_readTimingElapsedTicks / TimeSpan.TicksPerSecond;
|
||||||
var rate = Interlocked.Read(ref _readTimingBytesRead) / elapsedSeconds;
|
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);
|
Log.RequestBodyMininumDataRateNotSatisfied(_context.ConnectionId, _frame.TraceIdentifier, _frame.RequestBodyMinimumDataRate.Rate);
|
||||||
Timeout();
|
Timeout();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
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.Internal.System.IO.Pipelines;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,19 +50,60 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
_pipeFactory.Dispose();
|
_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]
|
[Fact]
|
||||||
public void TimesOutWhenRequestBodyDoesNotSatisfyMinimumDataRate()
|
public void TimesOutWhenRequestBodyDoesNotSatisfyMinimumDataRate()
|
||||||
{
|
{
|
||||||
var requestBodyMinimumDataRate = 100;
|
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);
|
var requestBodyGracePeriod = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
_frameConnectionContext.ServiceContext.ServerOptions.Limits.RequestBodyMinimumDataRate =
|
_frameConnectionContext.ServiceContext.ServerOptions.Limits.RequestBodyMinimumDataRate =
|
||||||
new MinimumDataRate(rate: requestBodyMinimumDataRate, gracePeriod: requestBodyGracePeriod);
|
new MinimumDataRate(rate: requestBodyMinimumDataRate, gracePeriod: requestBodyGracePeriod);
|
||||||
|
|
||||||
var mockLogger = new Mock<IKestrelTrace>();
|
_frameConnectionContext.ServiceContext.Log = logger;
|
||||||
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
|
|
||||||
|
|
||||||
_frameConnection.CreateFrame(new DummyApplication(context => Task.CompletedTask), _frameConnectionContext.Input.Reader, _frameConnectionContext.Output);
|
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Input.Reader, _frameConnectionContext.Output);
|
||||||
_frameConnection.Frame.Reset();
|
_frameConnection.Frame.Reset();
|
||||||
|
|
||||||
// Initialize timestamp
|
// Initialize timestamp
|
||||||
|
|
@ -75,11 +116,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
now += requestBodyGracePeriod + TimeSpan.FromSeconds(1);
|
now += requestBodyGracePeriod + TimeSpan.FromSeconds(1);
|
||||||
_frameConnection.BytesRead(1);
|
_frameConnection.BytesRead(1);
|
||||||
_frameConnection.Tick(now);
|
_frameConnection.Tick(now);
|
||||||
|
|
||||||
// Timed out
|
|
||||||
Assert.True(_frameConnection.TimedOut);
|
|
||||||
mockLogger.Verify(logger =>
|
|
||||||
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), requestBodyMinimumDataRate), Times.Once);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,11 @@ namespace Microsoft.AspNetCore.Testing
|
||||||
private readonly RequestDelegate _requestDelegate;
|
private readonly RequestDelegate _requestDelegate;
|
||||||
private readonly IHttpContextFactory _httpContextFactory;
|
private readonly IHttpContextFactory _httpContextFactory;
|
||||||
|
|
||||||
|
public DummyApplication()
|
||||||
|
: this(_ => Task.CompletedTask)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DummyApplication(RequestDelegate requestDelegate)
|
public DummyApplication(RequestDelegate requestDelegate)
|
||||||
: this(requestDelegate, null)
|
: this(requestDelegate, null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue