Compare commits
9 Commits
cffb888ac8
...
800dbcb019
Author | SHA1 | Date |
---|---|---|
Trisha | 800dbcb019 | |
Trisha | 365c878a90 | |
Trisha | fb9b0c67f5 | |
Trisha | 436adb98ef | |
Trisha | de5338f4f2 | |
Trisha | e6273209f9 | |
Trisha | 81141e2faf | |
Trisha | 8c4e7d484c | |
Trisha | 1d9ef8f510 |
|
@ -246,6 +246,26 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ct-logs"
|
||||
version = "0.8.0"
|
||||
|
@ -345,20 +365,22 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
|||
|
||||
[[package]]
|
||||
name = "fltk"
|
||||
version = "1.2.8"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "889d9b2176b88c6f8f90ba64b0b030e52807ed1d6e416df0c15611225b40cc1d"
|
||||
checksum = "14c92a8adbc0189c9cade37f90bb50b3023cdef1c0c4de7eb5c36238dfdd2a23"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossbeam-channel",
|
||||
"fltk-sys",
|
||||
"paste",
|
||||
"ttf-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fltk-sys"
|
||||
version = "1.2.8"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e38b2f3fb23b4bd46fc492d5d8d099b0bf766a7ab5d18b8424d93089ae934a48"
|
||||
checksum = "cbc9366cd150615afd138f261903e0029f40db720bceabe8e81bc7dac8f9cc11"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
]
|
||||
|
@ -2155,6 +2177,12 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c74c96594835e10fa545e2a51e8709f30b173a092bfd6036ef2cec53376244f3"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.14.0"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::HashMap,
|
||||
collections::*,
|
||||
convert::{Infallible, TryFrom},
|
||||
io::SeekFrom,
|
||||
path::{
|
||||
|
@ -56,6 +56,7 @@ use errors::FileServerError;
|
|||
#[derive (Default)]
|
||||
pub struct Config {
|
||||
pub file_server_root: PathBuf,
|
||||
pub file_server_roots: BTreeMap <String, PathBuf>,
|
||||
}
|
||||
|
||||
pub struct FileServer {
|
||||
|
@ -68,7 +69,7 @@ pub struct FileServer {
|
|||
|
||||
impl FileServer {
|
||||
pub fn new (
|
||||
file_server_root: PathBuf,
|
||||
config: Config,
|
||||
asset_root: &Path,
|
||||
name: String,
|
||||
metrics_interval: Arc <ArcSwap <Option <metrics::Interval>>>,
|
||||
|
@ -76,9 +77,7 @@ impl FileServer {
|
|||
) -> Result <Self, FileServerError>
|
||||
{
|
||||
Ok (Self {
|
||||
config: Config {
|
||||
file_server_root,
|
||||
},
|
||||
config,
|
||||
handlebars: load_templates (asset_root)?,
|
||||
metrics_startup: metrics::Startup::new (name),
|
||||
metrics_interval,
|
||||
|
@ -374,9 +373,12 @@ impl FileServer {
|
|||
resp
|
||||
}
|
||||
|
||||
let root: &std::path::Path = &self.config.file_server_root;
|
||||
let roots = internal::FileRoots {
|
||||
files: &self.config.file_server_root,
|
||||
dirs: &self.config.file_server_roots,
|
||||
};
|
||||
|
||||
Ok (match internal::serve_all (root, method, uri, headers, self.hidden_path.as_deref ()).await? {
|
||||
Ok (match internal::serve_all (roots, method, uri, headers, self.hidden_path.as_deref ()).await? {
|
||||
Favicon => serve_error (StatusCode::NotFound, "Not found\n"),
|
||||
Forbidden => serve_error (StatusCode::Forbidden, "403 Forbidden\n"),
|
||||
MethodNotAllowed => serve_error (StatusCode::MethodNotAllowed, "Unsupported method\n"),
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// human-readable HTML
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::*,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
|
@ -244,11 +244,59 @@ async fn serve_api (
|
|||
Ok (NotFound)
|
||||
}
|
||||
|
||||
#[derive (Clone, Copy)]
|
||||
pub struct FileRoots <'a> {
|
||||
pub files: &'a Path,
|
||||
pub dirs: &'a BTreeMap <String, PathBuf>,
|
||||
}
|
||||
|
||||
struct RoutedPath <'a> {
|
||||
root: &'a Path,
|
||||
path_s: std::borrow::Cow <'a, str>,
|
||||
}
|
||||
|
||||
impl <'a> FileRoots <'a> {
|
||||
fn route (self, input: &'a str) -> Result <Option <RoutedPath>, FileServerError> {
|
||||
// TODO: There is totally a dir traversal attack in here somewhere
|
||||
|
||||
if let Some (path) = input.strip_prefix ("/dirs/") {
|
||||
if let Some ((dir, path)) = path.split_once ('/') {
|
||||
let root = match self.dirs.get (dir) {
|
||||
None => return Ok (None),
|
||||
Some (x) => x,
|
||||
};
|
||||
|
||||
let path_s = percent_decode (path.as_bytes ()).decode_utf8 ().map_err (FileServerError::PathNotUtf8)?;
|
||||
|
||||
return Ok (Some (RoutedPath {
|
||||
root,
|
||||
path_s,
|
||||
}));
|
||||
}
|
||||
else {
|
||||
return Ok (None);
|
||||
}
|
||||
}
|
||||
if let Some (path) = input.strip_prefix ("/files/") {
|
||||
let encoded_path = &path [0..];
|
||||
|
||||
let path_s = percent_decode (encoded_path.as_bytes ()).decode_utf8 ().map_err (FileServerError::PathNotUtf8)?;
|
||||
|
||||
return Ok (Some (RoutedPath {
|
||||
root: self.files,
|
||||
path_s,
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok (None);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the requests internally without knowing anything about PTTH or
|
||||
// HTML / handlebars
|
||||
|
||||
pub async fn serve_all (
|
||||
root: &Path,
|
||||
roots: FileRoots <'_>,
|
||||
method: Method,
|
||||
uri: &str,
|
||||
headers: &HashMap <String, Vec <u8>>,
|
||||
|
@ -283,19 +331,17 @@ pub async fn serve_all (
|
|||
}
|
||||
|
||||
if let Some (path) = path.strip_prefix ("/api") {
|
||||
return serve_api (root, &uri, hidden_path, path).await;
|
||||
return serve_api (roots.files, &uri, hidden_path, path).await;
|
||||
}
|
||||
|
||||
let path = match path.strip_prefix ("/files/") {
|
||||
Some (x) => x,
|
||||
let RoutedPath {
|
||||
root,
|
||||
path_s,
|
||||
} = match roots.route (path)? {
|
||||
None => return Ok (NotFound),
|
||||
Some (x) => x,
|
||||
};
|
||||
|
||||
// TODO: There is totally a dir traversal attack in here somewhere
|
||||
|
||||
let encoded_path = &path [0..];
|
||||
|
||||
let path_s = percent_decode (encoded_path.as_bytes ()).decode_utf8 ().map_err (FileServerError::PathNotUtf8)?;
|
||||
let path = Path::new (&*path_s);
|
||||
|
||||
let full_path = root.join (path);
|
||||
|
|
|
@ -207,9 +207,14 @@ pub struct ConfigFile {
|
|||
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.
|
||||
/// over the relay, under `/files`. If None, the current working dir is used.
|
||||
pub file_server_root: PathBuf,
|
||||
|
||||
/// The file server module will expose these directories to clients under
|
||||
/// `/dirs`. If symlinks can't be used (like on Windows), this allows PTTH
|
||||
/// to serve multiple directories easily.
|
||||
pub file_server_roots: BTreeMap <String, PathBuf>,
|
||||
|
||||
/// For debugging.
|
||||
pub throttle_upload: bool,
|
||||
|
||||
|
@ -226,6 +231,7 @@ impl ConfigFile {
|
|||
api_key,
|
||||
relay_url,
|
||||
file_server_root: PathBuf::from ("."),
|
||||
file_server_roots: Default::default (),
|
||||
throttle_upload: false,
|
||||
client_keys: Default::default (),
|
||||
allow_any_client: true,
|
||||
|
@ -268,6 +274,7 @@ impl Builder {
|
|||
api_key: ptth_core::gen_key (),
|
||||
relay_url,
|
||||
file_server_root: PathBuf::from ("."),
|
||||
file_server_roots: Default::default (),
|
||||
throttle_upload: false,
|
||||
client_keys: Default::default (),
|
||||
allow_any_client: true,
|
||||
|
@ -301,7 +308,8 @@ pub async fn run_server (
|
|||
config_file: ConfigFile,
|
||||
shutdown_oneshot: oneshot::Receiver <()>,
|
||||
hidden_path: Option <PathBuf>,
|
||||
asset_root: Option <PathBuf>
|
||||
asset_root: Option <PathBuf>,
|
||||
hit_counter: Option <mpsc::Sender <()>>,
|
||||
)
|
||||
-> Result <(), ServerError>
|
||||
{
|
||||
|
@ -312,7 +320,10 @@ pub async fn run_server (
|
|||
});
|
||||
|
||||
let file_server = file_server::FileServer::new (
|
||||
config_file.file_server_root.clone (),
|
||||
file_server::Config {
|
||||
file_server_root: config_file.file_server_root.clone (),
|
||||
file_server_roots: config_file.file_server_roots.clone (),
|
||||
},
|
||||
&asset_root.clone ().unwrap_or_else (|| PathBuf::from (".")),
|
||||
config_file.name.clone (),
|
||||
metrics_interval,
|
||||
|
@ -330,8 +341,13 @@ pub async fn run_server (
|
|||
|
||||
let mut spawn_handler = || {
|
||||
let file_server = Arc::clone (&file_server);
|
||||
let hit_counter = hit_counter.clone ();
|
||||
|
||||
|req: http_serde::RequestParts| async move {
|
||||
if let Some (hit_tx) = &hit_counter {
|
||||
eprintln! ("hit_tx.send");
|
||||
hit_tx.send (()).await;
|
||||
}
|
||||
Ok (file_server.serve_all (req.method, &req.uri, &req.headers).await?)
|
||||
}
|
||||
};
|
||||
|
@ -529,6 +545,12 @@ pub mod executable {
|
|||
api_key: config_file.api_key,
|
||||
relay_url: opt.relay_url.or (config_file.relay_url).ok_or (anyhow::anyhow! ("`--relay-url` must be provided in command line or `relay_url` in config file"))?,
|
||||
file_server_root: opt.file_server_root.or (config_file.file_server_root).unwrap_or_else (PathBuf::new),
|
||||
file_server_roots: vec! [
|
||||
("c", "C:/"),
|
||||
("d", "D:/"),
|
||||
].into_iter ()
|
||||
.map (|(k, v)| (String::from (k), PathBuf::from (v)))
|
||||
.collect (),
|
||||
throttle_upload: opt.throttle_upload,
|
||||
allow_any_client: true,
|
||||
client_keys: Default::default (),
|
||||
|
@ -544,7 +566,8 @@ pub mod executable {
|
|||
config_file,
|
||||
ptth_core::graceful_shutdown::init (),
|
||||
Some (path),
|
||||
asset_root
|
||||
asset_root,
|
||||
None,
|
||||
).await?;
|
||||
|
||||
Ok (())
|
||||
|
|
|
@ -6,7 +6,7 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.38"
|
||||
fltk = "1.1.0"
|
||||
fltk = "1.3.1"
|
||||
serde = {version = "1.0.117", features = ["derive"]}
|
||||
tokio = "1.4.0"
|
||||
tracing = "0.1.25"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
collections::*,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
|
@ -13,7 +14,10 @@ use fltk::{
|
|||
};
|
||||
|
||||
use tokio::{
|
||||
sync::oneshot,
|
||||
sync::{
|
||||
mpsc,
|
||||
oneshot,
|
||||
},
|
||||
};
|
||||
|
||||
struct ServerInstance {
|
||||
|
@ -23,6 +27,7 @@ struct ServerInstance {
|
|||
|
||||
#[derive (Clone, Copy)]
|
||||
enum Message {
|
||||
Hit,
|
||||
RunServer,
|
||||
StopServer,
|
||||
}
|
||||
|
@ -42,7 +47,22 @@ fn main ()
|
|||
let app = app::App::default();
|
||||
let mut wind = Window::new (100, 100, 500, 180, "PTTH server");
|
||||
|
||||
let mut gui = Gui::new (fltk_tx);
|
||||
let config_file_opt = ptth_server::load_toml::load::<ConfigFile, _> ("./config/ptth_server.toml").ok ();
|
||||
|
||||
let (hit_tx, mut hit_rx) = mpsc::channel (1);
|
||||
{
|
||||
let fltk_tx = fltk_tx.clone ();
|
||||
|
||||
rt.spawn (async move {
|
||||
eprintln! ("Entering channel task");
|
||||
while let Some (_) = hit_rx.recv ().await {
|
||||
eprintln! ("fltk_tx");
|
||||
fltk_tx.send (Message::Hit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut gui = Gui::new (fltk_tx, config_file_opt.as_ref ());
|
||||
gui.set_server_running (false);
|
||||
|
||||
wind.end ();
|
||||
|
@ -50,25 +70,40 @@ fn main ()
|
|||
|
||||
while app.wait () {
|
||||
match fltk_rx.recv () {
|
||||
Some (Message::Hit) => {
|
||||
gui.hits += 1;
|
||||
gui.refresh_label ();
|
||||
},
|
||||
Some (Message::RunServer) => {
|
||||
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel ();
|
||||
|
||||
let roots = match &config_file_opt {
|
||||
None => Default::default (),
|
||||
Some (cf) => match &cf.file_server_roots {
|
||||
None => Default::default (),
|
||||
Some (x) => x.clone (),
|
||||
},
|
||||
};
|
||||
|
||||
let config_file = ptth_server::ConfigFile {
|
||||
name: gui.input_name.value ().to_string (),
|
||||
api_key: gui.input_api_key.value ().to_string (),
|
||||
relay_url: gui.input_relay_url.value ().to_string (),
|
||||
file_server_root: std::path::PathBuf::from (gui.input_file_server_root.value ()),
|
||||
file_server_root: PathBuf::from (gui.input_file_server_root.value ()),
|
||||
file_server_roots: roots,
|
||||
throttle_upload: false,
|
||||
client_keys: Default::default (),
|
||||
allow_any_client: true,
|
||||
};
|
||||
|
||||
let hit_tx = hit_tx.clone ();
|
||||
let task = rt.spawn (async {
|
||||
if let Err (e) = ptth_server::run_server (
|
||||
config_file,
|
||||
shutdown_rx,
|
||||
None,
|
||||
None
|
||||
None,
|
||||
Some (hit_tx),
|
||||
).await
|
||||
{
|
||||
tracing::error! ("ptth_server crashed: {}", e);
|
||||
|
@ -100,6 +135,9 @@ struct Gui {
|
|||
input_relay_url: Input,
|
||||
input_file_server_root: Input,
|
||||
input_api_key: SecretInput,
|
||||
|
||||
server_is_running: bool,
|
||||
hits: u64,
|
||||
}
|
||||
|
||||
#[derive (Default, serde::Deserialize)]
|
||||
|
@ -108,10 +146,16 @@ pub struct ConfigFile {
|
|||
pub api_key: String,
|
||||
pub relay_url: Option <String>,
|
||||
pub file_server_root: Option <PathBuf>,
|
||||
pub file_server_roots: Option <BTreeMap <String, PathBuf>>,
|
||||
}
|
||||
|
||||
impl Gui {
|
||||
fn new (fltk_tx: app::Sender <Message>) -> Self {
|
||||
fn new (
|
||||
fltk_tx: app::Sender <Message>,
|
||||
config_file_opt: Option <&ConfigFile>,
|
||||
)
|
||||
-> Self
|
||||
{
|
||||
let mut input_name = Input::new (200, 10, 290, 20, "name");
|
||||
input_name.set_value ("my_ptth_server");
|
||||
|
||||
|
@ -134,7 +178,7 @@ impl Gui {
|
|||
but_stop.set_trigger (CallbackTrigger::Changed);
|
||||
but_stop.emit (fltk_tx, Message::StopServer);
|
||||
|
||||
if let Ok (config_file) = ptth_server::load_toml::load::<ConfigFile, _> ("./config/ptth_server.toml")
|
||||
if let Some (config_file) = config_file_opt
|
||||
{
|
||||
if let Some (v) = config_file.name.as_ref () {
|
||||
input_name.set_value (v);
|
||||
|
@ -158,16 +202,16 @@ impl Gui {
|
|||
input_relay_url,
|
||||
input_file_server_root,
|
||||
input_api_key,
|
||||
|
||||
server_is_running: false,
|
||||
hits: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_server_running (&mut self, b: bool) {
|
||||
self.frame.set_label (if b {
|
||||
"Running"
|
||||
}
|
||||
else {
|
||||
"Stopped"
|
||||
});
|
||||
self.server_is_running = b;
|
||||
|
||||
self.refresh_label ();
|
||||
|
||||
set_active (&mut self.but_run, ! b);
|
||||
set_active (&mut self.but_stop, b);
|
||||
|
@ -179,6 +223,18 @@ impl Gui {
|
|||
set_active (&mut self.input_file_server_root, ! b);
|
||||
set_active (&mut self.input_api_key, ! b);
|
||||
}
|
||||
|
||||
fn refresh_label (&mut self) {
|
||||
let s =
|
||||
if self.server_is_running {
|
||||
format! ("Running. Requests: {}", self.hits)
|
||||
}
|
||||
else {
|
||||
"Stopped".to_string ()
|
||||
};
|
||||
|
||||
self.frame.set_label (&s);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_active <W: WidgetExt> (w: &mut W, b: bool) {
|
||||
|
|
Loading…
Reference in New Issue