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
12pub async fn forward_http(req: Request<Incoming>) -> Result<Response<Incoming>, Error> {
17 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 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 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}