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

@MDA2AV MDA2AV 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-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown

Http11Probe — Compliance Comparison

Server Score
uWebSockets.js 119/159 ███████████████░░░░░ 75%

✅ Baseline Passed

Compliance

Test Expected uWebSockets.js
BASELINE 2xx 200
BARE-LF-REQUEST-LINE 400 or close (pass), 2xx (warn) 505
BARE-LF-HEADER 400 or close (pass), 2xx (warn) 400
OBS-FOLD 400 400
SP-BEFORE-COLON 400 400
MULTI-SP-REQUEST-LINE 400 or 2xx; close/timeout = warn 505
MISSING-HOST 400 400
INVALID-VERSION 400/505, close, or timeout = warn 505
EMPTY-HEADER-NAME 400 or close 200
CR-ONLY-LINE-ENDING 400, close, or timeout = warn 505
MISSING-TARGET 400, close, or timeout = warn 505
FRAGMENT-IN-TARGET 400 or 2xx; 404 = warn ⚠️200
HTTP09-REQUEST 400/close/timeout 505
INVALID-HEADER-NAME 400 or close 400
HEADER-NO-COLON 400 or close 400
DUPLICATE-HOST 400 400
CL-NON-NUMERIC 400 or close 400
CL-PLUS-SIGN 400 or close 400
WHITESPACE-BEFORE-HEADERS 400 or close 400
DUPLICATE-HOST-SAME 400 400
HOST-WITH-USERINFO 400 or close 200
HOST-WITH-PATH 400 or close 200
ASTERISK-WITH-GET 400, close, or timeout = warn 505
OPTIONS-STAR 2xx or 405; close/timeout = warn 505
UNKNOWN-TE-501 400/501 or close TimedOut
LEADING-CRLF 400 or 2xx; close/timeout = warn ⚠️TimedOut
ABSOLUTE-FORM 2xx preferred; 400/close/timeout = warn 505
METHOD-CASE 400/405/501 or 2xx; close/timeout = warn ⚠️200
POST-CL-BODY 2xx + echo 200
POST-CL-ZERO 2xx or close 200
POST-NO-CL-NO-TE 2xx or close 200
POST-CL-UNDERSEND 400/close/timeout TimedOut
CHUNKED-BODY 2xx + echo 200
CHUNKED-MULTI 2xx + echo 200
CHUNKED-EMPTY 2xx or close 200
CHUNKED-NO-FINAL 400/close/timeout TimedOut
METHOD-CONNECT 400/405/501 or close 505
EXPECT-UNKNOWN 417 or 2xx ⚠️200
GET-WITH-CL-BODY 400 or 2xx ⚠️200
CHUNKED-EXTENSION 2xx preferred; 400 warns ⚠️400
METHOD-TRACE 405/501 or 2xx ⚠️200
HOST-EMPTY-VALUE 400 or close 200
REQUEST-LINE-TAB 400 or 2xx; close/timeout = warn 505
VERSION-MISSING-MINOR 400, close, or timeout = warn 505
VERSION-LEADING-ZEROS 400, close, or timeout = warn 505
VERSION-WHITESPACE 400, close, or timeout = warn 505
CONNECTION-CLOSE 2xx + close 200
HTTP10-DEFAULT-CLOSE 2xx + close 505
HTTP10-NO-HOST 200 or 400 505
HTTP12-VERSION 200 or 505 ⚠️505
TRACE-WITH-BODY 400/405 or 200 ⚠️200
CHUNKED-TRAILER-VALID 2xx + echo 505
CHUNKED-HEX-UPPERCASE 2xx + echo 200
RANGE-POST 2xx (Range ignored) 200
HEAD-NO-BODY 2xx with no body 200
UNKNOWN-METHOD 501/405/400 or close ⚠️200
DATE-HEADER 2xx with Date header 200
DATE-FORMAT IMF-fixdate format 200
NO-1XX-HTTP10 non-1xx response 505
OPTIONS-ALLOW 2xx with Allow header, or 405 ⚠️200
CONTENT-TYPE 2xx with Content-Type 200
VERSION-CASE 400, close, or timeout = warn 505
LONG-URL-OK not 414; close/timeout = warn 431
SPACE-IN-TARGET 400, close, or timeout = warn 505
DUPLICATE-CT 400 or 2xx ⚠️200
TRACE-SENSITIVE 405/501, or 200 without Auth 200
RANGE-INVALID 200 or 416 200
ACCEPT-NONSENSE 406 or 2xx ⚠️200
POST-UNSUPPORTED-CT 415 or 2xx 200

