diff options
-rw-r--r-- | Cargo.lock | 20 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | server/respack_http/Cargo.toml | 12 | ||||
-rw-r--r-- | server/respack_http/src/main.rs | 105 |
4 files changed, 137 insertions, 2 deletions
@@ -1301,7 +1301,7 @@ dependencies = [ "log", "presser", "thiserror 1.0.69", - "windows 0.54.0", + "windows 0.58.0", ] [[package]] @@ -1371,6 +1371,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] name = "hexf-parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2682,6 +2688,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] +name = "respack_http" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.32", + "env_logger", + "hex", + "log", + "weareshared", +] + +[[package]] name = "rgb" version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,3 +1,3 @@ [workspace] -members = ["shared", "server", "client", "import", "cli"] +members = ["shared", "server", "server/respack_http", "client", "import", "cli"] resolver = "3" 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()) + } +} |