diff --git a/ptth_handlebars/relay_server_list.html b/ptth_handlebars/relay_server_list.html
index 6dd6c4b..ca4fac0 100644
--- a/ptth_handlebars/relay_server_list.html
+++ b/ptth_handlebars/relay_server_list.html
@@ -26,7 +26,7 @@
{{#if servers}}
{{#each servers}}
{{/each}}
{{else}}
diff --git a/src/bin/file_server.rs b/src/bin/file_server.rs
index 0c5a8ce..fb3fabf 100644
--- a/src/bin/file_server.rs
+++ b/src/bin/file_server.rs
@@ -20,6 +20,7 @@ use hyper::{
use ptth::{
http_serde::RequestParts,
+ prefix_match,
server::file_server,
};
@@ -33,16 +34,6 @@ fn status_reply > (status: StatusCode, b: B)
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 , state: Arc >)
-> Result , Infallible>
{
@@ -61,7 +52,7 @@ async fn handle_all (req: Request , state: Arc >)
_ => return Ok (status_reply (StatusCode::BAD_REQUEST, "Bad request")),
};
- let ptth_resp = file_server::serve_all (state.handlebars.clone (), &root, ptth_req).await;
+ let ptth_resp = file_server::serve_all (&state.handlebars, &root, ptth_req.method, &ptth_req.uri, &ptth_req.headers).await;
let mut resp = Response::builder ()
.status (StatusCode::from (ptth_resp.parts.status_code));
diff --git a/src/lib.rs b/src/lib.rs
index fecadc7..369d97b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,6 +13,17 @@ pub const PTTH_MAGIC_HEADER: &str = "X-PTTH-2LJYXWC4";
pub mod relay;
pub mod server;
+pub fn prefix_match <'a> (hay: &'a str, needle: &str) -> Option <&'a str>
+{
+ if hay.starts_with (needle) {
+ Some (&hay [needle.len ()..])
+ }
+ else {
+ None
+ }
+}
+
+
#[cfg (test)]
mod tests {
use std::{
@@ -89,7 +100,7 @@ mod tests {
assert_eq! (resp, "Relay is up\n");
- let resp = client.get (&format! ("{}/servers/{}/COPYING", relay_url, server_name))
+ let resp = client.get (&format! ("{}/servers/{}/files/COPYING", relay_url, server_name))
.send ().await.unwrap ().bytes ().await.unwrap ();
if blake3::hash (&resp) != blake3::Hash::from ([
diff --git a/src/relay/mod.rs b/src/relay/mod.rs
index d75f861..a4d3693 100644
--- a/src/relay/mod.rs
+++ b/src/relay/mod.rs
@@ -33,6 +33,7 @@ use tokio::{
use crate::{
http_serde,
+ prefix_match,
};
/*
@@ -291,16 +292,6 @@ async fn handle_http_request (
}
}
-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 , state: Arc )
-> Result , Infallible>
{
diff --git a/src/server/file_server.rs b/src/server/file_server.rs
index 5d57114..7798121 100644
--- a/src/server/file_server.rs
+++ b/src/server/file_server.rs
@@ -2,11 +2,11 @@
use std::{
cmp::{min, max},
+ collections::*,
convert::{Infallible, TryInto},
error::Error,
io::SeekFrom,
path::{Path, PathBuf},
- sync::Arc,
};
use handlebars::Handlebars;
@@ -99,7 +99,7 @@ async fn read_dir_entry (entry: DirEntry) -> TemplateDirEntry
use std::borrow::Cow;
async fn serve_dir (
- handlebars: Arc >,
+ handlebars: &Handlebars <'static>,
path: Cow <'_, str>,
mut dir: ReadDir
) -> http_serde::Response
@@ -226,34 +226,33 @@ async fn serve_error (
}
pub async fn serve_all (
- handlebars: Arc >,
+ handlebars: &Handlebars <'static>,
root: &Path,
- parts: http_serde::RequestParts
+ method: http_serde::Method,
+ uri: &str,
+ headers: &HashMap >,
)
-> http_serde::Response
{
- println! ("Client requested {}", parts.uri);
+ println! ("Client requested {}", uri);
let mut range_start = None;
let mut range_end = None;
- for (k, v) in parts.headers.iter () {
+ if let Some (v) = headers.get ("range") {
let v = std::str::from_utf8 (v).unwrap ();
- //println! ("{}: {}", k, v);
- if k == "range" {
- let (start, end) = parse_range_header (v);
- range_start = start;
- range_end = end;
- }
+ let (start, end) = parse_range_header (v);
+ range_start = start;
+ range_end = end;
}
- let should_send_body = matches! (&parts.method, http_serde::Method::Get);
+ let should_send_body = matches! (&method, http_serde::Method::Get);
use percent_encoding::*;
// TODO: There is totally a dir traversal attack in here somewhere
- let encoded_path = &parts.uri [1..];
+ let encoded_path = &uri [1..];
let path_s = percent_decode (encoded_path.as_bytes ()).decode_utf8 ().unwrap ();
let path = Path::new (&*path_s);
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 3238f5e..8f3acc0 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -15,14 +15,29 @@ use tokio::{
time::delay_for,
};
-use crate::http_serde;
+use crate::{
+ http_serde,
+ prefix_match,
+};
pub mod file_server;
+struct ServerState {
+ opt: Opt,
+ handlebars: Handlebars <'static>,
+ client: Client,
+}
+
+fn status_reply (c: http_serde::StatusCode, body: &str) -> http_serde::Response
+{
+ let mut r = http_serde::Response::default ();
+ r.status_code (c)
+ .body_bytes (body.as_bytes ().to_vec ());
+ r
+}
+
async fn handle_req_resp <'a> (
- opt: &'a Opt,
- handlebars: Arc >,
- client: Arc ,
+ state: Arc ,
req_resp: reqwest::Response
) {
//println! ("Step 1");
@@ -40,17 +55,26 @@ async fn handle_req_resp <'a> (
};
for wrapped_req in wrapped_reqs.into_iter () {
- let handlebars = handlebars.clone ();
- let opt = opt.clone ();
- let client = client.clone ();
+ let state = state.clone ();
tokio::spawn (async move {
let (req_id, parts) = (wrapped_req.id, wrapped_req.req);
- let response = file_server::serve_all (handlebars, &opt.file_server_root, parts).await;
+ let response = if let Some (uri) = prefix_match (&parts.uri, "/files") {
+ file_server::serve_all (
+ &state.handlebars,
+ &state.opt.file_server_root,
+ parts.method,
+ uri,
+ &parts.headers
+ ).await
+ }
+ else {
+ status_reply (http_serde::StatusCode::NotFound, "404 Not Found")
+ };
- let mut resp_req = client
- .post (&format! ("{}/7ZSFUKGV_http_response/{}", opt.relay_url, req_id))
+ let mut resp_req = state.client
+ .post (&format! ("{}/7ZSFUKGV_http_response/{}", state.opt.relay_url, req_id))
.header (crate::PTTH_MAGIC_HEADER, base64::encode (rmp_serde::to_vec (&response.parts).unwrap ()));
if let Some (body) = response.body {
@@ -89,12 +113,16 @@ pub async fn main (config_file: ConfigFile, opt: Opt)
let mut headers = reqwest::header::HeaderMap::new ();
headers.insert ("X-ApiKey", config_file.api_key.try_into ().unwrap ());
- // TODO: (FN46S2M2) Combine these Arcs
- let client = Arc::new (Client::builder ()
+ let client = Client::builder ()
.default_headers (headers)
- .build ().unwrap ());
- let opt = Arc::new (opt);
- let handlebars = Arc::new (file_server::load_templates ()?);
+ .build ().unwrap ();
+ let handlebars = file_server::load_templates ()?;
+
+ let state = Arc::new (ServerState {
+ opt,
+ handlebars,
+ client,
+ });
let mut backoff_delay = 0;
@@ -103,7 +131,7 @@ pub async fn main (config_file: ConfigFile, opt: Opt)
delay_for (Duration::from_millis (backoff_delay)).await;
}
- let req_req = client.get (&format! ("{}/7ZSFUKGV_http_listen/{}", opt.relay_url, config_file.name));
+ let req_req = state.client.get (&format! ("{}/7ZSFUKGV_http_listen/{}", state.opt.relay_url, config_file.name));
let err_backoff_delay = std::cmp::min (30_000, backoff_delay * 2 + 500);
@@ -129,12 +157,10 @@ pub async fn main (config_file: ConfigFile, opt: Opt)
// Spawn another task for each request so we can
// immediately listen for the next connection
- let client = client.clone ();
- let opt = opt.clone ();
- let handlebars = handlebars.clone ();
+ let state = state.clone ();
tokio::spawn (async move {
- handle_req_resp (&opt, handlebars, client, req_resp).await;
+ handle_req_resp (state, req_resp).await;
});
}
}
diff --git a/todo.md b/todo.md
index f97d589..e09fdef 100644
--- a/todo.md
+++ b/todo.md
@@ -1,4 +1,4 @@
-- FN46S2M2 Combine Arcs
+- Server-side hash?
- Log / audit log?
- Prevent directory traversal attacks