gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] branch master updated (3460db01 -> fa330be7)


From: gnunet
Subject: [libmicrohttpd] branch master updated (3460db01 -> fa330be7)
Date: Sun, 25 Oct 2020 15:26:23 +0100

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from 3460db01 daemon.c: fixed leak of resources if daemon is failed to start
     new 19b038f6 Fixed thread-safety for externally added connections Fully 
re-implemented scheme of adding connections from external thread (application)
     new fa330be7 Added tests for cleanup of unprocessed externally added 
connections

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/microhttpd/daemon.c      | 237 ++++++++++++++++++++++++++++++++++---------
 src/microhttpd/internal.h    |  21 ++++
 src/testcurl/.gitignore      |   2 +
 src/testcurl/Makefile.am     |  22 ++++
 src/testcurl/test_add_conn.c | 211 ++++++++++++++++++++++++++++++--------
 5 files changed, 400 insertions(+), 93 deletions(-)

diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 7ffd93cd..c0be6bc0 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -2606,35 +2606,56 @@ new_connection_prepare_ (struct MHD_Daemon *daemon,
 }
 
 
+/**
+ * Close prepared, but not yet processed connection.
+ * @param daemon     the daemon
+ * @param connection the connection to close
+ */
+static void
+new_connection_close_ (struct MHD_Daemon *daemon,
+                       struct MHD_Connection *connection)
+{
+  mhd_assert (connection->daemon == daemon);
+  mhd_assert (! connection->in_cleanup);
+  mhd_assert (NULL == connection->next);
+  mhd_assert (NULL == connection->nextX);
+#ifdef EPOLL_SUPPORT
+  mhd_assert (NULL == connection->nextE);
+#endif /* EPOLL_SUPPORT */
+
+#ifdef HTTPS_SUPPORT
+  if (NULL != connection->tls_session)
+  {
+    mhd_assert (0 != (daemon->options & MHD_USE_TLS));
+    gnutls_deinit (connection->tls_session);
+  }
+#endif /* HTTPS_SUPPORT */
+  MHD_socket_close_chk_ (connection->socket_fd);
+  MHD_ip_limit_del (daemon,
+                    connection->addr,
+                    connection->addr_len);
+  free (connection->addr);
+  free (connection);
+}
+
+
 /**
  * Finally insert the new connection to the list of connections
- * served by the daemon.
+ * 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 client_socket socket to manage (MHD will expect
- *        to receive an HTTP request from this socket next).
- * @param addr IP address of the client
- * @param addrlen number of bytes in @a addr
- * @param external_add perform additional operations needed due
- *        to the application calling us directly
  * @param connection the newly created connection
- * @return #MHD_YES on success, #MHD_NO if this daemon could
- *        not handle the connection (i.e. malloc failed, etc).
- *        The socket will be closed in any case; 'errno' is
- *        set to indicate further details about the error.
+ * @return #MHD_YES on success, #MHD_NO on error
  */
 static enum MHD_Result
