test_add_conn.c (37661B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2009, 2011 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) - large rework, 5 multithreading. 6 7 libmicrohttpd is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published 9 by the Free Software Foundation; either version 2, or (at your 10 option) any later version. 11 12 libmicrohttpd is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with libmicrohttpd; see the file COPYING. If not, write to the 19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 Boston, MA 02110-1301, USA. 21 */ 22 /** 23 * @file test_add_conn.c 24 * @brief Testcase for libmicrohttpd GET operations 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 #include "MHD_config.h" 29 #include "platform.h" 30 #include <curl/curl.h> 31 #include <microhttpd.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <time.h> 35 #include <errno.h> 36 #include "mhd_has_in_name.h" 37 #include "mhd_has_param.h" 38 #include "mhd_sockets.h" /* only macros used */ 39 40 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 41 test into a marked, classifiable test error (TESTING.md, P5). */ 42 #include "mhd_panic_tripwire.h" 43 44 45 #ifdef _WIN32 46 #ifndef WIN32_LEAN_AND_MEAN 47 #define WIN32_LEAN_AND_MEAN 1 48 #endif /* !WIN32_LEAN_AND_MEAN */ 49 #include <windows.h> 50 #endif 51 52 #ifndef WINDOWS 53 #include <unistd.h> 54 #include <sys/socket.h> 55 #endif 56 57 #ifdef HAVE_LIMITS_H 58 #include <limits.h> 59 #endif /* HAVE_LIMITS_H */ 60 61 #ifdef HAVE_PTHREAD_H 62 #include <pthread.h> 63 #endif /* HAVE_PTHREAD_H */ 64 65 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 66 #undef MHD_CPU_COUNT 67 #endif 68 #if ! defined(MHD_CPU_COUNT) 69 #define MHD_CPU_COUNT 2 70 #endif 71 #if MHD_CPU_COUNT > 32 72 #undef MHD_CPU_COUNT 73 /* Limit to reasonable value */ 74 #define MHD_CPU_COUNT 32 75 #endif /* MHD_CPU_COUNT > 32 */ 76 77 /* Could be increased to facilitate debugging */ 78 #define TIMEOUTS_VAL 5 79 80 /* Number of requests per daemon in cleanup test, 81 * the number must be more than one as the first connection 82 * will be processed and the rest will stay in the list of unprocessed */ 83 #define CLEANUP_NUM_REQS_PER_DAEMON 6 84 85 /* Cleanup test: max number of concurrent daemons depending on maximum number 86 * of open FDs. */ 87 #define CLEANUP_MAX_DAEMONS(max_fds) (unsigned int) \ 88 ( ((max_fds) < 10) ? \ 89 0 : ( (((max_fds) - 10) / (CLEANUP_NUM_REQS_PER_DAEMON * 5 + 3)) ) ) 90 91 #define EXPECTED_URI_BASE_PATH "/hello_world" 92 #define EXPECTED_URI_QUERY "a=%26&b=c" 93 #define EXPECTED_URI_FULL_PATH EXPECTED_URI_BASE_PATH "?" EXPECTED_URI_QUERY 94 95 /* Global parameters */ 96 static int oneone; /**< Use HTTP/1.1 instead of HTTP/1.0 */ 97 static int no_listen; /**< Start MHD daemons without listen socket */ 98 static uint16_t global_port; /**< MHD daemons listen port number */ 99 static int cleanup_test; /**< Test for final cleanup */ 100 static int slow_reply = 0; /**< Slowdown MHD replies */ 101 static int ignore_response_errors = 0; /**< Do not fail test if CURL 102 returns error */ 103 static int response_timeout_val = TIMEOUTS_VAL; 104 static int sys_max_fds; /**< Current system limit for number of open 105 files. */ 106 107 108 struct CBC 109 { 110 char *buf; 111 size_t pos; 112 size_t size; 113 }; 114 115 116 static size_t 117 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 118 { 119 struct CBC *cbc = ctx; 120 121 if (cbc->pos + size * nmemb > cbc->size) 122 return 0; /* overflow */ 123 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 124 cbc->pos += size * nmemb; 125 return size * nmemb; 126 } 127 128 129 static void * 130 log_cb (void *cls, 131 const char *uri, 132 struct MHD_Connection *con) 133 { 134 (void) cls; 135 (void) con; 136 if (0 != strcmp (uri, 137 EXPECTED_URI_FULL_PATH)) 138 { 139 fprintf (stderr, 140 "Wrong URI: `%s'\n", 141 uri); 142 _exit (22); 143 } 144 return NULL; 145 } 146 147 148 static enum MHD_Result 149 ahc_echo (void *cls, 150 struct MHD_Connection *connection, 151 const char *url, 152 const char *method, 153 const char *version, 154 const char *upload_data, size_t *upload_data_size, 155 void **req_cls) 156 { 157 static int ptr; 158 struct MHD_Response *response; 159 enum MHD_Result ret; 160 const char *v; 161 (void) cls; 162 (void) version; 163 (void) upload_data; 164 (void) upload_data_size; /* Unused. Silence compiler warning. */ 165 166 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 167 return MHD_NO; /* unexpected method */ 168 if (&ptr != *req_cls) 169 { 170 *req_cls = &ptr; 171 return MHD_YES; 172 } 173 *req_cls = NULL; 174 v = MHD_lookup_connection_value (connection, 175 MHD_GET_ARGUMENT_KIND, 176 "a"); 177 if ( (NULL == v) || 178 (0 != strcmp ("&", 179 v)) ) 180 { 181 fprintf (stderr, "Found while looking for 'a=&': 'a=%s'\n", 182 NULL == v ? "NULL" : v); 183 _exit (17); 184 } 185 v = NULL; 186 if (MHD_YES != MHD_lookup_connection_value_n (connection, 187 MHD_GET_ARGUMENT_KIND, 188 "b", 189 1, 190 &v, 191 NULL)) 192 { 193 fprintf (stderr, "Not found 'b' GET argument.\n"); 194 _exit (18); 195 } 196 if ( (NULL == v) || 197 (0 != strcmp ("c", 198 v)) ) 199 { 200 fprintf (stderr, "Found while looking for 'b=c': 'b=%s'\n", 201 NULL == v ? "NULL" : v); 202 _exit (19); 203 } 204 if (slow_reply) 205 usleep (200000); 206 207 response = MHD_create_response_from_buffer_copy (strlen (url), 208 (const void *) url); 209 ret = MHD_queue_response (connection, 210 MHD_HTTP_OK, 211 response); 212 MHD_destroy_response (response); 213 if (ret == MHD_NO) 214 { 215 fprintf (stderr, "Failed to queue response.\n"); 216 _exit (19); 217 } 218 return ret; 219 } 220 221 222 _MHD_NORETURN static void 223 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 224 { 225 if ((NULL != errDesc) && (0 != errDesc[0])) 226 fprintf (stderr, "%s", errDesc); 227 else 228 fprintf (stderr, "System or external library call failed"); 229 if ((NULL != funcName) && (0 != funcName[0])) 230 fprintf (stderr, " in %s", funcName); 231 if (0 < lineNum) 232 fprintf (stderr, " at line %d", lineNum); 233 234 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 235 strerror (errno)); 236 #ifdef MHD_WINSOCK_SOCKETS 237 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 238 #endif /* MHD_WINSOCK_SOCKETS */ 239 fflush (stderr); 240 _exit (99); 241 } 242 243 244 #if defined(HAVE___FUNC__) 245 #define externalErrorExit(ignore) \ 246 _externalErrorExit_func (NULL, __func__, __LINE__) 247 #define externalErrorExitDesc(errDesc) \ 248 _externalErrorExit_func (errDesc, __func__, __LINE__) 249 #elif defined(HAVE___FUNCTION__) 250 #define externalErrorExit(ignore) \ 251 _externalErrorExit_func (NULL, __FUNCTION__, __LINE__) 252 #define externalErrorExitDesc(errDesc) \ 253 _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__) 254 #else 255 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__) 256 #define externalErrorExitDesc(errDesc) \ 257 _externalErrorExit_func (errDesc, NULL, __LINE__) 258 #endif 259 260 261 /* Static const value, indicates that result value was not set yet */ 262 static const unsigned int eMarker = 0xCE; 263 264 265 static MHD_socket 266 createListeningSocket (uint16_t *pport) 267 { 268 MHD_socket skt; 269 struct sockaddr_in sin; 270 socklen_t sin_len; 271 #ifdef MHD_POSIX_SOCKETS 272 static int on = 1; 273 #endif /* MHD_POSIX_SOCKETS */ 274 275 skt = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); 276 if (MHD_INVALID_SOCKET == skt) 277 externalErrorExitDesc ("socket() failed"); 278 279 #ifdef MHD_POSIX_SOCKETS 280 setsockopt (skt, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof (on)); 281 /* Ignore possible error */ 282 #endif /* MHD_POSIX_SOCKETS */ 283 284 memset (&sin, 0, sizeof(sin)); 285 sin.sin_family = AF_INET; 286 sin.sin_port = htons (*pport); 287 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 288 if (0 != bind (skt, (struct sockaddr *) &sin, sizeof(sin))) 289 externalErrorExitDesc ("bind() failed"); 290 291 if (0 != listen (skt, SOMAXCONN)) 292 externalErrorExitDesc ("listen() failed"); 293 294 if (0 == *pport) 295 { 296 memset (&sin, 0, sizeof(sin)); 297 sin_len = (socklen_t) sizeof(sin); 298 if (0 != getsockname (skt, (struct sockaddr *) &sin, &sin_len)) 299 externalErrorExitDesc ("getsockname() failed"); 300 301 if (sizeof(sin) < (size_t) sin_len) 302 externalErrorExitDesc ("getsockname() failed"); 303 304 if (AF_INET != sin.sin_family) 305 externalErrorExitDesc ("getsockname() returned wrong socket family"); 306 307 *pport = ntohs (sin.sin_port); 308 } 309 310 return skt; 311 } 312 313 314 static MHD_socket 315 acceptTimeLimited (MHD_socket lstn_sk, struct sockaddr *paddr, 316 socklen_t *paddr_len) 317 { 318 fd_set rs; 319 struct timeval timeoutval; 320 MHD_socket accepted; 321 322 FD_ZERO (&rs); 323 FD_SET (lstn_sk, &rs); 324 timeoutval.tv_sec = TIMEOUTS_VAL; 325 timeoutval.tv_usec = 0; 326 if (1 != select (((int) lstn_sk) + 1, &rs, NULL, NULL, &timeoutval)) 327 externalErrorExitDesc ("select() failed"); 328 329 accepted = accept (lstn_sk, paddr, paddr_len); 330 if (MHD_INVALID_SOCKET == accepted) 331 externalErrorExitDesc ("accept() failed"); 332 333 return accepted; 334 } 335 336 337 struct addConnParam 338 { 339 struct MHD_Daemon *d; 340 341 MHD_socket lstn_sk; 342 343 MHD_socket clent_sk; 344 /* Non-zero indicate error */ 345 volatile unsigned int result; 346 347 #ifdef HAVE_PTHREAD_H 348 pthread_t addConnThread; 349 #endif /* HAVE_PTHREAD_H */ 350 }; 351 352 static unsigned int 353 doAcceptAndAddConnInThread (struct addConnParam *p) 354 { 355 struct sockaddr addr; 356 socklen_t addr_len = sizeof(addr); 357 358 p->clent_sk = acceptTimeLimited (p->lstn_sk, &addr, &addr_len); 359 360 p->result = (MHD_YES == MHD_add_connection (p->d, p->clent_sk, 361 &addr, addr_len)) ? 362 0 : 1; 363 if (p->result) 364 fprintf (stderr, "MHD_add_connection() failed, errno=%d.\n", errno); 365 return p->result; 366 } 367 368 369 #ifdef HAVE_PTHREAD_H 370 static void * 371 doAcceptAndAddConn (void *param) 372 { 373 struct addConnParam *p = param; 374 375 (void) doAcceptAndAddConnInThread (p); 376 377 return (void *) p; 378 } 379 380 381 static void 382 startThreadAddConn (struct addConnParam *param) 383 { 384 /* thread must reset this value to zero if succeed */ 385 param->result = eMarker; 386 387 if (0 != pthread_create (¶m->addConnThread, NULL, &doAcceptAndAddConn, 388 (void *) param)) 389 externalErrorExitDesc ("pthread_create() failed"); 390 } 391 392 393 static unsigned int 394 finishThreadAddConn (struct addConnParam *param) 395 { 396 struct addConnParam *result; 397 398 if (0 != pthread_join (param->addConnThread, (void **) &result)) 399 externalErrorExitDesc ("pthread_join() failed"); 400 401 if (param != result) 402 abort (); /* Test used in a wrong way */ 403 404 if (eMarker == param->result) 405 abort (); /* Test used in a wrong way */ 406 407 return result->result; 408 } 409 410 411 #endif /* HAVE_PTHREAD_H */ 412 413 414 struct curlQueryParams 415 { 416 /* Destination path for CURL query */ 417 const char *queryPath; 418 419 /* Destination port for CURL query */ 420 uint16_t queryPort; 421 422 /* CURL query result error flag */ 423 volatile unsigned int queryError; 424 425 #ifdef HAVE_PTHREAD_H 426 pthread_t queryThread; 427 #endif /* HAVE_PTHREAD_H */ 428 }; 429 430 static CURL * 431 curlEasyInitForTest (const char *queryPath, uint16_t port, struct CBC *pcbc) 432 { 433 CURL *c; 434 435 c = curl_easy_init (); 436 if (NULL == c) 437 { 438 fprintf (stderr, "curl_easy_init() failed.\n"); 439 _exit (99); 440 } 441 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 442 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, queryPath)) || 443 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) || 444 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 445 ©Buffer)) || 446 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, pcbc)) || 447 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 448 (long) response_timeout_val)) || 449 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 450 (long) response_timeout_val)) || 451 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 452 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 453 (oneone) ? 454 CURL_HTTP_VERSION_1_1 : 455 CURL_HTTP_VERSION_1_0))) 456 { 457 fprintf (stderr, "curl_easy_setopt() failed.\n"); 458 _exit (99); 459 } 460 461 return c; 462 } 463 464 465 static unsigned int 466 doCurlQueryInThread (struct curlQueryParams *p) 467 { 468 CURL *c; 469 char buf[2048]; 470 struct CBC cbc; 471 CURLcode errornum; 472 473 if (NULL == p->queryPath) 474 abort (); 475 476 if (0 == p->queryPort) 477 abort (); 478 479 cbc.buf = buf; 480 cbc.size = sizeof(buf); 481 cbc.pos = 0; 482 483 c = curlEasyInitForTest (p->queryPath, p->queryPort, &cbc); 484 485 errornum = curl_easy_perform (c); 486 if (ignore_response_errors) 487 { 488 p->queryError = 0; 489 curl_easy_cleanup (c); 490 491 return p->queryError; 492 } 493 if (CURLE_OK != errornum) 494 { 495 fprintf (stderr, 496 "curl_easy_perform failed: `%s'\n", 497 curl_easy_strerror (errornum)); 498 p->queryError = 2; 499 } 500 else 501 { 502 if (cbc.pos != strlen (EXPECTED_URI_BASE_PATH)) 503 { 504 fprintf (stderr, "curl reports wrong size of MHD reply body data.\n"); 505 p->queryError = 4; 506 } 507 else if (0 != strncmp (EXPECTED_URI_BASE_PATH, cbc.buf, 508 strlen (EXPECTED_URI_BASE_PATH))) 509 { 510 fprintf (stderr, "curl reports wrong MHD reply body data.\n"); 511 p->queryError = 4; 512 } 513 else 514 p->queryError = 0; 515 } 516 curl_easy_cleanup (c); 517 518 return p->queryError; 519 } 520 521 522 #ifdef HAVE_PTHREAD_H 523 static void * 524 doCurlQuery (void *param) 525 { 526 struct curlQueryParams *p = (struct curlQueryParams *) param; 527 528 (void) doCurlQueryInThread (p); 529 530 return param; 531 } 532 533 534 static void 535 startThreadCurlQuery (struct curlQueryParams *param) 536 { 537 /* thread must reset this value to zero if succeed */ 538 param->queryError = eMarker; 539 540 if (0 != pthread_create (¶m->queryThread, NULL, &doCurlQuery, 541 (void *) param)) 542 externalErrorExitDesc ("pthread_create() failed"); 543 } 544 545 546 static unsigned int 547 finishThreadCurlQuery (struct curlQueryParams *param) 548 { 549 struct curlQueryParams *result; 550 551 if (0 != pthread_join (param->queryThread, (void **) &result)) 552 externalErrorExitDesc ("pthread_join() failed"); 553 554 if (param != result) 555 abort (); /* Test used in wrong way */ 556 557 if (eMarker == param->queryError) 558 abort (); /* Test used in wrong way */ 559 560 return result->queryError; 561 } 562 563 564 /* Perform test queries and shut down MHD daemon */ 565 static unsigned int 566 performTestQueries (struct MHD_Daemon *d, uint16_t d_port) 567 { 568 struct curlQueryParams qParam; 569 struct addConnParam aParam; 570 uint16_t a_port; /* Additional listening socket port */ 571 unsigned int ret = 0; /* Return value */ 572 573 qParam.queryPath = "http://127.0.0.1" EXPECTED_URI_FULL_PATH; 574 a_port = 0; /* auto-assign */ 575 576 aParam.d = d; 577 aParam.lstn_sk = createListeningSocket (&a_port); /* Sets a_port */ 578 579 /* Test of adding connection in the same thread */ 580 qParam.queryError = eMarker; /* to be zeroed in new thread */ 581 qParam.queryPort = a_port; /* Connect to additional socket */ 582 startThreadCurlQuery (&qParam); 583 ret |= doAcceptAndAddConnInThread (&aParam); 584 ret |= finishThreadCurlQuery (&qParam); 585 586 if (! no_listen) 587 { 588 /* Test of the daemon itself can accept and process new connection. */ 589 ret <<= 3; /* Remember errors for each step */ 590 qParam.queryPort = d_port; /* Connect to the daemon */ 591 ret |= doCurlQueryInThread (&qParam); 592 } 593 594 /* Test of adding connection in an external thread */ 595 ret <<= 3; /* Remember errors for each step */ 596 aParam.result = eMarker; /* to be zeroed in new thread */ 597 qParam.queryPort = a_port; /* Connect to the daemon */ 598 startThreadAddConn (&aParam); 599 ret |= doCurlQueryInThread (&qParam); 600 ret |= finishThreadAddConn (&aParam); 601 602 (void) MHD_socket_close_ (aParam.lstn_sk); 603 MHD_stop_daemon (d); 604 605 return ret; 606 } 607 608 609 /* Perform test for cleanup and shutdown MHD daemon */ 610 static unsigned int 611 performTestCleanup (struct MHD_Daemon *d, unsigned int num_queries) 612 { 613 struct curlQueryParams *qParamList; 614 struct addConnParam aParam; 615 MHD_socket lstn_sk; /* Additional listening socket */ 616 MHD_socket *clntSkList; 617 uint16_t a_port; /* Additional listening socket port */ 618 unsigned int i; 619 unsigned int ret = 0; /* Return value */ 620 621 a_port = 0; /* auto-assign */ 622 623 if (0 >= num_queries) 624 abort (); /* Test's API violation */ 625 626 lstn_sk = createListeningSocket (&a_port); /* Sets a_port */ 627 628 qParamList = malloc (sizeof(struct curlQueryParams) * num_queries); 629 clntSkList = malloc (sizeof(MHD_socket) * num_queries); 630 if ((NULL == qParamList) || (NULL == clntSkList)) 631 externalErrorExitDesc ("malloc failed"); 632 633 /* Start CURL queries */ 634 for (i = 0; i < num_queries; i++) 635 { 636 qParamList[i].queryPath = "http://127.0.0.1" EXPECTED_URI_FULL_PATH; 637 qParamList[i].queryError = 0; 638 qParamList[i].queryPort = a_port; 639 640 startThreadCurlQuery (qParamList + i); 641 } 642 643 /* Accept and add required number of client sockets */ 644 aParam.d = d; 645 aParam.lstn_sk = lstn_sk; 646 for (i = 0; i < num_queries; i++) 647 { 648 aParam.clent_sk = MHD_INVALID_SOCKET; 649 ret |= doAcceptAndAddConnInThread (&aParam); 650 clntSkList[i] = aParam.clent_sk; 651 } 652 653 /* Stop daemon while some of new connection are not yet 654 * processed because of slow response to the first queries. */ 655 MHD_stop_daemon (d); 656 (void) MHD_socket_close_ (aParam.lstn_sk); 657 658 /* Check whether all client sockets were closed by MHD. 659 * Closure of socket by MHD indicate valid cleanup performed. */ 660 for (i = 0; i < num_queries; i++) 661 { 662 if (MHD_INVALID_SOCKET != clntSkList[i]) 663 { /* Check whether socket could be closed one more time. */ 664 if (MHD_socket_close_ (clntSkList[i])) 665 { 666 ret |= 2; 667 fprintf (stderr, "Client socket was not closed by MHD during" \ 668 "cleanup process.\n"); 669 } 670 } 671 } 672 673 /* Wait for CURL threads to complete. */ 674 /* Ignore soft CURL errors as many connection shouldn't get any response. 675 * Hard failures are detected in processing function. */ 676 for (i = 0; i < num_queries; i++) 677 (void) finishThreadCurlQuery (qParamList + i); 678 679 free (clntSkList); 680 free (qParamList); 681 682 return ret; 683 } 684 685 686 #endif /* HAVE_PTHREAD_H */ 687 688 enum testMhdThreadsType 689 { 690 testMhdThreadExternal = 0, 691 testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD, 692 testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION 693 | MHD_USE_INTERNAL_POLLING_THREAD, 694 testMhdThreadInternalPool 695 }; 696 697 enum testMhdPollType 698 { 699 testMhdPollBySelect = 0, 700 testMhdPollByPoll = MHD_USE_POLL, 701 testMhdPollByEpoll = MHD_USE_EPOLL, 702 testMhdPollAuto = MHD_USE_AUTO 703 }; 704 705 /* Get number of threads for thread pool depending 706 * on used poll function and test type. */ 707 static unsigned int 708 testNumThreadsForPool (enum testMhdPollType pollType) 709 { 710 unsigned int numThreads = MHD_CPU_COUNT; 711 if (! cleanup_test) 712 return numThreads; /* No practical limit for non-cleanup test */ 713 if (CLEANUP_MAX_DAEMONS (sys_max_fds) < numThreads) 714 numThreads = CLEANUP_MAX_DAEMONS (sys_max_fds); 715 if ((testMhdPollBySelect == pollType) && 716 (CLEANUP_MAX_DAEMONS (FD_SETSIZE) < numThreads)) 717 numThreads = CLEANUP_MAX_DAEMONS (FD_SETSIZE); 718 719 if (2 > numThreads) 720 abort (); 721 return (unsigned int) numThreads; 722 } 723 724 725 static struct MHD_Daemon * 726 startTestMhdDaemon (enum testMhdThreadsType thrType, 727 enum testMhdPollType pollType, uint16_t *pport) 728 { 729 struct MHD_Daemon *d; 730 const union MHD_DaemonInfo *dinfo; 731 732 if ( (0 == *pport) && 733 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) 734 { 735 *pport = 1550; 736 if (oneone) 737 *pport += 1; 738 if (no_listen) 739 *pport += 2; 740 if (cleanup_test) 741 *pport += 4; 742 } 743 744 switch (thrType) 745 { 746 case testMhdThreadExternal: 747 d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) 748 | MHD_USE_NO_THREAD_SAFETY 749 | (no_listen ? MHD_USE_NO_LISTEN_SOCKET : 0) 750 | MHD_USE_ERROR_LOG, 751 *pport, NULL, NULL, 752 &ahc_echo, NULL, 753 MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, 754 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 755 MHD_OPTION_END); 756 break; 757 case testMhdThreadInternalPool: 758 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD 759 | ((unsigned int) pollType) 760 | MHD_USE_ITC 761 | (no_listen ? MHD_USE_NO_LISTEN_SOCKET : 0) 762 | MHD_USE_ERROR_LOG, 763 *pport, NULL, NULL, 764 &ahc_echo, NULL, 765 MHD_OPTION_THREAD_POOL_SIZE, 766 testNumThreadsForPool (pollType), 767 MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, 768 MHD_OPTION_END); 769 break; 770 case testMhdThreadInternal: 771 case testMhdThreadInternalPerConnection: 772 d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) 773 | MHD_USE_ITC 774 | (no_listen ? MHD_USE_NO_LISTEN_SOCKET : 0) 775 | MHD_USE_ERROR_LOG, 776 *pport, NULL, NULL, 777 &ahc_echo, NULL, 778 MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, 779 MHD_OPTION_END); 780 break; 781 default: 782 abort (); 783 break; 784 } 785 786 if (NULL == d) 787 { 788 fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno); 789 abort (); 790 } 791 792 if ((! no_listen) && (0 == *pport)) 793 { 794 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 795 if ((NULL == dinfo) || (0 == dinfo->port) ) 796 { 797 fprintf (stderr, "MHD_get_daemon_info() failed.\n"); 798 abort (); 799 } 800 *pport = dinfo->port; 801 } 802 803 return d; 804 } 805 806 807 /* Test runners */ 808 809 810 static unsigned int 811 testExternalGet (void) 812 { 813 struct MHD_Daemon *d; 814 CURL *c_d; 815 char buf_d[2048]; 816 struct CBC cbc_d; 817 CURL *c_a; 818 char buf_a[2048]; 819 struct CBC cbc_a; 820 CURLM *multi; 821 time_t start; 822 struct timeval tv; 823 uint16_t d_port = global_port; /* Daemon's port */ 824 uint16_t a_port = 0; /* Additional listening socket port */ 825 struct addConnParam aParam; 826 unsigned int ret = 0; /* Return value of the test */ 827 const int c_no_listen = no_listen; /* Local const value to mute analyzer */ 828 829 d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port); 830 831 aParam.d = d; 832 aParam.lstn_sk = createListeningSocket (&a_port); 833 834 multi = NULL; 835 cbc_d.buf = buf_d; 836 cbc_d.size = sizeof(buf_d); 837 cbc_d.pos = 0; 838 cbc_a.buf = buf_a; 839 cbc_a.size = sizeof(buf_a); 840 cbc_a.pos = 0; 841 842 if (cleanup_test) 843 abort (); /* Not possible with "external poll" as connections are directly 844 added to the daemon processing in the mode. */ 845 846 if (! c_no_listen) 847 c_d = curlEasyInitForTest ("http://127.0.0.1" EXPECTED_URI_FULL_PATH, 848 d_port, &cbc_d); 849 else 850 c_d = NULL; /* To mute compiler warning only */ 851 852 c_a = curlEasyInitForTest ("http://127.0.0.1" EXPECTED_URI_FULL_PATH, 853 a_port, &cbc_a); 854 855 multi = curl_multi_init (); 856 if (multi == NULL) 857 { 858 fprintf (stderr, "curl_multi_init() failed.\n"); 859 _exit (99); 860 } 861 if (! c_no_listen) 862 { 863 if (CURLM_OK != curl_multi_add_handle (multi, c_d)) 864 { 865 fprintf (stderr, "curl_multi_add_handle() failed.\n"); 866 _exit (99); 867 } 868 } 869 870 if (CURLM_OK != curl_multi_add_handle (multi, c_a)) 871 { 872 fprintf (stderr, "curl_multi_add_handle() failed.\n"); 873 _exit (99); 874 } 875 876 start = time (NULL); 877 while (time (NULL) - start <= TIMEOUTS_VAL) 878 { 879 fd_set rs; 880 fd_set ws; 881 fd_set es; 882 MHD_socket maxMhdSk; 883 int maxCurlSk; 884 int running; 885 886 maxMhdSk = MHD_INVALID_SOCKET; 887 maxCurlSk = -1; 888 FD_ZERO (&rs); 889 FD_ZERO (&ws); 890 FD_ZERO (&es); 891 curl_multi_perform (multi, &running); 892 if (0 == running) 893 { 894 struct CURLMsg *msg; 895 int msgLeft; 896 int totalMsgs = 0; 897 do 898 { 899 msg = curl_multi_info_read (multi, &msgLeft); 900 if (NULL == msg) 901 { 902 fprintf (stderr, "curl_multi_info_read failed, NULL returned.\n"); 903 _exit (99); 904 } 905 totalMsgs++; 906 if (CURLMSG_DONE == msg->msg) 907 { 908 if (CURLE_OK != msg->data.result) 909 { 910 fprintf (stderr, "curl_multi_info_read failed, error: '%s'\n", 911 curl_easy_strerror (msg->data.result)); 912 ret |= 2; 913 } 914 } 915 } while (msgLeft > 0); 916 if ((no_listen ? 1 : 2) != totalMsgs) 917 { 918 fprintf (stderr, 919 "curl_multi_info_read returned wrong " 920 "number of results (%d).\n", 921 totalMsgs); 922 _exit (99); 923 } 924 break; /* All transfers have finished. */ 925 } 926 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk)) 927 { 928 fprintf (stderr, "curl_multi_fdset() failed.\n"); 929 _exit (99); 930 } 931 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) 932 { 933 ret |= 8; 934 break; 935 } 936 FD_SET (aParam.lstn_sk, &rs); 937 if (maxMhdSk < aParam.lstn_sk) 938 maxMhdSk = aParam.lstn_sk; 939 tv.tv_sec = 0; 940 tv.tv_usec = 1000; 941 #ifdef MHD_POSIX_SOCKETS 942 if (maxMhdSk > maxCurlSk) 943 maxCurlSk = maxMhdSk; 944 #endif /* MHD_POSIX_SOCKETS */ 945 if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv)) 946 { 947 #ifdef MHD_POSIX_SOCKETS 948 if (EINTR != errno) 949 { 950 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 951 (int) errno, __LINE__); 952 fflush (stderr); 953 exit (99); 954 } 955 #else 956 if ((WSAEINVAL != WSAGetLastError ()) || 957 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 958 { 959 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 960 (int) WSAGetLastError (), __LINE__); 961 fflush (stderr); 962 exit (99); 963 } 964 Sleep (1); 965 #endif 966 } 967 if (FD_ISSET (aParam.lstn_sk, &rs)) 968 ret |= doAcceptAndAddConnInThread (&aParam); 969 970 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) 971 { 972 fprintf (stderr, "MHD_run_from_select() failed.\n"); 973 ret |= 1; 974 break; 975 } 976 } 977 978 MHD_stop_daemon (d); 979 (void) MHD_socket_close_ (aParam.lstn_sk); 980 981 if (! c_no_listen) 982 { 983 curl_multi_remove_handle (multi, c_d); 984 curl_easy_cleanup (c_d); 985 if (cbc_d.pos != strlen ("/hello_world")) 986 { 987 fprintf (stderr, 988 "curl reports wrong size of MHD reply body data at line %d.\n", 989 __LINE__); 990 ret |= 4; 991 } 992 if (0 != strncmp ("/hello_world", cbc_d.buf, strlen ("/hello_world"))) 993 { 994 fprintf (stderr, "curl reports wrong MHD reply body data at line %d.\n", 995 __LINE__); 996 ret |= 4; 997 } 998 } 999 curl_multi_remove_handle (multi, c_a); 1000 curl_easy_cleanup (c_a); 1001 curl_multi_cleanup (multi); 1002 if (cbc_a.pos != strlen ("/hello_world")) 1003 { 1004 fprintf (stderr, 1005 "curl reports wrong size of MHD reply body data at line %d.\n", 1006 __LINE__); 1007 ret |= 4; 1008 } 1009 if (0 != strncmp ("/hello_world", cbc_a.buf, strlen ("/hello_world"))) 1010 { 1011 fprintf (stderr, "curl reports wrong MHD reply body data at line %d.\n", 1012 __LINE__); 1013 ret |= 4; 1014 } 1015 return ret; 1016 } 1017 1018 1019 #ifdef HAVE_PTHREAD_H 1020 static unsigned int 1021 testInternalGet (enum testMhdPollType pollType) 1022 { 1023 struct MHD_Daemon *d; 1024 uint16_t d_port = global_port; /* Daemon's port */ 1025 1026 d = startTestMhdDaemon (testMhdThreadInternal, pollType, 1027 &d_port); 1028 if (cleanup_test) 1029 return performTestCleanup (d, CLEANUP_NUM_REQS_PER_DAEMON); 1030 1031 return performTestQueries (d, d_port); 1032 } 1033 1034 1035 static unsigned int 1036 testMultithreadedGet (enum testMhdPollType pollType) 1037 { 1038 struct MHD_Daemon *d; 1039 uint16_t d_port = global_port; /* Daemon's port */ 1040 1041 d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, 1042 &d_port); 1043 if (cleanup_test) 1044 abort (); /* Cannot be tested as main daemon thread cannot be slowed down 1045 by slow responses, so it processes all new connections before 1046 daemon could be stopped. */ 1047 1048 return performTestQueries (d, d_port); 1049 } 1050 1051 1052 static unsigned int 1053 testMultithreadedPoolGet (enum testMhdPollType pollType) 1054 { 1055 struct MHD_Daemon *d; 1056 uint16_t d_port = global_port; /* Daemon's port */ 1057 1058 d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, 1059 &d_port); 1060 1061 if (cleanup_test) 1062 return performTestCleanup (d, CLEANUP_NUM_REQS_PER_DAEMON 1063 * testNumThreadsForPool (pollType)); 1064 return performTestQueries (d, d_port); 1065 } 1066 1067 1068 static unsigned int 1069 testStopRace (enum testMhdPollType pollType) 1070 { 1071 struct MHD_Daemon *d; 1072 uint16_t d_port = global_port; /* Daemon's port */ 1073 uint16_t a_port = 0; /* Additional listening socket port */ 1074 struct sockaddr_in sin; 1075 MHD_socket fd1; 1076 MHD_socket fd2; 1077 struct addConnParam aParam; 1078 unsigned int ret = 0; /* Return value of the test */ 1079 1080 d = startTestMhdDaemon (testMhdThreadInternal, pollType, 1081 &d_port); 1082 1083 if (! no_listen) 1084 { 1085 fd1 = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); 1086 if (MHD_INVALID_SOCKET == fd1) 1087 externalErrorExitDesc ("socket() failed"); 1088 1089 memset (&sin, 0, sizeof(sin)); 1090 sin.sin_family = AF_INET; 1091 sin.sin_port = htons (d_port); 1092 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 1093 if (connect (fd1, (struct sockaddr *) (&sin), sizeof(sin)) < 0) 1094 externalErrorExitDesc ("socket() failed"); 1095 } 1096 else 1097 fd1 = MHD_INVALID_SOCKET; 1098 1099 aParam.d = d; 1100 aParam.lstn_sk = createListeningSocket (&a_port); /* Sets a_port */ 1101 startThreadAddConn (&aParam); 1102 1103 fd2 = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); 1104 if (MHD_INVALID_SOCKET == fd2) 1105 externalErrorExitDesc ("socket() failed"); 1106 memset (&sin, 0, sizeof(sin)); 1107 sin.sin_family = AF_INET; 1108 sin.sin_port = htons (a_port); 1109 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 1110 if (connect (fd2, (struct sockaddr *) (&sin), sizeof(sin)) < 0) 1111 externalErrorExitDesc ("socket() failed"); 1112 ret |= finishThreadAddConn (&aParam); 1113 1114 /* Let the thread get going. */ 1115 usleep (500000); 1116 1117 MHD_stop_daemon (d); 1118 1119 if (MHD_INVALID_SOCKET != fd1) 1120 (void) MHD_socket_close_ (fd1); 1121 (void) MHD_socket_close_ (aParam.lstn_sk); 1122 (void) MHD_socket_close_ (fd2); 1123 1124 return ret; 1125 } 1126 1127 1128 #endif /* HAVE_PTHREAD_H */ 1129 1130 1131 int 1132 main (int argc, char *const *argv) 1133 { 1134 unsigned int errorCount = 0; 1135 unsigned int test_result = 0; 1136 int verbose = 0; 1137 1138 if ((NULL == argv) || (0 == argv[0])) 1139 return 99; 1140 oneone = has_in_name (argv[0], "11"); 1141 /* Whether to test MHD daemons without listening socket. */ 1142 no_listen = has_in_name (argv[0], "_nolisten"); 1143 /* Whether to test for correct final cleanup instead of 1144 * of test of normal processing. */ 1145 cleanup_test = has_in_name (argv[0], "_cleanup"); 1146 /* There are almost nothing that could be tested externally 1147 * for final cleanup. Cleanup test actually just tests that 1148 * all added client connections were closed by MHD and 1149 * nothing fails or crashes when final cleanup is performed. 1150 * Mostly useful when configured with '--enable-asserts. */ 1151 slow_reply = cleanup_test; 1152 ignore_response_errors = cleanup_test; 1153 #ifndef HAVE_PTHREAD_H 1154 if (cleanup_test) 1155 return 77; /* Cannot run without threads */ 1156 #endif /* HAVE_PTHREAD_H */ 1157 verbose = ! (has_param (argc, argv, "-q") || 1158 has_param (argc, argv, "--quiet") || 1159 has_param (argc, argv, "-s") || 1160 has_param (argc, argv, "--silent")); 1161 if (cleanup_test) 1162 { 1163 #ifndef _WIN32 1164 /* Find system limit for number of open FDs. */ 1165 #if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX) 1166 sys_max_fds = sysconf (_SC_OPEN_MAX) > 500000 ? 1167 500000 : (int) sysconf (_SC_OPEN_MAX); 1168 #else /* ! HAVE_SYSCONF || ! _SC_OPEN_MAX */ 1169 sys_max_fds = -1; 1170 #endif /* ! HAVE_SYSCONF || ! _SC_OPEN_MAX */ 1171 if (0 > sys_max_fds) 1172 { 1173 #if defined(OPEN_MAX) && (0 < ((OPEN_MAX) +1)) 1174 sys_max_fds = OPEN_MAX > 500000 ? 500000 : (int) OPEN_MAX; 1175 #else /* ! OPEN_MAX */ 1176 sys_max_fds = 256; /* Use reasonable value */ 1177 #endif /* ! OPEN_MAX */ 1178 if (2 > CLEANUP_MAX_DAEMONS (sys_max_fds)) 1179 return 77; /* Multithreaded test cannot be run */ 1180 } 1181 #else /* _WIN32 */ 1182 sys_max_fds = 120; /* W32 has problems with ports exhaust */ 1183 #endif /* _WIN32 */ 1184 } 1185 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 1186 return 99; 1187 /* Could be set to non-zero value to enforce using specific port 1188 * in the test */ 1189 global_port = 0; 1190 if (! cleanup_test) 1191 { 1192 test_result = testExternalGet (); 1193 if (test_result) 1194 fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result); 1195 else if (verbose) 1196 printf ("PASSED: testExternalGet ().\n"); 1197 errorCount += test_result; 1198 } 1199 #ifdef HAVE_PTHREAD_H 1200 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 1201 { 1202 test_result = testInternalGet (testMhdPollBySelect); 1203 if (test_result) 1204 fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n", 1205 test_result); 1206 else if (verbose) 1207 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 1208 errorCount += test_result; 1209 test_result = testMultithreadedPoolGet (testMhdPollBySelect); 1210 if (test_result) 1211 fprintf (stderr, 1212 "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n", 1213 test_result); 1214 else if (verbose) 1215 printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n"); 1216 errorCount += test_result; 1217 if (! cleanup_test) 1218 { 1219 test_result = testMultithreadedGet (testMhdPollBySelect); 1220 if (test_result) 1221 fprintf (stderr, 1222 "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n", 1223 test_result); 1224 else if (verbose) 1225 printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n"); 1226 errorCount += test_result; 1227 test_result = testStopRace (testMhdPollBySelect); 1228 if (test_result) 1229 fprintf (stderr, "FAILED: testStopRace (testMhdPollBySelect) - %u.\n", 1230 test_result); 1231 else if (verbose) 1232 printf ("PASSED: testStopRace (testMhdPollBySelect).\n"); 1233 errorCount += test_result; 1234 } 1235 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) 1236 { 1237 test_result = testInternalGet (testMhdPollByPoll); 1238 if (test_result) 1239 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n", 1240 test_result); 1241 else if (verbose) 1242 printf ("PASSED: testInternalGet (testMhdPollByPoll).\n"); 1243 errorCount += test_result; 1244 test_result = testMultithreadedPoolGet (testMhdPollByPoll); 1245 if (test_result) 1246 fprintf (stderr, 1247 "FAILED: testMultithreadedPoolGet (testMhdPollByPoll) - %u.\n", 1248 test_result); 1249 else if (verbose) 1250 printf ("PASSED: testMultithreadedPoolGet (testMhdPollByPoll).\n"); 1251 errorCount += test_result; 1252 if (! cleanup_test) 1253 { 1254 test_result = testMultithreadedGet (testMhdPollByPoll); 1255 if (test_result) 1256 fprintf (stderr, 1257 "FAILED: testMultithreadedGet (testMhdPollByPoll) - %u.\n", 1258 test_result); 1259 else if (verbose) 1260 printf ("PASSED: testMultithreadedGet (testMhdPollByPoll).\n"); 1261 errorCount += test_result; 1262 test_result = testStopRace (testMhdPollByPoll); 1263 if (test_result) 1264 fprintf (stderr, "FAILED: testStopRace (testMhdPollByPoll) - %u.\n", 1265 test_result); 1266 else if (verbose) 1267 printf ("PASSED: testStopRace (testMhdPollByPoll).\n"); 1268 errorCount += test_result; 1269 } 1270 } 1271 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 1272 { 1273 test_result = testInternalGet (testMhdPollByEpoll); 1274 if (test_result) 1275 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n", 1276 test_result); 1277 else if (verbose) 1278 printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n"); 1279 errorCount += test_result; 1280 test_result = testMultithreadedPoolGet (testMhdPollByEpoll); 1281 if (test_result) 1282 fprintf (stderr, 1283 "FAILED: testMultithreadedPoolGet (testMhdPollByEpoll) - %u.\n", 1284 test_result); 1285 else if (verbose) 1286 printf ("PASSED: testMultithreadedPoolGet (testMhdPollByEpoll).\n"); 1287 errorCount += test_result; 1288 } 1289 } 1290 #endif /* HAVE_PTHREAD_H */ 1291 if (0 != errorCount) 1292 fprintf (stderr, 1293 "Error (code: %u)\n", 1294 errorCount); 1295 else if (verbose) 1296 printf ("All tests passed.\n"); 1297 curl_global_cleanup (); 1298 return (errorCount == 0) ? 0 : 1; /* 0 == pass */ 1299 }