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:
parent
1cd7e15fb2
commit
9f398d9a56
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"/>.
|
||||
|
|
|
|||
|
|
@ -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>>();
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue