curl-cpp
static c++17 wrapper for curl with -fno-exceptions support
curl_multi.cc
1 #include "curl_multi.hpp"
2 #include <curl/curl.h>
3 
4 #include <cassert>
5 
6 namespace curl {
7 auto curl_t::create_multi() noexcept -> Ret_except<Multi_t, curl::Exception>
8 {
9  auto *multi = curl_multi_init();
10  if (!multi)
11  return {curl::Exception{"curl_multi_init failed"}};
12  return {std::in_place_type<Multi_t>, multi};
13 }
14 
15 Multi_t::Multi_t(void *multi) noexcept:
16  curl_multi{multi}
17 {}
18 
19 Multi_t::Multi_t(Multi_t &&other) noexcept:
20  curl_multi{other.curl_multi}
21 {
22  other.curl_multi = nullptr;
23 
24  handles = other.handles;
25 }
26 Multi_t& Multi_t::operator = (Multi_t &&other) noexcept
27 {
28  if (curl_multi)
29  curl_multi_cleanup(curl_multi);
30  curl_multi = other.curl_multi;
31  other.curl_multi = nullptr;
32 
33  handles = other.handles;
34 
35  return *this;
36 }
37 
38 Multi_t::operator bool () const noexcept
39 {
40  return curl_multi != nullptr;
41 }
42 
44 {
45  if (curl_multi)
46  curl_multi_cleanup(curl_multi);
47 }
48 
49 bool Multi_t::add_easy(Easy_ref_t &easy) noexcept
50 {
51  bool success = curl_multi_add_handle(curl_multi, easy.curl_easy) != CURLM_ADDED_ALREADY;
52 
53  handles += success;
54 
55  return success;
56 }
57 void Multi_t::remove_easy(Easy_ref_t &easy) noexcept
58 {
59  --handles;
60  curl_multi_remove_handle(curl_multi, easy.curl_easy);
61 }
62 
63 std::size_t Multi_t::get_number_of_handles() const noexcept
64 {
65  return handles;
66 }
67 
68 void Multi_t::set_multiplexing(long max_concurrent_stream) noexcept
69 {
70  long bitmask = max_concurrent_stream != 0 ? CURLPIPE_MULTIPLEX : CURLPIPE_NOTHING;
71  curl_multi_setopt(curl_multi, CURLMOPT_PIPELINING, bitmask);
72 
73  if (max_concurrent_stream >= 1)
74  curl_multi_setopt(curl_multi, CURLMOPT_MAX_CONCURRENT_STREAMS, max_concurrent_stream);
75 }
76 
77 /* Interface for poll + perform - multi_poll interface */
78 auto Multi_t::poll(curl_waitfd *extra_fds, unsigned extra_nfds, int timeout) noexcept ->
79  Ret_except<int, std::bad_alloc, libcurl_bug>
80 {
81  int numfds;
82 
83  auto code = curl_multi_poll(curl_multi, extra_fds, extra_nfds, timeout, &numfds);
84  if (code == CURLM_OUT_OF_MEMORY)
85  return {std::bad_alloc{}};
86  else if (code == CURLM_INTERNAL_ERROR)
87  return {libcurl_bug{"Bug in curl_multi_poll!"}};
88 
89  assert(code == CURLM_OK);
90  return {numfds};
91 }
92 
93 auto Multi_t::break_or_poll(curl_waitfd *extra_fds, unsigned extra_nfds, int timeout) noexcept ->
94  Ret_except<int, std::bad_alloc, libcurl_bug>
95 {
96  if (get_number_of_handles() == 0)
97  return {-1};
98  else
99  return poll(extra_fds, extra_nfds, timeout);
100 }
101 
102 auto Multi_t::get_finished_easy() const noexcept -> std::pair<Easy_ref_t, Easy_ref_t::perform_ret_t>
103 {
104  int msgq = 0;
105  for (CURLMsg *m; (m = curl_multi_info_read(curl_multi, &msgq)); )
106  if (m->msg == CURLMSG_DONE) {
107  Easy_ref_t easy_ref{static_cast<char*>(m->easy_handle)};
108  return {easy_ref, easy_ref.check_perform(m->data.result, "")};
109  }
110 
111  return {Easy_ref_t{nullptr}, Easy_ref_t::perform_ret_t{Easy_ref_t::code::ok}};
112 }
113 auto Multi_t::check_perform(long code, int running_handles, const char *fname) noexcept ->
114  Ret_except<int, std::bad_alloc, Exception, Recursive_api_call_Exception, libcurl_bug>
115 {
116  if (code == CURLM_OUT_OF_MEMORY)
117  return {std::bad_alloc{}};
118  else if (code == CURLM_INTERNAL_ERROR)
119  return {libcurl_bug{fname}};
120  else if (code == CURLM_RECURSIVE_API_CALL)
121  return {Recursive_api_call_Exception{fname}};
122 
123  assert(code == CURLM_OK);
124 
125  return {running_handles};
126 }
127 auto Multi_t::perform_impl() noexcept -> perform_ret_t
128 {
129  int running_handles = 0;
130 
131  CURLMcode code;
132  while ((code = curl_multi_perform(curl_multi, &running_handles)) == CURLM_CALL_MULTI_PERFORM);
133 
134  return check_perform(code, running_handles, "In curl_multi_perform");
135 }
136 
137 /* Interface for using arbitary event-based interface - multi_socket interface */
138 void Multi_t::register_callback(socket_callback_t socket_callback, void *socket_data,
139  timer_callback_t timer_callback, void *timer_data) noexcept
140 {
141  curl_multi_setopt(curl_multi, CURLMOPT_SOCKETFUNCTION, socket_callback);
142  curl_multi_setopt(curl_multi, CURLMOPT_SOCKETDATA, socket_data);
143 
144  curl_multi_setopt(curl_multi, CURLMOPT_TIMERFUNCTION, timer_callback);
145  curl_multi_setopt(curl_multi, CURLMOPT_TIMERDATA, timer_data);
146 }
147 
148 auto Multi_t::multi_assign(curl_socket_t socketfd, void *per_sockptr) noexcept ->
149  Ret_except<void, std::invalid_argument>
150 {
151  auto code = curl_multi_assign(curl_multi, socketfd, per_sockptr);
152  if (code == CURLM_BAD_SOCKET)
153  return {std::invalid_argument{"In curl::Multi_t::multi_assign: socketfd is not valid."}};
154 
155  return {};
156 }
157 auto Multi_t::multi_socket_action_impl(curl_socket_t socketfd, int ev_bitmask) noexcept -> perform_ret_t
158 {
159  int running_handles;
160 
161  CURLMcode code;
162  while ((code = curl_multi_socket_action(curl_multi, socketfd, ev_bitmask, &running_handles)) ==
163  CURLM_CALL_MULTI_PERFORM);
164 
165  return check_perform(code, running_handles, "In curl_multi_socket_action");
166 }
167 } /* namespace curl */
curl::Multi_t::register_callback
void register_callback(socket_callback_t socket_callback, void *socket_data, timer_callback_t timer_callback, void *timer_data) noexcept
Definition: curl_multi.cc:138
curl::Exception
Definition: curl.hpp:16
curl::Multi_t::Multi_t
Multi_t(Multi_t &&) noexcept
Definition: curl_multi.cc:19
curl::Multi_t::poll
auto poll(curl_waitfd *extra_fds=nullptr, unsigned extra_nfds=0U, int timeout=0) noexcept -> Ret_except< int, std::bad_alloc, libcurl_bug >
Definition: curl_multi.cc:78
curl::Multi_t::operator=
Multi_t & operator=(Multi_t &&) noexcept
Definition: curl_multi.cc:26
curl::Multi_t::multi_assign
auto multi_assign(curl_socket_t socketfd, void *per_sockptr) noexcept -> Ret_except< void, std::invalid_argument >
Definition: curl_multi.cc:148
curl::Easy_ref_t
Definition: curl_easy.hpp:53
curl::Multi_t::set_multiplexing
void set_multiplexing(long max_concurrent_stream) noexcept
Definition: curl_multi.cc:68
curl::Multi_t
Definition: curl_multi.hpp:29
curl::Multi_t::~Multi_t
~Multi_t()
Definition: curl_multi.cc:43
curl::curl_t
Definition: curl.hpp:62
curl::Easy_ref_t::code
code
Definition: curl_easy.hpp:551
curl::Multi_t::Exception
Definition: curl_multi.hpp:35
curl::Multi_t::add_easy
bool add_easy(Easy_ref_t &easy) noexcept
Definition: curl_multi.cc:49
curl::Recursive_api_call_Exception
Definition: curl.hpp:40
curl::Multi_t::Multi_t
Multi_t(void *multi) noexcept
Definition: curl_multi.cc:15
curl::Multi_t::operator bool
operator bool() const noexcept
Definition: curl_multi.cc:38
curl::Multi_t::break_or_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 >
Definition: curl_multi.cc:93
curl::curl_t::create_multi
auto create_multi() noexcept -> Ret_except< Multi_t, curl::Exception >
Definition: curl_multi.cc:7
curl::Multi_t::get_finished_easy
auto get_finished_easy() const noexcept -> std::pair< Easy_ref_t, Easy_ref_t::perform_ret_t >
Definition: curl_multi.cc:102
curl::Multi_t::remove_easy
void remove_easy(Easy_ref_t &easy) noexcept
Definition: curl_multi.cc:57
curl::libcurl_bug
Definition: curl.hpp:32