Unnie Ayilliath

Microsoft 365 | Azure | Identity Solutions

Using HTTP/3 in gRPC

HTTP/3 is the latest version of HTTP protocol. HTTP/3 originated from Chromium project QUIC which started as an alternative to HTTP/2 with the goal of making the web faster. Unlike HTTP/1.1 and HTTP/2 which relies on TCP , HTTP/3 uses UDP under the hood in transport layer. HTTP/3 was published as a standard on June 2022 and hence it is fairly new, and browser support is growing rapidly.

What makes HTTP/3 significant? It introduces three key elements that have the potential to enhance internet performance.

  • Firstly, by using UDP instead of TCP, HTTP/3 avoids the Head of Line blocking issues present in TCP-based protocols.
  • Secondly, HTTP/3 facilitates faster initial connection set up through 0-RTT (assuming that the client has previously connected to the server and caching is employed) and 1-RTT.
  • Finally, the Connection ID feature of HTTP/3 allows for connection migration or network transitions, enabling clients to move from one network or IP address to another and continue their connection with the server. This is useful for activities such as downloading or uploading data while switching between networks.

Read this amazing write up on HTTP/3

As of February 2023, when this blog was written, gRPC does not currently offer support for HTTP/3. However, the gRPC GitHub repository includes several helpful discussions that can provide further insight into the current status. To gain a better understanding of the current state of gRPC and HTTP/3, please refer to the links below:

G2: gRPC over HTTP/3

Github issue: Support gRPC over HTTP/3

While there are numerous preview implementations of gRPC over QUIC, this blog will specifically focus on grpc-dotnet. The reason being that Microsoft is one of the first to support HTTP/3 in .NET gRPC version, and they have announced preview support for HTTP/3 in .NET 6. This preview includes support for HTTP/3 in gRPC as well

HTTP/3 support in .NET 6

A crucial aspect of the current implementation of HTTP/3 is that the client’s initial request still employs either the HTTP/2 or HTTP/1.1 protocols. Subsequently, the server responds over HTTP/2 or 1.1 with an alt-svc (alternate services) header. This header serves as a mechanism for the server to inform the client that it is available through an alternate protocol, namely HTTP/3 in this case. Upon receiving the alt-svc header, the client can then switch to the HTTP/3 protocol for subsequent connections. To gain a better understanding of this process, you may want to read a detailed blog on practical deployment options for HTTP/3.

Let’s start building a gRPC client and service set up running over HTTP/3.

Prerequisites:

  • VS 2022 or VS Code ( I am using VS 2022 in this blog)
  • .NET 6.0 or later (If possible use >=NET 7.0)
  • Windows 11 Build 22000 or later, or Server 2022 RTM
  • For Linux, libmsquic package needs to be installed. Also, specifically 1.9.x version of the package needs to be installed.

You can find the sample code developed for this blog in this GitHub Repo

Create a gRPC Service

In VS 2022, create a new project using the template “ASP.NET Core gRPC Service“. If you don’t see it in the list you can search in the search box.

Choose the .NET framework. Please note that it must be 6.0 or higher.

Once the solution is created, note that you will have the standard gRPC packages installed.

Also, in the Protos folder, a default sample greet.proto file is also created. In this blog, the main motive is to show the http/3 configuration for gRPC. So, I will just use this sample protobuf.

Also, in the program.cs file you can see that .NET Core will create a default Kestrel web server. Next we need to enable HTTP/3 for the web server. For that add below code, here in the listenOptions it is important to mention, HttpProtocols.Http1AndHttp2AndHttp3 , since the first request to server is always in HTTP/1.1 or HTTP/2.

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel((context, options) =>
{
    options.ListenAnyIP(5001, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
        listenOptions.UseHttps();
    });
});

If you are using .NET 6.0, then HTTP/3 is only available as preview feature. So preview feature needs to be enabled in project config file by adding <EnablePreviewFeatures>True</EnablePreviewFeatures> below Target Framework element:

<TargetFramework>net6.0</TargetFramework>
<EnablePreviewFeatures>True</EnablePreviewFeatures>

Now, when you run the service for the first time , VS 2022 will first prompt you to install a developer certificate and then after installation you might get below warning message:

“The ASP.NET Core developer certificate is not trusted.”

To trust the dev cert run below command in that folder. Open Tools -> Command Line -> Developer Command Prompt

dotnet dev-certs https --trust 

Create a gRPC Client

Next create a gRPC client console application.

Choose .NET 6.0 or higher

Now, a plain Hello World console app will be created. Next, install the packages relevant for gRPC.

Use NuGet Package manager (Tools > NuGet Package Manager > Package Manager Console), and install below packages.

Install-Package Grpc.Net.Client
Install-Package Google.Protobuf
Install-Package Grpc.Tools

Next, create a folder called “Protos” and copy the greet.proto file from the service solution to the client solution.

After copying, change the csharp_namespace value in the proto file to “gRPC_HTTP3_Client” or whatever namespace you choose for your client project.

option csharp_namespace = "gRPC_HTTP3_Client";

Next, edit the project file for the client and add below entry:

<ItemGroup>
   <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

If you are using .NET 6.0, then HTTP/3 is only available as preview feature. So preview feature needs to be enabled in project config file by adding <EnablePreviewFeatures>True</EnablePreviewFeatures> below Target Framework element:

<TargetFramework>net6.0</TargetFramework>
<EnablePreviewFeatures>True</EnablePreviewFeatures>

Also for .NET 6.0, enable HTTP/3 support for HttpClient by adding below in project config file.

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

Now in the Program.cs, add below code to communicate to gRPC service.

using System.Threading.Tasks;
using Grpc.Net.Client;
using gRPC_HTTP3_Client;

// The port number must match the port of the gRPC server.
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
                  new HelloRequest { Name = "GreeterClient" });
Console.WriteLine("Greeting: " + reply.Message);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();

Now, if you run the service and client, you might get below error from the client, if the service certificate installation was not successfully done and validated as trusted:

This is because the client is expecting a valid certificate from the service, so since this is a dev setup we can bypass the certificate validation by using below code:

using System.Threading.Tasks;
using Grpc.Net.Client;
using gRPC_HTTP3_Client;
// create a httpHandler
var httpHandler = new HttpClientHandler();
//ignore certificate validations
httpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; 

var httpClient = new HttpClient(httpHandler);
// The port number must match the port of the gRPC server.
//pass the httpClient while creating gRPC connection
using var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);
// send 10 messages to the server
// send 10 messages to the server
for (int i = 0;i < 10; i++)
{
    var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
    Console.WriteLine("Greeting: " + reply.Message);
}
Console.WriteLine("Greeting: " + reply.Message);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();

Now, if you run the client you will see that set up is working and if you are running on .NET 7.0 after the first request the client will automatically switch to HTTP/3 and if you are on .NET 6.0 since HTTP/3 is a preview feature it will not automatically upgrade to HTTP/3 but rather continue to use TCP along with HTTP/1.1 or 2.

Now, for .NET 6.0 to use HTTP/3 add below changes to the httpClient object.

httpClient.DefaultRequestVersion = HttpVersion.Version30;
httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;

Now, run the client and service, and you can notice in the network traffic that first a TCP connection is made and then all the subsequent requests are done via HTTP/3 using UDP or QUIC connections. See below Wireshark logs.

Best way to check the setup is by running Wireshark and see all loopback traffic for port 5001 (or whatever port you have used in the setup).

You can see below, the first request uses TCP connection.

And the subsequent requests uses UDP mentioned as QUIC in Wireshark.

You can find the sample code developed for this blog in this GitHub Repo

Published by

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Website Powered by WordPress.com.

%d bloggers like this: