1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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())
}
}
|