test_tricky.c (37178B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2014-2025 Evgeny Grin (Karlson2k) 4 Copyright (C) 2007, 2009, 2011 Christian Grothoff 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 2, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 /** 22 * @file test_toolarge.c 23 * @brief Testcase for handling of untypical data. 24 * @author Karlson2k (Evgeny Grin) 25 * @author Christian Grothoff 26 */ 27 #include "MHD_config.h" 28 #include "platform.h" 29 #include <curl/curl.h> 30 #include <microhttpd.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <time.h> 34 #include <errno.h> 35 #include "mhd_has_in_name.h" 36 #include "mhd_has_param.h" 37 #include "mhd_sockets.h" /* only macros used */ 38 39 #ifdef HAVE_STRINGS_H 40 #include <strings.h> 41 #endif /* HAVE_STRINGS_H */ 42 43 #ifdef _WIN32 44 #ifndef WIN32_LEAN_AND_MEAN 45 #define WIN32_LEAN_AND_MEAN 1 46 #endif /* !WIN32_LEAN_AND_MEAN */ 47 #include <windows.h> 48 #endif 49 50 #ifndef WINDOWS 51 #include <unistd.h> 52 #include <sys/socket.h> 53 #endif 54 55 #ifdef HAVE_LIMITS_H 56 #include <limits.h> 57 #endif /* HAVE_LIMITS_H */ 58 59 #ifndef CURL_VERSION_BITS 60 #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) 61 #endif /* ! CURL_VERSION_BITS */ 62 #ifndef CURL_AT_LEAST_VERSION 63 #define CURL_AT_LEAST_VERSION(x,y,z) \ 64 (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) 65 #endif /* ! CURL_AT_LEAST_VERSION */ 66 67 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 68 #undef MHD_CPU_COUNT 69 #endif 70 #if ! defined(MHD_CPU_COUNT) 71 #define MHD_CPU_COUNT 2 72 #endif 73 #if MHD_CPU_COUNT > 32 74 #undef MHD_CPU_COUNT 75 /* Limit to reasonable value */ 76 #define MHD_CPU_COUNT 32 77 #endif /* MHD_CPU_COUNT > 32 */ 78 79 80 #if defined(HAVE___FUNC__) 81 #define externalErrorExit(ignore) \ 82 _externalErrorExit_func(NULL, __func__, __LINE__) 83 #define externalErrorExitDesc(errDesc) \ 84 _externalErrorExit_func(errDesc, __func__, __LINE__) 85 #define libcurlErrorExit(ignore) \ 86 _libcurlErrorExit_func(NULL, __func__, __LINE__) 87 #define libcurlErrorExitDesc(errDesc) \ 88 _libcurlErrorExit_func(errDesc, __func__, __LINE__) 89 #define mhdErrorExit(ignore) \ 90 _mhdErrorExit_func(NULL, __func__, __LINE__) 91 #define mhdErrorExitDesc(errDesc) \ 92 _mhdErrorExit_func(errDesc, __func__, __LINE__) 93 #elif defined(HAVE___FUNCTION__) 94 #define externalErrorExit(ignore) \ 95 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__) 96 #define externalErrorExitDesc(errDesc) \ 97 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__) 98 #define libcurlErrorExit(ignore) \ 99 _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__) 100 #define libcurlErrorExitDesc(errDesc) \ 101 _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__) 102 #define mhdErrorExit(ignore) \ 103 _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__) 104 #define mhdErrorExitDesc(errDesc) \ 105 _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__) 106 #else 107 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__) 108 #define externalErrorExitDesc(errDesc) \ 109 _externalErrorExit_func(errDesc, NULL, __LINE__) 110 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__) 111 #define libcurlErrorExitDesc(errDesc) \ 112 _libcurlErrorExit_func(errDesc, NULL, __LINE__) 113 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__) 114 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__) 115 #endif 116 117 118 _MHD_NORETURN static void 119 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 120 { 121 if ((NULL != errDesc) && (0 != errDesc[0])) 122 fprintf (stderr, "%s", errDesc); 123 else 124 fprintf (stderr, "System or external library call failed"); 125 if ((NULL != funcName) && (0 != funcName[0])) 126 fprintf (stderr, " in %s", funcName); 127 if (0 < lineNum) 128 fprintf (stderr, " at line %d", lineNum); 129 130 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 131 strerror (errno)); 132 #ifdef MHD_WINSOCK_SOCKETS 133 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 134 #endif /* MHD_WINSOCK_SOCKETS */ 135 fflush (stderr); 136 exit (99); 137 } 138 139 140 static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; 141 142 _MHD_NORETURN static void 143 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 144 { 145 if ((NULL != errDesc) && (0 != errDesc[0])) 146 fprintf (stderr, "%s", errDesc); 147 else 148 fprintf (stderr, "CURL library call failed"); 149 if ((NULL != funcName) && (0 != funcName[0])) 150 fprintf (stderr, " in %s", funcName); 151 if (0 < lineNum) 152 fprintf (stderr, " at line %d", lineNum); 153 154 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 155 strerror (errno)); 156 if (0 != libcurl_errbuf[0]) 157 fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf); 158 159 fflush (stderr); 160 exit (99); 161 } 162 163 164 _MHD_NORETURN static void 165 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 166 { 167 if ((NULL != errDesc) && (0 != errDesc[0])) 168 fprintf (stderr, "%s", errDesc); 169 else 170 fprintf (stderr, "MHD unexpected error"); 171 if ((NULL != funcName) && (0 != funcName[0])) 172 fprintf (stderr, " in %s", funcName); 173 if (0 < lineNum) 174 fprintf (stderr, " at line %d", lineNum); 175 176 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 177 strerror (errno)); 178 179 fflush (stderr); 180 exit (8); 181 } 182 183 184 /* Could be increased to facilitate debugging */ 185 #define TIMEOUTS_VAL 5 186 187 #define EXPECTED_URI_BASE_PATH "/a" 188 189 #define EXPECTED_URI_BASE_PATH_TRICKY "/one\rtwo" 190 191 #define URL_SCHEME "http:/" "/" 192 193 #define URL_HOST "127.0.0.1" 194 195 #define URL_SCHEME_HOST URL_SCHEME URL_HOST 196 197 #define HEADER1_NAME "First" 198 #define HEADER1_VALUE "1st" 199 #define HEADER1 HEADER1_NAME ": " HEADER1_VALUE 200 #define HEADER2_NAME "Second" 201 #define HEADER2CR_VALUE "2\rnd" 202 #define HEADER2CR HEADER2_NAME ": " HEADER2CR_VALUE 203 /* Use headers when it would be properly supported by MHD 204 #define HEADER3CR_NAME "Thi\rrd" 205 #define HEADER3CR_VALUE "3r\rd" 206 #define HEADER3CR HEADER3CR_NAME ": " HEADER3CR_VALUE 207 */ 208 #define HEADER4_NAME "Normal" 209 #define HEADER4_VALUE "it's fine" 210 #define HEADER4 HEADER4_NAME ": " HEADER4_VALUE 211 212 /* Global parameters */ 213 static int verbose; /**< Be verbose */ 214 static int oneone; /**< If false use HTTP/1.0 for requests*/ 215 static uint16_t global_port; /**< MHD daemons listen port number */ 216 static int response_timeout_val = TIMEOUTS_VAL; 217 218 static int tricky_url; /**< Tricky request URL */ 219 static int tricky_header2; /**< Tricky request header2 */ 220 221 /* Current test parameters */ 222 /* * Moved to local variables * */ 223 224 /* Static helper variables */ 225 /* * None for this test * */ 226 227 static void 228 test_global_init (void) 229 { 230 libcurl_errbuf[0] = 0; 231 232 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 233 externalErrorExit (); 234 } 235 236 237 static void 238 test_global_cleanup (void) 239 { 240 curl_global_cleanup (); 241 } 242 243 244 struct headers_check_result 245 { 246 int dummy; /* no checks in this test */ 247 }; 248 249 250 static size_t 251 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems, 252 void *userdata) 253 { 254 const size_t data_size = size * nitems; 255 struct headers_check_result *check_res = 256 (struct headers_check_result *) userdata; 257 258 /* no checks in this test */ 259 (void) check_res; (void) buffer; 260 261 return data_size; 262 } 263 264 265 struct lcurl_data_cb_param 266 { 267 char *buf; 268 size_t pos; 269 size_t size; 270 }; 271 272 273 static size_t 274 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 275 { 276 struct lcurl_data_cb_param *cbc = ctx; 277 278 if (cbc->pos + size * nmemb > cbc->size) 279 externalErrorExit (); /* overflow */ 280 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 281 cbc->pos += size * nmemb; 282 return size * nmemb; 283 } 284 285 286 struct check_uri_cls 287 { 288 const char *volatile uri; 289 }; 290 291 static void * 292 check_uri_cb (void *cls, 293 const char *uri, 294 struct MHD_Connection *con) 295 { 296 struct check_uri_cls *param = (struct check_uri_cls *) cls; 297 (void) con; 298 299 if (0 != strcmp (param->uri, 300 uri)) 301 { 302 fprintf (stderr, 303 "Wrong URI: '%s', line: %d\n", 304 uri, __LINE__); 305 exit (22); 306 } 307 return NULL; 308 } 309 310 311 struct mhd_header_checker_param 312 { 313 int found_header1; 314 int found_header2; 315 int found_header4; 316 }; 317 318 static enum MHD_Result 319 headerCheckerInterator (void *cls, 320 enum MHD_ValueKind kind, 321 const char *key, 322 size_t key_size, 323 const char *value, 324 size_t value_size) 325 { 326 struct mhd_header_checker_param *const param = 327 (struct mhd_header_checker_param *) cls; 328 329 if (NULL == param) 330 mhdErrorExitDesc ("cls parameter is NULL"); 331 332 if (MHD_HEADER_KIND != kind) 333 return MHD_YES; /* Continue iteration */ 334 335 if (0 == key_size) 336 mhdErrorExitDesc ("Zero key length"); 337 338 if ((strlen (HEADER1_NAME) == key_size) && 339 (0 == memcmp (key, HEADER1_NAME, key_size))) 340 { 341 if ((strlen (HEADER1_VALUE) == value_size) && 342 (0 == memcmp (value, HEADER1_VALUE, value_size))) 343 param->found_header1 = 1; 344 else 345 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n", 346 (int) value_size, value, HEADER1_VALUE); 347 } 348 else if ((strlen (HEADER2_NAME) == key_size) && 349 (0 == memcmp (key, HEADER2_NAME, key_size))) 350 { 351 if ((strlen (HEADER2CR_VALUE) == value_size) && 352 (0 == memcmp (value, HEADER2CR_VALUE, value_size))) 353 param->found_header2 = 1; 354 else 355 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n", 356 (int) value_size, value, HEADER2CR_VALUE); 357 } 358 else if ((strlen (HEADER4_NAME) == key_size) && 359 (0 == memcmp (key, HEADER4_NAME, key_size))) 360 { 361 if ((strlen (HEADER4_VALUE) == value_size) && 362 (0 == memcmp (value, HEADER4_VALUE, value_size))) 363 param->found_header4 = 1; 364 else 365 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n", 366 (int) value_size, value, HEADER4_VALUE); 367 } 368 return MHD_YES; 369 } 370 371 372 struct ahc_cls_type 373 { 374 const char *volatile rp_data; 375 volatile size_t rp_data_size; 376 struct mhd_header_checker_param header_check_param; 377 const char *volatile rq_method; 378 const char *volatile rq_url; 379 }; 380 381 382 static enum MHD_Result 383 ahcCheck (void *cls, 384 struct MHD_Connection *connection, 385 const char *url, 386 const char *method, 387 const char *version, 388 const char *upload_data, size_t *upload_data_size, 389 void **req_cls) 390 { 391 static int ptr; 392 struct MHD_Response *response; 393 enum MHD_Result ret; 394 struct ahc_cls_type *const param = (struct ahc_cls_type *) cls; 395 396 if (NULL == param) 397 mhdErrorExitDesc ("cls parameter is NULL"); 398 399 if (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) 400 mhdErrorExitDesc ("Unexpected HTTP version"); 401 402 if (0 != strcmp (url, param->rq_url)) 403 mhdErrorExitDesc ("Unexpected URI"); 404 405 if (NULL != upload_data) 406 mhdErrorExitDesc ("'upload_data' is not NULL"); 407 408 if (NULL == upload_data_size) 409 mhdErrorExitDesc ("'upload_data_size' pointer is NULL"); 410 411 if (0 != *upload_data_size) 412 mhdErrorExitDesc ("'*upload_data_size' value is not zero"); 413 414 if (0 != strcmp (param->rq_method, method)) 415 mhdErrorExitDesc ("Unexpected request method"); 416 417 if (&ptr != *req_cls) 418 { 419 *req_cls = &ptr; 420 return MHD_YES; 421 } 422 *req_cls = NULL; 423 424 if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND, 425 &headerCheckerInterator, 426 ¶m->header_check_param)) 427 mhdErrorExitDesc ("Wrong number of headers in the request"); 428 429 response = 430 MHD_create_response_from_buffer_copy (param->rp_data_size, 431 (const void *) param->rp_data); 432 if (NULL == response) 433 mhdErrorExitDesc ("Failed to create response"); 434 435 ret = MHD_queue_response (connection, 436 MHD_HTTP_OK, 437 response); 438 MHD_destroy_response (response); 439 if (MHD_YES != ret) 440 mhdErrorExitDesc ("Failed to queue response"); 441 442 return ret; 443 } 444 445 446 struct curlQueryParams 447 { 448 /* Destination path for CURL query */ 449 const char *queryPath; 450 451 #if CURL_AT_LEAST_VERSION (7, 62, 0) 452 CURLU *url; 453 #endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */ 454 455 #if CURL_AT_LEAST_VERSION (7, 55, 0) 456 /* A string used as the request target directly, without modifications */ 457 const char *queryTarget; 458 #endif /* CURL_AT_LEAST_VERSION(7, 55, 0) */ 459 460 /* Custom query method, NULL for default */ 461 const char *method; 462 463 /* Destination port for CURL query */ 464 uint16_t queryPort; 465 466 /* List of additional request headers */ 467 struct curl_slist *headers; 468 469 /* CURL query result error flag */ 470 volatile unsigned int queryError; 471 472 /* Response HTTP code, zero if no response */ 473 volatile int responseCode; 474 }; 475 476 477 static CURL * 478 curlEasyInitForTest (struct curlQueryParams *p, 479 struct lcurl_data_cb_param *dcbp, 480 struct headers_check_result *hdr_chk_result) 481 { 482 CURL *c; 483 484 c = curl_easy_init (); 485 if (NULL == c) 486 libcurlErrorExitDesc ("curl_easy_init() failed"); 487 488 #if CURL_AT_LEAST_VERSION (7, 62, 0) 489 if (NULL != p->url) 490 { 491 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CURLU, p->url)) 492 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 493 } 494 else /* combined with the next 'if()' */ 495 #endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */ 496 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, p->queryPath)) 497 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 498 499 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 500 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) p->queryPort)) || 501 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 502 ©Buffer)) || 503 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, dcbp)) || 504 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 505 (long) response_timeout_val)) || 506 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 507 (long) response_timeout_val)) || 508 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, 509 libcurl_errbuf)) || 510 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, 511 lcurl_hdr_callback)) || 512 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA, 513 hdr_chk_result)) || 514 #if CURL_AT_LEAST_VERSION (7, 42, 0) 515 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PATH_AS_IS, 516 (long) 1)) || 517 #endif /* CURL_AT_LEAST_VERSION(7, 42, 0) */ 518 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 519 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 520 (oneone) ? 521 CURL_HTTP_VERSION_1_1 : 522 CURL_HTTP_VERSION_1_0))) 523 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 524 525 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, p->method)) 526 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 527 528 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, p->headers)) 529 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 530 531 532 #if CURL_AT_LEAST_VERSION (7, 55, 0) 533 if (NULL != p->queryTarget) 534 { 535 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_REQUEST_TARGET, 536 p->queryTarget)) 537 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 538 } 539 #endif /* CURL_AT_LEAST_VERSION(7, 55, 0) */ 540 541 return c; 542 } 543 544 545 static CURLcode 546 performQueryExternal (struct MHD_Daemon *d, CURL *c) 547 { 548 CURLM *multi; 549 time_t start; 550 struct timeval tv; 551 CURLcode ret; 552 553 ret = CURLE_FAILED_INIT; /* will be replaced with real result */ 554 multi = NULL; 555 multi = curl_multi_init (); 556 if (multi == NULL) 557 libcurlErrorExitDesc ("curl_multi_init() failed"); 558 if (CURLM_OK != curl_multi_add_handle (multi, c)) 559 libcurlErrorExitDesc ("curl_multi_add_handle() failed"); 560 561 start = time (NULL); 562 while (time (NULL) - start <= TIMEOUTS_VAL) 563 { 564 fd_set rs; 565 fd_set ws; 566 fd_set es; 567 MHD_socket maxMhdSk; 568 int maxCurlSk; 569 int running; 570 571 maxMhdSk = MHD_INVALID_SOCKET; 572 maxCurlSk = -1; 573 FD_ZERO (&rs); 574 FD_ZERO (&ws); 575 FD_ZERO (&es); 576 if (NULL != multi) 577 { 578 curl_multi_perform (multi, &running); 579 if (0 == running) 580 { 581 struct CURLMsg *msg; 582 int msgLeft; 583 int totalMsgs = 0; 584 do 585 { 586 msg = curl_multi_info_read (multi, &msgLeft); 587 if (NULL == msg) 588 libcurlErrorExitDesc ("curl_multi_info_read() failed"); 589 totalMsgs++; 590 if (CURLMSG_DONE == msg->msg) 591 ret = msg->data.result; 592 } while (msgLeft > 0); 593 if (1 != totalMsgs) 594 { 595 fprintf (stderr, 596 "curl_multi_info_read returned wrong " 597 "number of results (%d).\n", 598 totalMsgs); 599 externalErrorExit (); 600 } 601 curl_multi_remove_handle (multi, c); 602 curl_multi_cleanup (multi); 603 multi = NULL; 604 } 605 else 606 { 607 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk)) 608 libcurlErrorExitDesc ("curl_multi_fdset() failed"); 609 } 610 } 611 if (NULL == multi) 612 { /* libcurl has finished, check whether MHD still needs to perform cleanup */ 613 if (0 != MHD_get_timeout64s (d)) 614 break; /* MHD finished as well */ 615 } 616 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) 617 mhdErrorExitDesc ("MHD_get_fdset() failed"); 618 tv.tv_sec = 0; 619 tv.tv_usec = 1000; 620 #ifdef MHD_POSIX_SOCKETS 621 if (maxMhdSk > maxCurlSk) 622 maxCurlSk = maxMhdSk; 623 #endif /* MHD_POSIX_SOCKETS */ 624 if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv)) 625 { 626 #ifdef MHD_POSIX_SOCKETS 627 if (EINTR != errno) 628 externalErrorExitDesc ("Unexpected select() error"); 629 #else 630 if ((WSAEINVAL != WSAGetLastError ()) || 631 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 632 externalErrorExitDesc ("Unexpected select() error"); 633 Sleep (1); 634 #endif 635 } 636 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) 637 mhdErrorExitDesc ("MHD_run_from_select() failed"); 638 } 639 640 return ret; 641 } 642 643 644 /* Returns zero for successful response and non-zero for failed response */ 645 static unsigned int 646 doCurlQueryInThread (struct MHD_Daemon *d, 647 struct curlQueryParams *p, 648 struct headers_check_result *hdr_res, 649 const char *expected_data, 650 size_t expected_data_size) 651 { 652 const union MHD_DaemonInfo *dinfo; 653 CURL *c; 654 struct lcurl_data_cb_param dcbp; 655 CURLcode errornum; 656 int use_external_poll; 657 long resp_code; 658 659 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS); 660 if (NULL == dinfo) 661 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 662 use_external_poll = (0 == (dinfo->flags 663 & MHD_USE_INTERNAL_POLLING_THREAD)); 664 665 if (NULL == p->queryPath 666 #if CURL_AT_LEAST_VERSION (7, 62, 0) 667 && NULL == p->url 668 #endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */ 669 ) 670 abort (); 671 672 if (0 == p->queryPort) 673 abort (); 674 675 /* Test must not fail due to test's internal buffer shortage */ 676 dcbp.size = expected_data_size * 2 + 1; 677 dcbp.buf = malloc (dcbp.size); 678 if (NULL == dcbp.buf) 679 externalErrorExit (); 680 dcbp.pos = 0; 681 682 memset (hdr_res, 0, sizeof(*hdr_res)); 683 684 c = curlEasyInitForTest (p, 685 &dcbp, hdr_res); 686 687 if (! use_external_poll) 688 errornum = curl_easy_perform (c); 689 else 690 errornum = performQueryExternal (d, c); 691 692 if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &resp_code)) 693 libcurlErrorExitDesc ("curl_easy_getinfo() failed"); 694 695 p->responseCode = (int) resp_code; 696 if ((CURLE_OK == errornum) && (200 != resp_code)) 697 { 698 fprintf (stderr, 699 "Got reply with unexpected status code: %d\n", 700 p->responseCode); 701 mhdErrorExit (); 702 } 703 704 if (CURLE_OK != errornum) 705 { 706 if ((CURLE_GOT_NOTHING != errornum) && (CURLE_RECV_ERROR != errornum) 707 && (CURLE_HTTP_RETURNED_ERROR != errornum)) 708 { 709 if (CURLE_OPERATION_TIMEDOUT == errornum) 710 mhdErrorExitDesc ("Request was aborted due to timeout"); 711 fprintf (stderr, "libcurl returned unexpected error: %s\n", 712 curl_easy_strerror (errornum)); 713 mhdErrorExitDesc ("Request failed due to unexpected error"); 714 } 715 p->queryError = 1; 716 if ((0 != resp_code) && 717 ((499 < resp_code) || (400 > resp_code))) /* TODO: add all expected error codes */ 718 { 719 fprintf (stderr, 720 "Got reply with unexpected status code: %ld\n", 721 resp_code); 722 mhdErrorExit (); 723 } 724 } 725 else 726 { 727 if (dcbp.pos != expected_data_size) 728 mhdErrorExit ("libcurl reports wrong size of MHD reply body data"); 729 else if (0 != memcmp (expected_data, dcbp.buf, 730 expected_data_size)) 731 mhdErrorExit ("libcurl reports wrong MHD reply body data"); 732 else 733 p->queryError = 0; 734 } 735 736 curl_easy_cleanup (c); 737 free (dcbp.buf); 738 739 return p->queryError; 740 } 741 742 743 /* Perform test queries, shut down MHD daemon, and free parameters */ 744 static unsigned int 745 performTestQueries (struct MHD_Daemon *d, uint16_t d_port, 746 struct ahc_cls_type *ahc_param, 747 struct check_uri_cls *uri_cb_param) 748 { 749 struct curlQueryParams qParam; 750 unsigned int ret = 0; /* Return value */ 751 struct headers_check_result rp_headers_check; 752 struct curl_slist *curl_headers; 753 curl_headers = NULL; 754 755 /* Common parameters, to be individually overridden by specific test cases */ 756 qParam.queryPort = d_port; 757 qParam.method = NULL; /* Use libcurl default: GET */ 758 qParam.queryPath = URL_SCHEME_HOST EXPECTED_URI_BASE_PATH; 759 #if CURL_AT_LEAST_VERSION (7, 55, 0) 760 qParam.queryTarget = NULL; 761 #endif /* CURL_AT_LEAST_VERSION(7, 55, 0) */ 762 #if CURL_AT_LEAST_VERSION (7, 62, 0) 763 qParam.url = NULL; 764 #endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */ 765 qParam.headers = NULL; /* No additional headers */ 766 uri_cb_param->uri = EXPECTED_URI_BASE_PATH; 767 ahc_param->rq_url = EXPECTED_URI_BASE_PATH; 768 ahc_param->rq_method = "GET"; /* Default expected method */ 769 770 ahc_param->rp_data = "~"; 771 ahc_param->rp_data_size = 1; 772 773 curl_headers = curl_slist_append (curl_headers, HEADER1); 774 if (NULL == curl_headers) 775 externalErrorExit (); 776 curl_headers = curl_slist_append (curl_headers, HEADER4); 777 if (NULL == curl_headers) 778 externalErrorExit (); 779 qParam.headers = curl_headers; 780 781 memset (&ahc_param->header_check_param, 0, 782 sizeof (ahc_param->header_check_param)); 783 784 if (tricky_url) 785 { 786 #if CURL_AT_LEAST_VERSION (7, 55, 0) 787 #if CURL_AT_LEAST_VERSION (7, 62, 0) 788 unsigned int urlu_flags = CURLU_PATH_AS_IS; 789 CURLU *url; 790 url = curl_url (); 791 if (NULL == url) 792 externalErrorExit (); 793 qParam.url = url; 794 795 #ifdef CURLU_ALLOW_SPACE 796 urlu_flags |= CURLU_ALLOW_SPACE; 797 #endif /* CURLU_ALLOW_SPACE */ 798 799 if ((CURLUE_OK != curl_url_set (qParam.url, CURLUPART_SCHEME, "http", 0)) || 800 (CURLUE_OK != curl_url_set (qParam.url, CURLUPART_HOST, URL_HOST, 801 urlu_flags)) || 802 (CURLUE_OK != curl_url_set (qParam.url, CURLUPART_PATH, 803 EXPECTED_URI_BASE_PATH_TRICKY, 804 urlu_flags))) 805 libcurlErrorExit (); 806 807 #endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */ 808 809 qParam.queryTarget = EXPECTED_URI_BASE_PATH_TRICKY; 810 uri_cb_param->uri = EXPECTED_URI_BASE_PATH_TRICKY; 811 ahc_param->rq_url = EXPECTED_URI_BASE_PATH_TRICKY; 812 813 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 814 ahc_param->rp_data, 815 ahc_param->rp_data_size)) 816 { 817 /* TODO: Allow fail only if relevant MHD mode set */ 818 if (0 == qParam.responseCode) 819 { 820 fprintf (stderr, "Request failed without any valid response.\n"); 821 ret = 1; 822 } 823 else 824 { 825 if (verbose) 826 printf ("Request failed with %d response code.\n", 827 qParam.responseCode); 828 (void) qParam.responseCode; /* TODO: check for the right response code */ 829 ret = 0; 830 } 831 } 832 else 833 { 834 if (200 != qParam.responseCode) 835 { 836 fprintf (stderr, "Request succeed with wrong response code: %d.\n", 837 qParam.responseCode); 838 ret = 1; 839 } 840 else 841 { 842 ret = 0; 843 if (verbose) 844 printf ("Request succeed.\n"); 845 } 846 847 if (! ahc_param->header_check_param.found_header1) 848 mhdErrorExitDesc ("Required header1 was not detected in request"); 849 if (! ahc_param->header_check_param.found_header4) 850 mhdErrorExitDesc ("Required header4 was not detected in request"); 851 } 852 #if CURL_AT_LEAST_VERSION (7, 62, 0) 853 curl_url_cleanup (url); 854 #endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */ 855 #else 856 fprintf (stderr, "This test requires libcurl version 7.55.0 or newer.\n"); 857 abort (); 858 #endif /* CURL_AT_LEAST_VERSION(7, 55, 0) */ 859 } 860 else if (tricky_header2) 861 { 862 /* Reset libcurl headers */ 863 qParam.headers = NULL; 864 curl_slist_free_all (curl_headers); 865 curl_headers = NULL; 866 867 /* Set special libcurl headers */ 868 curl_headers = curl_slist_append (curl_headers, HEADER1); 869 if (NULL == curl_headers) 870 externalErrorExit (); 871 curl_headers = curl_slist_append (curl_headers, HEADER2CR); 872 if (NULL == curl_headers) 873 externalErrorExit (); 874 curl_headers = curl_slist_append (curl_headers, HEADER4); 875 if (NULL == curl_headers) 876 externalErrorExit (); 877 qParam.headers = curl_headers; 878 879 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 880 ahc_param->rp_data, 881 ahc_param->rp_data_size)) 882 { 883 /* TODO: Allow fail only if relevant MHD mode set */ 884 if (0 == qParam.responseCode) 885 { 886 fprintf (stderr, "Request failed without any valid response.\n"); 887 ret = 1; 888 } 889 else 890 { 891 if (verbose) 892 printf ("Request failed with %d response code.\n", 893 qParam.responseCode); 894 (void) qParam.responseCode; /* TODO: check for the right response code */ 895 ret = 0; 896 } 897 } 898 else 899 { 900 if (200 != qParam.responseCode) 901 { 902 fprintf (stderr, "Request succeed with wrong response code: %d.\n", 903 qParam.responseCode); 904 ret = 1; 905 } 906 else 907 { 908 ret = 0; 909 if (verbose) 910 printf ("Request succeed.\n"); 911 } 912 913 if (! ahc_param->header_check_param.found_header1) 914 mhdErrorExitDesc ("Required header1 was not detected in request"); 915 if (! ahc_param->header_check_param.found_header2) 916 mhdErrorExitDesc ("Required header2 was not detected in request"); 917 if (! ahc_param->header_check_param.found_header4) 918 mhdErrorExitDesc ("Required header4 was not detected in request"); 919 } 920 } 921 else 922 externalErrorExitDesc ("No valid test test was selected"); 923 924 MHD_stop_daemon (d); 925 curl_slist_free_all (curl_headers); 926 free (uri_cb_param); 927 free (ahc_param); 928 929 return ret; 930 } 931 932 933 enum testMhdThreadsType 934 { 935 testMhdThreadExternal = 0, 936 testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD, 937 testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION 938 | MHD_USE_INTERNAL_POLLING_THREAD, 939 testMhdThreadInternalPool 940 }; 941 942 enum testMhdPollType 943 { 944 testMhdPollBySelect = 0, 945 testMhdPollByPoll = MHD_USE_POLL, 946 testMhdPollByEpoll = MHD_USE_EPOLL, 947 testMhdPollAuto = MHD_USE_AUTO 948 }; 949 950 /* Get number of threads for thread pool depending 951 * on used poll function and test type. */ 952 static unsigned int 953 testNumThreadsForPool (enum testMhdPollType pollType) 954 { 955 unsigned int numThreads = MHD_CPU_COUNT; 956 (void) pollType; /* Don't care about pollType for this test */ 957 return numThreads; /* No practical limit for non-cleanup test */ 958 } 959 960 961 static struct MHD_Daemon * 962 startTestMhdDaemon (enum testMhdThreadsType thrType, 963 enum testMhdPollType pollType, uint16_t *pport, 964 struct ahc_cls_type **ahc_param, 965 struct check_uri_cls **uri_cb_param) 966 { 967 struct MHD_Daemon *d; 968 const union MHD_DaemonInfo *dinfo; 969 970 if ((NULL == ahc_param) || (NULL == uri_cb_param)) 971 abort (); 972 973 *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type)); 974 if (NULL == *ahc_param) 975 externalErrorExit (); 976 *uri_cb_param = 977 (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls)); 978 if (NULL == *uri_cb_param) 979 externalErrorExit (); 980 981 if ( (0 == *pport) && 982 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) 983 { 984 *pport = 4150; 985 if (tricky_url) 986 *pport += 1; 987 if (tricky_header2) 988 *pport += 2; 989 if (! oneone) 990 *pport += 16; 991 } 992 993 if (testMhdThreadExternal == thrType) 994 d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) 995 | (verbose ? MHD_USE_ERROR_LOG : 0) 996 | MHD_USE_NO_THREAD_SAFETY, 997 *pport, NULL, NULL, 998 &ahcCheck, *ahc_param, 999 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 1000 *uri_cb_param, 1001 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 1002 MHD_OPTION_END); 1003 else if (testMhdThreadInternalPool != thrType) 1004 d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) 1005 | (verbose ? MHD_USE_ERROR_LOG : 0), 1006 *pport, NULL, NULL, 1007 &ahcCheck, *ahc_param, 1008 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 1009 *uri_cb_param, 1010 MHD_OPTION_END); 1011 else 1012 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD 1013 | ((unsigned int) pollType) 1014 | (verbose ? MHD_USE_ERROR_LOG : 0), 1015 *pport, NULL, NULL, 1016 &ahcCheck, *ahc_param, 1017 MHD_OPTION_THREAD_POOL_SIZE, 1018 testNumThreadsForPool (pollType), 1019 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 1020 *uri_cb_param, 1021 MHD_OPTION_END); 1022 1023 if (NULL == d) 1024 { 1025 fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno); 1026 abort (); 1027 } 1028 1029 if (0 == *pport) 1030 { 1031 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 1032 if ((NULL == dinfo) || (0 == dinfo->port) ) 1033 { 1034 fprintf (stderr, "MHD_get_daemon_info() failed.\n"); 1035 abort (); 1036 } 1037 *pport = dinfo->port; 1038 if (0 == global_port) 1039 global_port = *pport; /* Reuse the same port for all tests */ 1040 } 1041 1042 return d; 1043 } 1044 1045 1046 /* Test runners */ 1047 1048 1049 static unsigned int 1050 testExternalGet (void) 1051 { 1052 struct MHD_Daemon *d; 1053 uint16_t d_port = global_port; /* Daemon's port */ 1054 struct ahc_cls_type *ahc_param; 1055 struct check_uri_cls *uri_cb_param; 1056 1057 d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port, 1058 &ahc_param, &uri_cb_param); 1059 1060 return performTestQueries (d, d_port, ahc_param, uri_cb_param); 1061 } 1062 1063 1064 static unsigned int 1065 testInternalGet (enum testMhdPollType pollType) 1066 { 1067 struct MHD_Daemon *d; 1068 uint16_t d_port = global_port; /* Daemon's port */ 1069 struct ahc_cls_type *ahc_param; 1070 struct check_uri_cls *uri_cb_param; 1071 1072 d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port, 1073 &ahc_param, &uri_cb_param); 1074 1075 return performTestQueries (d, d_port, ahc_param, uri_cb_param); 1076 } 1077 1078 1079 static unsigned int 1080 testMultithreadedGet (enum testMhdPollType pollType) 1081 { 1082 struct MHD_Daemon *d; 1083 uint16_t d_port = global_port; /* Daemon's port */ 1084 struct ahc_cls_type *ahc_param; 1085 struct check_uri_cls *uri_cb_param; 1086 1087 d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port, 1088 &ahc_param, &uri_cb_param); 1089 return performTestQueries (d, d_port, ahc_param, uri_cb_param); 1090 } 1091 1092 1093 static unsigned int 1094 testMultithreadedPoolGet (enum testMhdPollType pollType) 1095 { 1096 struct MHD_Daemon *d; 1097 uint16_t d_port = global_port; /* Daemon's port */ 1098 struct ahc_cls_type *ahc_param; 1099 struct check_uri_cls *uri_cb_param; 1100 1101 d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port, 1102 &ahc_param, &uri_cb_param); 1103 return performTestQueries (d, d_port, ahc_param, uri_cb_param); 1104 } 1105 1106 1107 static void 1108 check_test_can_be_used (void) 1109 { 1110 #if ! CURL_AT_LEAST_VERSION (7, 55, 0) 1111 if (tricky_url) 1112 { 1113 fprintf (stderr, "This test requires libcurl version 7.55.0 or newer.\n"); 1114 exit (77); 1115 } 1116 #endif /* ! CURL_AT_LEAST_VERSION(7, 55, 0) */ 1117 return; 1118 } 1119 1120 1121 int 1122 main (int argc, char *const *argv) 1123 { 1124 unsigned int errorCount = 0; 1125 unsigned int test_result = 0; 1126 verbose = 0; 1127 1128 if ((NULL == argv) || (0 == argv[0])) 1129 return 99; 1130 oneone = ! has_in_name (argv[0], "10"); 1131 tricky_url = has_in_name (argv[0], "_url") ? 1 : 0; 1132 tricky_header2 = has_in_name (argv[0], "_header2") ? 1 : 0; 1133 if (1 != tricky_url + tricky_header2) 1134 return 99; 1135 verbose = ! (has_param (argc, argv, "-q") || 1136 has_param (argc, argv, "--quiet") || 1137 has_param (argc, argv, "-s") || 1138 has_param (argc, argv, "--silent")); 1139 1140 check_test_can_be_used (); 1141 1142 test_global_init (); 1143 1144 /* Could be set to non-zero value to enforce using specific port 1145 * in the test */ 1146 global_port = 0; 1147 test_result = testExternalGet (); 1148 if (test_result) 1149 fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result); 1150 else if (verbose) 1151 printf ("PASSED: testExternalGet ().\n"); 1152 errorCount += test_result; 1153 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 1154 { 1155 test_result = testInternalGet (testMhdPollAuto); 1156 if (test_result) 1157 fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto) - %u.\n", 1158 test_result); 1159 else if (verbose) 1160 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 1161 errorCount += test_result; 1162 #ifdef _MHD_HEAVY_TESTS 1163 /* Actually tests are not heavy, but took too long to complete while 1164 * not really provide any additional results. */ 1165 test_result = testInternalGet (testMhdPollBySelect); 1166 if (test_result) 1167 fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n", 1168 test_result); 1169 else if (verbose) 1170 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 1171 errorCount += test_result; 1172 test_result = testMultithreadedPoolGet (testMhdPollBySelect); 1173 if (test_result) 1174 fprintf (stderr, 1175 "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n", 1176 test_result); 1177 else if (verbose) 1178 printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n"); 1179 errorCount += test_result; 1180 test_result = testMultithreadedGet (testMhdPollBySelect); 1181 if (test_result) 1182 fprintf (stderr, 1183 "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n", 1184 test_result); 1185 else if (verbose) 1186 printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n"); 1187 errorCount += test_result; 1188 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) 1189 { 1190 test_result = testInternalGet (testMhdPollByPoll); 1191 if (test_result) 1192 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n", 1193 test_result); 1194 else if (verbose) 1195 printf ("PASSED: testInternalGet (testMhdPollByPoll).\n"); 1196 errorCount += test_result; 1197 } 1198 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 1199 { 1200 test_result = testInternalGet (testMhdPollByEpoll); 1201 if (test_result) 1202 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n", 1203 test_result); 1204 else if (verbose) 1205 printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n"); 1206 errorCount += test_result; 1207 } 1208 #else 1209 /* Mute compiler warnings */ 1210 (void) testMultithreadedGet; 1211 (void) testMultithreadedPoolGet; 1212 #endif /* _MHD_HEAVY_TESTS */ 1213 } 1214 if (0 != errorCount) 1215 fprintf (stderr, 1216 "Error (code: %u)\n", 1217 errorCount); 1218 else if (verbose) 1219 printf ("All tests passed.\n"); 1220 1221 test_global_cleanup (); 1222 1223 return (errorCount == 0) ? 0 : 1; /* 0 == pass */ 1224 }