HTTP/3 support in .NET 6

Sam Spencer

.NET 6 includes preview support for HTTP/3:

  • In Kestrel, HTTP.Sys & IIS for ASP.NET for server scenarios
  • In HttpClient to make outbound requests
  • For gRPC

What is HTTP/3 and why is support important?

HTTP through version 1.1 was a relatively simple protocol, open a TCP connection, send a set of headers over clear text and then receive the response. Requests can be pipelined over the same connection, but each has to be handled in order. TLS adds some additional complications, and a couple of round-trips to initiate a connection, but once established, HTTP is used the same way over the secure channel.

As many sites have moved to require TLS encryption, and you can only serve a request at a time per connection with HTTP/1.1, performance of web pages that typically require downloading multiple resources (scripts, images, CSS, fonts, etc.) was being limited as multiple connections are needed, each of which has high setup costs.

HTTP/2 solved the problem by changing to become a binary protocol using a framing concept to enable multiple requests to be handled at the same time over the same connection. The setup costs for TLS could be paid once, and then all the requests can be interleaved over that single connection.

That’s all great, except we have all gone mobile and much of the access is now from phones and tablets using Wi-Fi and cellular connections which can be unreliable. Although HTTP/2 enables multiple streams, they all go over a connection which is TLS encrypted, so if a TCP packet is lost all of the streams are blocked until the data can be recovered. This is known as the head of line blocking problem.

HTTP/3 solves these problems by using a new underlying connection protocol called QUIC. QUIC uses UDP and has TLS built in, so it’s faster to establish connections as the TLS handshake occurs as part of the connection. Each frame of data is independently encrypted so it no longer has the head of line blocking in the case of packet loss. Unlike TCP a QUIC connection is independent of the IP address, so mobile clients can roam between wifi and cellular networks, keeping the same logical connection, continuing long downloads etc.

Amongst the metrics that sites and services use, tracking the latency of the worst connections using P90, P95 or P99 is common. HTTP/3 is already proving to have a positive impact on these numbers, improving the experience for users with the worst connections, for example Facebook, Snapchat & Google Cloud.

QUIC Support in .NET

QUIC is designed as a base layer for HTTP/3 but it can also be used by other protocols. It is designed to work well for mobile with the ability to handle network changes, and have good recovery if packet loss occurs.

.NET uses the MSQuic library for its QUIC implementation. This is an open source, cross platform library from the Windows networking team. For packaging reasons it is included in .NET 6 for Windows, and as a separate package for Linux.

A key difference with QUIC is that TLS encryption is built in, and so the connection establishment includes the TLS handshake. This means that the TLS library used needs to provide APIs to enable this type of handshake. For Windows, the APIs are included in SChannel / Bcrypt.dll. For Linux it’s a bit more complicated – OpenSSL which is used by .NET and most other software on Linux does not yet include these APIs. The OpenSSL team has been heads down working on OpenSSL 3.0, which has a hard deadline to be submitted for FIPS 140-2 certification, so was unable to add that support directly in OpenSSL 3.0.

This created a problem for us, and many others working on QUIC and HTTP/3. To provide a stopgap solution until OpenSSL can include API support for QUIC handshakes, Microsoft has partnered with Akamai to create a fork of OpenSSL – QuicTLS – that provides the APIs to enable the QUIC handshake. Unlike other forks which have diverged over time from OpenSSL, QuicTLS provides a minimal delta over mainline OpenSSL, and is kept in sync with the upstream.

The MSQuic package for Linux is statically linked with QuicTLS, and so does not need a separate download and managing multiple variants of the OpenSSL library. This also means that when mainline OpenSSL includes QUIC APIs, the package will be updated to use those instead.

In .NET 6, we are not exposing the .NET QUIC APIs, the goal is to make them public in .NET 7. QUIC can be used like a TCP socket and is not specific to HTTP/3 so we expect other protocols to be built on QUIC over time, such as SMB over QUIC.

HTTP/3 Support in .NET 6

At the time of publishing this post, the RFC for HTTP/3 is not yet finalized, and so can still change. We have included HTTP/3 in .NET 6 so that customers can start experimenting with it, but it is a preview feature for .NET 6 – this is because it does not meet the quality standards of the rest of .NET 6. There may be rough edges, and there needs to be broader testing with other servers & clients to ensure compatibility, especially in the edge cases.

Prerequisites

To use HTTP/3 the prerequisite versions of MSQuic and its TLS dependencies need to be installed.

Windows

MsQuic is installed as part of .NET 6, but it needs an updated version of Schannel SSP which provides the TLS API, this is supplied with recent releases of the OS.

  • Windows 11 Build 22000 or later, or Server 2022 RTM

Windows 11 builds are currently only available to Windows Insiders.

Linux

On Linux, libmsquic is published via Microsoft official Linux package repository packages.microsoft.com. In order to consume it, it must be added manually. See Linux Software Repository for Microsoft Products. After configuring the package feed, it can be installed via the package manager for your distro, for example, on Ubuntu:

sudo apt install libmsquic=1.9*

Update: .NET 6 is only compatible with the 1.9.x versions of libmsquic. Libmsquic 2.x is not compatible due to breaking changes. Libmsquic will get updates to 1.9.x when needed to incorporate security fixes.

Kestrel Server

Server support is included in Kestrel. Preview features need to be enabled using the following project property:

<PropertyGroup>
  <EnablePreviewFeatures>True</EnablePreviewFeatures>
</PropertyGroup>

And then set in the listener options, for example:

public static async Task Main(string[] args)
{
  var builder = WebApplication.CreateBuilder(args);
  builder.WebHost.ConfigureKestrel((context, options) =>
  {
    options.Listen(IPAddress.Any, 5001, listenOptions =>
    {
      // Use HTTP/3
      listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
      listenOptions.UseHttps();
    });
  });
}

