Add another layer in the URI to make room for future features

main
_ 2020-11-01 22:07:55 -06:00
parent 13117e4237
commit c5ecf4bc88
7 changed files with 76 additions and 58 deletions

View File

@ -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}}

View File

@ -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));

View File

@ -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 ([

View File

@ -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>
{

View File

@ -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);

View File

@ -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;
});
}
}

View File

@ -1,4 +1,4 @@
- FN46S2M2 Combine Arcs
- Server-side hash?
- Log / audit log?
- Prevent directory traversal attacks