feat(transport): serve the session over QUIC on loopback
A Session with listen = true (the default) now listens on 127.0.0.1 with the ALPN crafter-debug/1 and writes its discovery record (port, token, protocol, source id, client path), which goes first when it ends. One request per stream the client opens: the request and a FIN in, zero or more stream lines and then the reply and a FIN out, one JSON object per line. It holds no thread while nothing arrives. Crafter.Network delivers a new stream on the pool, its bytes on msquic's worker through OnRecieve, and replies leave through SendAsync, which never waits. The handler, which takes the session's mutex, runs on a pool task rather than inside the stream's callback, so no msquic worker ever waits on the session, and the session's threads only send, which Network allows under any lock. Stream wrappers are let go outside every lock their callbacks take. The rules head the implementation. Limits: a request over 1 MiB is answered with an error, not buffered, and a log follower more than 8 MiB behind is reset. quit --force exits 200 ms after its reply was taken, from a thread of its own, since "sent" means msquic has the bytes, not that they left. A client killed without closing is let go at the 10 s idle timeout. The Session refuses a pool without a worker: Network hands it new streams there, and with Start(1) nothing would run them. The partition exports nothing, so an app's view of the module is unchanged and it does not see Crafter.Network's types. Crafter.Network comes from forgejo, unpinned, with the args unchanged; --local takes ../Crafter.Network, and $CRAFTER_DEBUG_NETWORK any project.cpp, for the branch the callback API is on until it lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d75f935bf2
commit
47f2325664
5 changed files with 416 additions and 3 deletions
31
project.cpp
31
project.cpp
|
|
@ -12,6 +12,10 @@
|
|||
// debug build compiles it in the same pass as the app; an executable adds no
|
||||
// link flags to what depends on it. Consumers load this file through
|
||||
// Debug::Use (tooling/), which also writes their launcher.
|
||||
//
|
||||
// The library and the client talk QUIC through Crafter.Network, which comes
|
||||
// from forgejo, with --local from the checkout beside this one, or from the
|
||||
// project.cpp $CRAFTER_DEBUG_NETWORK names (a Network worktree).
|
||||
import std;
|
||||
import Crafter.Build;
|
||||
namespace fs = std::filesystem;
|
||||
|
|
@ -30,6 +34,23 @@ namespace {
|
|||
return fs::path(std::source_location::current().file_name()).parent_path();
|
||||
}
|
||||
|
||||
// Crafter.Network, with the args unchanged: a consumer that also uses
|
||||
// it passes the same ones, so both resolve to one configuration rather
|
||||
// than one module built twice.
|
||||
Configuration* Network(std::span<const std::string_view> args) {
|
||||
const std::vector<std::string> forwarded(args.begin(), args.end());
|
||||
if (const char* local = std::getenv("CRAFTER_DEBUG_NETWORK"); local != nullptr && *local != '\0') {
|
||||
return LocalProject({ .projectFile = local, .args = forwarded });
|
||||
}
|
||||
if (std::ranges::find(args, std::string_view("--local")) != args.end()) {
|
||||
return LocalProject({ .projectFile = Root().parent_path() / "Crafter.Network" / "project.cpp", .args = forwarded });
|
||||
}
|
||||
return GitProject({
|
||||
.source = { .url = "https://forgejo.catcrafts.net/Catcrafts/Crafter.Network.git" },
|
||||
.args = forwarded,
|
||||
});
|
||||
}
|
||||
|
||||
// Tests of the core, linked against it alone.
|
||||
constexpr std::array<std::string_view, 5> coreTests = {
|
||||
"ShouldRoundTripJson",
|
||||
|
|
@ -118,6 +139,8 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
cfg.type = ConfigurationType::LibraryStatic;
|
||||
ApplyStandardArgs(cfg, args);
|
||||
|
||||
Configuration* network = Network(args);
|
||||
|
||||
Configuration& core = owned.emplace_back();
|
||||
core.path = Root();
|
||||
core.name = "Crafter.Debug.Core";
|
||||
|
|
@ -183,20 +206,22 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
client.GetInterfacesAndImplementations(interfaces, implementations);
|
||||
}
|
||||
|
||||
cfg.dependencies = { &core, &client };
|
||||
cfg.dependencies = { &core, &client, network };
|
||||
{
|
||||
std::array<fs::path, 5> interfaces = {
|
||||
std::array<fs::path, 6> interfaces = {
|
||||
"interfaces/Crafter.Debug-Registry",
|
||||
"interfaces/Crafter.Debug-Timeline",
|
||||
"interfaces/Crafter.Debug-Condition",
|
||||
"interfaces/Crafter.Debug-Session",
|
||||
"interfaces/Crafter.Debug-Transport",
|
||||
"interfaces/Crafter.Debug",
|
||||
};
|
||||
std::array<fs::path, 4> implementations = {
|
||||
std::array<fs::path, 5> implementations = {
|
||||
"implementations/Crafter.Debug-Registry",
|
||||
"implementations/Crafter.Debug-Timeline",
|
||||
"implementations/Crafter.Debug-Condition",
|
||||
"implementations/Crafter.Debug-Session",
|
||||
"implementations/Crafter.Debug-Transport",
|
||||
};
|
||||
cfg.GetInterfacesAndImplementations(interfaces, implementations);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue