//SPDX-License-Identifier: LGPL-3.0-only //SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® export module Crafter.Network:ClientHTTP1; import std; import :HTTP; import :HTTP1; import :TLS; #ifndef CRAFTER_NETWORK_BROWSER namespace Crafter { // HTTP/1.1 client over TCP, with or without TLS, for peers that cannot // speak HTTP/3. The request/response types are the ones the HTTP/3 client // uses, so swapping ClientHTTP for ClientHTTP1 is a one-line change at the // call site. // // The connection is persistent: the first Send() dials, and later calls // reuse the socket unless the peer asked for it to be closed // (`Connection: close`, or an HTTP/1.0 response without // `Connection: keep-alive`). A reused connection that turns out to have // been closed by the peer in the meantime — the unavoidable race in // HTTP/1.1 keep-alive — is redialled once and the request replayed; // a freshly dialled connection is never replayed on, so a genuinely // broken server surfaces as an exception rather than a retry loop. // // Thread-affinity matches ClientHTTP: one ClientHTTP1 serves one caller // at a time; distinct instances are independent. // // `http://` or `https://` is chosen by the constructor: pass // TLSClientCredentials and every byte goes through libssl (see :TLS), // leave them out and the transport is plaintext. TLS changes nothing // above the transport — the same keep-alive, replay and framing rules // apply, and `authority` still defaults to host:port with the scheme's // default port elided (443 under TLS, 80 without). export class ClientHTTP1 { public: std::string host; std::uint16_t port; ClientHTTP1(const char* host, std::uint16_t port); ClientHTTP1(std::string host, std::uint16_t port); // https://. The credentials verify the server's certificate chain and // its name against `host` by default; see TLSClientCredentials for // self-signed peers, private trust anchors and client certificates. // Throws TLSException if the certificate is rejected. ClientHTTP1(const char* host, std::uint16_t port, TLSClientCredentials credentials); ClientHTTP1(std::string host, std::uint16_t port, TLSClientCredentials credentials); ~ClientHTTP1(); ClientHTTP1(const ClientHTTP1&) = delete; ClientHTTP1(ClientHTTP1&&) noexcept; // Send a request and read the full response. `authority` defaults to // the host:port this client was constructed with; `scheme` is ignored // — HTTP/1.1 request targets are origin-form and the transport was // already decided by the constructor. HTTPResponse Send(const HTTPRequest& request); // Send a request and deliver the response (or the error text) via // callback, on Crafter.Thread's ThreadPool. void SendAsync(const HTTPRequest& request, std::function onSuccess, std::function onError); // Whether a pooled connection is currently open. Mostly useful for // tests asserting that keep-alive actually kept the socket. bool Connected() const noexcept; // Whether this client speaks https://. bool Secure() const noexcept; // ALPN protocol the last connection negotiated, empty for plaintext // or when the server offered no ALPN. For an https:// client with the // default credentials this is "http/1.1". std::string_view Protocol() const noexcept; // Drop the pooled connection; the next Send() dials again. void Disconnect(); // Limits applied to responses. Set before the first Send(). HTTP1::MessageLimits limits; // How long to wait for the next piece of a response before giving // up on a server that accepted the connection and then went quiet. std::chrono::milliseconds timeout{30000}; // How long the TLS handshake may take, on an https:// client. Separate // from `timeout` because it covers a multi-round-trip exchange before // any request has been written. std::chrono::milliseconds handshakeTimeout{15000}; private: struct Impl; std::unique_ptr impl; }; } #endif