aboutsummaryrefslogtreecommitdiffhomepage
path: root/pingora-proxy
diff options
context:
space:
mode:
authorAndrew Hauck <[email protected]>2024-06-10 10:23:48 -0700
committerMatthew (mbg) <[email protected]>2024-06-21 09:54:09 -0700
commita1f1ad8a4326c66e794aba494f13d62b132fdd25 (patch)
tree42edc8a11d4cc0a5f28850546c2fe1d47e16eeea /pingora-proxy
parent2d30077683f2c18e22f36b2872bcc7eaca85c505 (diff)
downloadpingora-a1f1ad8a4326c66e794aba494f13d62b132fdd25.tar.gz
pingora-a1f1ad8a4326c66e794aba494f13d62b132fdd25.zip
h2c support
Diffstat (limited to 'pingora-proxy')
-rw-r--r--pingora-proxy/src/lib.rs8
-rw-r--r--pingora-proxy/src/proxy_h2.rs27
-rw-r--r--pingora-proxy/tests/test_basic.rs24
-rw-r--r--pingora-proxy/tests/utils/conf/origin/conf/nginx.conf2
-rw-r--r--pingora-proxy/tests/utils/server_utils.rs20
5 files changed, 69 insertions, 12 deletions
diff --git a/pingora-proxy/src/lib.rs b/pingora-proxy/src/lib.rs
index f306387..0add56a 100644
--- a/pingora-proxy/src/lib.rs
+++ b/pingora-proxy/src/lib.rs
@@ -52,7 +52,7 @@ use tokio::sync::{mpsc, Notify};
use tokio::time;
use pingora_cache::NoCacheReason;
-use pingora_core::apps::HttpServerApp;
+use pingora_core::apps::{HttpServerApp, HttpServerOptions};
use pingora_core::connectors::{http::Connector, ConnectorOptions};
use pingora_core::modules::http::compression::ResponseCompressionBuilder;
use pingora_core::modules::http::{HttpModuleCtx, HttpModules};
@@ -95,6 +95,7 @@ pub struct HttpProxy<SV> {
inner: SV, // TODO: name it better than inner
client_upstream: Connector,
shutdown: Notify,
+ pub server_options: Option<HttpServerOptions>,
pub downstream_modules: HttpModules,
}
@@ -104,6 +105,7 @@ impl<SV> HttpProxy<SV> {
inner,
client_upstream: Connector::new(Some(ConnectorOptions::from_server_conf(&conf))),
shutdown: Notify::new(),
+ server_options: None,
downstream_modules: HttpModules::new(),
}
}
@@ -725,6 +727,10 @@ where
// TODO: impl shutting down flag so that we don't need to read stack.is_shutting_down()
}
+ fn server_options(&self) -> Option<&HttpServerOptions> {
+ self.server_options.as_ref()
+ }
+
// TODO implement h2_options
}
diff --git a/pingora-proxy/src/proxy_h2.rs b/pingora-proxy/src/proxy_h2.rs
index 534564f..c213857 100644
--- a/pingora-proxy/src/proxy_h2.rs
+++ b/pingora-proxy/src/proxy_h2.rs
@@ -18,7 +18,11 @@ use crate::proxy_common::*;
use pingora_core::protocols::http::v2::client::{write_body, Http2Session};
// add scheme and authority as required by h2 lib
-fn update_h2_scheme_authority(header: &mut http::request::Parts, raw_host: &[u8]) -> Result<()> {
+fn update_h2_scheme_authority(
+ header: &mut http::request::Parts,
+ raw_host: &[u8],
+ tls: bool,
+) -> Result<()> {
let authority = if let Ok(s) = std::str::from_utf8(raw_host) {
if s.starts_with('[') {
// don't mess with ipv6 host
@@ -43,8 +47,9 @@ fn update_h2_scheme_authority(header: &mut http::request::Parts, raw_host: &[u8]
);
};
+ let scheme = if tls { "https" } else { "http" };
let uri = http::uri::Builder::new()
- .scheme("https")
+ .scheme(scheme)
.authority(authority)
.path_and_query(header.uri.path_and_query().as_ref().unwrap().as_str())
.build();
@@ -123,7 +128,7 @@ impl<SV> HttpProxy<SV> {
// H2 requires authority to be set, so copy that from H1 host if that is set
if let Some(host) = host {
- if let Err(e) = update_h2_scheme_authority(&mut req, host.as_bytes()) {
+ if let Err(e) = update_h2_scheme_authority(&mut req, host.as_bytes(), peer.is_tls()) {
return (false, Some(e));
}
}
@@ -619,14 +624,20 @@ fn test_update_authority() {
.unwrap()
.into_parts()
.0;
- update_h2_scheme_authority(&mut parts, b"example.com").unwrap();
+ update_h2_scheme_authority(&mut parts, b"example.com", true).unwrap();
assert_eq!("example.com", parts.uri.authority().unwrap());
- update_h2_scheme_authority(&mut parts, b"example.com:456").unwrap();
+ update_h2_scheme_authority(&mut parts, b"example.com:456", true).unwrap();
assert_eq!("example.com:456", parts.uri.authority().unwrap());
- update_h2_scheme_authority(&mut parts, b"example.com:").unwrap();
+ update_h2_scheme_authority(&mut parts, b"example.com:", true).unwrap();
assert_eq!("example.com:", parts.uri.authority().unwrap());
- update_h2_scheme_authority(&mut parts, b"example.com:123:345").unwrap();
+ update_h2_scheme_authority(&mut parts, b"example.com:123:345", true).unwrap();
assert_eq!("example.com:123", parts.uri.authority().unwrap());
- update_h2_scheme_authority(&mut parts, b"[::1]").unwrap();
+ update_h2_scheme_authority(&mut parts, b"[::1]", true).unwrap();
assert_eq!("[::1]", parts.uri.authority().unwrap());
+
+ // verify scheme
+ update_h2_scheme_authority(&mut parts, b"example.com", true).unwrap();
+ assert_eq!("https://example.com", parts.uri);
+ update_h2_scheme_authority(&mut parts, b"example.com", false).unwrap();
+ assert_eq!("http://example.com", parts.uri);
}
diff --git a/pingora-proxy/tests/test_basic.rs b/pingora-proxy/tests/test_basic.rs
index 4f11c2b..744f9b7 100644
--- a/pingora-proxy/tests/test_basic.rs
+++ b/pingora-proxy/tests/test_basic.rs
@@ -14,7 +14,7 @@
mod utils;
-use hyper::Client;
+use hyper::{body::HttpBody, header::HeaderValue, Body, Client};
use hyperlocal::{UnixClientExt, Uri};
use reqwest::{header, StatusCode};
@@ -144,6 +144,28 @@ async fn test_h2_to_h2() {
}
#[tokio::test]
+async fn test_h2c_to_h2c() {
+ init();
+
+ let client = hyper::client::Client::builder()
+ .http2_only(true)
+ .build_http();
+
+ let mut req = hyper::Request::builder()
+ .uri("http://127.0.0.1:6146")
+ .body(Body::empty())
+ .unwrap();
+ req.headers_mut()
+ .insert("x-h2", HeaderValue::from_bytes(b"true").unwrap());
+ let res = client.request(req).await.unwrap();
+ assert_eq!(res.status(), reqwest::StatusCode::OK);
+ assert_eq!(res.version(), reqwest::Version::HTTP_2);
+
+ let body = res.into_body().data().await.unwrap().unwrap();
+ assert_eq!(body.as_ref(), b"Hello World!\n");
+}
+
+#[tokio::test]
async fn test_h2_to_h2_host_override() {
init();
let client = reqwest::Client::builder()
diff --git a/pingora-proxy/tests/utils/conf/origin/conf/nginx.conf b/pingora-proxy/tests/utils/conf/origin/conf/nginx.conf
index 1309bf6..07e57e8 100644
--- a/pingora-proxy/tests/utils/conf/origin/conf/nginx.conf
+++ b/pingora-proxy/tests/utils/conf/origin/conf/nginx.conf
@@ -80,7 +80,7 @@ http {
}
server {
- listen 8000;
+ listen 8000 http2;
# 8001 is used for bad_lb test only to avoid unexpected connection reuse
listen 8001;
listen [::]:8000;
diff --git a/pingora-proxy/tests/utils/server_utils.rs b/pingora-proxy/tests/utils/server_utils.rs
index 84b2c12..7b20630 100644
--- a/pingora-proxy/tests/utils/server_utils.rs
+++ b/pingora-proxy/tests/utils/server_utils.rs
@@ -26,6 +26,7 @@ use pingora_cache::{
set_compression_dict_path, CacheMeta, CacheMetaDefaults, CachePhase, MemCache, NoCacheReason,
RespCacheable,
};
+use pingora_core::apps::{HttpServerApp, HttpServerOptions};
use pingora_core::modules::http::compression::ResponseCompression;
use pingora_core::protocols::{l4::socket::SocketAddr, Digest};
use pingora_core::server::configuration::Opt;
@@ -270,11 +271,18 @@ impl ProxyHttp for ExampleProxyHttp {
.headers
.get("x-port")
.map_or("8000", |v| v.to_str().unwrap());
- let peer = Box::new(HttpPeer::new(
+
+ let mut peer = Box::new(HttpPeer::new(
format!("127.0.0.1:{port}"),
false,
"".to_string(),
));
+
+ if session.get_header_bytes("x-h2") == b"true" {
+ // default is 1, 1
+ peer.options.set_http_version(2, 2);
+ }
+
Ok(peer)
}
@@ -502,6 +510,15 @@ fn test_main() {
proxy_service_http.add_tcp("0.0.0.0:6147");
proxy_service_http.add_uds("/tmp/pingora_proxy.sock", None);
+ let mut proxy_service_h2c =
+ pingora_proxy::http_proxy_service(&my_server.configuration, ExampleProxyHttp {});
+
+ let http_logic = proxy_service_h2c.app_logic_mut().unwrap();
+ let mut http_server_options = HttpServerOptions::default();
+ http_server_options.h2c = true;
+ http_logic.server_options = Some(http_server_options);
+ proxy_service_h2c.add_tcp("0.0.0.0:6146");
+
let mut proxy_service_https =
pingora_proxy::http_proxy_service(&my_server.configuration, ExampleProxyHttps {});
proxy_service_https.add_tcp("0.0.0.0:6149");
@@ -517,6 +534,7 @@ fn test_main() {
proxy_service_cache.add_tcp("0.0.0.0:6148");
let services: Vec<Box<dyn Service>> = vec![
+ Box::new(proxy_service_h2c),
Box::new(proxy_service_http),
Box::new(proxy_service_https),
Box::new(proxy_service_cache),