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();
+ }
+ }
}
}