Browse Source

test case for external event loop

Christian Grothoff 10 months ago
parent
commit
9bab7100bd

+ 6 - 6
src/include/microhttpd2.h

@@ -4728,7 +4728,7 @@ MHD_FN_PAR_NONNULL_ALL_;
  *         error code otherwise
  */
 #define MHD_daemon_set_option(daemon, option_ptr) \
-        MHD_daemon_set_options (daemon, options_ptr, 1)
+        MHD_daemon_set_options (daemon, option_ptr, 1)
 
 
 /* *INDENT-OFF* */
@@ -6098,10 +6098,10 @@ MHD_response_from_callback (enum MHD_HTTP_StatusCode sc,
  *               needs to be valid while the response is used
  * @param free_cb the callback to free any allocated data, called
  *                when response is being destroyed, can be NULL
- *                to skip the free/cleanup callback
+ *                to skip the free/cleanup callback;
  * @param free_cb_cls the parameter for @a free_cb
  * @return NULL on error (i.e. invalid arguments, out of memory)
- * FIXME: Call free callback on error?
+ *   on error, @a free_cb is NOT called
  * @ingroup response
  */
 MHD_EXTERN_ struct MHD_Response *
@@ -6975,7 +6975,7 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh,
                    void *MHD_RESTRICT recv_buf,
                    size_t *MHD_RESTRICT received_size,
                    uint_fast64_t max_wait_millisec)
-MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2)
 MHD_FN_PAR_OUT_ (4);
 
 
@@ -7030,7 +7030,7 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh,
                    size_t *MHD_RESTRICT sent_size,
                    uint_fast64_t max_wait_millisec,
                    enum MHD_Bool more_data_to_come)
-MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_(3,2)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2)
 MHD_FN_PAR_OUT_ (4);
 
 
@@ -8365,7 +8365,7 @@ MHD_response_add_auth_basic_challenge (
   struct MHD_Response *MHD_RESTRICT response,
   const char *MHD_RESTRICT realm,
   enum MHD_Bool prefer_utf8)
-MHD_FN_PAR_NONNULL_(2) MHD_FN_PAR_CSTR_ (2);
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2);
 
 #ifndef MHD_NO_STATIC_INLINE
 

+ 27 - 0
src/tests/client_server/libtest.h

@@ -741,6 +741,19 @@ MHDT_server_setup_minimal (const void *cls,
                            struct MHD_Daemon *d);
 
 
