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);
            }
        }
Example #2
0
 public void GlobalSetup()
 {
     _responseHeaders        = new HttpResponseHeaders();
     _responseHeadersDict    = _responseHeaders;
     _dateHeaderValueManager = new DateHeaderValueManager();
     _dateHeaderValueManager.OnHeartbeat(DateTimeOffset.Now);
     _writer = new Writer();
 }
        public void GetDateHeaderValue_ReturnsDateValueInRFC1123Format()
        {
            var now = DateTimeOffset.UtcNow;

            var dateHeaderValueManager = new DateHeaderValueManager();

            dateHeaderValueManager.OnHeartbeat(now);

            Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
        }
        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);
        }
Example #5
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);
        }
        public void GetDateHeaderValue_ReturnsLastDateValueAfterHeartbeatDisposed()
        {
            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);

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

            systemClock.UtcNow = future;
            Assert.Equal(now.ToString(Rfc1123DateFormat), dateHeaderValueManager.GetDateHeaderValues().String);
        }
        public void GetDateHeaderValue_ReturnsCachedValueBetweenTimerTicks()
        {
            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 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);
        }
Example #8
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);
        }
Example #9
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);
        }
Example #10
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);
        }