curl: add --rate to set max request rate per time unit

--rate "12/m" - for 12 per minute or
--rate "5/h" - for 5 per hour

Removed from TODO

Closes #8671
This commit is contained in:
Daniel Stenberg 2022-05-23 17:59:56 +02:00
parent 64641d2170
commit 8f48b5d783
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
8 changed files with 103 additions and 15 deletions

View file

@ -323,6 +323,8 @@ struct GlobalConfig {
char *libcurl; /* Output libcurl code to this file name */
bool fail_early; /* exit on first transfer error */
bool styled_output; /* enable fancy output style detection */
long ms_per_transfer; /* start next transfer after (at least) this
many milliseconds */
#ifdef CURLDEBUG
bool test_event_based;
#endif

View file

@ -93,6 +93,7 @@ static const struct LongShort aliases[]= {
{"*h", "trace-ascii", ARG_FILENAME},
{"*H", "alpn", ARG_BOOL},
{"*i", "limit-rate", ARG_STRING},
{"*I", "rate", ARG_STRING},
{"*j", "compressed", ARG_BOOL},
{"*J", "tr-encoding", ARG_BOOL},
{"*k", "digest", ARG_BOOL},
@ -738,6 +739,51 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
config->sendpersecond = value;
}
break;
case 'I': /* --rate (request rate) */
{
/* support a few different suffixes, extract the suffix first, then
get the number and convert to per hour.
/s == per second
/m == per minute
/h == per hour (default)
/d == per day (24 hours)
*/
char *div = strchr(nextarg, '/');
char number[26];
long denominator;
long numerator = 60*60*1000; /* default per hour */
size_t numlen = div ? (size_t)(div - nextarg) : strlen(nextarg);
if(numlen > sizeof(number)-1)
return PARAM_NUMBER_TOO_LARGE;
strncpy(number, nextarg, numlen);
number[numlen] = 0;
err = str2unum(&denominator, number);
if(err)
return err;
if(denominator < 1)
return PARAM_BAD_USE;
if(div) {
char unit = div[1];
switch(unit) {
case 's': /* per second */
numerator = 1000;
break;
case 'm': /* per minute */
numerator = 60*1000;
break;
case 'h': /* per hour */
break;
case 'd': /* per day */
numerator = 24*60*60*1000;
break;
default:
errorf(global, "unsupported --rate unit\n");
return PARAM_BAD_USE;
}
}
global->ms_per_transfer = numerator/denominator;
}
break;
case 'j': /* --compressed */
if(toggle &&

View file

@ -559,6 +559,9 @@ const struct helptxt helptext[] = {
{"-r, --range <range>",
"Retrieve only the bytes within RANGE",
CURLHELP_HTTP | CURLHELP_FTP | CURLHELP_SFTP | CURLHELP_FILE},
{" --rate <max request rate>",
"Request rate for serial transfers",
CURLHELP_CONNECTION},
{" --raw",
"Do HTTP \"raw\"; no transfer decoding",
CURLHELP_HTTP},

View file

@ -2359,8 +2359,9 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
}
for(per = transfers; per;) {
bool retry;
long delay;
long delay_ms;
bool bailout = FALSE;
struct timeval start;
result = pre_transfer(global, per);
if(result)
break;
@ -2372,6 +2373,7 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
break;
}
#endif
start = tvnow();
#ifdef CURLDEBUG
if(global->test_event_based)
result = curl_easy_perform_ev(per->curl);
@ -2379,9 +2381,9 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
#endif
result = curl_easy_perform(per->curl);
returncode = post_per_transfer(global, per, result, &retry, &delay);
returncode = post_per_transfer(global, per, result, &retry, &delay_ms);
if(retry) {
tool_go_sleep(delay);
tool_go_sleep(delay_ms);
continue;
}
@ -2399,6 +2401,18 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
if(bailout)
break;
if(per && global->ms_per_transfer) {
/* how long time did the most recent transfer take in number of
milliseconds */
long milli = tvdiff(tvnow(), start);
if(milli < global->ms_per_transfer) {
notef(global, "Transfer took %ld ms, waits %ldms as set by --rate\n",
milli, global->ms_per_transfer - milli);
/* The transfer took less time than wanted. Wait a little. */
tool_go_sleep(global->ms_per_transfer - milli);
}
}
}
if(returncode)
/* returncode errors have priority */