improve test shutdown and indentation
[oweals/gnunet.git] / src / curl / curl.c
1 /*
2   This file is part of GNUnet
3   Copyright (C) 2014, 2015, 2016 GNUnet e.V.
4
5   GNUnet is free software; you can redistribute it and/or modify it under the
6   terms of the GNU General Public License as published by the Free Software
7   Foundation; either version 3, or (at your option) any later version.
8
9   GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
12
13   You should have received a copy of the GNU General Public License along with
14   GNUnet; see the file COPYING.  If not, If not, see
15   <http://www.gnu.org/licenses/>
16 */
17 /**
18  * @file curl/curl.c
19  * @brief API for downloading JSON via CURL
20  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
21  * @author Christian Grothoff
22  */
23 #include "platform.h"
24 #if HAVE_CURL_CURL_H
25 #include <curl/curl.h>
26 #elif HAVE_GNURL_CURL_H
27 #include <gnurl/curl.h>
28 #endif
29 #include <jansson.h>
30 #include "gnunet_curl_lib.h"
31
32
33 /**
34  * Log error related to CURL operations.
35  *
36  * @param type log level
37  * @param function which function failed to run
38  * @param code what was the curl error code
39  */
40 #define CURL_STRERROR(type, function, code)      \
41  GNUNET_log (type,                               \
42              "Curl function `%s' has failed at `%s:%d' with error: %s\n", \
43              function, __FILE__, __LINE__, curl_easy_strerror (code));
44
45 /**
46  * Print JSON parsing related error information
47  */
48 #define JSON_WARN(error)                                                \
49     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,                              \
50                 "JSON parsing failed at %s:%u: %s (%s)\n",              \
51                 __FILE__, __LINE__, error.text, error.source)
52
53
54 /**
55  * Failsafe flag. Raised if our constructor fails to initialize
56  * the Curl library.
57  */
58 static int curl_fail;
59
60
61 /**
62  * @brief Buffer data structure we use to buffer the HTTP download
63  * before giving it to the JSON parser.
64  */
65 struct DownloadBuffer
66 {
67
68   /**
69    * Download buffer
70    */
71   void *buf;
72
73   /**
74    * The size of the download buffer
75    */
76   size_t buf_size;
77
78   /**
79    * Error code (based on libc errno) if we failed to download
80    * (i.e. response too large).
81    */
82   int eno;
83
84 };
85
86
87 /**
88  * Jobs are CURL requests running within a `struct GNUNET_CURL_Context`.
89  */
90 struct GNUNET_CURL_Job
91 {
92
93   /**
94    * We keep jobs in a DLL.
95    */
96   struct GNUNET_CURL_Job *next;
97
98   /**
99    * We keep jobs in a DLL.
100    */
101   struct GNUNET_CURL_Job *prev;
102
103   /**
104    * Easy handle of the job.
105    */
106   CURL *easy_handle;
107
108   /**
109    * Context this job runs in.
110    */
111   struct GNUNET_CURL_Context *ctx;
112
113   /**
114    * Function to call upon completion.
115    */
116   GNUNET_CURL_JobCompletionCallback jcc;
117
118   /**
119    * Closure for @e jcc.
120    */
121   void *jcc_cls;
122
123   /**
124    * Buffer for response received from CURL.
125    */
126   struct DownloadBuffer db;
127
128 };
129
130
131 /**
132  * Context
133  */
134 struct GNUNET_CURL_Context
135 {
136   /**
137    * Curl multi handle
138    */
139   CURLM *multi;
140
141   /**
142    * Curl share handle
143    */
144   CURLSH *share;
145
146   /**
147    * We keep jobs in a DLL.
148    */
149   struct GNUNET_CURL_Job *jobs_head;
150
151   /**
152    * We keep jobs in a DLL.
153    */
154   struct GNUNET_CURL_Job *jobs_tail;
155
156   /**
157    * HTTP header "application/json", created once and used
158    * for all requests that need it.
159    */
160   struct curl_slist *json_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,
185                   void *cb_cls)
186 {
187   struct GNUNET_CURL_Context *ctx;
188   CURLM *multi;
189   CURLSH *share;
190
191   if (curl_fail)
192   {
193     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194                 "Curl was not initialised properly\n");
195     return NULL;
196   }
197   if (NULL == (multi = curl_multi_init ()))
198   {
199     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
200                 "Failed to create a Curl multi handle\n");
201     return NULL;
202   }
203   if (NULL == (share = curl_share_init ()))
204   {
205     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
206                 "Failed to create a Curl share handle\n");
207     return NULL;
208   }
209   ctx = GNUNET_new (struct GNUNET_CURL_Context);
210   ctx->cb = cb;
211   ctx->cb_cls = cb_cls;
212   ctx->multi = multi;
213   ctx->share = share;
214   GNUNET_assert (NULL != (ctx->json_header =
215                           curl_slist_append (NULL,
216                                              "Content-Type: application/json")));
217   return ctx;
218 }
219
220
221 /**
222  * Callback used when downloading the reply to an HTTP request.
223  * Just appends all of the data to the `buf` in the
224  * `struct DownloadBuffer` for further processing. The size of
225  * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
226  * the download exceeds this size, we abort with an error.
227  *
228  * @param bufptr data downloaded via HTTP
229  * @param size size of an item in @a bufptr
230  * @param nitems number of items in @a bufptr
231  * @param cls the `struct DownloadBuffer`
232  * @return number of bytes processed from @a bufptr
233  */
234 static size_t
235 download_cb (char *bufptr,
236              size_t size,
237              size_t nitems,
238              void *cls)
239 {
240   struct DownloadBuffer *db = cls;
241   size_t msize;
242   void *buf;
243
244   if (0 == size * nitems)
245   {
246     /* Nothing (left) to do */
247     return 0;
248   }
249   msize = size * nitems;
250   if ( (msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
251   {
252     db->eno = ENOMEM;
253     return 0; /* signals an error to curl */
254   }
255   db->buf = GNUNET_realloc (db->buf,
256                             db->buf_size + msize);
257   buf = db->buf + db->buf_size;
258   GNUNET_memcpy (buf, bufptr, msize);
259   db->buf_size += msize;
260   return msize;
261 }
262
263
264 /**
265  * Schedule a CURL request to be executed and call the given @a jcc
266  * upon its completion.  Note that the context will make use of the
267  * CURLOPT_PRIVATE facility of the CURL @a eh.
268  *
269  * This function modifies the CURL handle to add the
270  * "Content-Type: application/json" header if @a add_json is set.
271  *
272  * @param ctx context to execute the job in
273  * @param eh curl easy handle for the request, will
274  *           be executed AND cleaned up
275  * @param add_json add "application/json" content type header
276  * @param jcc callback to invoke upon completion
277  * @param jcc_cls closure for @a jcc
278  * @return NULL on error (in this case, @eh is still released!)
279  */
280 struct GNUNET_CURL_Job *
281 GNUNET_CURL_job_add (struct GNUNET_CURL_Context *ctx,
282                      CURL *eh,
283                      int add_json,
284                      GNUNET_CURL_JobCompletionCallback jcc,
285                      void *jcc_cls)
286 {
287   struct GNUNET_CURL_Job *job;
288
289   if (GNUNET_YES == add_json)
290     if (CURLE_OK !=
291         curl_easy_setopt (eh,
292                           CURLOPT_HTTPHEADER,
293                           ctx->json_header))
294     {
295       GNUNET_break (0);
296       curl_easy_cleanup (eh);
297       return NULL;
298     }
299
300   job = GNUNET_new (struct GNUNET_CURL_Job);
301   if ( (CURLE_OK !=
302         curl_easy_setopt (eh,
303                           CURLOPT_PRIVATE,
304                           job)) ||
305        (CURLE_OK !=
306         curl_easy_setopt (eh,
307                           CURLOPT_WRITEFUNCTION,
308                           &download_cb)) ||
309        (CURLE_OK !=
310         curl_easy_setopt (eh,
311                           CURLOPT_WRITEDATA,
312                           &job->db)) ||
313        (CURLE_OK !=
314         curl_easy_setopt (eh,
315                           CURLOPT_SHARE,
316                           ctx->share)) ||
317        (CURLM_OK !=
318         curl_multi_add_handle (ctx->multi,
319                                eh)) )
320   {
321     GNUNET_break (0);
322     GNUNET_free (job);
323     curl_easy_cleanup (eh);
324     return NULL;
325   }
326
327   job->easy_handle = eh;
328   job->ctx = ctx;
329   job->jcc = jcc;
330   job->jcc_cls = jcc_cls;
331   GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
332                                ctx->jobs_tail,
333                                job);
334   ctx->cb (ctx->cb_cls);
335   return job;
336 }
337
338
339 /**
340  * Cancel a job.  Must only be called before the job completion
341  * callback is called for the respective job.
342  *
343  * @param job job to cancel
344  */
345 void
346 GNUNET_CURL_job_cancel (struct GNUNET_CURL_Job *job)
347 {
348   struct GNUNET_CURL_Context *ctx = job->ctx;
349
350   GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
351                                ctx->jobs_tail,
352                                job);
353   GNUNET_break (CURLM_OK ==
354                 curl_multi_remove_handle (ctx->multi,
355                                           job->easy_handle));
356   curl_easy_cleanup (job->easy_handle);
357   GNUNET_free_non_null (job->db.buf);
358   GNUNET_free (job);
359 }
360
361
362 /**
363  * Obtain information about the final result about the
364  * HTTP download. If the download was successful, parses
365  * the JSON in the @a db and returns it. Also returns
366  * the HTTP @a response_code.  If the download failed,
367  * the return value is NULL.  The response code is set
368  * in any case, on download errors to zero.
369  *
370  * Calling this function also cleans up @a db.
371  *
372  * @param db download buffer
373  * @param eh CURL handle (to get the response code)
374  * @param[out] response_code set to the HTTP response code
375  *             (or zero if we aborted the download, i.e.
376  *              because the response was too big, or if
377  *              the JSON we received was malformed).
378  * @return NULL if downloading a JSON reply failed
379  */
380 static json_t *
381 download_get_result (struct DownloadBuffer *db,
382                      CURL *eh,
383                      long *response_code)
384 {
385   json_t *json;
386   json_error_t error;
387   char *ct;
388
389   if ( (CURLE_OK !=
390         curl_easy_getinfo (eh,
391                            CURLINFO_CONTENT_TYPE,
392                            &ct)) ||
393        (NULL == ct) ||
394        (0 != strcasecmp (ct,
395                          "application/json")) )
396   {
397     /* No content type or explicitly not JSON, refuse to parse
398        (but keep response code) */
399     if (CURLE_OK !=
400         curl_easy_getinfo (eh,
401                            CURLINFO_RESPONSE_CODE,
402                            response_code))
403     {
404       /* unexpected error... */
405       GNUNET_break (0);
406       *response_code = 0;
407     }
408     return NULL;
409   }
410   json = NULL;
411   if (0 == db->eno)
412   {
413     json = json_loadb (db->buf,
414                        db->buf_size,
415                        JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
416                        &error);
417     if (NULL == json)
418     {
419       JSON_WARN (error);
420       *response_code = 0;
421     }
422   }
423   GNUNET_free_non_null (db->buf);
424   db->buf = NULL;
425   db->buf_size = 0;
426   if (NULL != json)
427   {
428     if (CURLE_OK !=
429         curl_easy_getinfo (eh,
430                            CURLINFO_RESPONSE_CODE,
431                            response_code))
432     {
433       /* unexpected error... */
434       GNUNET_break (0);
435       *response_code = 0;
436     }
437   }
438   return json;
439 }
440
441
442 /**
443  * Run the main event loop for the Taler interaction.
444  *
445  * @param ctx the library context
446  */
447 void
448 GNUNET_CURL_perform (struct GNUNET_CURL_Context *ctx)
449 {
450   CURLMsg *cmsg;
451   struct GNUNET_CURL_Job *job;
452   int n_running;
453   int n_completed;
454   long response_code;
455   json_t *j;
456
457   (void) curl_multi_perform (ctx->multi,
458                              &n_running);
459   while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
460                                                &n_completed)))
461   {
462     /* Only documented return value is CURLMSG_DONE */
463     GNUNET_break (CURLMSG_DONE == cmsg->msg);
464     GNUNET_assert (CURLE_OK ==
465                    curl_easy_getinfo (cmsg->easy_handle,
466                                       CURLINFO_PRIVATE,
467                                       (char **) &job));
468     GNUNET_assert (job->ctx == ctx);
469     j = download_get_result (&job->db,
470                              job->easy_handle,
471                              &response_code);
472     job->jcc (job->jcc_cls,
473               response_code,
474               j);
475     json_decref (j);
476     GNUNET_CURL_job_cancel (job);
477   }
478 }
479
480
481 /**
482  * Obtain the information for a select() call to wait until
483  * #GNUNET_CURL_perform() is ready again.  Note that calling
484  * any other GNUNET_CURL-API may also imply that the library
485  * is again ready for #GNUNET_CURL_perform().
486  *
487  * Basically, a client should use this API to prepare for select(),
488  * then block on select(), then call #GNUNET_CURL_perform() and then
489  * start again until the work with the context is done.
490  *
491  * This function will NOT zero out the sets and assumes that @a max_fd
492  * and @a timeout are already set to minimal applicable values.  It is
493  * safe to give this API FD-sets and @a max_fd and @a timeout that are
494  * already initialized to some other descriptors that need to go into
495  * the select() call.
496  *
497  * @param ctx context to get the event loop information for
498  * @param read_fd_set will be set for any pending read operations
499  * @param write_fd_set will be set for any pending write operations
500  * @param except_fd_set is here because curl_multi_fdset() has this argument
501  * @param max_fd set to the highest FD included in any set;
502  *        if the existing sets have no FDs in it, the initial
503  *        value should be "-1". (Note that `max_fd + 1` will need
504  *        to be passed to select().)
505  * @param timeout set to the timeout in milliseconds (!); -1 means
506  *        no timeout (NULL, blocking forever is OK), 0 means to
507  *        proceed immediately with #GNUNET_CURL_perform().
508  */
509 void
510 GNUNET_CURL_get_select_info (struct GNUNET_CURL_Context *ctx,
511                              fd_set *read_fd_set,
512                              fd_set *write_fd_set,
513                              fd_set *except_fd_set,
514                              int *max_fd,
515                              long *timeout)
516 {
517   long to;
518   int m;
519
520   m = -1;
521   GNUNET_assert (CURLM_OK ==
522                  curl_multi_fdset (ctx->multi,
523                                    read_fd_set,
524                                    write_fd_set,
525                                    except_fd_set,
526                                    &m));
527   to = *timeout;
528   *max_fd = GNUNET_MAX (m, *max_fd);
529   GNUNET_assert (CURLM_OK ==
530                  curl_multi_timeout (ctx->multi,
531                                      &to));
532
533   /* Only if what we got back from curl is smaller than what we
534      already had (-1 == infinity!), then update timeout */
535   if ( (to < *timeout) &&
536        (-1 != to) )
537     *timeout = to;
538   if ( (-1 == (*timeout)) &&
539        (NULL != ctx->jobs_head) )
540     *timeout = to;
541 }
542
543
544 /**
545  * Cleanup library initialisation resources.  This function should be called
546  * after using this library to cleanup the resources occupied during library's
547  * initialisation.
548  *
549  * @param ctx the library context
550  */
551 void
552 GNUNET_CURL_fini (struct GNUNET_CURL_Context *ctx)
553 {
554   /* all jobs must have been cancelled at this time, assert this */
555   GNUNET_assert (NULL == ctx->jobs_head);
556   curl_share_cleanup (ctx->share);
557   curl_multi_cleanup (ctx->multi);
558   curl_slist_free_all (ctx->json_header);
559   GNUNET_free (ctx);
560 }
561
562
563 /**
564  * Initial global setup logic, specifically runs the Curl setup.
565  */
566 __attribute__ ((constructor))
567 void
568 GNUNET_CURL_constructor__ (void)
569 {
570   CURLcode ret;
571
572   if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
573   {
574     CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
575                    "curl_global_init",
576                    ret);
577     curl_fail = 1;
578   }
579 }
580
581
582 /**
583  * Cleans up after us, specifically runs the Curl cleanup.
584  */
585 __attribute__ ((destructor))
586 void
587 GNUNET_CURL_destructor__ (void)
588 {
589   if (curl_fail)
590     return;
591   curl_global_cleanup ();
592 }
593
594 /* end of curl.c */