From 0bf1b91afc6b8c9705b8a1b62db43264f9337df2 Mon Sep 17 00:00:00 2001
From: breezystatic77
Date: Sun, 26 Jan 2025 12:42:38 -0500
Subject: add delay

---
 src/main.rs | 136 +++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 76 insertions(+), 60 deletions(-)

(limited to 'src/main.rs')

diff --git a/src/main.rs b/src/main.rs
index 760247a..8e61fc9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -27,15 +27,18 @@ struct Args {
 	/// port for serving http traffic
 	#[arg(short, long, default_value_t = 3200)]
 	port: u16,
+	/// delay all responses (in milliseconds)
+	#[arg(short, long, default_value_t = 0)]
+	delay: u32,
 }
 
 #[tokio::main]
 async fn main() -> Result<ExitCode, Box<dyn std::error::Error + Send + Sync>> {
 	let args = Args::parse();
 
-	tracing_subscriber::fmt::init();
+	tracing_subscriber::fmt::fmt().init();
 
-	let svc = RandomPageService::new(args.seed.as_str());
+	let svc = RandomPageService::new(&args);
 
 	let addr = SocketAddr::from(([127, 0, 0, 1], args.port));
 
@@ -58,20 +61,21 @@ async fn main() -> Result<ExitCode, Box<dyn std::error::Error + Send + Sync>> {
 
 #[derive(Debug, Clone)]
 struct RandomPageService {
-	pub seed: String,
-	pub dict: Arc<Vec<&'static str>>,
-	pub dict_set: Arc<HashSet<&'static str>>,
+	pub ctx: Arc<PageGenerator>,
 }
 
 impl RandomPageService {
-	pub fn new(seed: &str) -> Self {
+	pub fn new(args: &Args) -> Self {
 		let dictionary_data = include_bytes!(env!("DICTIONARY_FILE_PATH"));
 		let dictionary_string: &'static str =
 			std::str::from_utf8(dictionary_data).unwrap();
 		Self {
-			seed: seed.to_string(),
-			dict: Arc::new(dictionary_string.split_whitespace().collect()),
-			dict_set: Arc::new(dictionary_string.split_whitespace().collect()),
+			ctx: Arc::new(PageGenerator {
+				seed: args.seed.clone(),
+				delay: args.delay,
+				dict: dictionary_string.split_whitespace().collect(),
+				dict_set: dictionary_string.split_whitespace().collect(),
+			}),
 		}
 	}
 }
@@ -96,25 +100,15 @@ impl Service<Request<hyper::body::Incoming>> for RandomPageService {
 			)
 		}
 
-		let path = req.uri().path();
+		let ctx = self.ctx.clone();
 
-		tracing::info!(path, "path");
-
-		let path_segments = path.split('/').filter(|v| v.len() > 0);
-
-		for segment in path_segments {
-			if !self.dict_set.contains(segment) {
-				return Box::pin(async {
-					mk_response("not found".to_string(), StatusCode::NOT_FOUND)
-				});
+		Box::pin(async move {
+			let path = req.uri().path();
+			match ctx.build_page(path).await {
+				Ok(body) => mk_response(body, StatusCode::OK),
+				Err((err_message, code)) => mk_response(err_message.to_string(), code),
 			}
-		}
-
-		let res_body = build_page(&self.seed, path, &self.dict);
-
-		let res = mk_response(res_body, StatusCode::OK);
-
-		Box::pin(async { res })
+		})
 	}
 }
 
@@ -122,40 +116,62 @@ const MAX_ROUTE_SEGMENTS: usize = 6;
 const PARAGRAPH_WORDS: RangeInclusive<usize> = 10..=200;
 const N_LINKS: RangeInclusive<usize> = 3..=10;
 
-pub fn build_page(
-	base_seed: &str,
-	route: &str,
-	dictionary: &[&'static str],
-) -> String {
-	let mut rng: Pcg64 =
-		Seeder::from(format!("{}---{}", base_seed, route)).make_rng();
-	let n_words = rng.gen_range(PARAGRAPH_WORDS);
-	let n_links = rng.gen_range(N_LINKS);
-
-	let random_paragraph = (0..n_words)
-		.map(|_| random_word(&mut rng, dictionary))
-		.collect::<Vec<&str>>()
-		.join(" ");
-
-	let random_links = (0..n_links)
-		.map(|_| random_route_link(&mut rng, dictionary))
-		.map(|link| format!("<p>{}</p>", link))
-		.collect::<Vec<String>>()
-		.join("\n");
-
-	format!(
-		r#"
-			<html>
-				<head>
-				</head>
-				<body>
-					<p>{}</p>
-					{}
-				</body>
-			</html>
-		"#,
-		random_paragraph, random_links,
-	)
+#[derive(Debug)]
+struct PageGenerator {
+	seed: String,
+	delay: u32,
+	dict: Vec<&'static str>,
+	dict_set: HashSet<&'static str>,
+}
+
+impl PageGenerator {
+	async fn build_page(
+		&self,
+		route: &str,
+	) -> Result<String, (&'static str, StatusCode)> {
+		if self.delay > 0 {
+			tokio::time::sleep(tokio::time::Duration::from_millis(self.delay as u64))
+				.await;
+		}
+
+		for segment in route.split('/').filter(|v| v.len() > 0) {
+			if !self.dict_set.contains(segment) {
+				return Err(("not found", StatusCode::NOT_FOUND));
+			}
+		}
+
+		let mut rng: Pcg64 =
+			Seeder::from(format!("{}---{}", self.seed, route)).make_rng();
+		let n_words = rng.gen_range(PARAGRAPH_WORDS);
+		let n_links = rng.gen_range(N_LINKS);
+
+		let random_paragraph = (0..n_words)
+			.map(|_| random_word(&mut rng, &self.dict))
+			.collect::<Vec<&str>>()
+			.join(" ");
+
+		let random_links = (0..n_links)
+			.map(|_| random_route_link(&mut rng, &self.dict))
+			.map(|link| format!("<p>{}</p>", link))
+			.collect::<Vec<String>>()
+			.join("\n");
+
+		tracing::info!(route);
+
+		Ok(format!(
+			r#"
+				<html>
+					<head>
+					</head>
+					<body>
+						<p>{}</p>
+						{}
+					</body>
+				</html>
+			"#,
+			random_paragraph, random_links,
+		))
+	}
 }
 
 fn random_route_link(rng: &mut Pcg64, dictionary: &[&'static str]) -> String {
-- 
cgit v1.2.3