Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Commit

Permalink
Rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
JunTaoLuo committed Apr 13, 2017
1 parent eec9f21 commit 2398330
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 73 deletions.
148 changes: 75 additions & 73 deletions src/Microsoft.AspNetCore.Server.Kestrel.Core/KestrelServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public async Task StartAsync<TContext>(IHttpApplication<TContext> application, C
listenOptions.Clear();
}

BindToServerAddresses(listenOptions, serviceContext, application);
await BindToServerAddresses(listenOptions, serviceContext, application, cancellationToken);
}
else if (hasListenOptions)
{
Expand All @@ -138,88 +138,22 @@ public async Task StartAsync<TContext>(IHttpApplication<TContext> application, C
var joined = string.Join(", ", _serverAddresses.Addresses);
_logger.LogWarning($"Overriding address(es) '{joined}'. Binding to endpoints defined in UseKestrel() instead.");

// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
await StartLocalhostAsync(ServerAddress.FromUrl(Constants.DefaultServerAddress), serviceContext, application, cancellationToken);

try
{
transport.BindAsync().Wait();
}
catch (AggregateException ex) when (ex.InnerException is AddressInUseException)
{
throw new IOException($"Failed to bind to address {endPoint}: address already in use.", ex);
}

// If requested port was "0", replace with assigned dynamic port.
_serverAddresses.Addresses.Add(endPoint.ToString());
_serverAddresses.Addresses.Clear();
}

await BindToEndpoints(listenOptions, serviceContext, application);
}
else if (hasServerAddresses)
{
// If no endpoints are configured directly using KestrelServerOptions, use those configured via the IServerAddressesFeature.
var copiedAddresses = _serverAddresses.Addresses.ToArray();
_serverAddresses.Addresses.Clear();

foreach (var address in copiedAddresses)
{
var parsedAddress = ServerAddress.FromUrl(address);

if (parsedAddress.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"HTTPS endpoints can only be configured using {nameof(KestrelServerOptions)}.{nameof(KestrelServerOptions.Listen)}().");
}

if (!string.IsNullOrEmpty(parsedAddress.PathBase))
{
throw new InvalidOperationException($"A path base can only be configured using {nameof(IApplicationBuilder)}.UsePathBase().");
}

if (!string.IsNullOrEmpty(parsedAddress.PathBase))
{
_logger.LogWarning($"Path base in address {address} is not supported and will be ignored. To specify a path base, use {nameof(IApplicationBuilder)}.UsePathBase().");
}

if (parsedAddress.IsUnixPipe)
{
listenOptions.Add(new ListenOptions(parsedAddress.UnixPipePath)
{
Scheme = parsedAddress.Scheme,
});
}
else
{
if (string.Equals(parsedAddress.Host, "localhost", StringComparison.OrdinalIgnoreCase))
{
// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
await StartLocalhostAsync(parsedAddress, serviceContext, application, cancellationToken);

// If StartLocalhost doesn't throw, there is at least one listener.
// The port cannot change for "localhost".
_serverAddresses.Addresses.Add(parsedAddress.ToString());
}
else
{
// These endPoints will be added later to _serverAddresses.Addresses
listenOptions.Add(new ListenOptions(CreateIPEndPoint(parsedAddress))
{
Scheme = parsedAddress.Scheme,
});
}
}
}
await BindToServerAddresses(listenOptions, serviceContext, application, cancellationToken);
}
else
{
_logger.LogDebug($"No listening endpoints were configured. Binding to {Constants.DefaultServerAddress} by default.");

try
{
await transport.BindAsync();
}
catch (AddressInUseException ex)
{
throw new IOException($"Failed to bind to address {endPoint}: address already in use.", ex);
}
// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
await StartLocalhostAsync(ServerAddress.FromUrl(Constants.DefaultServerAddress), serviceContext, application, cancellationToken);

// If StartLocalhost doesn't throw, there is at least one listener.
// The port cannot change for "localhost".
Expand All @@ -234,6 +168,74 @@ public async Task StartAsync<TContext>(IHttpApplication<TContext> application, C
}
}

private async Task BindToServerAddresses<TContext>(List<ListenOptions> listenOptions, ServiceContext serviceContext, IHttpApplication<TContext> application, CancellationToken cancellationToken)
{
var copiedAddresses = _serverAddresses.Addresses.ToArray();
_serverAddresses.Addresses.Clear();
foreach (var address in copiedAddresses)
{
var parsedAddress = ServerAddress.FromUrl(address);

if (parsedAddress.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"HTTPS endpoints can only be configured using {nameof(KestrelServerOptions)}.{nameof(KestrelServerOptions.Listen)}().");
}

if (!string.IsNullOrEmpty(parsedAddress.PathBase))
{
throw new InvalidOperationException($"A path base can only be configured using {nameof(IApplicationBuilder)}.UsePathBase().");
}

if (parsedAddress.IsUnixPipe)
{
listenOptions.Add(new ListenOptions(parsedAddress.UnixPipePath)
{
Scheme = parsedAddress.Scheme,
});
}
else if (string.Equals(parsedAddress.Host, "localhost", StringComparison.OrdinalIgnoreCase))
{
// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
await StartLocalhostAsync(parsedAddress, serviceContext, application, cancellationToken);
// If StartLocalhost doesn't throw, there is at least one listener.
// The port cannot change for "localhost".
_serverAddresses.Addresses.Add(parsedAddress.ToString());
}
else
{
// These endPoints will be added later to _serverAddresses.Addresses
listenOptions.Add(new ListenOptions(CreateIPEndPoint(parsedAddress))
{
Scheme = parsedAddress.Scheme,
});
}
}

await BindToEndpoints(listenOptions, serviceContext, application);
}

private async Task BindToEndpoints<TContext>(List<ListenOptions> listenOptions, ServiceContext serviceContext, IHttpApplication<TContext> application)
{
foreach (var endPoint in listenOptions)
{
var connectionHandler = new ConnectionHandler<TContext>(endPoint, serviceContext, application);
var transport = _transportFactory.Create(endPoint, connectionHandler);
_transports.Add(transport);

try
{
await transport.BindAsync();
}
catch (AddressInUseException ex)
{
throw new IOException($"Failed to bind to address {endPoint}: address already in use.", ex);
}

// If requested port was "0", replace with assigned dynamic port.
_serverAddresses.Addresses.Add(endPoint.ToString());
}
}

// Graceful shutdown if possible
public async Task StopAsync(CancellationToken cancellationToken)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,73 @@ public void ThrowsWhenBindingToIPv6AddressInUse()
}
}
}
[ConditionalFact]
[PortSupportedCondition(5002)]
public async Task OverrideDirectConfigurationWithIServerAddressesFeature_Succeeds()
{
var overrideAddress = "http://localhost:5002";
var testLogger = new TestApplicationErrorLogger();

var hostBuilder = new WebHostBuilder()
.UseKestrel(options => options.Listen(IPAddress.Loopback, 5001))
.UseUrls(overrideAddress)
.PreferHostingUrls(true)
.UseLoggerFactory(_ => new KestrelTestLoggerFactory(testLogger))
.Configure(ConfigureEchoAddress);

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

Assert.Equal(5002, host.GetPort());
Assert.Single(testLogger.Messages, log => log.LogLevel == LogLevel.Warning &&
string.Equals($"Overriding endpoints defined in UseKestrel() since {nameof(IServerAddressesFeature.PreferHostingUrls)} is set to true. Binding to address(es) '{overrideAddress}' instead.",
log.Message, StringComparison.Ordinal));

Assert.Equal(new Uri(overrideAddress).ToString(), await HttpClientSlim.GetStringAsync(overrideAddress));
}
}

[ConditionalFact]
[PortSupportedCondition(5001)]
public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_IfPreferHostingUrlsFalse()
{
var endPointAddress = "http://localhost:5001";

var hostBuilder = new WebHostBuilder()
.UseKestrel(options => options.Listen(IPAddress.Loopback, 5001))
.UseUrls("http://localhost:5002")
.PreferHostingUrls(false)
.Configure(ConfigureEchoAddress);

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

Assert.Equal(5001, host.GetPort());
Assert.Equal(new Uri(endPointAddress).ToString(), await HttpClientSlim.GetStringAsync(endPointAddress));
}
}

[ConditionalFact]
[PortSupportedCondition(5001)]
public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_IfAddressesEmpty()
{
var endPointAddress = "http://localhost:5001";

var hostBuilder = new WebHostBuilder()
.UseKestrel(options => options.Listen(IPAddress.Loopback, 5001))
.PreferHostingUrls(true)
.Configure(ConfigureEchoAddress);

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

Assert.Equal(5001, host.GetPort());
Assert.Equal(new Uri(endPointAddress).ToString(), await HttpClientSlim.GetStringAsync(endPointAddress));
}
}

[ConditionalFact]
[PortSupportedCondition(5002)]
Expand Down

0 comments on commit 2398330

Please sign in to comment.