Smuggling

Test Expected uWebSockets.js
CL-TE-BOTH 400 or 2xx 400
DUPLICATE-CL 400 or close 200
CL-LEADING-ZEROS 400 or 2xx ⚠️200
TE-XCHUNKED 400/501 or close 400
TE-TRAILING-SPACE 400/501 or 2xx+close 400
TE-SP-BEFORE-COLON 400 or close 400
CL-NEGATIVE 400 or close 400
CLTE-PIPELINE 400 or close preferred; 2xx acceptable 400
TECL-PIPELINE 400 or close preferred; 2xx acceptable 400
CL-TRAILING-SPACE 400 or 2xx ⚠️200
TE-DOUBLE-CHUNKED 400 or 2xx 400
CL-EXTRA-LEADING-SP 400 or 2xx ⚠️200
TE-CASE-MISMATCH 400 or 2xx 400
CL-COMMA-DIFFERENT 400 or close 400
TE-NOT-FINAL-CHUNKED 400 or close 200
TE-HTTP10 400 or close 505
CHUNK-BARE-SEMICOLON 400 or close TimedOut
CHUNK-EXT-INVALID-TOKEN 400 or close 400
BARE-CR-HEADER-VALUE 400 or close TimedOut
CL-OCTAL 400 or close 400
CHUNK-UNDERSCORE 400 or close 400
TE-EMPTY-VALUE 400 or close 200
TE-LEADING-COMMA 400 or 2xx 400
TE-DUPLICATE-HEADERS 400 or close 400
CHUNK-HEX-PREFIX 400 or close 400
CHUNK-SIZE-PLUS 400 or close 400
CHUNK-SIZE-TRAILING-OWS 400 or close 200
CL-HEX-PREFIX 400 or close 400
CL-INTERNAL-SPACE 400 or close 400
CHUNK-LEADING-SP 400 or close 200
CHUNK-MISSING-TRAILING-CRLF 400 or close 200
CHUNK-EXT-LF 400 or 2xx TimedOut
CHUNK-SPILL 400 or close 200
CHUNK-LF-TERM 400 or 2xx ⚠️200
CHUNK-EXT-CTRL 400 or close TimedOut
CHUNK-EXT-CR 400 or close TimedOut
TE-VTAB 400 or close 400
TE-FORMFEED 400 or close 400
TE-NULL 400 or close 400
CHUNK-LF-TRAILER 400 or 2xx ⚠️200
TE-IDENTITY 400/501 or close 400
CHUNK-NEGATIVE 400 or close 400
TRANSFER_ENCODING 400 or 2xx ⚠️200
CL-COMMA-SAME 400 or 2xx 400
CL-COMMA-TRIPLE 400 or 2xx 400
CHUNKED-WITH-PARAMS 400 or 2xx 400
EXPECT-100-CL 100, 400 or 2xx ⚠️100
TRAILER-CL 400 or 2xx 505
TRAILER-TE 400 or 2xx 505
TRAILER-HOST 400 or 2xx 505
TRAILER-AUTH 400 or 2xx 505
HEAD-CL-BODY 400 or 2xx ⚠️200
OPTIONS-CL-BODY 400/405 or 2xx ⚠️200
CL-UNDERSCORE 400 or close 400
CL-NEGATIVE-ZERO 400 or close 400
CL-DOUBLE-ZERO 400 or 2xx ⚠️200
CL-LEADING-ZEROS-OCTAL 400 or 2xx ⚠️200
TE-OBS-FOLD 400 or 2xx+close 400
TE-TRAILING-COMMA 400 or 2xx 400
TE-TAB-BEFORE-VALUE 400 or 2xx 400
ABSOLUTE-URI-HOST-MISMATCH 400 or 2xx 505
MULTIPLE-HOST-COMMA 400 or close 200
CHUNK-BARE-CR-TERM 400 or close TimedOut
TRAILER-CONTENT-TYPE 400 or 2xx 505
CLTE-CONN-CLOSE 400, or 2xx + close 400
TECL-CONN-CLOSE 400, or 2xx + close 400
CLTE-DESYNC 400, or close 400
CLTE-SMUGGLED-GET 400, or close (no extra response) 400
CLTE-SMUGGLED-GET-CL-PLUS 400, or close (no extra response) 400
CLTE-SMUGGLED-GET-CL-NON-NUMERIC 400, or close (no extra response) 400
CLTE-SMUGGLED-GET-TE-OBS-FOLD 400, or close (no extra response) 400
CLTE-SMUGGLED-HEAD 400, or close (no extra response) 400
CLTE-SMUGGLED-GET-TE-TRAILING-SPACE 400, or close (no extra response) 400
CLTE-SMUGGLED-GET-TE-LEADING-COMMA 400, or close (no extra response) 400
CLTE-SMUGGLED-GET-TE-CASE-MISMATCH 400, or close (no extra response) 400
TE-DUPLICATE-HEADERS-SMUGGLED-GET 400, or close (no extra response) 400
TECL-SMUGGLED-GET 400, or close (no extra response) 400
DUPLICATE-CL-SMUGGLED-GET 400, or close (no extra response) 200
GET-CL-PREFIX-DESYNC 400/close preferred; extra response on step 2 = warn ⚠️200
TECL-DESYNC 400, or close 400
CL0-BODY-POISON 400/close preferred; poisoned follow-up = warn ⚠️200
GET-CL-BODY-DESYNC 400/close/pass-through; poisoned follow-up = warn 200
OPTIONS-CL-BODY-DESYNC 400/close/pass-through; poisoned follow-up = warn 200
EXPECT-100-CL-DESYNC 417/400/close preferred; poisoned follow-up = warn ⚠️200
OPTIONS-TE-OBS-FOLD 400, or 2xx + close 400
CHUNK-INVALID-SIZE-DESYNC 400, or close 400
PIPELINE-SAFE 2xx + 2xx 200

