test_get_response_cleanup.c (11251B)
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 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 59 #undef MHD_CPU_COUNT 60 #endif 61 #if ! defined(MHD_CPU_COUNT) 62 #define MHD_CPU_COUNT 2 63 #endif 64 65 static int oneone; 66 67 static int ok; 68 69 70 static pid_t 71 fork_curl (const char *url) 72 { 73 pid_t ret; 74 75 ret = fork (); 76 if (ret != 0) 77 return ret; 78 execlp ("curl", "curl", "-s", "-N", "-o", "/dev/null", "-GET", url, NULL); 79 fprintf (stderr, 80 "Failed to exec curl: %s\n", 81 strerror (errno)); 82 _exit (-1); 83 } 84 85 86 static void 87 kill_curl (pid_t pid) 88 { 89 int status; 90 91 /* fprintf (stderr, "Killing curl\n"); */ 92 kill (pid, SIGTERM); 93 waitpid (pid, &status, 0); 94 } 95 96 97 static ssize_t 98 push_callback (void *cls, uint64_t pos, char *buf, size_t max) 99 { 100 (void) cls; (void) pos; /* Unused. Silent compiler warning. */ 101 102 if (max == 0) 103 return 0; 104 buf[0] = 'd'; 105 return 1; 106 } 107 108 109 static void 110 push_free_callback (void *cls) 111 { 112 int *ok_p = cls; 113 114 /* fprintf (stderr, "Cleanup callback called!\n"); */ 115 *ok_p = 0; 116 } 117 118 119 static enum MHD_Result 120 ahc_echo (void *cls, 121 struct MHD_Connection *connection, 122 const char *url, 123 const char *method, 124 const char *version, 125 const char *upload_data, size_t *upload_data_size, 126 void **req_cls) 127 { 128 static int ptr; 129 struct MHD_Response *response; 130 enum MHD_Result ret; 131 (void) cls; 132 (void) url; (void) version; /* Unused. Silent compiler warning. */ 133 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 134 135 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 136 return MHD_NO; /* unexpected method */ 137 if (&ptr != *req_cls) 138 { 139 *req_cls = &ptr; 140 return MHD_YES; 141 } 142 *req_cls = NULL; 143 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 144 32 * 1024, 145 &push_callback, 146 &ok, 147 &push_free_callback); 148 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 149 MHD_destroy_response (response); 150 if (ret == MHD_NO) 151 abort (); 152 return ret; 153 } 154 155 156 static unsigned int 157 testInternalGet (void) 158 { 159 struct MHD_Daemon *d; 160 pid_t curl; 161 uint16_t port; 162 char url[127]; 163 164 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 165 port = 0; 166 else 167 { 168 port = 1180; 169 if (oneone) 170 port += 10; 171 } 172 173 ok = 1; 174 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 175 port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 176 if (d == NULL) 177 return 1; 178 if (0 == port) 179 { 180 const union MHD_DaemonInfo *dinfo; 181 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 182 if ((NULL == dinfo) || (0 == dinfo->port) ) 183 { 184 MHD_stop_daemon (d); return 32; 185 } 186 port = dinfo->port; 187 } 188 snprintf (url, 189 sizeof (url), 190 "http://127.0.0.1:%u/", 191 (unsigned int) port); 192 curl = fork_curl (url); 193 (void) sleep (1); 194 kill_curl (curl); 195 (void) sleep (1); 196 /* fprintf (stderr, "Stopping daemon!\n"); */ 197 MHD_stop_daemon (d); 198 if (ok != 0) 199 return 2; 200 return 0; 201 } 202 203 204 static unsigned int 205 testMultithreadedGet (void) 206 { 207 struct MHD_Daemon *d; 208 pid_t curl; 209 uint16_t port; 210 char url[127]; 211 212 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 213 port = 0; 214 else 215 { 216 port = 1181; 217 if (oneone) 218 port += 10; 219 } 220 221 ok = 1; 222 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 223 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 224 port, NULL, NULL, &ahc_echo, NULL, 225 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 2, 226 MHD_OPTION_END); 227 if (d == NULL) 228 return 16; 229 if (0 == port) 230 { 231 const union MHD_DaemonInfo *dinfo; 232 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 233 if ((NULL == dinfo) || (0 == dinfo->port) ) 234 { 235 MHD_stop_daemon (d); return 32; 236 } 237 port = dinfo->port; 238 } 239 snprintf (url, 240 sizeof (url), 241 "http://127.0.0.1:%u/", 242 (unsigned int) port); 243 /* fprintf (stderr, "Forking cURL!\n"); */ 244 curl = fork_curl (url); 245 (void) sleep (1); 246 kill_curl (curl); 247 (void) sleep (1); 248 curl = fork_curl (url); 249 (void) sleep (1); 250 if (ok != 0) 251 { 252 kill_curl (curl); 253 MHD_stop_daemon (d); 254 return 64; 255 } 256 kill_curl (curl); 257 (void) sleep (1); 258 /* fprintf (stderr, "Stopping daemon!\n"); */ 259 MHD_stop_daemon (d); 260 if (ok != 0) 261 return 32; 262 263 return 0; 264 } 265 266 267 static unsigned int 268 testMultithreadedPoolGet (void) 269 { 270 struct MHD_Daemon *d; 271 pid_t curl; 272 uint16_t port; 273 char url[127]; 274 275 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 276 port = 0; 277 else 278 { 279 port = 1182; 280 if (oneone) 281 port += 10; 282 } 283 284 ok = 1; 285 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 286 port, NULL, NULL, &ahc_echo, NULL, 287 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 288 MHD_OPTION_END); 289 if (d == NULL) 290 return 64; 291 if (0 == port) 292 { 293 const union MHD_DaemonInfo *dinfo; 294 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 295 if ((NULL == dinfo) || (0 == dinfo->port) ) 296 { 297 MHD_stop_daemon (d); return 32; 298 } 299 port = dinfo->port; 300 } 301 snprintf (url, 302 sizeof (url), 303 "http://127.0.0.1:%u/", 304 (unsigned int) port); 305 curl = fork_curl (url); 306 (void) sleep (1); 307 kill_curl (curl); 308 (void) sleep (1); 309 /* fprintf (stderr, "Stopping daemon!\n"); */ 310 MHD_stop_daemon (d); 311 if (ok != 0) 312 return 128; 313 return 0; 314 } 315 316 317 static unsigned int 318 testExternalGet (void) 319 { 320 struct MHD_Daemon *d; 321 fd_set rs; 322 fd_set ws; 323 fd_set es; 324 MHD_socket max; 325 time_t start; 326 struct timeval tv; 327 pid_t curl; 328 uint16_t port; 329 char url[127]; 330 331 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 332 port = 0; 333 else 334 { 335 port = 1183; 336 if (oneone) 337 port += 10; 338 } 339 340 ok = 1; 341 d = MHD_start_daemon (MHD_USE_ERROR_LOG, 342 port, NULL, NULL, &ahc_echo, NULL, 343 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 344 MHD_OPTION_END); 345 if (d == NULL) 346 return 256; 347 if (0 == port) 348 { 349 const union MHD_DaemonInfo *dinfo; 350 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 351 if ((NULL == dinfo) || (0 == dinfo->port) ) 352 { 353 MHD_stop_daemon (d); return 32; 354 } 355 port = dinfo->port; 356 } 357 snprintf (url, 358 sizeof (url), 359 "http://127.0.0.1:%u/", 360 (unsigned int) port); 361 curl = fork_curl (url); 362 363 start = time (NULL); 364 while ((time (NULL) - start < 2)) 365 { 366 max = 0; 367 FD_ZERO (&rs); 368 FD_ZERO (&ws); 369 FD_ZERO (&es); 370 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 371 { 372 MHD_stop_daemon (d); 373 return 4096; 374 } 375 tv.tv_sec = 0; 376 tv.tv_usec = 1000; 377 if (-1 == select (max + 1, &rs, &ws, &es, &tv)) 378 { 379 #ifdef MHD_POSIX_SOCKETS 380 if (EINTR != errno) 381 { 382 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 383 (int) errno, __LINE__); 384 fflush (stderr); 385 exit (99); 386 } 387 #else 388 if ((WSAEINVAL != WSAGetLastError ()) || 389 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 390 { 391 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 392 (int) WSAGetLastError (), __LINE__); 393 fflush (stderr); 394 exit (99); 395 } 396 Sleep (1); 397 #endif 398 } 399 MHD_run (d); 400 } 401 kill_curl (curl); 402 start = time (NULL); 403 while ((time (NULL) - start < 2)) 404 { 405 max = 0; 406 FD_ZERO (&rs); 407 FD_ZERO (&ws); 408 FD_ZERO (&es); 409 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 410 { 411 MHD_stop_daemon (d); 412 return 4096; 413 } 414 tv.tv_sec = 0; 415 tv.tv_usec = 1000; 416 if (-1 == select (max + 1, &rs, &ws, &es, &tv)) 417 { 418 #ifdef MHD_POSIX_SOCKETS 419 if (EINTR != errno) 420 { 421 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 422 (int) errno, __LINE__); 423 fflush (stderr); 424 exit (99); 425 } 426 #else 427 if ((WSAEINVAL != WSAGetLastError ()) || 428 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 429 { 430 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 431 (int) WSAGetLastError (), __LINE__); 432 fflush (stderr); 433 exit (99); 434 } 435 Sleep (1); 436 #endif 437 } 438 MHD_run (d); 439 } 440 /* fprintf (stderr, "Stopping daemon!\n"); */ 441 MHD_stop_daemon (d); 442 if (ok != 0) 443 return 1024; 444 return 0; 445 } 446 447 448 int 449 main (int argc, char *const *argv) 450 { 451 unsigned int errorCount = 0; 452 (void) argc; /* Unused. Silent compiler warning. */ 453 454 #ifndef _WIN32 455 /* Solaris has no way to disable SIGPIPE on socket disconnect. */ 456 if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE)) 457 { 458 struct sigaction act; 459 460 act.sa_handler = SIG_IGN; 461 sigaction (SIGPIPE, &act, NULL); 462 } 463 #endif /* _WIN32 */ 464 465 if ((NULL == argv) || (0 == argv[0])) 466 return 99; 467 oneone = has_in_name (argv[0], "11"); 468 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 469 { 470 errorCount += testInternalGet (); 471 errorCount += testMultithreadedGet (); 472 errorCount += testMultithreadedPoolGet (); 473 } 474 errorCount += testExternalGet (); 475 if (errorCount != 0) 476 fprintf (stderr, "Error (code: %u)\n", errorCount); 477 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 478 }