libmicrohttpd

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

mhd_opt_matrix.c (15322B)


      1 /*
      2      This file is part of GNU libmicrohttpd
      3      Copyright (C) 2026 Christian Grothoff
      4 
      5      GNU libmicrohttpd is free software; you can redistribute it and/or
      6      modify it under the terms of the GNU Lesser General Public
      7      License as published by the Free Software Foundation; either
      8      version 2.1 of the License, or (at your option) any later version.
      9 
     10      This library is distributed in the hope that it will be useful,
     11      but WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      Lesser General Public License for more details.
     14 
     15      You should have received a copy of the GNU Lesser General Public
     16      License along with GNU libmicrohttpd.
     17      If not, see <http://www.gnu.org/licenses/>.
     18 */
     19 
     20 /**
     21  * @file microhttpd/mhd_opt_matrix.c
     22  * @brief  The environment-driven daemon option matrix shared by the tests
     23  * @author Christian Grothoff
     24  */
     25 
     26 #include "platform.h"
     27 #include <microhttpd.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <stdio.h>
     31 
     32 #include "mhd_opt_matrix.h"
     33 
     34 
     35 /**
     36  * The built-in matrix of daemon option profiles.
     37  *
     38  * Every value of every dimension listed in the header appears at least once:
     39  *
     40  * * memory limit: 0 (default), 64, 128, 256, 512, 1024, 2048, 3072, 4096;
     41  * * client discipline level: -3, -2, -1, 0, 1, 2, 3;
     42  * * #MHD_OPTION_STRICT_FOR_CLIENT with a positive and with a negative value;
     43  * * threading: external polling, one internal thread, thread-per-connection,
     44  *   thread pool;
     45  * * polling backend: select(), poll(), epoll.
     46  *
     47  * Profile zero is "neutral": it is exactly the configuration the tests of
     48  * this directory used before the matrix was introduced.
     49  *
     50  * The combination "thread-per-connection + epoll" is deliberately absent: MHD
     51  * rejects it (daemon.c:8680).  External polling is always paired with the
     52  * select()-style interface, as that is the only one the tests can drive.
     53  */
     54 static const struct MHD_OptMatrixProfile opt_profiles[] = {
     55   /* name            mem  discp legacy threading
     56      poll                          pool */
     57   { "default",         0,   0, 0, MHD_OPT_MATRIX_THR_INTERNAL,
     58     MHD_OPT_MATRIX_POLL_SELECT, 0 },
     59   { "mem-64",         64,   0, 0, MHD_OPT_MATRIX_THR_INTERNAL,
     60     MHD_OPT_MATRIX_POLL_SELECT, 0 },
     61   { "mem-128",       128,   0, 0, MHD_OPT_MATRIX_THR_INTERNAL,
     62     MHD_OPT_MATRIX_POLL_POLL, 0 },
     63   { "mem-256",       256,   0, 0, MHD_OPT_MATRIX_THR_PER_CONNECTION,
     64     MHD_OPT_MATRIX_POLL_SELECT, 0 },
     65   { "mem-512",       512,   0, 0, MHD_OPT_MATRIX_THR_PER_CONNECTION,
     66     MHD_OPT_MATRIX_POLL_POLL, 0 },
     67   { "mem-1024",     1024,   0, 0, MHD_OPT_MATRIX_THR_INTERNAL,
     68     MHD_OPT_MATRIX_POLL_EPOLL, 0 },
     69   { "mem-2048",     2048,   0, 0, MHD_OPT_MATRIX_THR_POOL,
     70     MHD_OPT_MATRIX_POLL_SELECT, 4 },
     71   { "mem-3072",     3072,   0, 0, MHD_OPT_MATRIX_THR_EXTERNAL,
     72     MHD_OPT_MATRIX_POLL_SELECT, 0 },
     73   { "mem-4096",     4096,   0, 0, MHD_OPT_MATRIX_THR_POOL,
     74     MHD_OPT_MATRIX_POLL_EPOLL, 3 },
     75   { "strict-1",        0,   1, 0, MHD_OPT_MATRIX_THR_INTERNAL,
     76     MHD_OPT_MATRIX_POLL_SELECT, 0 },
     77   { "strict-2",     1024,   2, 0, MHD_OPT_MATRIX_THR_INTERNAL,
     78     MHD_OPT_MATRIX_POLL_POLL, 0 },
     79   { "strict-3",      512,   3, 0, MHD_OPT_MATRIX_THR_INTERNAL,
     80     MHD_OPT_MATRIX_POLL_EPOLL, 0 },
     81   { "lax-1",         512,  -1, 0, MHD_OPT_MATRIX_THR_INTERNAL,
     82     MHD_OPT_MATRIX_POLL_SELECT, 0 },
     83   { "lax-2",         256,  -2, 0, MHD_OPT_MATRIX_THR_PER_CONNECTION,
     84     MHD_OPT_MATRIX_POLL_SELECT, 0 },
     85   { "lax-3",        2048,  -3, 0, MHD_OPT_MATRIX_THR_POOL,
     86     MHD_OPT_MATRIX_POLL_SELECT, 4 },
     87   { "legacy-strict", 768,   1, 1, MHD_OPT_MATRIX_THR_INTERNAL,
     88     MHD_OPT_MATRIX_POLL_SELECT, 0 },
     89   { "legacy-lax",    768,  -1, 1, MHD_OPT_MATRIX_THR_EXTERNAL,
     90     MHD_OPT_MATRIX_POLL_SELECT, 0 }
     91 };
     92 
     93 /**
     94  * The number of built-in profiles.
     95  */
     96 #define NUM_PROFILES \
     97         ((unsigned int) (sizeof(opt_profiles) / sizeof(opt_profiles[0])))
     98 
     99 
    100 /**
    101  * Non-zero once the environment has been read into @a env_profile.
    102  */
    103 static int env_parsed;
    104 
    105 /**
    106  * Non-zero if the environment selects a profile at all.
    107  */
    108 static int env_selected;
    109 
    110 /**
    111  * The profile derived from the environment.
    112  */
    113 static struct MHD_OptMatrixProfile env_profile;
    114 
    115 /**
    116  * The name of @a env_profile.
    117  */
    118 static char env_profile_name[64];
    119 
    120 
    121 unsigned int
    122 mhd_opt_matrix_num_profiles (void)
    123 {
    124   return NUM_PROFILES;
    125 }
    126 
    127 
    128 const struct MHD_OptMatrixProfile *
    129 mhd_opt_matrix_profile (unsigned int idx)
    130 {
    131   return opt_profiles + (idx % NUM_PROFILES);
    132 }
    133 
    134 
    135 const struct MHD_OptMatrixProfile *
    136 mhd_opt_matrix_lookup (const char *name_or_idx)
    137 {
    138   unsigned int i;
    139 
    140   if ((NULL == name_or_idx) || (0 == name_or_idx[0]))
    141     return NULL;
    142   for (i = 0; i < NUM_PROFILES; ++i)
    143   {
    144     if (0 == strcmp (name_or_idx, opt_profiles[i].name))
    145       return opt_profiles + i;
    146   }
    147   if (('0' <= name_or_idx[0]) && ('9' >= name_or_idx[0]))
    148   {
    149     char *end;
    150     unsigned long v;
    151 
    152     v = strtoul (name_or_idx, &end, 10);
    153     if ((NULL != end) && (0 == *end) && (NUM_PROFILES > v))
    154       return opt_profiles + v;
    155   }
    156   return NULL;
    157 }
    158 
    159 
    160 /**
    161  * Parse the threading mode name.
    162  *
    163  * @param s the name
    164  * @param[out] out the parsed mode
    165  * @return non-zero on success
    166  */
    167 static int
    168 parse_threading (const char *s, enum MHD_OptMatrixThreading *out)
    169 {
    170   if (0 == strcmp (s, "external"))
    171     *out = MHD_OPT_MATRIX_THR_EXTERNAL;
    172   else if (0 == strcmp (s, "internal"))
    173     *out = MHD_OPT_MATRIX_THR_INTERNAL;
    174   else if ((0 == strcmp (s, "per-connection")) ||
    175            (0 == strcmp (s, "tpc")))
    176     *out = MHD_OPT_MATRIX_THR_PER_CONNECTION;
    177   else if (0 == strcmp (s, "pool"))
    178     *out = MHD_OPT_MATRIX_THR_POOL;
    179   else
    180     return 0;
    181   return ! 0;
    182 }
    183 
    184 
    185 /**
    186  * Parse the polling backend name.
    187  *
    188  * @param s the name
    189  * @param[out] out the parsed backend
    190  * @return non-zero on success
    191  */
    192 static int
    193 parse_poll (const char *s, enum MHD_OptMatrixPoll *out)
    194 {
    195   if (0 == strcmp (s, "select"))
    196     *out = MHD_OPT_MATRIX_POLL_SELECT;
    197   else if (0 == strcmp (s, "poll"))
    198     *out = MHD_OPT_MATRIX_POLL_POLL;
    199   else if (0 == strcmp (s, "epoll"))
    200     *out = MHD_OPT_MATRIX_POLL_EPOLL;
    201   else
    202     return 0;
    203   return ! 0;
    204 }
    205 
    206 
    207 /**
    208  * Report a broken environment variable and terminate: a typo in a driver
    209  * script must not silently produce a run with the default configuration.
    210  *
    211  * @param name the name of the variable
    212  * @param value the offending value
    213  */
    214 _MHD_NORETURN static void
    215 env_error (const char *name, const char *value)
    216 {
    217   fprintf (stderr,
    218            "Invalid value '%s' for the environment variable %s.\n",
    219            (NULL != value) ? value : "(null)",
    220            name);
    221   fflush (stderr);
    222   exit (99);
    223 }
    224 
    225 
    226 const struct MHD_OptMatrixProfile *
    227 mhd_opt_matrix_from_env (void)
    228 {
    229   const char *prof_env;
    230   const char *mem_env;
    231   const char *discp_env;
    232   const char *strict_env;
    233   const char *thr_env;
    234   const char *poll_env;
    235 
    236   if (0 != env_parsed)
    237     return env_selected ? &env_profile : NULL;
    238   env_parsed = ! 0;
    239 
    240   prof_env = getenv (MHD_OPT_MATRIX_PROFILE_ENV);
    241   mem_env = getenv (MHD_OPT_MATRIX_MEM_ENV);
    242   discp_env = getenv (MHD_OPT_MATRIX_DISCP_ENV);
    243   strict_env = getenv (MHD_OPT_MATRIX_STRICT_ENV);
    244   thr_env = getenv (MHD_OPT_MATRIX_THREADING_ENV);
    245   poll_env = getenv (MHD_OPT_MATRIX_POLL_ENV);
    246   if ((NULL == prof_env) && (NULL == mem_env) && (NULL == discp_env) &&
    247       (NULL == strict_env) && (NULL == thr_env) && (NULL == poll_env))
    248     return NULL; /* Nothing selected: keep the previous behaviour */
    249 
    250   if (NULL != prof_env)
    251   {
    252     const struct MHD_OptMatrixProfile *base;
    253 
    254     base = mhd_opt_matrix_lookup (prof_env);
    255     if (NULL == base)
    256       env_error (MHD_OPT_MATRIX_PROFILE_ENV, prof_env);
    257     env_profile = *base;
    258   }
    259   else
    260     env_profile = opt_profiles[0];
    261   strncpy (env_profile_name, env_profile.name, sizeof(env_profile_name) - 1);
    262   env_profile_name[sizeof(env_profile_name) - 1] = 0;
    263   env_profile.name = env_profile_name;
    264 
    265   /* The explicit per-dimension overrides below are NOT clamped: they are an
    266      explicit request by the caller, see the header. */
    267   if (NULL != mem_env)
    268   {
    269     if (0 == strcmp (mem_env, "default"))
    270       env_profile.mem_limit = 0;
    271     else
    272     {
    273       char *end;
    274       unsigned long v;
    275 
    276       v = strtoul (mem_env, &end, 10);
    277       if ((NULL == end) || (0 != *end))
    278         env_error (MHD_OPT_MATRIX_MEM_ENV, mem_env);
    279       env_profile.mem_limit = (size_t) v;
    280     }
    281   }
    282   if (NULL != discp_env)
    283   {
    284     char *end;
    285     long v;
    286 
    287     v = strtol (discp_env, &end, 10);
    288     if ((NULL == end) || (0 != *end) || (-3 > v) || (3 < v))
    289       env_error (MHD_OPT_MATRIX_DISCP_ENV, discp_env);
    290     env_profile.discipline_lvl = (int) v;
    291     env_profile.use_legacy_strict = 0;
    292   }
    293   if (NULL != strict_env)
    294   {
    295     char *end;
    296     long v;
    297 
    298     v = strtol (strict_env, &end, 10);
    299     if ((NULL == end) || (0 != *end))
    300       env_error (MHD_OPT_MATRIX_STRICT_ENV, strict_env);
    301     env_profile.discipline_lvl = (int) v;
    302     env_profile.use_legacy_strict = ! 0;
    303   }
    304   if (NULL != thr_env)
    305   {
    306     if (! parse_threading (thr_env, &env_profile.threading))
    307       env_error (MHD_OPT_MATRIX_THREADING_ENV, thr_env);
    308     if ((MHD_OPT_MATRIX_THR_POOL == env_profile.threading) &&
    309         (2 > env_profile.thread_pool_size))
    310       env_profile.thread_pool_size = 4;
    311   }
    312   if (NULL != poll_env)
    313   {
    314     if (! parse_poll (poll_env, &env_profile.poll_backend))
    315       env_error (MHD_OPT_MATRIX_POLL_ENV, poll_env);
    316   }
    317   env_selected = ! 0;
    318   return &env_profile;
    319 }
    320 
    321 
    322 int
    323 mhd_opt_matrix_effective_discipline (const struct MHD_OptMatrixProfile *prof)
    324 {
    325   if (NULL == prof)
    326     return 0;
    327   if (! prof->use_legacy_strict)
    328     return prof->discipline_lvl;
    329   /* Mirror the mapping of daemon.c:7102-7106 exactly. */
    330   if (-1 >= prof->discipline_lvl)
    331     return -3;
    332   if (1 <= prof->discipline_lvl)
    333     return 1;
    334   return 0;
    335 }
    336 
    337 
    338 int
    339 mhd_opt_matrix_profile_supported (const struct MHD_OptMatrixProfile *prof)
    340 {
    341   if (NULL == prof)
    342     return ! 0;
    343   if ((MHD_OPT_MATRIX_THR_EXTERNAL != prof->threading) &&
    344       (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_THREADS)))
    345     return 0;
    346   if (MHD_OPT_MATRIX_THR_EXTERNAL == prof->threading)
    347   {
    348     /* Only the select()-style external interface is driven by the tests. */
    349     return (MHD_OPT_MATRIX_POLL_SELECT == prof->poll_backend) ? ! 0 : 0;
    350   }
    351   if ((MHD_OPT_MATRIX_POLL_POLL == prof->poll_backend) &&
    352       (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_POLL)))
    353     return 0;
    354   if (MHD_OPT_MATRIX_POLL_EPOLL == prof->poll_backend)
    355   {
    356     if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_EPOLL))
    357       return 0;
    358     /* MHD refuses to combine 'epoll' with a thread per connection. */
    359     if (MHD_OPT_MATRIX_THR_PER_CONNECTION == prof->threading)
    360       return 0;
    361   }
    362   return ! 0;
    363 }
    364 
    365 
    366 const char *
    367 mhd_opt_matrix_describe (const struct MHD_OptMatrixProfile *prof,
    368                          char *buf,
    369                          size_t buf_size)
    370 {
    371   static const char *const thr_names[] = {
    372     "external polling", "internal thread", "thread-per-connection",
    373     "thread pool"
    374   };
    375   static const char *const poll_names[] = { "select()", "poll()", "epoll" };
    376   char mem[32];
    377   char pool[32];
    378   char discp[64];
    379 
    380   if (NULL == prof)
    381   {
    382     snprintf (buf, buf_size, "(built-in defaults of the test)");
    383     return buf;
    384   }
    385   if (0 == prof->mem_limit)
    386     snprintf (mem, sizeof(mem), "default");
    387   else
    388     snprintf (mem, sizeof(mem), "%lu", (unsigned long) prof->mem_limit);
    389   if (MHD_OPT_MATRIX_THR_POOL == prof->threading)
    390     snprintf (pool, sizeof(pool), " x%u", prof->thread_pool_size);
    391   else
    392     pool[0] = 0;
    393   /* Always show the level that is really in effect: the deprecated option
    394      silently maps its value, see mhd_opt_matrix_effective_discipline(). */
    395   if (prof->use_legacy_strict)
    396     snprintf (discp, sizeof(discp), "strict_for_client=%d -> discipline_lvl=%d",
    397               prof->discipline_lvl,
    398               mhd_opt_matrix_effective_discipline (prof));
    399   else
    400     snprintf (discp, sizeof(discp), "discipline_lvl=%d", prof->discipline_lvl);
    401   snprintf (buf, buf_size,
    402             "'%s' (mem_limit=%s, %s, %s%s, %s)",
    403             prof->name,
    404             mem,
    405             discp,
    406             thr_names[(unsigned int) prof->threading],
    407             pool,
    408             poll_names[(unsigned int) prof->poll_backend]);
    409   return buf;
    410 }
    411 
    412 
    413 unsigned int
    414 mhd_opt_matrix_fill_options (const struct MHD_OptMatrixProfile *prof,
    415                              struct MHD_OptionItem *ops,
    416                              unsigned int max_ops)
    417 {
    418   unsigned int n = 0;
    419 
    420   if (4 > max_ops)
    421     return 0;
    422   if (NULL != prof)
    423   {
    424     if (0 != prof->mem_limit)
    425     {
    426       ops[n].option = MHD_OPTION_CONNECTION_MEMORY_LIMIT;
    427       ops[n].value = (intptr_t) prof->mem_limit;
    428       ops[n].ptr_value = NULL;
    429       ++n;
    430     }
    431     if (0 != prof->discipline_lvl)
    432     {
    433       ops[n].option = prof->use_legacy_strict ?
    434                       MHD_OPTION_STRICT_FOR_CLIENT :
    435                       MHD_OPTION_CLIENT_DISCIPLINE_LVL;
    436       ops[n].value = (intptr_t) prof->discipline_lvl;
    437       ops[n].ptr_value = NULL;
    438       ++n;
    439     }
    440     if ((MHD_OPT_MATRIX_THR_POOL == prof->threading) &&
    441         (1 < prof->thread_pool_size))
    442     {
    443       ops[n].option = MHD_OPTION_THREAD_POOL_SIZE;
    444       ops[n].value = (intptr_t) prof->thread_pool_size;
    445       ops[n].ptr_value = NULL;
    446       ++n;
    447     }
    448   }
    449   ops[n].option = MHD_OPTION_END;
    450   ops[n].value = 0;
    451   ops[n].ptr_value = NULL;
    452   ++n;
    453   return n;
    454 }
    455 
    456 
    457 unsigned int
    458 mhd_opt_matrix_daemon_flags (const struct MHD_OptMatrixProfile *prof,
    459                              unsigned int base_flags,
    460                              int allow_external)
    461 {
    462   unsigned int flags = base_flags;
    463 
    464   if (NULL == prof)
    465     return flags;
    466   switch (prof->threading)
    467   {
    468   case MHD_OPT_MATRIX_THR_EXTERNAL:
    469     if (allow_external)
    470       return flags; /* No internal thread, no polling backend flag */
    471     /* The caller cannot drive MHD_run(); fall back to an internal thread. */
    472     return flags | MHD_USE_INTERNAL_POLLING_THREAD;
    473   case MHD_OPT_MATRIX_THR_PER_CONNECTION:
    474     flags |= MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION;
    475     break;
    476   case MHD_OPT_MATRIX_THR_INTERNAL:
    477   case MHD_OPT_MATRIX_THR_POOL:
    478   default:
    479     flags |= MHD_USE_INTERNAL_POLLING_THREAD;
    480     break;
    481   }
    482   switch (prof->poll_backend)
    483   {
    484   case MHD_OPT_MATRIX_POLL_POLL:
    485     flags |= MHD_USE_POLL;
    486     break;
    487   case MHD_OPT_MATRIX_POLL_EPOLL:
    488     flags |= MHD_USE_EPOLL;
    489     break;
    490   case MHD_OPT_MATRIX_POLL_SELECT:
    491   default:
    492     break;
    493   }
    494   return flags;
    495 }
    496 
    497 
    498 int
    499 mhd_opt_matrix_is_external (const struct MHD_OptMatrixProfile *prof)
    500 {
    501   if (NULL == prof)
    502     return 0;
    503   return (MHD_OPT_MATRIX_THR_EXTERNAL == prof->threading) ? ! 0 : 0;
    504 }
    505 
    506 
    507 int
    508 mhd_opt_matrix_raise_mem_limit (struct MHD_OptMatrixProfile *prof,
    509                                 size_t min_limit)
    510 {
    511   if ((NULL == prof) || (0 == min_limit) || (0 == prof->mem_limit))
    512     return 0;
    513   if (prof->mem_limit >= min_limit)
    514     return 0;
    515   prof->mem_limit = min_limit;
    516   return ! 0;
    517 }
    518 
    519 
    520 void
    521 mhd_opt_matrix_print_notice (const char *test_name)
    522 {
    523   const struct MHD_OptMatrixProfile *prof;
    524   const char *name = (NULL != test_name) ? test_name : "test";
    525   char desc[256];
    526 
    527   prof = mhd_opt_matrix_from_env ();
    528   printf ("%s: option matrix profile %s\n",
    529           name,
    530           mhd_opt_matrix_describe (prof, desc, sizeof(desc)));
    531   fflush (stdout);
    532 }