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 ConnectionsGetRoundRobinedToSecondaryListeners() { var libuv = new LibuvFunctions(); var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); var serviceContextPrimary = new TestServiceContext(); var transportContextPrimary = new TestLibuvTransportContext(); var builderPrimary = new ConnectionBuilder(); builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1); transportContextPrimary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextPrimary, builderPrimary.Build()); var serviceContextSecondary = new TestServiceContext(); 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); // Until a secondary listener is added, TCP connections get dispatched directly Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); var listenerCount = listenerPrimary.UvPipeCount; // Add secondary listener var libuvThreadSecondary = new LibuvThread(libuvTransport); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadSecondary); var maxWait = Task.Delay(TestConstants.DefaultTimeout); // wait for ListenerPrimary.ReadCallback to add the secondary pipe while (listenerPrimary.UvPipeCount == listenerCount) { var completed = await Task.WhenAny(maxWait, Task.Delay(100)); if (ReferenceEquals(completed, maxWait)) { throw new TimeoutException("Timed out waiting for secondary listener to become available"); } } // Once a secondary listener is added, TCP connections start getting dispatched to it await AssertResponseEventually(address, "Secondary", allowed : new[] { "Primary" }); // TCP connections will still get round-robined to the primary listener 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)); }
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 ConnectionsGetRoundRobinedToSecondaryListeners() { var libuv = new LibuvFunctions(); var endpoint = new IPEndPoint(IPAddress.Loopback, 0); var transportContextPrimary = new TestLibuvTransportContext(); 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); var acceptTask = listenerPrimary.AcceptAsync().AsTask(); using (var socket = await HttpClientSlim.GetSocket(address)) { await(await acceptTask.DefaultTimeout()).DisposeAsync(); } acceptTask = listenerPrimary.AcceptAsync().AsTask(); using (var socket = await HttpClientSlim.GetSocket(address)) { await(await acceptTask.DefaultTimeout()).DisposeAsync(); } var listenerCount = listenerPrimary.UvPipeCount; // Add secondary listener var libuvThreadSecondary = new LibuvThread(libuv, transportContextSecondary); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadSecondary); var maxWait = Task.Delay(TestConstants.DefaultTimeout); // wait for ListenerPrimary.ReadCallback to add the secondary pipe while (listenerPrimary.UvPipeCount == listenerCount) { var completed = await Task.WhenAny(maxWait, Task.Delay(100)); if (ReferenceEquals(completed, maxWait)) { throw new TimeoutException("Timed out waiting for secondary listener to become available"); } } // Once a secondary listener is added, TCP connections start getting dispatched to it // This returns the incomplete primary task after the secondary listener got the last // connection var primary = await WaitForSecondaryListener(address, listenerPrimary, listenerSecondary); // TCP connections will still get round-robined to the primary listener ListenerContext currentListener = listenerSecondary; Task <LibuvConnection> expected = primary; await AssertRoundRobin(address, listenerPrimary, listenerSecondary, currentListener, expected); await listenerSecondary.DisposeAsync(); await libuvThreadSecondary.StopAsync(TimeSpan.FromSeconds(5)); await listenerPrimary.DisposeAsync(); await libuvThreadPrimary.StopAsync(TimeSpan.FromSeconds(5)); }