http: ignore invalid Retry-After times

- Treat negative Retry-After date-based times as 0.

- Treat Retry-After times greater than 6 hours as 6 hours.

Prior to this change Retry-After did not have a limited range and the
server could have set a time greater than 6 hours or a date in the past
that would result in a negative time, either of which may be unexpected
by the user.

The 6 hour limit is purposely not documented so that it can be changed
in the future if necessary.

Closes https://github.com/curl/curl/pull/15833
This commit is contained in:
Jay Satiro 2024-12-26 15:38:39 -05:00
parent 8d1f26b866
commit 6c70ec16c7
4 changed files with 20 additions and 12 deletions

View File

@ -36,6 +36,11 @@ While the HTTP header might contain a fixed date string, the
CURLINFO_RETRY_AFTER(3) always returns the number of seconds to wait - CURLINFO_RETRY_AFTER(3) always returns the number of seconds to wait -
or zero if there was no header or the header could not be parsed. or zero if there was no header or the header could not be parsed.
This option used to return a negative wait time if the server provided a date
in the past. Since 8.12.0, a negative wait time is returned as zero. In any
case we recommend checking that the wait time is within an acceptable range for
your circumstance.
# DEFAULT # DEFAULT
Zero if there was no header. Zero if there was no header.

View File

@ -2901,11 +2901,19 @@ static CURLcode http_header(struct Curl_easy *data,
(void)curlx_strtoofft(v, NULL, 10, &retry_after); (void)curlx_strtoofft(v, NULL, 10, &retry_after);
if(!retry_after) { if(!retry_after) {
time_t date = Curl_getdate_capped(v); time_t date = Curl_getdate_capped(v);
if((time_t)-1 != date) time_t current = time(NULL);
if((time_t)-1 != date && date > current) {
/* convert date to number of seconds into the future */ /* convert date to number of seconds into the future */
retry_after = date - time(NULL); retry_after = date - current;
}
} }
data->info.retry_after = retry_after; /* store it */ if(retry_after < 0)
retry_after = 0;
/* limit to 6 hours max. this is not documented so that it can be changed
in the future if necessary. */
if(retry_after > 21600)
retry_after = 21600;
data->info.retry_after = retry_after;
return CURLE_OK; return CURLE_OK;
} }
break; break;

View File

@ -13,9 +13,9 @@ If-Modified-Since
<reply> <reply>
<data nocheck="yes"> <data nocheck="yes">
HTTP/1.1 429 Too Many Requests HTTP/1.1 429 Too Many Requests
Date: Thu, 11 Jul 2019 02:26:59 GMT Date: Wed, 31 Dec 2036 02:26:59 GMT
Server: test-server/swsclose Server: test-server/swsclose
Retry-After: Thu, 11 Jul 2024 02:26:59 GMT Retry-After: Wed, 31 Dec 2036 02:26:59 GMT
</data> </data>
</reply> </reply>
@ -42,8 +42,9 @@ Host: %HOSTIP:%HTTPPORT
Accept: */* Accept: */*
</protocol> </protocol>
# Retry-After time is limited to 6 hours (21600 seconds)
<stdout> <stdout>
Retry-After 172066 Retry-After 21600
</stdout> </stdout>
</verify> </verify>
</testcase> </testcase>

View File

@ -49,12 +49,6 @@ CURLcode test(char *URL)
if(res) if(res)
goto test_cleanup; goto test_cleanup;
#ifdef LIB1596
/* we get a relative number of seconds, so add the number of seconds
we're at to make it a somewhat stable number. Then remove accuracy. */
retry += time(NULL);
retry /= 10000;
#endif
printf("Retry-After %" CURL_FORMAT_CURL_OFF_T "\n", retry); printf("Retry-After %" CURL_FORMAT_CURL_OFF_T "\n", retry);
test_cleanup: test_cleanup: