diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h index ff4c5ee9..d9abf71a 100644 --- a/src/include/microhttpd2.h +++ b/src/include/microhttpd2.h @@ -1296,6 +1296,14 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED = 50351 , + /** + * Daemon system data is broken (like listen socket was unexpectedly closed). + * The daemon needs to be closed. + * A new daemon can be started as a replacement after closing the current + * daemon. + */ + MHD_SC_DAEMON_SYS_DATA_BROKEN = 50370 + , /** * Failed to acquire response mutex lock */ @@ -1666,6 +1674,16 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode * The Digest Auth is not supported due to configuration */ MHD_SC_AUTH_DIGEST_UNSUPPORTED = 60242 + , + /** + * The application failed to register FD for the external events monitoring + */ + MHD_SC_EXTR_EVENT_REG_FAILED = 60243 + , + /** + * The application failed to de-register FD for the external events monitoring + */ + MHD_SC_EXTR_EVENT_DEREG_FAILED = 60244 }; /** @@ -3923,6 +3941,13 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode */ MHD_REQUEST_ENDED_BY_APP_ERROR = 41 , + /** + * The request was aborted due to the application failed to register external + * event monitoring for the connection. + * @ingroup request + */ + MHD_REQUEST_ENDED_BY_EXT_EVENT_ERROR = 42 + , /** * Error handling the connection due to resources exhausted. * @ingroup request diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in index 8878fbb0..b1503262 100644 --- a/src/include/microhttpd2_preamble.h.in +++ b/src/include/microhttpd2_preamble.h.in @@ -1296,6 +1296,14 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode */ MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED = 50351 , + /** + * Daemon system data is broken (like listen socket was unexpectedly closed). + * The daemon needs to be closed. + * A new daemon can be started as a replacement after closing the current + * daemon. + */ + MHD_SC_DAEMON_SYS_DATA_BROKEN = 50370 + , /** * Failed to acquire response mutex lock */ @@ -1666,6 +1674,16 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode * The Digest Auth is not supported due to configuration */ MHD_SC_AUTH_DIGEST_UNSUPPORTED = 60242 + , + /** + * The application failed to register FD for the external events monitoring + */ + MHD_SC_EXTR_EVENT_REG_FAILED = 60243 + , + /** + * The application failed to de-register FD for the external events monitoring + */ + MHD_SC_EXTR_EVENT_DEREG_FAILED = 60244 }; /** @@ -3923,6 +3941,13 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode */ MHD_REQUEST_ENDED_BY_APP_ERROR = 41 , + /** + * The request was aborted due to the application failed to register external + * event monitoring for the connection. + * @ingroup request + */ + MHD_REQUEST_ENDED_BY_EXT_EVENT_ERROR = 42 + , /** * Error handling the connection due to resources exhausted. * @ingroup request diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am index faf0692f..c2eec010 100644 --- a/src/mhd2/Makefile.am +++ b/src/mhd2/Makefile.am @@ -77,6 +77,7 @@ libmicrohttpd2_la_SOURCES = \ daemon_get_info.c \ daemon_add_conn.c daemon_add_conn.h \ daemon_funcs.c daemon_funcs.h \ + daemon_event_update.c extr_events_funcs.c extr_events_funcs.h \ conn_data_process.c conn_data_process.h \ conn_data_recv.c conn_data_recv.h \ conn_data_send.c conn_data_send.h \ diff --git a/src/mhd2/daemon_event_update.c b/src/mhd2/daemon_event_update.c new file mode 100644 index 00000000..9ffdd70a --- /dev/null +++ b/src/mhd2/daemon_event_update.c @@ -0,0 +1,126 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2025 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/daemon_event_update.c + * @brief The implementation of MHD_daemon_event_update() function for external + * events updates + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +#include "mhd_assert.h" + +#include "mhd_daemon.h" +#include "mhd_connection.h" + +#include "daemon_logger.h" + +#include "mhd_public_api.h" + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) void +MHD_daemon_event_update ( + struct MHD_Daemon *MHD_RESTRICT daemon, + struct MHD_EventUpdateContext *MHD_RESTRICT ecb_cntx, + enum MHD_FdState fd_current_state) +{ + bool broken_app_data; + bool unneeded_event; + + if (mhd_DAEMON_STATE_STARTED > daemon->state) + return; + if (! mhd_WM_INT_HAS_EXT_EVENTS (daemon->wmode_int)) + return; /* FIXME: log error? */ + if (mhd_DAEMON_STATE_STARTED < daemon->state) + return; + + broken_app_data = false; + + switch ((mhd_SockRelMarker) ecb_cntx) + { + case mhd_SOCKET_REL_MARKER_EMPTY: + broken_app_data = true; + break; + case mhd_SOCKET_REL_MARKER_ITC: +#ifdef MHD_SUPPORT_THREADS + if (MHD_FD_STATE_IS_SET_EXCEPT (fd_current_state)) + daemon->events.data.extr.itc_data.is_broken = true; + else + { + daemon->events.data.extr.itc_data.is_active = + MHD_FD_STATE_IS_SET_RECV (fd_current_state); + unneeded_event = MHD_FD_STATE_IS_SET_SEND (fd_current_state); + } +#else /* ! MHD_SUPPORT_THREADS */ + broken_app_data = true; +#endif /* ! MHD_SUPPORT_THREADS */ + break; + case mhd_SOCKET_REL_MARKER_LISTEN: + if (MHD_INVALID_SOCKET == daemon->net.listen.fd) + broken_app_data = true; + else if (MHD_FD_STATE_IS_SET_EXCEPT (fd_current_state)) + daemon->net.listen.is_broken = true; + else + { + daemon->events.accept_pending = + MHD_FD_STATE_IS_SET_RECV (fd_current_state); + unneeded_event = MHD_FD_STATE_IS_SET_SEND (fd_current_state); + } + break; + default: + if (((struct MHD_Connection *) ecb_cntx)->daemon != daemon) + broken_app_data = true; + else + { + struct MHD_Connection *const c = ((struct MHD_Connection *) ecb_cntx); + unsigned int err_flag; + + mhd_assert (MHD_FD_STATE_NONE != c->extr_event.reg_for); + + unneeded_event = (0 != ((~((unsigned int) c->extr_event.reg_for)) + & ((unsigned int) fd_current_state))); + + /* Preserve connection's "error flag" */ + err_flag = (((unsigned int) c->sk.ready) + & (unsigned int) mhd_SOCKET_NET_STATE_ERROR_READY); + + c->sk.ready = + (enum mhd_SocketNetState) + (err_flag | (((unsigned int) fd_current_state) + & ((unsigned int) c->extr_event.reg_for))); + } + break; + } + + if (broken_app_data) + { + // TODO: log error + (void) 0; + } + else if (unneeded_event) + { + // TODO: log error + (void) 0; + } +} diff --git a/src/mhd2/daemon_start.c b/src/mhd2/daemon_start.c index 0ec3d847..0cbf46e0 100644 --- a/src/mhd2/daemon_start.c +++ b/src/mhd2/daemon_start.c @@ -79,10 +79,11 @@ # include "mhd_tls_funcs.h" #endif +#include "events_process.h" + #ifdef MHD_SUPPORT_THREADS # include "mhd_itc.h" # include "mhd_threads.h" -# include "events_process.h" # include "daemon_funcs.h" #endif @@ -1339,11 +1340,15 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d, (MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE == s->work_mode.mode)); mhd_assert (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); d->events.poll_type = mhd_POLL_TYPE_EXT; - d->events.data.ext.cb_data.cb = + d->events.data.extr.cb_data.cb = s->work_mode.params.v_external_event_loop_cb.reg_cb; - d->events.data.ext.cb_data.cls = + d->events.data.extr.cb_data.cls = s->work_mode.params.v_external_event_loop_cb.reg_cb_cls; - d->events.data.ext.reg_all = (MHD_NO != s->reregister_all); + d->events.data.extr.reg_all = (MHD_NO != s->reregister_all); +#ifdef MHD_SUPPORT_THREADS + d->events.data.extr.itc_data.app_cntx = NULL; +#endif /* MHD_SUPPORT_THREADS */ + d->events.data.extr.listen_data.app_cntx = NULL; break; #ifdef MHD_SUPPORT_SELECT case mhd_POLL_TYPE_SELECT: @@ -1795,7 +1800,8 @@ allocate_events (struct MHD_Daemon *restrict d) switch (d->events.poll_type) { case mhd_POLL_TYPE_EXT: - mhd_assert (NULL != d->events.data.ext.cb_data.cb); + mhd_assert (NULL != d->events.data.extr.cb_data.cb); + /* Nothing to do: allocation is not needed */ #ifndef NDEBUG d->dbg.events_allocated = true; #endif @@ -2064,21 +2070,25 @@ init_daemon_fds_monitoring (struct MHD_Daemon *restrict d) switch (d->events.poll_type) { case mhd_POLL_TYPE_EXT: - mhd_assert (NULL != d->events.data.ext.cb_data.cb); - if (! d->events.data.ext.reg_all) + mhd_assert (NULL != d->events.data.extr.cb_data.cb); +#ifdef MHD_SUPPORT_THREADS + d->events.data.extr.itc_data.is_active = false; + d->events.data.extr.itc_data.is_broken = false; +#endif /* MHD_SUPPORT_THREADS */ + if (! d->events.data.extr.reg_all) { bool itc_reg_succeed; /* Register daemon's FDs now */ #ifdef MHD_SUPPORT_THREADS - d->events.data.ext.app_cntx.itc = - d->events.data.ext.cb_data.cb (d->events.data.ext.cb_data.cls, - mhd_itc_r_fd (d->threading.itc), - MHD_FD_STATE_RECV, - NULL, - (struct MHD_EventUpdateContext *) - mhd_SOCKET_REL_MARKER_ITC); - itc_reg_succeed = (NULL != d->events.data.ext.app_cntx.itc); + d->events.data.extr.itc_data.app_cntx = + d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, + mhd_itc_r_fd (d->threading.itc), + MHD_FD_STATE_RECV_EXCEPT, + NULL, + (struct MHD_EventUpdateContext *) + mhd_SOCKET_REL_MARKER_ITC); + itc_reg_succeed = (NULL != d->events.data.extr.itc_data.app_cntx); #else /* ! MHD_SUPPORT_THREADS */ itc_reg_succeed = true; #endif /* ! MHD_SUPPORT_THREADS */ @@ -2086,31 +2096,31 @@ init_daemon_fds_monitoring (struct MHD_Daemon *restrict d) { if (MHD_INVALID_SOCKET == d->net.listen.fd) { - d->events.data.ext.app_cntx.listen = NULL; + d->events.data.extr.listen_data.app_cntx = NULL; return MHD_SC_OK; /* Success exit point */ } /* Need to register the listen FD */ - d->events.data.ext.app_cntx.listen = - d->events.data.ext.cb_data.cb (d->events.data.ext.cb_data.cls, - d->net.listen.fd, - MHD_FD_STATE_RECV, - NULL, - (struct MHD_EventUpdateContext *) - mhd_SOCKET_REL_MARKER_LISTEN); - if (NULL != d->events.data.ext.app_cntx.listen) + d->events.data.extr.listen_data.app_cntx = + d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, + d->net.listen.fd, + MHD_FD_STATE_RECV_EXCEPT, + NULL, + (struct MHD_EventUpdateContext *) + mhd_SOCKET_REL_MARKER_LISTEN); + if (NULL != d->events.data.extr.listen_data.app_cntx) return MHD_SC_OK; /* Success exit point */ /* Below is a clean-up path for 'case mhd_POLL_TYPE_EXT:' */ #ifdef MHD_SUPPORT_THREADS /* De-register ITC FD */ - (void) d->events.data.ext.cb_data.cb (d->events.data.ext.cb_data.cls, - mhd_itc_r_fd (d->threading.itc), - MHD_FD_STATE_NONE, - d->events.data.ext.app_cntx.itc, - (struct MHD_EventUpdateContext *) - mhd_SOCKET_REL_MARKER_ITC); - d->events.data.ext.app_cntx.itc = NULL; + (void) d->events.data.extr.cb_data.cb ( + d->events.data.extr.cb_data.cls, + mhd_itc_r_fd (d->threading.itc), + MHD_FD_STATE_NONE, + d->events.data.extr.itc_data.app_cntx, + (struct MHD_EventUpdateContext *) mhd_SOCKET_REL_MARKER_ITC); + d->events.data.extr.itc_data.app_cntx = NULL; #endif /* MHD_SUPPORT_THREADS */ } @@ -2123,9 +2133,9 @@ init_daemon_fds_monitoring (struct MHD_Daemon *restrict d) { /* Daemons FDs are repeatedly registered every processing cycle */ #ifdef MHD_SUPPORT_THREADS - d->events.data.ext.app_cntx.itc = NULL; + d->events.data.extr.itc_data.app_cntx = NULL; #endif /* MHD_SUPPORT_THREADS */ - d->events.data.ext.app_cntx.listen = NULL; + d->events.data.extr.listen_data.app_cntx = NULL; return MHD_SC_OK; } break; @@ -2229,21 +2239,21 @@ deinit_daemon_fds_monitoring (struct MHD_Daemon *restrict d) switch (d->events.poll_type) { case mhd_POLL_TYPE_EXT: - if (NULL != d->events.data.ext.app_cntx.listen) - (void) d->events.data.ext.cb_data.cb (d->events.data.ext.cb_data.cls, - d->net.listen.fd, - MHD_FD_STATE_NONE, - d->events.data.ext.app_cntx.listen, - (struct MHD_EventUpdateContext *) - mhd_SOCKET_REL_MARKER_LISTEN); + if (NULL != d->events.data.extr.listen_data.app_cntx) + (void) d->events.data.extr.cb_data.cb ( + d->events.data.extr.cb_data.cls, + d->net.listen.fd, + MHD_FD_STATE_NONE, + d->events.data.extr.listen_data.app_cntx, + (struct MHD_EventUpdateContext *) mhd_SOCKET_REL_MARKER_LISTEN); #ifdef MHD_SUPPORT_THREADS - if (NULL != d->events.data.ext.app_cntx.itc) - (void) d->events.data.ext.cb_data.cb (d->events.data.ext.cb_data.cls, - mhd_itc_r_fd (d->threading.itc), - MHD_FD_STATE_NONE, - d->events.data.ext.app_cntx.itc, - (struct MHD_EventUpdateContext *) - mhd_SOCKET_REL_MARKER_ITC); + if (NULL != d->events.data.extr.itc_data.app_cntx) + (void) d->events.data.extr.cb_data.cb ( + d->events.data.extr.cb_data.cls, + mhd_itc_r_fd (d->threading.itc), + MHD_FD_STATE_NONE, + d->events.data.extr.itc_data.app_cntx, + (struct MHD_EventUpdateContext *) mhd_SOCKET_REL_MARKER_ITC); #endif /* MHD_SUPPORT_THREADS */ return; #ifdef MHD_SUPPORT_SELECT @@ -2624,7 +2634,7 @@ set_d_threading_type (struct MHD_Daemon *restrict d) case mhd_WM_INT_EXTERNAL_EVENTS_LEVEL: mhd_assert (! mhd_WM_INT_HAS_THREADS (d->wmode_int)); mhd_assert (mhd_POLL_TYPE_EXT == d->events.poll_type); - mhd_assert (NULL != d->events.data.ext.cb_data.cb); + mhd_assert (NULL != d->events.data.extr.cb_data.cb); #ifdef MHD_SUPPORT_THREADS d->threading.d_type = mhd_DAEMON_TYPE_SINGLE; #endif /* MHD_SUPPORT_THREADS */ @@ -3183,9 +3193,7 @@ daemon_start_threads (struct MHD_Daemon *restrict d) /** * Stop the daemon internal threads, if the daemon configured to use them. - * @param d the daemon object, the threads (if any) must be started - * @return #MHD_SC_OK on success, - * the error code otherwise + * @param d the daemon object to stop threads */ static MHD_FN_PAR_NONNULL_ (1) void daemon_stop_threads (struct MHD_Daemon *restrict d) @@ -3219,6 +3227,24 @@ daemon_stop_threads (struct MHD_Daemon *restrict d) } +/** + * Close all daemon connections for modes without internal threads + * @param d the daemon object + */ +static MHD_FN_PAR_NONNULL_ (1) void +daemon_close_connections (struct MHD_Daemon *restrict d) +{ + if (mhd_WM_INT_HAS_THREADS (d->wmode_int)) + { + /* In these modes connections must be closed in the daemon thread */ + mhd_assert (NULL == mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn)); + return; + } + + mhd_daemon_close_all_conns (d); +} + + /** * Internal daemon initialisation function. * This function calls all required initialisation stages one-by-one. @@ -3333,6 +3359,8 @@ MHD_daemon_destroy (struct MHD_Daemon *daemon) daemon_stop_threads (daemon); + daemon_close_connections (daemon); + daemon_deinit_threading_and_conn (daemon); daemon_deinit_large_buf (daemon); diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c index 8e3a1375..8f22f661 100644 --- a/src/mhd2/events_process.c +++ b/src/mhd2/events_process.c @@ -57,6 +57,7 @@ #include "daemon_funcs.h" #include "conn_data_process.h" #include "stream_funcs.h" +#include "extr_events_funcs.h" #ifdef MHD_SUPPORT_UPGRADE # include "upgrade_proc.h" @@ -373,8 +374,8 @@ daemon_cleanup_upgraded_conns (struct MHD_Daemon *d) #define daemon_cleanup_upgraded_conns(d) ((void) d) #endif /* ! MHD_SUPPORT_UPGRADE */ -static void -close_all_daemon_conns (struct MHD_Daemon *d) +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_daemon_close_all_conns (struct MHD_Daemon *d) { struct MHD_Connection *c; bool has_upgraded_unclosed; @@ -415,6 +416,198 @@ close_all_daemon_conns (struct MHD_Daemon *d) } +/** + * Process all external events updated of existing connections, information + * about new connections pending to be accept()'ed, presence of the events on + * the daemon's ITC. + * @return 'true' if processed successfully, + * 'false' is unrecoverable error occurs and the daemon must be + * closed + */ +static MHD_FN_PAR_NONNULL_ (1) bool +ext_events_process_net_updates (struct MHD_Daemon *restrict d) +{ + struct MHD_Connection *restrict c; + + mhd_assert (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); + mhd_assert (mhd_POLL_TYPE_EXT == d->events.poll_type); + +#ifdef MHD_SUPPORT_THREADS + if (d->events.data.extr.itc_data.is_active) + { + d->events.data.extr.itc_data.is_active = false; + /* Clear ITC here, before other data processing. + * Any external events will activate ITC again if additional data to + * process is added externally. Clearing ITC early ensures that new data + * (with additional ITC activation) will not be missed. */ + mhd_itc_clear (d->threading.itc); + } +#endif /* MHD_SUPPORT_THREADS */ + + for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn); + NULL != c; + c = mhd_DLINKEDL_GET_NEXT (c,all_conn)) + { + bool has_err_state; + if (is_conn_excluded_from_http_comm (c)) + continue; + + has_err_state = + (0 != (((unsigned int) c->sk.ready) & mhd_SOCKET_NET_STATE_ERROR_READY)); + + mhd_conn_mark_ready_update3 (c, + has_err_state, + d); + } + + return true; +} + + +/** + * Update all registrations of FDs for external monitoring. + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ (1) enum MHD_StatusCode +ext_events_update_registrations (struct MHD_Daemon *restrict d) +{ + const bool rereg_all = d->events.data.extr.reg_all; + const bool edge_trigg = (mhd_WM_INT_EXTERNAL_EVENTS_EDGE == d->wmode_int); + bool daemon_fds_succeed; + struct MHD_Connection *c; + struct MHD_Connection *c_next; + + mhd_assert (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); + mhd_assert (mhd_POLL_TYPE_EXT == d->events.poll_type); + + /* (Re-)register daemon's FDs */ + +#ifdef MHD_SUPPORT_THREADS + if (rereg_all || + (NULL == d->events.data.extr.itc_data.app_cntx)) + { + /* (Re-)register ITC FD */ + d->events.data.extr.itc_data.app_cntx = + d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, + mhd_itc_r_fd (d->threading.itc), + MHD_FD_STATE_RECV_EXCEPT, + d->events.data.extr.itc_data.app_cntx, + (struct MHD_EventUpdateContext *) + mhd_SOCKET_REL_MARKER_ITC); + if (NULL != d->events.data.extr.itc_data.app_cntx) + mhd_log_extr_event_dereg_failed (d); + } + daemon_fds_succeed = (NULL != d->events.data.extr.itc_data.app_cntx); +#else /* ! MHD_SUPPORT_THREADS */ + daemon_fds_succeed = true; +#endif /* ! MHD_SUPPORT_THREADS */ + + if (daemon_fds_succeed) + { + if ((MHD_INVALID_SOCKET == d->net.listen.fd) && + (NULL != d->events.data.extr.listen_data.app_cntx)) + { + /* De-register the listen FD */ + d->events.data.extr.listen_data.app_cntx = + d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, + d->net.listen.fd, + MHD_FD_STATE_NONE, + d->events.data.extr.listen_data.app_cntx + , + (struct MHD_EventUpdateContext *) + mhd_SOCKET_REL_MARKER_LISTEN); + if (NULL != d->events.data.extr.listen_data.app_cntx) + mhd_log_extr_event_dereg_failed (d); + } + else if ((MHD_INVALID_SOCKET != d->net.listen.fd) && + (rereg_all || (NULL == d->events.data.extr.listen_data.app_cntx))) + { + /* (Re-)register listen FD */ + d->events.data.extr.listen_data.app_cntx = + d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, + d->net.listen.fd, + MHD_FD_STATE_RECV_EXCEPT, + d->events.data.extr.listen_data.app_cntx + , + (struct MHD_EventUpdateContext *) + mhd_SOCKET_REL_MARKER_LISTEN); + + daemon_fds_succeed = (NULL != d->events.data.extr.listen_data.app_cntx); + } + } + + if (! daemon_fds_succeed) + { + mhd_LOG_MSG (d, MHD_SC_EXT_EVENT_REG_DAEMON_FDS_FAILURE, \ + "Failed to register daemon FDs in the application " + "(external events) monitoring."); + return MHD_SC_EXT_EVENT_REG_DAEMON_FDS_FAILURE; + } + + for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn); + NULL != c; + c = c_next) + { + enum MHD_FdState watch_for; + + /* Get the next connection now, as the current connection could be removed + from the daemon. */ + c_next = mhd_DLINKEDL_GET_NEXT (c,all_conn); + + if (is_conn_excluded_from_http_comm (c)) + { + if (NULL != c->extr_event.app_cntx) + { + c->extr_event.app_cntx = + d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, + c->sk.fd, + MHD_FD_STATE_NONE, + c->extr_event.app_cntx, + (struct MHD_EventUpdateContext *) c); + if (NULL != c->extr_event.app_cntx) + mhd_log_extr_event_dereg_failed (d); + } + continue; + } + + watch_for = + edge_trigg ? + MHD_FD_STATE_RECV_SEND_EXCEPT : + (enum MHD_FdState) (MHD_FD_STATE_EXCEPT + | (((unsigned int) c->event_loop_info) + & (MHD_EVENT_LOOP_INFO_RECV + | MHD_EVENT_LOOP_INFO_SEND))); + + mhd_assert ((! edge_trigg) || \ + (MHD_FD_STATE_RECV_SEND_EXCEPT == c->extr_event.reg_for) || \ + (NULL == c->extr_event.app_cntx)); + + if ((NULL == c->extr_event.app_cntx) || + rereg_all || + (! edge_trigg && (watch_for != c->extr_event.reg_for))) + { + c->extr_event.app_cntx = + d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, + c->sk.fd, + watch_for, + c->extr_event.app_cntx, + (struct MHD_EventUpdateContext *) c); + if (NULL == c->extr_event.app_cntx) + { + mhd_conn_start_closing_ext_event_failed (c); + mhd_conn_pre_clean (c); + mhd_conn_remove_from_daemon (c); + mhd_conn_close_final (c); + } + c->extr_event.reg_for = watch_for; + } + } + + return MHD_SC_OK; +} + + #ifdef MHD_SUPPORT_SELECT /** @@ -1147,7 +1340,9 @@ process_all_events_and_data (struct MHD_Daemon *restrict d) switch (d->events.poll_type) { case mhd_POLL_TYPE_EXT: - return false; // TODO: implement + mhd_assert (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); + if (! ext_events_process_net_updates (d)) + return false; break; #ifdef MHD_SUPPORT_SELECT case mhd_POLL_TYPE_SELECT: @@ -1192,6 +1387,64 @@ process_all_events_and_data (struct MHD_Daemon *restrict d) } +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (1) enum MHD_StatusCode +MHD_daemon_process_reg_events (struct MHD_Daemon *MHD_RESTRICT daemon, + uint_fast64_t *MHD_RESTRICT next_max_wait) +{ + enum MHD_StatusCode res; + + if (mhd_DAEMON_STATE_STARTED > daemon->state) + return MHD_SC_TOO_EARLY; + if (! mhd_WM_INT_HAS_EXT_EVENTS (daemon->wmode_int)) + return MHD_SC_EXTERNAL_EVENT_ONLY; + if (mhd_DAEMON_STATE_STARTED < daemon->state) + return MHD_SC_TOO_LATE; + +#ifdef MHD_SUPPORT_THREADS + if (daemon->events.data.extr.itc_data.is_broken) + return MHD_SC_DAEMON_SYS_DATA_BROKEN; +#endif /* MHD_SUPPORT_THREADS */ + + if (daemon->net.listen.is_broken) + return MHD_SC_DAEMON_SYS_DATA_BROKEN; + +#ifdef MHD_SUPPORT_THREADS + if (daemon->threading.resume_requested) + mhd_daemon_resume_conns (daemon); +#endif /* MHD_SUPPORT_THREADS */ + + /* Ignore returned value */ + (void) process_all_events_and_data (daemon); + + if (NULL != next_max_wait) + *next_max_wait = MHD_WAIT_INDEFINITELY; + + res = ext_events_update_registrations (daemon); + if (MHD_SC_OK != res) + return res; + +#ifdef MHD_SUPPORT_THREADS + if (daemon->events.data.extr.itc_data.is_broken) + { + log_itc_broken (daemon); + return MHD_SC_DAEMON_SYS_DATA_BROKEN; + } +#endif /* MHD_SUPPORT_THREADS */ + + if (daemon->net.listen.is_broken) + { + log_listen_broken (daemon); + return MHD_SC_DAEMON_SYS_DATA_BROKEN; + } + + if (NULL != next_max_wait) + *next_max_wait = mhd_daemon_get_wait_max (); + + return MHD_SC_OK; +} + + /** * The entry point for the daemon worker thread * @param cls the closure @@ -1230,7 +1483,7 @@ mhd_worker_all_events (void *cls) "The daemon thread is stopping, but termination has not " \ "been requested for the daemon."); } - close_all_daemon_conns (d); + mhd_daemon_close_all_conns (d); return (mhd_THRD_RTRN_TYPE) 0; } diff --git a/src/mhd2/events_process.h b/src/mhd2/events_process.h index 8a0205ab..3592f13f 100644 --- a/src/mhd2/events_process.h +++ b/src/mhd2/events_process.h @@ -66,4 +66,13 @@ MHD_INTERNAL uint_fast64_t mhd_daemon_get_wait_max (struct MHD_Daemon *restrict d) MHD_FN_PAR_NONNULL_ALL_; +/** + * Close all daemon connections. + * Must not be called when any other connections processing function is running + * @param d the daemon to use + */ +MHD_INTERNAL void +mhd_daemon_close_all_conns (struct MHD_Daemon *d) +MHD_FN_PAR_NONNULL_ALL_; + #endif /* ! MHD_EVENTS_PROCESS_H */ diff --git a/src/mhd2/extr_events_funcs.c b/src/mhd2/extr_events_funcs.c new file mode 100644 index 00000000..93516996 --- /dev/null +++ b/src/mhd2/extr_events_funcs.c @@ -0,0 +1,44 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2025 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/extr_events_funcs.c + * @brief The implementation of the external events internal helper functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" +#include "extr_events_funcs.h" + +#ifdef MHD_SUPPORT_LOG_FUNCTIONALITY + +#include "daemon_logger.h" + +MHD_INTERNAL void +mhd_log_extr_event_dereg_failed (struct MHD_Daemon *restrict d) +{ + mhd_LOG_MSG (d, \ + MHD_SC_EXTR_EVENT_DEREG_FAILED, \ + "Application failed to de-register FD: registration callback " \ + "returned non-NULL value"); +} + + +#endif /* MHD_SUPPORT_LOG_FUNCTIONALITY */ diff --git a/src/mhd2/extr_events_funcs.h b/src/mhd2/extr_events_funcs.h new file mode 100644 index 00000000..1c34f229 --- /dev/null +++ b/src/mhd2/extr_events_funcs.h @@ -0,0 +1,51 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2025 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/extr_events_funcs.h + * @brief The definition of the external events internal helper functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_EXTR_EVENTS_FUNCS_H +#define MHD_EXTR_EVENTS_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#ifdef MHD_SUPPORT_LOG_FUNCTIONALITY + +struct MHD_Daemon; /* forward declaration */ + +/** + * Log message about failed de-registration of FDs + */ +MHD_INTERNAL void +mhd_log_extr_event_dereg_failed (struct MHD_Daemon *restrict d); + +#else /* ! MHD_SUPPORT_LOG_FUNCTIONALITY */ + +/** + * Log message about failed de-registration of FDs (no-op implementation) + */ +#define mhd_log_extr_event_dereg_failed(d) ((void) 0) + +#endif /* ! MHD_SUPPORT_LOG_FUNCTIONALITY */ + +#endif /* ! MHD_EXTR_EVENTS_FUNCS_H */ diff --git a/src/mhd2/mhd_connection.h b/src/mhd2/mhd_connection.h index f3e51e8c..3338812a 100644 --- a/src/mhd2/mhd_connection.h +++ b/src/mhd2/mhd_connection.h @@ -456,6 +456,24 @@ enum MHD_FIXED_ENUM_ mhd_HttpStage }; + +/** + * The connection's external event data + */ +struct mhd_ConnExtrEvents +{ + /** + * Connection's application context for the external events monitoring + */ + void *app_cntx; + + /** + * The last targets for which the connection FD has been registration for + */ + enum MHD_FdState reg_for; +}; + + struct mhd_ConnDebugData { bool closing_started; @@ -506,6 +524,11 @@ struct MHD_Connection */ struct mhd_ConnSocket sk; + /** + * The connection's external event data + */ + struct mhd_ConnExtrEvents extr_event; + #ifdef MHD_SUPPORT_HTTPS /** * Connection-specific TLS data. diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h index 402d4906..b8491b44 100644 --- a/src/mhd2/mhd_daemon.h +++ b/src/mhd2/mhd_daemon.h @@ -332,22 +332,41 @@ struct mhd_DaemonEventsExternalCallback }; -/** - * Application context for external events for daemon's FDs - */ -struct mhd_DaemonEventsExternalScktAppCntx -{ #ifdef MHD_SUPPORT_THREADS +/** + * External events data for ITC FD + */ +struct mhd_DaemonEventsExternalDaemonItcData +{ /** * Application context for ITC FD */ - void *itc; -#endif /* MHD_SUPPORT_THREADS */ + void *app_cntx; /** - * Application context for listen socket + * Set to 'true' when active state was detected on ITC by + * external polling */ - void *listen; + bool is_active; + + /** + * Set to 'true' when error state was detected on ITC by + * external polling. + * The daemon may become non-functional. + */ + bool is_broken; +}; +#endif /* MHD_SUPPORT_THREADS */ + +/** + * External events data for the listen socket + */ +struct mhd_DaemonEventsExternalDaemonListenData +{ + /** + * Application context for ITC FD + */ + void *app_cntx; }; /** @@ -361,10 +380,17 @@ struct mhd_DaemonEventsExternal */ struct mhd_DaemonEventsExternalCallback cb_data; +#ifdef MHD_SUPPORT_THREADS /** - * Application context for external events for daemon's FDs + * External events data for ITC FD */ - struct mhd_DaemonEventsExternalScktAppCntx app_cntx; + struct mhd_DaemonEventsExternalDaemonItcData itc_data; +#endif /* MHD_SUPPORT_THREADS */ + + /** + * External events data for the listen socket + */ + struct mhd_DaemonEventsExternalDaemonListenData listen_data; /** * If set to 'true' then all FDs must be registered each round. @@ -405,7 +431,7 @@ union mhd_DaemonEventMonitoringTypeSpecificData /** * Daemon's data for external events for sockets monitoring. */ - struct mhd_DaemonEventsExternal ext; + struct mhd_DaemonEventsExternal extr; }; diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c index 3155b4d6..b067b762 100644 --- a/src/mhd2/stream_funcs.c +++ b/src/mhd2/stream_funcs.c @@ -53,6 +53,7 @@ #include "daemon_funcs.h" #include "conn_mark_ready.h" #include "stream_process_reply.h" +#include "extr_events_funcs.h" #ifdef MHD_SUPPORT_HTTPS # include "mhd_tls_funcs.h" @@ -750,6 +751,11 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, close_hard = true; end_code = MHD_REQUEST_ENDED_NO_RESOURCES; break; + case mhd_CONN_CLOSE_EXTR_EVENT_REG_FAILED: + close_hard = true; + end_code = MHD_REQUEST_ENDED_BY_EXT_EVENT_ERROR; + sc = MHD_SC_EXTR_EVENT_REG_FAILED; + break; case mhd_CONN_CLOSE_NO_SYS_RESOURCES: close_hard = true; end_code = MHD_REQUEST_ENDED_NO_RESOURCES; @@ -941,8 +947,23 @@ mhd_conn_pre_clean_part1 (struct MHD_Connection *restrict c) if (NULL != c->rq.cntn.lbuf.data) mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); + if (mhd_WM_INT_HAS_EXT_EVENTS (c->daemon->wmode_int)) + { + struct MHD_Daemon *const d = c->daemon; + if (NULL != c->extr_event.app_cntx) + { + c->extr_event.app_cntx = + d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, + c->sk.fd, + MHD_FD_STATE_NONE, + c->extr_event.app_cntx, + (struct MHD_EventUpdateContext *) c); + if (NULL != c->extr_event.app_cntx) + mhd_log_extr_event_dereg_failed (d); + } + } #ifdef MHD_SUPPORT_EPOLL - if (mhd_POLL_TYPE_EPOLL == c->daemon->events.poll_type) + else if (mhd_POLL_TYPE_EPOLL == c->daemon->events.poll_type) { struct epoll_event event; diff --git a/src/mhd2/stream_funcs.h b/src/mhd2/stream_funcs.h index e45ccda0..2001a6d2 100644 --- a/src/mhd2/stream_funcs.h +++ b/src/mhd2/stream_funcs.h @@ -228,6 +228,11 @@ enum mhd_ConnCloseReason */ mhd_CONN_CLOSE_INT_ERROR , + /** + * Failed to register the connection for the external event monitoring + */ + mhd_CONN_CLOSE_EXTR_EVENT_REG_FAILED + , /** * No system resources available to handle connection */ @@ -321,6 +326,29 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3); #define mhd_conn_start_closing_app_abort(c) \ mhd_conn_start_closing ((c), mhd_CONN_CLOSE_APP_ABORTED, NULL) + +#ifdef MHD_SUPPORT_LOG_FUNCTIONALITY +/** + * Perform initial clean-up and mark for closing. + * Set the reason to "socket error" + * @param c the connection for pre-closing + */ +#define mhd_conn_start_closing_ext_event_failed(c) \ + mhd_conn_start_closing ((c), \ + mhd_CONN_CLOSE_EXTR_EVENT_REG_FAILED, \ + "The application failed to register FD for " \ + "the external events monitoring.") +#else /* ! MHD_SUPPORT_LOG_FUNCTIONALITY */ +/** + * Perform initial clean-up and mark for closing. + * Set the reason to "socket error" + * @param c the connection for pre-closing + */ +#define mhd_conn_start_closing_ext_event_failed(c) \ + mhd_conn_start_closing ((c), \ + mhd_CONN_CLOSE_EXTR_EVENT_REG_FAILED, NULL) +#endif /* ! MHD_SUPPORT_LOG_FUNCTIONALITY */ + /** * Perform initial clean-up and mark for closing. * Set the reason to "socket error"