Line data Source code
1 : #ifndef THINGER_HTTP_CLIENT_HTTP_CLIENT_BASE_HPP
2 : #define THINGER_HTTP_CLIENT_HTTP_CLIENT_BASE_HPP
3 :
4 : #include "../common/http_request.hpp"
5 : #include "../common/http_response.hpp"
6 : #include "client_connection.hpp"
7 : #include "client_response.hpp"
8 : #include "connection_pool.hpp"
9 : #include "websocket_client.hpp"
10 : #include "stream_types.hpp"
11 : #include "../../util/types.hpp"
12 : #include "form.hpp"
13 : #include <memory>
14 : #include <string>
15 : #include <chrono>
16 : #include <map>
17 : #include <optional>
18 :
19 : namespace thinger::http {
20 :
21 : using headers_map = std::map<std::string, std::string>;
22 :
23 : class http_client_base {
24 : protected:
25 : // Configuration
26 702 : std::chrono::seconds timeout_{30};
27 : unsigned int max_redirects_{5};
28 : bool follow_redirects_{true};
29 : std::string user_agent_{"ThingerHTTP/2.0"};
30 : bool auto_decompress_{true};
31 : bool verify_ssl_{true};
32 :
33 : // Connection pool for keep-alive
34 : connection_pool pool_;
35 :
36 : // Abstract method - each derived class provides its own io_context
37 : virtual boost::asio::io_context& get_io_context() = 0;
38 :
39 : // Apply default headers
40 : void apply_default_headers(const std::shared_ptr<http_request>& request);
41 :
42 : // Create or get connection for request
43 : std::shared_ptr<client_connection> get_or_create_connection(const std::shared_ptr<http_request>& request);
44 :
45 : public:
46 2106 : http_client_base() = default;
47 : virtual ~http_client_base();
48 :
49 : // Configuration setters (fluent API)
50 348 : http_client_base& timeout(std::chrono::seconds t) { timeout_ = t; return *this; }
51 38 : http_client_base& max_redirects(unsigned int max) { max_redirects_ = max; return *this; }
52 48 : http_client_base& follow_redirects(bool follow) { follow_redirects_ = follow; return *this; }
53 22 : http_client_base& user_agent(const std::string& agent) { user_agent_ = agent; return *this; }
54 24 : http_client_base& auto_decompress(bool decompress) { auto_decompress_ = decompress; return *this; }
55 68 : http_client_base& verify_ssl(bool verify) { verify_ssl_ = verify; return *this; }
56 :
57 : // Configuration getters
58 18 : std::chrono::seconds get_timeout() const { return timeout_; }
59 30 : unsigned int get_max_redirects() const { return max_redirects_; }
60 36 : bool get_follow_redirects() const { return follow_redirects_; }
61 18 : const std::string& get_user_agent() const { return user_agent_; }
62 18 : bool get_auto_decompress() const { return auto_decompress_; }
63 22 : bool get_verify_ssl() const { return verify_ssl_; }
64 :
65 : // Request creation
66 : std::shared_ptr<http_request> create_request(method m, const std::string& url);
67 : std::shared_ptr<http_request> create_request(method m, const std::string& url, const std::string& unix_socket);
68 :
69 : // Main HTTP methods - all return awaitable
70 : awaitable<client_response> get(const std::string& url, headers_map headers = {});
71 : awaitable<client_response> post(const std::string& url, std::string body = {},
72 : std::string content_type = "application/json", headers_map headers = {});
73 : awaitable<client_response> post(const std::string& url, const form& form, headers_map headers = {});
74 : awaitable<client_response> put(const std::string& url, std::string body = {},
75 : std::string content_type = "application/json", headers_map headers = {});
76 : awaitable<client_response> patch(const std::string& url, std::string body = {},
77 : std::string content_type = "application/json", headers_map headers = {});
78 : awaitable<client_response> del(const std::string& url, headers_map headers = {});
79 : awaitable<client_response> head(const std::string& url, headers_map headers = {});
80 : awaitable<client_response> options(const std::string& url, headers_map headers = {});
81 :
82 : // Unix socket variants
83 : awaitable<client_response> get(const std::string& url, const std::string& unix_socket, headers_map headers = {});
84 : awaitable<client_response> post(const std::string& url, const std::string& unix_socket,
85 : std::string body = {}, std::string content_type = "application/json",
86 : headers_map headers = {});
87 :
88 : // Generic send with custom request
89 : awaitable<client_response> send(std::shared_ptr<http_request> request);
90 :
91 : // Streaming send - streams response through callback without loading into memory
92 : awaitable<stream_result> send_streaming(std::shared_ptr<http_request> request,
93 : stream_callback callback);
94 :
95 : // Send and get connection back (for upgrades like WebSocket)
96 : awaitable<std::pair<client_response, std::shared_ptr<client_connection>>>
97 : send_with_connection(std::shared_ptr<http_request> request);
98 :
99 : // WebSocket upgrade (simple API)
100 : awaitable<std::optional<websocket_client>> upgrade_websocket(
101 : const std::string& url,
102 : const std::string& subprotocol = "");
103 :
104 : // WebSocket upgrade with custom request/headers (for builder pattern)
105 : awaitable<std::optional<websocket_client>> upgrade_websocket(
106 : std::shared_ptr<http_request> request,
107 : const std::string& subprotocol = "");
108 :
109 : // Connection pool management
110 2 : void clear_connections() { pool_.clear(); }
111 8 : size_t pool_size() const { return pool_.size(); }
112 :
113 : private:
114 : // Internal send with redirect handling
115 : awaitable<client_response> send_with_redirects(std::shared_ptr<http_request> request,
116 : std::shared_ptr<client_connection> connection,
117 : unsigned int redirect_count = 0);
118 :
119 : // Check if URLs have same origin (for security when forwarding headers)
120 : static bool is_same_origin(const std::string& url1, const std::string& url2);
121 : };
122 :
123 : } // namespace thinger::http
124 :
125 : #endif
|