chore: great error handling
This commit is contained in:
+49
-33
@@ -1,10 +1,11 @@
|
||||
//! # Tuxcord Reverse Proxy Header Authenthication
|
||||
#![feature(
|
||||
rwlock_downgrade,
|
||||
try_blocks,
|
||||
anonymous_lifetime_in_impl_trait,
|
||||
iterator_try_collect,
|
||||
anonymous_lifetime_in_impl_trait
|
||||
rwlock_downgrade,
|
||||
try_blocks
|
||||
)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
@@ -14,20 +15,22 @@ use std::{
|
||||
thread,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::Parser;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::utils::headers::HeadersExt;
|
||||
|
||||
pub mod args;
|
||||
pub mod config;
|
||||
pub mod constants;
|
||||
#[cfg(feature = "ipc")]
|
||||
pub mod ipc;
|
||||
pub mod utils;
|
||||
|
||||
use args::Args;
|
||||
|
||||
const DEFAULT_CONFIG: &[u8] = include_bytes!("../config.default.toml");
|
||||
fn main() {
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
if !args.config.exists() {
|
||||
@@ -36,27 +39,27 @@ fn main() {
|
||||
&args.config
|
||||
);
|
||||
File::create(args.config)
|
||||
.expect("failure creating the config file")
|
||||
.write_all(DEFAULT_CONFIG)
|
||||
.expect("failure writting the contents to the config file");
|
||||
return;
|
||||
.context("failure creating the config file")?
|
||||
.write_all(constants::DEFAULT_CONFIG)
|
||||
.context("failure writting the contents to the config file")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut config_file = File::open(&args.config).expect("failure opening the config file");
|
||||
let mut config_file = File::open(&args.config).context("failure opening the config file")?;
|
||||
let mut config = String::new();
|
||||
config_file
|
||||
.read_to_string(&mut config)
|
||||
.expect("failure reading the config file");
|
||||
let config: config::Schema = toml::from_str(&config).expect("invalid config file");
|
||||
.context("failure reading the config file")?;
|
||||
let config: config::Schema = toml::from_str(&config).context("invalid config file")?;
|
||||
|
||||
#[cfg(feature = "ipc")]
|
||||
if let Some(args::Commands::Ipc) = args.command {
|
||||
ipc::start_client(config);
|
||||
return;
|
||||
ipc::start_client(config)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("config: {config:#?}");
|
||||
let listener = TcpListener::bind(config.listen_at).expect("failure tcp listening");
|
||||
let listener = TcpListener::bind(config.listen_at).context("failure tcp listening")?;
|
||||
let config_arc = Arc::new(config); // will also serve as a counter
|
||||
|
||||
#[cfg(feature = "ipc")]
|
||||
@@ -68,7 +71,7 @@ fn main() {
|
||||
let config_arc = config_arc.clone();
|
||||
thread::spawn(|| {
|
||||
if let Err(err) = handle_client(&mut client, config_arc.as_ref()) {
|
||||
eprintln!("err: invalid req head ({err:?}), closing...");
|
||||
eprintln!("err: invalid req head ({err}), closing...");
|
||||
_ = client.shutdown(Shutdown::Both);
|
||||
}
|
||||
drop(client);
|
||||
@@ -82,7 +85,23 @@ fn main() {
|
||||
unreachable!("listener had to be killed unexpectedly");
|
||||
}
|
||||
|
||||
fn handle_client(client: &mut TcpStream, config: &config::Schema) -> Result<(), &'static str> {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ClientError {
|
||||
#[error("error reading request head: {0}")]
|
||||
HeadReadError(io::Error),
|
||||
#[error("failed to find \"host\" header")]
|
||||
HostHeaderNotFound,
|
||||
#[error("requested host {0:?}, but it's not registered")]
|
||||
HostNotRegistered(String),
|
||||
#[error("failed to connect to backend: {0}")]
|
||||
BackendConnectFail(io::Error),
|
||||
#[error("io error exchanging client and backend: {0}")]
|
||||
ExchangeIoError(io::Error),
|
||||
}
|
||||
|
||||
fn handle_client(client: &mut TcpStream, config: &config::Schema) -> Result<(), ClientError> {
|
||||
use ClientError as E;
|
||||
|
||||
let mut header_buf = [0u8; 1024 * 8];
|
||||
let mut read_pos = 0usize;
|
||||
|
||||
@@ -91,9 +110,9 @@ fn handle_client(client: &mut TcpStream, config: &config::Schema) -> Result<(),
|
||||
headers = [httparse::EMPTY_HEADER; 16];
|
||||
let mut req = httparse::Request::new(&mut headers);
|
||||
|
||||
let Ok(n) = client.read(&mut header_buf[read_pos..]) else {
|
||||
return Err("error reading stream");
|
||||
};
|
||||
let n = client
|
||||
.read(&mut header_buf[read_pos..])
|
||||
.map_err(E::HeadReadError)?;
|
||||
read_pos += n;
|
||||
|
||||
if let Ok(httparse::Status::Complete(n)) = req.parse(&header_buf[0..read_pos]) {
|
||||
@@ -103,31 +122,28 @@ fn handle_client(client: &mut TcpStream, config: &config::Schema) -> Result<(),
|
||||
|
||||
let theres_body = req.headers.has_any(["conten-length", "transfer-encoding"]);
|
||||
|
||||
let Some(host_header) = req.headers.get("host") else {
|
||||
return Err("failed to find \"host\" header");
|
||||
};
|
||||
let host_header = req.headers.get("host").ok_or(E::HostHeaderNotFound)?;
|
||||
|
||||
// Now find that header and pas everything
|
||||
let read_hosts = config.hosts.read();
|
||||
let Some((addr, _, _)) = read_hosts.get(host_header.as_ref()) else {
|
||||
return Err("host not in hashmap");
|
||||
};
|
||||
let (addr, _, _) = read_hosts
|
||||
.get(host_header.as_ref())
|
||||
.ok_or_else(|| E::HostNotRegistered(host_header.to_string()))?;
|
||||
|
||||
let Ok(mut stream) = TcpStream::connect(addr) else {
|
||||
return Err("failed to connect to the hashmap address");
|
||||
};
|
||||
let mut stream = TcpStream::connect(addr).map_err(E::BackendConnectFail)?;
|
||||
drop(read_hosts);
|
||||
let Ok(_): io::Result<()> = (try {
|
||||
let r: io::Result<()> = try {
|
||||
stream.write_all(&header_buf[0..pos])?;
|
||||
// here we sent all original headers, append our own (TODO)
|
||||
stream.write_all(&header_buf[pos..read_pos])?; // send our overhead
|
||||
|
||||
// send our overhead
|
||||
stream.write_all(&header_buf[pos..read_pos])?;
|
||||
if theres_body {
|
||||
io::copy(client, &mut stream)?;
|
||||
}
|
||||
io::copy(&mut stream, client)?;
|
||||
}) else {
|
||||
return Err("io error exchanging head and/or body");
|
||||
};
|
||||
r.map_err(E::ExchangeIoError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user