-new_connection_insert_ (struct MHD_Daemon *daemon,
-                        MHD_socket client_socket,
-                        const struct sockaddr *addr,
-                        socklen_t addrlen,
-                        bool external_add,
-                        struct MHD_Connection *connection)
+new_connection_process_ (struct MHD_Daemon *daemon,
+                         struct MHD_Connection *connection)
 {
   int eno = 0;
 
+  mhd_assert (connection->daemon == daemon);
   /* Allocate memory pool in the processing thread so
    * intensively used memory area is allocated in "good"
    * (for the thread) memory region. It is important with
@@ -2647,10 +2668,10 @@ new_connection_insert_ (struct MHD_Daemon *daemon,
               _ ("Error allocating memory: %s\n"),
               MHD_strerror_ (errno));
 #endif
-    MHD_socket_close_chk_ (client_socket);
+    MHD_socket_close_chk_ (connection->socket_fd);
     MHD_ip_limit_del (daemon,
-                      addr,
-                      addrlen);
+                      connection->addr,
+                      connection->addr_len);
     free (connection);
 #if ENOMEM
     errno = ENOMEM;
@@ -2721,15 +2742,15 @@ new_connection_insert_ (struct MHD_Daemon *daemon,
 #ifdef EPOLL_SUPPORT
   if (0 != (daemon->options & MHD_USE_EPOLL))
   {
-    if ((0 == (daemon->options & MHD_USE_TURBO)) || (external_add))
-    { /* Do not manipulate EReady DL-list in 'external_add' mode. */
+    if (0 == (daemon->options & MHD_USE_TURBO))
+    {
       struct epoll_event event;
 
       event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
       event.data.ptr = connection;
       if (0 != epoll_ctl (daemon->epoll_fd,
                           EPOLL_CTL_ADD,
-                          client_socket,
+                          connection->socket_fd,
                           &event))
       {
         eno = errno;
@@ -2752,20 +2773,10 @@ new_connection_insert_ (struct MHD_Daemon *daemon,
                    connection);
     }
   }
-  else /* This 'else' is combined with next 'if'. */
-#endif
-  if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
-       (external_add) &&
-       (MHD_ITC_IS_VALID_ (daemon->itc)) &&
-       (! MHD_itc_activate_ (daemon->itc, "n")) )
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (daemon,
-              _ (
-                "Failed to signal new connection via inter-thread 
communication channel.\n"));
 #endif
-  }
+
   return MHD_YES;
+
 cleanup:
   if (NULL != daemon->notify_connection)
     daemon->notify_connection (daemon->notify_connection_cls,
@@ -2776,10 +2787,10 @@ cleanup:
   if (NULL != connection->tls_session)
     gnutls_deinit (connection->tls_session);
 #endif /* HTTPS_SUPPORT */
-  MHD_socket_close_chk_ (client_socket);
+  MHD_socket_close_chk_ (connection->socket_fd);
   MHD_ip_limit_del (daemon,
-                    addr,
-                    addrlen);
+                    connection->addr,
+                    connection->addr_len);
 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
   MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
 #endif
@@ -2880,8 +2891,82 @@ internal_add_connection (struct MHD_Daemon *daemon,
                                          non_blck, &connection))
     return MHD_NO;
 
-  return new_connection_insert_ (daemon, client_socket, addr, addrlen,
-                                 external_add, connection);
+  if ((external_add) &&
+      (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)))
+  {
+    /* Connection is added externally and MHD is handling its own threads. */
+    MHD_mutex_lock_chk_ (&daemon->new_connections_mutex);
+    DLL_insert (daemon->new_connections_head,
+                daemon->new_connections_tail,
+                connection);
+    daemon->have_new = true;
+    MHD_mutex_unlock_chk_ (&daemon->new_connections_mutex);
+
+    /* The rest of connection processing must be handled in
+     * the daemon thread. */
+    if ((MHD_ITC_IS_VALID_ (daemon->itc)) &&
+        (! MHD_itc_activate_ (daemon->itc, "n")))
+    {
+ #ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _ ("Failed to signal new connection via inter-thread " \
+                   "communication channel.\n"));
+ #endif
+    }
+    return MHD_YES;
+  }
+
+  return new_connection_process_ (daemon, connection);
+}
+
+
+static void
+new_connections_list_process_ (struct MHD_Daemon *daemon)
+{
+  struct MHD_Connection *local_head;
+  struct MHD_Connection *local_tail;
+  struct MHD_Connection *c;   /**< Currently processed connection */
+  mhd_assert (daemon->have_new);
+  mhd_assert (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD));
+
+  local_head = NULL;
+  local_tail = NULL;
+
+  /* Move all new connections to the local DL-list to release the mutex
+   * as quick as possible. */
+  MHD_mutex_lock_chk_ (&daemon->new_connections_mutex);
+  mhd_assert (NULL != daemon->new_connections_head);
+  do
+  { /* Move connection in FIFO order. */
+    c = daemon->new_connections_tail;
+    DLL_remove (daemon->new_connections_head,
+                daemon->new_connections_tail,
+                c);
+    DLL_insert (local_head,
+                local_tail,
+                c);
+  } while (NULL != daemon->new_connections_tail);
+  daemon->have_new = false;
+  MHD_mutex_unlock_chk_ (&daemon->new_connections_mutex);
+
+  /* Process new connections in FIFO order. */
+  do
+  {
+    c = local_tail;
+    DLL_remove (local_head,
+                local_tail,
+                c);
+    mhd_assert (daemon == c->daemon);
+    if (MHD_NO == new_connection_process_ (daemon, c))
+    {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _ ("Failed to start serving new connection.\n"));
+#endif
+      (void) 0;
+    }
+  } while (NULL != local_tail);
+
 }
 
 
