1use crate::config::{Backend, UpstreamHttpVersion};
6use crate::vendored::conn::InFlight;
7use crate::vendored::hyper_ext::{empty_body, IncomingBodyExt, ProxyBody};
8use crate::vendored::types::ProxyError;
9use base64::Engine;
10use hyper::body::Incoming;
11use hyper::header::{CONNECTION, HOST, UPGRADE};
12use hyper::{Method, Request, Response, StatusCode, Version};
13use hyper_util::client::legacy::Client;
14use hyper_util::rt::TokioExecutor as HyperTokioExecutor;
15use hyper_util::rt::TokioIo;
16use rand::RngCore;
17use std::time::Duration;
18use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
19use tokio::net::TcpStream;
20use tracing::debug;
21
22type HttpConnector = hyper_util::client::legacy::connect::HttpConnector;
24
25pub struct ForwarderClient {
27 http_client: Client<HttpConnector, ProxyBody>,
29 http2_client: Client<HttpConnector, ProxyBody>,
35 timeout: Duration,
37}
38
39impl ForwarderClient {
40 pub fn new(timeout: Duration) -> Self {
42 let mut http_connector = HttpConnector::new();
43 http_connector.set_nodelay(true);
46 let http_client = Client::builder(HyperTokioExecutor::new())
47 .pool_idle_timeout(Duration::from_secs(30))
48 .pool_max_idle_per_host(32)
49 .build(http_connector);
50
51 let mut http2_connector = HttpConnector::new();
52 http2_connector.set_nodelay(true);
53 let http2_client = Client::builder(HyperTokioExecutor::new())
54 .pool_idle_timeout(Duration::from_secs(30))
55 .http2_only(true)
56 .build(http2_connector);
57
58 Self {
59 http_client,
60 http2_client,
61 timeout,
62 }
63 }
64
65 pub async fn forward(
67 &self,
68 backend: &Backend,
69 mut request: Request<ProxyBody>,
70 ) -> Result<Response<Incoming>, ProxyError> {
71 let uri = request.uri();
73 let path_and_query = uri.path_and_query().map(|pq| pq.as_str()).unwrap_or("/");
74
75 let upstream_uri = format!("{}://{}{}", backend.scheme, backend.address, path_and_query);
76
77 *request.uri_mut() = upstream_uri
79 .parse()
80 .map_err(|e| ProxyError::Request(format!("Invalid upstream URI: {}", e)))?;
81
82 let (client, version) = match backend.http_version {
87 UpstreamHttpVersion::H2c => (&self.http2_client, Version::HTTP_2),
88 UpstreamHttpVersion::Http11 => (&self.http_client, Version::HTTP_11),
89 };
90 *request.version_mut() = version;
91
92 let response = tokio::time::timeout(self.timeout, client.request(request))
94 .await
95 .map_err(|_| ProxyError::Timeout)?
96 .map_err(|e| ProxyError::Connection(e.to_string()))?;
97
98 Ok(response)
99 }
100
101 pub async fn forward_upgrade(
116 &self,
117 backend: &Backend,
118 mut request: Request<ProxyBody>,
119 tunnel_idle: Duration,
120 ) -> Result<Response<ProxyBody>, ProxyError> {
121 let client_upgrade = hyper::upgrade::on(&mut request);
124 let in_flight = request.extensions_mut().remove::<InFlight>();
128
129 let path_and_query = request
131 .uri()
132 .path_and_query()
133 .map(|pq| pq.as_str().to_string())
134 .unwrap_or_else(|| "/".to_string());
135 *request.uri_mut() = path_and_query
136 .parse()
137 .map_err(|e| ProxyError::Request(format!("Invalid upstream URI: {}", e)))?;
138 *request.version_mut() = Version::HTTP_11;
139
140 let stream = tokio::time::timeout(self.timeout, TcpStream::connect(&backend.address))
141 .await
142 .map_err(|_| ProxyError::Timeout)?
143 .map_err(|e| ProxyError::Connection(e.to_string()))?;
144
145 if let Err(e) = stream.set_nodelay(true) {
152 debug!("Could not set TCP_NODELAY on the upstream socket: {}", e);
153 }
154
155 let (mut sender, conn) = hyper::client::conn::http1::handshake(TokioIo::new(stream))
156 .await
157 .map_err(|e| ProxyError::Connection(e.to_string()))?;
158
159 tokio::spawn(async move {
162 if let Err(e) = conn.with_upgrades().await {
163 debug!("Upstream upgrade connection error: {}", e);
164 }
165 });
166
167 let mut response = tokio::time::timeout(self.timeout, sender.send_request(request))
168 .await
169 .map_err(|_| ProxyError::Timeout)?
170 .map_err(|e| ProxyError::Connection(e.to_string()))?;
171
172 if response.status() != StatusCode::SWITCHING_PROTOCOLS {
174 let (parts, body) = response.into_parts();
175 return Ok(Response::from_parts(parts, body.boxed_body()));
176 }
177
178 let upstream_upgrade = hyper::upgrade::on(&mut response);
179
180 tokio::spawn(async move {
183 let _in_flight = in_flight;
184 match tokio::try_join!(client_upgrade, upstream_upgrade) {
185 Ok((client_io, upstream_io)) => {
186 Self::splice(
187 TokioIo::new(client_io),
188 TokioIo::new(upstream_io),
189 tunnel_idle,
190 )
191 .await
192 }
193 Err(e) => debug!("Upgrade handshake failed: {}", e),
194 }
195 });
196
197 let (parts, _) = response.into_parts();
199 Ok(Response::from_parts(parts, empty_body()))
200 }
201
202 pub async fn forward_h2_websocket(
225 &self,
226 backend: &Backend,
227 mut request: Request<ProxyBody>,
228 protocol: &str,
229 tunnel_idle: Duration,
230 ) -> Result<Response<ProxyBody>, ProxyError> {
231 let client_upgrade = hyper::upgrade::on(&mut request);
232 let in_flight = request.extensions_mut().remove::<InFlight>();
233
234 let path_and_query = request
235 .uri()
236 .path_and_query()
237 .map(|pq| pq.as_str().to_string())
238 .unwrap_or_else(|| "/".to_string());
239
240 let mut nonce = [0u8; 16];
242 rand::rng().fill_bytes(&mut nonce);
243 let websocket_key = base64::engine::general_purpose::STANDARD.encode(nonce);
244
245 let mut builder = Request::builder()
246 .method(Method::GET)
247 .uri(&path_and_query)
248 .version(Version::HTTP_11)
249 .header(HOST, &backend.address)
250 .header(UPGRADE, protocol)
251 .header(CONNECTION, "upgrade")
252 .header("sec-websocket-version", "13")
253 .header("sec-websocket-key", &websocket_key);
254
255 const REWRITTEN: [&str; 5] = [
258 "host",
259 "upgrade",
260 "connection",
261 "sec-websocket-key",
262 "sec-websocket-version",
263 ];
264 for (name, value) in request.headers() {
265 if !REWRITTEN.contains(&name.as_str()) {
266 builder = builder.header(name, value);
267 }
268 }
269
270 let upstream_request = builder
271 .body(empty_body())
272 .map_err(|e| ProxyError::Request(format!("could not build upgrade request: {}", e)))?;
273
274 let stream = tokio::time::timeout(self.timeout, TcpStream::connect(&backend.address))
275 .await
276 .map_err(|_| ProxyError::Timeout)?
277 .map_err(|e| ProxyError::Connection(e.to_string()))?;
278 if let Err(e) = stream.set_nodelay(true) {
279 debug!("Could not set TCP_NODELAY on the upstream socket: {}", e);
280 }
281
282 let (mut sender, conn) = hyper::client::conn::http1::handshake(TokioIo::new(stream))
283 .await
284 .map_err(|e| ProxyError::Connection(e.to_string()))?;
285 tokio::spawn(async move {
286 if let Err(e) = conn.with_upgrades().await {
287 debug!("Upstream upgrade connection error: {}", e);
288 }
289 });
290
291 let mut response =
292 tokio::time::timeout(self.timeout, sender.send_request(upstream_request))
293 .await
294 .map_err(|_| ProxyError::Timeout)?
295 .map_err(|e| ProxyError::Connection(e.to_string()))?;
296
297 if response.status() != StatusCode::SWITCHING_PROTOCOLS {
298 let status = response.status();
301 let mut out = Response::builder().status(status);
302 for (name, value) in response.headers() {
303 if name != UPGRADE && name != CONNECTION {
304 out = out.header(name, value);
305 }
306 }
307 return out
308 .body(empty_body())
309 .map_err(|e| ProxyError::Response(e.to_string()));
310 }
311
312 let upstream_upgrade = hyper::upgrade::on(&mut response);
313
314 tokio::spawn(async move {
315 let _in_flight = in_flight;
316 match tokio::try_join!(client_upgrade, upstream_upgrade) {
317 Ok((client_io, upstream_io)) => {
318 Self::splice(
319 TokioIo::new(client_io),
320 TokioIo::new(upstream_io),
321 tunnel_idle,
322 )
323 .await
324 }
325 Err(e) => debug!("RFC 8441 upgrade handshake failed: {}", e),
326 }
327 });
328
329 let mut out = Response::builder().status(StatusCode::OK);
332 for name in ["sec-websocket-protocol", "sec-websocket-extensions"] {
333 if let Some(value) = response.headers().get(name) {
334 out = out.header(name, value);
335 }
336 }
337 out.body(empty_body())
338 .map_err(|e| ProxyError::Response(e.to_string()))
339 }
340
341 async fn splice<A, B>(client_io: A, upstream_io: B, idle: Duration)
360 where
361 A: AsyncRead + AsyncWrite + Unpin,
362 B: AsyncRead + AsyncWrite + Unpin,
363 {
364 let (mut client_read, mut client_write) = tokio::io::split(client_io);
365 let (mut upstream_read, mut upstream_write) = tokio::io::split(upstream_io);
366 let last_activity = parking_lot::Mutex::new(tokio::time::Instant::now());
367
368 let to_upstream = async {
369 let result = relay(&mut client_read, &mut upstream_write, &last_activity).await;
370 let _ = upstream_write.shutdown().await;
373 result
374 };
375
376 let to_client = async {
377 let result = relay(&mut upstream_read, &mut client_write, &last_activity).await;
378 let _ = client_write.shutdown().await;
379 result
380 };
381
382 let watchdog = async {
383 loop {
384 let deadline = *last_activity.lock() + idle;
385 if tokio::time::Instant::now() >= deadline {
386 return;
387 }
388 tokio::time::sleep_until(deadline).await;
389 }
390 };
391
392 tokio::select! {
393 (up, down) = async { tokio::join!(to_upstream, to_client) } => match (up, down) {
394 (Ok(up), Ok(down)) => {
395 debug!("Tunnel closed: {} bytes up, {} bytes down", up, down)
396 }
397 (up, down) => {
398 debug!("Tunnel closed with errors: up={:?} down={:?}", up, down)
401 }
402 },
403 _ = watchdog => debug!("Tunnel closed after {:?} idle", idle),
404 }
405 }
406}
407
408async fn relay<R, W>(
414 reader: &mut R,
415 writer: &mut W,
416 last_activity: &parking_lot::Mutex<tokio::time::Instant>,
417) -> std::io::Result<u64>
418where
419 R: AsyncRead + Unpin,
420 W: AsyncWrite + Unpin,
421{
422 let mut buf = vec![0u8; 16 * 1024];
423 let mut total = 0u64;
424 loop {
425 let n = reader.read(&mut buf).await?;
426 if n == 0 {
427 return Ok(total);
428 }
429 writer.write_all(&buf[..n]).await?;
430 writer.flush().await?;
431 total += n as u64;
432 *last_activity.lock() = tokio::time::Instant::now();
433 }
434}
435
436impl Default for ForwarderClient {
437 fn default() -> Self {
438 Self::new(Duration::from_secs(30))
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445
446 #[tokio::test(start_paused = true)]
447 async fn a_tunnel_that_goes_quiet_is_closed() {
448 let (mut client, client_side) = tokio::io::duplex(1024);
449 let (upstream_side, mut upstream) = tokio::io::duplex(1024);
450 let splice = tokio::spawn(ForwarderClient::splice(
451 client_side,
452 upstream_side,
453 Duration::from_secs(10),
454 ));
455
456 for _ in 0..3 {
458 tokio::time::sleep(Duration::from_secs(8)).await;
459 client.write_all(b"ping").await.unwrap();
460 let mut buf = [0u8; 4];
461 upstream.read_exact(&mut buf).await.unwrap();
462 assert_eq!(&buf, b"ping");
463 }
464 assert!(!splice.is_finished());
465
466 tokio::time::sleep(Duration::from_secs(11)).await;
468 splice.await.unwrap();
469 let mut buf = [0u8; 1];
470 assert_eq!(client.read(&mut buf).await.unwrap(), 0);
471 assert_eq!(upstream.read(&mut buf).await.unwrap(), 0);
472 }
473
474 #[tokio::test(start_paused = true)]
475 async fn a_tunnel_relays_both_ways_and_half_closes() {
476 let (mut client, client_side) = tokio::io::duplex(1024);
477 let (upstream_side, mut upstream) = tokio::io::duplex(1024);
478 let splice = tokio::spawn(ForwarderClient::splice(
479 client_side,
480 upstream_side,
481 Duration::from_secs(10),
482 ));
483
484 upstream.write_all(b"hello").await.unwrap();
485 let mut buf = [0u8; 5];
486 client.read_exact(&mut buf).await.unwrap();
487 assert_eq!(&buf, b"hello");
488
489 client.shutdown().await.unwrap();
490 let mut rest = Vec::new();
491 upstream.read_to_end(&mut rest).await.unwrap();
492 assert!(rest.is_empty());
493 drop(upstream);
494 splice.await.unwrap();
495 }
496}