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