Kurlyk
Loading...
Searching...
No Matches
HttpClient.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _KURLYK_HTTP_CLIENT_HPP_INCLUDED
3#define _KURLYK_HTTP_CLIENT_HPP_INCLUDED
4
7
8namespace kurlyk {
9
13 class HttpClient {
14 public:
15
21
24 HttpClient(const std::string& host) :
25 m_host(host) {
28 }
29
30 HttpClient(const HttpClient&) = delete;
31 void operator=(const HttpClient&) = delete;
32
34 virtual ~HttpClient() {
36 auto& instance = HttpRequestManager::get_instance();
38 instance.remove_limit(m_request.general_rate_limit_id);
39 }
41 instance.remove_limit(m_request.specific_rate_limit_id);
42 }
43 }
44
48 auto promise = std::make_shared<std::promise<void>>();
49 auto future = promise->get_future();
51 try {
52 promise->set_value();
53 } catch (const std::future_error& e) {
54 if (e.code() == std::make_error_condition(std::future_errc::promise_already_satisfied)) {
55 KURLYK_HANDLE_ERROR(e, "Promise already satisfied in HttpClient::request callback");
56 } else {
57 KURLYK_HANDLE_ERROR(e, "Future error in HttpClient::request callback");
58 }
59 } catch (const std::exception& e) {
60 KURLYK_HANDLE_ERROR(e, "Unhandled exception in HttpClient::request callback");
61 } catch (...) {
62 // Unknown fatal error in request callback
63 }
64 });
66 try {
67 future.get();
68 } catch (const std::exception& e) {
69 KURLYK_HANDLE_ERROR(e, "cancel_requests() future.get() failed");
70 }
71 }
72
75 void set_host(const std::string& host) {
76 m_host = host;
77 }
78
81 void set_headers(const kurlyk::Headers& headers) {
82 m_request.headers = headers;
83 }
84
89 long limit_id,
91 auto& instance = HttpRequestManager::get_instance();
92 switch (type) {
95 instance.remove_limit(m_request.general_rate_limit_id);
96 }
97 m_request.general_rate_limit_id = limit_id;
99 break;
102 instance.remove_limit(m_request.specific_rate_limit_id);
103 }
104 m_request.specific_rate_limit_id = limit_id;
106 break;
107 }
108 }
109
115 long limit_id,
117 assign_rate_limit_id(limit_id, type);
118 }
119
125 long requests_per_period,
126 long period_ms,
128 auto& instance = HttpRequestManager::get_instance();
129 switch (type) {
132 instance.remove_limit(m_request.general_rate_limit_id);
133 }
134 m_request.general_rate_limit_id = instance.create_rate_limit(requests_per_period, period_ms);
136 break;
139 instance.remove_limit(m_request.specific_rate_limit_id);
140 }
141 m_request.specific_rate_limit_id = instance.create_rate_limit(requests_per_period, period_ms);
143 break;
144 }
145 }
146
151 long requests_per_minute,
153 long period_ms = 60000; // 1 minute in milliseconds
154 return set_rate_limit(requests_per_minute, period_ms, type);
155 }
156
161 long requests_per_second,
163 long period_ms = 1000; // 1 second in milliseconds
164 return set_rate_limit(requests_per_second, period_ms, type);
165 }
166
173 bool identity = false,
174 bool deflate = false,
175 bool gzip = false,
176 bool brotli = false) {
177 m_request.set_accept_encoding(identity, deflate, gzip, brotli);
178 }
179
182 void set_accept_encoding(const std::string& value) {
183 m_request.accept_encoding = value;
184 }
185
188 void set_accept_language(const std::string& value) {
189 m_request.headers.emplace("Accept-Language", value);
190 }
191
194 void set_content_type(const std::string& value) {
195 m_request.headers.emplace("Content-Type", value);
196 }
197
200 void set_origin(const std::string& value) {
201 m_request.headers.emplace("Origin", value);
202 }
203
206 void set_referer(const std::string& value) {
207 m_request.headers.emplace("Referer", value);
208 }
209
212 void set_dnt(const bool value) {
213 if (value) m_request.headers.emplace("dnt", "1");
214 }
215
218 void set_follow_location(bool value) {
219 m_request.follow_location = value;
220 }
221
224 void set_auto_referer(bool value) {
225 m_request.auto_referer = value;
226 }
227
230 void set_proxy_tunnel(bool value) {
231 m_request.proxy_tunnel = value;
232 }
233
237 void set_head_only(bool value) {
238 m_request.head_only = value;
239 }
240
246 const std::string& ip,
247 int port,
249 m_request.set_proxy(ip, port, type);
250 }
251
259 const std::string& ip,
260 const int port,
261 const std::string& username,
262 const std::string& password,
264 m_request.set_proxy(ip, port, username, password, type);
265 }
266
271 const std::string& username,
272 const std::string& password) {
273 m_request.set_proxy_auth(username, password);
274 }
275
278 void set_proxy_server(const std::string& server) {
279 m_request.set_proxy_server(server);
280 }
281
284 void set_proxy_auth(const std::string& auth) {
285 m_request.set_proxy_auth(auth);
286 }
287
291 m_request.set_proxy_type(type);
292 }
293
297 void set_retry_attempts(long retry_attempts, long retry_delay_ms) {
298 m_request.set_retry_attempts(retry_attempts, retry_delay_ms);
299 }
300
303 void add_valid_status(long status) {
304 m_request.add_valid_status(status);
305 }
306
309 void set_valid_statuses(const std::set<long>& statuses) {
310 m_request.set_valid_statuses(statuses);
311 }
312
315 m_request.clear_valid_statuses();
316 }
317
320 void set_user_agent(const std::string& user_agent) {
321 m_request.set_user_agent(user_agent);
322 }
323
326 void set_cookie(const std::string& cookie) {
327 m_request.set_cookie(cookie);
328 }
329
332 void set_cert_file(const std::string& cert_file) {
333 m_request.set_cert_file(cert_file);
334 }
335
338 void set_ca_file(const std::string& ca_file) {
339 m_request.set_ca_file(ca_file);
340 }
341
344 void set_timeout(long timeout) {
345 m_request.set_timeout(timeout);
346 }
347
350 void set_connect_timeout(long connect_timeout) {
351 m_request.set_connect_timeout(connect_timeout);
352 }
353
356 void set_verbose(bool verbose) {
357 m_request.verbose = verbose;
358 }
359
362 void set_debug_header(bool debug_header) {
363 m_request.debug_header = debug_header;
364 }
365
368 void set_max_redirects(long max_redirects) {
369 m_request.max_redirects = max_redirects;
370 }
371
381 const std::string &method,
382 const std::string& path,
383 const QueryParams &query,
384 const Headers &headers,
385 const std::string &content,
386 HttpResponseCallback callback) {
387# if __cplusplus >= 201402L
388 std::unique_ptr<HttpRequest> request_ptr = std::make_unique<HttpRequest>(m_request);
389# else
390 std::unique_ptr<HttpRequest> request_ptr = std::unique_ptr<HttpRequest>(new HttpRequest(m_request));
391# endif
392 request_ptr->method = method;
393 request_ptr->set_url(m_host, path, query);
394 request_ptr->headers.insert(headers.begin(), headers.end());
395 request_ptr->content = content;
396 return request(std::move(request_ptr), std::move(callback));
397 }
398
409 const std::string &method,
410 const std::string& path,
411 const QueryParams &query,
412 const Headers &headers,
413 const std::string &content,
414 long specific_rate_limit_id,
415 HttpResponseCallback callback) {
416# if __cplusplus >= 201402L
417 std::unique_ptr<HttpRequest> request_ptr = std::make_unique<HttpRequest>(m_request);
418# else
419 std::unique_ptr<HttpRequest> request_ptr = std::unique_ptr<HttpRequest>(new HttpRequest(m_request));
420# endif
421 request_ptr->method = method;
422 request_ptr->set_url(m_host, path, query);
423 request_ptr->headers.insert(headers.begin(), headers.end());
424 request_ptr->content = content;
425
426 // Set the specific rate limit ID for this request
428 HttpRequestManager::get_instance().remove_limit(request_ptr->specific_rate_limit_id);
429 }
430 request_ptr->specific_rate_limit_id = specific_rate_limit_id;
432
433 return request(std::move(request_ptr), std::move(callback));
434 }
435
442 bool get(
443 const std::string& path,
444 const QueryParams& query,
445 const Headers& headers,
446 HttpResponseCallback callback) {
447 return request("GET", path, query, headers, std::string(), std::move(callback));
448 }
449
457 bool post(
458 const std::string& path,
459 const QueryParams& query,
460 const Headers& headers,
461 const std::string& content,
462 HttpResponseCallback callback) {
463 return request("POST", path, query, headers, content, std::move(callback));
464 }
465
473 bool get(
474 const std::string& path,
475 const QueryParams& query,
476 const Headers& headers,
477 long specific_rate_limit_id,
478 HttpResponseCallback callback) {
479 return request("GET", path, query, headers, std::string(), specific_rate_limit_id, std::move(callback));
480 }
481
490 bool post(
491 const std::string& path,
492 const QueryParams& query,
493 const Headers& headers,
494 const std::string& content,
495 long specific_rate_limit_id,
496 HttpResponseCallback callback) {
497 return request("POST", path, query, headers, content, specific_rate_limit_id, std::move(callback));
498 }
499
507 std::future<HttpResponsePtr> request(
508 const std::string& method,
509 const std::string& path,
510 const QueryParams& query,
511 const Headers& headers,
512 const std::string& content) {
513# if __cplusplus >= 201402L
514 auto request_ptr = std::make_unique<HttpRequest>(m_request);
515# else
516 auto request_ptr = std::unique_ptr<HttpRequest>(new HttpRequest(m_request));
517# endif
518 request_ptr->method = method;
519 request_ptr->set_url(m_host, path, query);
520 request_ptr->headers.insert(headers.begin(), headers.end());
521 request_ptr->content = content;
522
523 auto promise = std::make_shared<std::promise<HttpResponsePtr>>();
524 auto future = promise->get_future();
525
526 HttpResponseCallback callback = [promise](HttpResponsePtr response) {
527 safe_set_response(promise, std::move(response));
528 };
529
531 promise, std::move(request_ptr), std::move(callback));
532
533 return future;
534 }
535
544 std::future<HttpResponsePtr> request(
545 const std::string& method,
546 const std::string& path,
547 const QueryParams& query,
548 const Headers& headers,
549 const std::string& content,
550 long specific_rate_limit_id) {
551# if __cplusplus >= 201402L
552 auto request_ptr = std::make_unique<HttpRequest>(m_request);
553# else
554 auto request_ptr = std::unique_ptr<HttpRequest>(new HttpRequest(m_request));
555# endif
556 request_ptr->method = method;
557 request_ptr->set_url(m_host, path, query);
558 request_ptr->headers.insert(headers.begin(), headers.end());
559 request_ptr->content = content;
560
561 // Set the specific rate limit ID for this request
563 HttpRequestManager::get_instance().remove_limit(request_ptr->specific_rate_limit_id);
564 }
565 request_ptr->specific_rate_limit_id = specific_rate_limit_id;
567
568 auto promise = std::make_shared<std::promise<HttpResponsePtr>>();
569 auto future = promise->get_future();
570
571 HttpResponseCallback callback = [promise](HttpResponsePtr response) {
572 safe_set_response(promise, std::move(response));
573 };
574
576 promise, std::move(request_ptr), std::move(callback));
577
578 return future;
579 }
580
586 std::future<HttpResponsePtr> get(
587 const std::string& path,
588 const QueryParams& query,
589 const Headers& headers) {
590 return request("GET", path, query, headers, std::string());
591 }
592
599 std::future<HttpResponsePtr> post(
600 const std::string& path,
601 const QueryParams& query,
602 const Headers& headers,
603 const std::string& content) {
604 return request("POST", path, query, headers, content);
605 }
606
613 std::future<HttpResponsePtr> get(
614 const std::string& path,
615 const QueryParams& query,
616 const Headers& headers,
617 long specific_rate_limit_id) {
618 return request("GET", path, query, headers, std::string(), specific_rate_limit_id);
619 }
620
628 std::future<HttpResponsePtr> post(
629 const std::string& path,
630 const QueryParams& query,
631 const Headers& headers,
632 const std::string& content,
633 long specific_rate_limit_id) {
634 return request("POST", path, query, headers, content, specific_rate_limit_id);
635 }
636
637 private:
639 std::string m_host;
642
648 std::unique_ptr<HttpRequest> request_ptr,
649 HttpResponseCallback callback) {
650 const bool status = HttpRequestManager::get_instance().add_request(std::move(request_ptr), std::move(callback));
652 return status;
653 }
654
658 static void safe_set_response(
659 std::shared_ptr<std::promise<HttpResponsePtr>> promise,
660 HttpResponsePtr response) {
661 if (!response || !response->ready) return;
662 try {
663 promise->set_value(std::move(response));
664 } catch (const std::future_error& e) {
665 if (e.code() == std::make_error_condition(std::future_errc::promise_already_satisfied)) {
666 KURLYK_HANDLE_ERROR(e, "Promise already satisfied in HttpClient::request callback");
667 } else {
668 KURLYK_HANDLE_ERROR(e, "Future error in HttpClient::request callback");
669 }
670 } catch (const std::exception& e) {
671 KURLYK_HANDLE_ERROR(e, "Unhandled exception in HttpClient::request callback");
672 } catch (...) {
673 // Unknown fatal error in request callback
674 }
675 }
676
682 std::shared_ptr<std::promise<HttpResponsePtr>> promise,
683 std::unique_ptr<HttpRequest> request_ptr,
684 HttpResponseCallback callback) {
685 bool exception_set = false;
686 try {
687 if (!request(std::move(request_ptr), std::move(callback))) {
688 promise->set_exception(std::make_exception_ptr(
689 std::runtime_error("Failed to add request to RequestManager")));
690 exception_set = true;
691 }
692 } catch (const std::exception& e) {
693 KURLYK_HANDLE_ERROR(e, "Exception while submitting request");
694 if (!exception_set) {
695 try {
696 promise->set_exception(std::current_exception());
697 } catch (...) {} // fallback
698 }
699 } catch (...) {
700 // Unknown fatal error while submitting request
701 if (!exception_set) {
702 try {
703 promise->set_exception(std::current_exception());
704 } catch (...) {} // fallback
705 }
706 }
707 }
708
710 static void ensure_initialized() {
711 static bool is_initialized = false;
712 if (!is_initialized) {
713 is_initialized = true;
716 }
717 }
718
719 }; // HttpClient
720
721}; // namespace kurlyk
722
723#endif // _KURLYK_HTTP_CLIENT_HPP_INCLUDED
#define KURLYK_HANDLE_ERROR(e, msg)
bool get(const std::string &path, const QueryParams &query, const Headers &headers, long specific_rate_limit_id, HttpResponseCallback callback)
Sends a GET request with a specific rate limit ID.
bool request(std::unique_ptr< HttpRequest > request_ptr, HttpResponseCallback callback)
Adds the request to the request manager and notifies the worker to process it.
void set_proxy_auth(const std::string &auth)
Sets the proxy authentication credentials.
HttpRequest m_request
The request object used for configuring and sending requests.
void set_valid_statuses(const std::set< long > &statuses)
Replaces all valid HTTP status codes for the request.
void set_head_only(bool value)
Configures whether to send only the HTTP headers (HEAD request).
void set_rate_limit(long requests_per_period, long period_ms, RateLimitType type=RateLimitType::RL_GENERAL)
Sets the rate limit for HTTP requests.
void set_verbose(bool verbose)
Enables or disables verbose output.
void set_accept_language(const std::string &value)
Sets the Accept-Language header value.
std::future< HttpResponsePtr > request(const std::string &method, const std::string &path, const QueryParams &query, const Headers &headers, const std::string &content, long specific_rate_limit_id)
Sends an HTTP request with a specified method, path, specific rate limit ID and parameters,...
std::string m_host
The base host URL for the HTTP client.
void set_max_redirects(long max_redirects)
Sets the maximum number of redirects for the client.
void set_rate_limit_id(long limit_id, RateLimitType type=RateLimitType::RL_GENERAL)
Sets the rate limit ID for the HTTP request (alias for assign_rate_limit_id).
std::future< HttpResponsePtr > post(const std::string &path, const QueryParams &query, const Headers &headers, const std::string &content, long specific_rate_limit_id)
Sends an asynchronous POST request with a specific rate limit ID and returns a future with the respon...
void set_content_type(const std::string &value)
Sets the Content-Type header value.
bool post(const std::string &path, const QueryParams &query, const Headers &headers, const std::string &content, HttpResponseCallback callback)
Sends a POST request.
bool request(const std::string &method, const std::string &path, const QueryParams &query, const Headers &headers, const std::string &content, HttpResponseCallback callback)
Sends an HTTP request with the specified method, path, and parameters.
HttpClient(const HttpClient &)=delete
void add_valid_status(long status)
Adds a valid HTTP status code to the request.
HttpClient()
Default constructor for HttpClient.
void set_ca_file(const std::string &ca_file)
Sets the path to the CA certificate file.
void clear_valid_statuses()
Clears the set of valid HTTP status codes for the request.
std::future< HttpResponsePtr > request(const std::string &method, const std::string &path, const QueryParams &query, const Headers &headers, const std::string &content)
Sends an HTTP request with a specified method, path, and parameters, and returns a future with the re...
void set_proxy(const std::string &ip, int port, ProxyType type=ProxyType::PROXY_HTTP)
Sets the proxy server address.
void set_rate_limit_rps(long requests_per_second, RateLimitType type=RateLimitType::RL_GENERAL)
Sets the rate limit based on requests per second (RPS).
void safe_submit_request(std::shared_ptr< std::promise< HttpResponsePtr > > promise, std::unique_ptr< HttpRequest > request_ptr, HttpResponseCallback callback)
Submits a request and propagates any failure to the provided promise.
void set_cert_file(const std::string &cert_file)
Sets the client certificate file path.
static void safe_set_response(std::shared_ptr< std::promise< HttpResponsePtr > > promise, HttpResponsePtr response)
Safely sets the response value on the given promise.
void set_auto_referer(bool value)
Configures whether to automatically set the Referer header on redirects.
void set_connect_timeout(long connect_timeout)
Sets the connection timeout duration.
bool is_general_limit_owned
Flag indicating if the client owns the general rate limit.
void assign_rate_limit_id(long limit_id, RateLimitType type=RateLimitType::RL_GENERAL)
Assigns an existing rate limit to the HTTP request.
void set_retry_attempts(long retry_attempts, long retry_delay_ms)
Sets retry attempts and delay between retries for HTTP requests.
void set_timeout(long timeout)
Sets the timeout duration for HTTP requests.
void set_proxy_server(const std::string &server)
Sets the proxy server address.
std::future< HttpResponsePtr > get(const std::string &path, const QueryParams &query, const Headers &headers)
Sends a GET request asynchronously and returns a future with the response.
void set_referer(const std::string &value)
Sets the Referer header value.
void set_follow_location(bool value)
Configures whether to follow redirects automatically.
void set_proxy(const std::string &ip, const int port, const std::string &username, const std::string &password, ProxyType type=ProxyType::PROXY_HTTP)
Sets the proxy server address with authentication details.
void set_cookie(const std::string &cookie)
Sets the cookie string for HTTP requests.
std::future< HttpResponsePtr > get(const std::string &path, const QueryParams &query, const Headers &headers, long specific_rate_limit_id)
Sends an asynchronous GET request with a specific rate limit ID and returns a future with the respons...
std::future< HttpResponsePtr > post(const std::string &path, const QueryParams &query, const Headers &headers, const std::string &content)
Sends a POST request asynchronously and returns a future with the response.
void set_accept_encoding(const std::string &value)
Sets a custom Accept-Encoding header value.
void set_user_agent(const std::string &user_agent)
Sets the User-Agent header.
void set_origin(const std::string &value)
Sets the Origin header value.
HttpClient(const std::string &host)
Constructs an HttpClient with the specified host.
void set_proxy_tunnel(bool value)
Configures whether to use a tunneling proxy for HTTP requests.
bool post(const std::string &path, const QueryParams &query, const Headers &headers, const std::string &content, long specific_rate_limit_id, HttpResponseCallback callback)
Sends a POST request with a specific rate limit ID.
static void ensure_initialized()
Ensures that the network worker and request manager are initialized.
void set_host(const std::string &host)
Sets the host URL for the HTTP client.
bool get(const std::string &path, const QueryParams &query, const Headers &headers, HttpResponseCallback callback)
Sends a GET request.
void set_debug_header(bool debug_header)
Enables or disables debug headers.
void set_proxy_auth(const std::string &username, const std::string &password)
Sets proxy authentication credentials.
void set_headers(const kurlyk::Headers &headers)
Sets the default headers for HTTP requests.
void set_dnt(const bool value)
Sets the Do Not Track (DNT) header value.
bool is_specific_limit_owned
Flag indicating if the client owns the specific rate limit.
void set_rate_limit_rpm(long requests_per_minute, RateLimitType type=RateLimitType::RL_GENERAL)
Sets the rate limit based on requests per minute (RPM).
bool request(const std::string &method, const std::string &path, const QueryParams &query, const Headers &headers, const std::string &content, long specific_rate_limit_id, HttpResponseCallback callback)
Sends an HTTP request with the specified method, path, parameters, and specific rate limit ID.
void operator=(const HttpClient &)=delete
virtual ~HttpClient()
Destructor for HttpClient.
void set_accept_encoding(bool identity=false, bool deflate=false, bool gzip=false, bool brotli=false)
Sets the Accept-Encoding header.
void set_proxy_type(ProxyType type)
Sets the proxy type.
void cancel_requests()
Cancels the active request associated with this client and waits for its completion.
const bool add_request(std::unique_ptr< HttpRequest > request_ptr, HttpResponseCallback callback)
Adds a new HTTP request to the manager.
void cancel_request_by_id(uint64_t request_id, std::function< void()> callback)
Cancels a request by its unique identifier.
static HttpRequestManager & get_instance()
Get the singleton instance of HttpRequestManager.
bool remove_limit(long limit_id)
Removes an existing rate limit with the specified identifier.
uint64_t generate_request_id()
Generates a new unique request ID.
Represents an HTTP request.
void notify()
Notifies the worker to begin processing requests or tasks.
static NetworkWorker & get_instance()
Get the singleton instance of NetworkWorker.
void start(const bool use_async)
Starts the worker thread for asynchronous task processing.
#define KURLYK_AUTO_INIT_USE_ASYNC
Determines whether the NetworkWorker runs in a background thread during automatic initialization.
Definition kurlyk.hpp:20
Primary namespace for the Kurlyk library, encompassing initialization, request management,...
std::function< void(HttpResponsePtr response)> HttpResponseCallback
Type definition for the callback function used to handle HTTP responses.
RateLimitType
Defines rate limit scope categories.
Definition enums.hpp:24
@ RL_GENERAL
Applies globally to all requests.
Definition enums.hpp:25
@ RL_SPECIFIC
Applies to specific client/request.
Definition enums.hpp:26
ProxyType
Enumeration of supported proxy types compatible with libcurl.
Definition enums.hpp:12
@ PROXY_HTTP
HTTP proxy.
Definition enums.hpp:13
std::unique_ptr< HttpResponse > HttpResponsePtr
A unique pointer to an HttpResponse object for memory management.
utils::CaseInsensitiveMultimap Headers
Alias for HTTP headers, providing a case-insensitive unordered multimap.
utils::CaseInsensitiveMultimap QueryParams
Alias for query parameters in HTTP requests, stored case-insensitively.