Skip to content

HttpProtocols.Http1AndHttp2 over HTTP without HTTPS/ALPN #13502

@hagen93

Description

@hagen93

Is your feature request related to a problem? Please describe.

We are trying to write gRPC based microservices with frontends and backends in the same project the same way an ASP.NET Core MVC + Razor app would be structured. For this we need support for serving static files (provided by IApplicationBuilder.UseDefaultFiles()), serving gRPC (provided by the NuGet package Grpc.AspNetCore), and serving gRPC Web (provided by our custom middleware https://github.com/KnowitSolutions/Grpc.Helpers/blob/master/Grpc.Web/GrpcWebMiddleware.cs). Browsers consuming the static files and gRPC Web expects HTTP/1.1, while gRPC requires HTTP/2.0, which means Kestrel will have to switch back and forth between serving HTTP/1.1 and HTTP/2.0. Currently this is implemented by setting the Protocol field in Kestrel's listen option to HttpProtocols.Http1AndHttp2, but this implementation works by using ALPN to negotiate the HTTP protocol and therefore does not support plain HTTP as there is no TLS handshake for the ALPN negotiation to take place during. This makes it cumbersome to run this kind of setup in orchestrated container environments like Kubernetes and Docker Swarm because TLS termination in these environments usually happens at the cluster's ingress, and thus Kestrel would only ever see normal HTTP traffic. Introducing extra TLS inside such a cluster is also a hassle because then TLS certificates must be managed and distributed automatically. Solutions for this does exist, but for the problem at hand seems needlessly complicated.

Describe the solution you'd like

Instead of, or in addition to, using ALPN to negotiate the HTTP protocol it would be nice if Kestrel could sniff out the protocol used by the client on the fly. HTTP/2.0 can easily be detected as the protocol defines a 24 octets long mandatory preface before any other communication can take place (https://http2.github.io/http2-spec/#ConnectionHeader). In Kestrel this could be implemented by peeking the incoming stream at this point, looking for the HTTP/2.0 preface, and setting HttpConnectionContext.Protocols accordingly. As a proof of concept we have implemented a middleware that does this and changes HttpConnectionMiddleware._protocols by introspection (https://github.com/KnowitSolutions/Grpc.Helpers/blob/master/Kestrel.ProtocolMultiplexing/ProtocolMultiplexingMiddleware.cs), which works well, but having this as an official feature of Kestrel would be a lot more clean.

Additional context

As we already have a working implementation of this we would be happy to contribute a fix to the project that introduces this feature.

As an extension to this one could also implement the HTTP Upgrade mechanism (https://en.wikipedia.org/wiki/HTTP/1.1_Upgrade_header#Use_with_HTTP/2) to allow for clients to switch to HTTP/2.0 dynamically. This could for example be implemented as an ASP.NET middleware that returns HTTP 101 Switching Protocols if the header Upgrade: h2c is found. This extra automation might make HTTP/2.0 easier to use since many clients supporting it would automatically switch without extra configuration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    HTTP2area-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractionsenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-kestrel

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions