➕ update: add InstanceMetrics and replace ServerInfo
parent
7a47c705d7
commit
d03c1a5476
|
@ -1218,6 +1218,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"structopt",
|
"structopt",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1257,6 +1258,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.12.3",
|
"base64 0.12.3",
|
||||||
"blake3",
|
"blake3",
|
||||||
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"handlebars",
|
"handlebars",
|
||||||
"http",
|
"http",
|
||||||
|
@ -1278,6 +1280,7 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-futures",
|
"tracing-futures",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"ulid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -15,6 +15,7 @@ hyper = "0.13.8"
|
||||||
serde = {version = "1.0.117", features = ["derive"]}
|
serde = {version = "1.0.117", features = ["derive"]}
|
||||||
structopt = "0.3.20"
|
structopt = "0.3.20"
|
||||||
tokio = { version = "0.2.22", features = ["full"] }
|
tokio = { version = "0.2.22", features = ["full"] }
|
||||||
|
tracing = "0.1.21"
|
||||||
tracing-subscriber = "0.2.15"
|
tracing-subscriber = "0.2.15"
|
||||||
|
|
||||||
ptth_core = { path = "../ptth_core" }
|
ptth_core = { path = "../ptth_core" }
|
||||||
|
|
|
@ -18,13 +18,17 @@ use hyper::{
|
||||||
StatusCode,
|
StatusCode,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
use ptth_core::{
|
use ptth_core::{
|
||||||
http_serde::RequestParts,
|
http_serde::RequestParts,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use ptth_server::{
|
use ptth_server::{
|
||||||
file_server,
|
file_server::{
|
||||||
|
self,
|
||||||
|
metrics::InstanceMetrics,
|
||||||
|
},
|
||||||
load_toml,
|
load_toml,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,7 +40,7 @@ pub struct Config {
|
||||||
struct ServerState <'a> {
|
struct ServerState <'a> {
|
||||||
config: Config,
|
config: Config,
|
||||||
handlebars: handlebars::Handlebars <'a>,
|
handlebars: handlebars::Handlebars <'a>,
|
||||||
server_info: file_server::ServerInfo,
|
instance_metrics: InstanceMetrics,
|
||||||
hidden_path: Option <PathBuf>,
|
hidden_path: Option <PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +67,7 @@ async fn handle_all (req: Request <Body>, state: Arc <ServerState <'static>>)
|
||||||
|
|
||||||
let ptth_resp = file_server::serve_all (
|
let ptth_resp = file_server::serve_all (
|
||||||
&state.handlebars,
|
&state.handlebars,
|
||||||
&state.server_info,
|
&state.instance_metrics,
|
||||||
file_server_root,
|
file_server_root,
|
||||||
ptth_req.method,
|
ptth_req.method,
|
||||||
&ptth_req.uri,
|
&ptth_req.uri,
|
||||||
|
@ -101,14 +105,18 @@ async fn main () -> Result <(), anyhow::Error> {
|
||||||
|
|
||||||
let handlebars = file_server::load_templates (&PathBuf::new ())?;
|
let handlebars = file_server::load_templates (&PathBuf::new ())?;
|
||||||
|
|
||||||
|
let instance_metrics = InstanceMetrics::new (
|
||||||
|
config_file.name.unwrap_or_else (|| "PTTH File Server".to_string ())
|
||||||
|
);
|
||||||
|
|
||||||
|
debug! ("{:?}", instance_metrics);
|
||||||
|
|
||||||
let state = Arc::new (ServerState {
|
let state = Arc::new (ServerState {
|
||||||
config: Config {
|
config: Config {
|
||||||
file_server_root: config_file.file_server_root,
|
file_server_root: config_file.file_server_root,
|
||||||
},
|
},
|
||||||
handlebars,
|
handlebars,
|
||||||
server_info: crate::file_server::ServerInfo {
|
instance_metrics,
|
||||||
server_name: config_file.name.unwrap_or_else (|| "PTTH File Server".to_string ()),
|
|
||||||
},
|
|
||||||
hidden_path: Some (path),
|
hidden_path: Some (path),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ aho-corasick = "0.7.14"
|
||||||
anyhow = "1.0.34"
|
anyhow = "1.0.34"
|
||||||
base64 = "0.12.3"
|
base64 = "0.12.3"
|
||||||
blake3 = "0.3.7"
|
blake3 = "0.3.7"
|
||||||
|
chrono = {version = "0.4.19", features = ["serde"]}
|
||||||
futures = "0.3.7"
|
futures = "0.3.7"
|
||||||
handlebars = "3.5.1"
|
handlebars = "3.5.1"
|
||||||
http = "0.2.1"
|
http = "0.2.1"
|
||||||
|
@ -30,6 +31,7 @@ tracing = "0.1.21"
|
||||||
tracing-futures = "0.2.4"
|
tracing-futures = "0.2.4"
|
||||||
tracing-subscriber = "0.2.15"
|
tracing-subscriber = "0.2.15"
|
||||||
toml = "0.5.7"
|
toml = "0.5.7"
|
||||||
|
ulid = "0.4.1"
|
||||||
|
|
||||||
always_equal = { path = "../always_equal" }
|
always_equal = { path = "../always_equal" }
|
||||||
ptth_core = { path = "../ptth_core" }
|
ptth_core = { path = "../ptth_core" }
|
||||||
|
|
|
@ -218,6 +218,9 @@ async fn serve_api (
|
||||||
Ok (NotFound)
|
Ok (NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle the requests internally without knowing anything about PTTH or
|
||||||
|
// HTML / handlebars
|
||||||
|
|
||||||
pub async fn serve_all (
|
pub async fn serve_all (
|
||||||
root: &Path,
|
root: &Path,
|
||||||
method: Method,
|
method: Method,
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use ulid::Ulid;
|
||||||
|
|
||||||
|
fn serialize_ulid <S: serde::Serializer> (t: &Ulid, s: S)
|
||||||
|
-> Result <S::Ok, S::Error>
|
||||||
|
{
|
||||||
|
let t = t.to_string ();
|
||||||
|
s.serialize_str (&t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance metrics are captured when the ptth_server process starts.
|
||||||
|
// They don't change after that.
|
||||||
|
|
||||||
|
#[derive (Debug, serde::Serialize)]
|
||||||
|
pub struct InstanceMetrics {
|
||||||
|
// D-Bus machine ID, if we're on Linux
|
||||||
|
pub machine_id: Option <String>,
|
||||||
|
|
||||||
|
// Git version that ptth_server was built from (unimplemented)
|
||||||
|
pub _git_version: Option <String>,
|
||||||
|
|
||||||
|
// User-assigned and human-readable name for this server.
|
||||||
|
// Must be unique within a relay.
|
||||||
|
pub server_name: String,
|
||||||
|
|
||||||
|
// Random base64 instance ID. ptth_server generates this at process start.
|
||||||
|
// It's a fallback for detecting outages without relying on any clocks.
|
||||||
|
#[serde (serialize_with = "serialize_ulid")]
|
||||||
|
pub instance_id: Ulid,
|
||||||
|
|
||||||
|
pub startup_utc: DateTime <Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_machine_id () -> Option <String> {
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::Read,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = vec! [0u8; 1024];
|
||||||
|
let mut f = File::open ("/etc/machine-id").ok ()?;
|
||||||
|
let bytes_read = f.read (&mut buf).ok ()?;
|
||||||
|
|
||||||
|
buf.truncate (bytes_read);
|
||||||
|
|
||||||
|
let s = std::str::from_utf8 (&buf).ok ()?;
|
||||||
|
let s = s.trim_end ().to_string ();
|
||||||
|
|
||||||
|
Some (s)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstanceMetrics {
|
||||||
|
pub fn new (server_name: String) -> Self
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
machine_id: get_machine_id (),
|
||||||
|
_git_version: None,
|
||||||
|
server_name,
|
||||||
|
instance_id: ulid::Ulid::new (),
|
||||||
|
startup_utc: Utc::now (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg (test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ulid_null () {
|
||||||
|
let a = InstanceMetrics::new ("bogus".to_string ());
|
||||||
|
let b = InstanceMetrics::new ("bogus".to_string ());
|
||||||
|
|
||||||
|
assert_ne! (a.instance_id, b.instance_id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ use std::{
|
||||||
cmp::min,
|
cmp::min,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
convert::{Infallible, TryFrom},
|
convert::{Infallible, TryFrom},
|
||||||
fmt::Debug,
|
|
||||||
io::SeekFrom,
|
io::SeekFrom,
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
@ -38,11 +37,14 @@ use ptth_core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
pub mod metrics;
|
||||||
|
|
||||||
mod internal;
|
mod internal;
|
||||||
mod markdown;
|
mod markdown;
|
||||||
mod range;
|
mod range;
|
||||||
|
|
||||||
use errors::FileServerError;
|
use errors::FileServerError;
|
||||||
|
use metrics::InstanceMetrics;
|
||||||
|
|
||||||
mod emoji {
|
mod emoji {
|
||||||
pub const VIDEO: &str = "\u{1f39e}\u{fe0f}";
|
pub const VIDEO: &str = "\u{1f39e}\u{fe0f}";
|
||||||
|
@ -52,11 +54,6 @@ mod emoji {
|
||||||
pub const ERROR: &str = "\u{26a0}\u{fe0f}";
|
pub const ERROR: &str = "\u{26a0}\u{fe0f}";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Debug, Serialize)]
|
|
||||||
pub struct ServerInfo {
|
|
||||||
pub server_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive (Serialize)]
|
#[derive (Serialize)]
|
||||||
struct DirEntryJson {
|
struct DirEntryJson {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -93,7 +90,7 @@ struct DirEntryHtml {
|
||||||
#[derive (Serialize)]
|
#[derive (Serialize)]
|
||||||
struct DirHtml <'a> {
|
struct DirHtml <'a> {
|
||||||
#[serde (flatten)]
|
#[serde (flatten)]
|
||||||
server_info: &'a ServerInfo,
|
instance_metrics: &'a InstanceMetrics,
|
||||||
|
|
||||||
path: Cow <'a, str>,
|
path: Cow <'a, str>,
|
||||||
entries: Vec <DirEntryHtml>,
|
entries: Vec <DirEntryHtml>,
|
||||||
|
@ -191,10 +188,10 @@ async fn read_dir_entry_json (entry: DirEntry) -> Option <DirEntryJson>
|
||||||
|
|
||||||
async fn serve_root (
|
async fn serve_root (
|
||||||
handlebars: &Handlebars <'static>,
|
handlebars: &Handlebars <'static>,
|
||||||
server_info: &ServerInfo
|
instance_metrics: &InstanceMetrics
|
||||||
) -> Result <Response, FileServerError>
|
) -> Result <Response, FileServerError>
|
||||||
{
|
{
|
||||||
let s = handlebars.render ("file_server_root", &server_info)?;
|
let s = handlebars.render ("file_server_root", &instance_metrics)?;
|
||||||
|
|
||||||
Ok (serve_html (s))
|
Ok (serve_html (s))
|
||||||
}
|
}
|
||||||
|
@ -233,10 +230,10 @@ async fn serve_dir_json (
|
||||||
Ok (response)
|
Ok (response)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument (level = "debug", skip (handlebars, dir))]
|
#[instrument (level = "debug", skip (handlebars, instance_metrics, dir))]
|
||||||
async fn serve_dir_html (
|
async fn serve_dir_html (
|
||||||
handlebars: &Handlebars <'static>,
|
handlebars: &Handlebars <'static>,
|
||||||
server_info: &ServerInfo,
|
instance_metrics: &InstanceMetrics,
|
||||||
path: Cow <'_, str>,
|
path: Cow <'_, str>,
|
||||||
mut dir: ReadDir
|
mut dir: ReadDir
|
||||||
) -> Result <Response, FileServerError>
|
) -> Result <Response, FileServerError>
|
||||||
|
@ -252,7 +249,7 @@ async fn serve_dir_html (
|
||||||
let s = handlebars.render ("file_server_dir", &DirHtml {
|
let s = handlebars.render ("file_server_dir", &DirHtml {
|
||||||
path,
|
path,
|
||||||
entries,
|
entries,
|
||||||
server_info,
|
instance_metrics,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok (serve_html (s))
|
Ok (serve_html (s))
|
||||||
|
@ -355,10 +352,14 @@ async fn serve_file (
|
||||||
Ok (response)
|
Ok (response)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument (level = "debug", skip (handlebars, headers))]
|
// Pass a request to the internal decision-making logic.
|
||||||
|
// When it returns, prettify it as HTML or JSON based on what the client
|
||||||
|
// asked for.
|
||||||
|
|
||||||
|
#[instrument (level = "debug", skip (handlebars, headers, instance_metrics))]
|
||||||
pub async fn serve_all (
|
pub async fn serve_all (
|
||||||
handlebars: &Handlebars <'static>,
|
handlebars: &Handlebars <'static>,
|
||||||
server_info: &ServerInfo,
|
instance_metrics: &InstanceMetrics,
|
||||||
root: &Path,
|
root: &Path,
|
||||||
method: Method,
|
method: Method,
|
||||||
uri: &str,
|
uri: &str,
|
||||||
|
@ -404,14 +405,14 @@ pub async fn serve_all (
|
||||||
},
|
},
|
||||||
InvalidQuery => serve_error (StatusCode::BadRequest, "Query is invalid for this object\n"),
|
InvalidQuery => serve_error (StatusCode::BadRequest, "Query is invalid for this object\n"),
|
||||||
|
|
||||||
Root => serve_root (handlebars, server_info).await?,
|
Root => serve_root (handlebars, instance_metrics).await?,
|
||||||
ServeDir (internal::ServeDirParams {
|
ServeDir (internal::ServeDirParams {
|
||||||
path,
|
path,
|
||||||
dir,
|
dir,
|
||||||
format
|
format
|
||||||
}) => match format {
|
}) => match format {
|
||||||
OutputFormat::Json => serve_dir_json (dir.into_inner ()).await?,
|
OutputFormat::Json => serve_dir_json (dir.into_inner ()).await?,
|
||||||
OutputFormat::Html => serve_dir_html (handlebars, server_info, path.to_string_lossy (), dir.into_inner ()).await?,
|
OutputFormat::Html => serve_dir_html (handlebars, instance_metrics, path.to_string_lossy (), dir.into_inner ()).await?,
|
||||||
},
|
},
|
||||||
ServeFile (internal::ServeFileParams {
|
ServeFile (internal::ServeFileParams {
|
||||||
file,
|
file,
|
||||||
|
|
|
@ -51,11 +51,14 @@ pub fn password_is_bad (mut password: String) -> bool {
|
||||||
struct ServerState {
|
struct ServerState {
|
||||||
config: Config,
|
config: Config,
|
||||||
handlebars: Handlebars <'static>,
|
handlebars: Handlebars <'static>,
|
||||||
server_info: file_server::ServerInfo,
|
instance_metrics: file_server::metrics::InstanceMetrics,
|
||||||
client: Client,
|
client: Client,
|
||||||
hidden_path: Option <PathBuf>,
|
hidden_path: Option <PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unwrap a request from PTTH format and pass it into file_server.
|
||||||
|
// When file_server responds, wrap it back up and stream it to the relay.
|
||||||
|
|
||||||
async fn handle_one_req (
|
async fn handle_one_req (
|
||||||
state: &Arc <ServerState>,
|
state: &Arc <ServerState>,
|
||||||
wrapped_req: http_serde::WrappedRequest
|
wrapped_req: http_serde::WrappedRequest
|
||||||
|
@ -72,7 +75,7 @@ async fn handle_one_req (
|
||||||
|
|
||||||
let response = file_server::serve_all (
|
let response = file_server::serve_all (
|
||||||
&state.handlebars,
|
&state.handlebars,
|
||||||
&state.server_info,
|
&state.instance_metrics,
|
||||||
file_server_root,
|
file_server_root,
|
||||||
parts.method,
|
parts.method,
|
||||||
&parts.uri,
|
&parts.uri,
|
||||||
|
@ -186,10 +189,6 @@ pub async fn run_server (
|
||||||
return Err (ServerError::WeakApiKey);
|
return Err (ServerError::WeakApiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
let server_info = file_server::ServerInfo {
|
|
||||||
server_name: config_file.name.clone (),
|
|
||||||
};
|
|
||||||
|
|
||||||
info! ("Server name is {}", config_file.name);
|
info! ("Server name is {}", config_file.name);
|
||||||
info! ("Tripcode is {}", config_file.tripcode ());
|
info! ("Tripcode is {}", config_file.tripcode ());
|
||||||
|
|
||||||
|
@ -202,13 +201,15 @@ pub async fn run_server (
|
||||||
.build ().map_err (ServerError::CantBuildHttpClient)?;
|
.build ().map_err (ServerError::CantBuildHttpClient)?;
|
||||||
let handlebars = file_server::load_templates (&asset_root)?;
|
let handlebars = file_server::load_templates (&asset_root)?;
|
||||||
|
|
||||||
|
let instance_metrics = file_server::metrics::InstanceMetrics::new (config_file.name);
|
||||||
|
|
||||||
let state = Arc::new (ServerState {
|
let state = Arc::new (ServerState {
|
||||||
config: Config {
|
config: Config {
|
||||||
relay_url: config_file.relay_url,
|
relay_url: config_file.relay_url,
|
||||||
file_server_root: config_file.file_server_root,
|
file_server_root: config_file.file_server_root,
|
||||||
},
|
},
|
||||||
handlebars,
|
handlebars,
|
||||||
server_info,
|
instance_metrics,
|
||||||
client,
|
client,
|
||||||
hidden_path,
|
hidden_path,
|
||||||
});
|
});
|
||||||
|
@ -233,7 +234,7 @@ pub async fn run_server (
|
||||||
|
|
||||||
debug! ("http_listen");
|
debug! ("http_listen");
|
||||||
|
|
||||||
let req_req = state.client.get (&format! ("{}/http_listen/{}", state.config.relay_url, config_file.name)).send ();
|
let req_req = state.client.get (&format! ("{}/http_listen/{}", state.config.relay_url, state.instance_metrics.server_name)).send ();
|
||||||
|
|
||||||
let err_backoff_delay = std::cmp::min (30_000, backoff_delay * 2 + 500);
|
let err_backoff_delay = std::cmp::min (30_000, backoff_delay * 2 + 500);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue