Пример #1
0
        public async Task ConnectionDoesNotResumeAfterSocketCloseIfBackpressureIsApplied()
        {
            var mockConnectionHandler = new MockConnectionHandler();
            var mockLibuv             = new MockLibuv();
            var transportContext      = new TestLibuvTransportContext()
            {
                ConnectionHandler = mockConnectionHandler
            };
            var transport = new LibuvTransport(mockLibuv, transportContext, null);
            var thread    = new LibuvThread(transport);

            mockConnectionHandler.InputOptions = pool =>
                                                 new PipeOptions(
                pool: pool,
                maximumSizeHigh: 3);

            // We don't set the output writer scheduler here since we want to run the callback inline

            mockConnectionHandler.OutputOptions = pool => new PipeOptions(pool: pool, readerScheduler: thread);


            Task connectionTask = null;

            try
            {
                await thread.StartAsync();

                // Write enough to make sure back pressure will be applied
                await thread.PostAsync <object>(_ =>
                {
                    var listenerContext = new ListenerContext(transportContext)
                    {
                        Thread = thread
                    };
                    var socket     = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log);
                    var connection = new LibuvConnection(listenerContext, socket);
                    connectionTask = connection.Start();

                    mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored);
                    mockLibuv.ReadCallback(socket.InternalGetHandle(), 5, ref ignored);
                }, null);

                // Now assert that we removed the callback from libuv to stop reading
                Assert.Null(mockLibuv.AllocCallback);
                Assert.Null(mockLibuv.ReadCallback);

                // Now complete the output writer so that the connection closes
                mockConnectionHandler.Output.Writer.Complete();

                await connectionTask.TimeoutAfter(TimeSpan.FromSeconds(10));

                // Assert that we don't try to start reading
                Assert.Null(mockLibuv.AllocCallback);
                Assert.Null(mockLibuv.ReadCallback);
            }
            finally
            {
                await thread.StopAsync(TimeSpan.FromSeconds(1));
            }
        }
        public async Task ConnectionCanReadAndWrite(ListenOptions listenOptions)
        {
            var serviceContext = new TestServiceContext();

            listenOptions.UseHttpServer(listenOptions.ConnectionAdapters, serviceContext, new DummyApplication(TestApp.EchoApp), HttpProtocols.Http1);

            var transportContext = new TestLibuvTransportContext()
            {
                ConnectionDispatcher = new ConnectionDispatcher(serviceContext, listenOptions.Build())
            };

            var transport = new LibuvTransport(transportContext, listenOptions);

            await transport.BindAsync();

            using (var socket = TestConnection.CreateConnectedLoopbackSocket(listenOptions.IPEndPoint.Port))
            {
                var data = "Hello World";
                socket.Send(Encoding.ASCII.GetBytes($"POST / HTTP/1.0\r\nContent-Length: 11\r\n\r\n{data}"));
                var buffer = new byte[data.Length];
                var read   = 0;
                while (read < data.Length)
                {
                    read += socket.Receive(buffer, read, buffer.Length - read, SocketFlags.None);
                }
            }

            Assert.True(await serviceContext.ConnectionManager.CloseAllConnectionsAsync(new CancellationTokenSource(TestConstants.DefaultTimeout).Token));
            await transport.UnbindAsync();

            await transport.StopAsync();
        }
Пример #3
0
        public async Task ConnectionCanReadAndWrite(ListenOptions listenOptions)
        {
            var transportContext = new TestLibuvTransportContext()
            {
                ConnectionHandler = new ConnectionHandler <HttpContext>(listenOptions, new TestServiceContext(), new DummyApplication(TestApp.EchoApp))
            };
            var transport = new LibuvTransport(transportContext, listenOptions);

            await transport.BindAsync();

            using (var socket = TestConnection.CreateConnectedLoopbackSocket(listenOptions.IPEndPoint.Port))
            {
                var data = "Hello World";
                socket.Send(Encoding.ASCII.GetBytes($"POST / HTTP/1.0\r\nContent-Length: 11\r\n\r\n{data}"));
                var buffer = new byte[data.Length];
                var read   = 0;
                while (read < data.Length)
                {
                    read += socket.Receive(buffer, read, buffer.Length - read, SocketFlags.None);
                }
            }

            await transport.UnbindAsync();

            await transport.StopAsync();
        }
Пример #4
0
        public async Task LibuvThreadDoesNotThrowIfPostingWorkAfterDispose()
        {
            var mockConnectionHandler = new MockConnectionHandler();
            var mockLibuv             = new MockLibuv();
            var transportContext      = new TestLibuvTransportContext()
            {
                ConnectionHandler = mockConnectionHandler
            };
            var transport = new LibuvTransport(mockLibuv, transportContext, null);
            var thread    = new LibuvThread(transport);
            var ranOne    = false;
            var ranTwo    = false;
            var ranThree  = false;
            var ranFour   = false;

            await thread.StartAsync();

            await thread.PostAsync <object>(_ =>
            {
                ranOne = true;
            },
                                            null);

            Assert.Equal(1, mockLibuv.PostCount);

            // Shutdown the libuv thread
            await thread.StopAsync(TimeSpan.FromSeconds(5));

            Assert.Equal(2, mockLibuv.PostCount);

            var task = thread.PostAsync <object>(_ =>
            {
                ranTwo = true;
            },
                                                 null);

            Assert.Equal(2, mockLibuv.PostCount);

            thread.Post <object>(_ =>
            {
                ranThree = true;
            },
                                 null);

            Assert.Equal(2, mockLibuv.PostCount);

            thread.Schedule(_ =>
            {
                ranFour = true;
            },
                            (object)null);

            Assert.Equal(2, mockLibuv.PostCount);

            Assert.True(task.IsCompleted);
            Assert.True(ranOne);
            Assert.False(ranTwo);
            Assert.False(ranThree);
            Assert.False(ranFour);
        }
Пример #5
0
        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());
        }
Пример #6
0
        public async Task TransportCanBindUnbindAndStop()
        {
            var transportContext = new TestLibuvTransportContext();
            var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0));

            await transport.BindAsync();
            await transport.UnbindAsync();
            await transport.DisposeAsync();
        }
Пример #7
0
        public async Task ConnectionCanReadAndWrite()
        {
            var transportContext = new TestLibuvTransportContext();

            await using var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0));

            await transport.BindAsync();

            var endpoint = (IPEndPoint)transport.EndPoint;

            async Task EchoServerAsync()
            {
                while (true)
                {
                    await using var connection = await transport.AcceptAsync();

                    if (connection == null)
                    {
                        break;
                    }

                    while (true)
                    {
                        var result = await connection.Transport.Input.ReadAsync();

                        if (result.IsCompleted)
                        {
                            break;
                        }
                        await connection.Transport.Output.WriteAsync(result.Buffer.ToArray());

                        connection.Transport.Input.AdvanceTo(result.Buffer.End);
                    }
                }
            }

            var serverTask = EchoServerAsync();

            using (var socket = TestConnection.CreateConnectedLoopbackSocket(endpoint.Port))
            {
                var data = Encoding.ASCII.GetBytes("Hello World");
                await socket.SendAsync(data, SocketFlags.None);

                var buffer = new byte[data.Length];
                var read   = 0;
                while (read < data.Length)
                {
                    read += await socket.ReceiveAsync(buffer.AsMemory(read, buffer.Length - read), SocketFlags.None);
                }

                Assert.Equal(data, buffer);
            }

            await transport.UnbindAsync();

            await serverTask.DefaultTimeout();
        }
Пример #8
0
        public async Task TransportCanBindAndStop()
        {
            var transportContext = new TestLibuvTransportContext();
            var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0));

            // The transport can no longer start threads without binding to an endpoint.
            await transport.BindAsync();
            await transport.DisposeAsync();
        }
        public LibuvOutputConsumerTests()
        {
            _memoryPool = SlabMemoryPoolFactory.Create();
            _mockLibuv  = new MockLibuv();

            var context = new TestLibuvTransportContext();

            _libuvThread = new LibuvThread(_mockLibuv, context, maxLoops: 1);
            _libuvThread.StartAsync().Wait();
        }
Пример #10
0
        public async Task ConnectionDoesNotResumeAfterSocketCloseIfBackpressureIsApplied()
        {
            var mockLibuv        = new MockLibuv();
            var transportContext = new TestLibuvTransportContext();
            var thread           = new LibuvThread(mockLibuv, transportContext);
            var listenerContext  = new ListenerContext(transportContext)
            {
                Thread       = thread,
                InputOptions = new PipeOptions(
                    pool: thread.MemoryPool,
                    pauseWriterThreshold: 3,
                    readerScheduler: PipeScheduler.Inline,
                    writerScheduler: PipeScheduler.Inline,
                    useSynchronizationContext: false),

                // We don't set the output writer scheduler here since we want to run the callback inline
                OutputOptions = new PipeOptions(
                    pool: thread.MemoryPool,
                    readerScheduler: thread,
                    writerScheduler: PipeScheduler.Inline,
                    useSynchronizationContext: false)
            };

            try
            {
                await thread.StartAsync();

                // Write enough to make sure back pressure will be applied
                await thread.PostAsync <object>(_ =>
                {
                    var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log);
                    listenerContext.HandleConnection(socket);

                    mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored);
                    mockLibuv.ReadCallback(socket.InternalGetHandle(), 5, ref ignored);
                }, null);

                var connection = await listenerContext.AcceptAsync();

                // Now assert that we removed the callback from libuv to stop reading
                Assert.Null(mockLibuv.AllocCallback);
                Assert.Null(mockLibuv.ReadCallback);

                // Now complete the output writer so that the connection closes
                await connection.DisposeAsync();

                // Assert that we don't try to start reading
                Assert.Null(mockLibuv.AllocCallback);
                Assert.Null(mockLibuv.ReadCallback);
            }
            finally
            {
                await thread.StopAsync(TimeSpan.FromSeconds(5));
            }
        }
Пример #11
0
        public async Task TransportCanBindUnbindAndStop(ListenOptions listenOptions)
        {
            var transportContext = new TestLibuvTransportContext();
            var transport        = new LibuvTransport(transportContext, listenOptions);

            await transport.BindAsync();

            await transport.UnbindAsync();

            await transport.StopAsync();
        }
Пример #12
0
        public async Task TransportCanBindUnbindAndStop()
        {
            var transportContext = new TestLibuvTransportContext();
            var transport        = new LibuvTransport(transportContext, new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)));

            await transport.BindAsync();

            await transport.UnbindAsync();

            await transport.StopAsync();
        }
Пример #13
0
        public async Task CallingAcceptAfterDisposeAsyncThrows()
        {
            var transportContext = new TestLibuvTransportContext();
            var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0));

            await transport.BindAsync();
            var endpoint = (IPEndPoint)transport.EndPoint;

            await transport.UnbindAsync();
            await transport.DisposeAsync();

            await Assert.ThrowsAsync<ObjectDisposedException>(() => transport.AcceptAsync().AsTask());
        }
Пример #14
0
        public async Task OneToTenThreads(int threadCount)
        {
            var listenOptions   = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0));
            var serviceContext  = new TestServiceContext();
            var testApplication = new DummyApplication(context =>
            {
                return(context.Response.WriteAsync("Hello World"));
            });

            listenOptions.UseHttpServer(serviceContext, testApplication, Core.HttpProtocols.Http1, addAltSvcHeader: false);

            var transportContext = new TestLibuvTransportContext
            {
#pragma warning disable CS0618
                Options = new LibuvTransportOptions {
                    ThreadCount = threadCount
                }
#pragma warning restore CS0618
            };

            await using var transport = new LibuvConnectionListener(transportContext, listenOptions.EndPoint);
            await transport.BindAsync();

            listenOptions.EndPoint = transport.EndPoint;

            var transportConnectionManager = new TransportConnectionManager(serviceContext.ConnectionManager);
            var dispatcher = new ConnectionDispatcher <ConnectionContext>(serviceContext, c => listenOptions.Build()(c), transportConnectionManager);
            var acceptTask = dispatcher.StartAcceptingConnections(new GenericConnectionListener(transport));

            using (var client = new HttpClient())
            {
                // Send 20 requests just to make sure we don't get any failures
                var requestTasks = new List <Task <string> >();
                for (int i = 0; i < 20; i++)
                {
                    var requestTask = client.GetStringAsync($"http://127.0.0.1:{listenOptions.IPEndPoint.Port}/");
                    requestTasks.Add(requestTask);
                }

                foreach (var result in await Task.WhenAll(requestTasks))
                {
                    Assert.Equal("Hello World", result);
                }
            }

            await transport.UnbindAsync();

            await acceptTask;

            if (!await transportConnectionManager.CloseAllConnectionsAsync(default))
