gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] branch master updated (3dd2ee38 -> 54e12358)


From: gnunet
Subject: [libmicrohttpd] branch master updated (3dd2ee38 -> 54e12358)
Date: Mon, 25 Apr 2022 21:52:41 +0200

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

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from 3dd2ee38 configure: fixed clang's "argument unused"
     new efdd6e0f MHD_set_panic_func: clarified doxy
     new 54e12358 set_test_panic: added new test

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:
 README                                             |   3 +-
 src/include/microhttpd.h                           |  25 +-
 src/microhttpd/Makefile.am                         |   8 +-
 src/microhttpd/mhd_panic.c                         |  20 +-
 .../{test_client_put_stop.c => test_set_panic.c}   | 753 +++++----------------
 5 files changed, 204 insertions(+), 605 deletions(-)
 copy src/microhttpd/{test_client_put_stop.c => test_set_panic.c} (67%)

diff --git a/README b/README
index 7d355292..a330e191 100644
--- a/README
+++ b/README
@@ -84,9 +84,8 @@ library stable, we should have testcases for the following 
features:
 - MHD basic and digest authentication
 
 In particular, the following functions are not covered by 'make check':
-- mhd_panic_std (daemon.c); special case (abort)
+- mhd_panic_std (mhd_panic.c); special case (abort)
 - parse_options (daemon.c)
-- MHD_set_panic_func (daemon.c)
 - MHD_get_version (daemon.c)
 
 
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index b4bc85e3..06ca6964 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -2355,7 +2355,8 @@ enum MHD_DaemonInfoType
  * an error message and `abort()`.
  *
  * @param cls user specified value
- * @param file where the error occurred
+ * @param file where the error occurred, may be NULL if MHD was built without
+ *             messages support
  * @param line where the error occurred
  * @param reason error detail, may be NULL
  * @ingroup logging
