diff options
author | Christian Grothoff <christian@grothoff.org> | 2019-11-24 19:11:34 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2019-11-24 19:12:16 +0100 |
commit | 5f38569fce2e77afeed58cbd3429c67bf8ab9109 (patch) | |
tree | f348bc7b24e5cfafe9abe1fee3f7502a4a2fe434 /src/curl/curl.c | |
parent | a6e31c91476191a038e240c030a799247fde39fb (diff) | |
download | gnunet-5f38569fce2e77afeed58cbd3429c67bf8ab9109.tar.gz gnunet-5f38569fce2e77afeed58cbd3429c67bf8ab9109.zip |
add RAW mode for libgnunetcurl
Diffstat (limited to 'src/curl/curl.c')
-rw-r--r-- | src/curl/curl.c | 324 |
1 files changed, 227 insertions, 97 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 | */ |
278 | struct GNUNET_CURL_Job * | 276 | static struct curl_slist * |
279 | GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx, | 277 | setup_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 | */ | ||
325 | static struct GNUNET_CURL_Job * | ||
326 | setup_job (CURL *eh, | ||
327 | struct GNUNET_CURL_Context *ctx, | ||
328 | struct curl_slist *all_headers) | ||
329 | { | ||
330 | struct GNUNET_CURL_Job *job; | ||
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 | */ | ||
377 | struct GNUNET_CURL_Job * | ||
378 | GNUNET_CURL_job_add_raw (struct GNUNET_CURL_Context *ctx, | ||
379 | CURL *eh, | ||
380 | const struct curl_slist *job_headers, | ||
381 | GNUNET_CURL_RawJobCompletionCallback jcc, | ||
382 | void *jcc_cls) | ||
383 | { | ||
384 | struct GNUNET_CURL_Job *job; | ||
385 | struct curl_slist *all_headers; | ||
386 | |||
387 | GNUNET_assert (NULL != jcc); | ||
388 | all_headers = setup_job_headers (ctx, | ||
389 | job_headers); | ||
390 | if (NULL == (job = setup_job (eh, | ||
391 | ctx, | ||
392 | all_headers))) | ||
393 | return NULL; | ||
394 | job->jcc_raw = jcc; | ||
395 | job->jcc_raw_cls = jcc_cls; | ||
396 | ctx->cb (ctx->cb_cls); | ||
397 | return job; | ||
398 | } | ||
399 | |||
400 | |||
401 | /** | ||
402 | * Schedule a CURL request to be executed and call the given @a jcc | ||
403 | * upon its completion. Note that the context will make use of the | ||
404 | * CURLOPT_PRIVATE facility of the CURL @a eh. | ||
405 | * | ||
406 | * This function modifies the CURL handle to add the | ||
407 | * "Content-Type: application/json" header if @a add_json is set. | ||
408 | * | ||
409 | * @param ctx context to execute the job in | ||
410 | * @param eh curl easy handle for the request, will be executed AND | ||
411 | * cleaned up. NOTE: the handle should _never_ have gotten | ||
412 | * any headers list, as that would then be ovverridden by | ||
413 | * @a jcc. Therefore, always pass custom headers as the | ||
414 | * @a job_headers parameter. | ||
415 | * @param job_headers extra headers to add for this request | ||
416 | * @param jcc callback to invoke upon completion | ||
417 | * @param jcc_cls closure for @a jcc | ||
418 | * @return NULL on error (in this case, @eh is still released!) | ||
419 | */ | ||
420 | struct GNUNET_CURL_Job * | ||
421 | GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx, | ||
422 | CURL *eh, | ||
423 | const struct curl_slist *job_headers, | ||
424 | GNUNET_CURL_JobCompletionCallback jcc, | ||
425 | void *jcc_cls) | ||
426 | { | ||
427 | struct GNUNET_CURL_Job *job; | ||
428 | struct curl_slist *all_headers; | ||
429 | |||
430 | GNUNET_assert (NULL != jcc); | ||
431 | all_headers = setup_job_headers (ctx, | ||
432 | job_headers); | ||
433 | if (NULL == (job = setup_job (eh, | ||
434 | ctx, | ||
435 | all_headers))) | ||
436 | return NULL; | ||
437 | |||
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 | ||
613 | static void | ||
614 | do_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 | */ |