diff --git a/Cargo.lock b/Cargo.lock index 27301b84..342d58f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3298,7 +3298,7 @@ dependencies = [ [[package]] name = "shadowsocks-rust" -version = "1.21.1" +version = "1.21.2" dependencies = [ "base64 0.22.1", "build-time", @@ -3339,7 +3339,7 @@ dependencies = [ [[package]] name = "shadowsocks-service" -version = "1.21.1" +version = "1.21.2" dependencies = [ "arc-swap", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 748cb7b7..1002e194 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shadowsocks-rust" -version = "1.21.1" +version = "1.21.2" authors = ["Shadowsocks Contributors"] description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls." repository = "https://github.com/shadowsocks/shadowsocks-rust" @@ -248,7 +248,7 @@ jemallocator = { version = "0.5", optional = true } snmalloc-rs = { version = "0.3", optional = true } rpmalloc = { version = "0.2", optional = true } -shadowsocks-service = { version = "1.21.1", path = "./crates/shadowsocks-service" } +shadowsocks-service = { version = "1.21.2", path = "./crates/shadowsocks-service" } windows-service = { version = "0.7", optional = true } diff --git a/crates/shadowsocks-service/Cargo.toml b/crates/shadowsocks-service/Cargo.toml index 76f51bd3..9b1771b3 100644 --- a/crates/shadowsocks-service/Cargo.toml +++ b/crates/shadowsocks-service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shadowsocks-service" -version = "1.21.1" +version = "1.21.2" authors = ["Shadowsocks Contributors"] description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls." repository = "https://github.com/shadowsocks/shadowsocks-rust" diff --git a/crates/shadowsocks-service/src/local/http/http_client.rs b/crates/shadowsocks-service/src/local/http/http_client.rs index e15113e4..301b7b89 100644 --- a/crates/shadowsocks-service/src/local/http/http_client.rs +++ b/crates/shadowsocks-service/src/local/http/http_client.rs @@ -1,6 +1,7 @@ //! HTTP Client use std::{ + borrow::Cow, collections::VecDeque, fmt::Debug, future::Future, @@ -11,7 +12,8 @@ use std::{ time::{Duration, Instant}, }; -use http::Uri; +use bson::doc; +use http::{header::InvalidHeaderValue, HeaderValue, Method as HttpMethod, Uri, Version as HttpVersion}; use hyper::{ body::{self, Body}, client::conn::{http1, http2}, @@ -47,6 +49,9 @@ pub enum HttpClientError { /// Errors from http #[error("{0}")] Http(#[from] http::Error), + /// Errors from http header + #[error("{0}")] + InvalidHeaderValue(#[from] InvalidHeaderValue), } #[derive(Clone, Debug)] @@ -137,7 +142,7 @@ where pub async fn send_request( &self, context: Arc, - req: Request, + mut req: Request, balancer: Option<&PingBalancer>, ) -> Result, HttpClientError> { let host = match host_addr(req.uri()) { @@ -145,12 +150,24 @@ where None => panic!("URI missing host: {}", req.uri()), }; + // Set Host header if it was missing in the Request + { + let headers = req.headers_mut(); + if !headers.contains_key("Host") { + let host_value = match host { + Address::DomainNameAddress(ref domain, _) => HeaderValue::from_str(domain)?, + Address::SocketAddress(ref saddr) => HeaderValue::from_str(saddr.ip().to_string().as_str())?, + }; + headers.insert("Host", host_value); + } + } + // 1. Check if there is an available client // // FIXME: If the cached connection is closed unexpectedly, this request will fail immediately. if let Some(c) = self.get_cached_connection(&host).await { trace!("HTTP client for host: {} taken from cache", host); - return self.send_request_conn(host, c, req).await + return self.send_request_conn(host, c, req).await; } // 2. If no. Make a new connection @@ -159,13 +176,12 @@ where None => &Scheme::HTTP, }; - let domain = req - .uri() - .host() - .unwrap() - .trim_start_matches('[') - .trim_start_matches(']'); - let c = match HttpConnection::connect(context.clone(), scheme, host.clone(), domain, balancer).await { + let domain = match host { + Address::DomainNameAddress(ref domain, _) => Cow::Borrowed(domain.as_str()), + Address::SocketAddress(ref saddr) => Cow::Owned(saddr.ip().to_string()), + }; + + let c = match HttpConnection::connect(context.clone(), scheme, host.clone(), &domain, balancer).await { Ok(c) => c, Err(err) => { error!("failed to connect to host: {}, error: {}", host, err); @@ -196,19 +212,8 @@ where &self, host: Address, mut c: HttpConnection, - mut req: Request, + req: Request, ) -> Result, HttpClientError> { - // Remove Scheme, Host part from URI - if req.uri().scheme().is_some() || req.uri().authority().is_some() { - let mut builder = Uri::builder(); - if let Some(path_and_query) = req.uri().path_and_query() { - builder = builder.path_and_query(path_and_query.as_str()); - } else { - builder = builder.path_and_query("/"); - } - *(req.uri_mut()) = builder.build()?; - } - trace!("HTTP making request to host: {}, request: {:?}", host, req); let response = c.send_request(req).await?; trace!("HTTP received response from host: {}, response: {:?}", host, response); @@ -351,10 +356,45 @@ where } #[inline] - pub async fn send_request(&mut self, req: Request) -> hyper::Result> { + pub async fn send_request(&mut self, mut req: Request) -> Result, HttpClientError> { match self { - HttpConnection::Http1(r) => r.send_request(req).await, - HttpConnection::Http2(r) => r.send_request(req).await, + HttpConnection::Http1(r) => { + if !matches!( + req.version(), + HttpVersion::HTTP_09 | HttpVersion::HTTP_10 | HttpVersion::HTTP_11 + ) { + trace!( + "HTTP client changed Request.version to HTTP/1.1 from {:?}", + req.version() + ); + + *req.version_mut() = HttpVersion::HTTP_11; + } + + // Remove Scheme, Host part from URI + if req.method() != HttpMethod::CONNECT + && (req.uri().scheme().is_some() || req.uri().authority().is_some()) + { + let mut builder = Uri::builder(); + if let Some(path_and_query) = req.uri().path_and_query() { + builder = builder.path_and_query(path_and_query.as_str()); + } else { + builder = builder.path_and_query("/"); + } + *(req.uri_mut()) = builder.build()?; + } + + r.send_request(req).await.map_err(Into::into) + } + HttpConnection::Http2(r) => { + if !matches!(req.version(), HttpVersion::HTTP_2) { + trace!("HTTP client changed Request.version to HTTP/2 from {:?}", req.version()); + + *req.version_mut() = HttpVersion::HTTP_2; + } + + r.send_request(req).await.map_err(Into::into) + } } } diff --git a/crates/shadowsocks-service/src/local/http/http_service.rs b/crates/shadowsocks-service/src/local/http/http_service.rs index 8d4ab982..cba174fd 100644 --- a/crates/shadowsocks-service/src/local/http/http_service.rs +++ b/crates/shadowsocks-service/src/local/http/http_service.rs @@ -162,6 +162,10 @@ impl HttpService { error!("failed to make request to host: {}, error: {}", host, err); return make_bad_request(); } + Err(HttpClientError::InvalidHeaderValue(err)) => { + error!("failed to make request to host: {}, error: {}", host, err); + return make_bad_request(); + } }; trace!("received {} <- {} {:?}", self.peer_addr, host, res);