LCOV - code coverage report
Current view: top level - http/client - client.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 97.4 % 39 38
Test Date: 2026-02-20 15:38:22 Functions: 95.8 % 24 23

            Line data    Source code
       1              : #ifndef THINGER_HTTP_CLIENT_STANDALONE_HPP
       2              : #define THINGER_HTTP_CLIENT_STANDALONE_HPP
       3              : 
       4              : #include "http_client_base.hpp"
       5              : #include "request_builder.hpp"
       6              : #include <boost/asio/io_context.hpp>
       7              : 
       8              : namespace thinger::http {
       9              : 
      10              : /**
      11              :  * Standalone HTTP client with simple synchronous API.
      12              :  * Perfect for scripts, CLI tools, and simple applications.
      13              :  *
      14              :  * Usage:
      15              :  *   http::client client;
      16              :  *
      17              :  *   // Simple GET
      18              :  *   auto response = client.get("https://api.example.com/users");
      19              :  *   if (response) {
      20              :  *       std::cout << response.body() << std::endl;
      21              :  *   }
      22              :  *
      23              :  *   // POST with JSON
      24              :  *   auto response = client.post("https://api.example.com/users", R"({"name":"test"})");
      25              :  *
      26              :  *   // With configuration (fluent API)
      27              :  *   client.timeout(std::chrono::seconds(30)).verify_ssl(false);
      28              :  *   auto response = client.get("https://internal-api/data");
      29              :  *
      30              :  * For async/concurrent operations, use http::async_client instead.
      31              :  */
      32              : class client : public http_client_base {
      33              : private:
      34              :     boost::asio::io_context io_context_;
      35              : 
      36              :     // Internal helper to run an awaitable synchronously
      37              :     template<typename T>
      38          408 :     T exec(awaitable<T> coro) {
      39          408 :         T result;
      40          816 :         co_spawn(io_context_, [&result, coro = std::move(coro)]() mutable -> awaitable<void> {
      41              :             result = co_await std::move(coro);
      42              :         }, detached);
      43          408 :         io_context_.run();
      44          408 :         io_context_.restart();
      45          408 :         return result;
      46            0 :     }
      47              : 
      48              : protected:
      49          430 :     boost::asio::io_context& get_io_context() override {
      50          430 :         return io_context_;
      51              :     }
      52              : 
      53              : public:
      54          461 :     client() = default;
      55          461 :     ~client() override = default;
      56              : 
      57              :     // ============================================
      58              :     // Synchronous HTTP methods
      59              :     // ============================================
      60              : 
      61          214 :     client_response get(const std::string& url, headers_map headers = {}) {
      62          214 :         return exec(http_client_base::get(url, std::move(headers)));
      63              :     }
      64              : 
      65           46 :     client_response post(const std::string& url, std::string body = {},
      66              :                          std::string content_type = "application/json", headers_map headers = {}) {
      67           46 :         return exec(http_client_base::post(url, std::move(body), std::move(content_type), std::move(headers)));
      68              :     }
      69              : 
      70              :     client_response post(const std::string& url, const form& form, headers_map headers = {}) {
      71              :         return exec(http_client_base::post(url, form, std::move(headers)));
      72              :     }
      73              : 
      74           10 :     client_response put(const std::string& url, std::string body = {},
      75              :                         std::string content_type = "application/json", headers_map headers = {}) {
      76           10 :         return exec(http_client_base::put(url, std::move(body), std::move(content_type), std::move(headers)));
      77              :     }
      78              : 
      79            6 :     client_response patch(const std::string& url, std::string body = {},
      80              :                           std::string content_type = "application/json", headers_map headers = {}) {
      81            6 :         return exec(http_client_base::patch(url, std::move(body), std::move(content_type), std::move(headers)));
      82              :     }
      83              : 
      84            4 :     client_response del(const std::string& url, headers_map headers = {}) {
      85            4 :         return exec(http_client_base::del(url, std::move(headers)));
      86              :     }
      87              : 
      88            4 :     client_response head(const std::string& url, headers_map headers = {}) {
      89            4 :         return exec(http_client_base::head(url, std::move(headers)));
      90              :     }
      91              : 
      92            2 :     client_response options(const std::string& url, headers_map headers = {}) {
      93            2 :         return exec(http_client_base::options(url, std::move(headers)));
      94              :     }
      95              : 
      96              :     // Unix socket variants
      97           36 :     client_response get(const std::string& url, const std::string& unix_socket, headers_map headers = {}) {
      98           36 :         return exec(http_client_base::get(url, unix_socket, std::move(headers)));
      99              :     }
     100              : 
     101            6 :     client_response post(const std::string& url, const std::string& unix_socket,
     102              :                          std::string body = {}, std::string content_type = "application/json",
     103              :                          headers_map headers = {}) {
     104            6 :         return exec(http_client_base::post(url, unix_socket, std::move(body), std::move(content_type), std::move(headers)));
     105              :     }
     106              : 
     107              :     // Generic send with custom request
     108           50 :     client_response send(std::shared_ptr<http_request> request) {
     109           50 :         return exec(http_client_base::send(std::move(request)));
     110              :     }
     111              : 
     112              :     // Streaming send
     113            2 :     stream_result send_streaming(std::shared_ptr<http_request> request, stream_callback callback) {
     114            2 :         return exec(http_client_base::send_streaming(std::move(request), std::move(callback)));
     115              :     }
     116              : 
     117              :     // ============================================
     118              :     // Request builder for fluent API
     119              :     // ============================================
     120              : 
     121              :     /**
     122              :      * Create a request builder for fluent API with streaming support.
     123              :      *
     124              :      * Usage:
     125              :      *   auto res = client.request("https://api.com/data")
     126              :      *       .header("Authorization", "Bearer xxx")
     127              :      *       .get();
     128              :      */
     129           55 :     request_builder<client> request(const std::string& url) {
     130           55 :         return request_builder<client>(this, url);
     131              :     }
     132              : 
     133              :     // ============================================
     134              :     // WebSocket
     135              :     // ============================================
     136              : 
     137              :     /**
     138              :      * Connect to a WebSocket server (simple API).
     139              :      *
     140              :      * Usage:
     141              :      *   if (auto ws = client.websocket("ws://server.com/path")) {
     142              :      *       ws->send_text("Hello!");
     143              :      *       auto [msg, binary] = ws->receive();
     144              :      *       ws->close();
     145              :      *   }
     146              :      */
     147           20 :     std::optional<websocket_client> websocket(const std::string& url,
     148              :                                                const std::string& subprotocol = "") {
     149           20 :         return exec(http_client_base::upgrade_websocket(url, subprotocol));
     150              :     }
     151              : 
     152              :     /**
     153              :      * Connect to a WebSocket server with custom request/headers (used by request_builder).
     154              :      */
     155            8 :     std::optional<websocket_client> websocket(std::shared_ptr<http_request> request,
     156              :                                                const std::string& subprotocol = "") {
     157            8 :         return exec(http_client_base::upgrade_websocket(std::move(request), subprotocol));
     158              :     }
     159              : };
     160              : 
     161              : } // namespace thinger::http
     162              : 
     163              : #endif
        

Generated by: LCOV version 2.0-1