public async Task GetDateHeaderValue_ReturnsUpdatedValueAfterIdle()
        {
            var now         = DateTimeOffset.UtcNow;
            var future      = now.AddSeconds(10);
            var systemClock = new MockSystemClock
            {
                UtcNow = now
            };
            var    timeWithoutRequestsUntilIdle = TimeSpan.FromMilliseconds(250);
            var    timerInterval          = TimeSpan.FromMilliseconds(100);
            var    dateHeaderValueManager = new DateHeaderValueManager(systemClock, timeWithoutRequestsUntilIdle, timerInterval);
            string result1;
            string result2;

            try
            {
                result1            = dateHeaderValueManager.GetDateHeaderValue();
                systemClock.UtcNow = future;
                // Wait for longer than the idle timeout to ensure the timer is stopped
                await Task.Delay(TimeSpan.FromSeconds(1));

                result2 = dateHeaderValueManager.GetDateHeaderValue();
            }
            finally
            {
                dateHeaderValueManager.Dispose();
            }

            Assert.Equal(now.ToString(Constants.RFC1123DateFormat), result1);
            Assert.Equal(future.ToString(Constants.RFC1123DateFormat), result2);
            Assert.True(systemClock.UtcNowCalled >= 2);
        }
        public void GetDateHeaderValue_ReturnsCachedValueBetweenTimerTicks()
        {
            var now         = DateTimeOffset.UtcNow;
            var future      = now.AddSeconds(10);
            var systemClock = new MockSystemClock
            {
                UtcNow = now
            };
            var    timeWithoutRequestsUntilIdle = TimeSpan.FromSeconds(1);
            var    timerInterval          = TimeSpan.FromSeconds(10);
            var    dateHeaderValueManager = new DateHeaderValueManager(systemClock, timeWithoutRequestsUntilIdle, timerInterval);
            string result1;
            string result2;

            try
            {
                result1            = dateHeaderValueManager.GetDateHeaderValue();
                systemClock.UtcNow = future;
                result2            = dateHeaderValueManager.GetDateHeaderValue();
            }
            finally
            {
                dateHeaderValueManager.Dispose();
            }

            Assert.Equal(now.ToString(Constants.RFC1123DateFormat), result1);
            Assert.Equal(now.ToString(Constants.RFC1123DateFormat), result2);
            Assert.Equal(1, systemClock.UtcNowCalled);
        }
Beispiel #3
0
 public void Initialize(DateHeaderValueManager dateValueManager)
 {
     ResponseHeaders.SetRawDate(
         dateValueManager.GetDateHeaderValue(),
         dateValueManager.GetDateHeaderValueBytes());
     ResponseHeaders.SetRawServer("Kestrel", BytesServer);
 }
        public void GetDateHeaderValue_ReturnsUpdatedValueAfterHeartbeat()
        {
            var now         = DateTimeOffset.UtcNow;
            var future      = now.AddSeconds(10);
            var systemClock = new MockSystemClock
            {
                UtcNow = now
            };

            var dateHeaderValueManager = new DateHeaderValueManager();

            dateHeaderValueManager.OnHeartbeat(now);

            var testKestrelTrace = new KestrelTrace(NullLoggerFactory.Instance);

            var mockHeartbeatHandler = new Mock <IHeartbeatHandler>();

            using (var heartbeat = new Heartbeat(new[] { dateHeaderValueManager, mockHeartbeatHandler.Object }, systemClock, DebuggerWrapper.Singleton, testKestrelTrace))
            {
                heartbeat.OnHeartbeat();

                Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);

                // Wait for the next heartbeat before verifying GetDateHeaderValues picks up new time.
                systemClock.UtcNow = future;

                heartbeat.OnHeartbeat();

                Assert.Equal(future.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
                Assert.Equal(4, systemClock.UtcNowCalled);
            }
        }
Beispiel #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,
            });
        }
