Add another layer in the URI to make room for future features
parent
13117e4237
commit
c5ecf4bc88
|
@ -26,7 +26,7 @@
|
||||||
{{#if servers}}
|
{{#if servers}}
|
||||||
{{#each servers}}
|
{{#each servers}}
|
||||||
<div>
|
<div>
|
||||||
<a class="entry" href="{{this.path}}/">{{this.name}}</a>
|
<a class="entry" href="{{this.path}}/files/">{{this.name}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -20,6 +20,7 @@ use hyper::{
|
||||||
|
|
||||||
use ptth::{
|
use ptth::{
|
||||||
http_serde::RequestParts,
|
http_serde::RequestParts,
|
||||||
|
prefix_match,
|
||||||
server::file_server,
|
server::file_server,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,16 +34,6 @@ fn status_reply <B: Into <Body>> (status: StatusCode, b: B)
|
||||||
Response::builder ().status (status).body (b.into ()).unwrap ()
|
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 <Body>, state: Arc <ServerState <'static>>)
|
async fn handle_all (req: Request <Body>, state: Arc <ServerState <'static>>)
|
||||||
-> Result <Response <Body>, Infallible>
|
-> Result <Response <Body>, Infallible>
|
||||||
{
|
{
|
||||||
|
@ -61,7 +52,7 @@ async fn handle_all (req: Request <Body>, state: Arc <ServerState <'static>>)
|
||||||
_ => return Ok (status_reply (StatusCode::BAD_REQUEST, "Bad request")),
|
_ => 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 ()
|
let mut resp = Response::builder ()
|
||||||
.status (StatusCode::from (ptth_resp.parts.status_code));
|
.status (StatusCode::from (ptth_resp.parts.status_code));
|
||||||
|
|
13
src/lib.rs
13
src/lib.rs
|
@ -13,6 +13,17 @@ pub const PTTH_MAGIC_HEADER: &str = "X-PTTH-2LJYXWC4";
|
||||||
pub mod relay;
|
pub mod relay;
|
||||||
pub mod server;
|
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)]
|
#[cfg (test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -89,7 +100,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq! (resp, "Relay is up\n");
|
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 ();
|
.send ().await.unwrap ().bytes ().await.unwrap ();
|
||||||
|
|
||||||
if blake3::hash (&resp) != blake3::Hash::from ([
|
if blake3::hash (&resp) != blake3::Hash::from ([
|
||||||
|
|
|
@ -33,6 +33,7 @@ use tokio::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
http_serde,
|
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 <Body>, state: Arc <RelayState>)
|
async fn handle_all (req: Request <Body>, state: Arc <RelayState>)
|
||||||
-> Result <Response <Body>, Infallible>
|
-> Result <Response <Body>, Infallible>
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{min, max},
|
cmp::{min, max},
|
||||||
|
collections::*,
|
||||||
convert::{Infallible, TryInto},
|
convert::{Infallible, TryInto},
|
||||||
error::Error,
|
error::Error,
|
||||||
io::SeekFrom,
|
io::SeekFrom,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use handlebars::Handlebars;
|
use handlebars::Handlebars;
|
||||||
|
@ -99,7 +99,7 @@ async fn read_dir_entry (entry: DirEntry) -> TemplateDirEntry
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
async fn serve_dir (
|
async fn serve_dir (
|
||||||
handlebars: Arc <Handlebars <'static>>,
|
handlebars: &Handlebars <'static>,
|
||||||
path: Cow <'_, str>,
|
path: Cow <'_, str>,
|
||||||
mut dir: ReadDir
|
mut dir: ReadDir
|
||||||
) -> http_serde::Response
|
) -> http_serde::Response
|
||||||
|
@ -226,34 +226,33 @@ async fn serve_error (
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn serve_all (
|
pub async fn serve_all (
|
||||||
handlebars: Arc <Handlebars <'static>>,
|
handlebars: &Handlebars <'static>,
|
||||||
root: &Path,
|
root: &Path,
|
||||||
parts: http_serde::RequestParts
|
method: http_serde::Method,
|
||||||
|
uri: &str,
|
||||||
|
headers: &HashMap <String, Vec <u8>>,
|
||||||
)
|
)
|
||||||
-> http_serde::Response
|
-> http_serde::Response
|
||||||
{
|
{
|
||||||
println! ("Client requested {}", parts.uri);
|
println! ("Client requested {}", uri);
|
||||||
let mut range_start = None;
|
let mut range_start = None;
|
||||||
let mut range_end = 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 ();
|
let v = std::str::from_utf8 (v).unwrap ();
|
||||||
//println! ("{}: {}", k, v);
|
|
||||||
|
|
||||||
if k == "range" {
|
|
||||||
let (start, end) = parse_range_header (v);
|
let (start, end) = parse_range_header (v);
|
||||||
range_start = start;
|
range_start = start;
|
||||||
range_end = end;
|
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::*;
|
use percent_encoding::*;
|
||||||
|
|
||||||
// TODO: There is totally a dir traversal attack in here somewhere
|
// 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_s = percent_decode (encoded_path.as_bytes ()).decode_utf8 ().unwrap ();
|
||||||
let path = Path::new (&*path_s);
|
let path = Path::new (&*path_s);
|
||||||
|
|
|
@ -15,14 +15,29 @@ use tokio::{
|
||||||
time::delay_for,
|
time::delay_for,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::http_serde;
|
use crate::{
|
||||||
|
http_serde,
|
||||||
|
prefix_match,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod file_server;
|
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> (
|
async fn handle_req_resp <'a> (
|
||||||
opt: &'a Opt,
|
state: Arc <ServerState>,
|
||||||
handlebars: Arc <Handlebars <'static>>,
|
|
||||||
client: Arc <Client>,
|
|
||||||
req_resp: reqwest::Response
|
req_resp: reqwest::Response
|
||||||
) {
|
) {
|
||||||
//println! ("Step 1");
|
//println! ("Step 1");
|
||||||
|
@ -40,17 +55,26 @@ async fn handle_req_resp <'a> (
|
||||||
};
|
};
|
||||||
|
|
||||||
for wrapped_req in wrapped_reqs.into_iter () {
|
for wrapped_req in wrapped_reqs.into_iter () {
|
||||||
let handlebars = handlebars.clone ();
|
let state = state.clone ();
|
||||||
let opt = opt.clone ();
|
|
||||||
let client = client.clone ();
|
|
||||||
|
|
||||||
tokio::spawn (async move {
|
tokio::spawn (async move {
|
||||||
let (req_id, parts) = (wrapped_req.id, wrapped_req.req);
|
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
|
let mut resp_req = state.client
|
||||||
.post (&format! ("{}/7ZSFUKGV_http_response/{}", opt.relay_url, req_id))
|
.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 ()));
|
.header (crate::PTTH_MAGIC_HEADER, base64::encode (rmp_serde::to_vec (&response.parts).unwrap ()));
|
||||||
|
|
||||||
if let Some (body) = response.body {
|
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 ();
|
let mut headers = reqwest::header::HeaderMap::new ();
|
||||||
headers.insert ("X-ApiKey", config_file.api_key.try_into ().unwrap ());
|
headers.insert ("X-ApiKey", config_file.api_key.try_into ().unwrap ());
|
||||||
|
|
||||||
// TODO: (FN46S2M2) Combine these Arcs
|
let client = Client::builder ()
|
||||||
let client = Arc::new (Client::builder ()
|
|
||||||
.default_headers (headers)
|
.default_headers (headers)
|
||||||
.build ().unwrap ());
|
.build ().unwrap ();
|
||||||
let opt = Arc::new (opt);
|
let handlebars = file_server::load_templates ()?;
|
||||||
let handlebars = Arc::new (file_server::load_templates ()?);
|
|
||||||
|
let state = Arc::new (ServerState {
|
||||||
|
opt,
|
||||||
|
handlebars,
|
||||||
|
client,
|
||||||
|
});
|
||||||
|
|
||||||
let mut backoff_delay = 0;
|
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;
|
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);
|
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
|
// Spawn another task for each request so we can
|
||||||
// immediately listen for the next connection
|
// immediately listen for the next connection
|
||||||
|
|
||||||
let client = client.clone ();
|
let state = state.clone ();
|
||||||
let opt = opt.clone ();
|
|
||||||
let handlebars = handlebars.clone ();
|
|
||||||
|
|
||||||
tokio::spawn (async move {
|
tokio::spawn (async move {
|
||||||
handle_req_resp (&opt, handlebars, client, req_resp).await;
|
handle_req_resp (state, req_resp).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue