Warn instead of throw when ignoring IServerAddressesFeature

- Throwing could be too much when IServerAddressesFeature URLs come from VS
- Listen on 127.0.0.1:5000 by default

https://github.com/aspnet/Hosting/issues/917
This commit is contained in:
Stephen Halter 2017-01-17 16:30:02 -08:00
parent 20e02e8fd6
commit f32058c5c3
8 changed files with 132 additions and 73 deletions

View File

@ -36,6 +36,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC
ProjectSection(SolutionItems) = preProject
test\shared\DummyApplication.cs = test\shared\DummyApplication.cs
test\shared\HttpClientSlim.cs = test\shared\HttpClientSlim.cs
test\shared\KestrelTestLoggerFactory.cs = test\shared\KestrelTestLoggerFactory.cs
test\shared\LifetimeNotImplemented.cs = test\shared\LifetimeNotImplemented.cs
test\shared\MockConnection.cs = test\shared\MockConnection.cs
test\shared\MockFrameControl.cs = test\shared\MockFrameControl.cs

View File

@ -34,33 +34,34 @@ namespace SampleApp
public static void Main(string[] args)
{
var hostBuilder = new WebHostBuilder().UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5000, listenOptions =>
var host = new WebHostBuilder()
.UseKestrel(options =>
{
// Uncomment the following to enable Nagle's algorithm for this endpoint.
//listenOptions.NoDelay = false;
options.Listen(IPAddress.Loopback, 5000, listenOptions =>
{
// Uncomment the following to enable Nagle's algorithm for this endpoint.
//listenOptions.NoDelay = false;
listenOptions.UseConnectionLogging();
});
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
listenOptions.UseConnectionLogging();
});
listenOptions.UseConnectionLogging();
});
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
listenOptions.UseConnectionLogging();
});
options.UseSystemd();
options.UseSystemd();
// The following section should be used to demo sockets
//options.ListenUnixSocket("/tmp/kestrel-test.sock");
// The following section should be used to demo sockets
//options.ListenUnixSocket("/tmp/kestrel-test.sock");
// Uncomment the following line to change the default number of libuv threads for all endpoints.
//options.ThreadCount = 4;
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>();
// Uncomment the following line to change the default number of libuv threads for all endpoints.
//options.ThreadCount = 4;
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
var host = hostBuilder.Build();
host.Run();
}
}

View File

@ -1,12 +1,12 @@
// 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.Net;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
{
internal class Constants
internal static class Constants
{
public const int ListenBacklog = 128;
@ -14,6 +14,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
public static readonly int? ECONNRESET = GetECONNRESET();
public static readonly int? EADDRINUSE = GetEADDRINUSE();
/// <summary>
/// The IPEndPoint Kestrel will bind to if nothing else is specified.
/// </summary>
public static readonly IPEndPoint DefaultIPEndPoint = new IPEndPoint(IPAddress.Loopback, 5000);
/// <summary>
/// Prefix of host name used to specify Unix sockets in the configuration.
/// </summary>

View File

@ -112,22 +112,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel
}
engine.Start(threadCount);
var atLeastOneListener = false;
var listenOptions = Options.ListenOptions;
var hasListenOptions = listenOptions.Any();
var hasServerAddresses = _serverAddresses.Addresses.Any();
if (listenOptions.Any())
if (hasListenOptions && hasServerAddresses)
{
var addresses = _serverAddresses.Addresses;
if (addresses.SingleOrDefault() != "http://localhost:5000")
{
var joined = string.Join(", ", addresses);
throw new NotSupportedException($"Specifying address(es) '{joined}' is incompatible with also configuring endpoint(s) in UseKestrel.");
}
var joined = string.Join(", ", _serverAddresses.Addresses);
_logger.LogWarning($"Overriding address(es) '{joined}'. Binding to endpoints defined in {nameof(WebHostBuilderKestrelExtensions.UseKestrel)}() instead.");
_serverAddresses.Addresses.Clear();
}
else
else if (!hasListenOptions && !hasServerAddresses)
{
_logger.LogDebug($"No listening endpoints were configured. Binding to {Constants.DefaultIPEndPoint} by default.");
listenOptions.Add(new ListenOptions(Constants.DefaultIPEndPoint));
}
else if (!hasListenOptions)
{
// If no endpoints are configured directly using KestrelServerOptions, use those configured via the IServerAddressesFeature.
var copiedAddresses = _serverAddresses.Addresses.ToArray();
@ -155,7 +157,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
// If StartLocalhost doesn't throw, there is at least one listener.
// The port cannot change for "localhost".
_serverAddresses.Addresses.Add(parsedAddress.ToString());
atLeastOneListener = true;
}
else
{
@ -172,8 +173,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
foreach (var endPoint in listenOptions)
{
atLeastOneListener = true;
try
{
_disposables.Push(engine.CreateServer(endPoint));
@ -191,11 +190,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
// If requested port was "0", replace with assigned dynamic port.
_serverAddresses.Addresses.Add(endPoint.ToString());
}
if (!atLeastOneListener)
{
throw new InvalidOperationException("No recognized listening addresses were configured.");
}
}
catch (Exception ex)
{

View File

@ -16,6 +16,8 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Xunit;
@ -126,6 +128,35 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
}
[ConditionalFact(Skip = "Waiting on https://github.com/aspnet/Hosting/issues/917")]
[PortSupportedCondition(5000)]
public async Task DefaultsToPort5000()
{
var testLogger = new TestApplicationErrorLogger();
var hostBuilder = new WebHostBuilder()
.UseKestrel()
.ConfigureServices(services =>
{
services.AddSingleton<ILoggerFactory>(new KestrelTestLoggerFactory(testLogger));
})
.Configure(ConfigureEchoAddress);
using (var host = hostBuilder.Build())
{
host.Start();
var debugLog = testLogger.Messages.Single(log => log.LogLevel == LogLevel.Debug);
Assert.True(debugLog.Message.Contains("default"));
foreach (var testUrl in new[] { "http://127.0.0.1:5000", "http://localhost:5000" })
{
var response = await HttpClientSlim.GetStringAsync(testUrl);
Assert.Equal(new Uri(testUrl).ToString(), response);
}
}
}
[Fact]
public void ThrowsWhenBindingToIPv4AddressInUse()
{

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Server.Kestrel;
@ -43,16 +45,26 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
Assert.Equal(1, testLogger.CriticalErrorsLogged);
}
[Fact]
public void StartWithEmptyAddressesThrows()
[Theory]
[InlineData("http://localhost:5000")]
[InlineData("The value of the string shouldn't matter.")]
[InlineData(null)]
public void StartWarnsWhenIgnoringIServerAddressesFeature(string ignoredAddress)
{
var testLogger = new TestApplicationErrorLogger();
var server = CreateServer(new KestrelServerOptions(), testLogger);
var kestrelOptions = new KestrelServerOptions();
var exception = Assert.Throws<InvalidOperationException>(() => StartDummyApplication(server));
// Directly configuring an endpoint using Listen causes the IServerAddressesFeature to be ignored.
kestrelOptions.Listen(IPAddress.Loopback, 0);
Assert.Equal("No recognized listening addresses were configured.", exception.Message);
Assert.Equal(1, testLogger.CriticalErrorsLogged);
using (var server = CreateServer(kestrelOptions, testLogger))
{
server.Features.Get<IServerAddressesFeature>().Addresses.Add(ignoredAddress);
StartDummyApplication(server);
var warning = testLogger.Messages.Single(log => log.LogLevel == LogLevel.Warning);
Assert.True(warning.Message.Contains("Overriding"));
}
}
[Theory]
@ -87,37 +99,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{
var lifetime = new LifetimeNotImplemented();
return new KestrelServer(Options.Create(options), lifetime, new TestLoggerFactory(testLogger));
return new KestrelServer(Options.Create(options), lifetime, new KestrelTestLoggerFactory(testLogger));
}
private static void StartDummyApplication(IServer server)
{
server.Start(new DummyApplication(context => TaskCache.CompletedTask));
}
private class TestLoggerFactory : ILoggerFactory
{
private readonly ILogger _testLogger;
public TestLoggerFactory(ILogger testLogger)
{
_testLogger = testLogger;
}
public ILogger CreateLogger(string categoryName)
{
return _testLogger;
}
public void AddProvider(ILoggerProvider provider)
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
}
}
}

View File

@ -0,0 +1,33 @@
// 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;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Testing
{
public class KestrelTestLoggerFactory : ILoggerFactory
{
private readonly ILogger _testLogger;
public KestrelTestLoggerFactory(ILogger testLogger)
{
_testLogger = testLogger;
}
public ILogger CreateLogger(string categoryName)
{
return _testLogger;
}
public void AddProvider(ILoggerProvider provider)
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
}
}

View File

@ -38,7 +38,13 @@ namespace Microsoft.AspNetCore.Testing
Console.WriteLine($"Log {logLevel}[{eventId}]: {formatter(state, exception)} {exception?.Message}");
#endif
Messages.Add(new LogMessage { LogLevel = logLevel, EventId = eventId, Exception = exception });
Messages.Add(new LogMessage
{
LogLevel = logLevel,
EventId = eventId,
Exception = exception,
Message = formatter(state, exception)
});
}
public class LogMessage
@ -46,6 +52,7 @@ namespace Microsoft.AspNetCore.Testing
public LogLevel LogLevel { get; set; }
public EventId EventId { get; set; }
public Exception Exception { get; set; }
public string Message { get; set; }
}
}
}