libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

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                                &param_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                                &param_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                                &param_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 }