mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
test_client_put_stop: support rate limiting of RST
RST rate limiting is required for FreeBSD.
This commit is contained in:
@@ -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*],[
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user