@@ -3710,6 +3795,10 @@ internal_run_from_select (struct MHD_Daemon *daemon,
                   read_fd_set)) )
     MHD_itc_clear_ (daemon->itc);
 
+  /* Process externally added connection if any */
+  if (daemon->have_new)
+    new_connections_list_process_ (daemon);
+
   /* select connection thread handling type */
   if ( (MHD_INVALID_SOCKET != (ds = daemon->listen_fd)) &&
        (! daemon->was_quiesced) &&
@@ -4141,9 +4230,6 @@ MHD_poll_all (struct MHD_Daemon *daemon,
       return MHD_NO;
     }
 
-    /* Reset. New value will be set when connections are processed. */
-    daemon->data_already_pending = false;
-
     /* handle ITC FD */
     /* do it before any other processing so
        new signals will be processed in next loop */
@@ -4157,6 +4243,19 @@ MHD_poll_all (struct MHD_Daemon *daemon,
       free (p);
       return MHD_NO;
     }
+
+    /* Process externally added connection if any */
+    if (daemon->have_new)
+      new_connections_list_process_ (daemon);
+
+    /* handle 'listen' FD */
+    if ( (-1 != poll_listen) &&
+         (0 != (p[poll_listen].revents & POLLIN)) )
+      (void) MHD_accept_connection (daemon);
+
+    /* Reset. New value will be set when connections are processed. */
+    daemon->data_already_pending = false;
+
     i = 0;
     prev = daemon->connections_tail;
     while (NULL != (pos = prev))
@@ -4209,10 +4308,6 @@ MHD_poll_all (struct MHD_Daemon *daemon,
       }
     }
 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
-    /* handle 'listen' FD */
-    if ( (-1 != poll_listen) &&
-         (0 != (p[poll_listen].revents & POLLIN)) )
-      (void) MHD_accept_connection (daemon);
 
     free (p);
   }
@@ -4294,6 +4389,11 @@ MHD_poll_listen_socket (struct MHD_Daemon *daemon,
   /* handle shutdown */
   if (daemon->shutdown)
     return MHD_NO;
+
+  /* Process externally added connection if any */
+  if (daemon->have_new)
+    new_connections_list_process_ (daemon);
+
   if ( (-1 != poll_listen) &&
        (0 != (p[poll_listen].revents & POLLIN)) )
     (void) MHD_accept_connection (daemon);
@@ -4739,6 +4839,10 @@ MHD_epoll (struct MHD_Daemon *daemon,
     }
   }
 
