mirror of
https://github.com/curl/curl.git
synced 2026-04-11 12:01:42 +08:00
tool: simplify retrycheck()
- By making retry_sleep() a separate funtion that determines how long to wait until the next retry. - switch the retry timer to uint32 to make it uniform across platforms Closes #21279
This commit is contained in:
parent
008aa2b38f
commit
bb3670f929
@ -185,8 +185,8 @@ struct OperationConfig {
|
|||||||
long httpversion;
|
long httpversion;
|
||||||
unsigned long socks5_auth;/* auth bitmask for socks5 proxies */
|
unsigned long socks5_auth;/* auth bitmask for socks5 proxies */
|
||||||
long req_retry; /* number of retries */
|
long req_retry; /* number of retries */
|
||||||
long retry_delay_ms; /* delay between retries (in milliseconds),
|
uint32_t retry_delay_ms; /* delay between retries (in milliseconds), 0 means
|
||||||
0 means increase exponentially */
|
increase exponentially */
|
||||||
long retry_maxtime_ms; /* maximum time to keep retrying */
|
long retry_maxtime_ms; /* maximum time to keep retrying */
|
||||||
|
|
||||||
unsigned long mime_options; /* Mime option flags. */
|
unsigned long mime_options; /* Mime option flags. */
|
||||||
|
|||||||
@ -2429,7 +2429,9 @@ static ParameterError opt_secs(struct OperationConfig *config,
|
|||||||
config->connecttimeout_ms = val;
|
config->connecttimeout_ms = val;
|
||||||
break;
|
break;
|
||||||
case C_RETRY_DELAY: /* --retry-delay */
|
case C_RETRY_DELAY: /* --retry-delay */
|
||||||
config->retry_delay_ms = val;
|
if(val >= INT32_MAX)
|
||||||
|
val = INT32_MAX;
|
||||||
|
config->retry_delay_ms = (uint32_t)val;
|
||||||
break;
|
break;
|
||||||
case C_RETRY_MAX_TIME: /* --retry-max-time */
|
case C_RETRY_MAX_TIME: /* --retry-max-time */
|
||||||
config->retry_maxtime_ms = val;
|
config->retry_maxtime_ms = val;
|
||||||
|
|||||||
@ -356,35 +356,102 @@ static bool is_outfile_auto_resumable(struct OperationConfig *config,
|
|||||||
result != CURLE_WRITE_ERROR && result != CURLE_RANGE_ERROR;
|
result != CURLE_WRITE_ERROR && result != CURLE_RANGE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum retryreason {
|
||||||
|
RETRY_NO,
|
||||||
|
RETRY_ALL_ERRORS,
|
||||||
|
RETRY_TIMEOUT,
|
||||||
|
RETRY_CONNREFUSED,
|
||||||
|
RETRY_HTTP,
|
||||||
|
RETRY_FTP
|
||||||
|
};
|
||||||
|
|
||||||
|
/* figure out how long to wait until retry */
|
||||||
|
static uint32_t retry_sleep(struct OperationConfig *config,
|
||||||
|
struct per_transfer *per,
|
||||||
|
enum retryreason retry,
|
||||||
|
bool *retryp)
|
||||||
|
{
|
||||||
|
static const char * const m[] = {
|
||||||
|
"(retrying all errors)",
|
||||||
|
": timeout",
|
||||||
|
": connection refused",
|
||||||
|
": HTTP error",
|
||||||
|
": FTP error"
|
||||||
|
};
|
||||||
|
CURL *curl = per->curl;
|
||||||
|
uint32_t sleeptime = 0;
|
||||||
|
if(RETRY_HTTP == retry) {
|
||||||
|
curl_off_t retry_after = 0;
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry_after);
|
||||||
|
if(retry_after) {
|
||||||
|
/* make sure it does not overflow */
|
||||||
|
if(retry_after > (curl_off_t)(UINT32_MAX / 1000))
|
||||||
|
sleeptime = UINT32_MAX;
|
||||||
|
else
|
||||||
|
sleeptime = (uint32_t)retry_after * 1000U; /* milliseconds */
|
||||||
|
|
||||||
|
/* if adding retry_after seconds to the process would exceed the
|
||||||
|
maximum time allowed for retrying, then exit the retries right
|
||||||
|
away */
|
||||||
|
if(config->retry_maxtime_ms) {
|
||||||
|
timediff_t ms = curlx_timediff_ms(curlx_now(), per->retrystart);
|
||||||
|
|
||||||
|
if((CURL_OFF_T_MAX - sleeptime < ms) ||
|
||||||
|
(ms + sleeptime > config->retry_maxtime_ms)) {
|
||||||
|
warnf("The Retry-After: time would "
|
||||||
|
"make this command line exceed the maximum allowed time "
|
||||||
|
"for retries.");
|
||||||
|
*retryp = FALSE;
|
||||||
|
return 0; /* no retry */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!sleeptime && !config->retry_delay_ms) {
|
||||||
|
if(!per->retry_sleep)
|
||||||
|
per->retry_sleep = RETRY_SLEEP_DEFAULT;
|
||||||
|
else
|
||||||
|
per->retry_sleep *= 2;
|
||||||
|
if(per->retry_sleep > RETRY_SLEEP_MAX)
|
||||||
|
per->retry_sleep = RETRY_SLEEP_MAX;
|
||||||
|
}
|
||||||
|
if(!sleeptime)
|
||||||
|
sleeptime = per->retry_sleep;
|
||||||
|
|
||||||
|
warnf("Problem %s. Retrying in %u%s%.*u second%s. "
|
||||||
|
"%ld retr%s left.",
|
||||||
|
m[retry - 1], sleeptime / 1000,
|
||||||
|
(sleeptime % 1000 ? "." : ""),
|
||||||
|
(sleeptime % 1000 ? 3 : 0),
|
||||||
|
sleeptime % 1000,
|
||||||
|
(sleeptime == 1000 ? "" : "s"),
|
||||||
|
per->retry_remaining,
|
||||||
|
(per->retry_remaining > 1 ? "ies" : "y"));
|
||||||
|
|
||||||
|
return sleeptime;
|
||||||
|
}
|
||||||
|
|
||||||
static CURLcode retrycheck(struct OperationConfig *config,
|
static CURLcode retrycheck(struct OperationConfig *config,
|
||||||
struct per_transfer *per,
|
struct per_transfer *per,
|
||||||
CURLcode result,
|
CURLcode result,
|
||||||
bool *retryp,
|
bool *retryp,
|
||||||
long *delayms)
|
uint32_t *delayms)
|
||||||
{
|
{
|
||||||
CURL *curl = per->curl;
|
CURL *curl = per->curl;
|
||||||
struct OutStruct *outs = &per->outs;
|
struct OutStruct *outs = &per->outs;
|
||||||
enum {
|
enum retryreason reason = RETRY_NO;
|
||||||
RETRY_NO,
|
|
||||||
RETRY_ALL_ERRORS,
|
|
||||||
RETRY_TIMEOUT,
|
|
||||||
RETRY_CONNREFUSED,
|
|
||||||
RETRY_HTTP,
|
|
||||||
RETRY_FTP,
|
|
||||||
RETRY_LAST /* not used */
|
|
||||||
} retry = RETRY_NO;
|
|
||||||
if((result == CURLE_OPERATION_TIMEDOUT) ||
|
if((result == CURLE_OPERATION_TIMEDOUT) ||
|
||||||
(result == CURLE_COULDNT_RESOLVE_HOST) ||
|
(result == CURLE_COULDNT_RESOLVE_HOST) ||
|
||||||
(result == CURLE_COULDNT_RESOLVE_PROXY) ||
|
(result == CURLE_COULDNT_RESOLVE_PROXY) ||
|
||||||
(result == CURLE_FTP_ACCEPT_TIMEOUT))
|
(result == CURLE_FTP_ACCEPT_TIMEOUT))
|
||||||
/* retry timeout always */
|
/* retry timeout always */
|
||||||
retry = RETRY_TIMEOUT;
|
reason = RETRY_TIMEOUT;
|
||||||
else if(config->retry_connrefused &&
|
else if(config->retry_connrefused &&
|
||||||
(result == CURLE_COULDNT_CONNECT)) {
|
(result == CURLE_COULDNT_CONNECT)) {
|
||||||
long oserrno = 0;
|
long oserrno = 0;
|
||||||
curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &oserrno);
|
curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &oserrno);
|
||||||
if(SOCKECONNREFUSED == oserrno)
|
if(SOCKECONNREFUSED == oserrno)
|
||||||
retry = RETRY_CONNREFUSED;
|
reason = RETRY_CONNREFUSED;
|
||||||
}
|
}
|
||||||
else if((result == CURLE_OK) ||
|
else if((result == CURLE_OK) ||
|
||||||
(config->fail && (result == CURLE_HTTP_RETURNED_ERROR))) {
|
(config->fail && (result == CURLE_HTTP_RETURNED_ERROR))) {
|
||||||
@ -408,7 +475,7 @@ static CURLcode retrycheck(struct OperationConfig *config,
|
|||||||
case 504: /* Gateway Timeout */
|
case 504: /* Gateway Timeout */
|
||||||
case 522: /* Connection Timed Out (Cloudflare) */
|
case 522: /* Connection Timed Out (Cloudflare) */
|
||||||
case 524: /* Proxy Read Timeout (Cloudflare) */
|
case 524: /* Proxy Read Timeout (Cloudflare) */
|
||||||
retry = RETRY_HTTP;
|
reason = RETRY_HTTP;
|
||||||
/*
|
/*
|
||||||
* At this point, we have already written data to the output
|
* At this point, we have already written data to the output
|
||||||
* file (or terminal). If we write to a file, we must rewind
|
* file (or terminal). If we write to a file, we must rewind
|
||||||
@ -437,72 +504,24 @@ static CURLcode retrycheck(struct OperationConfig *config,
|
|||||||
* amount of users and we are not one of them. All 4xx codes
|
* amount of users and we are not one of them. All 4xx codes
|
||||||
* are transient.
|
* are transient.
|
||||||
*/
|
*/
|
||||||
retry = RETRY_FTP;
|
reason = RETRY_FTP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result && !retry && config->retry_all_errors)
|
if(result && !reason && config->retry_all_errors)
|
||||||
retry = RETRY_ALL_ERRORS;
|
reason = RETRY_ALL_ERRORS;
|
||||||
|
|
||||||
if(retry) {
|
if(reason) {
|
||||||
long sleeptime = 0;
|
|
||||||
curl_off_t retry_after = 0;
|
|
||||||
static const char * const m[] = {
|
|
||||||
NULL,
|
|
||||||
"(retrying all errors)",
|
|
||||||
": timeout",
|
|
||||||
": connection refused",
|
|
||||||
": HTTP error",
|
|
||||||
": FTP error"
|
|
||||||
};
|
|
||||||
bool truncate = TRUE; /* truncate output file */
|
bool truncate = TRUE; /* truncate output file */
|
||||||
|
|
||||||
if(RETRY_HTTP == retry) {
|
/* how long to wait until next attempt */
|
||||||
curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry_after);
|
uint32_t sleeptime;
|
||||||
if(retry_after) {
|
|
||||||
/* store in a 'long', make sure it does not overflow */
|
|
||||||
if(retry_after > LONG_MAX / 1000)
|
|
||||||
sleeptime = LONG_MAX;
|
|
||||||
else if((retry_after * 1000) > sleeptime)
|
|
||||||
sleeptime = (long)retry_after * 1000; /* milliseconds */
|
|
||||||
|
|
||||||
/* if adding retry_after seconds to the process would exceed the
|
*retryp = TRUE;
|
||||||
maximum time allowed for retrying, then exit the retries right
|
|
||||||
away */
|
|
||||||
if(config->retry_maxtime_ms) {
|
|
||||||
timediff_t ms = curlx_timediff_ms(curlx_now(), per->retrystart);
|
|
||||||
|
|
||||||
if((CURL_OFF_T_MAX - sleeptime < ms) ||
|
|
||||||
(ms + sleeptime > config->retry_maxtime_ms)) {
|
|
||||||
warnf("The Retry-After: time would "
|
|
||||||
"make this command line exceed the maximum allowed time "
|
|
||||||
"for retries.");
|
|
||||||
*retryp = FALSE;
|
|
||||||
return CURLE_OK; /* no retry */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!sleeptime && !config->retry_delay_ms) {
|
|
||||||
if(!per->retry_sleep)
|
|
||||||
per->retry_sleep = RETRY_SLEEP_DEFAULT;
|
|
||||||
else
|
|
||||||
per->retry_sleep *= 2;
|
|
||||||
if(per->retry_sleep > RETRY_SLEEP_MAX)
|
|
||||||
per->retry_sleep = RETRY_SLEEP_MAX;
|
|
||||||
}
|
|
||||||
if(!sleeptime)
|
|
||||||
sleeptime = per->retry_sleep;
|
|
||||||
warnf("Problem %s. "
|
|
||||||
"Retrying in %ld%s%.*ld second%s. "
|
|
||||||
"%ld retr%s left.",
|
|
||||||
m[retry], sleeptime / 1000L,
|
|
||||||
(sleeptime % 1000L ? "." : ""),
|
|
||||||
(sleeptime % 1000L ? 3 : 0),
|
|
||||||
sleeptime % 1000L,
|
|
||||||
(sleeptime == 1000L ? "" : "s"),
|
|
||||||
per->retry_remaining,
|
|
||||||
(per->retry_remaining > 1 ? "ies" : "y"));
|
|
||||||
|
|
||||||
|
sleeptime = retry_sleep(config, per, reason, retryp);
|
||||||
|
if(!*retryp)
|
||||||
|
/* no retry */
|
||||||
|
return CURLE_OK;
|
||||||
per->retry_remaining--;
|
per->retry_remaining--;
|
||||||
|
|
||||||
/* Skip truncation of outfile if auto-resume is enabled for download and
|
/* Skip truncation of outfile if auto-resume is enabled for download and
|
||||||
@ -576,7 +595,6 @@ static CURLcode retrycheck(struct OperationConfig *config,
|
|||||||
outs->bytes = 0; /* clear for next round */
|
outs->bytes = 0; /* clear for next round */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*retryp = TRUE;
|
|
||||||
per->num_retries++;
|
per->num_retries++;
|
||||||
*delayms = sleeptime;
|
*delayms = sleeptime;
|
||||||
result = CURLE_OK;
|
result = CURLE_OK;
|
||||||
@ -710,7 +728,7 @@ static CURLcode post_close_output(struct per_transfer *per,
|
|||||||
static CURLcode post_per_transfer(struct per_transfer *per,
|
static CURLcode post_per_transfer(struct per_transfer *per,
|
||||||
CURLcode result,
|
CURLcode result,
|
||||||
bool *retryp,
|
bool *retryp,
|
||||||
long *delay) /* milliseconds! */
|
uint32_t *delay) /* milliseconds! */
|
||||||
{
|
{
|
||||||
struct OutStruct *outs = &per->outs;
|
struct OutStruct *outs = &per->outs;
|
||||||
struct OperationConfig *config = per->config;
|
struct OperationConfig *config = per->config;
|
||||||
@ -1804,7 +1822,7 @@ static CURLcode check_finished(struct parastate *s)
|
|||||||
msg = curl_multi_info_read(s->multi, &rc);
|
msg = curl_multi_info_read(s->multi, &rc);
|
||||||
if(msg) {
|
if(msg) {
|
||||||
bool retry;
|
bool retry;
|
||||||
long delay;
|
uint32_t delay;
|
||||||
struct per_transfer *ended;
|
struct per_transfer *ended;
|
||||||
CURL *easy = msg->easy_handle;
|
CURL *easy = msg->easy_handle;
|
||||||
CURLcode tres = msg->data.result;
|
CURLcode tres = msg->data.result;
|
||||||
@ -1993,7 +2011,7 @@ static CURLcode serial_transfers(CURLSH *share)
|
|||||||
}
|
}
|
||||||
for(per = transfers; per;) {
|
for(per = transfers; per;) {
|
||||||
bool retry;
|
bool retry;
|
||||||
long delay_ms;
|
uint32_t delay_ms;
|
||||||
bool bailout = FALSE;
|
bool bailout = FALSE;
|
||||||
struct curltime start;
|
struct curltime start;
|
||||||
|
|
||||||
@ -2256,7 +2274,7 @@ static CURLcode run_all_transfers(CURLSH *share,
|
|||||||
/* cleanup if there are any left */
|
/* cleanup if there are any left */
|
||||||
for(per = transfers; per;) {
|
for(per = transfers; per;) {
|
||||||
bool retry;
|
bool retry;
|
||||||
long delay;
|
uint32_t delay;
|
||||||
CURLcode result2 = post_per_transfer(per, result, &retry, &delay);
|
CURLcode result2 = post_per_transfer(per, result, &retry, &delay);
|
||||||
if(!result)
|
if(!result)
|
||||||
/* do not overwrite the original error */
|
/* do not overwrite the original error */
|
||||||
|
|||||||
@ -30,15 +30,16 @@
|
|||||||
#include "tool_cfgable.h"
|
#include "tool_cfgable.h"
|
||||||
|
|
||||||
struct per_transfer {
|
struct per_transfer {
|
||||||
|
char errorbuffer[CURL_ERROR_SIZE];
|
||||||
/* double linked */
|
/* double linked */
|
||||||
struct per_transfer *next;
|
struct per_transfer *next;
|
||||||
struct per_transfer *prev;
|
struct per_transfer *prev;
|
||||||
struct OperationConfig *config; /* for this transfer */
|
struct OperationConfig *config; /* for this transfer */
|
||||||
const struct curl_certinfo *certinfo;
|
const struct curl_certinfo *certinfo;
|
||||||
CURL *curl;
|
CURL *curl;
|
||||||
|
/* NULL or malloced */
|
||||||
|
char *uploadfile;
|
||||||
long retry_remaining;
|
long retry_remaining;
|
||||||
long retry_sleep_default;
|
|
||||||
long retry_sleep;
|
|
||||||
long num_retries; /* counts the performed retries */
|
long num_retries; /* counts the performed retries */
|
||||||
struct curltime start; /* start of this transfer */
|
struct curltime start; /* start of this transfer */
|
||||||
struct curltime retrystart;
|
struct curltime retrystart;
|
||||||
@ -62,12 +63,10 @@ struct per_transfer {
|
|||||||
curl_off_t ulnow;
|
curl_off_t ulnow;
|
||||||
curl_off_t uploadfilesize; /* expected total amount */
|
curl_off_t uploadfilesize; /* expected total amount */
|
||||||
curl_off_t uploadedsofar; /* amount delivered from the callback */
|
curl_off_t uploadedsofar; /* amount delivered from the callback */
|
||||||
|
uint32_t retry_sleep_default;
|
||||||
|
uint32_t retry_sleep;
|
||||||
BIT(dltotal_added); /* if the total has been added from this */
|
BIT(dltotal_added); /* if the total has been added from this */
|
||||||
BIT(ultotal_added);
|
BIT(ultotal_added);
|
||||||
|
|
||||||
/* NULL or malloced */
|
|
||||||
char *uploadfile;
|
|
||||||
char errorbuffer[CURL_ERROR_SIZE];
|
|
||||||
BIT(infdopen); /* TRUE if infd needs closing */
|
BIT(infdopen); /* TRUE if infd needs closing */
|
||||||
BIT(noprogress);
|
BIT(noprogress);
|
||||||
BIT(was_last_header_empty);
|
BIT(was_last_header_empty);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user