Malformed Input

Test Expected uWebSockets.js
BINARY-GARBAGE 400/close/timeout 505
LONG-URL 400/414/431 or close 431
LONG-HEADER-VALUE 400/431 or close 431
MANY-HEADERS 400/431 or close 431
NUL-IN-URL 400 or close 505
CONTROL-CHARS-HEADER 400 or close 400
INCOMPLETE-REQUEST 400/close/timeout TimedOut
EMPTY-REQUEST 400/close/timeout TimedOut
LONG-HEADER-NAME 400/431 or close 431
LONG-METHOD 400 or close 431
NON-ASCII-HEADER-NAME 400 or close 400
NON-ASCII-URL 400 or close 200
CL-OVERFLOW 400 or close 400
WHITESPACE-ONLY-LINE 400/close/timeout 505
NUL-IN-HEADER-VALUE 400 or close 400
CHUNK-SIZE-OVERFLOW 400 or close 400
H2-PREFACE 400/505/close/timeout 505
CL-EMPTY 400 or close 200
CL-TAB-BEFORE-VALUE 400 or 2xx ⚠️200
URL-BACKSLASH 400 or 2xx/404 ⚠️200
URL-OVERLONG-UTF8 400 or close 200
URL-PERCENT-NULL 400 or 2xx/404 ⚠️200
URL-PERCENT-CRLF 400 or 2xx/404 ⚠️200
CHUNK-EXT-64K 400 or 2xx 400
RANGE-OVERLAPPING 200/206/400/416 ⚠️200
POST-CL-HUGE-NO-BODY 400/413/close/timeout TimedOut

Header Normalization

Test Expected uWebSockets.js
UNDERSCORE-CL Reject/drop (pass), normalize (fail), preserve (warn) ⚠️200
SP-BEFORE-COLON-CL Reject/drop (pass), normalize (fail), preserve (warn) 400
TAB-IN-NAME Reject/drop (pass), normalize (fail), preserve (warn) 400
CASE-TE Reject/drop (pass), normalize casing (fail), preserve (warn) 400
UNDERSCORE-TE Reject/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
MDA2AV force-pushed the add-uwebsockets-js branch from b8b0cad to 77706a9 Compare August 15, 2026 17:10
@sonarqubecloud

Copy link
Copy Markdown

@MDA2AV MDA2AV mentioned this pull request Aug 15, 2026
@MDA2AV MDA2AV closed this in #152 Aug 15, 2026
Sign up for free to 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