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.
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 toHttpProtocols.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.Protocolsaccordingly. As a proof of concept we have implemented a middleware that does this and changesHttpConnectionMiddleware._protocolsby 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: h2cis found. This extra automation might make HTTP/2.0 easier to use since many clients supporting it would automatically switch without extra configuration.