For more details, see Use HTTP/3 with the ASP.NET Core Kestrel web server

HTTP/3 Client

HttpClient has been updated to include support for HTTP/3, but it needs to be enabled with a runtime flag. Include the following in the project file to enable HTTP/3 with HttpClient:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
</ItemGroup>

HTTP/3 needs to be specified as the version for the request:

// See https://aka.ms/new-console-template for more information
using System.Net;

var client = new HttpClient();
client.DefaultRequestVersion = HttpVersion.Version30;
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;

var resp = await client.GetAsync("https://localhost:5001/");
var body = await resp.Content.ReadAsStringAsync();

Console.WriteLine($"status: {resp.StatusCode}, version: {resp.Version}, body: {body.Substring(0, Math.Min(100, body.Length))}");

HTTP/3 via HTTP.sys & IIS

On Windows Server 2022, Http.sys supports HTTP/3 when it’s enabled with a registry key, and TLS 1.3 must be enabled (default). This is independent of ASP.NET’s support for HTTP/3, as the HTTP protocol is handled by HTTP.sys in this configuration – so it applies to not just ASP.NET but any content or services served by HTTP.sys. For more details see this blog post from the Windows networking team.

gRPC with HTTP/3

gRPC is a RPC mechanism using the protobuf serialization format. gRPC typically uses HTTP/2 as its transport. HTTP/3 uses the same semantics, so there is little change required to make it work. gRPC over HTTP/3 is not yet a standard, and is proposed by the .NET team.

The following code is based on the greeter sample, with the hello world proto.

The client and server projects require the same respective preview feature enablement in their projects as the samples further above.

ASP.NET Server

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddGrpc();
builder.WebHost.ConfigureKestrel((context, options) =>
{
  options.Listen(IPAddress.Any, 5001, listenOptions =>
  {
    listenOptions.Protocols = HttpProtocols.Http3;
    listenOptions.UseHttps();
  });
});
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.MapGrpcService<GreeterService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");

app.Run();

Client

using Grpc.Net.Client;
using GrpcService1;
using System.Net;

var httpClient = new HttpClient();
httpClient.DefaultRequestVersion = HttpVersion.Version30;
httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions() { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

var response = await client.SayHelloAsync(
new HelloRequest { Name = "World" });

Console.WriteLine(response.Message);

macOS support

.NET 6 does not include support for HTTP/3 on macOS, primarily because of a lack of a QUIC compatible TLS API. .NET uses SecureTransport on macOS for its TLS implementation, which does not yet include TLS APIs to support QUIC handshake. While we could use OpenSSL, we felt it better to not introduce an additional dependency which is not integrated with the cert management of the OS.

Going Forward

We will be investing further in QUIC and HTTP/3 in .NET 7, so expect to see updated functionality in the previews.

11 comments

Discussion is closed. Login to edit/delete existing comments.

  • Laszlo Deak 0

    What was the motivation for not having a managed implementation?

    • Sam SpencerMicrosoft employee 0

      The work for HTTP/3 has come in hot for .NET 6, and so we have not had much time to bake the API surface. The APIs needed to be exposed for Http support were small, the surface for QUIC will be much bigger, so it needs more time to bake. We endeavor to not break APIs from release to release, so what we make public needs to be able to stand the tests of time.
      There are few protocols currently using QUIC as the transport, and only Http in .NET, so its not had enough coverage to be confident in the current QUIC APIs – but there also hasn’t been a strong need for them. With the QUIC rfc’s being approved, that should change in the next year or so.

  • Gavin Ray 0

    Thanks for the writeup Sam, really interesting!
    I’m not very familiar with the HTTP/3 standard but recall an “HTTP/2 over QUIC” protocol several years ago (that I believe then evolved into this?) and many folks being excited about it.

    As someone who isn’t an expert in this area, are there any details on what the potential performance gains in P90 etc latencies look like when using HTTP/3 versus HTTP/1.1 and HTTP/2?

    Would be curious to see any numbers if you had them available.
    Much appreciated.

    • Sam SpencerMicrosoft employee 0

      These numbers are best going to come from services that have deployed on HTTP/3 – we haven’t done that for the .NET sites yet. The numbers Snapchat published showed a lot of promise.

      Another aspect of QUIC is that as its typically implemented at the app layer, rather than as part of the OS, there is more flexibility to be able to experiment with congestion protocols etc. Its still very early days for QUIC deployment, and I expect there to be improvements as more is learned about how it operates in the wild.

  • Ahmed Mohamed Abdel-Razek 0

    in windows support you only mentioned windows 11 does that meant windows 10 will not be supported?!

    • Sam SpencerMicrosoft employee 0

      Windows 10 is not currently supported. It needs the updated SChannel with TLS 1.3 APIs that support QUIC handshakes. If the demand is there we can bring that up with the Windows team.

      • Mike-E 0

        Srsly? It’s like only 95% of your userbase and easily the best OS ever. 😛

    • Tomas WeinfurtMicrosoft employee 0

      Yes, custom certificate validation should work “normal”. Since QUIC is not exposed directly in 6.0, what you will get is actually https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.servercertificatecustomvalidationcallback?view=net-5.0 @mu88 The plumbing is done behind the scenes o either use the validation hook on SslStream stream or on Quic. With few small exceptions everything should work pretty much the same. (you can always look for active Quic/Http3 issues in runtime repo)

      • mu88 0

        Good to hear, thank you for the explanation

  • Jin Jeon 0

    How can I bind HttpClient to a specific (source) ip address when I use HTTP/3? ConnectCallback of the SocketsHttpHandler never called.

Feedback usabilla icon