Пример #15
0
        public async Task CallingDisposeAsyncWillYieldPendingAccepts()
        {
            var transportContext = new TestLibuvTransportContext();
            await using var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0));

            await transport.BindAsync();

            var acceptTask = transport.AcceptAsync();

            await transport.UnbindAsync();

            var connection = await acceptTask.DefaultTimeout();

            Assert.Null(connection);
        }
        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);
        }
Пример #17
0
        public async Task DoesNotThrowIfOnReadCallbackCalledWithEOFButAllocCallbackNotCalled()
        {
            var mockConnectionDispatcher = new MockConnectionDispatcher();
            var mockLibuv        = new MockLibuv();
            var transportContext = new TestLibuvTransportContext()
            {
                ConnectionDispatcher = mockConnectionDispatcher
            };
            var transport = new LibuvTransport(mockLibuv, transportContext, null);
            var thread    = new LibuvThread(transport);

            Task connectionTask = null;

            try
            {
                await thread.StartAsync();

                await thread.PostAsync(_ =>
                {
                    var listenerContext = new ListenerContext(transportContext)
                    {
                        Thread = thread
                    };
                    var socket     = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log);
                    var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null);
                    listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection);
                    connectionTask = connection.Start();

                    var ignored = new LibuvFunctions.uv_buf_t();
                    mockLibuv.ReadCallback(socket.InternalGetHandle(), TestConstants.EOF, ref ignored);
                }, (object)null);

                var readAwaitable = await mockConnectionDispatcher.Input.Reader.ReadAsync();

                Assert.True(readAwaitable.IsCompleted);
            }
            finally
            {
                mockConnectionDispatcher.Input.Reader.Complete();
                mockConnectionDispatcher.Output.Writer.Complete();
                await connectionTask;

                await thread.StopAsync(TimeSpan.FromSeconds(5));
            }
        }
Пример #18
0
        public async Task OneToTenThreads(int threadCount)
        {
            var listenOptions   = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0));
            var serviceContext  = new TestServiceContext();
            var testApplication = new DummyApplication(context =>
            {
                return(context.Response.WriteAsync("Hello World"));
            });

            listenOptions.UseHttpServer(listenOptions.ConnectionAdapters, serviceContext, testApplication, HttpProtocols.Http1);

            var transportContext = new TestLibuvTransportContext()
            {
                ConnectionHandler = new ConnectionHandler(serviceContext, listenOptions.Build()),
                Options           = new LibuvTransportOptions {
                    ThreadCount = threadCount
                }
            };

            var transport = new LibuvTransport(transportContext, listenOptions);

            await transport.BindAsync();

            using (var client = new HttpClient())
            {
                // Send 20 requests just to make sure we don't get any failures
                var requestTasks = new List <Task <string> >();
                for (int i = 0; i < 20; i++)
                {
                    var requestTask = client.GetStringAsync($"http://127.0.0.1:{listenOptions.IPEndPoint.Port}/");
                    requestTasks.Add(requestTask);
                }

                foreach (var result in await Task.WhenAll(requestTasks))
                {
                    Assert.Equal("Hello World", result);
                }
            }

            await transport.UnbindAsync();

            await transport.StopAsync();
        }
Пример #19
0
        public async Task DoesNotEndConnectionOnZeroRead()
        {
            var mockConnectionDispatcher = new MockConnectionDispatcher();
            var mockLibuv        = new MockLibuv();
            var transportContext = new TestLibuvTransportContext()
            {
                ConnectionDispatcher = mockConnectionDispatcher
            };
            var transport = new LibuvTransport(mockLibuv, transportContext, null);
            var thread    = new LibuvThread(transport);

            try
            {
                await thread.StartAsync();

                await thread.PostAsync(_ =>
                {
                    var listenerContext = new ListenerContext(transportContext)
                    {
                        Thread = thread
                    };
                    var socket     = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log);
                    var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null);
                    listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection);
                    _ = connection.Start();

                    mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored);
                    mockLibuv.ReadCallback(socket.InternalGetHandle(), 0, ref ignored);
                }, (object)null);

                var readAwaitable = mockConnectionDispatcher.Input.Reader.ReadAsync();
                Assert.False(readAwaitable.IsCompleted);
            }
            finally
            {
                await thread.StopAsync(TimeSpan.FromSeconds(5));
            }
        }