Beispiel #6
0
 public void GlobalSetup()
 {
     _responseHeaders        = new HttpResponseHeaders();
     _responseHeadersDict    = _responseHeaders;
     _dateHeaderValueManager = new DateHeaderValueManager();
     _dateHeaderValueManager.OnHeartbeat(DateTimeOffset.Now);
     _writer = new Writer();
 }
Beispiel #7
0
        public void GetDateHeaderValue_ReturnsDateValueInRFC1123Format()
        {
            var now = DateTimeOffset.UtcNow;

            var dateHeaderValueManager = new DateHeaderValueManager(now);

            Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
        }
 public TestServiceContext()
 {
     AppLifetime            = new LifetimeNotImplemented();
     Log                    = new TestKestrelTrace();
     ThreadPool             = new LoggingThreadPool(Log);
     DateHeaderValueManager = new DateHeaderValueManager(systemClock: new MockSystemClock());
     DateHeaderValue        = DateHeaderValueManager.GetDateHeaderValues().String;
     ServerOptions          = new KestrelServerOptions {
         AddServerHeader = false
     };
     ServerOptions.ShutdownTimeout = TimeSpan.FromSeconds(5);
 }
Beispiel #9
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 FrameConnectionManager(
                trace,
                serverOptions.Limits.MaxConcurrentConnections,
                serverOptions.Limits.MaxConcurrentUpgradedConnections);

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

            // 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
            IThreadPool threadPool = null;

            switch (serverOptions.ApplicationSchedulingMode)
            {
            case SchedulingMode.Default:
            case SchedulingMode.ThreadPool:
                threadPool = new LoggingThreadPool(trace);
                break;

            case SchedulingMode.Inline:
                threadPool = new InlineLoggingThreadPool(trace);
                break;

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

            return(new ServiceContext
            {
                Log = trace,
                HttpParserFactory = frameParser => new HttpParser <FrameAdapter>(frameParser.Frame.ServiceContext.Log.IsEnabled(LogLevel.Information)),
                ThreadPool = threadPool,
                SystemClock = systemClock,
                DateHeaderValueManager = dateHeaderValueManager,
                ConnectionManager = connectionManager,
                ServerOptions = serverOptions
            });
        }
Beispiel #10
0
        public Headers CreateHeaders(DateHeaderValueManager dateValueManager)
        {
            Headers headers;

            if (!_headerPool.TryDequeue(out headers))
            {
                headers = new Headers();
            }

            headers.Initialize(dateValueManager);

            return(headers);
        }
 private void Initialize(ILoggerFactory loggerFactory, IKestrelTrace kestrelTrace)
 {
     LoggerFactory          = loggerFactory;
     Log                    = kestrelTrace;
     ThreadPool             = new LoggingThreadPool(Log);
     SystemClock            = new MockSystemClock();
     DateHeaderValueManager = new DateHeaderValueManager(SystemClock);
     ConnectionManager      = new HttpConnectionManager(Log, ResourceCounter.Unlimited);
     HttpParser             = new HttpParser <Http1ParsingHandler>(Log.IsEnabled(LogLevel.Information));
     ServerOptions          = new KestrelServerOptions
     {
         AddServerHeader = false
     };
 }
Beispiel #12
0
 public TestServiceContext(ILoggerFactory loggerFactory, IKestrelTrace kestrelTrace)
 {
     LoggerFactory          = loggerFactory;
     Log                    = kestrelTrace;
     ThreadPool             = new LoggingThreadPool(Log);
     SystemClock            = new MockSystemClock();
     DateHeaderValueManager = new DateHeaderValueManager(SystemClock);
     ConnectionManager      = new FrameConnectionManager(Log, ResourceCounter.Unlimited, ResourceCounter.Unlimited);
     HttpParserFactory      = frameAdapter => new HttpParser <FrameAdapter>(frameAdapter.Frame.ServiceContext.Log.IsEnabled(LogLevel.Information));
     ServerOptions          = new KestrelServerOptions
     {
         AddServerHeader = false
     };
 }
