pub mod config; use crate::config::Config; use anyhow::{bail, Context, Result}; use bytes::Bytes; use http_body_util::{combinators::BoxBody, BodyExt, Empty}; use hyper::{body::Incoming, server::conn::http1, service::service_fn, Method, Request, Response}; use log::{debug, error, info}; use std::{fs::File, io::BufReader, path::Path, sync::Arc}; use tokio::{ io::{AsyncRead, AsyncWrite}, net::{TcpListener, TcpStream}, }; use tokio_rustls::TlsAcceptor; #[tokio::main] async fn main() -> anyhow::Result<()> { env_logger::init_from_env("LOG"); let config = Arc::new(Config::load()?); tokio::select! { x = serve_http(config.clone()) => x.context("serving http")?, x = serve_https(config.clone()) => x.context("serving https")?, }; Ok(()) } async fn serve_http(config: Arc) -> Result<()> { let http_config = match &config.http { Some(n) => n, None => return Ok(()), }; let listener = TcpListener::bind(http_config.bind).await?; info!("serving http"); loop { let (stream, addr) = listener.accept().await.context("accepting connection")?; debug!("connection from {addr}"); serve_stream(config.clone(), stream) } } async fn serve_https(config: Arc) -> Result<()> { let https_config = match &config.https { Some(n) => n, None => return Ok(()), }; let tls_config = { let certs = load_certs(&https_config.tls_cert)?; let key = load_private_key(&https_config.tls_key)?; let mut cfg = rustls::ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(certs, key)?; cfg.alpn_protocols = vec![ // b"h2".to_vec(), b"http/1.1".to_vec(), ]; Arc::new(cfg) }; let listener = TcpListener::bind(https_config.bind).await?; let tls_acceptor = TlsAcceptor::from(tls_config); info!("serving https"); loop { let (stream, addr) = listener.accept().await.context("accepting connection")?; debug!("connection from {addr}"); let stream = tls_acceptor.accept(stream).await.context("accepting tls")?; serve_stream(config.clone(), stream) } } pub fn serve_stream( config: Arc, stream: T, ) { tokio::task::spawn(async move { if let Err(err) = http1::Builder::new() .serve_connection(stream, service_fn(move |req| service(config.clone(), req))) .await { error!("{:?}", err); } }); } fn load_certs(path: &Path) -> anyhow::Result> { let mut reader = BufReader::new(File::open(path).context("reading tls certs")?); let certs = rustls_pemfile::certs(&mut reader).context("parsing tls certs")?; Ok(certs.into_iter().map(rustls::Certificate).collect()) } fn load_private_key(path: &Path) -> anyhow::Result { let mut reader = BufReader::new(File::open(path).context("reading tls private key")?); let keys = rustls_pemfile::pkcs8_private_keys(&mut reader).context("parsing tls private key")?; if keys.len() != 1 { bail!("expected a single private key, found {}", keys.len()) } Ok(rustls::PrivateKey(keys[0].clone())) } async fn service( config: Arc, mut req: Request, ) -> Result>, hyper::Error> { let uri_string = format!( "http://127.0.0.1:8080{}", req.uri() .path_and_query() .map(|x| x.as_str()) .unwrap_or("/") ); let uri = uri_string.parse().unwrap(); *req.uri_mut() = uri; let host = req.uri().host().expect("uri has no host"); let port = req.uri().port_u16().unwrap_or(80); let addr = format!("{}:{}", host, port); let client_stream = TcpStream::connect(addr).await.unwrap(); if req.method() == Method::CONNECT { Ok(Response::new(empty())) } else { let (mut sender, conn) = hyper::client::conn::http1::handshake(client_stream).await?; tokio::task::spawn(async move { if let Err(err) = conn.await { println!("Connection failed: {:?}", err); } }); let resp = sender.send_request(req).await?; Ok(resp.map(|b| b.boxed())) } } fn empty() -> BoxBody { Empty::::new() .map_err(|never| match never {}) .boxed() }