Skip to content

feat(libuvcpp): add the libuvcpp HTTP framework to the probe - #158

Open
Antruly wants to merge 3 commits into
MDA2AV:mainfrom
Antruly:add-libuvcpp
Open

Antruly wants to merge 3 commits into
MDA2AV:mainfrom
Antruly:add-libuvcpp

Conversation

@Antruly

@Antruly Antruly commented Oct 10, 2026

Copy link
Copy Markdown

Adds src/Servers/LibuvcppServer/ (Dockerfile, probe.json, server.cpp) and docs/content/servers/libuvcpp.md. libuvcpp is a C++11 HTTP framework built on libuv's event loop; its three endpoints are served through uvcpp_web_app, the library's documented high-level server, so the catch-all, the HEAD-to-GET fallback and the automatic OPTIONS answer are all the framework's own behaviour rather than a router written for this probe.

Criterion: the probe's own suite, run against the entry. Measured locally with this repo's own code, built from this tree (dotnet run --no-build -c Release --project src/Http11Probe.Cli -- --host 127.0.0.1 --port 8092): 149/159 -- 132 pass, 17 warn, 10 scored fail, 0 error, 213 tests. Both baseline gates pass, and no response in the run was a 5xx. The ten scored failures are all message-level properties of the library's parser, not of this entry: Host is not validated (missing, duplicate, empty, userinfo, path, comma-separated), obs-fold is accepted, Transfer-Encoding: with an empty value is accepted, and HTTP/1.2 is rejected with 400 where the RFC asks for 1.x tolerance.

The Dockerfile downloads the pinned v1.6.0 linux-x64 release package and compiles server.cpp against it. The sha256 in the Dockerfile, b64ae68d121b49543e7a37126bbf38adf95a00ac51bff463b11be2dc499a138b, was checked against the published asset before this commit, and the g++ line is the same one used for the local run above.

README.md: 41 -> 42 reference servers, since this adds the 42nd server page.

Adds src/Servers/LibuvcppServer/ (Dockerfile, probe.json, server.cpp) and
docs/content/servers/libuvcpp.md. libuvcpp is a C++11 HTTP framework built on
libuv's event loop; its three endpoints are served through `uvcpp_web_app`, the
library's documented high-level server, so the catch-all, the HEAD-to-GET
fallback and the automatic OPTIONS answer are all the framework's own behaviour
rather than a router written for this probe.

