Negotiate H2C on a Kestrel endpoint that supports HTTP/1.1 and HTTP/2
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
### Is there an existing issue for this?
- [X] I have searched the existing issues
### Is your feature request related to a problem? Please describe the problem.
Kestrel endpoints support multiple HTTP versions side-by-side. This feature requires the endpoint use TLS. The ALPN identifier in the TLS handshake tells Kestrel which HTTP version to use.
If an endpoint is cleartext (doesn't have TLS) then the connection always falls back to HTTP/1.1. If a client sends a prior knowledge H2C request to the server, the server will error with HTTP_1_1_REQUIRED.
This change in behavior can be confusing to users. They might use TLS in their local development environment, and then switch to cleartext in production. They don't know the mechanics of protocol negotiation and won't expect their app to break.
An example of a user hitting this problem: https://github.com/dotnet/dotnet-docker/issues/5691
Working around the lack of negotiation over cleartext can require significant changes to an app. Multiple ports must be exposed, one supporting HTTP/1.1 (required for browsers who don't support HTTP/2 over cleartext) and another support HTTP/2 only.
### Describe the solution you'd like
Kestrel to support negotiation between HTTP/1.1 and HTTP/2 without TLS. An HTTP/2 connection always starts with a [connection preface](https://www.rfc-editor.org/rfc/rfc7540#section-3.5).
Steps:
* Inspect the first bytes send over a connection for HTTP/2 connection preface
* Extra logic only runs when endpoint is cleartext and includes HTTP/1.1 and HTTP/2 as supported protocols. It doesn't matter if HTTP/3 is included or not. There is no special negotiation for HTTP/3 (it requires TLS and so can't work), but it doesn't stop negotiation between HTTP/1.1 and HTTP/2
* After the first bytes are inspected and the protocol is decided, the connection remains that protocol until the connection ends. A connection doesn't support both protocol's requests and doesn't support changing protocol later.
Should be mindful of [cross-protocol attacks](https://www.rfc-editor.org/rfc/rfc7540#section-10.2) and properly mitigate.
### Additional context
golang's h2c support includes protocol negotation over cleartext. See https://pkg.go.dev/golang.org/x/net/http2/h2c#NewHandler
Contributor guide
Assessment
This issue has not been assessed yet.