uvco 0.1
Loading...
Searching...
No Matches
uvco::UvCurlContext_ Class Reference

Classes

struct  PollCtx
 A uv poll handle with its associated CurlRequest_. More...

Public Member Functions

 UvCurlContext_ (const Loop &loop)
 UvCurlContext_ (const UvCurlContext_ &)=delete
 UvCurlContext_ (UvCurlContext_ &&)=delete
UvCurlContext_operator= (const UvCurlContext_ &)=delete
UvCurlContext_operator= (UvCurlContext_ &&)=delete
 ~UvCurlContext_ ()
void addHandle (CURL *handle)
 Add a Curl easy handle to the multi handle.
void removeHandle (CURL *handle)
 Remove a Curl easy handle from the multi handle.
void checkCurlInfo () const
 Checks with Curl for any completed/errored requests.
void initPoll (CurlRequestCore_ *request, curl_socket_t newSocket) noexcept
void uvStartPoll (curl_socket_t socket, unsigned int events) noexcept
void uvStopPoll (curl_socket_t socket) noexcept
CurlRequestCore_getRequest (curl_socket_t socket)
Promise< void > close ()
 Close all open sockets and the timer.

Static Public Member Functions

static int curlSocketFunction (CURL *easy, curl_socket_t socket, int action, void *userp, void *)
static int curlTimerFunction (CURLM *, long timeoutMs, void *userp)
 Called by Curl to inform us of a timeout to set up.
static void onUvTimeout (uv_timer_t *timer)
 Called by libuv when a timeout timer expires.
static void onCurlSocketActive (uv_poll_t *poll, int status, int events)
 Called by libuv when there is activity on a curl socket.

Private Attributes

CURLM * multi_
uv_loop_t * loop_
std::unique_ptr< uv_timer_t > timer_
std::map< curl_socket_t, PollCtxpolls_

Detailed Description

Contains references to the libcurl multi handle and the libuv loop. Only valid as long as one loop instance is running. Used as context for curl socket functions (SOCKETDATA) and timer functions (TIMERDATA).

Constructor & Destructor Documentation

◆ UvCurlContext_() [1/3]

