// 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"); }); } }