libtest_convenience_client_request.c (28107B)
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_TIMEOUT_MS, 271 500)) 272 || (CURLE_OK != 273 curl_easy_setopt (c, 274 CURLOPT_CONNECTTIMEOUT_MS, 275 50)) || 276 #ifdef CURLOPT_SERVER_RESPONSE_TIMEOUT_MS 277 (CURLE_OK != 278 curl_easy_setopt (c, 279 CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, 280 250)) || 281 #else 282 (CURLE_OK != 283 curl_easy_setopt (c, 284 CURLOPT_SERVER_RESPONSE_TIMEOUT, 285 1)) || 286 #endif 287 (CURLE_OK != 288 curl_easy_setopt (c, 289 CURLOPT_FRESH_CONNECT, 290 1L)) 291 || (CURLE_OK != 292 curl_easy_setopt (c, 293 CURLOPT_FORBID_REUSE, 294 1L)) 295 || (CURLE_OK != 296 curl_easy_setopt (c, 297 CURLOPT_VERBOSE, 298 0))) 299 { 300 curl_easy_cleanup (c); 301 return "Failed to set curl options"; 302 } 303 { 304 /* Force curl to do the request to 127.0.0.1 regardless of 305 hostname */ 306 const char *host; 307 const char *end; 308 char ri[1024]; 309 310 if (0 == strncasecmp (base_url, 311 "https://", 312 strlen ("https://"))) 313 host = &base_url[strlen ("https://")]; 314 else 315 host = &base_url[strlen ("http://")]; 316 end = strchr (host, '/'); 317 if (NULL == end) 318 end = host + strlen (host); 319 snprintf (ri, 320 sizeof (ri), 321 "%.*s:127.0.0.1", 322 (int)(end - host), 323 host); 324 pc->hosts = curl_slist_append (NULL, 325 ri); 326 if (CURLE_OK != 327 curl_easy_setopt (c, 328 CURLOPT_RESOLVE, 329 pc->hosts)) 330 { 331 curl_easy_cleanup (c); 332 return "Failed to override DNS"; 333 } 334 } 335 if (0 == strncasecmp (base_url, 336 "https://", 337 strlen ("https://"))) 338 { 339 struct MHDT_Phase *phase = pc->phase; 340 341 if (phase->check_server_cert) 342 { 343 if (CURLE_OK != 344 curl_easy_setopt (c, 345 CURLOPT_CAINFO, 346 "data/root-ca.crt")) 347 { 348 curl_easy_cleanup (c); 349 return "Failed to override root CA"; 350 } 351 } 352 else 353 { 354 /* disable certificate checking */ 355 if ((CURLE_OK != 356 curl_easy_setopt (c, 357 CURLOPT_SSL_VERIFYPEER, 358 0L)) 359 || (CURLE_OK != 360 curl_easy_setopt (c, 361 CURLOPT_SSL_VERIFYHOST, 362 0L))) 363 { 364 curl_easy_cleanup (c); 365 return "Failed to disable X509 server certificate checks"; 366 } 367 } 368 if (NULL != phase->client_cert) 369 { 370 if (CURLE_OK != 371 curl_easy_setopt (c, 372 CURLOPT_SSLCERT, 373 phase->client_cert)) 374 { 375 curl_easy_cleanup (c); 376 return "Failed to set client certificate"; 377 } 378 } 379 } 380 return NULL; 381 } 382 383 384 /** 385 * Create a curl handle for the given @a pc. 386 * 387 * @param pc current phase context with options to use 388 * @return NULL on error 389 */ 390 static CURL * 391 setup_curl (const struct MHDT_PhaseContext *pc) 392 { 393 struct MHDT_Phase *p = pc->phase; 394 CURL *c; 395 396 c = curl_easy_init (); 397 if (NULL == c) 398 return NULL; 399 switch (p->http_version) 400 { 401 case 0: /* unset == any */ 402 break; 403 case 1: 404 if (CURLE_OK != 405 curl_easy_setopt (c, 406 CURLOPT_HTTP_VERSION, 407 CURL_HTTP_VERSION_1_1)) 408 { 409 curl_easy_cleanup (c); 410 fprintf (stderr, 411 "HTTP/1 not supported by curl?\n"); 412 return NULL; 413 } 414 break; 415 case 2: /* HTTP/2 */ 416 if (p->use_tls) 417 { 418 if (CURLE_OK != 419 curl_easy_setopt (c, 420 CURLOPT_HTTP_VERSION, 421 CURL_HTTP_VERSION_2TLS)) 422 { 423 curl_easy_cleanup (c); 424 fprintf (stderr, 425 "HTTP/2 not supported by curl?\n"); 426 return NULL; 427 } 428 } 429 else 430 { 431 if (CURLE_OK != 432 curl_easy_setopt (c, 433 CURLOPT_HTTP_VERSION, 434 CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)) 435 { 436 curl_easy_cleanup (c); 437 fprintf (stderr, 438 "HTTP/2 not supported by curl?\n"); 439 return NULL; 440 } 441 } 442 break; 443 case 3: 444 abort (); // not yet supported 445 break; 446 default: 447 abort (); 448 break; 449 } 450 return c; 451 } 452 453 454 const char * 455 MHDT_client_get_host (const void *cls, 456 struct MHDT_PhaseContext *pc) 457 { 458 const char *host = cls; 459 const char *err; 460 size_t alen = strlen (host); 461 CURL *c; 462 size_t blen = strlen (pc->base_url); 463 char u[alen + blen + 1]; 464 const char *slash = strchr (pc->base_url, 465 '/'); 466 const char *colon; 467 468 if (NULL == slash) 469 return "'/' missing in base URL"; 470 colon = strchr (slash, 471 ':'); 472 if (NULL == colon) 473 return "':' missing in base URL"; 474 snprintf (u, 475 sizeof (u), 476 "https://%s%s", 477 host, 478 colon); 479 c = setup_curl (pc); 480 if (NULL == c) 481 return "Failed to initialize Curl handle"; 482 err = set_url (c, 483 u, 484 pc); 485 if (NULL != err) 486 return err; 487 PERFORM_REQUEST (c); 488 CHECK_STATUS (c, 489 MHD_HTTP_STATUS_OK); 490 curl_easy_cleanup (c); 491 return NULL; 492 } 493 494 495 const char * 496 MHDT_client_get_root ( 497 const void *cls, 498 struct MHDT_PhaseContext *pc) 499 { 500 const char *text = cls; 501 CURL *c; 502 const char *err; 503 DECLARE_WB (text); 504 505 c = setup_curl (pc); 506 if (NULL == c) 507 return "Failed to initialize Curl handle"; 508 err = set_url (c, 509 pc->base_url, 510 pc); 511 if (NULL != err) 512 return err; 513 SETUP_WB (c); 514 PERFORM_REQUEST (c); 515 CHECK_STATUS (c, 516 MHD_HTTP_STATUS_OK); 517 curl_easy_cleanup (c); 518 CHECK_WB (text); 519 return NULL; 520 } 521 522 523 const char * 524 MHDT_client_get_with_query ( 525 const void *cls, 526 struct MHDT_PhaseContext *pc) 527 { 528 const char *args = cls; 529 const char *err; 530 size_t alen = strlen (args); 531 CURL *c; 532 size_t blen = strlen (pc->base_url); 533 char u[alen + blen + 1]; 534 535 memcpy (u, 536 pc->base_url, 537 blen); 538 memcpy (u + blen, 539 args, 540 alen); 541 u[alen + blen] = '\0'; 542 c = setup_curl (pc); 543 if (NULL == c) 544 return "Failed to initialize Curl handle"; 545 err = set_url (c, 546 u, 547 pc); 548 if (NULL != err) 549 return err; 550 PERFORM_REQUEST (c); 551 CHECK_STATUS (c, 552 MHD_HTTP_STATUS_NO_CONTENT); 553 curl_easy_cleanup (c); 554 return NULL; 555 } 556 557 558 const char * 559 MHDT_client_set_header ( 560 const void *cls, 561 struct MHDT_PhaseContext *pc) 562 { 563 const char *hdr = cls; 564 const char *err; 565 CURL *c; 566 CURLcode res; 567 struct curl_slist *slist; 568 569 c = setup_curl (pc); 570 if (NULL == c) 571 return "Failed to initialize Curl handle"; 572 err = set_url (c, 573 pc->base_url, 574 pc); 575 if (NULL != err) 576 return err; 577 slist = curl_slist_append (NULL, 578 hdr); 579 if (CURLE_OK != 580 curl_easy_setopt (c, 581 CURLOPT_HTTPHEADER, 582 slist)) 583 { 584 curl_easy_cleanup (c); 585 curl_slist_free_all (slist); 586 return "Failed to set custom header for curl request"; 587 } 588 res = curl_easy_perform (c); 589 curl_slist_free_all (slist); 590 if (CURLE_OK != res) 591 { 592 curl_easy_cleanup (c); 593 return "Failed to fetch URL"; 594 } 595 CHECK_STATUS (c, 596 MHD_HTTP_STATUS_NO_CONTENT); 597 curl_easy_cleanup (c); 598 return NULL; 599 } 600 601 602 const char * 603 MHDT_client_expect_header (const void *cls, 604 struct MHDT_PhaseContext *pc) 605 { 606 #ifdef HAVE_LIBCRUL_NEW_HDR_API 607 const char *hdr = cls; 608 const char *err; 609 size_t hlen = strlen (hdr) + 1; 610 char key[hlen]; 611 const char *colon = strchr (hdr, ':'); 612 const char *value; 613 CURL *c; 614 bool found = false; 615 616 if (NULL == colon) 617 return "Invalid expected header passed"; 618 memcpy (key, 619 hdr, 620 hlen); 621 key[colon - hdr] = '\0'; 622 value = &key[colon - hdr + 1]; 623 c = setup_curl (pc); 624 if (NULL == c) 625 return "Failed to initialize Curl handle"; 626 err = set_url (c, 627 pc->base_url, 628 pc); 629 if (NULL != err) 630 return err; 631 PERFORM_REQUEST (c); 632 CHECK_STATUS (c, 633 MHD_HTTP_STATUS_NO_CONTENT); 634 for (size_t index = 0; !found; index++) 635 { 636 CURLHcode rval; 637 struct curl_header *hout; 638 639 rval = curl_easy_header (c, 640 key, 641 index, 642 CURLH_HEADER, 643 -1 /* last request */, 644 &hout); 645 if (CURLHE_OK != rval) 646 break; 647 found = (0 == strcmp (value, 648 hout->value)); 649 } 650 if (!found) 651 { 652 curl_easy_cleanup (c); 653 return "Expected HTTP response header not found"; 654 } 655 curl_easy_cleanup (c); 656 return NULL; 657 #else /* ! HAVE_LIBCRUL_NEW_HDR_API */ 658 (void)cls; 659 (void)pc; 660 return NULL; 661 #endif /* ! HAVE_LIBCRUL_NEW_HDR_API */ 662 } 663 664 665 /** 666 * Closure for the read_cb(). 667 */ 668 struct ReadBuffer 669 { 670 /** 671 * Origin of data to upload. 672 */ 673 const char *buf; 674 675 /** 676 * Number of bytes in @e buf. 677 */ 678 size_t len; 679 680 /** 681 * Current read offset in @e buf. 682 */ 683 size_t pos; 684 685 /** 686 * Number of chunks to user when sending. 687 */ 688 unsigned int chunks; 689 690 }; 691 692 693 /** 694 * Callback for CURLOPT_READFUNCTION for uploading 695 * data to the HTTP server. 696 * 697 * @param ptr data uploaded 698 * @param size size of a member 699 * @param nmemb number of members 700 * @param stream must be a `struct ReadBuffer` 701 * @return bytes processed (size*nmemb) or error 702 */ 703 static size_t 704 read_cb (void *ptr, 705 size_t size, 706 size_t nmemb, 707 void *stream) 708 { 709 struct ReadBuffer *rb = stream; 710 size_t limit = size * nmemb; 711 712 if (limit / size != nmemb) 713 return CURLE_WRITE_ERROR; 714 if (limit > rb->len - rb->pos) 715 limit = rb->len - rb->pos; 716 if ((rb->chunks > 1) 717 && (limit > 1)) 718 { 719 limit /= rb->chunks; 720 rb->chunks--; 721 } 722 memcpy (ptr, 723 rb->buf + rb->pos, 724 limit); 725 rb->pos += limit; 726 return limit; 727 } 728 729 730 const char * 731 MHDT_client_put_data ( 732 const void *cls, 733 struct MHDT_PhaseContext *pc) 734 { 735 const char *text = cls; 736 const char *err; 737 struct ReadBuffer rb = { 738 .buf = text, 739 .len = strlen (text) 740 }; 741 CURL *c; 742 743 c = setup_curl (pc); 744 if (NULL == c) 745 return "Failed to initialize Curl handle"; 746 err = set_url (c, 747 pc->base_url, 748 pc); 749 if (NULL != err) 750 return err; 751 if (CURLE_OK != 752 curl_easy_setopt (c, 753 CURLOPT_UPLOAD, 754 1L)) 755 { 756 curl_easy_cleanup (c); 757 return "Failed to set PUT method for curl request"; 758 } 759 if (CURLE_OK != 760 curl_easy_setopt (c, 761 CURLOPT_READFUNCTION, 762 &read_cb)) 763 { 764 curl_easy_cleanup (c); 765 return "Failed to set READFUNCTION for curl request"; 766 } 767 if (CURLE_OK != 768 curl_easy_setopt (c, 769 CURLOPT_READDATA, 770 &rb)) 771 { 772 curl_easy_cleanup (c); 773 return "Failed to set READFUNCTION for curl request"; 774 } 775 if (CURLE_OK != 776 curl_easy_setopt (c, 777 CURLOPT_INFILESIZE_LARGE, 778 (curl_off_t)rb.len)) 779 { 780 curl_easy_cleanup (c); 781 return "Failed to set INFILESIZE_LARGE for curl request"; 782 } 783 PERFORM_REQUEST (c); 784 CHECK_STATUS (c, 785 MHD_HTTP_STATUS_NO_CONTENT); 786 curl_easy_cleanup (c); 787 return NULL; 788 } 789 790 791 const char * 792 MHDT_client_chunk_data ( 793 const void *cls, 794 struct MHDT_PhaseContext *pc) 795 { 796 const char *text = cls; 797 const char *err; 798 struct ReadBuffer rb = { 799 .buf = text, 800 .len = strlen (text), 801 .chunks = 2 802 }; 803 CURL *c; 804 805 c = setup_curl (pc); 806 if (NULL == c) 807 return "Failed to initialize Curl handle"; 808 err = set_url (c, 809 pc->base_url, 810 pc); 811 if (NULL != err) 812 return err; 813 if (CURLE_OK != 814 curl_easy_setopt (c, 815 CURLOPT_UPLOAD, 816 1L)) 817 { 818 curl_easy_cleanup (c); 819 return "Failed to set PUT method for curl request"; 820 } 821 if (CURLE_OK != 822 curl_easy_setopt (c, 823 CURLOPT_READFUNCTION, 824 &read_cb)) 825 { 826 curl_easy_cleanup (c); 827 return "Failed to set READFUNCTION for curl request"; 828 } 829 if (CURLE_OK != 830 curl_easy_setopt (c, 831 CURLOPT_READDATA, 832 &rb)) 833 { 834 curl_easy_cleanup (c); 835 return "Failed to set READFUNCTION for curl request"; 836 } 837 PERFORM_REQUEST (c); 838 CHECK_STATUS (c, 839 MHD_HTTP_STATUS_NO_CONTENT); 840 curl_easy_cleanup (c); 841 return NULL; 842 } 843 844 845 const char * 846 MHDT_client_do_post ( 847 const void *cls, 848 struct MHDT_PhaseContext *pc) 849 { 850 const struct MHDT_PostInstructions *pi = cls; 851 const char *err; 852 CURL *c; 853 struct curl_slist *request_hdr = NULL; 854 855 /* reset wants in case we re-use the array */ 856 if (NULL != pi->wants) 857 { 858 for (unsigned int i = 0; NULL != pi->wants[i].key; i++) 859 { 860 pi->wants[i].value_off = 0; 861 pi->wants[i].satisfied = false; 862 } 863 } 864 c = setup_curl (pc); 865 if (NULL == c) 866 return "Failed to initialize Curl handle"; 867 err = set_url (c, 868 pc->base_url, 869 pc); 870 if (NULL != err) 871 return err; 872 if (CURLE_OK != 873 curl_easy_setopt (c, 874 CURLOPT_POST, 875 1L)) 876 { 877 curl_easy_cleanup (c); 878 return "Failed to set POST method for curl request"; 879 } 880 if (CURLE_OK != 881 curl_easy_setopt (c, 882 CURLOPT_POSTFIELDS, 883 pi->postdata)) 884 { 885 curl_easy_cleanup (c); 886 return "Failed to set POSTFIELDS for curl request"; 887 } 888 if (0 != pi->postdata_size) 889 { 890 if (CURLE_OK != 891 curl_easy_setopt (c, 892 CURLOPT_POSTFIELDSIZE_LARGE, 893 (curl_off_t)pi->postdata_size)) 894 { 895 curl_easy_cleanup (c); 896 return "Failed to set POSTFIELDS for curl request"; 897 } 898 } 899 if (NULL != pi->postheader) 900 { 901 request_hdr = curl_slist_append (request_hdr, 902 pi->postheader); 903 } 904 if (CURLE_OK != 905 curl_easy_setopt (c, 906 CURLOPT_HTTPHEADER, 907 request_hdr)) 908 { 909 curl_easy_cleanup (c); 910 curl_slist_free_all (request_hdr); 911 return "Failed to set HTTPHEADER for curl request"; 912 } 913 PERFORM_REQUEST (c); 914 CHECK_STATUS (c, 915 MHD_HTTP_STATUS_NO_CONTENT); 916 curl_easy_cleanup (c); 917 curl_slist_free_all (request_hdr); 918 if (NULL != pi->wants) 919 { 920 for (unsigned int i = 0; NULL != pi->wants[i].key; i++) 921 { 922 if (!pi->wants[i].satisfied) 923 { 924 fprintf (stderr, 925 "Server did not correctly detect key '%s'\n", 926 pi->wants[i].key); 927 return "key-value data not matched by server"; 928 } 929 } 930 } 931 return NULL; 932 } 933 934 935 /** 936 * Send HTTP request with basic authentication. 937 * 938 * @param cred $USERNAME:$PASSWORD to use 939 * @param[in,out] phase context 940 * @param[out] http_status set to HTTP status 941 * @return error message, NULL on success 942 */ 943 static const char * 944 send_basic_auth (const char *cred, 945 struct MHDT_PhaseContext *pc, 946 unsigned int *http_status) 947 { 948 CURL *c; 949 const char *err; 950 long status; 951 char *pass = strchr (cred, ':'); 952 char *user; 953 954 if (NULL == pass) 955 return "invalid credential given"; 956 user = strndup (cred, 957 pass - cred); 958 pass++; 959 c = setup_curl (pc); 960 if (NULL == c) 961 { 962 free (user); 963 return "Failed to initialize Curl handle"; 964 } 965 err = set_url (c, 966 pc->base_url, 967 pc); 968 if (NULL != err) 969 { 970 free (user); 971 curl_easy_cleanup (c); 972 return err; 973 } 974 if ((CURLE_OK != 975 curl_easy_setopt (c, 976 CURLOPT_HTTPAUTH, 977 (long)CURLAUTH_BASIC)) 978 || (CURLE_OK != 979 curl_easy_setopt (c, 980 CURLOPT_USERNAME, 981 user)) 982 || (CURLE_OK != 983 curl_easy_setopt (c, 984 CURLOPT_PASSWORD, 985 pass))) 986 { 987 curl_easy_cleanup (c); 988 free (user); 989 return "Failed to set basic authentication header for curl request"; 990 } 991 free (user); 992 PERFORM_REQUEST (c); 993 if (CURLE_OK != 994 curl_easy_getinfo (c, 995 CURLINFO_RESPONSE_CODE, 996 &status)) 997 { 998 return "Failed to get HTTP status"; 999 } 1000 *http_status = (unsigned int)status; 1001 curl_easy_cleanup (c); 1002 return NULL; 1003 } 1004 1005 1006 const char * 1007 MHDT_client_send_basic_auth ( 1008 const void *cls, 1009 struct MHDT_PhaseContext *pc) 1010 { 1011 const char *cred = cls; 1012 const char *ret; 1013 unsigned int status; 1014 1015 ret = send_basic_auth (cred, 1016 pc, 1017 &status); 1018 if (NULL != ret) 1019 return ret; 1020 if (MHD_HTTP_STATUS_NO_CONTENT != status) 1021 return "invalid HTTP response code"; 1022 return NULL; 1023 } 1024 1025 1026 const char * 1027 MHDT_client_fail_basic_auth ( 1028 const void *cls, 1029 struct MHDT_PhaseContext *pc) 1030 { 1031 const char *cred = cls; 1032 const char *ret; 1033 unsigned int status; 1034 1035 ret = send_basic_auth (cred, 1036 pc, 1037 &status); 1038 if (NULL != ret) 1039 return ret; 1040 if (MHD_HTTP_STATUS_UNAUTHORIZED != status) 1041 return "invalid HTTP response code"; 1042 return NULL; 1043 } 1044 1045 1046 /** 1047 * Send HTTP request with digest authentication. 1048 * 1049 * @param cred $USERNAME:$PASSWORD to use 1050 * @param[in,out] phase context 1051 * @param[out] http_status set to HTTP status 1052 * @return error message, NULL on success 1053 */ 1054 static const char * 1055 send_digest_auth (const char *cred, 1056 struct MHDT_PhaseContext *pc, 1057 unsigned int *http_status) 1058 { 1059 CURL *c; 1060 const char *err; 1061 long status; 1062 char *pass = strchr (cred, ':'); 1063 char *user; 1064 1065 if (NULL == pass) 1066 return "invalid credential given"; 1067 user = strndup (cred, 1068 pass - cred); 1069 pass++; 1070 c = setup_curl (pc); 1071 if (NULL == c) 1072 { 1073 free (user); 1074 return "Failed to initialize Curl handle"; 1075 } 1076 err = set_url (c, 1077 pc->base_url, 1078 pc); 1079 if (NULL != err) 1080 { 1081 free (user); 1082 curl_easy_cleanup (c); 1083 return err; 1084 } 1085 if ((CURLE_OK != 1086 curl_easy_setopt (c, 1087 CURLOPT_HTTPAUTH, 1088 (long)CURLAUTH_DIGEST)) 1089 || (CURLE_OK != 1090 curl_easy_setopt (c, 1091 CURLOPT_USERNAME, 1092 user)) 1093 || (CURLE_OK != 1094 curl_easy_setopt (c, 1095 CURLOPT_PASSWORD, 1096 pass))) 1097 { 1098 curl_easy_cleanup (c); 1099 free (user); 1100 return "Failed to set digest authentication header for curl request"; 1101 } 1102 free (user); 1103 PERFORM_REQUEST (c); 1104 if (CURLE_OK != 1105 curl_easy_getinfo (c, 1106 CURLINFO_RESPONSE_CODE, 1107 &status)) 1108 { 1109 return "Failed to get HTTP status"; 1110 } 1111 *http_status = (unsigned int)status; 1112 curl_easy_cleanup (c); 1113 return NULL; 1114 } 1115 1116 1117 const char * 1118 MHDT_client_send_digest_auth ( 1119 const void *cls, 1120 struct MHDT_PhaseContext *pc) 1121 { 1122 const char *cred = cls; 1123 const char *ret; 1124 unsigned int status; 1125 1126 ret = send_digest_auth (cred, 1127 pc, 1128 &status); 1129 if (NULL != ret) 1130 return ret; 1131 if (MHD_HTTP_STATUS_NO_CONTENT != status) 1132 return "invalid HTTP response code"; 1133 return NULL; 1134 } 1135 1136 1137 const char * 1138 MHDT_client_fail_digest_auth ( 1139 const void *cls, 1140 struct MHDT_PhaseContext *pc) 1141 { 1142 const char *cred = cls; 1143 const char *ret; 1144 unsigned int status; 1145 1146 ret = send_digest_auth (cred, 1147 pc, 1148 &status); 1149 if (NULL != ret) 1150 return ret; 1151 if (MHD_HTTP_STATUS_FORBIDDEN != status) 1152 return "invalid HTTP response code"; 1153 return NULL; 1154 }