Kurlyk
Loading...
Searching...
No Matches
HttpRequestManager.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef KURLYK_HEADER_KURLYK_HTTP_HTTP_REQUEST_MANAGER_HPP_INCLUDED
3#define KURLYK_HEADER_KURLYK_HTTP_HTTP_REQUEST_MANAGER_HPP_INCLUDED
4
7
8#include <type_traits>
9
16
17
18namespace kurlyk {
19
24 public:
25
29 static HttpRequestManager* instance = new HttpRequestManager();
30 return *instance;
31 }
32
38 std::unique_ptr<HttpRequest> request_ptr,
39 HttpResponseCallback callback) {
40 return submit_request(std::move(request_ptr), std::move(callback)).accepted;
41 }
42
48 std::unique_ptr<HttpRequest> request_ptr,
49 HttpResponseCallback callback) {
50 std::lock_guard<std::mutex> lock(m_mutex);
51 if (m_shutdown) {
53 }
54
55 const std::size_t queue_limit = m_max_pending_requests.load();
56 if (queue_limit && m_pending_requests.size() >= queue_limit) {
58 }
59
60# if __cplusplus >= 201402L
61 auto context = std::make_unique<HttpRequestContext>(std::move(request_ptr), std::move(callback));
62# else
63 auto context = std::unique_ptr<HttpRequestContext>(
64 new HttpRequestContext(std::move(request_ptr), std::move(callback)));
65# endif
66 context->in_flight_token = m_next_in_flight_token.fetch_add(1, std::memory_order_relaxed);
67 const uint64_t group_id = context->request ? context->request->group_id : 0;
68 if (group_id != 0) {
69 context->on_group_complete = [this, group_id]() {
70 complete_group_request(group_id);
71 };
72 ++m_group_request_counts[group_id];
73 }
74 try {
75 m_pending_requests.push_back(std::move(context));
76 } catch (...) {
77 if (group_id != 0) {
78 auto count_it = m_group_request_counts.find(group_id);
79 if (count_it != m_group_request_counts.end()) {
80 if (count_it->second > 1) {
81 --count_it->second;
82 } else {
83 m_group_request_counts.erase(count_it);
84 }
85 }
86 }
87 throw;
88 }
89 return SubmitResult{true, std::error_code()};
90 }
91
98 HttpRateLimitHandlePtr create_rate_limit(long requests_per_period, long period_ms, bool sequential = false) {
99 return m_rate_limiter.create_limit_handle(requests_per_period, period_ms, sequential);
100 }
101
105 return m_rate_limiter.get_limit(limit_id);
106 }
107
111 bool remove_limit(long limit_id) {
112 return m_rate_limiter.remove_limit(limit_id);
113 }
114
118 return m_rate_limiter.remove_limit(limit);
119 }
120
129 const HttpRateLimitHandlePtr& general_limit,
130 const HttpRateLimitHandlePtr& specific_limit,
131 uint64_t in_flight_token,
132 const std::string& general_key,
133 const std::string& specific_key) {
135 general_limit, specific_limit, in_flight_token, general_key, specific_key);
136 }
137
145 const HttpRateLimitHandlePtr& general_limit,
146 const HttpRateLimitHandlePtr& specific_limit,
147 uint64_t in_flight_token,
148 const std::string& general_key,
149 const std::string& specific_key) {
151 general_limit, specific_limit, in_flight_token, general_key, specific_key);
152 }
153
161 template<typename Duration = std::chrono::milliseconds>
163 const HttpRateLimitHandlePtr& general_limit,
164 const HttpRateLimitHandlePtr& specific_limit,
165 const std::string& general_key,
166 const std::string& specific_key
167 ) {
169 general_limit, specific_limit, general_key, specific_key);
170 }
171
175 return m_request_id_counter++;
176 }
177
180 uint64_t generate_group_id() {
181 return m_group_id_counter++;
182 }
183
189
192 std::size_t max_pending_requests() const {
193 return m_max_pending_requests.load();
194 }
195
199 bool has_requests_by_group_id(uint64_t group_id) const {
200 return group_request_count(group_id) != 0;
201 }
202
206 std::size_t group_request_count(uint64_t group_id) const {
207 std::lock_guard<std::mutex> lock(m_mutex);
208 return group_request_count_unlocked(group_id);
209 }
210
216 uint64_t wait_requests_by_group_id(uint64_t group_id, std::function<void()> callback) {
217 bool invoke_now = false;
218 uint64_t waiter_id = 0;
219 {
220 std::lock_guard<std::mutex> lock(m_mutex);
221 if (m_shutdown || group_id == 0 || group_request_count_unlocked(group_id) == 0) {
222 invoke_now = true;
223 } else {
224 waiter_id = m_waiter_id_counter++;
225 m_group_waiters[group_id].push_back(
226 GroupWaiter{waiter_id, std::move(callback)});
227 }
228 }
229
230 if (invoke_now && callback) {
231 callback();
232 }
233 return waiter_id;
234 }
235
240 bool cancel_wait_requests_by_group_id(uint64_t group_id, uint64_t waiter_id) {
241 if (group_id == 0 || waiter_id == 0) return false;
242
243 std::lock_guard<std::mutex> lock(m_mutex);
244 auto group_it = m_group_waiters.find(group_id);
245 if (group_it == m_group_waiters.end()) return false;
246
247 auto& waiters = group_it->second;
248 for (auto waiter_it = waiters.begin(); waiter_it != waiters.end(); ++waiter_it) {
249 if (waiter_it->id != waiter_id) continue;
250 waiters.erase(waiter_it);
251 if (waiters.empty()) m_group_waiters.erase(group_it);
252 return true;
253 }
254 return false;
255 }
256
260 void cancel_request_by_id(uint64_t request_id, std::function<void()> callback) {
261 if (m_shutdown || request_id == 0) {
262 if (callback) callback();
263 return;
264 }
265 std::lock_guard<std::mutex> lock(m_mutex);
266 m_requests_to_cancel_by_id[request_id].push_back(std::move(callback));
267 }
268
272 void cancel_requests_by_group_id(uint64_t group_id, std::function<void()> callback) {
273 if (m_shutdown || group_id == 0) {
274 if (callback) callback();
275 return;
276 }
277 bool invoke_now = false;
278 {
279 std::lock_guard<std::mutex> lock(m_mutex);
280 if (group_request_count_unlocked(group_id) == 0) {
281 invoke_now = true;
282 } else {
283 m_groups_to_cancel[group_id].push_back(std::move(callback));
284 }
285 }
286 if (invoke_now && callback) {
287 callback();
288 }
289 }
290
305
308 void shutdown() override {
309 if (m_shutdown.exchange(true)) {
311 return;
312 }
317 }
318
321 bool is_loaded() const override {
322 std::lock_guard<std::mutex> lock(m_mutex);
323 return
324 !m_pending_requests.empty() ||
325 !m_failed_requests.empty() ||
326 !m_active_request_batches.empty() ||
327 !m_group_request_counts.empty() ||
329 !m_groups_to_cancel.empty();
330 }
331
332 private:
333 mutable std::mutex m_mutex;
334 std::list<std::unique_ptr<HttpRequestContext>> m_pending_requests;
335 std::list<std::unique_ptr<HttpRequestContext>> m_failed_requests;
336 std::list<std::unique_ptr<HttpBatchRequestHandler>> m_active_request_batches;
337 using callback_list_t = std::list<std::function<void()>>;
338 using cancel_map_t = std::unordered_map<uint64_t, callback_list_t>;
339 struct GroupWaiter {
340 uint64_t id;
341 std::function<void()> callback;
342 };
343 using waiter_list_t = std::list<GroupWaiter>;
344 using waiter_map_t = std::unordered_map<uint64_t, waiter_list_t>;
348 std::unordered_map<uint64_t, std::size_t> m_group_request_counts;
350 std::atomic<uint64_t> m_next_in_flight_token{1};
351 std::atomic<uint64_t> m_request_id_counter = ATOMIC_VAR_INIT(1);
352 std::atomic<uint64_t> m_group_id_counter = ATOMIC_VAR_INIT(1);
353 uint64_t m_waiter_id_counter = 1;
354 std::atomic<bool> m_shutdown = ATOMIC_VAR_INIT(false);
355 std::atomic<std::size_t> m_max_pending_requests = ATOMIC_VAR_INIT(0);
356
357 std::size_t group_request_count_unlocked(uint64_t group_id) const {
358 if (group_id == 0) return 0;
359
360 auto it = m_group_request_counts.find(group_id);
361 return it == m_group_request_counts.end() ? 0 : it->second;
362 }
363
365 waiter_map_t ready_waiters;
366 {
367 std::lock_guard<std::mutex> lock(m_mutex);
368 auto it = m_group_waiters.begin();
369 while (it != m_group_waiters.end()) {
370 if (group_request_count_unlocked(it->first) != 0) {
371 ++it;
372 continue;
373 }
374 ready_waiters.emplace(it->first, std::move(it->second));
375 it = m_group_waiters.erase(it);
376 }
377 }
378
379 for (const auto& item : ready_waiters) {
380 for (const auto& waiter : item.second) {
381 if (waiter.callback) waiter.callback();
382 }
383 }
384 }
385
387 waiter_map_t waiters;
388 {
389 std::lock_guard<std::mutex> lock(m_mutex);
390 waiters = std::move(m_group_waiters);
391 m_group_waiters.clear();
392 }
393
394 for (const auto& item : waiters) {
395 for (const auto& waiter : item.second) {
396 if (waiter.callback) waiter.callback();
397 }
398 }
399 }
400
401 void complete_group_request(uint64_t group_id) {
402 if (group_id == 0) return;
403
404 waiter_list_t ready_waiters;
405 {
406 std::lock_guard<std::mutex> lock(m_mutex);
407 auto count_it = m_group_request_counts.find(group_id);
408 if (count_it == m_group_request_counts.end()) return;
409 if (count_it->second > 1) {
410 --count_it->second;
411 } else {
412 m_group_request_counts.erase(count_it);
413 auto waiter_it = m_group_waiters.find(group_id);
414 if (waiter_it != m_group_waiters.end()) {
415 ready_waiters = std::move(waiter_it->second);
416 m_group_waiters.erase(waiter_it);
417 }
418 }
419 }
420
421 for (const auto& waiter : ready_waiters) {
422 if (waiter.callback) waiter.callback();
423 }
424 }
425
428 std::unique_lock<std::mutex> lock(m_mutex);
429 if (m_pending_requests.empty()) return;
430
431 std::vector<std::unique_ptr<HttpRequestContext>> pending_request;
432 std::vector<std::unique_ptr<HttpRequestContext>> failed_requests;
433
434 auto it = m_pending_requests.begin();
435 while (it != m_pending_requests.end()) {
436 auto& context = *it;
437 auto& request = context->request;
438 // Check if the request is valid.
439 if (!request) {
440 failed_requests.push_back(std::move(context));
441 it = m_pending_requests.erase(it);
442 continue;
443 }
444
445 // Set up completion callback before allow_request so it is armed
446 // even if an exception occurs after the token is committed.
447 auto general_limit = request->general_rate_limit;
448 auto specific_limit = request->specific_rate_limit;
449 uint64_t token = context->in_flight_token;
450 auto general_key = request->general_rate_limit_key;
451 auto specific_key = request->specific_rate_limit_key;
452
453 // Preserve any previous on_complete so a retry that already owns
454 // a sequential token does not lose its cleanup callback.
455 auto old_on_complete = std::move(context->on_complete);
456 context->on_complete = [this, general_limit, specific_limit, token, general_key, specific_key]() {
457 m_rate_limiter.release_request(general_limit, specific_limit, token, general_key, specific_key);
458 };
459
460 // Check if the request is allowed by the rate limiter.
461 const bool allowed = m_rate_limiter.allow_request(
462 general_limit,
463 specific_limit,
464 token,
465 request->general_rate_limit_key,
466 request->specific_rate_limit_key);
467 if (!allowed) {
468 context->on_complete = std::move(old_on_complete);
469 ++it;
470 continue;
471 }
472
473 pending_request.push_back(std::move(context));
474 it = m_pending_requests.erase(it);
475 }
476 lock.unlock();
477
478 // Handle failed requests by calling their callback with a 400 status.
479 if (!failed_requests.empty()) {
480 for (const auto &context : failed_requests) {
481# if __cplusplus >= 201402L
482 auto response = std::make_unique<HttpResponse>();
483# else
484 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
485# endif
486 const long BAD_REQUEST = 400;
487 response->error_code = utils::make_error_code(CURLE_OK);
488 response->status_code = BAD_REQUEST;
489 response->ready = true;
490 context->invoke_final_callback(std::move(response));
491 }
492 failed_requests.clear();
493 }
494
495 // If there are ready requests, create a new HttpBatchRequestHandler to manage them.
496 if (pending_request.empty()) return;
497 try {
498# if __cplusplus >= 201402L
499 m_active_request_batches.push_back(std::make_unique<HttpBatchRequestHandler>(pending_request));
500# else
501 m_active_request_batches.push_back(std::unique_ptr<HttpBatchRequestHandler>(new HttpBatchRequestHandler(pending_request)));
502# endif
503 } catch (...) {
504 for (auto& context : pending_request) {
505 if (!context) continue;
506# if __cplusplus >= 201402L
507 auto response = std::make_unique<HttpResponse>();
508# else
509 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
510# endif
512 response->status_code = 499; // Client closed request
513 response->ready = true;
514 context->invoke_final_callback(std::move(response));
515 }
516 return;
517 }
518 }
519
522 auto it = m_active_request_batches.begin();
523 while (it != m_active_request_batches.end()) {
524 auto& request = *it;
525 if (!request->process()) {
526 ++it;
527 continue;
528 }
529 auto failed_requests = request->extract_failed_requests();
530 it = m_active_request_batches.erase(it);
531 for (auto& request : failed_requests) {
532 m_failed_requests.push_back(std::move(request));
533 }
534 }
535 }
536
539 auto it = m_failed_requests.begin();
540 while (it != m_failed_requests.end()) {
541 auto& request_context = *it;
542 if (!request_context || !request_context->request) {
543 it = m_failed_requests.erase(it);
544 continue;
545 }
546
547 const auto now = std::chrono::steady_clock::now();
548 const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - request_context->start_time);
549 const auto& retry_delay_ms = request_context->request->retry_delay_ms;
550 if (duration.count() >= retry_delay_ms) {
551 std::unique_lock<std::mutex> lock(m_mutex);
552 m_pending_requests.push_back(std::move(request_context));
553 lock.unlock();
554 it = m_failed_requests.erase(it);
555 continue;
556 }
557 ++it;
558 }
559 }
560
563 std::unique_lock<std::mutex> lock(m_mutex);
564 if (m_requests_to_cancel_by_id.empty() && m_groups_to_cancel.empty()) return;
565
566 auto requests_to_cancel = std::move(m_requests_to_cancel_by_id);
567 auto groups_to_cancel = std::move(m_groups_to_cancel);
569 m_groups_to_cancel.clear();
570
571 std::list<std::unique_ptr<HttpRequestContext>> canceled_pending_requests;
572 auto pending_it = m_pending_requests.begin();
573 while (pending_it != m_pending_requests.end()) {
574 const auto& ctx = *pending_it;
575 if (!matches_cancel(ctx, requests_to_cancel, groups_to_cancel)) {
576 ++pending_it;
577 continue;
578 }
579 canceled_pending_requests.push_back(std::move(*pending_it));
580 pending_it = m_pending_requests.erase(pending_it);
581 }
582 lock.unlock();
583
584 for (const auto& request_context : canceled_pending_requests) {
585 request_context->invoke_final_callback(make_cancelled_response());
586 }
587
588 auto failed_it = m_failed_requests.begin();
589 while (failed_it != m_failed_requests.end()) {
590 const auto& request_context = *failed_it;
591 if (!matches_cancel(request_context, requests_to_cancel, groups_to_cancel)) {
592 ++failed_it;
593 continue;
594 }
595 request_context->invoke_final_callback(make_cancelled_response());
596 failed_it = m_failed_requests.erase(failed_it);
597 }
598
599 for (const auto &handler : m_active_request_batches) {
600 handler->cancel_requests(requests_to_cancel, groups_to_cancel);
601 }
602
603 invoke_cancel_callbacks(requests_to_cancel);
604 invoke_cancel_callbacks(groups_to_cancel);
605 }
606
607 static bool matches_cancel(
608 const std::unique_ptr<HttpRequestContext>& ctx,
609 const cancel_map_t& requests_to_cancel,
610 const cancel_map_t& groups_to_cancel) {
611 if (!ctx || !ctx->request) return false;
612 const auto request_id = ctx->request->request_id;
613 const auto group_id = ctx->request->group_id;
614 return
615 (request_id != 0 && requests_to_cancel.count(request_id) > 0) ||
616 (group_id != 0 && groups_to_cancel.count(group_id) > 0);
617 }
618
620# if __cplusplus >= 201402L
621 auto response = std::make_unique<HttpResponse>();
622# else
623 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
624# endif
626 response->status_code = 499;
627 response->ready = true;
628 return response;
629 }
630
631 static void invoke_cancel_callbacks(const cancel_map_t& requests_to_cancel) {
632 for (const auto& request : requests_to_cancel) {
633 for (const auto& callback : request.second) {
634 if (callback) callback();
635 }
636 }
637 }
638
641 std::unique_lock<std::mutex> lock(m_mutex);
642 auto pending_requests = std::move(m_pending_requests);
643 auto failed_requests = std::move(m_failed_requests);
644 m_pending_requests.clear();
645 m_failed_requests.clear();
646 lock.unlock();
647
648 for (const auto &request_context : pending_requests) {
649# if __cplusplus >= 201402L
650 auto response = std::make_unique<HttpResponse>();
651# else
652 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
653# endif
654 const long CANCELED_REQUEST_CODE = 499;
655 response->error_code = utils::make_error_code(CURLE_OK);
656 response->status_code = CANCELED_REQUEST_CODE;
657 response->ready = true;
658 request_context->invoke_final_callback(std::move(response));
659 }
660 for (const auto &request_context : failed_requests) {
661# if __cplusplus >= 201402L
662 auto response = std::make_unique<HttpResponse>();
663# else
664 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
665# endif
666 const long CANCELED_REQUEST_CODE = 499;
667 response->error_code = utils::make_error_code(CURLE_OK);
668 response->status_code = CANCELED_REQUEST_CODE;
669 response->ready = true;
670 request_context->invoke_final_callback(std::move(response));
671 }
672 }
673
676 curl_global_init(CURL_GLOBAL_ALL);
677 }
678
681 curl_global_cleanup();
682 }
683
686
689
690 }; // HttpRequestManager
691
692}; // namespace kurlyk
693
694#endif // KURLYK_HEADER_KURLYK_HTTP_HTTP_REQUEST_MANAGER_HPP_INCLUDED
Manages multiple asynchronous HTTP requests using libcurl's multi interface.
Defines the RateLimitDelay result type for time-until-allowed queries.
Defines RAII handle for keeping HTTP rate limits alive.
Defines the HttpRateLimiter class for managing rate limits on HTTP requests.
Defines the HttpRequestContext class for managing HTTP request context, including retries and timing.
HttpBatchRequestHandler(std::vector< std::unique_ptr< HttpRequestContext > > &context_list)
Constructs a handler for managing multiple HTTP requests asynchronously.
Manages rate limits for HTTP requests.
bool remove_limit(long limit_id)
Releases manager-owned handle for the specified limit ID.
HttpRateLimitHandlePtr get_limit(long limit_id)
Returns manager-owned handle by ID.
HttpRateLimitHandlePtr create_limit_handle(long requests_per_period, long period_ms, bool sequential=false)
Creates a new rate limit and returns its RAII handle.
void release_request(const HttpRateLimitHandlePtr &general_limit, const HttpRateLimitHandlePtr &specific_limit, uint64_t in_flight_token, const std::string &general_key, const std::string &specific_key)
Releases in-flight tokens for sequential rate limits.
bool allow_request(const HttpRateLimitHandlePtr &general_limit, const HttpRateLimitHandlePtr &specific_limit, uint64_t in_flight_token, const std::string &general_key, const std::string &specific_key)
Checks if a request is allowed by two optional rate-limit handles with in-flight token tracking for s...
RateLimitDelay< Duration > time_until_next_allowed(const HttpRateLimitHandlePtr &general_limit, const HttpRateLimitHandlePtr &specific_limit, const std::string &general_key, const std::string &specific_key)
std::unordered_map< uint64_t, waiter_list_t > waiter_map_t
void complete_group_request(uint64_t group_id)
HttpRequestManager(const HttpRequestManager &)=delete
Deleted copy constructor to enforce the singleton pattern.
std::size_t max_pending_requests() const
Returns the current maximum pending request count.
std::atomic< bool > m_shutdown
Flag indicating if shutdown has been requested.
static bool matches_cancel(const std::unique_ptr< HttpRequestContext > &ctx, const cancel_map_t &requests_to_cancel, const cancel_map_t &groups_to_cancel)
uint64_t generate_group_id()
Generates a new group ID.
bool has_requests_by_group_id(uint64_t group_id) const
Checks whether an admitted request remains outstanding for a group.
void set_max_pending_requests(std::size_t max_pending_requests)
Sets the maximum number of pending requests accepted into the global queue.
cancel_map_t m_groups_to_cancel
Map of group IDs to their associated cancellation callbacks.
std::list< std::unique_ptr< HttpRequestContext > > m_failed_requests
List of failed HTTP requests for retrying.
void process_active_requests()
Processes active requests, moving failed ones to the failed requests list for retrying.
std::atomic< uint64_t > m_group_id_counter
Atomic counter for group IDs.
bool remove_limit(const HttpRateLimitHandlePtr &limit)
Releases manager-owned handle for the specified rate-limit handle.
std::size_t group_request_count(uint64_t group_id) const
Counts admitted requests that have not delivered their final callback for a group.
void cancel_request_by_id(uint64_t request_id, std::function< void()> callback)
Cancels one request by request ID.
std::atomic< uint64_t > m_next_in_flight_token
Atomic counter for sequential rate-limit tokens.
static HttpRequestManager & get_instance()
Get the singleton instance of HttpRequestManager.
std::list< std::unique_ptr< HttpBatchRequestHandler > > m_active_request_batches
List of currently active HTTP request batches.
bool is_loaded() const override
Checks if there are active, pending, or failed requests.
std::atomic< uint64_t > m_request_id_counter
Atomic counter for unique request IDs.
std::list< GroupWaiter > waiter_list_t
void process_retry_failed_requests()
Attempts to retry failed requests if their retry delay has passed.
HttpRateLimitHandlePtr get_rate_limit(long limit_id)
Returns a handle for a registered rate limit ID.
std::atomic< std::size_t > m_max_pending_requests
Maximum number of requests accepted into the pending queue, or zero if unbounded.
uint64_t wait_requests_by_group_id(uint64_t group_id, std::function< void()> callback)
Registers a callback invoked after all requests from a group finish.
virtual ~HttpRequestManager()
Private destructor to clean up global resources.
RateLimitDelay< Duration > time_until_next_allowed(const HttpRateLimitHandlePtr &general_limit, const HttpRateLimitHandlePtr &specific_limit, const std::string &general_key, const std::string &specific_key)
Calculates delay until request is allowed by two handles.
SubmitResult submit_request(std::unique_ptr< HttpRequest > request_ptr, HttpResponseCallback callback)
Attempts to enqueue a new HTTP request and reports the admission result.
HttpRateLimiter m_rate_limiter
Rate limiter for controlling request frequency.
std::list< std::function< void()> > callback_list_t
std::mutex m_mutex
Mutex to protect access to manager state shared with public entry points.
std::size_t group_request_count_unlocked(uint64_t group_id) const
void process_cancel_requests()
Processes and cancels HTTP requests based on their request or group IDs.
bool remove_limit(long limit_id)
Removes an existing rate limit with the specified identifier.
void cancel_requests_by_group_id(uint64_t group_id, std::function< void()> callback)
Cancels all requests with the specified group ID.
bool allow_request(const HttpRateLimitHandlePtr &general_limit, const HttpRateLimitHandlePtr &specific_limit, uint64_t in_flight_token, const std::string &general_key, const std::string &specific_key)
Checks if a request is allowed by two optional rate-limit handles.
cancel_map_t m_requests_to_cancel_by_id
Map of request IDs to their associated cancellation callbacks.
static HttpResponsePtr make_cancelled_response()
void release_request(const HttpRateLimitHandlePtr &general_limit, const HttpRateLimitHandlePtr &specific_limit, uint64_t in_flight_token, const std::string &general_key, const std::string &specific_key)
Releases in-flight tokens for sequential rate limits.
waiter_map_t m_group_waiters
Map of group IDs to callbacks waiting until a group becomes idle.
bool cancel_wait_requests_by_group_id(uint64_t group_id, uint64_t waiter_id)
Removes a previously registered group waiter.
std::unordered_map< uint64_t, callback_list_t > cancel_map_t
uint64_t generate_request_id()
Generates a new unique request ID.
std::unordered_map< uint64_t, std::size_t > m_group_request_counts
Outstanding admitted requests by group.
bool add_request(std::unique_ptr< HttpRequest > request_ptr, HttpResponseCallback callback)
Adds a new HTTP request to the manager.
void shutdown() override
Shuts down the request manager, clearing all active and pending requests.
HttpRequestManager & operator=(const HttpRequestManager &)=delete
Deleted copy assignment operator to enforce the singleton pattern.
static void invoke_cancel_callbacks(const cancel_map_t &requests_to_cancel)
HttpRequestManager()
Private constructor to initialize global resources (e.g., cURL).
void cleanup_pending_requests()
Cleans up pending requests, marking each as failed and invoking its callback.
uint64_t m_waiter_id_counter
Counter for group waiter IDs.
HttpRateLimitHandlePtr create_rate_limit(long requests_per_period, long period_ms, bool sequential=false)
Creates a rate-limit handle with specified parameters.
void process() override
Processes all requests in the manager.
void process_pending_requests()
Processes all pending requests, moving valid requests to active batches or marking them as failed.
std::list< std::unique_ptr< HttpRequestContext > > m_pending_requests
List of pending HTTP requests awaiting processing.
Represents an HTTP response.
Interface for modules managed by NetworkWorker (e.g., HTTP, WebSocket).
@ ShuttingDown
Operation was rejected because the owning subsystem is shutting down.
@ AbortedDuringDestruction
Request handler was destroyed before completion, causing the request to abort.
@ QueueLimitExceeded
Operation was rejected because the bounded queue is already full.
@ CancelledByUser
Request was cancelled explicitly by the user via cancel().
std::error_code make_error_code(ClientError e)
Creates a std::error_code from a ClientError value.
Primary namespace for the Kurlyk library, encompassing initialization, request management,...
std::function< void(HttpResponsePtr response)> HttpResponseCallback
Callback invoked with an HTTP response.
std::unique_ptr< HttpResponse > HttpResponsePtr
Owning pointer to an HTTP response.
std::shared_ptr< HttpRateLimitHandle > HttpRateLimitHandlePtr
Shared RAII handle for HTTP rate limits.
Result type for time-until-allowed queries.
Represents the synchronous result of trying to enqueue or submit work.
bool accepted
Indicates whether the work item was accepted for processing.