public Http2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, Http2Connection http2Connection, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, MemoryPool <byte> memoryPool, ServiceContext serviceContext) { // Allow appending more data to the PipeWriter when a flush is pending. _outputWriter = new ConcurrentPipeWriter(outputPipeWriter, memoryPool, _writeLock); _connectionContext = connectionContext; _http2Connection = http2Connection; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = serviceContext.Log; _timeoutControl = timeoutControl; _minResponseDataRate = minResponseDataRate; _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, serviceContext.Log); _outgoingFrame = new Http2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; _hpackEncoder = new HPackEncoder(serviceContext.ServerOptions.AllowResponseHeaderCompression); }
public void WriteTimingAbortsConnectionWhenSmallWriteDoesNotCompleteWithinGracePeriod() { var systemClock = new MockSystemClock(); var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(5)); // Initialize timestamp var startTime = systemClock.UtcNow; _timeoutControl.Tick(startTime); // Should complete within 1 second, but the timeout is adjusted by adding Heartbeat.Interval _timeoutControl.BytesWrittenToBuffer(minRate, 100); _timeoutControl.StartTimingWrite(); // Tick just past 1s plus Heartbeat.Interval systemClock.UtcNow += TimeSpan.FromSeconds(1) + Heartbeat.Interval + TimeSpan.FromTicks(1); _timeoutControl.Tick(systemClock.UtcNow); // Still within grace period, not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Tick just past grace period (adjusted by Heartbeat.Interval) systemClock.UtcNow = startTime + minRate.GracePeriod + Heartbeat.Interval + TimeSpan.FromTicks(1); _timeoutControl.Tick(systemClock.UtcNow); _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.WriteDataRate), Times.Once); }
public void ReadTimingNotEnforcedWhenTimeoutIsSet() { var timeout = TimeSpan.FromSeconds(5); var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); var startTime = _systemClock.UtcNow; // Initialize timestamp _timeoutControl.Initialize(startTime.Ticks); _timeoutControl.StartRequestBody(minRate); _timeoutControl.StartTimingRead(); _timeoutControl.SetTimeout(timeout.Ticks, TimeoutReason.RequestBodyDrain); // Tick beyond grace period with low data rate _systemClock.UtcNow += TimeSpan.FromSeconds(3); _timeoutControl.BytesRead(1); _timeoutControl.Tick(_systemClock.UtcNow); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Tick just past timeout period, adjusted by Heartbeat.Interval _systemClock.UtcNow = startTime + timeout + Heartbeat.Interval + TimeSpan.FromTicks(1); _timeoutControl.Tick(_systemClock.UtcNow); // Timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.RequestBodyDrain), Times.Once); }
public void WriteTimingAbortsConnectionWhenRepeatedSmallWritesDoNotCompleteWithMinimumDataRate() { var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(5)); var numWrites = 5; var writeSize = 100; // Initialize timestamp var startTime = _systemClock.UtcNow; _timeoutControl.Initialize(startTime.Ticks); // 5 consecutive 100 byte writes. for (var i = 0; i < numWrites - 1; i++) { _timeoutControl.BytesWrittenToBuffer(minRate, writeSize); } // Stall the last write. _timeoutControl.BytesWrittenToBuffer(minRate, writeSize); _timeoutControl.StartTimingWrite(); // Move the clock forward Heartbeat.Interval + MinDataRate.GracePeriod + 4 seconds. // The grace period should only be added for the first write. The subsequent 4 100 byte writes should add 1 second each to the timeout given the 100 byte/s min rate. AdvanceClock(Heartbeat.Interval + minRate.GracePeriod + TimeSpan.FromSeconds((numWrites - 1) * writeSize / minRate.BytesPerSecond)); _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // On more tick forward triggers the timeout. _systemClock.UtcNow += TimeSpan.FromTicks(1); _timeoutControl.Tick(_systemClock.UtcNow); _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.WriteDataRate), Times.Once); }
public void WriteTimingTimeoutPushedOnConcurrentWrite() { var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); // Initialize timestamp _timeoutControl.Initialize(_systemClock.UtcNow.Ticks); // Should complete within 5 seconds, but the timeout is adjusted by adding Heartbeat.Interval _timeoutControl.BytesWrittenToBuffer(minRate, 500); _timeoutControl.StartTimingWrite(); // Start a concurrent write after 3 seconds, which should complete within 3 seconds (adjusted by Heartbeat.Interval) _timeoutControl.BytesWrittenToBuffer(minRate, 300); _timeoutControl.StartTimingWrite(); // Tick just past 5s plus Heartbeat.Interval, when the first write should have completed AdvanceClock(TimeSpan.FromSeconds(5) + Heartbeat.Interval + TimeSpan.FromTicks(1)); // Not timed out because the timeout was pushed by the second write _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Complete the first write, this should have no effect on the timeout _timeoutControl.StopTimingWrite(); // Tick just past +3s, when the second write should have completed AdvanceClock(TimeSpan.FromSeconds(3)); _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.WriteDataRate), Times.Once); }
public void RequestBodyMinimumDataRateNotEnforcedDuringGracePeriod() { var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); // Initialize timestamp var now = DateTimeOffset.UtcNow; _timeoutControl.Initialize(now.Ticks); _timeoutControl.StartRequestBody(minRate); _timeoutControl.StartTimingRead(); // Tick during grace period w/ low data rate now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(10); _timeoutControl.Tick(now); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Tick after grace period w/ low data rate now += TimeSpan.FromSeconds(1); _timeoutControl.Tick(now); now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(10); _timeoutControl.Tick(now); // Timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.ReadDataRate), Times.Once); }
public void RequestBodyDataRateIsAveragedOverTimeSpentReadingRequestBody() { var gracePeriod = TimeSpan.FromSeconds(2); var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: gracePeriod); // Initialize timestamp var now = DateTimeOffset.UtcNow; _timeoutControl.Initialize(now.Ticks); _timeoutControl.StartRequestBody(minRate); _timeoutControl.StartTimingRead(); // Set base data rate to 200 bytes/second now += TimeSpan.FromSeconds(1); _timeoutControl.Tick(now); now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(400); _timeoutControl.Tick(now); // Data rate: 200 bytes/second now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(200); _timeoutControl.Tick(now); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Data rate: 150 bytes/second now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(0); _timeoutControl.Tick(now); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Data rate: 120 bytes/second now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(0); _timeoutControl.Tick(now); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Data rate: 100 bytes/second now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(0); _timeoutControl.Tick(now); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Data rate: ~85 bytes/second now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(0); _timeoutControl.Tick(now); // Timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.ReadDataRate), Times.Once); }
public void BytesWrittenToBuffer(MinDataRate minRate, long count) { lock (_writeTimingLock) { // Add Heartbeat.Interval since this can be called right before the next heartbeat. var currentTimeUpperBound = Interlocked.Read(ref _lastTimestamp) + Heartbeat.Interval.Ticks; var ticksToCompleteWriteAtMinRate = TimeSpan.FromSeconds(count / minRate.BytesPerSecond).Ticks; // If ticksToCompleteWriteAtMinRate is less than the configured grace period, // allow that write to take up to the grace period to complete. Only add the grace period // to the current time and not to any accumulated timeout. var singleWriteTimeoutTimestamp = currentTimeUpperBound + Math.Max( minRate.GracePeriod.Ticks, ticksToCompleteWriteAtMinRate); // Don't penalize a connection for completing previous writes more quickly than required. // We don't want to kill a connection when flushing the chunk terminator just because the previous // chunk was large if the previous chunk was flushed quickly. // Don't add any grace period to this accumulated timeout because the grace period could // get accumulated repeatedly making the timeout for a bunch of consecutive small writes // far too conservative. var accumulatedWriteTimeoutTimestamp = _writeTimingTimeoutTimestamp + ticksToCompleteWriteAtMinRate; _writeTimingTimeoutTimestamp = Math.Max(singleWriteTimeoutTimestamp, accumulatedWriteTimeoutTimestamp); } }
public async Task ConnectionClosedWhenResponseNotDrainedAtMinimumDataRate(ListenOptions listenOptions) { var testContext = new TestServiceContext(LoggerFactory); var heartbeatManager = new HeartbeatManager(testContext.ConnectionManager); var minRate = new MinDataRate(16384, TimeSpan.FromSeconds(2)); await using (var server = new TestServer(context => { context.Features.Get <IHttpMinResponseDataRateFeature>().MinDataRate = minRate; return(Task.CompletedTask); }, testContext, listenOptions)) { using (var connection = server.CreateConnection()) { var transportConnection = connection.TransportConnection; var outputBufferedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); #pragma warning disable 0618 // TODO: Repalce OnWriterCompleted transportConnection.Output.OnWriterCompleted((ex, state) => { ((TaskCompletionSource)state).SetResult(); }, outputBufferedTcs); #pragma warning restore await connection.Send( "GET / HTTP/1.1", "Host:", "Connection: close", "", ""); // Wait for the drain timeout to be set. await outputBufferedTcs.Task.DefaultTimeout(); // Advance the clock to the grace period for (var i = 0; i < 2; i++) { testContext.MockSystemClock.UtcNow += TimeSpan.FromSeconds(1); heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow); } testContext.MockSystemClock.UtcNow += Heartbeat.Interval - TimeSpan.FromSeconds(.5); heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow); Assert.Null(transportConnection.AbortReason); testContext.MockSystemClock.UtcNow += TimeSpan.FromSeconds(1); heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow); Assert.NotNull(transportConnection.AbortReason); Assert.Equal(CoreStrings.ConnectionTimedBecauseResponseMininumDataRateNotSatisfied, transportConnection.AbortReason.Message); Assert.Single(LogMessages, w => w.EventId.Id == 28 && w.LogLevel <= LogLevel.Debug); } } }
public static MessageBody For(Http2Stream context, MinDataRate minRequestBodyDataRate) { if (context.EndStreamReceived && !context.RequestBodyStarted) { return(ZeroContentLengthClose); } return(new Http2MessageBody(context, minRequestBodyDataRate)); }
public long GetResponseDrainDeadline(long ticks, MinDataRate minRate) { // On grace period overflow, use max value. var gracePeriod = ticks + minRate.GracePeriod.Ticks; gracePeriod = gracePeriod >= 0 ? gracePeriod : long.MaxValue; return(Math.Max(_writeTimingTimeoutTimestamp, gracePeriod)); }
public static MessageBody For(Proto2Stream context, MinDataRate minRequestBodyDataRate) { if (context.ReceivedEmptyRequestBody) { return(ZeroContentLengthClose); } return(new Proto2MessageBody(context, minRequestBodyDataRate)); }
public static MessageBody For(Http2Stream context, MinDataRate minRequestBodyDataRate) { if (context.ReceivedEmptyRequestBody) { return ZeroContentLengthClose; } return new Http2MessageBody(context, minRequestBodyDataRate); }
public void StartTimingReads(MinDataRate minRate) { lock (_readTimingLock) { _minReadRate = minRate; _readTimingElapsedTicks = 0; _readTimingBytesRead = 0; _readTimingEnabled = true; } }
public void RequestBodyDataRateNotComputedOnPausedTime() { var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); // Initialize timestamp _timeoutControl.Initialize(_systemClock.UtcNow.Ticks); _timeoutControl.StartRequestBody(minRate); _timeoutControl.StartTimingRead(); // Tick at 3s, expected counted time is 3s, expected data rate is 200 bytes/second _systemClock.UtcNow += TimeSpan.FromSeconds(1); _timeoutControl.Tick(_systemClock.UtcNow); _systemClock.UtcNow += TimeSpan.FromSeconds(1); _timeoutControl.Tick(_systemClock.UtcNow); _systemClock.UtcNow += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(600); _timeoutControl.Tick(_systemClock.UtcNow); // Pause at 3.5s _systemClock.UtcNow += TimeSpan.FromSeconds(0.5); _timeoutControl.StopTimingRead(); // 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); _timeoutControl.Tick(_systemClock.UtcNow); // Tick at 6s, expected counted time is 4s, expected data rate is 150 bytes/second _systemClock.UtcNow += TimeSpan.FromSeconds(2); _timeoutControl.Tick(_systemClock.UtcNow); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Resume at 6.5s _systemClock.UtcNow += TimeSpan.FromSeconds(0.5); _timeoutControl.StartTimingRead(); // Tick at 9s, expected counted time is 6s, expected data rate is 100 bytes/second _systemClock.UtcNow += TimeSpan.FromSeconds(1.0); _timeoutControl.Tick(_systemClock.UtcNow); _systemClock.UtcNow += TimeSpan.FromSeconds(.5); _timeoutControl.Tick(_systemClock.UtcNow); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Tick at 10s, expected counted time is 7s, expected data rate drops below 100 bytes/second _systemClock.UtcNow += TimeSpan.FromSeconds(1); _timeoutControl.Tick(_systemClock.UtcNow); // Timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.ReadDataRate), Times.Once); }
//private int _unflushedBytes; public Http3FrameWriter(PipeWriter output, ConnectionContext connectionContext, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, MemoryPool <byte> memoryPool, IKestrelTrace log) { _outputWriter = output; _connectionContext = connectionContext; _timeoutControl = timeoutControl; _minResponseDataRate = minResponseDataRate; _memoryPool = memoryPool; _log = log; _outgoingFrame = new Http3RawFrame(); _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, log); _headerEncodingBuffer = new byte[_maxFrameSize]; }
public static void StartDrainTimeout(this ITimeoutControl timeoutControl, MinDataRate minDataRate, long?maxResponseBufferSize) { // If maxResponseBufferSize has no value, there's no backpressure and we can't reasonably time out draining. if (minDataRate == null || maxResponseBufferSize == null) { return; } // Ensure we have at least the grace period from this point to finish draining the response. timeoutControl.BytesWrittenToBuffer(minDataRate, 1); timeoutControl.StartTimingWrite(); }
protected override void OnReset() { ResetHttp1Features(); _requestTimedOut = false; _requestTargetForm = HttpRequestTarget.Unknown; _absoluteRequestTarget = null; _remainingRequestHeadersBytesAllowed = ServerOptions.Limits.MaxRequestHeadersTotalSize + 2; _requestCount++; MinRequestBodyDataRate = ServerOptions.Limits.MinRequestBodyDataRate; MinResponseDataRate = ServerOptions.Limits.MinResponseDataRate; }
public async Task WriteTimingAbortsConnectionWhenRepeadtedSmallWritesDoNotCompleteWithMinimumDataRate() { var systemClock = new MockSystemClock(); var minResponseDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(5)); var numWrites = 5; var writeSize = 100; var aborted = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); _httpConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate = minResponseDataRate; _httpConnectionContext.ServiceContext.SystemClock = systemClock; var mockLogger = new Mock <IKestrelTrace>(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; _httpConnection.Initialize(_httpConnectionContext.Transport); _httpConnection.Http1Connection.Reset(); _httpConnection.Http1Connection.RequestAborted.Register(() => { aborted.SetResult(null); }); // Initialize timestamp var startTime = systemClock.UtcNow; _httpConnection.Tick(startTime); // 5 consecutive 100 byte writes. for (var i = 0; i < numWrites - 1; i++) { _httpConnection.StartTimingWrite(writeSize); _httpConnection.StopTimingWrite(); } // Stall the last write. _httpConnection.StartTimingWrite(writeSize); // Move the clock forward Heartbeat.Interval + MinDataRate.GracePeriod + 4 seconds. // The grace period should only be added for the first write. The subsequent 4 100 byte writes should add 1 second each to the timeout given the 100 byte/s min rate. systemClock.UtcNow += Heartbeat.Interval + minResponseDataRate.GracePeriod + TimeSpan.FromSeconds((numWrites - 1) * writeSize / minResponseDataRate.BytesPerSecond); _httpConnection.Tick(systemClock.UtcNow); Assert.False(_httpConnection.RequestTimedOut); // On more tick forward triggers the timeout. systemClock.UtcNow += TimeSpan.FromTicks(1); _httpConnection.Tick(systemClock.UtcNow); Assert.True(_httpConnection.RequestTimedOut); await aborted.Task.TimeoutAfter(TimeSpan.FromSeconds(10)); }
public async Task ConnectionClosedWhenResponseNotDrainedAtMinimumDataRate(ListenOptions listenOptions) { var testContext = new TestServiceContext(LoggerFactory); var heartbeatManager = new HeartbeatManager(testContext.ConnectionManager); var minRate = new MinDataRate(16384, TimeSpan.FromSeconds(2)); using (var server = new TestServer(context => { context.Features.Get <IHttpMinResponseDataRateFeature>().MinDataRate = minRate; return(Task.CompletedTask); }, testContext, listenOptions)) { using (var connection = server.CreateConnection()) { var transportConnection = connection.TransportConnection; var outputBufferedTcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); transportConnection.Output.OnWriterCompleted((ex, state) => { ((TaskCompletionSource <object>)state).SetResult(null); }, outputBufferedTcs); await connection.Send( "GET / HTTP/1.1", "Host:", "Connection: close", "", ""); // Wait for the drain timeout to be set. await outputBufferedTcs.Task.DefaultTimeout(); testContext.MockSystemClock.UtcNow += Heartbeat.Interval + TimeSpan.FromSeconds(testContext.ServerOptions.Limits.MaxResponseBufferSize.Value * 2 / minRate.BytesPerSecond); heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow); Assert.Null(transportConnection.AbortReason); testContext.MockSystemClock.UtcNow += TimeSpan.FromTicks(1); heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow); Assert.NotNull(transportConnection.AbortReason); Assert.Equal(CoreStrings.ConnectionTimedBecauseResponseMininumDataRateNotSatisfied, transportConnection.AbortReason.Message); Assert.Single(TestApplicationErrorLogger.Messages, w => w.EventId.Id == 28 && w.LogLevel == LogLevel.Information); } } }
public void WriteTimingAbortsConnectionWhenWriteDoesNotCompleteWithMinimumDataRate() { var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); // Initialize timestamp _timeoutControl.Initialize(_systemClock.UtcNow.Ticks); // Should complete within 4 seconds, but the timeout is adjusted by adding Heartbeat.Interval _timeoutControl.BytesWrittenToBuffer(minRate, 400); _timeoutControl.StartTimingWrite(); // Tick just past 4s plus Heartbeat.Interval AdvanceClock(TimeSpan.FromSeconds(4) + Heartbeat.Interval + TimeSpan.FromTicks(1)); _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.WriteDataRate), Times.Once); }
public static void StartDrainTimeout(this ITimeoutControl timeoutControl, MinDataRate minDataRate, long?maxResponseBufferSize) { // If maxResponseBufferSize has no value, there's no backpressure and we can't reasonably timeout draining. if (minDataRate == null || maxResponseBufferSize == null) { return; } // With full backpressure and a connection adapter there could be 2 two pipes buffering. // We already validate that the buffer size is positive. // There's no reason to stop timing the write after the connection is closed. var oneBufferSize = maxResponseBufferSize.Value; var maxBufferedBytes = oneBufferSize < long.MaxValue / 2 ? oneBufferSize * 2 : long.MaxValue; timeoutControl.StartTimingWrite(minDataRate, maxBufferedBytes); }
public void ReadTimingNotEnforcedWhenLowConnectionInputFlowControlAvailability() { var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); var flowControl = new InputFlowControl(initialWindowSize: 2, minWindowSizeIncrement: 1); // Initialize timestamp var now = DateTimeOffset.UtcNow; _timeoutControl.Initialize(now.Ticks); _timeoutControl.InitializeHttp2(flowControl); _timeoutControl.StartRequestBody(minRate); _timeoutControl.StartTimingRead(); // Tick past grace period now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(100); _timeoutControl.Tick(now); now += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(100); _timeoutControl.Tick(now); // Induce low flow control availability flowControl.TryAdvance(2); // Read 0 bytes in 1 second now += TimeSpan.FromSeconds(1); _timeoutControl.Tick(now); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Relieve low flow control availability flowControl.TryUpdateWindow(2, out _); _timeoutControl.Tick(now); // Still not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Read 0 bytes in 1 second now += TimeSpan.FromSeconds(1); _timeoutControl.Tick(now);; // Timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.ReadDataRate), Times.Once); }
public void StartRequestBody(MinDataRate minRate) { lock (_readTimingLock) { // minRate is always KestrelServerLimits.MinRequestBodyDataRate for HTTP/2 which is the only protocol that supports concurrent request bodies. Debug.Assert(_concurrentIncompleteRequestBodies == 0 || minRate == _minReadRate, "Multiple simultaneous read data rates are not supported."); _minReadRate = minRate; _concurrentIncompleteRequestBodies++; if (_concurrentIncompleteRequestBodies == 1) { _readTimingElapsedTicks = 0; _readTimingBytesRead = 0; } } }
private void TickBodyWithMinimumDataRate(int bytesPerSecond) { var gracePeriod = TimeSpan.FromSeconds(5); var minRate = new MinDataRate(bytesPerSecond, gracePeriod); // Initialize timestamp var now = DateTimeOffset.UtcNow; _timeoutControl.Tick(now); _timeoutControl.StartTimingReads(minRate); // Tick after grace period w/ low data rate now += gracePeriod + TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(1); _timeoutControl.Tick(now); }
private void TickBodyWithMinimumDataRate(int bytesPerSecond) { var gracePeriod = TimeSpan.FromSeconds(5); var minRate = new MinDataRate(bytesPerSecond, gracePeriod); // Initialize timestamp _timeoutControl.Initialize(_systemClock.UtcNow.Ticks); _timeoutControl.StartRequestBody(minRate); _timeoutControl.StartTimingRead(); AdvanceClock(gracePeriod); // Tick after grace period w/ low data rate _systemClock.UtcNow += TimeSpan.FromSeconds(1); _timeoutControl.BytesRead(1); _timeoutControl.Tick(_systemClock.UtcNow); }
public void WriteTimingAbortsConnectionWhenSmallWriteDoesNotCompleteWithinGracePeriod() { var systemClock = new MockSystemClock(); var minResponseDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(5)); var aborted = new ManualResetEventSlim(); _httpConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate = minResponseDataRate; _httpConnectionContext.ServiceContext.SystemClock = systemClock; var mockLogger = new Mock <IKestrelTrace>(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); _httpConnection.Http1Connection.RequestAborted.Register(() => { aborted.Set(); }); // Initialize timestamp var startTime = systemClock.UtcNow; _httpConnection.Tick(startTime); // Should complete within 1 second, but the timeout is adjusted by adding Heartbeat.Interval _httpConnection.StartTimingWrite(100); // Tick just past 1s plus Heartbeat.Interval systemClock.UtcNow += TimeSpan.FromSeconds(1) + Heartbeat.Interval + TimeSpan.FromTicks(1); _httpConnection.Tick(systemClock.UtcNow); // Still within grace period, not timed out Assert.False(_httpConnection.RequestTimedOut); // Tick just past grace period (adjusted by Heartbeat.Interval) systemClock.UtcNow = startTime + minResponseDataRate.GracePeriod + Heartbeat.Interval + TimeSpan.FromTicks(1); _httpConnection.Tick(systemClock.UtcNow); Assert.True(_httpConnection.RequestTimedOut); Assert.True(aborted.Wait(TimeSpan.FromSeconds(10))); }
public void ReadTimingNotPausedWhenResumeCalledBeforeNextTick() { var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); // Initialize timestamp _timeoutControl.Initialize(_systemClock.UtcNow.Ticks); _timeoutControl.StartRequestBody(minRate); _timeoutControl.StartTimingRead(); // Tick at 2s, expected counted time is 2s, expected data rate is 100 bytes/second _systemClock.UtcNow += TimeSpan.FromSeconds(1); _timeoutControl.Tick(_systemClock.UtcNow); _systemClock.UtcNow += TimeSpan.FromSeconds(1); _timeoutControl.Tick(_systemClock.UtcNow); _timeoutControl.BytesRead(200); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Pause at 2.25s _systemClock.UtcNow += TimeSpan.FromSeconds(0.25); _timeoutControl.StopTimingRead(); // Resume at 2.5s _systemClock.UtcNow += TimeSpan.FromSeconds(0.25); _timeoutControl.StartTimingRead(); // Tick at 3s, expected counted time is 3s, expected data rate is 100 bytes/second _systemClock.UtcNow += TimeSpan.FromSeconds(0.5); _timeoutControl.BytesRead(100); _timeoutControl.Tick(_systemClock.UtcNow); // Not timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); // Tick at 4s, expected counted time is 4s, expected data rate drops below 100 bytes/second _systemClock.UtcNow += TimeSpan.FromSeconds(1); _timeoutControl.Tick(_systemClock.UtcNow); // Timed out _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.ReadDataRate), Times.Once); }
public async Task WriteTimingAbortsConnectionWhenSmallWriteDoesNotCompleteWithinGracePeriod() { var systemClock = new MockSystemClock(); var minResponseDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(5)); var aborted = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); _httpConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate = minResponseDataRate; _httpConnectionContext.ServiceContext.SystemClock = systemClock; var mockLogger = new Mock <IKestrelTrace>(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; _httpConnection.Initialize(_httpConnectionContext.Transport); _httpConnection.Http1Connection.Reset(); _httpConnection.Http1Connection.RequestAborted.Register(() => { aborted.SetResult(null); }); // Initialize timestamp var startTime = systemClock.UtcNow; _httpConnection.Tick(startTime); // Should complete within 1 second, but the timeout is adjusted by adding Heartbeat.Interval _httpConnection.StartTimingWrite(100); // Tick just past 1s plus Heartbeat.Interval systemClock.UtcNow += TimeSpan.FromSeconds(1) + Heartbeat.Interval + TimeSpan.FromTicks(1); _httpConnection.Tick(systemClock.UtcNow); // Still within grace period, not timed out Assert.False(_httpConnection.RequestTimedOut); // Tick just past grace period (adjusted by Heartbeat.Interval) systemClock.UtcNow = startTime + minResponseDataRate.GracePeriod + Heartbeat.Interval + TimeSpan.FromTicks(1); _httpConnection.Tick(systemClock.UtcNow); Assert.True(_httpConnection.RequestTimedOut); await aborted.Task.DefaultTimeout(); }
public Http2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, Http2Connection http2Connection, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, IKestrelTrace log) { _outputWriter = outputPipeWriter; _connectionContext = connectionContext; _http2Connection = http2Connection; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = log; _timeoutControl = timeoutControl; _minResponseDataRate = minResponseDataRate; _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, log); _outgoingFrame = new Http2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; }