Beispiel #13
0
        public void InitializeHeartbeat()
        {
            var heartbeatManager = new HeartbeatManager(ConnectionManager);

            DateHeaderValueManager = new DateHeaderValueManager();
            Heartbeat = new Heartbeat(
                new IHeartbeatHandler[] { DateHeaderValueManager, heartbeatManager },
                new SystemClock(),
                DebuggerWrapper.Singleton,
                Log);

            MockSystemClock = null;
            SystemClock     = heartbeatManager;
        }
        public async Task RequestTimesOutWhenNotDrainedWithinDrainTimeoutPeriod()
        {
            // This test requires a real clock since we can't control when the drain timeout is set
            var serviceContext = new TestServiceContext(LoggerFactory);

            serviceContext.InitializeHeartbeat();

            // Ensure there's still a constant date header value.
            var clock = new MockSystemClock();
            var date  = new DateHeaderValueManager();

            date.OnHeartbeat(clock.UtcNow);
            serviceContext.DateHeaderValueManager = date;

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

            using (var server = new TestServer(context =>
            {
                context.Features.Get <IHttpMinRequestBodyDataRateFeature>().MinDataRate = null;

                appRunningEvent.SetResult(null);
                return(Task.CompletedTask);
            }, serviceContext))
            {
                using (var connection = server.CreateConnection())
                {
                    await connection.Send(
                        "POST / HTTP/1.1",
                        "Host:",
                        "Content-Length: 1",
                        "",
                        "");

                    await appRunningEvent.Task.DefaultTimeout();

                    // Disconnects after response completes due to the timeout
                    await connection.ReceiveEnd(
                        "HTTP/1.1 200 OK",
                        $"Date: {serviceContext.DateHeaderValue}",
                        "Content-Length: 0",
                        "",
                        "");
                }
                await server.StopAsync();
            }

            Assert.Contains(TestSink.Writes, w => w.EventId.Id == 32 && w.LogLevel == LogLevel.Information);
            Assert.Contains(TestSink.Writes, w => w.EventId.Id == 33 && w.LogLevel == LogLevel.Information);
        }
Beispiel #15
0
        private void Initialize(ILoggerFactory loggerFactory, KestrelTrace kestrelTrace)
        {
            LoggerFactory          = loggerFactory;
            Log                    = kestrelTrace;
            Scheduler              = PipeScheduler.ThreadPool;
            MockSystemClock        = new MockSystemClock();
            SystemClock            = MockSystemClock;
            DateHeaderValueManager = new DateHeaderValueManager();
            ConnectionManager      = new ConnectionManager(Log, ResourceCounter.Unlimited);
            HttpParser             = new HttpParser <Http1ParsingHandler>(Log.IsEnabled(LogLevel.Information));
            ServerOptions          = new KestrelServerOptions
            {
                AddServerHeader = false
            };

            DateHeaderValueManager.OnHeartbeat(SystemClock.UtcNow);
        }
Beispiel #16
0
        public void GetDateHeaderValue_ReturnsCachedValueBetweenTimerTicks()
        {
            var now         = DateTimeOffset.UtcNow;
            var future      = now.AddSeconds(10);
            var systemClock = new MockSystemClock
            {
                UtcNow = now
            };

            var dateHeaderValueManager = new DateHeaderValueManager(now);
            var testKestrelTrace       = new TestKestrelTrace();

            using (var heartbeat = new Heartbeat(new IHeartbeatHandler[] { dateHeaderValueManager }, systemClock, DebuggerWrapper.Singleton, testKestrelTrace))
            {
                Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
                systemClock.UtcNow = future;
                Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
            }

            Assert.Equal(0, systemClock.UtcNowCalled);
        }
        public void GetDateHeaderValue_ReturnsDateValueAfterDisposed()
        {
            var now         = DateTimeOffset.UtcNow;
            var future      = now.AddSeconds(10);
            var systemClock = new MockSystemClock
            {
                UtcNow = now
            };
            var timeWithoutRequestsUntilIdle = TimeSpan.FromSeconds(1);
            var timerInterval          = TimeSpan.FromSeconds(10);
            var dateHeaderValueManager = new DateHeaderValueManager(systemClock, timeWithoutRequestsUntilIdle, timerInterval);

            var result1 = dateHeaderValueManager.GetDateHeaderValue();

            dateHeaderValueManager.Dispose();
            systemClock.UtcNow = future;
            var result2 = dateHeaderValueManager.GetDateHeaderValue();

            Assert.Equal(now.ToString(Constants.RFC1123DateFormat), result1);
            Assert.Equal(future.ToString(Constants.RFC1123DateFormat), result2);
        }
