Using HTTP/3 (QUIC) in .NET
#What's HTTP/3?
HTTP/3 is a new version of HTTP. HTTP/3 protocol is supported by most modern browsers and servers. This update should bring performance benefits mostly for mobile users or unreliable connections. The main idea is to replace TCP by a new protocol, QUIC, which removes some issues of TCP for HTTP. So, QUIC has the following advantages (non-exhaustive list):
- Faster connection set-up by combining TCP and TLS handshakes
- Less head of line blocking by better handling packet loss recovery logic
- Connection migration, so you don't need to reconnect (handshake) when moving between networks (e.g. WIFI to cellular)
If you want to understand in details what is HTTP/3, you can read the following articles:
- HTTP/3 From A To Z: Core Concepts (Part 1)
- HTTP/3: Performance Improvements (Part 2)
- HTTP/3: Practical Deployment Options (Part 3)
- Comparing HTTP/3 vs. HTTP/2 Performance - Cloudflare
- Deploying HTTP/3 on Windows Server at Scale
- QUIC at Snapchat
.NET 6 supports HTTP/3 for clients (HttpClient
, including gRPC) and servers (Kestrel
). This implementation is based on MsQuic, is a Microsoft implementation of the IETF QUIC protocol. Note that this is still in preview in .NET 6, so you need to explicitly enable it in the csproj or the code. At the moment, .NET supports HTTP/3 on:
- Windows 11 and Windows Server 2022
- Linux (you may need to install
msquic
usingapt install libmsquic
)
While msquic
supports macOS using OpenSSL, the .NET implementation doesn't support it at the moment. Indeed, the .NET team prefers relying on the OS security APIs instead of adding a new dependency, so SecureTransport
on macOS. It avoids integration issues, such as certificate management. However, SecureTransport
doesn't expose methods to implement QUIC.
#Server (Kestrel)
You first need to enable preview features in the csproj:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
</PropertyGroup>
</Project>
Then, you can configure Kestrel to listen on HTTP/1, HTTP/2 and HTTP/3. It's important to support older protocols as not all clients support newer protocols. Also, HTTP/3 requires secure connections, so you must use UseHttps
.
using Microsoft.AspNetCore.Server.Kestrel.Core;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel((context, options) =>
{
options.ListenAnyIP(5001, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
listenOptions.UseHttps();
});
});
var app = builder.Build();
app.MapGet("/", () => "hello world");
app.Run();
Most browsers don't allow HTTP/3 for localhost
addresses. However, you can validate it works by looking at the response header. The response should contain the the alt-svc
header with the value h3
:
You can also check the server uses HTTP/3 by enabling more detailed logging. You can change the configuration in appsettings.json
or appsettings.Development.json
:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Information"
}
}
}
Then, you should see the following in the logs:
You can also use W3C logging and check the protocol version used by the client:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddW3CLogging(logging =>
{
logging.LoggingFields = W3CLoggingFields.All;
logging.LogDirectory = @"C:\logs";
logging.FlushInterval = TimeSpan.FromSeconds(2);
});
builder.WebHost.ConfigureKestrel((context, options) =>
{
...
});
var app = builder.Build();
app.UseW3CLogging();
app.MapGet("/", () => "hello world");
app.Run();
Alternatively, you can use the client from the next section to validate the server works correctly.
#Client (HttpClient)
HTTP/3 has preview support in .NET 6. So, you need to manually enable the feature before using HTTP/3 in HttpClient
:
Option 1: Edit the
csproj
to add a runtime optioncsproj (MSBuild project file)<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" /> </ItemGroup> </Project>
Option 2: Set the following switch before creating the first
HttpClient
C#System.AppContext.SetSwitch("System.Net.SocketsHttpHandler.Http3Support", true);
Then, you can use the HttpClient
to make requests to HTTP/3 servers:
using var client = new HttpClient();
client.DefaultRequestVersion = HttpVersion.Version30;
// The client falls back to HTTP2 or HTTP1 if HTTP3 is not supported
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
// Will use HTTP3 if the server supports it
var data = await client.GetStringAsync("https://localhost:5001/");
You can also enable HTTP3 for specific requests:
using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:5001/");
request.Version = HttpVersion.Version30;
request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
using var response = await client.SendAsync(request);
var data = await response.Content.ReadAsStringAsync();
#Additional resources
This post is available in Spanish at https://www.campusmvp.es/recursos/post/como-utilizar-http-3-quic-en-net.aspx.
Do you have a question or a suggestion about this post? Contact me!