curl-curl/tests/unit/unit3216.c
Stefan Eissing 1a57302d1a
ratelimit: download finetune
When a download size is known and rate limiting is in effect, adjust the
duration of each measurement step and its rate for maximum precision.

Since it is unpredictable how long the last bytes of a download will
take, download speed can be thrown off if the "last bytes" are a
significant amount of the total download. Make the "last bytes" small in
comparision to the rest and "stretch" the rate limit intervals to
accommodate the difference.

Fix ngtcp2 receive data acknowldgements to be based on a local window
size tracking. This allows window updates controlled by rate limits.

Fix ratelimit wait time calculation to accomodate negative tokens.

h3 rate limit, update timeers

Make download rate limits work correctly in ngtcp2. Fix multi handling
of rate limits to set a timer for when limits will update again.

Without running the transfer on limit updates, protocols like h2/h3 may
stall if the server does not send due to stream windows being too small.

scorecard: measure download speedlimits

When running scorecard with --limit-rate=n, show the reported download
speed averages plus percentage deviation from the limit.

Closes #20228
2026-01-16 16:42:31 +01:00

105 lines
4.3 KiB
C

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "unitcheck.h"
#include "ratelimit.h"
static CURLcode test_unit3216(const char *arg)
{
UNITTEST_BEGIN_SIMPLE
struct Curl_rlimit r;
struct curltime ts;
/* A ratelimit that is unlimited */
ts = curlx_now();
Curl_rlimit_init(&r, 0, 0, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX, "inf");
Curl_rlimit_drain(&r, 1000000, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX, "drain keep inf");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 0, "inf never waits");
Curl_rlimit_block(&r, TRUE, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "inf blocked to 0");
Curl_rlimit_drain(&r, 1000000, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "blocked inf");
Curl_rlimit_block(&r, FALSE, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX,
"unblocked unlimited");
/* A ratelimit that give 10 tokens per second */
ts = curlx_now();
Curl_rlimit_init(&r, 10, 0, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "initial 10");
Curl_rlimit_drain(&r, 5, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 5, "drain to 5");
Curl_rlimit_drain(&r, 3, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 2, "drain to 2");
ts.tv_usec += 1000; /* 1ms */
Curl_rlimit_drain(&r, 3, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == -1, "drain to -1");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 1099, "wait 1099ms");
ts.tv_usec += 1000; /* 1ms */
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 1098, "wait 1098ms");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, &ts) == 9, "10 inc per sec");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, &ts) == 19, "10 inc per sec(2)");
ts = curlx_now();
Curl_rlimit_block(&r, TRUE, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "10 blocked to 0");
Curl_rlimit_block(&r, FALSE, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "unblocked 10");
/* A ratelimit that give 10 tokens per second, max burst 15/s */
ts = curlx_now();
Curl_rlimit_init(&r, 10, 15, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "initial 10");
Curl_rlimit_drain(&r, 5, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 5, "drain to 5");
Curl_rlimit_drain(&r, 3, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 2, "drain to 2");
Curl_rlimit_drain(&r, 3, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == -1, "drain to -1");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, &ts) == 9, "10 inc per sec");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10/15 burst limit");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10/15 burst limit(2)");
Curl_rlimit_drain(&r, 15, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "drain to 0");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 1000, "wait 1 sec");
ts.tv_usec += 500000; /* half a sec, cheating on second carry */
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "0 after 0.5 sec");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 500, "wait 0.5 sec");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "10 after 1.5 sec");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 0, "wait 0");
ts.tv_usec += 500000; /* half a sec, cheating on second carry */
fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10 after 2 sec");
UNITTEST_END_SIMPLE
}