nexus-web 0.8.0

Low-latency WebSocket, HTTP/1.1, and REST primitives. Sans-IO, zero-copy, SIMD-accelerated.
Documentation

nexus-web

Low-latency WebSocket, HTTP/1.1, and REST primitives. Sans-IO, zero-copy, SIMD-accelerated. Framework-agnostic — works with mio, io_uring, tokio, or raw syscalls.

Extracted from nexus-net 0.7.x. Protocol state machines operate on byte slices — no async runtime, no I/O layer.

Performance

vs tungstenite (in-memory parse, pinned to cores 0,2)

Payload Type nexus-web tungstenite Speedup
40B binary parse 19ns (52M/s) 61ns (16M/s) 3.2x
128B binary parse 24ns (42M/s) 75ns (13M/s) 3.1x
512B binary parse 49ns (20M/s) 105ns (10M/s) 2.1x
77B JSON quote parse+deser 146ns (6.9M/s) 205ns (4.9M/s) 1.4x
40B binary TCP loopback 30ns (33M/s) 66ns (15M/s) 2.2x

rdtsc cycle distribution (pinned to core 0, batch=64)

Path p50 p90 p99 p99.9
text unmasked 128B 39 39 43 65
binary unmasked 128B 35 36 44 129
text masked 128B 52 53 58 124
apply_mask 128B 12 12 16 31
encode_text 128B server 10 11 22 39
throughput 100x128B /msg 28 28 44 91

At 3GHz: 39 cycles = 13ns. In-memory throughput: 107M msg/sec (28 cycles/msg).

vs reqwest (REST HTTP/1.1 client)

Benchmark nexus-web reqwest Speedup
POST build+write+parse (mock) p50 494 cycles (165ns) 1,549 cycles (516ns) build-only 3.1x
TCP loopback round-trip p50 22,924 cycles (7.6us) 62,802 cycles (20.9us) 2.7x
TCP loopback throughput 114K req/sec 39K req/sec 2.9x

517/517 Autobahn WebSocket conformance. 16/16 httpbin.org REST conformance.

Architecture

Application
    |
    +-- WebSocket                    REST HTTP/1.1
    |   ^ Message<'a>               ^ Request<'a> / RestResponse<'a>
    |   FrameReader / FrameWriter   RequestWriter / ResponseReader    (sans-IO)
    |   ^ plaintext bytes           ^ plaintext bytes
    |   +----------+----------------+
    |              TlsCodec                     (optional, nexus-net)
    |              ^ encrypted bytes
    +--------------+
                   I/O                          (your choice)

Each layer is a pure state machine. No syscalls, no sockets, no async. Bytes in, messages out.

Async Adapters

For async usage, see nexus-async-web — thin async wrappers over the same sans-IO primitives. Supports both tokio and nexus-async-rt backends.

Quick Start

[dependencies]
# WebSocket + HTTP, no TLS
nexus-web = "0.8"

# With TLS (rustls + aws-lc-rs via nexus-net)
nexus-web = { version = "0.8", features = ["tls"] }

# Everything (TLS + socket options + bytes)
nexus-web = { version = "0.8", features = ["full"] }

