Пример #1
0
        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);
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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);
        }
Пример #8
0
        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);
            }
        }
Пример #9
0
        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);
                }
            }
        }
Пример #10
0
        public static MessageBody For(Http2Stream context, MinDataRate minRequestBodyDataRate)
        {
            if (context.EndStreamReceived && !context.RequestBodyStarted)
            {
                return(ZeroContentLengthClose);
            }

            return(new Http2MessageBody(context, minRequestBodyDataRate));
        }
Пример #11
0
    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));
    }
Пример #12
0
        public static MessageBody For(Proto2Stream context, MinDataRate minRequestBodyDataRate)
        {
            if (context.ReceivedEmptyRequestBody)
            {
                return(ZeroContentLengthClose);
            }

            return(new Proto2MessageBody(context, minRequestBodyDataRate));
        }
Пример #13
0
        public static MessageBody For(Http2Stream context, MinDataRate minRequestBodyDataRate)
        {
            if (context.ReceivedEmptyRequestBody)
            {
                return ZeroContentLengthClose;
            }

            return new Http2MessageBody(context, minRequestBodyDataRate);
        }
Пример #14
0
 public void StartTimingReads(MinDataRate minRate)
 {
     lock (_readTimingLock)
     {
         _minReadRate            = minRate;
         _readTimingElapsedTicks = 0;
         _readTimingBytesRead    = 0;
         _readTimingEnabled      = true;
     }
 }
Пример #15
0
        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];
        }
Пример #17
0
        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();
        }
Пример #18
0
        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);
                }
            }
        }
Пример #21
0
        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);
        }
Пример #22
0
        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);
        }
Пример #23
0
        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);
        }
Пример #24
0
        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;
                }
            }
        }
Пример #25
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);
        }
Пример #26
0
        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)));
        }
Пример #28
0
        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();
        }
Пример #30
0
 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];
 }