Ejemplo n.º 1
0
        public async Task CanReadAndWriteWithHttpsConnectionAdapter()
        {
            var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
            {
                ConnectionAdapters =
                {
                    new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions {
                        ServerCertificate = _x509Certificate2
                    })
                }
            };

            using (var server = new TestServer(App, new TestServiceContext(LoggerFactory), listenOptions))
            {
                var result = await HttpClientSlim.PostAsync($"https://localhost:{server.Port}/",
                                                            new FormUrlEncodedContent(new[] {
                    new KeyValuePair <string, string>("content", "Hello World?")
                }),
                                                            validateCertificate : false);

                Assert.Equal("content=Hello+World%3F", result);
            }
        }
        public async Task LoggingConnectionAdapterCanBeAddedBeforeAndAfterHttpsAdapter()
        {
            var host = new WebHostBuilder()
                       .ConfigureLogging(builder =>
            {
                builder.SetMinimumLevel(LogLevel.Trace);
                builder.AddXunit(_output);
            })
                       .UseKestrel(options =>
            {
                options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
                {
                    listenOptions.UseConnectionLogging();
                    listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
                    listenOptions.UseConnectionLogging();
                });
            })
                       .Configure(app =>
            {
                app.Run(context =>
                {
                    context.Response.ContentLength = 12;
                    return(context.Response.WriteAsync("Hello World!"));
                });
            })
                       .Build();

            using (host)
            {
                await host.StartAsync();

                var response = await HttpClientSlim.GetStringAsync($"https://localhost:{host.GetPort()}/", validateCertificate : false)
                               .TimeoutAfter(TimeSpan.FromSeconds(10));

                Assert.Equal("Hello World!", response);
            }
        }
        public async Task ClientAttemptingToUseUnsupportedProtocolIsLoggedAsDebug()
        {
            var loggerProvider = new HandshakeErrorLoggerProvider();

            LoggerFactory.AddProvider(loggerProvider);
            var hostBuilder = TransportSelector.GetWebHostBuilder()
                              .UseKestrel(options =>
            {
                options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
                {
                    listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
                });
            })
                              .ConfigureServices(AddTestLogging)
                              .Configure(app => app.Run(httpContext => Task.CompletedTask));

            using (var host = hostBuilder.Build())
            {
                host.Start();

                using (var socket = await HttpClientSlim.GetSocket(new Uri($"https://127.0.0.1:{host.GetPort()}/")))
                    using (var stream = new NetworkStream(socket, ownsSocket: false))
                        using (var sslStream = new SslStream(stream, true, (sender, certificate, chain, errors) => true))
                        {
                            // SslProtocols.Tls is TLS 1.0 which isn't supported by Kestrel by default.
                            await Assert.ThrowsAsync <IOException>(() =>
                                                                   sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null,
                                                                                                       enabledSslProtocols: SslProtocols.Tls,
                                                                                                       checkCertificateRevocation: false));
                        }
            }

            await loggerProvider.FilterLogger.LogTcs.Task.TimeoutAfter(TestConstants.DefaultTimeout);

            Assert.Equal(1, loggerProvider.FilterLogger.LastEventId);
            Assert.Equal(LogLevel.Debug, loggerProvider.FilterLogger.LastLogLevel);
        }
        public async Task OverrideDirectConfigurationWithIServerAddressesFeature_Succeeds()
        {
            var useUrlsAddress = $"http://127.0.0.1:0";
            var testLogger     = new TestApplicationErrorLogger();
            var hostBuilder    = TransportSelector.GetWebHostBuilder()
                                 .UseKestrel(options =>
            {
                options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
                {
                    listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
                });
            })
                                 .UseUrls(useUrlsAddress)
                                 .PreferHostingUrls(true)
                                 .ConfigureLogging(builder => builder.AddProvider(new KestrelTestLoggerProvider(testLogger)))
                                 .Configure(ConfigureEchoAddress);

            using (var host = hostBuilder.Build())
            {
                host.Start();

                var port = host.GetPort();

                // If this isn't working properly, we'll get the HTTPS endpoint defined in UseKestrel
                // instead of the HTTP endpoint defined in UseUrls.
                var serverAddresses = host.ServerFeatures.Get <IServerAddressesFeature>().Addresses;
                Assert.Equal(1, serverAddresses.Count);
                var useUrlsAddressWithPort = $"http://127.0.0.1:{port}";
                Assert.Equal(serverAddresses.First(), useUrlsAddressWithPort);

                Assert.Single(testLogger.Messages, log => log.LogLevel == LogLevel.Information &&
                              string.Equals(CoreStrings.FormatOverridingWithPreferHostingUrls(nameof(IServerAddressesFeature.PreferHostingUrls), useUrlsAddress),
                                            log.Message, StringComparison.Ordinal));

                Assert.Equal(new Uri(useUrlsAddressWithPort).ToString(), await HttpClientSlim.GetStringAsync(useUrlsAddressWithPort));
            }
        }
