Selaa lähdekoodia

Final part of external events

Evgeny Grin (Karlson2k) 10 kuukautta sitten
vanhempi
sitoutus
d2792c5e11

+ 25 - 0
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

+ 25 - 0
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

+ 1 - 0
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 \

+ 126 - 0
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;
+  }
+}

+ 79 - 51
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);

+ 257 - 4
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;
 }

+ 9 - 0
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 */

+ 44 - 0
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 */

+ 51 - 0
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 */

+ 23 - 0
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.

+ 35 - 9
src/mhd2/mhd_daemon.h

@@ -332,22 +332,41 @@ struct mhd_DaemonEventsExternalCallback
 };
 
 
+#ifdef MHD_SUPPORT_THREADS
 /**
- * Application context for external events for daemon's FDs
+ * External events data for ITC FD
  */
-struct mhd_DaemonEventsExternalScktAppCntx
+struct mhd_DaemonEventsExternalDaemonItcData
 {
-#ifdef MHD_SUPPORT_THREADS
   /**
    * Application context for ITC FD
    */
-  void *itc;
+  void *app_cntx;
+
+  /**
+   * Set to 'true' when active state was detected on ITC by
+   * external polling
+   */
+  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 listen socket
+   * Application context for ITC FD
    */
-  void *listen;
+  void *app_cntx;
 };
 
 /**
@@ -361,10 +380,17 @@ struct mhd_DaemonEventsExternal
    */
   struct mhd_DaemonEventsExternalCallback cb_data;
 
+#ifdef MHD_SUPPORT_THREADS
+  /**
+   * External events data for ITC FD
+   */
+  struct mhd_DaemonEventsExternalDaemonItcData itc_data;
+#endif /* MHD_SUPPORT_THREADS */
+
   /**
-   * Application context for external events for daemon's FDs
+   * External events data for the listen socket
    */
-  struct mhd_DaemonEventsExternalScktAppCntx app_cntx;
+  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;
 };
 
 

+ 22 - 1
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;
 

+ 28 - 0
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"