@@ -3138,16 +3139,18 @@ MHD_set_connection_value_n (struct MHD_Connection 
*connection,
 
 
 /**
- * Sets the global error handler to a different implementation.  @a cb
- * will only be called in the case of typically fatal, serious
- * internal consistency issues.  These issues should only arise in the
- * case of serious memory corruption or similar problems with the
- * architecture.  While @a cb is allowed to return and MHD will then
- * try to continue, this is never safe.
- *
- * The default implementation that is used if no panic function is set
- * simply prints an error message and calls `abort()`.  Alternative
- * implementations might call `exit()` or other similar functions.
+ * Sets the global error handler to a different implementation.
+ *
+ * @a cb will only be called in the case of typically fatal, serious internal
+ * consistency issues or serious system failures like failed lock of mutex.
+ *
+ * These issues should only arise in the case of serious memory corruption or
+ * similar problems with the architecture, there is no safe way to continue
+ * even for closing of the application.
+ *
+ * The default implementation that is used if no panic function is set simply
+ * prints an error message and calls `abort()`.
+ * Alternative implementations might call `exit()` or other similar functions.
  *
  * @param cb new error handler or NULL to use default handler
  * @param cls passed to @a cb
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index 6a08fe70..8db55a9a 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -207,7 +207,8 @@ check_PROGRAMS = \
   test_client_put_chunked_steps_shutdown \
   test_client_put_chunked_steps_close \
   test_client_put_chunked_steps_hard_close \
-  test_options
+  test_options \
+  test_set_panic
 
 if HAVE_POSIX_THREADS
 if ENABLE_UPGRADE
@@ -488,3 +489,8 @@ test_client_put_chunked_steps_hard_close_SOURCES = \
   test_client_put_stop.c
 test_client_put_chunked_steps_hard_close_LDADD = \
   libmicrohttpd.la
+
+test_set_panic_SOURCES = \
+  test_set_panic.c
+test_set_panic_LDADD = \
+  libmicrohttpd.la
diff --git a/src/microhttpd/mhd_panic.c b/src/microhttpd/mhd_panic.c
index ae2db248..dfe1f6b5 100644
--- a/src/microhttpd/mhd_panic.c
+++ b/src/microhttpd/mhd_panic.c
@@ -73,16 +73,18 @@ mhd_panic_std (void *cls,
 
 
 /**
- * Sets the global error handler to a different implementation.  @a cb
- * will only be called in the case of typically fatal, serious
- * internal consistency issues.  These issues should only arise in the
- * case of serious memory corruption or similar problems with the
- * architecture.  While @a cb is allowed to return and MHD will then
- * try to continue, this is never safe.
+ * Sets the global error handler to a different implementation.
  *
- * The default implementation that is used if no panic function is set
- * simply prints an error message and calls `abort()`.  Alternative
- * implementations might call `exit()` or other similar functions.
+ * @a cb will only be called in the case of typically fatal, serious internal
+ * consistency issues or serious system failures like failed lock of mutex.
+ *
+ * These issues should only arise in the case of serious memory corruption or
+ * similar problems with the architecture, there is no safe way to continue
+ * even for closing of the application.
+ *
+ * The default implementation that is used if no panic function is set simply
+ * prints an error message and calls `abort()`.
+ * Alternative implementations might call `exit()` or other similar functions.
  *
  * @param cb new error handler or NULL to use default handler
  * @param cls passed to @a cb
diff --git a/src/microhttpd/test_client_put_stop.c 
b/src/microhttpd/test_set_panic.c
similarity index 67%
copy from src/microhttpd/test_client_put_stop.c
copy to src/microhttpd/test_set_panic.c
index 09845ff9..2d81c620 100644
--- a/src/microhttpd/test_client_put_stop.c
+++ b/src/microhttpd/test_set_panic.c
@@ -1,6 +1,6 @@
 /*
      This file is part of libmicrohttpd
-     Copyright (C) 2021 Evgeny Grin (Karlson2k)
+     Copyright (C) 2021-2022 Evgeny Grin (Karlson2k)
 
      libmicrohttpd is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -18,8 +18,8 @@
      Boston, MA 02110-1301, USA.
 */
 /**
- * @file test_client_put_stop.c
- * @brief  Testcase for handling of clients aborts
+ * @file test_set_panic.c
+ * @brief  Testcase for MHD_set_panic_func()
  * @author Karlson2k (Evgeny Grin)
  * @author Christian Grothoff
  */
@@ -217,7 +217,7 @@ _mhdErrorExit_func (const char *errDesc, const char 
*funcName, int lineNum)
  * Pause execution for specified number of milliseconds.
  * @param ms the number of milliseconds to sleep
  */
-void
+static void
 _MHD_sleep (uint32_t ms)
 {
 #if defined(_WIN32)
@@ -255,92 +255,11 @@ _MHD_sleep (uint32_t ms)
 
 /* Global parameters */
 static int verbose;                 /**< Be verbose */
-static int oneone;                  /**< If false use HTTP/1.0 for requests*/
-static int global_port;             /**< MHD daemons listen port number */
-
-static int use_shutdown;            /**< Use shutdown at client side */
-static int use_close;               /**< Use socket close at client side */
-static int use_hard_close;          /**< Use socket close with RST at client 
side */
-static int by_step;                 /**< Send request byte-by-byte */
-static int upl_chunked;             /**< Use chunked encoding for request body 
*/
-
-static unsigned int rate_limiter;   /**< Maximum number of checks per second */
+static uint16_t global_port;        /**< MHD daemons listen port number */
 
 static void
 test_global_init (void)
 {
-  rate_limiter = 0;
-#ifdef HAVE_SYSCTLBYNAME
-  if (use_hard_close)
-  {
-    int blck_hl;
-    size_t blck_hl_size = sizeof (blck_hl);
-    if (0 == sysctlbyname ("net.inet.tcp.blackhole", &blck_hl, &blck_hl_size,
-                           NULL, 0))
-    {
-      if (2 <= blck_hl)
-      {
-        fprintf (stderr, "'sysctl net.inet.tcp.blackhole = %d', test is "
-                 "unreliable with this system setting, skipping.\n", blck_hl);
-        exit (77);
-      }
-    }
-    else
-    {
-      if (ENOENT != errno)
-        externalErrorExitDesc ("Cannot get 'net.inet.tcp.blackhole' value");
-    }
-  }
-#endif
-#if defined(HAVE_SYSCTL) && defined(CTL_NET) && defined(PF_INET) && \
-  defined(IPPROTO_ICMP) && defined(ICMPCTL_ICMPLIM)
-  if (use_hard_close)
-  {
-    int mib[4];
-    int limit;
-    size_t limit_size = sizeof(limit);
-    mib[0] = CTL_NET;
-    mib[1] = PF_INET;
-    mib[2] = IPPROTO_ICMP;
-    mib[3] = ICMPCTL_ICMPLIM;
-    if ((0 != sysctl (mib, 4, &limit, &limit_size, NULL, 0)) ||
-        (sizeof(limit) != limit_size) )
-      externalErrorExitDesc ("Cannot get RST rate limit value");
-    if (limit > 0)
-    {
-#ifndef _MHD_HEAVY_TESTS
-      fprintf (stderr, "This system has limits on number of RST packet"
-               " per second (%d).\nThis test will be used only if configured "
-               "with '--enable-heavy-test'.\n", limit);
-      exit (77);
-#else  /* _MHD_HEAVY_TESTS */
-      int test_limit; /**< Maximum number of checks per second */
-      test_limit = limit - limit / 10; /* Add some space to not hit the 
limiter */
-      test_limit /= 4;   /* Assume that all four tests with 'hard_close' run 
in parallel */
-      test_limit -= 5;   /* Add some more space to not hit the limiter */
-      test_limit /= 3;   /* Use only one third of available limit */
-      if (test_limit <= 0)
-      {
-        fprintf (stderr, "System limit for 'net.inet.icmp.icmplim' is "
-                 "too strict for this test (value: %d).\n", limit);
-        exit (77);
-      }
-      if (verbose)
-      {
-        printf ("Limiting number of checks to %d checks/second.\n", 
test_limit);
-        fflush (stdout);
-      }
-      rate_limiter = (unsigned int) test_limit;
-#if ! defined(HAVE_USLEEP) && ! defined(HAVE_NANOSLEEP) && ! defined(_WIN32)
-      fprintf (stderr, "Sleep function is required for this test, "
-               "but not available on this system.\n");
-      exit (77);
-#endif
-#endif /* _MHD_HEAVY_TESTS */
-    }
-  }
-#endif /* HAVE_SYSCTL && CTL_NET && PF_INET &&
-          IPPROTO_ICMP && ICMPCTL_ICMPLIM */
   if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE))
   {
 #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE)
@@ -437,9 +356,9 @@ struct _MHD_dumbClient
 
   int sckt_nonblock;  /**< non-zero if socket is non-blocking */
 
-  unsigned int port; /**< the port to connect to */
+  uint16_t port; /**< the port to connect to */
 
-  const char *send_buf; /**< the buffer for the request, malloced */
+  char *send_buf; /**< the buffer for the request, malloced */
 
   size_t req_size; /**< the size of the request, including header */
 
@@ -454,7 +373,62 @@ struct _MHD_dumbClient
 };
 
 struct _MHD_dumbClient *
-_MHD_dumbClient_create (unsigned int port, const char *method, const char *url,
+_MHD_dumbClient_create (uint16_t port, const char *method, const char *url,
+                        const char *add_headers,
+                        const uint8_t *req_body, size_t req_body_size,
+                        int chunked);
+
+
+void
+_MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt,
+                                 size_t step_size, size_t max_total_send);
+
+void
+_MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt);
+
+int
+_MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt);
+
+int
+_MHD_dumbClient_process (struct _MHD_dumbClient *clnt);
+
+void
+_MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt,
+                            MHD_socket *maxsckt,
+                            fd_set *rs, fd_set *ws, fd_set *es);
+
+
+/**
+ * Process the client data with send()/recv() as needed based on
+ * information in fd_sets.
+ * @param clnt the client to process
+ * @return non-zero if client finished processing the request,
+ *         zero otherwise.
+ */
+int
+_MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt,
+                                     fd_set *rs, fd_set *ws, fd_set *es);
+
+/**
+ * Perform full request.
+ * @param clnt the client to run
+ * @return zero if client finished processing the request,
+ *         non-zero if timeout is reached.
+ */
+int
+_MHD_dumbClient_perform (struct _MHD_dumbClient *clnt);
+
+
+/**
+ * Close the client and free internally allocated resources.
+ * @param clnt the client to close
+ */
+void
+_MHD_dumbClient_close (struct _MHD_dumbClient *clnt);
+
+
+struct _MHD_dumbClient *
+_MHD_dumbClient_create (uint16_t port, const char *method, const char *url,
                         const char *add_headers,
                         const uint8_t *req_body, size_t req_body_size,
                         int chunked)
