diff --git a/src/Servers/Kestrel/Core/src/CoreStrings.resx b/src/Servers/Kestrel/Core/src/CoreStrings.resx index 3e88281f1a..3a25467bcc 100644 --- a/src/Servers/Kestrel/Core/src/CoreStrings.resx +++ b/src/Servers/Kestrel/Core/src/CoreStrings.resx @@ -1,17 +1,17 @@ - @@ -559,7 +559,8 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l Detected a TLS handshake to an endpoint that does not have TLS enabled. - + + The ASP.NET Core developer certificate is in an invalid state. To fix this issue, run the following commands 'dotnet dev-certs https --clean' and 'dotnet dev-certs https' to remove all existing ASP.NET Core development certificates and create a new untrusted developer certificate. On macOS or Windows, use 'dotnet dev-certs https --trust' to trust the new certificate. @@ -574,4 +575,10 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l Decoded string length of {length} octets is greater than the configured maximum length of {maxStringLength} octets. - + + Quic transport not found when using HTTP/3. + + + Unable to resolve service for type 'Microsoft.AspNetCore.Connections.IConnectionListenerFactory' while attempting to activate 'Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer'. + + \ No newline at end of file diff --git a/src/Servers/Kestrel/Core/src/KestrelServer.cs b/src/Servers/Kestrel/Core/src/KestrelServer.cs index 802232326e..5d1c1b4cee 100644 --- a/src/Servers/Kestrel/Core/src/KestrelServer.cs +++ b/src/Servers/Kestrel/Core/src/KestrelServer.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core { private readonly List<(IConnectionListener, Task)> _transports = new List<(IConnectionListener, Task)>(); private readonly IServerAddressesFeature _serverAddresses; - private readonly IEnumerable _transportFactories; + private readonly List _transportFactories; private bool _hasStarted; private int _stopping; @@ -42,7 +42,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core throw new ArgumentNullException(nameof(transportFactories)); } - _transportFactories = transportFactories; + _transportFactories = transportFactories.ToList(); + + if (_transportFactories.Count == 0) + { + throw new InvalidOperationException(CoreStrings.TransportNotFound); + } + ServiceContext = serviceContext; Features = new FeatureCollection(); @@ -143,19 +149,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core { if (transportFactory is IMultiplexedConnectionListenerFactory) { + // Don't break early. Always use the last registered factory. factory = transportFactory; - break; } } if (factory == null) { - throw new Exception("Quic transport not found when using HTTP/3"); + throw new InvalidOperationException(CoreStrings.QuicTransportNotFound); } } else { - factory = _transportFactories.Single(); + factory = _transportFactories.Last(); } var transport = await factory.BindAsync(options.EndPoint).ConfigureAwait(false); diff --git a/src/Servers/Kestrel/Core/test/KestrelServerTests.cs b/src/Servers/Kestrel/Core/test/KestrelServerTests.cs index d29b50e1f0..56454bb7a4 100644 --- a/src/Servers/Kestrel/Core/test/KestrelServerTests.cs +++ b/src/Servers/Kestrel/Core/test/KestrelServerTests.cs @@ -204,22 +204,45 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLoggerFactory = new Mock(); var mockLogger = new Mock(); mockLoggerFactory.Setup(m => m.CreateLogger(It.IsAny())).Returns(mockLogger.Object); - new KestrelServer(Options.Create(null), Mock.Of>(), mockLoggerFactory.Object); + new KestrelServer(Options.Create(null), new List() { new MockTransportFactory() }, mockLoggerFactory.Object); mockLoggerFactory.Verify(factory => factory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel")); } [Fact] - public void StartWithNoTransportFactoryThrows() + public void ConstructorWithNullTransportFactoriesThrows() { - var mockLoggerFactory = new Mock(); - var mockLogger = new Mock(); - mockLoggerFactory.Setup(m => m.CreateLogger(It.IsAny())).Returns(mockLogger.Object); var exception = Assert.Throws(() => - new KestrelServer(Options.Create(null), null, mockLoggerFactory.Object)); + new KestrelServer( + Options.Create(null), + null, + new LoggerFactory(new[] { new KestrelTestLoggerProvider() }))); Assert.Equal("transportFactories", exception.ParamName); } + [Fact] + public void ConstructorWithNoTransportFactoriesThrows() + { + var exception = Assert.Throws(() => + new KestrelServer( + Options.Create(null), + new List(), + new LoggerFactory(new[] { new KestrelTestLoggerProvider() }))); + + Assert.Equal(CoreStrings.TransportNotFound, exception.Message); + } + + [Fact] + public void StartWithMultipleTransportFactoriesDoesNotThrow() + { + using var server = new KestrelServer( + Options.Create(CreateServerOptions()), + new List() { new ThrowingTransportFactory(), new MockTransportFactory() }, + new LoggerFactory(new[] { new KestrelTestLoggerProvider() })); + + StartDummyApplication(server); + } + [Fact] public async Task StopAsyncCallsCompleteWhenFirstCallCompletes() { @@ -456,5 +479,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests return new ValueTask(mock.Object); } } + + private class ThrowingTransportFactory : IConnectionListenerFactory + { + public ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default) + { + throw new InvalidOperationException(); + } + } } }