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