curl-cpp
static c++17 wrapper for curl with -fno-exceptions support
curl_share.hpp
1 #ifndef __curl_cpp_curl_share_HPP__
2 # define __curl_cpp_curl_share_HPP__
3 
4 # include "curl.hpp"
5 # include "utils/shared_mutex.hpp"
6 # include "return-exception/ret-exception.hpp"
7 
8 # include <curl/curl.h>
9 # include <memory>
10 
11 namespace curl {
12 /**
13  * @example curl_share.cc
14  *
15  * All easy handler must be removed before Share_base
16  * can be destroyed.
17  *
18  * To use in a thread-safe manner, you must call
19  * add_lock with non-nullptr.
20  */
21 class Share_base {
22 protected:
23  curl_t::Share_t curl_share;
24 
25 public:
26  /**
27  * @param share must be != nullptr
28  *
29  * After this ctor call, share.get() == nullptr,
30  * this class will take over the ownership.
31  */
32  Share_base(curl_t::Share_t &&share) noexcept;
33 
34  /**
35  * Since libcurl doesn't provide a dup
36  * function for curl_share, neither will Share_base.
37  */
38  Share_base(const Share_base&) = delete;
39 
40  /**
41  * @param other bool(other) can be false, but then this new instance
42  * of Share_base will be unusable.
43  */
44  Share_base(Share_base &&other) noexcept;
45 
46  /**
47  * Since libcurl doesn't provide a dup
48  * function for curl_share, neither will Share_base.
49  */
50  Share_base& operator = (const Share_base&) = delete;
51  /**
52  * @param other if bool(other) is true, then it will be unusable after this operation.
53  * <br>Otherwise, this object will also be destroyed.
54  * @param this this object can also be an unusable object which bool(*this) is false.
55  * <br>Calling this function on an unusable object will make that object again
56  * usable.
57  *
58  * **NOTE that if this object still holds easy handler and bool(other) is false,
59  * then it is undefined behavior.**
60  *
61  */
62  Share_base& operator = (Share_base &&other) noexcept;
63 
64  /**
65  * @return true if this object is usable, false otherwise.
66  */
67  operator bool () const noexcept;
68 
69  /**
70  * Defines data to share among Easy_t handles.
71  */
72  enum class Options {
73  none = CURL_LOCK_DATA_NONE,
74  cookie = CURL_LOCK_DATA_COOKIE,
75  /**
76  * share cached DNS hosts.
77  *
78  * If the libcurl has cookies disabled, then
79  * enable_sharing(Options::cookie) will return 0.
80  *
81  * NOTE that Multi_t interface share this by default
82  * without setting this option.
83  */
84  dns = CURL_LOCK_DATA_DNS,
85  /**
86  * SSL session IDs will be shared across the easy handles
87  * using this shared object.
88  *
89  * This will reduce the time spent in the SSL handshake
90  * when reconnecting to the same server.
91  *
92  * If curl_t::has_ssl_session_sharing_support() == false, setting
93  * this option shares nothing.
94  *
95  * NOTE SSL session IDs are reused within the same easy handle by default.
96  */
97  ssl_session = CURL_LOCK_DATA_SSL_SESSION,
98  /**
99  * If curl_t::has_connection_cache_sharing_support() == false, setting
100  * this option shares nothing.
101  *
102  * NOTE that Multi_t interface share this by default
103  * without setting this option.
104  */
105  connection_cache = CURL_LOCK_DATA_CONNECT,
106  /**
107  * Share Public Suffix List.
108  *
109  * If curl_t::has_psl_sharing_support() == false, setting
110  * this option shares nothing.
111  *
112  * NOTE that Multi_t interface share this by default
113  * without setting this option.
114  */
115  psl = CURL_LOCK_DATA_PSL,
116  };
117 
118  /**
119  * Possible value of data: same as enum class Options
120  *
121  * Possible value of access:
122  * - CURL_LOCK_ACCESS_SHARED,
123  * - CURL_LOCK_ACCESS_SINGLE,
124  * The mutex you used must be pthread_rwlock_t or std::shared_mutex.
125  */
126  using lock_function_t = void (*)(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr);
127  /**
128  * Possible value of data: same as enum class Options
129  *
130  * Possible value of access:
131  * - CURL_LOCK_ACCESS_SHARED,
132  * - CURL_LOCK_ACCESS_SINGLE,
133  * The mutex you used must be pthread_rwlock_t or std::shared_mutex.
134  */
135  using unlock_function_t = void (*)(CURL *handle, curl_lock_data data, void *userptr);
136 
137  /**
138  * Setting lock_func and unlock_func to nullptr disable locking.
139  */
140  void add_lock(lock_function_t lock_func, unlock_function_t unlock_func, void *userptr) noexcept;
141 
142  /**
143  * @param option must be one of enum class Options.
144  * <br>Cannot be or-ed value.
145  *
146  * All sharing enable/disable must be done when no easy is added
147  * or all easy is removed.
148  */
149  auto enable_sharing(Options option) noexcept -> Ret_except<int, std::bad_alloc>;
150  /**
151  * @param option must be one of enum class Options.
152  * <br>Cannot be or-ed value.
153  *
154  * All sharing enable/disable must be done when no easy is added
155  * or all easy is removed.
156  */
157  void disable_sharing(Options option) noexcept;
158 
159  void add_easy(Easy_ref_t &easy) noexcept;
160  void remove_easy(Easy_ref_t &easy) noexcept;
161 };
162 
163 /**
164  * @tparam Shared_mutex_t For shared or single lock, their unlock function must be
165  * the same function -- Shared_mutex_t::unlock().
166  * <br>If Shared_mutex_t lock, lock_shared or unlock
167  * throw an exception, it would terminte the program.
168  * <br>If Shared_mutex_t has type Ret_except_t (Ret == void), then
169  * Shared_mutex_t(Ret_except_t&) would be called.
170  * <br>Pass void to disable locking, which make
171  * multithreaded use unsafe.
172  */
173 template <class Shared_mutex_t = utils::shared_mutex>
174 class Share: public Share_base {
175  static constexpr const std::size_t mutex_num = 5;
176 
177  Shared_mutex_t mutexes[mutex_num];
178 
179  auto get_mutex(Options option) noexcept -> Shared_mutex_t*
180  {
181  switch (option) {
182  case Options::none:
183  return nullptr;
184  case Options::cookie:
185  return &mutexes[0];
186  case Options::dns:
187  return &mutexes[1];
189  return &mutexes[2];
191  return &mutexes[3];
192  case Options::psl:
193  return &mutexes[4];
194  default:
195  return nullptr;
196  }
197  }
198 
199 public:
200  using Share_base::Share_base;
201 
202  /**
203  * @param base any usable Share_base object.
204  */
205  Share(Share_base &&base) noexcept:
206  Share_base{std::move(base)}
207  {}
208 
209  void enable_multithreaded_share() noexcept
210  {
211  add_lock([](CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr) noexcept {
212  auto &share = *static_cast<Share*>(userptr);
213  auto *mutex = share.get_mutex(static_cast<Share_base::Options>(data));
214 
215  if (!mutex)
216  return;
217 
218  if (access == CURL_LOCK_ACCESS_SHARED)
219  mutex->lock_shared();
220  else
221  mutex->lock();
222  }, [](CURL *handle, curl_lock_data data, void *userptr) noexcept {
223  auto &share = *static_cast<Share*>(userptr);
224  auto *mutex = share.get_mutex(static_cast<Share_base::Options>(data));
225 
226  if (mutex)
227  mutex->unlock();
228  }, this);
229  }
230  void disable_multithreaded_share() noexcept
231  {
232  add_lock(nullptr, nullptr, nullptr);
233  }
234 };
235 
236 /**
237  * Sepecialiation of Share that does no locking, thus
238  * is multithread unsafe.
239  *
240  * This provides the same interface as any other Share<T>,
241  * so that your template can simply pass void to
242  * disable locking.
243  */
244 template <>
245 class Share<void>: public Share_base {
246 public:
247  using Share_base::Share_base;
248 
249  constexpr void enable_multithreaded_share() const noexcept {}
250  constexpr void disable_multithreaded_share() const noexcept {}
251 };
252 } /* namespace curl */
253 
254 #endif
curl::Share_base::operator=
Share_base & operator=(Share_base &&other) noexcept
Definition: curl_share.cc:22
curl::Share_base::Options
Options
Definition: curl_share.hpp:72
curl::Share::Share
Share(Share_base &&base) noexcept
Definition: curl_share.hpp:205
curl::Easy_ref_t
Definition: curl_easy.hpp:53
curl::Share_base::Share_base
Share_base(const Share_base &)=delete
curl::utils::shared_mutex
Definition: shared_mutex.hpp:18
curl::Share_base::Options::connection_cache
@ connection_cache
curl::Share_base::operator=
Share_base & operator=(const Share_base &)=delete
curl::curl_t
Definition: curl.hpp:62
curl::Share_base::Options::psl
@ psl
curl::Share_base::disable_sharing
void disable_sharing(Options option) noexcept
Definition: curl_share.cc:50
curl::Share_base
Definition: curl_share.hpp:21
curl::Share_base::Options::dns
@ dns
curl::Share_base::enable_sharing
auto enable_sharing(Options option) noexcept -> Ret_except< int, std::bad_alloc >
Definition: curl_share.cc:40
curl::Share_base::Share_base
Share_base(Share_base &&other) noexcept
Definition: curl_share.cc:19
curl::Share_base::Options::ssl_session
@ ssl_session
curl::Share_base::operator bool
operator bool() const noexcept
Definition: curl_share.cc:28
curl::Share_base::Share_base
Share_base(curl_t::Share_t &&share) noexcept
Definition: curl_share.cc:15
curl::Share_base::add_lock
void add_lock(lock_function_t lock_func, unlock_function_t unlock_func, void *userptr) noexcept
Definition: curl_share.cc:33