1use crate::config::{Backend, UpstreamHttpVersion};
6use crate::vendored::hyper_ext::{empty_body, IncomingBodyExt, ProxyBody};
7use crate::vendored::types::ProxyError;
8use base64::Engine;
9use hyper::body::Incoming;
10use hyper::header::{CONNECTION, HOST, UPGRADE};
11use hyper::{Method, Request, Response, StatusCode, Version};
12use hyper_util::client::legacy::Client;
13use hyper_util::rt::TokioExecutor as HyperTokioExecutor;
14use hyper_util::rt::TokioIo;
15use rand::RngCore;
16use std::time::Duration;
17use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
18use tokio::net::TcpStream;
19use tracing::debug;
20
21type HttpConnector = hyper_util::client::legacy::connect::HttpConnector;
23
24pub struct ForwarderClient {
26 http_client: Client<HttpConnector, ProxyBody>,
28 http2_client: Client<HttpConnector, ProxyBody>,
34 timeout: Duration,
36}
37
38impl ForwarderClient {
39 pub fn new(timeout: Duration) -> Self {
41 let mut http_connector = HttpConnector::new();
42 http_connector.set_nodelay(true);
45 let http_client = Client::builder(HyperTokioExecutor::new())
46 .pool_idle_timeout(Duration::from_secs(30))
47 .pool_max_idle_per_host(32)
48 .build(http_connector);
49
50 let mut http2_connector = HttpConnector::new();
51 http2_connector.set_nodelay(true);
52 let http2_client = Client::builder(HyperTokioExecutor::new())
53 .pool_idle_timeout(Duration::from_secs(30))
54 .http2_only(true)
55 .build(http2_connector);
56
57 Self {
58 http_client,
59 http2_client,
60 timeout,
61 }
62 }
63
64 pub async fn forward(
66 &self,
67 backend: &Backend,
68 mut request: Request<ProxyBody>,
69 ) -> Result<Response<Incoming>, ProxyError> {
70 let uri = request.uri();
72 let path_and_query = uri.path_and_query().map(|pq| pq.as_str()).unwrap_or("/");
73
74 let upstream_uri = format!("{}://{}{}", backend.scheme, backend.address, path_and_query);
75
76 *request.uri_mut() = upstream_uri
78 .parse()
79 .map_err(|e| ProxyError::Request(format!("Invalid upstream URI: {}", e)))?;
80
81 let (client, version) = match backend.http_version {
86 UpstreamHttpVersion::H2c => (&self.http2_client, Version::HTTP_2),
87 UpstreamHttpVersion::Http11 => (&self.http_client, Version::HTTP_11),
88 };
89 *request.version_mut() = version;
90
91 let response = tokio::time::timeout(self.timeout, client.request(request))
93 .await
94 .map_err(|_| ProxyError::Timeout)?
95 .map_err(|e| ProxyError::Connection(e.to_string()))?;
96
97 Ok(response)
98 }
99
100 pub async fn forward_upgrade(
111 &self,
112 backend: &Backend,
113 mut request: Request<ProxyBody>,
114 ) -> Result<Response<ProxyBody>, ProxyError> {
115 let client_upgrade = hyper::upgrade::on(&mut request);
118
119 let path_and_query = request
121 .uri()
122 .path_and_query()
123 .map(|pq| pq.as_str().to_string())
124 .unwrap_or_else(|| "/".to_string());
125 *request.uri_mut() = path_and_query
126 .parse()
127 .map_err(|e| ProxyError::Request(format!("Invalid upstream URI: {}", e)))?;
128 *request.version_mut() = Version::HTTP_11;
129
130 let stream = tokio::time::timeout(self.timeout, TcpStream::connect(&backend.address))
131 .await
132 .map_err(|_| ProxyError::Timeout)?
133 .map_err(|e| ProxyError::Connection(e.to_string()))?;
134
135 if let Err(e) = stream.set_nodelay(true) {
142 debug!("Could not set TCP_NODELAY on the upstream socket: {}", e);
143 }
144
145 let (mut sender, conn) = hyper::client::conn::http1::handshake(TokioIo::new(stream))
146 .await
147 .map_err(|e| ProxyError::Connection(e.to_string()))?;
148
149 tokio::spawn(async move {
152 if let Err(e) = conn.with_upgrades().await {
153 debug!("Upstream upgrade connection error: {}", e);
154 }
155 });
156
157 let mut response = tokio::time::timeout(self.timeout, sender.send_request(request))
158 .await
159 .map_err(|_| ProxyError::Timeout)?
160 .map_err(|e| ProxyError::Connection(e.to_string()))?;
161
162 if response.status() != StatusCode::SWITCHING_PROTOCOLS {
164 let (parts, body) = response.into_parts();
165 return Ok(Response::from_parts(parts, body.boxed_body()));
166 }
167
168 let upstream_upgrade = hyper::upgrade::on(&mut response);
169
170 tokio::spawn(async move {
173 match tokio::try_join!(client_upgrade, upstream_upgrade) {
174 Ok((client_io, upstream_io)) => {
175 Self::splice(TokioIo::new(client_io), TokioIo::new(upstream_io)).await
176 }
177 Err(e) => debug!("Upgrade handshake failed: {}", e),
178 }
179 });
180
181 let (parts, _) = response.into_parts();
183 Ok(Response::from_parts(parts, empty_body()))
184 }
185
186 pub async fn forward_h2_websocket(
206 &self,
207 backend: &Backend,
208 mut request: Request<ProxyBody>,
209 protocol: &str,
210 ) -> Result<Response<ProxyBody>, ProxyError> {
211 let client_upgrade = hyper::upgrade::on(&mut request);
212
213 let path_and_query = request
214 .uri()
215 .path_and_query()
216 .map(|pq| pq.as_str().to_string())
217 .unwrap_or_else(|| "/".to_string());
218
219 let mut nonce = [0u8; 16];
221 rand::rng().fill_bytes(&mut nonce);
222 let websocket_key = base64::engine::general_purpose::STANDARD.encode(nonce);
223
224 let mut builder = Request::builder()
225 .method(Method::GET)
226 .uri(&path_and_query)
227 .version(Version::HTTP_11)
228 .header(HOST, &backend.address)
229 .header(UPGRADE, protocol)
230 .header(CONNECTION, "upgrade")
231 .header("sec-websocket-version", "13")
232 .header("sec-websocket-key", &websocket_key);
233
234 const REWRITTEN: [&str; 5] = [
237 "host",
238 "upgrade",
239 "connection",
240 "sec-websocket-key",
241 "sec-websocket-version",
242 ];
243 for (name, value) in request.headers() {
244 if !REWRITTEN.contains(&name.as_str()) {
245 builder = builder.header(name, value);
246 }
247 }
248
249 let upstream_request = builder
250 .body(empty_body())
251 .map_err(|e| ProxyError::Request(format!("could not build upgrade request: {}", e)))?;
252
253 let stream = tokio::time::timeout(self.timeout, TcpStream::connect(&backend.address))
254 .await
255 .map_err(|_| ProxyError::Timeout)?
256 .map_err(|e| ProxyError::Connection(e.to_string()))?;
257 if let Err(e) = stream.set_nodelay(true) {
258 debug!("Could not set TCP_NODELAY on the upstream socket: {}", e);
259 }
260
261 let (mut sender, conn) = hyper::client::conn::http1::handshake(TokioIo::new(stream))
262 .await
263 .map_err(|e| ProxyError::Connection(e.to_string()))?;
264 tokio::spawn(async move {
265 if let Err(e) = conn.with_upgrades().await {
266 debug!("Upstream upgrade connection error: {}", e);
267 }
268 });
269
270 let mut response =
271 tokio::time::timeout(self.timeout, sender.send_request(upstream_request))
272 .await
273 .map_err(|_| ProxyError::Timeout)?
274 .map_err(|e| ProxyError::Connection(e.to_string()))?;
275
276 if response.status() != StatusCode::SWITCHING_PROTOCOLS {
277 let status = response.status();
280 let mut out = Response::builder().status(status);
281 for (name, value) in response.headers() {
282 if name != UPGRADE && name != CONNECTION {
283 out = out.header(name, value);
284 }
285 }
286 return out
287 .body(empty_body())
288 .map_err(|e| ProxyError::Response(e.to_string()));
289 }
290
291 let upstream_upgrade = hyper::upgrade::on(&mut response);
292
293 tokio::spawn(async move {
294 match tokio::try_join!(client_upgrade, upstream_upgrade) {
295 Ok((client_io, upstream_io)) => {
296 Self::splice(TokioIo::new(client_io), TokioIo::new(upstream_io)).await
297 }
298 Err(e) => debug!("RFC 8441 upgrade handshake failed: {}", e),
299 }
300 });
301
302 let mut out = Response::builder().status(StatusCode::OK);
305 for name in ["sec-websocket-protocol", "sec-websocket-extensions"] {
306 if let Some(value) = response.headers().get(name) {
307 out = out.header(name, value);
308 }
309 }
310 out.body(empty_body())
311 .map_err(|e| ProxyError::Response(e.to_string()))
312 }
313
314 async fn splice<A, B>(client_io: A, upstream_io: B)
328 where
329 A: AsyncRead + AsyncWrite + Unpin,
330 B: AsyncRead + AsyncWrite + Unpin,
331 {
332 let (mut client_read, mut client_write) = tokio::io::split(client_io);
333 let (mut upstream_read, mut upstream_write) = tokio::io::split(upstream_io);
334
335 let to_upstream = async {
336 let result = tokio::io::copy(&mut client_read, &mut upstream_write).await;
337 let _ = upstream_write.shutdown().await;
340 result
341 };
342
343 let to_client = async {
344 let result = tokio::io::copy(&mut upstream_read, &mut client_write).await;
345 let _ = client_write.shutdown().await;
346 result
347 };
348
349 let (up, down) = tokio::join!(to_upstream, to_client);
350
351 match (up, down) {
352 (Ok(up), Ok(down)) => {
353 debug!("Tunnel closed: {} bytes up, {} bytes down", up, down)
354 }
355 (up, down) => {
356 debug!("Tunnel closed with errors: up={:?} down={:?}", up, down)
359 }
360 }
361 }
362}
363
364impl Default for ForwarderClient {
365 fn default() -> Self {
366 Self::new(Duration::from_secs(30))
367 }
368}