libmicrohttpd2

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

commit 9605c318522d4432b2ce55029f82ea74c8bb91a6
parent 4cc464be2ea4c61a05129208ffef55df81d02fb8
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Tue,  1 Apr 2025 04:23:37 +0300

test case for external event loop

Diffstat:
Msrc/tests/client_server/libtest.h | 27+++++++++++++++++++++++++++
Msrc/tests/client_server/libtest_convenience.c | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/tests/client_server/test_client_server.c | 6++++++
3 files changed, 223 insertions(+), 0 deletions(-)

diff --git a/src/tests/client_server/libtest.h b/src/tests/client_server/libtest.h @@ -742,6 +742,19 @@ MHDT_server_setup_minimal (const void *cls, /** + * Initialize MHD daemon for an external event loop. + * Must be used together with #MHDT_server_run_external(). + * + * @param cls closure (use NULL) + * @param[in,out] d daemon to initialize + * @return error message, NULL on success + */ +const char * +MHDT_server_setup_external (const void *cls, + struct MHD_Daemon *d); + + +/** * Initialize MHD daemon with TLS support, binding to any free port. * * @param cls closure @@ -825,6 +838,20 @@ MHDT_server_run_blocking (void *cls, /** + * Function that runs an MHD daemon with an external event loop until + * a read() against @a finsig succeeds. + * + * @param cls closure + * @param finsig fd to read from to detect termination request + * @param[in,out] d daemon to run + */ +void +MHDT_server_run_external (void *cls, + int finsig, + struct MHD_Daemon *d); + + +/** * Run test suite with @a phases for a daemon initialized * using @a ss_cb on the local machine. * diff --git a/src/tests/client_server/libtest_convenience.c b/src/tests/client_server/libtest_convenience.c @@ -30,6 +30,7 @@ #include <unistd.h> #include <errno.h> #include <curl/curl.h> +#include <sys/epoll.h> const char * @@ -290,3 +291,192 @@ MHDT_server_run_blocking (void *cls, "Failed to drain termination signal\n"); } } + + +static int my_epoll_fd = -1; + + +/** + * The callback for registration/de-registration of the sockets to watch. + * + * This callback must not call #MHD_daemon_destroy(), #MHD_daemon_quiesce(), + * #MHD_daemon_add_connection(). + * + * @param cls the closure + * @param fd the socket to watch + * @param watch_for the states of the @a fd to watch, if set to + * #MHD_FD_STATE_NONE the socket must be de-registred + * @param app_cntx_old the old application defined context for the socket, + * NULL if @a fd socket was not registered before + * @param ecb_cntx the context handle to be used + * with #MHD_daemon_event_update() + * @return NULL if error (to connection will be aborted), + * or the new socket context + * @ingroup event + */ +static void * +update_fd ( + void *cls, + MHD_Socket fd, + enum MHD_FdState watch_for, + MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old, + struct MHD_EventUpdateContext *ecb_cntx) +{ + struct epoll_event ev; + + if (watch_for == MHD_FD_STATE_NONE) + { + epoll_ctl (my_epoll_fd, + EPOLL_CTL_DEL, + fd, + &ev /* for Linux 2.6.9-compatibility */); + return NULL; + } + ev.data.ptr = ecb_cntx; + ev.events = 0; + if (0 != (watch_for & MHD_FD_STATE_RECV)) + ev.events |= EPOLLIN; + if (0 != (watch_for & MHD_FD_STATE_SEND)) + ev.events |= EPOLLOUT; + if (0 != (watch_for & MHD_FD_STATE_EXCEPT)) + ev.events |= EPOLLHUP; + if (0 != + epoll_ctl (my_epoll_fd, + NULL == app_cntx_old + ? EPOLL_CTL_ADD + : EPOLL_CTL_MOD, + fd, + &ev)) + { + fprintf (stderr, + "epoll_ctl failed: %s\n", + strerror (errno)); + return NULL; + } + return ecb_cntx; +} + + +const char * +MHDT_server_setup_external (const void *cls, + struct MHD_Daemon *d) +{ + (void) cls; + if (MHD_SC_OK != + MHD_DAEMON_SET_OPTIONS ( + d, + MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL (&update_fd, + NULL))) + return "Failed to configure external mode!"; + if (MHD_SC_OK != + MHD_DAEMON_SET_OPTIONS ( + d, + MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO, + 0))) + return "Failed to bind to port 0!"; + my_epoll_fd = epoll_create1 (0); + + return NULL; +} + + +void +MHDT_server_run_external (void *cls, + int finsig, + struct MHD_Daemon *d) +{ + fd_set r; + + (void) cls; /* Unused */ + if (-1 == my_epoll_fd) + abort (); + while (1) + { + uint_fast64_t next_wait; + struct timeval timeout; + + if (MHD_SC_OK != + MHD_daemon_process_reg_events (d, + &next_wait)) + { + fprintf (stderr, + "MHD_daemon_process_reg_events() failed\n"); + break; + } + timeout.tv_sec = next_wait / 1000000; + timeout.tv_usec = next_wait % 1000000; + + FD_ZERO (&r); + FD_SET (finsig, + &r); + FD_SET (my_epoll_fd, + &r); + if ( (-1 == + select ((my_epoll_fd > finsig ? my_epoll_fd : finsig) + 1, + &r, + NULL, + NULL, + &timeout)) && + (EAGAIN != errno) ) + { + fprintf (stderr, + "Failure in select(): %s\n", + strerror (errno)); + break; + } + if (FD_ISSET (finsig, + &r)) + break; + if (FD_ISSET (my_epoll_fd, + &r)) + { + int maxevents = 40; + struct epoll_event events[maxevents]; + int n; + int i; + + n = epoll_wait (my_epoll_fd, + events, + maxevents, + 0); + if (-1 == n) + { + fprintf (stderr, + "epoll_wait() failed: %s\n", + strerror (errno)); + break; + } + for (i = 0; i < n; i++) + { + enum MHD_FdState state = MHD_FD_STATE_NONE; + + if (0 != (events[i].events & EPOLLIN)) + state |= MHD_FD_STATE_RECV; + if (0 != (events[i].events & EPOLLOUT)) + state |= MHD_FD_STATE_SEND; + if (0 != (events[i].events & (EPOLLERR | EPOLLHUP)) ) + state |= MHD_FD_STATE_EXCEPT; + MHD_daemon_event_update (d, + events[i].data.ptr, + state); + } + } + } + + { + char c; + + if ( (FD_ISSET (finsig, + &r)) && + (1 != read (finsig, + &c, + 1)) ) + { + fprintf (stderr, + "Failed to drain termination signal\n"); + } + } + + close (my_epoll_fd); + my_epoll_fd = -1; +} diff --git a/src/tests/client_server/test_client_server.c b/src/tests/client_server/test_client_server.c @@ -125,6 +125,12 @@ main (int argc, char *argv[]) .server_setup_cls = thread1auto, .server_runner = &MHDT_server_run_minimal, }, + { + .label = "external events loop mode, no internal threads", + .server_setup = &MHDT_server_setup_external, + .server_setup_cls = NULL, + .server_runner = &MHDT_server_run_external, + }, #if 1 /* FIXME: remove once MHD_daemon_process_blocking has been implemented */