diff options
author | metamuffin <metamuffin@disroot.org> | 2023-02-24 13:23:58 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-02-24 13:23:58 +0100 |
commit | 3ed98e04da0917e790063549676729c7051d67f7 (patch) | |
tree | 81fbb2c93172a7cbb284aaeed8c98d3f6a7c26b5 /src/proxy.rs | |
parent | c0d504f9ae77f99e5484e92e2e9d3f68561129c5 (diff) | |
download | gnix-3ed98e04da0917e790063549676729c7051d67f7.tar gnix-3ed98e04da0917e790063549676729c7051d67f7.tar.bz2 gnix-3ed98e04da0917e790063549676729c7051d67f7.tar.zst |
static file serving + bugs
Diffstat (limited to 'src/proxy.rs')
-rw-r--r-- | src/proxy.rs | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/proxy.rs b/src/proxy.rs new file mode 100644 index 0000000..65fc5a8 --- /dev/null +++ b/src/proxy.rs @@ -0,0 +1,100 @@ +use crate::ServiceError; +use http_body_util::{combinators::BoxBody, BodyExt}; +use hyper::{ + body::Incoming, + header::UPGRADE, + http::{ + uri::{PathAndQuery, Scheme}, + HeaderValue, + }, + upgrade::OnUpgrade, + Request, Uri, +}; +use log::{debug, error, warn}; +use std::net::SocketAddr; +use tokio::net::TcpStream; + +pub async fn proxy_request( + mut req: Request<Incoming>, + addr: SocketAddr, + backend: &SocketAddr, +) -> Result<hyper::Response<BoxBody<bytes::Bytes, ServiceError>>, ServiceError> { + let scheme_secure = req.uri().scheme() == Some(&Scheme::HTTPS); + *req.uri_mut() = Uri::builder() + .path_and_query( + req.uri() + .clone() + .path_and_query() + .cloned() + .unwrap_or(PathAndQuery::from_static("/")), + ) + .build() + .unwrap(); + + req.headers_mut().insert( + "x-forwarded-for", + HeaderValue::from_str(&format!("{addr}")).unwrap(), + ); + req.headers_mut().insert( + "x-forwarded-proto", + if scheme_secure { + HeaderValue::from_static("https") + } else { + HeaderValue::from_static("http") + }, + ); + + let do_upgrade = req.headers().contains_key(UPGRADE); + let on_upgrade_downstream = req.extensions_mut().remove::<OnUpgrade>(); + + debug!("\tforwarding to {}", backend); + let mut resp = { + let client_stream = TcpStream::connect(backend) + .await + .map_err(|_| ServiceError::CantConnect)?; + + let (mut sender, conn) = hyper::client::conn::http1::handshake(client_stream) + .await + .map_err(ServiceError::Hyper)?; + tokio::task::spawn(async move { + if let Err(err) = conn.await { + warn!("connection failed: {:?}", err); + } + }); + sender + .send_request(req) + .await + .map_err(ServiceError::Hyper)? + }; + + resp.headers_mut() + .insert("server", HeaderValue::from_static("gnix")); + + if do_upgrade { + let on_upgrade_upstream = resp.extensions_mut().remove::<OnUpgrade>(); + tokio::task::spawn(async move { + debug!("about upgrading connection, sending empty response"); + match ( + on_upgrade_upstream.unwrap().await, + on_upgrade_downstream.unwrap().await, + ) { + (Ok(mut upgraded_upstream), Ok(mut upgraded_downstream)) => { + debug!("upgrade successful"); + match tokio::io::copy_bidirectional( + &mut upgraded_downstream, + &mut upgraded_upstream, + ) + .await + { + Ok((from_client, from_server)) => { + debug!("proxy socket terminated: {from_server} sent, {from_client} received") + } + Err(e) => warn!("proxy socket error: {e}"), + } + } + (a, b) => error!("upgrade error: upstream={a:?} downstream={b:?}"), + } + }); + } + Ok(resp.map(|b| b.map_err(ServiceError::Hyper).boxed())) +} |