// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // The app's end of the wire: QUIC on 127.0.0.1 with the ALPN // crafter-debug/1, one request per stream the client opens. // // client {"token", "method", "params", "timeoutMs"}, then FIN // server zero or more stream lines ({"entry": ...}, `log --follow`), // then the reply ({"result": ...} or {"error": ...}), then FIN // // Every object is one line of JSON. Nothing here is exported: the session // is what an app sees, and this partition keeps Crafter.Network's types out // of the interface an app imports. export module Crafter.Debug:Transport; import std; import Crafter.Debug.Core; import :Session; namespace Crafter::Debug::Detail { // Listens, and hands each complete request to the handler on a pool // task, with an Exchange that answers on the request's own stream. // // Holds no thread while nothing arrives: Crafter.Network delivers bytes // on msquic's workers and new streams on the host's pool, and replies go // out without waiting. The one thread it has is ListenerQUIC's accept // loop, which only registers each new connection's callbacks. class Server { public: using Handler = std::function exchange)>; // On 127.0.0.1:port, 0 for any free port. Throws when it can't // listen. Needs Crafter.Thread's pool running, with a worker. Server(std::uint16_t port, Handler handler); // Stops listening and closes every connection. Replies already // given still reach their clients: each connection waits, bounded, // for its streams to finish before it closes. ~Server(); Server(const Server&) = delete; Server& operator=(const Server&) = delete; std::uint16_t Port() const; struct State; private: std::shared_ptr state; std::uint16_t port = 0; }; }