Beispiel #18
0
        public void GetDateHeaderValue_ReturnsLastDateValueAfterHeartbeatDisposed()
        {
            var now         = DateTimeOffset.UtcNow;
            var future      = now.AddSeconds(10);
            var systemClock = new MockSystemClock
            {
                UtcNow = now
            };

            var dateHeaderValueManager = new DateHeaderValueManager(now);
            var testKestrelTrace       = new TestKestrelTrace();

            using (var heatbeat = new Heartbeat(new IHeartbeatHandler[] { dateHeaderValueManager }, systemClock, DebuggerWrapper.Singleton, testKestrelTrace))
            {
                heatbeat.OnHeartbeat();
                Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
            }

            systemClock.UtcNow = future;
            Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
        }
Beispiel #19
0
    private static ServiceContext CreateServiceContext(IOptions <KestrelServerOptions> options, ILoggerFactory loggerFactory, DiagnosticSource?diagnosticSource)
    {
        if (options == null)
        {
            throw new ArgumentNullException(nameof(options));
        }
        if (loggerFactory == null)
        {
            throw new ArgumentNullException(nameof(loggerFactory));
        }

        var serverOptions     = options.Value ?? new KestrelServerOptions();
        var trace             = new KestrelTrace(loggerFactory);
        var connectionManager = new ConnectionManager(
            trace,
            serverOptions.Limits.MaxConcurrentUpgradedConnections);

        var heartbeatManager       = new HeartbeatManager(connectionManager);
        var dateHeaderValueManager = new DateHeaderValueManager();

        var heartbeat = new Heartbeat(
            new IHeartbeatHandler[] { dateHeaderValueManager, heartbeatManager },
            new SystemClock(),
            DebuggerWrapper.Singleton,
            trace);

        return(new ServiceContext
        {
            Log = trace,
            Scheduler = PipeScheduler.ThreadPool,
            HttpParser = new HttpParser <Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information)),
            SystemClock = heartbeatManager,
            DateHeaderValueManager = dateHeaderValueManager,
            ConnectionManager = connectionManager,
            Heartbeat = heartbeat,
            ServerOptions = serverOptions,
            DiagnosticSource = diagnosticSource
        });
    }
Beispiel #20
0
    public static ServiceContext CreateServiceContext(
        KestrelServerOptions serverOptions,
        IHttpParser <Http1ParsingHandler> httpParser = null,
        PipeScheduler scheduler  = null,
        ISystemClock systemClock = null,
        DateHeaderValueManager dateHeaderValueManager = null,
        ConnectionManager connectionManager           = null,
        Heartbeat heartbeat = null)
    {
        var context = new ServiceContext
        {
            Log                    = new KestrelTrace(NullLoggerFactory.Instance),
            Scheduler              = scheduler,
            HttpParser             = httpParser,
            SystemClock            = systemClock,
            DateHeaderValueManager = dateHeaderValueManager,
            ConnectionManager      = connectionManager,
            Heartbeat              = heartbeat,
            ServerOptions          = serverOptions
        };

        return(context);
    }
        public void GetDateHeaderValue_ReturnsDateValueInRFC1123Format()
        {
            var now         = DateTimeOffset.UtcNow;
            var systemClock = new MockSystemClock
            {
                UtcNow = now
            };
            var    timeWithoutRequestsUntilIdle = TimeSpan.FromSeconds(1);
            var    timerInterval          = TimeSpan.FromSeconds(10);
            var    dateHeaderValueManager = new DateHeaderValueManager(systemClock, timeWithoutRequestsUntilIdle, timerInterval);
            string result;

            try
            {
                result = dateHeaderValueManager.GetDateHeaderValue();
            }
            finally
            {
                dateHeaderValueManager.Dispose();
            }

            Assert.Equal(now.ToString(Constants.RFC1123DateFormat), result);
        }