Ejemplo n.º 5
0
        public async Task HandshakeTimesOutAndIsLoggedAsDebug()
        {
            var loggerProvider = new HandshakeErrorLoggerProvider();

            LoggerFactory.AddProvider(loggerProvider);
            var hostBuilder = TransportSelector.GetWebHostBuilder()
                              .UseKestrel(options =>
            {
                options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
                {
                    listenOptions.UseHttps(o =>
                    {
                        o.ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
                        o.HandshakeTimeout  = TimeSpan.FromSeconds(1);
                    });
                });
            })
                              .ConfigureServices(AddTestLogging)
                              .Configure(app => app.Run(httpContext => Task.CompletedTask));

            using (var host = hostBuilder.Build())
            {
                host.Start();

                using (var socket = await HttpClientSlim.GetSocket(new Uri($"https://127.0.0.1:{host.GetPort()}/")))
                    using (var stream = new NetworkStream(socket, ownsSocket: false))
                    {
                        // No data should be sent and the connection should be closed in well under 30 seconds.
                        Assert.Equal(0, await stream.ReadAsync(new byte[1], 0, 1).TimeoutAfter(TestConstants.DefaultTimeout));
                    }
            }

            await loggerProvider.FilterLogger.LogTcs.Task.TimeoutAfter(TestConstants.DefaultTimeout);

            Assert.Equal(2, loggerProvider.FilterLogger.LastEventId);
            Assert.Equal(LogLevel.Debug, loggerProvider.FilterLogger.LastLogLevel);
        }
        public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_IfPreferHostingUrlsFalse()
        {
            var useUrlsAddress = $"http://127.0.0.1:0";

            var hostBuilder = TransportSelector.GetWebHostBuilder()
                              .ConfigureServices(AddTestLogging)
                              .UseKestrel(options =>
            {
                options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
                {
                    listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
                });
            })
                              .UseUrls($"http://127.0.0.1:0")
                              .PreferHostingUrls(false)
                              .Configure(ConfigureEchoAddress);

            using (var host = hostBuilder.Build())
            {
                host.Start();

                var port = host.GetPort();

                // If this isn't working properly, we'll get the HTTP endpoint defined in UseUrls
                // instead of the HTTPS endpoint defined in UseKestrel.
                var serverAddresses = host.ServerFeatures.Get <IServerAddressesFeature>().Addresses;
                Assert.Equal(1, serverAddresses.Count);
                var endPointAddress = $"https://127.0.0.1:{port}";
                Assert.Equal(serverAddresses.First(), endPointAddress);

                Assert.Single(TestApplicationErrorLogger.Messages, log => log.LogLevel == LogLevel.Warning &&
                              string.Equals(CoreStrings.FormatOverridingWithKestrelOptions(useUrlsAddress, "UseKestrel()"),
                                            log.Message, StringComparison.Ordinal));

                Assert.Equal(new Uri(endPointAddress).ToString(), await HttpClientSlim.GetStringAsync(endPointAddress, validateCertificate: false));
            }
        }
