libtest_convenience.c (15893B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2024 Christian Grothoff 5 Copyright (C) 2025 Evgeny Grin (Karlson2k) 6 7 GNU libmicrohttpd is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License as published by the Free Software Foundation; either 10 version 2.1 of the License, or (at your option) any later version. 11 12 GNU libmicrohttpd is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 Alternatively, you can redistribute GNU libmicrohttpd and/or 18 modify it under the terms of the GNU General Public License as 19 published by the Free Software Foundation; either version 2 of 20 the License, or (at your option) any later version, together 21 with the eCos exception, as follows: 22 23 As a special exception, if other files instantiate templates or 24 use macros or inline functions from this file, or you compile this 25 file and link it with other works to produce a work based on this 26 file, this file does not by itself cause the resulting work to be 27 covered by the GNU General Public License. However the source code 28 for this file must still be made available in accordance with 29 section (3) of the GNU General Public License v2. 30 31 This exception does not invalidate any other reasons why a work 32 based on this file might be covered by the GNU General Public 33 License. 34 35 You should have received copies of the GNU Lesser General Public 36 License and the GNU General Public License along with this library; 37 if not, see <https://www.gnu.org/licenses/>. 38 */ 39 40 /** 41 * @file libtest_convenience.c 42 * @brief convenience functions for libtest users 43 * @author Christian Grothoff 44 */ 45 #include "libtest.h" 46 #include <pthread.h> 47 #include <fcntl.h> 48 #include <unistd.h> 49 #include <errno.h> 50 #ifdef MHD_SUPPORT_EPOLL 51 # include <sys/epoll.h> 52 #endif 53 #include <curl/curl.h> 54 55 56 /** 57 * Return the port to bind to. Usually zero (any), but can be 58 * forced to a particular port via environment variables. 59 */ 60 static uint16_t 61 get_port (void) 62 { 63 const char *portenv; 64 65 portenv = getenv ("MHD_TEST_FORCE_SERVER_PORT"); 66 if (NULL != portenv) 67 { 68 unsigned int i; 69 char dummy; 70 71 if ( (1 != sscanf (portenv, 72 "%u%c", 73 &i, 74 &dummy)) || 75 (i > 65535) ) 76 { 77 fprintf (stderr, 78 "Invalid port number specified in MHD_TEST_FORCE_PORT, using 0"); 79 return 0; 80 } 81 return (uint16_t) i; 82 } 83 return 0; 84 } 85 86 87 const char * 88 MHDT_server_setup_minimal (const void *cls, 89 struct MHD_Daemon *d) 90 { 91 const struct MHD_DaemonOptionAndValue *options 92 = (const struct MHD_DaemonOptionAndValue *) cls; 93 94 if (MHD_SC_OK != 95 MHD_daemon_set_options ( 96 d, 97 options, 98 MHD_OPTIONS_ARRAY_MAX_SIZE)) 99 return "Failed to configure threading mode!"; 100 if (MHD_SC_OK != 101 MHD_DAEMON_SET_OPTIONS ( 102 d, 103 MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (2000), 104 MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO, 105 get_port ()))) 106 return "Failed to bind to port!"; 107 return NULL; 108 } 109 110 111 /** 112 * Setup TLS at @a d for the given backend @a be. 113 * 114 * @return NULL on success, otherwise error message 115 */ 116 static const char * 117 server_setup_tls (struct MHD_Daemon *d, 118 enum MHD_TlsBackend be) 119 { 120 static const char *mem_cert = 121 "-----BEGIN CERTIFICATE-----\n\ 122 MIIDjTCCAnWgAwIBAgIUKkxAx2lVnvYcaNqBpJmTgXh1/VgwDQYJKoZIhvcNAQEL\n\ 123 BQAwVjELMAkGA1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxDzANBgNV\n\ 124 BAcMBkJvc3RvbjENMAsGA1UECgwEUm9vdDEPMA0GA1UEAwwGY2EuZ251MB4XDTI0\n\ 125 MTEyOTEyNDUyOFoXDTM0MTEyNzEyNDUyOFowVjELMAkGA1UEBhMCVVMxFjAUBgNV\n\ 126 BAgMDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcMBkJvc3RvbjENMAsGA1UECgwEUm9v\n\ 127 dDEPMA0GA1UEAwwGY2EuZ251MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n\ 128 AQEA23YSrcGIBgZf9bbzTnmYFy+4tM82kUhsVFKxWCNEMdKmhaeVvXogyd6Evq4P\n\ 129 NvBGdUABDtHp4pSEijrxWbn8sxddTznoT/8IOuHI0/PtwXYP/sHQ/HzekEUVKN2Z\n\ 130 NMbMUzQfaJyiIV5TrZlaBwHjQ+sRs8E56C3cQjkwuyjll2zDsEfmEnPimZRAL3kb\n\ 131 wW8VFfBcR2Id+a9xKjwlnB4eXQFAgYINoRgCtUOUxSeFgNnwkOUSqDknO6Xi47YZ\n\ 132 EdLlHyUnv5eX547xUkrYhfQuQwaqpGrjHf3GFoysN8P9kd2f1qsJKtQcUbF9DDeZ\n\ 133 6ya47X/LBO8QflMsVjb1V3oz9QIDAQABo1MwUTAdBgNVHQ4EFgQUsvdZoX3RxdN6\n\ 134 wrONr31SOA9Qbc4wHwYDVR0jBBgwFoAUsvdZoX3RxdN6wrONr31SOA9Qbc4wDwYD\n\ 135 VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAS3PyV7crGk9brqU90aML\n\ 136 2TWkjgzFb3/nASnpvVYiqyiV4neGiEjtDy7eVlqP6GlD2pYcVodY+ly9wNlo85/h\n\ 137 YfgCFFl37tMG7PpRac2qBqaSn1DpwsCb08LjRrOvoaRffWUikSoZmsYDlaCgl9nT\n\ 138 pGtIrz0BSoyu5mHalIZTVQOrbkNBNK6ZgnYy2iWuiLa5Z1xzKpsRBRaKJc1pcQE/\n\ 139 QVbPdCiyGQMPEVn/KHxitlycFoee/fA+izXVdstVwfig2DoMvrlGZvEkN1ER3Yz4\n\ 140 QPJ6HzOsBQL1F+YhnMCQfc2rpcwxAWf8JMy6jsCq42KGq53tkWqHyQ6Zu2SiLRYk\n\ 141 CA==\n\ 142 -----END CERTIFICATE-----"; 143 static const char *mem_key = 144 "-----BEGIN ENCRYPTED PRIVATE KEY-----\n\ 145 MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQJ1VSHi+akaaVYO3O\n\ 146 H7I0EAICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIZlNzQR1bh4IEggTI\n\ 147 8U86bfGmyAXXSi/R/l3G8ziZFyHrRE5Q/Q3uUW/jyUpe+S0gMRPqwW3V542ForbH\n\ 148 IH/Aa+KVxlwmsq0jlheCQewj9qZMQGuqa3iTl/OfCcuGMfsuQs2HsutoDMdEYuBI\n\ 149 6yOqNIrRvSHunZILLDpKz/AmCO6JnRiAwiSqPBixE5M+cm1qc7dy024REiW9l9K6\n\ 150 Hth9A0iYc94CUyUfHFj4CEkCNqk533Z2Ktkk3RQJnx5ORQG0iBJvoFiVODFKnoAk\n\ 151 Ge2HNrJH3bVvhQ+p8A/L4VmnWUCfcTyqgzo887WXRxORya6gcWWtrcEJGUbLh8sL\n\ 152 /mXFYj/0kEllIY+fHOmSx94I3GwBkQKER/CeOCIp+C392Pujgzrz23pdq20uIt3d\n\ 153 FCgbnIB+5IwOwQcqCkTYa1+Y5qCa6eFLgd8PXGTDyFwP4BHfG6WT/ctHQFi8vnXV\n\ 154 D1S726do1mA6CFE3DYmi45sf+Te2Xb346xk1GTSWtxGh9y4FblFDAWva4oTuvxPR\n\ 155 IDseBhXBsIqnOy1gb/5cGj0SIOQzqR1qlg4igv3UZFC8cVl+fNnngDBiX+nTYQVm\n\ 156 rDyxTzcX9txPSNpLyYRdNHwLGpzZAMoN46bUFnxt0cvRWN6MA7j1r0TYWBZKJ7b7\n\ 157 Yt/SuYsqSE0UJQEJz4QcQnlxu3qu4HJl7dOlto3fa42MWTkOcNr9XinHmKCZ9oYZ\n\ 158 PYNTggRGMXlqm66KmHWDqXqw9CeufprHq15SIJJR8v4SlvEZr+YlYQeHRI4E+FDA\n\ 159 mEFZy/U3ZL7ZHSDsEvpeBzIJkWxHobt57BIxYHE8KN0ZIz/mJZTxljacblFWnJRb\n\ 160 AUXTfrRZn3lGX+4WA6Biilwyxb71slCKaiz28C55Hnj1UwoUF8vNA3G2FGAX5Wk0\n\ 161 m3J2SoCHtJQYc/3lEC7zR9i3/F/7vgRxZMUWt/y6KRYq8ZnoQl3Eo2yvJYX/z7I6\n\ 162 JyqexAx3OvA+frN3rbO/o/k6w9333Smi0QxZzDM9tHn1BAgAtmyC1lizzKn7hDYK\n\ 163 o/eaPeatILbS0a/bHJBbP/R53keVr0hJ3MWK2nb/DV5Dl9j4Z6sHpo3P9L+Kq06y\n\ 164 G9q7NhBd7cxGq4AkCp+eSjqTvwgOX1PtAry00TUmzisLz8gIYutwJqbfZGL8WpR/\n\ 165 /wnLQXuM/tPLdQNy+PZeTQnPFwWQeZz4VgkMRhHV2xDw0mpzE+cdD204+YjHVdMH\n\ 166 D4MNrDlUmKM0OVoYgXd9YyLKzYVgW95GvY1X0SxTlIUuDiRv/SqRsurPFkSG457d\n\ 167 zmTUny1NRsnbv9bTXqt1Xewqsylyu02N1dZvjIzBnYMVYXl0r4aej1VNEXozQtWO\n\ 168 YRfWaZ29dXwZqUzd83ETQvhI4mZbwAlHbqm/CoyY6Vw4Am8hGa7II134lz2b3tkr\n\ 169 F1zBkvzzl6+HXewGOEjm+YorDMtfADiU/hkkykWq01NG3QSwk7jaKieb5Rlou53d\n\ 170 IXJQBw0KW5UrgbIFqMjpSZz1jdALBKsV+dw0wvCQ8BVXZm3zZpsV+0E4Z0sdj3TI\n\ 171 UbkFqQ6GpoxB25UUUlLZhBbtKy7dheuPBk0HowitYlo1kLVA/JiFB4qbdf5X/9Tm\n\ 172 XRkN+T0orEgy7rBQa7dJN9bdLj+dS5q8\n\ 173 -----END ENCRYPTED PRIVATE KEY-----"; 174 175 if (MHD_SC_OK != 176 MHD_DAEMON_SET_OPTIONS ( 177 d, 178 MHD_D_OPTION_TLS (be))) 179 return "Failed to enable TLS!"; 180 if (MHD_SC_OK != 181 MHD_DAEMON_SET_OPTIONS ( 182 d, 183 MHD_D_OPTION_TLS_CERT_KEY (mem_cert, 184 mem_key, 185 "masterword"))) 186 return "Failed to enable TLS!"; 187 return NULL; 188 } 189 190 191 const char * 192 MHDT_server_setup_tls (const void *cls, 193 struct MHD_Daemon *d) 194 { 195 const struct MHD_DaemonOptionAndValue *options 196 = (const struct MHD_DaemonOptionAndValue *) cls; 197 const char *err; 198 199 err = MHDT_server_setup_minimal (options, 200 d); 201 if (NULL != err) 202 return err; 203 err = server_setup_tls (d, 204 MHD_TLS_BACKEND_ANY); 205 if (NULL != err) 206 return err; 207 return NULL; 208 } 209 210 211 const char * 212 MHDT_server_setup_gnutls (const void *cls, 213 struct MHD_Daemon *d) 214 { 215 const struct MHD_DaemonOptionAndValue *options 216 = (const struct MHD_DaemonOptionAndValue *) cls; 217 const char *err; 218 219 err = MHDT_server_setup_minimal (options, 220 d); 221 if (NULL != err) 222 return err; 223 err = server_setup_tls (d, 224 MHD_TLS_BACKEND_GNUTLS); 225 if (NULL != err) 226 return err; 227 return NULL; 228 } 229 230 231 const char * 232 MHDT_server_setup_openssl (const void *cls, 233 struct MHD_Daemon *d) 234 { 235 const struct MHD_DaemonOptionAndValue *options 236 = (const struct MHD_DaemonOptionAndValue *) cls; 237 const char *err; 238 239 err = MHDT_server_setup_minimal (options, 240 d); 241 if (NULL != err) 242 return err; 243 err = server_setup_tls (d, 244 MHD_TLS_BACKEND_OPENSSL); 245 if (NULL != err) 246 return err; 247 return NULL; 248 } 249 250 251 void 252 MHDT_server_run_minimal (void *cls, 253 int finsig, 254 struct MHD_Daemon *d) 255 { 256 fd_set r; 257 char c; 258 259 (void) cls; /* Unused */ 260 (void) d; /* Unused */ 261 262 FD_ZERO (&r); 263 FD_SET (finsig, &r); 264 while (1) 265 { 266 if ( (-1 == 267 select (finsig + 1, 268 &r, 269 NULL, 270 NULL, 271 NULL)) && 272 (EAGAIN != errno) ) 273 { 274 fprintf (stderr, 275 "Failure waiting on termination signal: %s\n", 276 strerror (errno)); 277 break; 278 } 279 if (FD_ISSET (finsig, 280 &r)) 281 break; 282 } 283 if ( (FD_ISSET (finsig, 284 &r)) && 285 (1 != read (finsig, 286 &c, 287 1)) ) 288 { 289 fprintf (stderr, 290 "Failed to drain termination signal\n"); 291 } 292 } 293 294 295 void 296 MHDT_server_run_blocking (void *cls, 297 int finsig, 298 struct MHD_Daemon *d) 299 { 300 fd_set r; 301 char c; 302 303 (void) cls; /* Unused */ 304 (void) d; /* Unused */ 305 306 FD_ZERO (&r); 307 FD_SET (finsig, &r); 308 while (1) 309 { 310 struct timeval timeout = { 311 .tv_usec = 1000 /* 1000 microseconds */ 312 }; 313 314 if ( (-1 == 315 select (finsig + 1, 316 &r, 317 NULL, 318 NULL, 319 &timeout)) && 320 (EAGAIN != errno) ) 321 { 322 fprintf (stderr, 323 "Failure waiting on termination signal: %s\n", 324 strerror (errno)); 325 break; 326 } 327 #ifdef FIXME 328 if (MHD_SC_OK != 329 MHD_daemon_process_blocking (d, 330 1000)) 331 { 332 fprintf (stderr, 333 "Failure running MHD_daemon_process_blocking()\n"); 334 break; 335 } 336 #else 337 abort (); 338 #endif 339 } 340 if ( (FD_ISSET (finsig, 341 &r)) && 342 (1 != read (finsig, 343 &c, 344 1)) ) 345 { 346 fprintf (stderr, 347 "Failed to drain termination signal\n"); 348 } 349 } 350 351 352 #ifdef MHD_SUPPORT_EPOLL 353 static int my_epoll_fd = -1; 354 355 356 /** 357 * The callback for registration/de-registration of the sockets to watch. 358 * 359 * This callback must not call #MHD_daemon_destroy(), #MHD_daemon_quiesce(), 360 * #MHD_daemon_add_connection(). 361 * 362 * @param cls the closure 363 * @param fd the socket to watch 364 * @param watch_for the states of the @a fd to watch, if set to 365 * #MHD_FD_STATE_NONE the socket must be de-registred 366 * @param app_cntx_old the old application defined context for the socket, 367 * NULL if @a fd socket was not registered before 368 * @param ecb_cntx the context handle to be used 369 * with #MHD_daemon_event_update() 370 * @return NULL if error (to connection will be aborted), 371 * or the new socket context 372 * @ingroup event 373 */ 374 static void * 375 update_fd ( 376 void *cls, 377 MHD_Socket fd, 378 enum MHD_FdState watch_for, 379 MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old, 380 struct MHD_EventUpdateContext *ecb_cntx) 381 { 382 struct epoll_event ev; 383 384 (void) cls; 385 if (watch_for == MHD_FD_STATE_NONE) 386 { 387 epoll_ctl (my_epoll_fd, 388 EPOLL_CTL_DEL, 389 fd, 390 &ev /* for Linux 2.6.9-compatibility */); 391 return NULL; 392 } 393 ev.data.ptr = ecb_cntx; 394 ev.events = 0; 395 if (0 != (watch_for & MHD_FD_STATE_RECV)) 396 ev.events |= EPOLLIN; 397 if (0 != (watch_for & MHD_FD_STATE_SEND)) 398 ev.events |= EPOLLOUT; 399 if (0 != (watch_for & MHD_FD_STATE_EXCEPT)) 400 ev.events |= EPOLLHUP; 401 if (0 != 402 epoll_ctl (my_epoll_fd, 403 NULL == app_cntx_old 404 ? EPOLL_CTL_ADD 405 : EPOLL_CTL_MOD, 406 fd, 407 &ev)) 408 { 409 fprintf (stderr, 410 "epoll_ctl failed: %s\n", 411 strerror (errno)); 412 return NULL; 413 } 414 return ecb_cntx; 415 } 416 417 418 const char * 419 MHDT_server_setup_external (const void *cls, 420 struct MHD_Daemon *d) 421 { 422 (void) cls; 423 if (MHD_SC_OK != 424 MHD_DAEMON_SET_OPTIONS ( 425 d, 426 MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL (&update_fd, 427 NULL))) 428 return "Failed to configure external mode!"; 429 if (MHD_SC_OK != 430 MHD_DAEMON_SET_OPTIONS ( 431 d, 432 MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (2000), 433 MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO, 434 get_port ()))) 435 return "Failed to bind to port 0!"; 436 my_epoll_fd = epoll_create1 (0); 437 438 return NULL; 439 } 440 441 442 void 443 MHDT_server_run_external (void *cls, 444 int finsig, 445 struct MHD_Daemon *d) 446 { 447 fd_set r; 448 449 (void) cls; /* Unused */ 450 if (-1 == my_epoll_fd) 451 abort (); 452 while (1) 453 { 454 uint_fast64_t next_wait; 455 struct timeval timeout; 456 457 if (MHD_SC_OK != 458 MHD_daemon_process_reg_events (d, 459 &next_wait)) 460 { 461 fprintf (stderr, 462 "MHD_daemon_process_reg_events() failed\n"); 463 break; 464 } 465 #ifdef HAVE_TIME_H 466 timeout.tv_sec = (time_t) (next_wait / 1000u); 467 #else 468 timeout.tv_sec = (long) (next_wait / 1000u); 469 #endif 470 #ifdef HAVE_SUSECONDS_T 471 timeout.tv_usec = (suseconds_t) ((next_wait % 1000u) * 1000u); 472 #else 473 timeout.tv_usec = (long) ((next_wait % 1000u) * 1000u); 474 #endif 475 476 FD_ZERO (&r); 477 FD_SET (finsig, 478 &r); 479 FD_SET (my_epoll_fd, 480 &r); 481 if ( (-1 == 482 select ((my_epoll_fd > finsig ? my_epoll_fd : finsig) + 1, 483 &r, 484 NULL, 485 NULL, 486 &timeout)) && 487 (EAGAIN != errno) ) 488 { 489 fprintf (stderr, 490 "Failure in select(): %s\n", 491 strerror (errno)); 492 break; 493 } 494 if (FD_ISSET (finsig, 495 &r)) 496 break; 497 if (FD_ISSET (my_epoll_fd, 498 &r)) 499 { 500 int maxevents = 40; 501 struct epoll_event events[maxevents]; 502 int n; 503 int i; 504 505 n = epoll_wait (my_epoll_fd, 506 events, 507 maxevents, 508 0); 509 if (-1 == n) 510 { 511 fprintf (stderr, 512 "epoll_wait() failed: %s\n", 513 strerror (errno)); 514 break; 515 } 516 for (i = 0; i < n; i++) 517 { 518 enum MHD_FdState state = MHD_FD_STATE_NONE; 519 520 if (0 != (events[i].events & EPOLLIN)) 521 MHD_FD_STATE_SET_RECV (state); 522 if (0 != (events[i].events & EPOLLOUT)) 523 MHD_FD_STATE_SET_SEND (state); 524 if (0 != (events[i].events & (EPOLLERR | EPOLLHUP)) ) 525 MHD_FD_STATE_SET_EXCEPT (state); 526 MHD_daemon_event_update ( 527 d, 528 (struct MHD_EventUpdateContext *) events[i].data.ptr, 529 state); 530 } 531 } 532 } 533 534 { 535 char c; 536 537 if ( (FD_ISSET (finsig, 538 &r)) && 539 (1 != read (finsig, 540 &c, 541 1)) ) 542 { 543 fprintf (stderr, 544 "Failed to drain termination signal\n"); 545 } 546 } 547 548 close (my_epoll_fd); 549 my_epoll_fd = -1; 550 } 551 552 553 #endif /* MHD_SUPPORT_EPOLL */