Kurlyk
Loading...
Searching...
No Matches
HttpBatchRequestHandler.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _KURLYK_HTTP_MULTI_REQUEST_HANDLER_HPP_INCLUDED
3#define _KURLYK_HTTP_MULTI_REQUEST_HANDLER_HPP_INCLUDED
4
7
8namespace kurlyk {
9
13 public:
14
17 explicit HttpBatchRequestHandler(std::vector<std::unique_ptr<HttpRequestContext>>& context_list)
18 : m_multi_handle(curl_multi_init()) {
19 if (!m_multi_handle) {
20 // libcurl multi handle creation failed: fail all requests immediately.
21 for (auto& context : context_list) {
22 if (!context || !context->callback) continue;
23# if __cplusplus >= 201402L
24 auto response = std::make_unique<HttpResponse>();
25# else
26 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
27# endif
29 response->status_code = 499; // Client closed request
30 response->ready = true;
31 context->callback(std::move(response));
32 context->complete();
33 context.reset();
34 }
35 return;
36 }
37 for (auto& context : context_list) {
38 if (!context) continue;
39# if __cplusplus >= 201402L
40 auto handler = std::make_unique<HttpRequestHandler>(std::move(context));
41# else
42 auto handler = std::unique_ptr<HttpRequestHandler>(new HttpRequestHandler(std::move(context)));
43# endif
44 CURL* curl = handler->get_curl();
45 if (!curl) {
46 // curl_easy_init failed: deliver error immediately.
47 auto ctx = handler->get_request_context();
48 if (ctx && ctx->callback) {
49# if __cplusplus >= 201402L
50 auto response = std::make_unique<HttpResponse>();
51# else
52 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
53# endif
55 response->status_code = 499; // Client closed request
56 response->ready = true;
57 ctx->callback(std::move(response));
58 ctx->complete();
59 }
60 continue;
61 }
62
63 curl_multi_add_handle(m_multi_handle, curl);
64 m_handlers.push_back(std::move(handler));
65 }
66 // Ensure the source vector is empty after moving contexts into handlers or failing them.
67 for (auto& context : context_list) {
68 context.reset();
69 }
70 }
71
74 for (auto& handler : m_handlers) {
75 CURL* curl = handler->get_curl();
76 if (!curl) continue;
77 curl_multi_remove_handle(m_multi_handle, curl);
78 }
79 if (m_multi_handle) {
80 curl_multi_cleanup(m_multi_handle);
81 }
82 }
83
86 bool process() {
87 if (!m_multi_handle) {
88 // Multi handle was never created: fail all handlers immediately.
89 for (auto& handler : m_handlers) {
90 auto ctx = handler->get_request_context();
91 if (ctx && ctx->callback) {
92# if __cplusplus >= 201402L
93 auto response = std::make_unique<HttpResponse>();
94# else
95 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
96# endif
98 response->status_code = 499; // Client closed request
99 response->ready = true;
100 ctx->callback(std::move(response));
101 ctx->complete();
102 }
103 }
104 m_handlers.clear();
105 return true;
106 }
107 int still_running = 0;
108 CURLMcode res = curl_multi_perform(m_multi_handle, &still_running);
109 if (res != CURLM_OK) return false;
110
111 int pending_messages;
112 while (CURLMsg* message = curl_multi_info_read(m_multi_handle, &pending_messages)) {
113 if (message->msg != CURLMSG_DONE) continue;
115 }
116 if (still_running == 0) {
117 m_handlers.clear();
118 return true;
119 }
120 return false;
121 }
122
125 std::list<std::unique_ptr<HttpRequestContext>> extract_failed_requests() {
126 return std::move(m_failed_requests);
127 }
128
132 bool has_group_id(uint64_t group_id) const {
133 return group_request_count(group_id) != 0;
134 }
135
139 std::size_t group_request_count(uint64_t group_id) const {
140 if (group_id == 0) return 0;
141
142 std::size_t count = 0;
143 for (const auto& handler : m_handlers) {
144 if (handler && !handler->is_done() && handler->get_group_id() == group_id) {
145 ++count;
146 }
147 }
148 return count;
149 }
150
153 void cancel_request_by_id(const std::unordered_map<uint64_t, std::list<std::function<void()>>>& to_cancel) {
154 cancel_requests(to_cancel, std::unordered_map<uint64_t, std::list<std::function<void()>>>());
155 }
156
161 const std::unordered_map<uint64_t, std::list<std::function<void()>>>& requests_to_cancel,
162 const std::unordered_map<uint64_t, std::list<std::function<void()>>>& groups_to_cancel) {
163 auto it = m_handlers.begin();
164 while (it != m_handlers.end()) {
165 const uint64_t request_id = (*it)->get_request_id();
166 const uint64_t group_id = (*it)->get_group_id();
167 const bool should_cancel =
168 (request_id != 0 && requests_to_cancel.count(request_id) > 0) ||
169 (group_id != 0 && groups_to_cancel.count(group_id) > 0);
170 if (!should_cancel) {
171 ++it;
172 continue;
173 }
174 curl_multi_remove_handle(m_multi_handle, (*it)->get_curl());
175 (*it)->cancel();
176 it = m_handlers.erase(it);
177 }
178 }
179
180 private:
181 CURLM* m_multi_handle = nullptr;
182 std::vector<std::unique_ptr<HttpRequestHandler>> m_handlers;
183 std::list<std::unique_ptr<HttpRequestContext>> m_failed_requests;
184
187 void handle_completed_request(CURLMsg* message) {
188 CURL* curl = message->easy_handle;
189
190 void* ptr = nullptr;
191 curl_easy_getinfo(curl, CURLINFO_PRIVATE, &ptr);
192 auto* handler = static_cast<HttpRequestHandler*>(ptr);
193 if (!handler) return;
194
195 const bool completed = handler->handle_curl_message(message);
196 curl_multi_remove_handle(m_multi_handle, curl);
197
198 if (!completed) {
199 handler->mark_done();
200 m_failed_requests.push_back(handler->get_request_context());
201 }
202 }
203
204 }; // HttpBatchRequestHandler
205
206} // namespace kurlyk
207
208#endif // _KURLYK_HTTP_MULTI_REQUEST_HANDLER_HPP_INCLUDED
void cancel_request_by_id(const std::unordered_map< uint64_t, std::list< std::function< void()> > > &to_cancel)
Cancels HTTP requests based on their unique IDs.
bool has_group_id(uint64_t group_id) const
Checks whether this batch contains a request from the specified group.
std::list< std::unique_ptr< HttpRequestContext > > m_failed_requests
List of failed request contexts.
void handle_completed_request(CURLMsg *message)
Handles the completion of a single request.
bool process()
Processes the requests within the handler.
std::list< std::unique_ptr< HttpRequestContext > > extract_failed_requests()
Extracts the list of failed requests.
std::vector< std::unique_ptr< HttpRequestHandler > > m_handlers
Collection of active request handlers.
HttpBatchRequestHandler(std::vector< std::unique_ptr< HttpRequestContext > > &context_list)
Constructs a handler for managing multiple HTTP requests asynchronously.
~HttpBatchRequestHandler()
Cleans up the multi handle and removes all request handles.
CURLM * m_multi_handle
libcurl multi handle.
std::size_t group_request_count(uint64_t group_id) const
Counts active requests from the specified group.
void cancel_requests(const std::unordered_map< uint64_t, std::list< std::function< void()> > > &requests_to_cancel, const std::unordered_map< uint64_t, std::list< std::function< void()> > > &groups_to_cancel)
Cancels HTTP requests based on their request or group IDs.
Manages asynchronous HTTP requests, including handling responses, retries, and error processing.
bool handle_curl_message(CURLMsg *message)
Processes a CURL message and determines if a callback should be invoked.
Represents an HTTP response.
@ AbortedDuringDestruction
Request handler was destroyed before completion, causing the request to abort.
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,...