Skip to content

Refactor quic setup #34877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Logging.W3C.Sample", "src\M
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Razor.Internal.SourceGenerator.Transport", "src\Razor\Microsoft.AspNetCore.Razor.Internal.SourceGenerator.Transport\Microsoft.AspNetCore.Razor.Internal.SourceGenerator.Transport.csproj", "{247E7B6F-FBA2-41A9-BA03-C7C4DF28091C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpClientApp", "src\Servers\Kestrel\samples\HttpClientApp\HttpClientApp.csproj", "{514726D2-3D2E-44C1-B056-163E37DE3E8B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -7803,6 +7805,18 @@ Global
{247E7B6F-FBA2-41A9-BA03-C7C4DF28091C}.Release|x64.Build.0 = Release|Any CPU
{247E7B6F-FBA2-41A9-BA03-C7C4DF28091C}.Release|x86.ActiveCfg = Release|Any CPU
{247E7B6F-FBA2-41A9-BA03-C7C4DF28091C}.Release|x86.Build.0 = Release|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Debug|x64.ActiveCfg = Debug|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Debug|x64.Build.0 = Debug|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Debug|x86.ActiveCfg = Debug|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Debug|x86.Build.0 = Debug|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Release|Any CPU.Build.0 = Release|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Release|x64.ActiveCfg = Release|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Release|x64.Build.0 = Release|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Release|x86.ActiveCfg = Release|Any CPU
{514726D2-3D2E-44C1-B056-163E37DE3E8B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -8613,6 +8627,7 @@ Global
{F599EAA6-399F-4A91-9B1F-D311305B43D9} = {323C3EB6-1D15-4B3D-918D-699D7F64DED9}
{17459B97-1AA3-4154-83D3-C6BDC9FA3F85} = {022B4B80-E813-4256-8034-11A68146F4EF}
{247E7B6F-FBA2-41A9-BA03-C7C4DF28091C} = {B27FBAC2-ADA3-4A05-B232-64011B6B2DA3}
{514726D2-3D2E-44C1-B056-163E37DE3E8B} = {7B976D8F-EA31-4C0B-97BD-DFD9B3CC86FB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ public async Task<EndPoint> BindAsync(EndPoint endPoint, MultiplexedConnectionDe
// TODO Set other relevant values on options
var sslServerAuthenticationOptions = new SslServerAuthenticationOptions
{
ServerCertificate = listenOptions.HttpsOptions.ServerCertificate
ServerCertificate = listenOptions.HttpsOptions.ServerCertificate,
ApplicationProtocols = new List<SslApplicationProtocol>() { new SslApplicationProtocol("h3") }
};

features.Set(sslServerAuthenticationOptions);
Expand Down
38 changes: 30 additions & 8 deletions src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,36 @@ public async Task StartAsync<TContext>(IHttpApplication<TContext> application, C

async Task OnBind(ListenOptions options, CancellationToken onBindCancellationToken)
{
var hasHttp1 = options.Protocols.HasFlag(HttpProtocols.Http1);
var hasHttp2 = options.Protocols.HasFlag(HttpProtocols.Http2);
var hasHttp3 = options.Protocols.HasFlag(HttpProtocols.Http3);
var hasTls = options.IsTls;

// Filter out invalid combinations.

if (!hasTls)
{
// Http/1 without TLS, no-op HTTP/2 and 3.
if (hasHttp1)
{
hasHttp2 = false;
hasHttp3 = false;
}
// Http/3 requires TLS. Note we only let it fall back to HTTP/1, not HTTP/2
else if (hasHttp3)
{
throw new InvalidOperationException("HTTP/3 requires https.");
}
}

// Quic isn't registered if it's not supported, throw if we can't fall back to 1 or 2
if (hasHttp3 && _multiplexedTransportFactory is null && !(hasHttp1 || hasHttp2))
{
throw new InvalidOperationException("This platform doesn't support QUIC or HTTP/3.");
}

// Add the HTTP middleware as the terminal connection middleware
if ((options.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1
|| (options.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2
if (hasHttp1 || hasHttp2
|| 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?
{
Expand All @@ -180,13 +207,8 @@ async Task OnBind(ListenOptions options, CancellationToken onBindCancellationTok
options.EndPoint = await _transportManager.BindAsync(options.EndPoint, connectionDelegate, options.EndpointConfig, onBindCancellationToken).ConfigureAwait(false);
}

if ((options.Protocols & HttpProtocols.Http3) == HttpProtocols.Http3)
if (hasHttp3 && _multiplexedTransportFactory is not null)
{
if (_multiplexedTransportFactory is null)
{
throw new InvalidOperationException($"Cannot start HTTP/3 server if no {nameof(IMultiplexedConnectionListenerFactory)} is registered.");
}

options.UseHttp3Server(ServiceContext, application, options.Protocols, !options.DisableAltSvcHeader);
var multiplexedConnectionDelegate = ((IMultiplexedConnectionBuilder)options).Build();

Expand Down
5 changes: 3 additions & 2 deletions src/Servers/Kestrel/Kestrel.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"solution": {
"path": "..\\..\\..\\AspNetCore.sln",
"projects": [
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
"src\\Extensions\\Features\\test\\Microsoft.Extensions.Features.Tests.csproj",
"src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj",
"src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj",
"src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj",
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
"src\\Extensions\\Features\\test\\Microsoft.Extensions.Features.Tests.csproj",
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
"src\\Http\\Http.Abstractions\\src\\Microsoft.AspNetCore.Http.Abstractions.csproj",
"src\\Http\\Http.Extensions\\src\\Microsoft.AspNetCore.Http.Extensions.csproj",
Expand All @@ -27,6 +27,7 @@
"src\\Servers\\Kestrel\\perf\\Microbenchmarks\\Microsoft.AspNetCore.Server.Kestrel.Microbenchmarks.csproj",
"src\\Servers\\Kestrel\\samples\\Http2SampleApp\\Http2SampleApp.csproj",
"src\\Servers\\Kestrel\\samples\\Http3SampleApp\\Http3SampleApp.csproj",
"src\\Servers\\Kestrel\\samples\\HttpClientApp\\HttpClientApp.csproj",
"src\\Servers\\Kestrel\\samples\\LargeResponseApp\\LargeResponseApp.csproj",
"src\\Servers\\Kestrel\\samples\\PlaintextApp\\PlaintextApp.csproj",
"src\\Servers\\Kestrel\\samples\\SampleApp\\Kestrel.SampleApp.csproj",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Core" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Quic" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static class WebHostBuilderKestrelExtensions
/// </returns>
public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder)
{
hostBuilder.UseQuic();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So anyone using Kestrel will be using Quic, even if not using Http3? Does it no-op for Http1/2?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, this adds the services but they're only used if you enable http3.

return hostBuilder.ConfigureServices(services =>
{
// Don't override an already-configured transport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ public void DefaultConfigSectionCanSetProtocols_MacAndWin7(string input, HttpPro
[InlineData("http1", HttpProtocols.Http1)]
[InlineData("http2", HttpProtocols.Http2)]
[InlineData("http1AndHttp2", HttpProtocols.Http1AndHttp2)]
[InlineData("http1AndHttp2andHttp3", HttpProtocols.Http1AndHttp2AndHttp3)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win81)]
public void DefaultConfigSectionCanSetProtocols_NonMacAndWin7(string input, HttpProtocols expected)
Expand Down
4 changes: 1 addition & 3 deletions src/Servers/Kestrel/Transport.Quic/src/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@

[assembly: InternalsVisibleTo("Quic.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: SupportedOSPlatform("windows")]
[assembly: SupportedOSPlatform("macos")]
[assembly: SupportedOSPlatform("linux")]

Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,10 @@ public QuicConnectionListener(QuicTransportOptions options, IQuicTrace log, EndP
throw new NotSupportedException("QUIC is not supported or enabled on this platform. See https://aka.ms/aspnet/kestrel/http3reqs for details.");
}

if (options.Alpn == null)
{
throw new InvalidOperationException("QuicTransportOptions.Alpn must be configured with a value.");
}

_log = log;
_context = new QuicTransportContext(_log, options);
var quicListenerOptions = new QuicListenerOptions();

// TODO Should HTTP/3 specific ALPN still be global? Revisit whether it can be statically set once HTTP/3 is finalized.
sslServerAuthenticationOptions.ApplicationProtocols = new List<SslApplicationProtocol>() { new SslApplicationProtocol(options.Alpn) };

quicListenerOptions.ServerAuthenticationOptions = sslServerAuthenticationOptions;
quicListenerOptions.ListenEndPoint = endpoint as IPEndPoint;
quicListenerOptions.IdleTimeout = options.IdleTimeout;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;kestrel</PackageTags>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>CS1591;CS0436;$(NoWarn)</NoWarn><!-- Conflicts between internal and public Quic APIs -->
<NoWarn>CA1416;CS1591;CS0436;$(NoWarn)</NoWarn><!-- Conflicts between internal and public Quic APIs; Platform support warnings. -->
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
*REMOVED*~static Microsoft.AspNetCore.Hosting.WebHostBuilderMsQuicExtensions.UseQuic(this Microsoft.AspNetCore.Hosting.IWebHostBuilder hostBuilder) -> Microsoft.AspNetCore.Hosting.IWebHostBuilder
Microsoft.AspNetCore.Hosting.WebHostBuilderQuicExtensions
Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.QuicTransportOptions
Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.QuicTransportOptions.Alpn.get -> string?
Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.QuicTransportOptions.Alpn.set -> void
Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.QuicTransportOptions.IdleTimeout.get -> System.TimeSpan
Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.QuicTransportOptions.IdleTimeout.set -> void
Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.QuicTransportOptions.MaxBidirectionalStreamCount.get -> ushort
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,8 @@ public async ValueTask<MultiplexedConnectionContext> ConnectAsync(EndPoint endPo
{
throw new NotSupportedException($"{endPoint} is not supported");
}
if (_transportContext.Options.Alpn == null)
{
throw new InvalidOperationException("QuicTransportOptions.Alpn must be configured with a value.");
}

var sslOptions = new SslClientAuthenticationOptions();
sslOptions.ApplicationProtocols = new List<SslApplicationProtocol>() { new SslApplicationProtocol(_transportContext.Options.Alpn) };
var sslOptions = features?.Get<SslClientAuthenticationOptions>();
var connection = new QuicConnection(QuicImplementationProviders.MsQuic, (IPEndPoint)endPoint, sslOptions);

await connection.ConnectAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,10 @@ public class QuicTransportOptions
/// </summary>
public ushort MaxUnidirectionalStreamCount { get; set; } = 10;

/// <summary>
/// The Application Layer Protocol Negotiation string.
/// </summary>
public string? Alpn { get; set; }

/// <summary>
/// Sets the idle timeout for connections and streams.
/// </summary>
public TimeSpan IdleTimeout { get; set; }
public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(130); // Matches KestrelServerLimits.KeepAliveTimeout.

/// <summary>
/// The maximum read size.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ public static class WebHostBuilderQuicExtensions
{
public static IWebHostBuilder UseQuic(this IWebHostBuilder hostBuilder)
{
if (!QuicImplementationProviders.Default.IsSupported)
if (QuicImplementationProviders.Default.IsSupported)
{
throw new NotSupportedException("QUIC is not supported or enabled on this platform. See https://aka.ms/aspnet/kestrel/http3reqs for details.");
return hostBuilder.ConfigureServices(services =>
{
services.AddSingleton<IMultiplexedConnectionListenerFactory, QuicTransportFactory>();
});
}
return hostBuilder.ConfigureServices(services =>
{
services.AddSingleton<IMultiplexedConnectionListenerFactory, QuicTransportFactory>();
});

return hostBuilder;
}

public static IWebHostBuilder UseQuic(this IWebHostBuilder hostBuilder, Action<QuicTransportOptions> configureOptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ internal static class QuicTestHelpers
public static QuicTransportFactory CreateTransportFactory(ILoggerFactory loggerFactory = null, ISystemClock systemClock = null)
{
var quicTransportOptions = new QuicTransportOptions();
quicTransportOptions.Alpn = Alpn;
quicTransportOptions.IdleTimeout = TimeSpan.FromMinutes(1);
quicTransportOptions.MaxBidirectionalStreamCount = 200;
quicTransportOptions.MaxUnidirectionalStreamCount = 200;
Expand Down Expand Up @@ -59,6 +58,7 @@ public static FeatureCollection CreateBindAsyncFeatures()
var cert = TestResources.GetTestCertificate();

var sslServerAuthenticationOptions = new SslServerAuthenticationOptions();
sslServerAuthenticationOptions.ApplicationProtocols = new List<SslApplicationProtocol>() { new SslApplicationProtocol(Alpn) };
sslServerAuthenticationOptions.ServerCertificate = cert;
sslServerAuthenticationOptions.RemoteCertificateValidationCallback = RemoteCertificateValidationCallback;

Expand Down
35 changes: 5 additions & 30 deletions src/Servers/Kestrel/Transport.Quic/test/WebHostTests.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Buffers;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.FunctionalTests;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Xunit;

namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.Tests
Expand All @@ -31,7 +21,7 @@ public class WebHostTests : LoggedTest
public async Task UseUrls_HelloWorld_ClientSuccess()
{
// Arrange
var builder = GetHostBuilder()
var builder = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
Expand Down Expand Up @@ -82,7 +72,7 @@ public async Task UseUrls_HelloWorld_ClientSuccess()
public async Task Listen_Http3AndSocketsCoexistOnDifferentEndpoints_ClientSuccess(int http3Port, int http1Port)
{
// Arrange
var builder = GetHostBuilder()
var builder = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
Expand Down Expand Up @@ -122,7 +112,7 @@ public async Task Listen_Http3AndSocketsCoexistOnDifferentEndpoints_ClientSucces
public async Task Listen_Http3AndSocketsCoexistOnSameEndpoint_ClientSuccess()
{
// Arrange
var builder = GetHostBuilder()
var builder = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
Expand Down Expand Up @@ -157,7 +147,7 @@ public async Task Listen_Http3AndSocketsCoexistOnSameEndpoint_ClientSuccess()
public async Task Listen_Http3AndSocketsCoexistOnSameEndpoint_AltSvcEnabled_Upgrade()
{
// Arrange
var builder = GetHostBuilder()
var builder = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
Expand Down Expand Up @@ -221,7 +211,7 @@ public async Task Listen_Http3AndSocketsCoexistOnSameEndpoint_AltSvcEnabled_Upgr
public async Task Listen_Http3AndSocketsCoexistOnSameEndpoint_AltSvcDisabled_NoUpgrade()
{
// Arrange
var builder = GetHostBuilder()
var builder = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
Expand Down Expand Up @@ -321,20 +311,5 @@ private static HttpClient CreateClient()

return new HttpClient(httpHandler);
}

public static IHostBuilder GetHostBuilder(long? maxReadBufferSize = null)
{
return new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
.UseQuic(options =>
{
options.MaxReadBufferSize = maxReadBufferSize;
options.Alpn = QuicTestHelpers.Alpn;
options.IdleTimeout = TimeSpan.FromSeconds(20);
});
});
}
}
}
Loading