# HTTP/3 with ASP.NET Core and browsers

## Configure ASP.NET Core WebApi

In this post I explore how to test locally an ASP.NET Core WebApi with HTTP/3 in a local environment using a browser. In ASP.NET Core with .NET 10, HTTP/3 is already supported, but it is an opt-in feature.

To enable HTTP/3, add the following configuration to the application, either in the **appsettings.json** file

```json
"Kestrel": {
  "EndpointDefaults": {
    "Protocols": "Http1AndHttp2AndHttp3"
  }
}
```

or by **environment variables**: `ASPNETCORE_Kestrel__EndpointDefaults__Protocols=Http1AndHttp2AndHttp3` or by [code](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/http3?view=aspnetcore-10.0).

On a local environment, the application is hosted on localhost:port, something similar to this in the **launchSettings.json**:

> In the following examples, I use port 7297. Substitute this with the port used by your application.

```json
"https": {
  "commandName": "Project",
  "dotnetRunMessages": true,
  "launchBrowser": false,
  "launchUrl": "weatherforecast",
  "applicationUrl": "https://localhost:7297;http://localhost:5177",
  "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_Kestrel__EndpointDefaults__Protocols": "Http1AndHttp2AndHttp3" // 👈 HTTP/3 enabled as env var.
}
```

The Kestrel webserver will add a response header: `alt-svc: h3=":7297"; ma=86400` advertising the HTTP/3 service endpoint. The very first request is always HTTP/1.1 or HTTP/2. Future requests may use HTTP/3 for the initial request too, if [0-RTT](https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4.2) is supported by the client and the server.

The localhost (127.0.0.1) means that [msquic](https://github.com/microsoft/msquic) will listen to all IP addresses (0.0.0.0), but internally filters incoming requests' IP address to 127.0.0.1. See [this reference](https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/quic/quic-troubleshooting#client-receives-unexpected-alpn-error) for further details.

In certain clients this means that using the `https://localhost:7297` address might return an ALPN error:

```
Request Error System.Net.Http.HttpRequestException: Application layer protocol negotiation error was encountered. (localhost:7297)
 ---> System.Security.Authentication.AuthenticationException: Application layer protocol negotiation error was encountered.
   at System.Net.Quic.QuicConnection.
```

In such cases, try accessing the API as `https://127.0.0.1:7297`.

Note that only the *https* address will work, as QUIC requires TLS encryption. By default an ASP.NET Core application in the dev environment will set up self-signed certificates. On Windows these are stored under *Current User/My* as well as the *Trusted Roots*. Browsers can use the certificates from this store. Here, the latest .NET 10 certificates are required, as those include both the `DNS Name=localhost` and the `IP Address=127.0.0.1` in the Subject Alternative Name field.

Most browsers won't support requesting the endpoint on HTTP/3 with a self-signed certificate out of the box. For that, some of the settings must be tweaked.

## Chrome / Chromium / Edge

The following [Chromium documentation](https://www.chromium.org/quic/playing-with-quic) explains how to configure Chrome. TLDR:

1. Export the dev certificate (public part) from the certificate store and convert it to PEM format.
1. Use openssl to generate the SPKI using the following commands: `openssl x509 -noout -pubkey -in aspnetdevcert.pem | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64`
1. Start the browser (here chromium based msedge) by passing output from the previous command on an ignore list argument: `msedge --user-data-dir=/tmp/chrome-profile --origin-to-force-quic-on=127.0.0.1:7297 --ignore-certificate-errors-spki-list='5tq...' https://127.0.0.1:7297`
1. The browser will show a warning and open the page using HTTP/3 (remember, after refresh only with 1-RTT).

## Firefox

1. Open Firefox and in **about:config** update **network.http.http3.disable_when_third_party_roots_found** setting to `false` and **network.http.http3.force-use-alt-svc-mapping-for-testing** to `true`.
1. Restart the browser and open the webapi endpoint.
1. Refresh after the initial request (remember alt-svc, 1-RTT).

For further reference, see the following [documentation](https://github.com/mozilla/neqo?tab=readme-ov-file#connect-with-firefox-to-local-neqo-server).

## CHttp

[CHttp](https://github.com/ladeak/Http3Tools) is a *curl* like tool that uses `HttpClient` (and msquic) under the hood to send HTTP/3 requests. Run the following command to load the endpoint: 

```
.\chttp.exe --uri https://localhost:7297 --method GET -v 3
```

If the dev certificates are not installed locally, add the `--no-certificate-validation` flag to suppress cert errors.