@@ -640,7 +614,7 @@ _MHD_dumbClient_connect_init (struct _MHD_dumbClient *clnt)
   mhd_assert (DUMB_CLIENT_INIT == clnt->stage);
 
   sa.sin_family = AF_INET;
-  sa.sin_port = htons ((uint16_t) clnt->port);
+  sa.sin_port = htons (clnt->port);
   sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
 
   if (0 != connect (clnt->sckt, (struct sockaddr *) &sa, sizeof(sa)))
@@ -748,16 +722,6 @@ _MHD_dumbClient_socket_close (struct _MHD_dumbClient *clnt)
 {
   if (MHD_INVALID_SOCKET != clnt->sckt)
   {
-    if (use_hard_close)
-    {
-#ifdef SO_LINGER
-      static const struct linger hard_close = {1, 0};
-      mhd_assert (0 == hard_close.l_linger);
-      if (0 != setsockopt (clnt->sckt, SOL_SOCKET, SO_LINGER,
-                           (const void *) &hard_close, sizeof (hard_close)))
-#endif /* SO_LINGER */
-      externalErrorExitDesc ("Failed to set SO_LINGER option");
-    }
     if (! MHD_socket_close_ (clnt->sckt))
       externalErrorExitDesc ("Unexpected error while closing " \
                              "the client socket");
@@ -772,23 +736,14 @@ _MHD_dumbClient_finalize (struct _MHD_dumbClient *clnt)
 {
   if (MHD_INVALID_SOCKET != clnt->sckt)
   {
-    if (use_shutdown)
-    {
-      if (0 != shutdown (clnt->sckt, SHUT_WR))
-      {
-        const int err = MHD_socket_get_error_ ();
-        if (! MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_) &&
-            ! MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
-          mhdErrorExitDesc ("Unexpected error when shutting down " \
-                            "the client socket");
-      }
-    }
-    else if (use_close)
+    if (0 != shutdown (clnt->sckt, SHUT_WR))
     {
-      _MHD_dumbClient_socket_close (clnt);
+      const int err = MHD_socket_get_error_ ();
+      if (! MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_) &&
+          ! MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
+        mhdErrorExitDesc ("Unexpected error when shutting down " \
+                          "the client socket");
     }
-    else
-      mhd_assert (0);
   }
   clnt->stage = DUMB_CLIENT_FINISHED;
 }
@@ -969,9 +924,13 @@ _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt)
       maxMhdSk = MHD_INVALID_SOCKET;
       _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
       mhd_assert (now >= start);
-      tv.tv_sec = TIMEOUTS_VAL * 2 - (now - start) + 1;
+#ifndef _WIN32
+      tv.tv_sec = (time_t) (TIMEOUTS_VAL * 2 - (now - start) + 1);
+#else
+      tv.tv_sec = (long) (TIMEOUTS_VAL * 2 - (now - start) + 1);
+#endif
       tv.tv_usec = 250 * 1000;
-      if (-1 == select (maxMhdSk + 1, &rs, &ws, &es, &tv))
+      if (-1 == select ((int) (maxMhdSk + 1), &rs, &ws, &es, &tv))
       {
 #ifdef MHD_POSIX_SOCKETS
         if (EINTR != errno)
@@ -980,7 +939,7 @@ _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt)
         mhd_assert ((0 != rs.fd_count) || (0 != ws.fd_count) || \
                     (0 != es.fd_count));
         externalErrorExitDesc ("Unexpected select() error");
-        Sleep (tv.tv_sec * 1000 + tv.tv_usec / 1000);
+        _MHD_sleep ((uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
 #endif /* ! MHD_POSIX_SOCKETS */
         continue;
       }
@@ -1013,190 +972,20 @@ _MHD_dumbClient_close (struct _MHD_dumbClient *clnt)
 }
 
 
-struct sckt_notif_cb_param
-{
-  volatile unsigned int num_started;
-  volatile unsigned int num_finished;
-};
-
-void
+static void
 socket_cb (void *cls,
            struct MHD_Connection *c,
            void **socket_context,
            enum MHD_ConnectionNotificationCode toe)
 {
-  struct sckt_notif_cb_param *param = (struct sckt_notif_cb_param *) cls;
-  if (NULL == socket_context)
-    mhdErrorExitDesc ("'socket_context' pointer is NULL");
-  if (NULL == c)
-    mhdErrorExitDesc ("'connection' pointer is NULL");
-  if (NULL == param)
-    mhdErrorExitDesc ("'cls' pointer is NULL");
-
-  if (MHD_CONNECTION_NOTIFY_STARTED == toe)
-    param->num_started++;
-  else if (MHD_CONNECTION_NOTIFY_CLOSED == toe)
-    param->num_finished++;
-  else
-    mhdErrorExitDesc ("Unknown 'toe' value");
-}
-
-
-struct term_notif_cb_param
-{
-  volatile int term_reason;
-  volatile int num_called;
-};
-
-
-static void
-term_cb (void *cls,
-         struct MHD_Connection *c,
-         void **req_cls,
-         enum MHD_RequestTerminationCode term_code)
-{
-  struct term_notif_cb_param *param = (struct term_notif_cb_param *) cls;
-  if (NULL == req_cls)
-    mhdErrorExitDesc ("'req_cls' pointer is NULL");
-  if (NULL == c)
-    mhdErrorExitDesc ("'connection' pointer is NULL");
-  if (NULL == param)
-    mhdErrorExitDesc ("'cls' pointer is NULL");
-  param->term_reason = (int) term_code;
-  param->num_called++;
-}
-
-
-const char *
-term_reason_str (enum MHD_RequestTerminationCode term_code)
-{
-  switch ((int) term_code)
-  {
-  case MHD_REQUEST_TERMINATED_COMPLETED_OK:
-    return "COMPLETED_OK";
-  case MHD_REQUEST_TERMINATED_WITH_ERROR:
-    return "TERMINATED_WITH_ERROR";
-  case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED:
-    return "TIMEOUT_REACHED";
-  case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
-    return "DAEMON_SHUTDOWN";
-  case MHD_REQUEST_TERMINATED_READ_ERROR:
-    return "READ_ERROR";
-  case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
-    return "CLIENT_ABORT";
-  case -1:
-    return "(not called)";
-  default:
-    return "(unknown code)";
-  }
-  return "(problem)"; /* unreachable */
-}
-
-
-struct check_uri_cls
-{
-  const char *volatile uri;
-  volatile unsigned int cb_called;
-};
-
-static void *
-check_uri_cb (void *cls,
-              const char *uri,
-              struct MHD_Connection *con)
-{
-  struct check_uri_cls *param = (struct check_uri_cls *) cls;
-
-  if (NULL == con)
-    mhdErrorExitDesc ("The 'con' pointer is NULL");
-
-  param->cb_called++;
-
-  if (0 != strcmp (param->uri,
-                   uri))
-  {
-    fprintf (stderr, "Wrong URI: '%s'\n", uri);
-    mhdErrorExit ();
-  }
-  return NULL;
-}
-
-
-struct mhd_header_checker_param
-{
-  int found_header_host; /**< the number of 'Host' headers */
-  int found_header_ua;   /**< the number of 'User-Agent' headers */
-  int found_header_ct;   /**< the number of 'Content-Type' headers */
-  int found_header_cl;   /**< the number of 'Content-Length' headers */
-  int found_header_te;   /**< the number of 'Transfer-Encoding' headers */
-};
-
-enum MHD_Result
-headerCheckerInterator (void *cls,
-                        enum MHD_ValueKind kind,
-                        const char *key,
-                        size_t key_size,
-                        const char *value,
-                        size_t value_size)
-{
-  struct mhd_header_checker_param *const param =
-    (struct mhd_header_checker_param *) cls;
-
-  if (NULL == param)
-    mhdErrorExitDesc ("cls parameter is NULL");
-
-  if (MHD_HEADER_KIND != kind)
-    return MHD_YES; /* Continue iteration */
-
-  if (0 == key_size)
-    mhdErrorExitDesc ("Zero key length");
-
-  if ((strlen (REQ_HEADER_HOST_NAME) == key_size) &&
-      (0 == memcmp (key, REQ_HEADER_HOST_NAME, key_size)))
-  {
-    if ((strlen (REQ_HEADER_HOST_VALUE) == value_size) &&
-        (0 == memcmp (value, REQ_HEADER_HOST_VALUE, value_size)))
-      param->found_header_host++;
-    else
-      fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
-               (int) value_size, value, REQ_HEADER_HOST_VALUE);
-  }
-  else if ((strlen (REQ_HEADER_UA_NAME) == key_size) &&
-           (0 == memcmp (key, REQ_HEADER_UA_NAME, key_size)))
-  {
-    if ((strlen (REQ_HEADER_UA_VALUE) == value_size) &&
-        (0 == memcmp (value, REQ_HEADER_UA_VALUE, value_size)))
-      param->found_header_ua++;
-    else
-      fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
-               (int) value_size, value, REQ_HEADER_UA_VALUE);
-  }
-  else if ((strlen (REQ_HEADER_CT_NAME) == key_size) &&
-           (0 == memcmp (key, REQ_HEADER_CT_NAME, key_size)))
-  {
-    if ((strlen (REQ_HEADER_CT_VALUE) == value_size) &&
-        (0 == memcmp (value, REQ_HEADER_CT_VALUE, value_size)))
-      param->found_header_ct++;
-    else
-      fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
-               (int) value_size, value, REQ_HEADER_CT_VALUE);
-  }
-  else if ((strlen (MHD_HTTP_HEADER_CONTENT_LENGTH) == key_size) &&
-           (0 == memcmp (key, MHD_HTTP_HEADER_CONTENT_LENGTH, key_size)))
-  {
-    /* do not check value of the header here for simplicity */
-    param->found_header_cl++;
-  }
-  else if ((strlen (MHD_HTTP_HEADER_TRANSFER_ENCODING) == key_size) &&
-           (0 == memcmp (key, MHD_HTTP_HEADER_TRANSFER_ENCODING, key_size)))
-  {
-    if ((strlen ("chunked") == value_size) &&
-        (0 == memcmp (value, "chunked", value_size)))
-      param->found_header_te++;
-    else
-      fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
-               (int) value_size, value, "chunked");
-  }
-  return MHD_YES;
+  (void) cls;            /* Unused */
+  (void) socket_context; /* Unused */
+  (void) toe;            /* Unused */
+
+  MHD_suspend_connection (c); /* Should trigger panic */
+  mhdErrorExitDesc ("Function \"MHD_suspend_connection()\" succeed, while " \
+                    "it must fail as daemon was started without 
MHD_ALLOW_SUSPEND_RESUME " \
+                    "flag");
 }
 
 
