Skip to main content

genmeta_proxy/
forward.rs

1use hyper::{Request, Response, body::Incoming};
2use hyper_util::rt::TokioIo;
3use snafu::ResultExt;
4use tokio::net::TcpStream;
5use tracing::Instrument as _;
6
7use crate::{
8    Error, ForwardConnectSnafu, ForwardHandshakeSnafu, ForwardInvalidHostSnafu,
9    ForwardMissingHostSnafu, ForwardSendRequestSnafu,
10};
11
12/// Forward a plain HTTP/1.1 request to its target host.
13///
14/// The target host is extracted from the request URI authority, falling back
15/// to the `Host` header. Port defaults to 80 if not specified.
16pub async fn forward_http(req: Request<Incoming>) -> Result<Response<Incoming>, Error> {
17    // Extract host from URI authority or Host header
18    let host_port = if let Some(authority) = req.uri().authority() {
19        authority.to_string()
20    } else if let Some(host_header) = req.headers().get(http::header::HOST) {
21        host_header
22            .to_str()
23            .context(ForwardInvalidHostSnafu)?
24            .to_string()
25    } else {
26        return ForwardMissingHostSnafu.fail();
27    };
28
29    // Default to port 80 if no port specified
30    let addr = if host_port.contains(':') {
31        host_port
32    } else {
33        format!("{}:80", host_port)
34    };
35
36    let stream = TcpStream::connect(&addr)
37        .await
38        .context(ForwardConnectSnafu { addr: addr.clone() })?;
39    crate::configure_tcp_keepalive(&stream);
40    let io = TokioIo::new(stream);
41
42    let (mut sender, conn) = hyper::client::conn::http1::handshake(io)
43        .await
44        .context(ForwardHandshakeSnafu { addr: addr.clone() })?;
45
46    // Terminates when the HTTP/1.1 connection closes.
47    tokio::spawn(conn.in_current_span());
48
49    let resp = sender
50        .send_request(req)
51        .await
52        .context(ForwardSendRequestSnafu)?;
53
54    Ok(resp)
55}