WebSocket Client (ws://)

use nexus_web::ws::{Client, Message, CloseCode};

let mut ws = Client::builder().connect("ws://exchange.com:80/ws/v1")?;

ws.send_text(r#"{"subscribe":"trades.BTC-USD"}"#)?;

loop {
    match ws.recv()? {
        Some(Message::Text(json)) => process(json),     // &str, zero-copy
        Some(Message::Binary(data)) => process(data),   // &[u8], zero-copy
        Some(Message::Ping(p)) => ws.send_pong(p)?,
        Some(Message::Close(frame)) => {
            ws.close(CloseCode::Normal, "")?;
            break;
        }
        Some(Message::Pong(_)) => {}
        None => break,
    }
}

WebSocket Client (wss://)

use nexus_web::ws::Client;
use nexus_web::tls::TlsConfig;  // re-exported from nexus-net

let tls = TlsConfig::new()?;
let mut ws = Client::builder().tls(&tls).connect("wss://exchange.com/ws/v1")?;

REST Client (HTTP/1.1, blocking)

use nexus_web::rest::{Client, RequestWriter};
use nexus_web::http::ResponseReader;

let mut writer = RequestWriter::new("httpbin.org")?;
writer.default_header("Accept", "application/json")?;

let mut reader = ResponseReader::new(32 * 1024).max_body_size(32 * 1024);

let tls = nexus_web::tls::TlsConfig::new()?;
let mut conn = Client::builder().tls(&tls).connect("https://httpbin.org")?;

let req = writer.get("/get")
    .query("symbol", "BTC-USD")
    .query("limit", "100")
    .finish()?;
let resp = conn.send(req, &mut reader)?;
println!("status: {}", resp.status());

Sans-IO (decoupled from sockets)

use nexus_web::ws::{self, Message, Role};

let (mut reader, writer) = ws::pair(Role::Client);

reader.read_from(&mut socket)?;

for _ in 0..8 {
    match reader.next()? {
        Some(Message::Text(s)) => handle(s),
        Some(Message::Ping(p)) => {
            let mut dst = [0u8; 131];
            let n = writer.encode_pong(p, &mut dst)?;
            socket.write_all(&dst[..n])?;
        }
        None => break,
        _ => {}
    }
}

Modules

ws — WebSocket (RFC 6455)

  • FrameReader — sans-IO inbound parser. Frame parsing, fragment assembly, control frame interleaving, SIMD masking, UTF-8 validation.
  • FrameWriter — sans-IO outbound encoder.
  • Client<S> — convenience I/O wrapper with HTTP upgrade handshake.
  • Message<'a>Text(&str), Binary(&[u8]), Ping, Pong, Close.

http — HTTP/1.1 Primitives

  • RequestReader / ResponseReader — sans-IO HTTP parsers backed by httparse (SIMD-accelerated).
  • ChunkedDecoder — sans-IO chunked transfer encoding decoder.
  • write_request / write_response — zero-alloc HTTP construction.

rest — HTTP/1.1 REST Client

  • RequestWriter — sans-IO request encoder with typestate builder.
  • Client<S> — pure transport. TLS handled at the stream level.
  • RestResponse<'a> — borrows from ResponseReader.

Features

Feature Default Description
tls No TLS support via nexus-net (rustls + aws-lc-rs)
socket-opts No Socket options via socket2
bytes No bytes::Bytes conversion on OwnedMessage and RestResponse
full No tls + socket-opts + bytes

Design Decisions

Zero-copy inbound. Message::Text(&str) borrows from the reader's internal buffer. No heap allocation per message.

Sans-IO. Protocol logic is a pure state machine. The same FrameReader works with blocking sockets, mio, io_uring, tokio, or kernel bypass.

SIMD-accelerated. XOR masking uses SSE2/AVX2. UTF-8 validation uses simdutf8. HTTP header parsing uses httparse (SIMD vectorized).

No permessage-deflate. Exchanges that compress use application-level gzip, not WebSocket compression.

Testing

cargo test -p nexus-web
cargo test -p nexus-web --features tls

# Autobahn conformance (requires Podman)
podman run --rm -d --network=host \
    -v "${PWD}/nexus-web/tests/autobahn:/config:Z" \
    -v "${PWD}/target/autobahn-reports:/reports:Z" \
    docker.io/crossbario/autobahn-testsuite \
    wstest -m fuzzingserver -s /config/fuzzingserver.json
cargo test -p nexus-web --test autobahn -- --ignored --nocapture

# httpbin.org conformance (requires network)
cargo test -p nexus-web --all-features --test httpbin -- --ignored --test-threads=1

# Benchmarks
cargo run --release -p nexus-web --example perf_ws
cargo run --release -p nexus-web --example perf_vs_tungstenite
cargo run --release -p nexus-web --example perf_rest
cargo run --release -p nexus-web --features tls --example perf_tls