feat(common,server): add ProtocolMode and route server through protocol dispatch

This commit is contained in:
2026-02-26 14:50:49 +02:00
parent 0ea39e7663
commit 6198e3ab2e
11 changed files with 79 additions and 47 deletions

View File

@@ -1,7 +1,7 @@
use miette::Diagnostic; use miette::Diagnostic;
use thiserror::Error; use thiserror::Error;
/// Result type using the common's custom error type. /// Result type using the `common`'s custom error type.
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error, Diagnostic)] #[derive(Debug, Error, Diagnostic)]

View File

@@ -1,5 +1,3 @@
//! Common types and utilities for the TLS benchmark harness
pub mod cert; pub mod cert;
pub mod error; pub mod error;
pub mod prelude; pub mod prelude;
@@ -10,7 +8,7 @@ use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use strum::{Display, EnumString}; 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumString, Display)]
#[strum(serialize_all = "lowercase")] #[strum(serialize_all = "lowercase")]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
@@ -21,6 +19,23 @@ pub enum KeyExchangeMode {
X25519Mlkem768, 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 /// A single benchmark measurement record, output as NDJSON
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchRecord { pub struct BenchRecord {

View File

@@ -1,4 +1,4 @@
pub use crate::{ pub use crate::{
BenchRecord, KeyExchangeMode, BenchRecord, KeyExchangeMode, ProtocolMode,
protocol::{read_payload, read_request, write_payload, write_request}, protocol::{read_payload, read_request, write_payload, write_request},
}; };

View File

@@ -42,6 +42,7 @@ pub async fn write_request<W: AsyncWriteExt + Unpin>(writer: &mut W, size: u64)
/// Generate deterministic payload of the given size. /// Generate deterministic payload of the given size.
/// ///
/// The pattern is a repeating sequence: 0x00, 0x01, ..., 0xFF, 0x00, ... /// The pattern is a repeating sequence: 0x00, 0x01, ..., 0xFF, 0x00, ...
#[inline]
#[must_use] #[must_use]
pub fn generate_payload(size: u64) -> Vec<u8> { pub fn generate_payload(size: u64) -> Vec<u8> {
(0..size).map(|i| (i & 0xFF) as u8).collect() (0..size).map(|i| (i & 0xFF) as u8).collect()

View File

@@ -66,10 +66,11 @@ server mode="x25519" listen="127.0.0.1:4433":
# Run benchmark runner # Run benchmark runner
[group("run")] [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 -- \ cargo run --release --bin runner -- \
--server {{server}} \ --server {{server}} \
--mode {{mode}} \ --mode {{mode}} \
--proto {{proto}} \
--payload-bytes {{payload}} \ --payload-bytes {{payload}} \
--iters {{iters}} \ --iters {{iters}} \
--warmup {{warmup}} --warmup {{warmup}}

View File

@@ -1,5 +1,5 @@
use clap::Parser; use clap::Parser;
use common::KeyExchangeMode; use common::prelude::*;
use std::{net::SocketAddr, path::PathBuf}; use std::{net::SocketAddr, path::PathBuf};
/// TLS benchmark runner. /// TLS benchmark runner.
@@ -10,31 +10,31 @@ pub struct Args {
#[arg(long, default_value = "x25519")] #[arg(long, default_value = "x25519")]
pub mode: KeyExchangeMode, pub mode: KeyExchangeMode,
/// Server address to connect to. /// Server address to connect to
#[arg(long, required_unless_present = "config")] #[arg(long, required_unless_present = "config")]
pub server: Option<SocketAddr>, pub server: Option<SocketAddr>,
/// Payload size in bytes to request from server. /// Payload size in bytes to request from server
#[arg(long, default_value = "1024")] #[arg(long, default_value = "1024")]
pub payload_bytes: u32, pub payload_bytes: u32,
/// Number of benchmark iterations (excluding warmup). /// Number of benchmark iterations (excluding warmup)
#[arg(long, default_value = "100")] #[arg(long, default_value = "100")]
pub iters: u32, pub iters: u32,
/// Number of warmup iterations (not recorded). /// Number of warmup iterations (not recorded)
#[arg(long, default_value = "10")] #[arg(long, default_value = "10")]
pub warmup: u32, pub warmup: u32,
/// Number of concurrent connections. /// Number of concurrent connections
#[arg(long, default_value = "1")] #[arg(long, default_value = "1")]
pub concurrency: u32, pub concurrency: u32,
/// Output file for NDJSON records (stdout if not specified). /// Output file for NDJSON records (stdout if not specified)
#[arg(long)] #[arg(long)]
pub out: Option<PathBuf>, pub out: Option<PathBuf>,
/// Config file for matrix benchmarks (TOML). /// Config file for matrix benchmarks (TOML)
#[arg(long)] #[arg(long, short)]
pub config: Option<PathBuf>, pub config: Option<PathBuf>,
} }

View File

@@ -5,7 +5,7 @@ use miette::{Diagnostic, NamedSource, SourceSpan};
use std::path::PathBuf; use std::path::PathBuf;
use thiserror::Error; use thiserror::Error;
/// Result type using the runner's custom error type. /// Result type using the `runner`'s custom error type.
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
/// Errors that can occur during benchmark execution. /// Errors that can occur during benchmark execution.

View File

@@ -1,7 +1,7 @@
use miette::Diagnostic; use miette::Diagnostic;
use thiserror::Error; use thiserror::Error;
/// Result type using the servers's custom error type. /// Result type using the `servers`'s custom error type.
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error, Diagnostic)] #[derive(Debug, Error, Diagnostic)]

View File

@@ -20,11 +20,15 @@ use tracing_subscriber::EnvFilter;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[command(name = "server", version, about)] #[command(name = "server", version, about)]
struct Args { struct Args {
/// Key exchange mode. /// Key exchange mode
#[arg(long, default_value = "x25519")] #[arg(long, default_value = "x25519")]
mode: KeyExchangeMode, 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")] #[arg(long, default_value = "127.0.0.1:4433")]
listen: SocketAddr, listen: SocketAddr,
} }
@@ -65,7 +69,7 @@ async fn main() -> miette::Result<()> {
"CA cert (truncated)" "CA cert (truncated)"
); );
Ok(run_server(args, tls_config).await?) Ok(run_server(&args, tls_config).await?)
} }
#[cfg(test)] #[cfg(test)]