Beispiel #22
0
        public async Task ClientCanReceiveFullConnectionCloseResponseWithoutErrorAtALowDataRate()
        {
            var chunkSize  = 64 * 128 * 1024;
            var chunkCount = 4;
            var chunkData  = new byte[chunkSize];

            var requestAborted   = false;
            var appFuncCompleted = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);
            var mockKestrelTrace = new Mock <IKestrelTrace>();

            var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object)
            {
                ServerOptions =
                {
                    Limits                  =
                    {
                        MinResponseDataRate = new MinDataRate(bytesPerSecond: 240, gracePeriod: TimeSpan.FromSeconds(2))
                    }
                }
            };

            testContext.InitializeHeartbeat();
            var dateHeaderValueManager = new DateHeaderValueManager();

            dateHeaderValueManager.OnHeartbeat(DateTimeOffset.MinValue);
            testContext.DateHeaderValueManager = dateHeaderValueManager;

            var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0));

            async Task App(HttpContext context)
            {
                context.RequestAborted.Register(() =>
                {
                    requestAborted = true;
                });

                for (var i = 0; i < chunkCount; i++)
                {
                    await context.Response.BodyWriter.WriteAsync(new Memory <byte>(chunkData, 0, chunkData.Length), context.RequestAborted);
                }

                appFuncCompleted.SetResult(null);
            }

            using (var server = new TestServer(App, testContext, listenOptions))
            {
                using (var connection = server.CreateConnection())
                {
                    // Close the connection with the last request so AssertStreamCompleted actually completes.
                    await connection.Send(
                        "GET / HTTP/1.1",
                        "Host:",
                        "Connection: close",
                        "",
                        "");

                    await connection.Receive(
                        "HTTP/1.1 200 OK",
                        "Connection: close",
                        $"Date: {dateHeaderValueManager.GetDateHeaderValues().String}");

                    // Make sure consuming a single chunk exceeds the 2 second timeout.
                    var targetBytesPerSecond = chunkSize / 4;

                    // expectedBytes was determined by manual testing. A constant Date header is used, so this shouldn't change unless
                    // the response header writing logic or response body chunking logic itself changes.
                    await AssertStreamCompletedAtTargetRate(connection.Stream, expectedBytes : 33_553_556, targetBytesPerSecond);

                    await appFuncCompleted.Task.DefaultTimeout();
                }
                await server.StopAsync();
            }

            mockKestrelTrace.Verify(t => t.ResponseMinimumDataRateNotSatisfied(It.IsAny <string>(), It.IsAny <string>()), Times.Never());
            mockKestrelTrace.Verify(t => t.ConnectionStop(It.IsAny <string>()), Times.Once());
            Assert.False(requestAborted);
        }
