gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] 02/03: test_client_put_stop: support rate limiting of RS


From: gnunet
Subject: [libmicrohttpd] 02/03: test_client_put_stop: support rate limiting of RST
Date: Mon, 06 Dec 2021 19:41:15 +0100

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

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

commit 1a7fb032ba128c81136f92ce4cd71449916a0486
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Tue Nov 30 09:28:25 2021 +0300

    test_client_put_stop: support rate limiting of RST
    
    RST rate limiting is required for FreeBSD.
---
 configure.ac                          |  40 +++++++
 m4/mhd_shutdown_socket_trigger.m4     |   6 +-
 src/microhttpd/test_client_put_stop.c | 196 ++++++++++++++++++++++++++++++++--
 3 files changed, 230 insertions(+), 12 deletions(-)

diff --git a/configure.ac b/configure.ac
index 35436e7f..cbe609d4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -135,6 +135,30 @@ AC_CHECK_HEADER([[search.h]],
 
 AM_CONDITIONAL([MHD_HAVE_TSEARCH], [[test "x$ac_cv_header_search_h" = xyes && 
test "x$HAVE_TSEARCH" = "x1" && test "x$REPLACE_TSEARCH" != "x1"]])
 
+# Optional headers used for tests
+AC_CHECK_HEADERS([sys/sysctl.h netinet/ip_icmp.h netinet/icmp_var.h], [], [],
+  [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif /* HAVE_SYS_SYSCTL_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif /* HAVE_NETINET_IP_H */
+#ifdef HAVE_NETINET_IP_ICMP_H
+#include <netinet/ip_icmp.h>
+#endif /* HAVE_NETINET_IP_ICMP_H */
+  ]]
+)
+
 # Checks for gettext.
 m4_ifdef([AM_GNU_GETTEXT], [
   AS_VAR_SET_IF([enable_nls], [], [[enable_nls=no]])
@@ -1881,6 +1905,22 @@ AC_MSG_RESULT($have_inet6)
 
 MHD_CHECK_FUNC([[sysconf]], [[#include <unistd.h>]], [[long a = sysconf(0); if 
(a) return 1;]])
 
+MHD_CHECK_FUNC([[sysctl]], [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+  ]], [[int mib[2] = {CTL_KERN, KERN_MAXPROC}; if (sysctl(mib, 2, NULL, NULL, 
NULL, 0)) return 1;]]
+)
+
+MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]])
+MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, 
ts1 = {0, 0}; nanosleep(&ts1, &ts2);]])
+
 HIDDEN_VISIBILITY_CFLAGS=""
 AS_CASE(["$host"],
   [*-*-mingw*],[
diff --git a/m4/mhd_shutdown_socket_trigger.m4 
b/m4/mhd_shutdown_socket_trigger.m4
index 03a1df02..469271b7 100644
--- a/m4/mhd_shutdown_socket_trigger.m4
+++ b/m4/mhd_shutdown_socket_trigger.m4
@@ -26,8 +26,10 @@ AC_DEFUN([MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER],[dnl
   AC_REQUIRE([AC_PROG_CC])dnl
   AC_REQUIRE([AX_PTHREAD])dnl
   AC_CHECK_HEADERS([sys/time.h],[AC_CHECK_FUNCS([gettimeofday])],[], 
[AC_INCLUDES_DEFAULT])
-  AC_CHECK_HEADERS([time.h],[AC_CHECK_FUNCS([nanosleep])],[], 
[AC_INCLUDES_DEFAULT])
-  AC_CHECK_HEADERS([unistd.h],[AC_CHECK_FUNCS([usleep])],[], 
[AC_INCLUDES_DEFAULT])
+  dnl AC_CHECK_HEADERS([time.h],[AC_CHECK_FUNCS([nanosleep])],[], 
[AC_INCLUDES_DEFAULT])
+  MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]])
+  dnl AC_CHECK_HEADERS([unistd.h],[AC_CHECK_FUNCS([usleep])],[], 
[AC_INCLUDES_DEFAULT])
+  MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, 
ts1 = {0, 0}; nanosleep(&ts1, &ts2);]])
   AC_CHECK_HEADERS([string.h sys/types.h sys/socket.h netinet/in.h time.h 
sys/select.h netinet/tcp.h],[],[], [AC_INCLUDES_DEFAULT])
   AC_CACHE_CHECK([[whether shutdown of listen socket triggers select()]],
     [[mhd_cv_host_shtdwn_trgr_select]], [dnl
diff --git a/src/microhttpd/test_client_put_stop.c 
b/src/microhttpd/test_client_put_stop.c
index d5ce701f..342583e0 100644
--- a/src/microhttpd/test_client_put_stop.c
+++ b/src/microhttpd/test_client_put_stop.c
@@ -54,6 +54,32 @@
 #include <signal.h>
 #endif /* HAVE_SIGNAL_H */
 
+#ifdef HAVE_SYSCTL
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif /* HAVE_SYS_SYSCTL_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif /* HAVE_NETINET_IP_H */
+#ifdef HAVE_NETINET_IP_ICMP_H
+#include <netinet/ip_icmp.h>
+#endif /* HAVE_NETINET_IP_ICMP_H */
+#ifdef HAVE_NETINET_ICMP_VAR_H
+#include <netinet/icmp_var.h>
+#endif /* HAVE_NETINET_ICMP_VAR_H */
+#endif /* HAVE_SYSCTL */
+
+#include <stdio.h>
+
 #include "mhd_sockets.h" /* only macros used */
 #include "test_helpers.h"
 #include "mhd_assert.h"
@@ -90,6 +116,9 @@
 /* Could be increased to facilitate debugging */
 #define TIMEOUTS_VAL 5
 
+/* Time in ms to wait for final packets to be delivered */
+#define FINAL_PACKETS_MS 20
+
 #define EXPECTED_URI_BASE_PATH  "/a"
 
 #define REQ_HOST "localhost"
@@ -184,6 +213,46 @@ _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
+_MHD_sleep (uint32_t ms)
+{
+#if defined(_WIN32)
+  Sleep (ms);
+#elif defined(HAVE_NANOSLEEP)
+  struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
+  struct timespec rmn;
+  int num_retries = 0;
+  while (0 != nanosleep (&slp, &rmn))
+  {
+    if (EINTR != errno)
+      externalErrorExit ();
+    if (num_retries++ > 8)
+      break;
+    slp = rmn;
+  }
+#elif defined(HAVE_USLEEP)
+  uint64_t us = ms * 1000;
+  do
+  {
+    uint64_t this_sleep;
+    if (999999 < us)
+      this_sleep = 999999;
+    else
+      this_sleep = us;
+    /* Ignore return value as it could be void */
+    usleep (this_sleep);
+    us -= this_sleep;
+  } while (us > 0);
+#else
+  externalErrorExitDesc ("No sleep function available on this system");
+#endif
+}
+
+
 /* Global parameters */
 static int verbose;                 /**< Be verbose */
 static int oneone;                  /**< If false use HTTP/1.0 for requests*/
@@ -195,6 +264,8 @@ static int use_hard_close;          /**< Use socket close 
with RST at client sid
 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 void
 test_global_init (void)
 {
@@ -208,6 +279,57 @@ test_global_init (void)
     /* exit (77); */
 #endif
   }
+  rate_limiter = 0;
+#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 */
+
 }
 
 
