public async Task DoesNotEndConnectionOnZeroRead()
        {
            var mockLibuv        = new MockLibuv();
            var transportContext = new TestLibuvTransportContext();
            var thread           = new LibuvThread(mockLibuv, transportContext);
            var listenerContext  = new ListenerContext(transportContext)
            {
                Thread = thread
            };

            try
            {
                await thread.StartAsync();

                await thread.PostAsync(_ =>
                {
                    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(), 0, ref ignored);
                }, (object)null);

                await using var connection = await listenerContext.AcceptAsync();

                var readAwaitable = connection.Transport.Input.ReadAsync();
                Assert.False(readAwaitable.IsCompleted);
            }
            finally
            {
                await thread.StopAsync(TimeSpan.FromSeconds(5));
            }
        }
        public async Task DoesNotThrowIfOnReadCallbackCalledWithEOFButAllocCallbackNotCalled()
        {
            var mockLibuv        = new MockLibuv();
            var transportContext = new TestLibuvTransportContext();
            var thread           = new LibuvThread(mockLibuv, transportContext);
            var listenerContext  = new ListenerContext(transportContext)
            {
                Thread = thread
            };

            try
            {
                await thread.StartAsync();

                await thread.PostAsync(_ =>
                {
                    var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log);
                    listenerContext.HandleConnection(socket);

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

                await using var connection = await listenerContext.AcceptAsync();

                var readAwaitable = await connection.Transport.Input.ReadAsync();

                Assert.True(readAwaitable.IsCompleted);
            }
            finally
            {
                await thread.StopAsync(TimeSpan.FromSeconds(5));
            }
        }
        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(
                bufferPool: 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(bufferPool: 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 LibuvThreadDoesNotThrowIfPostingWorkAfterDispose()
        {
            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 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 virtual void Init(LibuvThread thread)
        {
#if DEBUG
            // Store weak handles to all UvRequest objects so we can do leak detection
            // while running tests
            thread.Requests.Add(new WeakReference(this));
#endif
        }
        public LibuvOutputConsumerTests()
        {
            _memoryPool = new MemoryPool();
            _mockLibuv  = new MockLibuv();

            var libuvTransport = new LibuvTransport(_mockLibuv, new TestLibuvTransportContext(), new ListenOptions((ulong)0));

            _libuvThread = new LibuvThread(libuvTransport, maxLoops: 1);
            _libuvThread.StartAsync().Wait();
        }
        public LibuvOutputConsumerTests()
        {
            _memoryPool = SlabMemoryPoolFactory.Create();
            _mockLibuv  = new MockLibuv();

            var context = new TestLibuvTransportContext();

            _libuvThread = new LibuvThread(_mockLibuv, context, maxLoops: 1);
            _libuvThread.StartAsync().Wait();
        }
        public LibuvOutputConsumerTests()
        {
            _pipeFactory = new PipeFactory();
            _mockLibuv   = new MockLibuv();

            var libuvTransport = new LibuvTransport(_mockLibuv, new TestLibuvTransportContext(), new ListenOptions(0));

            _libuvThread = new LibuvThread(libuvTransport, 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 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));
            }
        }
        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);
        }
Пример #13
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));
            }
        }
Пример #14
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));
        }
Пример #15
0
        public override void Init(LibuvThread thread)
        {
            DangerousInit(thread.Loop);

            base.Init(thread);
        }
Пример #16
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);
        }
        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());
        }
Пример #18
0
 public virtual void Init(LibuvThread thread)
 {
 }
        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));
        }
