Fix Http/3 and mark multiplexed factory as experimental (#25012)

* Fix Http/3 and mark multiplexed factory as experimental

* mark apis as internal and delete quic samples

* Remove extra public api

* Mark kestrel server as obsolete

* Fixing obsoletion and improved message

* Revert "Fixing obsoletion and improved message"

This reverts commit acdae8713d467264b4b3762b56fb4b882fa9127d.

* Revert "Mark kestrel server as obsolete"

This reverts commit fbd861f53afe93951b1f9cd004859551e085952d.

* Internalize SocketCOnnectionFactory

* nit

* fixup
This commit is contained in:
Justin Kotalik 2020-08-19 14:27:25 -07:00 committed by GitHub
parent 1cd7e15fb2
commit 9f398d9a56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 477 additions and 670 deletions

View File

@ -1397,14 +1397,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sockets.BindTests", "src\Se
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "http2cat", "src\Servers\Kestrel\samples\http2cat\http2cat.csproj", "{8BDEC645-73BD-453B-8A5C-D616BC4EA08D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuicSampleApp", "src\Servers\Kestrel\samples\QuicSampleApp\QuicSampleApp.csproj", "{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Transport.Quic", "Transport.Quic", "{EE9D0952-6060-4723-B329-94A2950A6762}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic", "src\Servers\Kestrel\Transport.Quic\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic.csproj", "{132D43A2-067A-4E24-A520-45B9F14DCB8E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuicSampleClient", "src\Servers\Kestrel\samples\QuicSampleClient\QuicSampleClient.csproj", "{FA8D7CA4-C33B-4409-865F-54192BAC59A4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Http3SampleApp", "src\Servers\Kestrel\samples\Http3SampleApp\Http3SampleApp.csproj", "{2EC4E939-513F-44CD-A956-498966EAC929}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpStress", "src\Servers\Kestrel\stress\HttpStress.csproj", "{987E1C29-F124-40C8-8E6F-1B2B6A4CB62A}"
@ -6667,18 +6663,6 @@ Global
{8BDEC645-73BD-453B-8A5C-D616BC4EA08D}.Release|x64.Build.0 = Release|Any CPU
{8BDEC645-73BD-453B-8A5C-D616BC4EA08D}.Release|x86.ActiveCfg = Release|Any CPU
{8BDEC645-73BD-453B-8A5C-D616BC4EA08D}.Release|x86.Build.0 = Release|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Debug|x64.ActiveCfg = Debug|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Debug|x64.Build.0 = Debug|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Debug|x86.ActiveCfg = Debug|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Debug|x86.Build.0 = Debug|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Release|Any CPU.Build.0 = Release|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Release|x64.ActiveCfg = Release|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Release|x64.Build.0 = Release|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Release|x86.ActiveCfg = Release|Any CPU
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D}.Release|x86.Build.0 = Release|Any CPU
{132D43A2-067A-4E24-A520-45B9F14DCB8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{132D43A2-067A-4E24-A520-45B9F14DCB8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{132D43A2-067A-4E24-A520-45B9F14DCB8E}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -6691,18 +6675,6 @@ Global
{132D43A2-067A-4E24-A520-45B9F14DCB8E}.Release|x64.Build.0 = Release|Any CPU
{132D43A2-067A-4E24-A520-45B9F14DCB8E}.Release|x86.ActiveCfg = Release|Any CPU
{132D43A2-067A-4E24-A520-45B9F14DCB8E}.Release|x86.Build.0 = Release|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Debug|x64.ActiveCfg = Debug|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Debug|x64.Build.0 = Debug|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Debug|x86.ActiveCfg = Debug|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Debug|x86.Build.0 = Debug|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Release|Any CPU.Build.0 = Release|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Release|x64.ActiveCfg = Release|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Release|x64.Build.0 = Release|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Release|x86.ActiveCfg = Release|Any CPU
{FA8D7CA4-C33B-4409-865F-54192BAC59A4}.Release|x86.Build.0 = Release|Any CPU
{2EC4E939-513F-44CD-A956-498966EAC929}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2EC4E939-513F-44CD-A956-498966EAC929}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2EC4E939-513F-44CD-A956-498966EAC929}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -7857,10 +7829,8 @@ Global
{8550A02D-BA13-411A-AAD3-6124D33D669F} = {47EF1A9F-89DB-4EBA-9BC1-1D4E0E12DE44}
{EDE77D0C-321A-49FD-95D7-56ED41242A93} = {47EF1A9F-89DB-4EBA-9BC1-1D4E0E12DE44}
{8BDEC645-73BD-453B-8A5C-D616BC4EA08D} = {7B976D8F-EA31-4C0B-97BD-DFD9B3CC86FB}
{CBCD6AC2-72D3-4E82-9E78-12E3A9C68E1D} = {7B976D8F-EA31-4C0B-97BD-DFD9B3CC86FB}
{EE9D0952-6060-4723-B329-94A2950A6762} = {4FDDC525-4E60-4CAF-83A3-261C5B43721F}
{132D43A2-067A-4E24-A520-45B9F14DCB8E} = {EE9D0952-6060-4723-B329-94A2950A6762}
{FA8D7CA4-C33B-4409-865F-54192BAC59A4} = {7B976D8F-EA31-4C0B-97BD-DFD9B3CC86FB}
{2EC4E939-513F-44CD-A956-498966EAC929} = {7B976D8F-EA31-4C0B-97BD-DFD9B3CC86FB}
{987E1C29-F124-40C8-8E6F-1B2B6A4CB62A} = {4FDDC525-4E60-4CAF-83A3-261C5B43721F}
{3CBC4802-E9B8-48B7-BC8C-B0AFB9EEC643} = {0ACCEDA7-339C-4B4D-8DD4-1AC271F31C04}

