aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-11-30 09:28:25 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-12-06 21:38:59 +0300
commit1a7fb032ba128c81136f92ce4cd71449916a0486 (patch)
treeba319676de7c34524185fa59a0ad355e73b706d3
parent9f0d5f6eb158a3908249084b7dd59e354b74bcde (diff)
downloadlibmicrohttpd-1a7fb032ba128c81136f92ce4cd71449916a0486.tar.gz
libmicrohttpd-1a7fb032ba128c81136f92ce4cd71449916a0486.zip
test_client_put_stop: support rate limiting of RST
RST rate limiting is required for FreeBSD.
-rw-r--r--configure.ac40
-rw-r--r--m4/mhd_shutdown_socket_trigger.m46
-rw-r--r--src/microhttpd/test_client_put_stop.c196
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]],
135 135
136AM_CONDITIONAL([MHD_HAVE_TSEARCH], [[test "x$ac_cv_header_search_h" = xyes && test "x$HAVE_TSEARCH" = "x1" && test "x$REPLACE_TSEARCH" != "x1"]]) 136AM_CONDITIONAL([MHD_HAVE_TSEARCH], [[test "x$ac_cv_header_search_h" = xyes && test "x$HAVE_TSEARCH" = "x1" && test "x$REPLACE_TSEARCH" != "x1"]])
137 137
138# Optional headers used for tests
139AC_CHECK_HEADERS([sys/sysctl.h netinet/ip_icmp.h netinet/icmp_var.h], [], [],
140 [[
141#ifdef HAVE_SYS_TYPES_H
142#include <sys/types.h>
143#endif /* HAVE_SYS_TYPES_H */
144#ifdef HAVE_SYS_SYSCTL_H
145#include <sys/sysctl.h>
146#endif /* HAVE_SYS_SYSCTL_H */
147#ifdef HAVE_SYS_SOCKET_H
148#include <sys/socket.h>
149#endif /* HAVE_SYS_SOCKET_H */
150#ifdef HAVE_NETINET_IN_H
151#include <netinet/in.h>
152#endif /* HAVE_NETINET_IN_H */
153#ifdef HAVE_NETINET_IP_H
154#include <netinet/ip.h>
155#endif /* HAVE_NETINET_IP_H */
156#ifdef HAVE_NETINET_IP_ICMP_H
157#include <netinet/ip_icmp.h>
158#endif /* HAVE_NETINET_IP_ICMP_H */
159 ]]
160)
161
138# Checks for gettext. 162# Checks for gettext.
139m4_ifdef([AM_GNU_GETTEXT], [ 163m4_ifdef([AM_GNU_GETTEXT], [
140 AS_VAR_SET_IF([enable_nls], [], [[enable_nls=no]]) 164 AS_VAR_SET_IF([enable_nls], [], [[enable_nls=no]])
@@ -1881,6 +1905,22 @@ AC_MSG_RESULT($have_inet6)
1881 1905
1882MHD_CHECK_FUNC([[sysconf]], [[#include <unistd.h>]], [[long a = sysconf(0); if (a) return 1;]]) 1906MHD_CHECK_FUNC([[sysconf]], [[#include <unistd.h>]], [[long a = sysconf(0); if (a) return 1;]])
1883 1907
1908MHD_CHECK_FUNC([[sysctl]], [[
1909#ifdef HAVE_SYS_TYPES_H
1910#include <sys/types.h>
1911#endif
1912#ifdef HAVE_SYS_SYSCTL_H
1913#include <sys/sysctl.h>
1914#endif
1915#ifdef HAVE_STDDEF_H
1916#include <stddef.h>
1917#endif
1918 ]], [[int mib[2] = {CTL_KERN, KERN_MAXPROC}; if (sysctl(mib, 2, NULL, NULL, NULL, 0)) return 1;]]
1919)
1920
1921MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]])
1922MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, ts1 = {0, 0}; nanosleep(&ts1, &ts2);]])
1923
1884HIDDEN_VISIBILITY_CFLAGS="" 1924HIDDEN_VISIBILITY_CFLAGS=""
1885AS_CASE(["$host"], 1925AS_CASE(["$host"],
1886 [*-*-mingw*],[ 1926 [*-*-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
26 AC_REQUIRE([AC_PROG_CC])dnl 26 AC_REQUIRE([AC_PROG_CC])dnl
27 AC_REQUIRE([AX_PTHREAD])dnl 27 AC_REQUIRE([AX_PTHREAD])dnl
28 AC_CHECK_HEADERS([sys/time.h],[AC_CHECK_FUNCS([gettimeofday])],[], [AC_INCLUDES_DEFAULT]) 28 AC_CHECK_HEADERS([sys/time.h],[AC_CHECK_FUNCS([gettimeofday])],[], [AC_INCLUDES_DEFAULT])
29 AC_CHECK_HEADERS([time.h],[AC_CHECK_FUNCS([nanosleep])],[], [AC_INCLUDES_DEFAULT]) 29 dnl AC_CHECK_HEADERS([time.h],[AC_CHECK_FUNCS([nanosleep])],[], [AC_INCLUDES_DEFAULT])
30 AC_CHECK_HEADERS([unistd.h],[AC_CHECK_FUNCS([usleep])],[], [AC_INCLUDES_DEFAULT]) 30 MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]])
31 dnl AC_CHECK_HEADERS([unistd.h],[AC_CHECK_FUNCS([usleep])],[], [AC_INCLUDES_DEFAULT])
32 MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, ts1 = {0, 0}; nanosleep(&ts1, &ts2);]])
31 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]) 33 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])
32 AC_CACHE_CHECK([[whether shutdown of listen socket triggers select()]], 34 AC_CACHE_CHECK([[whether shutdown of listen socket triggers select()]],
33 [[mhd_cv_host_shtdwn_trgr_select]], [dnl 35 [[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 @@
54#include <signal.h> 54#include <signal.h>
55#endif /* HAVE_SIGNAL_H */ 55#endif /* HAVE_SIGNAL_H */
56 56
57#ifdef HAVE_SYSCTL
58#ifdef HAVE_SYS_TYPES_H
59#include <sys/types.h>
60#endif /* HAVE_SYS_TYPES_H */
61#ifdef HAVE_SYS_SYSCTL_H
62#include <sys/sysctl.h>
63#endif /* HAVE_SYS_SYSCTL_H */
64#ifdef HAVE_SYS_SOCKET_H
65#include <sys/socket.h>
66#endif /* HAVE_SYS_SOCKET_H */
67#ifdef HAVE_NETINET_IN_H
68#include <netinet/in.h>
69#endif /* HAVE_NETINET_IN_H */
70#ifdef HAVE_NETINET_IP_H
71#include <netinet/ip.h>
72#endif /* HAVE_NETINET_IP_H */
73#ifdef HAVE_NETINET_IP_ICMP_H
74#include <netinet/ip_icmp.h>
75#endif /* HAVE_NETINET_IP_ICMP_H */
76#ifdef HAVE_NETINET_ICMP_VAR_H
77#include <netinet/icmp_var.h>
78#endif /* HAVE_NETINET_ICMP_VAR_H */
79#endif /* HAVE_SYSCTL */
80
81#include <stdio.h>
82
57#include "mhd_sockets.h" /* only macros used */ 83#include "mhd_sockets.h" /* only macros used */
58#include "test_helpers.h" 84#include "test_helpers.h"
59#include "mhd_assert.h" 85#include "mhd_assert.h"
@@ -90,6 +116,9 @@
90/* Could be increased to facilitate debugging */ 116/* Could be increased to facilitate debugging */
91#define TIMEOUTS_VAL 5 117#define TIMEOUTS_VAL 5
92 118
119/* Time in ms to wait for final packets to be delivered */
120#define FINAL_PACKETS_MS 20
121
93#define EXPECTED_URI_BASE_PATH "/a" 122#define EXPECTED_URI_BASE_PATH "/a"
94 123
95#define REQ_HOST "localhost" 124#define REQ_HOST "localhost"
@@ -184,6 +213,46 @@ _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
184} 213}
185 214
186 215
216/**
217 * Pause execution for specified number of milliseconds.
218 * @param ms the number of milliseconds to sleep
219 */
220void
221_MHD_sleep (uint32_t ms)
222{
223#if defined(_WIN32)
224 Sleep (ms);
225#elif defined(HAVE_NANOSLEEP)
226 struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
227 struct timespec rmn;
228 int num_retries = 0;
229 while (0 != nanosleep (&slp, &rmn))
230 {
231 if (EINTR != errno)
232 externalErrorExit ();
233 if (num_retries++ > 8)
234 break;
235 slp = rmn;
236 }
237#elif defined(HAVE_USLEEP)
238 uint64_t us = ms * 1000;
239 do
240 {
241 uint64_t this_sleep;
242 if (999999 < us)
243 this_sleep = 999999;
244 else
245 this_sleep = us;
246 /* Ignore return value as it could be void */
247 usleep (this_sleep);
248 us -= this_sleep;
249 } while (us > 0);
250#else
251 externalErrorExitDesc ("No sleep function available on this system");
252#endif
253}
254
255
187/* Global parameters */ 256/* Global parameters */
188static int verbose; /**< Be verbose */ 257static int verbose; /**< Be verbose */
189static int oneone; /**< If false use HTTP/1.0 for requests*/ 258static 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
195static int by_step; /**< Send request byte-by-byte */ 264static int by_step; /**< Send request byte-by-byte */
196static int upl_chunked; /**< Use chunked encoding for request body */ 265static int upl_chunked; /**< Use chunked encoding for request body */
197 266
267static unsigned int rate_limiter; /**< Maximum number of checks per second */
268
198static void 269static void
199test_global_init (void) 270test_global_init (void)
200{ 271{
@@ -208,6 +279,57 @@ test_global_init (void)
208 /* exit (77); */ 279 /* exit (77); */
209#endif 280#endif
210 } 281 }
282 rate_limiter = 0;
283#if defined(HAVE_SYSCTL) && defined(CTL_NET) && defined(PF_INET) && \
284 defined(IPPROTO_ICMP) && defined(ICMPCTL_ICMPLIM)
285 if (use_hard_close)
286 {
287 int mib[4];
288 int limit;
289 size_t limit_size = sizeof(limit);
290 mib[0] = CTL_NET;
291 mib[1] = PF_INET;
292 mib[2] = IPPROTO_ICMP;
293 mib[3] = ICMPCTL_ICMPLIM;
294 if ((0 != sysctl (mib, 4, &limit, &limit_size, NULL, 0)) ||
295 (sizeof(limit) != limit_size) )
296 externalErrorExitDesc ("Cannot get RST rate limit value");
297 if (limit > 0)
298 {
299#ifndef _MHD_HEAVY_TESTS
300 fprintf (stderr, "This system has limits on number of RST packet"
301 " per second (%d).\nThis test will be used only if configured "
302 "with '--enable-heavy-test'.\n", limit);
303 exit (77);
304#else /* _MHD_HEAVY_TESTS */
305 int test_limit; /**< Maximum number of checks per second */
306 test_limit = limit - limit / 10; /* Add some space to not hit the limiter */
307 test_limit /= 4; /* Assume that all four tests with 'hard_close' run in parallel */
308 test_limit -= 5; /* Add some more space to not hit the limiter */
309 test_limit /= 3; /* Use only one third of available limit */
310 if (test_limit <= 0)
311 {
312 fprintf (stderr, "System limit for 'net.inet.icmp.icmplim' is "
313 "too strict for this test (value: %d).\n", limit);
314 exit (77);
315 }
316 if (verbose)
317 {
318 printf ("Limiting number of checks to %d checks/second.\n", test_limit);
319 fflush (stdout);
320 }
321 rate_limiter = (unsigned int) test_limit;
322#if ! defined(HAVE_USLEEP) && ! defined(HAVE_NANOSLEEP) && ! defined(_WIN32)
323 fprintf (stderr, "Sleep function is required for this test, "
324 "but not available on this system.\n");
325 exit (77);
326#endif
327#endif /* _MHD_HEAVY_TESTS */
328 }
329 }
330#endif /* HAVE_SYSCTL && CTL_NET && PF_INET &&
331 IPPROTO_ICMP && ICMPCTL_ICMPLIM */
332
211} 333}
212 334
213 335
@@ -1229,6 +1351,8 @@ performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
1229 const union MHD_DaemonInfo *di; 1351 const union MHD_DaemonInfo *di;
1230 MHD_socket lstn_sk; 1352 MHD_socket lstn_sk;
1231 int client_accepted; 1353 int client_accepted;
1354 int full_req_recieved;
1355 int full_req_sent;
1232 1356
1233 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD); 1357 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
1234 if (NULL == di) 1358 if (NULL == di)
@@ -1240,6 +1364,7 @@ performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
1240 1364
1241 _MHD_dumbClient_start_connect (clnt); 1365 _MHD_dumbClient_start_connect (clnt);
1242 1366
1367 full_req_recieved = 0;
1243 start = time (NULL); 1368 start = time (NULL);
1244 do 1369 do
1245 { 1370 {
@@ -1247,6 +1372,7 @@ performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
1247 fd_set ws; 1372 fd_set ws;
1248 fd_set es; 1373 fd_set es;
1249 MHD_socket maxMhdSk; 1374 MHD_socket maxMhdSk;
1375 int num_ready;
1250 1376
1251 maxMhdSk = MHD_INVALID_SOCKET; 1377 maxMhdSk = MHD_INVALID_SOCKET;
1252 FD_ZERO (&rs); 1378 FD_ZERO (&rs);
@@ -1257,6 +1383,7 @@ performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
1257 /* client has finished, check whether MHD is still 1383 /* client has finished, check whether MHD is still
1258 * processing any connections */ 1384 * processing any connections */
1259 unsigned long long to; 1385 unsigned long long to;
1386 full_req_sent = 1;
1260 if (client_accepted && (MHD_YES != MHD_get_timeout (d, &to))) 1387 if (client_accepted && (MHD_YES != MHD_get_timeout (d, &to)))
1261 { 1388 {
1262 ret = 0; 1389 ret = 0;
@@ -1264,12 +1391,25 @@ performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
1264 } 1391 }
1265 } 1392 }
1266 else 1393 else
1267 _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es); 1394 {
1395 full_req_sent = _MHD_dumbClient_is_req_sent (clnt);
1396 if ((! full_req_sent) || full_req_recieved || (0 == rate_limiter))
1397 _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
1398 }
1268 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) 1399 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
1269 mhdErrorExitDesc ("MHD_get_fdset() failed"); 1400 mhdErrorExitDesc ("MHD_get_fdset() failed");
1270 tv.tv_sec = 1; 1401 if ((! full_req_sent) || full_req_recieved || (0 == rate_limiter))
1271 tv.tv_usec = 250 * 1000; 1402 {
1272 if (-1 == select (maxMhdSk + 1, &rs, &ws, &es, &tv)) 1403 tv.tv_sec = 1;
1404 tv.tv_usec = 250 * 1000;
1405 }
1406 else
1407 { /* Request completely sent but not yet fully received */
1408 tv.tv_sec = 0;
1409 tv.tv_usec = FINAL_PACKETS_MS * 1000;
1410 }
1411 num_ready = select (maxMhdSk + 1, &rs, &ws, &es, &tv);
1412 if (-1 == num_ready)
1273 { 1413 {
1274#ifdef MHD_POSIX_SOCKETS 1414#ifdef MHD_POSIX_SOCKETS
1275 if (EINTR != errno) 1415 if (EINTR != errno)
@@ -1282,6 +1422,11 @@ performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
1282#endif 1422#endif
1283 continue; 1423 continue;
1284 } 1424 }
1425 if (0 == num_ready)
1426 { /* select() finished by timeout, looks like no more packets are pending */
1427 if (full_req_sent && (! full_req_recieved))
1428 full_req_recieved = 1;
1429 }
1285 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) 1430 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
1286 mhdErrorExitDesc ("MHD_run_from_select() failed"); 1431 mhdErrorExitDesc ("MHD_run_from_select() failed");
1287 if (! client_accepted) 1432 if (! client_accepted)
@@ -1290,11 +1435,15 @@ performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
1290 { 1435 {
1291 /* Do not close the socket on client side until 1436 /* Do not close the socket on client side until
1292 * MHD is accepted and processed the socket. */ 1437 * MHD is accepted and processed the socket. */
1293 if (! _MHD_dumbClient_is_req_sent (clnt) || 1438 if (! full_req_sent || (client_accepted && ! FD_ISSET (lstn_sk, &rs)))
1294 (client_accepted && ! FD_ISSET (lstn_sk, &rs)))
1295 { 1439 {
1296 if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es)) 1440 if ((! full_req_sent) || full_req_recieved || (0 == rate_limiter))
1297 clnt = NULL; 1441 {
1442 /* When rate limiter is enabled, all sent packets must be received
1443 * before client close connection to avoid RST for every ACK. */
1444 if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
1445 clnt = NULL;
1446 }
1298 } 1447 }
1299 } 1448 }
1300 /* Use double timeout value here so MHD would be able to catch timeout 1449 /* 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,
1403 int ret = 0; /* Return value */ 1552 int ret = 0; /* Return value */
1404 size_t req_total_size; 1553 size_t req_total_size;
1405 size_t limit_send_size; 1554 size_t limit_send_size;
1555 size_t inc_size;
1406 int expected_reason; 1556 int expected_reason;
1407 int found_right_reason; 1557 int found_right_reason;
1408 1558
@@ -1441,11 +1591,35 @@ performTestQueries (struct MHD_Daemon *d, int d_port,
1441 MHD_REQUEST_TERMINATED_READ_ERROR : 1591 MHD_REQUEST_TERMINATED_READ_ERROR :
1442 MHD_REQUEST_TERMINATED_CLIENT_ABORT; 1592 MHD_REQUEST_TERMINATED_CLIENT_ABORT;
1443 found_right_reason = 0; 1593 found_right_reason = 0;
1594 if (0 != rate_limiter)
1595 {
1596 if (verbose)
1597 {
1598 printf ("Pausing for rate limiter...");
1599 fflush (stdout);
1600 }
1601 _MHD_sleep (1150); /* Just a bit more than one second */
1602 if (verbose)
1603 {
1604 printf (" OK\n");
1605 fflush (stdout);
1606 }
1607 inc_size = ((req_total_size - 1) + (rate_limiter - 1)) / rate_limiter;
1608 if (0 == inc_size)
1609 inc_size = 1;
1610 }
1611 else
1612 inc_size = 1;
1613
1444 start = time (NULL); 1614 start = time (NULL);
1445 for (limit_send_size = 1; limit_send_size < req_total_size; limit_send_size++) 1615 for (limit_send_size = 1; limit_send_size < req_total_size;
1616 limit_send_size += inc_size)
1446 { 1617 {
1447 int test_succeed; 1618 int test_succeed;
1448 test_succeed = 0; 1619 test_succeed = 0;
1620 /* Make sure that maximum size is tested */
1621 if (req_total_size - inc_size < limit_send_size)
1622 limit_send_size = req_total_size - 1;
1449 qParam.total_send_max = limit_send_size; 1623 qParam.total_send_max = limit_send_size;
1450 /* To be updated by callbacks */ 1624 /* To be updated by callbacks */
1451 ahc_param->cb_called = 0; 1625 ahc_param->cb_called = 0;
@@ -1501,7 +1675,9 @@ performTestQueries (struct MHD_Daemon *d, int d_port,
1501 term_result, sckt_result); 1675 term_result, sckt_result);
1502 } 1676 }
1503 1677
1504 if (time (NULL) - start > TIMEOUTS_VAL * 25) 1678 if (time (NULL) - start >
1679 (time_t) ((TIMEOUTS_VAL * 25)
1680 + (rate_limiter * FINAL_PACKETS_MS) / 1000 + 1))
1505 { 1681 {
1506 ret |= 1 << 2; 1682 ret |= 1 << 2;
1507 fprintf (stderr, "FAILED: Test total time exceeded.\n"); 1683 fprintf (stderr, "FAILED: Test total time exceeded.\n");