uvco::UvCurlContext_::UvCurlContext_ ( const Loop & loop)
explicit
403 : multi_{curl_multi_init()}, loop_{loop.uvloop()},
404 timer_{std::make_unique<uv_timer_t>()} {
405 uv_timer_init(loop_, timer_.get());
406 // Set handle data for onUvTimeout callback.
407 setData(timer_.get(), this);
408
409 // Initialize Curl callbacks.
410 curl_multi_setopt(multi_, CURLMOPT_SOCKETFUNCTION,
412 curl_multi_setopt(multi_, CURLMOPT_SOCKETDATA, this);
413 curl_multi_setopt(multi_, CURLMOPT_TIMERFUNCTION,
415 curl_multi_setopt(multi_, CURLMOPT_TIMERDATA, this);
416}
static int curlSocketFunction(CURL *easy, curl_socket_t socket, int action, void *userp, void *)
Definition curl.cc:492
std::unique_ptr< uv_timer_t > timer_
Definition curl.cc:193
uv_loop_t * loop_
Definition curl.cc:192
CURLM * multi_
Definition curl.cc:191
static int curlTimerFunction(CURLM *, long timeoutMs, void *userp)
Called by Curl to inform us of a timeout to set up.
Definition curl.cc:444
void setData(Handle *handle, Data *data)
Obtain data pointer set on request with type cast. Data may be nullptr.
Definition internal_utils.h:77

◆ UvCurlContext_() [2/3]

uvco::UvCurlContext_::UvCurlContext_ ( const UvCurlContext_ & )
delete

◆ UvCurlContext_() [3/3]

uvco::UvCurlContext_::UvCurlContext_ ( UvCurlContext_ && )
delete

◆ ~UvCurlContext_()

uvco::UvCurlContext_::~UvCurlContext_ ( )
inline
77 {
78 if (multi_ != nullptr) {
79 curl_multi_cleanup(multi_);
80 multi_ = nullptr;
81 }
82
83 for (auto &[socket, poll] : polls_) {
84 closeHandle(poll.poll.release());
85 }
86 polls_.clear();
87 if (timer_ != nullptr) {
88 closeHandle(timer_.release());
89 }
90 BOOST_ASSERT_MSG(polls_.empty(),
91 "UvCurlContext_ must be closed before destruction");
92 }
std::map< curl_socket_t, PollCtx > polls_
Definition curl.cc:194
void closeHandle(Handle *handle, void(*closer)(CloserArg *, void(*)(uv_handle_t *)))
Definition close.h:37

Member Function Documentation

◆ addHandle()

void uvco::UvCurlContext_::addHandle ( CURL * handle)
inline

Add a Curl easy handle to the multi handle.

95{ curl_multi_add_handle(multi_, handle); }

◆ checkCurlInfo()

void uvco::UvCurlContext_::checkCurlInfo ( ) const

Checks with Curl for any completed/errored requests.

418 {
419 CURLMsg *msg{};
420 long responseCode{};
421 long verifyResult{};
422 int inQueue{};
423
424 // Check for completed requests.
425 while (nullptr != (msg = curl_multi_info_read(multi_, &inQueue))) {
426 if (msg->msg == CURLMSG_DONE) {
427 CurlRequestCore_ *request{};
428 BOOST_VERIFY(CURLE_OK == curl_easy_getinfo(msg->easy_handle,
429 CURLINFO_PRIVATE, &request));
430 BOOST_VERIFY(CURLE_OK == curl_easy_getinfo(msg->easy_handle,
431 CURLINFO_RESPONSE_CODE,
432 &responseCode));
433 BOOST_VERIFY(CURLE_OK == curl_easy_getinfo(msg->easy_handle,
434 CURLINFO_SSL_VERIFYRESULT,
435 &verifyResult));
436 request->setCurlStatus(msg->data.result);
437 request->setResponseCode(responseCode);
438 request->setVerifyResult(verifyResult);
439 request->onError(0);
440 }
441 }
442}

◆ close()

Promise< void > uvco::UvCurlContext_::close ( )
inline

Close all open sockets and the timer.

165 {
166 if (multi_ != nullptr) {
167 curl_multi_cleanup(multi_);
168 multi_ = nullptr;
169 }
170
171 std::vector<Promise<void>> promises;
172 promises.reserve(polls_.size());
173 for (auto &[socket, poll] : polls_) {
174 closeHandle(poll.poll.release());
175 }
176 for (auto &promise : promises) {
177 co_await promise;
178 }
179 polls_.clear();
180 closeHandle(timer_.release());
181 timer_.reset();
182 }

◆ curlSocketFunction()

int uvco::UvCurlContext_::curlSocketFunction ( CURL * easy,
curl_socket_t socket,
int action,
void * userp,
void *  )
static

Called by Curl to inform us of a new socket to monitor or a socket to be removed.

494 {
495 auto *context = static_cast<UvCurlContext_ *>(userp);
496
497 switch (action) {
498 case CURL_POLL_IN:
499 case CURL_POLL_OUT:
500 case CURL_POLL_INOUT: {
501 unsigned int watchFor{};
502
503 // Obtain request object so it can be associated with socket for error
504 // handling.
505 CurlRequestCore_ *request{};
506 curl_easy_getinfo(easy, CURLINFO_PRIVATE, &request);
507
508 if (action != CURL_POLL_IN) {
509 watchFor |= UV_WRITABLE;
510 }
511 if (action != CURL_POLL_OUT) {
512 watchFor |= UV_READABLE;
513 }
514
515 context->initPoll(request, socket);
516 context->uvStartPoll(socket, watchFor);
517 break;
518 }
519
520 case CURL_POLL_REMOVE: {
521 context->uvStopPoll(socket);
522 break;
523 }
524
525 default:
526 BOOST_ASSERT(false);
527 }
528
529 return 0;
530}
UvCurlContext_(const Loop &loop)
Definition curl.cc:402

◆ curlTimerFunction()

int uvco::UvCurlContext_::curlTimerFunction ( CURLM * ,
long timeoutMs,
void * userp )
static

Called by Curl to inform us of a timeout to set up.

445 {
446 auto *curl = static_cast<UvCurlContext_ *>(userp);
447 uv_timer_t &timer = *curl->timer_;
448 if (timeoutMs < 0) {
449 uv_timer_stop(&timer);
450 } else {
451 uv_timer_start(&timer, UvCurlContext_::onUvTimeout, timeoutMs, 0);
452 }
453 return 0;
454}
static void onUvTimeout(uv_timer_t *timer)
Called by libuv when a timeout timer expires.
Definition curl.cc:456

◆ getRequest()

CurlRequestCore_ & uvco::UvCurlContext_::getRequest ( curl_socket_t socket)
inline

Returns the CurlRequest associated with the given socket. This is essential for notifying individual requests of socket errors; socket errors occur in a uv callbcak which only has access to the socket.

158 {
159 const auto it = polls_.find(socket);
160 BOOST_ASSERT(it != polls_.end());
161 return *it->second.request;
162 }

◆ initPoll()

void uvco::UvCurlContext_::initPoll ( CurlRequestCore_ * request,
curl_socket_t newSocket )
inlinenoexcept
124 {
125 const auto it = polls_.find(newSocket);
126 if (it != polls_.end()) {
127 // Already initialized. Ensure that socket is associated with the correct
128 // request in case Curl reuses a socket.
129 it->second.request = request;
130 return;
131 }
132 const auto newPollCtx =
133 polls_.emplace(newSocket, std::make_unique<uv_poll_t>());
134 uv_poll_t &poll = *newPollCtx.first->second.poll;
135 newPollCtx.first->second.request = request;
136 uv_poll_init_socket(loop_, &poll, newSocket);
137 setData(&poll, this);
138 }

◆ onCurlSocketActive()

void uvco::UvCurlContext_::onCurlSocketActive ( uv_poll_t * poll,
int status,
int events )
static

Called by libuv when there is activity on a curl socket.

464 {
465 int runningHandles{};
466 unsigned int flags{};
467 auto *context = getData<UvCurlContext_>(poll);
468
469 curl_socket_t socket{};
470 const uv_status filenoStatus = uv_fileno((uv_handle_t *)poll, &socket);
471 if (filenoStatus != 0) {
472 throw UvcoException{filenoStatus, "while getting file descriptor"};
473 }
474
475 if (status != 0) {
476 context->getRequest(socket).onError(status);
477 return;
478 }
479
480 if ((static_cast<unsigned>(events) & UV_READABLE) != 0) {
481 flags |= CURL_CSELECT_IN;
482 }
483 if ((static_cast<unsigned>(events) & UV_WRITABLE) != 0) {
484 flags |= CURL_CSELECT_OUT;
485 }
486
487 curl_multi_socket_action(context->multi_, socket, static_cast<int>(flags),
488 &runningHandles);
489 context->checkCurlInfo();
490}
Into * getData(const Handle *handle)
Obtain data pointer set on handle with nullptr check and type cast.
Definition internal_utils.h:42
int uv_status
Result of a libuv operation, an errno error code.
Definition internal_utils.h:22

◆ onUvTimeout()

void uvco::UvCurlContext_::onUvTimeout ( uv_timer_t * timer)
static

Called by libuv when a timeout timer expires.

456 {
457 auto *curl = getData<UvCurlContext_>(timer);
458 int runningHandles = 0;
459 curl_multi_socket_action(curl->multi_, CURL_SOCKET_TIMEOUT, 0,
460 &runningHandles);
461}

◆ operator=() [1/2]

UvCurlContext_ & uvco::UvCurlContext_::operator= ( const UvCurlContext_ & )
delete

◆ operator=() [2/2]

UvCurlContext_ & uvco::UvCurlContext_::operator= ( UvCurlContext_ && )
delete

◆ removeHandle()

void uvco::UvCurlContext_::removeHandle ( CURL * handle)
inline

Remove a Curl easy handle from the multi handle.

98{ curl_multi_remove_handle(multi_, handle); }

◆ uvStartPoll()

void uvco::UvCurlContext_::uvStartPoll ( curl_socket_t socket,
unsigned int events )
inlinenoexcept
140 {
141 const auto it = polls_.find(socket);
142 BOOST_ASSERT(it != polls_.end());
143 uv_poll_t &poll = *it->second.poll;
144 uv_poll_start(&poll, static_cast<int>(events),
146 }
static void onCurlSocketActive(uv_poll_t *poll, int status, int events)
Called by libuv when there is activity on a curl socket.
Definition curl.cc:463

◆ uvStopPoll()

void uvco::UvCurlContext_::uvStopPoll ( curl_socket_t socket)
inlinenoexcept
148 {
149 const auto it = polls_.find(socket);
150 if (it != polls_.end()) {
151 uv_poll_stop(it->second.poll.get());
152 }
153 }

Member Data Documentation

◆ loop_

uv_loop_t* uvco::UvCurlContext_::loop_
private

◆ multi_

CURLM* uvco::UvCurlContext_::multi_
private

◆ polls_

std::map<curl_socket_t, PollCtx> uvco::UvCurlContext_::polls_
private

◆ timer_

std::unique_ptr<uv_timer_t> uvco::UvCurlContext_::timer_
private

The documentation for this class was generated from the following file: