diff --git a/crates/yaak-proxy/src/request.rs b/crates/yaak-proxy/src/request.rs index a67b4d6d1..5bcb597ca 100644 --- a/crates/yaak-proxy/src/request.rs +++ b/crates/yaak-proxy/src/request.rs @@ -412,26 +412,26 @@ async fn forward_https( let (mut parts, body) = req.into_parts(); let uri = match &upstream { - Upstream::H2(_) => url.parse::(), + // `Host` moves into `:authority`. Google's servers reset any stream that has both, even + // when they match. + Upstream::H2(_) => match parts.headers.remove(HOST) { + Some(host) => match host.to_str() { + Ok(host) => format!("https://{host}{path}").parse::(), + Err(e) => return Ok(flow.fail(e.to_string())), + }, + None => url.parse::(), + }, Upstream::H1(_) => path.parse::(), }; parts.uri = match uri { Ok(uri) => uri, Err(e) => return Ok(flow.fail(e.to_string())), }; - match &upstream { - // The URL carries `:authority`. Google's servers reset any stream that also has `Host`, - // even a matching one. - Upstream::H2(_) => { - parts.headers.remove(HOST); - } - Upstream::H1(_) => { - if !parts.headers.contains_key(HOST) - && let Ok(host) = target.authority.parse() - { - parts.headers.insert(HOST, host); - } - } + if matches!(upstream, Upstream::H1(_)) + && !parts.headers.contains_key(HOST) + && let Ok(host) = target.authority.parse() + { + parts.headers.insert(HOST, host); } let outgoing = Request::from_parts(parts, flow.capture_request(body)); diff --git a/crates/yaak-proxy/tests/proxy.rs b/crates/yaak-proxy/tests/proxy.rs index ec6e76b2b..3ed587fe6 100644 --- a/crates/yaak-proxy/tests/proxy.rs +++ b/crates/yaak-proxy/tests/proxy.rs @@ -441,6 +441,17 @@ async fn https_h2_upstream_gets_authority_without_host() { assert!(captured_host, "the captured request keeps Host"); } +/// A `Host` other than the CONNECT target still names the virtual host upstream. +#[tokio::test(flavor = "multi_thread")] +async fn https_h2_upstream_gets_the_clients_host_as_authority() { + let h = Harness::start(1024); + let upstream = spawn_upstream(Some(h.pki.server.clone())).await; + let url = format!("https://localhost:{}/authority", upstream.port()); + + let resp = h.client().get(&url).header("host", "yaak.test:8443").send().await.unwrap(); + assert_eq!(resp.text().await.unwrap(), "authority=yaak.test:8443 host=none"); +} + #[tokio::test(flavor = "multi_thread")] async fn https_not_found() { let h = Harness::start(1024);