test_set_panic.c (49753B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2021-2022 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_set_panic.c 22 * @brief Testcase for MHD_set_panic_func() 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 <errno.h> 33 34 #ifdef HAVE_STRINGS_H 35 #include <strings.h> 36 #endif /* HAVE_STRINGS_H */ 37 38 #ifdef _WIN32 39 #ifndef WIN32_LEAN_AND_MEAN 40 #define WIN32_LEAN_AND_MEAN 1 41 #endif /* !WIN32_LEAN_AND_MEAN */ 42 #include <windows.h> 43 #endif 44 45 #ifndef WINDOWS 46 #include <unistd.h> 47 #include <sys/socket.h> 48 #endif 49 50 #ifdef HAVE_LIMITS_H 51 #include <limits.h> 52 #endif /* HAVE_LIMITS_H */ 53 54 #ifdef HAVE_SIGNAL_H 55 #include <signal.h> 56 #endif /* HAVE_SIGNAL_H */ 57 58 #ifdef HAVE_SYSCTL 59 #ifdef HAVE_SYS_TYPES_H 60 #include <sys/types.h> 61 #endif /* HAVE_SYS_TYPES_H */ 62 #ifdef HAVE_SYS_SYSCTL_H 63 #include <sys/sysctl.h> 64 #endif /* HAVE_SYS_SYSCTL_H */ 65 #ifdef HAVE_SYS_SOCKET_H 66 #include <sys/socket.h> 67 #endif /* HAVE_SYS_SOCKET_H */ 68 #ifdef HAVE_NETINET_IN_H 69 #include <netinet/in.h> 70 #endif /* HAVE_NETINET_IN_H */ 71 #ifdef HAVE_NETINET_IP_H 72 #include <netinet/ip.h> 73 #endif /* HAVE_NETINET_IP_H */ 74 #ifdef HAVE_NETINET_IP_ICMP_H 75 #include <netinet/ip_icmp.h> 76 #endif /* HAVE_NETINET_IP_ICMP_H */ 77 #ifdef HAVE_NETINET_ICMP_VAR_H 78 #include <netinet/icmp_var.h> 79 #endif /* HAVE_NETINET_ICMP_VAR_H */ 80 #endif /* HAVE_SYSCTL */ 81 82 #include <stdio.h> 83 84 #include "mhd_sockets.h" /* only macros used */ 85 #include "test_helpers.h" 86 #include "mhd_assert.h" 87 88 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 89 #undef MHD_CPU_COUNT 90 #endif 91 #if ! defined(MHD_CPU_COUNT) 92 #define MHD_CPU_COUNT 2 93 #endif 94 #if MHD_CPU_COUNT > 32 95 #undef MHD_CPU_COUNT 96 /* Limit to reasonable value */ 97 #define MHD_CPU_COUNT 32 98 #endif /* MHD_CPU_COUNT > 32 */ 99 100 #ifndef MHD_STATICSTR_LEN_ 101 /** 102 * Determine length of static string / macro strings at compile time. 103 */ 104 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) 105 #endif /* ! MHD_STATICSTR_LEN_ */ 106 107 #ifndef _MHD_INSTRMACRO 108 /* Quoted macro parameter */ 109 #define _MHD_INSTRMACRO(a) #a 110 #endif /* ! _MHD_INSTRMACRO */ 111 #ifndef _MHD_STRMACRO 112 /* Quoted expanded macro parameter */ 113 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) 114 #endif /* ! _MHD_STRMACRO */ 115 116 117 /* Could be increased to facilitate debugging */ 118 #define TIMEOUTS_VAL 5 119 120 /* Time in ms to wait for final packets to be delivered */ 121 #define FINAL_PACKETS_MS 20 122 123 #define EXPECTED_URI_BASE_PATH "/a" 124 125 #define REQ_HOST "localhost" 126 127 #define REQ_METHOD "PUT" 128 129 #define REQ_BODY "Some content data." 130 131 #define REQ_LINE_END "\r\n" 132 133 /* Mandatory request headers */ 134 #define REQ_HEADER_HOST_NAME "Host" 135 #define REQ_HEADER_HOST_VALUE REQ_HOST 136 #define REQ_HEADER_HOST \ 137 REQ_HEADER_HOST_NAME ": " REQ_HEADER_HOST_VALUE REQ_LINE_END 138 #define REQ_HEADER_UA_NAME "User-Agent" 139 #define REQ_HEADER_UA_VALUE "dummyclient/0.9" 140 #define REQ_HEADER_UA REQ_HEADER_UA_NAME ": " REQ_HEADER_UA_VALUE REQ_LINE_END 141 142 /* Optional request headers */ 143 #define REQ_HEADER_CT_NAME "Content-Type" 144 #define REQ_HEADER_CT_VALUE "text/plain" 145 #define REQ_HEADER_CT REQ_HEADER_CT_NAME ": " REQ_HEADER_CT_VALUE REQ_LINE_END 146 147 148 #if defined(HAVE___FUNC__) 149 #define externalErrorExit(ignore) \ 150 _externalErrorExit_func (NULL, __func__, __LINE__) 151 #define externalErrorExitDesc(errDesc) \ 152 _externalErrorExit_func (errDesc, __func__, __LINE__) 153 #define mhdErrorExit(ignore) \ 154 _mhdErrorExit_func (NULL, __func__, __LINE__) 155 #define mhdErrorExitDesc(errDesc) \ 156 _mhdErrorExit_func (errDesc, __func__, __LINE__) 157 #elif defined(HAVE___FUNCTION__) 158 #define externalErrorExit(ignore) \ 159 _externalErrorExit_func (NULL, __FUNCTION__, __LINE__) 160 #define externalErrorExitDesc(errDesc) \ 161 _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__) 162 #define mhdErrorExit(ignore) \ 163 _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__) 164 #define mhdErrorExitDesc(errDesc) \ 165 _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__) 166 #else 167 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__) 168 #define externalErrorExitDesc(errDesc) \ 169 _externalErrorExit_func (errDesc, NULL, __LINE__) 170 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__) 171 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__) 172 #endif 173 174 175 _MHD_NORETURN static void 176 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 177 { 178 if ((NULL != errDesc) && (0 != errDesc[0])) 179 fprintf (stderr, "%s", errDesc); 180 else 181 fprintf (stderr, "System or external library call failed"); 182 if ((NULL != funcName) && (0 != funcName[0])) 183 fprintf (stderr, " in %s", funcName); 184 if (0 < lineNum) 185 fprintf (stderr, " at line %d", lineNum); 186 187 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 188 strerror (errno)); 189 #ifdef MHD_WINSOCK_SOCKETS 190 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 191 #endif /* MHD_WINSOCK_SOCKETS */ 192 fflush (stderr); 193 exit (99); 194 } 195 196 197 _MHD_NORETURN static void 198 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 199 { 200 if ((NULL != errDesc) && (0 != errDesc[0])) 201 fprintf (stderr, "%s", errDesc); 202 else 203 fprintf (stderr, "MHD unexpected error"); 204 if ((NULL != funcName) && (0 != funcName[0])) 205 fprintf (stderr, " in %s", funcName); 206 if (0 < lineNum) 207 fprintf (stderr, " at line %d", lineNum); 208 209 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 210 strerror (errno)); 211 212 fflush (stderr); 213 exit (8); 214 } 215 216 217 #ifndef MHD_POSIX_SOCKETS 218 219 /** 220 * Pause execution for specified number of milliseconds. 221 * @param ms the number of milliseconds to sleep 222 */ 223 static void 224 _MHD_sleep (uint32_t ms) 225 { 226 #if defined(_WIN32) 227 Sleep (ms); 228 #elif defined(HAVE_NANOSLEEP) 229 struct timespec slp = {ms / 1000, (ms % 1000) * 1000000}; 230 struct timespec rmn; 231 int num_retries = 0; 232 while (0 != nanosleep (&slp, &rmn)) 233 { 234 if (EINTR != errno) 235 externalErrorExit (); 236 if (num_retries++ > 8) 237 break; 238 slp = rmn; 239 } 240 #elif defined(HAVE_USLEEP) 241 uint64_t us = ms * 1000; 242 do 243 { 244 uint64_t this_sleep; 245 if (999999 < us) 246 this_sleep = 999999; 247 else 248 this_sleep = us; 249 /* Ignore return value as it could be void */ 250 usleep (this_sleep); 251 us -= this_sleep; 252 } while (us > 0); 253 #else 254 externalErrorExitDesc ("No sleep function available on this system"); 255 #endif 256 } 257 258 259 #endif /* ! MHD_POSIX_SOCKETS */ 260 261 /* Global parameters */ 262 static int verbose; /**< Be verbose */ 263 static uint16_t global_port; /**< MHD daemons listen port number */ 264 265 static void 266 test_global_init (void) 267 { 268 if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE)) 269 { 270 #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE) 271 if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) 272 externalErrorExitDesc ("Error suppressing SIGPIPE signal"); 273 #else /* ! HAVE_SIGNAL_H || ! SIGPIPE */ 274 fprintf (stderr, "Cannot suppress SIGPIPE signal.\n"); 275 /* exit (77); */ 276 #endif 277 } 278 } 279 280 281 static void 282 test_global_cleanup (void) 283 { 284 } 285 286 287 /** 288 * Change socket to blocking. 289 * 290 * @param fd the socket to manipulate 291 */ 292 static void 293 make_blocking (MHD_socket fd) 294 { 295 #if defined(MHD_POSIX_SOCKETS) 296 int flags; 297 298 flags = fcntl (fd, F_GETFL); 299 if (-1 == flags) 300 externalErrorExitDesc ("Cannot make socket non-blocking"); 301 if ((flags & ~O_NONBLOCK) != flags) 302 { 303 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) 304 externalErrorExitDesc ("Cannot make socket non-blocking"); 305 } 306 #elif defined(MHD_WINSOCK_SOCKETS) 307 unsigned long flags = 0; 308 309 if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) 310 externalErrorExitDesc ("Cannot make socket non-blocking"); 311 #endif /* MHD_WINSOCK_SOCKETS */ 312 } 313 314 315 /** 316 * Change socket to non-blocking. 317 * 318 * @param fd the socket to manipulate 319 */ 320 static void 321 make_nonblocking (MHD_socket fd) 322 { 323 #if defined(MHD_POSIX_SOCKETS) 324 int flags; 325 326 flags = fcntl (fd, F_GETFL); 327 if (-1 == flags) 328 externalErrorExitDesc ("Cannot make socket non-blocking"); 329 if ((flags | O_NONBLOCK) != flags) 330 { 331 if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK)) 332 externalErrorExitDesc ("Cannot make socket non-blocking"); 333 } 334 #elif defined(MHD_WINSOCK_SOCKETS) 335 unsigned long flags = 1; 336 337 if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) 338 externalErrorExitDesc ("Cannot make socket non-blocking"); 339 #endif /* MHD_WINSOCK_SOCKETS */ 340 } 341 342 343 enum _MHD_clientStage 344 { 345 DUMB_CLIENT_INIT = 0, 346 DUMB_CLIENT_CONNECTING, 347 DUMB_CLIENT_CONNECTED, 348 DUMB_CLIENT_REQ_SENDING, 349 DUMB_CLIENT_REQ_SENT, 350 DUMB_CLIENT_HEADER_RECVEIVING, 351 DUMB_CLIENT_HEADER_RECVEIVED, 352 DUMB_CLIENT_BODY_RECVEIVING, 353 DUMB_CLIENT_BODY_RECVEIVED, 354 DUMB_CLIENT_FINISHING, 355 DUMB_CLIENT_FINISHED 356 }; 357 358 struct _MHD_dumbClient 359 { 360 MHD_socket sckt; /**< the socket to communicate */ 361 362 int sckt_nonblock; /**< non-zero if socket is non-blocking */ 363 364 uint16_t port; /**< the port to connect to */ 365 366 char *send_buf; /**< the buffer for the request, malloced */ 367 368 size_t req_size; /**< the size of the request, including header */ 369 370 size_t send_off; /**< the number of bytes already sent */ 371 372 enum _MHD_clientStage stage; 373 374 /* the test-specific variables */ 375 size_t single_send_size; /**< the maximum number of bytes to be sent by 376 single send() */ 377 size_t send_size_limit; /**< the total number of send bytes limit */ 378 }; 379 380 struct _MHD_dumbClient * 381 _MHD_dumbClient_create (uint16_t port, const char *method, const char *url, 382 const char *add_headers, 383 const uint8_t *req_body, size_t req_body_size, 384 int chunked); 385 386 387 void 388 _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt, 389 size_t step_size, size_t max_total_send); 390 391 void 392 _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt); 393 394 int 395 _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt); 396 397 int 398 _MHD_dumbClient_process (struct _MHD_dumbClient *clnt); 399 400 void 401 _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt, 402 MHD_socket *maxsckt, 403 fd_set *rs, fd_set *ws, fd_set *es); 404 405 406 /** 407 * Process the client data with send()/recv() as needed based on 408 * information in fd_sets. 409 * @param clnt the client to process 410 * @return non-zero if client finished processing the request, 411 * zero otherwise. 412 */ 413 int 414 _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt, 415 fd_set *rs, fd_set *ws, fd_set *es); 416 417 /** 418 * Perform full request. 419 * @param clnt the client to run 420 * @return zero if client finished processing the request, 421 * non-zero if timeout is reached. 422 */ 423 int 424 _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt); 425 426 427 /** 428 * Close the client and free internally allocated resources. 429 * @param clnt the client to close 430 */ 431 void 432 _MHD_dumbClient_close (struct _MHD_dumbClient *clnt); 433 434 435 struct _MHD_dumbClient * 436 _MHD_dumbClient_create (uint16_t port, const char *method, const char *url, 437 const char *add_headers, 438 const uint8_t *req_body, size_t req_body_size, 439 int chunked) 440 { 441 struct _MHD_dumbClient *clnt; 442 size_t method_size; 443 size_t url_size; 444 size_t add_hdrs_size; 445 size_t buf_alloc_size; 446 char *send_buf; 447 448 mhd_assert (0 != port); 449 mhd_assert (NULL != req_body || 0 == req_body_size); 450 mhd_assert (0 == req_body_size || NULL != req_body); 451 452 clnt = (struct _MHD_dumbClient *) malloc (sizeof(struct _MHD_dumbClient)); 453 if (NULL == clnt) 454 externalErrorExit (); 455 memset (clnt, 0, sizeof(struct _MHD_dumbClient)); 456 clnt->sckt = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 457 if (MHD_INVALID_SOCKET == clnt->sckt) 458 externalErrorExitDesc ("Cannot create the client socket"); 459 460 #ifdef MHD_socket_nosignal_ 461 if (! MHD_socket_nosignal_ (clnt->sckt)) 462 externalErrorExitDesc ("Cannot suppress SIGPIPE on the client socket"); 463 #endif /* MHD_socket_nosignal_ */ 464 465 clnt->sckt_nonblock = 0; 466 if (clnt->sckt_nonblock) 467 make_nonblocking (clnt->sckt); 468 else 469 make_blocking (clnt->sckt); 470 471 if (1) 472 { /* Always set TCP NODELAY */ 473 const MHD_SCKT_OPT_BOOL_ on_val = 1; 474 475 if (0 != setsockopt (clnt->sckt, IPPROTO_TCP, TCP_NODELAY, 476 (const void *) &on_val, sizeof (on_val))) 477 externalErrorExitDesc ("Cannot set TCP_NODELAY option"); 478 } 479 480 clnt->port = port; 481 482 if (NULL != method) 483 method_size = strlen (method); 484 else 485 { 486 method = MHD_HTTP_METHOD_GET; 487 method_size = MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET); 488 } 489 mhd_assert (0 != method_size); 490 if (NULL != url) 491 url_size = strlen (url); 492 else 493 { 494 url = "/"; 495 url_size = 1; 496 } 497 mhd_assert (0 != url_size); 498 add_hdrs_size = (NULL == add_headers) ? 0 : strlen (add_headers); 499 buf_alloc_size = 1024 + method_size + url_size 500 + add_hdrs_size + req_body_size; 501 send_buf = (char *) malloc (buf_alloc_size); 502 if (NULL == send_buf) 503 externalErrorExit (); 504 505 clnt->req_size = 0; 506 /* Form the request line */ 507 memcpy (send_buf + clnt->req_size, method, method_size); 508 clnt->req_size += method_size; 509 send_buf[clnt->req_size++] = ' '; 510 memcpy (send_buf + clnt->req_size, url, url_size); 511 clnt->req_size += url_size; 512 send_buf[clnt->req_size++] = ' '; 513 memcpy (send_buf + clnt->req_size, MHD_HTTP_VERSION_1_1, 514 MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1)); 515 clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1); 516 send_buf[clnt->req_size++] = '\r'; 517 send_buf[clnt->req_size++] = '\n'; 518 /* Form the header */ 519 memcpy (send_buf + clnt->req_size, REQ_HEADER_HOST, 520 MHD_STATICSTR_LEN_ (REQ_HEADER_HOST)); 521 clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_HOST); 522 memcpy (send_buf + clnt->req_size, REQ_HEADER_UA, 523 MHD_STATICSTR_LEN_ (REQ_HEADER_UA)); 524 clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_UA); 525 if ((NULL != req_body) || chunked) 526 { 527 if (! chunked) 528 { 529 int prn_size; 530 531 memcpy (send_buf + clnt->req_size, 532 MHD_HTTP_HEADER_CONTENT_LENGTH ": ", 533 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ")); 534 clnt->req_size += 535 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "); 536 prn_size = snprintf (send_buf + clnt->req_size, 537 (buf_alloc_size - clnt->req_size), 538 "%u", (unsigned int) req_body_size); 539 if (0 >= prn_size) 540 externalErrorExit (); 541 if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size) 542 externalErrorExit (); 543 clnt->req_size += (unsigned int) prn_size; 544 send_buf[clnt->req_size++] = '\r'; 545 send_buf[clnt->req_size++] = '\n'; 546 } 547 else 548 { 549 memcpy (send_buf + clnt->req_size, 550 MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked\r\n", 551 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \ 552 ": chunked\r\n")); 553 clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \ 554 ": chunked\r\n"); 555 } 556 } 557 if (0 != add_hdrs_size) 558 { 559 memcpy (send_buf + clnt->req_size, add_headers, add_hdrs_size); 560 clnt->req_size += add_hdrs_size; 561 } 562 /* Terminate header */ 563 send_buf[clnt->req_size++] = '\r'; 564 send_buf[clnt->req_size++] = '\n'; 565 566 /* Add body (if any) */ 567 if (! chunked) 568 { 569 if (0 != req_body_size) 570 { 571 memcpy (send_buf + clnt->req_size, req_body, req_body_size); 572 clnt->req_size += req_body_size; 573 } 574 } 575 else 576 { 577 if (0 != req_body_size) 578 { 579 int prn_size; 580 prn_size = snprintf (send_buf + clnt->req_size, 581 (buf_alloc_size - clnt->req_size), 582 "%x", (unsigned int) req_body_size); 583 if (0 >= prn_size) 584 externalErrorExit (); 585 if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size) 586 externalErrorExit (); 587 clnt->req_size += (unsigned int) prn_size; 588 send_buf[clnt->req_size++] = '\r'; 589 send_buf[clnt->req_size++] = '\n'; 590 memcpy (send_buf + clnt->req_size, req_body, req_body_size); 591 clnt->req_size += req_body_size; 592 send_buf[clnt->req_size++] = '\r'; 593 send_buf[clnt->req_size++] = '\n'; 594 } 595 send_buf[clnt->req_size++] = '0'; 596 send_buf[clnt->req_size++] = '\r'; 597 send_buf[clnt->req_size++] = '\n'; 598 send_buf[clnt->req_size++] = '\r'; 599 send_buf[clnt->req_size++] = '\n'; 600 } 601 mhd_assert (clnt->req_size < buf_alloc_size); 602 clnt->send_buf = send_buf; 603 604 return clnt; 605 } 606 607 608 void 609 _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt, 610 size_t step_size, size_t max_total_send) 611 { 612 clnt->single_send_size = step_size; 613 clnt->send_size_limit = max_total_send; 614 } 615 616 617 /* internal */ 618 static void 619 _MHD_dumbClient_connect_init (struct _MHD_dumbClient *clnt) 620 { 621 struct sockaddr_in sa; 622 mhd_assert (DUMB_CLIENT_INIT == clnt->stage); 623 624 sa.sin_family = AF_INET; 625 sa.sin_port = htons (clnt->port); 626 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 627 628 if (0 != connect (clnt->sckt, (struct sockaddr *) &sa, sizeof(sa))) 629 { 630 const int err = MHD_socket_get_error_ (); 631 if ( (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINPROGRESS_)) || 632 (MHD_SCKT_ERR_IS_EAGAIN_ (err))) 633 clnt->stage = DUMB_CLIENT_CONNECTING; 634 else 635 externalErrorExitDesc ("Cannot 'connect()' the client socket"); 636 } 637 else 638 clnt->stage = DUMB_CLIENT_CONNECTED; 639 } 640 641 642 void 643 _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt) 644 { 645 mhd_assert (DUMB_CLIENT_INIT == clnt->stage); 646 _MHD_dumbClient_connect_init (clnt); 647 } 648 649 650 /* internal */ 651 static void 652 _MHD_dumbClient_connect_finish (struct _MHD_dumbClient *clnt) 653 { 654 int err = 0; 655 socklen_t err_size = sizeof(err); 656 mhd_assert (DUMB_CLIENT_CONNECTING == clnt->stage); 657 if (0 != getsockopt (clnt->sckt, SOL_SOCKET, SO_ERROR, 658 (void *) &err, &err_size)) 659 externalErrorExitDesc ("'getsockopt()' call failed"); 660 if (0 != err) 661 externalErrorExitDesc ("Socket connect() failed"); 662 clnt->stage = DUMB_CLIENT_CONNECTED; 663 } 664 665 666 /* internal */ 667 static void 668 _MHD_dumbClient_send_req (struct _MHD_dumbClient *clnt) 669 { 670 size_t send_size; 671 ssize_t res; 672 mhd_assert (DUMB_CLIENT_CONNECTED <= clnt->stage); 673 mhd_assert (DUMB_CLIENT_REQ_SENT > clnt->stage); 674 mhd_assert (clnt->req_size > clnt->send_off); 675 676 send_size = (((0 != clnt->send_size_limit) && 677 (clnt->req_size > clnt->send_size_limit)) ? 678 clnt->send_size_limit : clnt->req_size) - clnt->send_off; 679 mhd_assert (0 != send_size); 680 if ((0 != clnt->single_send_size) && 681 (clnt->single_send_size < send_size)) 682 send_size = clnt->single_send_size; 683 684 res = MHD_send_ (clnt->sckt, clnt->send_buf + clnt->send_off, send_size); 685 686 if (res < 0) 687 { 688 const int err = MHD_socket_get_error_ (); 689 if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) 690 return; 691 if (MHD_SCKT_ERR_IS_EINTR_ (err)) 692 return; 693 if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err)) 694 mhdErrorExitDesc ("The connection was aborted by MHD"); 695 if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_)) 696 mhdErrorExitDesc ("The connection was shut down on MHD side"); 697 externalErrorExitDesc ("Unexpected network error"); 698 } 699 clnt->send_off += (size_t) res; 700 mhd_assert (clnt->send_off <= clnt->req_size); 701 mhd_assert (clnt->send_off <= clnt->send_size_limit || \ 702 0 == clnt->send_size_limit); 703 if (clnt->req_size == clnt->send_off) 704 clnt->stage = DUMB_CLIENT_REQ_SENT; 705 if ((0 != clnt->send_size_limit) && 706 (clnt->send_size_limit == clnt->send_off)) 707 clnt->stage = DUMB_CLIENT_FINISHING; 708 } 709 710 711 /* internal */ 712 _MHD_NORETURN /* not implemented */ 713 static void 714 _MHD_dumbClient_recv_reply (struct _MHD_dumbClient *clnt) 715 { 716 (void) clnt; 717 externalErrorExitDesc ("Not implemented for this test"); 718 } 719 720 721 int 722 _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt) 723 { 724 return DUMB_CLIENT_REQ_SENT <= clnt->stage; 725 } 726 727 728 /* internal */ 729 static void 730 _MHD_dumbClient_socket_close (struct _MHD_dumbClient *clnt) 731 { 732 if (MHD_INVALID_SOCKET != clnt->sckt) 733 { 734 if (! MHD_socket_close_ (clnt->sckt)) 735 externalErrorExitDesc ("Unexpected error while closing " \ 736 "the client socket"); 737 clnt->sckt = MHD_INVALID_SOCKET; 738 } 739 } 740 741 742 /* internal */ 743 static void 744 _MHD_dumbClient_finalize (struct _MHD_dumbClient *clnt) 745 { 746 if (MHD_INVALID_SOCKET != clnt->sckt) 747 { 748 if (0 != shutdown (clnt->sckt, SHUT_WR)) 749 { 750 const int err = MHD_socket_get_error_ (); 751 if (! MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_) && 752 ! MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err)) 753 mhdErrorExitDesc ("Unexpected error when shutting down " \ 754 "the client socket"); 755 } 756 } 757 clnt->stage = DUMB_CLIENT_FINISHED; 758 } 759 760 761 /* internal */ 762 static int 763 _MHD_dumbClient_needs_send (const struct _MHD_dumbClient *clnt) 764 { 765 return ((DUMB_CLIENT_CONNECTING <= clnt->stage) && 766 (DUMB_CLIENT_REQ_SENT > clnt->stage)) || 767 (DUMB_CLIENT_FINISHING == clnt->stage); 768 } 769 770 771 /* internal */ 772 static int 773 _MHD_dumbClient_needs_recv (const struct _MHD_dumbClient *clnt) 774 { 775 return (DUMB_CLIENT_HEADER_RECVEIVING <= clnt->stage) && 776 (DUMB_CLIENT_BODY_RECVEIVED > clnt->stage); 777 } 778 779 780 /* internal */ 781 /** 782 * Check whether the client needs unconditionally process the data. 783 * @param clnt the client to check 784 * @return non-zero if client needs unconditionally process the data, 785 * zero otherwise. 786 */ 787 static int 788 _MHD_dumbClient_needs_process (const struct _MHD_dumbClient *clnt) 789 { 790 switch (clnt->stage) 791 { 792 case DUMB_CLIENT_INIT: 793 case DUMB_CLIENT_REQ_SENT: 794 case DUMB_CLIENT_HEADER_RECVEIVED: 795 case DUMB_CLIENT_BODY_RECVEIVED: 796 case DUMB_CLIENT_FINISHED: 797 return ! 0; 798 case DUMB_CLIENT_CONNECTING: 799 case DUMB_CLIENT_CONNECTED: 800 case DUMB_CLIENT_REQ_SENDING: 801 case DUMB_CLIENT_HEADER_RECVEIVING: 802 case DUMB_CLIENT_BODY_RECVEIVING: 803 case DUMB_CLIENT_FINISHING: 804 default: 805 return 0; 806 } 807 return 0; /* Should be unreachable */ 808 } 809 810 811 /** 812 * Process the client data with send()/recv() as needed. 813 * @param clnt the client to process 814 * @return non-zero if client finished processing the request, 815 * zero otherwise. 816 */ 817 int 818 _MHD_dumbClient_process (struct _MHD_dumbClient *clnt) 819 { 820 do 821 { 822 switch (clnt->stage) 823 { 824 case DUMB_CLIENT_INIT: 825 _MHD_dumbClient_connect_init (clnt); 826 break; 827 case DUMB_CLIENT_CONNECTING: 828 _MHD_dumbClient_connect_finish (clnt); 829 break; 830 case DUMB_CLIENT_CONNECTED: 831 case DUMB_CLIENT_REQ_SENDING: 832 _MHD_dumbClient_send_req (clnt); 833 break; 834 case DUMB_CLIENT_REQ_SENT: 835 mhd_assert (0); 836 clnt->stage = DUMB_CLIENT_HEADER_RECVEIVING; 837 break; 838 case DUMB_CLIENT_HEADER_RECVEIVING: 839 _MHD_dumbClient_recv_reply (clnt); 840 break; 841 case DUMB_CLIENT_HEADER_RECVEIVED: 842 clnt->stage = DUMB_CLIENT_BODY_RECVEIVING; 843 break; 844 case DUMB_CLIENT_BODY_RECVEIVING: 845 _MHD_dumbClient_recv_reply (clnt); 846 break; 847 case DUMB_CLIENT_BODY_RECVEIVED: 848 clnt->stage = DUMB_CLIENT_FINISHING; 849 break; 850 case DUMB_CLIENT_FINISHING: 851 _MHD_dumbClient_finalize (clnt); 852 break; 853 case DUMB_CLIENT_FINISHED: 854 return ! 0; 855 default: 856 mhd_assert (0); 857 mhdErrorExit (); 858 } 859 } while (_MHD_dumbClient_needs_process (clnt)); 860 return DUMB_CLIENT_FINISHED == clnt->stage; 861 } 862 863 864 void 865 _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt, 866 MHD_socket *maxsckt, 867 fd_set *rs, fd_set *ws, fd_set *es) 868 { 869 mhd_assert (NULL != rs); 870 mhd_assert (NULL != ws); 871 mhd_assert (NULL != es); 872 if (DUMB_CLIENT_FINISHED > clnt->stage) 873 { 874 if (MHD_INVALID_SOCKET != clnt->sckt) 875 { 876 if ( (MHD_INVALID_SOCKET == *maxsckt) || 877 (clnt->sckt > *maxsckt) ) 878 *maxsckt = clnt->sckt; 879 if (_MHD_dumbClient_needs_recv (clnt)) 880 FD_SET (clnt->sckt, rs); 881 if (_MHD_dumbClient_needs_send (clnt)) 882 FD_SET (clnt->sckt, ws); 883 FD_SET (clnt->sckt, es); 884 } 885 } 886 } 887 888 889 /** 890 * Process the client data with send()/recv() as needed based on 891 * information in fd_sets. 892 * @param clnt the client to process 893 * @return non-zero if client finished processing the request, 894 * zero otherwise. 895 */ 896 int 897 _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt, 898 fd_set *rs, fd_set *ws, fd_set *es) 899 { 900 if (_MHD_dumbClient_needs_process (clnt)) 901 return _MHD_dumbClient_process (clnt); 902 else if (MHD_INVALID_SOCKET != clnt->sckt) 903 { 904 if (_MHD_dumbClient_needs_recv (clnt) && FD_ISSET (clnt->sckt, rs)) 905 return _MHD_dumbClient_process (clnt); 906 else if (_MHD_dumbClient_needs_send (clnt) && FD_ISSET (clnt->sckt, ws)) 907 return _MHD_dumbClient_process (clnt); 908 else if (FD_ISSET (clnt->sckt, es)) 909 return _MHD_dumbClient_process (clnt); 910 } 911 return DUMB_CLIENT_FINISHED == clnt->stage; 912 } 913 914 915 /** 916 * Perform full request. 917 * @param clnt the client to run 918 * @return zero if client finished processing the request, 919 * non-zero if timeout is reached. 920 */ 921 int 922 _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt) 923 { 924 time_t start; 925 time_t now; 926 start = time (NULL); 927 now = start; 928 do 929 { 930 fd_set rs; 931 fd_set ws; 932 fd_set es; 933 MHD_socket maxMhdSk; 934 struct timeval tv; 935 936 FD_ZERO (&rs); 937 FD_ZERO (&ws); 938 FD_ZERO (&es); 939 940 if (! _MHD_dumbClient_needs_process (clnt)) 941 { 942 maxMhdSk = MHD_INVALID_SOCKET; 943 _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es); 944 mhd_assert (now >= start); 945 #ifndef _WIN32 946 tv.tv_sec = (time_t) (TIMEOUTS_VAL * 2 - (now - start) + 1); 947 #else 948 tv.tv_sec = (long) (TIMEOUTS_VAL * 2 - (now - start) + 1); 949 #endif 950 tv.tv_usec = 250 * 1000; 951 if (-1 == select ((int) (maxMhdSk + 1), &rs, &ws, &es, &tv)) 952 { 953 #ifdef MHD_POSIX_SOCKETS 954 if (EINTR != errno) 955 externalErrorExitDesc ("Unexpected select() error"); 956 #else /* ! MHD_POSIX_SOCKETS */ 957 mhd_assert ((0 != rs.fd_count) || (0 != ws.fd_count) || \ 958 (0 != es.fd_count)); 959 externalErrorExitDesc ("Unexpected select() error"); 960 _MHD_sleep ((uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 961 #endif /* ! MHD_POSIX_SOCKETS */ 962 continue; 963 } 964 if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es)) 965 return 0; 966 } 967 /* Use double timeout value here as MHD must catch timeout situations 968 * in this test. Timeout in client as a last resort. */ 969 } while ((now = time (NULL)) - start <= (TIMEOUTS_VAL * 2)); 970 return 1; 971 } 972 973 974 /** 975 * Close the client and free internally allocated resources. 976 * @param clnt the client to close 977 */ 978 void 979 _MHD_dumbClient_close (struct _MHD_dumbClient *clnt) 980 { 981 if (DUMB_CLIENT_FINISHED != clnt->stage) 982 _MHD_dumbClient_finalize (clnt); 983 _MHD_dumbClient_socket_close (clnt); 984 if (NULL != clnt->send_buf) 985 { 986 free ((void *) clnt->send_buf); 987 clnt->send_buf = NULL; 988 } 989 free (clnt); 990 } 991 992 993 _MHD_NORETURN static void 994 socket_cb (void *cls, 995 struct MHD_Connection *c, 996 void **socket_context, 997 enum MHD_ConnectionNotificationCode toe) 998 { 999 (void) cls; /* Unused */ 1000 (void) socket_context; /* Unused */ 1001 (void) toe; /* Unused */ 1002 1003 MHD_suspend_connection (c); /* Should trigger panic */ 1004 mhdErrorExitDesc ("Function \"MHD_suspend_connection()\" succeed, while " \ 1005 "it must fail as daemon was started without MHD_ALLOW_SUSPEND_RESUME " \ 1006 "flag"); 1007 } 1008 1009 1010 struct ahc_cls_type 1011 { 1012 const char *volatile rp_data; 1013 volatile size_t rp_data_size; 1014 const char *volatile rq_method; 1015 const char *volatile rq_url; 1016 const char *volatile req_body; 1017 volatile unsigned int cb_called; /* Non-zero indicates that callback was called at least one time */ 1018 size_t req_body_size; /**< The number of bytes in @a req_body */ 1019 size_t req_body_uploaded; /* Updated by callback */ 1020 }; 1021 1022 1023 static enum MHD_Result 1024 ahcCheck (void *cls, 1025 struct MHD_Connection *connection, 1026 const char *url, 1027 const char *method, 1028 const char *version, 1029 const char *upload_data, size_t *upload_data_size, 1030 void **req_cls) 1031 { 1032 static int marker; 1033 enum MHD_Result ret; 1034 struct ahc_cls_type *const param = (struct ahc_cls_type *) cls; 1035 (void) connection; /* Unused */ 1036 1037 if (NULL == param) 1038 mhdErrorExitDesc ("cls parameter is NULL"); 1039 param->cb_called++; 1040 1041 if (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) 1042 mhdErrorExitDesc ("Unexpected HTTP version"); 1043 1044 if (0 != strcmp (url, param->rq_url)) 1045 mhdErrorExitDesc ("Unexpected URI"); 1046 1047 if (0 != strcmp (param->rq_method, method)) 1048 mhdErrorExitDesc ("Unexpected request method"); 1049 1050 if (NULL == upload_data_size) 1051 mhdErrorExitDesc ("'upload_data_size' pointer is NULL"); 1052 1053 if (0 != *upload_data_size) 1054 { 1055 const char *const upload_body = param->req_body; 1056 if (NULL == upload_data) 1057 mhdErrorExitDesc ("'upload_data' is NULL while " \ 1058 "'*upload_data_size' value is not zero"); 1059 if (NULL == upload_body) 1060 mhdErrorExitDesc ("'*upload_data_size' value is not zero " \ 1061 "while no request body is expected"); 1062 if (param->req_body_uploaded + *upload_data_size > param->req_body_size) 1063 { 1064 fprintf (stderr, "Too large upload body received. Got %u, expected %u", 1065 (unsigned int) (param->req_body_uploaded + *upload_data_size), 1066 (unsigned int) param->req_body_size); 1067 mhdErrorExit (); 1068 } 1069 if (0 != memcmp (upload_data, upload_body + param->req_body_uploaded, 1070 *upload_data_size)) 1071 { 1072 fprintf (stderr, "Unexpected request body at offset %u: " \ 1073 "'%.*s', expected: '%.*s'\n", 1074 (unsigned int) param->req_body_uploaded, 1075 (int) *upload_data_size, upload_data, 1076 (int) *upload_data_size, upload_body + param->req_body_uploaded); 1077 mhdErrorExit (); 1078 } 1079 param->req_body_uploaded += *upload_data_size; 1080 *upload_data_size = 0; 1081 } 1082 1083 if (&marker != *req_cls) 1084 { 1085 /* The first call of the callback for this connection */ 1086 mhd_assert (NULL == upload_data); 1087 param->req_body_uploaded = 0; 1088 1089 *req_cls = ▮ 1090 return MHD_YES; 1091 } 1092 1093 if (NULL != upload_data) 1094 return MHD_YES; /* Full request has not been received so far */ 1095 1096 #if 0 /* Code unused in this test */ 1097 struct MHD_Response *response; 1098 response = MHD_create_response_from_buffer (param->rp_data_size, 1099 (void *) param->rp_data, 1100 MHD_RESPMEM_MUST_COPY); 1101 if (NULL == response) 1102 mhdErrorExitDesc ("Failed to create response"); 1103 1104 ret = MHD_queue_response (connection, 1105 MHD_HTTP_OK, 1106 response); 1107 MHD_destroy_response (response); 1108 if (MHD_YES != ret) 1109 mhdErrorExitDesc ("Failed to queue response"); 1110 #else 1111 if (NULL == upload_data) 1112 mhdErrorExitDesc ("Full request received, " \ 1113 "while incomplete request expected"); 1114 ret = MHD_NO; 1115 #endif 1116 1117 return ret; 1118 } 1119 1120 1121 struct simpleQueryParams 1122 { 1123 /* Destination path for HTTP query */ 1124 const char *queryPath; 1125 1126 /* Custom query method, NULL for default */ 1127 const char *method; 1128 1129 /* Destination port for HTTP query */ 1130 uint16_t queryPort; 1131 1132 /* Additional request headers, static */ 1133 const char *headers; 1134 1135 /* NULL for request without body */ 1136 const uint8_t *req_body; 1137 size_t req_body_size; 1138 1139 /* Non-zero to use chunked encoding for request body */ 1140 int chunked; 1141 1142 /* HTTP query result error flag */ 1143 volatile int queryError; 1144 1145 /* Response HTTP code, zero if no response */ 1146 volatile int responseCode; 1147 }; 1148 1149 1150 /* returns non-zero if timed-out */ 1151 static int 1152 performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt) 1153 { 1154 time_t start; 1155 struct timeval tv; 1156 int ret; 1157 const union MHD_DaemonInfo *di; 1158 MHD_socket lstn_sk; 1159 int client_accepted; 1160 int full_req_recieved; 1161 int full_req_sent; 1162 int some_data_recieved; 1163 1164 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD); 1165 if (NULL == di) 1166 mhdErrorExitDesc ("Cannot get listener socket"); 1167 lstn_sk = di->listen_fd; 1168 1169 ret = 1; /* will be replaced with real result */ 1170 client_accepted = 0; 1171 1172 _MHD_dumbClient_start_connect (clnt); 1173 1174 full_req_recieved = 0; 1175 some_data_recieved = 0; 1176 start = time (NULL); 1177 do 1178 { 1179 fd_set rs; 1180 fd_set ws; 1181 fd_set es; 1182 MHD_socket maxMhdSk; 1183 int num_ready; 1184 int do_client; /**< Process data in client */ 1185 1186 maxMhdSk = MHD_INVALID_SOCKET; 1187 FD_ZERO (&rs); 1188 FD_ZERO (&ws); 1189 FD_ZERO (&es); 1190 if (NULL == clnt) 1191 { 1192 /* client has finished, check whether MHD is still 1193 * processing any connections */ 1194 full_req_sent = 1; 1195 do_client = 0; 1196 if (client_accepted && (0 > MHD_get_timeout64s (d))) 1197 { 1198 ret = 0; 1199 break; /* MHD finished as well */ 1200 } 1201 } 1202 else 1203 { 1204 full_req_sent = _MHD_dumbClient_is_req_sent (clnt); 1205 if (! full_req_sent) 1206 do_client = 1; /* Request hasn't been sent yet, send the data */ 1207 else 1208 { 1209 /* All request data has been sent. 1210 * Client will close the socket as the next step. */ 1211 if (full_req_recieved) 1212 do_client = 1; /* All data has been received by the MHD */ 1213 else if (some_data_recieved) 1214 { 1215 /* at least something was received by the MHD */ 1216 do_client = 1; 1217 } 1218 else 1219 { 1220 /* The MHD must receive at least something before closing 1221 * the connection. */ 1222 do_client = 0; 1223 } 1224 } 1225 1226 if (do_client) 1227 _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es); 1228 } 1229 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) 1230 mhdErrorExitDesc ("MHD_get_fdset() failed"); 1231 if (do_client) 1232 { 1233 tv.tv_sec = 1; 1234 tv.tv_usec = 250 * 1000; 1235 } 1236 else 1237 { /* Request completely sent but not yet fully received */ 1238 tv.tv_sec = 0; 1239 tv.tv_usec = FINAL_PACKETS_MS * 1000; 1240 } 1241 num_ready = select ((int) (maxMhdSk + 1), &rs, &ws, &es, &tv); 1242 if (-1 == num_ready) 1243 { 1244 #ifdef MHD_POSIX_SOCKETS 1245 if (EINTR != errno) 1246 externalErrorExitDesc ("Unexpected select() error"); 1247 #else 1248 if ((WSAEINVAL != WSAGetLastError ()) || 1249 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 1250 externalErrorExitDesc ("Unexpected select() error"); 1251 _MHD_sleep ((uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 1252 #endif 1253 continue; 1254 } 1255 if (0 == num_ready) 1256 { /* select() finished by timeout, looks like no more packets are pending */ 1257 if (do_client) 1258 externalErrorExitDesc ("Timeout waiting for sockets"); 1259 if (full_req_sent && (! full_req_recieved)) 1260 full_req_recieved = 1; 1261 } 1262 if (full_req_recieved) 1263 mhdErrorExitDesc ("Full request has been received by MHD, while it " 1264 "must be aborted by the panic function"); 1265 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) 1266 mhdErrorExitDesc ("MHD_run_from_select() failed"); 1267 if (! client_accepted) 1268 client_accepted = FD_ISSET (lstn_sk, &rs); 1269 else 1270 { /* Client connection was already accepted by MHD */ 1271 if (! some_data_recieved) 1272 { 1273 if (! do_client) 1274 { 1275 if (0 != num_ready) 1276 { /* Connection was accepted before, "ready" socket means data */ 1277 some_data_recieved = 1; 1278 } 1279 } 1280 else 1281 { 1282 if (2 == num_ready) 1283 some_data_recieved = 1; 1284 else if ((1 == num_ready) && 1285 ((MHD_INVALID_SOCKET == clnt->sckt) || 1286 ! FD_ISSET (clnt->sckt, &ws))) 1287 some_data_recieved = 1; 1288 } 1289 } 1290 } 1291 if (do_client) 1292 { 1293 if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es)) 1294 clnt = NULL; 1295 } 1296 /* Use double timeout value here so MHD would be able to catch timeout 1297 * internally */ 1298 } while (time (NULL) - start <= (TIMEOUTS_VAL * 2)); 1299 1300 return ret; 1301 } 1302 1303 1304 /* Returns zero for successful response and non-zero for failed response */ 1305 static int 1306 doClientQueryInThread (struct MHD_Daemon *d, 1307 struct simpleQueryParams *p) 1308 { 1309 const union MHD_DaemonInfo *dinfo; 1310 struct _MHD_dumbClient *c; 1311 int errornum; 1312 int use_external_poll; 1313 1314 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS); 1315 if (NULL == dinfo) 1316 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 1317 use_external_poll = (0 == (dinfo->flags 1318 & MHD_USE_INTERNAL_POLLING_THREAD)); 1319 1320 if (0 == p->queryPort) 1321 externalErrorExit (); 1322 1323 c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath, 1324 p->headers, p->req_body, p->req_body_size, 1325 p->chunked); 1326 _MHD_dumbClient_set_send_limits (c, 1, 0); 1327 1328 /* 'internal' polling should not be used in this test */ 1329 mhd_assert (use_external_poll); 1330 if (! use_external_poll) 1331 errornum = _MHD_dumbClient_perform (c); 1332 else 1333 errornum = performQueryExternal (d, c); 1334 1335 if (errornum) 1336 fprintf (stderr, "Request timeout out.\n"); 1337 else 1338 mhdErrorExitDesc ("Request succeed, but it must fail"); 1339 1340 _MHD_dumbClient_close (c); 1341 1342 return errornum; 1343 } 1344 1345 1346 /* Perform test queries, shut down MHD daemon, and free parameters */ 1347 static unsigned int 1348 performTestQueries (struct MHD_Daemon *d, uint16_t d_port, 1349 struct ahc_cls_type *ahc_param) 1350 { 1351 struct simpleQueryParams qParam; 1352 1353 /* Common parameters, to be individually overridden by specific test cases 1354 * if needed */ 1355 qParam.queryPort = d_port; 1356 qParam.method = MHD_HTTP_METHOD_PUT; 1357 qParam.queryPath = EXPECTED_URI_BASE_PATH; 1358 qParam.headers = REQ_HEADER_CT; 1359 qParam.req_body = (const uint8_t *) REQ_BODY; 1360 qParam.req_body_size = MHD_STATICSTR_LEN_ (REQ_BODY); 1361 qParam.chunked = 0; 1362 1363 ahc_param->rq_url = EXPECTED_URI_BASE_PATH; 1364 ahc_param->rq_method = MHD_HTTP_METHOD_PUT; 1365 ahc_param->rp_data = "~"; 1366 ahc_param->rp_data_size = 1; 1367 ahc_param->req_body = (const char *) qParam.req_body; 1368 ahc_param->req_body_size = qParam.req_body_size; 1369 1370 /* Make sure that maximum size is tested */ 1371 /* To be updated by callbacks */ 1372 ahc_param->cb_called = 0; 1373 1374 if (0 != doClientQueryInThread (d, &qParam)) 1375 fprintf (stderr, "FAILED: client query failed."); 1376 1377 MHD_stop_daemon (d); 1378 free (ahc_param); 1379 1380 return 1; /* Always error if reached this point */ 1381 } 1382 1383 1384 enum testMhdThreadsType 1385 { 1386 testMhdThreadExternal = 0, 1387 testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD, 1388 testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION 1389 | MHD_USE_INTERNAL_POLLING_THREAD, 1390 testMhdThreadInternalPool 1391 }; 1392 1393 enum testMhdPollType 1394 { 1395 testMhdPollBySelect = 0, 1396 testMhdPollByPoll = MHD_USE_POLL, 1397 testMhdPollByEpoll = MHD_USE_EPOLL, 1398 testMhdPollAuto = MHD_USE_AUTO 1399 }; 1400 1401 /* Get number of threads for thread pool depending 1402 * on used poll function and test type. */ 1403 static unsigned int 1404 testNumThreadsForPool (enum testMhdPollType pollType) 1405 { 1406 unsigned int numThreads = MHD_CPU_COUNT; 1407 (void) pollType; /* Don't care about pollType for this test */ 1408 return numThreads; /* No practical limit for non-cleanup test */ 1409 } 1410 1411 1412 #define PANIC_MAGIC_CHECK 1133 1413 1414 _MHD_NORETURN static void 1415 myPanicCallback (void *cls, 1416 const char *file, 1417 unsigned int line, 1418 const char *reason) 1419 { 1420 int *const param = (int *) cls; 1421 if (NULL == cls) 1422 mhdErrorExitDesc ("The 'cls' parameter is NULL"); 1423 if (PANIC_MAGIC_CHECK != *param) 1424 mhdErrorExitDesc ("Wrong '*cls' value"); 1425 #ifdef HAVE_MESSAGES 1426 if (NULL == file) 1427 mhdErrorExitDesc ("The 'file' parameter is NULL"); 1428 if (NULL == reason) 1429 mhdErrorExitDesc ("The 'reason' parameter is NULL"); 1430 #else /* ! HAVE_MESSAGES */ 1431 if (NULL != file) 1432 mhdErrorExitDesc ("The 'file' parameter is not NULL"); 1433 if (NULL != reason) 1434 mhdErrorExitDesc ("The 'reason' parameter is not NULL"); 1435 #endif /* ! HAVE_MESSAGES */ 1436 fflush (stderr); 1437 fflush (stdout); 1438 printf ("User panic function has been called from file '%s' at line '%u' " 1439 "with the reason:\n%s", file, line, 1440 ((NULL != reason) ? reason : "(NULL)\n")); 1441 fflush (stdout); 1442 exit (0); 1443 } 1444 1445 1446 static struct MHD_Daemon * 1447 startTestMhdDaemon (enum testMhdThreadsType thrType, 1448 enum testMhdPollType pollType, uint16_t *pport, 1449 struct ahc_cls_type **ahc_param) 1450 { 1451 struct MHD_Daemon *d; 1452 const union MHD_DaemonInfo *dinfo; 1453 static int magic_panic_param = PANIC_MAGIC_CHECK; 1454 1455 if (NULL == ahc_param) 1456 externalErrorExit (); 1457 1458 *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type)); 1459 if (NULL == *ahc_param) 1460 externalErrorExit (); 1461 1462 if ( (0 == *pport) && 1463 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) 1464 { 1465 *pport = 4190; 1466 } 1467 1468 MHD_set_panic_func (&myPanicCallback, (void *) &magic_panic_param); 1469 1470 if (testMhdThreadExternal == thrType) 1471 d = MHD_start_daemon (((unsigned int) pollType) 1472 | (verbose ? MHD_USE_ERROR_LOG : 0) 1473 | MHD_USE_NO_THREAD_SAFETY, 1474 *pport, NULL, NULL, 1475 &ahcCheck, *ahc_param, 1476 MHD_OPTION_NOTIFY_CONNECTION, &socket_cb, 1477 NULL, 1478 MHD_OPTION_CONNECTION_TIMEOUT, 1479 (unsigned) TIMEOUTS_VAL, 1480 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 1481 MHD_OPTION_END); 1482 else if (testMhdThreadInternalPool != thrType) 1483 d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) 1484 | (verbose ? MHD_USE_ERROR_LOG : 0), 1485 *pport, NULL, NULL, 1486 &ahcCheck, *ahc_param, 1487 MHD_OPTION_NOTIFY_CONNECTION, &socket_cb, 1488 NULL, 1489 MHD_OPTION_CONNECTION_TIMEOUT, 1490 (unsigned) TIMEOUTS_VAL, 1491 MHD_OPTION_END); 1492 else 1493 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD 1494 | ((unsigned int) pollType) 1495 | (verbose ? MHD_USE_ERROR_LOG : 0), 1496 *pport, NULL, NULL, 1497 &ahcCheck, *ahc_param, 1498 MHD_OPTION_THREAD_POOL_SIZE, 1499 testNumThreadsForPool (pollType), 1500 MHD_OPTION_NOTIFY_CONNECTION, &socket_cb, 1501 NULL, 1502 MHD_OPTION_CONNECTION_TIMEOUT, 1503 (unsigned) TIMEOUTS_VAL, 1504 MHD_OPTION_END); 1505 1506 if (NULL == d) 1507 mhdErrorExitDesc ("Failed to start MHD daemon"); 1508 1509 if (0 == *pport) 1510 { 1511 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 1512 if ((NULL == dinfo) || (0 == dinfo->port)) 1513 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 1514 *pport = dinfo->port; 1515 if (0 == global_port) 1516 global_port = *pport; /* Reuse the same port for all tests */ 1517 } 1518 1519 return d; 1520 } 1521 1522 1523 /* Test runners */ 1524 1525 1526 static unsigned int 1527 testExternalGet (void) 1528 { 1529 struct MHD_Daemon *d; 1530 uint16_t d_port = global_port; /* Daemon's port */ 1531 struct ahc_cls_type *ahc_param; 1532 1533 d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port, 1534 &ahc_param); 1535 1536 return performTestQueries (d, d_port, ahc_param); 1537 } 1538 1539 1540 #if 0 /* disabled runners, not suitable for this test */ 1541 static unsigned int 1542 testInternalGet (enum testMhdPollType pollType) 1543 { 1544 struct MHD_Daemon *d; 1545 uint16_t d_port = global_port; /* Daemon's port */ 1546 struct ahc_cls_type *ahc_param; 1547 struct check_uri_cls *uri_cb_param; 1548 struct term_notif_cb_param *term_result; 1549 1550 d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port, 1551 &ahc_param, &uri_cb_param, &term_result); 1552 1553 return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); 1554 } 1555 1556 1557 static int 1558 testMultithreadedGet (enum testMhdPollType pollType) 1559 { 1560 struct MHD_Daemon *d; 1561 uint16_t d_port = global_port; /* Daemon's port */ 1562 struct ahc_cls_type *ahc_param; 1563 struct check_uri_cls *uri_cb_param; 1564 struct term_notif_cb_param *term_result; 1565 1566 d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port, 1567 &ahc_param, &uri_cb_param); 1568 return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); 1569 } 1570 1571 1572 static unsigned int 1573 testMultithreadedPoolGet (enum testMhdPollType pollType) 1574 { 1575 struct MHD_Daemon *d; 1576 uint16_t d_port = global_port; /* Daemon's port */ 1577 struct ahc_cls_type *ahc_param; 1578 struct check_uri_cls *uri_cb_param; 1579 struct term_notif_cb_param *term_result; 1580 1581 d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port, 1582 &ahc_param, &uri_cb_param); 1583 return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); 1584 } 1585 1586 1587 #endif /* disabled runners, not suitable for this test */ 1588 1589 int 1590 main (int argc, char *const *argv) 1591 { 1592 unsigned int errorCount = 0; 1593 unsigned int test_result = 0; 1594 verbose = 0; 1595 1596 (void) has_in_name; /* Unused, mute compiler warning */ 1597 if ((NULL == argv) || (0 == argv[0])) 1598 return 99; 1599 verbose = ! (has_param (argc, argv, "-q") || 1600 has_param (argc, argv, "--quiet") || 1601 has_param (argc, argv, "-s") || 1602 has_param (argc, argv, "--silent")); 1603 1604 test_global_init (); 1605 1606 /* Could be set to non-zero value to enforce using specific port 1607 * in the test */ 1608 global_port = 0; 1609 test_result = testExternalGet (); 1610 if (test_result) 1611 fprintf (stderr, "FAILED: testExternalGet (). Result: %u.\n", test_result); 1612 else if (verbose) 1613 printf ("PASSED: testExternalGet ().\n"); 1614 errorCount += test_result; 1615 #if 0 /* disabled runners, not suitable for this test */ 1616 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 1617 { 1618 test_result = testInternalGet (testMhdPollAuto); 1619 if (test_result) 1620 fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto). " 1621 "Result: %u.\n", 1622 test_result); 1623 else if (verbose) 1624 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 1625 errorCount += test_result; 1626 #ifdef _MHD_HEAVY_TESTS 1627 /* Actually tests are not heavy, but took too long to complete while 1628 * not really provide any additional results. */ 1629 test_result = testInternalGet (testMhdPollBySelect); 1630 if (test_result) 1631 fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect). " 1632 "Result: %u.\n", 1633 test_result); 1634 else if (verbose) 1635 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 1636 errorCount += test_result; 1637 test_result = testMultithreadedPoolGet (testMhdPollBySelect); 1638 if (test_result) 1639 fprintf (stderr, 1640 "FAILED: testMultithreadedPoolGet (testMhdPollBySelect). " 1641 "Result: %u.\n", 1642 test_result); 1643 else if (verbose) 1644 printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n"); 1645 errorCount += test_result; 1646 test_result = testMultithreadedGet (testMhdPollBySelect); 1647 if (test_result) 1648 fprintf (stderr, 1649 "FAILED: testMultithreadedGet (testMhdPollBySelect). " 1650 "Result: %u.\n", 1651 test_result); 1652 else if (verbose) 1653 printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n"); 1654 errorCount += test_result; 1655 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) 1656 { 1657 test_result = testInternalGet (testMhdPollByPoll); 1658 if (test_result) 1659 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll). " 1660 "Result: %u.\n", 1661 test_result); 1662 else if (verbose) 1663 printf ("PASSED: testInternalGet (testMhdPollByPoll).\n"); 1664 errorCount += test_result; 1665 } 1666 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 1667 { 1668 test_result = testInternalGet (testMhdPollByEpoll); 1669 if (test_result) 1670 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll). " 1671 "Result: %u.\n", 1672 test_result); 1673 else if (verbose) 1674 printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n"); 1675 errorCount += test_result; 1676 } 1677 #else 1678 /* Mute compiler warnings */ 1679 (void) testMultithreadedGet; 1680 (void) testMultithreadedPoolGet; 1681 #endif /* _MHD_HEAVY_TESTS */ 1682 } 1683 #endif /* disabled runners, not suitable for this test */ 1684 if (0 != errorCount) 1685 fprintf (stderr, 1686 "Error (code: %u)\n", 1687 errorCount); 1688 else if (verbose) 1689 printf ("All tests passed.\n"); 1690 1691 test_global_cleanup (); 1692 1693 return (errorCount == 0) ? 0 : 1; /* 0 == pass */ 1694 }