#![allow(dead_code)]
use armature::prelude::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Serialize)]
struct JsonMessage {
message: &'static str,
}
#[derive(Clone, Serialize, Deserialize)]
struct World {
id: i32,
#[serde(rename = "randomNumber")]
random_number: i32,
}
#[derive(Clone, Serialize)]
struct Fortune {
id: i32,
message: String,
}
struct InMemoryDb {
worlds: Vec<World>,
fortunes: Vec<Fortune>,
}
impl InMemoryDb {
fn new() -> Self {
let worlds: Vec<World> = (1..=10000)
.map(|id| World {
id,
random_number: fastrand::i32(1..=10000),
})
.collect();
let fortunes = vec![
Fortune { id: 1, message: "fortune: No such file or directory".to_string() },
Fortune { id: 2, message: "A computer scientist is someone who fixes things that aren't broken.".to_string() },
Fortune { id: 3, message: "After enough decimal places, nobody gives a damn.".to_string() },
Fortune { id: 4, message: "A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1".to_string() },
Fortune { id: 5, message: "A computer program does what you tell it to do, not what you want it to do.".to_string() },
Fortune { id: 6, message: "Emstrongs's Law: If you make something idiot-proof, someone will make a better idiot.".to_string() },
Fortune { id: 7, message: "Feature: A bug with seniority.".to_string() },
Fortune { id: 8, message: "Computers make very fast, very accurate mistakes.".to_string() },
Fortune { id: 9, message: "<script>alert(\"This should not be displayed in a alarm box.\");</script>".to_string() },
Fortune { id: 10, message: "フレームワークのベンチマーク".to_string() },
Fortune { id: 11, message: "Any program that runs right is obsolete.".to_string() },
Fortune { id: 12, message: "Waste not, compute not.".to_string() },
];
Self { worlds, fortunes }
}
fn get_random_world(&self) -> World {
let id = fastrand::i32(1..=10000);
let idx = ((id - 1) as usize) % self.worlds.len();
self.worlds[idx].clone()
}
fn update_world(&self, world: &mut World) {
world.random_number = fastrand::i32(1..=10000);
}
fn get_fortunes(&self) -> Vec<Fortune> {
self.fortunes.clone()
}
}
struct AppState {
db: InMemoryDb,
}
static APP_STATE: std::sync::OnceLock<Arc<AppState>> = std::sync::OnceLock::new();
fn get_state() -> &'static Arc<AppState> {
APP_STATE.get_or_init(|| {
Arc::new(AppState {
db: InMemoryDb::new(),
})
})
}
fn parse_queries(queries: Option<&str>) -> usize {
queries
.and_then(|s| s.parse::<usize>().ok())
.map(|n| n.clamp(1, 500))
.unwrap_or(1)
}
fn render_fortunes_html(fortunes: &[Fortune]) -> String {
let mut html = String::with_capacity(2048);
html.push_str("<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
for fortune in fortunes {
html.push_str("<tr><td>");
html.push_str(&fortune.id.to_string());
html.push_str("</td><td>");
html.push_str(&html_escape(&fortune.message));
html.push_str("</td></tr>");
}
html.push_str("</table></body></html>");
html
}
fn html_escape(s: &str) -> String {
s.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
#[controller("")]
#[derive(Default, Clone)]
struct TechEmpowerController;
#[routes]
impl TechEmpowerController {
#[get("/json")]
async fn json_handler() -> Result<HttpResponse, Error> {
HttpResponse::json(&JsonMessage {
message: "Hello, World!",
})
}
#[get("/plaintext")]
async fn plaintext_handler() -> Result<HttpResponse, Error> {
Ok(HttpResponse::ok()
.with_header("Content-Type".to_string(), "text/plain".to_string())
.with_body("Hello, World!".as_bytes().to_vec()))
}
#[get("/db")]
async fn db_handler() -> Result<HttpResponse, Error> {
let state = get_state();
let world = state.db.get_random_world();
HttpResponse::json(&world)
}
#[get("/queries")]
async fn queries_handler(req: HttpRequest) -> Result<HttpResponse, Error> {
let state = get_state();
let count = parse_queries(req.query_params.get("queries").map(|s| s.as_str()));
let worlds: Vec<World> = (0..count).map(|_| state.db.get_random_world()).collect();
HttpResponse::json(&worlds)
}
#[get("/fortunes")]
async fn fortunes_handler() -> Result<HttpResponse, Error> {
let state = get_state();
let mut fortunes = state.db.get_fortunes();
fortunes.push(Fortune {
id: 0,
message: "Additional fortune added at request time.".to_string(),
});
fortunes.sort_by(|a, b| a.message.cmp(&b.message));
let html = render_fortunes_html(&fortunes);
Ok(HttpResponse::ok()
.with_header(
"Content-Type".to_string(),
"text/html; charset=utf-8".to_string(),
)
.with_body(html.into_bytes()))
}
#[get("/updates")]
async fn updates_handler(req: HttpRequest) -> Result<HttpResponse, Error> {
let state = get_state();
let count = parse_queries(req.query_params.get("queries").map(|s| s.as_str()));
let worlds: Vec<World> = (0..count)
.map(|_| {
let mut world = state.db.get_random_world();
state.db.update_world(&mut world);
world
})
.collect();
HttpResponse::json(&worlds)
}
#[get("/cached-queries")]
async fn cached_queries_handler(req: HttpRequest) -> Result<HttpResponse, Error> {
let state = get_state();
let count = parse_queries(req.query_params.get("queries").map(|s| s.as_str()));
let worlds: Vec<World> = (0..count).map(|_| state.db.get_random_world()).collect();
HttpResponse::json(&worlds)
}
}
#[module(controllers: [TechEmpowerController])]
#[derive(Default)]
struct AppModule;
#[tokio::main]
async fn main() {
let _ = get_state();
println!("╔════════════════════════════════════════════════════════════╗");
println!("║ TechEmpower Framework Benchmark Server ║");
println!("╠════════════════════════════════════════════════════════════╣");
println!("║ Endpoints: ║");
println!("║ GET /json - JSON serialization ║");
println!("║ GET /plaintext - Plaintext response ║");
println!("║ GET /db - Single DB query ║");
println!("║ GET /queries?q=N - Multiple DB queries (1-500) ║");
println!("║ GET /fortunes - Template rendering ║");
println!("║ GET /updates?q=N - DB updates (1-500) ║");
println!("║ GET /cached-queries - Cached queries (1-500) ║");
println!("╠════════════════════════════════════════════════════════════╣");
println!("║ Benchmark with: ║");
println!("║ wrk -t4 -c256 -d15s http://127.0.0.1:8080/json ║");
println!("║ wrk -t4 -c256 -d15s http://127.0.0.1:8080/plaintext ║");
println!("╚════════════════════════════════════════════════════════════╝");
println!();
let app = Application::create::<AppModule>().await;
println!("🚀 Server starting on http://127.0.0.1:8080");
println!(" Press Ctrl+C to stop\n");
app.listen(8080).await.unwrap();
}