test_quiesce.c (23999B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2013, 2015 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 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_quiesce.c 23 * @brief Testcase for libmicrohttpd quiescing 24 * @author Christian Grothoff 25 * @author Karlson2k (Evgeny Grin) 26 */ 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 <sys/types.h> 36 #include <pthread.h> 37 #include <errno.h> 38 #include "mhd_sockets.h" /* only macros used */ 39 #include "mhd_has_in_name.h" 40 #include "mhd_has_param.h" 41 42 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 43 test into a marked, classifiable test error (TESTING.md, P5). */ 44 #include "mhd_panic_tripwire.h" 45 46 47 #ifndef WINDOWS 48 #include <unistd.h> 49 #include <sys/socket.h> 50 #endif 51 52 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 53 #undef MHD_CPU_COUNT 54 #endif 55 #if ! defined(MHD_CPU_COUNT) 56 #define MHD_CPU_COUNT 2 57 #endif 58 59 60 #ifndef _MHD_INSTRMACRO 61 /* Quoted macro parameter */ 62 #define _MHD_INSTRMACRO(a) #a 63 #endif /* ! _MHD_INSTRMACRO */ 64 #ifndef _MHD_STRMACRO 65 /* Quoted expanded macro parameter */ 66 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) 67 #endif /* ! _MHD_STRMACRO */ 68 69 #if defined(HAVE___FUNC__) 70 #define externalErrorExit(ignore) \ 71 _externalErrorExit_func(NULL, __func__, __LINE__) 72 #define externalErrorExitDesc(errDesc) \ 73 _externalErrorExit_func(errDesc, __func__, __LINE__) 74 #define libcurlErrorExit(ignore) \ 75 _libcurlErrorExit_func(NULL, __func__, __LINE__) 76 #define libcurlErrorExitDesc(errDesc) \ 77 _libcurlErrorExit_func(errDesc, __func__, __LINE__) 78 #define mhdErrorExit(ignore) \ 79 _mhdErrorExit_func(NULL, __func__, __LINE__) 80 #define mhdErrorExitDesc(errDesc) \ 81 _mhdErrorExit_func(errDesc, __func__, __LINE__) 82 #define checkCURLE_OK(libcurlcall) \ 83 _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \ 84 __func__, __LINE__) 85 #elif defined(HAVE___FUNCTION__) 86 #define externalErrorExit(ignore) \ 87 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__) 88 #define externalErrorExitDesc(errDesc) \ 89 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__) 90 #define libcurlErrorExit(ignore) \ 91 _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__) 92 #define libcurlErrorExitDesc(errDesc) \ 93 _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__) 94 #define mhdErrorExit(ignore) \ 95 _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__) 96 #define mhdErrorExitDesc(errDesc) \ 97 _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__) 98 #define checkCURLE_OK(libcurlcall) \ 99 _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \ 100 __FUNCTION__, __LINE__) 101 #else 102 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__) 103 #define externalErrorExitDesc(errDesc) \ 104 _externalErrorExit_func(errDesc, NULL, __LINE__) 105 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__) 106 #define libcurlErrorExitDesc(errDesc) \ 107 _libcurlErrorExit_func(errDesc, NULL, __LINE__) 108 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__) 109 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__) 110 #define checkCURLE_OK(libcurlcall) \ 111 _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), NULL, __LINE__) 112 #endif 113 114 115 _MHD_NORETURN static void 116 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 117 { 118 fflush (stdout); 119 if ((NULL != errDesc) && (0 != errDesc[0])) 120 fprintf (stderr, "%s", errDesc); 121 else 122 fprintf (stderr, "System or external library call failed"); 123 if ((NULL != funcName) && (0 != funcName[0])) 124 fprintf (stderr, " in %s", funcName); 125 if (0 < lineNum) 126 fprintf (stderr, " at line %d", lineNum); 127 128 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 129 strerror (errno)); 130 #ifdef MHD_WINSOCK_SOCKETS 131 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 132 #endif /* MHD_WINSOCK_SOCKETS */ 133 fflush (stderr); 134 exit (99); 135 } 136 137 138 static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; 139 140 _MHD_NORETURN static void 141 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 142 { 143 fflush (stdout); 144 if ((NULL != errDesc) && (0 != errDesc[0])) 145 fprintf (stderr, "%s", errDesc); 146 else 147 fprintf (stderr, "CURL library call failed"); 148 if ((NULL != funcName) && (0 != funcName[0])) 149 fprintf (stderr, " in %s", funcName); 150 if (0 < lineNum) 151 fprintf (stderr, " at line %d", lineNum); 152 153 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 154 strerror (errno)); 155 #ifdef MHD_WINSOCK_SOCKETS 156 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 157 #endif /* MHD_WINSOCK_SOCKETS */ 158 if (0 != libcurl_errbuf[0]) 159 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); 160 161 fflush (stderr); 162 exit (99); 163 } 164 165 166 _MHD_NORETURN static void 167 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 168 { 169 fflush (stdout); 170 if ((NULL != errDesc) && (0 != errDesc[0])) 171 fprintf (stderr, "%s", errDesc); 172 else 173 fprintf (stderr, "MHD unexpected error"); 174 if ((NULL != funcName) && (0 != funcName[0])) 175 fprintf (stderr, " in %s", funcName); 176 if (0 < lineNum) 177 fprintf (stderr, " at line %d", lineNum); 178 179 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 180 strerror (errno)); 181 #ifdef MHD_WINSOCK_SOCKETS 182 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 183 #endif /* MHD_WINSOCK_SOCKETS */ 184 185 fflush (stderr); 186 exit (8); 187 } 188 189 190 static void 191 _checkCURLE_OK_func (CURLcode code, const char *curlFunc, 192 const char *funcName, int lineNum) 193 { 194 if (CURLE_OK == code) 195 return; 196 197 fflush (stdout); 198 if ((NULL != curlFunc) && (0 != curlFunc[0])) 199 fprintf (stderr, "'%s' resulted in '%s'", curlFunc, 200 curl_easy_strerror (code)); 201 else 202 fprintf (stderr, "libcurl function call resulted in '%s'", 203 curl_easy_strerror (code)); 204 if ((NULL != funcName) && (0 != funcName[0])) 205 fprintf (stderr, " in %s", funcName); 206 if (0 < lineNum) 207 fprintf (stderr, " at line %d", lineNum); 208 209 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 210 strerror (errno)); 211 if (0 != libcurl_errbuf[0]) 212 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); 213 214 fflush (stderr); 215 exit (9); 216 } 217 218 219 /* Could be increased to facilitate debugging */ 220 #define TIMEOUTS_VAL 4 221 222 #define MHD_URI_BASE_PATH "/hello_world" 223 224 /* Global parameters */ 225 static int verbose; /**< Be verbose */ 226 static int oneone; /**< If false use HTTP/1.0 for requests*/ 227 static uint16_t global_port; /**< MHD daemons listen port number */ 228 229 struct CBC 230 { 231 char *buf; 232 size_t pos; 233 size_t size; 234 }; 235 236 static size_t 237 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 238 { 239 struct CBC *cbc = ctx; 240 241 if (cbc->pos + size * nmemb > cbc->size) 242 return 0; /* overflow */ 243 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 244 cbc->pos += size * nmemb; 245 return size * nmemb; 246 } 247 248 249 static enum MHD_Result 250 ahc_echo (void *cls, 251 struct MHD_Connection *connection, 252 const char *url, 253 const char *method, 254 const char *version, 255 const char *upload_data, size_t *upload_data_size, 256 void **req_cls) 257 { 258 static int ptr; 259 struct MHD_Response *response; 260 (void) cls; 261 (void) version; (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 262 263 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 264 { 265 fprintf (stderr, "Unexpected HTTP method '%s'. ", method); 266 externalErrorExit (); 267 } 268 if (&ptr != *req_cls) 269 { 270 *req_cls = &ptr; 271 return MHD_YES; 272 } 273 *req_cls = NULL; 274 response = MHD_create_response_from_buffer_copy (strlen (url), 275 (const void *) url); 276 if (NULL == response) 277 mhdErrorExitDesc ("MHD_create_response failed"); 278 /* Make sure that connection will not be reused */ 279 if (MHD_NO == MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, 280 "close")) 281 mhdErrorExitDesc ("MHD_add_response_header() failed"); 282 if (MHD_NO == MHD_queue_response (connection, MHD_HTTP_OK, response)) 283 mhdErrorExitDesc ("MHD_queue_response() failed"); 284 MHD_destroy_response (response); 285 return MHD_YES; 286 } 287 288 289 static void 290 request_completed (void *cls, struct MHD_Connection *connection, 291 void **req_cls, enum MHD_RequestTerminationCode code) 292 { 293 int *done = (int *) cls; 294 (void) connection; (void) req_cls; (void) code; /* Unused. Silent compiler warning. */ 295 if (MHD_REQUEST_TERMINATED_COMPLETED_OK != code) 296 { 297 fprintf (stderr, "Unexpected termination code: %d. ", (int) code); 298 mhdErrorExit (); 299 } 300 *done = 1; 301 if (verbose) 302 printf ("Notify callback has been called with OK code.\n"); 303 } 304 305 306 static void * 307 ServeOneRequest (void *param) 308 { 309 struct MHD_Daemon *d; 310 fd_set rs; 311 fd_set ws; 312 fd_set es; 313 MHD_socket fd, max; 314 time_t start; 315 struct timeval tv; 316 volatile int done = 0; 317 318 if (NULL == param) 319 externalErrorExit (); 320 321 fd = *((MHD_socket *) param); 322 323 d = MHD_start_daemon (MHD_USE_ERROR_LOG, 324 0, NULL, NULL, &ahc_echo, NULL, 325 MHD_OPTION_LISTEN_SOCKET, fd, 326 MHD_OPTION_NOTIFY_COMPLETED, &request_completed, &done, 327 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 328 MHD_OPTION_END); 329 if (d == NULL) 330 mhdErrorExit (); 331 332 if (verbose) 333 printf ("Started MHD daemon in ServeOneRequest().\n"); 334 335 start = time (NULL); 336 while ((time (NULL) - start < TIMEOUTS_VAL * 2) && done == 0) 337 { 338 max = 0; 339 FD_ZERO (&rs); 340 FD_ZERO (&ws); 341 FD_ZERO (&es); 342 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 343 mhdErrorExit ("MHD_get_fdset() failed"); 344 tv.tv_sec = 0; 345 tv.tv_usec = 100000; 346 if (-1 == MHD_SYS_select_ (max + 1, &rs, &ws, &es, &tv)) 347 { 348 #ifdef MHD_POSIX_SOCKETS 349 if (EINTR != errno) 350 externalErrorExitDesc ("Unexpected select() error"); 351 #else 352 if ((WSAEINVAL != WSAGetLastError ()) || 353 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 354 externalErrorExitDesc ("Unexpected select() error"); 355 Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 356 #endif 357 } 358 MHD_run (d); 359 } 360 if (! done) 361 mhdErrorExit ("ServeOneRequest() failed and finished by timeout"); 362 fd = MHD_quiesce_daemon (d); 363 if (MHD_INVALID_SOCKET == fd) 364 mhdErrorExit ("MHD_quiesce_daemon() failed in ServeOneRequest()"); 365 366 MHD_stop_daemon (d); 367 return NULL; 368 } 369 370 371 static CURL * 372 setupCURL (void *cbc) 373 { 374 CURL *c; 375 376 c = curl_easy_init (); 377 if (NULL == c) 378 libcurlErrorExitDesc ("curl_easy_init() failed"); 379 380 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 381 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, 382 "http://127.0.0.1" MHD_URI_BASE_PATH)) || 383 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) global_port)) || 384 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 385 ©Buffer)) || 386 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) || 387 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 388 (long) (TIMEOUTS_VAL / 2))) || 389 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 390 (long) TIMEOUTS_VAL)) || 391 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, 392 libcurl_errbuf)) || 393 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 394 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 395 (oneone) ? 396 CURL_HTTP_VERSION_1_1 : 397 CURL_HTTP_VERSION_1_0))) 398 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 399 return c; 400 } 401 402 403 static unsigned int 404 testGet (unsigned int type, int pool_count, uint32_t poll_flag) 405 { 406 struct MHD_Daemon *d; 407 CURL *c; 408 char buf[2048]; 409 struct CBC cbc; 410 MHD_socket fd; 411 pthread_t thrd; 412 char *thrdRet; 413 414 if (verbose) 415 printf ("testGet(%u, %d, %u) test started.\n", 416 type, pool_count, (unsigned int) poll_flag); 417 418 cbc.buf = buf; 419 cbc.size = sizeof(buf); 420 cbc.pos = 0; 421 if (pool_count > 0) 422 { 423 d = MHD_start_daemon (type | MHD_USE_ERROR_LOG | MHD_USE_ITC 424 | (enum MHD_FLAG) poll_flag, 425 global_port, NULL, NULL, &ahc_echo, NULL, 426 MHD_OPTION_THREAD_POOL_SIZE, 427 (unsigned int) pool_count, 428 MHD_OPTION_END); 429 430 } 431 else 432 { 433 d = MHD_start_daemon (type | MHD_USE_ERROR_LOG | MHD_USE_ITC 434 | (enum MHD_FLAG) poll_flag, 435 global_port, NULL, NULL, &ahc_echo, NULL, 436 MHD_OPTION_END); 437 } 438 if (d == NULL) 439 mhdErrorExitDesc ("MHD_start_daemon() failed"); 440 if (0 == global_port) 441 { 442 const union MHD_DaemonInfo *dinfo; 443 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 444 if ((NULL == dinfo) || (0 == dinfo->port) ) 445 mhdErrorExit (); 446 global_port = dinfo->port; 447 } 448 449 c = setupCURL (&cbc); 450 451 checkCURLE_OK (curl_easy_perform (c)); 452 453 if (cbc.pos != strlen (MHD_URI_BASE_PATH)) 454 { 455 fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", 456 (unsigned) cbc.pos, (int) cbc.pos, cbc.buf, 457 (unsigned) strlen (MHD_URI_BASE_PATH)); 458 mhdErrorExitDesc ("Wrong returned data length"); 459 } 460 if (0 != strncmp (MHD_URI_BASE_PATH, cbc.buf, strlen (MHD_URI_BASE_PATH))) 461 { 462 fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf); 463 mhdErrorExitDesc ("Wrong returned data"); 464 } 465 if (verbose) 466 printf ("Received valid response data.\n"); 467 468 fd = MHD_quiesce_daemon (d); 469 if (MHD_INVALID_SOCKET == fd) 470 mhdErrorExitDesc ("MHD_quiesce_daemon failed"); 471 472 if (0 != pthread_create (&thrd, NULL, &ServeOneRequest, 473 (void *) &fd)) 474 externalErrorExitDesc ("pthread_create() failed"); 475 476 /* No need for the thread sync as socket is already listening, 477 * so libcurl may start connecting before MHD is started in another thread */ 478 cbc.pos = 0; 479 checkCURLE_OK (curl_easy_perform (c)); 480 481 if (cbc.pos != strlen (MHD_URI_BASE_PATH)) 482 { 483 fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", 484 (unsigned) cbc.pos, (int) cbc.pos, cbc.buf, 485 (unsigned) strlen (MHD_URI_BASE_PATH)); 486 mhdErrorExitDesc ("Wrong returned data length"); 487 } 488 if (0 != strncmp (MHD_URI_BASE_PATH, cbc.buf, strlen (MHD_URI_BASE_PATH))) 489 { 490 fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf); 491 mhdErrorExitDesc ("Wrong returned data"); 492 } 493 494 if (0 != pthread_join (thrd, (void **) &thrdRet)) 495 externalErrorExitDesc ("pthread_join() failed"); 496 if (NULL != thrdRet) 497 externalErrorExitDesc ("ServeOneRequest() returned non-NULL result"); 498 499 if (verbose) 500 { 501 printf ("ServeOneRequest() thread was joined.\n"); 502 fflush (stdout); 503 } 504 505 /* at this point, the forked server quiesced and quit, 506 * so new requests should fail 507 */ 508 cbc.pos = 0; 509 if (CURLE_OK == curl_easy_perform (c)) 510 { 511 fprintf (stderr, "curl_easy_perform() succeed while it should fail. "); 512 fprintf (stderr, "Got %u bytes ('%.*s'), " 513 "valid data would be %u bytes (%s). ", 514 (unsigned) cbc.pos, (int) cbc.pos, cbc.buf, 515 (unsigned) strlen (MHD_URI_BASE_PATH), MHD_URI_BASE_PATH); 516 mhdErrorExitDesc ("Unexpected succeed request"); 517 } 518 if (verbose) 519 printf ("curl_easy_perform() failed as expected.\n"); 520 curl_easy_cleanup (c); 521 MHD_stop_daemon (d); 522 MHD_socket_close_chk_ (fd); 523 524 if (verbose) 525 { 526 printf ("testGet(%u, %d, %u) test succeed.\n", 527 type, pool_count, (unsigned int) poll_flag); 528 fflush (stdout); 529 } 530 531 return 0; 532 } 533 534 535 static unsigned int 536 testExternalGet (void) 537 { 538 struct MHD_Daemon *d; 539 CURL *c; 540 char buf[2048]; 541 struct CBC cbc; 542 CURLM *multi; 543 CURLMcode mret; 544 fd_set rs; 545 fd_set ws; 546 fd_set es; 547 int running; 548 struct CURLMsg *msg; 549 time_t start; 550 struct timeval tv; 551 int i; 552 MHD_socket fd = MHD_INVALID_SOCKET; 553 554 if (verbose) 555 printf ("testExternalGet test started.\n"); 556 557 fd = MHD_INVALID_SOCKET; 558 multi = NULL; 559 cbc.buf = buf; 560 cbc.size = sizeof(buf); 561 cbc.pos = 0; 562 d = MHD_start_daemon (MHD_USE_ERROR_LOG, 563 global_port, 564 NULL, NULL, 565 &ahc_echo, NULL, 566 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 567 MHD_OPTION_END); 568 if (d == NULL) 569 mhdErrorExitDesc ("Failed to start MHD daemon"); 570 if (0 == global_port) 571 { 572 const union MHD_DaemonInfo *dinfo; 573 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 574 if ((NULL == dinfo) || (0 == dinfo->port) ) 575 mhdErrorExit (); 576 global_port = dinfo->port; 577 } 578 579 for (i = 0; i < 2; i++) 580 { 581 c = setupCURL (&cbc); 582 583 multi = curl_multi_init (); 584 if (multi == NULL) 585 libcurlErrorExit (); 586 587 mret = curl_multi_add_handle (multi, c); 588 if (mret != CURLM_OK) 589 libcurlErrorExit (); 590 591 start = time (NULL); 592 while ( (time (NULL) - start < TIMEOUTS_VAL * 2) && 593 (NULL != multi) ) 594 { 595 MHD_socket maxsock; 596 int maxposixs; 597 maxsock = MHD_INVALID_SOCKET; 598 maxposixs = -1; 599 FD_ZERO (&rs); 600 FD_ZERO (&ws); 601 FD_ZERO (&es); 602 curl_multi_perform (multi, &running); 603 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 604 if (mret != CURLM_OK) 605 libcurlErrorExit (); 606 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 607 mhdErrorExit (); 608 #ifndef MHD_WINSOCK_SOCKETS 609 if (maxsock > maxposixs) 610 maxposixs = maxsock; 611 #endif /* MHD_POSIX_SOCKETS */ 612 tv.tv_sec = 0; 613 tv.tv_usec = 100000; 614 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 615 { 616 #ifdef MHD_POSIX_SOCKETS 617 if (EINTR != errno) 618 externalErrorExitDesc ("Unexpected select() error"); 619 #else 620 if ((WSAEINVAL != WSAGetLastError ()) || 621 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 622 externalErrorExitDesc ("Unexpected select() error"); 623 Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 624 #endif 625 } 626 curl_multi_perform (multi, &running); 627 if (0 == running) 628 { 629 int pending; 630 int curl_fine = 0; 631 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 632 { 633 if (msg->msg == CURLMSG_DONE) 634 { 635 if (msg->data.result == CURLE_OK) 636 { 637 curl_fine = 1; 638 if (verbose) 639 printf ("libcurl reported success.\n"); 640 } 641 else if (i == 0) 642 { 643 fprintf (stderr, 644 "curl_multi_perform() failed with '%s'. ", 645 curl_easy_strerror (msg->data.result)); 646 mhdErrorExit (); 647 } 648 } 649 } 650 if (i == 0) 651 { 652 if (! curl_fine) 653 { 654 fprintf (stderr, "libcurl haven't returned OK code\n"); 655 mhdErrorExit (); 656 } 657 /* MHD is running, result should be correct */ 658 if (cbc.pos != strlen (MHD_URI_BASE_PATH)) 659 { 660 fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", 661 (unsigned) cbc.pos, (int) cbc.pos, cbc.buf, 662 (unsigned) strlen (MHD_URI_BASE_PATH)); 663 mhdErrorExitDesc ("Wrong returned data length"); 664 } 665 if (0 != strncmp (MHD_URI_BASE_PATH, cbc.buf, 666 strlen (MHD_URI_BASE_PATH))) 667 { 668 fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, 669 cbc.buf); 670 mhdErrorExitDesc ("Wrong returned data"); 671 } 672 if (verbose) 673 { 674 printf ("First request was successful.\n"); 675 fflush (stdout); 676 } 677 } 678 else if (i == 1) 679 { 680 if (curl_fine) 681 { 682 fprintf (stderr, "libcurl returned OK code, while it shouldn't\n"); 683 mhdErrorExit (); 684 } 685 if (verbose) 686 printf ("Second request failed as expected.\n"); 687 } 688 curl_multi_remove_handle (multi, c); 689 curl_multi_cleanup (multi); 690 curl_easy_cleanup (c); 691 c = NULL; 692 multi = NULL; 693 break; 694 } 695 MHD_run (d); 696 } 697 698 if (NULL != multi) 699 mhdErrorExitDesc ("Test failed and finished by timeout"); 700 701 if (0 == i) 702 { 703 /* quiesce the daemon on the 1st iteration, so the 2nd should fail */ 704 fd = MHD_quiesce_daemon (d); 705 if (MHD_INVALID_SOCKET == fd) 706 mhdErrorExitDesc ("MHD_quiesce_daemon() failed"); 707 } 708 } 709 MHD_stop_daemon (d); 710 if (MHD_INVALID_SOCKET == fd) 711 { 712 fprintf (stderr, "Failed to MHD_quiesce_daemon() at some point. "); 713 externalErrorExit (); 714 } 715 MHD_socket_close_chk_ (fd); 716 717 if (verbose) 718 { 719 printf ("testExternalGet succeed.\n"); 720 fflush (stdout); 721 } 722 723 return 0; 724 } 725 726 727 int 728 main (int argc, char *const *argv) 729 { 730 unsigned int errorCount = 0; 731 oneone = ! has_in_name (argv[0], "10"); 732 verbose = ! (has_param (argc, argv, "-q") || 733 has_param (argc, argv, "--quiet") || 734 has_param (argc, argv, "-s") || 735 has_param (argc, argv, "--silent")); 736 737 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 738 return 2; 739 740 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 741 global_port = 0; 742 else 743 global_port = 1480 + (oneone ? 1 : 0); 744 745 errorCount += testExternalGet (); 746 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 747 { 748 errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, 0, 0); 749 errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION 750 | MHD_USE_INTERNAL_POLLING_THREAD, 0, 0); 751 errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, MHD_CPU_COUNT, 0); 752 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) 753 { 754 errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, 0, MHD_USE_POLL); 755 errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION 756 | MHD_USE_INTERNAL_POLLING_THREAD, 0, 757 MHD_USE_POLL); 758 errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, MHD_CPU_COUNT, 759 MHD_USE_POLL); 760 } 761 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 762 { 763 errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, 0, MHD_USE_EPOLL); 764 errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, MHD_CPU_COUNT, 765 MHD_USE_EPOLL); 766 } 767 } 768 if (0 != errorCount) 769 fprintf (stderr, 770 "Error (code: %u)\n", 771 errorCount); 772 curl_global_cleanup (); 773 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 774 }