How to accept HTTP/2 request and proxy it upstream as HTTP/1 #3956
-
I have built a proxy making use of Hyper. Normally we run in GCP Cloud Run, which by default downgrades HTTP/2 requests to HTTP/1. This is nice because then the request we make to our upstreams is always HTTP/1, which is always supported. For various reasons, we want to turn on end-to-end HTTP/2 now (ref), which means TLS is terminated and the request continues as h2c. This means that the proxy can receive either HTTP/2 or HTTP/1 requests. I would like to be able to set some configuration on a per upstream basis to declare whether the upstream can handle HTTP/2 requests. If not, when we receive a HTTP/2 request, we now need to handle proxying it upstream as HTTP/1, then proxying the response back to the client still as HTTP/2. I was hoping that Hyper would automagically handle this, and indeed it seems like it should be able to. Unfortunately I can't figure out the correct way to build the client to make this happen. First off, my dependencies: axum = { version = "0.7.4", features = ["http2", "ws"] }
http = "1.1.0"
http-body = "1.0.0"
http-body-util = "0.1.1"
hyper = { version = "1.2.0" }
hyper-tls = { version = "0.6.0" }
hyper-util = { version = "0.1.3", features = ["client-legacy"] } Currently I'm doing this: use axum::body::{Body, Bytes};
use http_body_util::BodyExt;
use hyper_util::{
client::legacy::{Builder, Client, connect::HttpConnector},
rt::TokioExecutor,
}
pub type HyperClient = Client<hyper_tls::HttpsConnector<HttpConnector>, axum::body::Body>;
/// Opionated function to return a client with a https connector.
pub fn build_http_client() -> HyperClient {
Client::builder(TokioExecutor::new())
.build::<_, axum::body::Body>(hyper_tls::HttpsConnector::new())
} When I receive a HTTP/2 request, no matter whether trying to proxy to a HTTP/1 and HTTP/2 or just HTTP/1 compatible upstream, if I try to proxy it with this client I get this error:
This led me to this issue. I added the (Though perhaps I can tolerate the restriction that if the request is HTTP/2, the upstream must be https. I'll need to do more testing to see if everything works with this restriction). Next I tried to build the client with http2 only: pub fn build_http_client() -> HyperClient {
Client::builder(TokioExecutor::new())
.http2_only(true)
.build::<_, axum::body::Body>(hyper_tls::HttpsConnector::new()) After this, HTTP/2 requests to upstreams that support HTTP/2 work, but obviously this doesn't work with HTTP/1 upstreams, nor can I proxy HTTP/1 requests anymore. At this point I'm at a bit of a loss. There is another option entirely, in which we'd run two HTTP gateways in Cloud Run, one that can handle HTTP/2 directly and one where Cloud Run downgrades requests to HTTP/1 for us. We then configure routing rules in the GLB that makes it that requests for upstreams that we know can handle HTTP/2 go to the HTTP/2 gateway, otherwise they go to the HTTP/1 gateway. But I'd love to get this all to work with a single gateway. So in short, is it possible for me to configure a client / multiple clients / set flags on a per request basis such that if we receive a HTTP/2 request, and I know the upstream can't handle HTTP/2, it gets proxied upstream as HTTP/1? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Can you change the version, like |
Beta Was this translation helpful? Give feedback.
Can you change the version, like
*req.version_mut() = Version::HTTP_11
, before passing it on the to client?