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