test_client_put_stop: support rate limiting of RST

RST rate limiting is required for FreeBSD.
This commit is contained in:
Evgeny Grin (Karlson2k)
2021-12-06 21:38:59 +03:00
parent 9f0d5f6eb1
commit 1a7fb032ba
3 changed files with 230 additions and 12 deletions
+40
View File
@@ -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*],[
+4 -2
View File
@@ -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
+186 -10
View File
@@ -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");