test_process_headers.c (16429B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 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 test_process_headers.c 24 * @brief Testcase for HTTP header access 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "MHD_config.h" 30 #include "platform.h" 31 #include <curl/curl.h> 32 #include <microhttpd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <time.h> 36 #include <errno.h> 37 #include "mhd_has_in_name.h" 38 39 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 40 test into a marked, classifiable test error (TESTING.md, P5). */ 41 #include "mhd_panic_tripwire.h" 42 43 #ifndef WINDOWS 44 #include <unistd.h> 45 #endif 46 47 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 48 #undef MHD_CPU_COUNT 49 #endif 50 #if ! defined(MHD_CPU_COUNT) 51 #define MHD_CPU_COUNT 2 52 #endif 53 54 static int oneone; 55 56 struct CBC 57 { 58 char *buf; 59 size_t pos; 60 size_t size; 61 }; 62 63 static size_t 64 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 65 { 66 struct CBC *cbc = ctx; 67 68 if (cbc->pos + size * nmemb > cbc->size) 69 return 0; /* overflow */ 70 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 71 cbc->pos += size * nmemb; 72 return size * nmemb; 73 } 74 75 76 static enum MHD_Result 77 kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) 78 { 79 if ((0 == strcmp (key, MHD_HTTP_HEADER_HOST)) && 80 (0 == strncmp (value, "127.0.0.1", strlen ("127.0.0.1"))) && (kind == 81 MHD_HEADER_KIND)) 82 { 83 *((int *) cls) = 1; 84 return MHD_NO; 85 } 86 return MHD_YES; 87 } 88 89 90 static enum MHD_Result 91 ahc_echo (void *cls, 92 struct MHD_Connection *connection, 93 const char *url, 94 const char *method, 95 const char *version, 96 const char *upload_data, size_t *upload_data_size, 97 void **req_cls) 98 { 99 static int ptr; 100 struct MHD_Response *response; 101 enum MHD_Result ret; 102 const char *hdr; 103 (void) cls; 104 (void) version; (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 105 106 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 107 return MHD_NO; /* unexpected method */ 108 if (&ptr != *req_cls) 109 { 110 *req_cls = &ptr; 111 return MHD_YES; 112 } 113 *req_cls = NULL; 114 ret = 0; 115 MHD_get_connection_values (connection, MHD_HEADER_KIND, &kv_cb, &ret); 116 if (ret != 1) 117 abort (); 118 hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "NotFound"); 119 if (hdr != NULL) 120 abort (); 121 hdr = MHD_lookup_connection_value (connection, 122 MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT); 123 if ((hdr == NULL) || (0 != strcmp (hdr, "*/*"))) 124 abort (); 125 hdr = MHD_lookup_connection_value (connection, 126 MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST); 127 if ((hdr == NULL) || (0 != strncmp (hdr, "127.0.0.1", strlen ("127.0.0.1")))) 128 abort (); 129 MHD_set_connection_value (connection, 130 MHD_HEADER_KIND, "FakeHeader", "NowPresent"); 131 hdr = MHD_lookup_connection_value (connection, 132 MHD_HEADER_KIND, "FakeHeader"); 133 if ((hdr == NULL) || (0 != strcmp (hdr, "NowPresent"))) 134 abort (); 135 136 response = MHD_create_response_from_buffer_copy (strlen (url), 137 (const void *) url); 138 if (NULL == response) 139 abort (); 140 MHD_add_response_header (response, "MyHeader", "MyValue"); 141 hdr = MHD_get_response_header (response, "MyHeader"); 142 if (0 != strcmp ("MyValue", hdr)) 143 abort (); 144 MHD_add_response_header (response, "MyHeader", "MyValueToo"); 145 if (MHD_YES != MHD_del_response_header (response, "MyHeader", "MyValue")) 146 abort (); 147 hdr = MHD_get_response_header (response, "MyHeader"); 148 if (0 != strcmp ("MyValueToo", hdr)) 149 abort (); 150 if (1 != MHD_get_response_headers (response, NULL, NULL)) 151 abort (); 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 CURL *c; 165 char buf[2048]; 166 struct CBC cbc; 167 CURLcode errornum; 168 uint16_t port; 169 170 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 171 port = 0; 172 else 173 { 174 port = 1420; 175 if (oneone) 176 port += 10; 177 } 178 179 cbc.buf = buf; 180 cbc.size = 2048; 181 cbc.pos = 0; 182 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 183 port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 184 if (d == NULL) 185 return 1; 186 if (0 == port) 187 { 188 const union MHD_DaemonInfo *dinfo; 189 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 190 if ((NULL == dinfo) || (0 == dinfo->port) ) 191 { 192 MHD_stop_daemon (d); return 32; 193 } 194 port = dinfo->port; 195 } 196 c = curl_easy_init (); 197 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 198 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 199 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 200 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 201 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 202 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 203 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 204 if (oneone) 205 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 206 else 207 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 208 /* NOTE: use of CONNECTTIMEOUT without also 209 setting NOSIGNAL results in really weird 210 crashes on my system! */ 211 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 212 if (CURLE_OK != (errornum = curl_easy_perform (c))) 213 { 214 fprintf (stderr, 215 "curl_easy_perform failed: `%s'\n", 216 curl_easy_strerror (errornum)); 217 curl_easy_cleanup (c); 218 MHD_stop_daemon (d); 219 return 2; 220 } 221 curl_easy_cleanup (c); 222 MHD_stop_daemon (d); 223 if (cbc.pos != strlen ("/hello_world")) 224 return 4; 225 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 226 return 8; 227 return 0; 228 } 229 230 231 static unsigned int 232 testMultithreadedGet (void) 233 { 234 struct MHD_Daemon *d; 235 CURL *c; 236 char buf[2048]; 237 struct CBC cbc; 238 CURLcode errornum; 239 uint16_t port; 240 241 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 242 port = 0; 243 else 244 { 245 port = 1421; 246 if (oneone) 247 port += 10; 248 } 249 250 cbc.buf = buf; 251 cbc.size = 2048; 252 cbc.pos = 0; 253 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 254 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 255 port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 256 if (d == NULL) 257 return 16; 258 if (0 == port) 259 { 260 const union MHD_DaemonInfo *dinfo; 261 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 262 if ((NULL == dinfo) || (0 == dinfo->port) ) 263 { 264 MHD_stop_daemon (d); return 32; 265 } 266 port = dinfo->port; 267 } 268 c = curl_easy_init (); 269 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 270 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 271 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 272 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 273 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 274 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 275 if (oneone) 276 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 277 else 278 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 279 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 280 /* NOTE: use of CONNECTTIMEOUT without also 281 setting NOSIGNAL results in really weird 282 crashes on my system! */ 283 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 284 if (CURLE_OK != (errornum = curl_easy_perform (c))) 285 { 286 fprintf (stderr, 287 "curl_easy_perform failed: `%s'\n", 288 curl_easy_strerror (errornum)); 289 curl_easy_cleanup (c); 290 MHD_stop_daemon (d); 291 return 32; 292 } 293 curl_easy_cleanup (c); 294 MHD_stop_daemon (d); 295 if (cbc.pos != strlen ("/hello_world")) 296 return 64; 297 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 298 return 128; 299 return 0; 300 } 301 302 303 static unsigned int 304 testMultithreadedPoolGet (void) 305 { 306 struct MHD_Daemon *d; 307 CURL *c; 308 char buf[2048]; 309 struct CBC cbc; 310 CURLcode errornum; 311 uint16_t port; 312 313 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 314 port = 0; 315 else 316 { 317 port = 1422; 318 if (oneone) 319 port += 10; 320 } 321 322 cbc.buf = buf; 323 cbc.size = 2048; 324 cbc.pos = 0; 325 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 326 port, NULL, NULL, &ahc_echo, NULL, 327 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 328 MHD_OPTION_END); 329 if (d == NULL) 330 return 16; 331 if (0 == port) 332 { 333 const union MHD_DaemonInfo *dinfo; 334 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 335 if ((NULL == dinfo) || (0 == dinfo->port) ) 336 { 337 MHD_stop_daemon (d); return 32; 338 } 339 port = dinfo->port; 340 } 341 c = curl_easy_init (); 342 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 343 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 344 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 345 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 346 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 347 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 348 if (oneone) 349 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 350 else 351 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 352 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 353 /* NOTE: use of CONNECTTIMEOUT without also 354 setting NOSIGNAL results in really weird 355 crashes on my system! */ 356 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 357 if (CURLE_OK != (errornum = curl_easy_perform (c))) 358 { 359 fprintf (stderr, 360 "curl_easy_perform failed: `%s'\n", 361 curl_easy_strerror (errornum)); 362 curl_easy_cleanup (c); 363 MHD_stop_daemon (d); 364 return 32; 365 } 366 curl_easy_cleanup (c); 367 MHD_stop_daemon (d); 368 if (cbc.pos != strlen ("/hello_world")) 369 return 64; 370 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 371 return 128; 372 return 0; 373 } 374 375 376 static unsigned int 377 testExternalGet (void) 378 { 379 struct MHD_Daemon *d; 380 CURL *c; 381 char buf[2048]; 382 struct CBC cbc; 383 CURLM *multi; 384 CURLMcode mret; 385 fd_set rs; 386 fd_set ws; 387 fd_set es; 388 MHD_socket maxsock; 389 #ifdef MHD_WINSOCK_SOCKETS 390 int maxposixs; /* Max socket number unused on W32 */ 391 #else /* MHD_POSIX_SOCKETS */ 392 #define maxposixs maxsock 393 #endif /* MHD_POSIX_SOCKETS */ 394 int running; 395 struct CURLMsg *msg; 396 time_t start; 397 struct timeval tv; 398 uint16_t port; 399 400 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 401 port = 0; 402 else 403 { 404 port = 1423; 405 if (oneone) 406 port += 10; 407 } 408 409 multi = NULL; 410 cbc.buf = buf; 411 cbc.size = 2048; 412 cbc.pos = 0; 413 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 414 port, NULL, NULL, &ahc_echo, NULL, 415 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 416 MHD_OPTION_END); 417 if (d == NULL) 418 return 256; 419 if (0 == port) 420 { 421 const union MHD_DaemonInfo *dinfo; 422 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 423 if ((NULL == dinfo) || (0 == dinfo->port) ) 424 { 425 MHD_stop_daemon (d); return 32; 426 } 427 port = dinfo->port; 428 } 429 c = curl_easy_init (); 430 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 431 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 432 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 433 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 434 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 435 if (oneone) 436 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 437 else 438 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 439 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 440 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 441 /* NOTE: use of CONNECTTIMEOUT without also 442 setting NOSIGNAL results in really weird 443 crashes on my system! */ 444 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 445 446 447 multi = curl_multi_init (); 448 if (multi == NULL) 449 { 450 curl_easy_cleanup (c); 451 MHD_stop_daemon (d); 452 return 512; 453 } 454 mret = curl_multi_add_handle (multi, c); 455 if (mret != CURLM_OK) 456 { 457 curl_multi_cleanup (multi); 458 curl_easy_cleanup (c); 459 MHD_stop_daemon (d); 460 return 1024; 461 } 462 start = time (NULL); 463 while ((time (NULL) - start < 5) && (multi != NULL)) 464 { 465 maxsock = MHD_INVALID_SOCKET; 466 maxposixs = -1; 467 FD_ZERO (&rs); 468 FD_ZERO (&ws); 469 FD_ZERO (&es); 470 curl_multi_perform (multi, &running); 471 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 472 if (mret != CURLM_OK) 473 { 474 curl_multi_remove_handle (multi, c); 475 curl_multi_cleanup (multi); 476 curl_easy_cleanup (c); 477 MHD_stop_daemon (d); 478 return 2048; 479 } 480 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 481 { 482 curl_multi_remove_handle (multi, c); 483 curl_multi_cleanup (multi); 484 curl_easy_cleanup (c); 485 MHD_stop_daemon (d); 486 return 4096; 487 } 488 tv.tv_sec = 0; 489 tv.tv_usec = 1000; 490 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 491 { 492 #ifdef MHD_POSIX_SOCKETS 493 if (EINTR != errno) 494 { 495 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 496 (int) errno, __LINE__); 497 fflush (stderr); 498 exit (99); 499 } 500 #else 501 if ((WSAEINVAL != WSAGetLastError ()) || 502 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 503 { 504 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 505 (int) WSAGetLastError (), __LINE__); 506 fflush (stderr); 507 exit (99); 508 } 509 Sleep (1); 510 #endif 511 } 512 curl_multi_perform (multi, &running); 513 if (0 == running) 514 { 515 int pending; 516 int curl_fine = 0; 517 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 518 { 519 if (msg->msg == CURLMSG_DONE) 520 { 521 if (msg->data.result == CURLE_OK) 522 curl_fine = 1; 523 else 524 { 525 fprintf (stderr, 526 "%s failed at %s:%d: `%s'\n", 527 "curl_multi_perform", 528 __FILE__, 529 __LINE__, curl_easy_strerror (msg->data.result)); 530 abort (); 531 } 532 } 533 } 534 if (! curl_fine) 535 { 536 fprintf (stderr, "libcurl haven't returned OK code\n"); 537 abort (); 538 } 539 curl_multi_remove_handle (multi, c); 540 curl_multi_cleanup (multi); 541 curl_easy_cleanup (c); 542 c = NULL; 543 multi = NULL; 544 } 545 MHD_run (d); 546 } 547 if (multi != NULL) 548 { 549 curl_multi_remove_handle (multi, c); 550 curl_easy_cleanup (c); 551 curl_multi_cleanup (multi); 552 } 553 MHD_stop_daemon (d); 554 if (cbc.pos != strlen ("/hello_world")) 555 return 8192; 556 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 557 return 16384; 558 return 0; 559 } 560 561 562 int 563 main (int argc, char *const *argv) 564 { 565 unsigned int errorCount = 0; 566 (void) argc; /* Unused. Silent compiler warning. */ 567 568 if ((NULL == argv) || (0 == argv[0])) 569 return 99; 570 oneone = has_in_name (argv[0], "11"); 571 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 572 { 573 errorCount += testInternalGet (); 574 errorCount += testMultithreadedGet (); 575 errorCount += testMultithreadedPoolGet (); 576 } 577 errorCount += testExternalGet (); 578 if (errorCount != 0) 579 fprintf (stderr, "Error (code: %u)\n", errorCount); 580 curl_global_cleanup (); 581 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 582 }