ptth/src/lib.rs

192 lines
4.7 KiB
Rust

// It's easier if the server can stream its response body
// back to the relay un-changed inside its request body
// So we wrap the server's actual response head
// (status code, headers, etc.) in this one header field.
pub const PTTH_MAGIC_HEADER: &str = "X-PTTH-2LJYXWC4";
pub mod git_version;
pub mod graceful_shutdown;
pub mod http_serde;
pub mod load_toml;
pub mod prelude;
pub mod relay;
pub mod server;
// The arguments are in order so they are in order overall:
// e.g. prefix_match ("/prefix", "/prefix/middle/suffix") -> "/middle/suffix"
pub fn prefix_match <'a> (prefix: &str, hay: &'a str) -> Option <&'a str>
{
if hay.starts_with (prefix) {
Some (&hay [prefix.len ()..])
}
else {
None
}
}
// Thanks to https://github.com/robsheldon/bad-passwords-index
const BAD_PASSWORDS: &[u8] = include_bytes! ("bad_passwords.txt");
pub fn password_is_bad (mut password: String) -> bool {
password.make_ascii_lowercase ();
let ac = aho_corasick::AhoCorasick::new (&[
password
]);
ac.find (BAD_PASSWORDS).is_some ()
}
#[cfg (test)]
mod tests {
use std::{
sync::{
Arc,
},
time::Duration,
};
use tokio::{
runtime::Runtime,
spawn,
sync::oneshot,
time::delay_for,
};
use super::{
relay,
server,
};
#[test]
fn check_bad_passwords () {
use crate::password_is_bad;
for pw in &[
"password",
"pAsSwOrD",
"secret",
"123123",
] {
assert! (password_is_bad (pw.to_string ()));
}
use rand::prelude::*;
let mut entropy = [0u8; 32];
thread_rng ().fill_bytes (&mut entropy);
let good_password = base64::encode (entropy);
assert! (! password_is_bad (good_password));
}
#[test]
fn end_to_end () {
use maplit::*;
use reqwest::Client;
use tracing::{info};
// Prefer this form for tests, since all tests share one process
// and we don't care if another test already installed a subscriber.
tracing_subscriber::fmt ().try_init ().ok ();
let mut rt = Runtime::new ().unwrap ();
// Spawn the root task
rt.block_on (async {
let server_name = "aliens_wildland";
let api_key = "AnacondaHardcoverGrannyUnlatchLankinessMutate";
let tripcode = base64::encode (blake3::hash (api_key.as_bytes ()).as_bytes ());
println! ("Relay is expecting tripcode {}", tripcode);
let config_file = relay::ConfigFile {
port: None,
server_tripcodes: hashmap! {
server_name.into () => tripcode,
},
};
let relay_state = Arc::new (relay::RelayState::from (&config_file));
let relay_state_2 = relay_state.clone ();
let (stop_relay_tx, stop_relay_rx) = oneshot::channel ();
let task_relay = spawn (async move {
relay::run_relay (relay_state_2, stop_relay_rx).await.unwrap ();
});
assert! (relay_state.list_servers ().await.is_empty ());
let relay_url = "http://127.0.0.1:4000";
let config_file = server::ConfigFile {
name: server_name.into (),
api_key: api_key.into (),
relay_url: "http://127.0.0.1:4000/7ZSFUKGV".into (),
file_server_root: None,
};
let (stop_server_tx, stop_server_rx) = oneshot::channel ();
let task_server = {
spawn (async move {
server::run_server (config_file, stop_server_rx, None).await.unwrap ();
})
};
delay_for (Duration::from_millis (500)).await;
assert_eq! (relay_state.list_servers ().await, vec! [
server_name.to_string (),
]);
let client = Client::builder ()
.timeout (Duration::from_secs (2))
.build ().unwrap ();
let resp = client.get (&format! ("{}/frontend/relay_up_check", relay_url))
.send ().await.unwrap ().bytes ().await.unwrap ();
assert_eq! (resp, "Relay is up\n");
let resp = client.get (&format! ("{}/frontend/servers/{}/files/COPYING", relay_url, server_name))
.send ().await.unwrap ().bytes ().await.unwrap ();
if blake3::hash (&resp) != blake3::Hash::from ([
0xca, 0x02, 0x92, 0x78,
0x9c, 0x0a, 0x0e, 0xcb,
0xa7, 0x06, 0xf4, 0xb3,
0xf3, 0x49, 0x30, 0x07,
0xa9, 0x95, 0x17, 0x31,
0xc1, 0xd4, 0x32, 0xc5,
0x2c, 0x4a, 0xac, 0x1f,
0x1a, 0xbb, 0xa8, 0xef,
]) {
panic! ("{}", String::from_utf8 (resp.to_vec ()).unwrap ());
}
// Requesting a file from a server that isn't registered
// will error out
let resp = client.get (&format! ("{}/frontend/servers/obviously_this_server_does_not_exist/files/COPYING", relay_url))
.send ().await.unwrap ();
assert_eq! (resp.status (), reqwest::StatusCode::NOT_FOUND);
info! ("Shutting down end-to-end test");
stop_server_tx.send (()).unwrap ();
stop_relay_tx.send (()).unwrap ();
info! ("Sent stop messages");
task_relay.await.unwrap ();
info! ("Relay stopped");
task_server.await.unwrap ();
info! ("Server stopped");
});
}
}