Allow more than one IConnectionListenerFactory (#17383)
This commit is contained in:
parent
cf116a7815
commit
b81c1a7954
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -559,7 +559,8 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
|
||||||
</data>
|
</data>
|
||||||
<data name="HttpParserTlsOverHttpError" xml:space="preserve">
|
<data name="HttpParserTlsOverHttpError" xml:space="preserve">
|
||||||
<value>Detected a TLS handshake to an endpoint that does not have TLS enabled.</value>
|
<value>Detected a TLS handshake to an endpoint that does not have TLS enabled.</value>
|
||||||
</data> <data name="BadDeveloperCertificateState" xml:space="preserve">
|
</data>
|
||||||
|
<data name="BadDeveloperCertificateState" xml:space="preserve">
|
||||||
<value>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.</value>
|
<value>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.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="QPackErrorIndexOutOfRange" xml:space="preserve">
|
<data name="QPackErrorIndexOutOfRange" xml:space="preserve">
|
||||||
|
|
@ -574,4 +575,10 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
|
||||||
<data name="QPackStringLengthTooLarge" xml:space="preserve">
|
<data name="QPackStringLengthTooLarge" xml:space="preserve">
|
||||||
<value>Decoded string length of {length} octets is greater than the configured maximum length of {maxStringLength} octets.</value>
|
<value>Decoded string length of {length} octets is greater than the configured maximum length of {maxStringLength} octets.</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
<data name="QuicTransportNotFound" xml:space="preserve">
|
||||||
|
<value>Quic transport not found when using HTTP/3.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TransportNotFound" xml:space="preserve">
|
||||||
|
<value>Unable to resolve service for type 'Microsoft.AspNetCore.Connections.IConnectionListenerFactory' while attempting to activate 'Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer'.</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
{
|
{
|
||||||
private readonly List<(IConnectionListener, Task)> _transports = new List<(IConnectionListener, Task)>();
|
private readonly List<(IConnectionListener, Task)> _transports = new List<(IConnectionListener, Task)>();
|
||||||
private readonly IServerAddressesFeature _serverAddresses;
|
private readonly IServerAddressesFeature _serverAddresses;
|
||||||
private readonly IEnumerable<IConnectionListenerFactory> _transportFactories;
|
private readonly List<IConnectionListenerFactory> _transportFactories;
|
||||||
|
|
||||||
private bool _hasStarted;
|
private bool _hasStarted;
|
||||||
private int _stopping;
|
private int _stopping;
|
||||||
|
|
@ -42,7 +42,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
throw new ArgumentNullException(nameof(transportFactories));
|
throw new ArgumentNullException(nameof(transportFactories));
|
||||||
}
|
}
|
||||||
|
|
||||||
_transportFactories = transportFactories;
|
_transportFactories = transportFactories.ToList();
|
||||||
|
|
||||||
|
if (_transportFactories.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(CoreStrings.TransportNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
ServiceContext = serviceContext;
|
ServiceContext = serviceContext;
|
||||||
|
|
||||||
Features = new FeatureCollection();
|
Features = new FeatureCollection();
|
||||||
|
|
@ -143,19 +149,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
{
|
{
|
||||||
if (transportFactory is IMultiplexedConnectionListenerFactory)
|
if (transportFactory is IMultiplexedConnectionListenerFactory)
|
||||||
{
|
{
|
||||||
|
// Don't break early. Always use the last registered factory.
|
||||||
factory = transportFactory;
|
factory = transportFactory;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (factory == null)
|
if (factory == null)
|
||||||
{
|
{
|
||||||
throw new Exception("Quic transport not found when using HTTP/3");
|
throw new InvalidOperationException(CoreStrings.QuicTransportNotFound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
factory = _transportFactories.Single();
|
factory = _transportFactories.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
var transport = await factory.BindAsync(options.EndPoint).ConfigureAwait(false);
|
var transport = await factory.BindAsync(options.EndPoint).ConfigureAwait(false);
|
||||||
|
|
|
||||||
|
|
@ -204,22 +204,45 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
var mockLoggerFactory = new Mock<ILoggerFactory>();
|
var mockLoggerFactory = new Mock<ILoggerFactory>();
|
||||||
var mockLogger = new Mock<ILogger>();
|
var mockLogger = new Mock<ILogger>();
|
||||||
mockLoggerFactory.Setup(m => m.CreateLogger(It.IsAny<string>())).Returns(mockLogger.Object);
|
mockLoggerFactory.Setup(m => m.CreateLogger(It.IsAny<string>())).Returns(mockLogger.Object);
|
||||||
new KestrelServer(Options.Create<KestrelServerOptions>(null), Mock.Of<IEnumerable<IConnectionListenerFactory>>(), mockLoggerFactory.Object);
|
new KestrelServer(Options.Create<KestrelServerOptions>(null), new List<IConnectionListenerFactory>() { new MockTransportFactory() }, mockLoggerFactory.Object);
|
||||||
mockLoggerFactory.Verify(factory => factory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel"));
|
mockLoggerFactory.Verify(factory => factory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void StartWithNoTransportFactoryThrows()
|
public void ConstructorWithNullTransportFactoriesThrows()
|
||||||
{
|
{
|
||||||
var mockLoggerFactory = new Mock<ILoggerFactory>();
|
|
||||||
var mockLogger = new Mock<ILogger>();
|
|
||||||
mockLoggerFactory.Setup(m => m.CreateLogger(It.IsAny<string>())).Returns(mockLogger.Object);
|
|
||||||
var exception = Assert.Throws<ArgumentNullException>(() =>
|
var exception = Assert.Throws<ArgumentNullException>(() =>
|
||||||
new KestrelServer(Options.Create<KestrelServerOptions>(null), null, mockLoggerFactory.Object));
|
new KestrelServer(
|
||||||
|
Options.Create<KestrelServerOptions>(null),
|
||||||
|
null,
|
||||||
|
new LoggerFactory(new[] { new KestrelTestLoggerProvider() })));
|
||||||
|
|
||||||
Assert.Equal("transportFactories", exception.ParamName);
|
Assert.Equal("transportFactories", exception.ParamName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConstructorWithNoTransportFactoriesThrows()
|
||||||
|
{
|
||||||
|
var exception = Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
new KestrelServer(
|
||||||
|
Options.Create<KestrelServerOptions>(null),
|
||||||
|
new List<IConnectionListenerFactory>(),
|
||||||
|
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<IConnectionListenerFactory>() { new ThrowingTransportFactory(), new MockTransportFactory() },
|
||||||
|
new LoggerFactory(new[] { new KestrelTestLoggerProvider() }));
|
||||||
|
|
||||||
|
StartDummyApplication(server);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task StopAsyncCallsCompleteWhenFirstCallCompletes()
|
public async Task StopAsyncCallsCompleteWhenFirstCallCompletes()
|
||||||
{
|
{
|
||||||
|
|
@ -456,5 +479,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
return new ValueTask<IConnectionListener>(mock.Object);
|
return new ValueTask<IConnectionListener>(mock.Object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ThrowingTransportFactory : IConnectionListenerFactory
|
||||||
|
{
|
||||||
|
public ValueTask<IConnectionListener> BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue