GNU libmicrohttpd 1.0.9
Loading...
Searching...
No Matches
mhd_opt_matrix.c
Go to the documentation of this file.
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
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
54static 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,
59 { "mem-64", 64, 0, 0, MHD_OPT_MATRIX_THR_INTERNAL,
61 { "mem-128", 128, 0, 0, MHD_OPT_MATRIX_THR_INTERNAL,
63 { "mem-256", 256, 0, 0, MHD_OPT_MATRIX_THR_PER_CONNECTION,
65 { "mem-512", 512, 0, 0, MHD_OPT_MATRIX_THR_PER_CONNECTION,
67 { "mem-1024", 1024, 0, 0, MHD_OPT_MATRIX_THR_INTERNAL,
69 { "mem-2048", 2048, 0, 0, MHD_OPT_MATRIX_THR_POOL,
71 { "mem-3072", 3072, 0, 0, MHD_OPT_MATRIX_THR_EXTERNAL,
73 { "mem-4096", 4096, 0, 0, MHD_OPT_MATRIX_THR_POOL,
75 { "strict-1", 0, 1, 0, MHD_OPT_MATRIX_THR_INTERNAL,
77 { "strict-2", 1024, 2, 0, MHD_OPT_MATRIX_THR_INTERNAL,
79 { "strict-3", 512, 3, 0, MHD_OPT_MATRIX_THR_INTERNAL,
81 { "lax-1", 512, -1, 0, MHD_OPT_MATRIX_THR_INTERNAL,
83 { "lax-2", 256, -2, 0, MHD_OPT_MATRIX_THR_PER_CONNECTION,
85 { "lax-3", 2048, -3, 0, MHD_OPT_MATRIX_THR_POOL,
87 { "legacy-strict", 768, 1, 1, MHD_OPT_MATRIX_THR_INTERNAL,
89 { "legacy-lax", 768, -1, 1, MHD_OPT_MATRIX_THR_EXTERNAL,
91};
92
96#define NUM_PROFILES \
97 ((unsigned int) (sizeof(opt_profiles) / sizeof(opt_profiles[0])))
98
99
103static int env_parsed;
104
108static int env_selected;
109
114
118static char env_profile_name[64];
119
120
121unsigned int
123{
124 return NUM_PROFILES;
125}
126
127
128const struct MHD_OptMatrixProfile *
129mhd_opt_matrix_profile (unsigned int idx)
130{
131 return opt_profiles + (idx % NUM_PROFILES);
132}
133
134
135const struct MHD_OptMatrixProfile *
136mhd_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
167static int
168parse_threading (const char *s, enum MHD_OptMatrixThreading *out)
169{
170 if (0 == strcmp (s, "external"))
172 else if (0 == strcmp (s, "internal"))
174 else if ((0 == strcmp (s, "per-connection")) ||
175 (0 == strcmp (s, "tpc")))
177 else if (0 == strcmp (s, "pool"))
179 else
180 return 0;
181 return ! 0;
182}
183
184
192static int
193parse_poll (const char *s, enum MHD_OptMatrixPoll *out)
194{
195 if (0 == strcmp (s, "select"))
197 else if (0 == strcmp (s, "poll"))
199 else if (0 == strcmp (s, "epoll"))
201 else
202 return 0;
203 return ! 0;
204}
205
206
214_MHD_NORETURN static void
215env_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
226const struct MHD_OptMatrixProfile *
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)
257 env_profile = *base;
258 }
259 else
261 strncpy (env_profile_name, env_profile.name, sizeof(env_profile_name) - 1);
262 env_profile_name[sizeof(env_profile_name) - 1] = 0;
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"))
271 else
272 {
273 char *end;
274 unsigned long v;
275
276 v = strtoul (mem_env, &end, 10);
277 if ((NULL == end) || (0 != *end))
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))
290 env_profile.discipline_lvl = (int) v;
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))
301 env_profile.discipline_lvl = (int) v;
303 }
304 if (NULL != thr_env)
305 {
306 if (! parse_threading (thr_env, &env_profile.threading))
311 }
312 if (NULL != poll_env)
313 {
314 if (! parse_poll (poll_env, &env_profile.poll_backend))
316 }
317 env_selected = ! 0;
318 return &env_profile;
319}
320
321
322int
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
338int
340{
341 if (NULL == prof)
342 return ! 0;
343 if ((MHD_OPT_MATRIX_THR_EXTERNAL != prof->threading) &&
345 return 0;
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) &&
353 return 0;
355 {
357 return 0;
358 /* MHD refuses to combine 'epoll' with a thread per connection. */
360 return 0;
361 }
362 return ! 0;
363}
364
365
366const char *
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);
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,
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
413unsigned int
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 {
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 ?
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 {
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
457unsigned int
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 {
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;
475 break;
478 default:
480 break;
481 }
482 switch (prof->poll_backend)
483 {
485 flags |= MHD_USE_POLL;
486 break;
488 flags |= MHD_USE_EPOLL;
489 break;
491 default:
492 break;
493 }
494 return flags;
495}
496
497
498int
500{
501 if (NULL == prof)
502 return 0;
503 return (MHD_OPT_MATRIX_THR_EXTERNAL == prof->threading) ? ! 0 : 0;
504}
505
506
507int
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
520void
521mhd_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}
_MHD_EXTERN enum MHD_Result MHD_is_feature_supported(enum MHD_FEATURE feature)
Definition daemon.c:9722
#define NULL
static struct MHD_OptMatrixProfile env_profile
#define NUM_PROFILES
unsigned int mhd_opt_matrix_num_profiles(void)
const struct MHD_OptMatrixProfile * mhd_opt_matrix_lookup(const char *name_or_idx)
static int env_selected
const struct MHD_OptMatrixProfile * mhd_opt_matrix_profile(unsigned int idx)
const struct MHD_OptMatrixProfile * mhd_opt_matrix_from_env(void)
static int env_parsed
int mhd_opt_matrix_profile_supported(const struct MHD_OptMatrixProfile *prof)
unsigned int mhd_opt_matrix_fill_options(const struct MHD_OptMatrixProfile *prof, struct MHD_OptionItem *ops, unsigned int max_ops)
int mhd_opt_matrix_effective_discipline(const struct MHD_OptMatrixProfile *prof)
static _MHD_NORETURN void env_error(const char *name, const char *value)
static int parse_threading(const char *s, enum MHD_OptMatrixThreading *out)
const char * mhd_opt_matrix_describe(const struct MHD_OptMatrixProfile *prof, char *buf, size_t buf_size)
static const struct MHD_OptMatrixProfile opt_profiles[]
void mhd_opt_matrix_print_notice(const char *test_name)
int mhd_opt_matrix_raise_mem_limit(struct MHD_OptMatrixProfile *prof, size_t min_limit)
static int parse_poll(const char *s, enum MHD_OptMatrixPoll *out)
int mhd_opt_matrix_is_external(const struct MHD_OptMatrixProfile *prof)
unsigned int mhd_opt_matrix_daemon_flags(const struct MHD_OptMatrixProfile *prof, unsigned int base_flags, int allow_external)
static char env_profile_name[64]
The environment-driven daemon option matrix shared by the tests.
MHD_OptMatrixThreading
@ MHD_OPT_MATRIX_THR_EXTERNAL
@ MHD_OPT_MATRIX_THR_POOL
@ MHD_OPT_MATRIX_THR_INTERNAL
@ MHD_OPT_MATRIX_THR_PER_CONNECTION
#define MHD_OPT_MATRIX_THREADING_ENV
#define MHD_OPT_MATRIX_STRICT_ENV
#define MHD_OPT_MATRIX_DISCP_ENV
#define MHD_OPT_MATRIX_MEM_ENV
#define MHD_OPT_MATRIX_PROFILE_ENV
MHD_OptMatrixPoll
@ MHD_OPT_MATRIX_POLL_POLL
@ MHD_OPT_MATRIX_POLL_EPOLL
@ MHD_OPT_MATRIX_POLL_SELECT
#define MHD_OPT_MATRIX_POLL_ENV
public interface to libmicrohttpd
@ MHD_FEATURE_THREADS
@ MHD_FEATURE_POLL
@ MHD_FEATURE_EPOLL
@ MHD_OPTION_CLIENT_DISCIPLINE_LVL
@ MHD_OPTION_THREAD_POOL_SIZE
@ MHD_OPTION_CONNECTION_MEMORY_LIMIT
@ MHD_OPTION_STRICT_FOR_CLIENT
@ MHD_OPTION_END
@ MHD_YES
Definition microhttpd.h:172
@ MHD_USE_EPOLL
@ MHD_USE_THREAD_PER_CONNECTION
@ MHD_USE_POLL
@ MHD_USE_INTERNAL_POLLING_THREAD
platform-specific includes for libmicrohttpd
enum MHD_OptMatrixThreading threading
enum MHD_OptMatrixPoll poll_backend
unsigned int thread_pool_size
enum MHD_OPTION option