Skip to content

Add uWebSockets (C++) - #152

Merged
MDA2AV merged 1 commit into
mainfrom
add-uwebsockets-cpp
Aug 15, 2026
Merged

Add uWebSockets (C++)#152
MDA2AV merged 1 commit into
mainfrom
add-uwebsockets-cpp

Conversation

@MDA2AV

@MDA2AVMDA2AV commented Aug 15, 2026

Copy link
Copy Markdown
Owner

So this adds the C++ library, tracking master.

Tracking master, not a tag

The clone is deliberately unpinned — that is the whole point of the request. Each rebuild picks up whatever master is at that moment, so upstream fixes surface here without a PR.

Worth stating the tradeoff plainly: builds are no longer reproducible, and a broken upstream commit will show up as this server regressing rather than as a build failure. That is the behaviour asked for, but it is the opposite of the lock-file pinning #151 just added, so it should be a conscious choice rather than an inconsistency someone later "fixes".

Only the uSockets submodule is initialised; the others are fuzzing corpora and test suites.

Build

Two stages. make -C uSockets, then g++ with the flags upstream's build.c uses — minus -march=native, which would otherwise bake the builder's CPU into the binary. Runtime stage is debian:trixie-slim + zlib1g, running as nobody.

Endpoints follow upstream's own EchoBody.cpp (described there as "Can be used to test compliance of HTTP spec"): any() routes with the wildcard last, range-for over the request for /echo, onData/onAborted for the body echo. Responses inside onData are already corked by uWS, so there is no explicit cork().

The Node.js remark does not hold for compliance

Built from fe7da4cb0562, run against the same suite as #151:

Score: 119/159 (40 failed, 22 warnings) 54 unscored (213 tests)

That is identical to uWebSockets.js — not similar, identical. Across all 213 tests there are zero verdict differences, and exactly one status-code difference:

TestuWebSockets.jsuWebSockets (C++)Verdict
SMUG-CHUNK-SPILL200505Fail in both

Which makes sense — the Node addon embeds this same C++ parser. Node.js may well constrain throughput, but it is not what is constraining standards behaviour: every finding reported on #151 reproduces here, including the generic 505 "This server does not support HTTP/1.0." for valid HTTP/1.1 requests and the accepted duplicate Content-Length.

That is arguably the more useful result for upstream. Fixes belong in the C++ parser, and both entries will move together when they land.

Relationship to #151

Independently mergeable — separate directory (UWebSocketsCppServer) and separate page slug, no conflicts. Whether to keep both or close#151 is a judgement call: the maintainer said "not JS", but having both is what demonstrates the bindings do not diverge, and it keeps that claim measured rather than assumed. Happy to fold this into a single entry instead.

Verified locally

Image builds from a clean context; all six endpoint contracts from the Add a Framework guide pass; container runs as nobody; site build renders servers/uwebsockets-cpp.html bound to renderServerPage('uWebSockets').

@github-actions

github-actionsBot commented Aug 15, 2026

Copy link
Copy Markdown

Http11Probe — Compliance Comparison

ServerScore
uWebSockets119/159███████████████░░░░░ 75%

✅ Baseline Passed

Compliance

