remove 'illegal' (non-reentrant) log logic from signal handler
[oweals/gnunet.git] / src / curl / curl.c
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 "gnunet_curl_lib.h"
29
30 #if ENABLE_BENCHMARK
31 #include "../util/benchmark.h"
32 #endif
33
34
35 /**
36  * Log error related to CURL operations.
37  *
38  * @param type log level
39  * @param function which function failed to run
40  * @param code what was the curl error code
41  */
42 #define CURL_STRERROR(type, function, code)                                \
43   GNUNET_log (type,                                                        \
44               "Curl function `%s' has failed at `%s:%d' with error: %s\n", \
45               function,                                                    \
46               __FILE__,                                                    \
47               __LINE__,                                                    \
48               curl_easy_strerror (code));
49
50 /**
51  * Print JSON parsing related error information
52  */
53 #define JSON_WARN(error)                                 \
54   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,                 \
55               "JSON parsing failed at %s:%u: %s (%s)\n", \
56               __FILE__,                                  \
57               __LINE__,                                  \
58               error.text,                                \
59               error.source)
60
61
62 /**
63  * Failsafe flag. Raised if our constructor fails to initialize
64  * the Curl library.
65  */
66 static int curl_fail;
67
68 /**
69  * Jobs are CURL requests running within a `struct GNUNET_CURL_Context`.
70  */
71 struct GNUNET_CURL_Job
72 {
73   /**
74    * We keep jobs in a DLL.
75    */
76   struct GNUNET_CURL_Job *next;
77
78   /**
79    * We keep jobs in a DLL.
80    */
81   struct GNUNET_CURL_Job *prev;
82
83   /**
84    * Easy handle of the job.
85    */
86   CURL *easy_handle;
87
88   /**
89    * Context this job runs in.
90    */
91   struct GNUNET_CURL_Context *ctx;
92
93   /**
94    * Function to call upon completion.
95    */
96   GNUNET_CURL_JobCompletionCallback jcc;
97
98   /**
99    * Closure for @e jcc.
100    */
101   void *jcc_cls;
102
103   /**
104    * Function to call upon completion.
105    */
106   GNUNET_CURL_RawJobCompletionCallback jcc_raw;
107
108   /**
109    * Closure for @e jcc_raw.
110    */
111   void *jcc_raw_cls;
112
113   /**
114    * Buffer for response received from CURL.
115    */
116   struct GNUNET_CURL_DownloadBuffer db;
117
118   /**
119    * Headers used for this job, the list needs to be freed
120    * after the job has finished.
121    */
122   struct curl_slist *job_headers;
123 };
124
125
126 /**
127  * Context
128  */
129 struct GNUNET_CURL_Context
130 {
131   /**
132    * Curl multi handle
133    */
134   CURLM *multi;
135
136   /**
137    * Curl share handle
138    */
139   CURLSH *share;
140
141   /**
142    * We keep jobs in a DLL.
143    */
144   struct GNUNET_CURL_Job *jobs_head;
145
146   /**
147    * We keep jobs in a DLL.
148    */
149   struct GNUNET_CURL_Job *jobs_tail;
150
151   /**
152    * Headers common for all requests in the context.
153    */
154   struct curl_slist *common_headers;
155
156   /**
157    * If non-NULL, the async scope ID is sent in a request
158    * header of this name.
159    */
160   const char *async_scope_id_header;
161
162   /**
163    * Function we need to call whenever the event loop's
164    * socket set changed.
165    */
166   GNUNET_CURL_RescheduleCallback cb;
167
168   /**
169    * Closure for @e cb.
170    */
171   void *cb_cls;
172 };
173
174
175 /**
176  * Initialise this library.  This function should be called before using any of
177  * the following functions.
178  *
179  * @param cb function to call when rescheduling is required
180  * @param cb_cls closure for @a cb
181  * @return library context
182  */
183 struct GNUNET_CURL_Context *
184 GNUNET_CURL_init (GNUNET_CURL_RescheduleCallback cb, void *cb_cls)
185 {
186   struct GNUNET_CURL_Context *ctx;
187   CURLM *multi;
188   CURLSH *share;
189
190   if (curl_fail)
191   {
192     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Curl was not initialised properly\n");
193     return NULL;
194   }
195   if (NULL == (multi = curl_multi_init ()))
196   {
197     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
198                 "Failed to create a Curl multi handle\n");
199     return NULL;
200   }
201   if (NULL == (share = curl_share_init ()))
202   {
203     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
204                 "Failed to create a Curl share handle\n");
205     return NULL;
206   }
207   ctx = GNUNET_new (struct GNUNET_CURL_Context);
208   ctx->cb = cb;
209   ctx->cb_cls = cb_cls;
210   ctx->multi = multi;
211   ctx->share = share;
212   return ctx;
213 }
214
215
216 /**
217  * Enable sending the async scope ID as a header.
218  *
219  * @param ctx the context to enable this for
220  * @param header_name name of the header to send.
221  */
222 void
223 GNUNET_CURL_enable_async_scope_header (struct GNUNET_CURL_Context *ctx,
224                                        const char *header_name)
225 {
226   ctx->async_scope_id_header = header_name;
227 }
228
229
230 /**
231  * Callback used when downloading the reply to an HTTP request.
232  * Just appends all of the data to the `buf` in the
233  * `struct DownloadBuffer` for further processing. The size of
234  * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
235  * the download exceeds this size, we abort with an error.
236  *
237  * @param bufptr data downloaded via HTTP
238  * @param size size of an item in @a bufptr
239  * @param nitems number of items in @a bufptr
240  * @param cls the `struct DownloadBuffer`
241  * @return number of bytes processed from @a bufptr
242  */
243 static size_t
244 download_cb (char *bufptr, size_t size, size_t nitems, void *cls)
245 {
246   struct GNUNET_CURL_DownloadBuffer *db = cls;
247   size_t msize;
248   void *buf;
249
250   if (0 == size * nitems)
251   {
252     /* Nothing (left) to do */
253     return 0;
254   }
255   msize = size * nitems;
256   if ((msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
257   {
258     db->eno = ENOMEM;
259     return 0;   /* signals an error to curl */
260   }
261   db->buf = GNUNET_realloc (db->buf, db->buf_size + msize);
262   buf = db->buf + db->buf_size;
263   GNUNET_memcpy (buf, bufptr, msize);
264   db->buf_size += msize;
265   return msize;
266 }
267
268
269 /**
270  * Create the HTTP headers for the request
271  *
272  * @param ctx context we run in
273  * @param job_headers job-specific headers
274  * @return all headers to use
275  */
276 static struct curl_slist *
277 setup_job_headers (struct GNUNET_CURL_Context *ctx,
278                    const struct curl_slist *job_headers)
279 {
280   struct curl_slist *all_headers = NULL;
281
282   for (const struct curl_slist *curr = job_headers; curr != NULL;
283        curr = curr->next)
284   {
285     GNUNET_assert (NULL !=
286                    (all_headers = curl_slist_append (all_headers, curr->data)));
287   }
288
289   for (const struct curl_slist *curr = ctx->common_headers; curr != NULL;
290        curr = curr->next)
291   {
292     GNUNET_assert (NULL !=
293                    (all_headers = curl_slist_append (all_headers, curr->data)));
294   }
295
296   if (NULL != ctx->async_scope_id_header)
297   {
298     struct GNUNET_AsyncScopeSave scope;
299
300     GNUNET_async_scope_get (&scope);
301     if (GNUNET_YES == scope.have_scope)
302     {
303       char *aid_header = NULL;
304       aid_header =
305         GNUNET_STRINGS_data_to_string_alloc (&scope.scope_id,
306                                              sizeof(
307                                                struct GNUNET_AsyncScopeId));
308       GNUNET_assert (NULL != aid_header);
309       GNUNET_assert (NULL != curl_slist_append (all_headers, aid_header));
310       GNUNET_free (aid_header);
311     }
312   }
313   return all_headers;
314 }
315
316
317 /**
318  * Create a job.
319  *
320  * @param eh easy handle to use
321  * @param ctx context to run the job in
322  * @param all_headers HTTP client headers to use (free'd)
323  * @return NULL on error
324  */
325 static struct GNUNET_CURL_Job *
326 setup_job (CURL *eh,
327            struct GNUNET_CURL_Context *ctx,
328            struct curl_slist *all_headers)
329 {
330   struct GNUNET_CURL_Job *job;
331
332   if (CURLE_OK !=
333       curl_easy_setopt (eh, CURLOPT_HTTPHEADER, all_headers))
334   {
335     GNUNET_break (0);
336     curl_slist_free_all (all_headers);
337     curl_easy_cleanup (eh);
338     return NULL;
339   }
340   job = GNUNET_new (struct GNUNET_CURL_Job);
341   job->job_headers = all_headers;
342
343   if ((CURLE_OK != curl_easy_setopt (eh, CURLOPT_PRIVATE, job)) ||
344       (CURLE_OK !=
345        curl_easy_setopt (eh, CURLOPT_WRITEFUNCTION, &download_cb)) ||
346       (CURLE_OK != curl_easy_setopt (eh, CURLOPT_WRITEDATA, &job->db)) ||
347       (CURLE_OK != curl_easy_setopt (eh, CURLOPT_SHARE, ctx->share)) ||
348       (CURLM_OK != curl_multi_add_handle (ctx->multi, eh)))
349   {
350     GNUNET_break (0);
351     GNUNET_free (job);
352     curl_easy_cleanup (eh);
353     return NULL;
354   }
355   job->easy_handle = eh;
356   job->ctx = ctx;
357   GNUNET_CONTAINER_DLL_insert (ctx->jobs_head, ctx->jobs_tail, job);
358   return job;
359 }
360
361
362 /**
363  * Schedule a CURL request to be executed and call the given @a jcc
364  * upon its completion.  Note that the context will make use of the
365  * CURLOPT_PRIVATE facility of the CURL @a eh.  Used to download
366  * resources that are NOT in JSON.  The raw body will be returned.
367  *
368  * @param ctx context to execute the job in
369  * @param eh curl easy handle for the request, will
370  *           be executed AND cleaned up
371  * @param job_headers extra headers to add for this request
372  * @param max_reply_size largest acceptable response body
373  * @param jcc callback to invoke upon completion
374  * @param jcc_cls closure for @a jcc
375  * @return NULL on error (in this case, @eh is still released!)
376  */
377 struct GNUNET_CURL_Job *
378 GNUNET_CURL_job_add_raw (struct GNUNET_CURL_Context *ctx,
379                          CURL *eh,
380                          const struct curl_slist *job_headers,
381                          GNUNET_CURL_RawJobCompletionCallback jcc,
382                          void *jcc_cls)
383 {
384   struct GNUNET_CURL_Job *job;
385   struct curl_slist *all_headers;
386
387   GNUNET_assert (NULL != jcc);
388   all_headers = setup_job_headers (ctx,
389                                    job_headers);
390   if (NULL == (job = setup_job (eh,
391                                 ctx,
392                                 all_headers)))
393     return NULL;
394   job->jcc_raw = jcc;
395   job->jcc_raw_cls = jcc_cls;
396   ctx->cb (ctx->cb_cls);
397   return job;
398 }
399
400
401 /**
402  * Schedule a CURL request to be executed and call the given @a jcc
403  * upon its completion.  Note that the context will make use of the
404  * CURLOPT_PRIVATE facility of the CURL @a eh.
405  *
406  * This function modifies the CURL handle to add the
407  * "Content-Type: application/json" header if @a add_json is set.
408  *
409  * @param ctx context to execute the job in
410  * @param eh curl easy handle for the request, will be executed AND
411  *           cleaned up.  NOTE: the handle should _never_ have gotten
412  *           any headers list, as that would then be ovverridden by
413  *           @a jcc.  Therefore, always pass custom headers as the
414  *           @a job_headers parameter.
415  * @param job_headers extra headers to add for this request
416  * @param jcc callback to invoke upon completion
417  * @param jcc_cls closure for @a jcc
418  * @return NULL on error (in this case, @eh is still released!)
419  */
420 struct GNUNET_CURL_Job *
421 GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx,
422                       CURL *eh,
423                       const struct curl_slist *job_headers,
424                       GNUNET_CURL_JobCompletionCallback jcc,
425                       void *jcc_cls)
426 {
427   struct GNUNET_CURL_Job *job;
428   struct curl_slist *all_headers;
429
430   GNUNET_assert (NULL != jcc);
431   all_headers = setup_job_headers (ctx,
432                                    job_headers);
433   if (NULL == (job = setup_job (eh,
434                                 ctx,
435                                 all_headers)))
436     return NULL;
437
438   job->jcc = jcc;
439   job->jcc_cls = jcc_cls;
440   ctx->cb (ctx->cb_cls);
441   return job;
442 }
443
444
445 /**
446  * Schedule a CURL request to be executed and call the given @a jcc
447  * upon its completion.  Note that the context will make use of the
448  * CURLOPT_PRIVATE facility of the CURL @a eh.
449  *
450  * This function modifies the CURL handle to add the
451  * "Content-Type: application/json" header if @a add_json is set.
452  *
453  * @param ctx context to execute the job in
454  * @param eh curl easy handle for the request, will
455  *           be executed AND cleaned up
456  * @param add_json add "application/json" content type header
457  * @param jcc callback to invoke upon completion
458  * @param jcc_cls closure for @a jcc
459  * @return NULL on error (in this case, @eh is still released!)
460  */
461 struct GNUNET_CURL_Job *
462 GNUNET_CURL_job_add (struct GNUNET_CURL_Context *ctx,
463                      CURL *eh,
464                      int add_json,
465                      GNUNET_CURL_JobCompletionCallback jcc,
466                      void *jcc_cls)
467 {
468   struct GNUNET_CURL_Job *job;
469   struct curl_slist *job_headers = NULL;
470
471   if (GNUNET_YES == add_json)
472   {
473     GNUNET_assert (
474       NULL != (job_headers =
475                  curl_slist_append (NULL, "Content-Type: application/json")));
476   }
477
478   job = GNUNET_CURL_job_add2 (ctx, eh, job_headers, jcc, jcc_cls);
479   curl_slist_free_all (job_headers);
480   return job;
481 }
482
483
484 /**
485  * Cancel a job.  Must only be called before the job completion
486  * callback is called for the respective job.
487  *
488  * @param job job to cancel
489  */
490 void
491 GNUNET_CURL_job_cancel (struct GNUNET_CURL_Job *job)
492 {
493   struct GNUNET_CURL_Context *ctx = job->ctx;
494
495   GNUNET_CONTAINER_DLL_remove (ctx->jobs_head, ctx->jobs_tail, job);
496   GNUNET_break (CURLM_OK ==
497                 curl_multi_remove_handle (ctx->multi, job->easy_handle));
498   curl_easy_cleanup (job->easy_handle);
499   GNUNET_free_non_null (job->db.buf);
500   curl_slist_free_all (job->job_headers);
501   ctx->cb (ctx->cb_cls);
502   GNUNET_free (job);
503 }
504
505
506 /**
507  * Obtain information about the final result about the
508  * HTTP download. If the download was successful, parses
509  * the JSON in the @a db and returns it. Also returns
510  * the HTTP @a response_code.  If the download failed,
511  * the return value is NULL.  The response code is set
512  * in any case, on download errors to zero.
513  *
514  * Calling this function also cleans up @a db.
515  *
516  * @param db download buffer
517  * @param eh CURL handle (to get the response code)
518  * @param[out] response_code set to the HTTP response code
519  *             (or zero if we aborted the download, i.e.
520  *              because the response was too big, or if
521  *              the JSON we received was malformed).
522  * @return NULL if downloading a JSON reply failed.
523  */
524 void *
525 GNUNET_CURL_download_get_result_ (struct GNUNET_CURL_DownloadBuffer *db,
526                                   CURL *eh,
527                                   long *response_code)
528 {
529   json_t *json;
530   json_error_t error;
531   char *ct;
532
533   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
534               "Downloaded body: %.*s\n",
535               (int) db->buf_size,
536               (char *) db->buf);
537
538   if ((CURLE_OK !=
539        curl_easy_getinfo (eh,
540                           CURLINFO_CONTENT_TYPE,
541                           &ct)) ||
542       (NULL == ct) ||
543       (0 != strcasecmp (ct,
544                         "application/json")))
545   {
546     /* No content type or explicitly not JSON, refuse to parse
547        (but keep response code) */
548     if (CURLE_OK !=
549         curl_easy_getinfo (eh,
550                            CURLINFO_RESPONSE_CODE,
551                            response_code))
552     {
553       /* unexpected error... */
554       GNUNET_break (0);
555       *response_code = 0;
556     }
557     if (0 != db->buf_size)
558       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
559                   "Did NOT detect response `%.*s' as JSON\n",
560                   (int) db->buf_size,
561                   (const char *) db->buf);
562     return NULL;
563   }
564   json = NULL;
565   if (0 == db->eno)
566   {
567     json = json_loadb (db->buf,
568                        db->buf_size,
569                        JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
570                        &error);
571     if (NULL == json)
572     {
573       JSON_WARN (error);
574       *response_code = 0;
575     }
576   }
577   GNUNET_free_non_null (db->buf);
578   db->buf = NULL;
579   db->buf_size = 0;
580   if (NULL != json)
581   {
582     if (CURLE_OK !=
583         curl_easy_getinfo (eh,
584                            CURLINFO_RESPONSE_CODE,
585                            response_code))
586     {
587       /* unexpected error... */
588       GNUNET_break (0);
589       *response_code = 0;
590     }
591   }
592   return json;
593 }
594
595
596 /**
597  * Add custom request header.
598  *
599  * @param ctx cURL context.
600  * @param header header string; will be given to the context AS IS.
601  * @return #GNUNET_OK if no errors occurred, #GNUNET_SYSERR otherwise.
602  */
603 int
604 GNUNET_CURL_append_header (struct GNUNET_CURL_Context *ctx, const char *header)
605 {
606   ctx->common_headers = curl_slist_append (ctx->common_headers, header);
607   if (NULL == ctx->common_headers)
608     return GNUNET_SYSERR;
609
610   return GNUNET_OK;
611 }
612
613
614 #if ENABLE_BENCHMARK
615 static void
616 do_benchmark (CURLMsg *cmsg)
617 {
618   char *url = NULL;
619   double total_as_double = 0;
620   struct GNUNET_TIME_Relative total;
621   struct UrlRequestData *urd;
622   /* Some care required, as curl is using data types (long vs curl_off_t vs
623    * double) inconsistently to store byte count. */
624   curl_off_t size_curl = 0;
625   long size_long = 0;
626   uint64_t bytes_sent = 0;
627   uint64_t bytes_received = 0;
628
629   GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
630                                                CURLINFO_TOTAL_TIME,
631                                                &total_as_double));
632   total.rel_value_us = total_as_double * 1000 * 1000;
633
634   GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
635                                                CURLINFO_EFFECTIVE_URL,
636                                                &url));
637
638   /* HEADER_SIZE + SIZE_DOWNLOAD_T is hopefully the total
639      number of bytes received, not clear from curl docs. */
640
641   GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
642                                                CURLINFO_HEADER_SIZE,
643                                                &size_long));
644   bytes_received += size_long;
645
646   GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
647                                                CURLINFO_SIZE_DOWNLOAD_T,
648                                                &size_curl));
649   bytes_received += size_curl;
650
651   /* REQUEST_SIZE + SIZE_UPLOAD_T is hopefully the total number of bytes
652      sent, again docs are not completely clear. */
653
654   GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
655                                                CURLINFO_REQUEST_SIZE,
656                                                &size_long));
657   bytes_sent += size_long;
658
659   /* We obtain this value to check an invariant, but never use it otherwise. */
660   GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
661                                                CURLINFO_SIZE_UPLOAD_T,
662                                                &size_curl));
663
664   /* CURLINFO_SIZE_UPLOAD_T <= CURLINFO_REQUEST_SIZE should
665      be an invariant.
666      As verified with
667      curl -w "foo%{size_request} -XPOST --data "ABC" $URL
668      the CURLINFO_REQUEST_SIZE should be the whole size of the request
669      including headers and body.
670    */GNUNET_break (size_curl <= size_long);
671
672   urd = get_url_benchmark_data (url, (unsigned int) response_code);
673   urd->count++;
674   urd->time = GNUNET_TIME_relative_add (urd->time, total);
675   urd->time_max = GNUNET_TIME_relative_max (total, urd->time_max);
676   urd->bytes_sent += bytes_sent;
677   urd->bytes_received += bytes_received;
678 }
679
680
681 #endif
682
683
684 /**
685  * Run the main event loop for the HTTP interaction.
686  *
687  * @param ctx the library context
688  * @param rp parses the raw response returned from
689  *        the Web server.
690  * @param rc cleans/frees the response
691  */
692 void
693 GNUNET_CURL_perform2 (struct GNUNET_CURL_Context *ctx,
694                       GNUNET_CURL_RawParser rp,
695                       GNUNET_CURL_ResponseCleaner rc)
696 {
697   CURLMsg *cmsg;
698   int n_running;
699   int n_completed;
700
701   (void) curl_multi_perform (ctx->multi,
702                              &n_running);
703   while (NULL != (cmsg = curl_multi_info_read (ctx->multi, &n_completed)))
704   {
705     struct GNUNET_CURL_Job *job;
706     long response_code;
707     void *response;
708
709     /* Only documented return value is CURLMSG_DONE */
710     GNUNET_break (CURLMSG_DONE == cmsg->msg);
711     GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
712                                                   CURLINFO_PRIVATE,
713                                                   (char **) &job));
714     GNUNET_assert (job->ctx == ctx);
715     response_code = 0;
716     if (NULL != job->jcc_raw)
717     {
718       /* RAW mode, no parsing */
719       GNUNET_break (CURLE_OK ==
720                     curl_easy_getinfo (job->easy_handle,
721                                        CURLINFO_RESPONSE_CODE,
722                                        &response_code));
723       job->jcc_raw (job->jcc_raw_cls,
724                     response_code,
725                     job->db.buf,
726                     job->db.buf_size);
727     }
728     else
729     {
730       /* to be parsed via 'rp' */
731       response = rp (&job->db,
732                      job->easy_handle,
733                      &response_code);
734       job->jcc (job->jcc_cls,
735                 response_code,
736                 response);
737       rc (response);
738     }
739 #if ENABLE_BENCHMARK
740     do_benchmark (cmsg);
741 #endif
742     GNUNET_CURL_job_cancel (job);
743   }
744 }
745
746
747 /**
748  * Run the main event loop for the HTTP interaction.
749  *
750  * @param ctx the library context
751  */
752 void
753 GNUNET_CURL_perform (struct GNUNET_CURL_Context *ctx)
754 {
755   GNUNET_CURL_perform2 (ctx,
756                         &GNUNET_CURL_download_get_result_,
757                         (GNUNET_CURL_ResponseCleaner) & json_decref);
758 }
759
760
761 /**
762  * Obtain the information for a select() call to wait until
763  * #GNUNET_CURL_perform() is ready again.  Note that calling
764  * any other GNUNET_CURL-API may also imply that the library
765  * is again ready for #GNUNET_CURL_perform().
766  *
767  * Basically, a client should use this API to prepare for select(),
768  * then block on select(), then call #GNUNET_CURL_perform() and then
769  * start again until the work with the context is done.
770  *
771  * This function will NOT zero out the sets and assumes that @a max_fd
772  * and @a timeout are already set to minimal applicable values.  It is
773  * safe to give this API FD-sets and @a max_fd and @a timeout that are
774  * already initialized to some other descriptors that need to go into
775  * the select() call.
776  *
777  * @param ctx context to get the event loop information for
778  * @param read_fd_set will be set for any pending read operations
779  * @param write_fd_set will be set for any pending write operations
780  * @param except_fd_set is here because curl_multi_fdset() has this argument
781  * @param max_fd set to the highest FD included in any set;
782  *        if the existing sets have no FDs in it, the initial
783  *        value should be "-1". (Note that `max_fd + 1` will need
784  *        to be passed to select().)
785  * @param timeout set to the timeout in milliseconds (!); -1 means
786  *        no timeout (NULL, blocking forever is OK), 0 means to
787  *        proceed immediately with #GNUNET_CURL_perform().
788  */
789 void
790 GNUNET_CURL_get_select_info (struct GNUNET_CURL_Context *ctx,
791                              fd_set *read_fd_set,
792                              fd_set *write_fd_set,
793                              fd_set *except_fd_set,
794                              int *max_fd,
795                              long *timeout)
796 {
797   long to;
798   int m;
799
800   m = -1;
801   GNUNET_assert (CURLM_OK == curl_multi_fdset (ctx->multi,
802                                                read_fd_set,
803                                                write_fd_set,
804                                                except_fd_set,
805                                                &m));
806   to = *timeout;
807   *max_fd = GNUNET_MAX (m, *max_fd);
808   GNUNET_assert (CURLM_OK == curl_multi_timeout (ctx->multi, &to));
809
810   /* Only if what we got back from curl is smaller than what we
811      already had (-1 == infinity!), then update timeout */
812   if ((to < *timeout) && (-1 != to))
813     *timeout = to;
814   if ((-1 == (*timeout)) && (NULL != ctx->jobs_head))
815     *timeout = to;
816 }
817
818
819 /**
820  * Cleanup library initialisation resources.  This function should be called
821  * after using this library to cleanup the resources occupied during library's
822  * initialisation.
823  *
824  * @param ctx the library context
825  */
826 void
827 GNUNET_CURL_fini (struct GNUNET_CURL_Context *ctx)
828 {
829   /* all jobs must have been cancelled at this time, assert this */
830   GNUNET_assert (NULL == ctx->jobs_head);
831   curl_share_cleanup (ctx->share);
832   curl_multi_cleanup (ctx->multi);
833   curl_slist_free_all (ctx->common_headers);
834   GNUNET_free (ctx);
835 }
836
837
838 /**
839  * Initial global setup logic, specifically runs the Curl setup.
840  */
841 __attribute__ ((constructor)) void
842 GNUNET_CURL_constructor__ (void)
843 {
844   CURLcode ret;
845
846   if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
847   {
848     CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR, "curl_global_init", ret);
849     curl_fail = 1;
850   }
851 }
852
853
854 /**
855  * Cleans up after us, specifically runs the Curl cleanup.
856  */
857 __attribute__ ((destructor)) void
858 GNUNET_CURL_destructor__ (void)
859 {
860   if (curl_fail)
861     return;
862   curl_global_cleanup ();
863 }
864
865
866 /* end of curl.c */