@@ -1224,8 +1013,8 @@ ahcCheck (void *cls,
 {
   static int marker;
   enum MHD_Result ret;
-  struct mhd_header_checker_param header_check_param;
   struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
+  (void) connection; /* Unused */
 
   if (NULL == param)
     mhdErrorExitDesc ("cls parameter is NULL");
@@ -1283,24 +1072,6 @@ ahcCheck (void *cls,
     return MHD_YES;
   }
 
-  memset (&header_check_param, 0, sizeof(header_check_param));
-  if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND,
-                                       &headerCheckerInterator,
-                                       &header_check_param))
-    mhdErrorExitDesc ("Wrong number of headers in the request");
-  if (1 != header_check_param.found_header_host)
-    mhdErrorExitDesc ("'Host' header has not been detected in request");
-  if (1 != header_check_param.found_header_ua)
-    mhdErrorExitDesc ("'User-Agent' header has not been detected in request");
-  if (1 != header_check_param.found_header_ct)
-    mhdErrorExitDesc ("'Content-Type' header has not been detected in 
request");
-  if (! upl_chunked && (1 != header_check_param.found_header_cl))
-    mhdErrorExitDesc ("'Content-Length' header has not been detected "
-                      "in request");
-  if (upl_chunked && (1 != header_check_param.found_header_te))
-    mhdErrorExitDesc ("'Transfer-Encoding' header has not been detected "
-                      "in request");
-
   if (NULL != upload_data)
     return MHD_YES; /* Full request has not been received so far */
 
@@ -1338,13 +1109,13 @@ struct simpleQueryParams
   const char *method;
 
   /* Destination port for HTTP query */
-  int queryPort;
+  uint16_t queryPort;
 
   /* Additional request headers, static */
   const char *headers;
 
   /* NULL for request without body */
-  uint8_t *req_body;
+  const uint8_t *req_body;
   size_t req_body_size;
 
   /* Non-zero to use chunked encoding for request body */
@@ -1380,7 +1151,7 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
 
   di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
   if (NULL == di)
-    mhdErrorExitDesc ("Cannot get lister socket");
+    mhdErrorExitDesc ("Cannot get listener socket");
   lstn_sk = di->listen_fd;
 
   ret = 1; /* will be replaced with real result */
@@ -1428,18 +1199,15 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
          * Client will close the socket as the next step. */
         if (full_req_recieved)
           do_client = 1; /* All data has been received by the MHD */
-        else if ((0 == rate_limiter) && some_data_recieved)
+        else if (some_data_recieved)
         {
-          /* No RST rate limiter, no need to avoid extra RST
-           * and at least something was received by the MHD */
+          /* at least something was received by the MHD */
           do_client = 1;
         }
         else
         {
-          /* When rate limiter is enabled, all sent packets must be received
-           * before client close connection to avoid RST for every ACK.
-           * When rate limiter is not enabled, the MHD must receive at
-           * least something before closing the connection. */
+          /* The MHD must receive at least something before closing
+           * the connection. */
           do_client = 0;
         }
       }
