diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Constants.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Constants.cs index 3031b022dc..de6294fee4 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Constants.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Constants.cs @@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure public const int EOF = -4095; public static readonly int? ECONNRESET = GetECONNRESET(); + public static readonly int? EADDRINUSE = GetEADDRINUSE(); /// /// Prefix of host name used to specify Unix sockets in the configuration. @@ -39,5 +40,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure return null; } } + + private static int? GetEADDRINUSE() + { + switch (PlatformServices.Default.Runtime.OperatingSystemPlatform) + { + case Platform.Windows: + return -4091; + case Platform.Linux: + return -98; + case Platform.Darwin: + return -48; + default: + return null; + } + } } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs index 558794bf6a..50b873c265 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Reflection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; @@ -11,6 +12,7 @@ using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Http; using Microsoft.AspNetCore.Server.Kestrel.Infrastructure; +using Microsoft.AspNetCore.Server.Kestrel.Networking; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -94,6 +96,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel { _logger.LogWarning("Unable to determine ECONNRESET value on this platform."); } + if (!Constants.EADDRINUSE.HasValue) + { + _logger.LogWarning("Unable to determine EADDRINUSE value on this platform."); + } engine.Start(threadCount); var atLeastOneListener = false; @@ -108,8 +114,69 @@ namespace Microsoft.AspNetCore.Server.Kestrel else { atLeastOneListener = true; - _disposables.Push(engine.CreateServer( - parsedAddress)); + + if (!parsedAddress.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase)) + { + _disposables.Push(engine.CreateServer( + parsedAddress)); + } + else + { + if (parsedAddress.Port == 0) + { + throw new InvalidOperationException("Dynamic port binding is not supported when binding to localhost. You must either bind to 127.0.0.1:0 or [::1]:0, or both."); + } + + var ipv4Address = parsedAddress.WithHost("127.0.0.1"); + var exceptions = new List(); + + try + { + _disposables.Push(engine.CreateServer(ipv4Address)); + } + catch (AggregateException ex) + { + var uvException = ex.InnerException as UvException; + + if (uvException != null && uvException.StatusCode != Constants.EADDRINUSE) + { + _logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv4 loopback interface."); + exceptions.Add(uvException); + } + else + { + throw; + } + } + + var ipv6Address = parsedAddress.WithHost("[::1]"); + + try + { + _disposables.Push(engine.CreateServer(ipv6Address)); + } + catch (AggregateException ex) + { + var uvException = ex.InnerException as UvException; + + if (uvException != null && uvException.StatusCode != Constants.EADDRINUSE) + { + _logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv6 loopback interface."); + exceptions.Add(uvException); + } + else + { + throw; + } + } + + if (exceptions.Count == 2) + { + var ex = new AggregateException(exceptions); + _logger.LogError(0, ex, $"Unable to bind to {parsedAddress.ToString()} on any loopback interface."); + throw ex; + } + } // If requested port was "0", replace with assigned dynamic port. _serverAddresses.Addresses.Remove(address); diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Networking/Libuv.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Networking/Libuv.cs index 1bbb502a7f..a5028b136c 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Networking/Libuv.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Networking/Libuv.cs @@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking { var errorName = err_name(statusCode); var errorDescription = strerror(statusCode); - error = new UvException("Error " + statusCode + " " + errorName + " " + errorDescription); + error = new UvException("Error " + statusCode + " " + errorName + " " + errorDescription, statusCode); } else { diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Networking/UvException.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Networking/UvException.cs index a63da72eba..791c1f95ac 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Networking/UvException.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Networking/UvException.cs @@ -7,6 +7,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking { public class UvException : Exception { - public UvException(string message) : base(message) { } + public UvException(string message, int statusCode) : base(message) + { + StatusCode = statusCode; + } + + public int StatusCode { get; } } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/ServerAddress.cs b/src/Microsoft.AspNetCore.Server.Kestrel/ServerAddress.cs index ce35ff39ce..528a39edb8 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/ServerAddress.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/ServerAddress.cs @@ -155,5 +155,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel return serverAddress; } + + internal ServerAddress WithHost(string host) + { + return new ServerAddress + { + Scheme = Scheme, + Host = host, + Port = Port, + PathBase = PathBase + }; + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs index aa966d8298..c5f90cf61b 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; @@ -13,8 +12,8 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Server.Kestrel.Networking; using Microsoft.AspNetCore.Testing.xunit; -using Microsoft.Extensions.Configuration; using Xunit; namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests @@ -42,7 +41,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests await RegisterAddresses_Success(addressInput, testUrls); } + [ConditionalTheory, MemberData(nameof(AddressRegistrationDataIPv6Port80))] + [IPv6SupportedCondition] + [Port80SupportedCondition] + public async Task RegisterAddresses_IPv6Port80_Success(string addressInput, Func testUrls) + { + await RegisterAddresses_Success(addressInput, testUrls); + } + [ConditionalTheory, MemberData(nameof(AddressRegistrationDataIPv6ScopeId))] + [IPv6SupportedCondition] [OSSkipCondition(OperatingSystems.Linux, SkipReason = "HttpClient does not support IPv6 with scope ID on Linux (https://github.com/dotnet/corefx/issues/8235).")] [OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "HttpClient does not support IPv6 with scope ID on Mac (https://github.com/dotnet/corefx/issues/8235).")] public async Task RegisterAddresses_IPv6ScopeId_Success(string addressInput, Func testUrls) @@ -50,18 +58,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests await RegisterAddresses_Success(addressInput, testUrls); } - public async Task RegisterAddresses_Success(string addressInput, Func testUrls) + private async Task RegisterAddresses_Success(string addressInput, Func testUrls) { - var config = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - { "server.urls", addressInput } - }) - .Build(); - var hostBuilder = new WebHostBuilder() - .UseConfiguration(config) .UseKestrel() + .UseUrls(addressInput) .Configure(ConfigureEchoAddress); using (var host = hostBuilder.Build()) @@ -84,6 +85,53 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests } } + [Fact] + public void ThrowsWhenBindingLocalhostToIPv4AddressInUse() + { + ThrowsWhenBindingLocalhostToAddressInUse(AddressFamily.InterNetwork, IPAddress.Loopback); + } + + [ConditionalFact] + [IPv6SupportedCondition] + public void ThrowsWhenBindingLocalhostToIPv6AddressInUse() + { + ThrowsWhenBindingLocalhostToAddressInUse(AddressFamily.InterNetworkV6, IPAddress.IPv6Loopback); + } + + [Fact] + public void ThrowsWhenBindingLocalhostToDynamicPort() + { + var hostBuilder = new WebHostBuilder() + .UseKestrel() + .UseUrls("http://localhost:0") + .Configure(ConfigureEchoAddress); + + using (var host = hostBuilder.Build()) + { + Assert.Throws(() => host.Start()); + } + } + + private void ThrowsWhenBindingLocalhostToAddressInUse(AddressFamily addressFamily, IPAddress address) + { + using (var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp)) + { + var port = GetNextPort(); + socket.Bind(new IPEndPoint(address, port)); + + var hostBuilder = new WebHostBuilder() + .UseKestrel() + .UseUrls($"http://localhost:{port}") + .Configure(ConfigureEchoAddress); + + using (var host = hostBuilder.Build()) + { + var exception = Assert.Throws(() => host.Start()); + Assert.Contains(exception.InnerExceptions, ex => ex is UvException); + } + } + } + public static TheoryData> AddressRegistrationDataIPv4 { get @@ -91,26 +139,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests var dataset = new TheoryData>(); // Default host and port - dataset.Add(null, _ => new[] { "http://localhost:5000/" }); - dataset.Add(string.Empty, _ => new[] { "http://localhost:5000/" }); + dataset.Add(null, _ => new[] { "http://127.0.0.1:5000/" }); + dataset.Add(string.Empty, _ => new[] { "http://127.0.0.1:5000/" }); - // Static port + // Static ports var port1 = GetNextPort(); var port2 = GetNextPort(); - // Ensure multiple addresses can be separated by semicolon - dataset.Add($"http://localhost:{port1};http://localhost:{port2}", - _ => new[] { $"http://localhost:{port1}/", $"http://localhost:{port2}/" }); + // Loopback + dataset.Add($"http://127.0.0.1:{port1}", _ => new[] { $"http://127.0.0.1:{port1}/" }); - // Ensure "localhost" and "127.0.0.1" are equivalent + // Localhost dataset.Add($"http://localhost:{port1}", _ => new[] { $"http://localhost:{port1}/", $"http://127.0.0.1:{port1}/" }); - dataset.Add($"http://127.0.0.1:{port1}", _ => new[] { $"http://localhost:{port1}/", $"http://127.0.0.1:{port1}/" }); + + // Any + dataset.Add($"http://*:{port1}/", _ => new[] { $"http://127.0.0.1:{port1}/" }); + dataset.Add($"http://+:{port1}/", _ => new[] { $"http://127.0.0.1:{port1}/" }); + + // Multiple addresses + dataset.Add($"http://127.0.0.1:{port1};http://127.0.0.1:{port2}", _ => new[] { $"http://127.0.0.1:{port1}/", $"http://127.0.0.1:{port2}/" }); // Path after port - dataset.Add($"http://localhost:{port1}/base/path", _ => new[] { $"http://localhost:{port1}/base/path" }); + dataset.Add($"http://127.0.0.1:{port1}/base/path", _ => new[] { $"http://127.0.0.1:{port1}/base/path" }); - // Dynamic port - dataset.Add("http://localhost:0/", GetTestUrls); + // Dynamic port and non-loopback addresses + dataset.Add("http://127.0.0.1:0/", GetTestUrls); dataset.Add($"http://{Dns.GetHostName()}:0/", GetTestUrls); var ipv4Addresses = Dns.GetHostAddressesAsync(Dns.GetHostName()).Result @@ -131,8 +184,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests var dataset = new TheoryData>(); // Default port for HTTP (80) - dataset.Add("http://*", _ => new[] { "http://localhost/" }); - dataset.Add("http://localhost", _ => new[] { "http://localhost/" }); + dataset.Add("http://127.0.0.1", _ => new[] { "http://127.0.0.1/" }); + dataset.Add("http://localhost", _ => new[] { "http://127.0.0.1/" }); + dataset.Add("http://*", _ => new[] { "http://127.0.0.1/" }); return dataset; } @@ -144,17 +198,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { var dataset = new TheoryData>(); - // Static port - var port = GetNextPort(); - dataset.Add($"http://*:{port}/", _ => new[] { $"http://localhost:{port}/", $"http://127.0.0.1:{port}/", $"http://[::1]:{port}/" }); - dataset.Add($"http://localhost:{port}/", _ => new[] { $"http://localhost:{port}/", $"http://127.0.0.1:{port}/", - /* // https://github.com/aspnet/KestrelHttpServer/issues/231 - $"http://[::1]:{port}/" - */ }); - dataset.Add($"http://[::1]:{port}/", _ => new[] { $"http://[::1]:{port}/", }); - dataset.Add($"http://127.0.0.1:{port}/;http://[::1]:{port}/", _ => new[] { $"http://127.0.0.1:{port}/", $"http://[::1]:{port}/" }); + // Default host and port + dataset.Add(null, _ => new[] { "http://127.0.0.1:5000/", "http://[::1]:5000/" }); + dataset.Add(string.Empty, _ => new[] { "http://127.0.0.1:5000/", "http://[::1]:5000/" }); - // Dynamic port + // Static ports + var port1 = GetNextPort(); + var port2 = GetNextPort(); + + // Loopback + dataset.Add($"http://[::1]:{port1}/", _ => new[] { $"http://[::1]:{port1}/" }); + + // Localhost + dataset.Add($"http://localhost:{port1}", _ => new[] { $"http://localhost:{port1}/", $"http://127.0.0.1:{port1}/", $"http://[::1]:{port1}/" }); + + // Any + dataset.Add($"http://*:{port1}/", _ => new[] { $"http://127.0.0.1:{port1}/", $"http://[::1]:{port1}/" }); + dataset.Add($"http://+:{port1}/", _ => new[] { $"http://127.0.0.1:{port1}/", $"http://[::1]:{port1}/" }); + + // Multiple addresses + dataset.Add($"http://127.0.0.1:{port1}/;http://[::1]:{port1}/", _ => new[] { $"http://127.0.0.1:{port1}/", $"http://[::1]:{port1}/" }); + dataset.Add($"http://[::1]:{port1};http://[::1]:{port2}", _ => new[] { $"http://[::1]:{port1}/", $"http://[::1]:{port2}/" }); + + // Path after port + dataset.Add($"http://[::1]:{port1}/base/path", _ => new[] { $"http://[::1]:{port1}/base/path" }); + + // Dynamic port and non-loopback addresses var ipv6Addresses = Dns.GetHostAddressesAsync(Dns.GetHostName()).Result .Where(ip => ip.AddressFamily == AddressFamily.InterNetworkV6) .Where(ip => ip.ScopeId == 0); @@ -167,6 +236,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests } } + public static TheoryData> AddressRegistrationDataIPv6Port80 + { + get + { + var dataset = new TheoryData>(); + + // Default port for HTTP (80) + dataset.Add("http://[::1]", _ => new[] { "http://[::1]/" }); + dataset.Add("http://localhost", _ => new[] { "http://127.0.0.1/", "http://[::1]/" }); + dataset.Add("http://*", _ => new[] { "http://[::1]/" }); + + return dataset; + } + } + public static TheoryData> AddressRegistrationDataIPv6ScopeId { get diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/IPv6SupportedConditionAttribute.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/IPv6SupportedConditionAttribute.cs index cafe02d229..8889c3b2d5 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/IPv6SupportedConditionAttribute.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/IPv6SupportedConditionAttribute.cs @@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { using (var socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp)) { - socket.Bind(new IPEndPoint(IPAddress.IPv6Loopback, 8787)); + socket.Bind(new IPEndPoint(IPAddress.IPv6Any, 0)); return true; } } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/PathBaseTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/PathBaseTests.cs index 6520d73eb5..0e6045f101 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/PathBaseTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/PathBaseTests.cs @@ -1,13 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Xunit; @@ -68,14 +66,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests private async Task TestPathBase(string registerPathBase, string requestPath, string expectedPathBase, string expectedPath) { - var config = new ConfigurationBuilder().AddInMemoryCollection( - new Dictionary { - { "server.urls", $"http://localhost:0{registerPathBase}" } - }).Build(); - var builder = new WebHostBuilder() - .UseConfiguration(config) .UseKestrel() + .UseUrls($"http://127.0.0.1:0{registerPathBase}") .Configure(app => { app.Run(async context => diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs index 3a032767ac..3ec8eead7d 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { var builder = new WebHostBuilder() .UseKestrel() - .UseUrls($"http://localhost:0/") + .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(async context => @@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { var builder = new WebHostBuilder() .UseKestrel() - .UseUrls($"http://localhost:0/") + .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(async context => @@ -117,12 +117,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests } } - [Theory] - [InlineData("127.0.0.1", "127.0.0.1")] - [InlineData("localhost", "127.0.0.1")] - public Task RemoteIPv4Address(string requestAddress, string expectAddress) + [Fact] + public Task RemoteIPv4Address() { - return TestRemoteIPAddress("localhost", requestAddress, expectAddress); + return TestRemoteIPAddress("127.0.0.1", "127.0.0.1", "127.0.0.1"); } [ConditionalFact] @@ -137,7 +135,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { var builder = new WebHostBuilder() .UseKestrel() - .UseUrls($"http://localhost:0") + .UseUrls($"http://127.0.0.1:0") .Configure(app => { app.Run(async context => @@ -165,7 +163,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { var builder = new WebHostBuilder() .UseKestrel() - .UseUrls($"http://localhost:0/\u0041\u030A") + .UseUrls($"http://127.0.0.1:0/\u0041\u030A") .Configure(app => { app.Run(async context => diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs index 2d3c70aff2..f4bf6c323f 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; @@ -10,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Primitives; using Xunit; @@ -21,16 +19,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests [Fact] public async Task LargeDownload() { - var config = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - { "server.urls", $"http://localhost:0/" } - }) - .Build(); - var hostBuilder = new WebHostBuilder() - .UseConfiguration(config) .UseKestrel() + .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(async context => @@ -80,16 +71,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests [Theory, MemberData(nameof(NullHeaderData))] public async Task IgnoreNullHeaderValues(string headerName, StringValues headerValue, string expectedValue) { - var config = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - { "server.urls", $"http://localhost:0/" } - }) - .Build(); - var hostBuilder = new WebHostBuilder() - .UseConfiguration(config) .UseKestrel() + .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(async context => @@ -127,19 +111,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests [Fact] public async Task OnCompleteCalledEvenWhenOnStartingNotCalled() { - var config = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - { "server.urls", $"http://localhost:0/" } - }) - .Build(); - var onStartingCalled = false; var onCompletedCalled = false; var hostBuilder = new WebHostBuilder() - .UseConfiguration(config) .UseKestrel() + .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(context => diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ThreadCountTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ThreadCountTests.cs index 2c10fd96a2..b185406de6 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ThreadCountTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ThreadCountTests.cs @@ -7,28 +7,22 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; using Xunit; namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class ThreadCountTests { - public async Task ZeroToTenThreads(int threadCount) + [Theory] + [MemberData(nameof(OneToTen))] + public async Task OneToTenThreads(int threadCount) { - var config = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - { "server.urls", $"http://localhost:0/" } - }) - .Build(); - var hostBuilder = new WebHostBuilder() - .UseConfiguration(config) .UseKestrel(options => { options.ThreadCount = threadCount; }) + .UseUrls("http://127.0.0.1:0/") .Configure(app => { app.Run(context => diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionTests.cs index 97dde60c89..f5313a6f10 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionTests.cs @@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests FrameFactory = connectionContext => new Frame( new DummyApplication(httpContext => TaskUtilities.CompletedTask), connectionContext), Memory = memory, - ServerAddress = ServerAddress.FromUrl($"http://localhost:0"), + ServerAddress = ServerAddress.FromUrl("http://127.0.0.1:0"), Thread = engine.Threads[0] }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, trace); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs index a7c88d914e..f6894e7dfb 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs @@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests testContext.App = App; var engine = new KestrelEngine(testContext); engine.Start(1); - var address = ServerAddress.FromUrl($"http://localhost:0/"); + var address = ServerAddress.FromUrl("http://127.0.0.1:0/"); var started = engine.CreateServer(address); started.Dispose(); engine.Dispose(); @@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests testContext.App = App; var engine = new KestrelEngine(testContext); engine.Start(1); - var address = ServerAddress.FromUrl($"http://localhost:0/"); + var address = ServerAddress.FromUrl("http://127.0.0.1:0/"); var started = engine.CreateServer(address); var socket = TestConnection.CreateConnectedLoopbackSocket(address.Port); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpsConnectionFilterTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/HttpsConnectionFilterTests.cs index 50b89a0416..61998ef317 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpsConnectionFilterTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/HttpsConnectionFilterTests.cs @@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests new NoOpConnectionFilter()) ); - using (var server = new TestServer(App, serviceContext, "https://localhost:0/")) + using (var server = new TestServer(App, serviceContext, "https://127.0.0.1:0/")) { using (var client = new HttpClient(handler)) { @@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests new NoOpConnectionFilter()) ); - using (var server = new TestServer(App, serviceContext, "https://localhost:0/")) + using (var server = new TestServer(App, serviceContext, "https://127.0.0.1:0/")) { using (var client = new HttpClient(handler)) { @@ -172,7 +172,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests return context.Response.WriteAsync("hello world"); }; - using (var server = new TestServer(app, serviceContext, "https://localhost:0/")) + using (var server = new TestServer(app, serviceContext, "https://127.0.0.1:0/")) { using (var client = new HttpClient(handler)) { @@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests return context.Response.WriteAsync("hello world"); }; - using (var server = new TestServer(app, serviceContext, "https://localhost:0/")) + using (var server = new TestServer(app, serviceContext, "https://127.0.0.1:0/")) { // SslStream is used to ensure the certificate is actually passed to the server // HttpClient might not send the certificate because it is invalid or it doesn't match any @@ -281,7 +281,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests RequestDelegate app = context => context.Response.WriteAsync(context.Request.Scheme); - using (var server = new TestServer(app, serviceContext, "https://localhost:0/")) + using (var server = new TestServer(app, serviceContext, "https://127.0.0.1:0/")) { using (var client = new HttpClient(handler)) { @@ -327,7 +327,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests return context.Response.WriteAsync("hello world"); }; - using (var server = new TestServer(app, serviceContext, "https://localhost:0/")) + using (var server = new TestServer(app, serviceContext, "https://127.0.0.1:0/")) { // SslStream is used to ensure the certificate is actually passed to the server // HttpClient might not send the certificate because it is invalid or it doesn't match any @@ -384,7 +384,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests return context.Response.WriteAsync("hello world"); }; - using (var server = new TestServer(app, serviceContext, "https://localhost:0/")) + using (var server = new TestServer(app, serviceContext, "https://127.0.0.1:0/")) { // SslStream is used to ensure the certificate is actually passed to the server // HttpClient might not send the certificate because it is invalid or it doesn't match any diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs index ead65ada56..1752f4f40f 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs @@ -157,7 +157,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests var serverListenTcp = new UvTcpHandle(_logger); serverListenTcp.Init(loop, (a, b) => { }); - var address = ServerAddress.FromUrl($"http://localhost:0/"); + var address = ServerAddress.FromUrl($"http://127.0.0.1:0/"); serverListenTcp.Bind(address); var port = serverListenTcp.GetSockIPEndPoint().Port; serverListenTcp.Listen(128, (_1, status, error, _2) => diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs index d1baa5c785..e8d3f66fcd 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs @@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop, (a, b) => { }); - var address = ServerAddress.FromUrl("http://localhost:0/"); + var address = ServerAddress.FromUrl("http://127.0.0.1:0/"); tcp.Bind(address); tcp.Dispose(); loop.Run(); @@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop, (a, b) => { }); - var address = ServerAddress.FromUrl($"http://localhost:0/"); + var address = ServerAddress.FromUrl($"http://127.0.0.1:0/"); tcp.Bind(address); var port = tcp.GetSockIPEndPoint().Port; tcp.Listen(10, (stream, status, error, state) => @@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop, (a, b) => { }); - var address = ServerAddress.FromUrl($"http://localhost:0/"); + var address = ServerAddress.FromUrl($"http://127.0.0.1:0/"); tcp.Bind(address); var port = tcp.GetSockIPEndPoint().Port; tcp.Listen(10, (_, status, error, state) => @@ -155,7 +155,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop, (a, b) => { }); - var address = ServerAddress.FromUrl($"http://localhost:0/"); + var address = ServerAddress.FromUrl($"http://127.0.0.1:0/"); tcp.Bind(address); var port = tcp.GetSockIPEndPoint().Port; tcp.Listen(10, (_, status, error, state) => diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ServerAddressTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/ServerAddressTests.cs index 8aa99d1917..50c96cc9c4 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ServerAddressTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/ServerAddressTests.cs @@ -60,5 +60,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests Assert.True(serverAddres.PathBase.IsNormalized(NormalizationForm.FormC)); Assert.Equal("/p\u00C5thbase", serverAddres.PathBase); } + + [Fact] + public void WithHostReturnsNewInstanceWithDifferentHost() + { + var serverAddress = ServerAddress.FromUrl("http://localhost:8080"); + var newAddress = serverAddress.WithHost("otherhost"); + Assert.NotSame(serverAddress, newAddress); + Assert.Equal("otherhost", newAddress.Host); + Assert.Equal("localhost", serverAddress.Host); + } } } diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestServer.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/TestServer.cs index c041f5f437..9719999265 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestServer.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/TestServer.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Threading; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel; using Microsoft.AspNetCore.Server.Kestrel.Http; @@ -16,7 +15,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests { private KestrelEngine _engine; private IDisposable _server; - ServerAddress _address; + private ServerAddress _address; public TestServer(RequestDelegate app) : this(app, new TestServiceContext()) @@ -24,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests } public TestServer(RequestDelegate app, ServiceContext context) - : this(app, context, "http://localhost:0/") + : this(app, context, "http://127.0.0.1:0/") { }