Skip to content

Commit d9ad69e

Browse files
committed
add actix
1 parent 9f7e2d7 commit d9ad69e

78 files changed

Lines changed: 1504 additions & 255 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frameworks/actix/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "httparena-actix"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
actix-web = { version = "4", features = ["rustls-0_23"] }
8+
actix-rt = "2"
9+
rustls = "0.23"
10+
rustls-pemfile = "2"
11+
serde = { version = "1", features = ["derive"] }
12+
serde_json = "1"
13+
num_cpus = "1"
14+
15+
[profile.release]
16+
opt-level = 3
17+
codegen-units = 1
18+
lto = "thin"
19+
panic = "abort"

frameworks/actix/Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM rust:1.88 AS build
2+
WORKDIR /app
3+
COPY Cargo.toml .
4+
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src/ target/release/httparena-actix* target/release/deps/httparena_actix*
5+
COPY src ./src
6+
RUN RUSTFLAGS="-C target-cpu=native" cargo build --release
7+
8+
FROM debian:bookworm-slim
9+
COPY --from=build /app/target/release/httparena-actix /server
10+
EXPOSE 8080
11+
CMD ["/server"]

frameworks/actix/meta.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"display_name": "actix",
3+
"language": "Rust",
4+
"type": "realistic",
5+
"engine": "actix",
6+
"description": "Actix-web 4 with rustls for HTTP/2 support, compiled with -O3 and thin LTO.",
7+
"repo": "https://github.com/actix/actix-web",
8+
"enabled": true,
9+
"tests": ["baseline", "pipelined", "limited-conn", "json", "baseline-h2", "static-h2"]
10+
}