Criterion: the probe's own suite, run against the entry. Measured locally with
this repo's own code, built from this tree (`dotnet run --no-build -c Release
--project src/Http11Probe.Cli -- --host 127.0.0.1 --port 8092`):
149/159 -- 132 pass, 17 warn, 10 scored fail, 0 error, 213 tests. Both baseline
gates pass, and no response in the run was a 5xx. The ten scored failures are
all message-level properties of the library's parser, not of this entry: Host
is not validated (missing, duplicate, empty, userinfo, path, comma-separated),
obs-fold is accepted, `Transfer-Encoding:` with an empty value is accepted, and
HTTP/1.2 is rejected with 400 where the RFC asks for 1.x tolerance.

The Dockerfile downloads the pinned v1.6.0 linux-x64 release package and
compiles server.cpp against it. The sha256 in the Dockerfile,
b64ae68d121b49543e7a37126bbf38adf95a00ac51bff463b11be2dc499a138b, was checked
against the published asset before this commit, and the g++ line is the same
one used for the local run above.

README.md: 41 -> 42 reference servers, since this adds the 42nd server page.
Criterion: SonarCloud's automatic analysis must pass the Quality Gate on this
PR. Read from the API rather than guessed: the `SonarCloud Code Analysis`
check-run on the head commit was `failure`, `Quality Gate failed`, and it named
exactly one failed condition -- "C Security Rating on New Code". The three
annotations resolve to `docker:S6506` (VULNERABILITY, MAJOR) on the Dockerfile's
`curl`: "Not enforcing HTTPS here might allow for redirections to insecure
websites", plus `docker:S7018` and `docker:S7026`, both CODE_SMELL MINOR and
neither of which gates.

`--proto '=https' --proto-redir '=https' --tlsv1.2` is the fix. The repo's only
other Dockerfile that downloads an artifact -- SwerverServer, added by MDA2AV#131 --
already carries the same flags, and that PR's analysis is clean: zero
unresolved issues on its new code. The package list is sorted as well, clearing
`docker:S7018`. `docker:S7026` ("Replace this invocation of curl with the ADD
instruction") is deliberately left standing: ADD takes no checksum, so obeying
it would drop the sha256 check that is what pins which bytes get built.

No server behaviour changes -- server.cpp, the endpoints and the routing are
untouched, and the compiled binary is identical. Only the transport of the
pinned archive and one comment line are.
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Http11Probe — Compliance Comparison

Server Score
libuvcpp 157/159 ████████████████████ 99%

✅ Baseline Passed

Compliance

Test Expected libuvcpp
BASELINE 2xx ✅200
BARE-LF-REQUEST-LINE 400 or close (pass), 2xx (warn) ✅400
BARE-LF-HEADER 400 or close (pass), 2xx (warn) ✅400
OBS-FOLD 400 ❌200
SP-BEFORE-COLON 400 ✅400
MULTI-SP-REQUEST-LINE 400 or 2xx; close/timeout = warn ⚠️200
MISSING-HOST 400 ✅400
INVALID-VERSION 400/505, close, or timeout = warn ✅400
EMPTY-HEADER-NAME 400 or close ✅400
CR-ONLY-LINE-ENDING 400, close, or timeout = warn ✅400
MISSING-TARGET 400, close, or timeout = warn ✅400
FRAGMENT-IN-TARGET 400 or 2xx; 404 = warn ⚠️200
HTTP09-REQUEST 400/close/timeout ✅TimedOut
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 ✅400
HOST-WITH-PATH 400 or close ✅400
ASTERISK-WITH-GET 400, close, or timeout = warn ❌200
OPTIONS-STAR 2xx or 405; close/timeout = warn ✅204
UNKNOWN-TE-501 400/501 or close ✅400
LEADING-CRLF 400 or 2xx; close/timeout = warn ⚠️200
ABSOLUTE-FORM 2xx preferred; 400/close/timeout = warn ✅200
METHOD-CASE 400/405/501 or 2xx; close/timeout = warn ✅400
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 ✅405
EXPECT-UNKNOWN 417 or 2xx ✅417
GET-WITH-CL-BODY 400 or 2xx ⚠️200
CHUNKED-EXTENSION 2xx preferred; 400 warns ✅200
METHOD-TRACE 405/501 or 2xx ✅405
HOST-EMPTY-VALUE 400 or close ✅400
REQUEST-LINE-TAB 400 or 2xx; close/timeout = warn ✅400
VERSION-MISSING-MINOR 400, close, or timeout = warn ✅400
VERSION-LEADING-ZEROS 400, close, or timeout = warn ✅400
VERSION-WHITESPACE 400, close, or timeout = warn ✅400
CONNECTION-CLOSE 2xx + close ✅200
HTTP10-DEFAULT-CLOSE 2xx + close ✅200
HTTP10-NO-HOST 200 or 400 ⚠️200
HTTP12-VERSION 200 or 505 ❌400
TRACE-WITH-BODY 400/405 or 200 ✅405
CHUNKED-TRAILER-VALID 2xx + echo ✅200
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 ✅400
DATE-HEADER 2xx with Date header ✅200
DATE-FORMAT IMF-fixdate format ✅200
NO-1XX-HTTP10 non-1xx response ✅200
OPTIONS-ALLOW 2xx with Allow header, or 405 ✅204
CONTENT-TYPE 2xx with Content-Type ✅200
VERSION-CASE 400, close, or timeout = warn ✅400
LONG-URL-OK not 414; close/timeout = warn ✅200
SPACE-IN-TARGET 400, close, or timeout = warn ✅400
DUPLICATE-CT 400 or 2xx ⚠️200
TRACE-SENSITIVE 405/501, or 200 without Auth ✅405
RANGE-INVALID 200 or 416 ✅200
ACCEPT-NONSENSE 406 or 2xx ⚠️200
POST-UNSUPPORTED-CT 415 or 2xx ✅200

Smuggling

Test Expected libuvcpp
CL-TE-BOTH 400 or 2xx ✅400
DUPLICATE-CL 400 or close ✅400
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 ✅400
TE-HTTP10 400 or close ✅400
CHUNK-BARE-SEMICOLON 400 or close ✅400
CHUNK-EXT-INVALID-TOKEN 400 or close ✅400
BARE-CR-HEADER-VALUE 400 or close ✅400
CL-OCTAL 400 or close ✅400
CHUNK-UNDERSCORE 400 or close ✅400
TE-EMPTY-VALUE 400 or close ✅400
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 ✅400
CL-HEX-PREFIX 400 or close ✅400
CL-INTERNAL-SPACE 400 or close ✅400
CHUNK-LEADING-SP 400 or close ✅400
CHUNK-MISSING-TRAILING-CRLF 400 or close ✅400
CHUNK-EXT-LF 400 or 2xx ✅400
CHUNK-SPILL 400 or close ✅400
CHUNK-LF-TERM 400 or 2xx ✅400
CHUNK-EXT-CTRL 400 or close ✅400
CHUNK-EXT-CR 400 or close ✅400
TE-VTAB 400 or close ✅400
TE-FORMFEED 400 or close ✅400
TE-NULL 400 or close ✅400
CHUNK-LF-TRAILER 400 or 2xx ✅400
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 ✅400
TRAILER-TE 400 or 2xx ✅400
TRAILER-HOST 400 or 2xx ⚠️200
TRAILER-AUTH 400 or 2xx ⚠️200
HEAD-CL-BODY 400 or 2xx ⚠️200
OPTIONS-CL-BODY 400/405 or 2xx ⚠️204
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 ⚠️200
MULTIPLE-HOST-COMMA 400 or close ✅400
CHUNK-BARE-CR-TERM 400 or close ✅400
TRAILER-CONTENT-TYPE 400 or 2xx ⚠️200
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) ✅400
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 libuvcpp
BINARY-GARBAGE 400/close/timeout ✅400
LONG-URL 400/414/431 or close ✅414
LONG-HEADER-VALUE 400/431 or close ✅431
MANY-HEADERS 400/431 or close ✅431
NUL-IN-URL 400 or close ✅400
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 ✅400
NON-ASCII-HEADER-NAME 400 or close ✅400
NON-ASCII-URL 400 or close ✅400
CL-OVERFLOW 400 or close ✅400
WHITESPACE-ONLY-LINE 400/close/timeout ✅400
NUL-IN-HEADER-VALUE 400 or close ✅400
CHUNK-SIZE-OVERFLOW 400 or close ✅400
H2-PREFACE 400/505/close/timeout ✅400
CL-EMPTY 400 or close ✅400
CL-TAB-BEFORE-VALUE 400 or 2xx ⚠️200
URL-BACKSLASH 400 or 2xx/404 ⚠️200
URL-OVERLONG-UTF8 400 or close ✅400
URL-PERCENT-NULL 400 or 2xx/404 ⚠️200
URL-PERCENT-CRLF 400 or 2xx/404 ⚠️200
CHUNK-EXT-64K 400 or 2xx ⚠️200
RANGE-OVERLAPPING 200/206/400/416 ⚠️200
POST-CL-HUGE-NO-BODY 400/413/close/timeout ✅413

Header Normalization

Test Expected libuvcpp
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: 3fd5355

… Host/TE holes

The entry was pinned to v1.6.0. Upstream cut v1.6.2 (2026-10-11), whose parser is
the first to answer 400 to an HTTP/1.1 request with no Host header, with more
than one, or with an invalid one (RFC 9112 section 3.2), and to an all-blank
Transfer-Encoding (RFC 9112 section 6.1) -- the latter previously fell through to
Content-Length framing while keeping the connection alive, which is the smuggling
shape. Moving the pin is the whole change: server.cpp is untouched.

Measured, not assumed. Downloaded the release asset and hashed it here:

  GET /repos/Antruly/libuvcpp/releases/assets/629841961   -> 200, 22483830 B
  sha256sum libuvcpp-1.6.2-linux-x64.zip
    = 636f07346314d5f5b8fc3dbae34861a6b4441c337fc24a632011c0d7ca72d4fe

which is the digest the release API reports for that asset, so the hash in the
Dockerfile is a hash of bytes seen, not of bytes promised. The extracted tree is
shaped exactly like 1.6.0's (libuvcpp-1.6.2-linux-x64/{include,lib,bindings},
lib/libuvcpp.so and libuvcppd.so), and uvcpp_version.h / uvcpp.pc both read 1.6.2.

Then compiled this entry's server.cpp against that package with the Dockerfile's
own flags (`g++ -std=c++11 -O2 -DNDEBUG`) and ran the full 213-test probe against
the resulting binary on 8080:

  Score: 157/159 (2 failed, 17 warnings)  54 unscored  (213 tests, 54.4s)

against 149/159 for the v1.6.0/v1.6.1 packages -- +8, and no previously passing
test regressed. Spot-checked directly first: missing Host -> 400, repeated Host
-> 400, `Transfer-Encoding:` blank + Content-Length -> 400, a normal GET -> 200.

The two remaining scored failures are deliberate leniency, not oversights:

  RFC9112-5.1-OBS-FOLD       section 5.2 lets a server either reject an obs-fold
                             with 400 or fold it to SP; this server folds. Both
                             branches are conformant.
  COMP-ASTERISK-WITH-GET     `GET *` -- section 3.2.4's MUST constrains what a
                             client sends, not what a server must reject.

(The third Fail in the report, COMP-HTTP12-VERSION, is unscored: a higher minor
version "should" be treated as 1.1, which is a SHOULD in RFC 9110 section 2.5.)

Also synced docs/content/servers/libuvcpp.md, which embeds this Dockerfile
verbatim -- regenerated the fenced block straight from the file this time, and
asserted byte equality, rather than editing both copies by hand.
@sonarqubecloud

Copy link
Copy Markdown

@Antruly

Antruly commented Oct 11, 2026

Copy link
Copy Markdown
Author

Re-pinned to the library's v1.6.2 release (320b2d2): the first release whose
parser rejects an HTTP/1.1 request with a missing, duplicated or invalid Host
(RFC 9112 §3.2), and an all-blank Transfer-Encoding (§6.1 — it used to fall
through to Content-Length framing with the connection kept alive). Only the
Dockerfile pin moved; server.cpp is untouched.

Measured locally with this entry's own server.cpp and the Dockerfile's flags,
against the downloaded release package: 157/159, up from 149/159, with no
previously passing test regressing.

The two remaining scored ❌ are deliberate leniency, not oversights —
RFC9112-5.1-OBS-FOLD (§5.2 permits folding the obs-fold to SP instead of
rejecting) and COMP-ASTERISK-WITH-GET (§3.2.4's MUST constrains what a client
sends, not what a server must reject). The third, COMP-HTTP12-VERSION, is
unscored.

The Probe run for 320b2d2 is on awaiting approval from a maintainer —
whenever someone has a moment, I'll watch it and confirm the platform's score
matches the local one.

This branch has not been deployed

No deployments
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