public async Task DoesNotHangOnConnectionCloseRequest() { var builder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => { app.Run(async context => { await context.Response.WriteAsync("hello, world"); }); }); }) .ConfigureServices(AddTestLogging); using (var host = builder.Build()) using (var client = new HttpClient()) { await host.StartAsync(); client.DefaultRequestHeaders.Connection.Clear(); client.DefaultRequestHeaders.Connection.Add("close"); var response = await client.GetAsync($"http://127.0.0.1:{host.GetPort()}/"); response.EnsureSuccessStatusCode(); await host.StopAsync(); } }
private async Task ListenLocalhost_Success(string[] testUrls, int testPort = 0) { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { options.ListenLocalhost(testPort); }) .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); 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); } await host.StopAsync(); } }
private async Task RegisterDefaultServerAddresses_Success(IEnumerable <string> addresses, bool mockHttps = false) { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { if (mockHttps) { options.DefaultCertificate = TestResources.GetTestCertificate(); } }) .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); Assert.Equal(5000, host.GetPort()); Assert.Single(LogMessages, log => log.LogLevel == LogLevel.Debug && (string.Equals(CoreStrings.FormatBindingToDefaultAddress(Constants.DefaultServerAddress), 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(); } }
public async Task EndpointDefaultsConfig_CanSetProtocolForUrlsConfig(string input, HttpProtocols expected) { KestrelServerOptions capturedOptions = null; var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { var config = new ConfigurationBuilder().AddInMemoryCollection(new[] { new KeyValuePair <string, string>("EndpointDefaults:Protocols", input), }).Build(); options.Configure(config); capturedOptions = options; }) .UseUrls("http://127.0.0.1:0") .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); Assert.Single(capturedOptions.OptionsInUse); Assert.Equal(expected, capturedOptions.OptionsInUse[0].Protocols); await host.StopAsync(); } }
public async Task AbortingTheConnectionSendsFIN() { var builder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => app.Run(context => { context.Abort(); return(Task.CompletedTask); })); }) .ConfigureServices(AddTestLogging); using (var host = builder.Build()) { await host.StartAsync(); using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort())); socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n")); int result = socket.Receive(new byte[32]); Assert.Equal(0, result); } await host.StopAsync(); } }
public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_IfAddressesEmpty() { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => { listenOptions.UseHttps(TestResources.GetTestCertificate()); }); }) .PreferHostingUrls(true) .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); var port = host.GetPort(); // If this isn't working properly, we'll not get the HTTPS endpoint defined in UseKestrel. var serverAddresses = host.Services.GetRequiredService <IServer>().Features.Get <IServerAddressesFeature>().Addresses; Assert.Equal(1, serverAddresses.Count); var endPointAddress = $"https://127.0.0.1:{port}"; Assert.Equal(serverAddresses.First(), endPointAddress); Assert.Equal(new Uri(endPointAddress).ToString(), await HttpClientSlim.GetStringAsync(endPointAddress, validateCertificate: false)); await host.StopAsync(); } }
public async Task ThrowsWhenBindingToIPv6AddressInUse() { ThrowOnCriticalErrors = false; using (var socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp)) { socket.Bind(new IPEndPoint(IPAddress.IPv6Loopback, 0)); socket.Listen(0); var port = ((IPEndPoint)socket.LocalEndPoint).Port; var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls($"http://[::1]:{port}") .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { var exception = Assert.Throws <IOException>(() => host.Start()); var expectedMessage = CoreStrings.FormatEndpointAlreadyInUse($"http://[::1]:{port}"); Assert.Equal(expectedMessage, exception.Message); Assert.Equal(0, LogMessages.Count(log => log.LogLevel == LogLevel.Critical && log.Exception is null && log.Message.EndsWith(expectedMessage, StringComparison.Ordinal))); await host.StopAsync(); } } }
public async Task LargeDownload() { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(async context => { var bytes = new byte[1024]; for (int i = 0; i < bytes.Length; i++) { bytes[i] = (byte)i; } context.Response.ContentLength = bytes.Length * 1024; for (int i = 0; i < 1024; i++) { await context.Response.BodyWriter.WriteAsync(new Memory <byte>(bytes, 0, bytes.Length)); } }); }); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); using (var client = new HttpClient()) { var response = await client.GetAsync($"http://127.0.0.1:{host.GetPort()}/"); response.EnsureSuccessStatusCode(); var responseBody = await response.Content.ReadAsStreamAsync(); // Read the full response body var total = 0; var bytes = new byte[1024]; var count = await responseBody.ReadAsync(bytes, 0, bytes.Length); while (count > 0) { for (int i = 0; i < count; i++) { Assert.Equal(total % 256, bytes[i]); total++; } count = await responseBody.ReadAsync(bytes, 0, bytes.Length); } } await host.StopAsync(); } }
public async Task ThrowsOnReadAfterConnectionError() { var requestStarted = new SemaphoreSlim(0); var connectionReset = new SemaphoreSlim(0); var appDone = new SemaphoreSlim(0); var expectedExceptionThrown = false; var builder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => app.Run(async context => { requestStarted.Release(); Assert.True(await connectionReset.WaitAsync(_semaphoreWaitTimeout)); try { await context.Request.Body.ReadAsync(new byte[1], 0, 1); } catch (ConnectionResetException) { expectedExceptionThrown = true; } appDone.Release(); })); }) .ConfigureServices(AddTestLogging); using (var host = builder.Build()) { await host.StartAsync(); using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort())); socket.LingerState = new LingerOption(true, 0); socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\nContent-Length: 1\r\n\r\n")); Assert.True(await requestStarted.WaitAsync(_semaphoreWaitTimeout)); } connectionReset.Release(); Assert.True(await appDone.WaitAsync(_semaphoreWaitTimeout)); Assert.True(expectedExceptionThrown); await host.StopAsync(); } }
public async Task CanRebindToMultipleEndPoints() { var port = GetNextPort(); var ipv4endPointAddress = $"http://127.0.0.1:{port}/"; var ipv6endPointAddress = $"http://[::1]:{port}/"; var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { options.Listen(IPAddress.Loopback, port); options.Listen(IPAddress.IPv6Loopback, port); }) .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); Assert.Equal(ipv4endPointAddress, await HttpClientSlim.GetStringAsync(ipv4endPointAddress)); Assert.Equal(ipv6endPointAddress, await HttpClientSlim.GetStringAsync(ipv6endPointAddress)); await host.StopAsync(); } hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { options.Listen(IPAddress.Loopback, port); options.Listen(IPAddress.IPv6Loopback, port); }) .Configure(ConfigureEchoAddress); }); using (var host = hostBuilder.Build()) { await host.StartAsync(); Assert.Equal(ipv4endPointAddress, await HttpClientSlim.GetStringAsync(ipv4endPointAddress)); Assert.Equal(ipv6endPointAddress, await HttpClientSlim.GetStringAsync(ipv6endPointAddress)); await host.StopAsync(); } }
public TestServer(RequestDelegate app, TestServiceContext context, Action <KestrelServerOptions> configureKestrel, Action <IServiceCollection> configureServices) { _app = app; Context = context; _host = TransportSelector.GetHostBuilder(context.MemoryPoolFactory, context.ServerOptions.Limits.MaxRequestBufferSize) .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { configureKestrel(options); _listenOptions = options.ListenOptions.First(); }) .ConfigureServices(services => { services.AddSingleton <IStartup>(this); services.AddSingleton(context.LoggerFactory); services.AddSingleton <IServer>(sp => { // Manually configure options on the TestServiceContext. // We're doing this so we can use the same instance that was passed in var configureOptions = sp.GetServices <IConfigureOptions <KestrelServerOptions> >(); foreach (var c in configureOptions) { c.Configure(context.ServerOptions); } return(new KestrelServerImpl(sp.GetRequiredService <IConnectionListenerFactory>(), context)); }); configureServices(services); }) .UseSetting(WebHostDefaults.ApplicationKey, typeof(TestServer).Assembly.FullName) .UseSetting(WebHostDefaults.ShutdownTimeoutKey, TestConstants.DefaultTimeout.TotalSeconds.ToString(CultureInfo.InvariantCulture)) .Configure(app => { app.Run(_app); }); }) .ConfigureServices(services => { services.Configure <HostOptions>(option => { option.ShutdownTimeout = TestConstants.DefaultTimeout; }); }) .Build(); _host.Start(); Context.Log.LogDebug($"TestServer is listening on port {Port}"); }
private IHost CreateWebHost(EndPoint endpoint, Action <SocketTransportOptions> configureSocketOptions) => TransportSelector.GetHostBuilder() .ConfigureWebHost( webHostBuilder => { webHostBuilder .UseSockets(configureSocketOptions) .UseKestrel(options => options.Listen(endpoint)) .Configure( app => app.Run(ctx => ctx.Response.WriteAsync("Hello World")) ); } ) .ConfigureServices(AddTestLogging) .Build();
private async Task TestRemoteIPAddress(string registerAddress, string requestAddress, string expectAddress) { var builder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls($"http://{registerAddress}:0") .Configure(app => { app.Run(async context => { var connection = context.Connection; await context.Response.WriteAsync(JsonConvert.SerializeObject(new { RemoteIPAddress = connection.RemoteIpAddress?.ToString(), RemotePort = connection.RemotePort, LocalIPAddress = connection.LocalIpAddress?.ToString(), LocalPort = connection.LocalPort })); }); }); }) .ConfigureServices(AddTestLogging); using (var host = builder.Build()) using (var client = new HttpClient()) { await host.StartAsync(); var response = await client.GetAsync($"http://{requestAddress}:{host.GetPort()}/"); response.EnsureSuccessStatusCode(); var connectionFacts = await response.Content.ReadAsStringAsync(); Assert.NotEmpty(connectionFacts); var facts = JsonConvert.DeserializeObject <JObject>(connectionFacts); Assert.Equal(expectAddress, facts["RemoteIPAddress"].Value <string>()); Assert.NotEmpty(facts["RemotePort"].Value <string>()); await host.StopAsync(); } }
public async Task IgnoreNullHeaderValues(string headerName, StringValues headerValue, string expectedValue) { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(async context => { context.Response.Headers.Add(headerName, headerValue); await context.Response.WriteAsync(""); }); }); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); using (var client = new HttpClient()) { var response = await client.GetAsync($"http://127.0.0.1:{host.GetPort()}/"); response.EnsureSuccessStatusCode(); var headers = response.Headers; if (expectedValue == null) { Assert.False(headers.Contains(headerName)); } else { Assert.True(headers.Contains(headerName)); Assert.Equal(headers.GetValues(headerName).Single(), expectedValue); } } await host.StopAsync(); } }
public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_IfPreferHostingUrlsFalse() { var useUrlsAddress = $"http://127.0.0.1:0"; var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .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); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); 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.Services.GetRequiredService <IServer>().Features.Get <IServerAddressesFeature>().Addresses; Assert.Equal(1, serverAddresses.Count); var endPointAddress = $"https://127.0.0.1:{port}"; Assert.Equal(serverAddresses.First(), endPointAddress); Assert.Single(LogMessages, log => log.LogLevel == LogLevel.Warning && string.Equals(CoreStrings.FormatOverridingWithKestrelOptions(useUrlsAddress), log.Message, StringComparison.Ordinal)); Assert.Equal(new Uri(endPointAddress).ToString(), await HttpClientSlim.GetStringAsync(endPointAddress, validateCertificate: false)); await host.StopAsync(); } }
private async Task RegisterAddresses_Success(string addressInput, string[] testUrls, int testPort = 0) { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(serverOptions => { serverOptions.ConfigureHttpsDefaults(httpsOptions => { httpsOptions.ServerCertificate = TestResources.GetTestCertificate(); }); }) .UseUrls(addressInput) .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); 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); } await host.StopAsync(); } }
public async Task CanReadAndWriteFromSocketFeatureInConnectionMiddleware() { var builder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { options.ListenAnyIP(0, lo => { lo.Use(next => { return(async connection => { var socket = connection.Features.Get <IConnectionSocketFeature>().Socket; Assert.NotNull(socket); var buffer = new byte[4096]; var read = await socket.ReceiveAsync(buffer, SocketFlags.None); static void ParseHttp(ReadOnlySequence <byte> data) { var parser = new HttpParser <ParserHandler>(); var handler = new ParserHandler(); var reader = new SequenceReader <byte>(data); // Assume we can parse the HTTP request in a single buffer Assert.True(parser.ParseRequestLine(handler, ref reader)); Assert.True(parser.ParseHeaders(handler, ref reader)); Assert.Equal(KestrelHttpMethod.Get, handler.HttpMethod); Assert.Equal(KestrelHttpVersion.Http11, handler.HttpVersion); } ParseHttp(new ReadOnlySequence <byte>(buffer[0..read])); await socket.SendAsync(Encoding.UTF8.GetBytes("HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n"), SocketFlags.None); }); }); });
public async Task ThrowsForUnsupportedAddressFromHosting(string address) { IgnoredCriticalLogExceptions.Add(typeof(InvalidOperationException)); var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls(address) .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { Assert.Throws <InvalidOperationException>(() => host.Start()); await host.StopAsync(); } }
public async Task ThrowsWhenBindingLocalhostToDynamicPort() { IgnoredCriticalLogExceptions.Add(typeof(InvalidOperationException)); var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls("http://localhost:0") .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { Assert.Throws <InvalidOperationException>(() => host.Start()); await host.StopAsync(); } }
public async Task RegisterHttpAddress_UpgradedToHttpsByConfigureEndpointDefaults() { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(serverOptions => { serverOptions.ConfigureEndpointDefaults(listenOptions => { listenOptions.UseHttps(TestResources.GetTestCertificate()); }); }) .UseUrls("http://127.0.0.1:0") .Configure(app => { var serverAddresses = app.ServerFeatures.Get <IServerAddressesFeature>(); app.Run(context => { Assert.Single(serverAddresses.Addresses); return(context.Response.WriteAsync(serverAddresses.Addresses.First())); }); }); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { host.Start(); var expectedUrl = $"https://127.0.0.1:{host.GetPort()}"; var response = await HttpClientSlim.GetStringAsync(expectedUrl, validateCertificate : false); Assert.Equal(expectedUrl, response); await host.StopAsync(); } }
private async Task RegisterIPEndPoint_Success(IPEndPoint endPoint, string testUrl, int testPort = 0) { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { options.Listen(endPoint, listenOptions => { if (testUrl.StartsWith("https", StringComparison.Ordinal)) { listenOptions.UseHttps(TestResources.GetTestCertificate()); } }); }) .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync(); var testUrlWithPort = $"{testUrl}:{(testPort == 0 ? host.GetPort() : testPort)}"; var options = ((IOptions <KestrelServerOptions>)host.Services.GetService(typeof(IOptions <KestrelServerOptions>))).Value; Assert.Single(options.ListenOptions); var response = await HttpClientSlim.GetStringAsync(testUrlWithPort, 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(testUrlWithPort).ToString(), response); await host.StopAsync(); } }
public async Task RequestAbortedTokenFiredOnClientFIN() { var appStarted = new SemaphoreSlim(0); var requestAborted = new SemaphoreSlim(0); var builder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => app.Run(async context => { appStarted.Release(); var token = context.RequestAborted; token.Register(() => requestAborted.Release(2)); await requestAborted.WaitAsync().DefaultTimeout(); })); }) .ConfigureServices(AddTestLogging); using (var host = builder.Build()) { await host.StartAsync(); using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort())); socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n")); await appStarted.WaitAsync(); socket.Shutdown(SocketShutdown.Send); await requestAborted.WaitAsync().DefaultTimeout(); } await host.StopAsync(); } }
public async Task SocketTransportExposesSocketsFeature() { var builder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => { app.Run(context => { var socket = context.Features.Get <IConnectionSocketFeature>().Socket; Assert.NotNull(socket); Assert.Equal(ProtocolType.Tcp, socket.ProtocolType); var ip = (IPEndPoint)socket.RemoteEndPoint; Assert.Equal(ip.Address, context.Connection.RemoteIpAddress); Assert.Equal(ip.Port, context.Connection.RemotePort); return(Task.CompletedTask); }); }); }) .ConfigureServices(AddTestLogging); using var host = builder.Build(); using var client = new HttpClient(); await host.StartAsync(); var response = await client.GetAsync($"http://127.0.0.1:{host.GetPort()}/"); response.EnsureSuccessStatusCode(); await host.StopAsync(); }
public async Task TestUnixDomainSocket() { var path = Path.GetTempFileName(); Delete(path); try { var serverConnectionCompletedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); async Task EchoServer(ConnectionContext connection) { // For graceful shutdown var notificationFeature = connection.Features.Get <IConnectionLifetimeNotificationFeature>(); try { while (true) { var result = await connection.Transport.Input.ReadAsync(notificationFeature.ConnectionClosedRequested); if (result.IsCompleted) { Logger.LogDebug("Application receive loop ending for connection {connectionId}.", connection.ConnectionId); break; } await connection.Transport.Output.WriteAsync(result.Buffer.ToArray()); connection.Transport.Input.AdvanceTo(result.Buffer.End); } } catch (OperationCanceledException) { Logger.LogDebug("Graceful shutdown triggered for {connectionId}.", connection.ConnectionId); } finally { serverConnectionCompletedTcs.TrySetResult(); } } var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(o => { o.ListenUnixSocket(path, builder => { builder.Run(EchoServer); }); }) .Configure(c => { }); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync().DefaultTimeout(); using (var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) { await socket.ConnectAsync(new UnixDomainSocketEndPoint(path)).DefaultTimeout(); var data = Encoding.ASCII.GetBytes("Hello World"); await socket.SendAsync(data, SocketFlags.None).DefaultTimeout(); var buffer = new byte[data.Length]; var read = 0; while (read < data.Length) { var bytesReceived = await socket.ReceiveAsync(buffer.AsMemory(read, buffer.Length - read), SocketFlags.None).DefaultTimeout(); read += bytesReceived; if (bytesReceived <= 0) { break; } } Assert.Equal(data, buffer); } // Wait for the server to complete the loop because of the FIN await serverConnectionCompletedTcs.Task.DefaultTimeout(); await host.StopAsync().DefaultTimeout(); } } finally { Delete(path); } }
public async Task TestUnixDomainSocketWithUrl() { var path = Path.GetTempFileName(); var url = $"http://unix:/{path}"; Delete(path); try { var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseUrls(url) .UseKestrel() .Configure(app => { app.Run(async context => { await context.Response.WriteAsync("Hello World"); }); }); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { await host.StartAsync().DefaultTimeout(); // https://github.com/dotnet/corefx/issues/5999 // .NET Core HttpClient does not support unix sockets, it's difficult to parse raw response data. below is a little hacky way. using (var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) { await socket.ConnectAsync(new UnixDomainSocketEndPoint(path)).DefaultTimeout(); var httpRequest = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\nConnection: close\r\n\r\n"); await socket.SendAsync(httpRequest, SocketFlags.None).DefaultTimeout(); var readBuffer = new byte[512]; var read = 0; while (true) { var bytesReceived = await socket.ReceiveAsync(readBuffer.AsMemory(read), SocketFlags.None).DefaultTimeout(); read += bytesReceived; if (bytesReceived <= 0) { break; } } var httpResponse = Encoding.ASCII.GetString(readBuffer, 0, read); int httpStatusStart = httpResponse.IndexOf(' ') + 1; Assert.False(httpStatusStart == 0, $"Space not found in '{httpResponse}'."); int httpStatusEnd = httpResponse.IndexOf(' ', httpStatusStart); Assert.False(httpStatusEnd == -1, $"Second space not found in '{httpResponse}'."); var httpStatus = int.Parse(httpResponse.Substring(httpStatusStart, httpStatusEnd - httpStatusStart), CultureInfo.InvariantCulture); Assert.Equal(httpStatus, StatusCodes.Status200OK); } await host.StopAsync().DefaultTimeout(); } } finally { Delete(path); } }
public async Task LargeUpload(long contentLength, bool checkBytes) { const int bufferLength = 1024 * 1024; Assert.True(contentLength % bufferLength == 0, $"{nameof(contentLength)} sent must be evenly divisible by {bufferLength}."); Assert.True(bufferLength % 256 == 0, $"{nameof(bufferLength)} must be evenly divisible by 256"); var builder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { options.Limits.MaxRequestBodySize = contentLength; options.Limits.MinRequestBodyDataRate = null; }) .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(async context => { // Read the full request body long total = 0; var receivedBytes = new byte[bufferLength]; var received = 0; while ((received = await context.Request.Body.ReadAsync(receivedBytes, 0, receivedBytes.Length)) > 0) { if (checkBytes) { for (var i = 0; i < received; i++) { // Do not use Assert.Equal here, it is to slow for this hot path Assert.True((byte)((total + i) % 256) == receivedBytes[i], "Data received is incorrect"); } } total += received; } await context.Response.WriteAsync($"bytesRead: {total}"); }); }); }) .ConfigureServices(AddTestLogging); using (var host = builder.Build()) { await host.StartAsync(); using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort())); socket.Send(Encoding.ASCII.GetBytes("POST / HTTP/1.1\r\nHost: \r\n")); socket.Send(Encoding.ASCII.GetBytes($"Content-Length: {contentLength}\r\n\r\n")); var contentBytes = new byte[bufferLength]; if (checkBytes) { for (var i = 0; i < contentBytes.Length; i++) { contentBytes[i] = (byte)i; } } for (var i = 0; i < contentLength / contentBytes.Length; i++) { socket.Send(contentBytes); } using (var stream = new NetworkStream(socket)) { await AssertStreamContains(stream, $"bytesRead: {contentLength}"); } } await host.StopAsync(); } }
private void ThrowsWhenBindingLocalhostToAddressInUse(AddressFamily addressFamily) { ThrowOnCriticalErrors = false; var addressInUseCount = 0; var wrongMessageCount = 0; var address = addressFamily == AddressFamily.InterNetwork ? IPAddress.Loopback : IPAddress.IPv6Loopback; var otherAddressFamily = addressFamily == AddressFamily.InterNetwork ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; while (addressInUseCount < 10 && wrongMessageCount < 10) { int port; using (var socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp)) { // Bind first to IPv6Any to ensure both the IPv4 and IPv6 ports are available. socket.Bind(new IPEndPoint(IPAddress.IPv6Any, 0)); socket.Listen(0); port = ((IPEndPoint)socket.LocalEndPoint).Port; } using (var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp)) { try { socket.Bind(new IPEndPoint(address, port)); socket.Listen(0); } catch (SocketException) { addressInUseCount++; continue; } var hostBuilder = TransportSelector.GetHostBuilder() .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel() .UseUrls($"http://localhost:{port}") .Configure(ConfigureEchoAddress); }) .ConfigureServices(AddTestLogging); using (var host = hostBuilder.Build()) { var exception = Assert.Throws <IOException>(() => host.Start()); var thisAddressString = $"http://{(addressFamily == AddressFamily.InterNetwork ? "127.0.0.1" : "[::1]")}:{port}"; var otherAddressString = $"http://{(addressFamily == AddressFamily.InterNetworkV6 ? "127.0.0.1" : "[::1]")}:{port}"; if (exception.Message == CoreStrings.FormatEndpointAlreadyInUse(otherAddressString)) { // Don't fail immediately, because it's possible that something else really did bind to the // same port for the other address family between the IPv6Any bind above and now. wrongMessageCount++; continue; } Assert.Equal(CoreStrings.FormatEndpointAlreadyInUse(thisAddressString), exception.Message); Assert.Equal(0, LogMessages.Count(log => log.LogLevel == LogLevel.Critical && log.Exception is null && log.Message.EndsWith(CoreStrings.FormatEndpointAlreadyInUse(thisAddressString), StringComparison.Ordinal))); break; } } } if (addressInUseCount >= 10) { Assert.True(false, $"The corresponding {otherAddressFamily} address was already in use 10 times."); } if (wrongMessageCount >= 10) { Assert.True(false, $"An error for a conflict with {otherAddressFamily} was thrown 10 times."); } }
private async Task <IHost> StartHost(long?maxRequestBufferSize, byte[] expectedBody, bool useConnectionAdapter, TaskCompletionSource startReadingRequestBody, TaskCompletionSource clientFinishedSendingRequestBody, Func <MemoryPool <byte> > memoryPoolFactory = null) { var host = TransportSelector.GetHostBuilder(memoryPoolFactory, maxRequestBufferSize) .ConfigureWebHost(webHostBuilder => { webHostBuilder .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => { if (useConnectionAdapter) { listenOptions.UsePassThrough(); } }); options.Limits.MaxRequestBufferSize = maxRequestBufferSize; if (maxRequestBufferSize.HasValue && maxRequestBufferSize.Value < options.Limits.MaxRequestLineSize) { options.Limits.MaxRequestLineSize = (int)maxRequestBufferSize; } if (maxRequestBufferSize.HasValue && maxRequestBufferSize.Value < options.Limits.MaxRequestHeadersTotalSize) { options.Limits.MaxRequestHeadersTotalSize = (int)maxRequestBufferSize; } options.Limits.MinRequestBodyDataRate = null; options.Limits.MaxRequestBodySize = _dataLength; }) .UseContentRoot(Directory.GetCurrentDirectory()) .Configure(app => app.Run(async context => { await startReadingRequestBody.Task.TimeoutAfter(TimeSpan.FromSeconds(120)); var buffer = new byte[expectedBody.Length]; var bytesRead = 0; while (bytesRead < buffer.Length) { bytesRead += await context.Request.Body.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead); } await clientFinishedSendingRequestBody.Task.TimeoutAfter(TimeSpan.FromSeconds(120)); // Verify client didn't send extra bytes if (await context.Request.Body.ReadAsync(new byte[1], 0, 1) != 0) { context.Response.StatusCode = StatusCodes.Status500InternalServerError; await context.Response.WriteAsync("Client sent more bytes than expectedBody.Length"); return; } await context.Response.WriteAsync($"bytesRead: {bytesRead}"); })); }) .ConfigureServices(AddTestLogging) .Build(); await host.StartAsync(); return(host); }