From 6eff5c96a43eb3c3d6d51b51a1f5c41a0242c123 Mon Sep 17 00:00:00 2001 From: Antruly Date: Sun, 11 Oct 2026 04:20:50 +0800 Subject: [PATCH 1/3] feat(libuvcpp): add the libuvcpp HTTP framework to the probe 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. --- README.md | 2 +- docs/content/servers/libuvcpp.md | 206 ++++++++++++++++++++++++++ src/Servers/LibuvcppServer/Dockerfile | 50 +++++++ src/Servers/LibuvcppServer/probe.json | 1 + src/Servers/LibuvcppServer/server.cpp | 100 +++++++++++++ 5 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 docs/content/servers/libuvcpp.md create mode 100644 src/Servers/LibuvcppServer/Dockerfile create mode 100644 src/Servers/LibuvcppServer/probe.json create mode 100644 src/Servers/LibuvcppServer/server.cpp diff --git a/README.md b/README.md index 6eb5ae2..5661bf3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Http11Probe is a compliance and security tester for HTTP/1.1 servers. It throws malformed, ambiguous, and oversized requests at a server over raw TCP sockets and checks how it responds against what RFC 9110 and RFC 9112 actually require. These are the awkward cases like bare LF line endings, obsolete line folding, CL/TE request smuggling, chunk-framing tricks, oversized headers, and NUL bytes, where a strict parser and a lenient one start to disagree. -The same 215 tests run against 41 reference servers written in 12 languages, from Nginx, Apache, and Envoy to Kestrel, Gin, Actix, and the built-in servers in Node, Bun, and Deno. Every result is scored against the MUST/SHOULD/MAY wording in the spec and marked **Pass**, **Fail**, or **Warn**. A Warn just means the RFC allows both the strict and the lenient behavior, so neither one is wrong. +The same 215 tests run against 42 reference servers written in 12 languages, from Nginx, Apache, and Envoy to Kestrel, Gin, Actix, and the built-in servers in Node, Bun, and Deno. Every result is scored against the MUST/SHOULD/MAY wording in the spec and marked **Pass**, **Fail**, or **Warn**. A Warn just means the RFC allows both the strict and the lenient behavior, so neither one is wrong. You'll find the full documentation, a per-test glossary with RFC citations, and the live results matrix for every server at [http-probe.com](https://www.http-probe.com/). diff --git a/docs/content/servers/libuvcpp.md b/docs/content/servers/libuvcpp.md new file mode 100644 index 0000000..4c07d86 --- /dev/null +++ b/docs/content/servers/libuvcpp.md @@ -0,0 +1,206 @@ +--- +title: "libuvcpp" +description: "libuvcpp (C++) tested against RFC 9110/9112 for HTTP/1.1 compliance, request smuggling resistance, and malformed input handling." +toc: true +breadcrumbs: false +--- + +**Language:** C++ · [View source on GitHub](https://github.com/MDA2AV/Http11Probe/tree/main/src/Servers/LibuvcppServer) + +A C++11 HTTP framework built on libuv's event loop, serving through `uvcpp_web_app` -- the library's high-level server, which owns routing, the HEAD-to-GET fallback and the automatic OPTIONS answer. Built against the pinned v1.6.0 linux-x64 release package, whose shared object already contains libuv, OpenSSL, nghttp2, ngtcp2 and zlib; the sha256 in the Dockerfile is what fixes which bytes get compiled. + +## Dockerfile + +```dockerfile +# libuvcpp entry for Http11Probe. +# +# The build downloads the official v1.6.0 linux-x64 release package and compiles +# server.cpp against it -- the same artifact a user gets from the GitHub release. +# That package's shared object already carries libuv, OpenSSL, nghttp2, ngtcp2 +# and zlib, so the runtime image needs no HTTP library and no toolchain. +# +# The download is pinned by sha256 rather than by tag: a release tag can be moved +# and its assets re-uploaded, so the hash is what fixes which bytes get built. +# +# Both stages are ubuntu:24.04 (glibc 2.39; the release package's floor is +# 2.34), so the compiler that links the binary and the libc that runs it are the +# same ones. Alpine is not an option: the package is glibc-linked. + +FROM ubuntu:24.04 AS build + +ARG LIBUVCPP_VERSION=1.6.0 +ARG LIBUVCPP_SHA256=b64ae68d121b49543e7a37126bbf38adf95a00ac51bff463b11be2dc499a138b + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates curl unzip g++ \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build +RUN curl -fsSL --retry 3 --retry-connrefused -o libuvcpp.zip \ + "https://github.com/Antruly/libuvcpp/releases/download/v${LIBUVCPP_VERSION}/libuvcpp-${LIBUVCPP_VERSION}-linux-x64.zip" \ + && echo "${LIBUVCPP_SHA256} libuvcpp.zip" | sha256sum -c - \ + && unzip -q libuvcpp.zip -d /opt \ + && rm -f libuvcpp.zip + +COPY src/Servers/LibuvcppServer/server.cpp . +RUN g++ -std=c++11 -O2 -DNDEBUG -o /probe-server server.cpp \ + -I/opt/libuvcpp-1.6.0-linux-x64/include \ + -L/opt/libuvcpp-1.6.0-linux-x64/lib \ + -luvcpp -Wl,-rpath,/opt/libuvcpp-1.6.0-linux-x64/lib -pthread + +FROM ubuntu:24.04 + +# g++ links the C++ runtime dynamically, so the runtime layer needs it. +RUN apt-get update \ + && apt-get install -y --no-install-recommends libstdc++6 \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=build /opt/libuvcpp-1.6.0-linux-x64/lib/libuvcpp.so \ + /opt/libuvcpp-1.6.0-linux-x64/lib/libuvcpp.so +COPY --from=build /probe-server /usr/local/bin/probe-server + +USER nobody +ENTRYPOINT ["probe-server", "8080"] +``` + +## Source — `server.cpp` + +```cpp +// libuvcpp entry for Http11Probe. +// +// Three endpoints, per docs/content/add-a-framework: `/` (baseline, and body +// echo on POST), `/echo` (the headers the server received) and `/cookie` (the +// cookies the server parsed). Everything goes through `uvcpp_web_app`, the +// library's documented high-level server. + +#include +#include + +#include +#include +#include +#include +#include + +using namespace uvcpp; + +namespace { + +typedef std::vector > cookie_list; + +std::string cookies_to_lines(const cookie_list& cs) { + std::string out; + for (size_t i = 0; i < cs.size(); ++i) { + out += cs[i].first; + out += '='; + out += cs[i].second; + out += '\n'; + } + return out; +} + +// The baseline endpoint: 200 for GET, and the request body echoed back for +// POST. +void root_get(uvcpp_web_request&, uvcpp_web_response& resp, uvcpp_web_next) { + resp.text("OK"); + resp.end(); +} + +void root_post(uvcpp_web_request& req, uvcpp_web_response& resp, uvcpp_web_next) { + const char* p = req.body_data(); + if (p == nullptr) { + resp.text(std::string()); + } else { + resp.body(p, req.body_size(), "text/plain"); + } + resp.end(); +} + +// One "name: value" line per header, in the order the parser recorded them. +void echo(uvcpp_web_request& req, uvcpp_web_response& resp, uvcpp_web_next) { + std::string out; + const http_headers& hs = req.headers(); + for (size_t i = 0; i < hs.size(); ++i) { + out += hs[i].name; + out += ": "; + out += hs[i].value; + out += '\n'; + } + resp.text(out); + resp.end(); +} + +// What the library's own cookie parser made of the Cookie header. +void cookie(uvcpp_web_request& req, uvcpp_web_response& resp, uvcpp_web_next) { + resp.text(cookies_to_lines(web_parse_cookies(req.header("cookie")))); + resp.end(); +} + +} // namespace + +int main(int argc, char** argv) { + const int port = argc > 1 ? std::atoi(argv[1]) : 8080; + + uvcpp_web_app app; + + // `/*path` is the catch-all; the framework's own HEAD-to-GET fallback and + // automatic OPTIONS answer the other methods, so neither is registered here. + app.get("/", root_get); + app.post("/", root_post); + app.get("/echo", echo); + app.post("/echo", echo); + app.get("/cookie", cookie); + app.post("/cookie", cookie); + app.get("/*path", root_get); + app.post("/*path", root_post); + + // Everything else is the framework's default: 0.0.0.0, port 8080 unless + // overridden, and the documented HEAD/OPTIONS behaviour above. + app.set_port(port); + + const int rc = app.start_background(); + if (rc != 0) { + std::fprintf(stderr, "listen on %d failed: %d\n", port, rc); + return 1; + } + app.join(); + return 0; +} +``` + +## Test Results + +

