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