diff --git a/frameworks/tokio/.dockerignore b/frameworks/tokio/.dockerignore new file mode 100644 index 00000000..c7f83c2c --- /dev/null +++ b/frameworks/tokio/.dockerignore @@ -0,0 +1,2 @@ +target +.git diff --git a/frameworks/tokio/.gitignore b/frameworks/tokio/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/frameworks/tokio/.gitignore @@ -0,0 +1 @@ +/target diff --git a/frameworks/tokio/Cargo.lock b/frameworks/tokio/Cargo.lock new file mode 100644 index 00000000..ba0a4478 --- /dev/null +++ b/frameworks/tokio/Cargo.lock @@ -0,0 +1,168 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "httparena-tokio" +version = "0.1.0" +dependencies = [ + "socket2 0.5.10", + "tokio", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2 0.6.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/frameworks/tokio/Cargo.toml b/frameworks/tokio/Cargo.toml new file mode 100644 index 00000000..0e16f1b9 --- /dev/null +++ b/frameworks/tokio/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "httparena-tokio" +version = "0.1.0" +edition = "2021" + +[dependencies] +socket2 = { version = "0.5", features = ["all"] } +tokio = { version = "1", default-features = false, features = ["io-util", "net", "rt"] } + +[profile.release] +codegen-units = 1 +lto = "thin" +opt-level = 3 +panic = "abort" diff --git a/frameworks/tokio/Dockerfile b/frameworks/tokio/Dockerfile new file mode 100644 index 00000000..5d72f3cd --- /dev/null +++ b/frameworks/tokio/Dockerfile @@ -0,0 +1,13 @@ +FROM rust:1.95 AS build +WORKDIR /app +COPY Cargo.toml Cargo.lock ./ +RUN mkdir src && echo "fn main() {}" > src/main.rs \ + && cargo build --release \ + && rm -rf src target/release/httparena-tokio target/release/deps/httparena_tokio* +COPY src ./src +RUN RUSTFLAGS="-C target-cpu=native" cargo build --release + +FROM debian:bookworm-slim +COPY --from=build /app/target/release/httparena-tokio /server +EXPOSE 8080 +CMD ["/server"] diff --git a/frameworks/tokio/README.md b/frameworks/tokio/README.md new file mode 100644 index 00000000..2d7386b8 --- /dev/null +++ b/frameworks/tokio/README.md @@ -0,0 +1,23 @@ +# tokio + +A minimal **HTTP/1.1** server on raw **tokio** — no HTTP framework. It serves the +H1-isolated profiles (`baseline`, `pipelined`, `limited-conn`) with a hand-rolled +request parser on a tokio `TcpStream`. + +## Serving model +One `current_thread` tokio runtime per core, each binding `:8080` with +`SO_REUSEPORT` (kernel-sharded accept, no cross-core work-stealing), `TCP_NODELAY` +per connection. Responses are batched per read, so a pipelined burst flushes in +one write. + +## Hand-rolled HTTP/1.1 +Request line + headers, `Content-Length` **and** `Transfer-Encoding: chunked` +bodies, keep-alive (multiple requests per connection), request pipelining, and +fragmented-read reassembly (requests split across `recv`s). + +| Endpoint | Response | +|---|---| +| `GET/POST /baseline11?a=&b=` | `text/plain` — `a + b` (+ POST body as an integer) | +| `GET /pipeline` | `text/plain` — `ok` | + +`PORT` overrides the listen port for local testing (defaults to 8080). diff --git a/frameworks/tokio/meta.json b/frameworks/tokio/meta.json new file mode 100644 index 00000000..af600940 --- /dev/null +++ b/frameworks/tokio/meta.json @@ -0,0 +1,15 @@ +{ + "display_name": "tokio", + "language": "Rust", + "type": "engine", + "engine": "tokio", + "description": "Minimal HTTP/1.1 server on raw tokio — no HTTP framework. A hand-rolled request parser (request line, headers, Content-Length + chunked bodies, keep-alive, pipelining, fragmented reads) on a tokio TcpStream, with one current-thread runtime per core and SO_REUSEPORT.", + "repo": "https://github.com/tokio-rs/tokio", + "enabled": true, + "tests": [ + "baseline", + "pipelined", + "limited-conn" + ], + "maintainers": [] +} diff --git a/frameworks/tokio/src/main.rs b/frameworks/tokio/src/main.rs new file mode 100644 index 00000000..8c4c3824 --- /dev/null +++ b/frameworks/tokio/src/main.rs @@ -0,0 +1,318 @@ +//! tokio — a minimal HTTP/1.1 server on raw tokio for the H1-isolated profiles +//! (baseline, pipelined, limited-conn). No HTTP framework: the request parser +//! (request line, headers, Content-Length + chunked bodies, keep-alive, +//! pipelining, fragmented reads) is hand-rolled on a tokio TcpStream. +//! +//! Serving model: one current-thread runtime per core, each with its own +//! SO_REUSEPORT listener. Listens on 0.0.0.0:8080 (PORT overrides for testing). +//! +//! Endpoints: +//! GET/POST /baseline11?a=&b= -> text/plain "a + b (+ body)" +//! GET /pipeline -> text/plain "ok" + +use socket2::{Domain, Protocol, Socket, Type}; +use std::net::SocketAddr; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::{TcpListener, TcpStream}; + +const READ_SIZE: usize = 16 * 1024; +const MAX_BUF: usize = 1 << 20; + +fn main() { + let threads = std::thread::available_parallelism() + .map(|n| n.get()) + .unwrap_or(1); + let mut handles = Vec::with_capacity(threads); + for _ in 0..threads { + handles.push(std::thread::spawn(|| { + tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .expect("runtime") + .block_on(serve()); + })); + } + for h in handles { + let _ = h.join(); + } +} + +async fn serve() { + let listener = bind_reuseport(); + loop { + match listener.accept().await { + Ok((stream, _)) => { + tokio::spawn(handle(stream)); + } + Err(_) => continue, + } + } +} + +fn bind_reuseport() -> TcpListener { + let port = std::env::var("PORT").ok().and_then(|p| p.parse().ok()).unwrap_or(8080u16); + let addr: SocketAddr = format!("0.0.0.0:{port}").parse().unwrap(); + let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); + socket.set_reuse_address(true).unwrap(); + socket.set_reuse_port(true).unwrap(); + socket.set_nonblocking(true).unwrap(); + socket.bind(&addr.into()).unwrap(); + socket.listen(1024).unwrap(); + TcpListener::from_std(socket.into()).unwrap() +} + +async fn handle(mut stream: TcpStream) { + let _ = stream.set_nodelay(true); + let mut buf: Vec = Vec::with_capacity(READ_SIZE); + let mut out: Vec = Vec::with_capacity(READ_SIZE); + let mut tmp = [0u8; READ_SIZE]; + + loop { + // Drain every complete request currently buffered, batching responses. + let mut pos = 0; + let mut keep = true; + loop { + match process(&buf[pos..], &mut out) { + Outcome::Incomplete => break, + Outcome::Complete { consumed, keep_alive } => { + pos += consumed; + if !keep_alive { + keep = false; + break; + } + } + } + } + if pos > 0 { + buf.drain(..pos); + } + if !out.is_empty() { + if stream.write_all(&out).await.is_err() { + return; + } + out.clear(); + } + if !keep || buf.len() > MAX_BUF { + return; + } + let n = match stream.read(&mut tmp).await { + Ok(0) | Err(_) => return, + Ok(n) => n, + }; + buf.extend_from_slice(&tmp[..n]); + } +} + +// ── Hand-rolled HTTP/1.1 ───────────────────────────────────────────────────── + +enum Outcome { + Incomplete, + Complete { consumed: usize, keep_alive: bool }, +} + +/// Parse one request from buf; append its response to out. Returns Incomplete if +/// the request isn't fully buffered yet. +fn process(buf: &[u8], out: &mut Vec) -> Outcome { + let he = match find(buf, b"\r\n\r\n") { + Some(h) => h, + None => return Outcome::Incomplete, + }; + + let mut lines = lines_crlf(&buf[..he]); + let req_line = lines.next().unwrap_or(b""); + let mut rl = req_line.split(|&c| c == b' '); + let _method = rl.next().unwrap_or(b""); + let target = rl.next().unwrap_or(b""); + + let mut content_length: Option = None; + let mut chunked = false; + let mut close = false; + for line in lines { + if let Some(c) = find(line, b":") { + let name = &line[..c]; + let val = trim(&line[c + 1..]); + if ci_eq(name, b"content-length") { + content_length = parse_usize(val); + } else if ci_eq(name, b"transfer-encoding") && ci_contains(val, b"chunked") { + chunked = true; + } else if ci_eq(name, b"connection") && ci_eq(val, b"close") { + close = true; + } + } + } + + let body_start = he + 4; + let (body_int, consumed) = if chunked { + match decode_chunked(&buf[body_start..]) { + Some((body, used)) => (parse_i64(&body), body_start + used), + None => return Outcome::Incomplete, + } + } else if let Some(cl) = content_length { + if buf.len() < body_start + cl { + return Outcome::Incomplete; + } + (parse_i64(&buf[body_start..body_start + cl]), body_start + cl) + } else { + (0, body_start) + }; + + respond(out, target, body_int); + Outcome::Complete { consumed, keep_alive: !close } +} + +fn respond(out: &mut Vec, target: &[u8], body_int: i64) { + let q = find(target, b"?"); + let path = match q { + Some(i) => &target[..i], + None => target, + }; + if path == b"/pipeline" { + write_resp(out, b"ok"); + } else { + let query = match q { + Some(i) => &target[i + 1..], + None => &[][..], + }; + let (a, b) = parse_ab(query); + let s = (a + b + body_int).to_string(); + write_resp(out, s.as_bytes()); + } +} + +fn write_resp(out: &mut Vec, body: &[u8]) { + out.extend_from_slice(b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: "); + out.extend_from_slice(body.len().to_string().as_bytes()); + out.extend_from_slice(b"\r\n\r\n"); + out.extend_from_slice(body); +} + +fn parse_ab(query: &[u8]) -> (i64, i64) { + let (mut a, mut b) = (0i64, 0i64); + for kv in query.split(|&c| c == b'&') { + if let Some(eq) = find(kv, b"=") { + let (k, v) = (&kv[..eq], &kv[eq + 1..]); + if k == b"a" { + a = parse_i64(v); + } else if k == b"b" { + b = parse_i64(v); + } + } + } + (a, b) +} + +/// Decode a chunked body. Returns (decoded_bytes, bytes_consumed) or None if the +/// terminating 0-chunk isn't fully buffered yet. +fn decode_chunked(buf: &[u8]) -> Option<(Vec, usize)> { + let mut body = Vec::new(); + let mut pos = 0; + loop { + let nl = find(&buf[pos..], b"\r\n")?; + let size = parse_hex(&buf[pos..pos + nl])?; + pos += nl + 2; + if size == 0 { + let end = find(&buf[pos..], b"\r\n")?; // final CRLF (no trailers) + return Some((body, pos + end + 2)); + } + if buf.len() < pos + size + 2 { + return None; + } + body.extend_from_slice(&buf[pos..pos + size]); + pos += size; + if &buf[pos..pos + 2] != b"\r\n" { + return None; + } + pos += 2; + } +} + +// ── byte helpers ───────────────────────────────────────────────────────────── + +fn find(h: &[u8], n: &[u8]) -> Option { + if n.is_empty() || h.len() < n.len() { + return None; + } + h.windows(n.len()).position(|w| w == n) +} + +fn lines_crlf(b: &[u8]) -> impl Iterator { + b.split(|&c| c == b'\n').map(|l| { + if l.last() == Some(&b'\r') { + &l[..l.len() - 1] + } else { + l + } + }) +} + +fn trim(mut b: &[u8]) -> &[u8] { + while matches!(b.first(), Some(b' ') | Some(b'\t')) { + b = &b[1..]; + } + while matches!(b.last(), Some(b' ') | Some(b'\t')) { + b = &b[..b.len() - 1]; + } + b +} + +fn ci_eq(a: &[u8], b: &[u8]) -> bool { + a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.eq_ignore_ascii_case(y)) +} + +fn ci_contains(h: &[u8], n: &[u8]) -> bool { + !n.is_empty() && h.len() >= n.len() && h.windows(n.len()).any(|w| ci_eq(w, n)) +} + +fn parse_i64(b: &[u8]) -> i64 { + let b = trim(b); + let (neg, b) = match b.first() { + Some(b'-') => (true, &b[1..]), + _ => (false, b), + }; + let mut n = 0i64; + for &c in b { + if c.is_ascii_digit() { + n = n * 10 + (c - b'0') as i64; + } else { + break; + } + } + if neg { + -n + } else { + n + } +} + +fn parse_usize(b: &[u8]) -> Option { + let b = trim(b); + if b.is_empty() || !b.iter().all(|c| c.is_ascii_digit()) { + return None; + } + let mut n = 0usize; + for &c in b { + n = n.checked_mul(10)?.checked_add((c - b'0') as usize)?; + } + Some(n) +} + +fn parse_hex(b: &[u8]) -> Option { + let mut n = 0usize; + let mut any = false; + for &c in b { + let d = match c { + b'0'..=b'9' => c - b'0', + b'a'..=b'f' => c - b'a' + 10, + b'A'..=b'F' => c - b'A' + 10, + b';' | b' ' => break, // chunk extensions / padding + _ => return if any { Some(n) } else { None }, + }; + n = n.checked_mul(16)?.checked_add(d as usize)?; + any = true; + } + if any { + Some(n) + } else { + None + } +} diff --git a/site/data/baseline-4096.json b/site/data/baseline-4096.json index b901c2e6..5738d0b1 100644 --- a/site/data/baseline-4096.json +++ b/site/data/baseline-4096.json @@ -1306,6 +1306,26 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio", + "language": "Rust", + "rps": 4187085, + "avg_latency": "977us", + "p99_latency": "1.34ms", + "cpu": "6417.9%", + "memory": "187MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "263.43MB/s", + "input_bw": "323.44MB/s", + "reconnects": 0, + "status_2xx": 20935425, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/baseline-512.json b/site/data/baseline-512.json index 4f014b60..663af5b8 100644 --- a/site/data/baseline-512.json +++ b/site/data/baseline-512.json @@ -1306,6 +1306,26 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio", + "language": "Rust", + "rps": 4269664, + "avg_latency": "119us", + "p99_latency": "266us", + "cpu": "6289.3%", + "memory": "43MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "268.64MB/s", + "input_bw": "329.82MB/s", + "reconnects": 0, + "status_2xx": 21348321, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/frameworks.json b/site/data/frameworks.json index 461a48d9..08e14ccc 100644 --- a/site/data/frameworks.json +++ b/site/data/frameworks.json @@ -625,6 +625,13 @@ "type": "engine", "engine": "tokio" }, + "tokio": { + "dir": "tokio", + "description": "Minimal HTTP/1.1 server on raw tokio \u2014 no HTTP framework. A hand-rolled request parser (request line, headers, Content-Length + chunked bodies, keep-alive, pipelining, fragmented reads) on a tokio TcpStream, with one current-thread runtime per core and SO_REUSEPORT.", + "repo": "https://github.com/tokio-rs/tokio", + "type": "engine", + "engine": "tokio" + }, "tonic-grpc": { "dir": "tonic-grpc", "description": "Rust gRPC server using tonic, a native Rust gRPC implementation built on top of hyper and tower.", diff --git a/site/data/limited-conn-4096.json b/site/data/limited-conn-4096.json index e3691171..d91fc60a 100644 --- a/site/data/limited-conn-4096.json +++ b/site/data/limited-conn-4096.json @@ -1306,6 +1306,26 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio", + "language": "Rust", + "rps": 2721455, + "avg_latency": "1.38ms", + "p99_latency": "2.36ms", + "cpu": "5727.1%", + "memory": "426MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "171.22MB/s", + "input_bw": "210.23MB/s", + "reconnects": 1360469, + "status_2xx": 13607276, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/limited-conn-512.json b/site/data/limited-conn-512.json index c2fc938b..bf0e1b93 100644 --- a/site/data/limited-conn-512.json +++ b/site/data/limited-conn-512.json @@ -1306,6 +1306,26 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio", + "language": "Rust", + "rps": 2563668, + "avg_latency": "185us", + "p99_latency": "638us", + "cpu": "5380.0%", + "memory": "106MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "161.29MB/s", + "input_bw": "198.04MB/s", + "reconnects": 1281825, + "status_2xx": 12818344, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/pipelined-4096.json b/site/data/pipelined-4096.json index 4187741a..b9260034 100644 --- a/site/data/pipelined-4096.json +++ b/site/data/pipelined-4096.json @@ -1236,6 +1236,25 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio", + "language": "Rust", + "rps": 54755795, + "avg_latency": "1.20ms", + "p99_latency": "1.62ms", + "cpu": "6057.7%", + "memory": "164MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 16, + "bandwidth": "3.36GB/s", + "reconnects": 0, + "status_2xx": 273778976, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/pipelined-512.json b/site/data/pipelined-512.json index c6cafa09..cb4d3983 100644 --- a/site/data/pipelined-512.json +++ b/site/data/pipelined-512.json @@ -1236,6 +1236,25 @@ "status_4xx": 0, "status_5xx": 2 }, + { + "framework": "tokio", + "language": "Rust", + "rps": 54824854, + "avg_latency": "148us", + "p99_latency": "272us", + "cpu": "6385.0%", + "memory": "46MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 16, + "bandwidth": "3.37GB/s", + "reconnects": 0, + "status_2xx": 274124272, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/static/logs/baseline/4096/tokio.log b/site/static/logs/baseline/4096/tokio.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/baseline/512/tokio.log b/site/static/logs/baseline/512/tokio.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/limited-conn/4096/tokio.log b/site/static/logs/limited-conn/4096/tokio.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/limited-conn/512/tokio.log b/site/static/logs/limited-conn/512/tokio.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/pipelined/4096/tokio.log b/site/static/logs/pipelined/4096/tokio.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/pipelined/512/tokio.log b/site/static/logs/pipelined/512/tokio.log new file mode 100644 index 00000000..e69de29b