aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/curl/curl.c324
-rw-r--r--src/include/gnunet_curl_lib.h40
2 files changed, 266 insertions, 98 deletions
diff --git a/src/curl/curl.c b/src/curl/curl.c
index f672bec02..dcbb43f58 100644
--- a/src/curl/curl.c
+++ b/src/curl/curl.c
@@ -101,6 +101,16 @@ struct GNUNET_CURL_Job
101 void *jcc_cls; 101 void *jcc_cls;
102 102
103 /** 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 /**
104 * Buffer for response received from CURL. 114 * Buffer for response received from CURL.
105 */ 115 */
106 struct GNUNET_CURL_DownloadBuffer db; 116 struct GNUNET_CURL_DownloadBuffer db;
@@ -257,32 +267,16 @@ download_cb (char *bufptr, size_t size, size_t nitems, void *cls)
257 267
258 268
259/** 269/**
260 * Schedule a CURL request to be executed and call the given @a jcc 270 * Create the HTTP headers for the request
261 * upon its completion. Note that the context will make use of the
262 * CURLOPT_PRIVATE facility of the CURL @a eh.
263 * 271 *
264 * This function modifies the CURL handle to add the 272 * @param ctx context we run in
265 * "Content-Type: application/json" header if @a add_json is set. 273 * @param job_headers job-specific headers
266 * 274 * @return all headers to use
267 * @param ctx context to execute the job in
268 * @param eh curl easy handle for the request, will be executed AND
269 * cleaned up. NOTE: the handle should _never_ have gotten
270 * any headers list, as that would then be ovverridden by
271 * @a jcc. Therefore, always pass custom headers as the
272 * @a job_headers parameter.
273 * @param job_headers extra headers to add for this request
274 * @param jcc callback to invoke upon completion
275 * @param jcc_cls closure for @a jcc
276 * @return NULL on error (in this case, @eh is still released!)
277 */ 275 */
278struct GNUNET_CURL_Job * 276static struct curl_slist *
279GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx, 277setup_job_headers (struct GNUNET_CURL_Context *ctx,
280 CURL *eh, 278 const struct curl_slist *job_headers)
281 const struct curl_slist *job_headers,
282 GNUNET_CURL_JobCompletionCallback jcc,
283 void *jcc_cls)
284{ 279{
285 struct GNUNET_CURL_Job *job;
286 struct curl_slist *all_headers = NULL; 280 struct curl_slist *all_headers = NULL;
287 281
288 for (const struct curl_slist *curr = job_headers; curr != NULL; 282 for (const struct curl_slist *curr = job_headers; curr != NULL;
@@ -316,15 +310,33 @@ GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx,
316 GNUNET_free (aid_header); 310 GNUNET_free (aid_header);
317 } 311 }
318 } 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 */
325static struct GNUNET_CURL_Job *
326setup_job (CURL *eh,
327 struct GNUNET_CURL_Context *ctx,
328 struct curl_slist *all_headers)
329{
330 struct GNUNET_CURL_Job *job;
319 331
320 if (CURLE_OK != curl_easy_setopt (eh, CURLOPT_HTTPHEADER, all_headers)) 332 if (CURLE_OK !=
333 curl_easy_setopt (eh, CURLOPT_HTTPHEADER, all_headers))
321 { 334 {
322 GNUNET_break (0); 335 GNUNET_break (0);
323 curl_slist_free_all (all_headers); 336 curl_slist_free_all (all_headers);
324 curl_easy_cleanup (eh); 337 curl_easy_cleanup (eh);
325 return NULL; 338 return NULL;
326 } 339 }
327
328 job = GNUNET_new (struct GNUNET_CURL_Job); 340 job = GNUNET_new (struct GNUNET_CURL_Job);
329 job->job_headers = all_headers; 341 job->job_headers = all_headers;
330 342
@@ -340,12 +352,91 @@ GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx,
340 curl_easy_cleanup (eh); 352 curl_easy_cleanup (eh);
341 return NULL; 353 return NULL;
342 } 354 }
343
344 job->easy_handle = eh; 355 job->easy_handle = eh;
345 job->ctx = ctx; 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 */
377struct GNUNET_CURL_Job *
378GNUNET_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 */
420struct GNUNET_CURL_Job *
421GNUNET_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
346 job->jcc = jcc; 438 job->jcc = jcc;
347 job->jcc_cls = jcc_cls; 439 job->jcc_cls = jcc_cls;
348 GNUNET_CONTAINER_DLL_insert (ctx->jobs_head, ctx->jobs_tail, job);
349 ctx->cb (ctx->cb_cls); 440 ctx->cb (ctx->cb_cls);
350 return job; 441 return job;
351} 442}
@@ -444,13 +535,20 @@ GNUNET_CURL_download_get_result_ (struct GNUNET_CURL_DownloadBuffer *db,
444 (int) db->buf_size, 535 (int) db->buf_size,
445 (char *) db->buf); 536 (char *) db->buf);
446 537
447 if ((CURLE_OK != curl_easy_getinfo (eh, CURLINFO_CONTENT_TYPE, &ct)) || 538 if ((CURLE_OK !=
448 (NULL == ct) || (0 != strcasecmp (ct, "application/json"))) 539 curl_easy_getinfo (eh,
540 CURLINFO_CONTENT_TYPE,
541 &ct)) ||
542 (NULL == ct) ||
543 (0 != strcasecmp (ct,
544 "application/json")))
449 { 545 {
450 /* No content type or explicitly not JSON, refuse to parse 546 /* No content type or explicitly not JSON, refuse to parse
451 (but keep response code) */ 547 (but keep response code) */
452 if (CURLE_OK != 548 if (CURLE_OK !=
453 curl_easy_getinfo (eh, CURLINFO_RESPONSE_CODE, response_code)) 549 curl_easy_getinfo (eh,
550 CURLINFO_RESPONSE_CODE,
551 response_code))
454 { 552 {
455 /* unexpected error... */ 553 /* unexpected error... */
456 GNUNET_break (0); 554 GNUNET_break (0);
@@ -480,7 +578,9 @@ GNUNET_CURL_download_get_result_ (struct GNUNET_CURL_DownloadBuffer *db,
480 if (NULL != json) 578 if (NULL != json)
481 { 579 {
482 if (CURLE_OK != 580 if (CURLE_OK !=
483 curl_easy_getinfo (eh, CURLINFO_RESPONSE_CODE, response_code)) 581 curl_easy_getinfo (eh,
582 CURLINFO_RESPONSE_CODE,
583 response_code))
484 { 584 {
485 /* unexpected error... */ 585 /* unexpected error... */
486 GNUNET_break (0); 586 GNUNET_break (0);
@@ -509,6 +609,76 @@ GNUNET_CURL_append_header (struct GNUNET_CURL_Context *ctx, const char *header)
509} 609}
510 610
511 611
612#if ENABLE_BENCHMARK
613static void
614do_benchmark (CURLMsg *cmsg)
615{
616 char *url = NULL;
617 double total_as_double = 0;
618 struct GNUNET_TIME_Relative total;
619 struct UrlRequestData *urd;
620 /* Some care required, as curl is using data types (long vs curl_off_t vs
621 * double) inconsistently to store byte count. */
622 curl_off_t size_curl = 0;
623 long size_long = 0;
624 uint64_t bytes_sent = 0;
625 uint64_t bytes_received = 0;
626
627 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
628 CURLINFO_TOTAL_TIME,
629 &total_as_double));
630 total.rel_value_us = total_as_double * 1000 * 1000;
631
632 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
633 CURLINFO_EFFECTIVE_URL,
634 &url));
635
636 /* HEADER_SIZE + SIZE_DOWNLOAD_T is hopefully the total
637 number of bytes received, not clear from curl docs. */
638
639 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
640 CURLINFO_HEADER_SIZE,
641 &size_long));
642 bytes_received += size_long;
643
644 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
645 CURLINFO_SIZE_DOWNLOAD_T,
646 &size_curl));
647 bytes_received += size_curl;
648
649 /* REQUEST_SIZE + SIZE_UPLOAD_T is hopefully the total number of bytes
650 sent, again docs are not completely clear. */
651
652 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
653 CURLINFO_REQUEST_SIZE,
654 &size_long));
655 bytes_sent += size_long;
656
657 /* We obtain this value to check an invariant, but never use it otherwise. */
658 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
659 CURLINFO_SIZE_UPLOAD_T,
660 &size_curl));
661
662 /* CURLINFO_SIZE_UPLOAD_T <= CURLINFO_REQUEST_SIZE should
663 be an invariant.
664 As verified with
665 curl -w "foo%{size_request} -XPOST --data "ABC" $URL
666 the CURLINFO_REQUEST_SIZE should be the whole size of the request
667 including headers and body.
668 */GNUNET_break (size_curl <= size_long);
669
670 urd = get_url_benchmark_data (url, (unsigned int) response_code);
671 urd->count++;
672 urd->time = GNUNET_TIME_relative_add (urd->time, total);
673 urd->time_max = GNUNET_TIME_relative_max (total, urd->time_max);
674 urd->bytes_sent += bytes_sent;
675 urd->bytes_received += bytes_received;
676}
677
678
679#endif
680
681
512/** 682/**
513 * Run the main event loop for the Taler interaction. 683 * Run the main event loop for the Taler interaction.
514 * 684 *
@@ -526,7 +696,8 @@ GNUNET_CURL_perform2 (struct GNUNET_CURL_Context *ctx,
526 int n_running; 696 int n_running;
527 int n_completed; 697 int n_completed;
528 698
529 (void) curl_multi_perform (ctx->multi, &n_running); 699 (void) curl_multi_perform (ctx->multi,
700 &n_running);
530 while (NULL != (cmsg = curl_multi_info_read (ctx->multi, &n_completed))) 701 while (NULL != (cmsg = curl_multi_info_read (ctx->multi, &n_completed)))
531 { 702 {
532 struct GNUNET_CURL_Job *job; 703 struct GNUNET_CURL_Job *job;
@@ -540,80 +711,39 @@ GNUNET_CURL_perform2 (struct GNUNET_CURL_Context *ctx,
540 (char **) &job)); 711 (char **) &job));
541 GNUNET_assert (job->ctx == ctx); 712 GNUNET_assert (job->ctx == ctx);
542 response_code = 0; 713 response_code = 0;
543 response = rp (&job->db, job->easy_handle, &response_code); 714 if (NULL != job->jcc_raw)
544#if ENABLE_BENCHMARK 715 {
716 /* RAW mode, no parsing */
717 GNUNET_break (CURLE_OK ==
718 curl_easy_getinfo (job->easy_handle,
719 CURLINFO_RESPONSE_CODE,
720 &response_code));
721 job->jcc_raw (job->jcc_raw_cls,
722 response_code,
723 job->db.buf,
724 job->db.buf_size);
725 }
726 else
545 { 727 {
546 char *url = NULL; 728 /* to be parsed via 'rp' */
547 double total_as_double = 0; 729 response = rp (&job->db,
548 struct GNUNET_TIME_Relative total; 730 job->easy_handle,
549 struct UrlRequestData *urd; 731 &response_code);
550 /* Some care required, as curl is using data types (long vs curl_off_t vs 732 job->jcc (job->jcc_cls,
551 * double) inconsistently to store byte count. */ 733 response_code,
552 curl_off_t size_curl = 0; 734 response);
553 long size_long = 0; 735 rc (response);
554 uint64_t bytes_sent = 0;
555 uint64_t bytes_received = 0;
556
557 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
558 CURLINFO_TOTAL_TIME,
559 &total_as_double));
560 total.rel_value_us = total_as_double * 1000 * 1000;
561
562 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
563 CURLINFO_EFFECTIVE_URL,
564 &url));
565
566 /* HEADER_SIZE + SIZE_DOWNLOAD_T is hopefully the total
567 number of bytes received, not clear from curl docs. */
568
569 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
570 CURLINFO_HEADER_SIZE,
571 &size_long));
572 bytes_received += size_long;
573
574 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
575 CURLINFO_SIZE_DOWNLOAD_T,
576 &size_curl));
577 bytes_received += size_curl;
578
579 /* REQUEST_SIZE + SIZE_UPLOAD_T is hopefully the total number of bytes
580 sent, again docs are not completely clear. */
581
582 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
583 CURLINFO_REQUEST_SIZE,
584 &size_long));
585 bytes_sent += size_long;
586
587 /* We obtain this value to check an invariant, but never use it otherwise. */
588 GNUNET_break (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
589 CURLINFO_SIZE_UPLOAD_T,
590 &size_curl));
591
592 /* CURLINFO_SIZE_UPLOAD_T <= CURLINFO_REQUEST_SIZE should
593 be an invariant.
594 As verified with
595 curl -w "foo%{size_request} -XPOST --data "ABC" $URL
596 the CURLINFO_REQUEST_SIZE should be the whole size of the request
597 including headers and body.
598 */GNUNET_break (size_curl <= size_long);
599
600 urd = get_url_benchmark_data (url, (unsigned int) response_code);
601 urd->count++;
602 urd->time = GNUNET_TIME_relative_add (urd->time, total);
603 urd->time_max = GNUNET_TIME_relative_max (total, urd->time_max);
604 urd->bytes_sent += bytes_sent;
605 urd->bytes_received += bytes_received;
606 } 736 }
737#if ENABLE_BENCHMARK
738 do_benchmark (cmsg);
607#endif 739#endif
608 job->jcc (job->jcc_cls, response_code, response);
609 rc (response);
610 GNUNET_CURL_job_cancel (job); 740 GNUNET_CURL_job_cancel (job);
611 } 741 }
612} 742}
613 743
614 744
615/** 745/**
616 * Run the main event loop for the Taler interaction. 746 * Run the main event loop for the HTTP interaction.
617 * 747 *
618 * @param ctx the library context 748 * @param ctx the library context
619 */ 749 */
diff --git a/src/include/gnunet_curl_lib.h b/src/include/gnunet_curl_lib.h
index 64b41ed83..48eb7e490 100644
--- a/src/include/gnunet_curl_lib.h
+++ b/src/include/gnunet_curl_lib.h
@@ -197,7 +197,7 @@ struct GNUNET_CURL_Job;
197 * 197 *
198 * @param cls closure 198 * @param cls closure
199 * @param response_code HTTP response code from server, 0 on hard error 199 * @param response_code HTTP response code from server, 0 on hard error
200 * @param json response, NULL if response was not in JSON format 200 * @param response in JSON, NULL if response was not in JSON format
201 */ 201 */
202typedef void 202typedef void
203(*GNUNET_CURL_JobCompletionCallback)(void *cls, 203(*GNUNET_CURL_JobCompletionCallback)(void *cls,
@@ -206,6 +206,21 @@ typedef void
206 206
207 207
208/** 208/**
209 * Function to call upon completion of a raw job.
210 *
211 * @param cls closure
212 * @param response_code HTTP response code from server, 0 on hard error
213 * @param body http body of the response
214 * @param body_size number of bytes in @a body
215 */
216typedef void
217(*GNUNET_CURL_RawJobCompletionCallback)(void *cls,
218 long response_code,
219 const void *body,
220 size_t body_size);
221
222
223/**
209 * Schedule a CURL request to be executed and call the given @a jcc 224 * Schedule a CURL request to be executed and call the given @a jcc
210 * upon its completion. Note that the context will make use of the 225 * upon its completion. Note that the context will make use of the
211 * CURLOPT_PRIVATE facility of the CURL @a eh. 226 * CURLOPT_PRIVATE facility of the CURL @a eh.
@@ -254,6 +269,29 @@ GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx,
254 269
255 270
256/** 271/**
272 * Schedule a CURL request to be executed and call the given @a jcc
273 * upon its completion. Note that the context will make use of the
274 * CURLOPT_PRIVATE facility of the CURL @a eh. Used to download
275 * resources that are NOT in JSON. The raw body will be returned.
276 *
277 * @param ctx context to execute the job in
278 * @param eh curl easy handle for the request, will
279 * be executed AND cleaned up
280 * @param job_headers extra headers to add for this request
281 * @param max_reply_size largest acceptable response body
282 * @param jcc callback to invoke upon completion
283 * @param jcc_cls closure for @a jcc
284 * @return NULL on error (in this case, @eh is still released!)
285 */
286struct GNUNET_CURL_Job *
287GNUNET_CURL_job_add_raw (struct GNUNET_CURL_Context *ctx,
288 CURL *eh,
289 const struct curl_slist *job_headers,
290 GNUNET_CURL_RawJobCompletionCallback jcc,
291 void *jcc_cls);
292
293
294/**
257 * Cancel a job. Must only be called before the job completion 295 * Cancel a job. Must only be called before the job completion
258 * callback is called for the respective job. 296 * callback is called for the respective job.
259 * 297 *