/* Crafter®.Network Copyright (C) 2026 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; #include export module Crafter.Network:ClientQUIC; import std; namespace Crafter { export class ListenerQUIC; export class QUICException : public std::runtime_error { public: using std::runtime_error::runtime_error; }; export class QUICClosedException : public std::exception { public: const char* what() const noexcept override { return "QUIC connection closed"; } }; // Server certificate sources. Pick one of (filePaths) or (selfSigned). // selfSigned generates an in-memory ephemeral cert — fine for dev/LAN. export struct QUICServerCredentials { std::string certPath; std::string keyPath; bool selfSigned = false; }; // Client-side credential validation. By default we require a real cert. // insecureNoServerValidation disables peer cert checks — only for dev. export struct QUICClientCredentials { bool insecureNoServerValidation = false; }; export class ClientQUIC; // A reliable, ordered, bidirectional stream within a QUIC connection. // Owned by ClientQUIC; do not destroy directly. Obtain via // ClientQUIC::OpenStream() or via the on-stream callback for inbound // streams initiated by the peer. export class QUICStream { public: // Underlying msquic HQUIC handle. Treated as opaque by callers. HQUIC handle = nullptr; // The connection that owns this stream (non-owning). ClientQUIC* connection = nullptr; QUICStream() = default; QUICStream(HQUIC handle, ClientQUIC* connection); ~QUICStream(); QUICStream(const QUICStream&) = delete; QUICStream(QUICStream&&) noexcept; QUICStream& operator=(QUICStream&&) noexcept; // Send a buffer. If finish=true, the send-side of the stream is closed // after the buffer is delivered (peer will see graceful shutdown). // Blocks until msquic accepts the buffer; throws on stream/conn close. void SendSync(const void* buffer, std::uint32_t size, bool finish = false); // Block until at least one byte is received; returns the received // chunk. Throws QUICClosedException once the peer has closed the // send-side and the buffer is drained. std::vector RecieveSync(); // Read until the peer closes the send-side, accumulating all chunks. std::vector RecieveUntilCloseSync(); // Read exactly bufferSize bytes; throws if the peer closes early. std::vector RecieveUntilFullSync(std::uint32_t bufferSize); // Async variants: dispatched on Crafter.Thread's ThreadPool. void RecieveAsync(std::function)> recieveCallback); void RecieveUntilCloseAsync(std::function)> recieveCallback); void RecieveUntilFullAsync(std::uint32_t bufferSize, std::function)> recieveCallback); // Cleanly shut down the stream (both directions). void Stop(); private: struct Impl; std::unique_ptr impl; friend class ClientQUIC; }; // A QUIC connection. On the client side, constructing one initiates the // handshake and blocks until it succeeds (or throws on failure). On the // server side, ListenerQUIC instantiates these for accepted peers. // // A connection multiplexes: // - Reliable, ordered streams (open via OpenStream() / observe inbound // via OnStream()). // - Unreliable, unordered datagrams (SendDatagram() / OnDatagram()). // // Lifetime: ~ClientQUIC closes the connection. Streams obtained from // OpenStream() are scoped to the connection and must be destroyed (or // moved out) before the ClientQUIC. export class ClientQUIC { public: // ALPN identifier exchanged in the handshake. Both peers must agree. // For 3DForts use e.g. "f3d/1" or similar — a short stable token. std::string alpn; // Client constructor: connects to host:port using QUIC. ALPN must // match the listener. Throws QUICException on connect failure. ClientQUIC(const char* host, std::uint16_t port, std::string alpn, QUICClientCredentials creds = {}); ClientQUIC(std::string host, std::uint16_t port, std::string alpn, QUICClientCredentials creds = {}); // Server-side constructor used by ListenerQUIC for accepted peers. // Takes ownership of an already-accepted msquic connection handle // and the server configuration handle. Not intended for direct use. ClientQUIC(HQUIC connectionHandle, HQUIC serverConfiguration, std::string alpn); ~ClientQUIC(); ClientQUIC(const ClientQUIC&) = delete; ClientQUIC(ClientQUIC&&) noexcept; // Open a new bidirectional stream initiated by this side. // Blocks until the stream is started; throws on failure. QUICStream OpenStream(); // Send a datagram. Best-effort: may be silently dropped under loss // or congestion. Size must fit within the path MTU (msquic surfaces // the maximum via QUIC_PARAM_CONN_DATAGRAM_SEND_ENABLED — typically // ~1200 bytes safely on the open internet). void SendDatagram(const void* buffer, std::uint32_t size); // Register a handler for streams the peer initiates toward us. // Called on the msquic worker; offload heavy work to ThreadPool. void OnStream(std::function callback); // Register a handler for datagrams from the peer. Called on the // msquic worker; copy/queue and return promptly. void OnDatagram(std::function)> callback); // Block the caller until the next datagram arrives; returns it. // Throws QUICClosedException if the connection closes first. std::vector RecieveDatagramSync(); // Cleanly shut down the connection. void Stop(); // Underlying handle for advanced use (parameter queries, etc.). HQUIC GetHandle() const; private: struct Impl; std::unique_ptr impl; friend class ListenerQUIC; friend class QUICStream; }; }