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

Commit f32058c

Browse files
committed
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 aspnet/Hosting#917
1 parent 20e02e8 commit f32058c

File tree

8 files changed

+132
-73
lines changed

8 files changed

+132
-73
lines changed

KestrelHttpServer.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC
3636
ProjectSection(SolutionItems) = preProject
3737
test\shared\DummyApplication.cs = test\shared\DummyApplication.cs
3838
test\shared\HttpClientSlim.cs = test\shared\HttpClientSlim.cs
39+
test\shared\KestrelTestLoggerFactory.cs = test\shared\KestrelTestLoggerFactory.cs
3940
test\shared\LifetimeNotImplemented.cs = test\shared\LifetimeNotImplemented.cs
4041
test\shared\MockConnection.cs = test\shared\MockConnection.cs
4142
test\shared\MockFrameControl.cs = test\shared\MockFrameControl.cs

samples/SampleApp/Startup.cs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,33 +34,34 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
3434

3535
public static void Main(string[] args)
3636
{
37-
var hostBuilder = new WebHostBuilder().UseKestrel(options =>
38-
{
39-
options.Listen(IPAddress.Loopback, 5000, listenOptions =>
37+
var host = new WebHostBuilder()
38+
.UseKestrel(options =>
4039
{
41-
// Uncomment the following to enable Nagle's algorithm for this endpoint.
42-
//listenOptions.NoDelay = false;
40+
options.Listen(IPAddress.Loopback, 5000, listenOptions =>
41+
{
42+
// Uncomment the following to enable Nagle's algorithm for this endpoint.
43+
//listenOptions.NoDelay = false;
4344

44-
listenOptions.UseConnectionLogging();
45-
});
46-
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
47-
{
48-
listenOptions.UseHttps("testCert.pfx", "testPassword");
49-
listenOptions.UseConnectionLogging();
50-
});
45+
listenOptions.UseConnectionLogging();
46+
});
47+
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
48+
{
49+
listenOptions.UseHttps("testCert.pfx", "testPassword");
50+
listenOptions.UseConnectionLogging();
51+
});
5152

52-
options.UseSystemd();
53+
options.UseSystemd();
5354

54-
// The following section should be used to demo sockets
55-
//options.ListenUnixSocket("/tmp/kestrel-test.sock");
55+
// The following section should be used to demo sockets
56+
//options.ListenUnixSocket("/tmp/kestrel-test.sock");
5657

57-
// Uncomment the following line to change the default number of libuv threads for all endpoints.
58-
//options.ThreadCount = 4;
59-
})
60-
.UseContentRoot(Directory.GetCurrentDirectory())
61-
.UseStartup<Startup>();
58+
// Uncomment the following line to change the default number of libuv threads for all endpoints.
59+
//options.ThreadCount = 4;
60+
})
61+
.UseContentRoot(Directory.GetCurrentDirectory())
62+
.UseStartup<Startup>()
63+
.Build();
6264

63-
var host = hostBuilder.Build();
6465
host.Run();
6566
}
6667
}

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/Constants.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.Net;
45
using System.Runtime.InteropServices;
5-
using System.Text;
66

77
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
88
{
9-
internal class Constants
9+
internal static class Constants
1010
{
1111
public const int ListenBacklog = 128;
1212

1313
public const int EOF = -4095;
1414
public static readonly int? ECONNRESET = GetECONNRESET();
1515
public static readonly int? EADDRINUSE = GetEADDRINUSE();
1616

17+
/// <summary>
18+
/// The IPEndPoint Kestrel will bind to if nothing else is specified.
19+
/// </summary>
20+
public static readonly IPEndPoint DefaultIPEndPoint = new IPEndPoint(IPAddress.Loopback, 5000);
21+
1722
/// <summary>
1823
/// Prefix of host name used to specify Unix sockets in the configuration.
1924
/// </summary>

src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -112,22 +112,24 @@ public void Start<TContext>(IHttpApplication<TContext> application)
112112
}
113113

114114
engine.Start(threadCount);
115-
var atLeastOneListener = false;
116115

117116
var listenOptions = Options.ListenOptions;
117+
var hasListenOptions = listenOptions.Any();
118+
var hasServerAddresses = _serverAddresses.Addresses.Any();
118119

119-
if (listenOptions.Any())
120+
if (hasListenOptions && hasServerAddresses)
120121
{
121-
var addresses = _serverAddresses.Addresses;
122-
if (addresses.SingleOrDefault() != "http://localhost:5000")
123-
{
124-
var joined = string.Join(", ", addresses);
125-
throw new NotSupportedException($"Specifying address(es) '{joined}' is incompatible with also configuring endpoint(s) in UseKestrel.");
126-
}
122+
var joined = string.Join(", ", _serverAddresses.Addresses);
123+
_logger.LogWarning($"Overriding address(es) '{joined}'. Binding to endpoints defined in {nameof(WebHostBuilderKestrelExtensions.UseKestrel)}() instead.");
127124

128125
_serverAddresses.Addresses.Clear();
129126
}
130-
else
127+
else if (!hasListenOptions && !hasServerAddresses)
128+
{
129+
_logger.LogDebug($"No listening endpoints were configured. Binding to {Constants.DefaultIPEndPoint} by default.");
130+
listenOptions.Add(new ListenOptions(Constants.DefaultIPEndPoint));
131+
}
132+
else if (!hasListenOptions)
131133
{
132134
// If no endpoints are configured directly using KestrelServerOptions, use those configured via the IServerAddressesFeature.
133135
var copiedAddresses = _serverAddresses.Addresses.ToArray();
@@ -155,7 +157,6 @@ public void Start<TContext>(IHttpApplication<TContext> application)
155157
// If StartLocalhost doesn't throw, there is at least one listener.
156158
// The port cannot change for "localhost".
157159
_serverAddresses.Addresses.Add(parsedAddress.ToString());
158-
atLeastOneListener = true;
159160
}
160161
else
161162
{
@@ -172,8 +173,6 @@ public void Start<TContext>(IHttpApplication<TContext> application)
172173

173174
foreach (var endPoint in listenOptions)
174175
{
175-
atLeastOneListener = true;
176-
177176
try
178177
{
179178
_disposables.Push(engine.CreateServer(endPoint));
@@ -191,11 +190,6 @@ public void Start<TContext>(IHttpApplication<TContext> application)
191190
// If requested port was "0", replace with assigned dynamic port.
192191
_serverAddresses.Addresses.Add(endPoint.ToString());
193192
}
194-
195-
if (!atLeastOneListener)
196-
{
197-
throw new InvalidOperationException("No recognized listening addresses were configured.");
198-
}
199193
}
200194
catch (Exception ex)
201195
{

test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
using Microsoft.AspNetCore.Http.Extensions;
1717
using Microsoft.AspNetCore.Testing;
1818
using Microsoft.AspNetCore.Testing.xunit;
19+
using Microsoft.Extensions.DependencyInjection;
20+
using Microsoft.Extensions.Logging;
1921
using Microsoft.Extensions.Options;
2022
using Xunit;
2123

@@ -126,6 +128,35 @@ private async Task RegisterIPEndPoint_Success(IPEndPoint endPoint, Func<IPEndPoi
126128
}
127129
}
128130

131+
[ConditionalFact(Skip = "Waiting on https://github.com/aspnet/Hosting/issues/917")]
132+
[PortSupportedCondition(5000)]
133+
public async Task DefaultsToPort5000()
134+
{
135+
var testLogger = new TestApplicationErrorLogger();
136+
137+
var hostBuilder = new WebHostBuilder()
138+
.UseKestrel()
139+
.ConfigureServices(services =>
140+
{
141+
services.AddSingleton<ILoggerFactory>(new KestrelTestLoggerFactory(testLogger));
142+
})
143+
.Configure(ConfigureEchoAddress);
144+
145+
using (var host = hostBuilder.Build())
146+
{
147+
host.Start();
148+
149+
var debugLog = testLogger.Messages.Single(log => log.LogLevel == LogLevel.Debug);
150+
Assert.True(debugLog.Message.Contains("default"));
151+
152+
foreach (var testUrl in new[] { "http://127.0.0.1:5000", "http://localhost:5000" })
153+
{
154+
var response = await HttpClientSlim.GetStringAsync(testUrl);
155+
Assert.Equal(new Uri(testUrl).ToString(), response);
156+
}
157+
}
158+
}
159+
129160
[Fact]
130161
public void ThrowsWhenBindingToIPv4AddressInUse()
131162
{

test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Linq;
6+
using System.Net;
57
using Microsoft.AspNetCore.Hosting.Server;
68
using Microsoft.AspNetCore.Hosting.Server.Features;
79
using Microsoft.AspNetCore.Server.Kestrel;
@@ -43,16 +45,26 @@ public void StartWithInvalidAddressThrows()
4345
Assert.Equal(1, testLogger.CriticalErrorsLogged);
4446
}
4547

46-
[Fact]
47-
public void StartWithEmptyAddressesThrows()
48+
[Theory]
49+
[InlineData("http://localhost:5000")]
50+
[InlineData("The value of the string shouldn't matter.")]
51+
[InlineData(null)]
52+
public void StartWarnsWhenIgnoringIServerAddressesFeature(string ignoredAddress)
4853
{
4954
var testLogger = new TestApplicationErrorLogger();
50-
var server = CreateServer(new KestrelServerOptions(), testLogger);
55+
var kestrelOptions = new KestrelServerOptions();
5156

52-
var exception = Assert.Throws<InvalidOperationException>(() => StartDummyApplication(server));
57+
// Directly configuring an endpoint using Listen causes the IServerAddressesFeature to be ignored.
58+
kestrelOptions.Listen(IPAddress.Loopback, 0);
5359

54-
Assert.Equal("No recognized listening addresses were configured.", exception.Message);
55-
Assert.Equal(1, testLogger.CriticalErrorsLogged);
60+
using (var server = CreateServer(kestrelOptions, testLogger))
61+
{
62+
server.Features.Get<IServerAddressesFeature>().Addresses.Add(ignoredAddress);
63+
StartDummyApplication(server);
64+
65+
var warning = testLogger.Messages.Single(log => log.LogLevel == LogLevel.Warning);
66+
Assert.True(warning.Message.Contains("Overriding"));
67+
}
5668
}
5769

5870
[Theory]
@@ -87,37 +99,12 @@ private static KestrelServer CreateServer(KestrelServerOptions options, ILogger
8799
{
88100
var lifetime = new LifetimeNotImplemented();
89101

90-
return new KestrelServer(Options.Create(options), lifetime, new TestLoggerFactory(testLogger));
102+
return new KestrelServer(Options.Create(options), lifetime, new KestrelTestLoggerFactory(testLogger));
91103
}
92104

93105
private static void StartDummyApplication(IServer server)
94106
{
95107
server.Start(new DummyApplication(context => TaskCache.CompletedTask));
96108
}
97-
98-
private class TestLoggerFactory : ILoggerFactory
99-
{
100-
private readonly ILogger _testLogger;
101-
102-
public TestLoggerFactory(ILogger testLogger)
103-
{
104-
_testLogger = testLogger;
105-
}
106-
107-
public ILogger CreateLogger(string categoryName)
108-
{
109-
return _testLogger;
110-
}
111-
112-
public void AddProvider(ILoggerProvider provider)
113-
{
114-
throw new NotImplementedException();
115-
}
116-
117-
public void Dispose()
118-
{
119-
throw new NotImplementedException();
120-
}
121-
}
122109
}
123110
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace Microsoft.AspNetCore.Testing
8+
{
9+
public class KestrelTestLoggerFactory : ILoggerFactory
10+
{
11+
private readonly ILogger _testLogger;
12+
13+
public KestrelTestLoggerFactory(ILogger testLogger)
14+
{
15+
_testLogger = testLogger;
16+
}
17+
18+
public ILogger CreateLogger(string categoryName)
19+
{
20+
return _testLogger;
21+
}
22+
23+
public void AddProvider(ILoggerProvider provider)
24+
{
25+
throw new NotImplementedException();
26+
}
27+
28+
public void Dispose()
29+
{
30+
throw new NotImplementedException();
31+
}
32+
}
33+
}

test/shared/TestApplicationErrorLogger.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,21 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
3838
Console.WriteLine($"Log {logLevel}[{eventId}]: {formatter(state, exception)} {exception?.Message}");
3939
#endif
4040

41-
Messages.Add(new LogMessage { LogLevel = logLevel, EventId = eventId, Exception = exception });
41+
Messages.Add(new LogMessage
42+
{
43+
LogLevel = logLevel,
44+
EventId = eventId,
45+
Exception = exception,
46+
Message = formatter(state, exception)
47+
});
4248
}
4349

4450
public class LogMessage
4551
{
4652
public LogLevel LogLevel { get; set; }
4753
public EventId EventId { get; set; }
4854
public Exception Exception { get; set; }
55+
public string Message { get; set; }
4956
}
5057
}
5158
}

0 commit comments

Comments
 (0)