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