32
server/src/server/mod.rs Normal file
View File

@@ -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<ServerConfig>) -> 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!(),
});
}
}

View File

@@ -1,15 +1,15 @@
use crate::{Args, error};
use common::prelude::*; use common::prelude::*;
use rustls::{ServerConfig, server::Acceptor}; use rustls::{ServerConfig, server::Acceptor};
use std::{io::ErrorKind, net::SocketAddr, sync::Arc}; use std::{io::ErrorKind, net::SocketAddr, sync::Arc};
use tokio::{ use tokio::{io::AsyncWriteExt, net::TcpStream};
io::AsyncWriteExt,
net::{TcpListener, TcpStream},
};
use tokio_rustls::LazyConfigAcceptor; use tokio_rustls::LazyConfigAcceptor;
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
pub async fn handle_connection(stream: TcpStream, peer: SocketAddr, tls_config: Arc<ServerConfig>) { pub async fn handle_raw_connection(
stream: TcpStream,
peer: SocketAddr,
tls_config: Arc<ServerConfig>,
) {
let acceptor = LazyConfigAcceptor::new(Acceptor::default(), stream); let acceptor = LazyConfigAcceptor::new(Acceptor::default(), stream);
let start_handshake = match acceptor.await { let start_handshake = match acceptor.await {
Ok(sh) => sh, 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<ServerConfig>) -> 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));
}
}