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

Warn instead of throw when ignoring IServerAddressesFeature #1312

Merged
merged 1 commit into from
Jan 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions KestrelHttpServer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 22 additions & 21 deletions samples/SampleApp/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,34 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)

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();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
// 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;

public const int EOF = -4095;
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>
Expand Down
28 changes: 11 additions & 17 deletions src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,24 @@ public void Start<TContext>(IHttpApplication<TContext> application)
}

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();
Expand Down Expand Up @@ -155,7 +157,6 @@ public void Start<TContext>(IHttpApplication<TContext> application)
// 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
{
Expand All @@ -172,8 +173,6 @@ public void Start<TContext>(IHttpApplication<TContext> application)

foreach (var endPoint in listenOptions)
{
atLeastOneListener = true;

try
{
_disposables.Push(engine.CreateServer(endPoint));
Expand All @@ -191,11 +190,6 @@ public void Start<TContext>(IHttpApplication<TContext> application)
// 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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
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;

Expand Down Expand Up @@ -126,6 +128,35 @@ private async Task RegisterIPEndPoint_Success(IPEndPoint endPoint, Func<IPEndPoi
}
}

[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()
{
Expand Down
51 changes: 19 additions & 32 deletions test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -43,16 +45,26 @@ public void StartWithInvalidAddressThrows()
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]
Expand Down Expand Up @@ -87,37 +99,12 @@ private static KestrelServer CreateServer(KestrelServerOptions options, ILogger
{
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();
}
}
}
}
33 changes: 33 additions & 0 deletions test/shared/KestrelTestLoggerFactory.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
9 changes: 8 additions & 1 deletion test/shared/TestApplicationErrorLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,21 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
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
{
public LogLevel LogLevel { get; set; }
public EventId EventId { get; set; }
public Exception Exception { get; set; }
public string Message { get; set; }
}
}
}