private async Task KeepAliveTimeoutDoesNotApplyToUpgradedConnections()
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);
            var cts = new CancellationTokenSource();

            using (var server = CreateServer(testContext, upgradeCt: cts.Token))
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "GET /upgrade HTTP/1.1",
                        "Host:",
                        "Connection: Upgrade",
                        "",
                        "");

                    await connection.Receive(
                        "HTTP/1.1 101 Switching Protocols",
                        "Connection: Upgrade",
                        $"Date: {testContext.DateHeaderValue}",
                        "",
                        "");

                    for (var totalDelay = TimeSpan.Zero; totalDelay < _longDelay; totalDelay += _shortDelay)
                    {
                        testContext.MockSystemClock.UtcNow += _shortDelay;
                        heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);
                    }

                    cts.Cancel();

                    await connection.Receive("hello, world");
                }
        }
        public async Task ConnectionNotTimedOutWhileRequestBeingSent()
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);

            using (var server = CreateServer(testContext))
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "POST /consume HTTP/1.1",
                        "Host:",
                        "Transfer-Encoding: chunked",
                        "",
                        "");

                    await _firstRequestReceived.Task.DefaultTimeout();

                    for (var totalDelay = TimeSpan.Zero; totalDelay < _longDelay; totalDelay += _shortDelay)
                    {
                        await connection.Send(
                            "1",
                            "a",
                            "");

                        testContext.MockSystemClock.UtcNow += _shortDelay;
                        heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);
                    }

                    await connection.Send(
                        "0",
                        "",
                        "");
                    await ReceiveResponse(connection, testContext);
                }
        }
        private async Task ConnectionNotTimedOutWhileAppIsRunning()
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);
            var cts = new CancellationTokenSource();

            using (var server = CreateServer(testContext, longRunningCt: cts.Token))
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "GET /longrunning HTTP/1.1",
                        "Host:",
                        "",
                        "");

                    await _firstRequestReceived.Task.DefaultTimeout();

                    for (var totalDelay = TimeSpan.Zero; totalDelay < _longDelay; totalDelay += _shortDelay)
                    {
                        testContext.MockSystemClock.UtcNow += _shortDelay;
                        heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);
                    }

                    cts.Cancel();

                    await ReceiveResponse(connection, testContext);

                    await connection.Send(
                        "GET / HTTP/1.1",
                        "Host:",
                        "",
                        "");
                    await ReceiveResponse(connection, testContext);
                }
        }
Exemplo n.º 4
0
        public async Task RequestHeadersTimeoutCanceledAfterHeadersReceived()
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);

            using (var server = CreateServer(testContext))
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "POST / HTTP/1.1",
                        "Host:",
                        "Content-Length: 1",
                        "",
                        "");

                    // Min amount of time between requests that triggers a request headers timeout.
                    testContext.MockSystemClock.UtcNow += RequestHeadersTimeout + Heartbeat.Interval + TimeSpan.FromTicks(1);
                    heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);

                    await connection.Send(
                        "a");

                    await ReceiveResponse(connection, testContext);
                }
        }
Exemplo n.º 5
0
        private static ServiceContext CreateServiceContext(IOptions <KestrelServerOptions> options, ILoggerFactory loggerFactory)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (loggerFactory == null)
            {
                throw new ArgumentNullException(nameof(loggerFactory));
            }

            var serverOptions     = options.Value ?? new KestrelServerOptions();
            var logger            = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel");
            var trace             = new KestrelTrace(logger);
            var connectionManager = new HttpConnectionManager(
                trace,
                serverOptions.Limits.MaxConcurrentUpgradedConnections);

            var systemClock            = new SystemClock();
            var dateHeaderValueManager = new DateHeaderValueManager(systemClock);

            var httpHeartbeatManager = new HttpHeartbeatManager(connectionManager);
            var heartbeat            = new Heartbeat(
                new IHeartbeatHandler[] { dateHeaderValueManager, httpHeartbeatManager },
                systemClock,
                DebuggerWrapper.Singleton,
                trace);

            // TODO: This logic will eventually move into the IConnectionHandler<T> and off
            // the service context once we get to https://github.com/aspnet/KestrelHttpServer/issues/1662
            PipeScheduler scheduler = null;

            switch (serverOptions.ApplicationSchedulingMode)
            {
            case SchedulingMode.Default:
            case SchedulingMode.ThreadPool:
                scheduler = PipeScheduler.ThreadPool;
                break;

            case SchedulingMode.Inline:
                scheduler = PipeScheduler.Inline;
                break;

            default:
                throw new NotSupportedException(CoreStrings.FormatUnknownTransportMode(serverOptions.ApplicationSchedulingMode));
            }

            return(new ServiceContext
            {
                Log = trace,
                HttpParser = new HttpParser <Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information)),
                Scheduler = scheduler,
                SystemClock = systemClock,
                DateHeaderValueManager = dateHeaderValueManager,
                ConnectionManager = connectionManager,
                Heartbeat = heartbeat,
                ServerOptions = serverOptions,
            });
        }
