test_client_put_stop.c (69729B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2021-2024 Evgeny Grin (Karlson2k) 4 5 libmicrohttpd is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 2, or (at your 8 option) any later version. 9 10 libmicrohttpd is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libmicrohttpd; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file test_client_put_stop.c 22 * @brief Testcase for handling of clients aborts 23 * @author Karlson2k (Evgeny Grin) 24 * @author Christian Grothoff 25 */ 26 #include "MHD_config.h" 27 #include "platform.h" 28 #include <microhttpd.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <time.h> 32 #include <stdint.h> 33 #include <errno.h> 34 35 #ifdef HAVE_STRINGS_H 36 #include <strings.h> 37 #endif /* HAVE_STRINGS_H */ 38 39 #ifdef _WIN32 40 #ifndef WIN32_LEAN_AND_MEAN 41 #define WIN32_LEAN_AND_MEAN 1 42 #endif /* !WIN32_LEAN_AND_MEAN */ 43 #include <windows.h> 44 #endif 45 46 #ifndef WINDOWS 47 #include <unistd.h> 48 #include <sys/socket.h> 49 #endif 50 51 #ifdef HAVE_LIMITS_H 52 #include <limits.h> 53 #endif /* HAVE_LIMITS_H */ 54 55 #ifdef HAVE_SIGNAL_H 56 #include <signal.h> 57 #endif /* HAVE_SIGNAL_H */ 58 59 #ifdef HAVE_SYSCTL 60 #ifdef HAVE_SYS_TYPES_H 61 #include <sys/types.h> 62 #endif /* HAVE_SYS_TYPES_H */ 63 #ifdef HAVE_SYS_SYSCTL_H 64 #include <sys/sysctl.h> 65 #endif /* HAVE_SYS_SYSCTL_H */ 66 #ifdef HAVE_SYS_SOCKET_H 67 #include <sys/socket.h> 68 #endif /* HAVE_SYS_SOCKET_H */ 69 #ifdef HAVE_NETINET_IN_SYSTM_H 70 #include <netinet/in_systm.h> 71 #endif /* HAVE_NETINET_IN_SYSTM_H */ 72 #ifdef HAVE_NETINET_IN_H 73 #include <netinet/in.h> 74 #endif /* HAVE_NETINET_IN_H */ 75 #ifdef HAVE_NETINET_IP_H 76 #include <netinet/ip.h> 77 #endif /* HAVE_NETINET_IP_H */ 78 #ifdef HAVE_NETINET_IP_ICMP_H 79 #include <netinet/ip_icmp.h> 80 #endif /* HAVE_NETINET_IP_ICMP_H */ 81 #ifdef HAVE_NETINET_ICMP_VAR_H 82 #include <netinet/icmp_var.h> 83 #endif /* HAVE_NETINET_ICMP_VAR_H */ 84 #endif /* HAVE_SYSCTL */ 85 86 #include <stdio.h> 87 88 #include "mhd_sockets.h" /* only macros used */ 89 #include "test_helpers.h" 90 #include "mhd_assert.h" 91 92 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 93 test into a marked, classifiable test error (TESTING.md, P5). */ 94 #include "mhd_panic_tripwire.h" 95 96 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 97 #undef MHD_CPU_COUNT 98 #endif 99 #if ! defined(MHD_CPU_COUNT) 100 #define MHD_CPU_COUNT 2 101 #endif 102 #if MHD_CPU_COUNT > 32 103 #undef MHD_CPU_COUNT 104 /* Limit to reasonable value */ 105 #define MHD_CPU_COUNT 32 106 #endif /* MHD_CPU_COUNT > 32 */ 107 108 #ifndef MHD_STATICSTR_LEN_ 109 /** 110 * Determine length of static string / macro strings at compile time. 111 */ 112 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) 113 #endif /* ! MHD_STATICSTR_LEN_ */ 114 115 #ifndef _MHD_INSTRMACRO 116 /* Quoted macro parameter */ 117 #define _MHD_INSTRMACRO(a) #a 118 #endif /* ! _MHD_INSTRMACRO */ 119 #ifndef _MHD_STRMACRO 120 /* Quoted expanded macro parameter */ 121 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) 122 #endif /* ! _MHD_STRMACRO */ 123 124 125 /* Could be increased to facilitate debugging */ 126 #define TIMEOUTS_VAL 5 127 128 /* Time in ms to wait for final packets to be delivered */ 129 #define FINAL_PACKETS_MS 20 130 131 #define EXPECTED_URI_BASE_PATH "/a" 132 133 #define REQ_HOST "localhost" 134 135 #define REQ_METHOD "PUT" 136 137 #define REQ_BODY "Some content data." 138 139 #define REQ_LINE_END "\r\n" 140 141 /* Mandatory request headers */ 142 #define REQ_HEADER_HOST_NAME "Host" 143 #define REQ_HEADER_HOST_VALUE REQ_HOST 144 #define REQ_HEADER_HOST \ 145 REQ_HEADER_HOST_NAME ": " REQ_HEADER_HOST_VALUE REQ_LINE_END 146 #define REQ_HEADER_UA_NAME "User-Agent" 147 #define REQ_HEADER_UA_VALUE "dummyclient/0.9" 148 #define REQ_HEADER_UA REQ_HEADER_UA_NAME ": " REQ_HEADER_UA_VALUE REQ_LINE_END 149 150 /* Optional request headers */ 151 #define REQ_HEADER_CT_NAME "Content-Type" 152 #define REQ_HEADER_CT_VALUE "text/plain" 153 #define REQ_HEADER_CT REQ_HEADER_CT_NAME ": " REQ_HEADER_CT_VALUE REQ_LINE_END 154 155 156 #if defined(HAVE___FUNC__) 157 #define externalErrorExit(ignore) \ 158 _externalErrorExit_func(NULL, __func__, __LINE__) 159 #define externalErrorExitDesc(errDesc) \ 160 _externalErrorExit_func(errDesc, __func__, __LINE__) 161 #define mhdErrorExit(ignore) \ 162 _mhdErrorExit_func(NULL, __func__, __LINE__) 163 #define mhdErrorExitDesc(errDesc) \ 164 _mhdErrorExit_func(errDesc, __func__, __LINE__) 165 #elif defined(HAVE___FUNCTION__) 166 #define externalErrorExit(ignore) \ 167 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__) 168 #define externalErrorExitDesc(errDesc) \ 169 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__) 170 #define mhdErrorExit(ignore) \ 171 _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__) 172 #define mhdErrorExitDesc(errDesc) \ 173 _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__) 174 #else 175 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__) 176 #define externalErrorExitDesc(errDesc) \ 177 _externalErrorExit_func(errDesc, NULL, __LINE__) 178 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__) 179 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__) 180 #endif 181 182 183 _MHD_NORETURN static void 184 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 185 { 186 if ((NULL != errDesc) && (0 != errDesc[0])) 187 fprintf (stderr, "%s", errDesc); 188 else 189 fprintf (stderr, "System or external library call failed"); 190 if ((NULL != funcName) && (0 != funcName[0])) 191 fprintf (stderr, " in %s", funcName); 192 if (0 < lineNum) 193 fprintf (stderr, " at line %d", lineNum); 194 195 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 196 strerror (errno)); 197 #ifdef MHD_WINSOCK_SOCKETS 198 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 199 #endif /* MHD_WINSOCK_SOCKETS */ 200 fflush (stderr); 201 exit (99); 202 } 203 204 205 _MHD_NORETURN static void 206 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 207 { 208 if ((NULL != errDesc) && (0 != errDesc[0])) 209 fprintf (stderr, "%s", errDesc); 210 else 211 fprintf (stderr, "MHD unexpected error"); 212 if ((NULL != funcName) && (0 != funcName[0])) 213 fprintf (stderr, " in %s", funcName); 214 if (0 < lineNum) 215 fprintf (stderr, " at line %d", lineNum); 216 217 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 218 strerror (errno)); 219 220 fflush (stderr); 221 exit (8); 222 } 223 224 225 /* Global generic functions */ 226 227 void 228 _MHD_sleep (uint32_t ms); 229 230 231 /** 232 * Pause execution for specified number of milliseconds. 233 * @param ms the number of milliseconds to sleep 234 */ 235 void 236 _MHD_sleep (uint32_t ms) 237 { 238 #if defined(_WIN32) 239 Sleep (ms); 240 #elif defined(HAVE_NANOSLEEP) 241 struct timespec slp = {ms / 1000, (ms % 1000) * 1000000}; 242 struct timespec rmn; 243 int num_retries = 0; 244 while (0 != nanosleep (&slp, &rmn)) 245 { 246 if (EINTR != errno) 247 externalErrorExit (); 248 if (num_retries++ > 8) 249 break; 250 slp = rmn; 251 } 252 #elif defined(HAVE_USLEEP) 253 uint64_t us = ms * 1000; 254 do 255 { 256 uint64_t this_sleep; 257 if (999999 < us) 258 this_sleep = 999999; 259 else 260 this_sleep = us; 261 /* Ignore return value as it could be void */ 262 usleep (this_sleep); 263 us -= this_sleep; 264 } while (us > 0); 265 #else 266 externalErrorExitDesc ("No sleep function available on this system"); 267 #endif 268 } 269 270 271 /* Global parameters */ 272 static int verbose; /**< Be verbose */ 273 static int oneone; /**< If false use HTTP/1.0 for requests*/ 274 static uint16_t global_port; /**< MHD daemons listen port number */ 275 276 static int use_shutdown; /**< Use shutdown at client side */ 277 static int use_close; /**< Use socket close at client side */ 278 static int use_hard_close; /**< Use socket close with RST at client side */ 279 static int use_stress_os; /**< Stress OS by RST before getting ACKs for sent packets */ 280 static int by_step; /**< Send request byte-by-byte */ 281 static int upl_chunked; /**< Use chunked encoding for request body */ 282 283 static unsigned int rate_limiter; /**< Maximum number of checks per second */ 284 285 static void 286 test_global_init (void) 287 { 288 rate_limiter = 0; 289 if (use_hard_close) 290 { 291 #ifdef HAVE_SYSCTLBYNAME 292 if (1) 293 { 294 int blck_hl; 295 size_t blck_hl_size = sizeof (blck_hl); 296 if (0 == sysctlbyname ("net.inet.tcp.blackhole", &blck_hl, &blck_hl_size, 297 NULL, 0)) 298 { 299 if (2 <= blck_hl) 300 { 301 fprintf (stderr, "'sysctl net.inet.tcp.blackhole = %d', test is " 302 "unreliable with this system setting, skipping.\n", blck_hl); 303 exit (77); 304 } 305 } 306 else 307 { 308 if (ENOENT != errno) 309 externalErrorExitDesc ("Cannot get 'net.inet.tcp.blackhole' value"); 310 } 311 } 312 #endif 313 #if defined(HAVE_SYSCTL) && defined(HAVE_DECL_CTL_NET) && \ 314 defined(HAVE_DECL_PF_INET) && defined(HAVE_DECL_IPPROTO_ICMP) && \ 315 defined(HAVE_DECL_ICMPCTL_ICMPLIM) 316 /* Macros may have zero values */ 317 #if HAVE_DECL_CTL_NET && HAVE_DECL_PF_INET && HAVE_DECL_IPPROTO_ICMP && \ 318 HAVE_DECL_ICMPCTL_ICMPLIM 319 if (1) 320 { 321 int mib[4]; 322 int limit; 323 size_t limit_size = sizeof(limit); 324 mib[0] = CTL_NET; 325 mib[1] = PF_INET; 326 mib[2] = IPPROTO_ICMP; 327 mib[3] = ICMPCTL_ICMPLIM; 328 if (0 != sysctl (mib, 4, &limit, &limit_size, NULL, 0)) 329 { 330 if (ENOENT == errno) 331 limit = 0; /* No such parameter (new Darwin versions) */ 332 else 333 externalErrorExitDesc ("Cannot get RST rate limit value"); 334 } 335 else if (sizeof(limit) != limit_size) 336 externalErrorExitDesc ("Cannot get RST rate limit value"); 337 if (limit > 0) 338 { 339 #ifndef _MHD_HEAVY_TESTS 340 fprintf (stderr, "This system has limits on number of RST packets" 341 " per second (%d).\nThis test will be used only if configured " 342 "with '--enable-heavy-test'.\n", limit); 343 exit (77); 344 #else /* _MHD_HEAVY_TESTS */ 345 int test_limit; /**< Maximum number of checks per second */ 346 347 if (use_stress_os) 348 { 349 fprintf (stderr, "This system has limits on number of RST packet" 350 " per second (%d).\n'_stress_os' is not possible.\n", limit); 351 exit (77); 352 } 353 test_limit = limit - limit / 10; /* Add some space to not hit the limiter */ 354 test_limit /= 4; /* Assume that all four tests with 'hard_close' run in parallel */ 355 test_limit -= 5; /* Add some more space to not hit the limiter */ 356 test_limit /= 3; /* Use only one third of available limit */ 357 if (test_limit <= 0) 358 { 359 fprintf (stderr, "System limit for 'net.inet.icmp.icmplim' is " 360 "too strict for this test (value: %d).\n", limit); 361 exit (77); 362 } 363 if (verbose) 364 { 365 printf ("Limiting number of checks to %d checks/second.\n", 366 test_limit); 367 fflush (stdout); 368 } 369 rate_limiter = (unsigned int) test_limit; 370 #if ! defined(HAVE_USLEEP) && ! defined(HAVE_NANOSLEEP) && ! defined(_WIN32) 371 fprintf (stderr, "Sleep function is required for this test, " 372 "but not available on this system.\n"); 373 exit (77); 374 #endif 375 #endif /* _MHD_HEAVY_TESTS */ 376 } 377 } 378 #endif /* HAVE_DECL_CTL_NET && HAVE_DECL_PF_INET && HAVE_DECL_IPPROTO_ICMP && \ 379 HAVE_DECL_ICMPCTL_ICMPLIM */ 380 #endif /* HAVE_SYSCTL && HAVE_DECL_CTL_NET && HAVE_DECL_PF_INET && 381 HAVE_DECL_IPPROTO_ICMP && HAVE_DECL_ICMPCTL_ICMPLIM */ 382 } 383 if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE)) 384 { 385 #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE) 386 if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) 387 externalErrorExitDesc ("Error suppressing SIGPIPE signal"); 388 #else /* ! HAVE_SIGNAL_H || ! SIGPIPE */ 389 fprintf (stderr, "Cannot suppress SIGPIPE signal.\n"); 390 /* exit (77); */ 391 #endif 392 } 393 } 394 395 396 static void 397 test_global_cleanup (void) 398 { 399 } 400 401 402 /** 403 * Change socket to blocking. 404 * 405 * @param fd the socket to manipulate 406 */ 407 static void 408 make_blocking (MHD_socket fd) 409 { 410 #if defined(MHD_POSIX_SOCKETS) 411 int flags; 412 413 flags = fcntl (fd, F_GETFL); 414 if (-1 == flags) 415 externalErrorExitDesc ("Cannot make socket non-blocking"); 416 if ((flags & ~O_NONBLOCK) != flags) 417 { 418 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) 419 externalErrorExitDesc ("Cannot make socket non-blocking"); 420 } 421 #elif defined(MHD_WINSOCK_SOCKETS) 422 unsigned long flags = 0; 423 424 if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) 425 externalErrorExitDesc ("Cannot make socket non-blocking"); 426 #endif /* MHD_WINSOCK_SOCKETS */ 427 } 428 429 430 /** 431 * Change socket to non-blocking. 432 * 433 * @param fd the socket to manipulate 434 */ 435 static void 436 make_nonblocking (MHD_socket fd) 437 { 438 #if defined(MHD_POSIX_SOCKETS) 439 int flags; 440 441 flags = fcntl (fd, F_GETFL); 442 if (-1 == flags) 443 externalErrorExitDesc ("Cannot make socket non-blocking"); 444 if ((flags | O_NONBLOCK) != flags) 445 { 446 if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK)) 447 externalErrorExitDesc ("Cannot make socket non-blocking"); 448 } 449 #elif defined(MHD_WINSOCK_SOCKETS) 450 unsigned long flags = 1; 451 452 if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) 453 externalErrorExitDesc ("Cannot make socket non-blocking"); 454 #endif /* MHD_WINSOCK_SOCKETS */ 455 } 456 457 458 /* DumbClient API */ 459 struct _MHD_dumbClient * 460 _MHD_dumbClient_create (uint16_t port, const char *method, const char *url, 461 const char *add_headers, 462 const uint8_t *req_body, size_t req_body_size, 463 int chunked); 464 465 void 466 _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt, 467 size_t step_size, size_t max_total_send); 468 469 void 470 _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt); 471 472 int 473 _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt); 474 475 476 /** 477 * Process the client data with send()/recv() as needed. 478 * @param clnt the client to process 479 * @return non-zero if client finished processing the request, 480 * zero otherwise. 481 */ 482 int 483 _MHD_dumbClient_process (struct _MHD_dumbClient *clnt); 484 485 486 void 487 _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt, 488 MHD_socket *maxsckt, 489 fd_set *rs, fd_set *ws, fd_set *es); 490 491 /** 492 * Process the client data with send()/recv() as needed based on 493 * information in fd_sets. 494 * @param clnt the client to process 495 * @return non-zero if client finished processing the request, 496 * zero otherwise. 497 */ 498 int 499 _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt, 500 fd_set *rs, fd_set *ws, fd_set *es); 501 502 503 /** 504 * Perform full request. 505 * @param clnt the client to run 506 * @return zero if client finished processing the request, 507 * non-zero if timeout is reached. 508 */ 509 int 510 _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt); 511 512 513 /** 514 * Close the client and free internally allocated resources. 515 * @param clnt the client to close 516 */ 517 void 518 _MHD_dumbClient_close (struct _MHD_dumbClient *clnt); 519 520 521 /* DumbClient implementation */ 522 523 enum _MHD_clientStage 524 { 525 DUMB_CLIENT_INIT = 0, 526 DUMB_CLIENT_CONNECTING, 527 DUMB_CLIENT_CONNECTED, 528 DUMB_CLIENT_REQ_SENDING, 529 DUMB_CLIENT_REQ_SENT, 530 DUMB_CLIENT_HEADER_RECVEIVING, 531 DUMB_CLIENT_HEADER_RECVEIVED, 532 DUMB_CLIENT_BODY_RECVEIVING, 533 DUMB_CLIENT_BODY_RECVEIVED, 534 DUMB_CLIENT_FINISHING, 535 DUMB_CLIENT_FINISHED 536 }; 537 538 struct _MHD_dumbClient 539 { 540 MHD_socket sckt; /**< the socket to communicate */ 541 542 int sckt_nonblock; /**< non-zero if socket is non-blocking */ 543 544 uint16_t port; /**< the port to connect to */ 545 546 const char *send_buf; /**< the buffer for the request, malloced */ 547 548 void *buf; /**< the buffer location */ 549 550 size_t req_size; /**< the size of the request, including header */ 551 552 size_t send_off; /**< the number of bytes already sent */ 553 554 enum _MHD_clientStage stage; 555 556 /* the test-specific variables */ 557 size_t single_send_size; /**< the maximum number of bytes to be sent by 558 single send() */ 559 size_t send_size_limit; /**< the total number of send bytes limit */ 560 }; 561 562 struct _MHD_dumbClient * 563 _MHD_dumbClient_create (uint16_t port, const char *method, const char *url, 564 const char *add_headers, 565 const uint8_t *req_body, size_t req_body_size, 566 int chunked) 567 { 568 struct _MHD_dumbClient *clnt; 569 size_t method_size; 570 size_t url_size; 571 size_t add_hdrs_size; 572 size_t buf_alloc_size; 573 char *send_buf; 574 mhd_assert (0 != port); 575 mhd_assert (NULL != req_body || 0 == req_body_size); 576 mhd_assert (0 == req_body_size || NULL != req_body); 577 578 clnt = (struct _MHD_dumbClient *) malloc (sizeof(struct _MHD_dumbClient)); 579 if (NULL == clnt) 580 externalErrorExit (); 581 memset (clnt, 0, sizeof(struct _MHD_dumbClient)); 582 clnt->sckt = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 583 if (MHD_INVALID_SOCKET == clnt->sckt) 584 externalErrorExitDesc ("Cannot create the client socket"); 585 586 #ifdef MHD_socket_nosignal_ 587 if (! MHD_socket_nosignal_ (clnt->sckt)) 588 externalErrorExitDesc ("Cannot suppress SIGPIPE on the client socket"); 589 #endif /* MHD_socket_nosignal_ */ 590 591 clnt->sckt_nonblock = 0; 592 if (clnt->sckt_nonblock) 593 make_nonblocking (clnt->sckt); 594 else 595 make_blocking (clnt->sckt); 596 597 if (1) 598 { /* Always set TCP NODELAY */ 599 const MHD_SCKT_OPT_BOOL_ on_val = 1; 600 601 if (0 != setsockopt (clnt->sckt, IPPROTO_TCP, TCP_NODELAY, 602 (const void *) &on_val, sizeof (on_val))) 603 externalErrorExitDesc ("Cannot set TCP_NODELAY option"); 604 } 605 606 clnt->port = port; 607 608 if (NULL != method) 609 method_size = strlen (method); 610 else 611 { 612 method = MHD_HTTP_METHOD_GET; 613 method_size = MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET); 614 } 615 mhd_assert (0 != method_size); 616 if (NULL != url) 617 url_size = strlen (url); 618 else 619 { 620 url = "/"; 621 url_size = 1; 622 } 623 mhd_assert (0 != url_size); 624 add_hdrs_size = (NULL == add_headers) ? 0 : strlen (add_headers); 625 buf_alloc_size = 1024 + method_size + url_size 626 + add_hdrs_size + req_body_size; 627 send_buf = (char *) malloc (buf_alloc_size); 628 if (NULL == send_buf) 629 externalErrorExit (); 630 631 clnt->req_size = 0; 632 /* Form the request line */ 633 memcpy (send_buf + clnt->req_size, method, method_size); 634 clnt->req_size += method_size; 635 send_buf[clnt->req_size++] = ' '; 636 memcpy (send_buf + clnt->req_size, url, url_size); 637 clnt->req_size += url_size; 638 send_buf[clnt->req_size++] = ' '; 639 memcpy (send_buf + clnt->req_size, MHD_HTTP_VERSION_1_1, 640 MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1)); 641 clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1); 642 send_buf[clnt->req_size++] = '\r'; 643 send_buf[clnt->req_size++] = '\n'; 644 /* Form the header */ 645 memcpy (send_buf + clnt->req_size, REQ_HEADER_HOST, 646 MHD_STATICSTR_LEN_ (REQ_HEADER_HOST)); 647 clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_HOST); 648 memcpy (send_buf + clnt->req_size, REQ_HEADER_UA, 649 MHD_STATICSTR_LEN_ (REQ_HEADER_UA)); 650 clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_UA); 651 if ((NULL != req_body) || chunked) 652 { 653 if (! chunked) 654 { 655 int prn_size; 656 memcpy (send_buf + clnt->req_size, MHD_HTTP_HEADER_CONTENT_LENGTH ": ", 657 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ")); 658 clnt->req_size += 659 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "); 660 prn_size = snprintf (send_buf + clnt->req_size, 661 (buf_alloc_size - clnt->req_size), 662 "%u", (unsigned int) req_body_size); 663 if (0 >= prn_size) 664 externalErrorExit (); 665 if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size) 666 externalErrorExit (); 667 clnt->req_size += (unsigned int) prn_size; 668 send_buf[clnt->req_size++] = '\r'; 669 send_buf[clnt->req_size++] = '\n'; 670 } 671 else 672 { 673 memcpy (send_buf + clnt->req_size, 674 MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked\r\n", 675 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \ 676 ": chunked\r\n")); 677 clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \ 678 ": chunked\r\n"); 679 } 680 } 681 if (0 != add_hdrs_size) 682 { 683 memcpy (send_buf + clnt->req_size, add_headers, add_hdrs_size); 684 clnt->req_size += add_hdrs_size; 685 } 686 /* Terminate header */ 687 send_buf[clnt->req_size++] = '\r'; 688 send_buf[clnt->req_size++] = '\n'; 689 690 /* Add body (if any) */ 691 if (! chunked) 692 { 693 if (0 != req_body_size) 694 { 695 memcpy (send_buf + clnt->req_size, req_body, req_body_size); 696 clnt->req_size += req_body_size; 697 } 698 } 699 else 700 { 701 if (0 != req_body_size) 702 { 703 int prn_size; 704 prn_size = snprintf (send_buf + clnt->req_size, 705 (buf_alloc_size - clnt->req_size), 706 "%x", (unsigned int) req_body_size); 707 if (0 >= prn_size) 708 externalErrorExit (); 709 if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size) 710 externalErrorExit (); 711 clnt->req_size += (unsigned int) prn_size; 712 send_buf[clnt->req_size++] = '\r'; 713 send_buf[clnt->req_size++] = '\n'; 714 memcpy (send_buf + clnt->req_size, req_body, req_body_size); 715 clnt->req_size += req_body_size; 716 send_buf[clnt->req_size++] = '\r'; 717 send_buf[clnt->req_size++] = '\n'; 718 } 719 send_buf[clnt->req_size++] = '0'; 720 send_buf[clnt->req_size++] = '\r'; 721 send_buf[clnt->req_size++] = '\n'; 722 send_buf[clnt->req_size++] = '\r'; 723 send_buf[clnt->req_size++] = '\n'; 724 } 725 mhd_assert (clnt->req_size < buf_alloc_size); 726 clnt->buf = send_buf; 727 clnt->send_buf = send_buf; 728 729 return clnt; 730 } 731 732 733 void 734 _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt, 735 size_t step_size, size_t max_total_send) 736 { 737 clnt->single_send_size = step_size; 738 clnt->send_size_limit = max_total_send; 739 } 740 741 742 /* internal */ 743 static void 744 _MHD_dumbClient_connect_init (struct _MHD_dumbClient *clnt) 745 { 746 struct sockaddr_in sa; 747 mhd_assert (DUMB_CLIENT_INIT == clnt->stage); 748 749 sa.sin_family = AF_INET; 750 sa.sin_port = htons ((uint16_t) clnt->port); 751 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 752 753 if (0 != connect (clnt->sckt, (struct sockaddr *) &sa, sizeof(sa))) 754 { 755 const int err = MHD_socket_get_error_ (); 756 if ( (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINPROGRESS_)) || 757 (MHD_SCKT_ERR_IS_EAGAIN_ (err))) 758 clnt->stage = DUMB_CLIENT_CONNECTING; 759 else 760 externalErrorExitDesc ("Cannot 'connect()' the client socket"); 761 } 762 else 763 clnt->stage = DUMB_CLIENT_CONNECTED; 764 } 765 766 767 void 768 _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt) 769 { 770 mhd_assert (DUMB_CLIENT_INIT == clnt->stage); 771 _MHD_dumbClient_connect_init (clnt); 772 } 773 774 775 /* internal */ 776 static void 777 _MHD_dumbClient_connect_finish (struct _MHD_dumbClient *clnt) 778 { 779 int err = 0; 780 socklen_t err_size = sizeof(err); 781 mhd_assert (DUMB_CLIENT_CONNECTING == clnt->stage); 782 if (0 != getsockopt (clnt->sckt, SOL_SOCKET, SO_ERROR, 783 (void *) &err, &err_size)) 784 externalErrorExitDesc ("'getsockopt()' call failed"); 785 if (0 != err) 786 externalErrorExitDesc ("Socket connect() failed"); 787 clnt->stage = DUMB_CLIENT_CONNECTED; 788 } 789 790 791 /* internal */ 792 static void 793 _MHD_dumbClient_send_req (struct _MHD_dumbClient *clnt) 794 { 795 size_t send_size; 796 ssize_t res; 797 mhd_assert (DUMB_CLIENT_CONNECTED <= clnt->stage); 798 mhd_assert (DUMB_CLIENT_REQ_SENT > clnt->stage); 799 mhd_assert (clnt->req_size > clnt->send_off); 800 801 send_size = (((0 != clnt->send_size_limit) && 802 (clnt->req_size > clnt->send_size_limit)) ? 803 clnt->send_size_limit : clnt->req_size) - clnt->send_off; 804 mhd_assert (0 != send_size); 805 if ((0 != clnt->single_send_size) && 806 (clnt->single_send_size < send_size)) 807 send_size = clnt->single_send_size; 808 809 res = MHD_send_ (clnt->sckt, clnt->send_buf + clnt->send_off, send_size); 810 811 if (res < 0) 812 { 813 const int err = MHD_socket_get_error_ (); 814 if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) 815 return; 816 if (MHD_SCKT_ERR_IS_EINTR_ (err)) 817 return; 818 if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err)) 819 mhdErrorExitDesc ("The connection was aborted by MHD"); 820 if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_)) 821 mhdErrorExitDesc ("The connection was shut down on MHD side"); 822 externalErrorExitDesc ("Unexpected network error"); 823 } 824 clnt->send_off += (size_t) res; 825 mhd_assert (clnt->send_off <= clnt->req_size); 826 mhd_assert (clnt->send_off <= clnt->send_size_limit || \ 827 0 == clnt->send_size_limit); 828 if (clnt->req_size == clnt->send_off) 829 clnt->stage = DUMB_CLIENT_REQ_SENT; 830 if ((0 != clnt->send_size_limit) && 831 (clnt->send_size_limit == clnt->send_off)) 832 clnt->stage = DUMB_CLIENT_FINISHING; 833 } 834 835 836 /* internal */ 837 _MHD_NORETURN /* Declared as 'noreturn' until it is implemented */ 838 static void 839 _MHD_dumbClient_recv_reply (struct _MHD_dumbClient *clnt) 840 { 841 (void) clnt; 842 externalErrorExitDesc ("Not implemented for this test"); 843 } 844 845 846 int 847 _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt) 848 { 849 return DUMB_CLIENT_REQ_SENT <= clnt->stage; 850 } 851 852 853 /* internal */ 854 static void 855 _MHD_dumbClient_socket_close (struct _MHD_dumbClient *clnt) 856 { 857 if (MHD_INVALID_SOCKET != clnt->sckt) 858 { 859 if (use_hard_close) 860 { 861 #ifdef SO_LINGER 862 static const struct linger hard_close = {1, 0}; 863 mhd_assert (0 == hard_close.l_linger); 864 if (0 != setsockopt (clnt->sckt, SOL_SOCKET, SO_LINGER, 865 (const void *) &hard_close, sizeof (hard_close))) 866 #endif /* SO_LINGER */ 867 externalErrorExitDesc ("Failed to set SO_LINGER option"); 868 } 869 if (! MHD_socket_close_ (clnt->sckt)) 870 externalErrorExitDesc ("Unexpected error while closing " \ 871 "the client socket"); 872 clnt->sckt = MHD_INVALID_SOCKET; 873 } 874 } 875 876 877 /* internal */ 878 static void 879 _MHD_dumbClient_finalize (struct _MHD_dumbClient *clnt) 880 { 881 if (MHD_INVALID_SOCKET != clnt->sckt) 882 { 883 if (use_shutdown) 884 { 885 if (0 != shutdown (clnt->sckt, SHUT_WR)) 886 { 887 const int err = MHD_socket_get_error_ (); 888 if (! MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_) && 889 ! MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err)) 890 mhdErrorExitDesc ("Unexpected error when shutting down " \ 891 "the client socket"); 892 } 893 } 894 else if (use_close) 895 { 896 _MHD_dumbClient_socket_close (clnt); 897 } 898 else 899 mhd_assert (0); 900 } 901 clnt->stage = DUMB_CLIENT_FINISHED; 902 } 903 904 905 /* internal */ 906 static int 907 _MHD_dumbClient_needs_send (const struct _MHD_dumbClient *clnt) 908 { 909 return ((DUMB_CLIENT_CONNECTING <= clnt->stage) && 910 (DUMB_CLIENT_REQ_SENT > clnt->stage)) || 911 (DUMB_CLIENT_FINISHING == clnt->stage); 912 } 913 914 915 /* internal */ 916 static int 917 _MHD_dumbClient_needs_recv (const struct _MHD_dumbClient *clnt) 918 { 919 return (DUMB_CLIENT_HEADER_RECVEIVING <= clnt->stage) && 920 (DUMB_CLIENT_BODY_RECVEIVED > clnt->stage); 921 } 922 923 924 /* internal */ 925 /** 926 * Check whether the client needs unconditionally process the data. 927 * @param clnt the client to check 928 * @return non-zero if client needs unconditionally process the data, 929 * zero otherwise. 930 */ 931 static int 932 _MHD_dumbClient_needs_process (const struct _MHD_dumbClient *clnt) 933 { 934 switch (clnt->stage) 935 { 936 case DUMB_CLIENT_INIT: 937 case DUMB_CLIENT_REQ_SENT: 938 case DUMB_CLIENT_HEADER_RECVEIVED: 939 case DUMB_CLIENT_BODY_RECVEIVED: 940 case DUMB_CLIENT_FINISHED: 941 return ! 0; 942 case DUMB_CLIENT_CONNECTING: 943 case DUMB_CLIENT_CONNECTED: 944 case DUMB_CLIENT_REQ_SENDING: 945 case DUMB_CLIENT_HEADER_RECVEIVING: 946 case DUMB_CLIENT_BODY_RECVEIVING: 947 case DUMB_CLIENT_FINISHING: 948 default: 949 break; 950 } 951 return 0; 952 } 953 954 955 /** 956 * Process the client data with send()/recv() as needed. 957 * @param clnt the client to process 958 * @return non-zero if client finished processing the request, 959 * zero otherwise. 960 */ 961 int 962 _MHD_dumbClient_process (struct _MHD_dumbClient *clnt) 963 { 964 do 965 { 966 switch (clnt->stage) 967 { 968 case DUMB_CLIENT_INIT: 969 _MHD_dumbClient_connect_init (clnt); 970 break; 971 case DUMB_CLIENT_CONNECTING: 972 _MHD_dumbClient_connect_finish (clnt); 973 break; 974 case DUMB_CLIENT_CONNECTED: 975 case DUMB_CLIENT_REQ_SENDING: 976 _MHD_dumbClient_send_req (clnt); 977 break; 978 case DUMB_CLIENT_REQ_SENT: 979 mhd_assert (0); 980 clnt->stage = DUMB_CLIENT_HEADER_RECVEIVING; 981 break; 982 case DUMB_CLIENT_HEADER_RECVEIVING: 983 _MHD_dumbClient_recv_reply (clnt); 984 break; 985 case DUMB_CLIENT_HEADER_RECVEIVED: 986 clnt->stage = DUMB_CLIENT_BODY_RECVEIVING; 987 break; 988 case DUMB_CLIENT_BODY_RECVEIVING: 989 _MHD_dumbClient_recv_reply (clnt); 990 break; 991 case DUMB_CLIENT_BODY_RECVEIVED: 992 clnt->stage = DUMB_CLIENT_FINISHING; 993 break; 994 case DUMB_CLIENT_FINISHING: 995 _MHD_dumbClient_finalize (clnt); 996 break; 997 case DUMB_CLIENT_FINISHED: 998 return ! 0; 999 default: 1000 mhd_assert (0); 1001 mhdErrorExit (); 1002 } 1003 } while (_MHD_dumbClient_needs_process (clnt)); 1004 return DUMB_CLIENT_FINISHED == clnt->stage; 1005 } 1006 1007 1008 void 1009 _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt, 1010 MHD_socket *maxsckt, 1011 fd_set *rs, fd_set *ws, fd_set *es) 1012 { 1013 mhd_assert (NULL != rs); 1014 mhd_assert (NULL != ws); 1015 mhd_assert (NULL != es); 1016 if (DUMB_CLIENT_FINISHED > clnt->stage) 1017 { 1018 if (MHD_INVALID_SOCKET != clnt->sckt) 1019 { 1020 if ( (MHD_INVALID_SOCKET == *maxsckt) || 1021 (clnt->sckt > *maxsckt) ) 1022 *maxsckt = clnt->sckt; 1023 if (_MHD_dumbClient_needs_recv (clnt)) 1024 FD_SET (clnt->sckt, rs); 1025 if (_MHD_dumbClient_needs_send (clnt)) 1026 FD_SET (clnt->sckt, ws); 1027 FD_SET (clnt->sckt, es); 1028 } 1029 } 1030 } 1031 1032 1033 /** 1034 * Process the client data with send()/recv() as needed based on 1035 * information in fd_sets. 1036 * @param clnt the client to process 1037 * @return non-zero if client finished processing the request, 1038 * zero otherwise. 1039 */ 1040 int 1041 _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt, 1042 fd_set *rs, fd_set *ws, fd_set *es) 1043 { 1044 if (_MHD_dumbClient_needs_process (clnt)) 1045 return _MHD_dumbClient_process (clnt); 1046 else if (MHD_INVALID_SOCKET != clnt->sckt) 1047 { 1048 if (_MHD_dumbClient_needs_recv (clnt) && FD_ISSET (clnt->sckt, rs)) 1049 return _MHD_dumbClient_process (clnt); 1050 else if (_MHD_dumbClient_needs_send (clnt) && FD_ISSET (clnt->sckt, ws)) 1051 return _MHD_dumbClient_process (clnt); 1052 else if (FD_ISSET (clnt->sckt, es)) 1053 return _MHD_dumbClient_process (clnt); 1054 } 1055 return DUMB_CLIENT_FINISHED == clnt->stage; 1056 } 1057 1058 1059 /** 1060 * Perform full request. 1061 * @param clnt the client to run 1062 * @return zero if client finished processing the request, 1063 * non-zero if timeout is reached. 1064 */ 1065 int 1066 _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt) 1067 { 1068 time_t start; 1069 time_t now; 1070 start = time (NULL); 1071 now = start; 1072 do 1073 { 1074 fd_set rs; 1075 fd_set ws; 1076 fd_set es; 1077 MHD_socket maxMhdSk; 1078 struct timeval tv; 1079 1080 FD_ZERO (&rs); 1081 FD_ZERO (&ws); 1082 FD_ZERO (&es); 1083 1084 if (! _MHD_dumbClient_needs_process (clnt)) 1085 { 1086 maxMhdSk = MHD_INVALID_SOCKET; 1087 _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es); 1088 mhd_assert (now >= start); 1089 #if ! defined(_WIN32) || defined(__CYGWIN__) 1090 tv.tv_sec = (time_t) (TIMEOUTS_VAL * 2 - (now - start) + 1); 1091 #else /* Native W32 */ 1092 tv.tv_sec = (long) (TIMEOUTS_VAL * 2 - (now - start) + 1); 1093 #endif /* Native W32 */ 1094 tv.tv_usec = 250 * 1000; 1095 if (-1 == select ((int) maxMhdSk + 1, &rs, &ws, &es, &tv)) 1096 { 1097 #ifdef MHD_POSIX_SOCKETS 1098 if (EINTR != errno) 1099 externalErrorExitDesc ("Unexpected select() error"); 1100 #else /* ! MHD_POSIX_SOCKETS */ 1101 mhd_assert ((0 != rs.fd_count) || (0 != ws.fd_count) || \ 1102 (0 != es.fd_count)); 1103 externalErrorExitDesc ("Unexpected select() error"); 1104 Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 1105 #endif /* ! MHD_POSIX_SOCKETS */ 1106 continue; 1107 } 1108 if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es)) 1109 return 0; 1110 } 1111 /* Use double timeout value here as MHD must catch timeout situations 1112 * in this test. Timeout in client as a last resort. */ 1113 } while ((now = time (NULL)) - start <= (TIMEOUTS_VAL * 2)); 1114 return 1; 1115 } 1116 1117 1118 /** 1119 * Close the client and free internally allocated resources. 1120 * @param clnt the client to close 1121 */ 1122 void 1123 _MHD_dumbClient_close (struct _MHD_dumbClient *clnt) 1124 { 1125 if (DUMB_CLIENT_FINISHED != clnt->stage) 1126 _MHD_dumbClient_finalize (clnt); 1127 _MHD_dumbClient_socket_close (clnt); 1128 if (NULL != clnt->send_buf) 1129 { 1130 mhd_assert (clnt->send_buf == clnt->buf); 1131 free (clnt->buf); 1132 clnt->buf = NULL; 1133 clnt->send_buf = NULL; 1134 } 1135 free (clnt); 1136 } 1137 1138 1139 struct sckt_notif_cb_param 1140 { 1141 volatile unsigned int num_started; 1142 volatile unsigned int num_finished; 1143 }; 1144 1145 static void 1146 socket_cb (void *cls, 1147 struct MHD_Connection *c, 1148 void **socket_context, 1149 enum MHD_ConnectionNotificationCode toe) 1150 { 1151 struct sckt_notif_cb_param *param = (struct sckt_notif_cb_param *) cls; 1152 if (NULL == socket_context) 1153 mhdErrorExitDesc ("'socket_context' pointer is NULL"); 1154 if (NULL == c) 1155 mhdErrorExitDesc ("'connection' pointer is NULL"); 1156 if (NULL == param) 1157 mhdErrorExitDesc ("'cls' pointer is NULL"); 1158 1159 if (MHD_CONNECTION_NOTIFY_STARTED == toe) 1160 param->num_started++; 1161 else if (MHD_CONNECTION_NOTIFY_CLOSED == toe) 1162 param->num_finished++; 1163 else 1164 mhdErrorExitDesc ("Unknown 'toe' value"); 1165 } 1166 1167 1168 struct term_notif_cb_param 1169 { 1170 volatile int term_reason; 1171 volatile unsigned int num_called; 1172 }; 1173 1174 1175 static void 1176 term_cb (void *cls, 1177 struct MHD_Connection *c, 1178 void **req_cls, 1179 enum MHD_RequestTerminationCode term_code) 1180 { 1181 struct term_notif_cb_param *param = (struct term_notif_cb_param *) cls; 1182 if (NULL == req_cls) 1183 mhdErrorExitDesc ("'req_cls' pointer is NULL"); 1184 if (NULL == c) 1185 mhdErrorExitDesc ("'connection' pointer is NULL"); 1186 if (NULL == param) 1187 mhdErrorExitDesc ("'cls' pointer is NULL"); 1188 param->term_reason = (int) term_code; 1189 param->num_called++; 1190 } 1191 1192 1193 static const char * 1194 term_reason_str (enum MHD_RequestTerminationCode term_code) 1195 { 1196 switch ((int) term_code) 1197 { 1198 case MHD_REQUEST_TERMINATED_COMPLETED_OK: 1199 return "COMPLETED_OK"; 1200 case MHD_REQUEST_TERMINATED_WITH_ERROR: 1201 return "TERMINATED_WITH_ERROR"; 1202 case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED: 1203 return "TIMEOUT_REACHED"; 1204 case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN: 1205 return "DAEMON_SHUTDOWN"; 1206 case MHD_REQUEST_TERMINATED_READ_ERROR: 1207 return "READ_ERROR"; 1208 case MHD_REQUEST_TERMINATED_CLIENT_ABORT: 1209 return "CLIENT_ABORT"; 1210 case -1: 1211 return "(not called)"; 1212 default: 1213 break; 1214 } 1215 return "(unknown code)"; 1216 } 1217 1218 1219 struct check_uri_cls 1220 { 1221 const char *volatile uri; 1222 volatile unsigned int cb_called; 1223 }; 1224 1225 static void * 1226 check_uri_cb (void *cls, 1227 const char *uri, 1228 struct MHD_Connection *con) 1229 { 1230 struct check_uri_cls *param = (struct check_uri_cls *) cls; 1231 1232 if (NULL == con) 1233 mhdErrorExitDesc ("The 'con' pointer is NULL"); 1234 1235 param->cb_called++; 1236 1237 if (0 != strcmp (param->uri, 1238 uri)) 1239 { 1240 fprintf (stderr, "Wrong URI: '%s'\n", uri); 1241 mhdErrorExit (); 1242 } 1243 return NULL; 1244 } 1245 1246 1247 struct mhd_header_checker_param 1248 { 1249 int found_header_host; /**< the number of 'Host' headers */ 1250 int found_header_ua; /**< the number of 'User-Agent' headers */ 1251 int found_header_ct; /**< the number of 'Content-Type' headers */ 1252 int found_header_cl; /**< the number of 'Content-Length' headers */ 1253 int found_header_te; /**< the number of 'Transfer-Encoding' headers */ 1254 }; 1255 1256 static enum MHD_Result 1257 headerCheckerInterator (void *cls, 1258 enum MHD_ValueKind kind, 1259 const char *key, 1260 size_t key_size, 1261 const char *value, 1262 size_t value_size) 1263 { 1264 struct mhd_header_checker_param *const param = 1265 (struct mhd_header_checker_param *) cls; 1266 1267 if (NULL == param) 1268 mhdErrorExitDesc ("cls parameter is NULL"); 1269 1270 if (MHD_HEADER_KIND != kind) 1271 return MHD_YES; /* Continue iteration */ 1272 1273 if (0 == key_size) 1274 mhdErrorExitDesc ("Zero key length"); 1275 1276 if ((strlen (REQ_HEADER_HOST_NAME) == key_size) && 1277 (0 == memcmp (key, REQ_HEADER_HOST_NAME, key_size))) 1278 { 1279 if ((strlen (REQ_HEADER_HOST_VALUE) == value_size) && 1280 (0 == memcmp (value, REQ_HEADER_HOST_VALUE, value_size))) 1281 param->found_header_host++; 1282 else 1283 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n", 1284 (int) value_size, value, REQ_HEADER_HOST_VALUE); 1285 } 1286 else if ((strlen (REQ_HEADER_UA_NAME) == key_size) && 1287 (0 == memcmp (key, REQ_HEADER_UA_NAME, key_size))) 1288 { 1289 if ((strlen (REQ_HEADER_UA_VALUE) == value_size) && 1290 (0 == memcmp (value, REQ_HEADER_UA_VALUE, value_size))) 1291 param->found_header_ua++; 1292 else 1293 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n", 1294 (int) value_size, value, REQ_HEADER_UA_VALUE); 1295 } 1296 else if ((strlen (REQ_HEADER_CT_NAME) == key_size) && 1297 (0 == memcmp (key, REQ_HEADER_CT_NAME, key_size))) 1298 { 1299 if ((strlen (REQ_HEADER_CT_VALUE) == value_size) && 1300 (0 == memcmp (value, REQ_HEADER_CT_VALUE, value_size))) 1301 param->found_header_ct++; 1302 else 1303 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n", 1304 (int) value_size, value, REQ_HEADER_CT_VALUE); 1305 } 1306 else if ((strlen (MHD_HTTP_HEADER_CONTENT_LENGTH) == key_size) && 1307 (0 == memcmp (key, MHD_HTTP_HEADER_CONTENT_LENGTH, key_size))) 1308 { 1309 /* do not check value of the header here for simplicity */ 1310 param->found_header_cl++; 1311 } 1312 else if ((strlen (MHD_HTTP_HEADER_TRANSFER_ENCODING) == key_size) && 1313 (0 == memcmp (key, MHD_HTTP_HEADER_TRANSFER_ENCODING, key_size))) 1314 { 1315 if ((strlen ("chunked") == value_size) && 1316 (0 == memcmp (value, "chunked", value_size))) 1317 param->found_header_te++; 1318 else 1319 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n", 1320 (int) value_size, value, "chunked"); 1321 } 1322 return MHD_YES; 1323 } 1324 1325 1326 struct ahc_cls_type 1327 { 1328 const char *volatile rp_data; 1329 volatile size_t rp_data_size; 1330 const char *volatile rq_method; 1331 const char *volatile rq_url; 1332 const char *volatile req_body; 1333 volatile unsigned int cb_called; /* Non-zero indicates that callback was called at least one time */ 1334 size_t req_body_size; /**< The number of bytes in @a req_body */ 1335 size_t req_body_uploaded; /* Updated by callback */ 1336 }; 1337 1338 1339 static enum MHD_Result 1340 ahcCheck (void *cls, 1341 struct MHD_Connection *connection, 1342 const char *url, 1343 const char *method, 1344 const char *version, 1345 const char *upload_data, size_t *upload_data_size, 1346 void **req_cls) 1347 { 1348 static int marker; 1349 enum MHD_Result ret; 1350 struct mhd_header_checker_param header_check_param; 1351 struct ahc_cls_type *const param = (struct ahc_cls_type *) cls; 1352 1353 if (NULL == param) 1354 mhdErrorExitDesc ("cls parameter is NULL"); 1355 param->cb_called++; 1356 1357 if (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) 1358 mhdErrorExitDesc ("Unexpected HTTP version"); 1359 1360 if (0 != strcmp (url, param->rq_url)) 1361 mhdErrorExitDesc ("Unexpected URI"); 1362 1363 if (0 != strcmp (param->rq_method, method)) 1364 mhdErrorExitDesc ("Unexpected request method"); 1365 1366 if (NULL == upload_data_size) 1367 mhdErrorExitDesc ("'upload_data_size' pointer is NULL"); 1368 1369 if (0 != *upload_data_size) 1370 { 1371 const char *const upload_body = param->req_body; 1372 if (NULL == upload_data) 1373 mhdErrorExitDesc ("'upload_data' is NULL while " \ 1374 "'*upload_data_size' value is not zero"); 1375 if (NULL == upload_body) 1376 mhdErrorExitDesc ("'*upload_data_size' value is not zero " \ 1377 "while no request body is expected"); 1378 if (param->req_body_uploaded + *upload_data_size > param->req_body_size) 1379 { 1380 fprintf (stderr, "Too large upload body received. Got %u, expected %u", 1381 (unsigned int) (param->req_body_uploaded + *upload_data_size), 1382 (unsigned int) param->req_body_size); 1383 mhdErrorExit (); 1384 } 1385 if (0 != memcmp (upload_data, upload_body + param->req_body_uploaded, 1386 *upload_data_size)) 1387 { 1388 fprintf (stderr, "Unexpected request body at offset %u: " \ 1389 "'%.*s', expected: '%.*s'\n", 1390 (unsigned int) param->req_body_uploaded, 1391 (int) *upload_data_size, upload_data, 1392 (int) *upload_data_size, upload_body + param->req_body_uploaded); 1393 mhdErrorExit (); 1394 } 1395 param->req_body_uploaded += *upload_data_size; 1396 *upload_data_size = 0; 1397 } 1398 1399 if (&marker != *req_cls) 1400 { 1401 /* The first call of the callback for this connection */ 1402 mhd_assert (NULL == upload_data); 1403 param->req_body_uploaded = 0; 1404 1405 *req_cls = ▮ 1406 return MHD_YES; 1407 } 1408 1409 memset (&header_check_param, 0, sizeof(header_check_param)); 1410 if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND, 1411 &headerCheckerInterator, 1412 &header_check_param)) 1413 mhdErrorExitDesc ("Wrong number of headers in the request"); 1414 if (1 != header_check_param.found_header_host) 1415 mhdErrorExitDesc ("'Host' header has not been detected in request"); 1416 if (1 != header_check_param.found_header_ua) 1417 mhdErrorExitDesc ("'User-Agent' header has not been detected in request"); 1418 if (1 != header_check_param.found_header_ct) 1419 mhdErrorExitDesc ("'Content-Type' header has not been detected in request"); 1420 if (! upl_chunked && (1 != header_check_param.found_header_cl)) 1421 mhdErrorExitDesc ("'Content-Length' header has not been detected " 1422 "in request"); 1423 if (upl_chunked && (1 != header_check_param.found_header_te)) 1424 mhdErrorExitDesc ("'Transfer-Encoding' header has not been detected " 1425 "in request"); 1426 1427 if (NULL != upload_data) 1428 return MHD_YES; /* Full request has not been received so far */ 1429 1430 #if 0 /* Code unused in this test */ 1431 struct MHD_Response *response; 1432 response = MHD_create_response_from_buffer (param->rp_data_size, 1433 (void *) param->rp_data, 1434 MHD_RESPMEM_MUST_COPY); 1435 if (NULL == response) 1436 mhdErrorExitDesc ("Failed to create response"); 1437 1438 ret = MHD_queue_response (connection, 1439 MHD_HTTP_OK, 1440 response); 1441 MHD_destroy_response (response); 1442 if (MHD_YES != ret) 1443 mhdErrorExitDesc ("Failed to queue response"); 1444 #else 1445 if (NULL == upload_data) 1446 mhdErrorExitDesc ("Full request received, " \ 1447 "while incomplete request expected"); 1448 ret = MHD_NO; 1449 #endif 1450 1451 return ret; 1452 } 1453 1454 1455 struct simpleQueryParams 1456 { 1457 /* Destination path for HTTP query */ 1458 const char *queryPath; 1459 1460 /* Custom query method, NULL for default */ 1461 const char *method; 1462 1463 /* Destination port for HTTP query */ 1464 uint16_t queryPort; 1465 1466 /* Additional request headers, static */ 1467 const char *headers; 1468 1469 /* NULL for request without body */ 1470 const uint8_t *req_body; 1471 size_t req_body_size; 1472 1473 /* Non-zero to use chunked encoding for request body */ 1474 int chunked; 1475 1476 /* Max size of data for single 'send()' call */ 1477 size_t step_size; 1478 1479 /* Limit for total amount of sent data */ 1480 size_t total_send_max; 1481 1482 /* HTTP query result error flag */ 1483 volatile int queryError; 1484 1485 /* Response HTTP code, zero if no response */ 1486 volatile int responseCode; 1487 }; 1488 1489 1490 /* returns non-zero if timed-out */ 1491 static int 1492 performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt) 1493 { 1494 time_t start; 1495 struct timeval tv; 1496 int ret; 1497 const union MHD_DaemonInfo *di; 1498 MHD_socket lstn_sk; 1499 int client_accepted; 1500 int full_req_recieved; 1501 int full_req_sent; 1502 int some_data_recieved; 1503 1504 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD); 1505 if (NULL == di) 1506 mhdErrorExitDesc ("Cannot get lister socket"); 1507 lstn_sk = di->listen_fd; 1508 1509 ret = 1; /* will be replaced with real result */ 1510 client_accepted = 0; 1511 1512 _MHD_dumbClient_start_connect (clnt); 1513 1514 full_req_recieved = 0; 1515 some_data_recieved = 0; 1516 start = time (NULL); 1517 do 1518 { 1519 fd_set rs; 1520 fd_set ws; 1521 fd_set es; 1522 MHD_socket maxMhdSk; 1523 int num_ready; 1524 int do_client; /**< Process data in client */ 1525 1526 maxMhdSk = MHD_INVALID_SOCKET; 1527 FD_ZERO (&rs); 1528 FD_ZERO (&ws); 1529 FD_ZERO (&es); 1530 if (NULL == clnt) 1531 { 1532 /* client has finished, check whether MHD is still 1533 * processing any connections */ 1534 full_req_sent = 1; 1535 do_client = 0; 1536 if (client_accepted && (0 > MHD_get_timeout64s (d))) 1537 { 1538 ret = 0; 1539 break; /* MHD finished as well */ 1540 } 1541 } 1542 else 1543 { 1544 full_req_sent = _MHD_dumbClient_is_req_sent (clnt); 1545 if (! full_req_sent) 1546 do_client = 1; /* Request hasn't been sent yet, send the data */ 1547 else 1548 { 1549 /* All request data has been sent. 1550 * Client will close the socket as the next step. */ 1551 if (full_req_recieved) 1552 { 1553 /* All data has been received by the MHD */ 1554 do_client = 1; /* Close the client socket */ 1555 } 1556 else if (some_data_recieved && 1557 (! use_hard_close || ((0 == rate_limiter) && use_stress_os))) 1558 { 1559 /* No RST rate limiter or no "hard close", no need to avoid extra RST 1560 * and at least something was received by the MHD */ 1561 /* In case of 'hard close' this can stress the OS, especially 1562 * if 'by_step' is enabled as several ACKs (for delivered packets 1563 * containing the request) from the server may arrive to the client 1564 * when the client has closed port and may be reflected by several 1565 * RSTs from the client side to the server side (when ACK received 1566 * without active connection then RST packet should be sent). 1567 * When listening socket receives RST packets, it may block 1568 * the sender preventing the next connection. */ 1569 do_client = 1; /* Proceed with the closure of the client socket */ 1570 } 1571 else 1572 { 1573 /* When rate limiter is enabled, all sent packets must be received 1574 * before client closes connection to avoid RST for every ACK. 1575 * When rate limiter is not enabled, the MHD must receive at 1576 * least something before closing the connection. */ 1577 do_client = 0; /* Do not close the client socket yet */ 1578 } 1579 } 1580 1581 if (do_client) 1582 _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es); 1583 } 1584 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) 1585 mhdErrorExitDesc ("MHD_get_fdset() failed"); 1586 if (do_client) 1587 { 1588 tv.tv_sec = 1; 1589 tv.tv_usec = 250 * 1000; 1590 } 1591 else 1592 { /* Request completely sent but not yet fully received */ 1593 tv.tv_sec = 0; 1594 tv.tv_usec = FINAL_PACKETS_MS * 1000; 1595 } 1596 num_ready = select ((int) maxMhdSk + 1, &rs, &ws, &es, &tv); 1597 if (-1 == num_ready) 1598 { 1599 #ifdef MHD_POSIX_SOCKETS 1600 if (EINTR != errno) 1601 externalErrorExitDesc ("Unexpected select() error"); 1602 #else 1603 if ((WSAEINVAL != WSAGetLastError ()) || 1604 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 1605 externalErrorExitDesc ("Unexpected select() error"); 1606 Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 1607 #endif 1608 continue; 1609 } 1610 if (0 == num_ready) 1611 { /* select() finished by timeout, looks like no more packets are pending */ 1612 if (do_client) 1613 externalErrorExitDesc ("Timeout waiting for sockets"); 1614 if (full_req_sent && (! full_req_recieved)) 1615 full_req_recieved = 1; 1616 } 1617 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) 1618 mhdErrorExitDesc ("MHD_run_from_select() failed"); 1619 if (! client_accepted) 1620 client_accepted = FD_ISSET (lstn_sk, &rs); 1621 else 1622 { /* Client connection was already accepted by MHD */ 1623 if (! some_data_recieved) 1624 { 1625 if (! do_client) 1626 { 1627 if (0 != num_ready) 1628 { /* Connection was accepted before, "ready" socket means data */ 1629 some_data_recieved = 1; 1630 } 1631 } 1632 else 1633 { 1634 if (2 == num_ready) 1635 some_data_recieved = 1; 1636 else if ((1 == num_ready) && 1637 ((MHD_INVALID_SOCKET == clnt->sckt) || 1638 ! FD_ISSET (clnt->sckt, &ws))) 1639 some_data_recieved = 1; 1640 } 1641 } 1642 } 1643 if (do_client) 1644 { 1645 if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es)) 1646 clnt = NULL; 1647 } 1648 /* Use double timeout value here so MHD would be able to catch timeout 1649 * internally */ 1650 } while (time (NULL) - start <= (TIMEOUTS_VAL * 2)); 1651 1652 return ret; 1653 } 1654 1655 1656 /* Returns zero for successful response and non-zero for failed response */ 1657 static int 1658 doClientQueryInThread (struct MHD_Daemon *d, 1659 struct simpleQueryParams *p) 1660 { 1661 const union MHD_DaemonInfo *dinfo; 1662 struct _MHD_dumbClient *c; 1663 int errornum; 1664 int use_external_poll; 1665 1666 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS); 1667 if (NULL == dinfo) 1668 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 1669 use_external_poll = (0 == (dinfo->flags 1670 & MHD_USE_INTERNAL_POLLING_THREAD)); 1671 1672 if (0 == p->queryPort) 1673 externalErrorExit (); 1674 1675 c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath, 1676 p->headers, p->req_body, p->req_body_size, 1677 p->chunked); 1678 _MHD_dumbClient_set_send_limits (c, p->step_size, p->total_send_max); 1679 1680 /* 'internal' polling should not be used in this test */ 1681 mhd_assert (use_external_poll); 1682 if (! use_external_poll) 1683 errornum = _MHD_dumbClient_perform (c); 1684 else 1685 errornum = performQueryExternal (d, c); 1686 1687 if (errornum) 1688 fprintf (stderr, "Request timeout out.\n"); 1689 1690 _MHD_dumbClient_close (c); 1691 1692 return errornum; 1693 } 1694 1695 1696 static void 1697 printTestResults (FILE *stream, 1698 struct simpleQueryParams *qParam, 1699 struct ahc_cls_type *ahc_param, 1700 struct check_uri_cls *uri_cb_param, 1701 struct term_notif_cb_param *term_result, 1702 struct sckt_notif_cb_param *sckt_result) 1703 { 1704 if (stderr != stream) 1705 fflush (stderr); 1706 fprintf (stream, " Request aborted at %u byte%s.", 1707 (unsigned int) qParam->total_send_max, 1708 1 == qParam->total_send_max ? "" : "s"); 1709 if ((1 == sckt_result->num_started) && (1 == sckt_result->num_finished)) 1710 fprintf (stream, " One socket has been accepted and then closed."); 1711 else 1712 fprintf (stream, " Sockets have been accepted %u time%s" 1713 " and closed %u time%s.", sckt_result->num_started, 1714 (1 == sckt_result->num_started) ? "" : "s", 1715 sckt_result->num_finished, 1716 (1 == sckt_result->num_finished) ? "" : "s"); 1717 if (0 == uri_cb_param->cb_called) 1718 fprintf (stream, " URI callback has NOT been called."); 1719 else 1720 fprintf (stream, " URI callback has been called %u time%s.", 1721 uri_cb_param->cb_called, 1722 1 == uri_cb_param->cb_called ? "" : "s"); 1723 if (0 == ahc_param->cb_called) 1724 fprintf (stream, " Access handler callback has NOT been called."); 1725 else 1726 fprintf (stream, " Access handler callback has been called %u time%s.", 1727 ahc_param->cb_called, 1728 1 == ahc_param->cb_called ? "" : "s"); 1729 if (0 == term_result->num_called) 1730 fprintf (stream, " Final notification callback has NOT been called."); 1731 else 1732 fprintf (stream, " Final notification callback has been called %u time%s " 1733 "with %s code.", term_result->num_called, 1734 (1 == term_result->num_called) ? "" : "s", 1735 term_reason_str ((enum MHD_RequestTerminationCode) 1736 term_result->term_reason)); 1737 fprintf (stream, "\n"); 1738 fflush (stream); 1739 } 1740 1741 1742 /* Perform test queries, shut down MHD daemon, and free parameters */ 1743 static unsigned int 1744 performTestQueries (struct MHD_Daemon *d, uint16_t d_port, 1745 struct ahc_cls_type *ahc_param, 1746 struct check_uri_cls *uri_cb_param, 1747 struct term_notif_cb_param *term_result, 1748 struct sckt_notif_cb_param *sckt_result) 1749 { 1750 struct simpleQueryParams qParam; 1751 time_t start; 1752 unsigned int ret = 0; /* Return value */ 1753 size_t req_total_size; 1754 size_t limit_send_size; 1755 size_t inc_size; 1756 int expected_reason; 1757 int found_right_reason; 1758 1759 /* Common parameters, to be individually overridden by specific test cases 1760 * if needed */ 1761 qParam.queryPort = d_port; 1762 qParam.method = MHD_HTTP_METHOD_PUT; 1763 qParam.queryPath = EXPECTED_URI_BASE_PATH; 1764 qParam.headers = REQ_HEADER_CT; 1765 qParam.req_body = (const uint8_t *) REQ_BODY; 1766 qParam.req_body_size = MHD_STATICSTR_LEN_ (REQ_BODY); 1767 qParam.chunked = upl_chunked; 1768 qParam.step_size = by_step ? 1 : 0; 1769 1770 uri_cb_param->uri = EXPECTED_URI_BASE_PATH; 1771 1772 ahc_param->rq_url = EXPECTED_URI_BASE_PATH; 1773 ahc_param->rq_method = MHD_HTTP_METHOD_PUT; 1774 ahc_param->rp_data = "~"; 1775 ahc_param->rp_data_size = 1; 1776 ahc_param->req_body = (const char *) qParam.req_body; 1777 ahc_param->req_body_size = qParam.req_body_size; 1778 1779 do 1780 { 1781 struct _MHD_dumbClient *test_c; 1782 struct simpleQueryParams *p = &qParam; 1783 test_c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath, 1784 p->headers, p->req_body, p->req_body_size, 1785 p->chunked); 1786 req_total_size = test_c->req_size; 1787 _MHD_dumbClient_close (test_c); 1788 } while (0); 1789 1790 expected_reason = use_hard_close ? 1791 MHD_REQUEST_TERMINATED_READ_ERROR : 1792 MHD_REQUEST_TERMINATED_CLIENT_ABORT; 1793 found_right_reason = 0; 1794 if (0 != rate_limiter) 1795 { 1796 if (verbose) 1797 { 1798 printf ("Pausing for rate limiter..."); 1799 fflush (stdout); 1800 } 1801 _MHD_sleep (1150); /* Just a bit more than one second */ 1802 if (verbose) 1803 { 1804 printf (" OK\n"); 1805 fflush (stdout); 1806 } 1807 inc_size = ((req_total_size - 1) + (rate_limiter - 1)) / rate_limiter; 1808 if (0 == inc_size) 1809 inc_size = 1; 1810 } 1811 else 1812 inc_size = 1; 1813 1814 start = time (NULL); 1815 for (limit_send_size = 1; limit_send_size < req_total_size; 1816 limit_send_size += inc_size) 1817 { 1818 int test_succeed; 1819 test_succeed = 0; 1820 /* Make sure that maximum size is tested */ 1821 if (req_total_size - inc_size < limit_send_size) 1822 limit_send_size = req_total_size - 1; 1823 qParam.total_send_max = limit_send_size; 1824 /* To be updated by callbacks */ 1825 ahc_param->cb_called = 0; 1826 uri_cb_param->cb_called = 0; 1827 term_result->num_called = 0; 1828 term_result->term_reason = -1; 1829 sckt_result->num_started = 0; 1830 sckt_result->num_finished = 0; 1831 1832 if (0 != doClientQueryInThread (d, &qParam)) 1833 fprintf (stderr, "FAILED: connection has NOT been closed by MHD."); 1834 else 1835 { 1836 if ((-1 != term_result->term_reason) && 1837 (MHD_REQUEST_TERMINATED_READ_ERROR != term_result->term_reason) && 1838 (MHD_REQUEST_TERMINATED_CLIENT_ABORT != term_result->term_reason) ) 1839 fprintf (stderr, "FAILED: Wrong termination code."); 1840 else if ((0 == term_result->num_called) && 1841 ((0 != uri_cb_param->cb_called) || (0 != ahc_param->cb_called))) 1842 fprintf (stderr, "FAILED: Missing required call of final notification " 1843 "callback."); 1844 else if (1 < uri_cb_param->cb_called) 1845 fprintf (stderr, "FAILED: Too many URI callbacks."); 1846 else if ((0 != ahc_param->cb_called) && (0 == uri_cb_param->cb_called)) 1847 fprintf (stderr, "FAILED: URI callback has NOT been called " 1848 "while Access Handler callback has been called."); 1849 else if (1 < term_result->num_called) 1850 fprintf (stderr, "FAILED: Too many final callbacks."); 1851 else if (1 != sckt_result->num_started) 1852 fprintf (stderr, "FAILED: Wrong number of sockets accepted."); 1853 else if (1 != sckt_result->num_finished) 1854 fprintf (stderr, "FAILED: Wrong number of sockets closed."); 1855 else 1856 { 1857 test_succeed = 1; 1858 if (expected_reason == term_result->term_reason) 1859 found_right_reason = 1; 1860 } 1861 } 1862 1863 if (! test_succeed) 1864 { 1865 ret = 1; 1866 printTestResults (stderr, 1867 &qParam, ahc_param, uri_cb_param, 1868 term_result, sckt_result); 1869 } 1870 else if (verbose) 1871 { 1872 printf ("SUCCEED:"); 1873 printTestResults (stdout, 1874 &qParam, ahc_param, uri_cb_param, 1875 term_result, sckt_result); 1876 } 1877 1878 if (time (NULL) - start > 1879 (time_t) ((TIMEOUTS_VAL * 25) 1880 + (rate_limiter * FINAL_PACKETS_MS) / 1000 + 1)) 1881 { 1882 ret |= 1 << 2; 1883 fprintf (stderr, "FAILED: Test total time exceeded.\n"); 1884 break; 1885 } 1886 } 1887 1888 MHD_stop_daemon (d); 1889 free (uri_cb_param); 1890 free (ahc_param); 1891 free (term_result); 1892 free (sckt_result); 1893 1894 if (! found_right_reason) 1895 { 1896 #ifndef __gnu_hurd__ 1897 fprintf (stderr, "FAILED: termination callback was not called with " 1898 "expected (%s) reason.\n", 1899 term_reason_str ((enum MHD_RequestTerminationCode) 1900 expected_reason)); 1901 fflush (stderr); 1902 ret |= 1 << 1; 1903 #endif /* ! __gnu_hurd__ */ 1904 (void) 0; 1905 } 1906 1907 return ret; 1908 } 1909 1910 1911 enum testMhdThreadsType 1912 { 1913 testMhdThreadExternal = 0, 1914 testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD, 1915 testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION 1916 | MHD_USE_INTERNAL_POLLING_THREAD, 1917 testMhdThreadInternalPool 1918 }; 1919 1920 enum testMhdPollType 1921 { 1922 testMhdPollBySelect = 0, 1923 testMhdPollByPoll = MHD_USE_POLL, 1924 testMhdPollByEpoll = MHD_USE_EPOLL, 1925 testMhdPollAuto = MHD_USE_AUTO 1926 }; 1927 1928 /* Get number of threads for thread pool depending 1929 * on used poll function and test type. */ 1930 static unsigned int 1931 testNumThreadsForPool (enum testMhdPollType pollType) 1932 { 1933 unsigned int numThreads = MHD_CPU_COUNT; 1934 (void) pollType; /* Don't care about pollType for this test */ 1935 return numThreads; /* No practical limit for non-cleanup test */ 1936 } 1937 1938 1939 static struct MHD_Daemon * 1940 startTestMhdDaemon (enum testMhdThreadsType thrType, 1941 enum testMhdPollType pollType, uint16_t *pport, 1942 struct ahc_cls_type **ahc_param, 1943 struct check_uri_cls **uri_cb_param, 1944 struct term_notif_cb_param **term_result, 1945 struct sckt_notif_cb_param **sckt_result) 1946 { 1947 struct MHD_Daemon *d; 1948 const union MHD_DaemonInfo *dinfo; 1949 1950 if ((NULL == ahc_param) || (NULL == uri_cb_param) || (NULL == term_result)) 1951 externalErrorExit (); 1952 1953 *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type)); 1954 if (NULL == *ahc_param) 1955 externalErrorExit (); 1956 *uri_cb_param = 1957 (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls)); 1958 if (NULL == *uri_cb_param) 1959 externalErrorExit (); 1960 *term_result = 1961 (struct term_notif_cb_param *) malloc (sizeof(struct term_notif_cb_param)); 1962 if (NULL == *term_result) 1963 externalErrorExit (); 1964 *sckt_result = 1965 (struct sckt_notif_cb_param *) malloc (sizeof(struct sckt_notif_cb_param)); 1966 if (NULL == *sckt_result) 1967 externalErrorExit (); 1968 1969 if ( (0 == *pport) && 1970 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) 1971 { 1972 *pport = 4170; 1973 if (use_shutdown) 1974 *pport = (uint16_t) (*pport + 0); 1975 if (use_close) 1976 *pport = (uint16_t) (*pport + 1); 1977 if (use_hard_close) 1978 *pport = (uint16_t) (*pport + 1); 1979 if (by_step) 1980 *pport = (uint16_t) (*pport + (1 << 2)); 1981 if (upl_chunked) 1982 *pport = (uint16_t) (*pport + (1 << 3)); 1983 if (! oneone) 1984 *pport = (uint16_t) (*pport + (1 << 4)); 1985 } 1986 1987 if (testMhdThreadExternal == thrType) 1988 d = MHD_start_daemon (((unsigned int) pollType) 1989 | (verbose ? MHD_USE_ERROR_LOG : 0) 1990 | MHD_USE_NO_THREAD_SAFETY, 1991 *pport, NULL, NULL, 1992 &ahcCheck, *ahc_param, 1993 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 1994 *uri_cb_param, 1995 MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result, 1996 MHD_OPTION_NOTIFY_CONNECTION, &socket_cb, 1997 *sckt_result, 1998 MHD_OPTION_CONNECTION_TIMEOUT, 1999 (unsigned) TIMEOUTS_VAL, 2000 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 2001 MHD_OPTION_END); 2002 else if (testMhdThreadInternalPool != thrType) 2003 d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) 2004 | (verbose ? MHD_USE_ERROR_LOG : 0), 2005 *pport, NULL, NULL, 2006 &ahcCheck, *ahc_param, 2007 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 2008 *uri_cb_param, 2009 MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result, 2010 MHD_OPTION_NOTIFY_CONNECTION, &socket_cb, 2011 *sckt_result, 2012 MHD_OPTION_CONNECTION_TIMEOUT, 2013 (unsigned) TIMEOUTS_VAL, 2014 MHD_OPTION_END); 2015 else 2016 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD 2017 | ((unsigned int) pollType) 2018 | (verbose ? MHD_USE_ERROR_LOG : 0), 2019 *pport, NULL, NULL, 2020 &ahcCheck, *ahc_param, 2021 MHD_OPTION_THREAD_POOL_SIZE, 2022 testNumThreadsForPool (pollType), 2023 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 2024 *uri_cb_param, 2025 MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result, 2026 MHD_OPTION_NOTIFY_CONNECTION, &socket_cb, 2027 *sckt_result, 2028 MHD_OPTION_CONNECTION_TIMEOUT, 2029 (unsigned) TIMEOUTS_VAL, 2030 MHD_OPTION_END); 2031 2032 if (NULL == d) 2033 mhdErrorExitDesc ("Failed to start MHD daemon"); 2034 2035 if (0 == *pport) 2036 { 2037 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 2038 if ((NULL == dinfo) || (0 == dinfo->port)) 2039 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 2040 *pport = dinfo->port; 2041 if (0 == global_port) 2042 global_port = *pport; /* Reuse the same port for all tests */ 2043 } 2044 2045 return d; 2046 } 2047 2048 2049 /* Test runners */ 2050 2051 2052 static unsigned int 2053 testExternalGet (void) 2054 { 2055 struct MHD_Daemon *d; 2056 uint16_t d_port = global_port; /* Daemon's port */ 2057 struct ahc_cls_type *ahc_param; 2058 struct check_uri_cls *uri_cb_param; 2059 struct term_notif_cb_param *term_result; 2060 struct sckt_notif_cb_param *sckt_result; 2061 2062 d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port, 2063 &ahc_param, &uri_cb_param, &term_result, 2064 &sckt_result); 2065 2066 return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result, 2067 sckt_result); 2068 } 2069 2070 2071 #if 0 /* disabled runners, not suitable for this test */ 2072 static int 2073 testInternalGet (enum testMhdPollType pollType) 2074 { 2075 struct MHD_Daemon *d; 2076 uint16_t d_port = global_port; /* Daemon's port */ 2077 struct ahc_cls_type *ahc_param; 2078 struct check_uri_cls *uri_cb_param; 2079 struct term_notif_cb_param *term_result; 2080 2081 d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port, 2082 &ahc_param, &uri_cb_param, &term_result); 2083 2084 return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); 2085 } 2086 2087 2088 static int 2089 testMultithreadedGet (enum testMhdPollType pollType) 2090 { 2091 struct MHD_Daemon *d; 2092 uint16_t d_port = global_port; /* Daemon's port */ 2093 struct ahc_cls_type *ahc_param; 2094 struct check_uri_cls *uri_cb_param; 2095 struct term_notif_cb_param *term_result; 2096 2097 d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port, 2098 &ahc_param, &uri_cb_param); 2099 return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); 2100 } 2101 2102 2103 static int 2104 testMultithreadedPoolGet (enum testMhdPollType pollType) 2105 { 2106 struct MHD_Daemon *d; 2107 uint16_t d_port = global_port; /* Daemon's port */ 2108 struct ahc_cls_type *ahc_param; 2109 struct check_uri_cls *uri_cb_param; 2110 struct term_notif_cb_param *term_result; 2111 2112 d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port, 2113 &ahc_param, &uri_cb_param); 2114 return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); 2115 } 2116 2117 2118 #endif /* disabled runners, not suitable for this test */ 2119 2120 int 2121 main (int argc, char *const *argv) 2122 { 2123 unsigned int errorCount = 0; 2124 unsigned int test_result = 0; 2125 verbose = 0; 2126 2127 if ((NULL == argv) || (0 == argv[0])) 2128 return 99; 2129 oneone = ! has_in_name (argv[0], "10"); 2130 use_shutdown = has_in_name (argv[0], "_shutdown") ? 1 : 0; 2131 use_close = has_in_name (argv[0], "_close") ? 1 : 0; 2132 use_hard_close = has_in_name (argv[0], "_hard_close") ? 1 : 0; 2133 use_stress_os = has_in_name (argv[0], "_stress_os") ? 1 : 0; 2134 by_step = has_in_name (argv[0], "_steps") ? 1 : 0; 2135 upl_chunked = has_in_name (argv[0], "_chunked") ? 1 : 0; 2136 #ifndef SO_LINGER 2137 if (use_hard_close) 2138 { 2139 fprintf (stderr, "This test requires SO_LINGER socket option support.\n"); 2140 return 77; 2141 } 2142 #endif /* ! SO_LINGER */ 2143 if (1 != use_shutdown + use_close) 2144 return 99; 2145 verbose = ! (has_param (argc, argv, "-q") || 2146 has_param (argc, argv, "--quiet") || 2147 has_param (argc, argv, "-s") || 2148 has_param (argc, argv, "--silent")); 2149 if (use_stress_os && ! use_hard_close) 2150 return 99; 2151 2152 test_global_init (); 2153 2154 /* Could be set to non-zero value to enforce using specific port 2155 * in the test */ 2156 global_port = 0; 2157 test_result = testExternalGet (); 2158 if (test_result) 2159 fprintf (stderr, "FAILED: testExternalGet (). Result: %u.\n", test_result); 2160 else if (verbose) 2161 printf ("PASSED: testExternalGet ().\n"); 2162 errorCount += test_result; 2163 #if 0 /* disabled runners, not suitable for this test */ 2164 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 2165 { 2166 test_result = testInternalGet (testMhdPollAuto); 2167 if (test_result) 2168 fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto). " 2169 "Result: %u.\n", 2170 test_result); 2171 else if (verbose) 2172 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 2173 errorCount += test_result; 2174 #ifdef _MHD_HEAVY_TESTS 2175 /* Actually tests are not heavy, but took too long to complete while 2176 * not really provide any additional results. */ 2177 test_result = testInternalGet (testMhdPollBySelect); 2178 if (test_result) 2179 fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect). " 2180 "Result: %u.\n", 2181 test_result); 2182 else if (verbose) 2183 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 2184 errorCount += test_result; 2185 test_result = testMultithreadedPoolGet (testMhdPollBySelect); 2186 if (test_result) 2187 fprintf (stderr, 2188 "FAILED: testMultithreadedPoolGet (testMhdPollBySelect). " 2189 "Result: %u.\n", 2190 test_result); 2191 else if (verbose) 2192 printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n"); 2193 errorCount += test_result; 2194 test_result = testMultithreadedGet (testMhdPollBySelect); 2195 if (test_result) 2196 fprintf (stderr, 2197 "FAILED: testMultithreadedGet (testMhdPollBySelect). " 2198 "Result: %u.\n", 2199 test_result); 2200 else if (verbose) 2201 printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n"); 2202 errorCount += test_result; 2203 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) 2204 { 2205 test_result = testInternalGet (testMhdPollByPoll); 2206 if (test_result) 2207 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll). " 2208 "Result: %u.\n", 2209 test_result); 2210 else if (verbose) 2211 printf ("PASSED: testInternalGet (testMhdPollByPoll).\n"); 2212 errorCount += test_result; 2213 } 2214 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 2215 { 2216 test_result = testInternalGet (testMhdPollByEpoll); 2217 if (test_result) 2218 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll). " 2219 "Result: %u.\n", 2220 test_result); 2221 else if (verbose) 2222 printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n"); 2223 errorCount += test_result; 2224 } 2225 #else 2226 /* Mute compiler warnings */ 2227 (void) testMultithreadedGet; 2228 (void) testMultithreadedPoolGet; 2229 #endif /* _MHD_HEAVY_TESTS */ 2230 } 2231 #endif /* disabled runners, not suitable for this test */ 2232 if (0 != errorCount) 2233 fprintf (stderr, 2234 "Error (code: %u)\n", 2235 errorCount); 2236 else if (verbose) 2237 printf ("All tests passed.\n"); 2238 2239 test_global_cleanup (); 2240 2241 return (errorCount == 0) ? 0 : 1; /* 0 == pass */ 2242 }