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