#![deny(clippy::pedantic)]
#![allow(clippy::used_underscore_binding)]
#![allow(clippy::module_name_repetitions)]
mod config;
mod handler;
mod metadata;
mod stream;
use clap::Clap;
use slog::{crit, debug, error, info, o, Drain};
use std::sync::Arc;
use tokio::net::TcpListener;
#[derive(thiserror::Error, Debug)]
pub enum InitError {
#[error("Couldn't read config file: `{0}`")]
ReadError(#[from] std::io::Error),
#[error("Couldn't parse config file: `{0}`")]
DeserializationError(#[from] toml::de::Error),
}
#[derive(Clap)]
#[clap(version = "1.0", author = "Jordan D. <jordan@doyle.la>")]
struct Opts {
#[clap(short, long)]
config: std::path::PathBuf,
}
#[tokio::main]
async fn main() {
let decorator = slog_term::TermDecorator::new().build();
let drain = slog_term::CompactFormat::new(decorator).build().fuse();
let drain = slog_async::Async::new(drain).build().fuse();
let log = slog::Logger::root(drain, o!());
let opts: Opts = Opts::parse();
let cfg = match std::fs::read_to_string(opts.config)
.map_err(InitError::ReadError)
.and_then(|s| toml::from_str(&s).map_err(Into::into))
{
Ok(v) => v,
Err(e) => {
crit!(log, "{}", e);
return;
}
};
listen_forward(cfg, log).await;
}
async fn listen_forward(config: config::Config, log: slog::Logger) {
let stream = Arc::new(stream::Stream::from(config.stream));
let mut listener = TcpListener::bind(config.server.listen_address)
.await
.unwrap();
info!(
log,
"Listening for new connections on {}", config.server.listen_address
);
loop {
let (conn, remote) = listener.accept().await.unwrap();
let log = log.new(o!("remote" => remote));
debug!(log, "Accepted connection from {}", remote);
let stream = stream.clone();
tokio::spawn(async move {
if let Err(e) = handler::process(conn, stream, log.clone()).await {
error!(log, "Error handling request: {}", e);
}
});
}
}