#![forbid(unsafe_code)]
use std::convert::TryInto;
use std::io::Cursor;
use std::num::NonZeroU16;
use std::str::from_utf8;
use std::sync::Arc;
use ar::Archive;
use tiny_http::Method;
use tiny_http::Response;
use tiny_http::Server as Httpd;
struct StopServer {
server: Arc<Httpd>,
}
impl Drop for StopServer {
fn drop(&mut self) {
self.server.unblock();
}
}
pub struct Server {
inner: Arc<StopServer>,
}
impl Server {
pub fn port(&self) -> NonZeroU16 {
let sv = &self.inner.server;
sv.server_addr().port().try_into().unwrap()
}
}
pub const IP: &'static str = "127.74.137.226";
fn decode_base64(s: &'static str) -> Result<Vec<u8>, base64::DecodeError> {
base64::decode(s.split('\n').collect::<String>())
}
pub fn serve(archive: &'static str) -> Server {
let server = Arc::new(Httpd::http((IP, 0u16)).unwrap());
let sv = server.clone();
std::thread::spawn(move || {
let archive = Cursor::new(archive);
loop {
let rq = match sv.recv() {
Ok(rq) => rq,
Err(_) => break,
};
match rq.method() {
Method::Get | Method::Head => {
let path = rq.url();
let path = path.split('#').next().unwrap();
let path = path.split('?').next().unwrap();
let path = path.split("%2E").collect::<Vec<_>>().join(".");
let path_parts = path.split('/');
let mut current = Some(Archive::new(archive.clone()));
let mut response = None;
for part in path_parts {
if part.is_empty() {
continue
}
if response.is_some() {
response = None;
break
}
let part = percent_encoding::percent_decode_str(part);
let part = part.decode_utf8();
if part.is_err() {
break
}
let part = part.unwrap();
let part: &str = ∂
let mut found = false;
let mut kind = "";
let mut size = 0usize;
while let Some(Ok(entry)) =
current.as_mut().unwrap().next_entry()
{
let name = entry.header().identifier();
let name = from_utf8(name).unwrap();
size = entry.header().size() as usize;
if let Some(suffix) = name.strip_prefix(&part) {
if suffix == ".a" {
found = true;
kind = ".a";
break
} else if suffix == ".b" {
found = true;
kind = ".b";
break
} else if suffix == ".t" {
found = true;
kind = ".t";
break
} else {
}
}
}
if found {
let inner = current.take().unwrap();
let inner = inner.into_inner().unwrap();
let pos = inner.position() as usize;
let inner = inner.into_inner();
let inner = Cursor::new(&inner[pos-size..pos]);
if kind == ".a" {
current = Some(Archive::new(inner));
} else if kind == ".b" {
let inner = decode_base64(inner.into_inner());
let inner = match inner {
Ok(inner) => inner,
Err(e) => {
eprintln!("{}", e);
break
}
};
let size = inner.len();
let inner = Cursor::new(inner);
response = Response::new(
200.into(),
vec![],
Box::new(inner) as Box<dyn std::io::Read>,
Some(size as usize),
None,
).into();
} else if kind == ".t" {
response = Response::new(
200.into(),
vec![],
Box::new(inner) as Box<dyn std::io::Read>,
Some(size as usize),
None,
).into();
} else {
unreachable!();
}
}
}
if let Some(response) = response {
rq.respond(response).unwrap();
} else {
rq.respond(Response::empty(404)).unwrap();
}
},
e => {
eprintln!("Unexpected method: {}", e);
}
}
}
});
Server {
inner: Arc::new(StopServer { server }),
}
}