ptth/src/bin/ptth_file_server.rs

148 lines
3.2 KiB
Rust

use std::{
error::Error,
net::SocketAddr,
path::PathBuf,
sync::Arc,
};
use hyper::{
Body,
Request,
Response,
Server,
service::{
make_service_fn,
service_fn,
},
StatusCode,
};
use serde::Deserialize;
use ptth::{
http_serde::RequestParts,
prelude::*,
server::file_server,
};
#[derive (Default)]
pub struct Config {
pub file_server_root: Option <PathBuf>,
}
struct ServerState <'a> {
config: Config,
handlebars: handlebars::Handlebars <'a>,
server_info: file_server::ServerInfo,
hidden_path: Option <PathBuf>,
}
fn status_reply <B: Into <Body>> (status: StatusCode, b: B)
-> Response <Body>
{
Response::builder ().status (status).body (b.into ()).unwrap ()
}
async fn handle_all (req: Request <Body>, state: Arc <ServerState <'static>>)
-> Result <Response <Body>, String>
{
debug! ("req.uri () = {:?}", req.uri ());
let path = req.uri ().path ();
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 default_root = PathBuf::from ("./");
let file_server_root: &std::path::Path = state.config.file_server_root
.as_ref ()
.unwrap_or (&default_root);
let ptth_resp = file_server::serve_all (
&state.handlebars,
&state.server_info,
file_server_root,
ptth_req.method,
&ptth_req.uri,
&ptth_req.headers,
state.hidden_path.as_ref ().map (|p| p.as_path ())
).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 (Body::wrap_stream)
.unwrap_or_else (Body::empty)
;
let resp = resp.body (body).unwrap ();
Ok (resp)
}
#[derive (Deserialize)]
pub struct ConfigFile {
pub file_server_root: Option <PathBuf>,
pub name: Option <String>,
}
#[tokio::main]
async fn main () -> Result <(), Box <dyn Error>> {
tracing_subscriber::fmt::init ();
let path = PathBuf::from ("./config/ptth_server.toml");
let config_file: ConfigFile = ptth::load_toml::load (&path);
info! ("file_server_root: {:?}", config_file.file_server_root);
let addr = SocketAddr::from(([0, 0, 0, 0], 4000));
let handlebars = file_server::load_templates ()?;
let state = Arc::new (ServerState {
config: Config {
file_server_root: config_file.file_server_root,
},
handlebars,
server_info: crate::file_server::ServerInfo {
server_name: config_file.name.unwrap_or_else (|| "PTTH File Server".to_string ()).clone (),
},
hidden_path: Some (path),
});
let make_svc = make_service_fn (|_conn| {
let state = state.clone ();
async {
Ok::<_, String> (service_fn (move |req| {
let state = state.clone ();
handle_all (req, state)
}))
}
});
let (shutdown_rx, forced_shutdown) = ptth::graceful_shutdown::init_with_force ();
let server = Server::bind (&addr)
.serve (make_svc)
.with_graceful_shutdown (async move {
shutdown_rx.await.ok ();
});
forced_shutdown.wrap_server (server).await??;
Ok (())
}