aboutsummaryrefslogtreecommitdiff
path: root/src/curl/curl.c
diff options
context:
space:
mode:
authorMartin Schanzenbach <schanzen@gnunet.org>2023-10-18 13:43:12 +0200
committerMartin Schanzenbach <schanzen@gnunet.org>2023-10-18 13:43:12 +0200
commit84bbd6f1be6686b3c816ee5bb5c0c786ac193c6c (patch)
tree979b0bda44513d2391ae9f575ed834a4146f08c6 /src/curl/curl.c
parent9ef4abad615bea12d13be542b8ae5fbeb2dfee32 (diff)
downloadgnunet-84bbd6f1be6686b3c816ee5bb5c0c786ac193c6c.tar.gz
gnunet-84bbd6f1be6686b3c816ee5bb5c0c786ac193c6c.zip
BUILD: Move curl to lib
Diffstat (limited to 'src/curl/curl.c')
-rw-r--r--src/curl/curl.c907
1 files changed, 0 insertions, 907 deletions
diff --git a/src/curl/curl.c b/src/curl/curl.c
deleted file mode 100644
index 648c9a14e..000000000
--- a/src/curl/curl.c
+++ /dev/null
@@ -1,907 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016, 2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file curl/curl.c
22 * @brief API for downloading JSON via CURL
23 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <jansson.h>
28#include <microhttpd.h>
29#include "gnunet_curl_lib.h"
30
31#if ENABLE_BENCHMARK
32#include "../util/benchmark.h"
33#endif
34
35/**
36 * Set to 1 for extra debug logging.
37 */
38#define DEBUG 0
39
40/**
41 * Log error related to CURL operations.
42 *
43 * @param type log level
44 * @param function which function failed to run
45 * @param code what was the curl error code
46 */
47#define CURL_STRERROR(type, function, code) \
48 GNUNET_log (type, \
49 "Curl function `%s' has failed at `%s:%d' with error: %s\n", \
50 function, \
51 __FILE__, \
52 __LINE__, \
53 curl_easy_strerror (code));
54
55/**
56 * Print JSON parsing related error information
57 */
58#define JSON_WARN(error) \
59 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
60 "JSON parsing failed at %s:%u: %s (%s)\n", \
61 __FILE__, \
62 __LINE__, \
63 error.text, \
64 error.source)
65
66
67/**
68 * Failsafe flag. Raised if our constructor fails to initialize
69 * the Curl library.
70 */
71static int curl_fail;
72
73/**
74 * Jobs are CURL requests running within a `struct GNUNET_CURL_Context`.
75 */
76struct GNUNET_CURL_Job
77{
78 /**
79 * We keep jobs in a DLL.
80 */
81 struct GNUNET_CURL_Job *next;
82
83 /**
84 * We keep jobs in a DLL.
85 */
86 struct GNUNET_CURL_Job *prev;
87
88 /**
89 * Easy handle of the job.
90 */
91 CURL *easy_handle;
92
93 /**
94 * Context this job runs in.
95 */
96 struct GNUNET_CURL_Context *ctx;
97
98 /**
99 * Function to call upon completion.
100 */
101 GNUNET_CURL_JobCompletionCallback jcc;
102
103 /**
104 * Closure for @e jcc.
105 */
106 void *jcc_cls;
107
108 /**
109 * Function to call upon completion.
110 */
111 GNUNET_CURL_RawJobCompletionCallback jcc_raw;
112
113 /**
114 * Closure for @e jcc_raw.
115 */
116 void *jcc_raw_cls;
117
118 /**
119 * Buffer for response received from CURL.
120 */
121 struct GNUNET_CURL_DownloadBuffer db;
122
123 /**
124 * Headers used for this job, the list needs to be freed
125 * after the job has finished.
126 */
127 struct curl_slist *job_headers;
128
129 /**
130 * When did we start the job?
131 */
132 struct GNUNET_TIME_Absolute start_time;
133};
134
135
136/**
137 * Context
138 */
139struct GNUNET_CURL_Context
140{
141 /**
142 * Curl multi handle
143 */
144 CURLM *multi;
145
146 /**
147 * Curl share handle
148 */
149 CURLSH *share;
150
151 /**
152 * We keep jobs in a DLL.
153 */
154 struct GNUNET_CURL_Job *jobs_head;
155
156 /**
157 * We keep jobs in a DLL.
158 */
159 struct GNUNET_CURL_Job *jobs_tail;
160
161 /**
162 * Headers common for all requests in the context.
163 */
164 struct curl_slist *common_headers;
165
166 /**
167 * If non-NULL, the async scope ID is sent in a request
168 * header of this name.
169 */
170 const char *async_scope_id_header;
171
172 /**
173 * Function we need to call whenever the event loop's
174 * socket set changed.
175 */
176 GNUNET_CURL_RescheduleCallback cb;
177
178 /**
179 * Closure for @e cb.
180 */
181 void *cb_cls;
182
183 /**
184 * USERNAME:PASSWORD to use for client-authentication
185 * with all requests of this context, or NULL.
186 */
187 char *userpass;
188
189 /**
190 * Type of the TLS client certificate used, or NULL.
191 */
192 char *certtype;
193
194 /**
195 * File with the TLS client certificate, or NULL.
196 */
197 char *certfile;
198
199 /**
200 * File with the private key to authenticate the
201 * TLS client, or NULL.
202 */
203 char *keyfile;
204
205 /**
206 * Passphrase to decrypt @e keyfile, or NULL.
207 */
208 char *keypass;
209
210};
211
212
213void
214GNUNET_CURL_set_userpass (struct GNUNET_CURL_Context *ctx,
215 const char *userpass)
216{
217 GNUNET_free (ctx->userpass);
218 if (NULL != userpass)
219 ctx->userpass = GNUNET_strdup (userpass);
220}
221
222
223void
224GNUNET_CURL_set_tlscert (struct GNUNET_CURL_Context *ctx,
225 const char *certtype,
226 const char *certfile,
227 const char *keyfile,
228 const char *keypass)
229{
230 GNUNET_free (ctx->certtype);
231 GNUNET_free (ctx->certfile);
232 GNUNET_free (ctx->keyfile);
233 GNUNET_free (ctx->keypass);
234 if (NULL != certtype)
235 ctx->certtype = GNUNET_strdup (certtype);
236 if (NULL != certfile)
237 ctx->certfile = GNUNET_strdup (certfile);
238 if (NULL != keyfile)
239 ctx->certtype = GNUNET_strdup (keyfile);
240 if (NULL != keypass)
241 ctx->certtype = GNUNET_strdup (keypass);
242}
243
244
245struct GNUNET_CURL_Context *
246GNUNET_CURL_init (GNUNET_CURL_RescheduleCallback cb,
247 void *cb_cls)
248{
249 struct GNUNET_CURL_Context *ctx;
250 CURLM *multi;
251 CURLSH *share;
252
253 if (curl_fail)
254 {
255 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
256 "Curl was not initialised properly\n");
257 return NULL;
258 }
259 if (NULL == (multi = curl_multi_init ()))
260 {
261 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
262 "Failed to create a Curl multi handle\n");
263 return NULL;
264 }
265 if (NULL == (share = curl_share_init ()))
266 {
267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
268 "Failed to create a Curl share handle\n");
269 return NULL;
270 }
271 ctx = GNUNET_new (struct GNUNET_CURL_Context);
272 ctx->cb = cb;
273 ctx->cb_cls = cb_cls;
274 ctx->multi = multi;
275 ctx->share = share;
276 return ctx;
277}
278
279
280void
281GNUNET_CURL_enable_async_scope_header (struct GNUNET_CURL_Context *ctx,
282 const char *header_name)
283{
284 ctx->async_scope_id_header = header_name;
285}
286
287
288enum GNUNET_GenericReturnValue
289GNUNET_CURL_is_valid_scope_id (const char *scope_id)
290{
291 if (strlen (scope_id) >= 64)
292 return GNUNET_NO;
293 for (size_t i = 0; i < strlen (scope_id); i++)
294 if (! (isalnum (scope_id[i]) || (scope_id[i] == '-')))
295 return GNUNET_NO;
296 return GNUNET_YES;
297}
298
299
300/**
301 * Callback used when downloading the reply to an HTTP request.
302 * Just appends all of the data to the `buf` in the
303 * `struct DownloadBuffer` for further processing. The size of
304 * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
305 * the download exceeds this size, we abort with an error.
306 *
307 * @param bufptr data downloaded via HTTP
308 * @param size size of an item in @a bufptr
309 * @param nitems number of items in @a bufptr
310 * @param cls the `struct DownloadBuffer`
311 * @return number of bytes processed from @a bufptr
312 */
313static size_t
314download_cb (char *bufptr,
315 size_t size,
316 size_t nitems,
317 void *cls)
318{
319 struct GNUNET_CURL_DownloadBuffer *db = cls;
320 size_t msize;
321 void *buf;
322
323 if (0 == size * nitems)
324 {
325 /* Nothing (left) to do */
326 return 0;
327 }
328 msize = size * nitems;
329 if ((msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
330 {
331 db->eno = ENOMEM;
332 return 0; /* signals an error to curl */
333 }
334 db->buf = GNUNET_realloc (db->buf,
335 db->buf_size + msize);
336 buf = db->buf + db->buf_size;
337 GNUNET_memcpy (buf, bufptr, msize);
338 db->buf_size += msize;
339 return msize;
340}
341
342
343/**
344 * Create the HTTP headers for the request
345 *
346 * @param ctx context we run in
347 * @param job_headers job-specific headers
348 * @return all headers to use
349 */
350static struct curl_slist *
351setup_job_headers (struct GNUNET_CURL_Context *ctx,
352 const struct curl_slist *job_headers)
353{
354 struct curl_slist *all_headers = NULL;
355
356 for (const struct curl_slist *curr = job_headers;
357 NULL != curr;
358 curr = curr->next)
359 {
360 GNUNET_assert (NULL !=
361 (all_headers = curl_slist_append (all_headers,
362 curr->data)));
363 }
364
365 for (const struct curl_slist *curr = ctx->common_headers;
366 NULL != curr;
367 curr = curr->next)
368 {
369 GNUNET_assert (NULL !=
370 (all_headers = curl_slist_append (all_headers,
371 curr->data)));
372 }
373
374 if (NULL != ctx->async_scope_id_header)
375 {
376 struct GNUNET_AsyncScopeSave scope;
377
378 GNUNET_async_scope_get (&scope);
379 if (GNUNET_YES == scope.have_scope)
380 {
381 char *aid_header;
382
383 aid_header =
384 GNUNET_STRINGS_data_to_string_alloc (
385 &scope.scope_id,
386 sizeof(struct GNUNET_AsyncScopeId));
387 GNUNET_assert (NULL != aid_header);
388 GNUNET_assert (NULL != curl_slist_append (all_headers,
389 aid_header));
390 GNUNET_free (aid_header);
391 }
392 }
393 return all_headers;
394}
395
396
397/**
398 * Create a job.
399 *
400 * @param eh easy handle to use
401 * @param ctx context to run the job in
402 * @param all_headers HTTP client headers to use (free'd)
403 * @return NULL on error
404 */
405static struct GNUNET_CURL_Job *
406setup_job (CURL *eh,
407 struct GNUNET_CURL_Context *ctx,
408 struct curl_slist *all_headers)
409{
410 struct GNUNET_CURL_Job *job;
411
412 if (CURLE_OK !=
413 curl_easy_setopt (eh,
414 CURLOPT_HTTPHEADER,
415 all_headers))
416 {
417 GNUNET_break (0);
418 curl_slist_free_all (all_headers);
419 curl_easy_cleanup (eh);
420 return NULL;
421 }
422 job = GNUNET_new (struct GNUNET_CURL_Job);
423 job->start_time = GNUNET_TIME_absolute_get ();
424 job->job_headers = all_headers;
425
426 if ( (CURLE_OK !=
427 curl_easy_setopt (eh,
428 CURLOPT_PRIVATE,
429 job)) ||
430 (CURLE_OK !=
431 curl_easy_setopt (eh,
432 CURLOPT_WRITEFUNCTION,
433 &download_cb)) ||
434 (CURLE_OK !=
435 curl_easy_setopt (eh,
436 CURLOPT_WRITEDATA,
437 &job->db)) ||
438 (CURLE_OK !=
439 curl_easy_setopt (eh,
440 CURLOPT_SHARE,
441 ctx->share)) )
442 {
443 GNUNET_break (0);
444 GNUNET_free (job);
445 curl_easy_cleanup (eh);
446 return NULL;
447 }
448 if ( (CURLM_OK !=
449 curl_multi_add_handle (ctx->multi,
450 eh)) )
451 {
452 GNUNET_break (0);
453 GNUNET_free (job);
454 curl_easy_cleanup (eh);
455 return NULL;
456 }
457 job->easy_handle = eh;
458 job->ctx = ctx;
459 GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
460 ctx->jobs_tail,
461 job);
462 return job;
463}
464
465
466void
467GNUNET_CURL_extend_headers (struct GNUNET_CURL_Job *job,
468 const struct curl_slist *extra_headers)
469{
470 struct curl_slist *all_headers = job->job_headers;
471
472 for (const struct curl_slist *curr = extra_headers;
473 NULL != curr;
474 curr = curr->next)
475 {
476 GNUNET_assert (NULL !=
477 (all_headers = curl_slist_append (all_headers,
478 curr->data)));
479 }
480 job->job_headers = all_headers;
481}
482
483
484struct GNUNET_CURL_Job *
485GNUNET_CURL_job_add_raw (struct GNUNET_CURL_Context *ctx,
486 CURL *eh,
487 const struct curl_slist *job_headers,
488 GNUNET_CURL_RawJobCompletionCallback jcc,
489 void *jcc_cls)
490{
491 struct GNUNET_CURL_Job *job;
492 struct curl_slist *all_headers;
493
494 GNUNET_assert (NULL != jcc);
495 all_headers = setup_job_headers (ctx,
496 job_headers);
497 if (NULL == (job = setup_job (eh,
498 ctx,
499 all_headers)))
500 return NULL;
501 job->jcc_raw = jcc;
502 job->jcc_raw_cls = jcc_cls;
503 ctx->cb (ctx->cb_cls);
504 return job;
505}
506
507
508struct GNUNET_CURL_Job *
509GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx,
510 CURL *eh,
511 const struct curl_slist *job_headers,
512 GNUNET_CURL_JobCompletionCallback jcc,
513 void *jcc_cls)
514{
515 struct GNUNET_CURL_Job *job;
516 struct curl_slist *all_headers;
517
518 GNUNET_assert (NULL != jcc);
519 if ( (NULL != ctx->userpass) &&
520 (0 != curl_easy_setopt (eh,
521 CURLOPT_USERPWD,
522 ctx->userpass)) )
523 return NULL;
524 if ( (NULL != ctx->certfile) &&
525 (0 != curl_easy_setopt (eh,
526 CURLOPT_SSLCERT,
527 ctx->certfile)) )
528 return NULL;
529 if ( (NULL != ctx->certtype) &&
530 (0 != curl_easy_setopt (eh,
531 CURLOPT_SSLCERTTYPE,
532 ctx->certtype)) )
533 return NULL;
534 if ( (NULL != ctx->keyfile) &&
535 (0 != curl_easy_setopt (eh,
536 CURLOPT_SSLKEY,
537 ctx->keyfile)) )
538 return NULL;
539 if ( (NULL != ctx->keypass) &&
540 (0 != curl_easy_setopt (eh,
541 CURLOPT_KEYPASSWD,
542 ctx->keypass)) )
543 return NULL;
544
545 all_headers = setup_job_headers (ctx,
546 job_headers);
547 if (NULL == (job = setup_job (eh,
548 ctx,
549 all_headers)))
550 return NULL;
551
552 job->jcc = jcc;
553 job->jcc_cls = jcc_cls;
554 ctx->cb (ctx->cb_cls);
555 return job;
556}
557
558
559struct GNUNET_CURL_Job *
560GNUNET_CURL_job_add_with_ct_json (struct GNUNET_CURL_Context *ctx,
561 CURL *eh,
562 GNUNET_CURL_JobCompletionCallback jcc,
563 void *jcc_cls)
564{
565 struct GNUNET_CURL_Job *job;
566 struct curl_slist *job_headers = NULL;
567
568 GNUNET_assert (NULL != (job_headers =
569 curl_slist_append (NULL,
570 "Content-Type: application/json")));
571 job = GNUNET_CURL_job_add2 (ctx,
572 eh,
573 job_headers,
574 jcc,
575 jcc_cls);
576 curl_slist_free_all (job_headers);
577 return job;
578}
579
580
581struct GNUNET_CURL_Job *
582GNUNET_CURL_job_add (struct GNUNET_CURL_Context *ctx,
583 CURL *eh,
584 GNUNET_CURL_JobCompletionCallback jcc,
585 void *jcc_cls)
586{
587 return GNUNET_CURL_job_add2 (ctx,
588 eh,
589 NULL,
590 jcc,
591 jcc_cls);
592}
593
594
595void
596GNUNET_CURL_job_cancel (struct GNUNET_CURL_Job *job)
597{
598 struct GNUNET_CURL_Context *ctx = job->ctx;
599
600 GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
601 ctx->jobs_tail,
602 job);
603 GNUNET_break (CURLM_OK ==
604 curl_multi_remove_handle (ctx->multi,
605 job->easy_handle));
606 curl_easy_cleanup (job->easy_handle);
607 GNUNET_free (job->db.buf);
608 curl_slist_free_all (job->job_headers);
609 ctx->cb (ctx->cb_cls);
610 GNUNET_free (job);
611}
612
613
614/**
615 * Test if the given content type @a ct is JSON
616 *
617 * @param ct a content type, e.g. "application/json; charset=UTF-8"
618 * @return true if @a ct denotes JSON
619 */
620static bool
621is_json (const char *ct)
622{
623 const char *semi;
624
625 /* check for "application/json" exact match */
626 if (0 == strcasecmp (ct,
627 "application/json"))
628 return true;
629 /* check for "application/json;[ANYTHING]" */
630 semi = strchr (ct,
631 ';');
632 /* also allow "application/json [ANYTHING]" (note the space!) */
633 if (NULL == semi)
634 semi = strchr (ct,
635 ' ');
636 if (NULL == semi)
637 return false; /* no delimiter we accept, forget it */
638 if (semi - ct != strlen ("application/json"))
639 return false; /* delimiter past desired length, forget it */
640 if (0 == strncasecmp (ct,
641 "application/json",
642 strlen ("application/json")))
643 return true; /* OK */
644 return false;
645}
646
647
648void *
649GNUNET_CURL_download_get_result_ (struct GNUNET_CURL_DownloadBuffer *db,
650 CURL *eh,
651 long *response_code)
652{
653 json_t *json;
654 json_error_t error;
655 char *ct;
656
657#if DEBUG
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "Downloaded body: %.*s\n",
660 (int) db->buf_size,
661 (char *) db->buf);
662#endif
663 if (CURLE_OK !=
664 curl_easy_getinfo (eh,
665 CURLINFO_RESPONSE_CODE,
666 response_code))
667 {
668 /* unexpected error... */
669 GNUNET_break (0);
670 *response_code = 0;
671 }
672 if (MHD_HTTP_NO_CONTENT == *response_code)
673 return NULL;
674 if ((CURLE_OK !=
675 curl_easy_getinfo (eh,
676 CURLINFO_CONTENT_TYPE,
677 &ct)) ||
678 (NULL == ct) ||
679 (! is_json (ct)))
680 {
681 /* No content type or explicitly not JSON, refuse to parse
682 (but keep response code) */
683 if (0 != db->buf_size)
684 {
685 char *url;
686
687 if (CURLE_OK !=
688 curl_easy_getinfo (eh,
689 CURLINFO_EFFECTIVE_URL,
690 &url))
691 url = "<unknown URL>";
692 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
693 "Request to `%s' was expected to return a body of type `application/json', got `%s'\n",
694 url,
695 ct);
696 }
697 return NULL;
698 }
699 if (0 == *response_code)
700 {
701 char *url;
702
703 if (CURLE_OK !=
704 curl_easy_getinfo (eh,
705 CURLINFO_EFFECTIVE_URL,
706 &url))
707 url = "<unknown URL>";
708 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
709 "Failed to download response from `%s': \n",
710 url);
711 return NULL;
712 }
713 json = NULL;
714 if (0 == db->eno)
715 {
716 json = json_loadb (db->buf,
717 db->buf_size,
718 JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
719 &error);
720 if (NULL == json)
721 {
722 JSON_WARN (error);
723 *response_code = 0;
724 }
725 }
726 GNUNET_free (db->buf);
727 db->buf = NULL;
728 db->buf_size = 0;
729 return json;
730}
731
732
733enum GNUNET_GenericReturnValue
734GNUNET_CURL_append_header (struct GNUNET_CURL_Context *ctx,
735 const char *header)
736{
737 ctx->common_headers = curl_slist_append (ctx->common_headers,
738 header);
739 if (NULL == ctx->common_headers)
740 return GNUNET_SYSERR;
741
742 return GNUNET_OK;
743}
744
745
746void
747GNUNET_CURL_perform2 (struct GNUNET_CURL_Context *ctx,
748 GNUNET_CURL_RawParser rp,
749 GNUNET_CURL_ResponseCleaner rc)
750{
751 CURLMsg *cmsg;
752 int n_running;
753 int n_completed;
754
755 (void) curl_multi_perform (ctx->multi,
756 &n_running);
757 while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
758 &n_completed)))
759 {
760 struct GNUNET_CURL_Job *job;
761 struct GNUNET_TIME_Relative duration;
762 long response_code;
763 void *response;
764
765 /* Only documented return value is CURLMSG_DONE */
766 GNUNET_break (CURLMSG_DONE == cmsg->msg);
767 GNUNET_assert (CURLE_OK ==
768 curl_easy_getinfo (cmsg->easy_handle,
769 CURLINFO_PRIVATE,
770 (char **) &job));
771 GNUNET_assert (job->ctx == ctx);
772 response_code = 0;
773 duration = GNUNET_TIME_absolute_get_duration (job->start_time);
774 if (NULL != job->jcc_raw)
775 {
776 /* RAW mode, no parsing */
777 GNUNET_break (CURLE_OK ==
778 curl_easy_getinfo (job->easy_handle,
779 CURLINFO_RESPONSE_CODE,
780 &response_code));
781 job->jcc_raw (job->jcc_raw_cls,
782 response_code,
783 job->db.buf,
784 job->db.buf_size);
785 }
786 else
787 {
788 /* to be parsed via 'rp' */
789 response = rp (&job->db,
790 job->easy_handle,
791 &response_code);
792 job->jcc (job->jcc_cls,
793 response_code,
794 response);
795 rc (response);
796 }
797 {
798 char *url = NULL;
799
800 if (CURLE_UNKNOWN_OPTION ==
801 curl_easy_getinfo (job->easy_handle,
802 CURLINFO_EFFECTIVE_URL,
803 &url))
804 url = "<unknown>";
805 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
806 "HTTP request for `%s' finished with %u after %s\n",
807 url,
808 (unsigned int) response_code,
809 GNUNET_TIME_relative2s (duration,
810 true));
811 /* Note: we MUST NOT free 'url' here */
812 }
813 GNUNET_CURL_job_cancel (job);
814 }
815}
816
817
818void
819GNUNET_CURL_perform (struct GNUNET_CURL_Context *ctx)
820{
821 GNUNET_CURL_perform2 (ctx,
822 &GNUNET_CURL_download_get_result_,
823 (GNUNET_CURL_ResponseCleaner) & json_decref);
824}
825
826
827void
828GNUNET_CURL_get_select_info (struct GNUNET_CURL_Context *ctx,
829 fd_set *read_fd_set,
830 fd_set *write_fd_set,
831 fd_set *except_fd_set,
832 int *max_fd,
833 long *timeout)
834{
835 long to;
836 int m;
837
838 m = -1;
839 GNUNET_assert (CURLM_OK ==
840 curl_multi_fdset (ctx->multi,
841 read_fd_set,
842 write_fd_set,
843 except_fd_set,
844 &m));
845 to = *timeout;
846 *max_fd = GNUNET_MAX (m, *max_fd);
847 GNUNET_assert (CURLM_OK ==
848 curl_multi_timeout (ctx->multi,
849 &to));
850
851 /* Only if what we got back from curl is smaller than what we
852 already had (-1 == infinity!), then update timeout */
853 if ((to < *timeout) && (-1 != to))
854 *timeout = to;
855 if ((-1 == (*timeout)) && (NULL != ctx->jobs_head))
856 *timeout = to;
857}
858
859
860void
861GNUNET_CURL_fini (struct GNUNET_CURL_Context *ctx)
862{
863 /* all jobs must have been cancelled at this time, assert this */
864 GNUNET_assert (NULL == ctx->jobs_head);
865 curl_share_cleanup (ctx->share);
866 curl_multi_cleanup (ctx->multi);
867 curl_slist_free_all (ctx->common_headers);
868 GNUNET_free (ctx->userpass);
869 GNUNET_free (ctx->certtype);
870 GNUNET_free (ctx->certfile);
871 GNUNET_free (ctx->keyfile);
872 GNUNET_free (ctx->keypass);
873 GNUNET_free (ctx);
874}
875
876
877/**
878 * Initial global setup logic, specifically runs the Curl setup.
879 */
880__attribute__ ((constructor)) void
881GNUNET_CURL_constructor__ (void)
882{
883 CURLcode ret;
884
885 if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
886 {
887 CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
888 "curl_global_init",
889 ret);
890 curl_fail = 1;
891 }
892}
893
894
895/**
896 * Cleans up after us, specifically runs the Curl cleanup.
897 */
898__attribute__ ((destructor)) void
899GNUNET_CURL_destructor__ (void)
900{
901 if (curl_fail)
902 return;
903 curl_global_cleanup ();
904}
905
906
907/* end of curl.c */