diff options
author | metamuffin <metamuffin@disroot.org> | 2023-02-12 10:15:03 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-02-12 10:15:03 +0100 |
commit | 1f6ade88e4f7b8fdb48ad040b469e33292faaf74 (patch) | |
tree | f5fa84348dfa84b38753dbe92e60ef2ebcbe5cf5 | |
parent | f7d989c8785bb83bfd02a0fa7287b8bf4e383ac6 (diff) | |
download | gnix-1f6ade88e4f7b8fdb48ad040b469e33292faaf74.tar gnix-1f6ade88e4f7b8fdb48ad040b469e33292faaf74.tar.bz2 gnix-1f6ade88e4f7b8fdb48ad040b469e33292faaf74.tar.zst |
doc + error handling
-rw-r--r-- | Cargo.lock | 21 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | readme.md | 7 | ||||
-rw-r--r-- | src/main.rs | 47 |
4 files changed, 56 insertions, 20 deletions
@@ -151,6 +151,7 @@ dependencies = [ "rustls", "rustls-pemfile", "serde", + "thiserror", "tokio", "tokio-rustls", "toml", @@ -629,6 +630,26 @@ dependencies = [ ] [[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "tokio" version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -14,6 +14,7 @@ tokio-rustls = "0.23.4" bytes = "1.4.0" anyhow = "1.0.69" +thiserror = "1.0.38" env_logger = "0.10.0" log = "0.4.17" @@ -2,6 +2,13 @@ a simple stupid reverse proxy +## Features + +- Simple to configure (see below) +- Handles connection upgrades correctly by default (websocket, etc.) +- TLS support +- _TODO: h2; match on uris; connection pools_ + ## Usage Run the binary with the a path to the configuration as the first argument. The diff --git a/src/main.rs b/src/main.rs index f507ed5..7f6ec88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,12 +5,12 @@ use anyhow::{anyhow, bail, Context, Result}; use http_body_util::{combinators::BoxBody, BodyExt}; use hyper::{ body::Incoming, - header::UPGRADE, + header::{HOST, UPGRADE}, http::{uri::PathAndQuery, HeaderValue}, server::conn::http1, service::service_fn, upgrade::OnUpgrade, - Request, Response, Uri, + Request, Response, StatusCode, Uri, }; use log::{debug, error, info, warn}; use std::{fs::File, io::BufReader, path::Path, sync::Arc}; @@ -20,6 +20,16 @@ use tokio::{ }; use tokio_rustls::TlsAcceptor; +#[derive(Debug, thiserror::Error)] +enum ServiceError { + #[error("hyper error")] + Hyper(hyper::Error), + #[error("unknown host")] + NoHost, + #[error("can't connect to the backend")] + CantConnect, +} + #[tokio::main] async fn main() -> anyhow::Result<()> { env_logger::init_from_env("LOG"); @@ -61,10 +71,7 @@ async fn serve_https(config: Arc<Config>) -> Result<()> { .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(), - ]; + 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?; @@ -74,8 +81,10 @@ async fn serve_https(config: Arc<Config>) -> Result<()> { 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) + match tls_acceptor.accept(stream).await { + Ok(stream) => serve_stream(config.clone(), stream), + Err(e) => warn!("error accepting tls: {e}"), + }; } } @@ -93,9 +102,13 @@ pub fn serve_stream<T: AsyncRead + AsyncWrite + Unpin + Send + 'static>( match service(config, req).await { Ok(r) => Ok(r), Err(ServiceError::Hyper(e)) => Err(e), - Err(other) => Ok(Response::new(format!( - "the reverse proxy encountered an error: gnix-{other:?}" - )) + Err(error) => Ok({ + let mut resp = Response::new(format!( + "the reverse proxy encountered an issue: {error}" + )); + *resp.status_mut() = StatusCode::BAD_REQUEST; + resp + } .map(|b| b.map_err(|e| match e {}).boxed())), } } @@ -103,7 +116,7 @@ pub fn serve_stream<T: AsyncRead + AsyncWrite + Unpin + Send + 'static>( ) .with_upgrades(); if let Err(err) = conn.await { - error!("error: {:?}", err); + warn!("error: {:?}", err); } }); } @@ -123,17 +136,11 @@ fn load_private_key(path: &Path) -> anyhow::Result<rustls::PrivateKey> { Ok(rustls::PrivateKey(keys[0].clone())) } -#[derive(Debug)] -enum ServiceError { - Hyper(hyper::Error), - NoHost, - CantConnect, -} - async fn service( config: Arc<Config>, mut req: Request<Incoming>, ) -> Result<hyper::Response<BoxBody<bytes::Bytes, hyper::Error>>, ServiceError> { + debug!("<- {:?} {}", req.headers().get(HOST), req.uri()); *req.uri_mut() = Uri::builder() .scheme("http") .authority("backend") @@ -151,7 +158,7 @@ async fn service( .hosts .get(remove_port( &req.headers() - .get("host") + .get(HOST) .and_then(|e| e.to_str().ok()) .map(String::from) .unwrap_or(String::from("")), |