From 6e28e79cbd2871011e9af0d8f1c0de1022f5e1ea Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 17 Dec 2024 16:37:21 +0100 Subject: [PATCH] fix(client): remove host header on http2 / http3 --- client/src/h2/proto/dispatcher.rs | 5 ++++- client/src/h3/proto/dispatcher.rs | 12 ++++++----- test/tests/h2.rs | 35 +++++++++++++++++++++++++++++++ test/tests/h3.rs | 35 +++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/client/src/h2/proto/dispatcher.rs b/client/src/h2/proto/dispatcher.rs index c8c2c1db8..f1ff16caf 100644 --- a/client/src/h2/proto/dispatcher.rs +++ b/client/src/h2/proto/dispatcher.rs @@ -7,7 +7,7 @@ use xitca_http::{ http::{ self, const_header_name::PROTOCOL, - header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE}, + header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, HOST, TRANSFER_ENCODING, UPGRADE}, method::Method, }, }; @@ -61,6 +61,9 @@ where req.headers_mut().remove(TRANSFER_ENCODING); req.headers_mut().remove(UPGRADE); + // remove host header if present, some web server may send 400 bad request if host header is present (like nginx) + req.headers_mut().remove(HOST); + if !req.headers().contains_key(DATE) { let date = date.with_date(HeaderValue::from_bytes).unwrap(); req.headers_mut().append(DATE, date); diff --git a/client/src/h3/proto/dispatcher.rs b/client/src/h3/proto/dispatcher.rs index c09655d3e..2cbc16893 100644 --- a/client/src/h3/proto/dispatcher.rs +++ b/client/src/h3/proto/dispatcher.rs @@ -1,19 +1,18 @@ use core::{future::poll_fn, net::SocketAddr, pin::pin}; -use ::h3_quinn::quinn::Endpoint; -use futures_core::stream::Stream; -use xitca_http::date::DateTime; - use crate::{ body::{BodyError, BodySize, ResponseBody}, bytes::{Buf, Bytes}, date::DateTimeHandle, h3::{Connection, Error}, http::{ - header::{HeaderValue, CONTENT_LENGTH, DATE}, + header::{HeaderValue, CONTENT_LENGTH, DATE, HOST}, Method, Request, Response, }, }; +use ::h3_quinn::quinn::Endpoint; +use futures_core::stream::Stream; +use xitca_http::date::DateTime; pub(crate) async fn send( stream: &mut Connection, @@ -49,6 +48,9 @@ where } }; + // remove host header if present, some web server may send 400 bad request if host header is present. + req.headers_mut().remove(HOST); + if !req.headers().contains_key(DATE) { let date = date.with_date(HeaderValue::from_bytes).unwrap(); req.headers_mut().append(DATE, date); diff --git a/test/tests/h2.rs b/test/tests/h2.rs index 527f507c0..2e66b2fe4 100644 --- a/test/tests/h2.rs +++ b/test/tests/h2.rs @@ -34,6 +34,32 @@ async fn h2_get() -> Result<(), Error> { Ok(()) } +#[tokio::test] +async fn h2_no_host_header() -> Result<(), Error> { + let mut handle = test_h2_server(fn_service(handle))?; + + let server_url = format!("https://{}/host", handle.ip_port_string()); + + let c = Client::new(); + + for _ in 0..3 { + let mut req = c.get(&server_url).version(Version::HTTP_2); + req.headers_mut().insert(header::HOST, "localhost".parse().unwrap()); + + let mut res = req.send().await?; + assert_eq!(res.status().as_u16(), 200); + assert!(!res.can_close_connection()); + let body = res.string().await?; + assert_eq!("", body); + } + + handle.try_handle()?.stop(false); + + handle.await?; + + Ok(()) +} + #[tokio::test] async fn h2_post() -> Result<(), Error> { let mut handle = test_h2_server(fn_service(handle))?; @@ -156,6 +182,15 @@ async fn handle(req: Request>) -> Result Ok(Response::new(Bytes::from("GET Response").into())), + (&Method::GET, "/host") => Ok(Response::new( + Bytes::from( + req.headers() + .get(header::HOST) + .map(|v| v.to_str().unwrap().to_string()) + .unwrap_or_default(), + ) + .into(), + )), (&Method::CONNECT, "/") => { let (_, mut body) = req.into_parts(); Ok(Response::new(ResponseBody::box_stream(async_stream::stream! { diff --git a/test/tests/h3.rs b/test/tests/h3.rs index 4f7a72b8c..0154bc426 100644 --- a/test/tests/h3.rs +++ b/test/tests/h3.rs @@ -31,6 +31,32 @@ async fn h3_get() -> Result<(), Error> { Ok(()) } +#[tokio::test] +async fn h3_no_host_header() -> Result<(), Error> { + let mut handle = test_h3_server(fn_service(handle))?; + + let server_url = format!("https://{}/host", handle.ip_port_string()); + + let c = Client::new(); + + for _ in 0..3 { + let mut req = c.get(&server_url).version(Version::HTTP_3); + req.headers_mut().insert(header::HOST, "localhost".parse().unwrap()); + + let mut res = req.send().await?; + assert_eq!(res.status().as_u16(), 200); + assert!(!res.can_close_connection()); + let body = res.string().await?; + assert_eq!("", body); + } + + handle.try_handle()?.stop(false); + + handle.await?; + + Ok(()) +} + #[tokio::test] async fn h3_post() -> Result<(), Error> { let mut handle = test_h3_server(fn_service(handle))?; @@ -64,6 +90,15 @@ async fn handle(req: Request>) -> Result Ok(Response::new(Bytes::from("GET Response").into())), + (&Method::GET, "/host") => Ok(Response::new( + Bytes::from( + req.headers() + .get(header::HOST) + .map(|v| v.to_str().unwrap().to_string()) + .unwrap_or_default(), + ) + .into(), + )), (&Method::POST, "/") => { let (parts, mut body) = req.into_parts();