-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcurl_multi.hpp
296 lines (267 loc) · 11.1 KB
/
curl_multi.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#ifndef __curl_cpp_curl_multi_HPP__
# define __curl_cpp_curl_multi_HPP__
# include "curl_easy.hpp"
# include <curl/curl.h>
namespace curl {
/**
* @example curl_multi_poll.cc
* @example curl_multi_poll2.cc
* @example curl_multi_socket_action_epoll.cc
* @example curl_multi_socket_action_event.cc
* @example curl_multi_socket_action_uv.cc
*
* Multi_t enables user to simultaneously do multiple request
* in the same thread, using any polling interface they prefer.
*
* This class contains poll() interface (wrapping curl_multi_poll, doesn't
* add additional space to use it), and multi_socket_action interface
* that enables using any interface. They are incompatible and should not be
* mixed.
*
* Multi_t's member function cannot be called in multiple threads simultaneously.
*
* All easy handles must be removed from this class before it can be
* properly destroyed.
* Failure to do so invokes undefined behavior.
*/
class Multi_t {
public:
/**
* Base class for any exceptions thrown via Ret_except in
* this class.
*/
class Exception: public curl::Exception {
public:
const long error_code;
Exception(long err_code_arg);
Exception(const Exception&) = default;
auto what() const noexcept -> const char*;
};
using perform_ret_t = Ret_except<int, std::bad_alloc, Exception, Recursive_api_call_Exception, libcurl_bug>;
protected:
void *curl_multi = nullptr;
std::size_t handles = 0;
/**
* @return handles that are finished.
* Return nullptr to signal all finished handles are returned.
*/
auto get_finished_easy() const noexcept ->
std::pair<Easy_ref_t, Easy_ref_t::perform_ret_t>;
template <class perform_callback_t, class T>
void callback_on_finished_easy(perform_callback_t &&perform_callback, T &&arg)
{
for (std::pair<Easy_ref_t, Easy_ref_t::perform_ret_t> ret;
(ret = get_finished_easy()).first.curl_easy; )
std::forward<perform_callback_t>(perform_callback)(ret.first, std::move(ret.second), *this,
std::forward<T>(arg));
}
auto perform_impl() noexcept -> perform_ret_t;
auto multi_socket_action_impl(curl_socket_t socketfd, int ev_bitmask) noexcept ->
perform_ret_t;
public:
/**
* Construct an empty Multi_t that can only be
* move assigned with value or be destroyed.
*/
Multi_t() = default;
/**
* This constructor takes CURLM*
*
* @param multi pass nullptr for an empty Multi_t that can only be
* move assigned with value or be destroyed.
*/
Multi_t(void *multi) noexcept;
Multi_t(const Multi_t&) = delete;
/**
* @param other after mv operation, other is in unusable state and can only be destroyed
* or move assign another value.
*/
Multi_t(Multi_t&&) noexcept;
Multi_t& operator = (const Multi_t&) = delete;
/**
* @param other after mv operation, other is in unusable state and can only be destroyed
* or move assign another value.
*/
Multi_t& operator = (Multi_t&&) noexcept;
/**
* @return true if this object is usable, false otherwise
*/
operator bool () const noexcept;
/**
* @param easy must be in valid state
* @return true if not yet added;
* <br>false if already added.
*/
bool add_easy(Easy_ref_t &easy) noexcept;
/**
* Undefined behavior if easy is not valid or not added to this multi.
*/
void remove_easy(Easy_ref_t &easy) noexcept;
std::size_t get_number_of_handles() const noexcept;
/**
* HTTP2 multiplexing configuration.
*
* @pre curl_t::has_http2_multiplex_support()
* @param max_concurrent_stream max concurrent stream for a given connection.
* <br>Should be between [0, 2 ^ 31 - 1].
* <br>Set it to 0 disable multiplexing.
*
* Since curl_t::version >= 7.62.0 (version released way after http2 multiplex support)
* , multiplex is turned on by default.
*
* NOTE that libcurl not always accept max_concurrent_stream tuning.
* <br>Check curl_t::has_max_concurrent_stream_support().
*
* If libcurl does not support tuning, this option will be only used
* for turning on and off the http2 multiplex.
*/
void set_multiplexing(long max_concurrent_stream) noexcept;
/* Interface for poll + perform - multi_poll interface */
/**
* @pre curl_t::has_multi_poll_support()
* @param timeout Must be >= 0, in ms. Pass 0 for infinite.
* @return number of fd on which interested events occured.
*
* poll can return if interesting events has happened or timeout ms
* has passed and nothing has heppend.
*
* It also can return due to pending internal timeout that
* has a shorter expiry time than timeout_ms.
*/
auto poll(curl_waitfd *extra_fds = nullptr, unsigned extra_nfds = 0U, int timeout = 0) noexcept ->
Ret_except<int, std::bad_alloc, libcurl_bug>;
/**
* @pre curl_t::has_multi_poll_support()
* @param timeout Must be >= 0, in ms. Pass 0 for infinite.
* @return -1 when get_number_of_handles() == 0;
* Otherwise number of fd on which interested events occured, can be 0.
*
* Behavior is same as poll.
*/
auto break_or_poll(curl_waitfd *extra_fds = nullptr, unsigned extra_nfds = 0U, int timeout = 0) noexcept ->
Ret_except<int, std::bad_alloc, libcurl_bug>;
/**
* @pre perform_callback is set.
*
* @param arg will be passed to perform_callback
* @param perform_callback Must be callable with (Easy_ref_t, Easy_ref_t::perform_ret_t, Multi_t&, T)
* <br>If you want to destroy and free easy.curl_easy, you must first
* multi.remove_easy(easy_ref) it.
* <br>If easy_ref isn't removed from Multi, then the same transfer will happen again
* in the next call to Multi_t::perform.
*
* @return number of running handles
*
* perform() is called only if poll is used.
*
* After perform, perform_callback will be called for each completed
* easy.
*
* **YOU MUST CALL perform() after to start the transfer, then poll**
*
* Using libcurl version >= 7.10.3 can provide better error message
* if Easy_ref_t::ProtocolInternal_error is thrown.
*/
template <class perform_callback_t, class T>
auto perform(perform_callback_t &&perform_callback, T &&arg) noexcept -> perform_ret_t
{
auto ret = perform_impl();
callback_on_finished_easy(std::forward<perform_callback_t>(perform_callback), std::forward<T>(arg));
return ret;
}
/**
* @return should be 0
*
* Interface for using arbitary event-based interface - multi_socket interface
*
* Precondition for using this interface:
* - curl_t::has_multi_socket_support()
*/
using socket_callback_t = int (*)(CURL *curl_easy,
curl_socket_t s,
/**
* Possible value for what:
* - CURL_POLL_IN,
* - CURL_POLL_OUT,
* - CURL_POLL_INOUT,
* - CURL_POLL_REMOVE
*/
int what,
void *userp,
void *per_socketp);
/**
* @param timeout_ms -1 means you should delete the timer.
* All other values are valid expire times in number of milliseconds.
* @return should be 0 on success,
* -1 on failure.
*
* Your callback function timer_callback should install a non-repeating timer with an interval of timeout_ms.
* <br>When time that timer fires, call multi_socket_action().
*
* The timer_callback will only be called when the timeout expire time is changed.
*/
using timer_callback_t = int (*)(CURLM *multi, long timeout_ms, void *userp);
/**
* @param socket_callback, timer_callback setting them to nullptr would
* disable multi_socket_action interface.
*
* You must call this function with non-NULL socket_callback and timer_callback
* before adding any easy handles.
*/
void register_callback(socket_callback_t socket_callback, void *socket_data,
timer_callback_t timer_callback, void *timer_data) noexcept;
/**
* @pre socketfd must be valid
* @return std::invalild_argument if socketfd is not valid.
*
* By default, per_sockptr == nullptr.
*
* You can call this function from socket_callback.
*/
auto multi_assign(curl_socket_t socketfd, void *per_sockptr) noexcept ->
Ret_except<void, std::invalid_argument>;
/**
* @pre enable_multi_socket_interface() is called,
* perform_callback, socket_callback, timer_callback is set.
*
* @param perform_callback Must be callable with (Easy_ref_t, Easy_ref_t::perform_ret_t, Multi_t&, T)
* <br>If you want to destroy and free easy.curl_easy, you must first
* multi.remove_easy(easy_ref) it.
* <br>If easy_ref isn't removed from Multi, then the same transfer will happen again
* in the next call to
* Multi_t::multi_socket_action(socketfd of this easy handler, events, ...).
*
* @param socketfd fd to be notified;
* <br>CURL_SOCKET_TIMEOUT on timeout or to initiate the whole process.
* @param ev_bitmask Or-ed with 0 or one of the value below:
* - CURL_CSELECT_IN,
* - CURL_CSELECT_OUT,
* - CURL_CSELECT_ERR,
*
* **YOU MUST CALL multi_socket_action(CURL_SOCKET_TIMEOUT, 0) to start the transfer,
* then call waitever poll interface you use**
*
* After multi_socket_action, perform_callback will be called for each completed
* easy.
*
* Using libcurl version >= 7.10.3 can provide better error message
* if Easy_ref_t::ProtocolInternal_error is thrown.
*/
template <class perform_callback_t, class T>
auto multi_socket_action(curl_socket_t socketfd, int ev_bitmask,
perform_callback_t &&perform_callback, T &&arg) noexcept -> perform_ret_t
{
auto ret = multi_socket_action_impl(socketfd, ev_bitmask);
callback_on_finished_easy(std::forward<perform_callback_t>(perform_callback), std::forward<T>(arg));
return ret;
}
/**
* @pre get_number_of_handles() == 0
*/
~Multi_t();
protected:
auto check_perform(long code, int running_handles_tmp, const char *fname) noexcept ->
Ret_except<int, std::bad_alloc, Exception, Recursive_api_call_Exception, libcurl_bug>;
};
} /* namespace curl */
#endif