From 3b1afad1d1a697e82c003e146ef2b7d5742e5210 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 14 Nov 2023 11:54:01 +0100 Subject: refactor architecture and start on http basic auth --- src/main.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 13 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 99374eb..059bdf0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ #![feature(try_trait_v2)] #![feature(exclusive_range_pattern)] +#![feature(slice_split_once)] +pub mod auth; pub mod config; pub mod error; pub mod files; @@ -10,11 +12,12 @@ pub mod proxy; pub mod reporting; use crate::{ - config::{Config, HostConfig}, + config::{Config, RouteFilter}, files::serve_files, proxy::proxy_request, }; use anyhow::{anyhow, bail, Context, Result}; +use bytes::Bytes; use error::ServiceError; use futures::future::try_join_all; use helper::TokioIo; @@ -30,7 +33,10 @@ use hyper::{ use log::{debug, error, info, warn}; #[cfg(feature = "mond")] use reporting::Reporting; -use std::{fs::File, io::BufReader, net::SocketAddr, path::Path, process::exit, sync::Arc}; +use std::{ + fs::File, io::BufReader, net::SocketAddr, ops::ControlFlow, path::Path, process::exit, + sync::Arc, +}; use tokio::{net::TcpListener, signal::ctrl_c, sync::Semaphore}; use tokio_rustls::TlsAcceptor; @@ -42,6 +48,10 @@ pub struct State { pub reporting: Reporting, } +pub type FilterRequest = Request; +pub type FilterResponseOut = Option>>; +pub type FilterResponse = Option>>; + #[tokio::main] async fn main() -> anyhow::Result<()> { env_logger::init_from_env("LOG"); @@ -96,12 +106,13 @@ async fn serve_http(state: Arc) -> Result<()> { let listen_futs: Result> = try_join_all(http_config.bind.iter().map(|e| async { let l = TcpListener::bind(e.clone()).await?; + info!("HTTP listener bound to {}", l.local_addr().unwrap()); loop { let (stream, addr) = l.accept().await.context("accepting connection")?; debug!("connection from {addr}"); let stream = TokioIo(stream); - let config = state.clone(); - tokio::spawn(async move { serve_stream(config, stream, addr).await }); + let state = state.clone(); + tokio::spawn(async move { serve_stream(state, stream, addr).await }); } })) .await; @@ -131,9 +142,9 @@ async fn serve_https(state: Arc) -> Result<()> { Arc::new(cfg) }; let tls_acceptor = Arc::new(TlsAcceptor::from(tls_config)); - let listen_futs: Result> = try_join_all(https_config.bind.iter().map(|e| async { let l = TcpListener::bind(e.clone()).await?; + info!("HTTPS listener bound to {}", l.local_addr().unwrap()); loop { let (stream, addr) = l.accept().await.context("accepting connection")?; let state = state.clone(); @@ -148,8 +159,6 @@ async fn serve_https(state: Arc) -> Result<()> { } })) .await; - - info!("serving https"); listen_futs?; Ok(()) } @@ -170,11 +179,14 @@ pub async fn serve_stream Ok(r), Err(ServiceError::Hyper(e)) => Err(e), Err(error) => Ok({ - let mut resp = - Response::new(format!("gnix encountered an issue: {error}")); + let mut resp = Response::new(format!( + "Sorry, we were unable to process your request: {error}" + )); *resp.status_mut() = StatusCode::BAD_REQUEST; resp.headers_mut() .insert(CONTENT_TYPE, HeaderValue::from_static("text/plain")); + resp.headers_mut() + .insert(SERVER, HeaderValue::from_static("gnix")); resp } .map(|b| b.map_err(|e| match e {}).boxed())), @@ -226,10 +238,41 @@ async fn service( #[cfg(feature = "mond")] state.reporting.hosts.get(host).unwrap().requests_in.inc(); - let mut resp = match route { - HostConfig::Backend { backend } => proxy_request(&state, req, addr, backend).await, - HostConfig::Files { files } => serve_files(req, files).await, - }?; + let mut req = Some(req); + let mut resp = None; + for filter in &route.0 { + let cf = match filter { + RouteFilter::Proxy { backend } => { + resp = Some( + proxy_request( + &state, + req.take().ok_or(ServiceError::RequestTaken)?, + addr, + backend, + ) + .await?, + ); + ControlFlow::Continue(()) + } + RouteFilter::Files { config } => { + resp = Some( + serve_files(req.as_ref().ok_or(ServiceError::RequestTaken)?, config).await?, + ); + ControlFlow::Continue(()) + } + RouteFilter::HttpBasicAuth { config } => auth::http_basic( + config, + req.as_ref().ok_or(ServiceError::RequestTaken)?, + &mut resp, + )?, + }; + match cf { + ControlFlow::Continue(_) => continue, + ControlFlow::Break(_) => break, + } + } + + let mut resp = resp.ok_or(ServiceError::NoResponse)?; let server_header = resp.headers().get(SERVER).cloned(); resp.headers_mut().insert( -- cgit v1.2.3-70-g09d2