commit 4cc464be2ea4c61a05129208ffef55df81d02fb8
parent 4facc093e133e66bc9aa49311e522badfb688164
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date: Wed, 9 Apr 2025 13:00:05 +0300
Implemented external events, suspend/resume + some fixes and debug prints
Diffstat:
34 files changed, 2347 insertions(+), 346 deletions(-)
diff --git a/src/incl_priv/mhd_sys_options.h b/src/incl_priv/mhd_sys_options.h
@@ -516,6 +516,21 @@
# endif
#endif
+#ifndef mhd_DEBUG_POLLING_FDS
+/* Use debug-print for FDs polling */
+/* # define mhd_DEBUG_POLLING_FDS 1 */
+#endif
+
+#ifndef mhd_DEBUG_SUSPEND_RESUME
+/* Use debug-print for suspending and resuming of the requests */
+/* # define mhd_DEBUG_SUSPEND_RESUME 1 */
+#endif
+
+#ifndef mhd_DEBUG_CONN_ADD_CLOSE
+/* Use debug-print for adding and closing of the connections */
+/* # define mhd_DEBUG_CONN_ADD_CLOSE 1 */
+#endif
+
#ifndef MHD_AUTH_DIGEST_DEF_TIMEOUT
# define MHD_AUTH_DIGEST_DEF_TIMEOUT 90
#endif /* ! MHD_AUTH_DIGEST_DEF_TIMEOUT */
diff --git a/src/include/d_options.rec b/src/include/d_options.rec
@@ -30,6 +30,21 @@ Comment: Select a sockets watch system call used for internal polling.
Argument1: enum MHD_SockPollSyscall els
Description1: FIXME
+Name: reregister_all
+Value: 45
+Type: enum MHD_Bool
+Comment: Instruct MHD to register all sockets every processing round.
++
++ By default (this options is not enabled) every processing round (every time
++ when #MHD_daemon_event_update() is called) MHD calls
++ #MHD_SocketRegistrationUpdateCallback only for the new sockets, for
++ the removed sockets and for the updated sockets.
++ Some sockets are registered when #MHD_daemon_start() is called.
++
++ If this options is enabled, then #MHD_SocketRegistrationUpdateCallback is
++ called for every socket each processing round. No sockets are registered when
++ the daemon is being started.
+
Name: log_callback
Value: 60
Comment: Set a callback to use for logging
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h
@@ -973,6 +973,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE = 50065
,
/**
+ * Failed to register daemon's FDs (ITC or listening) in the application
+ * (external event) monitoring
+ */
+ MHD_SC_EXT_EVENT_REG_DAEMON_FDS_FAILURE = 50066
+ ,
+ /**
* The select() syscall is not available on this platform or in this MHD
* build.
*/
@@ -1049,7 +1055,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_ITC_CHECK_FAILED = 500102
,
/**
- * System reported error conditions on the ITC FD..
+ * System reported error conditions on the ITC FD.
*/
MHD_SC_ITC_STATUS_ERROR = 500104
,
@@ -1288,7 +1294,15 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
/**
* Unexpected reasons for thread stop
*/
- MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED = 50350
+ 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
@@ -1429,6 +1443,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60011
,
/**
+ * The option provided or function called can be used only with "external
+ * events" modes.
+ */
+ MHD_SC_EXTERNAL_EVENT_ONLY = 60012
+ ,
+ /**
* MHD is closing a connection because the application
* logic to generate the response data failed.
*/
@@ -1654,6 +1674,27 @@ 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
+ ,
+ /**
+ * The application called #MHD_daemon_event_update() with broken data
+ */
+ MHD_SC_EXTR_EVENT_BROKEN_DATA = 60250
+ ,
+ /**
+ * The application called #MHD_daemon_event_update() with status that
+ * has not been requested
+ */
+ MHD_SC_EXTR_EVENT_UNEXPECTED_STATUS = 60251
};
/**
@@ -2574,10 +2615,10 @@ MHD_FN_PAR_NONNULL_ALL_;
/**
* The network status of the socket.
- * When set by MHD (by #MHD_SocketRegistrationUpdateCallback and
+ * When set by MHD (by #MHD_SocketRegistrationUpdateCallback or
* similar) it indicates a request to watch for specific socket state:
- * readiness for receiving the data, readiness for sending the data and/or
- * exception state of the socket.
+ * watch for readiness for receiving the data, watch for readiness for sending
+ * the data and/or watch for exception state of the socket.
* When set by application (and provided for #MHD_daemon_event_update() and
* similar) it must indicate the actual status of the socket.
*
@@ -2656,65 +2697,65 @@ enum MHD_FIXED_ENUM_ MHD_FdState
};
/**
- * Checks whether specific @a state is enabled in @a var
+ * Checks whether specific @a state is enabled/set in the @a var
*/
-#define MHD_FD_STATE_IS_SET(var,state) \
- (MHD_FD_STATE_NONE != \
+#define MHD_FD_STATE_IS_SET(var,state) \
+ (MHD_FD_STATE_NONE != \
(((enum MHD_FdState) (var)) & ((enum MHD_FdState) (state))))
/**
- * Checks whether RECV is enabled in @a var
+ * Checks whether RECV is enabled/set in the @a var
*/
#define MHD_FD_STATE_IS_SET_RECV(var) \
MHD_FD_STATE_IS_SET ((var),MHD_FD_STATE_RECV)
/**
- * Checks whether SEND is enabled in @a var
+ * Checks whether SEND is enabled/set in the @a var
*/
#define MHD_FD_STATE_IS_SET_SEND(var) \
MHD_FD_STATE_IS_SET ((var),MHD_FD_STATE_SEND)
/**
- * Checks whether EXCEPT is enabled in @a var
+ * Checks whether EXCEPT is enabled/set in the @a var
*/
#define MHD_FD_STATE_IS_SET_EXCEPT(var) \
MHD_FD_STATE_IS_SET ((var),MHD_FD_STATE_EXCEPT)
/**
- * Enable specific @a state in @a var
+ * Set/enable specific @a state in the @a var
*/
#define MHD_FD_STATE_SET(var,state) \
(var) = (enum MHD_FdState) ((var) | (state))
/**
- * Enable RECV state in @a var
+ * Set/enable RECV state in the @a var
*/
#define MHD_FD_STATE_SET_RECV(var) MHD_FD_STATE_SET ((var),MHD_FD_STATE_RECV)
/**
- * Enable SEND state in @a var
+ * Set/enable SEND state in the @a var
*/
#define MHD_FD_STATE_SET_SEND(var) MHD_FD_STATE_SET ((var),MHD_FD_STATE_SEND)
/**
- * Enable EXCEPT state in @a var
+ * Set/enable EXCEPT state in the @a var
*/
#define MHD_FD_STATE_SET_EXCEPT(var) \
MHD_FD_STATE_SET ((var),MHD_FD_STATE_EXCEPT)
/**
- * Clear/disable specific @a state in @a var
+ * Clear/disable specific @a state in the @a var
*/
#define MHD_FD_STATE_CLEAR(var,state) \
(var) = (enum MHD_FdState) ((var) & (((enum MHD_FdState))(~state)))
/**
- * Clear/disable RECV state in @a var
+ * Clear/disable RECV state in the @a var
*/
#define MHD_FD_STATE_CLEAR_RECV(var) \
MHD_FD_STATE_CLEAR ((var),MHD_FD_STATE_RECV)
/**
- * Clear/disable SEND state in @a var
+ * Clear/disable SEND state in the @a var
*/
#define MHD_FD_STATE_CLEAR_SEND(var) \
MHD_FD_STATE_CLEAR ((var),MHD_FD_STATE_SEND)
/**
- * Clear/disable EXCEPT state in @a var
+ * Clear/disable EXCEPT state in the @a var
*/
#define MHD_FD_STATE_CLEAR_EXCEPT(var) \
MHD_FD_STATE_CLEAR ((var),MHD_FD_STATE_EXCEPT)
@@ -2750,8 +2791,12 @@ struct MHD_EventUpdateContext;
* 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
+ * @return must be NULL for the removed (de-registred) sockets,
+ * for new and updated sockets: NULL in case of error (the connection
+ * will be aborted or daemon failed to start if FD does not belong to
+ * connection)
+ * or the new socket context (opaque for MHD, must be non-NULL)
+ * @sa #MHD_D_OPTION_REREGISTER_ALL
* @ingroup event
*/
typedef MHD_APP_SOCKET_CNTX_TYPE *
@@ -2768,8 +2813,9 @@ typedef MHD_APP_SOCKET_CNTX_TYPE *
* Update the sockets state.
* Must be called for every socket that got state updated.
* For #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL() mode
- * should be called for each socket.
- * Available only for daemons stated in
+ * this function must be called for each socket between any two calls of
+ * #MHD_daemon_process_reg_events() function.
+ * Available only for daemons started in
* #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL or
* #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_EDGE modes.
* @param daemon the daemon handle
@@ -2787,21 +2833,28 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2);
/**
- * Perform sockets registration, process registered network events.
+ * Perform all daemon activities based on FDs events provided earlier by
+ * application via #MHD_daemon_event_update().
*
- * This function first processes all registered (by MHD_daemon_event_update())
- * network events (if any) and then calls #MHD_SocketRegistrationUpdateCallback
+ * This function accepts new connections (if any), performs HTTP communications
+ * on all active connections, closes connections as needed and performs FDs
+ * registration updates by calling #MHD_SocketRegistrationUpdateCallback
* callback for every socket that needs to be added/updated/removed.
*
- * Available only for daemons stated in #MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL or
+ * Available only for daemons started in #MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL or
* #MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE modes.
*
+ * When used in #MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL mode, application must
+ * provide all updates by calling #MHD_daemon_event_update() for every
+ * registered FD between any two calls of this function.
+ *
* @param daemon the daemon handle
* @param[out] next_max_wait the optional pointer to receive the next maximum
* wait time in microseconds to be used for sockets
* polling function, can be NULL
* @return #MHD_SC_OK on success,
* error code otherwise
+ * @sa #MHD_D_OPTION_REREGISTER_ALL
* @ingroup event
*/
MHD_EXTERN_ enum MHD_StatusCode
@@ -2829,19 +2882,27 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_WorkMode
,
/**
* Work mode with an external event loop with level triggers.
- * Application uses #MHD_SocketRegistrationUpdateCallback, level triggered
- * sockets polling (like select() or poll()) and #MHD_daemon_event_update().
+ * MHD provides registration of all FDs to be monitored by using
+ * #MHD_SocketRegistrationUpdateCallback, application performs level triggered
+ * FDs polling (like select() or poll()), calls function
+ * #MHD_daemon_event_update() for every registered FD and then calls main
+ * function MHD_daemon_process_reg_events() to process the data.
* Use helper macro #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL() to enable
* this mode.
+ * @sa #MHD_D_OPTION_REREGISTER_ALL
*/
MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL = 8
,
/**
* Work mode with an external event loop with edge triggers.
- * Application uses #MHD_SocketRegistrationUpdateCallback, edge triggered
- * sockets polling (like epoll with EPOLLET) and #MHD_daemon_event_update().
+ * MHD provides registration of all FDs to be monitored by using
+ * #MHD_SocketRegistrationUpdateCallback, application performs edge triggered
+ * sockets polling (like epoll with EPOLLET), calls function
+ * #MHD_daemon_event_update() for FDs with updated states and then calls main
+ * function MHD_daemon_process_reg_events() to process the data.
* Use helper macro #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_EDGE() to enable
* this mode.
+ * @sa #MHD_D_OPTION_REREGISTER_ALL
*/
MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE = 9
,
@@ -3898,12 +3959,19 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
,
/**
* The request was aborted due to the application failed to provide a valid
- * resonse.
+ * response.
* @ingroup request
*/
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
*/
@@ -4011,6 +4079,25 @@ MHD_D_OPTION_POLL_SYSCALL (
);
/**
+ * Instruct MHD to register all sockets every processing round.
+ *
+By default (this options is not enabled) every processing round (every time
+ * when #MHD_daemon_event_update() is called) MHD calls
+ * #MHD_SocketRegistrationUpdateCallback only for the new sockets, for
+ * the removed sockets and for the updated sockets.
+ * Some sockets are registered when #MHD_daemon_start() is called.
+ *
+If this options is enabled, then #MHD_SocketRegistrationUpdateCallback is
+ * called for every socket each processing round. No sockets are registered when
+ * the daemon is being started.
+ * @param value the value of the parameter * @return structure with the requested setting
+ */
+struct MHD_DaemonOptionAndValue
+MHD_D_OPTION_REREGISTER_ALL (
+ enum MHD_Bool value
+ );
+
+/**
* Set a callback to use for logging
* @param log_cb the callback to use for logging,
* NULL to disable logging.
@@ -5309,7 +5396,9 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3);
* @param kind what kind of value are we looking for
* @param key the name of the value looking for (used for case-insensetive
match), empty to lookup 'trailing' value without a key
- * @return NULL if no such item was found
+ * @return NULL if no such item was found,
+ * non-NULL if item found (the inner pointer to string can be NULL
+ * if item found, but has no value)
* @ingroup request
*/
MHD_EXTERN_ const struct MHD_StringNullable *
@@ -5626,17 +5715,11 @@ MHD_FN_CONST_;
/**
- * Resume handling of network data for suspended request. It is
- * safe to resume a suspended request at any time. Calling this
- * function on a request that was not previously suspended will
+ * Resume handling of network data for suspended request.
+ * It is safe to resume a suspended request at any time.
+ * Calling this function on a request that was not previously suspended will
* result in undefined behaviour.
*
- * If you are using this function in ``external'' select mode, you must make
- * sure to run #MHD_daemon_process_blocking() afterwards (as otherwise the
- * change may not be reflected in the set returned to your
- * MHD_SocketRegistrationUpdateCallback and you may end up with a request
- * that is stuck until the next network activity.
- *
* @param[in,out] request the request to resume
*/
MHD_EXTERN_ void
diff --git a/src/include/microhttpd2_generated_daemon_options.h b/src/include/microhttpd2_generated_daemon_options.h
@@ -24,6 +24,22 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_DaemonOption
,
/**
+ * Instruct MHD to register all sockets every processing round.
+ *
+By default (this options is not enabled) every processing round (every time
+ * when #MHD_daemon_event_update() is called) MHD calls
+ * #MHD_SocketRegistrationUpdateCallback only for the new sockets, for
+ * the removed sockets and for the updated sockets.
+ * Some sockets are registered when #MHD_daemon_start() is called.
+ *
+If this options is enabled, then #MHD_SocketRegistrationUpdateCallback is
+ * called for every socket each processing round. No sockets are registered when
+ * the daemon is being started.
+ */
+ MHD_D_O_REREGISTER_ALL = 45
+ ,
+
+ /**
* Set a callback to use for logging
*/
MHD_D_O_LOG_CALLBACK = 60
@@ -604,6 +620,11 @@ union MHD_DaemonOptionValue
enum MHD_SockPollSyscall poll_syscall;
/**
+ * Value for #MHD_D_O_REREGISTER_ALL.
+ */
+ enum MHD_Bool reregister_all;
+
+ /**
* Value for #MHD_D_O_LOG_CALLBACK.
* the callback to use for logging,
* NULL to disable logging.
@@ -869,6 +890,28 @@ struct MHD_DaemonOptionAndValue
} \
MHD_RESTORE_WARN_COMPOUND_LITERALS_
/**
+ * Instruct MHD to register all sockets every processing round.
+ *
+By default (this options is not enabled) every processing round (every time
+ * when #MHD_daemon_event_update() is called) MHD calls
+ * #MHD_SocketRegistrationUpdateCallback only for the new sockets, for
+ * the removed sockets and for the updated sockets.
+ * Some sockets are registered when #MHD_daemon_start() is called.
+ *
+If this options is enabled, then #MHD_SocketRegistrationUpdateCallback is
+ * called for every socket each processing round. No sockets are registered when
+ * the daemon is being started.
+ * @param value the value of the parameter * @return structure with the requested setting
+ */
+# define MHD_D_OPTION_REREGISTER_ALL(value) \
+ MHD_NOWARN_COMPOUND_LITERALS_ \
+ (const struct MHD_DaemonOptionAndValue) \
+ { \
+ .opt = MHD_D_O_REREGISTER_ALL, \
+ .val.reregister_all = (value) \
+ } \
+ MHD_RESTORE_WARN_COMPOUND_LITERALS_
+/**
* Set a callback to use for logging
* @param log_cb the callback to use for logging,
* NULL to disable logging.
@@ -1530,6 +1573,34 @@ MHD_D_OPTION_POLL_SYSCALL (
/**
+ * Instruct MHD to register all sockets every processing round.
+ *
+By default (this options is not enabled) every processing round (every time
+ * when #MHD_daemon_event_update() is called) MHD calls
+ * #MHD_SocketRegistrationUpdateCallback only for the new sockets, for
+ * the removed sockets and for the updated sockets.
+ * Some sockets are registered when #MHD_daemon_start() is called.
+ *
+If this options is enabled, then #MHD_SocketRegistrationUpdateCallback is
+ * called for every socket each processing round. No sockets are registered when
+ * the daemon is being started.
+ * @param value the value of the parameter * @return structure with the requested setting
+ */
+static MHD_INLINE struct MHD_DaemonOptionAndValue
+MHD_D_OPTION_REREGISTER_ALL (
+ enum MHD_Bool value
+ )
+{
+ struct MHD_DaemonOptionAndValue opt_val;
+
+ opt_val.opt = MHD_D_O_REREGISTER_ALL;
+ opt_val.val.reregister_all = (value); \
+
+ return opt_val;
+}
+
+
+/**
* Set a callback to use for logging
* @param log_cb the callback to use for logging,
* NULL to disable logging.
diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in
@@ -687,7 +687,9 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3);
* @param kind what kind of value are we looking for
* @param key the name of the value looking for (used for case-insensetive
match), empty to lookup 'trailing' value without a key
- * @return NULL if no such item was found
+ * @return NULL if no such item was found,
+ * non-NULL if item found (the inner pointer to string can be NULL
+ * if item found, but has no value)
* @ingroup request
*/
MHD_EXTERN_ const struct MHD_StringNullable *
@@ -1004,17 +1006,11 @@ MHD_FN_CONST_;
/**
- * Resume handling of network data for suspended request. It is
- * safe to resume a suspended request at any time. Calling this
- * function on a request that was not previously suspended will
+ * Resume handling of network data for suspended request.
+ * It is safe to resume a suspended request at any time.
+ * Calling this function on a request that was not previously suspended will
* result in undefined behaviour.
*
- * If you are using this function in ``external'' select mode, you must make
- * sure to run #MHD_daemon_process_blocking() afterwards (as otherwise the
- * change may not be reflected in the set returned to your
- * MHD_SocketRegistrationUpdateCallback and you may end up with a request
- * that is stuck until the next network activity.
- *
* @param[in,out] request the request to resume
*/
MHD_EXTERN_ void
diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in
@@ -973,6 +973,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE = 50065
,
/**
+ * Failed to register daemon's FDs (ITC or listening) in the application
+ * (external event) monitoring
+ */
+ MHD_SC_EXT_EVENT_REG_DAEMON_FDS_FAILURE = 50066
+ ,
+ /**
* The select() syscall is not available on this platform or in this MHD
* build.
*/
@@ -1049,7 +1055,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_ITC_CHECK_FAILED = 500102
,
/**
- * System reported error conditions on the ITC FD..
+ * System reported error conditions on the ITC FD.
*/
MHD_SC_ITC_STATUS_ERROR = 500104
,
@@ -1288,7 +1294,15 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
/**
* Unexpected reasons for thread stop
*/
- MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED = 50350
+ 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
@@ -1429,6 +1443,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60011
,
/**
+ * The option provided or function called can be used only with "external
+ * events" modes.
+ */
+ MHD_SC_EXTERNAL_EVENT_ONLY = 60012
+ ,
+ /**
* MHD is closing a connection because the application
* logic to generate the response data failed.
*/
@@ -1654,6 +1674,27 @@ 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
+ ,
+ /**
+ * The application called #MHD_daemon_event_update() with broken data
+ */
+ MHD_SC_EXTR_EVENT_BROKEN_DATA = 60250
+ ,
+ /**
+ * The application called #MHD_daemon_event_update() with status that
+ * has not been requested
+ */
+ MHD_SC_EXTR_EVENT_UNEXPECTED_STATUS = 60251
};
/**
@@ -2574,10 +2615,10 @@ MHD_FN_PAR_NONNULL_ALL_;
/**
* The network status of the socket.
- * When set by MHD (by #MHD_SocketRegistrationUpdateCallback and
+ * When set by MHD (by #MHD_SocketRegistrationUpdateCallback or
* similar) it indicates a request to watch for specific socket state:
- * readiness for receiving the data, readiness for sending the data and/or
- * exception state of the socket.
+ * watch for readiness for receiving the data, watch for readiness for sending
+ * the data and/or watch for exception state of the socket.
* When set by application (and provided for #MHD_daemon_event_update() and
* similar) it must indicate the actual status of the socket.
*
@@ -2656,65 +2697,65 @@ enum MHD_FIXED_ENUM_ MHD_FdState
};
/**
- * Checks whether specific @a state is enabled in @a var
+ * Checks whether specific @a state is enabled/set in the @a var
*/
-#define MHD_FD_STATE_IS_SET(var,state) \
- (MHD_FD_STATE_NONE != \
+#define MHD_FD_STATE_IS_SET(var,state) \
+ (MHD_FD_STATE_NONE != \
(((enum MHD_FdState) (var)) & ((enum MHD_FdState) (state))))
/**
- * Checks whether RECV is enabled in @a var
+ * Checks whether RECV is enabled/set in the @a var
*/
#define MHD_FD_STATE_IS_SET_RECV(var) \
MHD_FD_STATE_IS_SET ((var),MHD_FD_STATE_RECV)
/**
- * Checks whether SEND is enabled in @a var
+ * Checks whether SEND is enabled/set in the @a var
*/
#define MHD_FD_STATE_IS_SET_SEND(var) \
MHD_FD_STATE_IS_SET ((var),MHD_FD_STATE_SEND)
/**
- * Checks whether EXCEPT is enabled in @a var
+ * Checks whether EXCEPT is enabled/set in the @a var
*/
#define MHD_FD_STATE_IS_SET_EXCEPT(var) \
MHD_FD_STATE_IS_SET ((var),MHD_FD_STATE_EXCEPT)
/**
- * Enable specific @a state in @a var
+ * Set/enable specific @a state in the @a var
*/
#define MHD_FD_STATE_SET(var,state) \
(var) = (enum MHD_FdState) ((var) | (state))
/**
- * Enable RECV state in @a var
+ * Set/enable RECV state in the @a var
*/
#define MHD_FD_STATE_SET_RECV(var) MHD_FD_STATE_SET ((var),MHD_FD_STATE_RECV)
/**
- * Enable SEND state in @a var
+ * Set/enable SEND state in the @a var
*/
#define MHD_FD_STATE_SET_SEND(var) MHD_FD_STATE_SET ((var),MHD_FD_STATE_SEND)
/**
- * Enable EXCEPT state in @a var
+ * Set/enable EXCEPT state in the @a var
*/
#define MHD_FD_STATE_SET_EXCEPT(var) \
MHD_FD_STATE_SET ((var),MHD_FD_STATE_EXCEPT)
/**
- * Clear/disable specific @a state in @a var
+ * Clear/disable specific @a state in the @a var
*/
#define MHD_FD_STATE_CLEAR(var,state) \
(var) = (enum MHD_FdState) ((var) & (((enum MHD_FdState))(~state)))
/**
- * Clear/disable RECV state in @a var
+ * Clear/disable RECV state in the @a var
*/
#define MHD_FD_STATE_CLEAR_RECV(var) \
MHD_FD_STATE_CLEAR ((var),MHD_FD_STATE_RECV)
/**
- * Clear/disable SEND state in @a var
+ * Clear/disable SEND state in the @a var
*/
#define MHD_FD_STATE_CLEAR_SEND(var) \
MHD_FD_STATE_CLEAR ((var),MHD_FD_STATE_SEND)
/**
- * Clear/disable EXCEPT state in @a var
+ * Clear/disable EXCEPT state in the @a var
*/
#define MHD_FD_STATE_CLEAR_EXCEPT(var) \
MHD_FD_STATE_CLEAR ((var),MHD_FD_STATE_EXCEPT)
@@ -2750,8 +2791,12 @@ struct MHD_EventUpdateContext;
* 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
+ * @return must be NULL for the removed (de-registred) sockets,
+ * for new and updated sockets: NULL in case of error (the connection
+ * will be aborted or daemon failed to start if FD does not belong to
+ * connection)
+ * or the new socket context (opaque for MHD, must be non-NULL)
+ * @sa #MHD_D_OPTION_REREGISTER_ALL
* @ingroup event
*/
typedef MHD_APP_SOCKET_CNTX_TYPE *
@@ -2768,8 +2813,9 @@ typedef MHD_APP_SOCKET_CNTX_TYPE *
* Update the sockets state.
* Must be called for every socket that got state updated.
* For #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL() mode
- * should be called for each socket.
- * Available only for daemons stated in
+ * this function must be called for each socket between any two calls of
+ * #MHD_daemon_process_reg_events() function.
+ * Available only for daemons started in
* #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL or
* #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_EDGE modes.
* @param daemon the daemon handle
@@ -2787,21 +2833,28 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2);
/**
- * Perform sockets registration, process registered network events.
+ * Perform all daemon activities based on FDs events provided earlier by
+ * application via #MHD_daemon_event_update().
*
- * This function first processes all registered (by MHD_daemon_event_update())
- * network events (if any) and then calls #MHD_SocketRegistrationUpdateCallback
+ * This function accepts new connections (if any), performs HTTP communications
+ * on all active connections, closes connections as needed and performs FDs
+ * registration updates by calling #MHD_SocketRegistrationUpdateCallback
* callback for every socket that needs to be added/updated/removed.
*
- * Available only for daemons stated in #MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL or
+ * Available only for daemons started in #MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL or
* #MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE modes.
*
+ * When used in #MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL mode, application must
+ * provide all updates by calling #MHD_daemon_event_update() for every
+ * registered FD between any two calls of this function.
+ *
* @param daemon the daemon handle
* @param[out] next_max_wait the optional pointer to receive the next maximum
* wait time in microseconds to be used for sockets
* polling function, can be NULL
* @return #MHD_SC_OK on success,
* error code otherwise
+ * @sa #MHD_D_OPTION_REREGISTER_ALL
* @ingroup event
*/
MHD_EXTERN_ enum MHD_StatusCode
@@ -2829,19 +2882,27 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_WorkMode
,
/**
* Work mode with an external event loop with level triggers.
- * Application uses #MHD_SocketRegistrationUpdateCallback, level triggered
- * sockets polling (like select() or poll()) and #MHD_daemon_event_update().
+ * MHD provides registration of all FDs to be monitored by using
+ * #MHD_SocketRegistrationUpdateCallback, application performs level triggered
+ * FDs polling (like select() or poll()), calls function
+ * #MHD_daemon_event_update() for every registered FD and then calls main
+ * function MHD_daemon_process_reg_events() to process the data.
* Use helper macro #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL() to enable
* this mode.
+ * @sa #MHD_D_OPTION_REREGISTER_ALL
*/
MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL = 8
,
/**
* Work mode with an external event loop with edge triggers.
- * Application uses #MHD_SocketRegistrationUpdateCallback, edge triggered
- * sockets polling (like epoll with EPOLLET) and #MHD_daemon_event_update().
+ * MHD provides registration of all FDs to be monitored by using
+ * #MHD_SocketRegistrationUpdateCallback, application performs edge triggered
+ * sockets polling (like epoll with EPOLLET), calls function
+ * #MHD_daemon_event_update() for FDs with updated states and then calls main
+ * function MHD_daemon_process_reg_events() to process the data.
* Use helper macro #MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_EDGE() to enable
* this mode.
+ * @sa #MHD_D_OPTION_REREGISTER_ALL
*/
MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE = 9
,
@@ -3898,12 +3959,19 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
,
/**
* The request was aborted due to the application failed to provide a valid
- * resonse.
+ * response.
* @ingroup request
*/
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
@@ -46,12 +46,14 @@ libmicrohttpd2_la_SOURCES = \
mhd_buffer.h \
mhd_limits.h \
mhd_iovec.h \
+ mhd_dbg_print.h \
mhd_panic.c mhd_panic.h \
mhd_lib_init.c mhd_lib_init_impl.h mhd_lib_init.h \
mhd_lib_init_auto.h \
lib_get_info.c \
mhd_dlinked_list.h \
mhd_conn_socket.h mhd_connection.h \
+ mhd_stream.h \
mhd_locks.h \
mhd_itc.c mhd_itc.h mhd_itc_types.h \
mhd_threads.c mhd_threads.h sys_thread_entry_type.h \
@@ -76,6 +78,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 \
@@ -83,6 +86,7 @@ libmicrohttpd2_la_SOURCES = \
conn_get_info.c \
request_funcs.c request_funcs.h \
request_get_value.c request_get_value.h \
+ request_resume.c \
respond_with_error.c respond_with_error.h \
request_get_info.c \
response_from.c response_from.h \
diff --git a/src/mhd2/conn_data_process.c b/src/mhd2/conn_data_process.c
@@ -58,6 +58,18 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
bool has_sock_err;
bool data_processed;
+ data_processed = false;
+
+ mhd_assert (! c->suspended);
+ if (c->resuming)
+ {
+ /* Fully resume the connection + call app callbacks for the data */
+ if (! mhd_conn_process_data (c))
+ return false;
+
+ data_processed = true;
+ }
+
#ifdef MHD_SUPPORT_HTTPS
if (mhd_C_HAS_TLS (c))
{
@@ -86,7 +98,6 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
(0 != (MHD_EVENT_LOOP_INFO_SEND & c->event_loop_info)));
has_sock_err =
(0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk.ready));
- data_processed = false;
if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
{
diff --git a/src/mhd2/conn_mark_ready.h b/src/mhd2/conn_mark_ready.h
@@ -103,7 +103,7 @@ mhd_conn_mark_unready (struct MHD_Connection *restrict c,
*/
MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void
mhd_conn_mark_ready_update3 (struct MHD_Connection *restrict c,
- unsigned int force_ready,
+ bool force_ready,
struct MHD_Daemon *restrict d)
{
if (force_ready ||
diff --git a/src/mhd2/daemon_add_conn.c b/src/mhd2/daemon_add_conn.c
@@ -43,6 +43,9 @@
#include "sys_sockets_headers.h"
#include "sys_ip_headers.h"
+#ifdef mhd_DEBUG_CONN_ADD_CLOSE
+# include <stdio.h>
+#endif /* mhd_DEBUG_CONN_ADD_CLOSE */
#include <string.h>
#ifdef MHD_SUPPORT_EPOLL
# include <sys/epoll.h>
@@ -54,6 +57,7 @@
#include "mhd_sockets_funcs.h"
#include "mhd_panic.h"
+#include "mhd_dbg_print.h"
#include "mhd_daemon.h"
#include "mhd_connection.h"
@@ -77,6 +81,11 @@
/**
* Set initial internal states for the connection to start reading and
* processing incoming data.
+ * This sets:
+ * + data processing stage
+ * + stream request and reply initial data
+ * + connection read and write buffers
+ *
* @param c the connection to process
*/
static void
@@ -89,14 +98,28 @@ connection_set_initial_state (struct MHD_Connection *restrict c)
c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE;
c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
- memset (&c->rq, 0, sizeof(c->rq));
- memset (&c->rp, 0, sizeof(c->rp));
+ // TODO: move request reset to special function
+ memset (&(c->rq), 0, sizeof(c->rq));
+ // TODO: move reply reset to special function
+ memset (&(c->rp), 0, sizeof(c->rp));
#ifndef HAVE_NULL_PTR_ALL_ZEROS
- mhd_DLINKEDL_INIT_LINKS (c, all_conn);
- mhd_DLINKEDL_INIT_LINKS (c, proc_ready);
- mhd_DLINKEDL_INIT_LINKS (c, by_timeout);
- // TODO: set all other pointers manually
+ // TODO: move request reset to special function
+ mhd_DLINKEDL_INIT_LIST (&(c->rq), fields);
+#ifdef MHD_SUPPORT_POST_PARSER
+ mhd_DLINKEDL_INIT_LIST (&(c->rq), post_fields);
+#endif /* MHD_SUPPORT_POST_PARSER */
+ c->rq.version = NULL;
+ c->rq.url = NULL;
+ c->rq.field_lines.start = NULL;
+ c->rq.app_context = NULL;
+ c->rq.hdrs.rq_line.rq_tgt = NULL;
+ c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
+
+ // TODO: move reply reset to special function
+ c->rp.app_act_ctx.connection = NULL;
+ c->rp.response = NULL;
+ c->rp.resp_iov.iov = NULL;
#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
c->write_buffer = NULL;
@@ -184,6 +207,17 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
}
else
{
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+ mhd_DLINKEDL_INIT_LINKS (c, all_conn);
+ c->extr_event.app_cntx = NULL;
+ mhd_DLINKEDL_INIT_LINKS (c, proc_ready);
+ mhd_DLINKEDL_INIT_LINKS (c, by_timeout);
+# ifdef MHD_SUPPORT_UPGRADE
+ c->upgr.c = NULL;
+ mhd_DLINKEDL_INIT_LINKS (c, upgr_cleanup);
+# endif /* MHD_SUPPORT_UPGRADE */
+ c->socket_context = NULL;
+#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
#ifdef MHD_SUPPORT_HTTPS
if (0 != tls_data_size)
c->tls = (struct mhd_TlsConnData *) (c + 1);
@@ -280,6 +314,7 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
/**
+ * Internal (inner) function.
* Finally insert the new connection to the list of connections
* served by the daemon and start processing.
* @remark To be called only from thread that process
@@ -287,11 +322,12 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
*
* @param daemon daemon that manages the connection
* @param connection the newly created connection
- * @return #MHD_YES on success, #MHD_NO on error
+ * @return #MHD_SC_OK on success,
+ * error code otherwise
*/
static enum MHD_StatusCode
-new_connection_process_ (struct MHD_Daemon *restrict daemon,
- struct MHD_Connection *restrict connection)
+new_connection_process_inner (struct MHD_Daemon *restrict daemon,
+ struct MHD_Connection *restrict connection)
{
enum MHD_StatusCode res;
mhd_assert (connection->daemon == daemon);
@@ -325,7 +361,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
daemon->conns.block_new =
(daemon->conns.count >= daemon->conns.cfg.count_limit);
mhd_DLINKEDL_INS_LAST (&(daemon->conns), connection, all_conn);
- if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != daemon->wmode_int)
+ if (! mhd_D_HAS_THR_PER_CONN (daemon))
mhd_DLINKEDL_INS_FIRST_D (&(daemon->conns.def_timeout), \
connection, by_timeout);
@@ -391,6 +427,11 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
}
else
{
+ mhd_dbg_print_fd_mon_req ("conn", \
+ connection->sk.fd, \
+ true, \
+ true, \
+ false);
if (0) // TODO: implement turbo
{
connection->sk.ready = mhd_SOCKET_NET_STATE_RECV_READY
@@ -409,7 +450,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
mhd_assert (MHD_SC_OK != res);
notify_app_conn (daemon, connection, true);
- if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != daemon->wmode_int)
+ if (! mhd_D_HAS_THR_PER_CONN (daemon))
mhd_DLINKEDL_DEL_D (&(daemon->conns.def_timeout), \
connection, by_timeout);
@@ -438,6 +479,41 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
/**
+ * Finally insert the new connection to the list of connections
+ * served by the daemon and start processing.
+ * @remark To be called only from thread that process
+ * daemon's select()/poll()/etc.
+ *
+ * @param daemon daemon that manages the connection
+ * @param connection the newly created connection
+ * @return #MHD_SC_OK on success,
+ * error code otherwise
+ */
+static enum MHD_StatusCode
+new_connection_process_ (struct MHD_Daemon *restrict daemon,
+ struct MHD_Connection *restrict connection)
+{
+ enum MHD_StatusCode res;
+
+ res = new_connection_process_inner (daemon,
+ connection);
+#ifdef mhd_DEBUG_CONN_ADD_CLOSE
+ if (MHD_SC_OK == res)
+ fprintf (stderr,
+ "&&& Added new connection, FD: %2llu\n",
+ (unsigned long long) connection->sk.fd);
+ else
+ fprintf (stderr,
+ "&&& Failed add connection, FD: %2llu -> %u\n",
+ (unsigned long long) connection->sk.fd,
+ (unsigned int) res);
+#endif /* mhd_DEBUG_CONN_ADD_CLOSE */
+
+ return res;
+}
+
+
+/**
* The given client socket will be managed (and closed!) by MHD after
* this call and must no longer be used directly by the application
* afterwards.
@@ -777,9 +853,9 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
bool sk_spipe_supprs;
bool sk_cloexec;
enum mhd_Tristate sk_non_ip;
-#if defined(_DEBUG) && defined (USE_ACCEPT4)
+#if defined(_DEBUG) && defined (mhd_USE_ACCEPT4)
const bool use_accept4 = ! daemon->dbg.avoid_accept4;
-#elif defined (USE_ACCEPT4)
+#elif defined (mhd_USE_ACCEPT4)
static const bool use_accept4 = true;
#else /* ! USE_ACCEPT4 && ! _DEBUG */
static const bool use_accept4 = false;
@@ -793,6 +869,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
fd = daemon->net.listen.fd;
mhd_assert (MHD_INVALID_SOCKET != fd);
+ mhd_assert (! daemon->net.listen.is_broken);
addrlen = (socklen_t) sizeof (addrstorage);
memset (&addrstorage,
@@ -808,44 +885,47 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
sk_cloexec = false;
s = MHD_INVALID_SOCKET;
-#ifdef USE_ACCEPT4
- if (use_accept4 &&
- (MHD_INVALID_SOCKET !=
- (s = accept4 (fd,
- (struct sockaddr *) &addrstorage,
- &addrlen,
- SOCK_CLOEXEC_OR_ZERO | SOCK_NONBLOCK_OR_ZERO
- | SOCK_NOSIGPIPE_OR_ZERO))))
+#ifdef mhd_USE_ACCEPT4
+ if (use_accept4)
{
- sk_nonbl = (SOCK_NONBLOCK_OR_ZERO != 0);
+ s = accept4 (fd,
+ (struct sockaddr *) &addrstorage,
+ &addrlen,
+ mhd_SOCK_CLOEXEC | mhd_SOCK_NONBLOCK | mhd_SOCK_NOSIGPIPE);
+ if (MHD_INVALID_SOCKET != s)
+ {
+ sk_nonbl = (mhd_SOCK_NONBLOCK != 0);
#ifndef MHD_SOCKETS_KIND_WINSOCK
- sk_spipe_supprs = (SOCK_NOSIGPIPE_OR_ZERO != 0);
+ sk_spipe_supprs = (mhd_SOCK_NOSIGPIPE != 0);
#else /* MHD_SOCKETS_KIND_WINSOCK */
- sk_spipe_supprs = true; /* Nothing to suppress on W32 */
+ sk_spipe_supprs = true; /* Nothing to suppress on W32 */
#endif /* MHD_SOCKETS_KIND_WINSOCK */
- sk_cloexec = (SOCK_CLOEXEC_OR_ZERO != 0);
+ sk_cloexec = (mhd_SOCK_CLOEXEC != 0);
+ }
}
-#endif /* USE_ACCEPT4 */
-#if defined(_DEBUG) || ! defined(USE_ACCEPT4)
- if (! use_accept4 &&
- (MHD_INVALID_SOCKET !=
- (s = accept (fd,
- (struct sockaddr *) &addrstorage,
- &addrlen))))
+#endif /* mhd_USE_ACCEPT4 */
+#if ! defined(mhd_USE_ACCEPT4) || defined(_DEBUG)
+ if (! use_accept4)
{
-#ifdef MHD_ACCEPT_INHERIT_NONBLOCK
- sk_nonbl = daemon->listen_nonblk;
-#else /* ! MHD_ACCEPT_INHERIT_NONBLOCK */
- sk_nonbl = false;
-#endif /* ! MHD_ACCEPT_INHERIT_NONBLOCK */
+ s = accept (fd,
+ (struct sockaddr *) &addrstorage,
+ &addrlen);
+ if (MHD_INVALID_SOCKET != s)
+ {
+#ifdef MHD_ACCEPTED_INHERITS_NONBLOCK
+ sk_nonbl = daemon->net.listen.non_block;
+#else /* ! MHD_ACCEPTED_INHERITS_NONBLOCK */
+ sk_nonbl = false;
+#endif /* ! MHD_ACCEPTED_INHERITS_NONBLOCK */
#ifndef MHD_SOCKETS_KIND_WINSOCK
- sk_spipe_supprs = false;
+ sk_spipe_supprs = false;
#else /* MHD_SOCKETS_KIND_WINSOCK */
- sk_spipe_supprs = true; /* Nothing to suppress on W32 */
+ sk_spipe_supprs = true; /* Nothing to suppress on W32 */
#endif /* MHD_SOCKETS_KIND_WINSOCK */
- sk_cloexec = false;
+ sk_cloexec = false;
+ }
}
-#endif /* _DEBUG || !USE_ACCEPT4 */
+#endif /* !mhd_USE_ACCEPT4 || _DEBUG */
if (MHD_INVALID_SOCKET == s)
{ /* This could be a common occurrence with multiple worker threads */
@@ -934,20 +1014,22 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
#endif /* HAVE_INET6 */
}
- if (! sk_nonbl && ! mhd_socket_nonblocking (s))
- {
- mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \
- "Failed to set nonblocking mode on incoming connection " \
- "socket.");
+ if (! sk_nonbl)
+ { /* Was not set automatically */
+ sk_nonbl = mhd_socket_nonblocking (s);
+ if (! sk_nonbl)
+ mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \
+ "Failed to set nonblocking mode on "
+ "new connection socket.");
}
- else
- sk_nonbl = true;
- if (! sk_cloexec && ! mhd_socket_noninheritable (s))
- {
- mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \
- "Failed to set non-inheritable mode on incoming connection " \
- "socket.");
+ if (! sk_cloexec)
+ { /* Was not set automatically */
+ sk_cloexec = mhd_socket_noninheritable (s);
+ if (! sk_cloexec)
+ mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \
+ "Failed to set non-inheritable mode on "
+ "new connection socket.");
}
#if defined(mhd_socket_nosignal)
@@ -962,7 +1044,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
if (! daemon->sigpipe_blocked)
{
(void) MHD_socket_close_ (s);
- return MHD_NO;
+ return mhd_DAEMON_ACCEPT_FAILED;
}
#endif /* HAVE_DCLR_MSG_NOSIGNAL */
}
@@ -1060,6 +1142,11 @@ mhd_conn_close_final (struct MHD_Connection *restrict c)
if (NULL != c->sk.addr.data)
free (c->sk.addr.data);
mhd_socket_close (c->sk.fd);
+#ifdef mhd_DEBUG_CONN_ADD_CLOSE
+ fprintf (stderr,
+ "&&& Closed connection, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_CONN_ADD_CLOSE */
free (c);
}
diff --git a/src/mhd2/daemon_event_update.c b/src/mhd2/daemon_event_update.c
@@ -0,0 +1,174 @@
+/*
+ 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"
+
+#ifdef mhd_DEBUG_POLLING_FDS
+# include "mhd_itc.h"
+# include <stdio.h>
+#endif /* mhd_DEBUG_POLLING_FDS */
+
+#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;
+
+#ifdef mhd_DEBUG_POLLING_FDS
+ if (1)
+ {
+ char state_str[] = "x:x:x";
+ state_str[0] = MHD_FD_STATE_IS_SET_RECV (fd_current_state) ? 'R' : '-';
+ state_str[2] = MHD_FD_STATE_IS_SET_SEND (fd_current_state) ? 'W' : '-';
+ state_str[4] = MHD_FD_STATE_IS_SET_EXCEPT (fd_current_state) ? 'E' : '-';
+
+ switch ((mhd_SockRelMarker) ecb_cntx)
+ {
+ case mhd_SOCKET_REL_MARKER_EMPTY:
+ fprintf (stderr,
+ "### MHD_daemon_event_update(daemon, [unknown], %s)\n",
+ state_str);
+ break;
+ case mhd_SOCKET_REL_MARKER_ITC:
+ fprintf (stderr,
+ "### MHD_daemon_event_update(daemon, [ITC: %2llu], %s)\n",
+ (unsigned long long) mhd_itc_r_fd (daemon->threading.itc),
+ state_str);
+ break;
+ case mhd_SOCKET_REL_MARKER_LISTEN:
+ fprintf (stderr,
+ "### MHD_daemon_event_update(daemon, [lstn: %2llu], %s)\n",
+ (unsigned long long) daemon->net.listen.fd,
+ state_str);
+ break;
+ default:
+ fprintf (stderr,
+ "### MHD_daemon_event_update(daemon, [conn: %2llu], %s)\n",
+ (unsigned long long)
+ (((struct MHD_Connection *) ecb_cntx)->sk.fd),
+ state_str);
+ break;
+ }
+ }
+#endif /* mhd_DEBUG_POLLING_FDS */
+
+ broken_app_data = false;
+ unneeded_event = 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)
+ {
+ mhd_LOG_MSG (daemon, \
+ MHD_SC_EXTR_EVENT_BROKEN_DATA, \
+ "MHD_daemon_event_update() is called with broken content " \
+ "data");
+ }
+ else if (unneeded_event)
+ {
+ mhd_LOG_MSG (daemon, \
+ MHD_SC_EXTR_EVENT_BROKEN_DATA, \
+ "MHD_daemon_event_update() is called with status that has " \
+ "not been requested");
+ }
+}
diff --git a/src/mhd2/daemon_funcs.c b/src/mhd2/daemon_funcs.c
@@ -68,16 +68,6 @@ mhd_daemon_trigger_itc (struct MHD_Daemon *restrict d)
#endif /* MHD_SUPPORT_THREADS */
-MHD_NORETURN_ // TODO: implement
-MHD_INTERNAL
-MHD_FN_PAR_NONNULL_ALL_ void
-mhd_daemon_resume_conns (struct MHD_Daemon *restrict d)
-{
- (void) d;
- mhd_assert (0 && "Not implemented yet");
-}
-
-
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
MHD_FN_MUST_CHECK_RESULT_ bool
mhd_daemon_claim_lbuf (struct MHD_Daemon *d,
diff --git a/src/mhd2/daemon_funcs.h b/src/mhd2/daemon_funcs.h
@@ -64,16 +64,6 @@ mhd_daemon_trigger_itc (struct MHD_Daemon *restrict d);
/**
- * Check whether any resuming connections are pending and resume them
- * @param d the daemon to use
- */
-MHD_NORETURN_ // TODO: implement
-MHD_INTERNAL void
-mhd_daemon_resume_conns (struct MHD_Daemon *restrict d)
-MHD_FN_PAR_NONNULL_ALL_;
-
-
-/**
* Request allocation of the large buffer
* @param d the daemon to use
* @param requested_size the requested size of allocation
diff --git a/src/mhd2/daemon_get_info.c b/src/mhd2/daemon_get_info.c
@@ -60,7 +60,8 @@ MHD_daemon_get_info_fixed_sz (
switch (info_type)
{
case MHD_DAEMON_INFO_FIXED_BIND_PORT:
- if (MHD_INVALID_SOCKET == daemon->net.listen.fd)
+ if ((MHD_INVALID_SOCKET == daemon->net.listen.fd)
+ && ! daemon->net.listen.is_broken)
return MHD_SC_INFO_GET_TYPE_NOT_APPLICABLE;
if (mhd_SOCKET_TYPE_UNKNOWN > daemon->net.listen.type)
return MHD_SC_INFO_GET_TYPE_NOT_APPLICABLE;
@@ -75,11 +76,17 @@ MHD_daemon_get_info_fixed_sz (
output_buf->v_bind_port_uint16 = daemon->net.listen.port;
return MHD_SC_OK;
case MHD_DAEMON_INFO_FIXED_LISTEN_SOCKET:
- if (MHD_INVALID_SOCKET == daemon->net.listen.fd)
- return MHD_SC_INFO_GET_TYPE_NOT_APPLICABLE;
- if (sizeof(output_buf->v_listen_socket) > output_buf_size)
- return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
- output_buf->v_listen_socket = daemon->net.listen.fd;
+ if (1)
+ {
+ MHD_Socket listen_fd = daemon->net.listen.fd;
+ if (MHD_INVALID_SOCKET == listen_fd)
+ return daemon->net.listen.is_broken ?
+ MHD_SC_INFO_GET_TYPE_UNOBTAINABLE :
+ MHD_SC_INFO_GET_TYPE_NOT_APPLICABLE;
+ if (sizeof(output_buf->v_listen_socket) > output_buf_size)
+ return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+ output_buf->v_listen_socket = listen_fd;
+ }
return MHD_SC_OK;
case MHD_DAEMON_INFO_FIXED_AGGREAGATE_FD:
#ifdef MHD_SUPPORT_EPOLL
diff --git a/src/mhd2/daemon_options.h b/src/mhd2/daemon_options.h
@@ -29,6 +29,12 @@ struct DaemonOptions
/**
+ * Value for #MHD_D_O_REREGISTER_ALL.
+ */
+ enum MHD_Bool reregister_all;
+
+
+ /**
* Value for #MHD_D_O_LOG_CALLBACK.
* the callback to use for logging,
* NULL to disable logging.
diff --git a/src/mhd2/daemon_set_options.c b/src/mhd2/daemon_set_options.c
@@ -44,6 +44,9 @@ MHD_daemon_set_options (
case MHD_D_O_POLL_SYSCALL:
settings->poll_syscall = option->val.poll_syscall;
continue;
+ case MHD_D_O_REREGISTER_ALL:
+ settings->reregister_all = option->val.reregister_all;
+ continue;
case MHD_D_O_LOG_CALLBACK:
/* Note: set directly to the daemon */
daemon->log_params = option->val.log_callback;
diff --git a/src/mhd2/daemon_start.c b/src/mhd2/daemon_start.c
@@ -62,6 +62,10 @@
# endif
#endif
+#include "extr_events_funcs.h"
+
+#include "mhd_dbg_print.h"
+
#include "mhd_limits.h"
#include "mhd_daemon.h"
@@ -79,10 +83,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
@@ -223,6 +228,18 @@ daemon_set_work_mode (struct MHD_Daemon *restrict d,
return MHD_SC_CONFIGURATION_UNEXPECTED_WM;
}
+ if ((mhd_WM_INT_EXTERNAL_EVENTS_LEVEL != d->wmode_int) &&
+ (mhd_WM_INT_EXTERNAL_EVENTS_EDGE != d->wmode_int) &&
+ (MHD_NO != s->reregister_all))
+ {
+ mhd_LOG_MSG ( \
+ d, \
+ MHD_SC_EXTERNAL_EVENT_ONLY, \
+ "The MHD_D_O_REREGISTER_ALL option can be used only with external " \
+ "events work modes.");
+ return MHD_SC_EXTERNAL_EVENT_ONLY;
+ }
+
return MHD_SC_OK;
}
@@ -633,6 +650,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
{
/* No listen socket */
d->net.listen.fd = MHD_INVALID_SOCKET;
+ d->net.listen.is_broken = false;
d->net.listen.type = mhd_SOCKET_TYPE_UNKNOWN;
d->net.listen.non_block = false;
d->net.listen.port = 0;
@@ -976,6 +994,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
/* Set to the daemon only when the listening socket is fully ready */
d->net.listen.fd = sk;
+ d->net.listen.is_broken = false;
switch (sk_type)
{
case mhd_SKT_UNKNOWN:
@@ -1322,19 +1341,26 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
{
case mhd_POLL_TYPE_EXT:
mhd_assert ((MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL == s->work_mode.mode) || \
- (MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE != s->work_mode.mode));
+ (MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE == s->work_mode.mode));
mhd_assert (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
- mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode);
d->events.poll_type = mhd_POLL_TYPE_EXT;
- d->events.data.ext.cb =
+ d->events.data.extr.cb_data.cb =
s->work_mode.params.v_external_event_loop_cb.reg_cb;
- d->events.data.ext.cls =
+ d->events.data.extr.cb_data.cls =
s->work_mode.params.v_external_event_loop_cb.reg_cb_cls;
+ 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:
mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode);
+ mhd_assert (MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL != s->work_mode.mode);
+ mhd_assert (MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE != s->work_mode.mode);
+ mhd_assert (MHD_NO == s->reregister_all);
d->events.poll_type = mhd_POLL_TYPE_SELECT;
d->events.data.select.rfds = NULL; /* Memory allocated during event and threads init */
d->events.data.select.wfds = NULL; /* Memory allocated during event and threads init */
@@ -1345,6 +1371,9 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
case mhd_POLL_TYPE_POLL:
mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
mhd_assert (MHD_WM_EXTERNAL_SINGLE_FD_WATCH != s->work_mode.mode);
+ mhd_assert (MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL != s->work_mode.mode);
+ mhd_assert (MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE != s->work_mode.mode);
+ mhd_assert (MHD_NO == s->reregister_all);
d->events.poll_type = mhd_POLL_TYPE_POLL;
d->events.data.poll.fds = NULL; /* Memory allocated during event and threads init */
d->events.data.poll.rel = NULL; /* Memory allocated during event and threads init */
@@ -1353,6 +1382,9 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
#ifdef MHD_SUPPORT_EPOLL
case mhd_POLL_TYPE_EPOLL:
mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+ mhd_assert (MHD_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL != s->work_mode.mode);
+ mhd_assert (MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE != s->work_mode.mode);
+ mhd_assert (MHD_NO == s->reregister_all);
/* Pre-initialised by init_epoll() */
mhd_assert (mhd_POLL_TYPE_EPOLL == d->events.poll_type);
mhd_assert (0 <= d->events.data.epoll.e_fd);
@@ -1424,7 +1456,7 @@ daemon_init_net (struct MHD_Daemon *restrict d,
{
if ((MHD_INVALID_SOCKET != d->net.listen.fd)
&& ! d->net.listen.non_block
- && ((mhd_WM_INT_EXTERNAL_EVENTS_EDGE == d->wmode_int) ||
+ && (mhd_D_IS_USING_EDGE_TRIG (d) ||
(mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int)))
{
mhd_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \
@@ -1772,7 +1804,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);
+ 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
@@ -2025,7 +2058,7 @@ deinit_itc (struct MHD_Daemon *restrict d)
*/
static MHD_FN_PAR_NONNULL_ (1)
MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
-add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
+init_daemon_fds_monitoring (struct MHD_Daemon *restrict d)
{
mhd_assert (d->dbg.net_inited);
mhd_assert (! d->dbg.net_deinited);
@@ -2036,13 +2069,79 @@ add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
#endif
+ d->events.accept_pending = false;
+
switch (d->events.poll_type)
{
case mhd_POLL_TYPE_EXT:
- mhd_assert (NULL != d->events.data.ext.cb);
- /* Nothing to do with the external events */
- // FIXME: Register the ITC and the listening NOW?
- return MHD_SC_OK;
+ 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.extr.itc_data.app_cntx =
+ mhd_daemon_extr_event_reg (d,
+ 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 */
+ if (itc_reg_succeed)
+ {
+ if (MHD_INVALID_SOCKET == d->net.listen.fd)
+ {
+ 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.extr.listen_data.app_cntx =
+ mhd_daemon_extr_event_reg (d,
+ 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) mhd_daemon_extr_event_reg (d,
+ 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 */
+ }
+
+ 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;
+ }
+ else
+ {
+ /* Daemons FDs are repeatedly registered every processing cycle */
+#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;
+ return MHD_SC_OK;
+ }
break;
#ifdef MHD_SUPPORT_SELECT
case mhd_POLL_TYPE_SELECT:
@@ -2086,15 +2185,20 @@ add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
{
struct epoll_event reg_event;
#ifdef MHD_SUPPORT_THREADS
- reg_event.events = EPOLLIN;
+ reg_event.events = EPOLLIN | EPOLLET;
reg_event.data.u64 = (uint64_t) mhd_SOCKET_REL_MARKER_ITC; /* uint64_t is used in the epoll header */
if (0 != epoll_ctl (d->events.data.epoll.e_fd, EPOLL_CTL_ADD,
mhd_itc_r_fd (d->threading.itc), ®_event))
{
mhd_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \
- "Failed to add ITC fd to the epoll monitoring.");
+ "Failed to add ITC FD to the epoll monitoring.");
return MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE;
}
+ mhd_dbg_print_fd_mon_req ("ITC", \
+ mhd_itc_r_fd (d->threading.itc), \
+ true, \
+ false, \
+ false);
#endif
if (MHD_INVALID_SOCKET != d->net.listen.fd)
{
@@ -2104,9 +2208,14 @@ add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
d->net.listen.fd, ®_event))
{
mhd_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \
- "Failed to add listening fd to the epoll monitoring.");
+ "Failed to add listening FD to the epoll monitoring.");
return MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE;
}
+ mhd_dbg_print_fd_mon_req ("lstn", \
+ d->net.listen.fd, \
+ true, \
+ false, \
+ false);
}
}
return MHD_SC_OK;
@@ -2132,6 +2241,73 @@ add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
/**
+ * The initial part of events de-initialisation: remove ITC and listening FD
+ * from the monitored items (if supported by monitoring syscall).
+ * @param d the daemon object
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+deinit_daemon_fds_monitoring (struct MHD_Daemon *restrict d)
+{
+ mhd_assert (d->dbg.events_fully_inited);
+
+ switch (d->events.poll_type)
+ {
+ case mhd_POLL_TYPE_EXT:
+ if (NULL != d->events.data.extr.listen_data.app_cntx)
+ (void) mhd_daemon_extr_event_reg (
+ d,
+ 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.extr.itc_data.app_cntx)
+ (void) mhd_daemon_extr_event_reg (d,
+ 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
+ case mhd_POLL_TYPE_SELECT:
+ /* Nothing to do when using 'select()' */
+ return;
+ break;
+#endif /* MHD_SUPPORT_SELECT */
+#ifdef MHD_SUPPORT_POLL
+ case mhd_POLL_TYPE_POLL:
+ /* Nothing to do when using 'poll()' */
+ return;
+ break;
+#endif /* MHD_SUPPORT_POLL */
+#ifdef MHD_SUPPORT_EPOLL
+ case mhd_POLL_TYPE_EPOLL:
+ /* Nothing to do when using epoll.
+ Monitoring stopped by closing epoll FD. */
+ return;
+ break;
+#endif /* MHD_SUPPORT_EPOLL */
+#ifndef MHD_SUPPORT_SELECT
+ case mhd_POLL_TYPE_SELECT:
+#endif /* ! MHD_SUPPORT_SELECT */
+#ifndef MHD_SUPPORT_POLL
+ case mhd_POLL_TYPE_POLL:
+#endif /* ! MHD_SUPPORT_POLL */
+#ifndef MHD_SUPPORT_EPOLL
+ case mhd_POLL_TYPE_EPOLL:
+#endif /* ! MHD_SUPPORT_EPOLL */
+ case mhd_POLL_TYPE_NOT_SET_YET:
+ default:
+ mhd_UNREACHABLE ();
+ break;
+ }
+ mhd_UNREACHABLE ();
+}
+
+
+/**
* Initialise daemon connections' data.
* @param d the daemon object
* @param s the user settings
@@ -2228,7 +2404,7 @@ init_individual_thread_data_events_conns (struct MHD_Daemon *restrict d,
res = init_itc (d);
if (MHD_SC_OK == res)
{
- res = add_itc_and_listen_to_monitoring (d);
+ res = init_daemon_fds_monitoring (d);
if (MHD_SC_OK == res)
{
@@ -2246,6 +2422,10 @@ init_individual_thread_data_events_conns (struct MHD_Daemon *restrict d,
res = init_individual_conns (d, s);
if (MHD_SC_OK == res)
return MHD_SC_OK;
+
+ /* Below is a clean-up path */
+
+ deinit_daemon_fds_monitoring (d);
}
deinit_itc (d);
}
@@ -2268,6 +2448,7 @@ static MHD_FN_PAR_NONNULL_ (1) void
deinit_individual_thread_data_events_conns (struct MHD_Daemon *restrict d)
{
deinit_individual_conns (d);
+ deinit_daemon_fds_monitoring (d);
deinit_itc (d);
deallocate_events (d);
mhd_assert (NULL == mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn));
@@ -2467,7 +2648,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);
+ 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 */
@@ -3026,9 +3207,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)
@@ -3063,6 +3242,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.
* @param d the daemon object
@@ -3176,6 +3373,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
@@ -30,6 +30,10 @@
#include "mhd_assert.h"
#include "mhd_unreachable.h"
+#if defined(mhd_DEBUG_SUSPEND_RESUME) || defined(mhd_DEBUG_POLLING_FDS)
+# include <stdio.h>
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+
#include "mhd_locks.h"
#include "mhd_socket_type.h"
@@ -45,6 +49,7 @@
#include "mhd_itc.h"
#include "mhd_panic.h"
+#include "mhd_dbg_print.h"
#include "mhd_sockets_macros.h"
@@ -57,6 +62,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"
@@ -64,27 +70,176 @@
#include "mhd_public_api.h"
+#ifdef mhd_DEBUG_POLLING_FDS
+/**
+ * Debug-printf request of FD polling/monitoring
+ * @param fd_name the name of FD ("ITC", "lstn" or "conn")
+ * @param fd the FD value
+ * @param r_ready the request for read (or receive) readiness
+ * @param w_ready the request for write (or send) readiness
+ * @param e_ready the request for exception (or error) readiness
+ */
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_dbg_print_fd_mon_req (const char *fd_name,
+ MHD_Socket fd,
+ bool r_ready,
+ bool w_ready,
+ bool e_ready)
+{
+ char state_str[] = "x:x:x";
+ state_str[0] = r_ready ? 'R' : '-';
+ state_str[2] = w_ready ? 'W' : '-';
+ state_str[4] = e_ready ? 'E' : '-';
+
+ fprintf (stderr,
+ "### Set FD watching: %4s [%2llu] for %s\n",
+ fd_name,
+ (unsigned long long) fd,
+ state_str);
+}
+
+
+/**
+ * Debug-printf reported (by polling) status of FD
+ * @param fd_name the name of FD ("ITC", "lstn" or "conn")
+ * @param fd the FD value
+ * @param r_ready the read (or receive) readiness
+ * @param w_ready the write (or send) readiness
+ * @param e_ready the exception (or error) readiness
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+dbg_print_fd_state_update (const char *fd_name,
+ MHD_Socket fd,
+ bool r_ready,
+ bool w_ready,
+ bool e_ready)
+{
+ char state_str[] = "x:x:x";
+ state_str[0] = r_ready ? 'R' : '-';
+ state_str[2] = w_ready ? 'W' : '-';
+ state_str[4] = e_ready ? 'E' : '-';
+
+ fprintf (stderr,
+ "### FD state update: %4s [%2llu] -> %s\n",
+ fd_name,
+ (unsigned long long) fd,
+ state_str);
+}
+
+
+#else /* ! mhd_DEBUG_POLLING_FDS */
+# define dbg_print_fd_state_update(fd_n,fd,r_ready,w_ready,e_ready) \
+ ((void) 0)
+#endif /* ! mhd_DEBUG_POLLING_FDS */
+
+/**
+ * Log error message about broken ITC
+ * @param d the daemon to use
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+log_itc_broken (struct MHD_Daemon *restrict d)
+{
+ mhd_LOG_MSG (d, \
+ MHD_SC_ITC_STATUS_ERROR, \
+ "System reported that ITC has an error status or broken.");
+}
+
+
+/**
+ * Log error message about broken listen socket
+ * @param d the daemon to use
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+log_listen_broken (struct MHD_Daemon *restrict d)
+{
+ mhd_LOG_MSG (d, MHD_SC_LISTEN_STATUS_ERROR, \
+ "System reported that the listening socket has an error " \
+ "status or broken. The daemon will not listen any more.");
+}
+
+
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ uint_fast64_t
mhd_daemon_get_wait_max (struct MHD_Daemon *restrict d)
{
- bool zero_wait = d->events.zero_wait;
mhd_assert (! mhd_D_HAS_WORKERS (d));
- if (d->events.act_req.accept && d->conns.block_new)
- d->events.act_req.accept = false;
-
- if (d->events.act_req.accept)
+ if (d->events.accept_pending && ! d->conns.block_new)
+ {
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### mhd_daemon_get_wait_max(daemon) -> zero "
+ "(accept new conn pending)\n");
+#endif
return 0;
- if (zero_wait)
+ }
+ if (d->threading.resume_requested) /* Remove? It is triggered by ITC anyway */
+ {
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### mhd_daemon_get_wait_max(daemon) -> zero "
+ "(resume connection pending)\n");
+#endif
return 0;
+ }
if (NULL != mhd_DLINKEDL_GET_FIRST (&(d->events), proc_ready))
+ {
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### mhd_daemon_get_wait_max(daemon) -> zero "
+ "(connection(s) is already ready)\n");
+#endif
return 0;
+ }
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### mhd_daemon_get_wait_max(daemon) -> MHD_WAIT_INDEFINITELY\n");
+#endif
return MHD_WAIT_INDEFINITELY; // TODO: calculate correct timeout value
}
+static MHD_FN_PAR_NONNULL_ALL_ void
+start_resuming_connection (struct MHD_Connection *restrict c,
+ struct MHD_Daemon *restrict d)
+{
+ mhd_assert (c->suspended);
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+ fprintf (stderr,
+ "%%%%%% Resuming connection, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+ c->suspended = false;
+ mhd_stream_resumed_activity_mark (c);
+ mhd_conn_mark_ready (c, d); /* Force processing connection in this round */
+}
+
+
+/**
+ * Check whether any resuming connections are pending and resume them
+ * @param d the daemon to use
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+daemon_resume_conns_if_needed (struct MHD_Daemon *restrict d)
+{
+ struct MHD_Connection *c;
+
+ if (! d->threading.resume_requested)
+ return;
+
+ d->threading.resume_requested = false; /* Reset flag before processing data */
+
+ for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn);
+ NULL != c;
+ c = mhd_DLINKEDL_GET_NEXT (c,all_conn))
+ {
+ if (c->resuming)
+ start_resuming_connection (c, d);
+ }
+}
+
+
mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
static MHD_FN_PAR_NONNULL_ALL_ int
@@ -93,9 +248,6 @@ get_max_wait (struct MHD_Daemon *restrict d)
uint_fast64_t ui64_wait = mhd_daemon_get_wait_max (d);
int i_wait = (int) ui64_wait;
- // TODO: move reset to the processing loop
- d->events.zero_wait = false; /* Reset as this pending data will be processed */
-
if ((0 > i_wait) ||
(ui64_wait != (uint_fast64_t) i_wait))
return INT_MAX;
@@ -118,6 +270,14 @@ update_conn_net_status (struct MHD_Daemon *restrict d,
enum mhd_SocketNetState sk_state;
mhd_assert (d == c->daemon);
+ /* "resuming" must be not processed yet */
+ mhd_assert (! c->resuming || c->suspended);
+
+ dbg_print_fd_state_update ("conn", \
+ c->sk.fd, \
+ recv_ready, \
+ send_ready, \
+ err_state);
sk_state = mhd_SOCKET_NET_STATE_NOTHING;
if (recv_ready)
@@ -131,7 +291,10 @@ update_conn_net_status (struct MHD_Daemon *restrict d,
(sk_state | (unsigned int) mhd_SOCKET_NET_STATE_ERROR_READY);
c->sk.ready = sk_state;
- mhd_conn_mark_ready_update3 (c, err_state, d);
+ if (! c->suspended)
+ mhd_conn_mark_ready_update3 (c, err_state, d);
+ else
+ mhd_assert (! c->in_proc_ready);
}
@@ -146,6 +309,7 @@ daemon_accept_new_conns (struct MHD_Daemon *restrict d)
{
unsigned int num_to_accept;
mhd_assert (MHD_INVALID_SOCKET != d->net.listen.fd);
+ mhd_assert (! d->net.listen.is_broken);
mhd_assert (! d->conns.block_new);
mhd_assert (d->conns.count < d->conns.cfg.count_limit);
mhd_assert (! mhd_D_HAS_WORKERS (d));
@@ -256,9 +420,10 @@ daemon_accept_new_conns (struct MHD_Daemon *restrict d)
if (mhd_DAEMON_ACCEPT_NO_MORE_PENDING == res)
return true;
if (mhd_DAEMON_ACCEPT_FAILED == res)
- break;
+ return false; /* This is probably "no system resources" error.
+ To do try to accept more connections now. */
}
- return false;
+ return false; /* More connections may need to be accepted */
}
@@ -281,9 +446,7 @@ is_conn_excluded_from_http_comm (struct MHD_Connection *restrict c)
}
#endif /* MHD_SUPPORT_UPGRADE */
- // TODO: Support suspended connection
-
- return false;
+ return c->suspended;
}
@@ -297,13 +460,19 @@ daemon_process_all_active_conns (struct MHD_Daemon *restrict d)
while (NULL != c)
{
struct MHD_Connection *next;
- next = mhd_DLINKEDL_GET_NEXT (c, proc_ready); /* The current connection can be closed */
+ /* The current connection can be closed or removed from
+ "ready" list */
+ next = mhd_DLINKEDL_GET_NEXT (c, proc_ready);
if (! mhd_conn_process_recv_send_data (c))
{
mhd_conn_pre_clean (c);
mhd_conn_remove_from_daemon (c);
mhd_conn_close_final (c);
}
+ else
+ {
+ mhd_assert (! c->resuming || c->suspended);
+ }
c = next;
}
@@ -317,10 +486,14 @@ daemon_process_all_active_conns (struct MHD_Daemon *restrict d)
* @param d the daemon to process
*/
static MHD_FN_PAR_NONNULL_ALL_ void
-daemon_cleanup_upgraded_conns (struct MHD_Daemon *restrict d)
+daemon_cleanup_upgraded_conns (struct MHD_Daemon *d)
{
+ volatile struct MHD_Daemon *voltl_d = d;
mhd_assert (! mhd_D_HAS_WORKERS (d));
+ if (NULL == mhd_DLINKEDL_GET_FIRST (&(voltl_d->conns.upgr), upgr_cleanup))
+ return;
+
while (true)
{
struct MHD_Connection *c;
@@ -347,8 +520,8 @@ daemon_cleanup_upgraded_conns (struct MHD_Daemon *restrict 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;
@@ -389,6 +562,209 @@ 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; resume connections.
+ * @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_and_resume_conn (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);
+
+ d->threading.resume_requested = false; /* Reset flag before processing data */
+
+#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 (c->resuming)
+ start_resuming_connection (c, d);
+ else
+ {
+ if (is_conn_excluded_from_http_comm (c))
+ {
+ mhd_assert (! c->in_proc_ready);
+ 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 =
+ mhd_daemon_extr_event_reg (d,
+ 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);
+ }
+ 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 =
+ mhd_daemon_extr_event_reg (d,
+ 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 =
+ mhd_daemon_extr_event_reg (d,
+ 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);
+
+ mhd_assert (! c->resuming || c->suspended);
+
+ if (is_conn_excluded_from_http_comm (c))
+ {
+ if (NULL != c->extr_event.app_cntx)
+ {
+ /* De-register the connection socket FD */
+ c->extr_event.app_cntx =
+ mhd_daemon_extr_event_reg (d,
+ 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)))
+ {
+ /* (Re-)register the connection socket FD */
+ c->extr_event.app_cntx =
+ mhd_daemon_extr_event_reg (d,
+ 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
/**
@@ -469,9 +845,17 @@ select_update_fdsets (struct MHD_Daemon *restrict d,
efds,
&ret,
d);
+ mhd_dbg_print_fd_mon_req ("ITC", \
+ mhd_itc_r_fd (d->threading.itc), \
+ true, \
+ false, \
+ true);
#endif
- if (MHD_INVALID_SOCKET != d->net.listen.fd)
+ if ((MHD_INVALID_SOCKET != d->net.listen.fd)
+ && ! d->conns.block_new)
{
+ mhd_assert (! d->net.listen.is_broken);
+
fd_set_wrap (d->net.listen.fd,
rfds,
&ret,
@@ -480,6 +864,11 @@ select_update_fdsets (struct MHD_Daemon *restrict d,
efds,
&ret,
d);
+ mhd_dbg_print_fd_mon_req ("lstn", \
+ d->net.listen.fd, \
+ true, \
+ false, \
+ true);
}
if (listen_only)
return ret;
@@ -505,6 +894,11 @@ select_update_fdsets (struct MHD_Daemon *restrict d,
efds,
&ret,
d);
+ mhd_dbg_print_fd_mon_req ("conn", \
+ c->sk.fd, \
+ FD_ISSET (c->sk.fd, rfds), \
+ FD_ISSET (c->sk.fd, wfds), \
+ true);
}
return ret;
@@ -512,18 +906,27 @@ select_update_fdsets (struct MHD_Daemon *restrict d,
static MHD_FN_PAR_NONNULL_ (1) bool
-select_update_statuses_from_fdsets (struct MHD_Daemon *d,
- int num_events)
+select_update_statuses_from_fdsets_and_resume_conn (struct MHD_Daemon *d,
+ int num_events)
{
struct MHD_Connection *c;
fd_set *const restrict rfds = d->events.data.select.rfds;
fd_set *const restrict wfds = d->events.data.select.wfds;
fd_set *const restrict efds = d->events.data.select.efds;
+ bool resuming_conn;
mhd_assert (mhd_POLL_TYPE_SELECT == d->events.poll_type);
mhd_assert (0 <= num_events);
mhd_assert (((unsigned int) num_events) <= d->dbg.num_events_elements);
+ resuming_conn = d->threading.resume_requested;
+ if (resuming_conn)
+ {
+ mhd_assert (mhd_DAEMON_TYPE_LISTEN_ONLY != d->threading.d_type);
+ num_events = (int) -1; /* Force process all connections */
+ d->threading.resume_requested = false;
+ }
+
#ifndef MHD_FAVOR_SMALL_CODE
if (0 == num_events)
return true;
@@ -531,10 +934,14 @@ select_update_statuses_from_fdsets (struct MHD_Daemon *d,
#ifdef MHD_SUPPORT_THREADS
mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
+ dbg_print_fd_state_update ("ITC", \
+ mhd_itc_r_fd (d->threading.itc), \
+ FD_ISSET (mhd_itc_r_fd (d->threading.itc), rfds), \
+ FD_ISSET (mhd_itc_r_fd (d->threading.itc), wfds), \
+ FD_ISSET (mhd_itc_r_fd (d->threading.itc), efds));
if (FD_ISSET (mhd_itc_r_fd (d->threading.itc), efds))
{
- mhd_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
- "System reported that ITC has an error status.");
+ log_itc_broken (d);
/* ITC is broken, need to stop the daemon thread now as otherwise
application will not be able to stop the thread. */
return false;
@@ -542,10 +949,10 @@ select_update_statuses_from_fdsets (struct MHD_Daemon *d,
if (FD_ISSET (mhd_itc_r_fd (d->threading.itc), rfds))
{
--num_events;
- /* Clear ITC here, as before any other data processing.
- * Any external events may activate ITC again if any data to process is
- * added externally. Cleaning ITC early ensures guaranteed that new data
- * will not be missed. */
+ /* 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);
}
@@ -557,23 +964,30 @@ select_update_statuses_from_fdsets (struct MHD_Daemon *d,
if (MHD_INVALID_SOCKET != d->net.listen.fd)
{
+ mhd_assert (! d->net.listen.is_broken);
+ dbg_print_fd_state_update ("lstn", \
+ d->net.listen.fd, \
+ FD_ISSET (d->net.listen.fd, rfds), \
+ FD_ISSET (d->net.listen.fd, wfds), \
+ FD_ISSET (d->net.listen.fd, efds));
if (FD_ISSET (d->net.listen.fd, efds))
{
--num_events;
- mhd_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
- "System reported that the listening socket has an error " \
- "status. The daemon will not listen any more.");
+ log_listen_broken (d);
/* Close the listening socket unless the master daemon should close it */
if (! mhd_D_HAS_MASTER (d))
mhd_socket_close (d->net.listen.fd);
+ d->events.accept_pending = false;
+ d->net.listen.is_broken = true;
/* Stop monitoring socket to avoid spinning with busy-waiting */
d->net.listen.fd = MHD_INVALID_SOCKET;
}
- else if (FD_ISSET (d->net.listen.fd, rfds))
+ else
{
- --num_events;
- d->events.act_req.accept = true;
+ d->events.accept_pending = FD_ISSET (d->net.listen.fd, rfds);
+ if (d->events.accept_pending)
+ --num_events;
}
}
@@ -582,39 +996,44 @@ select_update_statuses_from_fdsets (struct MHD_Daemon *d,
#ifdef MHD_FAVOR_SMALL_CODE
(void) num_events;
- num_events = 1; /* Use static value to optimise out the next look */
+ num_events = 1; /* Use static value to minimise the binary size of the next loop */
#endif /* ! MHD_FAVOR_SMALL_CODE */
for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns), all_conn);
(NULL != c) && (0 != num_events);
c = mhd_DLINKEDL_GET_NEXT (c, all_conn))
{
- MHD_Socket sk;
- bool recv_ready;
- bool send_ready;
- bool err_state;
+ if (c->resuming)
+ start_resuming_connection (c, d);
+ else
+ {
+ MHD_Socket sk;
+ bool recv_ready;
+ bool send_ready;
+ bool err_state;
- if (is_conn_excluded_from_http_comm (c))
- continue;
+ if (is_conn_excluded_from_http_comm (c))
+ continue;
- sk = c->sk.fd;
- recv_ready = FD_ISSET (sk, rfds);
- send_ready = FD_ISSET (sk, wfds);
- err_state = FD_ISSET (sk, efds);
+ sk = c->sk.fd;
+ recv_ready = FD_ISSET (sk, rfds);
+ send_ready = FD_ISSET (sk, wfds);
+ err_state = FD_ISSET (sk, efds);
- update_conn_net_status (d,
- c,
- recv_ready,
- send_ready,
- err_state);
+ update_conn_net_status (d,
+ c,
+ recv_ready,
+ send_ready,
+ err_state);
#ifndef MHD_FAVOR_SMALL_CODE
- if (recv_ready || send_ready || err_state)
- --num_events;
+ if (recv_ready || send_ready || err_state)
+ --num_events;
#endif /* MHD_FAVOR_SMALL_CODE */
+ }
}
- #ifndef MHD_FAVOR_SMALL_CODE
- mhd_assert (0 == num_events);
+#ifndef MHD_FAVOR_SMALL_CODE
+ mhd_assert ((0 == num_events) || resuming_conn);
#endif /* MHD_FAVOR_SMALL_CODE */
return true;
}
@@ -622,7 +1041,7 @@ select_update_statuses_from_fdsets (struct MHD_Daemon *d,
/**
* Update states of all connections, check for connection pending
- * to be accept()'ed, check for the events on ITC.
+ * to be accept()'ed, check for the events on ITC; resume connections
* @param listen_only set to 'true' if connections's sockets should NOT
* be monitored
* @return 'true' if processed successfully,
@@ -630,8 +1049,8 @@ select_update_statuses_from_fdsets (struct MHD_Daemon *d,
* closed
*/
static MHD_FN_PAR_NONNULL_ (1) bool
-get_all_net_updates_by_select (struct MHD_Daemon *restrict d,
- bool listen_only)
+get_all_net_updates_by_select_and_resume_conn (struct MHD_Daemon *restrict d,
+ bool listen_only)
{
int max_socket;
int max_wait;
@@ -659,11 +1078,27 @@ get_all_net_updates_by_select (struct MHD_Daemon *restrict d,
tmvl.tv_usec = (int) ((max_wait % 1000) * 1000);
#endif
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### (Starting) select(%d, rfds, wfds, efds, [%llu, %llu])...\n",
+ max_socket + 1,
+ (unsigned long long) tmvl.tv_sec,
+ (unsigned long long) tmvl.tv_usec);
+#endif /* mhd_DEBUG_POLLING_FDS */
num_events = select (max_socket + 1,
d->events.data.select.rfds,
d->events.data.select.wfds,
d->events.data.select.efds,
&tmvl);
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### (Finished) select(%d, rfds, wfds, efds, ->[%llu, %llu]) -> "
+ "%d\n",
+ max_socket + 1,
+ (unsigned long long) tmvl.tv_sec,
+ (unsigned long long) tmvl.tv_usec,
+ num_events);
+#endif /* mhd_DEBUG_POLLING_FDS */
if (0 > num_events)
{
@@ -700,7 +1135,7 @@ get_all_net_updates_by_select (struct MHD_Daemon *restrict d,
}
}
- return select_update_statuses_from_fdsets (d, num_events);
+ return select_update_statuses_from_fdsets_and_resume_conn (d, num_events);
}
@@ -727,15 +1162,28 @@ poll_update_fds (struct MHD_Daemon *restrict d,
mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
mhd_assert (d->events.data.poll.fds[i_s].fd == \
mhd_itc_r_fd (d->threading.itc));
+ mhd_assert (POLLIN == d->events.data.poll.fds[i_s].events);
mhd_assert (mhd_SOCKET_REL_MARKER_ITC == \
d->events.data.poll.rel[i_s].fd_id);
+ mhd_dbg_print_fd_mon_req ("ITC", \
+ mhd_itc_r_fd (d->threading.itc), \
+ true, \
+ false, \
+ false);
++i_s;
#endif
if (MHD_INVALID_SOCKET != d->net.listen.fd)
{
+ mhd_assert (! d->net.listen.is_broken);
mhd_assert (d->events.data.poll.fds[i_s].fd == d->net.listen.fd);
mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN == \
d->events.data.poll.rel[i_s].fd_id);
+ d->events.data.poll.fds[i_s].events = d->conns.block_new ? 0 : POLLIN;
+ mhd_dbg_print_fd_mon_req ("lstn", \
+ d->net.listen.fd, \
+ POLLIN == d->events.data.poll.fds[i_s].events, \
+ false, \
+ false);
++i_s;
}
if (listen_only)
@@ -768,6 +1216,11 @@ poll_update_fds (struct MHD_Daemon *restrict d,
events |= MHD_POLL_OUT;
d->events.data.poll.fds[i_c].events = (short) events;
+ mhd_dbg_print_fd_mon_req ("conn", \
+ c->sk.fd, \
+ MHD_POLL_IN == (MHD_POLL_IN & events), \
+ MHD_POLL_OUT == (MHD_POLL_OUT & events), \
+ false);
++i_c;
}
mhd_assert ((d->conns.count - num_skipped) == (i_c - i_s));
@@ -796,10 +1249,16 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d,
mhd_itc_r_fd (d->threading.itc));
mhd_assert (mhd_SOCKET_REL_MARKER_ITC == \
d->events.data.poll.rel[i_s].fd_id);
+ dbg_print_fd_state_update ( \
+ "ITC", \
+ d->events.data.poll.fds[i_s].fd, \
+ 0 != (d->events.data.poll.fds[i_s].revents & (MHD_POLL_IN | POLLIN)), \
+ 0 != (d->events.data.poll.fds[i_s].revents & (MHD_POLL_OUT | POLLOUT)), \
+ 0 != (d->events.data.poll.fds[i_s].revents & (POLLERR | POLLNVAL)));
+
if (0 != (d->events.data.poll.fds[i_s].revents & (POLLERR | POLLNVAL)))
{
- mhd_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
- "System reported that ITC has an error status.");
+ log_itc_broken (d);
/* ITC is broken, need to stop the daemon thread now as otherwise
application will not be able to stop the thread. */
return false;
@@ -807,10 +1266,10 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d,
if (0 != (d->events.data.poll.fds[i_s].revents & (MHD_POLL_IN | POLLIN)))
{
--num_events;
- /* Clear ITC here, as before any other data processing.
- * Any external events may activate ITC again if any data to process is
- * added externally. Cleaning ITC early ensures guaranteed that new data
- * will not be missed. */
+ /* 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);
}
++i_s;
@@ -823,27 +1282,43 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d,
{
const short revents = d->events.data.poll.fds[i_s].revents;
+ mhd_assert (! d->net.listen.is_broken);
mhd_assert (d->events.data.poll.fds[i_s].fd == d->net.listen.fd);
mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN == \
d->events.data.poll.rel[i_s].fd_id);
+ dbg_print_fd_state_update ("lstn", \
+ d->events.data.poll.fds[i_s].fd, \
+ 0 != (revents & (MHD_POLL_IN | POLLIN)), \
+ 0 != (revents & (MHD_POLL_OUT | POLLOUT)), \
+ 0 != (revents & (POLLERR | POLLNVAL | POLLHUP)));
if (0 != (revents & (POLLERR | POLLNVAL | POLLHUP)))
{
--num_events;
- mhd_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
- "System reported that the listening socket has an error " \
- "status. The daemon will not listen any more.");
+ log_listen_broken (d);
/* Close the listening socket unless the master daemon should close it */
if (! mhd_D_HAS_MASTER (d))
mhd_socket_close (d->net.listen.fd);
+ d->events.accept_pending = false;
+ d->net.listen.is_broken = true;
/* Stop monitoring socket to avoid spinning with busy-waiting */
d->net.listen.fd = MHD_INVALID_SOCKET;
}
- else if (0 !=
- (d->events.data.poll.fds[i_s].revents & (MHD_POLL_IN | POLLIN)))
+ else
{
- --num_events;
- d->events.act_req.accept = true;
+ const bool has_new_conns = (0 != (revents & (MHD_POLL_IN | POLLIN)));
+ if (has_new_conns)
+ {
+ --num_events;
+ d->events.accept_pending = true;
+ }
+ else
+ {
+ /* Check whether the listen socket was monitored for incoming
+ connections */
+ if (0 != (d->events.data.poll.fds[i_s].events & POLLIN))
+ d->events.accept_pending = false;
+ }
}
++i_s;
}
@@ -908,17 +1383,42 @@ static MHD_FN_PAR_NONNULL_ (1) bool
get_all_net_updates_by_poll (struct MHD_Daemon *restrict d,
bool listen_only)
{
+#ifdef mhd_DEBUG_POLLING_FDS
+# ifdef MHD_SOCKETS_KIND_POSIX
+ static const char poll_fn_name[] = "poll";
+# else /* MHD_SOCKETS_KIND_WINSOCK */
+ static const char poll_fn_name[] = "WSAPoll";
+# endif /* MHD_SOCKETS_KIND_WINSOCK */
+#endif /* mhd_DEBUG_POLLING_FDS */
unsigned int num_fds;
+ int max_wait;
int num_events;
+
mhd_assert (mhd_POLL_TYPE_POLL == d->events.poll_type);
num_fds = poll_update_fds (d, listen_only);
// TODO: handle empty list situation
+ max_wait = get_max_wait (d); // TODO: use correct timeout value
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### (Starting) %s(fds, %u, %d)...\n",
+ poll_fn_name,
+ num_fds,
+ max_wait);
+#endif /* mhd_DEBUG_POLLING_FDS */
num_events = mhd_poll (d->events.data.poll.fds,
num_fds,
- get_max_wait (d)); // TODO: use correct timeout value
+ max_wait); // TODO: use correct timeout value
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### (Finished) %s(fds, %u, %d) -> %d\n",
+ poll_fn_name,
+ num_fds,
+ max_wait,
+ num_events);
+#endif /* mhd_DEBUG_POLLING_FDS */
if (0 > num_events)
{
int err;
@@ -966,8 +1466,8 @@ get_all_net_updates_by_poll (struct MHD_Daemon *restrict d,
* listen socket states
*/
static MHD_FN_PAR_NONNULL_ (1) bool
-poll_update_statuses_from_eevents (struct MHD_Daemon *restrict d,
- unsigned int num_events)
+update_statuses_from_eevents (struct MHD_Daemon *restrict d,
+ unsigned int num_events)
{
unsigned int i;
struct epoll_event *const restrict events =
@@ -975,44 +1475,67 @@ poll_update_statuses_from_eevents (struct MHD_Daemon *restrict d,
for (i = 0; num_events > i; ++i)
{
struct epoll_event *const e = events + i;
+#ifdef MHD_SUPPORT_THREADS
if (((uint64_t) mhd_SOCKET_REL_MARKER_ITC) == e->data.u64) /* uint64_t is in the system header */
{
+ mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
+ dbg_print_fd_state_update ( \
+ "ITC", \
+ mhd_itc_r_fd (d->threading.itc), \
+ 0 != (e->events & EPOLLIN), \
+ 0 != (e->events & EPOLLOUT), \
+ 0 != (e->events & (EPOLLPRI | EPOLLERR | EPOLLHUP)));
+
if (0 != (e->events & (EPOLLPRI | EPOLLERR | EPOLLHUP)))
{
- mhd_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
- "System reported that ITC has an error status.");
+ log_itc_broken (d);
/* ITC is broken, need to stop the daemon thread now as otherwise
application will not be able to stop the thread. */
return false;
}
if (0 != (e->events & EPOLLIN))
{
- /* Clear ITC here, as before any other data processing.
- * Any external events may activate ITC again if any data to process is
- * added externally. Cleaning ITC early ensures guaranteed that new data
- * will not be missed. */
+ /* 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);
}
}
- else if (((uint64_t) mhd_SOCKET_REL_MARKER_LISTEN) == e->data.u64) /* uint64_t is in the system header */
+ else
+#endif /* MHD_SUPPORT_THREADS */
+ if (((uint64_t) mhd_SOCKET_REL_MARKER_LISTEN) == e->data.u64) /* uint64_t is in the system header */
{
+ mhd_assert (MHD_INVALID_SOCKET != d->net.listen.fd);
+ dbg_print_fd_state_update ( \
+ "lstn", \
+ d->net.listen.fd, \
+ 0 != (e->events & EPOLLIN), \
+ 0 != (e->events & EPOLLOUT), \
+ 0 != (e->events & (EPOLLPRI | EPOLLERR | EPOLLHUP)));
if (0 != (e->events & (EPOLLPRI | EPOLLERR | EPOLLHUP)))
{
- mhd_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
- "System reported that the listening socket has an error " \
- "status. The daemon will not listen any more.");
-
- // TODO: remove listen from epoll monitoring
+ log_listen_broken (d);
/* Close the listening socket unless the master daemon should close it */
if (! mhd_D_HAS_MASTER (d))
mhd_socket_close (d->net.listen.fd);
+ else
+ {
+ /* Ignore possible error as the socket could be already removed
+ from the epoll monitoring by closing the socket */
+ (void) epoll_ctl (d->events.data.epoll.e_fd,
+ EPOLL_CTL_DEL,
+ d->net.listen.fd,
+ NULL);
+ }
- /* Stop monitoring socket to avoid spinning with busy-waiting */
+ d->events.accept_pending = false;
+ d->net.listen.is_broken = true;
d->net.listen.fd = MHD_INVALID_SOCKET;
}
- if (0 != (e->events & EPOLLIN))
- d->events.act_req.accept = true;
+ else
+ d->events.accept_pending = (0 != (e->events & EPOLLIN));
}
else
{
@@ -1057,10 +1580,25 @@ get_all_net_updates_by_epoll (struct MHD_Daemon *restrict d)
max_wait = get_max_wait (d); // TODO: use correct timeout value
do
{
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### (Starting) epoll_wait(%d, events, %d, %d)...\n",
+ d->events.data.epoll.e_fd,
+ (int) d->events.data.epoll.num_elements,
+ max_wait);
+#endif /* mhd_DEBUG_POLLING_FDS */
num_events = epoll_wait (d->events.data.epoll.e_fd,
d->events.data.epoll.events,
(int) d->events.data.epoll.num_elements,
max_wait);
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### (Finished) epoll_wait(%d, events, %d, %d) -> %d\n",
+ d->events.data.epoll.e_fd,
+ (int) d->events.data.epoll.num_elements,
+ max_wait,
+ num_events);
+#endif /* mhd_DEBUG_POLLING_FDS */
max_wait = 0;
if (0 > num_events)
{
@@ -1073,7 +1611,7 @@ get_all_net_updates_by_epoll (struct MHD_Daemon *restrict d)
}
return true; /* EINTR, try next time */
}
- if (! poll_update_statuses_from_eevents (d, (unsigned int) num_events))
+ if (! update_statuses_from_eevents (d, (unsigned int) num_events))
return false;
events_processed += (unsigned int) num_events; /* Avoid reading too many events */
@@ -1087,17 +1625,36 @@ get_all_net_updates_by_epoll (struct MHD_Daemon *restrict d)
#endif /* MHD_SUPPORT_EPOLL */
+/**
+ * Perform one round of daemon connection and data processing.
+ *
+ * This function do the following:
+ * + poll all connections and daemon FDs (if internal polling is used);
+ * + resume connections pending to be resumed;
+ * + update connection statuses based on socket states (recv/send ready or
+ * disconnect detection);
+ * + receive, send and/or parse connections data as needed, including call of
+ * callbacks for processing requests and response generation;
+ * + close broken connections;
+ * + accept new connection (if needed);
+ * + cleanup closed "upgraded" connections.
+ * @param d the daemon to use
+ * @return 'true' on success,
+ * 'false' if daemon is broken
+ */
static MHD_FN_PAR_NONNULL_ (1) bool
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_and_resume_conn (d))
+ return false;
break;
#ifdef MHD_SUPPORT_SELECT
case mhd_POLL_TYPE_SELECT:
- if (! get_all_net_updates_by_select (d, false))
+ if (! get_all_net_updates_by_select_and_resume_conn (d, false))
return false;
break;
#endif /* MHD_SUPPORT_SELECT */
@@ -1105,12 +1662,14 @@ process_all_events_and_data (struct MHD_Daemon *restrict d)
case mhd_POLL_TYPE_POLL:
if (! get_all_net_updates_by_poll (d, false))
return false;
+ daemon_resume_conns_if_needed (d);
break;
#endif /* MHD_SUPPORT_POLL */
#ifdef MHD_SUPPORT_EPOLL
case mhd_POLL_TYPE_EPOLL:
if (! get_all_net_updates_by_epoll (d))
return false;
+ daemon_resume_conns_if_needed (d);
break;
#endif /* MHD_SUPPORT_EPOLL */
#ifndef MHD_SUPPORT_SELECT
@@ -1128,19 +1687,104 @@ process_all_events_and_data (struct MHD_Daemon *restrict d)
MHD_PANIC ("Daemon data integrity broken");
break;
}
- if (d->events.act_req.accept)
- {
- if (daemon_accept_new_conns (d))
- d->events.act_req.accept = false;
- else if (! d->net.listen.non_block)
- d->events.act_req.accept = false;
- }
+
+ if (d->events.accept_pending && ! d->conns.block_new)
+ d->events.accept_pending = ! daemon_accept_new_conns (d);
+
daemon_process_all_active_conns (d);
daemon_cleanup_upgraded_conns (d);
return ! d->threading.stop_requested;
}
+static
+MHD_FN_PAR_NONNULL_ (1) enum MHD_StatusCode
+process_reg_events_int (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;
+
+ /* 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 (daemon);
+
+ return MHD_SC_OK;
+}
+
+
+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;
+#ifdef mhd_DEBUG_POLLING_FDS
+ fprintf (stderr,
+ "### (Starting) MHD_daemon_process_reg_events(daemon, [%s])...\n",
+ (NULL != next_max_wait) ? "non-NULL" : "NULL");
+#endif
+ res = process_reg_events_int (daemon,
+ next_max_wait);
+#ifdef mhd_DEBUG_POLLING_FDS
+ if (NULL == next_max_wait)
+ fprintf (stderr,
+ "### (Finished) MHD_daemon_process_reg_events(daemon, [NULL]) ->"
+ "%u\n",
+ (unsigned int) res);
+ else if (MHD_WAIT_INDEFINITELY == *next_max_wait)
+ fprintf (stderr,
+ "### (Finished) MHD_daemon_process_reg_events(daemon, "
+ "->MHD_WAIT_INDEFINITELY) ->%u\n",
+ (unsigned int) res);
+ else
+ fprintf (stderr,
+ "### (Finished) MHD_daemon_process_reg_events(daemon, ->%llu) "
+ "->%u\n",
+ (unsigned long long) *next_max_wait,
+ (unsigned int) res);
+#endif
+ return res;
+}
+
+
/**
* The entry point for the daemon worker thread
* @param cls the closure
@@ -1167,9 +1811,6 @@ mhd_worker_all_events (void *cls)
while (! d->threading.stop_requested)
{
- if (d->threading.resume_requested)
- mhd_daemon_resume_conns (d);
-
if (! process_all_events_and_data (d))
break;
}
@@ -1179,7 +1820,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
@@ -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
@@ -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
@@ -0,0 +1,134 @@
+/*
+ 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_DEBUG_POLLING_FDS
+# include <stdio.h>
+# include "mhd_daemon.h"
+# include "mhd_assert.h"
+# include "sys_null_macro.h"
+#endif /* mhd_DEBUG_POLLING_FDS */
+
+#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 */
+
+#ifdef mhd_DEBUG_POLLING_FDS
+/**
+ * Call application event registration callback
+ * @param d the daemon to use
+ * @param fd the FD to register
+ * @param watch_for events/statuses to watch for
+ * @param app_cntx_old the previous application FD context
+ * @param ecb_cntx the MHD FD context
+ */
+MHD_static_inline_
+MHD_FN_PAR_NONNULL_ (1) void *
+mhd_daemon_extr_event_reg (struct MHD_Daemon *d,
+ MHD_Socket fd,
+ enum MHD_FdState watch_for,
+ void *app_cntx_old,
+ struct MHD_EventUpdateContext *ecb_cntx)
+{
+ void *res;
+ char state_str[] = "x:x:x";
+ const char *reg_type;
+ const char *fd_rel;
+
+ mhd_assert (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
+ mhd_assert (mhd_POLL_TYPE_EXT == d->events.poll_type);
+ mhd_assert (mhd_SOCKET_REL_MARKER_EMPTY != (mhd_SockRelMarker) ecb_cntx);
+
+ res =
+ d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls,
+ fd,
+ watch_for,
+ app_cntx_old,
+ ecb_cntx);
+
+ if (NULL == app_cntx_old)
+ reg_type = " Registration";
+ else if (MHD_FD_STATE_NONE == watch_for)
+ reg_type = "De-registration";
+ else
+ reg_type = "Re-registration";
+
+ state_str[0] = MHD_FD_STATE_IS_SET_RECV (watch_for) ? 'R' : '-';
+ state_str[2] = MHD_FD_STATE_IS_SET_SEND (watch_for) ? 'W' : '-';
+ state_str[4] = MHD_FD_STATE_IS_SET_EXCEPT (watch_for) ? 'E' : '-';
+
+ switch ((mhd_SockRelMarker) ecb_cntx)
+ {
+ case mhd_SOCKET_REL_MARKER_ITC:
+ fd_rel = "ITC: ";
+ break;
+ case mhd_SOCKET_REL_MARKER_LISTEN:
+ fd_rel = "lstn:";
+ break;
+ default:
+ fd_rel = "conn:";
+ break;
+ }
+ fprintf (stderr,
+ "### %s callback (cls, [%s %2llu], %s, 0x%08llX, ptr) "
+ "-> 0x%08llX\n",
+ reg_type,
+ fd_rel,
+ (unsigned long long) fd,
+ state_str,
+ (unsigned long long) app_cntx_old,
+ (unsigned long long) res);
+
+ return res;
+}
+#else /* ! mhd_DEBUG_POLLING_FDS */
+# define mhd_daemon_extr_event_reg(d,fd,w_for,app_cntx_old,ecb_cntx) \
+ d->events.data.extr.cb_data.cb (d->events.data.extr.cb_data.cls, \
+ fd, w_for, app_cntx_old, ecb_cntx)
+
+#endif /* ! mhd_DEBUG_POLLING_FDS */
+
+
+#endif /* ! MHD_EXTR_EVENTS_FUNCS_H */
diff --git a/src/mhd2/mhd_conn_socket.h b/src/mhd2/mhd_conn_socket.h
@@ -151,7 +151,7 @@ struct mhd_ConnSocket
struct mhd_ConnSocketState state;
/**
- * The receive / send / error readiness the socket
+ * The receive / send / error readiness of the socket
*/
enum mhd_SocketNetState ready;
diff --git a/src/mhd2/mhd_connection.h b/src/mhd2/mhd_connection.h
@@ -456,12 +456,31 @@ 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;
bool pre_cleaned;
bool removed_from_daemon;
bool tls_inited;
+ bool avoid_accept4;
};
/**
@@ -506,6 +525,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.
@@ -549,7 +573,7 @@ struct MHD_Connection
struct MHD_UpgradedHandle upgr;
/**
- * Double-linke list of HTTP-Upgraded connections waiting for clean-up
+ * Double-linked list of HTTP-Upgraded connections waiting for clean-up
*/
mhd_DLNKDL_LINKS (MHD_Connection,upgr_cleanup);
#endif /* MHD_SUPPORT_UPGRADE */
@@ -568,12 +592,12 @@ struct MHD_Connection
/**
* True if connection is suspended
*/
- bool suspended;
+ volatile bool suspended;
/**
* True if connection is resuming
*/
- bool resuming;
+ volatile bool resuming;
/**
* Request-specific data
diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h
@@ -215,9 +215,9 @@ typedef unsigned char *mhd_SockRelMarker;
#endif
-#define mhd_SOCKET_REL_MARKER_EMPTY ((mhd_SockRelMarker) 0)
+#define mhd_SOCKET_REL_MARKER_EMPTY ((mhd_SockRelMarker) (0))
-#define mhd_SOCKET_REL_MARKER_ITC ((mhd_SockRelMarker) - 1)
+#define mhd_SOCKET_REL_MARKER_ITC ((mhd_SockRelMarker) (-1))
#define mhd_SOCKET_REL_MARKER_LISTEN (mhd_SOCKET_REL_MARKER_ITC - 1)
/**
@@ -316,10 +316,10 @@ struct mhd_DaemonEventsEPollData
#endif
/**
- * Daemon's data for external events for sockets monitoring.
+ * Daemon's data for external events callback.
* Internal version of struct MHD_WorkModeExternalEventLoopCBParam.
*/
-struct mhd_DaemonEventsExternal
+struct mhd_DaemonEventsExternalCallback
{
/**
* Socket registration callback
@@ -331,6 +331,74 @@ struct mhd_DaemonEventsExternal
void *cls;
};
+
+#ifdef MHD_SUPPORT_THREADS
+/**
+ * External events data for ITC FD
+ */
+struct mhd_DaemonEventsExternalDaemonItcData
+{
+ /**
+ * Application context for ITC FD
+ */
+ 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 ITC FD
+ */
+ void *app_cntx;
+};
+
+/**
+ * Daemon's data for external events for sockets monitoring.
+ */
+struct mhd_DaemonEventsExternal
+{
+ /**
+ * Daemon's data for external events callback.
+ * Internal version of struct MHD_WorkModeExternalEventLoopCBParam.
+ */
+ struct mhd_DaemonEventsExternalCallback cb_data;
+
+#ifdef MHD_SUPPORT_THREADS
+ /**
+ * External events data for ITC FD
+ */
+ 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.
+ * If set to 'false' then only changed FDs must be registered.
+ */
+ bool reg_all;
+};
+
/**
* Type-specific events monitoring data
*/
@@ -362,9 +430,8 @@ union mhd_DaemonEventMonitoringTypeSpecificData
/**
* Daemon's data for external events for sockets monitoring.
- * Internal version of struct MHD_WorkModeExternalEventLoopCBParam.
*/
- struct mhd_DaemonEventsExternal ext;
+ struct mhd_DaemonEventsExternal extr;
};
@@ -374,9 +441,11 @@ union mhd_DaemonEventMonitoringTypeSpecificData
struct mhd_DaemonEventActionRequired
{
/**
- * If 'true' then connection is waiting to be accepted
+ * If 'true' connection resuming is required.
+ * TODO: implement resuming
+ * TODO: add initialisation
*/
- bool accept;
+ bool resume;
};
@@ -403,10 +472,9 @@ struct mhd_DaemonEventMonitoringData
struct mhd_DaemonEventActionRequired act_req;
/**
- * Indicate that daemon already has some data to be processed on the next
- * cycle
+ * When set to 'true' the listen socket has new incoming connection(s).
*/
- bool zero_wait;
+ bool accept_pending;
/**
* The list of the daemon's connections that need processing
@@ -452,6 +520,12 @@ struct mhd_ListenSocket
*/
MHD_Socket fd;
/**
+ * Set to 'true' when unrecoverable error is detected on the listen socket.
+ * If set to 'true', but @a fd is not #MHD_INVALID_SOCKET then "broken" state
+ * has not yet processed by MHD.
+ */
+ bool is_broken;
+ /**
* The type of the listening socket @a fd
*/
enum mhd_SocketType type;
@@ -485,6 +559,12 @@ struct mhd_DaemonNetworkSettings
/**
* The daemon network/sockets data
+ *
+ * This structure holds mostly static information—typically initialised once
+ * when the daemon starts, and possibly updated once later (e.g., if the
+ * listening socket fails or is closed).
+ *
+ * It does NOT contain any operational states.
*/
struct mhd_DaemonNetwork
{
@@ -1019,6 +1099,12 @@ struct MHD_Daemon
/**
* The daemon network/sockets data
+ *
+ * This structure holds mostly static information—typically initialised once
+ * when the daemon starts, and possibly updated once later (e.g., if the
+ * listening socket fails or is closed).
+ *
+ * It does NOT contain any operational states.
*/
struct mhd_DaemonNetwork net;
diff --git a/src/mhd2/mhd_dbg_print.h b/src/mhd2/mhd_dbg_print.h
@@ -0,0 +1,61 @@
+/*
+ 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/mhd_dbg_print.h
+ * @brief The declarations of interl debug-print helpers
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_DBG_PRINT_H
+#define MHD_DBG_PRINT_H 1
+
+#include "mhd_sys_options.h"
+
+#ifdef mhd_DEBUG_POLLING_FDS
+# include "sys_bool_type.h"
+# include "mhd_socket_type.h"
+#endif /* mhd_DEBUG_POLLING_FDS */
+
+
+#ifdef mhd_DEBUG_POLLING_FDS
+/**
+ * Debug-printf request of FD polling/monitoring
+ * @param fd_name the name of FD ("ITC", "lstn" or "conn")
+ * @param fd the FD value
+ * @param r_ready the request for read (or receive) readiness
+ * @param w_ready the request for write (or send) readiness
+ * @param e_ready the request for exception (or error) readiness
+ * @note Implemented in src/mhd2/events_process.c
+ */
+MHD_INTERNAL void
+mhd_dbg_print_fd_mon_req (const char *fd_name,
+ MHD_Socket fd,
+ bool r_ready,
+ bool w_ready,
+ bool e_ready)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#else /* ! mhd_DEBUG_POLLING_FDS */
+# define mhd_dbg_print_fd_mon_req(fd_n,fd,r_ready,w_ready,e_ready) ((void) 0)
+#endif /* ! mhd_DEBUG_POLLING_FDS */
+
+
+#endif /* ! MHD_DBG_PRINT_H */
diff --git a/src/mhd2/mhd_dlinked_list.h b/src/mhd2/mhd_dlinked_list.h
@@ -144,14 +144,14 @@
mhd_assert (NULL == (p_obj)->links_name.next); \
mhd_assert ((p_obj) != (p_list)->first); \
mhd_assert ((p_obj) != (p_list)->last); \
- mhd_assert (((p_list)->first) || (! ((p_list)->last))); \
- mhd_assert ((! ((p_list)->first)) || ((p_list)->last)); \
if (NULL != (p_list)->first) \
{ mhd_assert (NULL == (p_list)->first->links_name.prev); \
- mhd_assert ((p_obj) != (p_list)->first->links_name.next); \
- (p_obj)->links_name.next = (p_list)->first; \
- (p_obj)->links_name.next->links_name.prev = (p_obj); } else \
- { (p_list)->last = (p_obj); } \
+ mhd_assert ((p_obj) != (p_list)->first->links_name.next); \
+ mhd_assert (NULL != (p_list)->last); \
+ ((p_obj)->links_name.next = (p_list)->first) \
+ ->links_name.prev = (p_obj); } else \
+ { mhd_assert (NULL == (p_list)->last); \
+ (p_list)->last = (p_obj); } \
(p_list)->first = (p_obj); } while (0)
/**
@@ -167,14 +167,14 @@
mhd_assert (NULL == (p_obj)->links_name.next); \
mhd_assert ((p_obj) != (p_list)->first); \
mhd_assert ((p_obj) != (p_list)->last); \
- mhd_assert (((p_list)->first) || (! ((p_list)->last))); \
- mhd_assert ((! ((p_list)->first)) || ((p_list)->last)); \
if (NULL != (p_list)->last) \
{ mhd_assert (NULL == (p_list)->last->links_name.next); \
- mhd_assert ((p_obj) != (p_list)->last->links_name.prev); \
- (p_obj)->links_name.prev = (p_list)->last; \
- (p_obj)->links_name.prev->links_name.next = (p_obj); } else \
- { (p_list)->first = (p_obj); } \
+ mhd_assert ((p_obj) != (p_list)->last->links_name.prev); \
+ mhd_assert (NULL != (p_list)->first); \
+ ((p_obj)->links_name.prev = (p_list)->last) \
+ ->links_name.next = (p_obj); } else \
+ { mhd_assert (NULL == (p_list)->first); \
+ (p_list)->first = (p_obj); } \
(p_list)->last = (p_obj); } while (0)
/**
@@ -188,16 +188,18 @@
mhd_assert (NULL != (p_list)->first); \
mhd_assert (NULL != (p_list)->last); \
mhd_assert ((p_obj) != (p_obj)->links_name.prev); \
- mhd_assert ((p_obj) != (p_obj)->links_name.next); \
+ mhd_assert ((p_list)->last != (p_obj)->links_name.prev); \
+ mhd_assert ((p_obj) != (p_obj)->links_name.next); \
+ mhd_assert ((p_list)->first != (p_obj)->links_name.next); \
if (NULL != (p_obj)->links_name.next) \
- { mhd_assert (NULL != (p_obj)->links_name.next->links_name.prev); \
+ { mhd_assert ((p_obj) == (p_obj)->links_name.next->links_name.prev); \
mhd_assert ((p_obj) != (p_list)->last); \
(p_obj)->links_name.next->links_name.prev = \
(p_obj)->links_name.prev; } else \
{ mhd_assert ((p_obj) == (p_list)->last); \
(p_list)->last = (p_obj)->links_name.prev; } \
if (NULL != (p_obj)->links_name.prev) \
- { mhd_assert (NULL != (p_obj)->links_name.prev->links_name.next); \
+ { mhd_assert ((p_obj) == (p_obj)->links_name.prev->links_name.next); \
mhd_assert ((p_obj) != (p_list)->first); \
(p_obj)->links_name.prev->links_name.next = \
(p_obj)->links_name.next; } else \
diff --git a/src/mhd2/mhd_recv.c b/src/mhd2/mhd_recv.c
@@ -61,7 +61,8 @@ mhd_recv_plain (struct MHD_Connection *restrict c,
if (0 <= res)
{
*received = (size_t) res;
- if (buf_size > (size_t) res)
+ if ((buf_size > (size_t) res)
+ || ! c->sk.props.is_nonblck)
c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */
(((unsigned int) c->sk.ready)
& (~(enum mhd_SocketNetState)
@@ -109,6 +110,11 @@ mhd_recv_tls (struct MHD_Connection *restrict c,
mhd_SOCKET_NET_STATE_RECV_READY));
else if (mhd_SOCKET_ERR_NO_ERROR == res)
{
+ if (! c->sk.props.is_nonblck)
+ c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */
+ (((unsigned int) c->sk.ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_RECV_READY));
if (res == buf_size)
{
if (mhd_tls_conn_has_data_in (c->tls))
diff --git a/src/mhd2/mhd_send.c b/src/mhd2/mhd_send.c
@@ -805,7 +805,7 @@ mhd_send_plain (struct MHD_Connection *restrict c,
full_buf_sent = (buf_size == (size_t) res);
- if (! full_buf_sent)
+ if (! full_buf_sent || ! c->sk.props.is_nonblck)
c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
(((unsigned int) c->sk.ready)
& (~(enum mhd_SocketNetState)
@@ -858,6 +858,12 @@ mhd_send_tls (struct MHD_Connection *restrict c,
return res;
}
+ if (! c->sk.props.is_nonblck)
+ c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) c->sk.ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+
/* If there is a need to push the data from network buffers
* call post_send_setopt(). */
if (push_data && (buf_size == *sent))
@@ -1104,7 +1110,8 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
return err;
}
- if ((header_size + body_size) > *sent)
+ if (((header_size + body_size) > *sent)
+ || ! connection->sk.props.is_nonblck)
connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
(((unsigned int) connection->sk.ready)
& (~(enum mhd_SocketNetState)
@@ -1354,7 +1361,7 @@ mhd_send_sendfile (struct MHD_Connection *restrict c,
return mhd_SOCKET_ERR_INTR;
}
- if ((mhd_SOCKET_ERR_AGAIN == ret) ||
+ if ((mhd_SOCKET_ERR_AGAIN == ret) || ! c->sk.props.is_nonblck ||
((mhd_SOCKET_ERR_NO_ERROR == ret) && (send_size > sent_bytes)))
c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
(((unsigned int) c->sk.ready)
@@ -1502,6 +1509,13 @@ send_iov_nontls (struct MHD_Connection *restrict connection,
if (1)
{
size_t track_sent = (size_t) *sent;
+
+ if (! connection->sk.props.is_nonblck)
+ connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+ (((unsigned int) connection->sk.ready)
+ & (~(enum mhd_SocketNetState)
+ mhd_SOCKET_NET_STATE_SEND_READY));
+
/* Adjust the internal tracking information for the iovec to
* take this last send into account. */
while ((0 != track_sent) && (r_iov->iov[r_iov->sent].iov_len <= track_sent))
diff --git a/src/mhd2/request_resume.c b/src/mhd2/request_resume.c
@@ -0,0 +1,68 @@
+/*
+ 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/request_resume.c
+ * @brief The implementation of MHD_request_resume() function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_cntnr_ptr.h"
+
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+# include <stdio.h>
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+
+#include "daemon_funcs.h"
+
+#include "mhd_public_api.h"
+
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_ void
+MHD_request_resume (struct MHD_Request *request)
+{
+ struct MHD_Connection *c = mhd_CNTNR_PTR (request, \
+ struct MHD_Connection, \
+ rq);
+ struct MHD_Daemon *d = c->daemon;
+
+ if (! c->suspended)
+ {
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+ fprintf (stderr,
+ "%%%%%% Requested conn resume, FD: %2llu -> "
+ "failed as not suspended\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+ return;
+ }
+ c->resuming = true;
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+ fprintf (stderr,
+ "%%%%%% Requested conn resume, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+ d->threading.resume_requested = true;
+ mhd_daemon_trigger_itc (d);
+}
diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c
@@ -31,7 +31,11 @@
#include "mhd_assert.h"
#include "mhd_unreachable.h"
+#ifdef mhd_DEBUG_CONN_ADD_CLOSE
+# include <stdio.h>
+#endif /* mhd_DEBUG_CONN_ADD_CLOSE */
#include <string.h>
+#include "extr_events_funcs.h"
#ifdef MHD_SUPPORT_EPOLL
# include <sys/epoll.h>
#endif
@@ -53,6 +57,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"
@@ -566,12 +571,31 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
(0 == c->read_buffer_offset) ?
MHD_EVENT_LOOP_INFO_RECV : MHD_EVENT_LOOP_INFO_PROCESS;
+ // TODO: move request reset to special function
memset (&c->rq, 0, sizeof(c->rq));
+ // TODO: move reply reset to special function
/* iov (if any) will be deallocated by mhd_pool_reset */
memset (&c->rp, 0, sizeof(c->rp));
- // TODO: set all rq and tp pointers to NULL manually. Do the same in other places.
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+ // TODO: move request reset to special function
+ mhd_DLINKEDL_INIT_LIST (&(c->rq), fields);
+#ifdef MHD_SUPPORT_POST_PARSER
+ mhd_DLINKEDL_INIT_LIST (&(c->rq), post_fields);
+#endif /* MHD_SUPPORT_POST_PARSER */
+ c->rq.version = NULL;
+ c->rq.url = NULL;
+ c->rq.field_lines.start = NULL;
+ c->rq.app_context = NULL;
+ c->rq.hdrs.rq_line.rq_tgt = NULL;
+ c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
+
+ // TODO: move reply reset to special function
+ c->rp.app_act_ctx.connection = NULL;
+ c->rp.response = NULL;
+ c->rp.resp_iov.iov = NULL;
+#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
c->write_buffer = NULL;
c->write_buffer_size = 0;
@@ -672,6 +696,48 @@ mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
}
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_resumed_activity_mark (struct MHD_Connection *restrict c)
+{
+ struct MHD_Daemon *const restrict d = c->daemon;
+#if defined(MHD_SUPPORT_THREADS)
+ mhd_assert (! mhd_D_HAS_WORKERS (d));
+#endif /* MHD_SUPPORT_THREADS */
+
+ mhd_assert (! c->suspended);
+ mhd_assert (c->resuming);
+
+ /* Update of activity for connections without timeout timer unless
+ * no timeout is set */
+ if (0 != c->connection_timeout_ms)
+ c->last_activity = mhd_monotonic_msec_counter (); // TODO: Get and use time value one time per round
+
+ if (mhd_D_HAS_THR_PER_CONN (d))
+ return; /* each connection has personal timeout */
+
+ if (c->connection_timeout_ms == d->conns.cfg.timeout)
+ mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout);
+ else
+ mhd_DLINKEDL_INS_FIRST_D (&(d->conns.cust_timeout), c, by_timeout);
+}
+
+
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_remove_from_timeout_lists (struct MHD_Connection *restrict c)
+{
+ if (mhd_D_HAS_THR_PER_CONN (c->daemon))
+ return;
+
+ if (c->connection_timeout_ms == c->daemon->conns.cfg.timeout)
+ mhd_DLINKEDL_DEL_D (&(c->daemon->conns.def_timeout), \
+ c, by_timeout);
+ else
+ mhd_DLINKEDL_DEL_D (&(c->daemon->conns.cust_timeout), \
+ c, by_timeout);
+}
+
+
MHD_INTERNAL
MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3) void
mhd_conn_start_closing (struct MHD_Connection *restrict c,
@@ -682,6 +748,16 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
enum MHD_RequestEndedCode end_code;
enum MHD_StatusCode sc;
+#ifdef mhd_DEBUG_CONN_ADD_CLOSE
+ fprintf (stderr,
+ "&&& mhd_conn_start_closing([FD: %2llu], %u, %s%s%s)...\n",
+ (unsigned long long) c->sk.fd,
+ (unsigned int) reason,
+ log_msg ? "\"" : "",
+ log_msg ? log_msg : "[NULL]",
+ log_msg ? "\"" : "");
+#endif /* mhd_DEBUG_CONN_ADD_CLOSE */
+
sc = MHD_SC_INTERNAL_ERROR;
switch (reason)
{
@@ -750,6 +826,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;
@@ -914,14 +995,10 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
#endif
c->rq.app_aware = false;
- if (! mhd_D_HAS_THR_PER_CONN (c->daemon))
+ if (! c->suspended)
{
- if (c->connection_timeout_ms == c->daemon->conns.cfg.timeout)
- mhd_DLINKEDL_DEL_D (&(c->daemon->conns.def_timeout), \
- c, by_timeout);
- else
- mhd_DLINKEDL_DEL_D (&(c->daemon->conns.cust_timeout), \
- c, by_timeout);
+ mhd_assert (! c->resuming);
+ mhd_conn_remove_from_timeout_lists (c);
}
#ifndef NDEBUG
@@ -941,8 +1018,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 =
+ mhd_daemon_extr_event_reg (d,
+ 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;
@@ -965,7 +1057,11 @@ MHD_INTERNAL
MHD_FN_PAR_NONNULL_ (1) void
mhd_conn_pre_clean (struct MHD_Connection *restrict c)
{
- // TODO: support suspended connections
+#ifdef mhd_DEBUG_CONN_ADD_CLOSE
+ fprintf (stderr,
+ "&&& Closing connection, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_CONN_ADD_CLOSE */
mhd_assert (c->dbg.closing_started);
mhd_assert (! c->dbg.pre_cleaned);
diff --git a/src/mhd2/stream_funcs.h b/src/mhd2/stream_funcs.h
@@ -142,6 +142,24 @@ MHD_INTERNAL void
mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Update last activity mark on the resumed connection
+ * @param c the connection to update
+ */
+MHD_INTERNAL void
+mhd_stream_resumed_activity_mark (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Remove connection from time-out lists
+ * @param c the connection to process
+ */
+MHD_INTERNAL void
+mhd_conn_remove_from_timeout_lists (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
/**
* Check whether connection's timeout is expired.
* @param c the connection to update
@@ -229,6 +247,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
*/
mhd_CONN_CLOSE_NO_SYS_RESOURCES
@@ -321,6 +344,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"
diff --git a/src/mhd2/stream_process_request.c b/src/mhd2/stream_process_request.c
@@ -40,6 +40,10 @@
#include "sys_malloc.h"
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+# include <stdio.h>
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+
#include "mhd_str_types.h"
#include "mhd_str_macros.h"
#include "mhd_str.h"
@@ -3002,6 +3006,12 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
#endif /* MHD_SUPPORT_POST_PARSER */
case mhd_ACTION_SUSPEND:
c->suspended = true;
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+ fprintf (stderr,
+ "%%%%%% Suspending connection, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+ c->rq.app_act.head_act.act = mhd_ACTION_NO_ACTION;
return false;
#ifdef MHD_SUPPORT_UPGRADE
case mhd_ACTION_UPGRADE:
@@ -3063,6 +3073,12 @@ mhd_stream_process_upload_action (struct MHD_Connection *restrict c,
return false;
case mhd_UPLOAD_ACTION_SUSPEND:
c->suspended = true;
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+ fprintf (stderr,
+ "%%%%%% Suspending connection, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+ memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act));
return false;
#ifdef MHD_SUPPORT_UPGRADE
case mhd_UPLOAD_ACTION_UPGRADE:
diff --git a/src/mhd2/stream_process_states.c b/src/mhd2/stream_process_states.c
@@ -55,6 +55,10 @@
# include "upgrade_proc.h"
#endif /* MHD_SUPPORT_UPGRADE */
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+# include <stdio.h>
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_conn_event_loop_state_update (struct MHD_Connection *restrict c)
{
@@ -180,6 +184,26 @@ mhd_conn_event_loop_state_update (struct MHD_Connection *restrict c)
/**
+ * Finalise resuming of the connection
+ * @param c the connection to resume
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+finish_resume (struct MHD_Connection *restrict c)
+{
+ mhd_assert (c->resuming);
+ c->resuming = false;
+
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+ fprintf (stderr,
+ "%%%%%% Resumed connection, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
+ mhd_assert (! c->suspended);
+ mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS == c->event_loop_info);
+}
+
+
+/**
* Update current processing state: need to receive, need to send.
* Mark stream as ready or not ready for processing.
* Grow the receive buffer if neccesary, close stream if no buffer space left,
@@ -232,8 +256,8 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
struct MHD_Daemon *const d = c->daemon;
bool daemon_closing;
- /* 'daemon' is not used if epoll is not available and asserts are disabled */
- (void) d; /* Mute compiler warning */
+ if (c->suspended)
+ return true;
if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage))
{
@@ -248,13 +272,8 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
}
}
- mhd_assert (c->resuming || ! c->suspended);
if (c->resuming)
- {
- // TODO: finish resuming, update activity mark
- // TODO: move to special function
- (void) 0;
- }
+ finish_resume (c);
if ((mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err) ||
(0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY)))
@@ -278,6 +297,8 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
return false;
}
+ mhd_assert (! c->suspended);
+
while (! c->suspended)
{
#ifdef MHD_SUPPORT_HTTPS
@@ -509,8 +530,16 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
if (c->suspended)
{
- // TODO: process
- mhd_assert (0 && "Not implemented yet");
+ /* Do not perform any network activity while suspended */
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+
+ mhd_conn_mark_unready (c, d);
+ mhd_conn_remove_from_timeout_lists (c);
+#ifdef mhd_DEBUG_SUSPEND_RESUME
+ fprintf (stderr,
+ "%%%%%% Connection suspended, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+#endif /* mhd_DEBUG_SUSPEND_RESUME */
return true;
}
diff --git a/src/mhd2/sys_sockets_headers.h b/src/mhd2/sys_sockets_headers.h
@@ -124,9 +124,9 @@
#ifdef HAVE_DCLR_MSG_MORE
# ifdef __linux__
-/* MSG_MORE signal kernel to buffer outbond data and works like
- * TCP_CORK per call without actually setting TCP_CORK value.
- * It's known to work on Linux. Add more OSes if they are compatible. */
+/* MSG_MORE signal kernel to buffer outbound data and works like
+ TCP_CORK for a single call without actually setting the TCP_CORK flag.
+ It's known to work on Linux. Add more OSes if they are compatible. */
/**
* Indicate MSG_MORE is usable for buffered send().
*/
@@ -141,6 +141,12 @@
#endif
+#if defined(HAVE_ACCEPT4) && (defined(HAVE_DCLR_SOCK_NONBLOCK) || \
+ defined(HAVE_DCLR_SOCK_CLOEXEC) || defined(HAVE_DCLR_SOCK_NOSIGPIPE))
+# define mhd_USE_ACCEPT4 1
+#endif
+
+
/**
* mhd_SCKT_OPT_BOOL is the type for bool parameters
* for setsockopt()/getsockopt() functions