From 6198e3ab2ea57abdef83542f7ebe748f73ac9fde Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Thu, 26 Feb 2026 14:50:49 +0200 Subject: [PATCH] feat(common,server): add ProtocolMode and route server through protocol dispatch --- common/src/error.rs | 2 +- common/src/lib.rs | 21 +++++++++++++--- common/src/prelude.rs | 2 +- common/src/protocol.rs | 1 + justfile | 3 ++- runner/src/args.rs | 18 +++++++------- runner/src/error.rs | 2 +- server/src/error.rs | 2 +- server/src/main.rs | 10 +++++--- server/src/server/mod.rs | 32 ++++++++++++++++++++++++ server/src/{server.rs => server/raw.rs} | 33 +++++-------------------- 11 files changed, 79 insertions(+), 47 deletions(-) create mode 100644 server/src/server/mod.rs rename server/src/{server.rs => server/raw.rs} (65%) diff --git a/common/src/error.rs b/common/src/error.rs index 6b30841..b1be39c 100644 --- a/common/src/error.rs +++ b/common/src/error.rs @@ -1,7 +1,7 @@ use miette::Diagnostic; use thiserror::Error; -/// Result type using the common's custom error type. +/// Result type using the `common`'s custom error type. pub type Result = std::result::Result; #[derive(Debug, Error, Diagnostic)] diff --git a/common/src/lib.rs b/common/src/lib.rs index 133dbfa..85ae3a1 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,5 +1,3 @@ -//! Common types and utilities for the TLS benchmark harness - pub mod cert; pub mod error; pub mod prelude; @@ -10,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::fmt; use strum::{Display, EnumString}; -/// TLS key exchange mode +/// TLS 1.3 key exchange mode used for benchmark runs #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumString, Display)] #[strum(serialize_all = "lowercase")] #[serde(rename_all = "lowercase")] @@ -21,6 +19,23 @@ pub enum KeyExchangeMode { X25519Mlkem768, } +/// Application protocol carried over TLS in benchmark runs. +/// +/// `Raw` is a minimal custom framing protocol (8-byte LE length request, then N payload bytes) +/// used for low-overhead microbenchmarks. +/// +/// `Http1` is an HTTP/1.1 request/response mode (`GET /bytes/{n}`) used for realism-oriented +/// comparisons where HTTP parsing and headers are part of measured overhead. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumString, Display)] +#[strum(serialize_all = "lowercase")] +#[serde(rename_all = "lowercase")] +pub enum ProtocolMode { + /// Minimal custom framing protocol for primary microbenchmarks. + Raw, + /// HTTP/1.1 mode for realism-oriented comparisons. + Http1, +} + /// A single benchmark measurement record, output as NDJSON #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BenchRecord { diff --git a/common/src/prelude.rs b/common/src/prelude.rs index 2abb89a..d82f6e1 100644 --- a/common/src/prelude.rs +++ b/common/src/prelude.rs @@ -1,4 +1,4 @@ pub use crate::{ - BenchRecord, KeyExchangeMode, + BenchRecord, KeyExchangeMode, ProtocolMode, protocol::{read_payload, read_request, write_payload, write_request}, }; diff --git a/common/src/protocol.rs b/common/src/protocol.rs index 75e2122..aa4631c 100644 --- a/common/src/protocol.rs +++ b/common/src/protocol.rs @@ -42,6 +42,7 @@ pub async fn write_request(writer: &mut W, size: u64) /// Generate deterministic payload of the given size. /// /// The pattern is a repeating sequence: 0x00, 0x01, ..., 0xFF, 0x00, ... +#[inline] #[must_use] pub fn generate_payload(size: u64) -> Vec { (0..size).map(|i| (i & 0xFF) as u8).collect() diff --git a/justfile b/justfile index 651335b..0d4edcb 100644 --- a/justfile +++ b/justfile @@ -66,10 +66,11 @@ server mode="x25519" listen="127.0.0.1:4433": # Run benchmark runner [group("run")] -runner server mode="x25519" payload="1024" iters="100" warmup="10": +runner server mode="x25519" proto="raw" payload="1024" iters="100" warmup="10": cargo run --release --bin runner -- \ --server {{server}} \ --mode {{mode}} \ + --proto {{proto}} \ --payload-bytes {{payload}} \ --iters {{iters}} \ --warmup {{warmup}} diff --git a/runner/src/args.rs b/runner/src/args.rs index e1eb1aa..2b40b37 100644 --- a/runner/src/args.rs +++ b/runner/src/args.rs @@ -1,5 +1,5 @@ use clap::Parser; -use common::KeyExchangeMode; +use common::prelude::*; use std::{net::SocketAddr, path::PathBuf}; /// TLS benchmark runner. @@ -10,31 +10,31 @@ pub struct Args { #[arg(long, default_value = "x25519")] pub mode: KeyExchangeMode, - /// Server address to connect to. + /// Server address to connect to #[arg(long, required_unless_present = "config")] pub server: Option, - /// Payload size in bytes to request from server. + /// Payload size in bytes to request from server #[arg(long, default_value = "1024")] pub payload_bytes: u32, - /// Number of benchmark iterations (excluding warmup). + /// Number of benchmark iterations (excluding warmup) #[arg(long, default_value = "100")] pub iters: u32, - /// Number of warmup iterations (not recorded). + /// Number of warmup iterations (not recorded) #[arg(long, default_value = "10")] pub warmup: u32, - /// Number of concurrent connections. + /// Number of concurrent connections #[arg(long, default_value = "1")] pub concurrency: u32, - /// Output file for NDJSON records (stdout if not specified). + /// Output file for NDJSON records (stdout if not specified) #[arg(long)] pub out: Option, - /// Config file for matrix benchmarks (TOML). - #[arg(long)] + /// Config file for matrix benchmarks (TOML) + #[arg(long, short)] pub config: Option, } diff --git a/runner/src/error.rs b/runner/src/error.rs index 4570352..98ca784 100644 --- a/runner/src/error.rs +++ b/runner/src/error.rs @@ -5,7 +5,7 @@ use miette::{Diagnostic, NamedSource, SourceSpan}; use std::path::PathBuf; use thiserror::Error; -/// Result type using the runner's custom error type. +/// Result type using the `runner`'s custom error type. pub type Result = std::result::Result; /// Errors that can occur during benchmark execution. diff --git a/server/src/error.rs b/server/src/error.rs index 217940d..c56ab01 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -1,7 +1,7 @@ use miette::Diagnostic; use thiserror::Error; -/// Result type using the servers's custom error type. +/// Result type using the `servers`'s custom error type. pub type Result = std::result::Result; #[derive(Debug, Error, Diagnostic)] diff --git a/server/src/main.rs b/server/src/main.rs index dfe324a..3f9bde3 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -20,11 +20,15 @@ use tracing_subscriber::EnvFilter; #[derive(Debug, Parser)] #[command(name = "server", version, about)] struct Args { - /// Key exchange mode. + /// Key exchange mode #[arg(long, default_value = "x25519")] mode: KeyExchangeMode, - /// Address to listen on. + /// Protocol carrier + #[arg(long, default_value = "raw")] + pub proto: ProtocolMode, + + /// Address to listen on #[arg(long, default_value = "127.0.0.1:4433")] listen: SocketAddr, } @@ -65,7 +69,7 @@ async fn main() -> miette::Result<()> { "CA cert (truncated)" ); - Ok(run_server(args, tls_config).await?) + Ok(run_server(&args, tls_config).await?) } #[cfg(test)] diff --git a/server/src/server/mod.rs b/server/src/server/mod.rs new file mode 100644 index 0000000..0b29456 --- /dev/null +++ b/server/src/server/mod.rs @@ -0,0 +1,32 @@ +mod raw; + +use crate::{Args, error, server::raw::handle_raw_connection}; +use common::prelude::*; +use rustls::ServerConfig; +use std::sync::Arc; +use tokio::net::TcpListener; +use tracing::info; + +pub async fn run_server(args: &Args, tls_config: Arc) -> error::Result<()> { + let listener = TcpListener::bind(args.listen) + .await + .map_err(|e| error::Error::network(format!("failed to bind to {}: {e}", args.listen)))?; + + info!(listen = %args.listen, mode = %args.mode, "listening"); + + loop { + let (stream, peer) = match listener.accept().await { + Ok(conn) => conn, + Err(e) => { + error!(error = %e, "accept error"); + continue; + } + }; + + let config = tls_config.clone(); + tokio::spawn(match args.proto { + ProtocolMode::Raw => handle_raw_connection(stream, peer, config), + ProtocolMode::Http1 => todo!(), + }); + } +} diff --git a/server/src/server.rs b/server/src/server/raw.rs similarity index 65% rename from server/src/server.rs rename to server/src/server/raw.rs index eff9885..316f2fa 100644 --- a/server/src/server.rs +++ b/server/src/server/raw.rs @@ -1,15 +1,15 @@ -use crate::{Args, error}; use common::prelude::*; use rustls::{ServerConfig, server::Acceptor}; use std::{io::ErrorKind, net::SocketAddr, sync::Arc}; -use tokio::{ - io::AsyncWriteExt, - net::{TcpListener, TcpStream}, -}; +use tokio::{io::AsyncWriteExt, net::TcpStream}; use tokio_rustls::LazyConfigAcceptor; use tracing::{debug, info, warn}; -pub async fn handle_connection(stream: TcpStream, peer: SocketAddr, tls_config: Arc) { +pub async fn handle_raw_connection( + stream: TcpStream, + peer: SocketAddr, + tls_config: Arc, +) { let acceptor = LazyConfigAcceptor::new(Acceptor::default(), stream); let start_handshake = match acceptor.await { Ok(sh) => sh, @@ -57,24 +57,3 @@ pub async fn handle_connection(stream: TcpStream, peer: SocketAddr, tls_config: } } } - -pub async fn run_server(args: Args, tls_config: Arc) -> error::Result<()> { - let listener = TcpListener::bind(args.listen) - .await - .map_err(|e| error::Error::network(format!("failed to bind to {}: {e}", args.listen)))?; - - info!(listen = %args.listen, mode = %args.mode, "listening"); - - loop { - let (stream, peer) = match listener.accept().await { - Ok(conn) => conn, - Err(e) => { - error!(error = %e, "accept error"); - continue; - } - }; - - let config = tls_config.clone(); - tokio::spawn(handle_connection(stream, peer, config)); - } -}