Loading results...

+ +### Compliance + +
+ +### Smuggling + +
+ +### Malformed Input + +
+ +### Caching + +
+ +### Cookies + +
+ + + + diff --git a/src/Servers/LibuvcppServer/Dockerfile b/src/Servers/LibuvcppServer/Dockerfile new file mode 100644 index 0000000..37c95f6 --- /dev/null +++ b/src/Servers/LibuvcppServer/Dockerfile @@ -0,0 +1,50 @@ +# libuvcpp entry for Http11Probe. +# +# The build downloads the official v1.6.0 linux-x64 release package and compiles +# server.cpp against it -- the same artifact a user gets from the GitHub release. +# That package's shared object already carries libuv, OpenSSL, nghttp2, ngtcp2 +# and zlib, so the runtime image needs no HTTP library and no toolchain. +# +# The download is pinned by sha256 rather than by tag: a release tag can be moved +# and its assets re-uploaded, so the hash is what fixes which bytes get built. +# +# Both stages are ubuntu:24.04 (glibc 2.39; the release package's floor is +# 2.34), so the compiler that links the binary and the libc that runs it are the +# same ones. Alpine is not an option: the package is glibc-linked. + +FROM ubuntu:24.04 AS build + +ARG LIBUVCPP_VERSION=1.6.0 +ARG LIBUVCPP_SHA256=b64ae68d121b49543e7a37126bbf38adf95a00ac51bff463b11be2dc499a138b + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates curl unzip g++ \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build +RUN curl -fsSL --retry 3 --retry-connrefused -o libuvcpp.zip \ + "https://github.com/Antruly/libuvcpp/releases/download/v${LIBUVCPP_VERSION}/libuvcpp-${LIBUVCPP_VERSION}-linux-x64.zip" \ + && echo "${LIBUVCPP_SHA256} libuvcpp.zip" | sha256sum -c - \ + && unzip -q libuvcpp.zip -d /opt \ + && rm -f libuvcpp.zip + +COPY src/Servers/LibuvcppServer/server.cpp . +RUN g++ -std=c++11 -O2 -DNDEBUG -o /probe-server server.cpp \ + -I/opt/libuvcpp-1.6.0-linux-x64/include \ + -L/opt/libuvcpp-1.6.0-linux-x64/lib \ + -luvcpp -Wl,-rpath,/opt/libuvcpp-1.6.0-linux-x64/lib -pthread + +FROM ubuntu:24.04 + +# g++ links the C++ runtime dynamically, so the runtime layer needs it. +RUN apt-get update \ + && apt-get install -y --no-install-recommends libstdc++6 \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=build /opt/libuvcpp-1.6.0-linux-x64/lib/libuvcpp.so \ + /opt/libuvcpp-1.6.0-linux-x64/lib/libuvcpp.so +COPY --from=build /probe-server /usr/local/bin/probe-server + +USER nobody +ENTRYPOINT ["probe-server", "8080"] diff --git a/src/Servers/LibuvcppServer/probe.json b/src/Servers/LibuvcppServer/probe.json new file mode 100644 index 0000000..dd7871c --- /dev/null +++ b/src/Servers/LibuvcppServer/probe.json @@ -0,0 +1 @@ +{"name": "libuvcpp", "language": "C++", "repository": "https://github.com/Antruly/libuvcpp"} diff --git a/src/Servers/LibuvcppServer/server.cpp b/src/Servers/LibuvcppServer/server.cpp new file mode 100644 index 0000000..6bc03b7 --- /dev/null +++ b/src/Servers/LibuvcppServer/server.cpp @@ -0,0 +1,100 @@ +// libuvcpp entry for Http11Probe. +// +// Three endpoints, per docs/content/add-a-framework: `/` (baseline, and body +// echo on POST), `/echo` (the headers the server received) and `/cookie` (the +// cookies the server parsed). Everything goes through `uvcpp_web_app`, the +// library's documented high-level server. + +#include +#include + +#include +#include +#include +#include +#include + +using namespace uvcpp; + +namespace { + +typedef std::vector > cookie_list; + +std::string cookies_to_lines(const cookie_list& cs) { + std::string out; + for (size_t i = 0; i < cs.size(); ++i) { + out += cs[i].first; + out += '='; + out += cs[i].second; + out += '\n'; + } + return out; +} + +// The baseline endpoint: 200 for GET, and the request body echoed back for +// POST. +void root_get(uvcpp_web_request&, uvcpp_web_response& resp, uvcpp_web_next) { + resp.text("OK"); + resp.end(); +} + +void root_post(uvcpp_web_request& req, uvcpp_web_response& resp, uvcpp_web_next) { + const char* p = req.body_data(); + if (p == nullptr) { + resp.text(std::string()); + } else { + resp.body(p, req.body_size(), "text/plain"); + } + resp.end(); +} + +// One "name: value" line per header, in the order the parser recorded them. +void echo(uvcpp_web_request& req, uvcpp_web_response& resp, uvcpp_web_next) { + std::string out; + const http_headers& hs = req.headers(); + for (size_t i = 0; i < hs.size(); ++i) { + out += hs[i].name; + out += ": "; + out += hs[i].value; + out += '\n'; + } + resp.text(out); + resp.end(); +} + +// What the library's own cookie parser made of the Cookie header. +void cookie(uvcpp_web_request& req, uvcpp_web_response& resp, uvcpp_web_next) { + resp.text(cookies_to_lines(web_parse_cookies(req.header("cookie")))); + resp.end(); +} + +} // namespace + +int main(int argc, char** argv) { + const int port = argc > 1 ? std::atoi(argv[1]) : 8080; + + uvcpp_web_app app; + + // `/*path` is the catch-all; the framework's own HEAD-to-GET fallback and + // automatic OPTIONS answer the other methods, so neither is registered here. + app.get("/", root_get); + app.post("/", root_post); + app.get("/echo", echo); + app.post("/echo", echo); + app.get("/cookie", cookie); + app.post("/cookie", cookie); + app.get("/*path", root_get); + app.post("/*path", root_post); + + // Everything else is the framework's default: 0.0.0.0, port 8080 unless + // overridden, and the documented HEAD/OPTIONS behaviour above. + app.set_port(port); + + const int rc = app.start_background(); + if (rc != 0) { + std::fprintf(stderr, "listen on %d failed: %d\n", port, rc); + return 1; + } + app.join(); + return 0; +} From 5cf6fab4217330e1bb0a301534f695793b5efbe7 Mon Sep 17 00:00:00 2001 From: Antruly Date: Sun, 11 Oct 2026 04:28:46 +0800 Subject: [PATCH 2/3] fix(libuvcpp): hold the entry's download to HTTPS, redirects included 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 #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. --- docs/content/servers/libuvcpp.md | 7 +++++-- src/Servers/LibuvcppServer/Dockerfile | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/content/servers/libuvcpp.md b/docs/content/servers/libuvcpp.md index 4c07d86..e12f97c 100644 --- a/docs/content/servers/libuvcpp.md +++ b/docs/content/servers/libuvcpp.md @@ -21,6 +21,8 @@ A C++11 HTTP framework built on libuv's event loop, serving through `uvcpp_web_a # # The download is pinned by sha256 rather than by tag: a release tag can be moved # and its assets re-uploaded, so the hash is what fixes which bytes get built. +# The transfer is forced over HTTPS, redirects included, rather than left to +# curl's default of following a redirect into whatever scheme it names. # # Both stages are ubuntu:24.04 (glibc 2.39; the release package's floor is # 2.34), so the compiler that links the binary and the libc that runs it are the @@ -33,11 +35,12 @@ ARG LIBUVCPP_SHA256=b64ae68d121b49543e7a37126bbf38adf95a00ac51bff463b11be2dc499a RUN apt-get update \ && apt-get install -y --no-install-recommends \ - ca-certificates curl unzip g++ \ + ca-certificates curl g++ unzip \ && rm -rf /var/lib/apt/lists/* WORKDIR /build -RUN curl -fsSL --retry 3 --retry-connrefused -o libuvcpp.zip \ +RUN curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 \ + --retry 3 --retry-connrefused -o libuvcpp.zip \ "https://github.com/Antruly/libuvcpp/releases/download/v${LIBUVCPP_VERSION}/libuvcpp-${LIBUVCPP_VERSION}-linux-x64.zip" \ && echo "${LIBUVCPP_SHA256} libuvcpp.zip" | sha256sum -c - \ && unzip -q libuvcpp.zip -d /opt \ diff --git a/src/Servers/LibuvcppServer/Dockerfile b/src/Servers/LibuvcppServer/Dockerfile index 37c95f6..e28d226 100644 --- a/src/Servers/LibuvcppServer/Dockerfile +++ b/src/Servers/LibuvcppServer/Dockerfile @@ -7,6 +7,8 @@ # # The download is pinned by sha256 rather than by tag: a release tag can be moved # and its assets re-uploaded, so the hash is what fixes which bytes get built. +# The transfer is forced over HTTPS, redirects included, rather than left to +# curl's default of following a redirect into whatever scheme it names. # # Both stages are ubuntu:24.04 (glibc 2.39; the release package's floor is # 2.34), so the compiler that links the binary and the libc that runs it are the @@ -19,11 +21,12 @@ ARG LIBUVCPP_SHA256=b64ae68d121b49543e7a37126bbf38adf95a00ac51bff463b11be2dc499a RUN apt-get update \ && apt-get install -y --no-install-recommends \ - ca-certificates curl unzip g++ \ + ca-certificates curl g++ unzip \ && rm -rf /var/lib/apt/lists/* WORKDIR /build -RUN curl -fsSL --retry 3 --retry-connrefused -o libuvcpp.zip \ +RUN curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 \ + --retry 3 --retry-connrefused -o libuvcpp.zip \ "https://github.com/Antruly/libuvcpp/releases/download/v${LIBUVCPP_VERSION}/libuvcpp-${LIBUVCPP_VERSION}-linux-x64.zip" \ && echo "${LIBUVCPP_SHA256} libuvcpp.zip" | sha256sum -c - \ && unzip -q libuvcpp.zip -d /opt \ From 320b2d2d968923ee3dad8a8439e8e2a185474c5e Mon Sep 17 00:00:00 2001 From: Antruly Date: Sun, 11 Oct 2026 13:22:21 +0800 Subject: [PATCH 3/3] chore(libuvcpp): pin the entry to v1.6.2, the release that closes the 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. --- docs/content/servers/libuvcpp.md | 25 ++++++++++++++++--------- src/Servers/LibuvcppServer/Dockerfile | 23 +++++++++++++++-------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/docs/content/servers/libuvcpp.md b/docs/content/servers/libuvcpp.md index e12f97c..83c141a 100644 --- a/docs/content/servers/libuvcpp.md +++ b/docs/content/servers/libuvcpp.md @@ -7,18 +7,25 @@ breadcrumbs: false **Language:** C++ · [View source on GitHub](https://github.com/MDA2AV/Http11Probe/tree/main/src/Servers/LibuvcppServer) -A C++11 HTTP framework built on libuv's event loop, serving through `uvcpp_web_app` -- the library's high-level server, which owns routing, the HEAD-to-GET fallback and the automatic OPTIONS answer. Built against the pinned v1.6.0 linux-x64 release package, whose shared object already contains libuv, OpenSSL, nghttp2, ngtcp2 and zlib; the sha256 in the Dockerfile is what fixes which bytes get compiled. +A C++11 HTTP framework built on libuv's event loop, serving through `uvcpp_web_app` -- the library's high-level server, which owns routing, the HEAD-to-GET fallback and the automatic OPTIONS answer. Built against the pinned v1.6.2 linux-x64 release package, whose shared object already contains libuv, OpenSSL, nghttp2, ngtcp2 and zlib; the sha256 in the Dockerfile is what fixes which bytes get compiled. 1.6.2 is the first release that rejects a missing, duplicated or invalid Host header (RFC 9112 section 3.2) and an all-blank Transfer-Encoding (RFC 9112 section 6.1); it scores 157/159 where 1.6.1 scores 149/159. ## Dockerfile ```dockerfile # libuvcpp entry for Http11Probe. # -# The build downloads the official v1.6.0 linux-x64 release package and compiles +# The build downloads the official v1.6.2 linux-x64 release package and compiles # server.cpp against it -- the same artifact a user gets from the GitHub release. # That package's shared object already carries libuv, OpenSSL, nghttp2, ngtcp2 # and zlib, so the runtime image needs no HTTP library and no toolchain. # +# 1.6.2 is the first release whose parser answers 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. Entries built against 1.6.1 or earlier report 149/159 on the +# probe; 1.6.2 reports 157/159. +# # The download is pinned by sha256 rather than by tag: a release tag can be moved # and its assets re-uploaded, so the hash is what fixes which bytes get built. # The transfer is forced over HTTPS, redirects included, rather than left to @@ -30,8 +37,8 @@ A C++11 HTTP framework built on libuv's event loop, serving through `uvcpp_web_a FROM ubuntu:24.04 AS build -ARG LIBUVCPP_VERSION=1.6.0 -ARG LIBUVCPP_SHA256=b64ae68d121b49543e7a37126bbf38adf95a00ac51bff463b11be2dc499a138b +ARG LIBUVCPP_VERSION=1.6.2 +ARG LIBUVCPP_SHA256=636f07346314d5f5b8fc3dbae34861a6b4441c337fc24a632011c0d7ca72d4fe RUN apt-get update \ && apt-get install -y --no-install-recommends \ @@ -48,9 +55,9 @@ RUN curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 \ COPY src/Servers/LibuvcppServer/server.cpp . RUN g++ -std=c++11 -O2 -DNDEBUG -o /probe-server server.cpp \ - -I/opt/libuvcpp-1.6.0-linux-x64/include \ - -L/opt/libuvcpp-1.6.0-linux-x64/lib \ - -luvcpp -Wl,-rpath,/opt/libuvcpp-1.6.0-linux-x64/lib -pthread + -I/opt/libuvcpp-1.6.2-linux-x64/include \ + -L/opt/libuvcpp-1.6.2-linux-x64/lib \ + -luvcpp -Wl,-rpath,/opt/libuvcpp-1.6.2-linux-x64/lib -pthread FROM ubuntu:24.04 @@ -59,8 +66,8 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends libstdc++6 \ && rm -rf /var/lib/apt/lists/* -COPY --from=build /opt/libuvcpp-1.6.0-linux-x64/lib/libuvcpp.so \ - /opt/libuvcpp-1.6.0-linux-x64/lib/libuvcpp.so +COPY --from=build /opt/libuvcpp-1.6.2-linux-x64/lib/libuvcpp.so \ + /opt/libuvcpp-1.6.2-linux-x64/lib/libuvcpp.so COPY --from=build /probe-server /usr/local/bin/probe-server USER nobody diff --git a/src/Servers/LibuvcppServer/Dockerfile b/src/Servers/LibuvcppServer/Dockerfile index e28d226..5bac166 100644 --- a/src/Servers/LibuvcppServer/Dockerfile +++ b/src/Servers/LibuvcppServer/Dockerfile @@ -1,10 +1,17 @@ # libuvcpp entry for Http11Probe. # -# The build downloads the official v1.6.0 linux-x64 release package and compiles +# The build downloads the official v1.6.2 linux-x64 release package and compiles # server.cpp against it -- the same artifact a user gets from the GitHub release. # That package's shared object already carries libuv, OpenSSL, nghttp2, ngtcp2 # and zlib, so the runtime image needs no HTTP library and no toolchain. # +# 1.6.2 is the first release whose parser answers 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. Entries built against 1.6.1 or earlier report 149/159 on the +# probe; 1.6.2 reports 157/159. +# # The download is pinned by sha256 rather than by tag: a release tag can be moved # and its assets re-uploaded, so the hash is what fixes which bytes get built. # The transfer is forced over HTTPS, redirects included, rather than left to @@ -16,8 +23,8 @@ FROM ubuntu:24.04 AS build -ARG LIBUVCPP_VERSION=1.6.0 -ARG LIBUVCPP_SHA256=b64ae68d121b49543e7a37126bbf38adf95a00ac51bff463b11be2dc499a138b +ARG LIBUVCPP_VERSION=1.6.2 +ARG LIBUVCPP_SHA256=636f07346314d5f5b8fc3dbae34861a6b4441c337fc24a632011c0d7ca72d4fe RUN apt-get update \ && apt-get install -y --no-install-recommends \ @@ -34,9 +41,9 @@ RUN curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 \ COPY src/Servers/LibuvcppServer/server.cpp . RUN g++ -std=c++11 -O2 -DNDEBUG -o /probe-server server.cpp \ - -I/opt/libuvcpp-1.6.0-linux-x64/include \ - -L/opt/libuvcpp-1.6.0-linux-x64/lib \ - -luvcpp -Wl,-rpath,/opt/libuvcpp-1.6.0-linux-x64/lib -pthread + -I/opt/libuvcpp-1.6.2-linux-x64/include \ + -L/opt/libuvcpp-1.6.2-linux-x64/lib \ + -luvcpp -Wl,-rpath,/opt/libuvcpp-1.6.2-linux-x64/lib -pthread FROM ubuntu:24.04 @@ -45,8 +52,8 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends libstdc++6 \ && rm -rf /var/lib/apt/lists/* -COPY --from=build /opt/libuvcpp-1.6.0-linux-x64/lib/libuvcpp.so \ - /opt/libuvcpp-1.6.0-linux-x64/lib/libuvcpp.so +COPY --from=build /opt/libuvcpp-1.6.2-linux-x64/lib/libuvcpp.so \ + /opt/libuvcpp-1.6.2-linux-x64/lib/libuvcpp.so COPY --from=build /probe-server /usr/local/bin/probe-server USER nobody