Beispiel #23
0
        public async Task ConnectionNotClosedWhenClientSatisfiesMinimumDataRateGivenLargeResponseHeaders()
        {
            var headerSize         = 1024 * 1024; // 1 MB for each header value
            var headerCount        = 64;          // 64 MB of headers per response
            var requestCount       = 4;           // Minimum of 256 MB of total response headers
            var headerValue        = new string('a', headerSize);
            var headerStringValues = new StringValues(Enumerable.Repeat(headerValue, headerCount).ToArray());

            var requestAborted   = false;
            var mockKestrelTrace = new Mock <IKestrelTrace>();

            var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object)
            {
                ServerOptions =
                {
                    Limits                  =
                    {
                        MinResponseDataRate = new MinDataRate(bytesPerSecond: 240, gracePeriod: TimeSpan.FromSeconds(2))
                    }
                }
            };

            testContext.InitializeHeartbeat();
            var dateHeaderValueManager = new DateHeaderValueManager();

            dateHeaderValueManager.OnHeartbeat(DateTimeOffset.MinValue);
            testContext.DateHeaderValueManager = dateHeaderValueManager;

            var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0));

            async Task App(HttpContext context)
            {
                context.RequestAborted.Register(() =>
                {
                    requestAborted = true;
                });

                context.Response.Headers[$"X-Custom-Header"] = headerStringValues;
                context.Response.ContentLength = 0;

                await context.Response.BodyWriter.FlushAsync();
            }

            using (var server = new TestServer(App, testContext, listenOptions))
            {
                using (var connection = server.CreateConnection())
                {
                    for (var i = 0; i < requestCount - 1; i++)
                    {
                        await connection.Send(
                            "GET / HTTP/1.1",
                            "Host:",
                            "",
                            "");
                    }

                    await connection.Send(
                        "GET / HTTP/1.1",
                        "Host:",
                        "",
                        "");

                    await connection.Receive(
                        "HTTP/1.1 200 OK",
                        $"Date: {dateHeaderValueManager.GetDateHeaderValues().String}");

                    var minResponseSize    = headerSize * headerCount;
                    var minTotalOutputSize = requestCount * minResponseSize;

                    // Make sure consuming a single set of response headers exceeds the 2 second timeout.
                    var targetBytesPerSecond = minResponseSize / 4;

                    // expectedBytes was determined by manual testing. A constant Date header is used, so this shouldn't change unless
                    // the response header writing logic itself changes.
                    await AssertBytesReceivedAtTargetRate(connection.Stream, expectedBytes : 268_439_596, targetBytesPerSecond);

                    connection.ShutdownSend();
                    await connection.WaitForConnectionClose();
                }

                await server.StopAsync();
            }

            mockKestrelTrace.Verify(t => t.ResponseMinimumDataRateNotSatisfied(It.IsAny <string>(), It.IsAny <string>()), Times.Never());
            mockKestrelTrace.Verify(t => t.ConnectionStop(It.IsAny <string>()), Times.Once());
            Assert.False(requestAborted);
        }