View File

@ -7,12 +7,12 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Connections
namespace Microsoft.AspNetCore.Connections.Experimental
{
/// <summary>
/// Defines an interface that represents a listener bound to a specific <see cref="EndPoint"/>.
/// </summary>
public interface IMultiplexedConnectionListener : IAsyncDisposable
internal interface IMultiplexedConnectionListener : IAsyncDisposable
{
/// <summary>
/// The endpoint that was bound. This may differ from the requested endpoint, such as when the caller requested that any free port be selected.

View File

@ -3,12 +3,12 @@
using System;
namespace Microsoft.AspNetCore.Connections
namespace Microsoft.AspNetCore.Connections.Experimental
{
/// <summary>
/// Defines an interface that provides the mechanisms to configure a connection pipeline.
/// </summary>
public interface IMultiplexedConnectionBuilder
internal interface IMultiplexedConnectionBuilder
{
/// <summary>
/// Gets the <see cref="IServiceProvider"/> that provides access to the application's service container.

View File

@ -6,12 +6,12 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Connections
namespace Microsoft.AspNetCore.Connections.Experimental
{
/// <summary>
/// A factory abstraction for creating connections to an endpoint.
/// </summary>
public interface IMultiplexedConnectionFactory
internal interface IMultiplexedConnectionFactory
{
/// <summary>
/// Creates a new connection to an endpoint.

View File

@ -6,12 +6,12 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Connections
namespace Microsoft.AspNetCore.Connections.Experimental
{
/// <summary>
/// Defines an interface that provides the mechanisms for binding to various types of <see cref="EndPoint"/>s.
/// </summary>
public interface IMultiplexedConnectionListenerFactory
internal interface IMultiplexedConnectionListenerFactory
{
/// <summary>
/// Creates an <see cref="IMultiplexedConnectionListener"/> bound to the specified <see cref="EndPoint"/>.

View File

@ -6,9 +6,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Connections
namespace Microsoft.AspNetCore.Connections.Experimental
{
public class MultiplexedConnectionBuilder : IMultiplexedConnectionBuilder
internal class MultiplexedConnectionBuilder : IMultiplexedConnectionBuilder
{
private readonly IList<Func<MultiplexedConnectionDelegate, MultiplexedConnectionDelegate>> _components = new List<Func<MultiplexedConnectionDelegate, MultiplexedConnectionDelegate>>();

View File

@ -6,12 +6,12 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Connections
namespace Microsoft.AspNetCore.Connections.Experimental
{
/// <summary>
/// Encapsulates all information about a multiplexed connection.
/// </summary>
public abstract class MultiplexedConnectionContext : BaseConnectionContext, IAsyncDisposable
internal abstract class MultiplexedConnectionContext : BaseConnectionContext, IAsyncDisposable
{
/// <summary>
/// Asynchronously accept an incoming stream on the connection.

View File

@ -3,12 +3,12 @@
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Connections
namespace Microsoft.AspNetCore.Connections.Experimental
{
/// <summary>
/// A function that can process a connection.
/// </summary>
/// <param name="connection">A <see cref="MultiplexedConnectionContext" /> representing the connection.</param>
/// <returns>A <see cref="Task"/> that represents the connection lifetime. When the task completes, the connection will be closed.</returns>
public delegate Task MultiplexedConnectionDelegate(MultiplexedConnectionContext connection);
internal delegate Task MultiplexedConnectionDelegate(MultiplexedConnectionContext connection);
}

View File

@ -0,0 +1,9 @@
// 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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Core, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("InMemory.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -12,6 +12,7 @@
<Compile Include="$(SharedSourceRoot)runtime\SR.cs" LinkBase="Shared\SR.cs" />
<Compile Include="$(SharedSourceRoot)Http2cat\**\*.cs" LinkBase="Shared\Http2cat" />
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" LinkBase="Shared\" />
<Compile Remove="$(SharedSourceRoot)ServerInfrastructure\DuplexPipe.cs" />
<Compile Remove="$(SharedSourceRoot)ServerInfrastructure\StringUtilities.cs" />
</ItemGroup>

View File

@ -25,6 +25,7 @@
<Compile Include="$(SharedSourceRoot)runtime\IHttpHeadersHandler.cs" LinkBase="Shared\IHttpHeadersHandler.cs" />
<Compile Include="$(SharedSourceRoot)runtime\SR.cs" LinkBase="Shared\SR.cs" />
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" LinkBase="Shared\" />
<Compile Remove="$(SharedSourceRoot)ServerInfrastructure\DuplexPipe.cs" />
<Compile Include="$(SharedSourceRoot)TaskToApm.cs" Link="Shared\TaskToApm.cs" />
</ItemGroup>

View File

@ -18,6 +18,7 @@
<Compile Include="$(SharedSourceRoot)runtime\IHttpHeadersHandler.cs" LinkBase="Shared\IHttpHeadersHandler.cs" />
<Compile Include="$(SharedSourceRoot)runtime\SR.cs" LinkBase="Shared\SR.cs" />
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" LinkBase="Shared\" />
<Compile Remove="$(SharedSourceRoot)ServerInfrastructure\DuplexPipe.cs" />
<Compile Include="$(SharedSourceRoot)TaskToApm.cs" Link="Shared\TaskToApm.cs" />
</ItemGroup>

View File

@ -27,6 +27,7 @@
<Compile Include="$(SharedSourceRoot)runtime\IHttpHeadersHandler.cs" LinkBase="Shared\IHttpHeadersHandler.cs" />
<Compile Include="$(SharedSourceRoot)runtime\SR.cs" LinkBase="Shared\SR.cs" />
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" LinkBase="Shared\" />
<Compile Remove="$(SharedSourceRoot)ServerInfrastructure\DuplexPipe.cs" />
<Compile Include="$(SharedSourceRoot)TaskToApm.cs" Link="Shared\TaskToApm.cs" />
</ItemGroup>

View File

@ -29,6 +29,7 @@
<Compile Include="$(SharedSourceRoot)runtime\IHttpHeadersHandler.cs" LinkBase="Shared\IHttpHeadersHandler.cs" />
<Compile Include="$(SharedSourceRoot)runtime\SR.cs" LinkBase="Shared\SR.cs" />
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" LinkBase="Shared\" />
<Compile Remove="$(SharedSourceRoot)ServerInfrastructure\DuplexPipe.cs" />
<Compile Include="$(SharedSourceRoot)TaskToApm.cs" Link="Shared\TaskToApm.cs" />
</ItemGroup>

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
@ -10,6 +9,7 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;

View File

@ -4,6 +4,7 @@
using System.Buffers;
using System.Net;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;

View File

@ -10,6 +10,7 @@ using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{

View File

@ -0,0 +1,398 @@
// 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 System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Server.Kestrel.Core
{
internal class KestrelServerImpl : IServer
{
private readonly ServerAddressesFeature _serverAddresses;
private readonly TransportManager _transportManager;
private readonly IConnectionListenerFactory _transportFactory;
private readonly IMultiplexedConnectionListenerFactory _multiplexedTransportFactory;
private readonly SemaphoreSlim _bindSemaphore = new SemaphoreSlim(initialCount: 1);
private bool _hasStarted;
private int _stopping;
private readonly CancellationTokenSource _stopCts = new CancellationTokenSource();
private readonly TaskCompletionSource _stoppedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
private IDisposable _configChangedRegistration;
public KestrelServerImpl(
IOptions<KestrelServerOptions> options,
IEnumerable<IConnectionListenerFactory> transportFactories,
ILoggerFactory loggerFactory)
: this(transportFactories, null, CreateServiceContext(options, loggerFactory))
{
}
public KestrelServerImpl(
IOptions<KestrelServerOptions> options,
IEnumerable<IConnectionListenerFactory> transportFactories,
IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
ILoggerFactory loggerFactory)
: this(transportFactories, multiplexedFactories, CreateServiceContext(options, loggerFactory))
{
}
// For testing
internal KestrelServerImpl(IEnumerable<IConnectionListenerFactory> transportFactories, ServiceContext serviceContext)
: this(transportFactories, null, serviceContext)
{
}
// For testing
internal KestrelServerImpl(
IEnumerable<IConnectionListenerFactory> transportFactories,
IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
ServiceContext serviceContext)
{
if (transportFactories == null)
{
throw new ArgumentNullException(nameof(transportFactories));
}
_transportFactory = transportFactories?.LastOrDefault();
_multiplexedTransportFactory = multiplexedFactories?.LastOrDefault();
if (_transportFactory == null && _multiplexedTransportFactory == null)
{
throw new InvalidOperationException(CoreStrings.TransportNotFound);
}
ServiceContext = serviceContext;
Features = new FeatureCollection();
_serverAddresses = new ServerAddressesFeature();
Features.Set<IServerAddressesFeature>(_serverAddresses);
_transportManager = new TransportManager(_transportFactory, _multiplexedTransportFactory, ServiceContext);
HttpCharacters.Initialize();
}
private static ServiceContext CreateServiceContext(IOptions<KestrelServerOptions> options, ILoggerFactory loggerFactory)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
var serverOptions = options.Value ?? new KestrelServerOptions();
var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel");
var trace = new KestrelTrace(logger);
var connectionManager = new ConnectionManager(
trace,
serverOptions.Limits.MaxConcurrentUpgradedConnections);
var heartbeatManager = new HeartbeatManager(connectionManager);
var dateHeaderValueManager = new DateHeaderValueManager();
var heartbeat = new Heartbeat(
new IHeartbeatHandler[] { dateHeaderValueManager, heartbeatManager },
new SystemClock(),
DebuggerWrapper.Singleton,
trace);
return new ServiceContext
{
Log = trace,
HttpParser = new HttpParser<Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information)),
Scheduler = PipeScheduler.ThreadPool,
SystemClock = heartbeatManager,
DateHeaderValueManager = dateHeaderValueManager,
ConnectionManager = connectionManager,
Heartbeat = heartbeat,
ServerOptions = serverOptions,
};
}
public IFeatureCollection Features { get; }
public KestrelServerOptions Options => ServiceContext.ServerOptions;
private ServiceContext ServiceContext { get; }
private IKestrelTrace Trace => ServiceContext.Log;
private AddressBindContext AddressBindContext { get; set; }
public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
{
try
{
if (!BitConverter.IsLittleEndian)
{
throw new PlatformNotSupportedException(CoreStrings.BigEndianNotSupported);
}
ValidateOptions();
if (_hasStarted)
{
// The server has already started and/or has not been cleaned up yet
throw new InvalidOperationException(CoreStrings.ServerAlreadyStarted);
}
_hasStarted = true;
ServiceContext.Heartbeat?.Start();
async Task OnBind(ListenOptions options)
{
// INVESTIGATE: For some reason, MsQuic needs to bind before
// sockets for it to successfully listen. It also seems racy.
if ((options.Protocols & HttpProtocols.Http3) == HttpProtocols.Http3)
{
if (_multiplexedTransportFactory is null)
{
throw new InvalidOperationException($"Cannot start HTTP/3 server if no {nameof(IMultiplexedConnectionListenerFactory)} is registered.");
}
options.UseHttp3Server(ServiceContext, application, options.Protocols);
var multiplexedConnectionDelegate = ((IMultiplexedConnectionBuilder)options).Build();
// Add the connection limit middleware
multiplexedConnectionDelegate = EnforceConnectionLimit(multiplexedConnectionDelegate, Options.Limits.MaxConcurrentConnections, Trace);
options.EndPoint = await _transportManager.BindAsync(options.EndPoint, multiplexedConnectionDelegate, options.EndpointConfig).ConfigureAwait(false);
}
// Add the HTTP middleware as the terminal connection middleware
if ((options.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1
|| (options.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2
|| options.Protocols == HttpProtocols.None) // TODO a test fails because it doesn't throw an exception in the right place
// when there is no HttpProtocols in KestrelServer, can we remove/change the test?
{
if (_transportFactory is null)
{
throw new InvalidOperationException($"Cannot start HTTP/1.x or HTTP/2 server if no {nameof(IConnectionListenerFactory)} is registered.");
}
options.UseHttpServer(ServiceContext, application, options.Protocols);
var connectionDelegate = options.Build();
// Add the connection limit middleware
connectionDelegate = EnforceConnectionLimit(connectionDelegate, Options.Limits.MaxConcurrentConnections, Trace);
options.EndPoint = await _transportManager.BindAsync(options.EndPoint, connectionDelegate, options.EndpointConfig).ConfigureAwait(false);
}
}
AddressBindContext = new AddressBindContext
{
ServerAddressesFeature = _serverAddresses,
ServerOptions = Options,
Logger = Trace,
CreateBinding = OnBind,
};
await BindAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.LogCritical(0, ex, "Unable to start Kestrel.");
Dispose();
throw;
}
}
// Graceful shutdown if possible
public async Task StopAsync(CancellationToken cancellationToken)
{
if (Interlocked.Exchange(ref _stopping, 1) == 1)
{
await _stoppedTcs.Task.ConfigureAwait(false);
return;
}
_stopCts.Cancel();
// Don't use cancellationToken when acquiring the semaphore. Dispose calls this with a pre-canceled token.
await _bindSemaphore.WaitAsync().ConfigureAwait(false);
try
{
await _transportManager.StopAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_stoppedTcs.TrySetException(ex);
throw;
}
finally
{
ServiceContext.Heartbeat?.Dispose();
_configChangedRegistration?.Dispose();
_stopCts.Dispose();
_bindSemaphore.Release();
}
_stoppedTcs.TrySetResult();
}
// Ungraceful shutdown
public void Dispose()
{
StopAsync(new CancellationToken(canceled: true)).GetAwaiter().GetResult();
}
private async Task BindAsync(CancellationToken cancellationToken)
{
await _bindSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
if (_stopping == 1)
{
throw new InvalidOperationException("Kestrel has already been stopped.");
}
IChangeToken reloadToken = null;
_serverAddresses.InternalCollection.PreventPublicMutation();
if (Options.ConfigurationLoader?.ReloadOnChange == true && (!_serverAddresses.PreferHostingUrls || _serverAddresses.InternalCollection.Count == 0))
{
reloadToken = Options.ConfigurationLoader.Configuration.GetReloadToken();
}
Options.ConfigurationLoader?.Load();
await AddressBinder.BindAsync(Options.ListenOptions, AddressBindContext).ConfigureAwait(false);
_configChangedRegistration = reloadToken?.RegisterChangeCallback(async state => await ((KestrelServerImpl)state).RebindAsync(), this);
}
finally
{
_bindSemaphore.Release();
}
}
private async Task RebindAsync()
{
await _bindSemaphore.WaitAsync();
IChangeToken reloadToken = null;
try
{
if (_stopping == 1)
{
return;
}
reloadToken = Options.ConfigurationLoader.Configuration.GetReloadToken();
var (endpointsToStop, endpointsToStart) = Options.ConfigurationLoader.Reload();
Trace.LogDebug("Config reload token fired. Checking for changes...");
if (endpointsToStop.Count > 0)
{
var urlsToStop = endpointsToStop.Select(lo => lo.EndpointConfig.Url ?? "<unknown>");
Trace.LogInformation("Config changed. Stopping the following endpoints: '{endpoints}'", string.Join("', '", urlsToStop));
// 5 is the default value for WebHost's "shutdownTimeoutSeconds", so use that.
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(_stopCts.Token, timeoutCts.Token);
// TODO: It would be nice to start binding to new endpoints immediately and reconfigured endpoints as soon
// as the unbinding finished for the given endpoint rather than wait for all transports to unbind first.
var configsToStop = endpointsToStop.Select(lo => lo.EndpointConfig).ToList();
await _transportManager.StopEndpointsAsync(configsToStop, combinedCts.Token).ConfigureAwait(false);
foreach (var listenOption in endpointsToStop)
{
Options.OptionsInUse.Remove(listenOption);
_serverAddresses.InternalCollection.Remove(listenOption.GetDisplayName());
}
}
if (endpointsToStart.Count > 0)
{
var urlsToStart = endpointsToStart.Select(lo => lo.EndpointConfig.Url ?? "<unknown>");
Trace.LogInformation("Config changed. Starting the following endpoints: '{endpoints}'", string.Join("', '", urlsToStart));
foreach (var listenOption in endpointsToStart)
{
try
{
// TODO: This should probably be canceled by the _stopCts too, but we don't currently support bind cancellation even in StartAsync().
await listenOption.BindAsync(AddressBindContext).ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.LogCritical(0, ex, "Unable to bind to '{url}' on config reload.", listenOption.EndpointConfig.Url ?? "<unknown>");
}
}
}
}
catch (Exception ex)
{
Trace.LogCritical(0, ex, "Unable to reload configuration.");
}
finally
{
_configChangedRegistration = reloadToken?.RegisterChangeCallback(async state => await ((KestrelServerImpl)state).RebindAsync(), this);
_bindSemaphore.Release();
}
}
private void ValidateOptions()
{
if (Options.Limits.MaxRequestBufferSize.HasValue &&
Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestLineSize)
{
throw new InvalidOperationException(
CoreStrings.FormatMaxRequestBufferSmallerThanRequestLineBuffer(Options.Limits.MaxRequestBufferSize.Value, Options.Limits.MaxRequestLineSize));
}
if (Options.Limits.MaxRequestBufferSize.HasValue &&
Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestHeadersTotalSize)
{
throw new InvalidOperationException(
CoreStrings.FormatMaxRequestBufferSmallerThanRequestHeaderBuffer(Options.Limits.MaxRequestBufferSize.Value, Options.Limits.MaxRequestHeadersTotalSize));
}
}
private static ConnectionDelegate EnforceConnectionLimit(ConnectionDelegate innerDelegate, long? connectionLimit, IKestrelTrace trace)
{
if (!connectionLimit.HasValue)
{
return innerDelegate;
}
return new ConnectionLimitMiddleware<ConnectionContext>(c => innerDelegate(c), connectionLimit.Value, trace).OnConnectionAsync;
}
private static MultiplexedConnectionDelegate EnforceConnectionLimit(MultiplexedConnectionDelegate innerDelegate, long? connectionLimit, IKestrelTrace trace)
{
if (!connectionLimit.HasValue)
{
return innerDelegate;
}
return new ConnectionLimitMiddleware<MultiplexedConnectionContext>(c => innerDelegate(c), connectionLimit.Value, trace).OnConnectionAsync;
}
}
}

View File

@ -1,397 +1,55 @@
// 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 System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Server.Kestrel.Core
{
public class KestrelServer : IServer
{
private readonly ServerAddressesFeature _serverAddresses;
private readonly TransportManager _transportManager;
private readonly IConnectionListenerFactory _transportFactory;
private readonly IMultiplexedConnectionListenerFactory _multiplexedTransportFactory;
private readonly SemaphoreSlim _bindSemaphore = new SemaphoreSlim(initialCount: 1);
private bool _hasStarted;
private int _stopping;
private readonly CancellationTokenSource _stopCts = new CancellationTokenSource();
private readonly TaskCompletionSource _stoppedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
private IDisposable _configChangedRegistration;
private KestrelServerImpl _innerKestrelServer;
public KestrelServer(
IOptions<KestrelServerOptions> options,
IEnumerable<IConnectionListenerFactory> transportFactories,
ILoggerFactory loggerFactory)
: this(transportFactories, null, CreateServiceContext(options, loggerFactory))
{
}
public KestrelServer(
IOptions<KestrelServerOptions> options,
IEnumerable<IConnectionListenerFactory> transportFactories,
IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
ILoggerFactory loggerFactory)
: this(transportFactories, multiplexedFactories, CreateServiceContext(options, loggerFactory))
{
_innerKestrelServer = new KestrelServerImpl(options, transportFactories, loggerFactory);
}
// For testing
internal KestrelServer(IEnumerable<IConnectionListenerFactory> transportFactories, ServiceContext serviceContext)
: this(transportFactories, null, serviceContext)
{
_innerKestrelServer = new KestrelServerImpl(transportFactories, serviceContext);
}
// For testing
internal KestrelServer(
IEnumerable<IConnectionListenerFactory> transportFactories,
IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
ServiceContext serviceContext)
public IFeatureCollection Features => _innerKestrelServer.Features;
public KestrelServerOptions Options => _innerKestrelServer.Options;
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
{
if (transportFactories == null)
{
throw new ArgumentNullException(nameof(transportFactories));
}
_transportFactory = transportFactories?.LastOrDefault();
_multiplexedTransportFactory = multiplexedFactories?.LastOrDefault();
if (_transportFactory == null && _multiplexedTransportFactory == null)
{
throw new InvalidOperationException(CoreStrings.TransportNotFound);
}
ServiceContext = serviceContext;
Features = new FeatureCollection();
_serverAddresses = new ServerAddressesFeature();
Features.Set<IServerAddressesFeature>(_serverAddresses);
_transportManager = new TransportManager(_transportFactory, _multiplexedTransportFactory, ServiceContext);
HttpCharacters.Initialize();
}
private static ServiceContext CreateServiceContext(IOptions<KestrelServerOptions> options, ILoggerFactory loggerFactory)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
var serverOptions = options.Value ?? new KestrelServerOptions();
var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel");
var trace = new KestrelTrace(logger);
var connectionManager = new ConnectionManager(
trace,
serverOptions.Limits.MaxConcurrentUpgradedConnections);
var heartbeatManager = new HeartbeatManager(connectionManager);
var dateHeaderValueManager = new DateHeaderValueManager();
var heartbeat = new Heartbeat(
new IHeartbeatHandler[] { dateHeaderValueManager, heartbeatManager },
new SystemClock(),
DebuggerWrapper.Singleton,
trace);
return new ServiceContext
{
Log = trace,
HttpParser = new HttpParser<Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information)),
Scheduler = PipeScheduler.ThreadPool,
SystemClock = heartbeatManager,
DateHeaderValueManager = dateHeaderValueManager,
ConnectionManager = connectionManager,
Heartbeat = heartbeat,
ServerOptions = serverOptions,
};
}
public IFeatureCollection Features { get; }
public KestrelServerOptions Options => ServiceContext.ServerOptions;
private ServiceContext ServiceContext { get; }
private IKestrelTrace Trace => ServiceContext.Log;
private AddressBindContext AddressBindContext { get; set; }
public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
{
try
{
if (!BitConverter.IsLittleEndian)
{
throw new PlatformNotSupportedException(CoreStrings.BigEndianNotSupported);
}
ValidateOptions();
if (_hasStarted)
{
// The server has already started and/or has not been cleaned up yet
throw new InvalidOperationException(CoreStrings.ServerAlreadyStarted);
}
_hasStarted = true;
ServiceContext.Heartbeat?.Start();
async Task OnBind(ListenOptions options)
{
// INVESTIGATE: For some reason, MsQuic needs to bind before
// sockets for it to successfully listen. It also seems racy.
if ((options.Protocols & HttpProtocols.Http3) == HttpProtocols.Http3)
{
if (_multiplexedTransportFactory is null)
{
throw new InvalidOperationException($"Cannot start HTTP/3 server if no {nameof(IMultiplexedConnectionListenerFactory)} is registered.");
}
options.UseHttp3Server(ServiceContext, application, options.Protocols);
var multiplexedConnectionDelegate = ((IMultiplexedConnectionBuilder)options).Build();
// Add the connection limit middleware
multiplexedConnectionDelegate = EnforceConnectionLimit(multiplexedConnectionDelegate, Options.Limits.MaxConcurrentConnections, Trace);
options.EndPoint = await _transportManager.BindAsync(options.EndPoint, multiplexedConnectionDelegate, options.EndpointConfig).ConfigureAwait(false);
}
// Add the HTTP middleware as the terminal connection middleware
if ((options.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1
|| (options.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2
|| options.Protocols == HttpProtocols.None) // TODO a test fails because it doesn't throw an exception in the right place
// when there is no HttpProtocols in KestrelServer, can we remove/change the test?
{
if (_transportFactory is null)
{
throw new InvalidOperationException($"Cannot start HTTP/1.x or HTTP/2 server if no {nameof(IConnectionListenerFactory)} is registered.");
}
options.UseHttpServer(ServiceContext, application, options.Protocols);
var connectionDelegate = options.Build();
// Add the connection limit middleware
connectionDelegate = EnforceConnectionLimit(connectionDelegate, Options.Limits.MaxConcurrentConnections, Trace);
options.EndPoint = await _transportManager.BindAsync(options.EndPoint, connectionDelegate, options.EndpointConfig).ConfigureAwait(false);
}
}
AddressBindContext = new AddressBindContext
{
ServerAddressesFeature = _serverAddresses,
ServerOptions = Options,
Logger = Trace,
CreateBinding = OnBind,
};
await BindAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.LogCritical(0, ex, "Unable to start Kestrel.");
Dispose();
throw;
}
return _innerKestrelServer.StartAsync(application, cancellationToken);
}
// Graceful shutdown if possible
public async Task StopAsync(CancellationToken cancellationToken)
public Task StopAsync(CancellationToken cancellationToken)
{
if (Interlocked.Exchange(ref _stopping, 1) == 1)
{
await _stoppedTcs.Task.ConfigureAwait(false);
return;
}
_stopCts.Cancel();
// Don't use cancellationToken when acquiring the semaphore. Dispose calls this with a pre-canceled token.
await _bindSemaphore.WaitAsync().ConfigureAwait(false);
try
{
await _transportManager.StopAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_stoppedTcs.TrySetException(ex);
throw;
}
finally
{
ServiceContext.Heartbeat?.Dispose();
_configChangedRegistration?.Dispose();
_stopCts.Dispose();
_bindSemaphore.Release();
}
_stoppedTcs.TrySetResult();
return _innerKestrelServer.StopAsync(cancellationToken);
}
// Ungraceful shutdown
public void Dispose()
{
StopAsync(new CancellationToken(canceled: true)).GetAwaiter().GetResult();
}
private async Task BindAsync(CancellationToken cancellationToken)
{
await _bindSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
if (_stopping == 1)
{
throw new InvalidOperationException("Kestrel has already been stopped.");
}
IChangeToken reloadToken = null;
_serverAddresses.InternalCollection.PreventPublicMutation();
if (Options.ConfigurationLoader?.ReloadOnChange == true && (!_serverAddresses.PreferHostingUrls || _serverAddresses.InternalCollection.Count == 0))
{
reloadToken = Options.ConfigurationLoader.Configuration.GetReloadToken();
}
Options.ConfigurationLoader?.Load();
await AddressBinder.BindAsync(Options.ListenOptions, AddressBindContext).ConfigureAwait(false);
_configChangedRegistration = reloadToken?.RegisterChangeCallback(async state => await ((KestrelServer)state).RebindAsync(), this);
}
finally
{
_bindSemaphore.Release();
}
}
private async Task RebindAsync()
{
await _bindSemaphore.WaitAsync();
IChangeToken reloadToken = null;
try
{
if (_stopping == 1)
{
return;
}
reloadToken = Options.ConfigurationLoader.Configuration.GetReloadToken();
var (endpointsToStop, endpointsToStart) = Options.ConfigurationLoader.Reload();
Trace.LogDebug("Config reload token fired. Checking for changes...");
if (endpointsToStop.Count > 0)
{
var urlsToStop = endpointsToStop.Select(lo => lo.EndpointConfig.Url ?? "<unknown>");
Trace.LogInformation("Config changed. Stopping the following endpoints: '{endpoints}'", string.Join("', '", urlsToStop));
// 5 is the default value for WebHost's "shutdownTimeoutSeconds", so use that.
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(_stopCts.Token, timeoutCts.Token);
// TODO: It would be nice to start binding to new endpoints immediately and reconfigured endpoints as soon
// as the unbinding finished for the given endpoint rather than wait for all transports to unbind first.
var configsToStop = endpointsToStop.Select(lo => lo.EndpointConfig).ToList();
await _transportManager.StopEndpointsAsync(configsToStop, combinedCts.Token).ConfigureAwait(false);
foreach (var listenOption in endpointsToStop)
{
Options.OptionsInUse.Remove(listenOption);
_serverAddresses.InternalCollection.Remove(listenOption.GetDisplayName());
}
}
if (endpointsToStart.Count > 0)
{
var urlsToStart = endpointsToStart.Select(lo => lo.EndpointConfig.Url ?? "<unknown>");
Trace.LogInformation("Config changed. Starting the following endpoints: '{endpoints}'", string.Join("', '", urlsToStart));
foreach (var listenOption in endpointsToStart)
{
try
{
// TODO: This should probably be canceled by the _stopCts too, but we don't currently support bind cancellation even in StartAsync().
await listenOption.BindAsync(AddressBindContext).ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.LogCritical(0, ex, "Unable to bind to '{url}' on config reload.", listenOption.EndpointConfig.Url ?? "<unknown>");
}
}
}
}
catch (Exception ex)
{
Trace.LogCritical(0, ex, "Unable to reload configuration.");
}
finally
{
_configChangedRegistration = reloadToken?.RegisterChangeCallback(async state => await ((KestrelServer)state).RebindAsync(), this);
_bindSemaphore.Release();
}
}
private void ValidateOptions()
{
if (Options.Limits.MaxRequestBufferSize.HasValue &&
Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestLineSize)
{
throw new InvalidOperationException(
CoreStrings.FormatMaxRequestBufferSmallerThanRequestLineBuffer(Options.Limits.MaxRequestBufferSize.Value, Options.Limits.MaxRequestLineSize));
}
if (Options.Limits.MaxRequestBufferSize.HasValue &&
Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestHeadersTotalSize)
{
throw new InvalidOperationException(
CoreStrings.FormatMaxRequestBufferSmallerThanRequestHeaderBuffer(Options.Limits.MaxRequestBufferSize.Value, Options.Limits.MaxRequestHeadersTotalSize));
}
}
private static ConnectionDelegate EnforceConnectionLimit(ConnectionDelegate innerDelegate, long? connectionLimit, IKestrelTrace trace)
{
if (!connectionLimit.HasValue)
{
return innerDelegate;
}
return new ConnectionLimitMiddleware<ConnectionContext>(c => innerDelegate(c), connectionLimit.Value, trace).OnConnectionAsync;
}
private static MultiplexedConnectionDelegate EnforceConnectionLimit(MultiplexedConnectionDelegate innerDelegate, long? connectionLimit, IKestrelTrace trace)
{
if (!connectionLimit.HasValue)
{
return innerDelegate;
}
return new ConnectionLimitMiddleware<MultiplexedConnectionContext>(c => innerDelegate(c), connectionLimit.Value, trace).OnConnectionAsync;
_innerKestrelServer.Dispose();
}
}
}

View File

@ -7,6 +7,7 @@ using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;

View File

@ -4,6 +4,7 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3;

View File

@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Hosting
services.TryAddSingleton<IConnectionListenerFactory, SocketTransportFactory>();
services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
services.AddSingleton<IServer, KestrelServer>();
services.AddSingleton<IServer, KestrelServerImpl>();
});
}

View File

@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
@ -99,5 +100,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
Assert.IsType<SocketTransportFactory>(hostBuilderReversed.Build().Services.GetService<IConnectionListenerFactory>());
}
[Fact]
public void ServerIsKestrelServerImpl()
{
var hostBuilder = new WebHostBuilder()
.UseSockets()
.UseKestrel()
.Configure(app => { });
Assert.IsType<KestrelServerImpl>(hostBuilder.Build().Services.GetService<IServer>());
}
}
}

View File

@ -9,6 +9,7 @@ using System.Net.Security;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;

View File

@ -9,6 +9,7 @@ using System.Net.Security;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic.Internal;
using Microsoft.Extensions.Logging;
@ -16,7 +17,7 @@ using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic
{
public class QuicConnectionFactory : IMultiplexedConnectionFactory
internal class QuicConnectionFactory : IMultiplexedConnectionFactory
{
private QuicTransportContext _transportContext;

View File

@ -6,6 +6,7 @@ using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic.Internal;
using Microsoft.Extensions.Logging;
@ -13,7 +14,7 @@ using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic
{
public class QuicTransportFactory : IMultiplexedConnectionListenerFactory
internal class QuicTransportFactory : IMultiplexedConnectionListenerFactory
{
private QuicTrace _log;
private QuicTransportOptions _options;
@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic
_options = options.Value;
}
public ValueTask<IMultiplexedConnectionListener> BindAsync(EndPoint endpoint, IFeatureCollection features = null, CancellationToken cancellationToken = default)
public ValueTask<IMultiplexedConnectionListener> BindAsync(EndPoint endpoint, IFeatureCollection features = null, CancellationToken cancellationToken = default)
{
var transport = new QuicConnectionListener(_options, _log, endpoint);
return new ValueTask<IMultiplexedConnectionListener>(transport);

View File

@ -3,6 +3,7 @@
using System;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic;
using Microsoft.Extensions.DependencyInjection;

View File

@ -5,3 +5,10 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Sockets.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Sockets.BindTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("http2cat, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.HttpSys.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("http2cat, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("IIS.NewShim.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("IIS.NewHandler.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("IISExpress.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("IIS.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -16,7 +16,7 @@ using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
{
public class SocketConnectionFactory : IConnectionFactory, IAsyncDisposable
internal class SocketConnectionFactory : IConnectionFactory, IAsyncDisposable
{
private readonly SocketTransportOptions _options;
private readonly MemoryPool<byte> _memoryPool;

View File

@ -28,7 +28,7 @@ namespace Http3SampleApp
.UseQuic(options =>
{
options.Certificate = cert; // Shouldn't need this either here.
options.Alpn = "h3-25"; // Shouldn't need to populate this as well.
options.Alpn = "h3-29"; // Shouldn't need to populate this as well.
options.IdleTimeout = TimeSpan.FromHours(1);
})
.ConfigureKestrel((context, options) =>
@ -42,7 +42,7 @@ namespace Http3SampleApp
{
httpsOptions.ServerCertificate = cert;
});
listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
listenOptions.Protocols = HttpProtocols.Http3;
});
})
.UseStartup<Startup>();

View File

@ -1,88 +0,0 @@
using System;
using System.Buffers;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace QuicSampleApp
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run((httpContext) =>
{
return Task.CompletedTask;
});
}
public static void Main(string[] args)
{
var hostBuilder = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
.UseKestrel()
.UseQuic(options =>
{
options.Certificate = null;
options.Alpn = "QuicTest";
options.IdleTimeout = TimeSpan.FromHours(1);
})
.ConfigureKestrel((context, options) =>
{
var basePort = 5555;
options.Listen(IPAddress.Any, basePort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http3;
async Task EchoServer(MultiplexedConnectionContext connection)
{
// For graceful shutdown
while (true)
{
var stream = await connection.AcceptAsync();
while (true)
{
var result = await stream.Transport.Input.ReadAsync();
if (result.IsCompleted)
{
break;
}
await stream.Transport.Output.WriteAsync(result.Buffer.ToArray());
stream.Transport.Input.AdvanceTo(result.Buffer.End);
}
}
}
((IMultiplexedConnectionBuilder)listenOptions).Use(next =>
{
return context =>
{
return EchoServer(context);
};
});
});
})
.UseStartup<Startup>();
})
.ConfigureLogging((_, factory) =>
{
factory.SetMinimumLevel(LogLevel.Debug);
factory.AddConsole();
});
hostBuilder.Build().Run();
}
}
}

View File

@ -1,27 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<GenerateRazorAssemblyInfo>false</GenerateRazorAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic" />
</ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<PackageReference Include="MsQuicPackage.win-x64" Version="1.0.9">
<AllowExplicitReference>true</AllowExplicitReference>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Unix'">
<PackageReference Include="MsQuicPackage.linux-x64" Version="1.0.9">
<AllowExplicitReference>true</AllowExplicitReference>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

View File

@ -1,10 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

View File

@ -1,103 +0,0 @@
using System;
using System.Buffers;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Connections;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
namespace QuicSampleClient
{
class Program
{
static async Task Main(string[] args)
{
var host = new HostBuilder()
.ConfigureLogging(loggingBuilder =>
{
loggingBuilder.AddConsole();
loggingBuilder.SetMinimumLevel(LogLevel.Error);
})
.ConfigureServices(services =>
{
services.AddSingleton<IMultiplexedConnectionFactory, QuicConnectionFactory>();
services.AddSingleton<QuicClientService>();
services.AddOptions<QuicTransportOptions>();
services.Configure<QuicTransportOptions>((options) =>
{
options.Alpn = "QuicTest";
options.Certificate = null;
options.IdleTimeout = TimeSpan.FromHours(1);
});
})
.Build();
await host.Services.GetService<QuicClientService>().RunAsync();
}
private class QuicClientService
{
private readonly IMultiplexedConnectionFactory _connectionFactory;
private readonly ILogger<QuicClientService> _logger;
public QuicClientService(IMultiplexedConnectionFactory connectionFactory, ILogger<QuicClientService> logger)
{
_connectionFactory = connectionFactory;
_logger = logger;
}
public async Task RunAsync()
{
Console.WriteLine("Starting");
var connectionContext = await _connectionFactory.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 5555));
var streamContext = await connectionContext.ConnectAsync();
Console.CancelKeyPress += new ConsoleCancelEventHandler((sender, args) =>
{
streamContext.Transport.Input.CancelPendingRead();
streamContext.Transport.Output.CancelPendingFlush();
});
var input = "asdf";
while (true)
{
try
{
//var input = Console.ReadLine();
if (input.Length == 0)
{
continue;
}
var flushResult = await streamContext.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes(input));
if (flushResult.IsCanceled)
{
break;
}
var readResult = await streamContext.Transport.Input.ReadAsync();
if (readResult.IsCanceled || readResult.IsCompleted)
{
break;
}
if (readResult.Buffer.Length > 0)
{
Console.WriteLine(Encoding.ASCII.GetString(readResult.Buffer.ToArray()));
}
streamContext.Transport.Input.AdvanceTo(readResult.Buffer.End);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
break;
}
}
await streamContext.Transport.Input.CompleteAsync();
await streamContext.Transport.Output.CompleteAsync();
}
}
}
}

View File

@ -1,27 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
</ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<PackageReference Include="MsQuicPackage.win-x64" Version="1.0.9">
<AllowExplicitReference>true</AllowExplicitReference>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Unix'">
<PackageReference Include="MsQuicPackage.linux-x64" Version="1.0.9">
<AllowExplicitReference>true</AllowExplicitReference>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -12,6 +12,7 @@
<Compile Include="$(SharedSourceRoot)runtime\SR.cs" LinkBase="Shared\SR.cs" />
<Compile Include="$(SharedSourceRoot)Http2cat\**\*.cs" LinkBase="Shared\Http2cat" />
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" LinkBase="Shared\" />
<Compile Remove="$(SharedSourceRoot)ServerInfrastructure\DuplexPipe.cs" />
<Compile Include="$(SharedSourceRoot)TaskToApm.cs" Link="Shared\TaskToApm.cs" />
</ItemGroup>

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO.Pipelines;
using System.Net;
using System.Threading;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Connections

View File

@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Experimental;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;