libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

perf_replies.c (61785B)


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