diff options
author | metamuffin <metamuffin@disroot.org> | 2023-12-07 14:35:48 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-12-07 14:35:48 +0100 |
commit | 6566cbb3f25aa8b1247c259b5e546910b6044f93 (patch) | |
tree | e94dd775fc1fd90b4ea7b272d871e71118f102f6 | |
parent | ab0d780062bff88d4fbcdd2c91ad5352c0d6279f (diff) | |
download | gnix-6566cbb3f25aa8b1247c259b5e546910b6044f93.tar gnix-6566cbb3f25aa8b1247c259b5e546910b6044f93.tar.bz2 gnix-6566cbb3f25aa8b1247c259b5e546910b6044f93.tar.zst |
move some files around and add horrible access log
-rw-r--r-- | src/config.rs | 13 | ||||
-rw-r--r-- | src/filters/accesslog.rs | 48 | ||||
-rw-r--r-- | src/filters/auth.rs (renamed from src/auth.rs) | 0 | ||||
-rw-r--r-- | src/filters/files.rs (renamed from src/files.rs) | 0 | ||||
-rw-r--r-- | src/filters/mod.rs | 5 | ||||
-rw-r--r-- | src/filters/proxy.rs (renamed from src/proxy.rs) | 0 | ||||
-rw-r--r-- | src/main.rs | 31 |
7 files changed, 88 insertions, 9 deletions
diff --git a/src/config.rs b/src/config.rs index b60ac8c..bc4369d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -70,6 +70,19 @@ pub enum RouteFilter { #[serde(flatten)] config: FileserverConfig, }, + AccessLog { + #[serde(flatten)] + config: AccessLogConfig, + }, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AccessLogConfig { + pub file: PathBuf, + #[serde(default)] + pub flush: bool, + #[serde(default)] + pub reject_on_fail: bool, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/filters/accesslog.rs b/src/filters/accesslog.rs new file mode 100644 index 0000000..ff5a8d5 --- /dev/null +++ b/src/filters/accesslog.rs @@ -0,0 +1,48 @@ +use crate::{config::AccessLogConfig, error::ServiceError, FilterRequest, State}; +use futures::executor::block_on; +use log::error; +use std::{net::SocketAddr, ops::ControlFlow}; +use tokio::{ + fs::OpenOptions, + io::{AsyncWriteExt, BufWriter}, +}; + +pub async fn access_log( + state: &State, + host: &str, + addr: SocketAddr, + config: &AccessLogConfig, + req: &FilterRequest, +) -> Result<ControlFlow<()>, ServiceError> { + let mut g = state.access_logs.write().await; + + let log = g.entry(host.to_owned()).or_insert_with(|| { + BufWriter::new( + // TODO aaahh dont block the runtime and dont panic in any case.... + block_on( + OpenOptions::new() + .append(true) + .create(true) + .open(&config.file), + ) + .unwrap(), + ) + }); + + let method = req.method().as_str(); + let mut res = log + .write_all(format!("{addr}\t{method}\t{:?}\n", req.uri()).as_bytes()) + .await; + + if config.flush && res.is_ok() { + res = log.flush().await; + } + + if config.reject_on_fail { + res? + } else if let Err(e) = res { + error!("failed to write log: {e:?}") + } + + Ok(ControlFlow::Continue(())) +} diff --git a/src/auth.rs b/src/filters/auth.rs index 92a9ba3..92a9ba3 100644 --- a/src/auth.rs +++ b/src/filters/auth.rs diff --git a/src/files.rs b/src/filters/files.rs index 733d045..733d045 100644 --- a/src/files.rs +++ b/src/filters/files.rs diff --git a/src/filters/mod.rs b/src/filters/mod.rs new file mode 100644 index 0000000..fdeed51 --- /dev/null +++ b/src/filters/mod.rs @@ -0,0 +1,5 @@ + +pub mod auth; +pub mod files; +pub mod proxy; +pub mod accesslog;
\ No newline at end of file diff --git a/src/proxy.rs b/src/filters/proxy.rs index 40ebf17..40ebf17 100644 --- a/src/proxy.rs +++ b/src/filters/proxy.rs diff --git a/src/main.rs b/src/main.rs index 807d0ab..07f3d5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,19 +2,16 @@ #![feature(exclusive_range_pattern)] #![feature(slice_split_once)] -pub mod auth; pub mod config; pub mod error; -pub mod files; +pub mod filters; pub mod helper; -pub mod proxy; #[cfg(feature = "mond")] pub mod reporting; use crate::{ config::{Config, RouteFilter}, - files::serve_files, - proxy::proxy_request, + filters::{files::serve_files, proxy::proxy_request}, }; use anyhow::{bail, Context, Result}; use bytes::Bytes; @@ -35,7 +32,7 @@ use log::{debug, error, info, warn}; #[cfg(feature = "mond")] use reporting::Reporting; use std::{ - fs::File, + collections::HashMap, io::BufReader, net::SocketAddr, ops::ControlFlow, @@ -45,6 +42,8 @@ use std::{ sync::Arc, }; use tokio::{ + fs::File, + io::BufWriter, net::TcpListener, signal::ctrl_c, sync::{RwLock, Semaphore}, @@ -53,11 +52,13 @@ use tokio_rustls::TlsAcceptor; pub struct State { pub config: RwLock<Arc<Config>>, + pub access_logs: RwLock<HashMap<String, BufWriter<File>>>, pub l_incoming: Semaphore, pub l_outgoing: Semaphore, #[cfg(feature = "mond")] pub reporting: Reporting, } +pub struct HostState {} pub type FilterRequest = Request<Incoming>; pub type FilterResponseOut = Option<Response<BoxBody<Bytes, ServiceError>>>; @@ -89,6 +90,7 @@ async fn main() -> anyhow::Result<()> { #[cfg(feature = "mond")] reporting: Reporting::new(&config), config: RwLock::new(Arc::new(config)), + access_logs: Default::default(), }); if state.config.read().await.watch_config { @@ -228,12 +230,12 @@ pub async fn serve_stream<T: Unpin + Send + 'static + hyper::rt::Read + hyper::r } fn load_certs(path: &Path) -> anyhow::Result<Vec<rustls::Certificate>> { - let mut reader = BufReader::new(File::open(path).context("reading tls certs")?); + let mut reader = BufReader::new(std::fs::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<rustls::PrivateKey> { - let mut reader = BufReader::new(File::open(path).context("reading tls private key")?); + let mut reader = BufReader::new(std::fs::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 { @@ -263,6 +265,7 @@ async fn service( #[cfg(feature = "mond")] state.reporting.hosts.get(host).unwrap().requests_in.inc(); + // TODO this code is horrible let mut req = Some(req); let mut resp = None; for filter in &route.0 { @@ -285,11 +288,21 @@ async fn service( ); ControlFlow::Continue(()) } - RouteFilter::HttpBasicAuth { config } => auth::http_basic( + RouteFilter::HttpBasicAuth { config } => filters::auth::http_basic( config, req.as_ref().ok_or(ServiceError::RequestTaken)?, &mut resp, )?, + RouteFilter::AccessLog { config } => { + filters::accesslog::access_log( + &state, + host, + addr, + config, + req.as_ref().ok_or(ServiceError::RequestTaken)?, + ) + .await? + } }; match cf { ControlFlow::Continue(_) => continue, |