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 809 : 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 : std::string unix_socket_;
33 : size_t max_content_size_{8 * 1048576}; // 8 MB default; buffered-mode responses above this are rejected
34 :
35 : // Connection pool for keep-alive
36 : connection_pool pool_;
37 :
38 : // Abstract method - each derived class provides its own io_context
39 : virtual boost::asio::io_context& get_io_context() = 0;
40 :
41 : // Apply default headers
42 : void apply_default_headers(const std::shared_ptr<http_request>& request);
43 :
44 : // Create or get connection for request
45 : std::shared_ptr<client_connection> get_or_create_connection(const std::shared_ptr<http_request>& request);
46 :
47 : public:
48 2427 : http_client_base() = default;
49 : virtual ~http_client_base();
50 :
51 : // Configuration setters (fluent API)
52 401 : http_client_base& timeout(std::chrono::seconds t) { timeout_ = t; return *this; }
53 38 : http_client_base& max_redirects(unsigned int max) { max_redirects_ = max; return *this; }
54 48 : http_client_base& follow_redirects(bool follow) { follow_redirects_ = follow; return *this; }
55 22 : http_client_base& user_agent(const std::string& agent) { user_agent_ = agent; return *this; }
56 24 : http_client_base& auto_decompress(bool decompress) { auto_decompress_ = decompress; return *this; }
57 68 : http_client_base& verify_ssl(bool verify) { verify_ssl_ = verify; return *this; }
58 45 : http_client_base& unix_socket(const std::string& path) { unix_socket_ = path; return *this; }
59 : http_client_base& max_content_size(size_t size) { max_content_size_ = size; return *this; }
60 :
61 : // Configuration getters
62 18 : std::chrono::seconds get_timeout() const { return timeout_; }
63 30 : unsigned int get_max_redirects() const { return max_redirects_; }
64 36 : bool get_follow_redirects() const { return follow_redirects_; }
65 18 : const std::string& get_user_agent() const { return user_agent_; }
66 18 : bool get_auto_decompress() const { return auto_decompress_; }
67 22 : bool get_verify_ssl() const { return verify_ssl_; }
68 : size_t get_max_content_size() const { return max_content_size_; }
69 :
70 : // Request creation
71 : std::shared_ptr<http_request> create_request(method m, const std::string& url);
72 :
73 : // Main HTTP methods - all return awaitable
74 : awaitable<client_response> get(const std::string& url, headers_map headers = {});
75 : awaitable<client_response> post(const std::string& url, std::string body = {},
76 : std::string content_type = "application/json", headers_map headers = {});
77 : awaitable<client_response> post(const std::string& url, const form& form, headers_map headers = {});
78 : awaitable<client_response> put(const std::string& url, std::string body = {},
79 : std::string content_type = "application/json", headers_map headers = {});
80 : awaitable<client_response> patch(const std::string& url, std::string body = {},
81 : std::string content_type = "application/json", headers_map headers = {});
82 : awaitable<client_response> del(const std::string& url, headers_map headers = {});
83 : awaitable<client_response> head(const std::string& url, headers_map headers = {});
84 : awaitable<client_response> options(const std::string& url, headers_map headers = {});
85 :
86 : // Generic send with custom request
87 : awaitable<client_response> send(std::shared_ptr<http_request> request);
88 :
89 : // Streaming send - streams response through callback without loading into memory
90 : awaitable<stream_result> send_streaming(std::shared_ptr<http_request> request,
91 : stream_callback callback);
92 :
93 : // Send and get connection back (for upgrades like WebSocket)
94 : awaitable<std::pair<client_response, std::shared_ptr<client_connection>>>
95 : send_with_connection(std::shared_ptr<http_request> request);
96 :
97 : // WebSocket upgrade (simple API)
98 : awaitable<std::optional<websocket_client>> upgrade_websocket(
99 : const std::string& url,
100 : const std::string& subprotocol = "");
101 :
102 : // WebSocket upgrade with custom request/headers (for builder pattern)
103 : awaitable<std::optional<websocket_client>> upgrade_websocket(
104 : std::shared_ptr<http_request> request,
105 : const std::string& subprotocol = "");
106 :
107 : // Connection pool management
108 2 : void clear_connections() { pool_.clear(); }
109 8 : size_t pool_size() const { return pool_.size(); }
110 :
111 : private:
112 : // Internal send with redirect handling
113 : awaitable<client_response> send_with_redirects(std::shared_ptr<http_request> request,
114 : std::shared_ptr<client_connection> connection,
115 : unsigned int redirect_count = 0);
116 :
117 : // Check if URLs have same origin (for security when forwarding headers)
118 : static bool is_same_origin(const std::string& url1, const std::string& url2);
119 : };
120 :
121 : } // namespace thinger::http
122 :
123 : #endif
|