diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/respack_http/Cargo.toml | 12 | ||||
-rw-r--r-- | server/respack_http/src/main.rs | 105 |
2 files changed, 117 insertions, 0 deletions
diff --git a/server/respack_http/Cargo.toml b/server/respack_http/Cargo.toml new file mode 100644 index 0000000..47f9ccd --- /dev/null +++ b/server/respack_http/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "respack_http" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.97" +clap = { version = "4.5.32", features = ["derive"] } +env_logger = "0.11.7" +log = "0.4.26" +weareshared = { path = "../../shared" } +hex = "0.4.3" diff --git a/server/respack_http/src/main.rs b/server/respack_http/src/main.rs new file mode 100644 index 0000000..be703bd --- /dev/null +++ b/server/respack_http/src/main.rs @@ -0,0 +1,105 @@ +use anyhow::{Result, anyhow}; +use clap::Parser; +use log::{error, warn}; +use std::{ + io::{BufRead, BufReader, BufWriter, Write}, + marker::PhantomData, + net::{IpAddr, SocketAddr, TcpListener, TcpStream}, + path::PathBuf, + sync::Arc, + thread::spawn, +}; +use weareshared::{packets::Resource, store::ResourceStore}; + +#[derive(Parser, Debug)] +struct Args { + #[arg(short, long, default_value = "::")] + bind_addr: IpAddr, + #[arg(short, long, default_value = "28556")] + port: u16, + + pack: PathBuf, +} + +fn main() -> Result<()> { + env_logger::init_from_env("LOG"); + let args = Args::parse(); + let listener = TcpListener::bind(SocketAddr::new(args.bind_addr, args.port))?; + + let store = Arc::new(ResourceStore::new_respack_file(&args.pack)?); + + loop { + let (conn, _addr) = listener.accept()?; + let store = store.clone(); + spawn(move || { + if let Err(e) = handle_conn(conn, store) { + warn!("{e}") + } + }); + } +} + +fn handle_conn(conn: TcpStream, store: Arc<ResourceStore>) -> Result<()> { + let mut conn_read = BufReader::new(conn.try_clone()?); + let mut conn_write = BufWriter::new(conn); + + let mut buf = String::new(); + while !buf.ends_with("\r\n\r\n") { + conn_read.read_line(&mut buf)?; + } + + let mut lines = buf.lines(); + let mut status = lines + .next() + .ok_or(anyhow!("HTTP status line missing"))? + .split(" "); + let _method = status.next().ok_or(anyhow!("HTTP method missing"))?; + let path = status.next().ok_or(anyhow!("HTTP path missing"))?; + let _httpver = status.next().ok_or(anyhow!("HTTP version missing"))?; + + let path = path + .strip_prefix("/") + .ok_or(anyhow!("path does not start on /"))?; + + let (code, ty, data) = handle_request(path, &store); + + write!(conn_write, "HTTP/1.0 {code} OK\r\n")?; + write!(conn_write, "content-type: {ty}\r\n")?; + write!(conn_write, "server: write! macros\r\n")?; + write!(conn_write, "content-length: {}\r\n", data.len())?; + write!(conn_write, "\r\n")?; + conn_write.write_all(&data)?; + conn_write.flush()?; + + Ok(()) +} + +fn handle_request(path: &str, store: &ResourceStore) -> (u16, &'static str, Vec<u8>) { + if path == "entry" { + if let ResourceStore::Respack(pack) = store { + let pack = pack.lock().unwrap(); + if let Some(entry) = pack.entry() { + (200, "application/x.weareresource.id", entry.0.to_vec()) + } else { + (404, "text/plain", b"No entry found".to_vec()) + } + } else { + unreachable!() + } + } else if path.len() == 64 { + let mut hash = [0; 32]; + if let Err(_) = hex::decode_to_slice(path, &mut hash) { + return (400, "text/plain", b"Invalid hash format".to_vec()); + } + match store.get_raw(Resource(hash, PhantomData)) { + Ok(Some(res)) => (200, "application/x.weareresource", res), + Ok(None) => (404, "text/plain", b"Not found".to_vec()), + Err(e) => { + error!("{e}"); + (500, "text/plain", b"Internal server error".to_vec()) + } + } + } else { + (400, "text/plain", b"Bad path".to_vec()) + } +} |