Include address in error message when address already in use (#1086).

This commit is contained in:
Cesar Blum Silveira 2016-10-03 15:12:47 -07:00
parent f1f66448f4
commit 2c94884da9
2 changed files with 74 additions and 22 deletions

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -109,8 +110,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel
if (!parsedAddress.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase)) if (!parsedAddress.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase))
{ {
_disposables.Push(engine.CreateServer( try
parsedAddress)); {
_disposables.Push(engine.CreateServer(parsedAddress));
}
catch (AggregateException ex)
{
if ((ex.InnerException as UvException)?.StatusCode == Constants.EADDRINUSE)
{
throw new IOException($"Failed to bind to address {parsedAddress}: address already in use.", ex);
}
throw;
}
} }
else else
{ {
@ -120,24 +132,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel
} }
var ipv4Address = parsedAddress.WithHost("127.0.0.1"); var ipv4Address = parsedAddress.WithHost("127.0.0.1");
var exceptions = new List<UvException>(); var exceptions = new List<Exception>();
try try
{ {
_disposables.Push(engine.CreateServer(ipv4Address)); _disposables.Push(engine.CreateServer(ipv4Address));
} }
catch (AggregateException ex) catch (AggregateException ex) when (ex.InnerException is UvException)
{ {
var uvException = ex.InnerException as UvException; if ((ex.InnerException as UvException).StatusCode == Constants.EADDRINUSE)
if (uvException != null && uvException.StatusCode != Constants.EADDRINUSE)
{ {
_logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv4 loopback interface."); throw new IOException($"Failed to bind to address {parsedAddress.ToString()} on the IPv4 loopback interface: port already in use.", ex);
exceptions.Add(uvException);
} }
else else
{ {
throw; _logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv4 loopback interface.");
exceptions.Add(ex.InnerException);
} }
} }
@ -147,26 +157,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel
{ {
_disposables.Push(engine.CreateServer(ipv6Address)); _disposables.Push(engine.CreateServer(ipv6Address));
} }
catch (AggregateException ex) catch (AggregateException ex) when (ex.InnerException is UvException)
{ {
var uvException = ex.InnerException as UvException; if ((ex.InnerException as UvException).StatusCode == Constants.EADDRINUSE)
if (uvException != null && uvException.StatusCode != Constants.EADDRINUSE)
{ {
_logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv6 loopback interface."); throw new IOException($"Failed to bind to address {parsedAddress.ToString()} on the IPv6 loopback interface: port already in use.", ex);
exceptions.Add(uvException);
} }
else else
{ {
throw; _logger.LogWarning(0, ex, $"Unable to bind to {parsedAddress.ToString()} on the IPv6 loopback interface.");
exceptions.Add(ex.InnerException);
} }
} }
if (exceptions.Count == 2) if (exceptions.Count == 2)
{ {
var ex = new AggregateException(exceptions); throw new IOException($"Failed to bind to address {parsedAddress.ToString()}.", new AggregateException(exceptions));
_logger.LogError(0, ex, $"Unable to bind to {parsedAddress.ToString()} on any loopback interface.");
throw ex;
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
@ -97,6 +98,49 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
} }
} }
[Fact]
public void ThrowsWhenBindingToIPv4AddressInUse()
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
var port = GetNextPort();
socket.Bind(new IPEndPoint(IPAddress.Loopback, port));
var hostBuilder = new WebHostBuilder()
.UseKestrel()
.UseUrls($"http://127.0.0.1:{port}")
.Configure(ConfigureEchoAddress);
using (var host = hostBuilder.Build())
{
var exception = Assert.Throws<IOException>(() => host.Start());
Assert.Equal($"Failed to bind to address http://127.0.0.1:{port}: address already in use.", exception.Message);
}
}
}
[ConditionalFact]
[IPv6SupportedCondition]
public void ThrowsWhenBindingToIPv6AddressInUse()
{
using (var socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
{
var port = GetNextPort();
socket.Bind(new IPEndPoint(IPAddress.IPv6Loopback, port));
var hostBuilder = new WebHostBuilder()
.UseKestrel()
.UseUrls($"http://[::1]:{port}")
.Configure(ConfigureEchoAddress);
using (var host = hostBuilder.Build())
{
var exception = Assert.Throws<IOException>(() => host.Start());
Assert.Equal($"Failed to bind to address http://[::1]:{port}: address already in use.", exception.Message);
}
}
}
[Fact] [Fact]
public void ThrowsWhenBindingLocalhostToIPv4AddressInUse() public void ThrowsWhenBindingLocalhostToIPv4AddressInUse()
{ {
@ -138,8 +182,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
using (var host = hostBuilder.Build()) using (var host = hostBuilder.Build())
{ {
var exception = Assert.Throws<AggregateException>(() => host.Start()); var exception = Assert.Throws<IOException>(() => host.Start());
Assert.Contains(exception.InnerExceptions, ex => ex is UvException); Assert.Equal(
$"Failed to bind to address http://localhost:{port} on the {(addressFamily == AddressFamily.InterNetwork ? "IPv4" : "IPv6")} loopback interface: port already in use.",
exception.Message);
} }
} }
} }