Kurlyk
Loading...
Searching...
No Matches
HttpBatchRequestHandler.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef KURLYK_HEADER_KURLYK_HTTP_HTTP_REQUEST_MANAGER_HTTP_BATCH_REQUEST_HANDLER_HPP_INCLUDED
3#define KURLYK_HEADER_KURLYK_HTTP_HTTP_REQUEST_MANAGER_HTTP_BATCH_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) 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->invoke_final_callback(std::move(response));
32 context.reset();
33 }
34 return;
35 }
36 for (auto& context : context_list) {
37 if (!context) continue;
38# if __cplusplus >= 201402L
39 auto handler = std::make_unique<HttpRequestHandler>(std::move(context));
40# else
41 auto handler = std::unique_ptr<HttpRequestHandler>(new HttpRequestHandler(std::move(context)));
42# endif
43 CURL* curl = handler->get_curl();
44 if (!curl) {
45 // curl_easy_init failed: deliver error immediately.
46 auto ctx = handler->get_request_context();
47 if (ctx) {
48# if __cplusplus >= 201402L
49 auto response = std::make_unique<HttpResponse>();
50# else
51 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
52# endif
54 response->status_code = 499; // Client closed request
55 response->ready = true;
56 ctx->invoke_final_callback(std::move(response));
57 }
58 continue;
59 }
60
61 curl_multi_add_handle(m_multi_handle, curl);
62 m_handlers.push_back(std::move(handler));
63 }
64 // Ensure the source vector is empty after moving contexts into handlers or failing them.
65 for (auto& context : context_list) {
66 context.reset();
67 }
68 }
69
72 for (auto& handler : m_handlers) {
73 CURL* curl = handler->get_curl();
74 if (!curl) continue;
75 curl_multi_remove_handle(m_multi_handle, curl);
76 }
77 if (m_multi_handle) {
78 curl_multi_cleanup(m_multi_handle);
79 }
80 }
81
84 bool process() {
85 if (!m_multi_handle) {
86 // Multi handle was never created: fail all handlers immediately.
87 for (auto& handler : m_handlers) {
88 auto ctx = handler->get_request_context();
89 if (ctx) {
90# if __cplusplus >= 201402L
91 auto response = std::make_unique<HttpResponse>();
92# else
93 auto response = std::unique_ptr<HttpResponse>(new HttpResponse());
94# endif
96 response->status_code = 499; // Client closed request
97 response->ready = true;
98 ctx->invoke_final_callback(std::move(response));
99 }
100 }
101 m_handlers.clear();
102 return true;
103 }
104 int still_running = 0;
105 CURLMcode res = curl_multi_perform(m_multi_handle, &still_running);
106 if (res != CURLM_OK) return false;
107
108 int pending_messages;
109 while (CURLMsg* message = curl_multi_info_read(m_multi_handle, &pending_messages)) {
110 if (message->msg != CURLMSG_DONE) continue;
112 }
113 if (still_running == 0) {
114 m_handlers.clear();
115 return true;
116 }
117 return false;
118 }
119
122 std::list<std::unique_ptr<HttpRequestContext>> extract_failed_requests() {
123 return std::move(m_failed_requests);
124 }
125
129 bool has_group_id(uint64_t group_id) const {
130 return group_request_count(group_id) != 0;
131 }
132
136 std::size_t group_request_count(uint64_t group_id) const {
137 if (group_id == 0) return 0;
138
139 std::size_t count = 0;
140 for (const auto& handler : m_handlers) {
141 if (handler && !handler->is_done() && handler->get_group_id() == group_id) {
142 ++count;
143 }
144 }
145 return count;
146 }
147
150 void cancel_request_by_id(const std::unordered_map<uint64_t, std::list<std::function<void()>>>& to_cancel) {
151 cancel_requests(to_cancel, std::unordered_map<uint64_t, std::list<std::function<void()>>>());
152 }
153
158 const std::unordered_map<uint64_t, std::list<std::function<void()>>>& requests_to_cancel,
159 const std::unordered_map<uint64_t, std::list<std::function<void()>>>& groups_to_cancel) {
160 auto it = m_handlers.begin();
161 while (it != m_handlers.end()) {
162 const uint64_t request_id = (*it)->get_request_id();
163 const uint64_t group_id = (*it)->get_group_id();
164 const bool should_cancel =
165 (request_id != 0 && requests_to_cancel.count(request_id) > 0) ||
166 (group_id != 0 && groups_to_cancel.count(group_id) > 0);
167 if (!should_cancel) {
168 ++it;
169 continue;
170 }
171 curl_multi_remove_handle(m_multi_handle, (*it)->get_curl());
172 (*it)->cancel();
173 it = m_handlers.erase(it);
174 }
175 }
176
177 private:
178 CURLM* m_multi_handle = nullptr;
179 std::vector<std::unique_ptr<HttpRequestHandler>> m_handlers;
180 std::list<std::unique_ptr<HttpRequestContext>> m_failed_requests;
181
184 void handle_completed_request(CURLMsg* message) {
185 CURL* curl = message->easy_handle;
186
187 void* ptr = nullptr;
188 curl_easy_getinfo(curl, CURLINFO_PRIVATE, &ptr);
189 auto* handler = static_cast<HttpRequestHandler*>(ptr);
190 if (!handler) return;
191
192 const bool completed = handler->handle_curl_message(message);
193 curl_multi_remove_handle(m_multi_handle, curl);
194
195 if (!completed) {
196 handler->mark_done();
197 m_failed_requests.push_back(handler->get_request_context());
198 }
199 }
200
201 }; // HttpBatchRequestHandler
202
203} // namespace kurlyk
204
205#endif // KURLYK_HEADER_KURLYK_HTTP_HTTP_REQUEST_MANAGER_HTTP_BATCH_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.
HttpRequestHandler(std::unique_ptr< HttpRequestContext > context)
Constructs an HttpRequestHandler with the specified request context.
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,...