curl-cpp
static c++17 wrapper for curl with -fno-exceptions support
curl_easy.cc
1 #include "curl_easy.hpp"
2 #include "curl_url.hpp"
3 
4 #include <new>
5 #include <curl/curl.h>
6 
7 #include <utility>
8 #include <type_traits>
9 
10 #define CHECK_OOM(code)
11  if ((code) == CURLE_OUT_OF_MEMORY)
12  return {std::bad_alloc{}}
13 
14 namespace curl {
15 void curl_t::Easy_deleter::operator () (void *p) const noexcept
16 {
17  if (p)
18  curl_easy_cleanup(p);
19 }
20 auto curl_t::create_easy(std::size_t buffer_size) noexcept -> Easy_t
21 {
22  CURL *curl = curl_easy_init();
23  if (!curl)
24  return {nullptr};
25 
26  if (stderr_stream) {
27  curl_easy_setopt(curl, CURLOPT_STDERR, stderr_stream);
28  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
29  }
30 
31  // Enable TCP_keepalive
32  curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
33 
34  if (buffer_size)
35  curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, buffer_size);
36 
38  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
39 
40  using Easy_ptr = std::unique_ptr<char, Easy_deleter>;
41  return {Easy_ptr{static_cast<char*>(curl)}};
42 }
43 auto curl_t::dup_easy(const Easy_t &e, std::size_t buffer_size) noexcept -> Easy_t
44 {
45  CURL *curl = curl_easy_duphandle(e.get());
46  if (!curl)
47  return {nullptr};
48 
49  if (stderr_stream) {
50  curl_easy_setopt(curl, CURLOPT_STDERR, stderr_stream);
51  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
52  }
53 
54  if (buffer_size)
55  curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, buffer_size);
56 
58  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
59 
60  using Easy_ptr = std::unique_ptr<char, Easy_deleter>;
61  return {Easy_ptr{static_cast<char*>(curl)}};
62 }
63 
64 void Easy_ref_t::set_verbose(FILE *stderr_stream_arg) noexcept
65 {
66  if (stderr_stream_arg) {
67  curl_easy_setopt(curl_easy, CURLOPT_STDERR, stderr_stream_arg);
68  curl_easy_setopt(curl_easy, CURLOPT_VERBOSE, 1L);
69  }
70 }
71 std::size_t Easy_ref_t::get_error_buffer_size() noexcept
72 {
73  return CURL_ERROR_SIZE;
74 }
75 void Easy_ref_t::set_error_buffer(char *error_buffer) noexcept
76 {
77  curl_easy_setopt(curl_easy, CURLOPT_ERRORBUFFER, error_buffer);
78 }
79 
80 void Easy_ref_t::set_private(void *userp) noexcept
81 {
82  curl_easy_setopt(curl_easy, CURLOPT_PRIVATE, userp);
83 }
84 void* Easy_ref_t::get_private() const noexcept
85 {
86  char *userp;
87  curl_easy_getinfo(curl_easy, CURLINFO_PRIVATE, &userp);
88  return userp;
89 }
90 
91 void Easy_ref_t::set_writeback(writeback_t writeback, void *userp) noexcept
92 {
93  curl_easy_setopt(curl_easy, CURLOPT_WRITEFUNCTION, writeback);
94  curl_easy_setopt(curl_easy, CURLOPT_WRITEDATA, userp);
95 }
96 
97 void Easy_ref_t::set_url(const Url_ref_t &url) noexcept
98 {
99  curl_easy_setopt(curl_easy, CURLOPT_CURLU, url.url);
100 }
101 auto Easy_ref_t::set_url(const char *url) noexcept -> Ret_except<void, std::bad_alloc>
102 {
103  CHECK_OOM(curl_easy_setopt(curl_easy, CURLOPT_URL, url));
104  return {};
105 }
106 
107 auto Easy_ref_t::pin_publickey(const char *pubkey) ->
108  Ret_except<void, std::bad_alloc, curl::NotBuiltIn_error>
109 {
110  auto result = curl_easy_setopt(curl_easy, CURLOPT_PINNEDPUBLICKEY, pubkey);
111  if (result == CURLE_OUT_OF_MEMORY)
112  return {std::bad_alloc{}};
113  else if (result == CURLE_UNKNOWN_OPTION)
114  return {curl::NotBuiltIn_error{"pining public key is not supported by this version of libcurl "
115  "on this specific ssl version"}};
116  else
117  return {};
118 }
119 
120 auto Easy_ref_t::set_cookie(const char *cookies) noexcept ->
121  Ret_except<void, std::bad_alloc, curl::NotBuiltIn_error>
122 {
123  auto code = curl_easy_setopt(curl_easy, CURLOPT_COOKIE, cookies);
124  if (code == CURLE_UNKNOWN_OPTION)
125  return curl::NotBuiltIn_error{"cookies not supported"};
126  else if (code == CURLE_OUT_OF_MEMORY)
127  return {std::bad_alloc{}};
128 
129  return {};
130 }
131 auto Easy_ref_t::set_cookiefile(const char *cookie_filename) noexcept ->
132  Ret_except<void, curl::NotBuiltIn_error>
133 {
134  if (curl_easy_setopt(curl_easy, CURLOPT_COOKIEFILE, cookie_filename) == CURLE_UNKNOWN_OPTION)
135  return {curl::NotBuiltIn_error{"cookies not supported"}};
136  return {};
137 }
138 auto Easy_ref_t::set_cookiejar(const char *cookie_filename) noexcept ->
139  Ret_except<void, std::bad_alloc, curl::NotBuiltIn_error>
140 {
141  auto code = curl_easy_setopt(curl_easy, CURLOPT_COOKIEJAR, cookie_filename);
142  if (code == CURLE_UNKNOWN_OPTION)
143  return {curl::NotBuiltIn_error{"cookies not supported"}};
144  else if (code == CURLE_OUT_OF_MEMORY)
145  return {std::bad_alloc{}};
146 
147  return {};
148 }
149 auto Easy_ref_t::set_cookielist(const char *cookie) noexcept ->
150  Ret_except<void, std::bad_alloc, curl::NotBuiltIn_error>
151 {
152  auto code = curl_easy_setopt(curl_easy, CURLOPT_COOKIELIST, cookie);
153  if (code == CURLE_UNKNOWN_OPTION)
154  return {curl::NotBuiltIn_error{"cookies not supported"}};
155  else if (code == CURLE_OUT_OF_MEMORY)
156  return {std::bad_alloc{}};
157 
158  return {};
159 }
161 {
162  curl_easy_setopt(curl_easy, CURLOPT_COOKIESESSION, 1L);
163 }
165  Ret_except<void, std::bad_alloc, curl::NotBuiltIn_error>
166 {
167  return set_cookielist("ALL");
168 }
170  Ret_except<void, std::bad_alloc, curl::NotBuiltIn_error>
171 {
172  return set_cookielist("SESS");
173 }
174 auto Easy_ref_t::flush_cookies_to_jar() noexcept ->
175  Ret_except<void, std::bad_alloc, curl::NotBuiltIn_error>
176 {
177  return set_cookielist("FLUSH");
178 }
180  Ret_except<void, std::bad_alloc, curl::NotBuiltIn_error>
181 {
182  return set_cookielist("RELOAD");
183 }
184 
185 void Easy_ref_t::set_follow_location(long redir) noexcept
186 {
187  if (redir != 0) {
188  curl_easy_setopt(curl_easy, CURLOPT_FOLLOWLOCATION, 1L);
189  curl_easy_setopt(curl_easy, CURLOPT_MAXREDIRS, redir);
190  } else
191  curl_easy_setopt(curl_easy, CURLOPT_FOLLOWLOCATION, 0L);
192 }
193 
194 auto Easy_ref_t::set_useragent(const char *useragent) noexcept -> Ret_except<void, std::bad_alloc>
195 {
196  CHECK_OOM(curl_easy_setopt(curl_easy, CURLOPT_USERAGENT, useragent));
197  return {};
198 }
199 auto Easy_ref_t::set_encoding(const char *encoding) noexcept -> Ret_except<void, std::bad_alloc>
200 {
201  CHECK_OOM(curl_easy_setopt(curl_easy, CURLOPT_ACCEPT_ENCODING, encoding));
202  return {};
203 }
204 
205 auto Easy_ref_t::set_interface(const char *value) noexcept -> Ret_except<void, std::bad_alloc>
206 {
207  CHECK_OOM(curl_easy_setopt(curl_easy, CURLOPT_INTERFACE, value));
208  return {};
209 }
210 auto Easy_ref_t::set_ip_addr_only(const char *ip_addr) noexcept -> Ret_except<void, std::bad_alloc>
211 {
212  /**
213  * 46 is the maximum length for ipv6 address represented in string.
214  *
215  * Information retrieved from [here][1].
216  *
217  * [1]: https://stackoverflow.com/questions/166132/maximum-length-of-the-textual-representation-of-an-ipv6-address
218  */
219  constexpr const auto buffer_size = 5 + 46;
220  char buffer[buffer_size];
221  std::snprintf(buffer, buffer_size, "host!%s", ip_addr);
222 
223  return set_interface(buffer);
224 }
225 
226 void Easy_ref_t::set_timeout(unsigned long timeout) noexcept
227 {
228  curl_easy_setopt(curl_easy, CURLOPT_TIMEOUT_MS, timeout);
229 }
230 
231 void Easy_ref_t::set_http_header(const utils::slist &l, header_option option) noexcept
232 {
233  curl_easy_setopt(curl_easy, CURLOPT_HTTPHEADER, static_cast<struct curl_slist*>(l.get_underlying_ptr()));
234  if (option != header_option::unspecified) {
235  long value;
236  if (option == header_option::separate)
237  value = CURLHEADER_SEPARATE;
238  else
239  value = CURLHEADER_UNIFIED;
240 
241  curl_easy_setopt(curl_easy, CURLOPT_HEADEROPT, value);
242  }
243 }
244 
245 void Easy_ref_t::set_nobody(bool enable) noexcept
246 {
247  curl_easy_setopt(curl_easy, CURLOPT_NOBODY, enable);
248 }
249 
250 void Easy_ref_t::request_get() noexcept
251 {
252  curl_easy_setopt(curl_easy, CURLOPT_HTTPGET, 1L);
253 }
254 void Easy_ref_t::request_post(const void *data, std::size_t len) noexcept
255 {
256  curl_easy_setopt(curl_easy, CURLOPT_POSTFIELDSIZE_LARGE, len);
257  curl_easy_setopt(curl_easy, CURLOPT_POSTFIELDS, data);
258 }
259 void Easy_ref_t::request_post(readback_t readback, void *userp, std::size_t len) noexcept
260 {
261  request_post(nullptr, len);
262 
263  curl_easy_setopt(curl_easy, CURLOPT_READFUNCTION, readback);
264  curl_easy_setopt(curl_easy, CURLOPT_READDATA, userp);
265 }
266 
267 auto Easy_ref_t::perform() noexcept -> perform_ret_t
268 {
269  return check_perform(curl_easy_perform(curl_easy), "curl::Easy_ref_t::perform");
270 }
271 
272 auto operator & (Easy_ref_t::PauseOptions x, Easy_ref_t::PauseOptions y) noexcept
273 {
274  using type = std::underlying_type_t<Easy_ref_t::PauseOptions>;
275 
276  return static_cast<type>(x) & static_cast<type>(y);
277 }
278 auto Easy_ref_t::set_pause(PauseOptions option) noexcept -> Ret_except<code, std::bad_alloc, Exception>
279 {
280  int bitmask = 0;
281  if (option & PauseOptions::recv)
282  bitmask |= CURLPAUSE_RECV;
283  if (option & PauseOptions::send)
284  bitmask |= CURLPAUSE_SEND;
285 
286  auto result = curl_easy_pause(curl_easy, bitmask);
287  if (result == CURLE_WRITE_ERROR)
288  return {code::writeback_error};
289  else if (result == CURLE_OUT_OF_MEMORY)
290  return {std::bad_alloc{}};
291  else if (result != CURLE_OK)
292  return {Exception{result}};
293  else
294  return {code::ok};
295 }
296 
297 long Easy_ref_t::get_response_code() const noexcept
298 {
299  long response_code;
300  curl_easy_getinfo(curl_easy, CURLINFO_RESPONSE_CODE, &response_code);
301  return response_code;
302 }
303 
304 std::size_t Easy_ref_t::getinfo_sizeof_request() const noexcept
305 {
306  long size;
307  curl_easy_getinfo(curl_easy, CURLINFO_REQUEST_SIZE, &size);
308  return size;
309 }
310 std::size_t Easy_ref_t::getinfo_sizeof_uploaded() const noexcept
311 {
312  curl_off_t ul;
313  if (curl_easy_getinfo(curl_easy, CURLINFO_SIZE_UPLOAD_T, &ul)) {
314  double bytes;
315  curl_easy_getinfo(curl_easy, CURLINFO_SIZE_UPLOAD, &bytes);
316  return static_cast<std::size_t>(bytes);
317  }
318  return ul;
319 }
320 std::size_t Easy_ref_t::getinfo_sizeof_response_header() const noexcept
321 {
322  long size;
323  curl_easy_getinfo(curl_easy, CURLINFO_HEADER_SIZE, &size);
324  return size;
325 }
326 std::size_t Easy_ref_t::getinfo_sizeof_response_body() const noexcept
327 {
328  curl_off_t dl;
329  if (curl_easy_getinfo(curl_easy, CURLINFO_SIZE_DOWNLOAD_T, &dl) == CURLE_UNKNOWN_OPTION) {
330  double bytes;
331  curl_easy_getinfo(curl_easy, CURLINFO_SIZE_DOWNLOAD, &bytes);
332  return static_cast<std::size_t>(bytes);
333  }
334  return dl;
335 }
336 std::size_t Easy_ref_t::getinfo_transfer_time() const noexcept
337 {
338  curl_off_t total;
339  if (curl_easy_getinfo(curl_easy, CURLINFO_TOTAL_TIME_T, &total) == CURLE_UNKNOWN_OPTION) {
340  double seconds;
341  curl_easy_getinfo(curl_easy, CURLINFO_TOTAL_TIME, &seconds);
342  return static_cast<std::size_t>(seconds * 1000);
343  }
344  return total;
345 }
346 
347 auto Easy_ref_t::getinfo_redirect_url() const noexcept -> const char*
348 {
349  char *url = nullptr;
350  curl_easy_getinfo(curl_easy, CURLINFO_REDIRECT_URL, &url);
351  return url;
352 }
353 auto Easy_ref_t::getinfo_effective_url() const noexcept -> const char*
354 {
355  char *url = nullptr;
356  curl_easy_getinfo(curl_easy, CURLINFO_EFFECTIVE_URL, &url);
357  return url;
358 }
359 
360 auto Easy_ref_t::getinfo_cookie_list() const noexcept ->
361  Ret_except<utils::slist, curl::NotBuiltIn_error>
362 {
363  curl_slist *cookies = nullptr;
364  if (curl_easy_getinfo(curl_easy, CURLINFO_COOKIELIST, &cookies) == CURLE_UNKNOWN_OPTION)
365  return {curl::NotBuiltIn_error{"cookies not supported"}};
366 
367  return {utils::slist{cookies}};
368 }
369 
370 auto Easy_ref_t::get_active_socket() const noexcept -> curl_socket_t
371 {
372  curl_socket_t socketfd = CURL_SOCKET_BAD;
373  curl_easy_getinfo(curl_easy, CURLINFO_ACTIVESOCKET, &socketfd);
374  return socketfd;
375 }
376 
378 {
380  curl_easy_setopt(curl_easy, CURLOPT_NOBODY, 1);
381 }
382 } /* namespace curl */
curl::Easy_ref_t::PauseOptions::recv
@ recv
curl::Easy_ref_t::set_url
auto set_url(const char *url) noexcept -> Ret_except< void, std::bad_alloc >
Definition: curl_easy.cc:101
curl::Easy_ref_t::erase_all_session_cookies_in_mem
auto erase_all_session_cookies_in_mem() noexcept -> Ret_except< void, std::bad_alloc, curl::NotBuiltIn_error >
Definition: curl_easy.cc:169
curl::Easy_ref_t::set_cookielist
auto set_cookielist(const char *cookie) noexcept -> Ret_except< void, std::bad_alloc, curl::NotBuiltIn_error >
Definition: curl_easy.cc:149
curl::Easy_ref_t::setup_establish_connection_only
void setup_establish_connection_only() noexcept
Definition: curl_easy.cc:377
curl::Easy_ref_t::set_verbose
void set_verbose(FILE *stderr_stream_arg) noexcept
Definition: curl_easy.cc:64
curl::Easy_ref_t::set_useragent
auto set_useragent(const char *useragent) noexcept -> Ret_except< void, std::bad_alloc >
Definition: curl_easy.cc:194
curl::Easy_ref_t::reload_cookies_from_file
auto reload_cookies_from_file() noexcept -> Ret_except< void, std::bad_alloc, curl::NotBuiltIn_error >
Definition: curl_easy.cc:179
curl::Easy_ref_t::getinfo_effective_url
auto getinfo_effective_url() const noexcept -> const char *
Definition: curl_easy.cc:353
curl::Easy_ref_t::set_follow_location
void set_follow_location(long redir) noexcept
Definition: curl_easy.cc:185
curl::Easy_ref_t::set_cookie
auto set_cookie(const char *cookies) noexcept -> Ret_except< void, std::bad_alloc, curl::NotBuiltIn_error >
Definition: curl_easy.cc:120
curl::Easy_ref_t::getinfo_sizeof_uploaded
std::size_t getinfo_sizeof_uploaded() const noexcept
Definition: curl_easy.cc:310
curl::Url_ref_t
Definition: curl_url.hpp:24
curl::Easy_ref_t::set_ip_addr_only
auto set_ip_addr_only(const char *ip_addr) noexcept -> Ret_except< void, std::bad_alloc >
Definition: curl_easy.cc:210
curl::Easy_ref_t::getinfo_redirect_url
auto getinfo_redirect_url() const noexcept -> const char *
Definition: curl_easy.cc:347
curl::Easy_ref_t::request_get
void request_get() noexcept
Definition: curl_easy.cc:250
curl::Easy_ref_t::get_error_buffer_size
static std::size_t get_error_buffer_size() noexcept
Definition: curl_easy.cc:71
curl::Easy_ref_t::request_post
void request_post(const void *data, std::size_t len) noexcept
Definition: curl_easy.cc:254
curl::Easy_ref_t::set_encoding
auto set_encoding(const char *encoding) noexcept -> Ret_except< void, std::bad_alloc >
Definition: curl_easy.cc:199
curl::Easy_ref_t::header_option::unspecified
@ unspecified
curl::curl_t::disable_signal_handling_v
bool disable_signal_handling_v
Definition: curl.hpp:147
curl::Easy_ref_t
Definition: curl_easy.hpp:53
curl::Easy_ref_t::header_option
header_option
Definition: curl_easy.hpp:447
curl::Easy_ref_t::flush_cookies_to_jar
auto flush_cookies_to_jar() noexcept -> Ret_except< void, std::bad_alloc, curl::NotBuiltIn_error >
Definition: curl_easy.cc:174
curl::utils::slist
Definition: curl_slist.hpp:21
curl::Easy_ref_t::set_pause
auto set_pause(PauseOptions option) noexcept -> Ret_except< code, std::bad_alloc, Exception >
Definition: curl_easy.cc:278
curl::Easy_ref_t::erase_all_cookies_in_mem
auto erase_all_cookies_in_mem() noexcept -> Ret_except< void, std::bad_alloc, curl::NotBuiltIn_error >
Definition: curl_easy.cc:164
curl::Easy_ref_t::set_interface
auto set_interface(const char *value) noexcept -> Ret_except< void, std::bad_alloc >
Definition: curl_easy.cc:205
curl::Easy_ref_t::getinfo_transfer_time
std::size_t getinfo_transfer_time() const noexcept
Definition: curl_easy.cc:336
curl::Easy_ref_t::getinfo_cookie_list
auto getinfo_cookie_list() const noexcept -> Ret_except< utils::slist, curl::NotBuiltIn_error >
Definition: curl_easy.cc:360
curl::curl_t::stderr_stream
FILE * stderr_stream
Definition: curl.hpp:115
curl::curl_t
Definition: curl.hpp:62
curl::Easy_ref_t::pin_publickey
auto pin_publickey(const char *pubkey) -> Ret_except< void, std::bad_alloc, curl::NotBuiltIn_error >
Definition: curl_easy.cc:107
curl::utils::slist::get_underlying_ptr
auto get_underlying_ptr() const noexcept -> void *
Definition: curl_slist.cc:81
curl::Easy_ref_t::set_private
void set_private(void *userp) noexcept
Definition: curl_easy.cc:80
curl::Easy_ref_t::set_cookiefile
auto set_cookiefile(const char *cookie_filename) noexcept -> Ret_except< void, curl::NotBuiltIn_error >
Definition: curl_easy.cc:131
curl::NotBuiltIn_error
Definition: curl.hpp:25
curl::Easy_ref_t::Exception
Definition: curl_easy.hpp:65
curl::Easy_ref_t::code
code
Definition: curl_easy.hpp:551
curl::Easy_ref_t::set_url
void set_url(const Url_ref_t &url) noexcept
Definition: curl_easy.cc:97
curl::Url_ref_t::url
char * url
Definition: curl_url.hpp:33
curl::Easy_ref_t::set_timeout
void set_timeout(unsigned long timeout) noexcept
Definition: curl_easy.cc:226
curl::Easy_ref_t::set_writeback
void set_writeback(writeback_t writeback, void *userp) noexcept
Definition: curl_easy.cc:91
CHECK_OOM
#define CHECK_OOM(code)
Definition: curl_easy.cc:10
curl::Easy_ref_t::request_post
void request_post(readback_t readback, void *userp, std::size_t len=-1) noexcept
Definition: curl_easy.cc:259
curl::Easy_ref_t::getinfo_sizeof_response_header
std::size_t getinfo_sizeof_response_header() const noexcept
Definition: curl_easy.cc:320
curl::curl_t::dup_easy
auto dup_easy(const Easy_t &easy, std::size_t buffer_size=0) noexcept -> Easy_t
Definition: curl_easy.cc:43
curl::Easy_ref_t::start_new_cookie_session
void start_new_cookie_session() noexcept
Definition: curl_easy.cc:160
curl::Easy_ref_t::set_nobody
void set_nobody(bool enable) noexcept
Definition: curl_easy.cc:245
curl::curl_t::Easy_deleter
Definition: curl.hpp:271
curl::Easy_ref_t::set_cookiejar
auto set_cookiejar(const char *cookie_filename) noexcept -> Ret_except< void, std::bad_alloc, curl::NotBuiltIn_error >
Definition: curl_easy.cc:138
curl::Easy_ref_t::getinfo_sizeof_response_body
std::size_t getinfo_sizeof_response_body() const noexcept
Definition: curl_easy.cc:326
curl::Easy_ref_t::get_active_socket
auto get_active_socket() const noexcept -> curl_socket_t
Definition: curl_easy.cc:370
curl::Easy_ref_t::set_error_buffer
void set_error_buffer(char *error_buffer) noexcept
Definition: curl_easy.cc:75
curl::Easy_ref_t::set_http_header
void set_http_header(const utils::slist &l, header_option option=header_option::unspecified) noexcept
Definition: curl_easy.cc:231
curl::Easy_ref_t::PauseOptions::send
@ send
curl::Easy_ref_t::get_private
void * get_private() const noexcept
Definition: curl_easy.cc:84
curl::Easy_ref_t::PauseOptions
PauseOptions
Definition: curl_easy.hpp:572
curl::curl_t::create_easy
auto create_easy(std::size_t buffer_size=0) noexcept -> Easy_t
Definition: curl_easy.cc:20
curl::Easy_ref_t::getinfo_sizeof_request
std::size_t getinfo_sizeof_request() const noexcept
Definition: curl_easy.cc:304