Beispiel #24
0
        public async Task ConnectionNotClosedWhenClientSatisfiesMinimumDataRateGivenLargeResponseChunks()
        {
            var chunkSize  = 64 * 128 * 1024;
            var chunkCount = 4;
            var chunkData  = new byte[chunkSize];

            var requestAborted   = false;
            var appFuncCompleted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

            var testContext = new TestServiceContext(LoggerFactory)
            {
                ServerOptions =
                {
                    Limits                  =
                    {
                        MinResponseDataRate = new MinDataRate(bytesPerSecond: 240, gracePeriod: TimeSpan.FromSeconds(2))
                    }
                }
            };

            testContext.InitializeHeartbeat();
            var dateHeaderValueManager = new DateHeaderValueManager();

            dateHeaderValueManager.OnHeartbeat(DateTimeOffset.MinValue);
            testContext.DateHeaderValueManager = dateHeaderValueManager;

            var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0));

            async Task App(HttpContext context)
            {
                context.RequestAborted.Register(() =>
                {
                    requestAborted = true;
                });

                for (var i = 0; i < chunkCount; i++)
                {
                    await context.Response.BodyWriter.WriteAsync(new Memory <byte>(chunkData, 0, chunkData.Length), context.RequestAborted);
                }

                appFuncCompleted.SetResult();
            }

            await using (var server = new TestServer(App, testContext, listenOptions))
            {
                using (var connection = server.CreateConnection())
                {
                    // Close the connection with the last request so AssertStreamCompleted actually completes.
                    await connection.Send(
                        "GET / HTTP/1.1",
                        "Host:",
                        "",
                        "");

                    await connection.Receive(
                        "HTTP/1.1 200 OK",
                        $"Date: {dateHeaderValueManager.GetDateHeaderValues().String}");

                    // Make sure consuming a single chunk exceeds the 2 second timeout.
                    var targetBytesPerSecond = chunkSize / 4;

                    // expectedBytes was determined by manual testing. A constant Date header is used, so this shouldn't change unless
                    // the response header writing logic or response body chunking logic itself changes.
                    await AssertBytesReceivedAtTargetRate(connection.Stream, expectedBytes : 33_553_537, targetBytesPerSecond);

                    await appFuncCompleted.Task.DefaultTimeout();

                    connection.ShutdownSend();
                    await connection.WaitForConnectionClose();
                }
            }

            Assert.Equal(0, TestSink.Writes.Count(w => w.EventId.Name == "ResponseMinimumDataRateNotSatisfied"));
            Assert.Equal(1, TestSink.Writes.Count(w => w.EventId.Name == "ConnectionStop"));
            Assert.False(requestAborted);
        }
        public IDisposable Start(IFeatureCollection serverFeatures, Func <IFeatureCollection, Task> application)
        {
            var disposables = new Stack <IDisposable>();
            var disposer    = new Disposable(() =>
            {
                foreach (var disposable in disposables)
                {
                    disposable.Dispose();
                }
            });

            try
            {
                var information            = (KestrelServerInformation)serverFeatures.Get <IKestrelServerInformation>();
                var dateHeaderValueManager = new DateHeaderValueManager();
                var engine = new KestrelEngine(_libraryManager, new ServiceContext
                {
                    AppShutdown            = _appShutdownService,
                    Log                    = new KestrelTrace(_logger),
                    DateHeaderValueManager = dateHeaderValueManager
                });

                disposables.Push(engine);
                disposables.Push(dateHeaderValueManager);

                if (information.ThreadCount < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(information.ThreadCount),
                                                          information.ThreadCount,
                                                          "ThreadCount cannot be negative");
                }

                engine.Start(information.ThreadCount == 0 ? 1 : information.ThreadCount);
                bool atLeastOneListener = false;

                foreach (var address in information.Addresses)
                {
                    var parsedAddress = ServerAddress.FromUrl(address);
                    if (parsedAddress == null)
                    {
                        throw new FormatException("Unrecognized listening address: " + address);
                    }
                    else
                    {
                        atLeastOneListener = true;
                        disposables.Push(engine.CreateServer(
                                             parsedAddress.Scheme,
                                             parsedAddress.Host,
                                             parsedAddress.Port,
                                             async frame =>
                        {
                            var request = new ServerRequest(frame);
                            await application.Invoke(request.Features).ConfigureAwait(false);
                        }));
                    }
                }

                if (!atLeastOneListener)
                {
                    throw new InvalidOperationException("No recognized listening addresses were configured.");
                }

                return(disposer);
            }
            catch
            {
                disposer.Dispose();
                throw;
            }
        }
        public void Start <TContext>(IHttpApplication <TContext> application)
        {
            if (_disposables != null)
            {
                // The server has already started and/or has not been cleaned up yet
                throw new InvalidOperationException("Server has already started.");
            }
            _disposables = new Stack <IDisposable>();

            try
            {
                var componentFactory       = Features.Get <IHttpComponentFactory>();
                var dateHeaderValueManager = new DateHeaderValueManager();
                var trace  = new KestrelTrace(_logger);
                var engine = new KestrelEngine(new ServiceContext
                {
                    FrameFactory = context =>
                    {
                        return(new Frame <TContext>(application, context));
                    },
                    AppLifetime            = _applicationLifetime,
                    Log                    = trace,
                    ThreadPool             = new LoggingThreadPool(trace),
                    DateHeaderValueManager = dateHeaderValueManager,
                    ServerOptions          = Options,
                    HttpComponentFactory   = componentFactory
                });

                _disposables.Push(engine);
                _disposables.Push(dateHeaderValueManager);

                var threadCount = Options.ThreadCount;

                if (threadCount <= 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(threadCount),
                                                          threadCount,
                                                          "ThreadCount must be positive.");
                }

                engine.Start(threadCount);
                var atLeastOneListener = false;

                foreach (var address in _serverAddresses.Addresses)
                {
                    var parsedAddress = ServerAddress.FromUrl(address);
                    if (parsedAddress == null)
                    {
                        throw new FormatException("Unrecognized listening address: " + address);
                    }
                    else
                    {
                        atLeastOneListener = true;
                        _disposables.Push(engine.CreateServer(
                                              parsedAddress));
                    }
                }

                if (!atLeastOneListener)
                {
                    throw new InvalidOperationException("No recognized listening addresses were configured.");
                }
            }
            catch
            {
                Dispose();
                throw;
            }
        }