+  /* Process externally added connection if any */
+  if (daemon->have_new)
+    new_connections_list_process_ (daemon);
+
   if (need_to_accept)
   {
     unsigned int series_length = 0;
@@ -6633,6 +6737,18 @@ MHD_start_daemon_va (unsigned int flags,
 #endif /* ! HAVE_LISTEN_SHUTDOWN */
     if (0 == daemon->worker_pool_size)
     {
+      if (! MHD_mutex_init_ (&daemon->new_connections_mutex))
+      {
+#ifdef HAVE_MESSAGES
+        MHD_DLOG (daemon,
+                  _ ("Failed to initialise mutex.\n"));
+#endif
+        MHD_mutex_destroy_chk_ (&daemon->cleanup_connection_mutex);
+        MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex);
+        if (MHD_INVALID_SOCKET != listen_fd)
+          MHD_socket_close_chk_ (listen_fd);
+        goto free_and_fail;
+      }
       if (! MHD_create_named_thread_ (&daemon->pid,
                                       (*pflags
                                        & MHD_USE_THREAD_PER_CONNECTION) ?
@@ -6646,6 +6762,7 @@ MHD_start_daemon_va (unsigned int flags,
                   _ ("Failed to create listen thread: %s\n"),
                   MHD_strerror_ (errno));
 #endif
+        MHD_mutex_destroy_chk_ (&daemon->new_connections_mutex);
         MHD_mutex_destroy_chk_ (&daemon->cleanup_connection_mutex);
         MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex);
         if (MHD_INVALID_SOCKET != listen_fd)
@@ -6685,7 +6802,14 @@ MHD_start_daemon_va (unsigned int flags,
         d->master = daemon;
         d->worker_pool_size = 0;
         d->worker_pool = NULL;
-
+        if (! MHD_mutex_init_ (&d->new_connections_mutex))
+        {
+  #ifdef HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    _ ("Failed to initialise mutex.\n"));
+  #endif
+          goto thread_failed;
+        }
         if (0 != (*pflags & MHD_USE_ITC))
         {
           if (! MHD_itc_init_ (d->itc))
@@ -6696,6 +6820,7 @@ MHD_start_daemon_va (unsigned int flags,
                         "Failed to create worker inter-thread communication 
channel: %s\n"),
                       MHD_itc_last_strerror_ () );
 #endif
+            MHD_mutex_destroy_chk_ (&d->new_connections_mutex);
             goto thread_failed;
           }
           if ( (0 == (*pflags & (MHD_USE_POLL | MHD_USE_EPOLL))) &&
@@ -6707,6 +6832,7 @@ MHD_start_daemon_va (unsigned int flags,
                       _ (
                         "File descriptor for worker inter-thread communication 
channel exceeds maximum value.\n"));
 #endif
+            MHD_mutex_destroy_chk_ (&d->new_connections_mutex);
             MHD_itc_destroy_chk_ (d->itc);
             goto thread_failed;
           }
@@ -6733,6 +6859,7 @@ MHD_start_daemon_va (unsigned int flags,
         {
           if (MHD_ITC_IS_VALID_ (d->itc))
             MHD_itc_destroy_chk_ (d->itc);
+          MHD_mutex_destroy_chk_ (&d->new_connections_mutex);
           goto thread_failed;
         }
 #endif
@@ -6745,6 +6872,7 @@ MHD_start_daemon_va (unsigned int flags,
 #endif
           if (MHD_ITC_IS_VALID_ (d->itc))
             MHD_itc_destroy_chk_ (d->itc);
+          MHD_mutex_destroy_chk_ (&d->new_connections_mutex);
           goto thread_failed;
         }
 
@@ -6765,6 +6893,7 @@ MHD_start_daemon_va (unsigned int flags,
           MHD_mutex_destroy_chk_ (&d->cleanup_connection_mutex);
           if (MHD_ITC_IS_VALID_ (d->itc))
             MHD_itc_destroy_chk_ (d->itc);
+          MHD_mutex_destroy_chk_ (&d->new_connections_mutex);
           goto thread_failed;
         }
       }
@@ -6879,6 +7008,17 @@ close_all_connections (struct MHD_Daemon *daemon)
   mhd_assert (NULL == daemon->worker_pool);
 #endif
   mhd_assert (daemon->shutdown);
+
+  /* Remove externally added new connections that are
+   * not processed by the daemon thread. */
+  while (NULL != (pos = daemon->new_connections_tail))
+  {
+    mhd_assert (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD));
+    DLL_remove (daemon->new_connections_head,
+                daemon->new_connections_tail,
+                pos);
+    new_connection_close_ (daemon, pos);
+  }
   /* give upgraded HTTPS connections a chance to finish */
   /* 'daemon->urh_head' is not used in thread-per-connection mode. */
   for (urh = daemon->urh_tail; NULL != urh; urh = urhn)
@@ -7126,6 +7266,7 @@ MHD_stop_daemon (struct MHD_Daemon *daemon)
     }
     if (MHD_ITC_IS_VALID_ (daemon->itc))
       MHD_itc_destroy_chk_ (daemon->itc);
+    MHD_mutex_destroy_chk_ (&daemon->new_connections_mutex);
 
 #ifdef EPOLL_SUPPORT
     if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 5fb3da3d..3b044984 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -1301,6 +1301,16 @@ struct MHD_Daemon
    */
   void *default_handler_cls;
 
+  /**
+   * Head of doubly-linked list of new, externally added connections.
+   */
+  struct MHD_Connection *new_connections_head;
+
+  /**
+   * Tail of doubly-linked list of new, externally added connections.
+   */
+  struct MHD_Connection *new_connections_tail;
+
   /**
    * Head of doubly-linked list of our current, active connections.
    */
@@ -1516,6 +1526,11 @@ struct MHD_Daemon
    * "manual_timeout" DLLs.
    */
   MHD_mutex_ cleanup_connection_mutex;
+
+  /**
+   * Mutex for any access to the "new connections" DL-list.
+   */
+  MHD_mutex_ new_connections_mutex;
 #endif
 
   /**
@@ -1600,6 +1615,12 @@ struct MHD_Daemon
    */
   volatile bool resuming;
 
+  /**
+   * Indicate that new connections in @e new_connections_head list
+   * need to be processed.
+   */
+  volatile bool have_new;
+
   /**
    * 'True' if some data is already waiting to be processed.
    * If set to 'true' - zero timeout for select()/poll*()
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore
index 4c1c7f03..0ca3ac3c 100644
--- a/src/testcurl/.gitignore
+++ b/src/testcurl/.gitignore
@@ -113,4 +113,6 @@ test_patch
 test_patch11
 /test_add_conn
 /test_add_conn_nolisten
+/test_add_conn_cleanup
+/test_add_conn_cleanup_nolisten
 core
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index f45f13d3..6587c79a 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -23,6 +23,8 @@ THREAD_ONLY_TESTS = \
   test_long_header11 \
   test_iplimit11 \
   test_termination \
+  test_add_conn_cleanup \
+  test_add_conn_cleanup_nolisten \
   test_timeout
 
 if HAVE_POSIX_THREADS
@@ -276,16 +278,36 @@ test_put_chunked_LDADD = \
   
 test_add_conn_SOURCES = \
   test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
+test_add_conn_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
 test_add_conn_LDADD = \
   $(top_builddir)/src/microhttpd/libmicrohttpd.la \
   @LIBCURL@
 
 test_add_conn_nolisten_SOURCES = \
   test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
+test_add_conn_nolisten_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
 test_add_conn_nolisten_LDADD = \
   $(top_builddir)/src/microhttpd/libmicrohttpd.la \
   @LIBCURL@
 
+test_add_conn_cleanup_SOURCES = \
+  test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
+test_add_conn_cleanup_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+test_add_conn_cleanup_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_add_conn_cleanup_nolisten_SOURCES = \
+  test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
+test_add_conn_cleanup_nolisten_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+test_add_conn_cleanup_nolisten_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
 test_get11_SOURCES = \
   test_get.c
 test_get11_LDADD = \
diff --git a/src/testcurl/test_add_conn.c b/src/testcurl/test_add_conn.c
index 52aa2831..c7e49b91 100644
--- a/src/testcurl/test_add_conn.c
+++ b/src/testcurl/test_add_conn.c
@@ -57,6 +57,11 @@
 #if ! defined(CPU_COUNT)
 #define CPU_COUNT 2
 #endif
+#if CPU_COUNT > 32
+#undef CPU_COUNT
+/* Limit to reasonable value */
+#define CPU_COUNT 32
+#endif /* CPU_COUNT > 32 */
 
 /* Could be increased to facilitate debugging */
 #define TIMEOUTS_VAL 5
@@ -65,9 +70,16 @@
 #define EXPECTED_URI_QUERY      "a=%26&b=c"
 #define EXPECTED_URI_FULL_PATH  EXPECTED_URI_BASE_PATH "?" EXPECTED_URI_QUERY
 
-static int oneone;
-static int no_listen;
-static int global_port;
+/* Global parameters */
+static int oneone;         /**< Use HTTP/1.1 instead of HTTP/1.0 */
+static int no_listen;      /**< Start MHD daemons without listen socket */
+static int global_port;    /**< MHD deamons listen port number */
+static int cleanup_test;   /**< Test for final cleanup */
+static int slow_reply = 0; /**< Slowdown MHD replies */
+static int ignore_response_errors = 0; /**< Do not fail test if CURL
+                                            returns error */
+static int response_timeout_val = TIMEOUTS_VAL;
+
 
 struct CBC
 {
@@ -165,6 +177,9 @@ ahc_echo (void *cls,
              NULL == v ? "NULL" : v);
     _exit (19);
   }
+  if (slow_reply)
+    usleep (200000);
+
   response = MHD_create_response_from_buffer (strlen (url),
                                               (void *) url,
                                               MHD_RESPMEM_MUST_COPY);
@@ -401,8 +416,8 @@ curlEasyInitForTest (const char *queryPath, int port, 
struct CBC *pcbc)
   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
   curl_easy_setopt (c, CURLOPT_WRITEDATA, pcbc);
-  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, (long) TIMEOUTS_VAL);
-  curl_easy_setopt (c, CURLOPT_TIMEOUT, (long) TIMEOUTS_VAL);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, (long) response_timeout_val);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, (long) response_timeout_val);
   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
   if (oneone)
     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
@@ -434,6 +449,11 @@ doCurlQueryInThread (struct curlQueryParams *p)
   c = curlEasyInitForTest (p->queryPath, p->queryPort, &cbc);
 
   errornum = curl_easy_perform (c);
+  if (ignore_response_errors)
+  {
+    p->queryError = 0;
+    return p->queryError;
+  }
   if (CURLE_OK != errornum)
   {
     fprintf (stderr,
@@ -511,11 +531,11 @@ performTestQueries (struct MHD_Daemon *d, int d_port)
 {
   struct curlQueryParams qParam;
   struct addConnParam aParam;
-  int a_port = 0;           /* Additional listening socket port */
-  int ret = 0;              /* Return value */
+  int a_port;           /* Additional listening socket port */
+  int ret = 0;          /* Return value */
 
   qParam.queryPath = "http://127.0.0.1"; EXPECTED_URI_FULL_PATH;
-  qParam.queryPort = 0; /* autoassign */
+  a_port = 0; /* auto-assign */
 
   aParam.d = d;
   aParam.lstn_sk = createListeningSocket (&a_port); /* Sets a_port */
@@ -550,6 +570,59 @@ performTestQueries (struct MHD_Daemon *d, int d_port)
 }
 
 
+/* Perform test for cleanup and shutdown MHD daemon */
+static int
+performTestCleanup (struct MHD_Daemon *d, int num_queries)
+{
+  struct curlQueryParams *qParamList;
+  struct addConnParam aParam;
+  MHD_socket lstn_sk;   /* Additional listening socket */
+  int a_port;           /* Additional listening socket port */
+  int i;
+  int ret = 0;          /* Return value */
+
+  a_port = 0; /* auto-assign */
+
+  if (0 >= num_queries)
+    abort (); /* Test's API violation */
+
+  lstn_sk = createListeningSocket (&a_port); /* Sets a_port */
+
+  qParamList = malloc (sizeof(struct curlQueryParams) * num_queries);
+  if (NULL == qParamList)
+    externalErrorExitDesc ("malloc failed");
+
+  /* Start CURL queries */
+  for (i = 0; i < num_queries; i++)
+  {
+    qParamList[i].queryPath = "http://127.0.0.1"; EXPECTED_URI_FULL_PATH;
+    qParamList[i].queryError = 0;
+    qParamList[i].queryPort = a_port;
+
+    startThreadCurlQuery (qParamList + i);
+  }
+
+  /* Accept and add required number of client sockets */
+  aParam.d = d;
+  aParam.lstn_sk = lstn_sk;
+  for (i = 0; i < num_queries; i++)
+    ret |= doAcceptAndAddConnInThread (&aParam);
+
+  /* Stop daemon while some of new connection are not yet
+   * processed because of slow response to the first queries. */
+  MHD_stop_daemon (d);
+  (void) MHD_socket_close_ (aParam.lstn_sk);
+
+  /* Wait for CURL threads to complete. */
+  /* Ignore soft CURL errors as many connection shouldn't get any response.
+   * Hard failures are detected in processing function. */
+  for (i = 0; i < num_queries; i++)
+    (void) finishThreadCurlQuery (qParamList + i);
+
+  return ret;
+}
+
+
 #endif /* HAVE_PTHREAD_H */
 
 enum testMhdThreadsType
@@ -578,7 +651,15 @@ startTestMhdDaemon (enum testMhdThreadsType thrType,
 
   if ( (0 == *pport) &&
        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 
)
-    *pport = oneone ? 1550 : 1570;
+  {
+    *pport = 1550;
+    if (oneone)
+      *pport += 1;
+    if (no_listen)
+      *pport += 2;
+    if (cleanup_test)
+      *pport += 4;
+  }
 
   if (testMhdThreadInternalPool != thrType)
     d = MHD_start_daemon (((int) thrType) | ((int) pollType)
@@ -656,6 +737,10 @@ testExternalGet (void)
   cbc_a.size = sizeof(buf_a);
   cbc_a.pos = 0;
 
+  if (cleanup_test)
+    abort (); /* Not possible with "external poll" as connections are directly
+                 added to the daemon processing in the mode. */
+
   if (! no_listen)
     c_d = curlEasyInitForTest ("http://127.0.0.1"; EXPECTED_URI_FULL_PATH,
                                d_port, &cbc_d);
@@ -828,6 +913,8 @@ testInternalGet (enum testMhdPollType pollType)
 
   d = startTestMhdDaemon (testMhdThreadInternal, pollType,
                           &d_port);
+  if (cleanup_test)
+    return performTestCleanup (d, 10);
 
   return performTestQueries (d, d_port);
 }
@@ -841,6 +928,10 @@ testMultithreadedGet (enum testMhdPollType pollType)
 
   d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType,
                           &d_port);
+  if (cleanup_test)
+    abort (); /* Cannot be tested as main daemon thread cannot be slowed down
+                 by slow responses, so it processes all new connections before
+                 daemon could be stopped. */
 
   return performTestQueries (d, d_port);
 }
@@ -855,6 +946,9 @@ testMultithreadedPoolGet (enum testMhdPollType pollType)
   d = startTestMhdDaemon (testMhdThreadInternalPool, pollType,
                           &d_port);
 
+  if (cleanup_test)
+    return performTestCleanup (d, 10 * CPU_COUNT);
+
   return performTestQueries (d, d_port);
 }
 
@@ -930,19 +1024,40 @@ main (int argc, char *const *argv)
   if ((NULL == argv) || (0 == argv[0]))
     return 99;
   oneone = has_in_name (argv[0], "11");
+  /* Whether to test MHD daemons without listening socket. */
   no_listen = has_in_name (argv[0], "_nolisten");
+  /* Whether to test for correct final cleanup instead of
+   * of test of normal processing. */
+  cleanup_test = has_in_name (argv[0], "_cleanup");
+  /* There are almost nothing that could be tested externally
+   * for final cleanup. Cleanup test actually just tests that
+   * nothing fails or crashes when final cleanup is performed.
+   * Mostly useful when configured with '--enable-asserts. */
+  slow_reply = cleanup_test;
+  ignore_response_errors = cleanup_test;
+  if (cleanup_test)
+    response_timeout_val /= 5;
+  if (0 == response_timeout_val)
+    response_timeout_val = 1;
+#ifndef HAVE_PTHREAD_H
+  if (cleanup_test)
+    return 77; /* Cannot run without threads */
+#endif /* HAVE_PTHREAD_H */
   verbose = ! has_param (argc, argv, "-q") || has_param (argc, argv, 
"--quiet");
   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
     return 2;
   /* Could be set to non-zero value to enforce using specific port
    * in the test */
   global_port = 0;
-  test_result = testExternalGet ();
-  if (test_result)
-    fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
-  else if (verbose)
-    printf ("PASSED: testExternalGet ().\n");
-  errorCount += test_result;
+  if (! cleanup_test)
+  {
+    test_result = testExternalGet ();
+    if (test_result)
+      fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
+    else if (verbose)
+      printf ("PASSED: testExternalGet ().\n");
+    errorCount += test_result;
+  }
 #ifdef HAVE_PTHREAD_H
   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
   {
@@ -953,14 +1068,6 @@ main (int argc, char *const *argv)
     else if (verbose)
       printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
     errorCount += test_result;
-    test_result = testMultithreadedGet (testMhdPollBySelect);
-    if (test_result)
-      fprintf (stderr,
-               "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
-               test_result);
-    else if (verbose)
-      printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
-    errorCount += test_result;
     test_result = testMultithreadedPoolGet (testMhdPollBySelect);
     if (test_result)
       fprintf (stderr,
@@ -969,45 +1076,59 @@ main (int argc, char *const *argv)
     else if (verbose)
       printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
     errorCount += test_result;
-    test_result = testStopRace (testMhdPollBySelect);
-    if (test_result)
-      fprintf (stderr, "FAILED: testStopRace (testMhdPollBySelect) - %u.\n",
-               test_result);
-    else if (verbose)
-      printf ("PASSED: testStopRace (testMhdPollBySelect).\n");
-    errorCount += test_result;
-    if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
+    if (! cleanup_test)
     {
-      test_result = testInternalGet (testMhdPollByPoll);
+      test_result = testMultithreadedGet (testMhdPollBySelect);
       if (test_result)
-        fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
+        fprintf (stderr,
+                 "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
                  test_result);
       else if (verbose)
-        printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
+        printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
       errorCount += test_result;
-      test_result = testMultithreadedGet (testMhdPollByPoll);
+      test_result = testStopRace (testMhdPollBySelect);
       if (test_result)
-        fprintf (stderr,
-                 "FAILED: testMultithreadedGet (testMhdPollByPoll) - %u.\n",
+        fprintf (stderr, "FAILED: testStopRace (testMhdPollBySelect) - %u.\n",
                  test_result);
       else if (verbose)
-        printf ("PASSED: testMultithreadedGet (testMhdPollByPoll).\n");
+        printf ("PASSED: testStopRace (testMhdPollBySelect).\n");
       errorCount += test_result;
-      test_result = testMultithreadedPoolGet (testMhdPollByPoll);
+    }
+    if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
+    {
+      test_result = testInternalGet (testMhdPollByPoll);
       if (test_result)
-        fprintf (stderr,
-                 "FAILED: testMultithreadedPoolGet (testMhdPollByPoll) - 
%u.\n",
+        fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
                  test_result);
       else if (verbose)
-        printf ("PASSED: testMultithreadedPoolGet (testMhdPollByPoll).\n");
+        printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
       errorCount += test_result;
-      test_result = testStopRace (testMhdPollByPoll);
+      test_result = testMultithreadedPoolGet (testMhdPollByPoll);
       if (test_result)
-        fprintf (stderr, "FAILED: testStopRace (testMhdPollByPoll) - %u.\n",
+        fprintf (stderr,
+                 "FAILED: testMultithreadedPoolGet (testMhdPollByPoll) - 
%u.\n",
                  test_result);
       else if (verbose)
-        printf ("PASSED: testStopRace (testMhdPollByPoll).\n");
+        printf ("PASSED: testMultithreadedPoolGet (testMhdPollByPoll).\n");
       errorCount += test_result;
+      if (! cleanup_test)
+      {
+        test_result = testMultithreadedGet (testMhdPollByPoll);
+        if (test_result)
+          fprintf (stderr,
+                   "FAILED: testMultithreadedGet (testMhdPollByPoll) - %u.\n",
+                   test_result);
+        else if (verbose)
+          printf ("PASSED: testMultithreadedGet (testMhdPollByPoll).\n");
+        errorCount += test_result;
+        test_result = testStopRace (testMhdPollByPoll);
+        if (test_result)
+          fprintf (stderr, "FAILED: testStopRace (testMhdPollByPoll) - %u.\n",
+                   test_result);
+        else if (verbose)
+          printf ("PASSED: testStopRace (testMhdPollByPoll).\n");
+        errorCount += test_result;
+      }
     }
     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
     {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]