public void LogsInformationWhenKestrelAddressesAreOverridden() { var logger = new TestApplicationErrorLogger(); var overriddenAddress = "http://localhost:5000"; var addresses = new ServerAddressesFeature(); addresses.InternalCollection.Add(overriddenAddress); var options = new KestrelServerOptions(); options.ListenAnyIP(8080); var addressBindContext = TestContextFactory.CreateAddressBindContext( addresses, options, logger, endpoint => Task.CompletedTask); addressBindContext.ServerAddressesFeature.PreferHostingUrls = true; var bindTask = AddressBinder.BindAsync(options.ListenOptions, addressBindContext, CancellationToken.None); Assert.True(bindTask.IsCompletedSuccessfully); var log = Assert.Single(logger.Messages); Assert.Equal(LogLevel.Information, log.LogLevel); Assert.Equal(CoreStrings.FormatOverridingWithPreferHostingUrls(nameof(addressBindContext.ServerAddressesFeature.PreferHostingUrls), overriddenAddress), log.Message); }
public async Task ThrowingResultsIn500Response(ServiceContext testContext) { bool onStartingCalled = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(frame => { var response = frame.Get <IHttpResponseFeature>(); response.OnStarting(_ => { onStartingCalled = true; return(Task.FromResult <object>(null)); }, null); // Anything added to the ResponseHeaders dictionary is ignored response.Headers.Clear(); response.Headers["Content-Length"] = "11"; throw new Exception(); }, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "Connection: close", "", ""); Assert.False(onStartingCalled); Assert.Equal(2, testLogger.ApplicationErrorsLogged); } } }
private async Task RegisterDefaultServerAddresses_Success(IEnumerable <string> addresses) { var testLogger = new TestApplicationErrorLogger(); var hostBuilder = new WebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel() .ConfigureLogging(builder => builder .AddProvider(new KestrelTestLoggerProvider(testLogger)) .SetMinimumLevel(LogLevel.Debug)) .Configure(ConfigureEchoAddress); using (var host = hostBuilder.Build()) { host.Start(); Assert.Equal(5000, host.GetPort()); Assert.Single(testLogger.Messages, log => log.LogLevel == LogLevel.Debug && string.Equals(CoreStrings.FormatBindingToDefaultAddress(Constants.DefaultServerAddress), log.Message, StringComparison.Ordinal)); foreach (var address in addresses) { Assert.Equal(new Uri(address).ToString(), await HttpClientSlim.GetStringAsync(address)); } } }
public void StartWithMaxRequestBufferSizeLessThanMaxRequestHeadersTotalSizeThrows(long maxRequestBufferSize, int maxRequestHeadersTotalSize) { var testLogger = new TestApplicationErrorLogger { ThrowOnCriticalErrors = false }; var options = new KestrelServerOptions { Limits = { MaxRequestBufferSize = maxRequestBufferSize, MaxRequestLineSize = (int)maxRequestBufferSize, MaxRequestHeadersTotalSize = maxRequestHeadersTotalSize } }; using (var server = CreateServer(options, testLogger)) { var exception = Assert.Throws <InvalidOperationException>(() => StartDummyApplication(server)); Assert.Equal( CoreStrings.FormatMaxRequestBufferSmallerThanRequestHeaderBuffer(maxRequestBufferSize, maxRequestHeadersTotalSize), exception.Message); Assert.Equal(1, testLogger.CriticalErrorsLogged); } }
private async Task TestError <TException>(HttpProtocols serverProtocols, string expectedErrorMessage) where TException : Exception { var logger = new TestApplicationErrorLogger(); var loggerProvider = new Mock <ILoggerProvider>(); loggerProvider .Setup(provider => provider.CreateLogger(It.IsAny <string>())) .Returns(logger); var builder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(loggingBuilder => loggingBuilder.AddProvider(loggerProvider.Object)) .UseKestrel(options => options.Listen(IPAddress.Loopback, 0, listenOptions => listenOptions.Protocols = serverProtocols)) .Configure(app => app.Run(context => Task.CompletedTask)); using (var host = builder.Build()) { host.Start(); using (var connection = new TestConnection(host.GetPort())) { await connection.WaitForConnectionClose().TimeoutAfter(TimeSpan.FromSeconds(30)); } } Assert.Single(logger.Messages, message => message.LogLevel == LogLevel.Error && message.EventId.Id == 0 && message.Message == expectedErrorMessage); }
public async Task ImmediateShutdownDuringOnConnectionAsyncDoesNotCrash() { var waitingConnectionAdapter = new WaitingConnectionAdapter(); var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)) { ConnectionAdapters = { waitingConnectionAdapter } }; var serviceContext = new TestServiceContext(LoggerFactory); await using (var server = new TestServer(TestApp.EchoApp, serviceContext, listenOptions)) { Task stopTask; using (var connection = server.CreateConnection()) { var closingMessageTask = TestApplicationErrorLogger.WaitForMessage(m => m.Message.Contains(CoreStrings.ServerShutdownDuringConnectionInitialization)); stopTask = server.StopAsync(); await closingMessageTask.DefaultTimeout(); waitingConnectionAdapter.Complete(); } await stopTask; } }
public async Task PipeConnectionsWithWrongMessageAreLoggedAndIgnored() { var libuv = new LibuvFunctions(); var endpoint = new IPEndPoint(IPAddress.Loopback, 0); var logger = new TestApplicationErrorLogger(); var transportContextPrimary = new TestLibuvTransportContext { Log = logger }; var transportContextSecondary = new TestLibuvTransportContext(); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); // Start primary listener var libuvThreadPrimary = new LibuvThread(libuv, transportContextPrimary); await libuvThreadPrimary.StartAsync(); var listenerPrimary = new ListenerPrimary(transportContextPrimary); await listenerPrimary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadPrimary); var address = GetUri(listenerPrimary.EndPoint); // Add secondary listener with wrong pipe message var libuvThreadSecondary = new LibuvThread(libuv, transportContextSecondary); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, Guid.NewGuid().ToByteArray(), endpoint, libuvThreadSecondary); // Wait up to 10 seconds for error to be logged for (var i = 0; i < 10 && logger.TotalErrorsLogged == 0; i++) { await Task.Delay(100); } // TCP Connections don't get round-robined. This should time out if the request goes to the secondary listener for (int i = 0; i < 3; i++) { using var socket = await HttpClientSlim.GetSocket(address); await using var connection = await listenerPrimary.AcceptAsync().AsTask().DefaultTimeout(); } await listenerSecondary.DisposeAsync(); await libuvThreadSecondary.StopAsync(TimeSpan.FromSeconds(5)); await listenerPrimary.DisposeAsync(); await libuvThreadPrimary.StopAsync(TimeSpan.FromSeconds(5)); Assert.Equal(1, logger.TotalErrorsLogged); var errorMessage = logger.Messages.First(m => m.LogLevel == LogLevel.Error); Assert.IsType <IOException>(errorMessage.Exception); Assert.Contains("Bad data", errorMessage.Exception.ToString()); }
public async Task GracefulTurnsAbortiveIfRequestsDoNotFinish() { var requestStarted = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); var requestUnblocked = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); var memoryPoolFactory = new DiagnosticMemoryPoolFactory(allowLateReturn: true); var testContext = new TestServiceContext(LoggerFactory) { MemoryPoolFactory = memoryPoolFactory.Create, ExpectedConnectionMiddlewareCount = 1 }; TestApplicationErrorLogger.ThrowOnUngracefulShutdown = false; // Abortive shutdown leaves one request hanging using (var server = new TestServer(async context => { requestStarted.SetResult(null); await requestUnblocked.Task.DefaultTimeout(); await context.Response.WriteAsync("hello world " + context.Request.Protocol); }, testContext, kestrelOptions => { kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; listenOptions.UseHttps(_x509Certificate2); }); }, _ => { })) { var requestTask = Client.GetStringAsync($"https://localhost:{server.Port}/"); Assert.False(requestTask.IsCompleted); await requestStarted.Task.DefaultTimeout(); // Wait for the graceful shutdown log before canceling the token passed to StopAsync and triggering an ungraceful shutdown. // Otherwise, graceful shutdown might be skipped causing there to be no corresponding log. https://github.com/aspnet/AspNetCore/issues/6556 var closingMessageTask = TestApplicationErrorLogger.WaitForMessage(m => m.Message.Contains("is closing.")).DefaultTimeout(); var cts = new CancellationTokenSource(); var stopServerTask = server.StopAsync(cts.Token).DefaultTimeout(); await closingMessageTask; cts.Cancel(); await stopServerTask; } Assert.Contains(TestApplicationErrorLogger.Messages, m => m.Message.Contains("is closing.")); Assert.Contains(TestApplicationErrorLogger.Messages, m => m.Message.Contains("is closed. The last processed stream ID was 1.")); Assert.Contains(TestApplicationErrorLogger.Messages, m => m.Message.Contains("Some connections failed to close gracefully during server shutdown.")); Assert.DoesNotContain(TestApplicationErrorLogger.Messages, m => m.Message.Contains("Request finished in")); requestUnblocked.SetResult(null); await memoryPoolFactory.WhenAllBlocksReturned(TestConstants.DefaultTimeout); }
public TestLibuvTransportContext() { var logger = new TestApplicationErrorLogger(); AppLifetime = new LifetimeNotImplemented(); Log = new LibuvTrace(logger); Options = new LibuvTransportOptions { ThreadCount = 1 }; }
public TestLibuvTransportContext() { var logger = new TestApplicationErrorLogger(); AppLifetime = new LifetimeNotImplemented(); Log = logger; #pragma warning disable CS0618 Options = new LibuvTransportOptions { ThreadCount = 1 }; #pragma warning restore CS0618 }
public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(ServiceContext testContext) { var onCompletedCalled1 = false; var onCompletedCalled2 = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.OnCompleted(_ => { onCompletedCalled1 = true; throw new Exception(); }, null); response.OnCompleted(_ => { onCompletedCalled2 = true; throw new Exception(); }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); }, testContext)) { using (var connection = new TestConnection()) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello World"); } // All OnCompleted callbacks should be called even if they throw. Assert.Equal(2, testLogger.ApplicationErrorsLogged); Assert.True(onCompletedCalled1); Assert.True(onCompletedCalled2); } }
public void ReadStopIsIdempotent() { var libuvTrace = new TestApplicationErrorLogger(); using (var uvLoopHandle = new UvLoopHandle(libuvTrace)) using (var uvTcpHandle = new UvTcpHandle(libuvTrace)) { uvLoopHandle.Init(new MockLibuv()); uvTcpHandle.Init(uvLoopHandle, null); UvStreamHandle uvStreamHandle = uvTcpHandle; uvStreamHandle.ReadStop(); uvStreamHandle.ReadStop(); } }
public void StartWithInvalidAddressThrows() { var testLogger = new TestApplicationErrorLogger { ThrowOnCriticalErrors = false }; using (var server = CreateServer(CreateServerOptions(), testLogger)) { server.Features.Get <IServerAddressesFeature>().Addresses.Add("http:/asdf"); var exception = Assert.Throws <FormatException>(() => StartDummyApplication(server)); Assert.Contains("Invalid url", exception.Message); Assert.Equal(1, testLogger.CriticalErrorsLogged); } }
private LibuvOuputProcessor CreateOutputProducer(PipeOptions pipeOptions, CancellationTokenSource cts = null) { var pair = DuplexPipe.CreateConnectionPair(pipeOptions, pipeOptions); var logger = new TestApplicationErrorLogger(); var serviceContext = new TestServiceContext { Log = new TestKestrelTrace(logger), Scheduler = PipeScheduler.Inline }; var transportContext = new TestLibuvTransportContext { Log = new LibuvTrace(logger) }; var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log); var consumer = new LibuvOutputConsumer(pair.Application.Input, _libuvThread, socket, "0", transportContext.Log); var connectionFeatures = new FeatureCollection(); connectionFeatures.Set(Mock.Of <IConnectionLifetimeFeature>()); var http1Connection = new Http1Connection(new HttpConnectionContext { ServiceContext = serviceContext, ConnectionContext = Mock.Of <ConnectionContext>(), ConnectionFeatures = connectionFeatures, MemoryPool = _memoryPool, TimeoutControl = Mock.Of <ITimeoutControl>(), Transport = pair.Transport }); if (cts != null) { http1Connection.RequestAborted.Register(cts.Cancel); } var outputTask = WriteOutputAsync(consumer, pair.Application.Input, http1Connection); var processor = new LibuvOuputProcessor { ProcessingTask = outputTask, OutputProducer = (Http1OutputProducer)http1Connection.Output, PipeWriter = pair.Transport.Output, }; return(processor); }
public void StartWarnsWhenIgnoringIServerAddressesFeature(string ignoredAddress) { var testLogger = new TestApplicationErrorLogger(); var kestrelOptions = new KestrelServerOptions(); // Directly configuring an endpoint using Listen causes the IServerAddressesFeature to be ignored. kestrelOptions.Listen(IPAddress.Loopback, 0); using (var server = CreateServer(kestrelOptions, testLogger)) { server.Features.Get <IServerAddressesFeature>().Addresses.Add(ignoredAddress); StartDummyApplication(server); var warning = testLogger.Messages.Single(log => log.LogLevel == LogLevel.Warning); Assert.Contains("Overriding", warning.Message); } }
public void StartWithPathBaseInAddressThrows() { var testLogger = new TestApplicationErrorLogger { ThrowOnCriticalErrors = false }; using (var server = CreateServer(new KestrelServerOptions(), testLogger)) { server.Features.Get <IServerAddressesFeature>().Addresses.Add("http://127.0.0.1:0/base"); var exception = Assert.Throws <InvalidOperationException>(() => StartDummyApplication(server)); Assert.Equal( $"A path base can only be configured using {nameof(IApplicationBuilder)}.UsePathBase().", exception.Message); Assert.Equal(1, testLogger.CriticalErrorsLogged); } }
public void StartWithHttpsAddressThrows() { var testLogger = new TestApplicationErrorLogger { ThrowOnCriticalErrors = false }; using (var server = CreateServer(new KestrelServerOptions(), testLogger)) { server.Features.Get <IServerAddressesFeature>().Addresses.Add("https://127.0.0.1:0"); var exception = Assert.Throws <InvalidOperationException>(() => StartDummyApplication(server)); Assert.Equal( $"HTTPS endpoints can only be configured using {nameof(KestrelServerOptions)}.{nameof(KestrelServerOptions.Listen)}().", exception.Message); Assert.Equal(1, testLogger.CriticalErrorsLogged); } }
public async Task ThrowingAfterPartialWriteKillsConnection(ServiceContext testContext) { bool onStartingCalled = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.OnStarting(_ => { onStartingCalled = true; return(Task.FromResult <object>(null)); }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello"), 0, 5); throw new Exception(); }, testContext)) { using (var connection = new TestConnection()) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello"); Assert.True(onStartingCalled); Assert.Equal(1, testLogger.ApplicationErrorsLogged); } } }
private async Task RegisterDefaultServerAddresses_Success(IEnumerable <string> addresses, bool mockHttps = false) { var testLogger = new TestApplicationErrorLogger(); var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel(options => { if (mockHttps) { options.DefaultCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword"); } }) .ConfigureLogging(builder => builder .AddProvider(new KestrelTestLoggerProvider(testLogger)) .SetMinimumLevel(LogLevel.Debug)) .Configure(ConfigureEchoAddress); using (var host = hostBuilder.Build()) { host.Start(); Assert.Equal(5000, host.GetPort()); if (mockHttps) { Assert.Contains(5001, host.GetPorts()); } Assert.Single(testLogger.Messages, log => log.LogLevel == LogLevel.Debug && (string.Equals(CoreStrings.FormatBindingToDefaultAddresses(Constants.DefaultServerAddress, Constants.DefaultServerHttpsAddress), log.Message, StringComparison.Ordinal) || string.Equals(CoreStrings.FormatBindingToDefaultAddress(Constants.DefaultServerAddress), log.Message, StringComparison.Ordinal))); foreach (var address in addresses) { Assert.Equal(new Uri(address).ToString(), await HttpClientSlim.GetStringAsync(address, validateCertificate: false)); } } }
public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_IfPreferHostingUrlsFalse() { var useUrlsAddress = $"http://127.0.0.1:0"; var testLogger = new TestApplicationErrorLogger(); var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => { listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword"); }); }) .UseUrls($"http://127.0.0.1:0") .PreferHostingUrls(false) .ConfigureLogging(builder => builder.AddProvider(new KestrelTestLoggerProvider(testLogger))) .Configure(ConfigureEchoAddress); using (var host = hostBuilder.Build()) { host.Start(); var port = host.GetPort(); // If this isn't working properly, we'll get the HTTP endpoint defined in UseUrls // instead of the HTTPS endpoint defined in UseKestrel. var serverAddresses = host.ServerFeatures.Get <IServerAddressesFeature>().Addresses; Assert.Equal(1, serverAddresses.Count); var endPointAddress = $"https://127.0.0.1:{port}"; Assert.Equal(serverAddresses.First(), endPointAddress); Assert.Single(testLogger.Messages, log => log.LogLevel == LogLevel.Warning && string.Equals(CoreStrings.FormatOverridingWithKestrelOptions(useUrlsAddress, "UseKestrel()"), log.Message, StringComparison.Ordinal)); Assert.Equal(new Uri(endPointAddress).ToString(), await HttpClientSlim.GetStringAsync(endPointAddress, validateCertificate: false)); } }
public async Task OverrideDirectConfigurationWithIServerAddressesFeature_Succeeds() { var useUrlsAddress = $"http://127.0.0.1:0"; var testLogger = new TestApplicationErrorLogger(); var hostBuilder = new WebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => { listenOptions.UseHttps("testCert.pfx", "testPassword"); }); }) .UseUrls(useUrlsAddress) .PreferHostingUrls(true) .ConfigureLogging(builder => builder.AddProvider(new KestrelTestLoggerProvider(testLogger))) .Configure(ConfigureEchoAddress); using (var host = hostBuilder.Build()) { host.Start(); var port = host.GetPort(); // If this isn't working properly, we'll get the HTTPS endpoint defined in UseKestrel // instead of the HTTP endpoint defined in UseUrls. var serverAddresses = host.ServerFeatures.Get <IServerAddressesFeature>().Addresses; Assert.Equal(1, serverAddresses.Count); var useUrlsAddressWithPort = $"http://127.0.0.1:{port}"; Assert.Equal(serverAddresses.First(), useUrlsAddressWithPort); Assert.Single(testLogger.Messages, log => log.LogLevel == LogLevel.Information && string.Equals(CoreStrings.FormatOverridingWithPreferHostingUrls(nameof(IServerAddressesFeature.PreferHostingUrls), useUrlsAddress), log.Message, StringComparison.Ordinal)); Assert.Equal(new Uri(useUrlsAddressWithPort).ToString(), await HttpClientSlim.GetStringAsync(useUrlsAddressWithPort)); } }
public async Task OnConnectionCreatesLogScopeWithConnectionId() { var testLogger = new TestApplicationErrorLogger(); var loggerFactory = new LoggerFactory(new[] { new KestrelTestLoggerProvider(testLogger) }); var serviceContext = new TestServiceContext(loggerFactory); // This needs to run inline var tcs = new TaskCompletionSource(); var connection = new Mock <DefaultConnectionContext> { CallBase = true }.Object; connection.ConnectionClosed = new CancellationToken(canceled: true); var transportConnectionManager = new TransportConnectionManager(serviceContext.ConnectionManager); var kestrelConnection = new KestrelConnection <ConnectionContext>(0, serviceContext, transportConnectionManager, _ => tcs.Task, connection, serviceContext.Log); transportConnectionManager.AddConnection(0, kestrelConnection); var task = kestrelConnection.ExecuteAsync(); // The scope should be created var scopeObjects = testLogger.Scopes.OfType <IReadOnlyList <KeyValuePair <string, object> > >().ToList(); Assert.Single(scopeObjects); var pairs = scopeObjects[0].ToDictionary(p => p.Key, p => p.Value); Assert.True(pairs.ContainsKey("ConnectionId")); Assert.Equal(connection.ConnectionId, pairs["ConnectionId"]); tcs.TrySetResult(); await task; // Verify the scope was disposed after request processing completed Assert.True(testLogger.Scopes.IsEmpty); }
private OutputProducer CreateOutputProducer(PipeOptions pipeOptions, CancellationTokenSource cts = null) { var pipe = _pipeFactory.Create(pipeOptions); var logger = new TestApplicationErrorLogger(); var serviceContext = new TestServiceContext { Log = new TestKestrelTrace(logger), ThreadPool = new InlineLoggingThreadPool(new TestKestrelTrace(logger)) }; var transportContext = new TestLibuvTransportContext { Log = new LibuvTrace(logger) }; var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log); var consumer = new LibuvOutputConsumer(pipe.Reader, _libuvThread, socket, "0", transportContext.Log); var frame = new Frame <object>(null, new FrameContext { ServiceContext = serviceContext, ConnectionInformation = new MockConnectionInformation { PipeFactory = _pipeFactory }, TimeoutControl = Mock.Of <ITimeoutControl>(), Output = pipe }); if (cts != null) { frame.RequestAborted.Register(cts.Cancel); } var ignore = WriteOutputAsync(consumer, pipe.Reader, frame); return(frame.Output); }
private Http1OutputProducer CreateOutputProducer(PipeOptions pipeOptions, CancellationTokenSource cts = null) { var pair = DuplexPipe.CreateConnectionPair(pipeOptions, pipeOptions); var logger = new TestApplicationErrorLogger(); var serviceContext = new TestServiceContext { Log = new TestKestrelTrace(logger), ThreadPool = new InlineLoggingThreadPool(new TestKestrelTrace(logger)) }; var transportContext = new TestLibuvTransportContext { Log = new LibuvTrace(logger) }; var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log); var consumer = new LibuvOutputConsumer(pair.Application.Input, _libuvThread, socket, "0", transportContext.Log); var http1Connection = new Http1Connection(new Http1ConnectionContext { ServiceContext = serviceContext, ConnectionFeatures = new FeatureCollection(), MemoryPool = _memoryPool, TimeoutControl = Mock.Of <ITimeoutControl>(), Application = pair.Application, Transport = pair.Transport }); if (cts != null) { http1Connection.RequestAborted.Register(cts.Cancel); } var ignore = WriteOutputAsync(consumer, pair.Application.Input, http1Connection); return((Http1OutputProducer)http1Connection.Output); }
public async Task PipeConnectionsWithWrongMessageAreLoggedAndIgnored() { var libuv = new LibuvFunctions(); var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); var logger = new TestApplicationErrorLogger(); var serviceContextPrimary = new TestServiceContext(); var builderPrimary = new ConnectionBuilder(); builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1); var transportContextPrimary = new TestLibuvTransportContext() { Log = new LibuvTrace(logger) }; transportContextPrimary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextPrimary, builderPrimary.Build()); var serviceContextSecondary = new TestServiceContext { DateHeaderValueManager = serviceContextPrimary.DateHeaderValueManager, ServerOptions = serviceContextPrimary.ServerOptions, Scheduler = serviceContextPrimary.Scheduler, HttpParser = serviceContextPrimary.HttpParser, }; var builderSecondary = new ConnectionBuilder(); builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")), HttpProtocols.Http1); var transportContextSecondary = new TestLibuvTransportContext(); transportContextSecondary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextSecondary, builderSecondary.Build()); var libuvTransport = new LibuvTransport(libuv, transportContextPrimary, listenOptions); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); // Start primary listener var libuvThreadPrimary = new LibuvThread(libuvTransport); await libuvThreadPrimary.StartAsync(); var listenerPrimary = new ListenerPrimary(transportContextPrimary); await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary); var address = GetUri(listenOptions); // Add secondary listener with wrong pipe message var libuvThreadSecondary = new LibuvThread(libuvTransport); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, Guid.NewGuid().ToByteArray(), listenOptions, libuvThreadSecondary); // Wait up to 10 seconds for error to be logged for (var i = 0; i < 10 && logger.TotalErrorsLogged == 0; i++) { await Task.Delay(100); } // TCP Connections don't get round-robined Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); await listenerSecondary.DisposeAsync(); await libuvThreadSecondary.StopAsync(TimeSpan.FromSeconds(5)); await listenerPrimary.DisposeAsync(); await libuvThreadPrimary.StopAsync(TimeSpan.FromSeconds(5)); Assert.Equal(1, logger.TotalErrorsLogged); var errorMessage = logger.Messages.First(m => m.LogLevel == LogLevel.Error); Assert.IsType <IOException>(errorMessage.Exception); Assert.Contains("Bad data", errorMessage.Exception.ToString()); }
public async Task NonListenerPipeConnectionsAreLoggedAndIgnored() { var libuv = new LibuvFunctions(); var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); var logger = new TestApplicationErrorLogger(); var serviceContextPrimary = new TestServiceContext(); var builderPrimary = new ConnectionBuilder(); builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1); var transportContextPrimary = new TestLibuvTransportContext() { Log = new LibuvTrace(logger) }; transportContextPrimary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextPrimary, builderPrimary.Build()); var serviceContextSecondary = new TestServiceContext { DateHeaderValueManager = serviceContextPrimary.DateHeaderValueManager, ServerOptions = serviceContextPrimary.ServerOptions, Scheduler = serviceContextPrimary.Scheduler, HttpParser = serviceContextPrimary.HttpParser, }; var builderSecondary = new ConnectionBuilder(); builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")), HttpProtocols.Http1); var transportContextSecondary = new TestLibuvTransportContext(); transportContextSecondary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextSecondary, builderSecondary.Build()); var libuvTransport = new LibuvTransport(libuv, transportContextPrimary, listenOptions); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); // Start primary listener var libuvThreadPrimary = new LibuvThread(libuvTransport); await libuvThreadPrimary.StartAsync(); var listenerPrimary = new ListenerPrimary(transportContextPrimary); await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary); var address = GetUri(listenOptions); // Add secondary listener var libuvThreadSecondary = new LibuvThread(libuvTransport); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadSecondary); // TCP Connections get round-robined await AssertResponseEventually(address, "Secondary", allowed : new[] { "Primary" }); Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); // Create a pipe connection and keep it open without sending any data var connectTcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); var connectionTrace = new LibuvTrace(new TestApplicationErrorLogger()); var pipe = new UvPipeHandle(connectionTrace); libuvThreadPrimary.Post(_ => { var connectReq = new UvConnectRequest(connectionTrace); pipe.Init(libuvThreadPrimary.Loop, libuvThreadPrimary.QueueCloseHandle); connectReq.Init(libuvThreadPrimary); connectReq.Connect( pipe, pipeName, (req, status, ex, __) => { req.Dispose(); if (ex != null) { connectTcs.SetException(ex); } else { connectTcs.SetResult(null); } }, null); }, (object)null); await connectTcs.Task; // TCP connections will still get round-robined between only the two listeners Assert.Equal("Secondary", await HttpClientSlim.GetStringAsync(address)); Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); Assert.Equal("Secondary", await HttpClientSlim.GetStringAsync(address)); await libuvThreadPrimary.PostAsync(_ => pipe.Dispose(), (object)null); // Wait up to 10 seconds for error to be logged for (var i = 0; i < 10 && logger.TotalErrorsLogged == 0; i++) { await Task.Delay(100); } // Same for after the non-listener pipe connection is closed Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); Assert.Equal("Secondary", await HttpClientSlim.GetStringAsync(address)); Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); await listenerSecondary.DisposeAsync(); await libuvThreadSecondary.StopAsync(TimeSpan.FromSeconds(5)); await listenerPrimary.DisposeAsync(); await libuvThreadPrimary.StopAsync(TimeSpan.FromSeconds(5)); Assert.Equal(1, logger.TotalErrorsLogged); var errorMessage = logger.Messages.First(m => m.LogLevel == LogLevel.Error); Assert.Equal(TestConstants.EOF, Assert.IsType <UvException>(errorMessage.Exception).StatusCode); }
public async Task NonListenerPipeConnectionsAreLoggedAndIgnored() { var libuv = new LibuvFunctions(); var endpoint = new IPEndPoint(IPAddress.Loopback, 0); var logger = new TestApplicationErrorLogger(); var transportContextPrimary = new TestLibuvTransportContext { Log = logger }; var transportContextSecondary = new TestLibuvTransportContext(); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); // Start primary listener var libuvThreadPrimary = new LibuvThread(libuv, transportContextPrimary); await libuvThreadPrimary.StartAsync(); var listenerPrimary = new ListenerPrimary(transportContextPrimary); await listenerPrimary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadPrimary); var address = GetUri(listenerPrimary.EndPoint); // Add secondary listener var libuvThreadSecondary = new LibuvThread(libuv, transportContextSecondary); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadSecondary); // TCP Connections get round-robined var primary = await WaitForSecondaryListener(address, listenerPrimary, listenerSecondary); // Make sure the pending accept get yields using (var socket = await HttpClientSlim.GetSocket(address)) { await(await primary.DefaultTimeout()).DisposeAsync(); } // Create a pipe connection and keep it open without sending any data var connectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var connectionTrace = new TestApplicationErrorLogger(); var pipe = new UvPipeHandle(connectionTrace); libuvThreadPrimary.Post(_ => { var connectReq = new UvConnectRequest(connectionTrace); pipe.Init(libuvThreadPrimary.Loop, libuvThreadPrimary.QueueCloseHandle); connectReq.Init(libuvThreadPrimary); connectReq.Connect( pipe, pipeName, (req, status, ex, __) => { req.Dispose(); if (ex != null) { connectTcs.SetException(ex); } else { connectTcs.SetResult(); } }, null); }, (object)null); await connectTcs.Task; // TCP connections will still get round-robined between only the two listeners await AssertRoundRobin(address, listenerPrimary, listenerSecondary, listenerPrimary); await libuvThreadPrimary.PostAsync(_ => pipe.Dispose(), (object)null); // Wait up to 10 seconds for error to be logged for (var i = 0; i < 10 && logger.TotalErrorsLogged == 0; i++) { await Task.Delay(100); } // Same for after the non-listener pipe connection is closed await AssertRoundRobin(address, listenerPrimary, listenerSecondary, listenerPrimary); await listenerSecondary.DisposeAsync(); await libuvThreadSecondary.StopAsync(TimeSpan.FromSeconds(5)); await listenerPrimary.DisposeAsync(); await libuvThreadPrimary.StopAsync(TimeSpan.FromSeconds(5)); Assert.Equal(0, logger.TotalErrorsLogged); var logMessage = logger.Messages.Single(m => m.Message == "An internal pipe was opened unexpectedly."); Assert.Equal(LogLevel.Debug, logMessage.LogLevel); }
public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ServiceContext testContext) { var onStartingCallCount1 = 0; var onStartingCallCount2 = 0; var failedWriteCount = 0; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async httpContext => { var onStartingException = new Exception(); var response = httpContext.Response; response.OnStarting(_ => { onStartingCallCount1++; throw onStartingException; }, null); response.OnStarting(_ => { onStartingCallCount2++; throw onStartingException; }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; var writeException = await Assert.ThrowsAsync <ObjectDisposedException>(async() => await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11)); Assert.Same(onStartingException, writeException.InnerException); failedWriteCount++; }, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", "Connection: close", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "", ""); Assert.Equal(2, onStartingCallCount1); // The second OnStarting callback should not be called since the first failed. Assert.Equal(0, onStartingCallCount2); Assert.Equal(2, testLogger.ApplicationErrorsLogged); } } }
public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(ServiceContext testContext) { var onCompletedCalled1 = false; var onCompletedCalled2 = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async frame => { var response = frame.Get<IHttpResponseFeature>(); response.OnCompleted(_ => { onCompletedCalled1 = true; throw new Exception(); }, null); response.OnCompleted(_ => { onCompletedCalled2 = true; throw new Exception(); }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); }, testContext)) { using (var connection = new TestConnection()) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello World"); } // All OnCompleted callbacks should be called even if they throw. Assert.Equal(2, testLogger.ApplicationErrorsLogged); Assert.True(onCompletedCalled1); Assert.True(onCompletedCalled2); } }
public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ServiceContext testContext) { var onStartingCallCount1 = 0; var onStartingCallCount2 = 0; var failedWriteCount = 0; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async frame => { var onStartingException = new Exception(); var response = frame.Get<IHttpResponseFeature>(); response.OnStarting(_ => { onStartingCallCount1++; throw onStartingException; }, null); response.OnStarting(_ => { onStartingCallCount2++; throw onStartingException; }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; var writeException = await Assert.ThrowsAsync<ObjectDisposedException>(async () => await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11)); Assert.Same(onStartingException, writeException.InnerException); failedWriteCount++; }, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "Connection: close", "", ""); Assert.Equal(2, onStartingCallCount1); // The second OnStarting callback should not be called since the first failed. Assert.Equal(0, onStartingCallCount2); Assert.Equal(2, testLogger.ApplicationErrorsLogged); } } }
public async Task ThrowingAfterPartialWriteKillsConnection(ServiceContext testContext) { bool onStartingCalled = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async frame => { var response = frame.Get<IHttpResponseFeature>(); response.OnStarting(_ => { onStartingCalled = true; return Task.FromResult<object>(null); }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello"), 0, 5); throw new Exception(); }, testContext)) { using (var connection = new TestConnection()) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello"); Assert.True(onStartingCalled); Assert.Equal(1, testLogger.ApplicationErrorsLogged); } } }
public async Task ThrowingResultsIn500Response(ServiceContext testContext) { bool onStartingCalled = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(frame => { var response = frame.Get<IHttpResponseFeature>(); response.OnStarting(_ => { onStartingCalled = true; return Task.FromResult<object>(null); }, null); // Anything added to the ResponseHeaders dictionary is ignored response.Headers.Clear(); response.Headers["Content-Length"] = "11"; throw new Exception(); }, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "Connection: close", "", ""); Assert.False(onStartingCalled); Assert.Equal(2, testLogger.ApplicationErrorsLogged); } } }