test_get_response_cleanup.c (11418B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2009 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 2, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * @file daemontest_get_response_cleanup.c 24 * @brief Testcase for libmicrohttpd response cleanup 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "MHD_config.h" 30 #include "platform.h" 31 #include <microhttpd.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <string.h> 35 #include <time.h> 36 #include <sys/types.h> 37 #include <sys/wait.h> 38 #include <fcntl.h> 39 #include <errno.h> 40 #ifndef _WIN32 41 #include <signal.h> 42 #endif /* _WIN32 */ 43 44 #ifndef WINDOWS 45 #include <sys/socket.h> 46 #include <unistd.h> 47 #endif 48 49 #ifdef _WIN32 50 #ifndef WIN32_LEAN_AND_MEAN 51 #define WIN32_LEAN_AND_MEAN 1 52 #endif /* !WIN32_LEAN_AND_MEAN */ 53 #include <windows.h> 54 #endif 55 56 #include "mhd_has_in_name.h" 57 58 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 59 test into a marked, classifiable test error (TESTING.md, P5). */ 60 #include "mhd_panic_tripwire.h" 61 62 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 63 #undef MHD_CPU_COUNT 64 #endif 65 #if ! defined(MHD_CPU_COUNT) 66 #define MHD_CPU_COUNT 2 67 #endif 68 69 static int oneone; 70 71 static int ok; 72 73 74 static pid_t 75 fork_curl (const char *url) 76 { 77 pid_t ret; 78 79 ret = fork (); 80 if (ret != 0) 81 return ret; 82 execlp ("curl", "curl", "-s", "-N", "-o", "/dev/null", "-GET", url, NULL); 83 fprintf (stderr, 84 "Failed to exec curl: %s\n", 85 strerror (errno)); 86 _exit (-1); 87 } 88 89 90 static void 91 kill_curl (pid_t pid) 92 { 93 int status; 94 95 /* fprintf (stderr, "Killing curl\n"); */ 96 kill (pid, SIGTERM); 97 waitpid (pid, &status, 0); 98 } 99 100 101 static ssize_t 102 push_callback (void *cls, uint64_t pos, char *buf, size_t max) 103 { 104 (void) cls; (void) pos; /* Unused. Silent compiler warning. */ 105 106 if (max == 0) 107 return 0; 108 buf[0] = 'd'; 109 return 1; 110 } 111 112 113 static void 114 push_free_callback (void *cls) 115 { 116 int *ok_p = cls; 117 118 /* fprintf (stderr, "Cleanup callback called!\n"); */ 119 *ok_p = 0; 120 } 121 122 123 static enum MHD_Result 124 ahc_echo (void *cls, 125 struct MHD_Connection *connection, 126 const char *url, 127 const char *method, 128 const char *version, 129 const char *upload_data, size_t *upload_data_size, 130 void **req_cls) 131 { 132 static int ptr; 133 struct MHD_Response *response; 134 enum MHD_Result ret; 135 (void) cls; 136 (void) url; (void) version; /* Unused. Silent compiler warning. */ 137 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 138 139 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 140 return MHD_NO; /* unexpected method */ 141 if (&ptr != *req_cls) 142 { 143 *req_cls = &ptr; 144 return MHD_YES; 145 } 146 *req_cls = NULL; 147 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 148 32 * 1024, 149 &push_callback, 150 &ok, 151 &push_free_callback); 152 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 153 MHD_destroy_response (response); 154 if (ret == MHD_NO) 155 abort (); 156 return ret; 157 } 158 159 160 static unsigned int 161 testInternalGet (void) 162 { 163 struct MHD_Daemon *d; 164 pid_t curl; 165 uint16_t port; 166 char url[127]; 167 168 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 169 port = 0; 170 else 171 { 172 port = 1180; 173 if (oneone) 174 port += 10; 175 } 176 177 ok = 1; 178 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 179 port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 180 if (d == NULL) 181 return 1; 182 if (0 == port) 183 { 184 const union MHD_DaemonInfo *dinfo; 185 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 186 if ((NULL == dinfo) || (0 == dinfo->port) ) 187 { 188 MHD_stop_daemon (d); return 32; 189 } 190 port = dinfo->port; 191 } 192 snprintf (url, 193 sizeof (url), 194 "http://127.0.0.1:%u/", 195 (unsigned int) port); 196 curl = fork_curl (url); 197 (void) sleep (1); 198 kill_curl (curl); 199 (void) sleep (1); 200 /* fprintf (stderr, "Stopping daemon!\n"); */ 201 MHD_stop_daemon (d); 202 if (ok != 0) 203 return 2; 204 return 0; 205 } 206 207 208 static unsigned int 209 testMultithreadedGet (void) 210 { 211 struct MHD_Daemon *d; 212 pid_t curl; 213 uint16_t port; 214 char url[127]; 215 216 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 217 port = 0; 218 else 219 { 220 port = 1181; 221 if (oneone) 222 port += 10; 223 } 224 225 ok = 1; 226 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 227 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 228 port, NULL, NULL, &ahc_echo, NULL, 229 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 2, 230 MHD_OPTION_END); 231 if (d == NULL) 232 return 16; 233 if (0 == port) 234 { 235 const union MHD_DaemonInfo *dinfo; 236 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 237 if ((NULL == dinfo) || (0 == dinfo->port) ) 238 { 239 MHD_stop_daemon (d); return 32; 240 } 241 port = dinfo->port; 242 } 243 snprintf (url, 244 sizeof (url), 245 "http://127.0.0.1:%u/", 246 (unsigned int) port); 247 /* fprintf (stderr, "Forking cURL!\n"); */ 248 curl = fork_curl (url); 249 (void) sleep (1); 250 kill_curl (curl); 251 (void) sleep (1); 252 curl = fork_curl (url); 253 (void) sleep (1); 254 if (ok != 0) 255 { 256 kill_curl (curl); 257 MHD_stop_daemon (d); 258 return 64; 259 } 260 kill_curl (curl); 261 (void) sleep (1); 262 /* fprintf (stderr, "Stopping daemon!\n"); */ 263 MHD_stop_daemon (d); 264 if (ok != 0) 265 return 32; 266 267 return 0; 268 } 269 270 271 static unsigned int 272 testMultithreadedPoolGet (void) 273 { 274 struct MHD_Daemon *d; 275 pid_t curl; 276 uint16_t port; 277 char url[127]; 278 279 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 280 port = 0; 281 else 282 { 283 port = 1182; 284 if (oneone) 285 port += 10; 286 } 287 288 ok = 1; 289 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 290 port, NULL, NULL, &ahc_echo, NULL, 291 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 292 MHD_OPTION_END); 293 if (d == NULL) 294 return 64; 295 if (0 == port) 296 { 297 const union MHD_DaemonInfo *dinfo; 298 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 299 if ((NULL == dinfo) || (0 == dinfo->port) ) 300 { 301 MHD_stop_daemon (d); return 32; 302 } 303 port = dinfo->port; 304 } 305 snprintf (url, 306 sizeof (url), 307 "http://127.0.0.1:%u/", 308 (unsigned int) port); 309 curl = fork_curl (url); 310 (void) sleep (1); 311 kill_curl (curl); 312 (void) sleep (1); 313 /* fprintf (stderr, "Stopping daemon!\n"); */ 314 MHD_stop_daemon (d); 315 if (ok != 0) 316 return 128; 317 return 0; 318 } 319 320 321 static unsigned int 322 testExternalGet (void) 323 { 324 struct MHD_Daemon *d; 325 fd_set rs; 326 fd_set ws; 327 fd_set es; 328 MHD_socket max; 329 time_t start; 330 struct timeval tv; 331 pid_t curl; 332 uint16_t port; 333 char url[127]; 334 335 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 336 port = 0; 337 else 338 { 339 port = 1183; 340 if (oneone) 341 port += 10; 342 } 343 344 ok = 1; 345 d = MHD_start_daemon (MHD_USE_ERROR_LOG, 346 port, NULL, NULL, &ahc_echo, NULL, 347 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 348 MHD_OPTION_END); 349 if (d == NULL) 350 return 256; 351 if (0 == port) 352 { 353 const union MHD_DaemonInfo *dinfo; 354 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 355 if ((NULL == dinfo) || (0 == dinfo->port) ) 356 { 357 MHD_stop_daemon (d); return 32; 358 } 359 port = dinfo->port; 360 } 361 snprintf (url, 362 sizeof (url), 363 "http://127.0.0.1:%u/", 364 (unsigned int) port); 365 curl = fork_curl (url); 366 367 start = time (NULL); 368 while ((time (NULL) - start < 2)) 369 { 370 max = 0; 371 FD_ZERO (&rs); 372 FD_ZERO (&ws); 373 FD_ZERO (&es); 374 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 375 { 376 MHD_stop_daemon (d); 377 return 4096; 378 } 379 tv.tv_sec = 0; 380 tv.tv_usec = 1000; 381 if (-1 == select (max + 1, &rs, &ws, &es, &tv)) 382 { 383 #ifdef MHD_POSIX_SOCKETS 384 if (EINTR != errno) 385 { 386 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 387 (int) errno, __LINE__); 388 fflush (stderr); 389 exit (99); 390 } 391 #else 392 if ((WSAEINVAL != WSAGetLastError ()) || 393 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 394 { 395 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 396 (int) WSAGetLastError (), __LINE__); 397 fflush (stderr); 398 exit (99); 399 } 400 Sleep (1); 401 #endif 402 } 403 MHD_run (d); 404 } 405 kill_curl (curl); 406 start = time (NULL); 407 while ((time (NULL) - start < 2)) 408 { 409 max = 0; 410 FD_ZERO (&rs); 411 FD_ZERO (&ws); 412 FD_ZERO (&es); 413 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 414 { 415 MHD_stop_daemon (d); 416 return 4096; 417 } 418 tv.tv_sec = 0; 419 tv.tv_usec = 1000; 420 if (-1 == select (max + 1, &rs, &ws, &es, &tv)) 421 { 422 #ifdef MHD_POSIX_SOCKETS 423 if (EINTR != errno) 424 { 425 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 426 (int) errno, __LINE__); 427 fflush (stderr); 428 exit (99); 429 } 430 #else 431 if ((WSAEINVAL != WSAGetLastError ()) || 432 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 433 { 434 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 435 (int) WSAGetLastError (), __LINE__); 436 fflush (stderr); 437 exit (99); 438 } 439 Sleep (1); 440 #endif 441 } 442 MHD_run (d); 443 } 444 /* fprintf (stderr, "Stopping daemon!\n"); */ 445 MHD_stop_daemon (d); 446 if (ok != 0) 447 return 1024; 448 return 0; 449 } 450 451 452 int 453 main (int argc, char *const *argv) 454 { 455 unsigned int errorCount = 0; 456 (void) argc; /* Unused. Silent compiler warning. */ 457 458 #ifndef _WIN32 459 /* Solaris has no way to disable SIGPIPE on socket disconnect. */ 460 if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE)) 461 { 462 struct sigaction act; 463 464 act.sa_handler = SIG_IGN; 465 sigaction (SIGPIPE, &act, NULL); 466 } 467 #endif /* _WIN32 */ 468 469 if ((NULL == argv) || (0 == argv[0])) 470 return 99; 471 oneone = has_in_name (argv[0], "11"); 472 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 473 { 474 errorCount += testInternalGet (); 475 errorCount += testMultithreadedGet (); 476 errorCount += testMultithreadedPoolGet (); 477 } 478 errorCount += testExternalGet (); 479 if (errorCount != 0) 480 fprintf (stderr, "Error (code: %u)\n", errorCount); 481 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 482 }