perf_replies.c (61925B)
1 /* 2 This file is part of GNU libmicrohttpd 3 Copyright (C) 2023 Evgeny Grin (Karlson2k) 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions 7 are met: 8 1. Redistributions of source code must retain the above copyright 9 notice unmodified, this list of conditions and the following 10 disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in 13 the documentation and/or other materials provided with the 14 distribution. 15 16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 20 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /** 29 * @file tools/perf_replies.c 30 * @brief Implementation of HTTP server optimised for fast replies 31 * based on MHD. 32 * @author Karlson2k (Evgeny Grin) 33 */ 34 35 #include "mhd_options.h" 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <stdint.h> 40 #include "microhttpd.h" 41 #include "mhd_tool_str_to_uint.h" 42 #include "mhd_tool_get_cpu_count.h" 43 44 #if defined(MHD_REAL_CPU_COUNT) 45 #if MHD_REAL_CPU_COUNT == 0 46 #undef MHD_REAL_CPU_COUNT 47 #endif /* MHD_REAL_CPU_COUNT == 0 */ 48 #endif /* MHD_REAL_CPU_COUNT */ 49 50 #define PERF_RPL_ERR_CODE_BAD_PARAM 65 51 52 #ifndef MHD_STATICSTR_LEN_ 53 /** 54 * Determine length of static string / macro strings at compile time. 55 */ 56 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) 57 #endif /* ! MHD_STATICSTR_LEN_ */ 58 59 /* Static constants */ 60 static const char *const tool_copyright = 61 "Copyright (C) 2023 Evgeny Grin (Karlson2k)"; 62 63 /* Package or build specific string, like 64 "Debian 1.2.3-4" or "RevX, built by MSYS2" */ 65 static const char *const build_revision = "" 66 #ifdef MHD_BUILD_REV_STR 67 MHD_BUILD_REV_STR 68 #endif /* MHD_BUILD_REV_STR */ 69 ; 70 71 #define PERF_REPL_PORT_FALLBACK 48080 72 73 /* Dynamic variables */ 74 static char self_name[500] = "perf_replies"; 75 static uint16_t mhd_port = 0; 76 77 static void 78 set_self_name (int argc, char *const *argv) 79 { 80 if ((argc >= 1) && (NULL != argv[0])) 81 { 82 const char *last_dir_sep; 83 last_dir_sep = strrchr (argv[0], '/'); 84 #ifdef _WIN32 85 if (1) 86 { 87 const char *last_w32_dir_sep; 88 last_w32_dir_sep = strrchr (argv[0], '\\'); 89 if ((NULL == last_dir_sep) || 90 ((NULL != last_w32_dir_sep) && (last_w32_dir_sep > last_dir_sep))) 91 last_dir_sep = last_w32_dir_sep; 92 } 93 #endif /* _WIN32 */ 94 if (NULL != last_dir_sep) 95 { 96 size_t name_len; 97 name_len = strlen (last_dir_sep + 1); 98 if ((0 != name_len) && ((sizeof(self_name) / sizeof(char)) > name_len)) 99 { 100 strcpy (self_name, last_dir_sep + 1); 101 return; 102 } 103 } 104 } 105 /* Set default name */ 106 strcpy (self_name, "perf_replies"); 107 return; 108 } 109 110 111 static unsigned int 112 detect_cpu_core_count (void) 113 { 114 int sys_cpu_count; 115 sys_cpu_count = mhd_tool_get_system_cpu_count (); 116 if (0 >= sys_cpu_count) 117 { 118 int proc_cpu_count; 119 fprintf (stderr, "Failed to detect the number of logical CPU cores " 120 "available on the system.\n"); 121 proc_cpu_count = mhd_tool_get_proc_cpu_count (); 122 if (0 < proc_cpu_count) 123 { 124 fprintf (stderr, "The number of CPU cores available for this process " 125 "is used as a fallback.\n"); 126 sys_cpu_count = proc_cpu_count; 127 } 128 #ifdef MHD_REAL_CPU_COUNT 129 if (0 >= sys_cpu_count) 130 { 131 fprintf (stderr, "configure-detected hardcoded number is used " 132 "as a fallback.\n"); 133 sys_cpu_count = MHD_REAL_CPU_COUNT; 134 } 135 #endif 136 if (0 >= sys_cpu_count) 137 sys_cpu_count = 1; 138 printf ("Assuming %d logical CPU core%s on this system.\n", sys_cpu_count, 139 (1 == sys_cpu_count) ? "" : "s"); 140 } 141 else 142 { 143 printf ("Detected %d logical CPU core%s on this system.\n", sys_cpu_count, 144 (1 == sys_cpu_count) ? "" : "s"); 145 } 146 return (unsigned int) sys_cpu_count; 147 } 148 149 150 static unsigned int 151 get_cpu_core_count (void) 152 { 153 static unsigned int num_cpu_cores = 0; 154 if (0 == num_cpu_cores) 155 num_cpu_cores = detect_cpu_core_count (); 156 return num_cpu_cores; 157 } 158 159 160 static unsigned int 161 detect_process_cpu_core_count (void) 162 { 163 unsigned int num_proc_cpu_cores; 164 unsigned int sys_cpu_cores; 165 int res; 166 167 sys_cpu_cores = get_cpu_core_count (); 168 res = mhd_tool_get_proc_cpu_count (); 169 if (0 > res) 170 { 171 fprintf (stderr, "Cannot detect the number of logical CPU cores available " 172 "for this process.\n"); 173 if (1 != sys_cpu_cores) 174 printf ("Assuming all %u system logical CPU cores are available to run " 175 "threads of this process.\n", sys_cpu_cores); 176 else 177 printf ("Assuming single logical CPU core available for this process.\n"); 178 num_proc_cpu_cores = sys_cpu_cores; 179 } 180 else 181 { 182 printf ("Detected %d logical CPU core%s available to run threads " 183 "of this process.\n", res, (1 == res) ? "" : "s"); 184 num_proc_cpu_cores = (unsigned int) res; 185 } 186 if (num_proc_cpu_cores > sys_cpu_cores) 187 { 188 fprintf (stderr, "WARNING: Detected number of CPU cores available " 189 "for this process (%u) is larger than detected number " 190 "of CPU cores on the system (%u).\n", 191 num_proc_cpu_cores, sys_cpu_cores); 192 num_proc_cpu_cores = sys_cpu_cores; 193 fprintf (stderr, "Using %u as the number of logical CPU cores available " 194 "for this process.\n", num_proc_cpu_cores); 195 } 196 return num_proc_cpu_cores; 197 } 198 199 200 static unsigned int 201 get_process_cpu_core_count (void) 202 { 203 static unsigned int proc_num_cpu_cores = 0; 204 if (0 == proc_num_cpu_cores) 205 proc_num_cpu_cores = detect_process_cpu_core_count (); 206 return proc_num_cpu_cores; 207 } 208 209 210 static unsigned int num_threads = 0; 211 212 static unsigned int 213 get_num_threads (void) 214 { 215 #if 0 /* disabled code */ 216 static const unsigned int max_threads = 32; 217 #endif /* disabled code */ 218 if (0 < num_threads) 219 return num_threads; 220 221 num_threads = get_cpu_core_count () / 2; 222 if (0 == num_threads) 223 num_threads = 1; 224 else 225 { 226 unsigned int num_proc_cpus; 227 num_proc_cpus = get_process_cpu_core_count (); 228 if (num_proc_cpus >= num_threads) 229 { 230 printf ("Using half of all available CPU cores, assuming the other half " 231 "is used by client / requests generator.\n"); 232 } 233 else 234 { 235 printf ("Using all CPU cores available for this process as more than " 236 "half of CPU cores on this system are still available for use " 237 "by client / requests generator.\n"); 238 num_threads = num_proc_cpus; 239 } 240 } 241 #if 0 /* disabled code */ 242 if (max_threads < num_threads) 243 { 244 printf ("Number of threads is limited to %u as more threads " 245 "are unlikely to improve performance.\n", max_threads); 246 num_threads = max_threads; 247 } 248 #endif /* disabled code */ 249 250 return num_threads; 251 } 252 253 254 /** 255 * The result of short parameters processing 256 */ 257 enum PerfRepl_param_result 258 { 259 PERF_RPL_PARAM_ERROR, /**< Error processing parameter */ 260 PERF_RPL_PARAM_ONE_CHAR, /**< Processed exactly one character */ 261 PERF_RPL_PARAM_FULL_STR, /**< Processed current parameter completely */ 262 PERF_RPL_PARAM_STR_PLUS_NEXT /**< Current parameter completely and next parameter processed */ 263 }; 264 265 /** 266 * Extract parameter value 267 * @param param_name the name of the parameter 268 * @param param_tail the pointer to the character after parameter name in 269 * the parameter string 270 * @param next_param the pointer to the next parameter (if any) or NULL 271 * @param[out] param_value the pointer where to store resulting value 272 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 273 * this function 274 */ 275 static enum PerfRepl_param_result 276 get_param_value (const char *param_name, const char *param_tail, 277 const char *next_param, unsigned int *param_value) 278 { 279 const char *value_str; 280 size_t digits; 281 if (0 != param_tail[0]) 282 { 283 if ('=' != param_tail[0]) 284 value_str = param_tail; 285 else 286 value_str = param_tail + 1; 287 } 288 else 289 value_str = next_param; 290 291 if (NULL != value_str) 292 digits = mhd_tool_str_to_uint (value_str, param_value); 293 else 294 digits = 0; 295 296 if ((0 == digits) || (0 != value_str[digits])) 297 { 298 fprintf (stderr, "Parameter '%s' is not followed by valid number.\n", 299 param_name); 300 return PERF_RPL_PARAM_ERROR; 301 } 302 303 if (0 != param_tail[0]) 304 return PERF_RPL_PARAM_FULL_STR; 305 306 return PERF_RPL_PARAM_STR_PLUS_NEXT; 307 } 308 309 310 static void 311 show_help (void) 312 { 313 printf ("Usage: %s [OPTIONS] [PORT_NUMBER]\n", self_name); 314 printf ("Start MHD-based web-server optimised for fast replies.\n"); 315 printf ("\n"); 316 printf ("Threading options (mutually exclusive):\n"); 317 printf (" -A, --all-cpus use all available CPU cores (for \n" 318 " testing with remote client)\n"); 319 printf (" -t NUM, --threads=NUM use NUM threads\n"); 320 printf (" -P, --thread-per-conn use thread-per-connection mode,\n" 321 " the number of threads is limited only\n" 322 " by the number of connection\n"); 323 printf ("\n"); 324 printf ("Force polling function (mutually exclusive):\n"); 325 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 326 printf (" -e, --epoll use 'epoll' functionality\n"); 327 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_POLL)) 328 printf (" -p, --poll use poll() function\n"); 329 printf (" -s, --select use select() function\n"); 330 printf ("\n"); 331 printf ("Response body size options (mutually exclusive):\n"); 332 printf (" -E, --empty empty response, 0 bytes\n"); 333 printf (" -T, --tiny tiny response, 3 bytes (default)\n"); 334 printf (" -M, --medium medium response, 8 KiB\n"); 335 printf (" -L, --large large response, 1 MiB\n"); 336 printf (" -X, --xlarge extra large response, 8 MiB\n"); 337 printf (" -J, --jumbo jumbo response, 101 MiB\n"); 338 printf ("\n"); 339 printf ("Response use options (mutually exclusive):\n"); 340 printf (" -S, --shared pool of pre-generated shared response\n" 341 " objects (default)\n"); 342 printf (" -I, --single single pre-generated response object\n" 343 " used for all requests\n"); 344 printf (" -U, --unique response object generated for every\n" 345 " request and used one time only\n"); 346 printf ("\n"); 347 printf ("Other options:\n"); 348 printf (" -c NUM, --connections=NUM reject more than NUM client \n" 349 " connections\n"); 350 printf (" -O NUM, --timeout=NUM set connection timeout to NUM seconds,\n" 351 " zero means no timeout\n"); 352 printf (" --date-header use the 'Date:' header in every\n" 353 " reply\n"); 354 printf (" --help display this help and exit\n"); 355 printf (" -V, --version output version information and exit\n"); 356 printf ("\n"); 357 printf ("This tool is part of GNU libmicrohttpd suite.\n"); 358 printf ("%s\n", tool_copyright); 359 } 360 361 362 struct PerfRepl_parameters 363 { 364 unsigned int port; 365 int all_cpus; 366 unsigned int threads; 367 int thread_per_conn; 368 int epoll; 369 int poll; 370 int select; 371 int empty; 372 int tiny; 373 int medium; 374 int large; 375 int xlarge; 376 int jumbo; 377 int shared; 378 int single; 379 int unique; 380 unsigned int connections; 381 unsigned int timeout; 382 int date_header; 383 int help; 384 int version; 385 }; 386 387 static struct PerfRepl_parameters tool_params = { 388 0, 389 0, 390 0, 391 0, 392 0, 393 0, 394 0, 395 0, 396 0, 397 0, 398 0, 399 0, 400 0, 401 0, 402 0, 403 0, 404 0, 405 0, 406 0, 407 0, 408 0 409 }; 410 411 412 static enum PerfRepl_param_result 413 process_param__all_cpus (const char *param_name) 414 { 415 if (0 != tool_params.threads) 416 { 417 fprintf (stderr, "Parameter '%s' cannot be used together " 418 "with '-t' or '--threads'.\n", param_name); 419 return PERF_RPL_PARAM_ERROR; 420 } 421 if (tool_params.thread_per_conn) 422 { 423 fprintf (stderr, "Parameter '%s' cannot be used together " 424 "with '-P' or '--thread-per-conn'.\n", param_name); 425 return PERF_RPL_PARAM_ERROR; 426 } 427 tool_params.all_cpus = ! 0; 428 return '-' == param_name[1] ? 429 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 430 } 431 432 433 /** 434 * Process parameter '-t' or '--threads' 435 * @param param_name the name of the parameter as specified in command line 436 * @param param_tail the pointer to the character after parameter name in 437 * the parameter string 438 * @param next_param the pointer to the next parameter (if any) or NULL 439 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 440 * this function 441 */ 442 static enum PerfRepl_param_result 443 process_param__threads (const char *param_name, const char *param_tail, 444 const char *next_param) 445 { 446 unsigned int param_value; 447 enum PerfRepl_param_result value_res; 448 449 if (tool_params.all_cpus) 450 { 451 fprintf (stderr, "Parameter '%s' cannot be used together " 452 "with '-A' or '--all-cpus'.\n", param_name); 453 return PERF_RPL_PARAM_ERROR; 454 } 455 if (tool_params.thread_per_conn) 456 { 457 fprintf (stderr, "Parameter '%s' cannot be used together " 458 "with '-P' or '--thread-per-conn'.\n", param_name); 459 return PERF_RPL_PARAM_ERROR; 460 } 461 value_res = get_param_value (param_name, param_tail, next_param, 462 ¶m_value); 463 if (PERF_RPL_PARAM_ERROR == value_res) 464 return value_res; 465 466 if (0 == param_value) 467 { 468 fprintf (stderr, "'0' is not valid value for parameter '%s'.\n", 469 param_name); 470 return PERF_RPL_PARAM_ERROR; 471 } 472 tool_params.threads = param_value; 473 return value_res; 474 } 475 476 477 static enum PerfRepl_param_result 478 process_param__thread_per_conn (const char *param_name) 479 { 480 if (tool_params.all_cpus) 481 { 482 fprintf (stderr, "Parameter '%s' cannot be used together " 483 "with '-A' or '--all-cpus'.\n", param_name); 484 return PERF_RPL_PARAM_ERROR; 485 } 486 if (0 != tool_params.threads) 487 { 488 fprintf (stderr, "Parameter '%s' cannot be used together " 489 "with '-t' or '--threads'.\n", param_name); 490 return PERF_RPL_PARAM_ERROR; 491 } 492 tool_params.thread_per_conn = ! 0; 493 return '-' == param_name[1] ? 494 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 495 } 496 497 498 static enum PerfRepl_param_result 499 process_param__epoll (const char *param_name) 500 { 501 if (tool_params.poll) 502 { 503 fprintf (stderr, "Parameter '%s' cannot be used together " 504 "with '-p' or '--poll'.\n", param_name); 505 return PERF_RPL_PARAM_ERROR; 506 } 507 if (tool_params.select) 508 { 509 fprintf (stderr, "Parameter '%s' cannot be used together " 510 "with '-s' or '--select'.\n", param_name); 511 return PERF_RPL_PARAM_ERROR; 512 } 513 tool_params.epoll = ! 0; 514 return '-' == param_name[1] ? 515 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 516 } 517 518 519 static enum PerfRepl_param_result 520 process_param__poll (const char *param_name) 521 { 522 if (tool_params.epoll) 523 { 524 fprintf (stderr, "Parameter '%s' cannot be used together " 525 "with '-e' or '--epoll'.\n", param_name); 526 return PERF_RPL_PARAM_ERROR; 527 } 528 if (tool_params.select) 529 { 530 fprintf (stderr, "Parameter '%s' cannot be used together " 531 "with '-s' or '--select'.\n", param_name); 532 return PERF_RPL_PARAM_ERROR; 533 } 534 tool_params.poll = ! 0; 535 return '-' == param_name[1] ? 536 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 537 } 538 539 540 static enum PerfRepl_param_result 541 process_param__select (const char *param_name) 542 { 543 if (tool_params.epoll) 544 { 545 fprintf (stderr, "Parameter '%s' cannot be used together " 546 "with '-e' or '--epoll'.\n", param_name); 547 return PERF_RPL_PARAM_ERROR; 548 } 549 if (tool_params.poll) 550 { 551 fprintf (stderr, "Parameter '%s' cannot be used together " 552 "with '-p' or '--poll'.\n", param_name); 553 return PERF_RPL_PARAM_ERROR; 554 } 555 tool_params.select = ! 0; 556 return '-' == param_name[1] ? 557 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 558 } 559 560 561 static enum PerfRepl_param_result 562 process_param__empty (const char *param_name) 563 { 564 if (tool_params.tiny) 565 { 566 fprintf (stderr, "Parameter '%s' cannot be used together " 567 "with '-T' or '--tiny'.\n", param_name); 568 return PERF_RPL_PARAM_ERROR; 569 } 570 if (tool_params.medium) 571 { 572 fprintf (stderr, "Parameter '%s' cannot be used together " 573 "with '-M' or '--medium'.\n", param_name); 574 return PERF_RPL_PARAM_ERROR; 575 } 576 if (tool_params.large) 577 { 578 fprintf (stderr, "Parameter '%s' cannot be used together " 579 "with '-L' or '--large'.\n", param_name); 580 return PERF_RPL_PARAM_ERROR; 581 } 582 if (tool_params.xlarge) 583 { 584 fprintf (stderr, "Parameter '%s' cannot be used together " 585 "with '-X' or '--xlarge'.\n", param_name); 586 return PERF_RPL_PARAM_ERROR; 587 } 588 if (tool_params.jumbo) 589 { 590 fprintf (stderr, "Parameter '%s' cannot be used together " 591 "with '-J' or '--jumbo'.\n", param_name); 592 return PERF_RPL_PARAM_ERROR; 593 } 594 tool_params.empty = ! 0; 595 return '-' == param_name[1] ? 596 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 597 } 598 599 600 static enum PerfRepl_param_result 601 process_param__tiny (const char *param_name) 602 { 603 if (tool_params.empty) 604 { 605 fprintf (stderr, "Parameter '%s' cannot be used together " 606 "with '-E' or '--empty'.\n", param_name); 607 return PERF_RPL_PARAM_ERROR; 608 } 609 if (tool_params.medium) 610 { 611 fprintf (stderr, "Parameter '%s' cannot be used together " 612 "with '-M' or '--medium'.\n", param_name); 613 return PERF_RPL_PARAM_ERROR; 614 } 615 if (tool_params.large) 616 { 617 fprintf (stderr, "Parameter '%s' cannot be used together " 618 "with '-L' or '--large'.\n", param_name); 619 return PERF_RPL_PARAM_ERROR; 620 } 621 if (tool_params.xlarge) 622 { 623 fprintf (stderr, "Parameter '%s' cannot be used together " 624 "with '-X' or '--xlarge'.\n", param_name); 625 return PERF_RPL_PARAM_ERROR; 626 } 627 if (tool_params.jumbo) 628 { 629 fprintf (stderr, "Parameter '%s' cannot be used together " 630 "with '-J' or '--jumbo'.\n", param_name); 631 return PERF_RPL_PARAM_ERROR; 632 } 633 tool_params.tiny = ! 0; 634 return '-' == param_name[1] ? 635 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 636 } 637 638 639 static enum PerfRepl_param_result 640 process_param__medium (const char *param_name) 641 { 642 if (tool_params.empty) 643 { 644 fprintf (stderr, "Parameter '%s' cannot be used together " 645 "with '-E' or '--empty'.\n", param_name); 646 return PERF_RPL_PARAM_ERROR; 647 } 648 if (tool_params.tiny) 649 { 650 fprintf (stderr, "Parameter '%s' cannot be used together " 651 "with '-T' or '--tiny'.\n", param_name); 652 return PERF_RPL_PARAM_ERROR; 653 } 654 if (tool_params.large) 655 { 656 fprintf (stderr, "Parameter '%s' cannot be used together " 657 "with '-L' or '--large'.\n", param_name); 658 return PERF_RPL_PARAM_ERROR; 659 } 660 if (tool_params.xlarge) 661 { 662 fprintf (stderr, "Parameter '%s' cannot be used together " 663 "with '-X' or '--xlarge'.\n", param_name); 664 return PERF_RPL_PARAM_ERROR; 665 } 666 if (tool_params.jumbo) 667 { 668 fprintf (stderr, "Parameter '%s' cannot be used together " 669 "with '-J' or '--jumbo'.\n", param_name); 670 return PERF_RPL_PARAM_ERROR; 671 } 672 tool_params.medium = ! 0; 673 return '-' == param_name[1] ? 674 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 675 } 676 677 678 static enum PerfRepl_param_result 679 process_param__large (const char *param_name) 680 { 681 if (tool_params.empty) 682 { 683 fprintf (stderr, "Parameter '%s' cannot be used together " 684 "with '-E' or '--empty'.\n", param_name); 685 return PERF_RPL_PARAM_ERROR; 686 } 687 if (tool_params.tiny) 688 { 689 fprintf (stderr, "Parameter '%s' cannot be used together " 690 "with '-T' or '--tiny'.\n", param_name); 691 return PERF_RPL_PARAM_ERROR; 692 } 693 if (tool_params.medium) 694 { 695 fprintf (stderr, "Parameter '%s' cannot be used together " 696 "with '-M' or '--medium'.\n", param_name); 697 return PERF_RPL_PARAM_ERROR; 698 } 699 if (tool_params.xlarge) 700 { 701 fprintf (stderr, "Parameter '%s' cannot be used together " 702 "with '-X' or '--xlarge'.\n", param_name); 703 return PERF_RPL_PARAM_ERROR; 704 } 705 if (tool_params.jumbo) 706 { 707 fprintf (stderr, "Parameter '%s' cannot be used together " 708 "with '-J' or '--jumbo'.\n", param_name); 709 return PERF_RPL_PARAM_ERROR; 710 } 711 tool_params.large = ! 0; 712 return '-' == param_name[1] ? 713 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 714 } 715 716 717 static enum PerfRepl_param_result 718 process_param__xlarge (const char *param_name) 719 { 720 if (tool_params.empty) 721 { 722 fprintf (stderr, "Parameter '%s' cannot be used together " 723 "with '-E' or '--empty'.\n", param_name); 724 return PERF_RPL_PARAM_ERROR; 725 } 726 if (tool_params.tiny) 727 { 728 fprintf (stderr, "Parameter '%s' cannot be used together " 729 "with '-T' or '--tiny'.\n", param_name); 730 return PERF_RPL_PARAM_ERROR; 731 } 732 if (tool_params.medium) 733 { 734 fprintf (stderr, "Parameter '%s' cannot be used together " 735 "with '-M' or '--medium'.\n", param_name); 736 return PERF_RPL_PARAM_ERROR; 737 } 738 if (tool_params.large) 739 { 740 fprintf (stderr, "Parameter '%s' cannot be used together " 741 "with '-L' or '--large'.\n", param_name); 742 return PERF_RPL_PARAM_ERROR; 743 } 744 if (tool_params.jumbo) 745 { 746 fprintf (stderr, "Parameter '%s' cannot be used together " 747 "with '-J' or '--jumbo'.\n", param_name); 748 return PERF_RPL_PARAM_ERROR; 749 } 750 tool_params.xlarge = ! 0; 751 return '-' == param_name[1] ? 752 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 753 } 754 755 756 static enum PerfRepl_param_result 757 process_param__jumbo (const char *param_name) 758 { 759 if (tool_params.empty) 760 { 761 fprintf (stderr, "Parameter '%s' cannot be used together " 762 "with '-E' or '--empty'.\n", param_name); 763 return PERF_RPL_PARAM_ERROR; 764 } 765 if (tool_params.tiny) 766 { 767 fprintf (stderr, "Parameter '%s' cannot be used together " 768 "with '-T' or '--tiny'.\n", param_name); 769 return PERF_RPL_PARAM_ERROR; 770 } 771 if (tool_params.medium) 772 { 773 fprintf (stderr, "Parameter '%s' cannot be used together " 774 "with '-M' or '--medium'.\n", param_name); 775 return PERF_RPL_PARAM_ERROR; 776 } 777 if (tool_params.large) 778 { 779 fprintf (stderr, "Parameter '%s' cannot be used together " 780 "with '-L' or '--large'.\n", param_name); 781 return PERF_RPL_PARAM_ERROR; 782 } 783 if (tool_params.xlarge) 784 { 785 fprintf (stderr, "Parameter '%s' cannot be used together " 786 "with '-X' or '--xlarge'.\n", param_name); 787 return PERF_RPL_PARAM_ERROR; 788 } 789 tool_params.jumbo = ! 0; 790 return '-' == param_name[1] ? 791 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 792 } 793 794 795 static enum PerfRepl_param_result 796 process_param__shared (const char *param_name) 797 { 798 if (tool_params.single) 799 { 800 fprintf (stderr, "Parameter '%s' cannot be used together " 801 "with '-I' or '--single'.\n", param_name); 802 return PERF_RPL_PARAM_ERROR; 803 } 804 if (tool_params.unique) 805 { 806 fprintf (stderr, "Parameter '%s' cannot be used together " 807 "with '-U' or '--unique'.\n", param_name); 808 return PERF_RPL_PARAM_ERROR; 809 } 810 tool_params.shared = ! 0; 811 return '-' == param_name[1] ? 812 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 813 } 814 815 816 static enum PerfRepl_param_result 817 process_param__single (const char *param_name) 818 { 819 if (tool_params.shared) 820 { 821 fprintf (stderr, "Parameter '%s' cannot be used together " 822 "with '-S' or '--shared'.\n", param_name); 823 return PERF_RPL_PARAM_ERROR; 824 } 825 if (tool_params.unique) 826 { 827 fprintf (stderr, "Parameter '%s' cannot be used together " 828 "with '-U' or '--unique'.\n", param_name); 829 return PERF_RPL_PARAM_ERROR; 830 } 831 tool_params.single = ! 0; 832 return '-' == param_name[1] ? 833 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 834 } 835 836 837 static enum PerfRepl_param_result 838 process_param__unique (const char *param_name) 839 { 840 if (tool_params.shared) 841 { 842 fprintf (stderr, "Parameter '%s' cannot be used together " 843 "with '-S' or '--shared'.\n", param_name); 844 return PERF_RPL_PARAM_ERROR; 845 } 846 if (tool_params.single) 847 { 848 fprintf (stderr, "Parameter '%s' cannot be used together " 849 "with '-I' or '--single'.\n", param_name); 850 return PERF_RPL_PARAM_ERROR; 851 } 852 tool_params.unique = ! 0; 853 return '-' == param_name[1] ? 854 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 855 } 856 857 858 /** 859 * Process parameter '-c' or '--connections' 860 * @param param_name the name of the parameter as specified in command line 861 * @param param_tail the pointer to the character after parameter name in 862 * the parameter string 863 * @param next_param the pointer to the next parameter (if any) or NULL 864 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 865 * this function 866 */ 867 static enum PerfRepl_param_result 868 process_param__connections (const char *param_name, const char *param_tail, 869 const char *next_param) 870 { 871 unsigned int param_value; 872 enum PerfRepl_param_result value_res; 873 874 value_res = get_param_value (param_name, param_tail, next_param, 875 ¶m_value); 876 if (PERF_RPL_PARAM_ERROR == value_res) 877 return value_res; 878 879 if (0 == param_value) 880 { 881 fprintf (stderr, "'0' is not valid value for parameter '%s'.\n", 882 param_name); 883 return PERF_RPL_PARAM_ERROR; 884 } 885 tool_params.connections = param_value; 886 return value_res; 887 } 888 889 890 /** 891 * Process parameter '-O' or '--timeout' 892 * @param param_name the name of the parameter as specified in command line 893 * @param param_tail the pointer to the character after parameter name in 894 * the parameter string 895 * @param next_param the pointer to the next parameter (if any) or NULL 896 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 897 * this function 898 */ 899 static enum PerfRepl_param_result 900 process_param__timeout (const char *param_name, const char *param_tail, 901 const char *next_param) 902 { 903 unsigned int param_value; 904 enum PerfRepl_param_result value_res; 905 906 value_res = get_param_value (param_name, param_tail, next_param, 907 ¶m_value); 908 if (PERF_RPL_PARAM_ERROR == value_res) 909 return value_res; 910 911 tool_params.timeout = param_value; 912 return value_res; 913 } 914 915 916 static enum PerfRepl_param_result 917 process_param__date_header (const char *param_name) 918 { 919 tool_params.date_header = ! 0; 920 return '-' == param_name[1] ? 921 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 922 } 923 924 925 static enum PerfRepl_param_result 926 process_param__help (const char *param_name) 927 { 928 /* Use only one of help | version */ 929 if (! tool_params.version) 930 tool_params.help = ! 0; 931 return '-' == param_name[1] ? 932 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 933 } 934 935 936 static enum PerfRepl_param_result 937 process_param__version (const char *param_name) 938 { 939 /* Use only one of help | version */ 940 if (! tool_params.help) 941 tool_params.version = ! 0; 942 return '-' == param_name[1] ? 943 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 944 } 945 946 947 /** 948 * Process "short" (one character) parameter. 949 * @param param the pointer to character after "-" or after another valid 950 * parameter 951 * @param next_param the pointer to the next parameter (if any) or 952 * NULL if no next parameter 953 * @return enum value with result 954 */ 955 static enum PerfRepl_param_result 956 process_short_param (const char *param, const char *next_param) 957 { 958 const char param_chr = param[0]; 959 if ('A' == param_chr) 960 return process_param__all_cpus ("-A"); 961 else if ('t' == param_chr) 962 return process_param__threads ("-t", param + 1, next_param); 963 else if ('P' == param_chr) 964 return process_param__thread_per_conn ("-P"); 965 else if ('e' == param_chr) 966 return process_param__epoll ("-e"); 967 else if ('p' == param_chr) 968 return process_param__poll ("-p"); 969 else if ('s' == param_chr) 970 return process_param__select ("-s"); 971 else if ('E' == param_chr) 972 return process_param__empty ("-E"); 973 else if ('T' == param_chr) 974 return process_param__tiny ("-T"); 975 else if ('M' == param_chr) 976 return process_param__medium ("-M"); 977 else if ('L' == param_chr) 978 return process_param__large ("-L"); 979 else if ('X' == param_chr) 980 return process_param__xlarge ("-X"); 981 else if ('J' == param_chr) 982 return process_param__jumbo ("-J"); 983 else if ('S' == param_chr) 984 return process_param__shared ("-S"); 985 else if ('I' == param_chr) 986 return process_param__single ("-I"); 987 else if ('U' == param_chr) 988 return process_param__unique ("-U"); 989 else if ('c' == param_chr) 990 return process_param__connections ("-c", param + 1, next_param); 991 else if ('O' == param_chr) 992 return process_param__timeout ("-O", param + 1, next_param); 993 else if ('V' == param_chr) 994 return process_param__version ("-V"); 995 996 fprintf (stderr, "Unrecognised parameter: -%c.\n", param_chr); 997 return PERF_RPL_PARAM_ERROR; 998 } 999 1000 1001 /** 1002 * Process string of "short" (one character) parameters. 1003 * @param params_str the pointer to first character after "-" 1004 * @param next_param the pointer to the next parameter (if any) or 1005 * NULL if no next parameter 1006 * @return enum value with result 1007 */ 1008 static enum PerfRepl_param_result 1009 process_short_params_str (const char *params_str, const char *next_param) 1010 { 1011 if (0 == params_str[0]) 1012 { 1013 fprintf (stderr, "Unrecognised parameter: -\n"); 1014 return PERF_RPL_PARAM_ERROR; 1015 } 1016 do 1017 { 1018 enum PerfRepl_param_result param_res; 1019 param_res = process_short_param (params_str, next_param); 1020 if (PERF_RPL_PARAM_ONE_CHAR != param_res) 1021 return param_res; 1022 } while (0 != (++params_str)[0]); 1023 return PERF_RPL_PARAM_FULL_STR; 1024 } 1025 1026 1027 /** 1028 * Process "long" (--something) parameters. 1029 * @param param the pointer to first character after "--" 1030 * @param next_param the pointer to the next parameter (if any) or 1031 * NULL if no next parameter 1032 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 1033 * this function 1034 */ 1035 static enum PerfRepl_param_result 1036 process_long_param (const char *param, const char *next_param) 1037 { 1038 const size_t param_len = strlen (param); 1039 1040 if ((MHD_STATICSTR_LEN_ ("all-cpus") == param_len) && 1041 (0 == memcmp (param, "all-cpus", MHD_STATICSTR_LEN_ ("all-cpus")))) 1042 return process_param__all_cpus ("--all-cpus"); 1043 else if ((MHD_STATICSTR_LEN_ ("threads") <= param_len) && 1044 (0 == memcmp (param, "threads", MHD_STATICSTR_LEN_ ("threads")))) 1045 return process_param__threads ("--threads", 1046 param + MHD_STATICSTR_LEN_ ("threads"), 1047 next_param); 1048 else if ((MHD_STATICSTR_LEN_ ("thread-per-conn") == param_len) && 1049 (0 == memcmp (param, "thread-per-conn", 1050 MHD_STATICSTR_LEN_ ("thread-per-conn")))) 1051 return process_param__thread_per_conn ("--thread-per-conn"); 1052 else if ((MHD_STATICSTR_LEN_ ("epoll") == param_len) && 1053 (0 == memcmp (param, "epoll", MHD_STATICSTR_LEN_ ("epoll")))) 1054 return process_param__epoll ("--epoll"); 1055 else if ((MHD_STATICSTR_LEN_ ("poll") == param_len) && 1056 (0 == memcmp (param, "poll", MHD_STATICSTR_LEN_ ("poll")))) 1057 return process_param__poll ("--poll"); 1058 else if ((MHD_STATICSTR_LEN_ ("select") == param_len) && 1059 (0 == memcmp (param, "select", MHD_STATICSTR_LEN_ ("select")))) 1060 return process_param__select ("--select"); 1061 else if ((MHD_STATICSTR_LEN_ ("empty") == param_len) && 1062 (0 == memcmp (param, "empty", MHD_STATICSTR_LEN_ ("empty")))) 1063 return process_param__empty ("--empty"); 1064 else if ((MHD_STATICSTR_LEN_ ("tiny") == param_len) && 1065 (0 == memcmp (param, "tiny", MHD_STATICSTR_LEN_ ("tiny")))) 1066 return process_param__tiny ("--tiny"); 1067 else if ((MHD_STATICSTR_LEN_ ("medium") == param_len) && 1068 (0 == memcmp (param, "medium", MHD_STATICSTR_LEN_ ("medium")))) 1069 return process_param__medium ("--medium"); 1070 else if ((MHD_STATICSTR_LEN_ ("large") == param_len) && 1071 (0 == memcmp (param, "large", MHD_STATICSTR_LEN_ ("large")))) 1072 return process_param__large ("--large"); 1073 else if ((MHD_STATICSTR_LEN_ ("xlarge") == param_len) && 1074 (0 == memcmp (param, "xlarge", MHD_STATICSTR_LEN_ ("xlarge")))) 1075 return process_param__xlarge ("--xlarge"); 1076 else if ((MHD_STATICSTR_LEN_ ("jumbo") == param_len) && 1077 (0 == memcmp (param, "jumbo", MHD_STATICSTR_LEN_ ("jumbo")))) 1078 return process_param__jumbo ("--jumbo"); 1079 else if ((MHD_STATICSTR_LEN_ ("shared") == param_len) && 1080 (0 == memcmp (param, "shared", MHD_STATICSTR_LEN_ ("shared")))) 1081 return process_param__shared ("--shared"); 1082 else if ((MHD_STATICSTR_LEN_ ("single") == param_len) && 1083 (0 == memcmp (param, "single", MHD_STATICSTR_LEN_ ("single")))) 1084 return process_param__single ("--single"); 1085 else if ((MHD_STATICSTR_LEN_ ("unique") == param_len) && 1086 (0 == memcmp (param, "unique", MHD_STATICSTR_LEN_ ("unique")))) 1087 return process_param__unique ("--unique"); 1088 else if ((MHD_STATICSTR_LEN_ ("connections") <= param_len) && 1089 (0 == memcmp (param, "connections", 1090 MHD_STATICSTR_LEN_ ("connections")))) 1091 return process_param__connections ("--connections", 1092 param 1093 + MHD_STATICSTR_LEN_ ("connections"), 1094 next_param); 1095 else if ((MHD_STATICSTR_LEN_ ("timeout") <= param_len) && 1096 (0 == memcmp (param, "timeout", 1097 MHD_STATICSTR_LEN_ ("timeout")))) 1098 return process_param__timeout ("--timeout", 1099 param + MHD_STATICSTR_LEN_ ("timeout"), 1100 next_param); 1101 else if ((MHD_STATICSTR_LEN_ ("date-header") == param_len) && 1102 (0 == memcmp (param, "date-header", 1103 MHD_STATICSTR_LEN_ ("date-header")))) 1104 return process_param__date_header ("--date-header"); 1105 else if ((MHD_STATICSTR_LEN_ ("help") == param_len) && 1106 (0 == memcmp (param, "help", MHD_STATICSTR_LEN_ ("help")))) 1107 return process_param__help ("--help"); 1108 else if ((MHD_STATICSTR_LEN_ ("version") == param_len) && 1109 (0 == memcmp (param, "version", MHD_STATICSTR_LEN_ ("version")))) 1110 return process_param__version ("--version"); 1111 1112 fprintf (stderr, "Unrecognised parameter: --%s.\n", param); 1113 return PERF_RPL_PARAM_ERROR; 1114 } 1115 1116 1117 static int 1118 process_params (int argc, char *const *argv) 1119 { 1120 int proc_dash_param = ! 0; 1121 int i; 1122 for (i = 1; i < argc; ++i) 1123 { 1124 /** 1125 * The currently processed argument 1126 */ 1127 const char *const p = argv[i]; 1128 const char *const p_next = (argc == (i + 1)) ? NULL : (argv[i + 1]); 1129 if (NULL == p) 1130 { 1131 fprintf (stderr, "The NULL in the parameter number %d. " 1132 "The error in the C library?\n", i); 1133 continue; 1134 } 1135 else if (0 == p[0]) 1136 continue; /* Empty */ 1137 else if (proc_dash_param && ('-' == p[0])) 1138 { 1139 enum PerfRepl_param_result param_res; 1140 if ('-' == p[1]) 1141 { 1142 if (0 == p[2]) 1143 { 1144 proc_dash_param = 0; /* The '--' parameter */ 1145 continue; 1146 } 1147 param_res = process_long_param (p + 2, p_next); 1148 } 1149 else 1150 param_res = process_short_params_str (p + 1, p_next); 1151 1152 if (PERF_RPL_PARAM_ERROR == param_res) 1153 return PERF_RPL_ERR_CODE_BAD_PARAM; 1154 if (PERF_RPL_PARAM_STR_PLUS_NEXT == param_res) 1155 ++i; 1156 else if (PERF_RPL_PARAM_ONE_CHAR == param_res) 1157 abort (); 1158 continue; 1159 } 1160 else if (('0' <= p[0]) && ('9' >= p[0])) 1161 { 1162 /* Process the port number */ 1163 unsigned int read_port; 1164 size_t num_digits; 1165 num_digits = mhd_tool_str_to_uint (p, &read_port); 1166 if (0 != p[num_digits]) 1167 { 1168 fprintf (stderr, "Error in specified port number: %s\n", p); 1169 return PERF_RPL_ERR_CODE_BAD_PARAM; 1170 } 1171 else if (65535 < read_port) 1172 { 1173 fprintf (stderr, "Wrong port number: %s\n", p); 1174 return PERF_RPL_ERR_CODE_BAD_PARAM; 1175 } 1176 mhd_port = (uint16_t) read_port; 1177 } 1178 else 1179 { 1180 fprintf (stderr, "Unrecognised parameter: %s\n\n", p); 1181 return PERF_RPL_ERR_CODE_BAD_PARAM; 1182 } 1183 } 1184 return 0; 1185 } 1186 1187 1188 static void 1189 print_version (void) 1190 { 1191 printf ("%s (GNU libmicrohttpd", self_name); 1192 if (0 != build_revision[0]) 1193 printf ("; %s", build_revision); 1194 printf (") %s\n", MHD_get_version ()); 1195 printf ("%s\n", tool_copyright); 1196 } 1197 1198 1199 static void 1200 print_all_cores_used (void) 1201 { 1202 printf ("No CPU cores on this machine are left unused and available " 1203 "for the client / requests generator. " 1204 "Testing with remote client is recommended.\n"); 1205 } 1206 1207 1208 static void 1209 check_param_port (void) 1210 { 1211 if (0 != tool_params.port) 1212 return; 1213 if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 1214 tool_params.port = PERF_REPL_PORT_FALLBACK; 1215 } 1216 1217 1218 /** 1219 * Apply parameter '-A' or '--all-cpus' 1220 */ 1221 static void 1222 check_apply_param__all_cpus (void) 1223 { 1224 if (! tool_params.all_cpus) 1225 return; 1226 1227 num_threads = get_process_cpu_core_count (); 1228 printf ("Requested use of all available CPU cores for MHD threads.\n"); 1229 if (get_cpu_core_count () == num_threads) 1230 print_all_cores_used (); 1231 } 1232 1233 1234 /** 1235 * Apply parameter '-t' or '--threads' 1236 */ 1237 static void 1238 check_apply_param__threads (void) 1239 { 1240 if (0 == tool_params.threads) 1241 return; 1242 1243 num_threads = tool_params.threads; 1244 1245 if (get_process_cpu_core_count () < num_threads) 1246 { 1247 fprintf (stderr, "WARNING: The requested number of threads (%u) is " 1248 "higher than the number of detected available CPU cores (%u).\n", 1249 num_threads, get_process_cpu_core_count ()); 1250 fprintf (stderr, "This decreases the performance. " 1251 "Consider using fewer threads.\n"); 1252 } 1253 if (get_cpu_core_count () == num_threads) 1254 { 1255 printf ("The requested number of threads is equal to the number of " 1256 "detected CPU cores.\n"); 1257 print_all_cores_used (); 1258 } 1259 } 1260 1261 1262 /** 1263 * Apply parameter '-P' or '--thread-per-conn' 1264 * @return non-zero - OK, zero - error 1265 */ 1266 static int 1267 check_apply_param__thread_per_conn (void) 1268 { 1269 if (! tool_params.thread_per_conn) 1270 return ! 0; 1271 1272 if (tool_params.epoll) 1273 { 1274 fprintf (stderr, "'Thread-per-connection' mode cannot be used together " 1275 "with 'epoll'.\n"); 1276 return 0; 1277 } 1278 num_threads = 1; 1279 1280 return ! 0; 1281 } 1282 1283 1284 /* non-zero - OK, zero - error */ 1285 static int 1286 check_param__epoll (void) 1287 { 1288 if (! tool_params.epoll) 1289 return ! 0; 1290 if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 1291 { 1292 fprintf (stderr, "'epoll' was requested, but this MHD build does not " 1293 "support 'epoll' functionality.\n"); 1294 return 0; 1295 } 1296 return ! 0; 1297 } 1298 1299 1300 /* non-zero - OK, zero - error */ 1301 static int 1302 check_param__poll (void) 1303 { 1304 if (! tool_params.poll) 1305 return ! 0; 1306 if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_POLL)) 1307 { 1308 fprintf (stderr, "poll() was requested, but this MHD build does not " 1309 "support polling by poll().\n"); 1310 return 0; 1311 } 1312 return ! 0; 1313 } 1314 1315 1316 static void 1317 check_param__empty_tiny_medium_large_xlarge_jumbo (void) 1318 { 1319 if (0 == (tool_params.empty | tool_params.tiny | tool_params.medium 1320 | tool_params.large | tool_params.xlarge | tool_params.jumbo)) 1321 tool_params.tiny = ! 0; 1322 } 1323 1324 1325 static void 1326 check_param__shared_single_unique (void) 1327 { 1328 if (0 == (tool_params.shared | tool_params.single | tool_params.unique)) 1329 tool_params.shared = ! 0; 1330 } 1331 1332 1333 /* Must be called after 'check_apply_param__threads()' and 1334 'check_apply_param__all_cpus()' */ 1335 /* non-zero - OK, zero - error */ 1336 static int 1337 check_param__connections (void) 1338 { 1339 if (0 == tool_params.connections) 1340 return ! 0; 1341 if (get_num_threads () > tool_params.connections) 1342 { 1343 fprintf (stderr, "The connections number limit (%u) is less than number " 1344 "of threads used (%u). Use higher value for connections limit.\n", 1345 tool_params.connections, get_num_threads ()); 1346 return 0; 1347 } 1348 return ! 0; 1349 } 1350 1351 1352 /** 1353 * Apply decoded parameters 1354 * @return 0 if success, 1355 * positive error code if case of error, 1356 * -1 to exit program with success (0) error code. 1357 */ 1358 static int 1359 check_apply_params (void) 1360 { 1361 if (tool_params.help) 1362 { 1363 show_help (); 1364 return -1; 1365 } 1366 else if (tool_params.version) 1367 { 1368 print_version (); 1369 return -1; 1370 } 1371 check_param_port (); 1372 check_apply_param__all_cpus (); 1373 check_apply_param__threads (); 1374 if (! check_apply_param__thread_per_conn ()) 1375 return PERF_RPL_ERR_CODE_BAD_PARAM; 1376 if (! check_param__epoll ()) 1377 return PERF_RPL_ERR_CODE_BAD_PARAM; 1378 if (! check_param__poll ()) 1379 return PERF_RPL_ERR_CODE_BAD_PARAM; 1380 check_param__empty_tiny_medium_large_xlarge_jumbo (); 1381 check_param__shared_single_unique (); 1382 if (! check_param__connections ()) 1383 return PERF_RPL_ERR_CODE_BAD_PARAM; 1384 return 0; 1385 } 1386 1387 1388 static uint64_t 1389 mini_rnd (void) 1390 { 1391 /* Simple xoshiro256+ implementation */ 1392 static uint64_t s[4] = { 1393 0xE220A8397B1DCDAFuLL, 0x6E789E6AA1B965F4uLL, 1394 0x06C45D188009454FuLL, 0xF88BB8A8724C81ECuLL 1395 }; /* Good enough for static initialisation */ 1396 1397 const uint64_t ret = s[0] + s[3]; 1398 const uint64_t t = s[1] << 17; 1399 1400 s[2] ^= s[0]; 1401 s[3] ^= s[1]; 1402 s[1] ^= s[2]; 1403 s[0] ^= s[3]; 1404 s[2] ^= t; 1405 s[3] = ((s[3] << 45u) | (s[3] >> (64u - 45u))); 1406 1407 return ret; 1408 } 1409 1410 1411 /* The pool of shared responses */ 1412 static struct MHD_Response **resps = NULL; 1413 static unsigned int num_resps = 0; 1414 /* The single response */ 1415 static struct MHD_Response *resp_single = NULL; 1416 1417 /* Use the same memory area to avoid multiple copies. 1418 The system will keep it in cache. */ 1419 static const char tiny_body[] = "Hi!"; 1420 static char *body_dyn = NULL; /* Non-static body data */ 1421 static size_t body_dyn_size; 1422 1423 /* Non-zero - success, zero - failure */ 1424 static int 1425 init_response_body_data (void) 1426 { 1427 if (0 != body_dyn_size) 1428 { 1429 body_dyn = (char *) malloc (body_dyn_size); 1430 if (NULL == body_dyn) 1431 { 1432 fprintf (stderr, "Failed to allocate memory.\n"); 1433 return 0; 1434 } 1435 if (16u * 1024u >= body_dyn_size) 1436 { 1437 /* Fill the body with HTML-like content */ 1438 size_t pos; 1439 size_t filler_pos; 1440 static const char body_header[] = 1441 "<html>\n" 1442 "<head>\n<title>Sample page title</title>\n<head>\n" 1443 "<body>\n"; 1444 static const char body_filler[] = 1445 "The quick brown fox jumps over the lazy dog.<br>\n"; 1446 static const char body_footer[] = 1447 "</body>\n" 1448 "</html>\n"; 1449 pos = 0; 1450 memcpy (body_dyn + pos, body_header, MHD_STATICSTR_LEN_ (body_header)); 1451 pos += MHD_STATICSTR_LEN_ (body_header); 1452 for (filler_pos = 0; 1453 filler_pos < (body_dyn_size - (MHD_STATICSTR_LEN_ (body_header) 1454 + MHD_STATICSTR_LEN_ (body_footer))); 1455 ++filler_pos) 1456 { 1457 body_dyn[pos + filler_pos] = 1458 body_filler[filler_pos % MHD_STATICSTR_LEN_ (body_filler)]; 1459 } 1460 pos += filler_pos; 1461 memcpy (body_dyn + pos, body_footer, MHD_STATICSTR_LEN_ (body_footer)); 1462 } 1463 else if (2u * 1024u * 1024u >= body_dyn_size) 1464 { 1465 /* Fill the body with binary-like content */ 1466 size_t pos; 1467 for (pos = 0; pos < body_dyn_size; ++pos) 1468 { 1469 body_dyn[pos] = (char) (unsigned char) (255U - pos % 256U); 1470 } 1471 } 1472 else 1473 { 1474 /* Fill the body with pseudo-random binary-like content */ 1475 size_t pos; 1476 uint64_t rnd_data; 1477 for (pos = 0; pos < body_dyn_size - body_dyn_size % 8; 1478 pos += 8) 1479 { 1480 rnd_data = mini_rnd (); 1481 body_dyn[pos + 0] = (char) (unsigned char) (rnd_data >> 0); 1482 body_dyn[pos + 1] = (char) (unsigned char) (rnd_data >> 8); 1483 body_dyn[pos + 2] = (char) (unsigned char) (rnd_data >> 16); 1484 body_dyn[pos + 3] = (char) (unsigned char) (rnd_data >> 24); 1485 body_dyn[pos + 4] = (char) (unsigned char) (rnd_data >> 32); 1486 body_dyn[pos + 5] = (char) (unsigned char) (rnd_data >> 40); 1487 body_dyn[pos + 6] = (char) (unsigned char) (rnd_data >> 48); 1488 body_dyn[pos + 7] = (char) (unsigned char) (rnd_data >> 56); 1489 } 1490 rnd_data = mini_rnd (); 1491 for ((void) pos; pos < body_dyn_size; ++pos) 1492 { 1493 body_dyn[pos] = (char) (unsigned char) (rnd_data); 1494 rnd_data >>= 8u; 1495 } 1496 } 1497 } 1498 return ! 0; 1499 } 1500 1501 1502 static struct MHD_Response * 1503 create_response_object (void) 1504 { 1505 #if MHD_VERSION >= 0x00097701 1506 if (NULL != body_dyn) 1507 return MHD_create_response_from_buffer_static (body_dyn_size, 1508 body_dyn); 1509 else if (tool_params.empty) 1510 return MHD_create_response_empty (MHD_RF_NONE); 1511 1512 return MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body), 1513 tiny_body); 1514 1515 #else /* MHD_VERSION < 0x00097701 */ 1516 if (NULL != body_dyn) 1517 return MHD_create_response_from_buffer (body_dyn_size, 1518 (void *) body_dyn, 1519 MHD_RESPMEM_PERSISTENT); 1520 else if (tool_params.empty) 1521 return MHD_create_response_from_buffer (0, 1522 (void *) tiny_body, 1523 MHD_RESPMEM_PERSISTENT); 1524 1525 return MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body), 1526 (void *) tiny_body, 1527 MHD_RESPMEM_PERSISTENT); 1528 #endif /* MHD_VERSION < 0x00097701 */ 1529 } 1530 1531 1532 static int 1533 init_data (void) 1534 { 1535 unsigned int i; 1536 1537 if (tool_params.medium) 1538 body_dyn_size = 8U * 1024U; 1539 else if (tool_params.large) 1540 body_dyn_size = 1024U * 1024U; 1541 else if (tool_params.xlarge) 1542 body_dyn_size = 8U * 1024U * 1024U; 1543 else if (tool_params.jumbo) 1544 body_dyn_size = 101U * 1024U * 1024U; 1545 else 1546 body_dyn_size = 0; 1547 1548 if (! init_response_body_data ()) 1549 return 25; 1550 1551 if (tool_params.unique) 1552 return 0; /* Responses are generated on-fly */ 1553 1554 if (tool_params.single) 1555 { 1556 resp_single = create_response_object (); 1557 if (NULL == resp_single) 1558 { 1559 fprintf (stderr, "Failed to create response.\n"); 1560 return 25; 1561 } 1562 return 0; 1563 } 1564 1565 /* Use more responses to minimise waiting in threads while the response 1566 used by other thread. */ 1567 if (! tool_params.thread_per_conn) 1568 num_resps = 16 * get_num_threads (); 1569 else 1570 num_resps = 16 * get_cpu_core_count (); 1571 1572 resps = (struct MHD_Response **) 1573 malloc ((sizeof(struct MHD_Response *)) * num_resps); 1574 if (NULL == resps) 1575 { 1576 if (NULL != body_dyn) 1577 { 1578 free (body_dyn); 1579 body_dyn = NULL; 1580 } 1581 fprintf (stderr, "Failed to allocate memory.\n"); 1582 return 25; 1583 } 1584 for (i = 0; i < num_resps; ++i) 1585 { 1586 resps[i] = create_response_object (); 1587 if (NULL == resps[i]) 1588 { 1589 fprintf (stderr, "Failed to create responses.\n"); 1590 break; 1591 } 1592 } 1593 if (i == num_resps) 1594 return 0; /* Success */ 1595 1596 /* Cleanup */ 1597 while (i-- != 0) 1598 MHD_destroy_response (resps[i]); 1599 free (resps); 1600 resps = NULL; 1601 num_resps = 0; 1602 if (NULL != body_dyn) 1603 free (body_dyn); 1604 body_dyn = NULL; 1605 return 32; 1606 } 1607 1608 1609 static void 1610 deinit_data (void) 1611 { 1612 if (NULL != resp_single) 1613 MHD_destroy_response (resp_single); 1614 resp_single = NULL; 1615 if (NULL != resps) 1616 { 1617 unsigned int i; 1618 for (i = 0; i < num_resps; ++i) 1619 MHD_destroy_response (resps[i]); 1620 num_resps = 0; 1621 free (resps); 1622 } 1623 resps = NULL; 1624 if (NULL != body_dyn) 1625 free (body_dyn); 1626 body_dyn = NULL; 1627 } 1628 1629 1630 static enum MHD_Result 1631 answer_shared_response (void *cls, 1632 struct MHD_Connection *connection, 1633 const char *url, 1634 const char *method, 1635 const char *version, 1636 const char *upload_data, 1637 size_t *upload_data_size, 1638 void **req_cls) 1639 { 1640 static int marker = 0; 1641 unsigned int resp_index; 1642 static volatile unsigned int last_index = 0; 1643 (void) cls; /* Unused */ 1644 (void) url; (void) version; /* Unused */ 1645 (void) upload_data; (void) upload_data_size; /* Unused */ 1646 1647 if (NULL == *req_cls) 1648 { 1649 /* The first call */ 1650 *req_cls = (void *) ▮ 1651 /* Do not send reply yet. No error. */ 1652 return MHD_YES; 1653 } 1654 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1655 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1656 return MHD_NO; /* Unsupported method, close connection */ 1657 1658 /* This kind of operation does not guarantee that numbers are not reused 1659 in parallel threads, when processed simultaneously, but this should not 1660 be a big problem, as it just slow down replies a bit due to 1661 response locking. */ 1662 resp_index = (last_index++) % num_resps; 1663 return MHD_queue_response (connection, MHD_HTTP_OK, resps[resp_index]); 1664 } 1665 1666 1667 static enum MHD_Result 1668 answer_single_response (void *cls, 1669 struct MHD_Connection *connection, 1670 const char *url, 1671 const char *method, 1672 const char *version, 1673 const char *upload_data, 1674 size_t *upload_data_size, 1675 void **req_cls) 1676 { 1677 static int marker = 0; 1678 (void) cls; /* Unused */ 1679 (void) url; (void) version; /* Unused */ 1680 (void) upload_data; (void) upload_data_size; /* Unused */ 1681 1682 if (NULL == *req_cls) 1683 { 1684 /* The first call */ 1685 *req_cls = (void *) ▮ 1686 /* Do not send reply yet. No error. */ 1687 return MHD_YES; 1688 } 1689 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1690 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1691 return MHD_NO; /* Unsupported method, close connection */ 1692 1693 return MHD_queue_response (connection, MHD_HTTP_OK, resp_single); 1694 } 1695 1696 1697 static enum MHD_Result 1698 answer_unique_empty_response (void *cls, 1699 struct MHD_Connection *connection, 1700 const char *url, 1701 const char *method, 1702 const char *version, 1703 const char *upload_data, 1704 size_t *upload_data_size, 1705 void **req_cls) 1706 { 1707 static int marker = 0; 1708 struct MHD_Response *r; 1709 enum MHD_Result ret; 1710 (void) cls; /* Unused */ 1711 (void) url; (void) version; /* Unused */ 1712 (void) upload_data; (void) upload_data_size; /* Unused */ 1713 1714 if (NULL == *req_cls) 1715 { 1716 /* The first call */ 1717 *req_cls = (void *) ▮ 1718 /* Do not send reply yet. No error. */ 1719 return MHD_YES; 1720 } 1721 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1722 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1723 return MHD_NO; /* Unsupported method, close connection */ 1724 1725 #if MHD_VERSION >= 0x00097701 1726 r = MHD_create_response_empty (MHD_RF_NONE); 1727 #else /* MHD_VERSION < 0x00097701 */ 1728 r = MHD_create_response_from_buffer (0, 1729 NULL, 1730 MHD_RESPMEM_PERSISTENT); 1731 #endif /* MHD_VERSION < 0x00097701 */ 1732 ret = MHD_queue_response (connection, MHD_HTTP_OK, r); 1733 MHD_destroy_response (r); 1734 return ret; 1735 } 1736 1737 1738 static enum MHD_Result 1739 answer_unique_tiny_response (void *cls, 1740 struct MHD_Connection *connection, 1741 const char *url, 1742 const char *method, 1743 const char *version, 1744 const char *upload_data, 1745 size_t *upload_data_size, 1746 void **req_cls) 1747 { 1748 static int marker = 0; 1749 struct MHD_Response *r; 1750 enum MHD_Result ret; 1751 (void) cls; /* Unused */ 1752 (void) url; (void) version; /* Unused */ 1753 (void) upload_data; (void) upload_data_size; /* Unused */ 1754 1755 if (NULL == *req_cls) 1756 { 1757 /* The first call */ 1758 *req_cls = (void *) ▮ 1759 /* Do not send reply yet. No error. */ 1760 return MHD_YES; 1761 } 1762 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1763 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1764 return MHD_NO; /* Unsupported method, close connection */ 1765 1766 #if MHD_VERSION >= 0x00097701 1767 r = MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body), 1768 tiny_body); 1769 #else /* MHD_VERSION < 0x00097701 */ 1770 r = MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body), 1771 (void *) tiny_body, 1772 MHD_RESPMEM_PERSISTENT); 1773 #endif /* MHD_VERSION < 0x00097701 */ 1774 ret = MHD_queue_response (connection, MHD_HTTP_OK, r); 1775 MHD_destroy_response (r); 1776 return ret; 1777 } 1778 1779 1780 static enum MHD_Result 1781 answer_unique_dyn_response (void *cls, 1782 struct MHD_Connection *connection, 1783 const char *url, 1784 const char *method, 1785 const char *version, 1786 const char *upload_data, 1787 size_t *upload_data_size, 1788 void **req_cls) 1789 { 1790 static int marker = 0; 1791 struct MHD_Response *r; 1792 enum MHD_Result ret; 1793 (void) cls; /* Unused */ 1794 (void) url; (void) version; /* Unused */ 1795 (void) upload_data; (void) upload_data_size; /* Unused */ 1796 1797 if (NULL == *req_cls) 1798 { 1799 /* The first call */ 1800 *req_cls = (void *) ▮ 1801 /* Do not send reply yet. No error. */ 1802 return MHD_YES; 1803 } 1804 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1805 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1806 return MHD_NO; /* Unsupported method, close connection */ 1807 1808 #if MHD_VERSION >= 0x00097701 1809 r = MHD_create_response_from_buffer_static (body_dyn_size, 1810 body_dyn); 1811 #else /* MHD_VERSION < 0x00097701 */ 1812 r = MHD_create_response_from_buffer (body_dyn_size, 1813 (void *) body_dyn, 1814 MHD_RESPMEM_PERSISTENT); 1815 #endif /* MHD_VERSION < 0x00097701 */ 1816 ret = MHD_queue_response (connection, MHD_HTTP_OK, r); 1817 MHD_destroy_response (r); 1818 return ret; 1819 } 1820 1821 1822 static void 1823 print_perf_warnings (void) 1824 { 1825 int newline_needed = 0; 1826 #if defined (_DEBUG) 1827 fprintf (stderr, "WARNING: Running with debug asserts enabled, " 1828 "the performance is suboptimal.\n"); 1829 newline_needed |= ! 0; 1830 #endif /* _DEBUG */ 1831 #if defined(__GNUC__) && ! defined (__OPTIMIZE__) 1832 fprintf (stderr, "WARNING: This tool is compiled without enabled compiler " 1833 "optimisations, the performance is suboptimal.\n"); 1834 newline_needed |= ! 0; 1835 #endif /* __GNUC__ && ! __OPTIMIZE__ */ 1836 #if defined(__GNUC__) && defined (__OPTIMIZE_SIZE__) 1837 fprintf (stderr, "WARNING: This tool is compiled with size-optimisations, " 1838 "the performance is suboptimal.\n"); 1839 #endif /* __GNUC__ && ! __OPTIMIZE__ */ 1840 #if MHD_VERSION >= 0x00097701 1841 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_DEBUG_BUILD)) 1842 { 1843 fprintf (stderr, "WARNING: The libmicrohttpd is compiled with " 1844 "debug asserts enabled, the performance is suboptimal.\n"); 1845 newline_needed |= ! 0; 1846 } 1847 #endif /* MHD_VERSION >= 0x00097701 */ 1848 if (newline_needed) 1849 printf ("\n"); 1850 } 1851 1852 1853 /* Borrowed from daemon.c */ 1854 /* TODO: run-time detection */ 1855 /** 1856 * Default connection limit. 1857 */ 1858 #ifdef MHD_POSIX_SOCKETS 1859 #define MHD_MAX_CONNECTIONS_DEFAULT (FD_SETSIZE - 4) 1860 #else 1861 #define MHD_MAX_CONNECTIONS_DEFAULT (FD_SETSIZE - 2) 1862 #endif 1863 1864 static unsigned int 1865 get_mhd_conn_limit (struct MHD_Daemon *d) 1866 { 1867 /* TODO: implement run-time detection */ 1868 (void) d; /* Unused */ 1869 if (0 != tool_params.connections) 1870 return tool_params.connections; 1871 return (unsigned int) MHD_MAX_CONNECTIONS_DEFAULT; 1872 } 1873 1874 1875 static const char * 1876 get_mhd_response_size (void) 1877 { 1878 if (tool_params.empty) 1879 return "0 bytes (empty)"; 1880 else if (tool_params.tiny) 1881 return "3 bytes (tiny)"; 1882 else if (tool_params.medium) 1883 return "8 KiB (medium)"; 1884 else if (tool_params.large) 1885 return "1 MiB (large)"; 1886 else if (tool_params.xlarge) 1887 return "8 MiB (xlarge)"; 1888 else if (tool_params.jumbo) 1889 return "101 MiB (jumbo)"; 1890 return "!!internal error!!"; 1891 } 1892 1893 1894 static int 1895 run_mhd (void) 1896 { 1897 MHD_AccessHandlerCallback reply_func; 1898 struct MHD_Daemon *d; 1899 unsigned int use_num_threads; 1900 unsigned int flags = MHD_NO_FLAG; 1901 struct MHD_OptionItem opt_arr[16]; 1902 size_t opt_count = 0; 1903 const union MHD_DaemonInfo *d_info; 1904 const char *poll_mode; 1905 uint16_t port; 1906 1907 if (tool_params.thread_per_conn) 1908 use_num_threads = 0; 1909 else 1910 use_num_threads = get_num_threads (); 1911 printf ("\n"); 1912 1913 print_perf_warnings (); 1914 1915 printf ("Responses:\n"); 1916 printf (" Sharing: "); 1917 if (tool_params.shared) 1918 { 1919 reply_func = &answer_shared_response; 1920 printf ("pre-generated shared pool with %u objects\n", num_resps); 1921 } 1922 else if (tool_params.single) 1923 { 1924 reply_func = &answer_single_response; 1925 printf ("single pre-generated reused response object\n"); 1926 } 1927 else 1928 { 1929 /* Unique responses */ 1930 if (tool_params.empty) 1931 reply_func = &answer_unique_empty_response; 1932 else if (tool_params.tiny) 1933 reply_func = &answer_unique_tiny_response; 1934 else 1935 reply_func = &answer_unique_dyn_response; 1936 printf ("one-time response object generated for every request\n"); 1937 } 1938 printf (" Body size: %s\n", 1939 get_mhd_response_size ()); 1940 1941 flags |= MHD_USE_ERROR_LOG; 1942 flags |= MHD_USE_INTERNAL_POLLING_THREAD; 1943 if (tool_params.epoll) 1944 flags |= MHD_USE_EPOLL; 1945 else if (tool_params.poll) 1946 flags |= MHD_USE_POLL; 1947 else if (tool_params.select) 1948 (void) flags; /* No special additional flag */ 1949 else 1950 flags |= MHD_USE_AUTO; 1951 1952 if (tool_params.thread_per_conn) 1953 flags |= MHD_USE_THREAD_PER_CONNECTION; 1954 1955 if (! tool_params.date_header) 1956 flags |= MHD_USE_SUPPRESS_DATE_NO_CLOCK; 1957 1958 if (0 != tool_params.connections) 1959 { 1960 opt_arr[opt_count].option = MHD_OPTION_CONNECTION_LIMIT; 1961 opt_arr[opt_count].value = (intptr_t) tool_params.connections; 1962 opt_arr[opt_count].ptr_value = NULL; 1963 ++opt_count; 1964 } 1965 if (1 < use_num_threads) 1966 { 1967 opt_arr[opt_count].option = MHD_OPTION_THREAD_POOL_SIZE; 1968 opt_arr[opt_count].value = (intptr_t) use_num_threads; 1969 opt_arr[opt_count].ptr_value = NULL; 1970 ++opt_count; 1971 } 1972 if (1) 1973 { 1974 opt_arr[opt_count].option = MHD_OPTION_CONNECTION_TIMEOUT; 1975 opt_arr[opt_count].value = (intptr_t) tool_params.timeout; 1976 opt_arr[opt_count].ptr_value = NULL; 1977 ++opt_count; 1978 } 1979 if (1) 1980 { 1981 struct MHD_OptionItem option = 1982 { MHD_OPTION_END, 0, NULL }; 1983 opt_arr[opt_count] = option; 1984 if (opt_count >= (sizeof(opt_arr) / sizeof(opt_arr[0]))) 1985 abort (); 1986 } 1987 d = MHD_start_daemon (flags, mhd_port, NULL, NULL, reply_func, NULL, 1988 MHD_OPTION_ARRAY, opt_arr, MHD_OPTION_END); 1989 if (NULL == d) 1990 { 1991 fprintf (stderr, "Error starting MHD daemon.\n"); 1992 return 15; 1993 } 1994 d_info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS); 1995 if (NULL == d_info) 1996 abort (); 1997 flags = (unsigned int) d_info->flags; 1998 if (0 != (flags & MHD_USE_POLL)) 1999 poll_mode = "poll()"; 2000 else if (0 != (flags & MHD_USE_EPOLL)) 2001 poll_mode = "epoll"; 2002 else 2003 poll_mode = "select()"; 2004 d_info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 2005 if (NULL == d_info) 2006 abort (); 2007 port = d_info->port; 2008 if (0 == port) 2009 fprintf (stderr, "Cannot detect port number. Consider specifying " 2010 "port number explicitly.\n"); 2011 2012 printf ("MHD is running.\n"); 2013 printf (" Bind port: %u\n", (unsigned int) port); 2014 printf (" Polling function: %s\n", poll_mode); 2015 printf (" Threading: "); 2016 if (MHD_USE_THREAD_PER_CONNECTION == (flags & MHD_USE_THREAD_PER_CONNECTION)) 2017 printf ("thread per connection\n"); 2018 else if (1 == get_num_threads ()) 2019 printf ("one MHD thread\n"); 2020 else 2021 printf ("%u MHD threads in thread pool\n", get_num_threads ()); 2022 printf (" Connections limit: %u\n", get_mhd_conn_limit (d)); 2023 printf (" Connection timeout: %u%s\n", tool_params.timeout, 2024 0 == tool_params.timeout ? " (no timeout)" : ""); 2025 printf (" 'Date:' header: %s\n", 2026 tool_params.date_header ? "Yes" : "No"); 2027 printf ("To test with remote client use " 2028 "http://HOST_IP:%u/\n", (unsigned int) port); 2029 printf ("To test with client on the same host use " 2030 "http://127.0.0.1:%u/\n", (unsigned int) port); 2031 printf ("\nPress ENTER to stop.\n"); 2032 if (1) 2033 { 2034 char buf[10]; 2035 (void) fgets (buf, sizeof(buf), stdin); 2036 } 2037 MHD_stop_daemon (d); 2038 return 0; 2039 } 2040 2041 2042 int 2043 main (int argc, char *const *argv) 2044 { 2045 int ret; 2046 set_self_name (argc, argv); 2047 ret = process_params (argc, argv); 2048 if (0 != ret) 2049 return ret; 2050 ret = check_apply_params (); 2051 if (0 > ret) 2052 return 0; 2053 if (0 != ret) 2054 return ret; 2055 ret = init_data (); 2056 if (0 != ret) 2057 return ret; 2058 ret = run_mhd (); 2059 deinit_data (); 2060 return ret; 2061 }