TestExpecteduWebSockets
BASELINE2xx200
BARE-LF-REQUEST-LINE400 or close (pass), 2xx (warn)505
BARE-LF-HEADER400 or close (pass), 2xx (warn)400
OBS-FOLD400400
SP-BEFORE-COLON400400
MULTI-SP-REQUEST-LINE400 or 2xx; close/timeout = warn505
MISSING-HOST400400
INVALID-VERSION400/505, close, or timeout = warn505
EMPTY-HEADER-NAME400 or close200
CR-ONLY-LINE-ENDING400, close, or timeout = warn505
MISSING-TARGET400, close, or timeout = warn505
FRAGMENT-IN-TARGET400 or 2xx; 404 = warn⚠️200
HTTP09-REQUEST400/close/timeout505
INVALID-HEADER-NAME400 or close400
HEADER-NO-COLON400 or close400
DUPLICATE-HOST400400
CL-NON-NUMERIC400 or close400
CL-PLUS-SIGN400 or close400
WHITESPACE-BEFORE-HEADERS400 or close400
DUPLICATE-HOST-SAME400400
HOST-WITH-USERINFO400 or close200
HOST-WITH-PATH400 or close200
ASTERISK-WITH-GET400, close, or timeout = warn505
OPTIONS-STAR2xx or 405; close/timeout = warn505
UNKNOWN-TE-501400/501 or closeTimedOut
LEADING-CRLF400 or 2xx; close/timeout = warn⚠️TimedOut
ABSOLUTE-FORM2xx preferred; 400/close/timeout = warn505
METHOD-CASE400/405/501 or 2xx; close/timeout = warn⚠️200
POST-CL-BODY2xx + echo200
POST-CL-ZERO2xx or close200
POST-NO-CL-NO-TE2xx or close200
POST-CL-UNDERSEND400/close/timeoutTimedOut
CHUNKED-BODY2xx + echo200
CHUNKED-MULTI2xx + echo200
CHUNKED-EMPTY2xx or close200
CHUNKED-NO-FINAL400/close/timeoutTimedOut
METHOD-CONNECT400/405/501 or close505
EXPECT-UNKNOWN417 or 2xx⚠️200
GET-WITH-CL-BODY400 or 2xx⚠️200
CHUNKED-EXTENSION2xx preferred; 400 warns⚠️400
METHOD-TRACE405/501 or 2xx⚠️200
HOST-EMPTY-VALUE400 or close200
REQUEST-LINE-TAB400 or 2xx; close/timeout = warn505
VERSION-MISSING-MINOR400, close, or timeout = warn505
VERSION-LEADING-ZEROS400, close, or timeout = warn505
VERSION-WHITESPACE400, close, or timeout = warn505
CONNECTION-CLOSE2xx + close200
HTTP10-DEFAULT-CLOSE2xx + close505
HTTP10-NO-HOST200 or 400505
HTTP12-VERSION200 or 505⚠️505
TRACE-WITH-BODY400/405 or 200⚠️200
CHUNKED-TRAILER-VALID2xx + echo505
CHUNKED-HEX-UPPERCASE2xx + echo200
RANGE-POST2xx (Range ignored)200
HEAD-NO-BODY2xx with no body200
UNKNOWN-METHOD501/405/400 or close⚠️200
DATE-HEADER2xx with Date header200
DATE-FORMATIMF-fixdate format200
NO-1XX-HTTP10non-1xx response505
OPTIONS-ALLOW2xx with Allow header, or 405⚠️200
CONTENT-TYPE2xx with Content-Type200
VERSION-CASE400, close, or timeout = warn505
LONG-URL-OKnot 414; close/timeout = warn431
SPACE-IN-TARGET400, close, or timeout = warn505
DUPLICATE-CT400 or 2xx⚠️200
TRACE-SENSITIVE405/501, or 200 without Auth200
RANGE-INVALID200 or 416200
ACCEPT-NONSENSE406 or 2xx⚠️200
POST-UNSUPPORTED-CT415 or 2xx200

Smuggling

