The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
io.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 3c32a94e5a5aaffd95ecfa6cf6649ad5ba9e7526 $
19 * @file curl/io.c
20 * @brief Implement asynchronous callbacks for curl
21 *
22 * @copyright 2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24#include <freeradius-devel/curl/base.h>
25#include <freeradius-devel/util/debug.h>
26#include <freeradius-devel/util/syserror.h>
27
28/*
29 * CURL headers do:
30 *
31 * #define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param)
32 */
34DIAG_OFF(disabled-macro-expansion)
36#define SET_MOPTION(_mandle, _opt, _val)\
37do {\
38 if ((ret = curl_multi_setopt(mandle, _opt, _val)) != CURLM_OK) {\
39 option = STRINGIFY(_opt);\
40 goto error;\
41 }\
42} while (0)
43
44/** De-queue curl requests and wake up the requests that initiated them
45 *
46 * @param[in] mhandle containing the event loop and request counter.
47 * @param[in] mandle to dequeue curl easy handles/responses from.
48 */
49static inline void _fr_curl_io_demux(fr_curl_handle_t *mhandle, CURLM *mandle)
50{
51 struct CURLMsg *m;
52 int msg_queued = 0;
53
54 while ((m = curl_multi_info_read(mandle, &msg_queued))) {
55 switch (m->msg) {
56 case CURLMSG_DONE:
57 {
59 request_t *request = NULL;
60 CURL *candle = m->easy_handle;
61 CURLcode ret;
62
63 fr_assert(candle);
64
65 mhandle->transfers--;
66
67 ret = curl_easy_getinfo(candle, CURLINFO_PRIVATE, &randle);
68 /*
69 * In curl 7.61 we got bad request data returned
70 * here after cancellations were processed.
71 *
72 * For debug builds if the value is equal to
73 * 0xdeadc341, we know the request was cancelled.
74 *
75 * There is no good work around for this other than
76 * upgrading to a newer version of curl.
77 */
78 talloc_get_type_abort(randle, fr_curl_io_request_t);
79 if (!fr_cond_assert_msg(ret == CURLE_OK,
80 "Failed retrieving request data from CURL easy handle (candle)")) {
81 curl_multi_remove_handle(mandle, candle);
82 return;
83 }
84 request = randle->request;
85
86 REQUEST_VERIFY(request);
87
88 /*
89 * If the request failed, say why...
90 */
91 if (m->data.result != CURLE_OK) {
92 REDEBUG("curl request failed: %s (%i)",
93 curl_easy_strerror(m->data.result), m->data.result);
94 }
95 randle->result = m->data.result;
96
97 /*
98 * This needs to be done last, else m->data.result
99 * ends up being junk.
100 */
101 curl_multi_remove_handle(mandle, candle);
102
104 }
105 break;
106
107 default:
108#ifndef NDEBUG
109 DEBUG4("Got unknown msg (%i) when dequeueing curl responses", msg_queued);
110#endif
111 break;
112 }
113 }
114}
115
116/** libcurl's timer expired
117 *
118 * @param[in] tl the timer was inserted into.
119 * @param[in] now The current time according to the event loop.
120 * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
121 */
123{
124 fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
125 CURLM *mandle = mhandle->mandle;
126 CURLMcode ret;
127 int running = 0;
128
129 DEBUG4("multi-handle %p - Timer fired", mandle);
130
131 ret = curl_multi_socket_action(mandle, CURL_SOCKET_TIMEOUT, 0, &running);
132 if (ret != CURLM_OK) {
133 ERROR("multi-handle %p - Servicing timer failed -%s (%i)", mandle, curl_multi_strerror(ret), ret);
134 return;
135 }
136
137 DEBUG3("multi-handle %p - Serviced by timer. %i request(s) in progress, %" PRIu64 " requests(s) to dequeue",
138 mandle, running, mhandle->transfers - (uint64_t)running);
139
140 _fr_curl_io_demux(mhandle, mandle);
141}
142
143/** Service an IO event on a file descriptor
144 *
145 * @param[in] mhandle containing the event loop and request counter.
146 * @param[in] fd the IO event occurred for.
147 * @param[in] event type.
148 */
149static inline void _fr_curl_io_service(fr_curl_handle_t *mhandle, int fd, int event)
150{
151 CURLMcode ret;
152 CURLM *mandle = mhandle->mandle;
153 int running = 0;
154
155 ret = curl_multi_socket_action(mandle, fd, event, &running);
156 if (ret != CURLM_OK) {
157 ERROR("multi-handle %p - Servicing I/O failed - %s (%i)", mandle, curl_multi_strerror(ret), ret);
158 return;
159 }
160
161 if (DEBUG_ENABLED3) {
162 char const *event_str;
163
164 switch (event) {
165 case CURL_CSELECT_ERR:
166 event_str = "error";
167 break;
168
169 case CURL_CSELECT_OUT:
170 event_str = "socket-writable";
171 break;
172
173 case CURL_CSELECT_IN:
174 event_str = "socket-readable";
175 break;
176
177 case 0:
178 event_str = "closed"; /* Not really closed, more do your own eval! */
179 break;
180
181 default:
182 event_str = "<INVALID>";
183 break;
184 }
185
186 DEBUG3("multi-handle %p - Serviced on fd %i event (%s). "
187 "%i request(s) in progress, %" PRIu64 " requests(s) to dequeue",
188 mandle, fd, event_str, running, mhandle->transfers - (uint64_t)running);
189 }
190
191
192 _fr_curl_io_demux(mhandle, mandle);
193}
194
195/** File descriptor experienced an error
196 *
197 * @param[in] el fd was registered with.
198 * @param[in] fd that errored.
199 * @param[in] flags from kevent.
200 * @param[in] fd_errno from kevent.
201 * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
202 */
203static void _fr_curl_io_service_errored(UNUSED fr_event_list_t *el, int fd, int flags, int fd_errno, void *uctx)
204{
205 fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
206
207 DEBUG4("multi-handle %p - fd %i errored: %s", mhandle->mandle, fd, fr_syserror(fd_errno));
208
209 /*
210 * The remote server closed the socket
211 *
212 * Instead of signalling curl with CURL_CSELECT_ERR
213 * which always results in spurious errors being
214 * printed, pass in '0', which causes libcurl to do
215 * its own evaluation on the socket state, and hopefully
216 * run the right code for socket closure.
217 */
218 if (flags & EV_EOF) {
219 _fr_curl_io_service(mhandle, fd, 0);
220 return;
221 }
222
223 _fr_curl_io_service(mhandle, fd, CURL_CSELECT_ERR);
224}
225
226/** File descriptor became writable
227 *
228 * @param[in] el fd was registered with.
229 * @param[in] fd that became writable.
230 * @param[in] flags from kevent.
231 * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
232 */
233static void _fr_curl_io_service_writable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
234{
235 fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
236
237 DEBUG4("multi-handle %p - fd %i now writable", mhandle->mandle, fd);
238
239 _fr_curl_io_service(mhandle, fd, CURL_CSELECT_OUT);
240}
241
242/** File descriptor became readable
243 *
244 * @param[in] el fd was registered with.
245 * @param[in] fd that became readable.
246 * @param[in] flags from kevent.
247 * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
248 */
249static void _fr_curl_io_service_readable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
250{
251 fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
252
253 DEBUG4("multi-handle %p - fd %i now readable", mhandle->mandle, fd);
254
255 _fr_curl_io_service(mhandle, fd, CURL_CSELECT_IN);
256}
257
258/** Callback called by libcurl to set/unset timers
259 *
260 * Each rlm_fr_curl_thread_t has a timer event which is controller by libcurl.
261 * This allows libcurl to honour timeouts set on requests to remote hosts,
262 * and means we don't need to set timeouts for individual I/O events.
263 *
264 * @param[in] mandle handle requesting the timer be set/unset.
265 * @param[in] timeout_ms If > 0, how long to wait before calling curl_multi_socket_action.
266 * If == 0, we call curl_multi_socket_action as soon as possible.
267 * If < 0, we delete the timer.
268 * @param[in] ctx The rlm_fr_curl_thread_t specific to this thread.
269 * @return
270 * - 0 on success.
271 * - -1 on error.
272 */
273static int _fr_curl_io_timer_modify(CURLM *mandle, long timeout_ms, void *ctx)
274{
275 fr_curl_handle_t *mhandle = talloc_get_type_abort(ctx, fr_curl_handle_t);
276
277 if (timeout_ms < 0) {
278 FR_TIMER_DISARM_RETURN(mhandle->ev);
279 DEBUG3("multi-handle %p - Timer removed", mandle);
280 return 0;
281 }
282
283 DEBUG3("multi-handle %p will need servicing in %li ms", mandle, timeout_ms);
284
285 /*
286 * https://curl.haxx.se/libcurl/c/CURLMOPT_TIMERFUNCTION.html
287 *
288 * WARNING: even if it feels tempting, avoid calling libcurl directly from within the
289 * callback itself when the timeout_ms value is zero, since it risks triggering an
290 * unpleasant recursive behavior that immediately calls another call to the callback
291 * with a zero timeout...
292 *
293 * Setting a timeout of zero when calling fr_timer_in should result in the event
294 * repeating at most twice during one iteration of the event loop.
295 *
296 * In a previous version of this code we called curl_multi_socket_action immediately
297 * if timeout_ms was 0. It was observed that this lead to this callback being called
298 * ~665 times per request which is why we no longer do that.
299 */
300 if (fr_timer_in(mhandle, mhandle->el->tl, &mhandle->ev,
301 fr_time_delta_from_msec(timeout_ms),
302 false, _fr_curl_io_timer_expired, mhandle) < 0) return -1;
303 return 0;
304}
305
306/** Called by libcurl to register a socket that it's intefr_curled in receiving IO events for
307 *
308 *
309 * @param[in] easy handle this fd relates to.
310 * @param[in] fd File descriptor curl wants to be notified about.
311 * @param[in] what Which events libcurl wants to be notified of, may be one of:
312 * - CURL_POLL_IN Wait for incoming data. For the socket
313 * to become readable.
314 * - CURL_POLL_OUT Wait for outgoing data. For the socket
315 * to become writable.
316 * - CURL_POLL_INOUT Wait for incoming and outgoing data.
317 * For the socket to become readable or writable.
318 * - CURL_POLL_REMOVE The specified socket/file descriptor is no
319 * longer used by libcurl.
320 * @param[in] ctx The fr_curl_handle_t specific to this thread.
321 * @param[in] fd_ctx Private data associated with the socket.
322 */
323static int _fr_curl_io_event_modify(UNUSED CURL *easy, curl_socket_t fd, int what, void *ctx, UNUSED void *fd_ctx)
324{
325 fr_curl_handle_t *mhandle = talloc_get_type_abort(ctx, fr_curl_handle_t);
326
327 switch (what) {
328 case CURL_POLL_IN:
329 if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
331 NULL,
333 mhandle) < 0) {
334 PERROR("multi-handle %p registration failed for read+error events on FD %i",
335 mhandle->mandle, fd);
336 return -1;
337 }
338 DEBUG4("multi-handle %p registered for read+error events on FD %i", mhandle->mandle, fd);
339 break;
340
341 case CURL_POLL_OUT:
342 if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
343 NULL,
346 mhandle) < 0) {
347 PERROR("multi-handle %p registration failed for write+error events on FD %i",
348 mhandle->mandle, fd);
349 return -1;
350 }
351 DEBUG4("multi-handle %p registered for write+error events on FD %i", mhandle->mandle, fd);
352 break;
353
354 case CURL_POLL_INOUT:
355 if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
359 mhandle) < 0) {
360 PERROR("multi-handle %p registration failed for read+write+error events on FD %i",
361 mhandle->mandle, fd);
362 return -1;
363 }
364 DEBUG4("multi-handle %p registered for read+write+error events on FD %i", mhandle->mandle, fd);
365 break;
366
367 case CURL_POLL_REMOVE:
368 if (fr_event_fd_delete(mhandle->el, fd, FR_EVENT_FILTER_IO) < 0) {
369 PERROR("multi-handle %p de-registration failed for FD %i", mhandle->mandle, fd);
370 return -1;
371 }
372 DEBUG4("multi-handle %p unregistered events for FD %i", mhandle->mandle, fd);
373 break;
374
375 default:
376 fr_assert(0);
377 return -1;
378 }
379
380 return CURLM_OK;
381}
382
383/** Callback to receive debugging data from libcurl
384 *
385 * @note Should only be set on a handle if RDEBUG_ENABLED3 is true.
386 *
387 * @param[in] candle Curl handle the debugging data pertains to.
388 * @param[in] type The type of debugging data we received.
389 * @param[in] data Buffer containing debug data (not \0 terminated). Despite the
390 * type being char *, this can be binary data depending on the
391 * curl_infotype.
392 * @param[in] len The length of the data in the buffer.
393 * @param[in] uctx The current request.
394 * @return
395 * - 0 (we always indicate success)
396 */
397static int curl_debug_log(UNUSED CURL *candle, curl_infotype type, char *data, size_t len, void *uctx)
398{
399 request_t *request = talloc_get_type_abort(uctx, request_t);
400 char const *p = data, *q, *end = p + len;
401 char const *verb;
402
403 switch (type) {
404 case CURLINFO_TEXT:
405 /*
406 * Curl debug output has trailing newlines, and could conceivably
407 * span multiple lines. Take care of both cases.
408 */
409 while (p < end) {
410 q = memchr(p, '\n', end - p);
411 if (!q) q = end;
412
413 RDEBUG3("libcurl - %pV", fr_box_strvalue_len(p, q ? q - p : p - end));
414 p = q + 1;
415 }
416
417 break;
418
419 case CURLINFO_HEADER_IN:
420 verb = "received";
421 print_header:
422 while (p < end) {
423 q = memchr(p, '\n', end - p);
424 q = q ? q + 1 : end;
425
426 if (RDEBUG_ENABLED4) {
427 RHEXDUMP4((uint8_t const *)p, q - p,
428 "%s header: %pV",
429 verb, fr_box_strvalue_len(p, q - p));
430 } else {
431 RDEBUG3("%s header: %pV",
432 verb, fr_box_strvalue_len(p, q - p));
433 }
434 p = q;
435 }
436 break;
437
438 case CURLINFO_HEADER_OUT:
439 verb = "sending";
440 goto print_header;
441
442 case CURLINFO_DATA_IN:
443 RHEXDUMP4((uint8_t const *)data, len, "received data[length %zu]", len);
444 break;
445
446 case CURLINFO_DATA_OUT:
447 RHEXDUMP4((uint8_t const *)data, len, "sending data[length %zu]", len);
448 break;
449
450 case CURLINFO_SSL_DATA_OUT:
451 RHEXDUMP4((uint8_t const *)data, len, "sending ssl-data[length %zu]", len);
452 break;
453
454 case CURLINFO_SSL_DATA_IN:
455 RHEXDUMP4((uint8_t const *)data, len, "received ssl-data[length %zu]", len);
456 break;
457
458 default:
459 RHEXDUMP3((uint8_t const *)data, len, "libcurl - debug data (unknown type %i)", (int)type);
460 break;
461 }
462
463 return 0;
464}
465
466/** Sends a request using libcurl
467 *
468 * Send the actual curl request to the server. The response will be handled by
469 * the numerous callbacks configured for the easy handle.
470 *
471 * @param[in] mhandle Thread-specific mhandle wrapper.
472 * @param[in] request Current request.
473 * @param[in] randle representing the request.
474 * @return
475 * - 0 on success.
476 * - -1 on failure.
477 */
479{
480 CURLcode ret;
481 CURLMcode mret;
482
483 REQUEST_VERIFY(request);
484
485 randle->request = request;
486
487 /*
488 * Set debugging functions so we can track the
489 * IO request's progress.
490 */
491 if (RDEBUG_ENABLED3) {
492 FR_CURL_REQUEST_SET_OPTION(CURLOPT_DEBUGFUNCTION, curl_debug_log);
493 FR_CURL_REQUEST_SET_OPTION(CURLOPT_DEBUGDATA, request);
494 FR_CURL_REQUEST_SET_OPTION(CURLOPT_VERBOSE, 1L);
495 }
496
497 /*
498 * Stick the current request in the curl handle's
499 * private data. This makes it simple to resume
500 * the request in the demux function later...
501 *
502 * Note: If you get 0xdeadc341 in the private data
503 * in the demux function, the result for the easy
504 * handle was erroneously delivered after a
505 * cancellation.
506 */
507 ret = curl_easy_setopt(randle->candle, CURLOPT_PRIVATE, randle);
508 if (ret != CURLE_OK) {
509 REDEBUG("Request failed: %i - %s", ret, curl_easy_strerror(ret));
510 return -1;
511 }
512
513 /*
514 * Increment here, else the debug output looks
515 * messed up is curl_multi_add_handle triggers
516 * event loop modifications calls immediately.
517 */
518 mhandle->transfers++;
519 mret = curl_multi_add_handle(mhandle->mandle, randle->candle);
520 if (mret != CURLM_OK) {
521 mhandle->transfers--;
522 REDEBUG("Request failed: %i - %s", mret, curl_multi_strerror(mret));
523 return -1;
524 }
525
526 return 0;
527
528error:
529 return -1;
530}
531
533{
534 curl_easy_cleanup(randle->candle);
535
536 return 0;
537}
538
539/** Allocate a new curl easy request and wrapper struct
540 *
541 */
543{
544 fr_curl_io_request_t *randle;
545
546 randle = talloc_zero(ctx, fr_curl_io_request_t);
547 if (unlikely(!randle)) return NULL;
548
549 randle->candle = curl_easy_init();
550 if (!randle->candle) {
551 talloc_free(randle);
552 return NULL;
553 }
554
555 talloc_set_destructor(randle, _fr_curl_io_request_free);
556
557 return randle;
558}
559
560/** Free the multi-handle
561 *
562 */
563static int _mhandle_free(fr_curl_handle_t *mhandle)
564{
565 curl_multi_cleanup(mhandle->mandle);
566
567 return 0;
568}
569
570/** Performs the libcurl initialisation of the thread
571 *
572 * @param[in] ctx to alloc handle in.
573 * @param[in] el to initial.
574 * @param[in] multiplex Run multiple requests over the same connection simultaneously.
575 * HTTP/2 only.
576 * @return
577 * - 0 on success.
578 * - -1 on error.
579 */
582#ifndef CURLPIPE_MULTIPLEX
583 UNUSED
584#endif
585 bool multiplex)
586{
587 CURLMcode ret;
588 CURLM *mandle;
589 fr_curl_handle_t *mhandle;
590 char const *option;
591
592 mandle = curl_multi_init();
593 if (!mandle) {
594 ERROR("Curl multi-handle instantiation failed");
595 return NULL;
596 }
597
598 /*
599 * Structure to store extra data.
600 *
601 * Passed to all curl I/O and timer callbacks.
602 *
603 * If uctx data is needed in the future, can be added here.
604 */
605 MEM(mhandle = talloc_zero(ctx, fr_curl_handle_t));
606 mhandle->el = el;
607 mhandle->mandle = mandle;
608 talloc_set_destructor(mhandle, _mhandle_free);
609
610 SET_MOPTION(mandle, CURLMOPT_TIMERFUNCTION, _fr_curl_io_timer_modify);
611 SET_MOPTION(mandle, CURLMOPT_TIMERDATA, mhandle);
612
613 SET_MOPTION(mandle, CURLMOPT_SOCKETFUNCTION, _fr_curl_io_event_modify);
614 SET_MOPTION(mandle, CURLMOPT_SOCKETDATA, mhandle);
615
616#ifdef CURLPIPE_MULTIPLEX
617 SET_MOPTION(mandle, CURLMOPT_PIPELINING, multiplex ? CURLPIPE_MULTIPLEX : CURLPIPE_NOTHING);
618#endif
619
620 return mhandle;
621
622error:
623 ERROR("Failed setting curl option %s: %s (%i)", option, curl_multi_strerror(ret), ret);
624
625 return NULL;
626}
#define DIAG_UNKNOWN_PRAGMAS
Definition build.h:479
#define DIAG_ON(_x)
Definition build.h:481
#define unlikely(_x)
Definition build.h:402
#define UNUSED
Definition build.h:336
#define DIAG_OFF(_x)
Definition build.h:480
fr_timer_t * ev
Multi-Handle timer.
Definition base.h:93
#define FR_CURL_REQUEST_SET_OPTION(_x, _y)
Definition base.h:67
fr_event_list_t * el
Event list servicing I/O events.
Definition base.h:92
CURLcode result
Result of executing the request.
Definition base.h:103
uint64_t transfers
How many transfers are current in progress.
Definition base.h:94
CURLM * mandle
The multi handle.
Definition base.h:95
request_t * request
Current request.
Definition base.h:104
CURL * candle
Request specific handle.
Definition base.h:102
Uctx data for timer and I/O functions.
Definition base.h:91
Structure representing an individual request being passed to curl for processing.
Definition base.h:101
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:158
#define MEM(x)
Definition debug.h:46
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define fr_event_fd_insert(...)
Definition event.h:247
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1636
static int _fr_curl_io_event_modify(UNUSED CURL *easy, curl_socket_t fd, int what, void *ctx, UNUSED void *fd_ctx)
Called by libcurl to register a socket that it's intefr_curled in receiving IO events for.
Definition io.c:323
static int _fr_curl_io_request_free(fr_curl_io_request_t *randle)
Definition io.c:532
static void _fr_curl_io_service_writable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
File descriptor became writable.
Definition io.c:233
#define SET_MOPTION(_mandle, _opt, _val)
Definition io.c:36
static void _fr_curl_io_demux(fr_curl_handle_t *mhandle, CURLM *mandle)
De-queue curl requests and wake up the requests that initiated them.
Definition io.c:49
static void _fr_curl_io_service_errored(UNUSED fr_event_list_t *el, int fd, int flags, int fd_errno, void *uctx)
File descriptor experienced an error.
Definition io.c:203
static int curl_debug_log(UNUSED CURL *candle, curl_infotype type, char *data, size_t len, void *uctx)
Callback to receive debugging data from libcurl.
Definition io.c:397
static void _fr_curl_io_service_readable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
File descriptor became readable.
Definition io.c:249
int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, request_t *request, fr_curl_io_request_t *randle)
Sends a request using libcurl.
Definition io.c:478
static int _fr_curl_io_timer_modify(CURLM *mandle, long timeout_ms, void *ctx)
Callback called by libcurl to set/unset timers.
Definition io.c:273
fr_curl_io_request_t * fr_curl_io_request_alloc(TALLOC_CTX *ctx)
Allocate a new curl easy request and wrapper struct.
Definition io.c:542
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, UNUSED bool multiplex)
Performs the libcurl initialisation of the thread.
Definition io.c:580
static void _fr_curl_io_timer_expired(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
libcurl's timer expired
Definition io.c:122
static void _fr_curl_io_service(fr_curl_handle_t *mhandle, int fd, int event)
Service an IO event on a file descriptor.
Definition io.c:149
static int _mhandle_free(fr_curl_handle_t *mhandle)
Free the multi-handle.
Definition io.c:563
#define PERROR(_fmt,...)
Definition log.h:228
#define DEBUG3(_fmt,...)
Definition log.h:266
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:347
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RHEXDUMP4(_data, _len, _fmt,...)
Definition log.h:718
#define DEBUG4(_fmt,...)
Definition log.h:267
#define RHEXDUMP3(_data, _len, _fmt,...)
Definition log.h:717
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:259
#define RDEBUG_ENABLED4
True if request debug level 1-4 messages are enabled.
Definition log.h:348
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1203
Stores all information relating to an event list.
Definition event.c:377
unsigned char uint8_t
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define REQUEST_VERIFY(_x)
Definition request.h:309
fr_aka_sim_id_type_t type
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
#define FR_TIMER_DISARM_RETURN(_ev)
Definition timer.h:98
#define fr_timer_in(...)
Definition timer.h:87
static fr_event_list_t * el
static fr_slen_t data
Definition value.h:1340
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309