frameworks/actix/src/main.rs

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
use actix_web::http::header::{ContentType, HeaderValue, SERVER};
2+
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
3+
use rustls::ServerConfig;
4+
use serde::{Deserialize, Serialize};
5+
use std::collections::HashMap;
6+
use std::io;
7+
use std::sync::Arc;
8+
9+
static SERVER_HDR: HeaderValue = HeaderValue::from_static("actix");
10+
11+
#[derive(Deserialize, Clone)]
12+
struct Rating {
13+
score: f64,
14+
count: i64,
15+
}
16+
17+
#[derive(Deserialize, Clone)]
18+
struct DatasetItem {
19+
id: i64,
20+
name: String,
21+
category: String,
22+
price: f64,
23+
quantity: i64,
24+
active: bool,
25+
tags: Vec<String>,
26+
rating: Rating,
27+
}
28+
29+
#[derive(Serialize)]
30+
struct RatingOut {
31+
score: f64,
32+
count: i64,
33+
}
34+
35+
#[derive(Serialize)]
36+
struct ProcessedItem {
37+
id: i64,
38+
name: String,
39+
category: String,
40+
price: f64,
41+
quantity: i64,
42+
active: bool,
43+
tags: Vec<String>,
44+
rating: RatingOut,
45+
total: f64,
46+
}
47+
48+
#[derive(Serialize)]
49+
struct JsonResponse {
50+
items: Vec<ProcessedItem>,
51+
count: usize,
52+
}
53+
54+
struct StaticFile {
55+
data: Vec<u8>,
56+
content_type: String,
57+
}
58+
59+
struct AppState {
60+
dataset: Vec<DatasetItem>,
61+
json_cache: Vec<u8>,
62+
static_files: HashMap<String, StaticFile>,
63+
}
64+
65+
fn load_dataset() -> Vec<DatasetItem> {
66+
let path = std::env::var("DATASET_PATH").unwrap_or_else(|_| "/data/dataset.json".to_string());
67+
match std::fs::read_to_string(&path) {
68+
Ok(data) => serde_json::from_str(&data).unwrap_or_default(),
69+
Err(_) => Vec::new(),
70+
}
71+
}
72+
73+
fn build_json_cache(dataset: &[DatasetItem]) -> Vec<u8> {
74+
let items: Vec<ProcessedItem> = dataset
75+
.iter()
76+
.map(|d| ProcessedItem {
77+
id: d.id,
78+
name: d.name.clone(),
79+
category: d.category.clone(),
80+
price: d.price,
81+
quantity: d.quantity,
82+
active: d.active,
83+
tags: d.tags.clone(),
84+
rating: RatingOut {
85+
score: d.rating.score,
86+
count: d.rating.count,
87+
},
88+
total: (d.price * d.quantity as f64 * 100.0).round() / 100.0,
89+
})
90+
.collect();
91+
let resp = JsonResponse {
92+
count: items.len(),
93+
items,
94+
};
95+
serde_json::to_vec(&resp).unwrap_or_default()
96+
}
97+
98+
fn load_static_files() -> HashMap<String, StaticFile> {
99+
let mime_types: HashMap<&str, &str> = [
100+
(".css", "text/css"),
101+
(".js", "application/javascript"),
102+
(".html", "text/html"),
103+
(".woff2", "font/woff2"),
104+
(".svg", "image/svg+xml"),
105+
(".webp", "image/webp"),
106+
(".json", "application/json"),
107+
]
108+
.into();
109+
let mut files = HashMap::new();
110+
if let Ok(entries) = std::fs::read_dir("/data/static") {
111+
for entry in entries.flatten() {
112+
let name = entry.file_name().to_string_lossy().to_string();
113+
if let Ok(data) = std::fs::read(entry.path()) {
114+
let ext = name.rfind('.').map(|i| &name[i..]).unwrap_or("");
115+
let ct = mime_types.get(ext).unwrap_or(&"application/octet-stream");
116+
files.insert(
117+
name,
118+
StaticFile {
119+
data,
120+
content_type: ct.to_string(),
121+
},
122+
);
123+
}
124+
}
125+
}
126+
files
127+
}
128+
129+
fn parse_query_sum(query: &str) -> i64 {
130+
let mut sum: i64 = 0;
131+
for pair in query.split('&') {
132+
if let Some(val) = pair.split('=').nth(1) {
133+
if let Ok(n) = val.parse::<i64>() {
134+
sum += n;
135+
}
136+
}
137+
}
138+
sum
139+
}
140+
141+
async fn pipeline() -> HttpResponse {
142+
HttpResponse::Ok()
143+
.insert_header((SERVER, SERVER_HDR.clone()))
144+
.content_type(ContentType::plaintext())
145+
.body("ok")
146+
}
147+
148+
async fn baseline11_get(req: HttpRequest) -> HttpResponse {
149+
let sum = req
150+
.uri()
151+
.query()
152+
.map(parse_query_sum)
153+
.unwrap_or(0);
154+
HttpResponse::Ok()
155+
.insert_header((SERVER, SERVER_HDR.clone()))
156+
.content_type(ContentType::plaintext())
157+
.body(sum.to_string())
158+
}
159+
160+
async fn baseline11_post(req: HttpRequest, body: web::Bytes) -> HttpResponse {
161+
let mut sum = req
162+
.uri()
163+
.query()
164+
.map(parse_query_sum)
165+
.unwrap_or(0);
166+
if let Ok(s) = std::str::from_utf8(&body) {
167+
if let Ok(n) = s.trim().parse::<i64>() {
168+
sum += n;
169+
}
170+
}
171+
HttpResponse::Ok()
172+
.insert_header((SERVER, SERVER_HDR.clone()))
173+
.content_type(ContentType::plaintext())
174+
.body(sum.to_string())
175+
}
176+
177+
async fn baseline2(req: HttpRequest) -> HttpResponse {
178+
let sum = req
179+
.uri()
180+
.query()
181+
.map(parse_query_sum)
182+
.unwrap_or(0);
183+
HttpResponse::Ok()
184+
.insert_header((SERVER, SERVER_HDR.clone()))
185+
.content_type(ContentType::plaintext())
186+
.body(sum.to_string())
187+
}
188+
189+
async fn json_endpoint(state: web::Data<Arc<AppState>>) -> HttpResponse {
190+
HttpResponse::Ok()
191+
.insert_header((SERVER, SERVER_HDR.clone()))
192+
.content_type(ContentType::json())
193+
.body(state.json_cache.clone())
194+
}
195+
196+
async fn static_file(
197+
state: web::Data<Arc<AppState>>,
198+
path: web::Path<String>,
199+
) -> HttpResponse {
200+
let filename = path.into_inner();
201+
if let Some(sf) = state.static_files.get(&filename) {
202+
HttpResponse::Ok()
203+
.insert_header((SERVER, SERVER_HDR.clone()))
204+
.insert_header(("content-type", sf.content_type.as_str()))
205+
.body(sf.data.clone())
206+
} else {
207+
HttpResponse::NotFound().finish()
208+
}
209+
}
210+
211+
fn load_tls_config() -> Option<ServerConfig> {
212+
let cert_path = std::env::var("TLS_CERT").unwrap_or_else(|_| "/certs/server.crt".to_string());
213+
let key_path = std::env::var("TLS_KEY").unwrap_or_else(|_| "/certs/server.key".to_string());
214+
let cert_file = std::fs::File::open(&cert_path).ok()?;
215+
let key_file = std::fs::File::open(&key_path).ok()?;
216+
let certs: Vec<_> = rustls_pemfile::certs(&mut io::BufReader::new(cert_file))
217+
.filter_map(|r| r.ok())
218+
.collect();
219+
let key = rustls_pemfile::private_key(&mut io::BufReader::new(key_file)).ok()??;
220+
let mut config = ServerConfig::builder()
221+
.with_no_client_auth()
222+
.with_single_cert(certs, key)
223+
.ok()?;
224+
config.alpn_protocols = vec![b"h2".to_vec()];
225+
Some(config)
226+
}
227+
228+
#[actix_web::main]
229+
async fn main() -> io::Result<()> {
230+
let dataset = load_dataset();
231+
let json_cache = build_json_cache(&dataset);
232+
let state = Arc::new(AppState {
233+
dataset,
234+
json_cache,
235+
static_files: load_static_files(),
236+
});
237+
238+
let tls_config = load_tls_config();
239+
let workers = num_cpus::get();
240+
241+
let mut server = HttpServer::new({
242+
let state = state.clone();
243+
move || {
244+
App::new()
245+
.app_data(web::Data::new(state.clone()))
246+
.route("/pipeline", web::get().to(pipeline))
247+
.route("/baseline11", web::get().to(baseline11_get))
248+
.route("/baseline11", web::post().to(baseline11_post))
249+
.route("/baseline2", web::get().to(baseline2))
250+
.route("/json", web::get().to(json_endpoint))
251+
.route("/static/{filename}", web::get().to(static_file))
252+
}
253+
})
254+
.workers(workers)
255+
.backlog(4096)
256+
.bind("0.0.0.0:8080")?;
257+
258+
if let Some(tls_cfg) = tls_config {
259+
server = server.bind_rustls_0_23("0.0.0.0:8443", tls_cfg)?;
260+
}
261+
262+
server.run().await
263+
}

frameworks/lithium/main.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <sstream>
77
#include <string>
88
#include <thread>
9+
#include <unistd.h>
910

1011
// Simple JSON builder (avoids pulling in a JSON library)
1112
namespace sjson {
@@ -241,7 +242,8 @@ int main() {
241242
resp.write(std::to_string(sum));
242243
};
243244

244-
int nthreads = std::thread::hardware_concurrency();
245+
int nthreads = sysconf(_SC_NPROCESSORS_ONLN);
246+
if (nthreads < 1) nthreads = std::thread::hardware_concurrency();
245247
if (nthreads < 1) nthreads = 1;
246248
http_serve(api, 8080, s::nthreads = nthreads);
247249
return 0;

frameworks/lithium/meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
"engine": "li/http",
66
"description": "Lithium C++ HTTP framework with compile-time reflection, boost::context coroutines, and -O3 -march=native -flto.",
77
"repo": "https://github.com/matt-42/lithium",
8-
"enabled": true,
8+
"enabled": false,
99
"tests": ["baseline", "pipelined", "limited-conn"]
1010
}

frameworks/nginx/Dockerfile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
FROM debian:bookworm AS build
2+
3+
RUN apt-get update && apt-get install -y --no-install-recommends \
4+
build-essential libssl-dev libpcre2-dev zlib1g-dev wget ca-certificates
5+
6+
# Download nginx source
7+
WORKDIR /tmp
8+
ARG NGINX_VERSION=1.26.2
9+
RUN wget -q http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
10+
tar xzf nginx-${NGINX_VERSION}.tar.gz
11+
12+
# Download cJSON for JSON parsing
13+
RUN wget -q -O /tmp/cJSON.c https://raw.githubusercontent.com/DaveGamble/cJSON/v1.7.18/cJSON.c && \
14+
wget -q -O /tmp/cJSON.h https://raw.githubusercontent.com/DaveGamble/cJSON/v1.7.18/cJSON.h
15+
16+
# Copy module
17+
COPY ngx_http_httparena_module.c config /tmp/module/
18+
RUN cp /tmp/cJSON.c /tmp/cJSON.h /tmp/module/
19+
20+
# Build nginx with custom module
21+
WORKDIR /tmp/nginx-${NGINX_VERSION}
22+
RUN ./configure \
23+
--with-http_ssl_module \
24+
--with-http_v2_module \
25+
--add-module=/tmp/module \
26+
--with-cc-opt="-O3 -march=native -flto -DNDEBUG" \
27+
--with-ld-opt="-flto -lm" && \
28+
make -j$(nproc)
29+
30+
FROM debian:bookworm-slim
31+
RUN apt-get update && apt-get install -y --no-install-recommends \
32+
libssl3 libpcre2-8-0 && \
33+
rm -rf /var/lib/apt/lists/*
34+
COPY --from=build /tmp/nginx-1.26.2/objs/nginx /usr/sbin/nginx
35+
RUN mkdir -p /usr/local/nginx/logs
36+
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
37+
EXPOSE 8080 8443
38+
CMD ["nginx", "-g", "daemon off;"]

frameworks/nginx/config

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
ngx_addon_name=ngx_http_httparena_module
2+
3+
if test -n "$ngx_module_link"; then
4+
ngx_module_type=HTTP
5+
ngx_module_name=ngx_http_httparena_module
6+
ngx_module_srcs="$ngx_addon_dir/ngx_http_httparena_module.c $ngx_addon_dir/cJSON.c"
7+
. auto/module
8+
else
9+
HTTP_MODULES="$HTTP_MODULES ngx_http_httparena_module"
10+
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_httparena_module.c $ngx_addon_dir/cJSON.c"
11+
fi

frameworks/nginx/meta.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"display_name": "nginx",
3+
"language": "C",
4+
"type": "realistic",
5+
"engine": "nginx",
6+
"description": "Nginx with a custom C handler module, compiled with -O3 -march=native -flto.",
7+
"repo": "https://github.com/nginx/nginx",
8+
"enabled": true,
9+
"tests": ["baseline", "pipelined", "limited-conn", "json", "baseline-h2", "static-h2"]
10+
}

0 commit comments

Comments
 (0)