2020-11-29 18:37:33 +00:00
|
|
|
#![warn (clippy::pedantic)]
|
|
|
|
|
2020-11-29 19:05:28 +00:00
|
|
|
// I don't see the point of writing the type twice if I'm initializing a struct
|
|
|
|
// and the type is already in the struct definition.
|
|
|
|
#![allow (clippy::default_trait_access)]
|
|
|
|
|
2020-11-29 18:37:33 +00:00
|
|
|
// I'm not sure if I like this one
|
|
|
|
#![allow (clippy::enum_glob_use)]
|
|
|
|
|
|
|
|
// I don't see the point in documenting the errors outside of where the
|
|
|
|
// error type is defined.
|
|
|
|
#![allow (clippy::missing_errors_doc)]
|
|
|
|
|
|
|
|
// False positive on futures::select! macro
|
|
|
|
#![allow (clippy::mut_mut)]
|
|
|
|
|
2020-10-30 22:57:36 +00:00
|
|
|
use std::{
|
2020-12-14 07:07:13 +00:00
|
|
|
borrow::Cow,
|
2021-02-20 17:08:38 +00:00
|
|
|
convert::Infallible,
|
2020-10-30 22:57:36 +00:00
|
|
|
net::SocketAddr,
|
2020-11-29 16:58:56 +00:00
|
|
|
path::{Path, PathBuf},
|
2020-11-29 23:24:25 +00:00
|
|
|
sync::Arc,
|
2020-11-06 23:43:52 +00:00
|
|
|
time::Duration,
|
2020-10-30 22:57:36 +00:00
|
|
|
};
|
|
|
|
|
2020-11-29 18:37:33 +00:00
|
|
|
use chrono::{
|
|
|
|
DateTime,
|
|
|
|
SecondsFormat,
|
|
|
|
Utc
|
|
|
|
};
|
2020-11-02 02:07:46 +00:00
|
|
|
use dashmap::DashMap;
|
2021-03-06 21:15:41 +00:00
|
|
|
use futures_util::StreamExt;
|
2020-10-31 20:46:38 +00:00
|
|
|
use handlebars::Handlebars;
|
2020-10-30 22:57:36 +00:00
|
|
|
use hyper::{
|
|
|
|
Body,
|
|
|
|
Method,
|
|
|
|
Request,
|
|
|
|
Response,
|
|
|
|
Server,
|
|
|
|
StatusCode,
|
|
|
|
};
|
|
|
|
use hyper::service::{make_service_fn, service_fn};
|
2020-11-02 03:34:50 +00:00
|
|
|
use serde::{
|
|
|
|
Serialize,
|
|
|
|
};
|
2020-10-30 22:57:36 +00:00
|
|
|
use tokio::{
|
2020-11-06 03:35:55 +00:00
|
|
|
sync::{
|
|
|
|
oneshot,
|
|
|
|
},
|
2020-11-06 23:43:52 +00:00
|
|
|
};
|
2021-03-06 21:15:41 +00:00
|
|
|
use tokio_stream::wrappers::ReceiverStream;
|
2020-10-30 22:57:36 +00:00
|
|
|
|
2020-11-27 00:03:11 +00:00
|
|
|
use ptth_core::{
|
2020-10-30 22:57:36 +00:00
|
|
|
http_serde,
|
2020-11-02 04:07:55 +00:00
|
|
|
prefix_match,
|
2020-11-27 00:03:11 +00:00
|
|
|
prelude::*,
|
2020-10-30 22:57:36 +00:00
|
|
|
};
|
|
|
|
|
2020-11-26 23:30:33 +00:00
|
|
|
pub mod config;
|
2020-11-29 16:58:56 +00:00
|
|
|
pub mod errors;
|
2020-11-26 23:53:03 +00:00
|
|
|
pub mod git_version;
|
2020-12-12 17:11:22 +00:00
|
|
|
pub mod key_validity;
|
2020-12-13 03:29:54 +00:00
|
|
|
mod relay_state;
|
2020-12-12 17:11:22 +00:00
|
|
|
|
2020-12-13 03:42:00 +00:00
|
|
|
mod scraper_api;
|
2020-11-29 23:24:25 +00:00
|
|
|
mod server_endpoint;
|
2020-11-26 23:30:33 +00:00
|
|
|
|
2020-11-29 16:58:56 +00:00
|
|
|
pub use config::Config;
|
|
|
|
pub use errors::*;
|
2020-12-13 03:29:54 +00:00
|
|
|
pub use relay_state::RelayState;
|
2020-11-26 23:30:33 +00:00
|
|
|
|
2020-12-13 03:29:54 +00:00
|
|
|
use relay_state::*;
|
2020-10-30 22:57:36 +00:00
|
|
|
|
2020-11-06 23:43:52 +00:00
|
|
|
fn ok_reply <B: Into <Body>> (b: B)
|
2020-11-29 17:08:33 +00:00
|
|
|
-> Result <Response <Body>, http::Error>
|
2020-11-06 23:43:52 +00:00
|
|
|
{
|
2020-11-29 17:08:33 +00:00
|
|
|
Response::builder ().status (StatusCode::OK).body (b.into ())
|
2020-11-06 23:43:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn error_reply (status: StatusCode, b: &str)
|
2020-11-29 17:08:33 +00:00
|
|
|
-> Result <Response <Body>, http::Error>
|
2020-10-30 22:57:36 +00:00
|
|
|
{
|
2020-11-06 23:43:52 +00:00
|
|
|
Response::builder ()
|
|
|
|
.status (status)
|
|
|
|
.header ("content-type", "text/plain")
|
2020-11-29 17:08:33 +00:00
|
|
|
.body (format! ("{}\n", b).into ())
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
|
|
|
|
2020-11-06 20:55:55 +00:00
|
|
|
// Clients will come here to start requests, and always park for at least
|
|
|
|
// a short amount of time.
|
|
|
|
|
2020-10-30 22:57:36 +00:00
|
|
|
async fn handle_http_request (
|
|
|
|
req: http::request::Parts,
|
|
|
|
uri: String,
|
2020-11-02 02:07:46 +00:00
|
|
|
state: Arc <RelayState>,
|
2020-10-30 22:57:36 +00:00
|
|
|
watcher_code: String
|
|
|
|
)
|
2020-11-29 17:08:33 +00:00
|
|
|
-> Result <Response <Body>, http::Error>
|
2020-10-30 22:57:36 +00:00
|
|
|
{
|
2020-11-25 02:30:57 +00:00
|
|
|
{
|
|
|
|
let config = state.config.read ().await;
|
2020-11-26 23:30:33 +00:00
|
|
|
if ! config.servers.contains_key (&watcher_code) {
|
2020-11-25 02:30:57 +00:00
|
|
|
return error_reply (StatusCode::NOT_FOUND, "Unknown server");
|
|
|
|
}
|
2020-11-02 13:37:08 +00:00
|
|
|
}
|
|
|
|
|
2020-11-02 02:07:46 +00:00
|
|
|
let req = match http_serde::RequestParts::from_hyper (req.method, uri, req.headers) {
|
|
|
|
Ok (x) => x,
|
2020-11-29 18:37:33 +00:00
|
|
|
Err (_) => return error_reply (StatusCode::BAD_REQUEST, "Bad request"),
|
2020-10-30 22:57:36 +00:00
|
|
|
};
|
|
|
|
|
2020-11-02 02:07:46 +00:00
|
|
|
let (tx, rx) = oneshot::channel ();
|
2020-10-30 22:57:36 +00:00
|
|
|
|
2020-11-06 23:43:52 +00:00
|
|
|
let req_id = ulid::Ulid::new ().to_string ();
|
|
|
|
{
|
|
|
|
let response_rendezvous = state.response_rendezvous.read ().await;
|
|
|
|
response_rendezvous.insert (req_id.clone (), tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
trace! ("Created request {}", req_id);
|
2020-10-30 22:57:36 +00:00
|
|
|
|
|
|
|
{
|
2020-11-29 18:37:33 +00:00
|
|
|
use RequestRendezvous::*;
|
|
|
|
|
2020-11-02 02:07:46 +00:00
|
|
|
let mut request_rendezvous = state.request_rendezvous.lock ().await;
|
|
|
|
|
|
|
|
let wrapped = http_serde::WrappedRequest {
|
2020-11-06 23:43:52 +00:00
|
|
|
id: req_id.clone (),
|
2020-11-02 02:07:46 +00:00
|
|
|
req,
|
|
|
|
};
|
|
|
|
|
|
|
|
let new_rendezvous = match request_rendezvous.remove (&watcher_code) {
|
|
|
|
Some (ParkedClients (mut v)) => {
|
2020-11-06 23:43:52 +00:00
|
|
|
debug! ("Parking request {} ({} already queued)", req_id, v.len ());
|
2020-11-02 02:07:46 +00:00
|
|
|
v.push (wrapped);
|
|
|
|
ParkedClients (v)
|
|
|
|
},
|
|
|
|
Some (ParkedServer (s)) => {
|
|
|
|
// If sending to the server fails, queue it
|
|
|
|
|
2020-11-06 23:43:52 +00:00
|
|
|
match s.send (Ok (wrapped)) {
|
|
|
|
Ok (()) => {
|
|
|
|
// TODO: This can actually still fail, if the server
|
|
|
|
// disconnects right as we're sending this.
|
|
|
|
// Then what?
|
|
|
|
|
2020-12-17 01:06:15 +00:00
|
|
|
trace! (
|
2020-11-06 23:43:52 +00:00
|
|
|
"Sending request {} directly to server {}",
|
|
|
|
req_id,
|
|
|
|
watcher_code,
|
|
|
|
);
|
|
|
|
|
|
|
|
ParkedClients (vec! [])
|
|
|
|
},
|
|
|
|
Err (Ok (wrapped)) => {
|
|
|
|
debug! ("Parking request {}", req_id);
|
|
|
|
ParkedClients (vec! [wrapped])
|
|
|
|
},
|
|
|
|
Err (_) => unreachable! (),
|
2020-11-02 02:07:46 +00:00
|
|
|
}
|
|
|
|
},
|
2020-11-06 23:43:52 +00:00
|
|
|
None => {
|
|
|
|
debug! ("Parking request {}", req_id);
|
|
|
|
ParkedClients (vec! [wrapped])
|
|
|
|
},
|
2020-11-02 02:07:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
request_rendezvous.insert (watcher_code, new_rendezvous);
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
|
|
|
|
2021-03-06 21:15:41 +00:00
|
|
|
let timeout = tokio::time::sleep (std::time::Duration::from_secs (30));
|
2020-10-30 22:57:36 +00:00
|
|
|
|
2020-11-02 02:07:46 +00:00
|
|
|
let received = tokio::select! {
|
|
|
|
val = rx => val,
|
|
|
|
() = timeout => {
|
2020-11-06 23:43:52 +00:00
|
|
|
debug! ("Timed out request {}", req_id);
|
|
|
|
return error_reply (StatusCode::GATEWAY_TIMEOUT, "Remote server never responded")
|
2020-11-02 02:07:46 +00:00
|
|
|
},
|
|
|
|
};
|
2020-10-30 22:57:36 +00:00
|
|
|
|
2020-11-06 20:55:55 +00:00
|
|
|
// UKAUFFY4 (Receive half)
|
2020-11-02 02:07:46 +00:00
|
|
|
match received {
|
2020-11-06 23:43:52 +00:00
|
|
|
Ok (Ok ((parts, body))) => {
|
2020-10-30 22:57:36 +00:00
|
|
|
let mut resp = Response::builder ()
|
2020-11-02 02:07:46 +00:00
|
|
|
.status (hyper::StatusCode::from (parts.status_code));
|
2020-10-30 22:57:36 +00:00
|
|
|
|
2020-11-29 18:37:33 +00:00
|
|
|
for (k, v) in parts.headers {
|
2020-10-30 22:57:36 +00:00
|
|
|
resp = resp.header (&k, v);
|
|
|
|
}
|
|
|
|
|
2020-11-06 23:43:52 +00:00
|
|
|
debug! ("Unparked request {}", req_id);
|
|
|
|
|
2020-11-02 02:07:46 +00:00
|
|
|
resp.body (body)
|
2020-10-30 22:57:36 +00:00
|
|
|
},
|
2020-11-29 16:58:56 +00:00
|
|
|
Ok (Err (ShuttingDownError::ShuttingDown)) => {
|
2020-11-06 23:43:52 +00:00
|
|
|
error_reply (StatusCode::GATEWAY_TIMEOUT, "Relay shutting down")
|
|
|
|
},
|
|
|
|
Err (_) => {
|
|
|
|
debug! ("Responder sender dropped for request {}", req_id);
|
|
|
|
error_reply (StatusCode::GATEWAY_TIMEOUT, "Remote server timed out")
|
|
|
|
},
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-25 02:17:08 +00:00
|
|
|
#[derive (Debug, PartialEq)]
|
|
|
|
enum LastSeen {
|
|
|
|
Negative,
|
|
|
|
Connected,
|
|
|
|
Description (String),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mnemonic is "now - last_seen"
|
|
|
|
|
|
|
|
fn pretty_print_last_seen (
|
|
|
|
now: DateTime <Utc>,
|
|
|
|
last_seen: DateTime <Utc>
|
|
|
|
) -> LastSeen
|
|
|
|
{
|
|
|
|
use LastSeen::*;
|
|
|
|
|
|
|
|
let dur = now.signed_duration_since (last_seen);
|
|
|
|
|
|
|
|
if dur < chrono::Duration::zero () {
|
|
|
|
return Negative;
|
|
|
|
}
|
|
|
|
|
|
|
|
if dur.num_minutes () < 1 {
|
|
|
|
return Connected;
|
|
|
|
}
|
|
|
|
|
|
|
|
if dur.num_hours () < 1 {
|
|
|
|
return Description (format! ("{} m ago", dur.num_minutes ()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if dur.num_days () < 1 {
|
|
|
|
return Description (format! ("{} h ago", dur.num_hours ()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Description (last_seen.to_rfc3339_opts (SecondsFormat::Secs, true))
|
|
|
|
}
|
|
|
|
|
2020-11-26 21:50:55 +00:00
|
|
|
#[derive (Serialize)]
|
|
|
|
struct ServerEntry <'a> {
|
2020-12-14 07:07:13 +00:00
|
|
|
name: String,
|
2020-11-26 23:30:33 +00:00
|
|
|
display_name: String,
|
2020-11-26 21:50:55 +00:00
|
|
|
last_seen: Cow <'a, str>,
|
|
|
|
}
|
2020-11-25 02:17:08 +00:00
|
|
|
|
2020-11-26 21:50:55 +00:00
|
|
|
#[derive (Serialize)]
|
|
|
|
struct ServerListPage <'a> {
|
2020-12-13 02:25:18 +00:00
|
|
|
dev_mode: bool,
|
|
|
|
git_version: Option <String>,
|
2020-11-26 21:50:55 +00:00
|
|
|
servers: Vec <ServerEntry <'a>>,
|
|
|
|
}
|
|
|
|
|
2021-03-15 19:11:57 +00:00
|
|
|
#[derive (Serialize)]
|
|
|
|
struct UnregisteredServer {
|
|
|
|
name: String,
|
|
|
|
tripcode: String,
|
|
|
|
last_seen: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive (Serialize)]
|
|
|
|
struct UnregisteredServerListPage {
|
|
|
|
unregistered_servers: Vec <UnregisteredServer>,
|
|
|
|
}
|
|
|
|
|
2020-11-26 21:50:55 +00:00
|
|
|
async fn handle_server_list_internal (state: &Arc <RelayState>)
|
|
|
|
-> ServerListPage <'static>
|
2020-11-25 02:17:08 +00:00
|
|
|
{
|
2020-12-14 07:07:13 +00:00
|
|
|
use LastSeen::*;
|
|
|
|
|
|
|
|
let dev_mode = {
|
2020-11-26 21:50:55 +00:00
|
|
|
let guard = state.config.read ().await;
|
2020-12-14 07:07:13 +00:00
|
|
|
guard.iso.dev_mode.is_some ()
|
2020-11-26 21:50:55 +00:00
|
|
|
};
|
2020-12-14 07:07:13 +00:00
|
|
|
let git_version = git_version::read_git_version ().await;
|
2020-11-25 02:17:08 +00:00
|
|
|
|
2020-12-14 07:07:13 +00:00
|
|
|
let server_list = scraper_api::v1_server_list (&state).await;
|
2020-11-25 02:17:08 +00:00
|
|
|
|
|
|
|
let now = Utc::now ();
|
|
|
|
|
2020-12-14 07:07:13 +00:00
|
|
|
let servers = server_list.servers.into_iter ()
|
|
|
|
.map (|x| {
|
|
|
|
let last_seen = match x.last_seen {
|
|
|
|
None => "Never".into (),
|
|
|
|
Some (x) => match pretty_print_last_seen (now, x) {
|
|
|
|
Negative => "Error (negative time)".into (),
|
|
|
|
Connected => "Connected".into (),
|
|
|
|
Description (s) => s.into (),
|
2020-11-26 23:30:33 +00:00
|
|
|
},
|
2020-11-25 02:17:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
ServerEntry {
|
2020-12-14 07:07:13 +00:00
|
|
|
name: x.name,
|
|
|
|
display_name: x.display_name,
|
2020-11-26 23:30:33 +00:00
|
|
|
last_seen,
|
2020-11-25 02:17:08 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect ();
|
|
|
|
|
2020-11-26 21:50:55 +00:00
|
|
|
ServerListPage {
|
2020-12-13 02:25:18 +00:00
|
|
|
dev_mode,
|
2020-12-14 07:07:13 +00:00
|
|
|
git_version,
|
2020-11-25 02:17:08 +00:00
|
|
|
servers,
|
2020-11-26 21:50:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-15 19:11:57 +00:00
|
|
|
async fn handle_unregistered_servers_internal (state: &Arc <RelayState>)
|
|
|
|
-> UnregisteredServerListPage
|
|
|
|
{
|
2021-03-15 19:22:23 +00:00
|
|
|
use LastSeen::*;
|
|
|
|
|
|
|
|
let now = Utc::now ();
|
|
|
|
|
|
|
|
let server_list = {
|
|
|
|
let guard = state.unregistered_servers.read ().await;
|
|
|
|
(*guard).clone ()
|
|
|
|
};
|
|
|
|
|
|
|
|
let unregistered_servers = server_list.into_iter ()
|
|
|
|
.map (|x| {
|
|
|
|
let last_seen = match pretty_print_last_seen (now, x.seen) {
|
|
|
|
Negative => "Error (negative time)".into (),
|
|
|
|
Connected => "Recently".into (),
|
|
|
|
Description (s) => s.into (),
|
|
|
|
};
|
|
|
|
|
|
|
|
UnregisteredServer {
|
|
|
|
name: x.name,
|
|
|
|
tripcode: base64::encode (x.tripcode.as_bytes ()),
|
|
|
|
last_seen,
|
|
|
|
}
|
|
|
|
}).collect ();
|
|
|
|
|
2021-03-15 19:11:57 +00:00
|
|
|
UnregisteredServerListPage {
|
2021-03-15 19:22:23 +00:00
|
|
|
unregistered_servers,
|
2021-03-15 19:11:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-26 21:50:55 +00:00
|
|
|
async fn handle_server_list (
|
2020-12-13 04:03:30 +00:00
|
|
|
state: Arc <RelayState>,
|
|
|
|
handlebars: Arc <Handlebars <'static>>
|
2020-11-29 18:37:33 +00:00
|
|
|
) -> Result <Response <Body>, RequestError>
|
2020-11-26 21:50:55 +00:00
|
|
|
{
|
|
|
|
let page = handle_server_list_internal (&state).await;
|
2020-11-25 02:17:08 +00:00
|
|
|
|
2021-02-20 16:25:30 +00:00
|
|
|
let s = handlebars.render ("server_list", &page)?;
|
2020-11-29 18:37:33 +00:00
|
|
|
Ok (ok_reply (s)?)
|
2020-11-25 02:17:08 +00:00
|
|
|
}
|
|
|
|
|
2021-03-15 19:11:57 +00:00
|
|
|
async fn handle_unregistered_servers (
|
|
|
|
state: Arc <RelayState>,
|
|
|
|
handlebars: Arc <Handlebars <'static>>
|
|
|
|
) -> Result <Response <Body>, RequestError>
|
|
|
|
{
|
|
|
|
let page = handle_unregistered_servers_internal (&state).await;
|
|
|
|
|
|
|
|
let s = handlebars.render ("unregistered_servers", &page)?;
|
|
|
|
Ok (ok_reply (s)?)
|
|
|
|
}
|
|
|
|
|
2021-02-20 20:25:14 +00:00
|
|
|
async fn handle_endless_sink (req: Request <Body>) -> Result <Response <Body>, http::Error>
|
|
|
|
{
|
|
|
|
let (_parts, mut body) = req.into_parts ();
|
|
|
|
|
|
|
|
let mut bytes_received = 0;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let item = body.next ().await;
|
|
|
|
|
|
|
|
if let Some (item) = item {
|
|
|
|
if let Ok (bytes) = &item {
|
|
|
|
bytes_received += bytes.len ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
debug! ("Finished sinking debug bytes");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok (ok_reply (format! ("Sank {} bytes\n", bytes_received))?)
|
|
|
|
}
|
|
|
|
|
2021-02-20 17:28:39 +00:00
|
|
|
async fn handle_endless_source (gib: usize, throttle: Option <usize>)
|
|
|
|
-> Result <Response <Body>, http::Error>
|
|
|
|
{
|
|
|
|
use tokio::sync::mpsc;
|
|
|
|
|
|
|
|
let block_bytes = 64 * 1024;
|
|
|
|
let num_blocks = (1024 * 1024 * 1024 / block_bytes) * gib;
|
2021-02-20 17:08:38 +00:00
|
|
|
|
2021-02-20 17:28:39 +00:00
|
|
|
let (tx, rx) = mpsc::channel (1);
|
|
|
|
|
|
|
|
tokio::spawn (async move {
|
|
|
|
let random_block = {
|
|
|
|
use rand::RngCore;
|
|
|
|
|
|
|
|
let mut rng = rand::thread_rng ();
|
|
|
|
let mut block = vec! [0u8; 64 * 1024];
|
|
|
|
rng.fill_bytes (&mut block);
|
|
|
|
block
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut interval = tokio::time::interval (Duration::from_millis (1000));
|
|
|
|
let mut blocks_sent = 0;
|
|
|
|
|
|
|
|
while blocks_sent < num_blocks {
|
|
|
|
if throttle.is_some () {
|
|
|
|
interval.tick ().await;
|
|
|
|
}
|
|
|
|
|
|
|
|
for _ in 0..throttle.unwrap_or (1) {
|
|
|
|
let item = Ok::<_, Infallible> (random_block.clone ());
|
|
|
|
if let Err (_) = tx.send (item).await {
|
|
|
|
debug! ("Endless source dropped");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
blocks_sent += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debug! ("Endless source ended");
|
|
|
|
});
|
2021-02-20 17:08:38 +00:00
|
|
|
|
|
|
|
Response::builder ()
|
|
|
|
.status (StatusCode::OK)
|
|
|
|
.header ("content-type", "application/octet-stream")
|
2021-03-06 21:15:41 +00:00
|
|
|
.body (Body::wrap_stream (ReceiverStream::new (rx)))
|
2021-02-20 17:08:38 +00:00
|
|
|
}
|
|
|
|
|
2020-12-13 04:47:47 +00:00
|
|
|
#[instrument (level = "trace", skip (req, state, handlebars))]
|
2020-12-13 04:03:30 +00:00
|
|
|
async fn handle_all (
|
|
|
|
req: Request <Body>,
|
|
|
|
state: Arc <RelayState>,
|
|
|
|
handlebars: Arc <Handlebars <'static>>
|
|
|
|
)
|
2020-11-29 18:37:33 +00:00
|
|
|
-> Result <Response <Body>, RequestError>
|
2020-10-30 22:57:36 +00:00
|
|
|
{
|
2020-12-13 01:12:56 +00:00
|
|
|
let path = req.uri ().path ().to_string ();
|
2020-10-30 22:57:36 +00:00
|
|
|
//println! ("{}", path);
|
|
|
|
|
2020-11-08 02:37:11 +00:00
|
|
|
debug! ("Request path: {}", path);
|
|
|
|
|
2020-10-30 22:57:36 +00:00
|
|
|
if req.method () == Method::POST {
|
2020-10-31 01:35:39 +00:00
|
|
|
// This is stuff the server can use. Clients can't
|
|
|
|
// POST right now
|
|
|
|
|
2020-12-13 01:12:56 +00:00
|
|
|
return if let Some (request_code) = prefix_match ("/7ZSFUKGV/http_response/", &path) {
|
2020-10-30 22:57:36 +00:00
|
|
|
let request_code = request_code.into ();
|
2020-11-29 23:24:25 +00:00
|
|
|
Ok (server_endpoint::handle_response (req, state, request_code).await?)
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
2021-02-20 20:25:14 +00:00
|
|
|
else if path == "/frontend/debug/endless_sink" {
|
|
|
|
Ok (handle_endless_sink (req).await?)
|
|
|
|
}
|
2020-10-30 22:57:36 +00:00
|
|
|
else {
|
2021-02-20 20:25:14 +00:00
|
|
|
error! ("Can't POST {}", path);
|
2020-11-29 18:37:33 +00:00
|
|
|
Ok (error_reply (StatusCode::BAD_REQUEST, "Can't POST this")?)
|
2020-11-29 17:08:33 +00:00
|
|
|
};
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
|
|
|
|
2020-12-13 01:12:56 +00:00
|
|
|
if let Some (listen_code) = prefix_match ("/7ZSFUKGV/http_listen/", &path) {
|
|
|
|
let api_key = req.headers ().get ("X-ApiKey");
|
|
|
|
|
2020-11-02 03:34:50 +00:00
|
|
|
let api_key = match api_key {
|
2020-12-12 17:50:40 +00:00
|
|
|
None => return Ok (error_reply (StatusCode::FORBIDDEN, "Can't run server without an API key")?),
|
2020-11-02 03:34:50 +00:00
|
|
|
Some (x) => x,
|
|
|
|
};
|
2020-11-29 23:24:25 +00:00
|
|
|
server_endpoint::handle_listen (state, listen_code.into (), api_key.as_bytes ()).await
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
2020-12-13 01:12:56 +00:00
|
|
|
else if let Some (rest) = prefix_match ("/frontend/servers/", &path) {
|
2020-12-16 14:46:03 +00:00
|
|
|
// DRY T4H76LB3
|
|
|
|
|
2020-10-31 20:46:38 +00:00
|
|
|
if rest == "" {
|
2020-12-13 04:03:30 +00:00
|
|
|
Ok (handle_server_list (state, handlebars).await?)
|
2020-10-31 20:46:38 +00:00
|
|
|
}
|
|
|
|
else if let Some (idx) = rest.find ('/') {
|
2020-10-30 22:57:36 +00:00
|
|
|
let listen_code = String::from (&rest [0..idx]);
|
|
|
|
let path = String::from (&rest [idx..]);
|
|
|
|
let (parts, _) = req.into_parts ();
|
|
|
|
|
2020-11-29 18:37:33 +00:00
|
|
|
Ok (handle_http_request (parts, path, state, listen_code).await?)
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-11-29 18:37:33 +00:00
|
|
|
Ok (error_reply (StatusCode::BAD_REQUEST, "Bad URI format")?)
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-15 19:11:57 +00:00
|
|
|
else if path == "/frontend/unregistered_servers" {
|
|
|
|
Ok (handle_unregistered_servers (state, handlebars).await?)
|
|
|
|
}
|
2021-02-20 16:23:12 +00:00
|
|
|
else if let Some (rest) = prefix_match ("/frontend/debug/", &path) {
|
2021-02-20 17:08:38 +00:00
|
|
|
if rest == "" {
|
|
|
|
let s = handlebars.render ("debug", &())?;
|
|
|
|
Ok (ok_reply (s)?)
|
|
|
|
}
|
|
|
|
else if rest == "endless_source" {
|
2021-02-20 17:28:39 +00:00
|
|
|
Ok (handle_endless_source (1, None).await?)
|
|
|
|
}
|
|
|
|
else if rest == "endless_source_throttled" {
|
|
|
|
Ok (handle_endless_source (1, Some (1024 / 64)).await?)
|
2021-02-20 17:08:38 +00:00
|
|
|
}
|
2021-02-20 20:25:14 +00:00
|
|
|
else if rest == "endless_sink" {
|
|
|
|
Ok (error_reply (StatusCode::METHOD_NOT_ALLOWED, "Don't GET this URL, POST to it.")?)
|
|
|
|
}
|
2021-02-20 17:08:38 +00:00
|
|
|
else {
|
2021-02-20 20:25:14 +00:00
|
|
|
Ok (error_reply (StatusCode::NOT_FOUND, "Can't route URL")?)
|
2021-02-20 17:08:38 +00:00
|
|
|
}
|
2021-02-20 16:23:12 +00:00
|
|
|
}
|
2020-11-08 15:53:09 +00:00
|
|
|
else if path == "/" {
|
2021-02-20 16:25:30 +00:00
|
|
|
let s = handlebars.render ("root", &())?;
|
2020-11-29 18:37:33 +00:00
|
|
|
Ok (ok_reply (s)?)
|
2020-11-08 15:53:09 +00:00
|
|
|
}
|
2020-11-02 17:44:04 +00:00
|
|
|
else if path == "/frontend/relay_up_check" {
|
2020-11-29 18:37:33 +00:00
|
|
|
Ok (error_reply (StatusCode::OK, "Relay is up")?)
|
|
|
|
}
|
|
|
|
else if path == "/frontend/test_mysterious_error" {
|
|
|
|
Err (RequestError::Mysterious)
|
2020-10-30 23:18:42 +00:00
|
|
|
}
|
2020-12-13 01:12:56 +00:00
|
|
|
else if let Some (rest) = prefix_match ("/scraper/", &path) {
|
2020-12-14 07:07:13 +00:00
|
|
|
scraper_api::handle (req, state, rest).await
|
2020-12-12 17:50:40 +00:00
|
|
|
}
|
2020-10-30 22:57:36 +00:00
|
|
|
else {
|
2021-02-20 17:08:38 +00:00
|
|
|
Ok (error_reply (StatusCode::OK, "Can't route URL")?)
|
2020-11-29 17:08:33 +00:00
|
|
|
}
|
2020-10-31 20:46:38 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 23:24:47 +00:00
|
|
|
pub fn load_templates (asset_root: &Path)
|
2020-11-29 16:58:56 +00:00
|
|
|
-> Result <Handlebars <'static>, RelayError>
|
2020-10-31 20:46:38 +00:00
|
|
|
{
|
|
|
|
let mut handlebars = Handlebars::new ();
|
|
|
|
handlebars.set_strict_mode (true);
|
|
|
|
|
2020-11-18 23:24:47 +00:00
|
|
|
let asset_root = asset_root.join ("handlebars/relay");
|
|
|
|
|
2020-11-29 18:37:33 +00:00
|
|
|
for (k, v) in &[
|
2021-02-20 17:08:38 +00:00
|
|
|
("debug", "debug.hbs"),
|
2021-02-20 16:25:30 +00:00
|
|
|
("root", "root.hbs"),
|
2021-02-20 17:08:38 +00:00
|
|
|
("server_list", "server_list.hbs"),
|
2021-03-15 19:11:57 +00:00
|
|
|
("unregistered_servers", "unregistered_servers.hbs"),
|
2020-11-29 18:37:33 +00:00
|
|
|
] {
|
2020-11-18 23:24:47 +00:00
|
|
|
handlebars.register_template_file (k, &asset_root.join (v))?;
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
2020-10-31 20:46:38 +00:00
|
|
|
|
|
|
|
Ok (handlebars)
|
2020-10-30 22:57:36 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 03:09:21 +00:00
|
|
|
async fn reload_config (
|
|
|
|
state: &Arc <RelayState>,
|
|
|
|
config_reload_path: &Path
|
2020-11-26 23:30:33 +00:00
|
|
|
) -> Result <(), ConfigError> {
|
|
|
|
let new_config = Config::from_file (config_reload_path).await?;
|
2020-11-25 03:09:21 +00:00
|
|
|
let mut config = state.config.write ().await;
|
|
|
|
|
2020-12-17 01:06:15 +00:00
|
|
|
trace! ("Reloading config");
|
|
|
|
if config.servers.len () != new_config.servers.len () {
|
|
|
|
debug! ("Loaded {} server configs", config.servers.len ());
|
|
|
|
}
|
|
|
|
if config.iso.enable_scraper_api != new_config.iso.enable_scraper_api {
|
|
|
|
debug! ("enable_scraper_api: {}", config.iso.enable_scraper_api);
|
|
|
|
}
|
|
|
|
|
|
|
|
(*config) = new_config;
|
2020-12-12 01:26:58 +00:00
|
|
|
|
2020-12-12 17:11:22 +00:00
|
|
|
if config.iso.dev_mode.is_some () {
|
2020-12-12 01:26:58 +00:00
|
|
|
error! ("Dev mode is enabled! This might turn off some security features. If you see this in production, escalate it to someone!");
|
|
|
|
}
|
2020-11-25 03:09:21 +00:00
|
|
|
|
2020-11-26 23:30:33 +00:00
|
|
|
Ok (())
|
2020-11-25 03:09:21 +00:00
|
|
|
}
|
|
|
|
|
2020-11-02 03:34:50 +00:00
|
|
|
pub async fn run_relay (
|
2020-11-02 18:39:19 +00:00
|
|
|
state: Arc <RelayState>,
|
2020-12-13 04:03:30 +00:00
|
|
|
handlebars: Arc <Handlebars <'static>>,
|
2020-11-25 03:09:21 +00:00
|
|
|
shutdown_oneshot: oneshot::Receiver <()>,
|
|
|
|
config_reload_path: Option <PathBuf>
|
2020-11-02 03:34:50 +00:00
|
|
|
)
|
2020-11-29 18:37:33 +00:00
|
|
|
-> Result <(), RelayError>
|
2020-11-02 02:07:46 +00:00
|
|
|
{
|
2020-11-25 03:09:21 +00:00
|
|
|
if let Some (config_reload_path) = config_reload_path {
|
|
|
|
let state_2 = state.clone ();
|
|
|
|
tokio::spawn (async move {
|
|
|
|
let mut reload_interval = tokio::time::interval (Duration::from_secs (60));
|
|
|
|
|
|
|
|
loop {
|
|
|
|
reload_interval.tick ().await;
|
2020-11-26 23:30:33 +00:00
|
|
|
reload_config (&state_2, &config_reload_path).await.ok ();
|
2020-11-02 13:52:27 +00:00
|
|
|
}
|
2020-11-25 03:09:21 +00:00
|
|
|
});
|
2020-11-02 13:52:27 +00:00
|
|
|
}
|
|
|
|
|
2020-10-30 22:57:36 +00:00
|
|
|
let make_svc = make_service_fn (|_conn| {
|
|
|
|
let state = state.clone ();
|
2020-12-13 04:03:30 +00:00
|
|
|
let handlebars = handlebars.clone ();
|
2020-10-30 22:57:36 +00:00
|
|
|
|
|
|
|
async {
|
2021-02-20 16:36:45 +00:00
|
|
|
Ok::<_, Infallible> (service_fn (move |req| {
|
2020-10-30 22:57:36 +00:00
|
|
|
let state = state.clone ();
|
2020-12-13 04:03:30 +00:00
|
|
|
let handlebars = handlebars.clone ();
|
2020-10-30 22:57:36 +00:00
|
|
|
|
2021-02-20 16:36:45 +00:00
|
|
|
async {
|
|
|
|
Ok::<_, Infallible> (handle_all (req, state, handlebars).await.unwrap_or_else (|e| {
|
|
|
|
error! ("{}", e);
|
|
|
|
error_reply (StatusCode::INTERNAL_SERVER_ERROR, "Error in relay").unwrap ()
|
|
|
|
}))
|
|
|
|
}
|
2020-10-30 22:57:36 +00:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-12-13 01:54:54 +00:00
|
|
|
let addr = SocketAddr::from ((
|
|
|
|
[0, 0, 0, 0],
|
|
|
|
state.config.read ().await.port.unwrap_or (4000),
|
|
|
|
));
|
|
|
|
|
2020-11-02 18:39:19 +00:00
|
|
|
let server = Server::bind (&addr)
|
|
|
|
.serve (make_svc);
|
2020-10-30 22:57:36 +00:00
|
|
|
|
2020-11-06 03:44:32 +00:00
|
|
|
server.with_graceful_shutdown (async {
|
2020-11-29 18:37:33 +00:00
|
|
|
use ShuttingDownError::ShuttingDown;
|
|
|
|
|
2020-11-06 03:44:32 +00:00
|
|
|
shutdown_oneshot.await.ok ();
|
|
|
|
|
2021-03-06 21:15:41 +00:00
|
|
|
state.shutdown_watch_tx.send (true).expect ("Can't broadcast graceful shutdown");
|
2020-11-07 00:30:56 +00:00
|
|
|
|
2020-11-06 23:43:52 +00:00
|
|
|
let mut response_rendezvous = state.response_rendezvous.write ().await;
|
|
|
|
let mut swapped = DashMap::default ();
|
2020-11-06 03:44:32 +00:00
|
|
|
|
2020-11-06 23:43:52 +00:00
|
|
|
std::mem::swap (&mut swapped, &mut response_rendezvous);
|
|
|
|
|
2020-11-29 18:37:33 +00:00
|
|
|
for (_, sender) in swapped {
|
2020-11-29 16:58:56 +00:00
|
|
|
sender.send (Err (ShuttingDown)).ok ();
|
2020-11-06 23:43:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut request_rendezvous = state.request_rendezvous.lock ().await;
|
|
|
|
|
|
|
|
for (_, x) in request_rendezvous.drain () {
|
|
|
|
use RequestRendezvous::*;
|
|
|
|
|
|
|
|
match x {
|
|
|
|
ParkedClients (_) => (),
|
2020-11-29 16:58:56 +00:00
|
|
|
ParkedServer (sender) => drop (sender.send (Err (ShuttingDown))),
|
2020-11-06 23:43:52 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-07 00:30:56 +00:00
|
|
|
|
2020-11-07 02:29:45 +00:00
|
|
|
debug! ("Performed all cleanup");
|
2020-11-06 03:44:32 +00:00
|
|
|
}).await?;
|
2020-10-30 22:57:36 +00:00
|
|
|
|
|
|
|
Ok (())
|
|
|
|
}
|
2020-11-02 00:13:12 +00:00
|
|
|
|
2020-11-02 02:07:46 +00:00
|
|
|
#[cfg (test)]
|
2020-11-29 18:39:51 +00:00
|
|
|
mod tests;
|