Ejemplo n.º 7
0
        private async Task RegisterDefaultServerAddresses_Success(IEnumerable <string> addresses, bool mockHttps = false)
        {
            var hostBuilder = TransportSelector.GetWebHostBuilder()
                              .ConfigureServices(AddTestLogging)
                              .UseKestrel(options =>
            {
                if (mockHttps)
                {
                    options.DefaultCertificate = TestResources.GetTestCertificate();
                }
            })
                              .Configure(ConfigureEchoAddress);

            using (var host = hostBuilder.Build())
            {
                await host.StartAsync();

                Assert.Equal(5000, host.GetPort());

                if (mockHttps)
                {
                    Assert.Contains(5001, host.GetPorts());
                }

                Assert.Single(TestApplicationErrorLogger.Messages, log => log.LogLevel == LogLevel.Debug &&
                              (string.Equals(CoreStrings.FormatBindingToDefaultAddresses(Constants.DefaultServerAddress, Constants.DefaultServerHttpsAddress), log.Message, StringComparison.Ordinal) ||
                               string.Equals(CoreStrings.FormatBindingToDefaultAddress(Constants.DefaultServerAddress), log.Message, StringComparison.Ordinal)));

                foreach (var address in addresses)
                {
                    Assert.Equal(new Uri(address).ToString(), await HttpClientSlim.GetStringAsync(address, validateCertificate: false));
                }

                await host.StopAsync();
            }
        }
        private async Task ListenLocalhost_Success(string[] testUrls, int testPort = 0)
        {
            var hostBuilder = TransportSelector.GetWebHostBuilder()
                              .UseKestrel(options =>
            {
                options.ListenLocalhost(testPort);
            })
                              .ConfigureLogging(_configureLoggingDelegate)
                              .Configure(ConfigureEchoAddress);

            using (var host = hostBuilder.Build())
            {
                host.Start();

                foreach (var testUrl in testUrls.Select(testUrl => $"{testUrl}:{(testPort == 0 ? host.GetPort() : testPort)}"))
                {
                    var response = await HttpClientSlim.GetStringAsync(testUrl, validateCertificate : false);

                    // Compare the response with Uri.ToString(), rather than testUrl directly.
                    // Required to handle IPv6 addresses with zone index, like "fe80::3%1"
                    Assert.Equal(new Uri(testUrl).ToString(), response);
                }
            }
        }
Ejemplo n.º 9
0
        public async Task ClientHandshakeFailureLoggedAsDebug()
        {
            var loggerProvider = new HandshakeErrorLoggerProvider();

            LoggerFactory.AddProvider(loggerProvider);

            var hostBuilder = TransportSelector.GetWebHostBuilder()
                              .UseKestrel(options =>
            {
                options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
                {
                    listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
                });
            })
                              .ConfigureServices(AddTestLogging)
                              .Configure(app => { });

            using (var host = hostBuilder.Build())
            {
                host.Start();

                using (var socket = await HttpClientSlim.GetSocket(new Uri($"https://127.0.0.1:{host.GetPort()}/")))
                    using (var stream = new NetworkStream(socket))
                    {
                        // Send null bytes and close socket
                        await stream.WriteAsync(new byte[10], 0, 10);
                    }

                await loggerProvider.FilterLogger.LogTcs.Task.TimeoutAfter(TestConstants.DefaultTimeout);
            }

            Assert.Equal(1, loggerProvider.FilterLogger.LastEventId.Id);
            Assert.Equal(LogLevel.Debug, loggerProvider.FilterLogger.LastLogLevel);
            Assert.True(loggerProvider.ErrorLogger.TotalErrorsLogged == 0,
                        userMessage: string.Join(Environment.NewLine, loggerProvider.ErrorLogger.ErrorMessages));
        }
Ejemplo n.º 10
0
        public async Task HandshakeDoesNotTimeOutIfDisableHandshakeTimeoutSwitchActive()
        {
            var hostBuilder = new WebHostBuilder()
                .UseKestrel(options =>
                {
                    options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
                    {
                        listenOptions.UseHttps(new HttpsConnectionAdapterOptions
                        {
                            ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword")
                        });
                    });
                })
                .Configure(app => app.Run(httpContext => Task.CompletedTask));

            AppContext.SetSwitch(HttpsConnectionAdapter.DisableHandshakeTimeoutSwitch, true);

            try
            {
                using (var host = hostBuilder.Build())
                {
                    host.Start();

                    using (var socket = await HttpClientSlim.GetSocket(new Uri($"https://127.0.0.1:{host.GetPort()}/")))
                    using (var stream = new NetworkStream(socket, ownsSocket: false))
                    {
                        // No data should be sent and the connection should not be closed by the server.
                        await Assert.ThrowsAsync<TimeoutException>(async () => await stream.ReadAsync(new byte[1], 0, 1).TimeoutAfter(TimeSpan.FromSeconds(30)));
                    }
                }
            }
            finally
            {
                AppContext.SetSwitch(HttpsConnectionAdapter.DisableHandshakeTimeoutSwitch, false);
            }
        }
Ejemplo n.º 11
0
        private async Task RegisterAddresses_Success(string addressInput, string[] testUrls, int testPort = 0)
        {
            var hostBuilder = TransportSelector.GetWebHostBuilder()
                              .UseKestrel(serverOptions =>
            {
                serverOptions.ConfigureHttpsDefaults(httpsOptions =>
                {
                    httpsOptions.ServerCertificate = TestResources.GetTestCertificate();
                });
            })
                              .ConfigureServices(AddTestLogging)
                              .UseUrls(addressInput)
                              .Configure(ConfigureEchoAddress);

            using (var host = hostBuilder.Build())
            {
                host.Start();

                foreach (var testUrl in testUrls.Select(testUrl => $"{testUrl}:{(testPort == 0 ? host.GetPort() : testPort)}"))
                {
                    var response = await HttpClientSlim.GetStringAsync(testUrl, validateCertificate : false);

                    // Filter out the scope id for IPv6, that's not sent over the wire. "fe80::3%1"
                    // See https://github.com/aspnet/Common/pull/369
                    var uri = new Uri(testUrl);
                    if (uri.HostNameType == UriHostNameType.IPv6)
                    {
                        var builder = new UriBuilder(uri);
                        var ip      = IPAddress.Parse(builder.Host);
                        builder.Host = new IPAddress(ip.GetAddressBytes()).ToString(); // Without the scope id.
                        uri          = builder.Uri;
                    }
                    Assert.Equal(uri.ToString(), response);
                }
            }
        }
        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);
        }
Ejemplo n.º 13
0
    public void GetHostExcludesDefaultPort()
    {
        var requestUri = new Uri("http://[fe80::5d2a:d070:6fd6:1bac%7]:80/");

        Assert.Equal("[fe80::5d2a:d070:6fd6:1bac]", HttpClientSlim.GetHost(requestUri));
    }
Ejemplo n.º 14
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);
        }
Ejemplo n.º 15
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));
        }
Ejemplo n.º 16
0
        public async Task DoesNotThrowObjectDisposedExceptionOnConnectionAbort()
        {
            var loggerProvider = new HandshakeErrorLoggerProvider();

            LoggerFactory.AddProvider(loggerProvider);
            var hostBuilder = TransportSelector.GetWebHostBuilder()
                              .UseKestrel(options =>
            {
                options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
                {
                    listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
                });
            })
                              .ConfigureServices(AddTestLogging)
                              .ConfigureLogging(builder => builder.AddProvider(loggerProvider))
                              .Configure(app => app.Run(async httpContext =>
            {
                var ct = httpContext.RequestAborted;
                while (!ct.IsCancellationRequested)
                {
                    try
                    {
                        await httpContext.Response.WriteAsync($"hello, world", ct);
                        await Task.Delay(1000, ct);
                    }
                    catch (TaskCanceledException)
                    {
                        // Don't regard connection abort as an error
                    }
                }
            }));

            using (var host = hostBuilder.Build())
            {
                host.Start();

                using (var socket = await HttpClientSlim.GetSocket(new Uri($"https://127.0.0.1:{host.GetPort()}/")))
                    using (var stream = new NetworkStream(socket, ownsSocket: false))
                        using (var sslStream = new SslStream(stream, true, (sender, certificate, chain, errors) => true))
                        {
                            await sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates : null,
                                                                      enabledSslProtocols : SslProtocols.Tls11 | SslProtocols.Tls12,
                                                                      checkCertificateRevocation : false);

                            var request = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n");
                            await sslStream.WriteAsync(request, 0, request.Length);

                            // Temporary workaround for a deadlock when reading from an aborted client SslStream on Mac and Linux.
                            if (TestPlatformHelper.IsWindows)
                            {
                                await sslStream.ReadAsync(new byte[32], 0, 32);
                            }
                            else
                            {
                                await stream.ReadAsync(new byte[32], 0, 32);
                            }
                        }
            }

            Assert.False(loggerProvider.ErrorLogger.ObjectDisposedExceptionLogged);
        }
        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 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));
        }
Ejemplo n.º 19
0
    public void Ipv6ScopeIdsFilteredOut()
    {
        var requestUri = new Uri("http://[fe80::5d2a:d070:6fd6:1bac%7]:5003/");

        Assert.Equal("[fe80::5d2a:d070:6fd6:1bac]:5003", HttpClientSlim.GetHost(requestUri));
    }