Пример #20
0
        public async Task UnacceptedConnectionsAreAborted()
        {
            var transportContext = new TestLibuvTransportContext();
            var transport        = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0));

            await transport.BindAsync();

            var endpoint = (IPEndPoint)transport.EndPoint;

            async Task ConnectAsync()
            {
                using (var socket = TestConnection.CreateConnectedLoopbackSocket(endpoint.Port))
                {
                    try
                    {
                        var read = await socket.ReceiveAsync(new byte[10], SocketFlags.None);

                        Assert.Equal(0, read);
                    }
                    catch (SocketException)
                    {
                        // The connection can be reset sometimes
                    }
                }
            }

            var connectTask = ConnectAsync();

            await transport.UnbindAsync();

            await transport.DisposeAsync();

            // The connection was accepted because libuv eagerly accepts connections
            // they sit in a queue in each listener, we want to make sure that resources
            // are cleaned up if they are never accepted by the caller

            await connectTask.DefaultTimeout();
        }
        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 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);
        }
Пример #26
0
        public async Task ConnectionDoesNotResumeAfterReadCallbackScheduledAndSocketCloseIfBackpressureIsApplied()
        {
            var mockConnectionDispatcher = new MockConnectionDispatcher();
            var mockLibuv        = new MockLibuv();
            var transportContext = new TestLibuvTransportContext()
            {
                ConnectionDispatcher = mockConnectionDispatcher
            };
            var    transport     = new LibuvTransport(mockLibuv, transportContext, null);
            var    thread        = new LibuvThread(transport);
            var    mockScheduler = new Mock <PipeScheduler>();
            Action backPressure  = null;

            mockScheduler.Setup(m => m.Schedule(It.IsAny <Action <object> >(), It.IsAny <object>())).Callback <Action <object>, object>((a, o) =>
            {
                backPressure = () => a(o);
            });
            mockConnectionDispatcher.InputOptions = pool =>
                                                    new PipeOptions(
                pool: pool,
                pauseWriterThreshold: 3,
                resumeWriterThreshold: 3,
                writerScheduler: mockScheduler.Object,
                readerScheduler: PipeScheduler.Inline,
                useSynchronizationContext: false);

            mockConnectionDispatcher.OutputOptions = pool => new PipeOptions(pool: pool, readerScheduler: thread, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false);

            Task connectionTask = null;

            try
            {
                await thread.StartAsync();

                // Write enough to make sure back pressure will be applied
                await thread.PostAsync <object>(_ =>
                {
                    var listenerContext = new ListenerContext(transportContext)
                    {
                        Thread = thread
                    };
                    var socket     = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log);
                    var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null);
                    listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection);
                    connectionTask = connection.Start();

                    mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored);
                    mockLibuv.ReadCallback(socket.InternalGetHandle(), 5, ref ignored);
                }, null);

                // Now assert that we removed the callback from libuv to stop reading
                Assert.Null(mockLibuv.AllocCallback);
                Assert.Null(mockLibuv.ReadCallback);

                // Now release backpressure by reading the input
                var result = await mockConnectionDispatcher.Input.Reader.ReadAsync();

                // Calling advance will call into our custom scheduler that captures the back pressure
                // callback
                mockConnectionDispatcher.Input.Reader.AdvanceTo(result.Buffer.End);

                // Cancel the current pending flush
                mockConnectionDispatcher.Input.Writer.CancelPendingFlush();

                // Now release the back pressure
                await thread.PostAsync(a => a(), backPressure);

                // Assert that we don't try to start reading since the write was cancelled
                Assert.Null(mockLibuv.AllocCallback);
                Assert.Null(mockLibuv.ReadCallback);

                // Now complete the output writer and wait for the connection to close
                mockConnectionDispatcher.Output.Writer.Complete();

                await connectionTask.DefaultTimeout();

                // Assert that we don't try to start reading
                Assert.Null(mockLibuv.AllocCallback);
                Assert.Null(mockLibuv.ReadCallback);
            }
            finally
            {
                await thread.StopAsync(TimeSpan.FromSeconds(5));
            }
        }
Пример #27
0
        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));
        }
Пример #28
0
        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);
        }