// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® export module Crafter.Review:Http; import std; import :Json; // The HTTP the driver needs: app_ready's preflight, the OpenRouter catalogue // and key probe. On Crafter.Network's FetchHTTP1, which follows redirects; on // top of it a status outside 2xx thrown as HttpError, and every other failure // as Error with a fixed text, because those texts reach the run log and // app_ready's detail. Proxies are not consulted (AGENTS.md). export namespace Crafter::Review::Http { struct Response { std::int32_t status = 0; std::string body; }; // A status outside 2xx. what(): "HTTP Error 404: Not Found", with the // standard reason phrase rather than the server's. class HttpError : public std::runtime_error { public: HttpError(std::int32_t code, const std::string& message); explicit HttpError(std::int32_t code); std::int32_t Code() const; private: std::int32_t code; }; // Every other failure. What fails while the request goes out reads // "" or ""; what fails while the response comes in reads as it is: // "timed out". class Error : public std::runtime_error { public: using std::runtime_error::runtime_error; }; // A GET of `url`, read whole. Header names in any case; Accept-Encoding: // identity and Connection: close always go out, and a User-Agent unless // one is given. Response UrlOpen(std::string_view url, const std::map& headers, double timeout); // The body as JSON: Dyn::Error JSONDecodeError (or UnicodeDecodeError) // with the scanner's message for a body that is not. Json JsonBody(const Response& response); // http.HTTPStatus(code).phrase, "" for a code it does not know. std::string_view Reason(std::int32_t code); }