libtest_convenience_client_request.c (27191B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2024 Christian Grothoff 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file libtest_convenience_client_request.c 41 * @brief convenience functions implementing clients making requests for libtest users 42 * @author Christian Grothoff 43 */ 44 #include "libtest.h" 45 #include <pthread.h> 46 #include <stdbool.h> 47 #include <fcntl.h> 48 #include <unistd.h> 49 #include <errno.h> 50 #include <curl/curl.h> 51 52 53 #ifndef CURL_VERSION_BITS 54 # define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z)) 55 #endif 56 #ifndef CURL_AT_LEAST_VERSION 57 # define CURL_AT_LEAST_VERSION(x,y,z) \ 58 (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z)) 59 #endif 60 61 #if CURL_AT_LEAST_VERSION (7,83,0) 62 # define HAVE_LIBCRUL_NEW_HDR_API 1 63 #endif 64 65 66 /** 67 * Closure for the write_cb(). 68 */ 69 struct WriteBuffer 70 { 71 /** 72 * Where to store the response. 73 */ 74 char *buf; 75 76 /** 77 * Number of bytes in @e buf. 78 */ 79 size_t len; 80 81 /** 82 * Current write offset in @e buf. 83 */ 84 size_t pos; 85 86 /** 87 * Set to non-zero on errors (buffer full). 88 */ 89 int err; 90 }; 91 92 93 /** 94 * Callback for CURLOPT_WRITEFUNCTION processing 95 * data downloaded from the HTTP server. 96 * 97 * @param ptr data uploaded 98 * @param size size of a member 99 * @param nmemb number of members 100 * @param stream must be a `struct WriteBuffer` 101 * @return bytes processed (size*nmemb) or error 102 */ 103 static size_t 104 write_cb (void *ptr, 105 size_t size, 106 size_t nmemb, 107 void *stream) 108 { 109 struct WriteBuffer *wb = stream; 110 size_t prod = size * nmemb; 111 112 if ( (prod / size != nmemb) || 113 (wb->pos + prod < wb->pos) || 114 (wb->pos + prod > wb->len) ) 115 { 116 wb->err = 1; 117 return CURLE_WRITE_ERROR; 118 } 119 memcpy (wb->buf + wb->pos, 120 ptr, 121 prod); 122 wb->pos += prod; 123 return prod; 124 } 125 126 127 /** 128 * Declare variables needed to check a download. 129 * 130 * @param text text data we expect to receive 131 */ 132 #define DECLARE_WB(text) \ 133 size_t wb_tlen = strlen (text); \ 134 char wb_buf[wb_tlen]; \ 135 struct WriteBuffer wb = { \ 136 .buf = wb_buf, \ 137 .len = wb_tlen \ 138 } 139 140 141 /** 142 * Set CURL options to the write_cb() and wb buffer 143 * to check a download. 144 * 145 * @param c CURL handle 146 */ 147 #define SETUP_WB(c) do { \ 148 if (CURLE_OK != \ 149 curl_easy_setopt (c, \ 150 CURLOPT_WRITEFUNCTION, \ 151 &write_cb)) \ 152 { \ 153 curl_easy_cleanup (c); \ 154 return "Failed to set write callback for curl request"; \ 155 } \ 156 if (CURLE_OK != \ 157 curl_easy_setopt (c, \ 158 CURLOPT_WRITEDATA, \ 159 &wb)) \ 160 { \ 161 curl_easy_cleanup (c); \ 162 return "Failed to set write buffer for curl request"; \ 163 } \ 164 } while (0) 165 166 /** 167 * Check that we received the expected text. 168 * 169 * @param text text we expect to have downloaded 170 */ 171 #define CHECK_WB(text) do { \ 172 if ( (wb_tlen != wb.pos) || \ 173 (0 != wb.err) || \ 174 (0 != memcmp (text, \ 175 wb_buf, \ 176 wb_tlen)) ) \ 177 return "Downloaded data does not match expectations"; \ 178 } while (0) 179 180 181 /** 182 * Perform the curl request @a c and cleanup and 183 * return an error if the request failed. 184 * 185 * @param c request to perform 186 */ 187 #define PERFORM_REQUEST(c) do { \ 188 CURLcode res; \ 189 res = curl_easy_perform (c); \ 190 if (CURLE_OK != res) \ 191 { \ 192 curl_easy_cleanup (c); \ 193 return "Failed to fetch URL"; \ 194 } \ 195 } while (0) 196 197 /** 198 * Check that the curl request @a c completed 199 * with the @a want status code. 200 * Return an error if the status does not match. 201 * 202 * @param c request to check 203 * @param want desired HTTP status code 204 */ 205 #define CHECK_STATUS(c,want) do { \ 206 if (! check_status (c, want)) \ 207 { \ 208 curl_easy_cleanup (c); \ 209 return "Unexpected HTTP status"; \ 210 } \ 211 } while (0) 212 213 /** 214 * Chec that the HTTP status of @a c matches @a expected_status 215 * 216 * @param a completed CURL request 217 * @param expected_status the expected HTTP response code 218 * @return true if the status matches 219 */ 220 static bool 221 check_status (CURL *c, 222 unsigned int expected_status) 223 { 224 long status; 225 226 if (CURLE_OK != 227 curl_easy_getinfo (c, 228 CURLINFO_RESPONSE_CODE, 229 &status)) 230 { 231 fprintf (stderr, 232 "Failed to get HTTP status"); 233 return false; 234 } 235 if (((unsigned int) status) != expected_status) 236 { 237 fprintf (stderr, 238 "Expected HTTP status %u, got %ld\n", 239 expected_status, 240 status); 241 return false; 242 } 243 return true; 244 } 245 246 247 /** 248 * Set the @a base_url for the @a c handle. 249 * 250 * @param[in,out] c curl handle to manipulate 251 * @param base_url base URL to set 252 * @param[in,out] pc phase context with further options 253 * @return NULL on success, error message on failure (@a c will be cleaned up in this case) 254 */ 255 static const char * 256 set_url (CURL *c, 257 const char *base_url, 258 struct MHDT_PhaseContext *pc) 259 { 260 if (CURLE_OK != 261 curl_easy_setopt (c, 262 CURLOPT_URL, 263 base_url)) 264 { 265 curl_easy_cleanup (c); 266 return "Failed to set URL"; 267 } 268 if (CURLE_OK != 269 curl_easy_setopt (c, 270 CURLOPT_VERBOSE, 271 1)) 272 { 273 curl_easy_cleanup (c); 274 return "Failed to set verbosity"; 275 } 276 { 277 /* Force curl to do the request to 127.0.0.1 regardless of 278 hostname */ 279 const char *host; 280 const char *end; 281 char ri[1024]; 282 283 if (0 == strncasecmp (base_url, 284 "https://", 285 strlen ("https://"))) 286 host = &base_url[strlen ("https://")]; 287 else 288 host = &base_url[strlen ("http://")]; 289 end = strchr (host, '/'); 290 if (NULL == end) 291 end = host + strlen (host); 292 snprintf (ri, 293 sizeof (ri), 294 "%.*s:127.0.0.1", 295 (int) (end - host), 296 host); 297 pc->hosts = curl_slist_append (NULL, 298 ri); 299 if (CURLE_OK != 300 curl_easy_setopt (c, 301 CURLOPT_RESOLVE, 302 pc->hosts)) 303 { 304 curl_easy_cleanup (c); 305 return "Failed to override DNS"; 306 } 307 } 308 if (0 == strncasecmp (base_url, 309 "https://", 310 strlen ("https://"))) 311 { 312 struct MHDT_Phase *phase = pc->phase; 313 314 if (phase->check_server_cert) 315 { 316 if (CURLE_OK != 317 curl_easy_setopt (c, 318 CURLOPT_CAINFO, 319 "data/root-ca.crt")) 320 { 321 curl_easy_cleanup (c); 322 return "Failed to override root CA"; 323 } 324 } 325 else 326 { 327 /* disable certificate checking */ 328 if ( (CURLE_OK != 329 curl_easy_setopt (c, 330 CURLOPT_SSL_VERIFYPEER, 331 0L)) || 332 (CURLE_OK != 333 curl_easy_setopt (c, 334 CURLOPT_SSL_VERIFYHOST, 335 0L)) ) 336 { 337 curl_easy_cleanup (c); 338 return "Failed to disable X509 server certificate checks"; 339 } 340 } 341 if (NULL != phase->client_cert) 342 { 343 if (CURLE_OK != 344 curl_easy_setopt (c, 345 CURLOPT_SSLCERT, 346 phase->client_cert)) 347 { 348 curl_easy_cleanup (c); 349 return "Failed to set client certificate"; 350 } 351 } 352 } 353 return NULL; 354 } 355 356 357 /** 358 * Create a curl handle for the given @a pc. 359 * 360 * @param pc current phase context with options to use 361 * @return NULL on error 362 */ 363 static CURL * 364 setup_curl (const struct MHDT_PhaseContext *pc) 365 { 366 struct MHDT_Phase *p = pc->phase; 367 CURL *c; 368 369 c = curl_easy_init (); 370 if (NULL == c) 371 return NULL; 372 switch (p->http_version) 373 { 374 case 0: /* unset == any */ 375 break; 376 case 1: 377 if (CURLE_OK != 378 curl_easy_setopt (c, 379 CURLOPT_HTTP_VERSION, 380 CURL_HTTP_VERSION_1_1)) 381 { 382 curl_easy_cleanup (c); 383 fprintf (stderr, 384 "HTTP/1 not supported by curl?\n"); 385 return NULL; 386 } 387 break; 388 case 2: /* HTTP/2 */ 389 if (p->use_tls) 390 { 391 if (CURLE_OK != 392 curl_easy_setopt (c, 393 CURLOPT_HTTP_VERSION, 394 CURL_HTTP_VERSION_2TLS)) 395 { 396 curl_easy_cleanup (c); 397 fprintf (stderr, 398 "HTTP/2 not supported by curl?\n"); 399 return NULL; 400 } 401 } 402 else 403 { 404 if (CURLE_OK != 405 curl_easy_setopt (c, 406 CURLOPT_HTTP_VERSION, 407 CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)) 408 { 409 curl_easy_cleanup (c); 410 fprintf (stderr, 411 "HTTP/2 not supported by curl?\n"); 412 return NULL; 413 } 414 } 415 break; 416 case 3: 417 abort (); // not yet supported 418 break; 419 default: 420 abort (); 421 break; 422 } 423 return c; 424 } 425 426 427 const char * 428 MHDT_client_get_host (const void *cls, 429 struct MHDT_PhaseContext *pc) 430 { 431 const char *host = cls; 432 const char *err; 433 size_t alen = strlen (host); 434 CURL *c; 435 size_t blen = strlen (pc->base_url); 436 char u[alen + blen + 1]; 437 const char *slash = strchr (pc->base_url, 438 '/'); 439 const char *colon; 440 441 if (NULL == slash) 442 return "'/' missing in base URL"; 443 colon = strchr (slash, 444 ':'); 445 if (NULL == colon) 446 return "':' missing in base URL"; 447 snprintf (u, 448 sizeof (u), 449 "https://%s%s", 450 host, 451 colon); 452 c = setup_curl (pc); 453 if (NULL == c) 454 return "Failed to initialize Curl handle"; 455 err = set_url (c, 456 u, 457 pc); 458 if (NULL != err) 459 return err; 460 PERFORM_REQUEST (c); 461 CHECK_STATUS (c, 462 MHD_HTTP_STATUS_OK); 463 curl_easy_cleanup (c); 464 return NULL; 465 } 466 467 468 const char * 469 MHDT_client_get_root ( 470 const void *cls, 471 struct MHDT_PhaseContext *pc) 472 { 473 const char *text = cls; 474 CURL *c; 475 const char *err; 476 DECLARE_WB (text); 477 478 c = setup_curl (pc); 479 if (NULL == c) 480 return "Failed to initialize Curl handle"; 481 err = set_url (c, 482 pc->base_url, 483 pc); 484 if (NULL != err) 485 return err; 486 SETUP_WB (c); 487 PERFORM_REQUEST (c); 488 CHECK_STATUS (c, 489 MHD_HTTP_STATUS_OK); 490 curl_easy_cleanup (c); 491 CHECK_WB (text); 492 return NULL; 493 } 494 495 496 const char * 497 MHDT_client_get_with_query ( 498 const void *cls, 499 struct MHDT_PhaseContext *pc) 500 { 501 const char *args = cls; 502 const char *err; 503 size_t alen = strlen (args); 504 CURL *c; 505 size_t blen = strlen (pc->base_url); 506 char u[alen + blen + 1]; 507 508 memcpy (u, 509 pc->base_url, 510 blen); 511 memcpy (u + blen, 512 args, 513 alen); 514 u[alen + blen] = '\0'; 515 c = setup_curl (pc); 516 if (NULL == c) 517 return "Failed to initialize Curl handle"; 518 err = set_url (c, 519 u, 520 pc); 521 if (NULL != err) 522 return err; 523 PERFORM_REQUEST (c); 524 CHECK_STATUS (c, 525 MHD_HTTP_STATUS_NO_CONTENT); 526 curl_easy_cleanup (c); 527 return NULL; 528 } 529 530 531 const char * 532 MHDT_client_set_header ( 533 const void *cls, 534 struct MHDT_PhaseContext *pc) 535 { 536 const char *hdr = cls; 537 const char *err; 538 CURL *c; 539 CURLcode res; 540 struct curl_slist *slist; 541 542 c = setup_curl (pc); 543 if (NULL == c) 544 return "Failed to initialize Curl handle"; 545 err = set_url (c, 546 pc->base_url, 547 pc); 548 if (NULL != err) 549 return err; 550 slist = curl_slist_append (NULL, 551 hdr); 552 if (CURLE_OK != 553 curl_easy_setopt (c, 554 CURLOPT_HTTPHEADER, 555 slist)) 556 { 557 curl_easy_cleanup (c); 558 curl_slist_free_all (slist); 559 return "Failed to set custom header for curl request"; 560 } 561 res = curl_easy_perform (c); 562 curl_slist_free_all (slist); 563 if (CURLE_OK != res) 564 { 565 curl_easy_cleanup (c); 566 return "Failed to fetch URL"; 567 } 568 CHECK_STATUS (c, 569 MHD_HTTP_STATUS_NO_CONTENT); 570 curl_easy_cleanup (c); 571 return NULL; 572 } 573 574 575 const char * 576 MHDT_client_expect_header (const void *cls, 577 struct MHDT_PhaseContext *pc) 578 { 579 #ifdef HAVE_LIBCRUL_NEW_HDR_API 580 const char *hdr = cls; 581 const char *err; 582 size_t hlen = strlen (hdr) + 1; 583 char key[hlen]; 584 const char *colon = strchr (hdr, ':'); 585 const char *value; 586 CURL *c; 587 bool found = false; 588 589 if (NULL == colon) 590 return "Invalid expected header passed"; 591 memcpy (key, 592 hdr, 593 hlen); 594 key[colon - hdr] = '\0'; 595 value = &key[colon - hdr + 1]; 596 c = setup_curl (pc); 597 if (NULL == c) 598 return "Failed to initialize Curl handle"; 599 err = set_url (c, 600 pc->base_url, 601 pc); 602 if (NULL != err) 603 return err; 604 PERFORM_REQUEST (c); 605 CHECK_STATUS (c, 606 MHD_HTTP_STATUS_NO_CONTENT); 607 for (size_t index = 0; ! found; index++) 608 { 609 CURLHcode rval; 610 struct curl_header *hout; 611 612 rval = curl_easy_header (c, 613 key, 614 index, 615 CURLH_HEADER, 616 -1 /* last request */, 617 &hout); 618 if (CURLHE_BADINDEX == rval) 619 break; 620 found = (0 == strcmp (value, 621 hout->value)); 622 } 623 if (! found) 624 { 625 curl_easy_cleanup (c); 626 return "Expected HTTP response header not found"; 627 } 628 curl_easy_cleanup (c); 629 return NULL; 630 #else /* ! HAVE_LIBCRUL_NEW_HDR_API */ 631 (void) cls; (void) pc; 632 return NULL; 633 #endif /* ! HAVE_LIBCRUL_NEW_HDR_API */ 634 } 635 636 637 /** 638 * Closure for the read_cb(). 639 */ 640 struct ReadBuffer 641 { 642 /** 643 * Origin of data to upload. 644 */ 645 const char *buf; 646 647 /** 648 * Number of bytes in @e buf. 649 */ 650 size_t len; 651 652 /** 653 * Current read offset in @e buf. 654 */ 655 size_t pos; 656 657 /** 658 * Number of chunks to user when sending. 659 */ 660 unsigned int chunks; 661 662 }; 663 664 665 /** 666 * Callback for CURLOPT_READFUNCTION for uploading 667 * data to the HTTP server. 668 * 669 * @param ptr data uploaded 670 * @param size size of a member 671 * @param nmemb number of members 672 * @param stream must be a `struct ReadBuffer` 673 * @return bytes processed (size*nmemb) or error 674 */ 675 static size_t 676 read_cb (void *ptr, 677 size_t size, 678 size_t nmemb, 679 void *stream) 680 { 681 struct ReadBuffer *rb = stream; 682 size_t limit = size * nmemb; 683 684 if (limit / size != nmemb) 685 return CURLE_WRITE_ERROR; 686 if (limit > rb->len - rb->pos) 687 limit = rb->len - rb->pos; 688 if ( (rb->chunks > 1) && 689 (limit > 1) ) 690 { 691 limit /= rb->chunks; 692 rb->chunks--; 693 } 694 memcpy (ptr, 695 rb->buf + rb->pos, 696 limit); 697 rb->pos += limit; 698 return limit; 699 } 700 701 702 const char * 703 MHDT_client_put_data ( 704 const void *cls, 705 struct MHDT_PhaseContext *pc) 706 { 707 const char *text = cls; 708 const char *err; 709 struct ReadBuffer rb = { 710 .buf = text, 711 .len = strlen (text) 712 }; 713 CURL *c; 714 715 c = setup_curl (pc); 716 if (NULL == c) 717 return "Failed to initialize Curl handle"; 718 err = set_url (c, 719 pc->base_url, 720 pc); 721 if (NULL != err) 722 return err; 723 if (CURLE_OK != 724 curl_easy_setopt (c, 725 CURLOPT_UPLOAD, 726 1L)) 727 { 728 curl_easy_cleanup (c); 729 return "Failed to set PUT method for curl request"; 730 } 731 if (CURLE_OK != 732 curl_easy_setopt (c, 733 CURLOPT_READFUNCTION, 734 &read_cb)) 735 { 736 curl_easy_cleanup (c); 737 return "Failed to set READFUNCTION for curl request"; 738 } 739 if (CURLE_OK != 740 curl_easy_setopt (c, 741 CURLOPT_READDATA, 742 &rb)) 743 { 744 curl_easy_cleanup (c); 745 return "Failed to set READFUNCTION for curl request"; 746 } 747 if (CURLE_OK != 748 curl_easy_setopt (c, 749 CURLOPT_INFILESIZE_LARGE, 750 (curl_off_t) rb.len)) 751 { 752 curl_easy_cleanup (c); 753 return "Failed to set INFILESIZE_LARGE for curl request"; 754 } 755 PERFORM_REQUEST (c); 756 CHECK_STATUS (c, 757 MHD_HTTP_STATUS_NO_CONTENT); 758 curl_easy_cleanup (c); 759 return NULL; 760 } 761 762 763 const char * 764 MHDT_client_chunk_data ( 765 const void *cls, 766 struct MHDT_PhaseContext *pc) 767 { 768 const char *text = cls; 769 const char *err; 770 struct ReadBuffer rb = { 771 .buf = text, 772 .len = strlen (text), 773 .chunks = 2 774 }; 775 CURL *c; 776 777 c = setup_curl (pc); 778 if (NULL == c) 779 return "Failed to initialize Curl handle"; 780 err = set_url (c, 781 pc->base_url, 782 pc); 783 if (NULL != err) 784 return err; 785 if (CURLE_OK != 786 curl_easy_setopt (c, 787 CURLOPT_UPLOAD, 788 1L)) 789 { 790 curl_easy_cleanup (c); 791 return "Failed to set PUT method for curl request"; 792 } 793 if (CURLE_OK != 794 curl_easy_setopt (c, 795 CURLOPT_READFUNCTION, 796 &read_cb)) 797 { 798 curl_easy_cleanup (c); 799 return "Failed to set READFUNCTION for curl request"; 800 } 801 if (CURLE_OK != 802 curl_easy_setopt (c, 803 CURLOPT_READDATA, 804 &rb)) 805 { 806 curl_easy_cleanup (c); 807 return "Failed to set READFUNCTION for curl request"; 808 } 809 PERFORM_REQUEST (c); 810 CHECK_STATUS (c, 811 MHD_HTTP_STATUS_NO_CONTENT); 812 curl_easy_cleanup (c); 813 return NULL; 814 } 815 816 817 const char * 818 MHDT_client_do_post ( 819 const void *cls, 820 struct MHDT_PhaseContext *pc) 821 { 822 const struct MHDT_PostInstructions *pi = cls; 823 const char *err; 824 CURL *c; 825 struct curl_slist *request_hdr = NULL; 826 827 /* reset wants in case we re-use the array */ 828 if (NULL != pi->wants) 829 { 830 for (unsigned int i = 0; NULL != pi->wants[i].key; i++) 831 { 832 pi->wants[i].value_off = 0; 833 pi->wants[i].satisfied = false; 834 } 835 } 836 c = setup_curl (pc); 837 if (NULL == c) 838 return "Failed to initialize Curl handle"; 839 err = set_url (c, 840 pc->base_url, 841 pc); 842 if (NULL != err) 843 return err; 844 if (CURLE_OK != 845 curl_easy_setopt (c, 846 CURLOPT_POST, 847 1L)) 848 { 849 curl_easy_cleanup (c); 850 return "Failed to set POST method for curl request"; 851 } 852 if (CURLE_OK != 853 curl_easy_setopt (c, 854 CURLOPT_POSTFIELDS, 855 pi->postdata)) 856 { 857 curl_easy_cleanup (c); 858 return "Failed to set POSTFIELDS for curl request"; 859 } 860 if (0 != pi->postdata_size) 861 { 862 if (CURLE_OK != 863 curl_easy_setopt (c, 864 CURLOPT_POSTFIELDSIZE_LARGE, 865 (curl_off_t) pi->postdata_size)) 866 { 867 curl_easy_cleanup (c); 868 return "Failed to set POSTFIELDS for curl request"; 869 } 870 } 871 if (NULL != pi->postheader) 872 { 873 request_hdr = curl_slist_append (request_hdr, 874 pi->postheader); 875 } 876 if (CURLE_OK != 877 curl_easy_setopt (c, 878 CURLOPT_HTTPHEADER, 879 request_hdr)) 880 { 881 curl_easy_cleanup (c); 882 curl_slist_free_all (request_hdr); 883 return "Failed to set HTTPHEADER for curl request"; 884 } 885 PERFORM_REQUEST (c); 886 CHECK_STATUS (c, 887 MHD_HTTP_STATUS_NO_CONTENT); 888 curl_easy_cleanup (c); 889 curl_slist_free_all (request_hdr); 890 if (NULL != pi->wants) 891 { 892 for (unsigned int i = 0; NULL != pi->wants[i].key; i++) 893 { 894 if (! pi->wants[i].satisfied) 895 { 896 fprintf (stderr, 897 "Server did not correctly detect key '%s'\n", 898 pi->wants[i].key); 899 return "key-value data not matched by server"; 900 } 901 } 902 } 903 return NULL; 904 } 905 906 907 /** 908 * Send HTTP request with basic authentication. 909 * 910 * @param cred $USERNAME:$PASSWORD to use 911 * @param[in,out] phase context 912 * @param[out] http_status set to HTTP status 913 * @return error message, NULL on success 914 */ 915 static const char * 916 send_basic_auth (const char *cred, 917 struct MHDT_PhaseContext *pc, 918 unsigned int *http_status) 919 { 920 CURL *c; 921 const char *err; 922 long status; 923 char *pass = strchr (cred, ':'); 924 char *user; 925 926 if (NULL == pass) 927 return "invalid credential given"; 928 user = strndup (cred, 929 pass - cred); 930 pass++; 931 c = setup_curl (pc); 932 if (NULL == c) 933 { 934 free (user); 935 return "Failed to initialize Curl handle"; 936 } 937 err = set_url (c, 938 pc->base_url, 939 pc); 940 if (NULL != err) 941 { 942 free (user); 943 curl_easy_cleanup (c); 944 return err; 945 } 946 if ( (CURLE_OK != 947 curl_easy_setopt (c, 948 CURLOPT_HTTPAUTH, 949 (long) CURLAUTH_BASIC)) || 950 (CURLE_OK != 951 curl_easy_setopt (c, 952 CURLOPT_USERNAME, 953 user)) || 954 (CURLE_OK != 955 curl_easy_setopt (c, 956 CURLOPT_PASSWORD, 957 pass)) ) 958 { 959 curl_easy_cleanup (c); 960 free (user); 961 return "Failed to set basic authentication header for curl request"; 962 } 963 free (user); 964 PERFORM_REQUEST (c); 965 if (CURLE_OK != 966 curl_easy_getinfo (c, 967 CURLINFO_RESPONSE_CODE, 968 &status)) 969 { 970 return "Failed to get HTTP status"; 971 } 972 *http_status = (unsigned int) status; 973 curl_easy_cleanup (c); 974 return NULL; 975 } 976 977 978 const char * 979 MHDT_client_send_basic_auth ( 980 const void *cls, 981 struct MHDT_PhaseContext *pc) 982 { 983 const char *cred = cls; 984 const char *ret; 985 unsigned int status; 986 987 ret = send_basic_auth (cred, 988 pc, 989 &status); 990 if (NULL != ret) 991 return ret; 992 if (MHD_HTTP_STATUS_NO_CONTENT != status) 993 return "invalid HTTP response code"; 994 return NULL; 995 } 996 997 998 const char * 999 MHDT_client_fail_basic_auth ( 1000 const void *cls, 1001 struct MHDT_PhaseContext *pc) 1002 { 1003 const char *cred = cls; 1004 const char *ret; 1005 unsigned int status; 1006 1007 ret = send_basic_auth (cred, 1008 pc, 1009 &status); 1010 if (NULL != ret) 1011 return ret; 1012 if (MHD_HTTP_STATUS_UNAUTHORIZED != status) 1013 return "invalid HTTP response code"; 1014 return NULL; 1015 } 1016 1017 1018 /** 1019 * Send HTTP request with digest authentication. 1020 * 1021 * @param cred $USERNAME:$PASSWORD to use 1022 * @param[in,out] phase context 1023 * @param[out] http_status set to HTTP status 1024 * @return error message, NULL on success 1025 */ 1026 static const char * 1027 send_digest_auth (const char *cred, 1028 struct MHDT_PhaseContext *pc, 1029 unsigned int *http_status) 1030 { 1031 CURL *c; 1032 const char *err; 1033 long status; 1034 char *pass = strchr (cred, ':'); 1035 char *user; 1036 1037 if (NULL == pass) 1038 return "invalid credential given"; 1039 user = strndup (cred, 1040 pass - cred); 1041 pass++; 1042 c = setup_curl (pc); 1043 if (NULL == c) 1044 { 1045 free (user); 1046 return "Failed to initialize Curl handle"; 1047 } 1048 err = set_url (c, 1049 pc->base_url, 1050 pc); 1051 if (NULL != err) 1052 { 1053 free (user); 1054 curl_easy_cleanup (c); 1055 return err; 1056 } 1057 if ( (CURLE_OK != 1058 curl_easy_setopt (c, 1059 CURLOPT_HTTPAUTH, 1060 (long) CURLAUTH_DIGEST)) || 1061 (CURLE_OK != 1062 curl_easy_setopt (c, 1063 CURLOPT_USERNAME, 1064 user)) || 1065 (CURLE_OK != 1066 curl_easy_setopt (c, 1067 CURLOPT_PASSWORD, 1068 pass)) ) 1069 { 1070 curl_easy_cleanup (c); 1071 free (user); 1072 return "Failed to set digest authentication header for curl request"; 1073 } 1074 free (user); 1075 PERFORM_REQUEST (c); 1076 if (CURLE_OK != 1077 curl_easy_getinfo (c, 1078 CURLINFO_RESPONSE_CODE, 1079 &status)) 1080 { 1081 return "Failed to get HTTP status"; 1082 } 1083 *http_status = (unsigned int) status; 1084 curl_easy_cleanup (c); 1085 return NULL; 1086 } 1087 1088 1089 const char * 1090 MHDT_client_send_digest_auth ( 1091 const void *cls, 1092 struct MHDT_PhaseContext *pc) 1093 { 1094 const char *cred = cls; 1095 const char *ret; 1096 unsigned int status; 1097 1098 ret = send_digest_auth (cred, 1099 pc, 1100 &status); 1101 if (NULL != ret) 1102 return ret; 1103 if (MHD_HTTP_STATUS_NO_CONTENT != status) 1104 return "invalid HTTP response code"; 1105 return NULL; 1106 } 1107 1108 1109 const char * 1110 MHDT_client_fail_digest_auth ( 1111 const void *cls, 1112 struct MHDT_PhaseContext *pc) 1113 { 1114 const char *cred = cls; 1115 const char *ret; 1116 unsigned int status; 1117 1118 ret = send_digest_auth (cred, 1119 pc, 1120 &status); 1121 if (NULL != ret) 1122 return ret; 1123 if (MHD_HTTP_STATUS_FORBIDDEN != status) 1124 return "invalid HTTP response code"; 1125 return NULL; 1126 }