Exemplo n.º 6
0
        public void InitializeHeartbeat()
        {
            MockSystemClock        = null;
            SystemClock            = new SystemClock();
            DateHeaderValueManager = new DateHeaderValueManager(SystemClock);

            var heartbeatManager = new HttpHeartbeatManager(ConnectionManager);

            Heartbeat = new Heartbeat(
                new IHeartbeatHandler[] { DateHeaderValueManager, heartbeatManager },
                SystemClock,
                DebuggerWrapper.Singleton,
                Log);
        }
        private async Task ConnectionTimesOutWhenOpenedButNoRequestSent()
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);

            using (var server = CreateServer(testContext))
                using (var connection = server.CreateConnection())
                {
                    // Min amount of time between requests that triggers a keep-alive timeout.
                    testContext.MockSystemClock.UtcNow += _keepAliveTimeout + Heartbeat.Interval + TimeSpan.FromTicks(1);
                    heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);

                    await connection.WaitForConnectionClose();
                }
        }
Exemplo n.º 8
0
        public async Task ConnectionAbortedWhenRequestLineNotReceivedInTime(string requestLine)
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);

            using (var server = CreateServer(testContext))
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(requestLine);

                    // Min amount of time between requests that triggers a request headers timeout.
                    testContext.MockSystemClock.UtcNow += RequestHeadersTimeout + Heartbeat.Interval + TimeSpan.FromTicks(1);
                    heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);

                    await ReceiveTimeoutResponse(connection, testContext);
                }
        }
Exemplo n.º 9
0
        public async Task HandshakeTimesOutAndIsLoggedAsDebug()
        {
            var loggerProvider = new HandshakeErrorLoggerProvider();

            LoggerFactory.AddProvider(loggerProvider);

            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);

            var      handshakeStartedTcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);
            TimeSpan handshakeTimeout    = default;

            using (var server = new TestServer(context => Task.CompletedTask,
                                               testContext,
                                               listenOptions =>
            {
                listenOptions.UseHttps(o =>
                {
                    o.ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
                    o.OnHandshakeStarted = () => handshakeStartedTcs.SetResult(null);

                    handshakeTimeout = o.HandshakeTimeout;
                });
            }))
            {
                using (var connection = server.CreateConnection())
                {
                    // HttpsConnectionAdapter dispatches via Task.Run() before starting the handshake.
                    // Wait for the handshake to start before advancing the system clock.
                    await handshakeStartedTcs.Task.DefaultTimeout();

                    // Min amount of time between requests that triggers a handshake timeout.
                    testContext.MockSystemClock.UtcNow += handshakeTimeout + Heartbeat.Interval + TimeSpan.FromTicks(1);
                    heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);

                    Assert.Equal(0, await connection.Stream.ReadAsync(new byte[1], 0, 1).DefaultTimeout());
                }
            }

            await loggerProvider.FilterLogger.LogTcs.Task.DefaultTimeout();

            Assert.Equal(2, loggerProvider.FilterLogger.LastEventId);
            Assert.Equal(LogLevel.Debug, loggerProvider.FilterLogger.LastLogLevel);
        }
Exemplo n.º 10
0
        // For testing
        internal KestrelServer(ITransportFactory transportFactory, ServiceContext serviceContext)
        {
            if (transportFactory == null)
            {
                throw new ArgumentNullException(nameof(transportFactory));
            }

            _transportFactory = transportFactory;
            ServiceContext = serviceContext;

            var httpHeartbeatManager = new HttpHeartbeatManager(serviceContext.ConnectionManager);
            _heartbeat = new Heartbeat(
                new IHeartbeatHandler[] { serviceContext.DateHeaderValueManager, httpHeartbeatManager },
                serviceContext.SystemClock, Trace);

            Features = new FeatureCollection();
            _serverAddresses = new ServerAddressesFeature();
            Features.Set(_serverAddresses);
        }
        public async Task ConnectionClosedWhenKeepAliveTimeoutExpires()
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);

            using (var server = CreateServer(testContext))
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "GET / HTTP/1.1",
                        "Host:",
                        "",
                        "");
                    await ReceiveResponse(connection, testContext);

                    // Min amount of time between requests that triggers a keep-alive timeout.
                    testContext.MockSystemClock.UtcNow += _keepAliveTimeout + Heartbeat.Interval + TimeSpan.FromTicks(1);
                    heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);

                    await connection.WaitForConnectionClose();
                }
        }
        public async Task ConnectionKeptAliveBetweenRequests()
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);

            using (var server = CreateServer(testContext))
                using (var connection = server.CreateConnection())
                {
                    for (var i = 0; i < 10; i++)
                    {
                        await connection.Send(
                            "GET / HTTP/1.1",
                            "Host:",
                            "",
                            "");
                        await ReceiveResponse(connection, testContext);

                        // Max amount of time between requests that doesn't trigger a keep-alive timeout.
                        testContext.MockSystemClock.UtcNow += _keepAliveTimeout + Heartbeat.Interval;
                        heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);
                    }
                }
        }
Exemplo n.º 13
0
        public async Task TimeoutNotResetOnEachRequestLineCharacterReceived()
        {
            var testContext      = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(testContext.ConnectionManager);

            using (var server = CreateServer(testContext))
                using (var connection = server.CreateConnection())
                {
                    // When the in-memory connection is aborted, the input PipeWriter is completed behind the scenes
                    // so eventually connection.Send() throws an InvalidOperationException.
                    await Assert.ThrowsAsync <InvalidOperationException>(async() =>
                    {
                        foreach (var ch in "POST / HTTP/1.1\r\nHost:\r\n\r\n")
                        {
                            await connection.Send(ch.ToString());

                            testContext.MockSystemClock.UtcNow += ShortDelay;
                            heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow);
                        }
                    });

                    await ReceiveTimeoutResponse(connection, testContext);
                }
        }
Exemplo n.º 14
0
        public async Task DoesNotEnforceRequestBodyMinimumDataRateOnUpgradedRequest()
        {
            var appEvent         = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);
            var delayEvent       = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);
            var serviceContext   = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(serviceContext.ConnectionManager);

            using (var server = new TestServer(async context =>
            {
                context.Features.Get <IHttpMinRequestBodyDataRateFeature>().MinDataRate =
                    new MinDataRate(bytesPerSecond: double.MaxValue, gracePeriod: Heartbeat.Interval + TimeSpan.FromTicks(1));

                using (var stream = await context.Features.Get <IHttpUpgradeFeature>().UpgradeAsync())
                {
                    appEvent.SetResult(null);

                    // Read once to go through one set of TryPauseTimingReads()/TryResumeTimingReads() calls
                    await stream.ReadAsync(new byte[1], 0, 1);

                    await delayEvent.Task.DefaultTimeout();

                    // Read again to check that the connection is still alive
                    await stream.ReadAsync(new byte[1], 0, 1);

                    // Send a response to distinguish from the timeout case where the 101 is still received, but without any content
                    var response = Encoding.ASCII.GetBytes("hello");
                    await stream.WriteAsync(response, 0, response.Length);
                }
            }, serviceContext))
            {
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "GET / HTTP/1.1",
                        "Host:",
                        "Connection: upgrade",
                        "",
                        "a");

                    await appEvent.Task.DefaultTimeout();

                    serviceContext.MockSystemClock.UtcNow += TimeSpan.FromSeconds(5);
                    heartbeatManager.OnHeartbeat(serviceContext.SystemClock.UtcNow);

                    delayEvent.SetResult(null);

                    await connection.Send("b");

                    await connection.Receive(
                        "HTTP/1.1 101 Switching Protocols",
                        "Connection: Upgrade",
                        "");

                    await connection.ReceiveStartsWith(
                        $"Date: ");

                    await connection.ReceiveForcedEnd(
                        "",
                        "hello");
                }
            }
        }
