Crafter.Network/project.cpp

124 lines
5.6 KiB
C++
Raw Normal View History

import std;
import Crafter.Build;
namespace fs = std::filesystem;
using namespace Crafter;
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
2026-05-19 02:53:50 +02:00
constexpr std::array<std::string_view, 10> networkInterfaces = {
"interfaces/Crafter.Network",
"interfaces/Crafter.Network-ClientTCP",
"interfaces/Crafter.Network-ListenerTCP",
"interfaces/Crafter.Network-ClientHTTP",
"interfaces/Crafter.Network-ListenerHTTP",
"interfaces/Crafter.Network-HTTP",
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",
};
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";
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;
}
constexpr std::array<std::string_view, 7> networkImplementations = {
"implementations/Crafter.Network-ClientTCP",
"implementations/Crafter.Network-ListenerTCP",
"implementations/Crafter.Network-ClientHTTP",
"implementations/Crafter.Network-ListenerHTTP",
"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 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" };
2026-05-19 02:53:50 +02:00
std::array<fs::path, 10> ifaces;
2026-05-06 04:06:17 +02:00
std::ranges::copy(networkInterfaces, ifaces.begin());
2026-05-19 02:53:50 +02:00
std::array<fs::path, 7> impls;
2026-05-06 04:06:17 +02:00
std::ranges::copy(networkImplementations, impls.begin());
cfg.GetInterfacesAndImplementations(ifaces, impls);
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 });
fix(listener): stop dropping a peer's streams that arrive during connection setup ListenerQUIC installed only a no-op bootstrap connection callback in the NEW_CONNECTION handler and deferred the real ClientQUIC callback to the ThreadPool, alongside per-connection onConnect setup. An HTTP/3 peer (notably Chromium) opens its control + QPACK + request streams the instant the QUIC handshake completes — potentially before that deferred task ran. Those early PEER_STREAM_STARTED events were delivered to the bootstrap and silently dropped, so the session never completed. Over the network this surfaced as an intermittent "WebTransport connection rejected" that cleared on retry. Construct the ClientQUIC (and thus install its real connection callback) synchronously inside NEW_CONNECTION, before the handler returns and before msquic delivers any further events. pendingAccepted now holds the constructed ClientQUIC*; the accept loops just dispatch it, and the destructor cleans up any peer accepted but never dispatched. Also park WT data streams that arrive before their CONNECT session is registered (the stream demux races the CONNECT handler) and drain them on registration, instead of dropping them. Tests: - New ShouldNotDropEarlyStreams reproduces the race deterministically by saturating the ThreadPool so onConnect is gated while the client opens its request stream; fails on the pre-fix build, passes after. - Give ShouldEchoWebTransport its own port (8085) so it no longer collides with ShouldSendRecieveKeepaliveHTTP (8083) under the parallel test runner. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 16:56:07 +02:00
cfg.AddTest("ShouldNotDropEarlyStreams").Dependencies({ &cfg });
2026-05-28 16:48:55 +02:00
cfg.AddTest("ShouldSend").Dependencies({ &cfg });
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 });
}
return cfg;
}