2026-07-22 17:55:45 +02:00
|
|
|
//SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
|
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
|
|
2026-05-06 01:06:05 +02:00
|
|
|
import std;
|
|
|
|
|
import Crafter.Build;
|
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
using namespace Crafter;
|
|
|
|
|
|
|
|
|
|
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
constexpr std::array<std::string_view, 13> networkInterfaces = {
|
2026-05-06 01:06:05 +02:00
|
|
|
"interfaces/Crafter.Network",
|
|
|
|
|
"interfaces/Crafter.Network-ClientTCP",
|
|
|
|
|
"interfaces/Crafter.Network-ListenerTCP",
|
|
|
|
|
"interfaces/Crafter.Network-ClientHTTP",
|
|
|
|
|
"interfaces/Crafter.Network-ListenerHTTP",
|
|
|
|
|
"interfaces/Crafter.Network-HTTP",
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
"interfaces/Crafter.Network-HTTP1",
|
|
|
|
|
"interfaces/Crafter.Network-ClientHTTP1",
|
|
|
|
|
"interfaces/Crafter.Network-ListenerHTTP1",
|
2026-05-07 00:06:44 +02:00
|
|
|
"interfaces/Crafter.Network-HTTP3",
|
2026-05-06 04:06:17 +02:00
|
|
|
"interfaces/Crafter.Network-ClientQUIC",
|
|
|
|
|
"interfaces/Crafter.Network-ListenerQUIC",
|
2026-05-19 02:53:50 +02:00
|
|
|
"interfaces/Crafter.Network-WebTransport",
|
2026-05-06 01:06:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> depArgs(args.begin(), args.end());
|
|
|
|
|
Configuration* thread = GitProject({
|
|
|
|
|
.source = { .url = "https://forgejo.catcrafts.net/Catcrafts/Crafter.Thread.git" },
|
|
|
|
|
.args = depArgs,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Configuration cfg;
|
|
|
|
|
cfg.path = "./";
|
2026-05-06 04:06:17 +02:00
|
|
|
cfg.name = "crafter-network";
|
|
|
|
|
cfg.outputName = "crafter-network";
|
2026-05-06 01:06:05 +02:00
|
|
|
cfg.type = ConfigurationType::LibraryStatic;
|
|
|
|
|
ApplyStandardArgs(cfg, args);
|
|
|
|
|
cfg.dependencies = { thread };
|
|
|
|
|
|
2026-05-19 02:53:50 +02:00
|
|
|
// Browser path: any wasm32-* target gets the browser network stack
|
|
|
|
|
// (fetch + WebTransport via JS glue). msquic and the POSIX socket
|
|
|
|
|
// backends are skipped; the listener / TCP partitions stub to empty
|
|
|
|
|
// modules via #ifdef CRAFTER_NETWORK_BROWSER in their interface files.
|
|
|
|
|
// HTTP3 (varint / frame / QPACK codec) is dropped entirely — it threw
|
|
|
|
|
// exceptions for protocol errors, which the wasm build's -fno-exceptions
|
|
|
|
|
// forbids, and the browser's fetch() handles HTTP-layer framing itself.
|
|
|
|
|
bool browser = cfg.target.find("wasm") != std::string::npos;
|
|
|
|
|
if (browser) {
|
|
|
|
|
cfg.defines.push_back({"CRAFTER_NETWORK_BROWSER", ""});
|
|
|
|
|
|
|
|
|
|
std::array<fs::path, 9> browserIfaces = {
|
|
|
|
|
"interfaces/Crafter.Network",
|
|
|
|
|
"interfaces/Crafter.Network-ClientTCP",
|
|
|
|
|
"interfaces/Crafter.Network-ListenerTCP",
|
|
|
|
|
"interfaces/Crafter.Network-ClientHTTP",
|
|
|
|
|
"interfaces/Crafter.Network-ListenerHTTP",
|
|
|
|
|
"interfaces/Crafter.Network-HTTP",
|
|
|
|
|
"interfaces/Crafter.Network-ClientQUIC",
|
|
|
|
|
"interfaces/Crafter.Network-ListenerQUIC",
|
|
|
|
|
"interfaces/Crafter.Network-WebTransport",
|
|
|
|
|
};
|
|
|
|
|
std::array<fs::path, 2> browserImpls = {
|
|
|
|
|
"implementations/Crafter.Network-ClientHTTP-Browser",
|
|
|
|
|
"implementations/Crafter.Network-ClientQUIC-Browser",
|
|
|
|
|
};
|
|
|
|
|
cfg.GetInterfacesAndImplementations(browserIfaces, browserImpls);
|
|
|
|
|
|
|
|
|
|
// JS glue shipped alongside the .wasm. The consuming executable's
|
|
|
|
|
// wasi-browser runtime merges this into the env import object
|
|
|
|
|
// before instantiation (mirrors Crafter.Graphics/dom-env.js).
|
|
|
|
|
cfg.files.emplace_back(fs::path("additional/network-env.js"));
|
|
|
|
|
return cfg;
|
|
|
|
|
}
|
|
|
|
|
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
constexpr std::array<std::string_view, 9> networkImplementations = {
|
2026-05-19 02:53:50 +02:00
|
|
|
"implementations/Crafter.Network-ClientTCP",
|
|
|
|
|
"implementations/Crafter.Network-ListenerTCP",
|
|
|
|
|
"implementations/Crafter.Network-ClientHTTP",
|
|
|
|
|
"implementations/Crafter.Network-ListenerHTTP",
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
"implementations/Crafter.Network-ClientHTTP1",
|
|
|
|
|
"implementations/Crafter.Network-ListenerHTTP1",
|
2026-05-19 02:53:50 +02:00
|
|
|
"implementations/Crafter.Network-ClientQUIC",
|
|
|
|
|
"implementations/Crafter.Network-ListenerQUIC",
|
|
|
|
|
"implementations/Crafter.Network-WebTransport",
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-06 04:06:17 +02:00
|
|
|
// msquic — provides the QUIC transport used by ClientQUIC / ListenerQUIC.
|
|
|
|
|
// Cloned + built via CMake into the per-project external cache; no system
|
|
|
|
|
// package required. Submodules (quictls / clog / etc.) come via the
|
|
|
|
|
// recursive clone Crafter.Build performs. We disable msquic's own tests,
|
|
|
|
|
// tools and perf binaries since we only need the library.
|
|
|
|
|
ExternalDependency& msquic = cfg.externalDependencies.emplace_back();
|
|
|
|
|
msquic.name = "msquic";
|
|
|
|
|
msquic.source.url = "https://github.com/microsoft/msquic.git";
|
|
|
|
|
msquic.source.branch = "main";
|
|
|
|
|
msquic.builder = ExternalBuilder::CMake;
|
|
|
|
|
msquic.options = {
|
|
|
|
|
"-DQUIC_TLS_LIB=quictls",
|
|
|
|
|
"-DQUIC_BUILD_TEST=OFF",
|
|
|
|
|
"-DQUIC_BUILD_TOOLS=OFF",
|
|
|
|
|
"-DQUIC_BUILD_PERF=OFF",
|
|
|
|
|
"-DQUIC_BUILD_SHARED=ON",
|
2026-05-06 01:06:05 +02:00
|
|
|
};
|
2026-05-06 04:06:17 +02:00
|
|
|
msquic.includeDirs = { "src/inc" };
|
|
|
|
|
// msquic's CMakeLists overrides CMAKE_LIBRARY_OUTPUT_DIRECTORY with
|
|
|
|
|
// QUIC_OUTPUT_DIR (defaults to bin/$<CONFIG>), so libmsquic.so lands in
|
|
|
|
|
// a subdir of the cmake build dir rather than at its root. Point the
|
|
|
|
|
// linker at the actual output location.
|
|
|
|
|
msquic.libDirs = { "bin/Release" };
|
|
|
|
|
msquic.libs = { "msquic" };
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
std::array<fs::path, 13> ifaces;
|
2026-05-06 04:06:17 +02:00
|
|
|
std::ranges::copy(networkInterfaces, ifaces.begin());
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
std::array<fs::path, 9> impls;
|
2026-05-06 04:06:17 +02:00
|
|
|
std::ranges::copy(networkImplementations, impls.begin());
|
|
|
|
|
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
2026-05-06 01:06:05 +02:00
|
|
|
|
2026-05-28 16:48:55 +02:00
|
|
|
// Linux-only: msquic + POSIX socket backends. The browser path above
|
|
|
|
|
// returns early, so wasm builds skip these. Each test links the local
|
|
|
|
|
// crafter-network static lib via .Dependencies({ &cfg }).
|
|
|
|
|
if (cfg.target == "x86_64-pc-linux-gnu") {
|
|
|
|
|
cfg.AddTest("ShouldEchoWebTransport").Dependencies({ &cfg });
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
cfg.AddTest("ShouldInteropCurlHTTP1").Dependencies({ &cfg });
|
2026-05-29 16:56:07 +02:00
|
|
|
cfg.AddTest("ShouldNotDropEarlyStreams").Dependencies({ &cfg });
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
cfg.AddTest("ShouldParseHTTP1").Dependencies({ &cfg });
|
2026-05-28 16:48:55 +02:00
|
|
|
cfg.AddTest("ShouldSend").Dependencies({ &cfg });
|
feat(http1): add an HTTP/1.1 client and listener
HTTP/3-only is not a deployable position yet: plenty of clients, proxies
and CI tooling still speak nothing but HTTP/1.1. This adds that path
using the request/response types the HTTP/3 stack already uses, so a
route handler or call site moves between the two protocols by changing
the class name.
- :HTTP1 — transport-free wire format. Serialisation with the framing
headers owned by the serialiser, and an incremental parser that takes
arbitrary socket chunks and yields one message at a time: keep-alive,
pipelining, content-length and chunked bodies (with trailers),
read-to-EOF responses, interim 1xx skipping, HEAD/204/304 framing and
Expect: 100-continue. Ambiguous framing is rejected rather than
guessed at (content-length with transfer-encoding, disagreeing
content-lengths, whitespace before a colon), and CR/LF in a value we
are asked to serialise is refused.
- ClientHTTP1 — persistent connection, redialling once when a pooled
connection turns out to have been closed by the peer, which is the
race HTTP/1.1 keep-alive cannot avoid. Nothing is replayed after a
response byte has arrived.
- ListenerHTTP1 — one thread per connection (keep-alive connections are
idle most of their life and would pin every ThreadPool thread),
automatic Date, HEAD, 100-continue, handler-requested close, idle and
request timeouts, and 400/404/500 responses. Routes fall back to the
query-stripped path so `/thing?x=1` reaches the handler for `/thing`.
No TLS: this is `http://` only. Encrypted traffic still goes over
HTTP/3, or through a TLS-terminating proxy.
Tests: codec unit tests including the malformed inputs above, a
client/server round-trip, keep-alive and stale-connection recovery, a
10 MiB body both ways, and interop both directions against curl and
python3's http.server (skipped when those are not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 00:45:09 +00:00
|
|
|
cfg.AddTest("ShouldSendRecieveHTTP1").Dependencies({ &cfg });
|
|
|
|
|
cfg.AddTest("ShouldSendRecieveKeepaliveHTTP1").Dependencies({ &cfg });
|
|
|
|
|
cfg.AddTest("ShouldSendRecieveLargeHTTP1").Dependencies({ &cfg });
|
2026-05-28 16:48:55 +02:00
|
|
|
cfg.AddTest("ShouldSendRecieveHTTP").Dependencies({ &cfg });
|
|
|
|
|
cfg.AddTest("ShouldSendRecieveKeepaliveHTTP").Dependencies({ &cfg });
|
|
|
|
|
cfg.AddTest("ShouldSendRecieveLargeHTTP").Dependencies({ &cfg });
|
|
|
|
|
cfg.AddTest("ShouldSendRecieveQUICDatagram").Dependencies({ &cfg });
|
|
|
|
|
cfg.AddTest("ShouldSendRecieveQUICStream").Dependencies({ &cfg });
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-06 01:06:05 +02:00
|
|
|
return cfg;
|
|
|
|
|
}
|