curl.c 26 KB

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