From 02c333905f0f0a6e536e62d80b6401319c5437ad Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Wed, 9 Aug 2023 02:13:04 +0900 Subject: [PATCH] feat: changed options for http version of requests to upstream app servers --- TODO.md | 1 + config-example.toml | 2 +- quinn | 2 +- rpxy-lib/src/error.rs | 26 +++++++++++++------------- rpxy-lib/src/handler/forwarder.rs | 25 ++++++++++++++++++------- s2n-quic | 2 +- 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/TODO.md b/TODO.md index bf783c7..a069ecc 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,6 @@ # TODO List +- [Try in v0.5.1 or 0.6.0] Fix strategy for `h2c` requests on forwarded requests upstream. This needs to update forwarder definition. Also, maybe forwarder would have a cache corresponding to the following task. - [Try in v0.6.0] **Cache option for the response with `Cache-Control: public` header directive ([#55](https://github.com/junkurihara/rust-rpxy/issues/55))** - Improvement of path matcher - More flexible option for rewriting path diff --git a/config-example.toml b/config-example.toml index 3d90761..561ebc2 100644 --- a/config-example.toml +++ b/config-example.toml @@ -78,7 +78,7 @@ load_balance = "random" # or "round_robin" or "sticky" (sticky session) or "none upstream_options = [ "override_host", "upgrade_insecure_requests", - "convert_https_to_11", + "force_http11_upstream", ] ###################################################################### diff --git a/quinn b/quinn index 70e14b5..8076ffe 160000 --- a/quinn +++ b/quinn @@ -1 +1 @@ -Subproject commit 70e14b5c26b45ee1e3d5dd64b2a184e2d6376880 +Subproject commit 8076ffe94d38813ce0220af9d3438e7bfb5e8429 diff --git a/rpxy-lib/src/error.rs b/rpxy-lib/src/error.rs index dd88a9a..da56dac 100644 --- a/rpxy-lib/src/error.rs +++ b/rpxy-lib/src/error.rs @@ -7,13 +7,13 @@ pub type Result = std::result::Result; /// Describes things that can go wrong in the Rpxy #[derive(Debug, Error)] pub enum RpxyError { - #[error("Proxy build error")] + #[error("Proxy build error: {0}")] ProxyBuild(#[from] crate::proxy::ProxyBuilderError), - #[error("Backend build error")] + #[error("Backend build error: {0}")] BackendBuild(#[from] crate::backend::BackendBuilderError), - #[error("MessageHandler build error")] + #[error("MessageHandler build error: {0}")] HandlerBuild(#[from] crate::handler::HttpMessageHandlerBuilderError), #[error("Config builder error: {0}")] @@ -32,40 +32,40 @@ pub enum RpxyError { #[error("LoadBalance Layer Error: {0}")] LoadBalance(String), - #[error("I/O Error")] + #[error("I/O Error: {0}")] Io(#[from] io::Error), // #[error("Toml Deserialization Error")] // TomlDe(#[from] toml::de::Error), #[cfg(feature = "http3-quinn")] - #[error("Quic Connection Error")] + #[error("Quic Connection Error [quinn]: {0}")] QuicConn(#[from] quinn::ConnectionError), #[cfg(feature = "http3-s2n")] - #[error("Quic Connection Error [s2n-quic]")] + #[error("Quic Connection Error [s2n-quic]: {0}")] QUicConn(#[from] s2n_quic::connection::Error), #[cfg(feature = "http3-quinn")] - #[error("H3 Error")] + #[error("H3 Error [quinn]: {0}")] H3(#[from] h3::Error), #[cfg(feature = "http3-s2n")] - #[error("H3 Error [s2n-quic]")] + #[error("H3 Error [s2n-quic]: {0}")] H3(#[from] s2n_quic_h3::h3::Error), - #[error("rustls Connection Error")] + #[error("rustls Connection Error: {0}")] Rustls(#[from] rustls::Error), - #[error("Hyper Error")] + #[error("Hyper Error: {0}")] Hyper(#[from] hyper::Error), - #[error("Hyper Http Error")] + #[error("Hyper Http Error: {0}")] HyperHttp(#[from] hyper::http::Error), - #[error("Hyper Http HeaderValue Error")] + #[error("Hyper Http HeaderValue Error: {0}")] HyperHeaderValue(#[from] hyper::header::InvalidHeaderValue), - #[error("Hyper Http HeaderName Error")] + #[error("Hyper Http HeaderName Error: {0}")] HyperHeaderName(#[from] hyper::header::InvalidHeaderName), #[error(transparent)] diff --git a/rpxy-lib/src/handler/forwarder.rs b/rpxy-lib/src/handler/forwarder.rs index bbcebca..f1ba5e3 100644 --- a/rpxy-lib/src/handler/forwarder.rs +++ b/rpxy-lib/src/handler/forwarder.rs @@ -4,6 +4,7 @@ use derive_builder::Builder; use hyper::{ body::{Body, HttpBody}, client::{connect::Connect, HttpConnector}, + http::Version, Client, Request, Response, }; use hyper_rustls::HttpsConnector; @@ -21,24 +22,28 @@ pub struct Forwarder where C: Connect + Clone + Sync + Send + 'static, { - // TODO: need `h2c` or http/2-only client separately + // TODO: maybe this forwarder definition is suitable for cache handling. inner: Client, + inner_h2: Client, // `h2c` or http/2-only client is defined separately } #[async_trait] impl ForwardRequest for Forwarder where - B: HttpBody + Send + 'static, + B: HttpBody + Send + Sync + 'static, B::Data: Send, B::Error: Into>, C: Connect + Clone + Sync + Send + 'static, { type Error = RpxyError; async fn request(&self, req: Request) -> Result, Self::Error> { - // TODO: - // TODO: Implement here a client that handles `h2c` requests - // TODO: - self.inner.request(req).await.map_err(RpxyError::Hyper) + // TODO: This 'match' condition is always evaluated at every 'request' invocation. So, it is inefficient. + // Needs to be reconsidered. Currently, this is a kind of work around. + // This possibly relates to https://github.com/hyperium/hyper/issues/2417. + match req.version() { + Version::HTTP_2 => self.inner_h2.request(req).await.map_err(RpxyError::Hyper), // handles `h2c` requests + _ => self.inner.request(req).await.map_err(RpxyError::Hyper), + } } } @@ -51,8 +56,14 @@ impl Forwarder, Body> { .enable_http1() .enable_http2() .build(); + let connector_h2 = hyper_rustls::HttpsConnectorBuilder::new() + .with_webpki_roots() + .https_or_http() + .enable_http1() + .build(); let inner = Client::builder().build::<_, Body>(connector); - Self { inner } + let inner_h2 = Client::builder().http2_only(true).build::<_, Body>(connector_h2); + Self { inner, inner_h2 } } } diff --git a/s2n-quic b/s2n-quic index 8ef0a6b..1ff2cd2 160000 --- a/s2n-quic +++ b/s2n-quic @@ -1 +1 @@ -Subproject commit 8ef0a6b66a856dc9f34ce18159c617ac29154cc7 +Subproject commit 1ff2cd230fdf46596fe77830966857c438a8b31a