TestExpecteduWebSockets
CL-TE-BOTH400 or 2xx400
DUPLICATE-CL400 or close200
CL-LEADING-ZEROS400 or 2xx⚠️200
TE-XCHUNKED400/501 or close400
TE-TRAILING-SPACE400/501 or 2xx+close400
TE-SP-BEFORE-COLON400 or close400
CL-NEGATIVE400 or close400
CLTE-PIPELINE400 or close preferred; 2xx acceptable400
TECL-PIPELINE400 or close preferred; 2xx acceptable400
CL-TRAILING-SPACE400 or 2xx⚠️200
TE-DOUBLE-CHUNKED400 or 2xx400
CL-EXTRA-LEADING-SP400 or 2xx⚠️200
TE-CASE-MISMATCH400 or 2xx400
CL-COMMA-DIFFERENT400 or close400
TE-NOT-FINAL-CHUNKED400 or close200
TE-HTTP10400 or close505
CHUNK-BARE-SEMICOLON400 or closeTimedOut
CHUNK-EXT-INVALID-TOKEN400 or close400
BARE-CR-HEADER-VALUE400 or closeTimedOut
CL-OCTAL400 or close400
CHUNK-UNDERSCORE400 or close400
TE-EMPTY-VALUE400 or close200
TE-LEADING-COMMA400 or 2xx400
TE-DUPLICATE-HEADERS400 or close400
CHUNK-HEX-PREFIX400 or close400
CHUNK-SIZE-PLUS400 or close400
CHUNK-SIZE-TRAILING-OWS400 or close200
CL-HEX-PREFIX400 or close400
CL-INTERNAL-SPACE400 or close400
CHUNK-LEADING-SP400 or close200
CHUNK-MISSING-TRAILING-CRLF400 or close200
CHUNK-EXT-LF400 or 2xxTimedOut
CHUNK-SPILL400 or close505
CHUNK-LF-TERM400 or 2xx⚠️200
CHUNK-EXT-CTRL400 or closeTimedOut
CHUNK-EXT-CR400 or closeTimedOut
TE-VTAB400 or close400
TE-FORMFEED400 or close400
TE-NULL400 or close400
CHUNK-LF-TRAILER400 or 2xx⚠️200
TE-IDENTITY400/501 or close400
CHUNK-NEGATIVE400 or close400
TRANSFER_ENCODING400 or 2xx⚠️200
CL-COMMA-SAME400 or 2xx400
CL-COMMA-TRIPLE400 or 2xx400
CHUNKED-WITH-PARAMS400 or 2xx400
EXPECT-100-CL100, 400 or 2xx⚠️100
TRAILER-CL400 or 2xx505
TRAILER-TE400 or 2xx505
TRAILER-HOST400 or 2xx505
TRAILER-AUTH400 or 2xx505
HEAD-CL-BODY400 or 2xx⚠️200
OPTIONS-CL-BODY400/405 or 2xx⚠️200
CL-UNDERSCORE400 or close400
CL-NEGATIVE-ZERO400 or close400
CL-DOUBLE-ZERO400 or 2xx⚠️200
CL-LEADING-ZEROS-OCTAL400 or 2xx⚠️200
TE-OBS-FOLD400 or 2xx+close400
TE-TRAILING-COMMA400 or 2xx400
TE-TAB-BEFORE-VALUE400 or 2xx400
ABSOLUTE-URI-HOST-MISMATCH400 or 2xx505
MULTIPLE-HOST-COMMA400 or close200
CHUNK-BARE-CR-TERM400 or closeTimedOut
TRAILER-CONTENT-TYPE400 or 2xx505
CLTE-CONN-CLOSE400, or 2xx + close400
TECL-CONN-CLOSE400, or 2xx + close400
CLTE-DESYNC400, or close400
CLTE-SMUGGLED-GET400, or close (no extra response)400
CLTE-SMUGGLED-GET-CL-PLUS400, or close (no extra response)400
CLTE-SMUGGLED-GET-CL-NON-NUMERIC400, or close (no extra response)400
CLTE-SMUGGLED-GET-TE-OBS-FOLD400, or close (no extra response)400
CLTE-SMUGGLED-HEAD400, or close (no extra response)400
CLTE-SMUGGLED-GET-TE-TRAILING-SPACE400, or close (no extra response)400
CLTE-SMUGGLED-GET-TE-LEADING-COMMA400, or close (no extra response)400
CLTE-SMUGGLED-GET-TE-CASE-MISMATCH400, or close (no extra response)400
TE-DUPLICATE-HEADERS-SMUGGLED-GET400, or close (no extra response)400
TECL-SMUGGLED-GET400, or close (no extra response)400
DUPLICATE-CL-SMUGGLED-GET400, or close (no extra response)200
GET-CL-PREFIX-DESYNC400/close preferred; extra response on step 2 = warn⚠️200
TECL-DESYNC400, or close400
CL0-BODY-POISON400/close preferred; poisoned follow-up = warn⚠️200
GET-CL-BODY-DESYNC400/close/pass-through; poisoned follow-up = warn200
OPTIONS-CL-BODY-DESYNC400/close/pass-through; poisoned follow-up = warn200
EXPECT-100-CL-DESYNC417/400/close preferred; poisoned follow-up = warn⚠️200
OPTIONS-TE-OBS-FOLD400, or 2xx + close400
CHUNK-INVALID-SIZE-DESYNC400, or close400
PIPELINE-SAFE2xx + 2xx200

Malformed Input

TestExpecteduWebSockets
BINARY-GARBAGE400/close/timeout505
LONG-URL400/414/431 or close431
LONG-HEADER-VALUE400/431 or close431
MANY-HEADERS400/431 or close431
NUL-IN-URL400 or close505
CONTROL-CHARS-HEADER400 or close400
INCOMPLETE-REQUEST400/close/timeoutTimedOut
EMPTY-REQUEST400/close/timeoutTimedOut
LONG-HEADER-NAME400/431 or close431
LONG-METHOD400 or close431
NON-ASCII-HEADER-NAME400 or close400
NON-ASCII-URL400 or close200
CL-OVERFLOW400 or close400
WHITESPACE-ONLY-LINE400/close/timeout505
NUL-IN-HEADER-VALUE400 or close400
CHUNK-SIZE-OVERFLOW400 or close400
H2-PREFACE400/505/close/timeout505
CL-EMPTY400 or close200
CL-TAB-BEFORE-VALUE400 or 2xx⚠️200
URL-BACKSLASH400 or 2xx/404⚠️200
URL-OVERLONG-UTF8400 or close200
URL-PERCENT-NULL400 or 2xx/404⚠️200
URL-PERCENT-CRLF400 or 2xx/404⚠️200
CHUNK-EXT-64K400 or 2xx400
RANGE-OVERLAPPING200/206/400/416⚠️200
POST-CL-HUGE-NO-BODY400/413/close/timeoutTimedOut

Header Normalization

TestExpecteduWebSockets
UNDERSCORE-CLReject/drop (pass), normalize (fail), preserve (warn)⚠️200
SP-BEFORE-COLON-CLReject/drop (pass), normalize (fail), preserve (warn)400
TAB-IN-NAMEReject/drop (pass), normalize (fail), preserve (warn)400
CASE-TEReject/drop (pass), normalize casing (fail), preserve (warn)400
UNDERSCORE-TEReject/drop (pass), normalize (fail), preserve (warn)⚠️200

Commit: 042301b

Requested by the maintainer in uNetworking/uWebSockets.js#1298: the C++
library rather than the Node binding, built from master so their fixes show
up in these results as they land.
Implements the four probe endpoints on top of upstream's own EchoBody.cpp
pattern — any() routes with the wildcard last, range-for over the request for
/echo, and onData/onAborted for the body echo. Responses inside onData are
already corked by uWS, so no explicit cork is needed.
Built as a two-stage image: uSockets via its own make, then g++ with the
flags build.c uses, minus -march=native so the binary does not depend on the
builder's CPU. The clone is deliberately unpinned, which is the point of the
request, and only the uSockets submodule is initialised — the rest are
fuzzing corpora and test suites.
Scores 119/159 locally, with a verdict identical to uWebSockets.js on all
213 tests.
@MDA2AV
MDA2AVforce-pushed the add-uwebsockets-cpp branch from d665698 to bd9ff7cCompareAugust 15, 2026 18:23
@sonarqubecloud

Copy link
Copy Markdown

@MDA2AV
MDA2AV merged commit f7ce3a9 into mainAug 15, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MDA2AV