Пример #20
0
        static void Main(string[] args)
        {
            Console.WriteLine("Main: {0}", Thread.CurrentThread.ManagedThreadId);

            // Use the factory
            LibuvTransportContext transport = new LibuvTransportContext
            {
                Options     = new LibuvTransportOptions(),
                AppLifetime = new ApplicationLifetime(null),
                Log         = new LibuvTrace(LoggerFactory.Create(builder =>
                {
                    builder.AddConsole();
                }).CreateLogger("core"))
            };

            LibuvThread uvThread = new LibuvThread(libUv, transport);

            uvThread.StartAsync().Wait();

            Task.Run(async() =>
            {
                Console.WriteLine("NIO: {0}", Thread.CurrentThread.ManagedThreadId);

                LibuvConnectionListener listener = new LibuvConnectionListener(libUv, transport, new IPEndPoint(IPAddress.Parse("0.0.0.0"), 2593));
                Console.WriteLine("Binding... ({0})", Thread.CurrentThread.ManagedThreadId);
                await listener.BindAsync();

                Console.WriteLine("Listening... ({0})", Thread.CurrentThread.ManagedThreadId);
                ConnectionContext connectionContext = await listener.AcceptAsync();
                Console.WriteLine("Accepted Connection from {0}", connectionContext.RemoteEndPoint);
                PipeReader reader = connectionContext.Transport.Input;

                while (true)
                {
                    ReadResult readResult = await reader.ReadAsync();
                    int packetId          = readResult.Buffer.FirstSpan[0];
                    Console.WriteLine($"[0x{packetId:X}] Processing Packet");
                    int length     = PacketLengths[packetId];
                    int bodyLength = length - 1;
                    int bodyStart  = 1;
                    if (length == 0)
                    {
                        length     = BinaryPrimitives.ReadUInt16BigEndian(readResult.Buffer.FirstSpan.Slice(1, 2));
                        bodyLength = length - 3;
                        bodyStart  = 3;
                    }
                    else if (length == 0xFFFF)
                    {
                        Console.WriteLine($"[0x{packetId:X}] Unknown Packet");
                        throw new Exception($"[0x{packetId:X}] Unknown Packet");
                    }
                    Console.WriteLine($"[0x{packetId:X}] Found length {length}");
                    Console.WriteLine($"Packet Data: {ByteArrayToString(readResult.Buffer.FirstSpan.ToArray()):X}");
                    Memory <byte> mem = new Memory <byte>(readResult.Buffer.FirstSpan.Slice(bodyStart, bodyLength).ToArray());
                    Console.WriteLine($"[0x{packetId:X}] Buffer length {mem.Length}");

                    _ = uvThread.PostAsync((Tuple <ConnectionContext, Memory <byte> > t) =>
                    {
                        // stuff
                        var(conn, mem) = t;
                        // Do stuff wtih memOwner.Memory.Span;
                        Console.WriteLine($"Packet ID: 0x{packetId:X} - Length: {length} - Data: 0x{ByteArrayToString(mem.ToArray())}");
                    }, Tuple.Create(connectionContext, mem));

                    reader.AdvanceTo(readResult.Buffer.GetPosition(length));
                }
            });

            // Manually putting something on the queue from another thread (or the main thread)
            uvThread.PostAsync <object>(_ =>
            {
                Console.WriteLine("Game: {0}", Thread.CurrentThread.ManagedThreadId);
            }, null);

            // Send an Initialization Request for Timers

            /*
             * uvThread.PostAsync<object>(_ =>
             * {
             * UvTimerHandle timerHandle = new UvTimerHandle(null);
             * timerHandle.Init(uvThread.Loop, (callback, handle) =>
             * {
             *  Console.WriteLine("Closed ({0})", Thread.CurrentThread.ManagedThreadId);
             * });
             * Console.WriteLine("Timer Stuff {0}", Thread.CurrentThread.ManagedThreadId);
             * int count = 10;
             * void cb2(UvTimerHandle handle)
             * {
             *  Console.WriteLine("Called!2 {0} ({1})", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
             *  timerHandle.Start(cb2, 2000, 0);
             * }
             * void cb1(UvTimerHandle handle)
             * {
             *  Console.WriteLine("Called!1 {0} ({1})", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
             *  count--;
             *  if (count < 0)
             *    timerHandle.Start(cb2, 2000, 0);
             *  else
             *    timerHandle.Start(cb1, 1000, 0);
             * }
             * timerHandle.Start(cb1, 1000, 0);
             * }, null);
             */

            Console.ReadLine();
        }