Skip to content

Commit 5a3f45a

Browse files
MDA2AVclaudegithub-actions[bot]
authored
[C] Add ringzero-ws - WebSocket (#815)
* [C] Add ringzero-ws - WebSocket Hand-rolled WebSocket echo server on raw io_uring (liburing), with no WebSocket library. Multishot accept + multishot recv into a provided buffer ring give zero-copy ingest; the RFC 6455 handshake (with from-scratch SHA-1 + base64), frame parser/masking, and batched echo write path are implemented directly against io_uring completions. One ring per core with SO_REUSEPORT, matching the ringzero engine. Subscribes to echo-ws and echo-ws-pipeline. Passes validate-ws.py (7/7) plus large-frame / pipelined / concurrent stress tests via the Docker build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Benchmark results: ringzero-ws --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent b418f06 commit 5a3f45a

17 files changed

Lines changed: 932 additions & 3 deletions

frameworks/ringzero-ws/Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM ubuntu:24.04 AS build
2+
RUN apt-get update && apt-get install -y gcc make git libc6-dev \
3+
&& rm -rf /var/lib/apt/lists/*
4+
RUN git clone --depth 1 --branch liburing-2.9 https://github.com/axboe/liburing.git /tmp/liburing \
5+
&& cd /tmp/liburing && ./configure && make -C src -j"$(nproc)" && make -C src install && ldconfig \
6+
&& rm -rf /tmp/liburing
7+
WORKDIR /app
8+
COPY main.c .
9+
RUN gcc -O3 -march=native -pthread -Wall -o server main.c -luring
10+
11+
FROM ubuntu:24.04
12+
COPY --from=build /usr/lib/liburing.so.2.9 /usr/lib/
13+
RUN ln -s /usr/lib/liburing.so.2.9 /usr/lib/liburing.so.2 && ldconfig
14+
COPY --from=build /app/server /server
15+
EXPOSE 8080
16+
CMD ["/server", "64"]

frameworks/ringzero-ws/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# ringzero-ws
2+
3+
A WebSocket echo server written directly on **raw io_uring** (liburing), with the
4+
WebSocket protocol **hand-rolled** — no `tokio-tungstenite`, no library WS stack.
5+
It's the io_uring sibling of the `ringzero` HTTP engine, and an `engine`-tier
6+
reference for what the modern Linux completion-based I/O path can do on `echo-ws`.
7+
8+
## The "ring-zero" I/O path
9+
10+
- **One `io_uring` per core**, each with its own `SO_REUSEPORT` listener — the
11+
kernel shards new connections across rings, so there's no shared accept queue
12+
and no cross-core work-stealing.
13+
- **Multishot accept** — a single SQE yields every incoming connection.
14+
- **Multishot recv + provided buffer ring** — recv is armed once per connection
15+
with `IOSQE_BUFFER_SELECT`; the kernel writes incoming bytes straight into a
16+
registered buffer slab (`io_uring_setup_buf_ring`) and hands back the buffer
17+
id per completion. Frames are parsed **in place** out of that buffer, which is
18+
recycled to the ring immediately after.
19+
- **`IORING_SETUP_SINGLE_ISSUER | DEFER_TASKRUN`** for the single-thread-per-ring
20+
fast path (with a graceful fallback for older kernels).
21+
22+
Outgoing echoes are batched into a per-connection write queue so a pipelined
23+
burst flushes in as few `send`s as possible, and the in-flight chunk is sealed
24+
so the kernel never sees a reallocated buffer.
25+
26+
## Hand-rolled WebSocket
27+
28+
- **RFC 6455 handshake** — request parsing, `Sec-WebSocket-Accept` derivation,
29+
and the `101` reply, with from-scratch **SHA-1 + base64** (`main.c`). The only
30+
dependency is liburing.
31+
- **Frame codec** — 7/16/64-bit lengths, client→server unmasking, partial frames
32+
carried across reads. Echoes are re-emitted as unmasked server frames
33+
preserving FIN + opcode. `Ping``Pong`, `Close` echoed.
34+
35+
## Endpoint
36+
37+
| Method | Path | Behavior |
38+
|--------|-------|------------------------------------------|
39+
| GET | `/ws` | WebSocket upgrade, then echo every frame |
40+
41+
A non-upgrade `GET /ws` is rejected with `400`; other paths return `404`.
42+
43+
## Build & run
44+
45+
```bash
46+
docker build -t httparena-ringzero-ws .
47+
docker run --rm -p 18080:8080 httparena-ringzero-ws /server 8
48+
python3 ../../scripts/validate-ws.py localhost 18080 /ws
49+
```
50+
51+
`/server <N>` selects the reactor (thread) count; the benchmark image defaults
52+
to `64`.

0 commit comments

Comments
 (0)