@@ -1459,7 +1227,7 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
       tv.tv_sec = 0;
       tv.tv_usec = FINAL_PACKETS_MS * 1000;
     }
-    num_ready = select (maxMhdSk + 1, &rs, &ws, &es, &tv);
+    num_ready = select ((int) (maxMhdSk + 1), &rs, &ws, &es, &tv);
     if (-1 == num_ready)
     {
 #ifdef MHD_POSIX_SOCKETS
@@ -1469,7 +1237,7 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
       if ((WSAEINVAL != WSAGetLastError ()) ||
           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
         externalErrorExitDesc ("Unexpected select() error");
-      Sleep (tv.tv_sec * 1000 + tv.tv_usec / 1000);
+      _MHD_sleep ((uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
 #endif
       continue;
     }
@@ -1480,6 +1248,9 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
       if (full_req_sent && (! full_req_recieved))
         full_req_recieved = 1;
     }
+    if (full_req_recieved)
+      mhdErrorExitDesc ("Full request has been received by MHD, while it "
+                        "must be aborted by the panic function");
     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
       mhdErrorExitDesc ("MHD_run_from_select() failed");
     if (! client_accepted)
@@ -1552,6 +1323,8 @@ doClientQueryInThread (struct MHD_Daemon *d,
 
   if (errornum)
     fprintf (stderr, "Request timeout out.\n");
+  else
+    mhdErrorExitDesc ("Request succeed, but it must fail");
 
   _MHD_dumbClient_close (c);
 
@@ -1559,67 +1332,12 @@ doClientQueryInThread (struct MHD_Daemon *d,
 }
 
 
-void
-printTestResults (FILE *stream,
-                  struct simpleQueryParams *qParam,
-                  struct ahc_cls_type *ahc_param,
-                  struct check_uri_cls *uri_cb_param,
-                  struct term_notif_cb_param *term_result,
-                  struct sckt_notif_cb_param *sckt_result)
-{
-  if (stderr != stream)
-    fflush (stderr);
-  fprintf (stream, " Request aborted at %u byte%s.",
-           (unsigned int) qParam->total_send_max,
-           1 == qParam->total_send_max ? "" : "s");
-  if ((1 == sckt_result->num_started) && (1 == sckt_result->num_finished))
-    fprintf (stream, " One socket has been accepted and then closed.");
-  else
-    fprintf (stream, " Sockets have been accepted %u time%s"
-             " and closed %u time%s.", sckt_result->num_started,
-             (1 == sckt_result->num_started) ? "" : "s",
-             sckt_result->num_finished,
-             (1 == sckt_result->num_finished) ? "" : "s");
-  if (0 == uri_cb_param->cb_called)
-    fprintf (stream, " URI callback has NOT been called.");
-  else
-    fprintf (stream, " URI callback has been called %u time%s.",
-             uri_cb_param->cb_called,
-             1 == uri_cb_param->cb_called ? "" : "s");
-  if (0 == ahc_param->cb_called)
-    fprintf (stream, " Access handler callback has NOT been called.");
-  else
-    fprintf (stream, " Access handler callback has been called %u time%s.",
-             ahc_param->cb_called,
-             1 == ahc_param->cb_called ? "" : "s");
-  if (0 == term_result->num_called)
-    fprintf (stream, " Final notification callback has NOT been called.");
-  else
-    fprintf (stream, " Final notification callback has been called %u time%s "
-             "with %s code.", term_result->num_called,
-             (1 == term_result->num_called) ? "" : "s",
-             term_reason_str (term_result->term_reason));
-  fprintf (stream, "\n");
-  fflush (stream);
-}
-
-
 /* Perform test queries, shut down MHD daemon, and free parameters */
-static int
-performTestQueries (struct MHD_Daemon *d, int d_port,
-                    struct ahc_cls_type *ahc_param,
-                    struct check_uri_cls *uri_cb_param,
-                    struct term_notif_cb_param *term_result,
-                    struct sckt_notif_cb_param *sckt_result)
+static unsigned int
+performTestQueries (struct MHD_Daemon *d, uint16_t d_port,
+                    struct ahc_cls_type *ahc_param)
 {
   struct simpleQueryParams qParam;
-  time_t start;
-  int ret = 0;          /* Return value */
-  size_t req_total_size;
-  size_t limit_send_size;
-  size_t inc_size;
-  int expected_reason;
-  int found_right_reason;
 
   /* Common parameters, to be individually overridden by specific test cases
    * if needed */
@@ -1627,12 +1345,10 @@ performTestQueries (struct MHD_Daemon *d, int d_port,
   qParam.method = MHD_HTTP_METHOD_PUT;
   qParam.queryPath = EXPECTED_URI_BASE_PATH;
   qParam.headers = REQ_HEADER_CT;
-  qParam.req_body = (uint8_t *) REQ_BODY;
+  qParam.req_body = (const uint8_t *) REQ_BODY;
   qParam.req_body_size = MHD_STATICSTR_LEN_ (REQ_BODY);
-  qParam.chunked = upl_chunked;
-  qParam.step_size = by_step ? 1 : 0;
-
-  uri_cb_param->uri = EXPECTED_URI_BASE_PATH;
+  qParam.chunked = 0;
+  qParam.step_size = 0;
 
   ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
   ahc_param->rq_method = MHD_HTTP_METHOD_PUT;
@@ -1641,130 +1357,17 @@ performTestQueries (struct MHD_Daemon *d, int d_port,
   ahc_param->req_body = (const char *) qParam.req_body;
   ahc_param->req_body_size = qParam.req_body_size;
 
-  do
-  {
-    struct _MHD_dumbClient *test_c;
-    struct simpleQueryParams *p = &qParam;
-    test_c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath,
-                                     p->headers, p->req_body, p->req_body_size,
-                                     p->chunked);
-    req_total_size = test_c->req_size;
-    _MHD_dumbClient_close (test_c);
-  } while (0);
-
-  expected_reason = use_hard_close ?
-                    MHD_REQUEST_TERMINATED_READ_ERROR :
-                    MHD_REQUEST_TERMINATED_CLIENT_ABORT;
-  found_right_reason = 0;
-  if (0 != rate_limiter)
-  {
-    if (verbose)
-    {
-      printf ("Pausing for rate limiter...");
-      fflush (stdout);
-    }
-    _MHD_sleep (1150); /* Just a bit more than one second */
-    if (verbose)
-    {
-      printf (" OK\n");
-      fflush (stdout);
-    }
-    inc_size = ((req_total_size - 1) + (rate_limiter - 1)) / rate_limiter;
-    if (0 == inc_size)
-      inc_size = 1;
-  }
-  else
-    inc_size = 1;
-
-  start = time (NULL);
-  for (limit_send_size = 1; limit_send_size < req_total_size;
-       limit_send_size += inc_size)
-  {
-    int test_succeed;
-    test_succeed = 0;
-    /* Make sure that maximum size is tested */
-    if (req_total_size - inc_size < limit_send_size)
-      limit_send_size = req_total_size - 1;
-    qParam.total_send_max = limit_send_size;
-    /* To be updated by callbacks */
-    ahc_param->cb_called = 0;
-    uri_cb_param->cb_called = 0;
-    term_result->num_called = 0;
-    term_result->term_reason = -1;
-    sckt_result->num_started = 0;
-    sckt_result->num_finished = 0;
-
-    if (0 != doClientQueryInThread (d, &qParam))
-      fprintf (stderr, "FAILED: connection has NOT been closed by MHD.");
-    else
-    {
-      if ((-1 != term_result->term_reason) &&
-          (MHD_REQUEST_TERMINATED_READ_ERROR != term_result->term_reason) &&
-          (MHD_REQUEST_TERMINATED_CLIENT_ABORT != term_result->term_reason) )
-        fprintf (stderr, "FAILED: Wrong termination code.");
-      else if ((0 == term_result->num_called) &&
-               ((0 != uri_cb_param->cb_called) || (0 != ahc_param->cb_called)))
-        fprintf (stderr, "FAILED: Missing required call of final notification "
-                 "callback.");
-      else if (1 < uri_cb_param->cb_called)
-        fprintf (stderr, "FAILED: Too many URI callbacks.");
-      else if ((0 != ahc_param->cb_called) && (0 == uri_cb_param->cb_called))
-        fprintf (stderr, "FAILED: URI callback has NOT been called "
-                 "while Access Handler callback has been called.");
-      else if (1 < term_result->num_called)
-        fprintf (stderr, "FAILED: Too many final callbacks.");
-      else if (1 != sckt_result->num_started)
-        fprintf (stderr, "FAILED: Wrong number of sockets accepted.");
-      else if (1 != sckt_result->num_finished)
-        fprintf (stderr, "FAILED: Wrong number of sockets closed.");
-      else
-      {
-        test_succeed = 1;
-        if (expected_reason == term_result->term_reason)
-          found_right_reason = 1;
-      }
-    }
+  /* Make sure that maximum size is tested */
+  /* To be updated by callbacks */
+  ahc_param->cb_called = 0;
 
-    if (! test_succeed)
-    {
-      ret = 1;
-      printTestResults (stderr,
-                        &qParam, ahc_param, uri_cb_param,
-                        term_result, sckt_result);
-    }
-    else if (verbose)
-    {
-      printf ("SUCCEED:");
-      printTestResults (stdout,
-                        &qParam, ahc_param, uri_cb_param,
-                        term_result, sckt_result);
-    }
-
-    if (time (NULL) - start >
-        (time_t) ((TIMEOUTS_VAL * 25)
-                  + (rate_limiter * FINAL_PACKETS_MS) / 1000 + 1))
-    {
-      ret |= 1 << 2;
-      fprintf (stderr, "FAILED: Test total time exceeded.\n");
-      break;
-    }
-  }
+  if (0 != doClientQueryInThread (d, &qParam))
+    fprintf (stderr, "FAILED: client query failed.");
 
   MHD_stop_daemon (d);
-  free (uri_cb_param);
   free (ahc_param);
-  free (term_result);
-  free (sckt_result);
 
-  if (! found_right_reason)
-  {
-    fprintf (stderr, "FAILED: termination callback was not called with "
-             "expected (%s) reason.\n", term_reason_str (expected_reason));
-    fflush (stderr);
-    ret |= 1 << 1;
-  }
-
-  return ret;
+  return 1; /* Always error if reached this point */
 }
 
 
@@ -1790,85 +1393,90 @@ enum testMhdPollType
 static unsigned int
 testNumThreadsForPool (enum testMhdPollType pollType)
 {
-  int numThreads = MHD_CPU_COUNT;
+  unsigned int numThreads = MHD_CPU_COUNT;
   (void) pollType; /* Don't care about pollType for this test */
   return numThreads; /* No practical limit for non-cleanup test */
 }
 
 
+#define PANIC_MAGIC_CHECK 1133
+
+static void
+myPanicCallback (void *cls,
+                 const char *file,
+                 unsigned int line,
+                 const char *reason)
+{
+  int *const param = (int *) cls;
+  if (NULL == cls)
+    mhdErrorExitDesc ("The 'cls' parameter is NULL");
+  if (PANIC_MAGIC_CHECK != *param)
+    mhdErrorExitDesc ("Wrong '*cls' value");
+#ifdef HAVE_MESSAGES
+  if (NULL == file)
+    mhdErrorExitDesc ("The 'file' parameter is NULL");
+  if (NULL == reason)
+    mhdErrorExitDesc ("The 'reason' parameter is NULL");
+#else  /* ! HAVE_MESSAGES */
+  if (NULL != file)
+    mhdErrorExitDesc ("The 'file' parameter is not NULL");
+  if (NULL != reason)
+    mhdErrorExitDesc ("The 'reason' parameter is not NULL");
+#endif /* ! HAVE_MESSAGES */
+  fflush (stderr);
+  fflush (stdout);
+  printf ("User panic function has been called from file '%s' at line '%u' "
+          "with the reason:\n%s", file, line,
+          ((NULL != reason) ? reason : "(NULL)\n"));
+  fflush (stdout);
+  exit (0);
+}
+
+
 static struct MHD_Daemon *
 startTestMhdDaemon (enum testMhdThreadsType thrType,
-                    enum testMhdPollType pollType, int *pport,
-                    struct ahc_cls_type **ahc_param,
-                    struct check_uri_cls **uri_cb_param,
-                    struct term_notif_cb_param **term_result,
-                    struct sckt_notif_cb_param **sckt_result)
+                    enum testMhdPollType pollType, uint16_t *pport,
+                    struct ahc_cls_type **ahc_param)
 {
   struct MHD_Daemon *d;
   const union MHD_DaemonInfo *dinfo;
+  static int magic_panic_param = PANIC_MAGIC_CHECK;
 
-  if ((NULL == ahc_param) || (NULL == uri_cb_param) || (NULL == term_result))
+  if (NULL == ahc_param)
     externalErrorExit ();
 
   *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
   if (NULL == *ahc_param)
     externalErrorExit ();
-  *uri_cb_param =
-    (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls));
-  if (NULL == *uri_cb_param)
-    externalErrorExit ();
-  *term_result =
-    (struct term_notif_cb_param *) malloc (sizeof(struct term_notif_cb_param));
-  if (NULL == *term_result)
-    externalErrorExit ();
-  *sckt_result =
-    (struct sckt_notif_cb_param *) malloc (sizeof(struct sckt_notif_cb_param));
-  if (NULL == *sckt_result)
-    externalErrorExit ();
 
   if ( (0 == *pport) &&
        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 
)
   {
-    *pport = 4170;
-    if (use_shutdown)
-      *pport += 0;
-    if (use_close)
-      *pport += 1;
-    if (use_hard_close)
-      *pport += 1;
-    if (by_step)
-      *pport += 1 << 2;
-    if (upl_chunked)
-      *pport += 1 << 3;
-    if (! oneone)
-      *pport += 1 << 4;
+    *pport = 4190;
   }
 
+  MHD_set_panic_func (&myPanicCallback, (void *) &magic_panic_param);
+
   if (testMhdThreadInternalPool != thrType)
-    d = MHD_start_daemon (((int) thrType) | ((int) pollType)
+    d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType)
                           | (verbose ? MHD_USE_ERROR_LOG : 0),
                           *pport, NULL, NULL,
                           &ahcCheck, *ahc_param,
-                          MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
-                          *uri_cb_param,
-                          MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result,
                           MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
-                          *sckt_result,
+                          NULL,
                           MHD_OPTION_CONNECTION_TIMEOUT,
                           (unsigned) TIMEOUTS_VAL,
                           MHD_OPTION_END);
   else
-    d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ((int) pollType)
+    d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
+                          | ((unsigned int) pollType)
                           | (verbose ? MHD_USE_ERROR_LOG : 0),
                           *pport, NULL, NULL,
                           &ahcCheck, *ahc_param,
                           MHD_OPTION_THREAD_POOL_SIZE,
                           testNumThreadsForPool (pollType),
-                          MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
-                          *uri_cb_param,
-                          MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result,
                           MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
-                          *sckt_result,
+                          NULL,
                           MHD_OPTION_CONNECTION_TIMEOUT,
                           (unsigned) TIMEOUTS_VAL,
                           MHD_OPTION_END);
@@ -1881,7 +1489,7 @@ startTestMhdDaemon (enum testMhdThreadsType thrType,
     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
     if ((NULL == dinfo) || (0 == dinfo->port))
       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
-    *pport = (int) dinfo->port;
+    *pport = dinfo->port;
     if (0 == global_port)
       global_port = *pport; /* Reuse the same port for all tests */
   }
@@ -1893,31 +1501,26 @@ startTestMhdDaemon (enum testMhdThreadsType thrType,
 /* Test runners */
 
 
-static int
+static unsigned int
 testExternalGet (void)
 {
   struct MHD_Daemon *d;
-  int d_port = global_port; /* Daemon's port */
+  uint16_t d_port = global_port; /* Daemon's port */
   struct ahc_cls_type *ahc_param;
-  struct check_uri_cls *uri_cb_param;
-  struct term_notif_cb_param *term_result;
-  struct sckt_notif_cb_param *sckt_result;
 
   d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
-                          &ahc_param, &uri_cb_param, &term_result,
-                          &sckt_result);
+                          &ahc_param);
 
-  return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result,
-                             sckt_result);
+  return performTestQueries (d, d_port, ahc_param);
 }
 
 
 #if 0 /* disabled runners, not suitable for this test */
