Compare commits
13 Commits
c5aadbe632
...
98b43d1ba2
Author | SHA1 | Date |
---|---|---|
_ | 98b43d1ba2 | |
_ | 2972e85671 | |
_ | c53ed0d2bd | |
_ | f7b78b8a12 | |
_ | b8d07c526a | |
_ | 4911a37887 | |
_ | 4c79af3f4b | |
_ | 4329562aa3 | |
_ | f44613540e | |
_ | 97fc2c74d4 | |
_ | 30f8bbb0aa | |
_ | 0b10737403 | |
_ | b8c370a0a6 |
File diff suppressed because it is too large
Load Diff
|
@ -23,7 +23,7 @@ exclude = [
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
blake3 = "0.3.7"
|
blake3 = "1.0.0"
|
||||||
tokio = { version = "1.4.0", features = ["full"] }
|
tokio = { version = "1.4.0", features = ["full"] }
|
||||||
tracing-subscriber = "0.2.16"
|
tracing-subscriber = "0.2.16"
|
||||||
tracing = "0.1.25"
|
tracing = "0.1.25"
|
||||||
|
|
|
@ -10,6 +10,7 @@ description = "Common code for the PTTH relay and server"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
anyhow = "1.0.38"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
ctrlc = { version = "3.1.8", features = [ "termination" ] }
|
ctrlc = { version = "3.1.8", features = [ "termination" ] }
|
||||||
futures = "0.3.7"
|
futures = "0.3.7"
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
pub use std::{
|
pub use std::{
|
||||||
|
ffi::OsString,
|
||||||
|
io::Write,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use anyhow::{
|
||||||
|
Context,
|
||||||
|
bail,
|
||||||
|
};
|
||||||
pub use tracing::{
|
pub use tracing::{
|
||||||
debug, error, info, trace, warn,
|
debug, error, info, trace, warn,
|
||||||
instrument,
|
instrument,
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
#![warn (clippy::pedantic)]
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
ffi::OsString,
|
||||||
|
net::SocketAddr,
|
||||||
|
path::{PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
|
use hyper::{
|
||||||
|
Body,
|
||||||
|
Request,
|
||||||
|
Response,
|
||||||
|
Server,
|
||||||
|
service::{
|
||||||
|
make_service_fn,
|
||||||
|
service_fn,
|
||||||
|
},
|
||||||
|
StatusCode,
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use ptth_core::{
|
||||||
|
http_serde::RequestParts,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use ptth_server::{
|
||||||
|
file_server::{
|
||||||
|
self,
|
||||||
|
metrics,
|
||||||
|
FileServer,
|
||||||
|
},
|
||||||
|
load_toml,
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn handle_all (req: Request <Body>, state: Arc <FileServer>)
|
||||||
|
-> anyhow::Result <Response <Body>>
|
||||||
|
{
|
||||||
|
use std::str::FromStr;
|
||||||
|
use hyper::header::HeaderName;
|
||||||
|
|
||||||
|
debug! ("req.uri () = {:?}", req.uri ());
|
||||||
|
|
||||||
|
let path_and_query = req.uri ().path_and_query ().map_or_else (|| req.uri ().path (), http::uri::PathAndQuery::as_str);
|
||||||
|
|
||||||
|
let path_and_query = path_and_query.into ();
|
||||||
|
|
||||||
|
let (parts, _) = req.into_parts ();
|
||||||
|
|
||||||
|
let ptth_req = RequestParts::from_hyper (parts.method, path_and_query, parts.headers)?;
|
||||||
|
|
||||||
|
let ptth_resp = state.serve_all (
|
||||||
|
ptth_req.method,
|
||||||
|
&ptth_req.uri,
|
||||||
|
&ptth_req.headers
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
let mut resp = Response::builder ()
|
||||||
|
.status (StatusCode::from (ptth_resp.parts.status_code));
|
||||||
|
|
||||||
|
for (k, v) in ptth_resp.parts.headers {
|
||||||
|
resp = resp.header (HeaderName::from_str (&k)?, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = ptth_resp.body.map_or_else (Body::empty, |body| {
|
||||||
|
Body::wrap_stream (ReceiverStream::new (body))
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok (resp.body (body)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive (Deserialize)]
|
||||||
|
struct ConfigFile {
|
||||||
|
file_server_root: Option <PathBuf>,
|
||||||
|
name: Option <String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn main (_args: &[OsString]) -> anyhow::Result <()> {
|
||||||
|
let path = PathBuf::from ("./config/ptth_server.toml");
|
||||||
|
|
||||||
|
let file_server_root;
|
||||||
|
let name;
|
||||||
|
|
||||||
|
match load_toml::load::<ConfigFile, _> (&path) {
|
||||||
|
Ok (config_file) => {
|
||||||
|
file_server_root = config_file.file_server_root;
|
||||||
|
name = config_file.name;
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
info! ("No ptth_server.toml file, using default configs");
|
||||||
|
file_server_root = None;
|
||||||
|
name = None;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = name.unwrap_or_else (|| "PTTH File Server".to_string ());
|
||||||
|
|
||||||
|
info! ("file_server_root: {:?}", file_server_root);
|
||||||
|
|
||||||
|
let addr = SocketAddr::from(([0, 0, 0, 0], 4000));
|
||||||
|
info! ("Serving at {:?}", addr);
|
||||||
|
|
||||||
|
let metrics_interval = Arc::new (ArcSwap::default ());
|
||||||
|
|
||||||
|
let interval_writer = Arc::clone (&metrics_interval);
|
||||||
|
tokio::spawn (async move {
|
||||||
|
file_server::metrics::Interval::monitor (interval_writer).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
let state = Arc::new (FileServer::new (
|
||||||
|
file_server_root,
|
||||||
|
&PathBuf::new (),
|
||||||
|
name,
|
||||||
|
metrics_interval,
|
||||||
|
Some (path),
|
||||||
|
)?);
|
||||||
|
|
||||||
|
let make_svc = make_service_fn (|_conn| {
|
||||||
|
let state = state.clone ();
|
||||||
|
|
||||||
|
async {
|
||||||
|
Ok::<_, String> (service_fn (move |req| {
|
||||||
|
let state = state.clone ();
|
||||||
|
|
||||||
|
handle_all (req, state)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let (shutdown_rx, forced_shutdown) = ptth_core::graceful_shutdown::init_with_force ();
|
||||||
|
|
||||||
|
let server = Server::bind (&addr)
|
||||||
|
.serve (make_svc)
|
||||||
|
.with_graceful_shutdown (async move {
|
||||||
|
shutdown_rx.await.ok ();
|
||||||
|
});
|
||||||
|
|
||||||
|
forced_shutdown.wrap_server (server).await??;
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
||||||
|
|
|
@ -1,128 +1,12 @@
|
||||||
#![warn (clippy::pedantic)]
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
net::SocketAddr,
|
iter::FromIterator,
|
||||||
path::PathBuf,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use arc_swap::ArcSwap;
|
|
||||||
use hyper::{
|
|
||||||
Body,
|
|
||||||
Request,
|
|
||||||
Response,
|
|
||||||
Server,
|
|
||||||
service::{
|
|
||||||
make_service_fn,
|
|
||||||
service_fn,
|
|
||||||
},
|
|
||||||
StatusCode,
|
|
||||||
};
|
|
||||||
use serde::Deserialize;
|
|
||||||
use tokio_stream::wrappers::ReceiverStream;
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
use ptth_core::{
|
|
||||||
http_serde::RequestParts,
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use ptth_server::{
|
|
||||||
file_server::{
|
|
||||||
self,
|
|
||||||
metrics,
|
|
||||||
FileServer,
|
|
||||||
},
|
|
||||||
load_toml,
|
|
||||||
};
|
|
||||||
|
|
||||||
async fn handle_all (req: Request <Body>, state: Arc <FileServer>)
|
|
||||||
-> anyhow::Result <Response <Body>>
|
|
||||||
{
|
|
||||||
use std::str::FromStr;
|
|
||||||
use hyper::header::HeaderName;
|
|
||||||
|
|
||||||
debug! ("req.uri () = {:?}", req.uri ());
|
|
||||||
|
|
||||||
let path_and_query = req.uri ().path_and_query ().map_or_else (|| req.uri ().path (), http::uri::PathAndQuery::as_str);
|
|
||||||
|
|
||||||
let path_and_query = path_and_query.into ();
|
|
||||||
|
|
||||||
let (parts, _) = req.into_parts ();
|
|
||||||
|
|
||||||
let ptth_req = RequestParts::from_hyper (parts.method, path_and_query, parts.headers)?;
|
|
||||||
|
|
||||||
let ptth_resp = state.serve_all (
|
|
||||||
ptth_req.method,
|
|
||||||
&ptth_req.uri,
|
|
||||||
&ptth_req.headers
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
let mut resp = Response::builder ()
|
|
||||||
.status (StatusCode::from (ptth_resp.parts.status_code));
|
|
||||||
|
|
||||||
for (k, v) in ptth_resp.parts.headers {
|
|
||||||
resp = resp.header (HeaderName::from_str (&k)?, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
let body = ptth_resp.body.map_or_else (Body::empty, |body| {
|
|
||||||
Body::wrap_stream (ReceiverStream::new (body))
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok (resp.body (body)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive (Deserialize)]
|
|
||||||
pub struct ConfigFile {
|
|
||||||
pub file_server_root: Option <PathBuf>,
|
|
||||||
pub name: Option <String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main () -> anyhow::Result <()> {
|
async fn main () -> anyhow::Result <()> {
|
||||||
tracing_subscriber::fmt::init ();
|
tracing_subscriber::fmt::init ();
|
||||||
|
|
||||||
let path = PathBuf::from ("./config/ptth_server.toml");
|
let args = Vec::from_iter (std::env::args_os ());
|
||||||
let config_file: ConfigFile = load_toml::load (&path)?;
|
|
||||||
info! ("file_server_root: {:?}", config_file.file_server_root);
|
|
||||||
|
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], 4000));
|
ptth_file_server::main (&args).await
|
||||||
|
|
||||||
let metrics_interval = Arc::new (ArcSwap::default ());
|
|
||||||
|
|
||||||
let interval_writer = Arc::clone (&metrics_interval);
|
|
||||||
tokio::spawn (async move {
|
|
||||||
file_server::metrics::Interval::monitor (interval_writer).await;
|
|
||||||
});
|
|
||||||
|
|
||||||
let state = Arc::new (FileServer::new (
|
|
||||||
config_file.file_server_root,
|
|
||||||
&PathBuf::new (),
|
|
||||||
config_file.name.unwrap_or_else (|| "PTTH File Server".to_string ()),
|
|
||||||
metrics_interval,
|
|
||||||
Some (path),
|
|
||||||
)?);
|
|
||||||
|
|
||||||
let make_svc = make_service_fn (|_conn| {
|
|
||||||
let state = state.clone ();
|
|
||||||
|
|
||||||
async {
|
|
||||||
Ok::<_, String> (service_fn (move |req| {
|
|
||||||
let state = state.clone ();
|
|
||||||
|
|
||||||
handle_all (req, state)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let (shutdown_rx, forced_shutdown) = ptth_core::graceful_shutdown::init_with_force ();
|
|
||||||
|
|
||||||
let server = Server::bind (&addr)
|
|
||||||
.serve (make_svc)
|
|
||||||
.with_graceful_shutdown (async move {
|
|
||||||
shutdown_rx.await.ok ();
|
|
||||||
});
|
|
||||||
|
|
||||||
forced_shutdown.wrap_server (server).await??;
|
|
||||||
|
|
||||||
Ok (())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "ptth_multi_call_server"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Trish"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "AGPL-3.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.38"
|
||||||
|
ptth_file_server = { path = "../ptth_file_server_bin" }
|
||||||
|
ptth_server = { path = "../ptth_server" }
|
||||||
|
quic_demo = { path = "../../prototypes/quic_demo" }
|
||||||
|
tokio = { version = "1.8.1", features = ["full"] }
|
||||||
|
tracing-subscriber = "0.2.16"
|
||||||
|
tracing = "0.1.25"
|
|
@ -0,0 +1,143 @@
|
||||||
|
use std::{
|
||||||
|
ffi::OsString,
|
||||||
|
iter::FromIterator,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive (Clone, Copy, Debug, PartialEq)]
|
||||||
|
enum Subcommand {
|
||||||
|
PtthServer,
|
||||||
|
PtthFileServer,
|
||||||
|
PtthQuicEndServer,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_subcommand (arg: &str) -> Option <Subcommand>
|
||||||
|
{
|
||||||
|
use Subcommand::*;
|
||||||
|
|
||||||
|
let map = vec! [
|
||||||
|
("ptth_server", PtthServer),
|
||||||
|
("ptth_file_server", PtthFileServer),
|
||||||
|
("ptth_quic_end_server", PtthQuicEndServer),
|
||||||
|
];
|
||||||
|
|
||||||
|
let arg = arg.strip_suffix (".exe").unwrap_or (arg);
|
||||||
|
|
||||||
|
for (suffix, subcommand) in &map {
|
||||||
|
if arg.ends_with (suffix) {
|
||||||
|
return Some (*subcommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_args (args: &[OsString]) -> anyhow::Result <(Subcommand, &[OsString])>
|
||||||
|
{
|
||||||
|
let arg_0 = match args.get (0) {
|
||||||
|
Some (x) => x,
|
||||||
|
None => anyhow::bail! ("arg 0 must be the exe name"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let arg_0 = arg_0.to_str ().ok_or_else (|| anyhow::anyhow! ("arg 0 should be valid UTF-8"))?;
|
||||||
|
match parse_subcommand (arg_0) {
|
||||||
|
Some (x) => return Ok ((x, args)),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg_1 = match args.get (1) {
|
||||||
|
Some (x) => x,
|
||||||
|
None => anyhow::bail! ("arg 1 must be the subcommand if arg 0 is not"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let arg_1 = arg_1.to_str ().ok_or_else (|| anyhow::anyhow! ("arg 1 subcommand should be valid UTF-8"))?;
|
||||||
|
match parse_subcommand (arg_1) {
|
||||||
|
Some (x) => return Ok ((x, &args [1..])),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::bail! ("Subcommand must be either arg 0 (exe name) or arg 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main () -> anyhow::Result <()> {
|
||||||
|
tracing_subscriber::fmt::init ();
|
||||||
|
|
||||||
|
let args = Vec::from_iter (std::env::args_os ());
|
||||||
|
|
||||||
|
let (subcommand, args) = parse_args (&args)?;
|
||||||
|
match subcommand {
|
||||||
|
Subcommand::PtthServer => ptth_server::executable::main (&args).await,
|
||||||
|
Subcommand::PtthFileServer => ptth_file_server::main (&args).await,
|
||||||
|
Subcommand::PtthQuicEndServer => quic_demo::executable_end_server::main (&args).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg (test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multi_call () -> anyhow::Result <()> {
|
||||||
|
let negative_cases = vec! [
|
||||||
|
vec! [],
|
||||||
|
vec! ["invalid_exe_name"],
|
||||||
|
vec! ["ptth_multi_call_server"],
|
||||||
|
vec! ["ptth_server.ex"],
|
||||||
|
vec! ["ptth_multi_call_server", "invalid_subcommand"],
|
||||||
|
];
|
||||||
|
|
||||||
|
for input in &negative_cases {
|
||||||
|
let input: Vec <_> = input.iter ().map (OsString::from).collect ();
|
||||||
|
|
||||||
|
let actual = parse_args (&input);
|
||||||
|
assert! (actual.is_err ());
|
||||||
|
}
|
||||||
|
|
||||||
|
let positive_cases = vec! [
|
||||||
|
(vec! ["ptth_server.exe"], (Subcommand::PtthServer, vec! ["ptth_server.exe"])),
|
||||||
|
(vec! ["ptth_server"], (Subcommand::PtthServer, vec! ["ptth_server"])),
|
||||||
|
(vec! ["ptth_server", "--help"], (Subcommand::PtthServer, vec! ["ptth_server", "--help"])),
|
||||||
|
(vec! ["ptth_file_server"], (Subcommand::PtthFileServer, vec! ["ptth_file_server"])),
|
||||||
|
(vec! ["ptth_quic_end_server", "--help"], (Subcommand::PtthQuicEndServer, vec! ["ptth_quic_end_server", "--help"])),
|
||||||
|
(vec! ["ptth_multi_call_server", "ptth_server"], (Subcommand::PtthServer, vec! ["ptth_server"])),
|
||||||
|
(
|
||||||
|
vec! [
|
||||||
|
"ptth_multi_call_server",
|
||||||
|
"ptth_server",
|
||||||
|
"--help"
|
||||||
|
],
|
||||||
|
(
|
||||||
|
Subcommand::PtthServer,
|
||||||
|
vec! [
|
||||||
|
"ptth_server",
|
||||||
|
"--help"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
vec! [
|
||||||
|
"invalid_exe_name",
|
||||||
|
"ptth_server",
|
||||||
|
"--help"
|
||||||
|
],
|
||||||
|
(
|
||||||
|
Subcommand::PtthServer,
|
||||||
|
vec! [
|
||||||
|
"ptth_server",
|
||||||
|
"--help"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (input, (expected_subcommand, expected_args)) in &positive_cases {
|
||||||
|
let input: Vec <_> = input.iter ().map (OsString::from).collect ();
|
||||||
|
|
||||||
|
let (actual_subcommand, actual_args) = parse_args (&input)?;
|
||||||
|
assert_eq! (expected_subcommand, &actual_subcommand);
|
||||||
|
assert_eq! (expected_args, actual_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ description = "The PTTH relay"
|
||||||
|
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
blake3 = "0.3.7"
|
blake3 = "1.0.0"
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
dashmap = "4.0.2"
|
dashmap = "4.0.2"
|
||||||
|
|
|
@ -17,7 +17,7 @@ aho-corasick = "0.7.15"
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
arc-swap = "1.2.0"
|
arc-swap = "1.2.0"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
blake3 = "0.3.7"
|
blake3 = "1.0.0"
|
||||||
chrono = {version = "0.4.19", features = ["serde"]}
|
chrono = {version = "0.4.19", features = ["serde"]}
|
||||||
futures = "0.3.7"
|
futures = "0.3.7"
|
||||||
handlebars = "3.5.1"
|
handlebars = "3.5.1"
|
||||||
|
@ -28,6 +28,7 @@ pulldown-cmark = { version = "0.8.0", optional = true }
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
regex = "1.4.1"
|
regex = "1.4.1"
|
||||||
rmp-serde = "0.15.5"
|
rmp-serde = "0.15.5"
|
||||||
|
rust-embed = "6.2.0"
|
||||||
rusty_ulid = "0.10.1"
|
rusty_ulid = "0.10.1"
|
||||||
serde = {version = "1.0.117", features = ["derive"]}
|
serde = {version = "1.0.117", features = ["derive"]}
|
||||||
serde_json = "1.0.60"
|
serde_json = "1.0.60"
|
||||||
|
|
|
@ -1,126 +1,12 @@
|
||||||
#![warn (clippy::pedantic)]
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
iter::FromIterator,
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
use ptth_server::{
|
|
||||||
load_toml,
|
|
||||||
prelude::*,
|
|
||||||
run_server,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive (Debug, StructOpt)]
|
|
||||||
struct Opt {
|
|
||||||
#[structopt (long)]
|
|
||||||
auto_gen_key: bool,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
throttle_upload: bool,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
file_server_root: Option <PathBuf>,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
asset_root: Option <PathBuf>,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
config_path: Option <PathBuf>,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
name: Option <String>,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
print_tripcode: bool,
|
|
||||||
|
|
||||||
#[structopt (long)]
|
|
||||||
relay_url: Option <String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive (Default, serde::Deserialize)]
|
|
||||||
pub struct ConfigFile {
|
|
||||||
pub name: Option <String>,
|
|
||||||
pub api_key: String,
|
|
||||||
pub relay_url: Option <String>,
|
|
||||||
pub file_server_root: Option <PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_and_save_key (path: &Path) -> anyhow::Result <()> {
|
|
||||||
let api_key = ptth_core::gen_key ();
|
|
||||||
|
|
||||||
let mut f = File::create (path).with_context (|| format! ("Can't create config file `{:?}`", path))?;
|
|
||||||
|
|
||||||
#[cfg (unix)]
|
|
||||||
{
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
|
|
||||||
let metadata = f.metadata ()?;
|
|
||||||
let mut permissions = metadata.permissions ();
|
|
||||||
permissions.set_mode (0o600);
|
|
||||||
f.set_permissions (permissions)?;
|
|
||||||
}
|
|
||||||
#[cfg (not (unix))]
|
|
||||||
{
|
|
||||||
tracing::warn! ("Error VR6VW5QT: API keys aren't protected from clients on non-Unix OSes yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
f.write_all (format! ("api_key = \"{}\"\n", api_key).as_bytes ())?;
|
|
||||||
|
|
||||||
Ok (())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main () -> Result <(), anyhow::Error> {
|
async fn main () -> anyhow::Result <()> {
|
||||||
tracing_subscriber::fmt::init ();
|
tracing_subscriber::fmt::init ();
|
||||||
|
|
||||||
let opt = Opt::from_args ();
|
let args = Vec::from_iter (std::env::args_os ());
|
||||||
let asset_root = opt.asset_root;
|
|
||||||
|
|
||||||
let path = opt.config_path.clone ().unwrap_or_else (|| PathBuf::from ("./config/ptth_server.toml"));
|
ptth_server::executable::main (&args).await
|
||||||
|
|
||||||
let config_file: ConfigFile = if opt.auto_gen_key {
|
|
||||||
// If we're in autonomous mode, try harder to fix things
|
|
||||||
|
|
||||||
match load_toml::load (&path) {
|
|
||||||
Err (_) => {
|
|
||||||
gen_and_save_key (&path)?;
|
|
||||||
|
|
||||||
load_toml::load (&path)?
|
|
||||||
},
|
|
||||||
Ok (x) => x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
match load_toml::load (&path) {
|
|
||||||
Err (ptth_server::errors::LoadTomlError::Io (_)) => bail! ("API key not provided in config file and auto-gen-key not provided"),
|
|
||||||
Ok (x) => x,
|
|
||||||
Err (e) => return Err (e.into ()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let config_file = ptth_server::ConfigFile {
|
|
||||||
name: opt.name.or (config_file.name).ok_or (anyhow::anyhow! ("`name` must be provided in command line or config file"))?,
|
|
||||||
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),
|
|
||||||
throttle_upload: opt.throttle_upload,
|
|
||||||
};
|
|
||||||
|
|
||||||
if opt.print_tripcode {
|
|
||||||
println! (r#"name = "{}""#, config_file.name);
|
|
||||||
println! (r#"tripcode = "{}""#, config_file.tripcode ());
|
|
||||||
return Ok (());
|
|
||||||
}
|
|
||||||
|
|
||||||
run_server (
|
|
||||||
config_file,
|
|
||||||
ptth_core::graceful_shutdown::init (),
|
|
||||||
Some (path),
|
|
||||||
asset_root
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
Ok (())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,7 @@ async fn serve_dir_json (
|
||||||
|
|
||||||
#[instrument (level = "debug", skip (f))]
|
#[instrument (level = "debug", skip (f))]
|
||||||
async fn serve_file (
|
async fn serve_file (
|
||||||
|
uri: &str,
|
||||||
mut f: File,
|
mut f: File,
|
||||||
client_wants_body: bool,
|
client_wants_body: bool,
|
||||||
range: range::ValidParsed,
|
range: range::ValidParsed,
|
||||||
|
@ -211,6 +212,12 @@ async fn serve_file (
|
||||||
response.header (String::from ("content-range"), format! ("bytes {}-{}/{}", range.start, range.end - 1, range.end).into_bytes ());
|
response.header (String::from ("content-range"), format! ("bytes {}-{}/{}", range.start, range.end - 1, range.end).into_bytes ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guess MIME type based on the URI so that we can serve web games
|
||||||
|
|
||||||
|
if uri.ends_with (".js") {
|
||||||
|
response.header ("content-type".into (), b"application/javascript".to_vec ());
|
||||||
|
}
|
||||||
|
|
||||||
response.content_length = Some (content_length);
|
response.content_length = Some (content_length);
|
||||||
|
|
||||||
if let Some (body) = body {
|
if let Some (body) = body {
|
||||||
|
@ -402,7 +409,7 @@ impl FileServer {
|
||||||
file,
|
file,
|
||||||
send_body,
|
send_body,
|
||||||
range,
|
range,
|
||||||
}) => serve_file (file.into_inner (), send_body, range, headers.get ("if-none-match").map (|v| &v[..])).await?,
|
}) => serve_file (uri, file.into_inner (), send_body, range, headers.get ("if-none-match").map (|v| &v[..])).await?,
|
||||||
MarkdownErr (e) => {
|
MarkdownErr (e) => {
|
||||||
#[cfg (feature = "markdown")]
|
#[cfg (feature = "markdown")]
|
||||||
{
|
{
|
||||||
|
@ -429,20 +436,26 @@ impl FileServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_templates (
|
fn load_templates (
|
||||||
asset_root: &Path
|
_asset_root: &Path
|
||||||
)
|
)
|
||||||
-> Result <Handlebars <'static>, anyhow::Error>
|
-> anyhow::Result <Handlebars <'static>>
|
||||||
{
|
{
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
#[derive (RustEmbed)]
|
||||||
|
#[folder = "../../handlebars/server"]
|
||||||
|
struct HandlebarsServer;
|
||||||
|
|
||||||
let mut handlebars = Handlebars::new ();
|
let mut handlebars = Handlebars::new ();
|
||||||
handlebars.set_strict_mode (true);
|
handlebars.set_strict_mode (true);
|
||||||
|
|
||||||
let asset_root = asset_root.join ("handlebars/server");
|
|
||||||
|
|
||||||
for (k, v) in &[
|
for (k, v) in &[
|
||||||
("file_server_dir", "file_server_dir.html"),
|
("file_server_dir", "file_server_dir.html"),
|
||||||
("file_server_root", "file_server_root.html"),
|
("file_server_root", "file_server_root.html"),
|
||||||
] {
|
] {
|
||||||
handlebars.register_template_file (k, asset_root.join (v))?;
|
let asset_file = HandlebarsServer::get (v)
|
||||||
|
.ok_or_else (|| anyhow::anyhow! ("failed to load handlebars template file"))?;
|
||||||
|
let s = std::str::from_utf8 (asset_file.data.as_ref ())?;
|
||||||
|
handlebars.register_template_string (k, s)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok (handlebars)
|
Ok (handlebars)
|
||||||
|
|
|
@ -39,6 +39,17 @@
|
||||||
// False positive on futures::select! macro
|
// False positive on futures::select! macro
|
||||||
#![allow (clippy::mut_mut)]
|
#![allow (clippy::mut_mut)]
|
||||||
|
|
||||||
|
pub mod errors;
|
||||||
|
|
||||||
|
/// In-process file server module with byte range and ETag support
|
||||||
|
pub mod file_server;
|
||||||
|
|
||||||
|
/// Load and de-serialize structures from TOML, with a size limit
|
||||||
|
/// and checking permissions (On Unix)
|
||||||
|
pub mod load_toml;
|
||||||
|
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -58,21 +69,10 @@ use tokio_stream::wrappers::ReceiverStream;
|
||||||
|
|
||||||
use ptth_core::{
|
use ptth_core::{
|
||||||
http_serde,
|
http_serde,
|
||||||
prelude::*,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod errors;
|
|
||||||
|
|
||||||
/// In-process file server module with byte range and ETag support
|
|
||||||
pub mod file_server;
|
|
||||||
|
|
||||||
/// Load and de-serialize structures from TOML, with a size limit
|
|
||||||
/// and checking permissions (On Unix)
|
|
||||||
pub mod load_toml;
|
|
||||||
|
|
||||||
pub mod prelude;
|
|
||||||
|
|
||||||
use errors::ServerError;
|
use errors::ServerError;
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
// file_server: file_server::FileServer,
|
// file_server: file_server::FileServer,
|
||||||
|
@ -480,6 +480,128 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod executable {
|
||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use super::{
|
||||||
|
load_toml,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn main (args: &[OsString]) -> anyhow::Result <()> {
|
||||||
|
let opt = Opt::from_iter (args);
|
||||||
|
let asset_root = opt.asset_root;
|
||||||
|
|
||||||
|
let path = opt.config_path.clone ().unwrap_or_else (|| PathBuf::from ("./config/ptth_server.toml"));
|
||||||
|
|
||||||
|
let config_file: ConfigFile = if opt.auto_gen_key {
|
||||||
|
// If we're in autonomous mode, try harder to fix things
|
||||||
|
|
||||||
|
match load_toml::load (&path) {
|
||||||
|
Err (_) => {
|
||||||
|
gen_and_save_key (&path)?;
|
||||||
|
|
||||||
|
load_toml::load (&path)?
|
||||||
|
},
|
||||||
|
Ok (x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
match load_toml::load (&path) {
|
||||||
|
Err (super::errors::LoadTomlError::Io (_)) => bail! ("API key not provided in config file and auto-gen-key not provided"),
|
||||||
|
Ok (x) => x,
|
||||||
|
Err (e) => return Err (e.into ()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let config_file = super::ConfigFile {
|
||||||
|
name: opt.name.or (config_file.name).ok_or (anyhow::anyhow! ("`name` must be provided in command line or config file"))?,
|
||||||
|
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),
|
||||||
|
throttle_upload: opt.throttle_upload,
|
||||||
|
};
|
||||||
|
|
||||||
|
if opt.print_tripcode {
|
||||||
|
println! (r#"name = "{}""#, config_file.name);
|
||||||
|
println! (r#"tripcode = "{}""#, config_file.tripcode ());
|
||||||
|
return Ok (());
|
||||||
|
}
|
||||||
|
|
||||||
|
super::run_server (
|
||||||
|
config_file,
|
||||||
|
ptth_core::graceful_shutdown::init (),
|
||||||
|
Some (path),
|
||||||
|
asset_root
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive (Debug, StructOpt)]
|
||||||
|
struct Opt {
|
||||||
|
#[structopt (long)]
|
||||||
|
auto_gen_key: bool,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
throttle_upload: bool,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
file_server_root: Option <PathBuf>,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
asset_root: Option <PathBuf>,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
config_path: Option <PathBuf>,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
name: Option <String>,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
print_tripcode: bool,
|
||||||
|
|
||||||
|
#[structopt (long)]
|
||||||
|
relay_url: Option <String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive (Default, serde::Deserialize)]
|
||||||
|
struct ConfigFile {
|
||||||
|
pub name: Option <String>,
|
||||||
|
pub api_key: String,
|
||||||
|
pub relay_url: Option <String>,
|
||||||
|
pub file_server_root: Option <PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_and_save_key (path: &Path) -> anyhow::Result <()> {
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
let api_key = ptth_core::gen_key ();
|
||||||
|
|
||||||
|
let mut f = File::create (path).with_context (|| format! ("Can't create config file `{:?}`", path))?;
|
||||||
|
|
||||||
|
#[cfg (unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let metadata = f.metadata ()?;
|
||||||
|
let mut permissions = metadata.permissions ();
|
||||||
|
permissions.set_mode (0o600);
|
||||||
|
f.set_permissions (permissions)?;
|
||||||
|
}
|
||||||
|
#[cfg (not (unix))]
|
||||||
|
{
|
||||||
|
tracing::warn! ("Error VR6VW5QT: API keys aren't protected from clients on non-Unix OSes yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
f.write_all (format! ("api_key = \"{}\"\n", api_key).as_bytes ())?;
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg (test)]
|
#[cfg (test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,8 +1 @@
|
||||||
pub use std::{
|
pub use ptth_core::prelude::*;
|
||||||
io::Write,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use anyhow::{
|
|
||||||
Context,
|
|
||||||
bail,
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,107 +1,12 @@
|
||||||
use structopt::StructOpt;
|
use std::{
|
||||||
use tokio::net::TcpStream;
|
iter::FromIterator,
|
||||||
|
};
|
||||||
use quic_demo::prelude::*;
|
|
||||||
use protocol::PeerId;
|
|
||||||
|
|
||||||
#[derive (Debug, StructOpt)]
|
|
||||||
struct Opt {
|
|
||||||
#[structopt (long)]
|
|
||||||
relay_addr: Option <String>,
|
|
||||||
#[structopt (long)]
|
|
||||||
server_id: Option <PeerId>,
|
|
||||||
#[structopt (long)]
|
|
||||||
debug_echo: bool,
|
|
||||||
#[structopt (long)]
|
|
||||||
cert_url: Option <String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main () -> anyhow::Result <()> {
|
async fn main () -> anyhow::Result <()> {
|
||||||
tracing_subscriber::fmt::init ();
|
tracing_subscriber::fmt::init ();
|
||||||
|
|
||||||
let opt = Arc::new (Opt::from_args ());
|
let args = Vec::from_iter (std::env::args_os ());
|
||||||
|
|
||||||
let server_cert = match opt.cert_url.as_ref () {
|
quic_demo::executable_end_server::main (&args).await
|
||||||
Some (url) => reqwest::get (url).await?.bytes ().await?,
|
|
||||||
None => tokio::fs::read ("quic_server.crt").await?.into (),
|
|
||||||
};
|
|
||||||
let relay_addr = opt.relay_addr.clone ().unwrap_or_else (|| String::from ("127.0.0.1:30380")).parse ()?;
|
|
||||||
let endpoint = make_client_endpoint ("0.0.0.0:0".parse ()?, &[&server_cert])?;
|
|
||||||
|
|
||||||
trace! ("Connecting to relay server");
|
|
||||||
|
|
||||||
let server_id = opt.server_id.clone ().unwrap_or_else (|| "bogus_server".to_string ());
|
|
||||||
|
|
||||||
let quinn::NewConnection {
|
|
||||||
mut bi_streams,
|
|
||||||
..
|
|
||||||
} = protocol::p4_connect_to_p3 (&endpoint, &relay_addr, &server_id).await?;
|
|
||||||
|
|
||||||
debug! ("Connected to relay server");
|
|
||||||
trace! ("Accepting bi streams from P3");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let (relay_send, relay_recv) = bi_streams.next ().await.ok_or_else (|| anyhow::anyhow! ("Relay server didn't open a bi stream"))??;
|
|
||||||
|
|
||||||
tokio::spawn (handle_bi_stream (Arc::clone (&opt), relay_send, relay_recv));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_bi_stream (
|
|
||||||
opt: Arc <Opt>,
|
|
||||||
relay_send: quinn::SendStream,
|
|
||||||
mut relay_recv: quinn::RecvStream,
|
|
||||||
) -> anyhow::Result <()>
|
|
||||||
{
|
|
||||||
match protocol::p4_accept_p3_stream (&mut relay_recv).await? {
|
|
||||||
protocol::P3ToP4Stream::NewPtthConnection {
|
|
||||||
client_id,
|
|
||||||
..
|
|
||||||
} => handle_new_ptth_connection (opt, relay_send, relay_recv, client_id).await?,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok (())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_new_ptth_connection (
|
|
||||||
opt: Arc <Opt>,
|
|
||||||
mut relay_send: quinn::SendStream,
|
|
||||||
mut relay_recv: quinn::RecvStream,
|
|
||||||
_client_id: String,
|
|
||||||
) -> anyhow::Result <()>
|
|
||||||
{
|
|
||||||
// TODO: Check authorization for P2 --> P4
|
|
||||||
|
|
||||||
protocol::p4_authorize_p2_connection (&mut relay_send).await?;
|
|
||||||
let p4_to_p5_req = protocol::p4_expect_p5_request (&mut relay_recv).await?;
|
|
||||||
|
|
||||||
// TODO: Check authorization for P1 --> P5
|
|
||||||
|
|
||||||
protocol::p4_authorize_p1_connection (&mut relay_send).await?;
|
|
||||||
|
|
||||||
debug! ("Started PTTH connection");
|
|
||||||
|
|
||||||
if opt.debug_echo {
|
|
||||||
relay_send.write (b"Connected to P4=P5 debug echo server\n").await?;
|
|
||||||
debug! ("Relaying bytes using internal debug echo server (P4=P5)");
|
|
||||||
tokio::io::copy (&mut relay_recv, &mut relay_send).await?;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let stream = TcpStream::connect (("127.0.0.1", p4_to_p5_req.port)).await?;
|
|
||||||
let (local_recv, local_send) = stream.into_split ();
|
|
||||||
|
|
||||||
trace! ("Relaying bytes...");
|
|
||||||
|
|
||||||
let ptth_conn = quic_demo::connection::NewConnection {
|
|
||||||
local_send,
|
|
||||||
local_recv,
|
|
||||||
relay_send,
|
|
||||||
relay_recv,
|
|
||||||
}.build ();
|
|
||||||
|
|
||||||
ptth_conn.wait_for_close ().await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok (())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
use protocol::PeerId;
|
||||||
|
|
||||||
|
#[derive (Debug, StructOpt)]
|
||||||
|
struct Opt {
|
||||||
|
#[structopt (long)]
|
||||||
|
relay_addr: Option <String>,
|
||||||
|
#[structopt (long)]
|
||||||
|
server_id: Option <PeerId>,
|
||||||
|
#[structopt (long)]
|
||||||
|
debug_echo: bool,
|
||||||
|
#[structopt (long)]
|
||||||
|
cert_url: Option <String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn main (args: &[OsString]) -> anyhow::Result <()> {
|
||||||
|
let opt = Arc::new (Opt::from_iter (args));
|
||||||
|
|
||||||
|
let server_cert = match opt.cert_url.as_ref () {
|
||||||
|
Some (url) => reqwest::get (url).await?.bytes ().await?,
|
||||||
|
None => tokio::fs::read ("quic_server.crt").await?.into (),
|
||||||
|
};
|
||||||
|
let relay_addr = opt.relay_addr.clone ().unwrap_or_else (|| String::from ("127.0.0.1:30380")).parse ()?;
|
||||||
|
let endpoint = make_client_endpoint ("0.0.0.0:0".parse ()?, &[&server_cert])?;
|
||||||
|
|
||||||
|
trace! ("Connecting to relay server");
|
||||||
|
|
||||||
|
let server_id = opt.server_id.clone ().unwrap_or_else (|| "bogus_server".to_string ());
|
||||||
|
|
||||||
|
let quinn::NewConnection {
|
||||||
|
mut bi_streams,
|
||||||
|
..
|
||||||
|
} = protocol::p4_connect_to_p3 (&endpoint, &relay_addr, &server_id).await?;
|
||||||
|
|
||||||
|
debug! ("Connected to relay server");
|
||||||
|
trace! ("Accepting bi streams from P3");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let (relay_send, relay_recv) = bi_streams.next ().await.ok_or_else (|| anyhow::anyhow! ("Relay server didn't open a bi stream"))??;
|
||||||
|
|
||||||
|
tokio::spawn (handle_bi_stream (Arc::clone (&opt), relay_send, relay_recv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_bi_stream (
|
||||||
|
opt: Arc <Opt>,
|
||||||
|
relay_send: quinn::SendStream,
|
||||||
|
mut relay_recv: quinn::RecvStream,
|
||||||
|
) -> anyhow::Result <()>
|
||||||
|
{
|
||||||
|
match protocol::p4_accept_p3_stream (&mut relay_recv).await? {
|
||||||
|
protocol::P3ToP4Stream::NewPtthConnection {
|
||||||
|
client_id,
|
||||||
|
..
|
||||||
|
} => handle_new_ptth_connection (opt, relay_send, relay_recv, client_id).await?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_new_ptth_connection (
|
||||||
|
opt: Arc <Opt>,
|
||||||
|
mut relay_send: quinn::SendStream,
|
||||||
|
mut relay_recv: quinn::RecvStream,
|
||||||
|
_client_id: String,
|
||||||
|
) -> anyhow::Result <()>
|
||||||
|
{
|
||||||
|
// TODO: Check authorization for P2 --> P4
|
||||||
|
|
||||||
|
protocol::p4_authorize_p2_connection (&mut relay_send).await?;
|
||||||
|
let p4_to_p5_req = protocol::p4_expect_p5_request (&mut relay_recv).await?;
|
||||||
|
|
||||||
|
// TODO: Check authorization for P1 --> P5
|
||||||
|
|
||||||
|
protocol::p4_authorize_p1_connection (&mut relay_send).await?;
|
||||||
|
|
||||||
|
debug! ("Started PTTH connection");
|
||||||
|
|
||||||
|
if opt.debug_echo {
|
||||||
|
relay_send.write (b"Connected to P4=P5 debug echo server\n").await?;
|
||||||
|
debug! ("Relaying bytes using internal debug echo server (P4=P5)");
|
||||||
|
tokio::io::copy (&mut relay_recv, &mut relay_send).await?;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let stream = TcpStream::connect (("127.0.0.1", p4_to_p5_req.port)).await?;
|
||||||
|
let (local_recv, local_send) = stream.into_split ();
|
||||||
|
|
||||||
|
trace! ("Relaying bytes...");
|
||||||
|
|
||||||
|
let ptth_conn = crate::connection::NewConnection {
|
||||||
|
local_send,
|
||||||
|
local_recv,
|
||||||
|
relay_send,
|
||||||
|
relay_recv,
|
||||||
|
}.build ();
|
||||||
|
|
||||||
|
ptth_conn.wait_for_close ().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod client_proxy;
|
pub mod client_proxy;
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
|
pub mod executable_end_server;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
pub mod quinn_utils;
|
pub mod quinn_utils;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub use std::{
|
pub use std::{
|
||||||
collections::*,
|
collections::*,
|
||||||
|
ffi::OsString,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
![The PTTH logo, a green box sitting on a black conveyor belt. The box has an arrow pointing left, and the text "PTTH", in white. The conveyor belt has an arrow pointing right, in white.](assets/logo-128-pixel.png)
|
![The PTTH logo, a green box sitting on a black conveyor belt. The box has an arrow pointing left, and the text "PTTH", in white. The conveyor belt has an arrow pointing right, in white.](assets/logo-128-pixel.png)
|
||||||
|
|
||||||
|
TODO: "Splitting a server in half" diagram
|
||||||
|
|
||||||
# PTTH
|
# PTTH
|
||||||
|
|
||||||
PTTH lets you run file servers from behind NAT / firewalls.
|
PTTH lets you run file servers from behind NAT / firewalls.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1.50.0
|
1.55.0
|
||||||
|
|
Loading…
Reference in New Issue