diff --git a/.gitignore b/.gitignore index ea8c4bf..9f59e66 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +count.txt diff --git a/Cargo.lock b/Cargo.lock index 7b3774b..28ef665 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "addr2line" @@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ddeb19ee86cb16ecfc871e5b0660aff6285760957aaedda6284cf0e790d3769" +checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1" dependencies = [ "bindgen", "cc", @@ -287,9 +287,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", @@ -503,9 +503,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -992,7 +992,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -1175,9 +1175,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -1229,9 +1229,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -1543,18 +1543,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 122db6d..f402615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "babble" version = "0.1.0" -edition = "2024" +# edition = "2024" +edition = "2021" [dependencies] axum = "0.8.3" diff --git a/src/main.rs b/src/main.rs index de68c2c..fd8ccf5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,18 @@ mod generator; -use axum::{Router, extract::Path, response::Html, routing::get}; +use axum::{extract::Path, response::Html, routing::get, Router}; use axum_server::{bind_rustls, tls_rustls::RustlsConfig}; use clap::Parser; use hashbrown::HashMap; use itertools::Itertools; -use rand::{Rng as _, prelude::*, random_range}; -use rand_chacha::{ChaCha8Rng, rand_core::SeedableRng}; +use rand::{prelude::*, Rng as _}; +use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; use std::{ borrow::Cow, path::PathBuf, sync::{ - Arc, atomic::{AtomicU64, Ordering}, + Arc, }, time::Duration, }; @@ -32,6 +32,17 @@ pub struct Args { key: Option, } +#[inline] +fn create_rng(seed_bytes: impl IntoIterator) -> Rng { + let mut seed = [0; 32]; + for (i, b) in seed_bytes.into_iter().take(seed.len()).enumerate() { + seed[i] = b; + } + Rng::from_seed(seed) +} + +const COUNT_FILE: &str = "count.txt"; + #[tokio::main] async fn main() { let args = Args::parse(); @@ -41,43 +52,54 @@ async fn main() { Arc::new(generator::Ast::new()), ]; - let counter = Arc::new(AtomicU64::new(0)); + let counter = Arc::new(AtomicU64::new( + if let Some(prev_count) = std::fs::read_to_string(COUNT_FILE) + .ok() + .and_then(|s| s.parse().ok()) + { + prev_count + } else { + 0 + }, + )); let app = { let counter = counter.clone(); Router::new().route( "/{id}", get(|Path(id): Path| async move { - tokio::time::sleep(Duration::from_millis(random_range(200..1000))).await; - counter.fetch_add(1, Ordering::Relaxed); + // Create a RNG for this path (deterministic, to simulate static pages) + let mut rng = create_rng(id.bytes()); - let mut seed = [0; 32]; - for (i, b) in id - .bytes() - .chain(core::iter::repeat(0)) - .take(seed.len()) - .enumerate() - { - seed[i] = b; - } - let mut rng = Rng::from_seed(seed); + // Count the request. Also doubles as the non-deterministic seed + let count = counter.fetch_add(1, Ordering::Relaxed); + // Create a RNG for this session (non-deterministic) + let mut session_rng = create_rng(count.to_le_bytes()); + + // Artificially slow down connections as rudimentary DDoS protection, and to use up client resources + tokio::time::sleep(Duration::from_millis(session_rng.random_range(200..1000))) + .await; + + // Choose a bullshit generator from our collection for this page let generator = generators.choose(&mut rng).unwrap(); let title = generator .word_stream(rng.random_range(2..10), &mut rng.clone()) .join(" "); + let stats = format!("Served rubbish to {count} clients so far"); + let content = generator .word_stream(rng.random_range(50..5_000), &mut rng.clone()) .fold(String::new(), |mut content, word| { + // Small chance of every word becoming a link back into the void if rng.random_bool(0.05) { let url = generator.word_stream(3, &mut rng.clone()).join("-"); content += &format!(" {}", url, word); - } else if rng.random_bool(0.01) { - content += ".
"; } else { - content += " "; + // Also, a chance for every word to end with a newline. This should probably be controlled by the generator. + content += if rng.random_bool(0.01) { ".
" } else { " " }; content += &word } content @@ -92,6 +114,7 @@ async fn main() {

{title}

+

{stats}

{content}

@@ -116,6 +139,7 @@ async fn main() { chrono::offset::Local::now(), count, ); + let _ = std::fs::write(COUNT_FILE, &format!("{count}")); } } });