ptth/src/bin/file_server.rs

117 lines
2.3 KiB
Rust

use std::{
convert::Infallible,
error::Error,
path::PathBuf,
sync::Arc,
net::SocketAddr,
};
use hyper::{
Body,
Request,
Response,
Server,
service::{
make_service_fn,
service_fn,
},
StatusCode,
};
use ptth::{
http_serde::RequestParts,
server::file_server,
};
struct ServerState <'a> {
handlebars: Arc <handlebars::Handlebars <'a>>,
}
fn status_reply <B: Into <Body>> (status: StatusCode, b: B)
-> Response <Body>
{
Response::builder ().status (status).body (b.into ()).unwrap ()
}
fn prefix_match <'a> (hay: &'a str, needle: &str) -> Option <&'a str>
{
if hay.starts_with (needle) {
Some (&hay [needle.len ()..])
}
else {
None
}
}
async fn handle_all (req: Request <Body>, state: Arc <ServerState <'static>>)
-> Result <Response <Body>, Infallible>
{
let path = req.uri ().path ();
//println! ("{}", path);
if let Some (path) = prefix_match (path, "/files") {
let root = PathBuf::from ("./");
let path = path.into ();
let (parts, _) = req.into_parts ();
let ptth_req = match RequestParts::from_hyper (parts.method, path, parts.headers) {
Ok (x) => x,
_ => return Ok (status_reply (StatusCode::BAD_REQUEST, "Bad request")),
};
let ptth_resp = file_server::serve_all (state.handlebars.clone (), &root, ptth_req).await;
let mut resp = Response::builder ()
.status (StatusCode::from (ptth_resp.parts.status_code));
use std::str::FromStr;
for (k, v) in ptth_resp.parts.headers.into_iter () {
resp = resp.header (hyper::header::HeaderName::from_str (&k).unwrap (), v);
}
let body = ptth_resp.body
.map (|b| Body::wrap_stream (b))
.unwrap_or_else (|| Body::empty ())
;
let resp = resp.body (body).unwrap ();
Ok (resp)
}
else {
Ok (status_reply (StatusCode::NOT_FOUND, "404 Not Found\n"))
}
}
#[tokio::main]
async fn main () -> Result <(), Box <dyn Error>> {
let addr = SocketAddr::from(([0, 0, 0, 0], 4000));
let handlebars = Arc::new (file_server::load_templates ()?);
let state = Arc::new (ServerState {
handlebars,
});
let make_svc = make_service_fn (|_conn| {
let state = state.clone ();
async {
Ok::<_, Infallible> (service_fn (move |req| {
let state = state.clone ();
handle_all (req, state)
}))
}
});
let server = Server::bind (&addr).serve (make_svc);
server.await?;
Ok (())
}