Exemplo n.º 15
0
        public async Task RequestTimesOutWhenRequestBodyNotReceivedAtSpecifiedMinimumRate()
        {
            var gracePeriod      = TimeSpan.FromSeconds(5);
            var serviceContext   = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(serviceContext.ConnectionManager);

            var appRunningEvent = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);

            using (var server = new TestServer(context =>
            {
                context.Features.Get <IHttpMinRequestBodyDataRateFeature>().MinDataRate =
                    new MinDataRate(bytesPerSecond: 1, gracePeriod: gracePeriod);

                // The server must call Request.Body.ReadAsync() *before* the test sets systemClock.UtcNow (which is triggered by the
                // server calling appRunningEvent.SetResult(null)).  If systemClock.UtcNow is set first, it's possible for the test to fail
                // due to the following race condition:
                //
                // 1. [test]    systemClock.UtcNow += gracePeriod + TimeSpan.FromSeconds(1);
                // 2. [server]  Heartbeat._timer is triggered, which calls HttpConnection.Tick()
                // 3. [server]  HttpConnection.Tick() calls HttpConnection.CheckForReadDataRateTimeout()
                // 4. [server]  HttpConnection.CheckForReadDataRateTimeout() is a no-op, since _readTimingEnabled is false,
                //              since Request.Body.ReadAsync() has not been called yet
                // 5. [server]  HttpConnection.Tick() sets _lastTimestamp = timestamp
                // 6. [server]  Request.Body.ReadAsync() is called
                // 6. [test]    systemClock.UtcNow is never updated again, so server timestamp is never updated,
                //              so HttpConnection.CheckForReadDataRateTimeout() is always a no-op until test fails
                //
                // This is a pretty tight race, since the once-per-second Heartbeat._timer needs to fire between the test updating
                // systemClock.UtcNow and the server calling Request.Body.ReadAsync().  But it happened often enough to cause
                // test flakiness in our CI (https://github.com/aspnet/KestrelHttpServer/issues/2539).
                //
                // For verification, I was able to induce the race by adding a sleep in the RequestDelegate:
                //     appRunningEvent.SetResult(null);
                //     Thread.Sleep(5000);
                //     return context.Request.Body.ReadAsync(new byte[1], 0, 1);

                var readTask = context.Request.Body.ReadAsync(new byte[1], 0, 1);
                appRunningEvent.SetResult(null);
                return(readTask);
            }, serviceContext))
            {
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "POST / HTTP/1.1",
                        "Host:",
                        "Content-Length: 1",
                        "",
                        "");

                    await appRunningEvent.Task.DefaultTimeout();

                    serviceContext.MockSystemClock.UtcNow += gracePeriod + TimeSpan.FromSeconds(1);
                    heartbeatManager.OnHeartbeat(serviceContext.SystemClock.UtcNow);

                    await connection.Receive(
                        "HTTP/1.1 408 Request Timeout",
                        "");

                    await connection.ReceiveForcedEnd(
                        "Connection: close",
                        $"Date: {serviceContext.DateHeaderValue}",
                        "Content-Length: 0",
                        "",
                        "");
                }
            }
        }
Exemplo n.º 16
0
        public async Task ConnectionClosedEvenIfAppSwallowsException()
        {
            var gracePeriod      = TimeSpan.FromSeconds(5);
            var serviceContext   = new TestServiceContext(LoggerFactory);
            var heartbeatManager = new HttpHeartbeatManager(serviceContext.ConnectionManager);

            var appRunningTcs         = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);
            var exceptionSwallowedTcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);

            using (var server = new TestServer(async context =>
            {
                context.Features.Get <IHttpMinRequestBodyDataRateFeature>().MinDataRate =
                    new MinDataRate(bytesPerSecond: 1, gracePeriod: gracePeriod);

                // See comment in RequestTimesOutWhenRequestBodyNotReceivedAtSpecifiedMinimumRate for
                // why we call ReadAsync before setting the appRunningEvent.
                var readTask = context.Request.Body.ReadAsync(new byte[1], 0, 1);
                appRunningTcs.SetResult(null);

                try
                {
                    await readTask;
                }
                catch (BadHttpRequestException ex) when(ex.StatusCode == 408)
                {
                    exceptionSwallowedTcs.SetResult(null);
                }
                catch (Exception ex)
                {
                    exceptionSwallowedTcs.SetException(ex);
                }

                var response = "hello, world";
                context.Response.ContentLength = response.Length;
                await context.Response.WriteAsync("hello, world");
            }, serviceContext))
            {
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "POST / HTTP/1.1",
                        "Host:",
                        "Content-Length: 1",
                        "",
                        "");

                    await appRunningTcs.Task.DefaultTimeout();

                    serviceContext.MockSystemClock.UtcNow += gracePeriod + TimeSpan.FromSeconds(1);
                    heartbeatManager.OnHeartbeat(serviceContext.SystemClock.UtcNow);

                    await exceptionSwallowedTcs.Task.DefaultTimeout();

                    await connection.Receive(
                        "HTTP/1.1 200 OK",
                        "");

                    await connection.ReceiveForcedEnd(
                        $"Date: {serviceContext.DateHeaderValue}",
                        "Content-Length: 12",
                        "",
                        "hello, world");
                }
            }
        }