libtest_convenience_client_request.c (28069B)
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; (void) pc; 659 return NULL; 660 #endif /* ! HAVE_LIBCRUL_NEW_HDR_API */ 661 } 662 663 664 /** 665 * Closure for the read_cb(). 666 */ 667 struct ReadBuffer 668 { 669 /** 670 * Origin of data to upload. 671 */ 672 const char *buf; 673 674 /** 675 * Number of bytes in @e buf. 676 */ 677 size_t len; 678 679 /** 680 * Current read offset in @e buf. 681 */ 682 size_t pos; 683 684 /** 685 * Number of chunks to user when sending. 686 */ 687 unsigned int chunks; 688 689 }; 690 691 692 /** 693 * Callback for CURLOPT_READFUNCTION for uploading 694 * data to the HTTP server. 695 * 696 * @param ptr data uploaded 697 * @param size size of a member 698 * @param nmemb number of members 699 * @param stream must be a `struct ReadBuffer` 700 * @return bytes processed (size*nmemb) or error 701 */ 702 static size_t 703 read_cb (void *ptr, 704 size_t size, 705 size_t nmemb, 706 void *stream) 707 { 708 struct ReadBuffer *rb = stream; 709 size_t limit = size * nmemb; 710 711 if (limit / size != nmemb) 712 return CURLE_WRITE_ERROR; 713 if (limit > rb->len - rb->pos) 714 limit = rb->len - rb->pos; 715 if ( (rb->chunks > 1) && 716 (limit > 1) ) 717 { 718 limit /= rb->chunks; 719 rb->chunks--; 720 } 721 memcpy (ptr, 722 rb->buf + rb->pos, 723 limit); 724 rb->pos += limit; 725 return limit; 726 } 727 728 729 const char * 730 MHDT_client_put_data ( 731 const void *cls, 732 struct MHDT_PhaseContext *pc) 733 { 734 const char *text = cls; 735 const char *err; 736 struct ReadBuffer rb = { 737 .buf = text, 738 .len = strlen (text) 739 }; 740 CURL *c; 741 742 c = setup_curl (pc); 743 if (NULL == c) 744 return "Failed to initialize Curl handle"; 745 err = set_url (c, 746 pc->base_url, 747 pc); 748 if (NULL != err) 749 return err; 750 if (CURLE_OK != 751 curl_easy_setopt (c, 752 CURLOPT_UPLOAD, 753 1L)) 754 { 755 curl_easy_cleanup (c); 756 return "Failed to set PUT method for curl request"; 757 } 758 if (CURLE_OK != 759 curl_easy_setopt (c, 760 CURLOPT_READFUNCTION, 761 &read_cb)) 762 { 763 curl_easy_cleanup (c); 764 return "Failed to set READFUNCTION for curl request"; 765 } 766 if (CURLE_OK != 767 curl_easy_setopt (c, 768 CURLOPT_READDATA, 769 &rb)) 770 { 771 curl_easy_cleanup (c); 772 return "Failed to set READFUNCTION for curl request"; 773 } 774 if (CURLE_OK != 775 curl_easy_setopt (c, 776 CURLOPT_INFILESIZE_LARGE, 777 (curl_off_t) rb.len)) 778 { 779 curl_easy_cleanup (c); 780 return "Failed to set INFILESIZE_LARGE for curl request"; 781 } 782 PERFORM_REQUEST (c); 783 CHECK_STATUS (c, 784 MHD_HTTP_STATUS_NO_CONTENT); 785 curl_easy_cleanup (c); 786 return NULL; 787 } 788 789 790 const char * 791 MHDT_client_chunk_data ( 792 const void *cls, 793 struct MHDT_PhaseContext *pc) 794 { 795 const char *text = cls; 796 const char *err; 797 struct ReadBuffer rb = { 798 .buf = text, 799 .len = strlen (text), 800 .chunks = 2 801 }; 802 CURL *c; 803 804 c = setup_curl (pc); 805 if (NULL == c) 806 return "Failed to initialize Curl handle"; 807 err = set_url (c, 808 pc->base_url, 809 pc); 810 if (NULL != err) 811 return err; 812 if (CURLE_OK != 813 curl_easy_setopt (c, 814 CURLOPT_UPLOAD, 815 1L)) 816 { 817 curl_easy_cleanup (c); 818 return "Failed to set PUT method for curl request"; 819 } 820 if (CURLE_OK != 821 curl_easy_setopt (c, 822 CURLOPT_READFUNCTION, 823 &read_cb)) 824 { 825 curl_easy_cleanup (c); 826 return "Failed to set READFUNCTION for curl request"; 827 } 828 if (CURLE_OK != 829 curl_easy_setopt (c, 830 CURLOPT_READDATA, 831 &rb)) 832 { 833 curl_easy_cleanup (c); 834 return "Failed to set READFUNCTION for curl request"; 835 } 836 PERFORM_REQUEST (c); 837 CHECK_STATUS (c, 838 MHD_HTTP_STATUS_NO_CONTENT); 839 curl_easy_cleanup (c); 840 return NULL; 841 } 842 843 844 const char * 845 MHDT_client_do_post ( 846 const void *cls, 847 struct MHDT_PhaseContext *pc) 848 { 849 const struct MHDT_PostInstructions *pi = cls; 850 const char *err; 851 CURL *c; 852 struct curl_slist *request_hdr = NULL; 853 854 /* reset wants in case we re-use the array */ 855 if (NULL != pi->wants) 856 { 857 for (unsigned int i = 0; NULL != pi->wants[i].key; i++) 858 { 859 pi->wants[i].value_off = 0; 860 pi->wants[i].satisfied = false; 861 } 862 } 863 c = setup_curl (pc); 864 if (NULL == c) 865 return "Failed to initialize Curl handle"; 866 err = set_url (c, 867 pc->base_url, 868 pc); 869 if (NULL != err) 870 return err; 871 if (CURLE_OK != 872 curl_easy_setopt (c, 873 CURLOPT_POST, 874 1L)) 875 { 876 curl_easy_cleanup (c); 877 return "Failed to set POST method for curl request"; 878 } 879 if (CURLE_OK != 880 curl_easy_setopt (c, 881 CURLOPT_POSTFIELDS, 882 pi->postdata)) 883 { 884 curl_easy_cleanup (c); 885 return "Failed to set POSTFIELDS for curl request"; 886 } 887 if (0 != pi->postdata_size) 888 { 889 if (CURLE_OK != 890 curl_easy_setopt (c, 891 CURLOPT_POSTFIELDSIZE_LARGE, 892 (curl_off_t) pi->postdata_size)) 893 { 894 curl_easy_cleanup (c); 895 return "Failed to set POSTFIELDS for curl request"; 896 } 897 } 898 if (NULL != pi->postheader) 899 { 900 request_hdr = curl_slist_append (request_hdr, 901 pi->postheader); 902 } 903 if (CURLE_OK != 904 curl_easy_setopt (c, 905 CURLOPT_HTTPHEADER, 906 request_hdr)) 907 { 908 curl_easy_cleanup (c); 909 curl_slist_free_all (request_hdr); 910 return "Failed to set HTTPHEADER for curl request"; 911 } 912 PERFORM_REQUEST (c); 913 CHECK_STATUS (c, 914 MHD_HTTP_STATUS_NO_CONTENT); 915 curl_easy_cleanup (c); 916 curl_slist_free_all (request_hdr); 917 if (NULL != pi->wants) 918 { 919 for (unsigned int i = 0; NULL != pi->wants[i].key; i++) 920 { 921 if (! pi->wants[i].satisfied) 922 { 923 fprintf (stderr, 924 "Server did not correctly detect key '%s'\n", 925 pi->wants[i].key); 926 return "key-value data not matched by server"; 927 } 928 } 929 } 930 return NULL; 931 } 932 933 934 /** 935 * Send HTTP request with basic authentication. 936 * 937 * @param cred $USERNAME:$PASSWORD to use 938 * @param[in,out] phase context 939 * @param[out] http_status set to HTTP status 940 * @return error message, NULL on success 941 */ 942 static const char * 943 send_basic_auth (const char *cred, 944 struct MHDT_PhaseContext *pc, 945 unsigned int *http_status) 946 { 947 CURL *c; 948 const char *err; 949 long status; 950 char *pass = strchr (cred, ':'); 951 char *user; 952 953 if (NULL == pass) 954 return "invalid credential given"; 955 user = strndup (cred, 956 pass - cred); 957 pass++; 958 c = setup_curl (pc); 959 if (NULL == c) 960 { 961 free (user); 962 return "Failed to initialize Curl handle"; 963 } 964 err = set_url (c, 965 pc->base_url, 966 pc); 967 if (NULL != err) 968 { 969 free (user); 970 curl_easy_cleanup (c); 971 return err; 972 } 973 if ( (CURLE_OK != 974 curl_easy_setopt (c, 975 CURLOPT_HTTPAUTH, 976 (long) CURLAUTH_BASIC)) || 977 (CURLE_OK != 978 curl_easy_setopt (c, 979 CURLOPT_USERNAME, 980 user)) || 981 (CURLE_OK != 982 curl_easy_setopt (c, 983 CURLOPT_PASSWORD, 984 pass)) ) 985 { 986 curl_easy_cleanup (c); 987 free (user); 988 return "Failed to set basic authentication header for curl request"; 989 } 990 free (user); 991 PERFORM_REQUEST (c); 992 if (CURLE_OK != 993 curl_easy_getinfo (c, 994 CURLINFO_RESPONSE_CODE, 995 &status)) 996 { 997 return "Failed to get HTTP status"; 998 } 999 *http_status = (unsigned int) status; 1000 curl_easy_cleanup (c); 1001 return NULL; 1002 } 1003 1004 1005 const char * 1006 MHDT_client_send_basic_auth ( 1007 const void *cls, 1008 struct MHDT_PhaseContext *pc) 1009 { 1010 const char *cred = cls; 1011 const char *ret; 1012 unsigned int status; 1013 1014 ret = send_basic_auth (cred, 1015 pc, 1016 &status); 1017 if (NULL != ret) 1018 return ret; 1019 if (MHD_HTTP_STATUS_NO_CONTENT != status) 1020 return "invalid HTTP response code"; 1021 return NULL; 1022 } 1023 1024 1025 const char * 1026 MHDT_client_fail_basic_auth ( 1027 const void *cls, 1028 struct MHDT_PhaseContext *pc) 1029 { 1030 const char *cred = cls; 1031 const char *ret; 1032 unsigned int status; 1033 1034 ret = send_basic_auth (cred, 1035 pc, 1036 &status); 1037 if (NULL != ret) 1038 return ret; 1039 if (MHD_HTTP_STATUS_UNAUTHORIZED != status) 1040 return "invalid HTTP response code"; 1041 return NULL; 1042 } 1043 1044 1045 /** 1046 * Send HTTP request with digest authentication. 1047 * 1048 * @param cred $USERNAME:$PASSWORD to use 1049 * @param[in,out] phase context 1050 * @param[out] http_status set to HTTP status 1051 * @return error message, NULL on success 1052 */ 1053 static const char * 1054 send_digest_auth (const char *cred, 1055 struct MHDT_PhaseContext *pc, 1056 unsigned int *http_status) 1057 { 1058 CURL *c; 1059 const char *err; 1060 long status; 1061 char *pass = strchr (cred, ':'); 1062 char *user; 1063 1064 if (NULL == pass) 1065 return "invalid credential given"; 1066 user = strndup (cred, 1067 pass - cred); 1068 pass++; 1069 c = setup_curl (pc); 1070 if (NULL == c) 1071 { 1072 free (user); 1073 return "Failed to initialize Curl handle"; 1074 } 1075 err = set_url (c, 1076 pc->base_url, 1077 pc); 1078 if (NULL != err) 1079 { 1080 free (user); 1081 curl_easy_cleanup (c); 1082 return err; 1083 } 1084 if ( (CURLE_OK != 1085 curl_easy_setopt (c, 1086 CURLOPT_HTTPAUTH, 1087 (long) CURLAUTH_DIGEST)) || 1088 (CURLE_OK != 1089 curl_easy_setopt (c, 1090 CURLOPT_USERNAME, 1091 user)) || 1092 (CURLE_OK != 1093 curl_easy_setopt (c, 1094 CURLOPT_PASSWORD, 1095 pass)) ) 1096 { 1097 curl_easy_cleanup (c); 1098 free (user); 1099 return "Failed to set digest authentication header for curl request"; 1100 } 1101 free (user); 1102 PERFORM_REQUEST (c); 1103 if (CURLE_OK != 1104 curl_easy_getinfo (c, 1105 CURLINFO_RESPONSE_CODE, 1106 &status)) 1107 { 1108 return "Failed to get HTTP status"; 1109 } 1110 *http_status = (unsigned int) status; 1111 curl_easy_cleanup (c); 1112 return NULL; 1113 } 1114 1115 1116 const char * 1117 MHDT_client_send_digest_auth ( 1118 const void *cls, 1119 struct MHDT_PhaseContext *pc) 1120 { 1121 const char *cred = cls; 1122 const char *ret; 1123 unsigned int status; 1124 1125 ret = send_digest_auth (cred, 1126 pc, 1127 &status); 1128 if (NULL != ret) 1129 return ret; 1130 if (MHD_HTTP_STATUS_NO_CONTENT != status) 1131 return "invalid HTTP response code"; 1132 return NULL; 1133 } 1134 1135 1136 const char * 1137 MHDT_client_fail_digest_auth ( 1138 const void *cls, 1139 struct MHDT_PhaseContext *pc) 1140 { 1141 const char *cred = cls; 1142 const char *ret; 1143 unsigned int status; 1144 1145 ret = send_digest_auth (cred, 1146 pc, 1147 &status); 1148 if (NULL != ret) 1149 return ret; 1150 if (MHD_HTTP_STATUS_FORBIDDEN != status) 1151 return "invalid HTTP response code"; 1152 return NULL; 1153 }