diff options
Diffstat (limited to 'src/microhttpd/test_client_put_stop.c')
-rw-r--r-- | src/microhttpd/test_client_put_stop.c | 196 |
1 files changed, 186 insertions, 10 deletions
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 | */ | ||
220 | void | ||
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 */ |
188 | static int verbose; /**< Be verbose */ | 257 | static int verbose; /**< Be verbose */ |
189 | static int oneone; /**< If false use HTTP/1.0 for requests*/ | 258 | 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 | |||
195 | static int by_step; /**< Send request byte-by-byte */ | 264 | static int by_step; /**< Send request byte-by-byte */ |
196 | static int upl_chunked; /**< Use chunked encoding for request body */ | 265 | static int upl_chunked; /**< Use chunked encoding for request body */ |
197 | 266 | ||
267 | static unsigned int rate_limiter; /**< Maximum number of checks per second */ | ||
268 | |||
198 | static void | 269 | static void |
199 | test_global_init (void) | 270 | test_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"); |