Beispiel #27
0
        public void Start <TContext>(IHttpApplication <TContext> application)
        {
            if (_disposables != null)
            {
                // The server has already started and/or has not been cleaned up yet
                throw new InvalidOperationException("Server has already started.");
            }
            _disposables = new Stack <IDisposable>();

            try
            {
                var dateHeaderValueManager = new DateHeaderValueManager();
                var trace  = new KestrelTrace(_logger);
                var engine = new KestrelEngine(new ServiceContext
                {
                    FrameFactory = context =>
                    {
                        return(new Frame <TContext>(application, context));
                    },
                    AppLifetime            = _applicationLifetime,
                    Log                    = trace,
                    ThreadPool             = new LoggingThreadPool(trace),
                    DateHeaderValueManager = dateHeaderValueManager,
                    ServerOptions          = Options
                });

                _disposables.Push(engine);
                _disposables.Push(dateHeaderValueManager);

                var threadCount = Options.ThreadCount;

                if (threadCount <= 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(threadCount),
                                                          threadCount,
                                                          "ThreadCount must be positive.");
                }

                if (!Constants.ECONNRESET.HasValue)
                {
                    _logger.LogWarning("Unable to determine ECONNRESET value on this platform.");
                }
                if (!Constants.EADDRINUSE.HasValue)
                {
                    _logger.LogWarning("Unable to determine EADDRINUSE value on this platform.");
                }

                engine.Start(threadCount);
                var atLeastOneListener = false;

                foreach (var address in _serverAddresses.Addresses.ToArray())
                {
                    var parsedAddress = ServerAddress.FromUrl(address);
                    atLeastOneListener = true;

                    if (!parsedAddress.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase))
                    {
                        _disposables.Push(engine.CreateServer(
                                              parsedAddress));
                    }
                    else
                    {
                        if (parsedAddress.Port == 0)
                        {
                            throw new InvalidOperationException("Dynamic port binding is not supported when binding to localhost. You must either bind to 127.0.0.1:0 or [::1]:0, or both.");
                        }

                        var ipv4Address = parsedAddress.WithHost("127.0.0.1");
                        var exceptions  = new List <UvException>();

                        try
                        {
                            _disposables.Push(engine.CreateServer(ipv4Address));
                        }
                        catch (AggregateException ex)
                        {
                            var uvException = ex.InnerException as UvException;

                            if (uvException != null && uvException.StatusCode != Constants.EADDRINUSE)
                            {
                                _logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv4 loopback interface.");
                                exceptions.Add(uvException);
                            }
                            else
                            {
                                throw;
                            }
                        }

                        var ipv6Address = parsedAddress.WithHost("[::1]");

                        try
                        {
                            _disposables.Push(engine.CreateServer(ipv6Address));
                        }
                        catch (AggregateException ex)
                        {
                            var uvException = ex.InnerException as UvException;

                            if (uvException != null && uvException.StatusCode != Constants.EADDRINUSE)
                            {
                                _logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv6 loopback interface.");
                                exceptions.Add(uvException);
                            }
                            else
                            {
                                throw;
                            }
                        }

                        if (exceptions.Count == 2)
                        {
                            var ex = new AggregateException(exceptions);
                            _logger.LogError(0, ex, $"Unable to bind to {parsedAddress.ToString()} on any loopback interface.");
                            throw ex;
                        }
                    }

                    // If requested port was "0", replace with assigned dynamic port.
                    _serverAddresses.Addresses.Remove(address);
                    _serverAddresses.Addresses.Add(parsedAddress.ToString());
                }

                if (!atLeastOneListener)
                {
                    throw new InvalidOperationException("No recognized listening addresses were configured.");
                }
            }
            catch
            {
                Dispose();
                throw;
            }
        }