@@ -1229,6 +1351,8 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
   const union MHD_DaemonInfo *di;
   MHD_socket lstn_sk;
   int client_accepted;
+  int full_req_recieved;
+  int full_req_sent;
 
   di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
   if (NULL == di)
@@ -1240,6 +1364,7 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
 
   _MHD_dumbClient_start_connect (clnt);
 
+  full_req_recieved = 0;
   start = time (NULL);
   do
   {
@@ -1247,6 +1372,7 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
     fd_set ws;
     fd_set es;
     MHD_socket maxMhdSk;
+    int num_ready;
 
     maxMhdSk = MHD_INVALID_SOCKET;
     FD_ZERO (&rs);
@@ -1257,6 +1383,7 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
       /* client has finished, check whether MHD is still
        * processing any connections */
       unsigned long long to;
+      full_req_sent = 1;
       if (client_accepted && (MHD_YES != MHD_get_timeout (d, &to)))
       {
         ret = 0;
@@ -1264,12 +1391,25 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
       }
     }
     else
-      _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
+    {
+      full_req_sent = _MHD_dumbClient_is_req_sent (clnt);
+      if ((! full_req_sent) || full_req_recieved || (0 == rate_limiter))
+        _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
+    }
     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
       mhdErrorExitDesc ("MHD_get_fdset() failed");
-    tv.tv_sec = 1;
-    tv.tv_usec = 250 * 1000;
-    if (-1 == select (maxMhdSk + 1, &rs, &ws, &es, &tv))
+    if ((! full_req_sent) || full_req_recieved || (0 == rate_limiter))
+    {
+      tv.tv_sec = 1;
+      tv.tv_usec = 250 * 1000;
+    }
+    else
+    { /* Request completely sent but not yet fully received */
+      tv.tv_sec = 0;
+      tv.tv_usec = FINAL_PACKETS_MS * 1000;
+    }
+    num_ready = select (maxMhdSk + 1, &rs, &ws, &es, &tv);
+    if (-1 == num_ready)
     {
 #ifdef MHD_POSIX_SOCKETS
       if (EINTR != errno)
@@ -1282,6 +1422,11 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
 #endif
       continue;
     }
+    if (0 == num_ready)
+    { /* select() finished by timeout, looks like no more packets are pending 
*/
+      if (full_req_sent && (! full_req_recieved))
+        full_req_recieved = 1;
+    }
     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
       mhdErrorExitDesc ("MHD_run_from_select() failed");
     if (! client_accepted)
@@ -1290,11 +1435,15 @@ performQueryExternal (struct MHD_Daemon *d, struct 
_MHD_dumbClient *clnt)
     {
       /* Do not close the socket on client side until
        * MHD is accepted and processed the socket. */
-      if (! _MHD_dumbClient_is_req_sent (clnt) ||
-          (client_accepted && ! FD_ISSET (lstn_sk, &rs)))
+      if (! full_req_sent || (client_accepted && ! FD_ISSET (lstn_sk, &rs)))
       {
-        if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
-          clnt = NULL;
+        if ((! full_req_sent) || full_req_recieved || (0 == rate_limiter))
+        {
+          /* When rate limiter is enabled, all sent packets must be received
+           * before client close connection to avoid RST for every ACK. */
+          if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
+            clnt = NULL;
+        }
       }
     }
     /* Use double timeout value here so MHD would be able to catch timeout
@@ -1403,6 +1552,7 @@ performTestQueries (struct MHD_Daemon *d, int d_port,
   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;
 
@@ -1441,11 +1591,35 @@ performTestQueries (struct MHD_Daemon *d, int d_port,
                     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++)
+  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;
@@ -1501,7 +1675,9 @@ performTestQueries (struct MHD_Daemon *d, int d_port,
                         term_result, sckt_result);
     }
 
-    if (time (NULL) - start > TIMEOUTS_VAL * 25)
+    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");

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