[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.