-static int
+static unsigned int
 testInternalGet (enum testMhdPollType pollType)
 {
   struct MHD_Daemon *d;
-  int d_port = global_port; /* Daemon's port */
+  uint16_t d_port = global_port; /* Daemon's port */
   struct ahc_cls_type *ahc_param;
   struct check_uri_cls *uri_cb_param;
   struct term_notif_cb_param *term_result;
@@ -1933,7 +1536,7 @@ static int
 testMultithreadedGet (enum testMhdPollType pollType)
 {
   struct MHD_Daemon *d;
-  int d_port = global_port; /* Daemon's port */
+  uint16_t d_port = global_port; /* Daemon's port */
   struct ahc_cls_type *ahc_param;
   struct check_uri_cls *uri_cb_param;
   struct term_notif_cb_param *term_result;
@@ -1944,11 +1547,11 @@ testMultithreadedGet (enum testMhdPollType pollType)
 }
 
 
-static int
+static unsigned int
 testMultithreadedPoolGet (enum testMhdPollType pollType)
 {
   struct MHD_Daemon *d;
-  int d_port = global_port; /* Daemon's port */
+  uint16_t d_port = global_port; /* Daemon's port */
   struct ahc_cls_type *ahc_param;
   struct check_uri_cls *uri_cb_param;
   struct term_notif_cb_param *term_result;
@@ -1968,23 +1571,9 @@ main (int argc, char *const *argv)
   unsigned int test_result = 0;
   verbose = 0;
 
+  (void) has_in_name; /* Unused, mute compiler warning */
   if ((NULL == argv) || (0 == argv[0]))
     return 99;
-  oneone = ! has_in_name (argv[0], "10");
-  use_shutdown = has_in_name (argv[0], "_shutdown") ? 1 : 0;
-  use_close = has_in_name (argv[0], "_close") ? 1 : 0;
-  use_hard_close = has_in_name (argv[0], "_hard_close") ? 1 : 0;
-  by_step = has_in_name (argv[0], "_steps") ? 1 : 0;
-  upl_chunked = has_in_name (argv[0], "_chunked") ? 1 : 0;
-#ifndef SO_LINGER
-  if (use_hard_close)
-  {
-    fprintf (stderr, "This test requires SO_LINGER socket option support.\n");
-    return 77;
-  }
-#endif /* ! SO_LINGER */
-  if (1 != use_shutdown + use_close)
-    return 99;
   verbose = ! (has_param (argc, argv, "-q") ||
                has_param (argc, argv, "--quiet") ||
                has_param (argc, argv, "-s") ||

-- 
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]