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