♻️ refactor: clean up ptth_server
- Found I was passing the file server root twice - Removed handlebars from the public API. The templates are fixed when ptth_server ships, so I don't think users of the lib should care what handlebars is. - Making other stuff private where possiblemain
parent
86af3194e5
commit
ae33337156
|
@ -51,14 +51,8 @@ async fn handle_all (req: Request <Body>, state: Arc <State>)
|
||||||
|
|
||||||
let ptth_req = RequestParts::from_hyper (parts.method, path_and_query, parts.headers)?;
|
let ptth_req = RequestParts::from_hyper (parts.method, path_and_query, parts.headers)?;
|
||||||
|
|
||||||
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 (
|
let ptth_resp = file_server::serve_all (
|
||||||
&state,
|
&state,
|
||||||
file_server_root,
|
|
||||||
ptth_req.method,
|
ptth_req.method,
|
||||||
&ptth_req.uri,
|
&ptth_req.uri,
|
||||||
&ptth_req.headers
|
&ptth_req.headers
|
||||||
|
@ -94,12 +88,6 @@ async fn main () -> anyhow::Result <()> {
|
||||||
|
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], 4000));
|
let addr = SocketAddr::from(([0, 0, 0, 0], 4000));
|
||||||
|
|
||||||
let handlebars = file_server::load_templates (&PathBuf::new ())?;
|
|
||||||
|
|
||||||
let metrics_startup = metrics::Startup::new (
|
|
||||||
config_file.name.unwrap_or_else (|| "PTTH File Server".to_string ())
|
|
||||||
);
|
|
||||||
|
|
||||||
let metrics_interval = Arc::new (ArcSwap::default ());
|
let metrics_interval = Arc::new (ArcSwap::default ());
|
||||||
|
|
||||||
let interval_writer = Arc::clone (&metrics_interval);
|
let interval_writer = Arc::clone (&metrics_interval);
|
||||||
|
@ -107,15 +95,13 @@ async fn main () -> anyhow::Result <()> {
|
||||||
file_server::metrics::Interval::monitor (interval_writer).await;
|
file_server::metrics::Interval::monitor (interval_writer).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = Arc::new (State {
|
let state = Arc::new (State::new (
|
||||||
config: file_server::Config {
|
config_file.file_server_root,
|
||||||
file_server_root: config_file.file_server_root,
|
&PathBuf::new (),
|
||||||
},
|
config_file.name.unwrap_or_else (|| "PTTH File Server".to_string ()),
|
||||||
handlebars,
|
|
||||||
metrics_startup,
|
|
||||||
metrics_interval,
|
metrics_interval,
|
||||||
hidden_path: Some (path),
|
Some (path),
|
||||||
});
|
)?);
|
||||||
|
|
||||||
let make_svc = make_service_fn (|_conn| {
|
let make_svc = make_service_fn (|_conn| {
|
||||||
let state = state.clone ();
|
let state = state.clone ();
|
||||||
|
|
|
@ -1,20 +1,30 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Errors thrown when the server loads its TOML config file
|
||||||
|
|
||||||
#[derive (Debug, Error)]
|
#[derive (Debug, Error)]
|
||||||
pub enum LoadTomlError {
|
pub enum LoadTomlError {
|
||||||
|
/// The server's config file is readable by other users, meaning
|
||||||
|
/// that the file server module could accidentally serve it to
|
||||||
|
/// clients, and expose the server's API key.
|
||||||
#[error ("Config file has bad permissions mode, it should be octal 0600")]
|
#[error ("Config file has bad permissions mode, it should be octal 0600")]
|
||||||
ConfigBadPermissions,
|
ConfigBadPermissions,
|
||||||
|
|
||||||
|
/// Wraps `std::io::Error`
|
||||||
#[error ("I/O")]
|
#[error ("I/O")]
|
||||||
Io (#[from] std::io::Error),
|
Io (#[from] std::io::Error),
|
||||||
|
|
||||||
|
/// Wraps `std::string::FromUtf8Error`
|
||||||
#[error ("UTF-8")]
|
#[error ("UTF-8")]
|
||||||
Utf8 (#[from] std::string::FromUtf8Error),
|
Utf8 (#[from] std::string::FromUtf8Error),
|
||||||
|
|
||||||
|
/// Wraps `toml::de::Error`
|
||||||
#[error ("TOML")]
|
#[error ("TOML")]
|
||||||
Toml (#[from] toml::de::Error),
|
Toml (#[from] toml::de::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors thrown when the server is starting up or serving requests
|
||||||
|
|
||||||
#[derive (Debug, Error)]
|
#[derive (Debug, Error)]
|
||||||
pub enum ServerError {
|
pub enum ServerError {
|
||||||
#[error ("Loading TOML")]
|
#[error ("Loading TOML")]
|
||||||
|
@ -23,9 +33,6 @@ pub enum ServerError {
|
||||||
#[error ("Loading Handlebars template file")]
|
#[error ("Loading Handlebars template file")]
|
||||||
LoadHandlebars (#[from] handlebars::TemplateFileError),
|
LoadHandlebars (#[from] handlebars::TemplateFileError),
|
||||||
|
|
||||||
#[error ("API key is too weak, server can't use it")]
|
|
||||||
WeakApiKey,
|
|
||||||
|
|
||||||
#[error ("File server error")]
|
#[error ("File server error")]
|
||||||
FileServer (#[from] super::file_server::errors::FileServerError),
|
FileServer (#[from] super::file_server::errors::FileServerError),
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,6 @@ use thiserror::Error;
|
||||||
|
|
||||||
#[derive (Debug, Error)]
|
#[derive (Debug, Error)]
|
||||||
pub enum FileServerError {
|
pub enum FileServerError {
|
||||||
#[error ("Handlebars render error")]
|
|
||||||
Handlebars (#[from] handlebars::RenderError),
|
|
||||||
|
|
||||||
#[error ("I/O error")]
|
#[error ("I/O error")]
|
||||||
Io (#[from] std::io::Error),
|
Io (#[from] std::io::Error),
|
||||||
|
|
||||||
|
@ -28,6 +25,9 @@ pub enum FileServerError {
|
||||||
|
|
||||||
#[error ("Heim process error")]
|
#[error ("Heim process error")]
|
||||||
HeimProcess,
|
HeimProcess,
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Other (#[from] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From <heim::process::ProcessError> for FileServerError {
|
impl From <heim::process::ProcessError> for FileServerError {
|
||||||
|
|
|
@ -60,7 +60,7 @@ pub async fn serve_root (
|
||||||
metrics_interval: &**state.metrics_interval.load (),
|
metrics_interval: &**state.metrics_interval.load (),
|
||||||
};
|
};
|
||||||
|
|
||||||
let s = state.handlebars.render ("file_server_root", ¶ms)?;
|
let s = state.handlebars.render ("file_server_root", ¶ms).map_err (anyhow::Error::from)?;
|
||||||
|
|
||||||
Ok (serve (s))
|
Ok (serve (s))
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ pub async fn serve_dir (
|
||||||
path,
|
path,
|
||||||
entries,
|
entries,
|
||||||
instance_metrics,
|
instance_metrics,
|
||||||
})?;
|
}).map_err (anyhow::Error::from)?;
|
||||||
|
|
||||||
Ok (serve (s))
|
Ok (serve (s))
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,27 @@ pub struct State {
|
||||||
pub hidden_path: Option <PathBuf>,
|
pub hidden_path: Option <PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn new (
|
||||||
|
file_server_root: Option <PathBuf>,
|
||||||
|
asset_root: &Path,
|
||||||
|
name: String,
|
||||||
|
metrics_interval: Arc <ArcSwap <Option <metrics::Interval>>>,
|
||||||
|
hidden_path: Option <PathBuf>,
|
||||||
|
) -> Result <Self, FileServerError>
|
||||||
|
{
|
||||||
|
Ok (Self {
|
||||||
|
config: Config {
|
||||||
|
file_server_root,
|
||||||
|
},
|
||||||
|
handlebars: load_templates (asset_root)?,
|
||||||
|
metrics_startup: metrics::Startup::new (name),
|
||||||
|
metrics_interval,
|
||||||
|
hidden_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive (Serialize)]
|
#[derive (Serialize)]
|
||||||
struct DirJson {
|
struct DirJson {
|
||||||
entries: Vec <DirEntryJson>,
|
entries: Vec <DirEntryJson>,
|
||||||
|
@ -311,14 +332,14 @@ async fn stream_file (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass a request to the internal decision-making logic.
|
/// Top-level request handler for the file server module.
|
||||||
// When it returns, prettify it as HTML or JSON based on what the client
|
///
|
||||||
// asked for.
|
/// Passes a request to the internal file server logic.
|
||||||
|
/// Returns an HTTP response as HTML or JSON, depending on the request.
|
||||||
|
|
||||||
#[instrument (level = "debug", skip (state, headers))]
|
#[instrument (level = "debug", skip (state, headers))]
|
||||||
pub async fn serve_all (
|
pub async fn serve_all (
|
||||||
state: &State,
|
state: &State,
|
||||||
root: &Path,
|
|
||||||
method: Method,
|
method: Method,
|
||||||
uri: &str,
|
uri: &str,
|
||||||
headers: &HashMap <String, Vec <u8>>
|
headers: &HashMap <String, Vec <u8>>
|
||||||
|
@ -342,6 +363,11 @@ pub async fn serve_all (
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let default_root = PathBuf::from ("./");
|
||||||
|
let root: &std::path::Path = state.config.file_server_root
|
||||||
|
.as_ref ()
|
||||||
|
.unwrap_or (&default_root);
|
||||||
|
|
||||||
Ok (match internal::serve_all (root, method, uri, headers, state.hidden_path.as_deref ()).await? {
|
Ok (match internal::serve_all (root, method, uri, headers, state.hidden_path.as_deref ()).await? {
|
||||||
Favicon => serve_error (StatusCode::NotFound, "Not found\n"),
|
Favicon => serve_error (StatusCode::NotFound, "Not found\n"),
|
||||||
Forbidden => serve_error (StatusCode::Forbidden, "403 Forbidden\n"),
|
Forbidden => serve_error (StatusCode::Forbidden, "403 Forbidden\n"),
|
||||||
|
@ -400,10 +426,10 @@ pub async fn serve_all (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_templates (
|
fn load_templates (
|
||||||
asset_root: &Path
|
asset_root: &Path
|
||||||
)
|
)
|
||||||
-> Result <Handlebars <'static>, handlebars::TemplateFileError>
|
-> Result <Handlebars <'static>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let mut handlebars = Handlebars::new ();
|
let mut handlebars = Handlebars::new ();
|
||||||
handlebars.set_strict_mode (true);
|
handlebars.set_strict_mode (true);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! # PTTH Server
|
//! # PTTH Server
|
||||||
//!
|
//!
|
||||||
//! The PTTH server makes an outgoing HTTP connection to a
|
//! The PTTH server makes outgoing HTTP connections to a
|
||||||
//! PTTH relay, and then serves incoming HTTP requests through
|
//! PTTH relay, and then serves incoming HTTP requests through
|
||||||
//! the relay.
|
//! the relay.
|
||||||
|
|
||||||
|
@ -35,7 +35,12 @@ use ptth_core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|
||||||
|
/// In-process file server module with byte range and ETag support
|
||||||
pub mod file_server;
|
pub mod file_server;
|
||||||
|
|
||||||
|
/// Load and de-serialize structures from TOML, with a size limit
|
||||||
|
/// and checking permissions (On Unix)
|
||||||
pub mod load_toml;
|
pub mod load_toml;
|
||||||
|
|
||||||
use errors::ServerError;
|
use errors::ServerError;
|
||||||
|
@ -58,14 +63,8 @@ async fn handle_one_req (
|
||||||
|
|
||||||
debug! ("Handling request {}", req_id);
|
debug! ("Handling request {}", req_id);
|
||||||
|
|
||||||
let default_root = PathBuf::from ("./");
|
|
||||||
let file_server_root: &std::path::Path = state.file_server.config.file_server_root
|
|
||||||
.as_ref ()
|
|
||||||
.unwrap_or (&default_root);
|
|
||||||
|
|
||||||
let response = file_server::serve_all (
|
let response = file_server::serve_all (
|
||||||
&state.file_server,
|
&state.file_server,
|
||||||
file_server_root,
|
|
||||||
parts.method,
|
parts.method,
|
||||||
&parts.uri,
|
&parts.uri,
|
||||||
&parts.headers,
|
&parts.headers,
|
||||||
|
@ -161,16 +160,30 @@ async fn handle_req_resp (
|
||||||
Ok (())
|
Ok (())
|
||||||
}
|
}
|
||||||
|
|
||||||
// A complete config file. The bin frontend is allowed to use a different
|
/// Config for ptth_server and the file server module
|
||||||
// type to load an incomplete file, as long as the command line options
|
///
|
||||||
// complete it.
|
/// This is a complete config.
|
||||||
|
/// The bin frontend is allowed to load an incomplete config from
|
||||||
|
/// the TOML file, fill it out with command-line options, and put
|
||||||
|
/// the completed config in this struct.
|
||||||
|
|
||||||
#[derive (Clone)]
|
#[derive (Clone)]
|
||||||
pub struct ConfigFile {
|
pub struct ConfigFile {
|
||||||
|
/// A name that uniquely identifies this server on the relay.
|
||||||
|
/// May be human-readable.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
|
/// Secret API key used to authenticate the server with the relay
|
||||||
pub api_key: String,
|
pub api_key: String,
|
||||||
|
|
||||||
|
/// URL of the PTTH relay server that ptth_server should connect to
|
||||||
pub relay_url: String,
|
pub relay_url: String,
|
||||||
|
|
||||||
|
/// Directory that the file server module will expose to clients
|
||||||
|
/// over the relay. If None, the current working dir is used.
|
||||||
pub file_server_root: Option <PathBuf>,
|
pub file_server_root: Option <PathBuf>,
|
||||||
|
|
||||||
|
/// For debugging.
|
||||||
pub throttle_upload: bool,
|
pub throttle_upload: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,12 +205,19 @@ impl ConfigFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Config for ptth_server itself
|
||||||
|
|
||||||
#[derive (Default)]
|
#[derive (Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
/// URL of the PTTH relay server that ptth_server should connect to
|
||||||
pub relay_url: String,
|
pub relay_url: String,
|
||||||
|
|
||||||
|
/// For debugging.
|
||||||
pub throttle_upload: bool,
|
pub throttle_upload: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs a PTTH file server
|
||||||
|
|
||||||
pub async fn run_server (
|
pub async fn run_server (
|
||||||
config_file: ConfigFile,
|
config_file: ConfigFile,
|
||||||
shutdown_oneshot: oneshot::Receiver <()>,
|
shutdown_oneshot: oneshot::Receiver <()>,
|
||||||
|
@ -224,9 +244,7 @@ pub async fn run_server (
|
||||||
.default_headers (headers)
|
.default_headers (headers)
|
||||||
.connect_timeout (Duration::from_secs (30))
|
.connect_timeout (Duration::from_secs (30))
|
||||||
.build ().map_err (ServerError::CantBuildHttpClient)?;
|
.build ().map_err (ServerError::CantBuildHttpClient)?;
|
||||||
let handlebars = file_server::load_templates (&asset_root)?;
|
|
||||||
|
|
||||||
let metrics_startup = file_server::metrics::Startup::new (config_file.name);
|
|
||||||
let metrics_interval = Arc::new (ArcSwap::default ());
|
let metrics_interval = Arc::new (ArcSwap::default ());
|
||||||
|
|
||||||
let interval_writer = Arc::clone (&metrics_interval);
|
let interval_writer = Arc::clone (&metrics_interval);
|
||||||
|
@ -235,15 +253,13 @@ pub async fn run_server (
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = Arc::new (State {
|
let state = Arc::new (State {
|
||||||
file_server: file_server::State {
|
file_server: file_server::State::new (
|
||||||
config: file_server::Config {
|
config_file.file_server_root,
|
||||||
file_server_root: config_file.file_server_root,
|
&asset_root,
|
||||||
},
|
config_file.name,
|
||||||
handlebars,
|
|
||||||
metrics_startup,
|
|
||||||
metrics_interval,
|
metrics_interval,
|
||||||
hidden_path,
|
hidden_path,
|
||||||
},
|
)?,
|
||||||
config: Config {
|
config: Config {
|
||||||
relay_url: config_file.relay_url,
|
relay_url: config_file.relay_url,
|
||||||
throttle_upload: config_file.throttle_upload,
|
throttle_upload: config_file.throttle_upload,
|
||||||
|
|
|
@ -26,7 +26,7 @@ fn load_inner <
|
||||||
|
|
||||||
/// For files that contain public-viewable information
|
/// For files that contain public-viewable information
|
||||||
|
|
||||||
pub fn load_public <
|
fn load_public <
|
||||||
T: DeserializeOwned,
|
T: DeserializeOwned,
|
||||||
P: AsRef <Path> + Debug
|
P: AsRef <Path> + Debug
|
||||||
> (
|
> (
|
||||||
|
|
Loading…
Reference in New Issue