Add another layer in the URI to make room for future features
parent
13117e4237
commit
c5ecf4bc88
|
@ -26,7 +26,7 @@
|
|||
{{#if servers}}
|
||||
{{#each servers}}
|
||||
<div>
|
||||
<a class="entry" href="{{this.path}}/">{{this.name}}</a>
|
||||
<a class="entry" href="{{this.path}}/files/">{{this.name}}</a>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
|
|
|
@ -20,6 +20,7 @@ use hyper::{
|
|||
|
||||
use ptth::{
|
||||
http_serde::RequestParts,
|
||||
prefix_match,
|
||||
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 ()
|
||||
}
|
||||
|
||||
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>>)
|
||||
-> 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")),
|
||||
};
|
||||
|
||||
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));
|
||||
|
|
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 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 ([
|
||||
|
|
|
@ -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 <Body>, state: Arc <RelayState>)
|
||||
-> Result <Response <Body>, Infallible>
|
||||
{
|
||||
|
|
|
@ -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 <'static>>,
|
||||
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 <'static>>,
|
||||
handlebars: &Handlebars <'static>,
|
||||
root: &Path,
|
||||
parts: http_serde::RequestParts
|
||||
method: http_serde::Method,
|
||||
uri: &str,
|
||||
headers: &HashMap <String, Vec <u8>>,
|
||||
)
|
||||
-> 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 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);
|
||||
|
|
|
@ -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 <Handlebars <'static>>,
|
||||
client: Arc <Client>,
|
||||
state: Arc <ServerState>,
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue