Skip to content

Add uWebSockets.js - #151

Closed
MDA2AV wants to merge 2 commits into
mainfrom
add-uwebsockets-js
Closed

Add uWebSockets.js#151
MDA2AV wants to merge 2 commits into
mainfrom
add-uwebsockets-js

Conversation

@MDA2AV

@MDA2AVMDA2AV commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Two patterns account for most of it:

1. A generic 505 for anything the parser rejects. Valid HTTP/1.1 requests come back as 505 HTTP Version Not Supported with the body This server does not support HTTP/1.0. — including OPTIONS * HTTP/1.1 and a well-formed chunked request with a trailer:

POST / HTTP/1.1 · Transfer-Encoding: chunked · 5/hello/0/X-Checksum: abc
→ HTTP/1.1 505 HTTP Version Not Supported

This drives most of the Compliance and trailer-related Smuggling failures. The rejection itself is safe; the status code and message are misleading.

2. Duplicate Content-Length is accepted.SMUG-DUPLICATE-CL sends two conflicting lengths and gets a 200 using the first, leaving the remainder in the buffer — a smuggling vector, MUST-level:

Content-Length: 5 · Content-Length: 10 · body "helloworld"
→ 200, Content-Length: 5, body "hello"

Also worth flagging: COMP-HEAD-NO-BODY fails because uWS sends the body on a HEAD request (Content-Length: 2 and OK on the wire). Node's http suppresses it automatically; uWS does not, so every uWS app has to handle that itself.

These are uWS's own parser behaviours, not artifacts of the harness code in this PR — the 505 bodies are generated by uWS before any handler runs (<i>uWebSockets/20 Server</i>).

Verified locally

Image builds; all six endpoint contracts from the Add a Framework guide check out (GET /, POST / echo, HEAD /, OPTIONS /, POST /echo, /cookie); site build renders servers/uwebsockets.html bound to renderServerPage('uWebSockets.js').

Requested upstream in uNetworking/uWebSockets.js#1298, where the maintainer
asked to add uWS here and fix standards behaviour rather than chase
benchmark lists.
Implements the four probe endpoints with the idiomatic uWS API: any() routes
with the wildcard registered last, req.forEach for /echo, and onData/onAborted
with a cork()ed reply for the POST body echo. Header and cookie handling
mirror the Node and Express servers so the three stay comparable.
uWS is not on the npm registry, so the dependency is pinned to the v20.69.0
release tarball — release tags carry prebuilt .node binaries, so no build
step and no git in the image. The base image is node:22-trixie-slim rather
than the usual node:22-slim because those binaries need glibc 2.38 or newer
and bookworm ships 2.36; on bookworm the module fails to load at startup.
@github-actions

github-actionsBot commented Aug 15, 2026

Copy link
Copy Markdown

Http11Probe — Compliance Comparison

ServerScore
uWebSockets.js119/159███████████████░░░░░ 75%

✅ Baseline Passed

Compliance

TestExpecteduWebSockets.js
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.js
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 close200
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.js
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.js
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: b3b4c89

Quality gate failed on Security Rating C for new code, from three major
findings on the Dockerfile and one minor one.
Add a generated package-lock.json and switch to npm ci, which pins the
release tarball by sha512 integrity hash rather than trusting whatever the
tag currently resolves to (docker:S8543, text:S8564). Pass --ignore-scripts;
uWS declares no lifecycle scripts, so this costs nothing and closes the
install-time execution path (docker:S6505). Run as the image's node user
instead of root, matching Trillium, Effinitive, Horse and Swerver
(docker:S6471) — port 8080 needs no privilege.
.gitignore excluded every package-lock.json, which would have left the
image building from an unpinned tarball in CI while working locally. Negate
it for src/Servers so server locks are tracked; web/ is unaffected.
Also Number.parseInt over the global (javascript:S7773).
None of this touches HTTP behaviour: the suite scores 119/159 before and
after, identical failure set.
@MDA2AV
MDA2AVforce-pushed the add-uwebsockets-js branch from b8b0cad to 77706a9CompareAugust 15, 2026 17:10
@sonarqubecloud

Copy link
Copy Markdown

@MDA2AVMDA2AV mentioned this pull request Aug 15, 2026
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