+/**
+ * Initialize MHD daemon for an external event loop.
+ * Must be used together with #MHDT_server_run_external().
+ *
+ * @param cls closure (use NULL)
+ * @param[in,out] d daemon to initialize
+ * @return error message, NULL on success
+ */
+const char *
+MHDT_server_setup_external (const void *cls,
+                            struct MHD_Daemon *d);
+
+
 /**
  * Initialize MHD daemon with TLS support, binding to any free port.
  *
@@ -824,6 +837,20 @@ MHDT_server_run_blocking (void *cls,
                           struct MHD_Daemon *d);
 
 
+/**
+ * Function that runs an MHD daemon with an external event loop until
+ * a read() against @a finsig succeeds.
+ *
+ * @param cls closure
+ * @param finsig fd to read from to detect termination request
+ * @param[in,out] d daemon to run
+ */
+void
+MHDT_server_run_external (void *cls,
+                          int finsig,
+                          struct MHD_Daemon *d);
+
+
 /**
  * Run test suite with @a phases for a daemon initialized
  * using @a ss_cb on the local machine.

+ 198 - 0
src/tests/client_server/libtest_convenience.c

@@ -30,6 +30,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <curl/curl.h>
+#include <sys/epoll.h>
 
 
 const char *
@@ -290,3 +291,200 @@ MHDT_server_run_blocking (void *cls,
              "Failed to drain termination signal\n");
   }
 }
+
+
+static int my_epoll_fd = -1;
+
+
+/**
+ * The callback for registration/de-registration of the sockets to watch.
+ *
+ * This callback must not call #MHD_daemon_destroy(), #MHD_daemon_quiesce(),
+ * #MHD_daemon_add_connection().
+ *
+ * @param cls the closure
+ * @param fd the socket to watch
+ * @param watch_for the states of the @a fd to watch, if set to
+ *                  #MHD_FD_STATE_NONE the socket must be de-registred
+ * @param app_cntx_old the old application defined context for the socket,
+ *                     NULL if @a fd socket was not registered before
+ * @param ecb_cntx the context handle to be used
+ *                 with #MHD_daemon_event_update()
+ * @return NULL if error (to connection will be aborted),
+ *         or the new socket context
+ * @ingroup event
+ */
+static void *
+update_fd (
+  void *cls,
+  MHD_Socket fd,
+  enum MHD_FdState watch_for,
+  MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old,
+  struct MHD_EventUpdateContext *ecb_cntx)
+{
+  struct epoll_event ev;
+
+  if (watch_for == MHD_FD_STATE_NONE)
+  {
+    epoll_ctl (my_epoll_fd,
+               EPOLL_CTL_DEL,
+               fd,
+               &ev /* for Linux 2.6.9-compatibility */);
+    return NULL;
+  }
+  ev.data.ptr = ecb_cntx;
+  ev.events = 0;
+  if (0 != (watch_for & MHD_FD_STATE_RECV))
+    ev.events |= EPOLLIN;
+  if (0 != (watch_for & MHD_FD_STATE_SEND))
+    ev.events |= EPOLLOUT;
+  if (0 != (watch_for & MHD_FD_STATE_EXCEPT))
+    ev.events |= EPOLLRDHUP | EPOLLHUP | EPOLLERR;
+  if (0 !=
+      epoll_ctl (my_epoll_fd,
+                 NULL == app_cntx_old
+                 ? EPOLL_CTL_ADD
+                 : EPOLL_CTL_MOD,
+                 fd,
+                 &ev))
+  {
+    fprintf (stderr,
+             "epoll_ctl failed: %s\n",
+             strerror (errno));
+    return NULL;
+  }
+  return ecb_cntx;
+}
+
+
+const char *
+MHDT_server_setup_external (const void *cls,
+                            struct MHD_Daemon *d)
+{
+  (void) cls;
+  if (MHD_SC_OK !=
+      MHD_DAEMON_SET_OPTIONS (
+        d,
+        MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL (&update_fd,
+                                                      NULL)))
+    return "Failed to configure external mode!";
+  if (MHD_SC_OK !=
+      MHD_DAEMON_SET_OPTIONS (
+        d,
+        MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
+                                0)))
+    return "Failed to bind to port 0!";
+  my_epoll_fd = epoll_create1 (0);
+
+  return NULL;
+}
+
+
+void
+MHDT_server_run_external (void *cls,
+                          int finsig,
+                          struct MHD_Daemon *d)
+{
+  fd_set r;
+
+  (void) cls; /* Unused */
+  if (-1 == my_epoll_fd)
+    abort ();
+  while (1)
+  {
+    uint_fast64_t next_wait;
+    struct timeval timeout;
+
+    if (MHD_SC_OK !=
+        MHD_daemon_process_reg_events (d,
+                                       &next_wait))
+    {
+      fprintf (stderr,
+               "MHD_daemon_process_reg_events() failed\n");
+      break;
+    }
+    timeout.tv_usec = next_wait;
+
+    FD_ZERO (&r);
+    FD_SET (finsig,
+            &r);
+    FD_SET (my_epoll_fd,
+            &r);
+    if ( (-1 ==
+          select ((my_epoll_fd > finsig ? my_epoll_fd : finsig) + 1,
+                  &r,
+                  NULL,
+                  NULL,
+                  &timeout)) &&
+         (EAGAIN != errno) )
+    {
+      fprintf (stderr,
+               "Failure in select(): %s\n",
+               strerror (errno));
+      break;
+    }
+    if (FD_ISSET (finsig,
+                  &r))
+      break;
+    if (FD_ISSET (my_epoll_fd,
+                  &r))
+    {
+      int maxevents = 40;
+      struct epoll_event events[maxevents];
+      int n;
+      int i;
+
+      n = epoll_wait (my_epoll_fd,
+                      events,
+                      maxevents,
+                      0);
+      if (-1 == n)
+      {
+        fprintf (stderr,
+                 "epoll_wait() failed: %s\n",
+                 strerror (errno));
+        break;
+      }
+      for (i = 0; i < n; i++)
+      {
+        enum MHD_FdState state = MHD_FD_STATE_NONE;
+
+        if (0 != (events[i].events & EPOLLIN))
+          state |= MHD_FD_STATE_RECV;
+        if (0 != (events[i].events & EPOLLOUT))
+          state |= MHD_FD_STATE_SEND;
+        if (0 != (events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) )
+          state |= MHD_FD_STATE_EXCEPT;
+        MHD_daemon_event_update (d,
+                                 events[i].data.ptr,
+                                 state);
+      }
+
+      if (MHD_SC_OK !=
+          MHD_daemon_process_blocking (d,
+                                       1000))
+      {
+        fprintf (stderr,
+                 "Failure running MHD_daemon_process_blocking()\n");
+        break;
+      }
+    }
+  }
+
+  {
+    char c;
+
+    if ( (FD_ISSET (finsig,
+                    &r)) &&
+         (1 != read (finsig,
+                     &c,
+                     1)) )
+    {
+      fprintf (stderr,
+               "Failed to drain termination signal\n");
+    }
+  }
+
+  close (my_epoll_fd);
+  my_epoll_fd = -1;
+}

+ 6 - 0
src/tests/client_server/test_client_server.c

@@ -138,6 +138,12 @@ main (int argc, char *argv[])
       .server_setup_cls = external0auto,
       .server_runner = &MHDT_server_run_blocking,
     },
+    {
+      .label = "auto-selected external event loop mode, no threads",
+      .server_setup = &MHDT_server_setup_external,
+      .server_setup_cls = NULL,
+      .server_runner = &MHDT_server_run_external,
+    },
     {
       .label = "END"
     }