1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-24 00:58:48 -05:00

CURLINFO_RETRY_AFTER: parse the Retry-After header value

This is only the libcurl part that provides the information. There's no
user of the parsed value. This change includes three new tests for the
parser.

Ref: #3794
This commit is contained in:
Daniel Stenberg 2019-08-06 11:30:08 +02:00
parent 2bdb26a507
commit f933449d3b
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
14 changed files with 317 additions and 6 deletions

View File

@ -157,6 +157,9 @@ Upload size. See \fICURLINFO_CONTENT_LENGTH_UPLOAD_T(3)\fP
.IP CURLINFO_CONTENT_TYPE .IP CURLINFO_CONTENT_TYPE
Content type from the Content-Type header. Content type from the Content-Type header.
See \fICURLINFO_CONTENT_TYPE(3)\fP See \fICURLINFO_CONTENT_TYPE(3)\fP
.IP CURLINFO_RETRY_AFTER
The value from the from the Retry-After header.
See \fICURLINFO_RETRY_AFTER(3)\fP
.IP CURLINFO_PRIVATE .IP CURLINFO_PRIVATE
User's private data pointer. User's private data pointer.
See \fICURLINFO_PRIVATE(3)\fP See \fICURLINFO_PRIVATE(3)\fP

View File

@ -0,0 +1,63 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2019, 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.haxx.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.
.\" *
.\" **************************************************************************
.\"
.TH CURLINFO_RETRY_AFTER 3 "6 Aug 2019" "libcurl 7.66.0" "curl_easy_getinfo options"
.SH NAME
CURLINFO_RETRY_AFTER \- returns the Retry-After retry delay
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_RETRY_AFTER, curl_off_t *retry);
.SH DESCRIPTION
Pass a pointer to a curl_off_t variable to receive the number of seconds the
HTTP server suggesets the client should wait until the next request is
issued. The information from the "Retry-After:" header.
While the HTTP header might contain a fixed date string, the
\fICURLINFO_RETRY_AFTER(3)\fP will alwaus return number of seconds to wait -
or zero if there was no header or the header couldn't be parsed.
.SH DEFAULT
Returns zero delay if there was no header.
.SH PROTOCOLS
HTTP(S)
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
if(curl) {
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
res = curl_easy_perform(curl);
if(res == CURLE_OK) {
curl_off_t wait = 0;
curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &wait);
if(wait)
printf("Wait for %" CURL_FORMAT_CURL_OFF_T " seconds\\n", wait);
}
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in curl 7.66.0
.SH RETURN VALUE
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO"
.BR CURLOPT_STDERR "(3), " CURLOPT_HEADERFUNCTION "(3), "

View File

@ -43,6 +43,7 @@ man_MANS = \
CURLINFO_REDIRECT_URL.3 \ CURLINFO_REDIRECT_URL.3 \
CURLINFO_REQUEST_SIZE.3 \ CURLINFO_REQUEST_SIZE.3 \
CURLINFO_RESPONSE_CODE.3 \ CURLINFO_RESPONSE_CODE.3 \
CURLINFO_RETRY_AFTER.3 \
CURLINFO_RTSP_CLIENT_CSEQ.3 \ CURLINFO_RTSP_CLIENT_CSEQ.3 \
CURLINFO_RTSP_CSEQ_RECV.3 \ CURLINFO_RTSP_CSEQ_RECV.3 \
CURLINFO_RTSP_SERVER_CSEQ.3 \ CURLINFO_RTSP_SERVER_CSEQ.3 \

View File

@ -267,6 +267,7 @@ CURLINFO_REDIRECT_TIME_T 7.61.0
CURLINFO_REDIRECT_URL 7.18.2 CURLINFO_REDIRECT_URL 7.18.2
CURLINFO_REQUEST_SIZE 7.4.1 CURLINFO_REQUEST_SIZE 7.4.1
CURLINFO_RESPONSE_CODE 7.10.8 CURLINFO_RESPONSE_CODE 7.10.8
CURLINFO_RETRY_AFTER 7.66.0
CURLINFO_RTSP_CLIENT_CSEQ 7.20.0 CURLINFO_RTSP_CLIENT_CSEQ 7.20.0
CURLINFO_RTSP_CSEQ_RECV 7.20.0 CURLINFO_RTSP_CSEQ_RECV 7.20.0
CURLINFO_RTSP_SERVER_CSEQ 7.20.0 CURLINFO_RTSP_SERVER_CSEQ 7.20.0

View File

@ -2623,8 +2623,9 @@ typedef enum {
CURLINFO_STARTTRANSFER_TIME_T = CURLINFO_OFF_T + 54, CURLINFO_STARTTRANSFER_TIME_T = CURLINFO_OFF_T + 54,
CURLINFO_REDIRECT_TIME_T = CURLINFO_OFF_T + 55, CURLINFO_REDIRECT_TIME_T = CURLINFO_OFF_T + 55,
CURLINFO_APPCONNECT_TIME_T = CURLINFO_OFF_T + 56, CURLINFO_APPCONNECT_TIME_T = CURLINFO_OFF_T + 56,
CURLINFO_RETRY_AFTER = CURLINFO_OFF_T + 57,
CURLINFO_LASTONE = 56 CURLINFO_LASTONE = 57
} CURLINFO; } CURLINFO;
/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as

View File

@ -246,7 +246,6 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
case CURLINFO_PROTOCOL: case CURLINFO_PROTOCOL:
*param_longp = data->info.conn_protocol; *param_longp = data->info.conn_protocol;
break; break;
default: default:
return CURLE_UNKNOWN_OPTION; return CURLE_UNKNOWN_OPTION;
} }
@ -304,7 +303,9 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
case CURLINFO_REDIRECT_TIME_T: case CURLINFO_REDIRECT_TIME_T:
*param_offt = data->progress.t_redirect; *param_offt = data->progress.t_redirect;
break; break;
case CURLINFO_RETRY_AFTER:
*param_offt = data->info.retry_after;
break;
default: default:
return CURLE_UNKNOWN_OPTION; return CURLE_UNKNOWN_OPTION;
} }

View File

@ -3953,6 +3953,19 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(result) if(result)
return result; return result;
} }
else if(checkprefix("Retry-After:", k->p)) {
/* Retry-After = HTTP-date / delay-seconds */
curl_off_t retry_after = 0; /* zero for unknown or "now" */
time_t date = curl_getdate(&k->p[12], NULL);
if(-1 == date) {
/* not a date, try it as a decimal number */
(void)curlx_strtoofft(&k->p[12], NULL, 10, &retry_after);
}
else
/* convert date to number of seconds into the future */
retry_after = date - time(NULL);
data->info.retry_after = retry_after; /* store it */
}
else if(!k->http_bodyless && checkprefix("Content-Range:", k->p)) { else if(!k->http_bodyless && checkprefix("Content-Range:", k->p)) {
/* Content-Range: bytes [num]- /* Content-Range: bytes [num]-
Content-Range: bytes: [num]- Content-Range: bytes: [num]-

View File

@ -1071,6 +1071,7 @@ struct PureInfo {
long numconnects; /* how many new connection did libcurl created */ long numconnects; /* how many new connection did libcurl created */
char *contenttype; /* the content type of the object */ char *contenttype; /* the content type of the object */
char *wouldredirect; /* URL this would've been redirected to if asked to */ char *wouldredirect; /* URL this would've been redirected to if asked to */
curl_off_t retry_after; /* info from Retry-After: header */
/* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip' /* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip'
and, 'conn_local_port' are copied over from the connectdata struct in and, 'conn_local_port' are copied over from the connectdata struct in
@ -1090,7 +1091,6 @@ struct PureInfo {
OpenSSL, GnuTLS, Schannel, NSS and GSKit OpenSSL, GnuTLS, Schannel, NSS and GSKit
builds. Asked for with CURLOPT_CERTINFO builds. Asked for with CURLOPT_CERTINFO
/ CURLINFO_CERTINFO */ / CURLINFO_CERTINFO */
bit timecond:1; /* set to TRUE if the time condition didn't match, which bit timecond:1; /* set to TRUE if the time condition didn't match, which
thus made the document NOT get fetched */ thus made the document NOT get fetched */
}; };

View File

@ -178,7 +178,7 @@ test1540 test1541 \
test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \ test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \
test1558 test1559 test1560 test1561 test1562 test1563 \ test1558 test1559 test1560 test1561 test1562 test1563 \
\ \
test1590 test1591 test1592 test1593 \ test1590 test1591 test1592 test1593 test1594 \
\ \
test1600 test1601 test1602 test1603 test1604 test1605 test1606 test1607 \ test1600 test1601 test1602 test1603 test1604 test1605 test1606 test1607 \
test1608 test1609 test1620 test1621 \ test1608 test1609 test1620 test1621 \

52
tests/data/test1594 Normal file
View File

@ -0,0 +1,52 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
HTTP replaced headers
CURLOPT_TIMECONDITION
If-Modified-Since
</keywords>
</info>
# Server-side
<reply>
<data nocheck="yes">
HTTP/1.1 503 Error
Date: Thu, 11 Jul 2019 02:26:59 GMT
Server: test-server/swsclose
Retry-After: 22
</data>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP Retry-After header parsing and extraction
</name>
<tool>
lib1594
</tool>
<command>
http://%HOSTIP:%HTTPPORT/1594
</command>
</client>
<verify>
<strip>
^User-Agent:.*
</strip>
<protocol>
GET /1594 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
</protocol>
<stdout>
Retry-After: 22
</stdout>
</verify>
</testcase>

51
tests/data/test1595 Normal file
View File

@ -0,0 +1,51 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
HTTP replaced headers
CURLOPT_TIMECONDITION
If-Modified-Since
</keywords>
</info>
# Server-side
<reply>
<data nocheck="yes">
HTTP/1.1 503 Error
Date: Thu, 11 Jul 2019 02:26:59 GMT
Server: test-server/swsclose
</data>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP Retry-After header extraction (without header)
</name>
<tool>
lib1594
</tool>
<command>
http://%HOSTIP:%HTTPPORT/1595
</command>
</client>
<verify>
<strip>
^User-Agent:.*
</strip>
<protocol>
GET /1595 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
</protocol>
<stdout>
Retry-After: 0
</stdout>
</verify>
</testcase>

52
tests/data/test1596 Normal file
View File

@ -0,0 +1,52 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
HTTP replaced headers
CURLOPT_TIMECONDITION
If-Modified-Since
</keywords>
</info>
# Server-side
<reply>
<data nocheck="yes">
HTTP/1.1 503 Error
Date: Thu, 11 Jul 2019 02:26:59 GMT
Server: test-server/swsclose
Retry-After: Thu, 11 Jul 2024 02:26:59 GMT
</data>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP Retry-After header parsing using a date
</name>
<tool>
lib1596
</tool>
<command>
http://%HOSTIP:%HTTPPORT/1596
</command>
</client>
<verify>
<strip>
^User-Agent:.*
</strip>
<protocol>
GET /1596 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
</protocol>
<stdout>
Retry-After: 172066
</stdout>
</verify>
</testcase>

View File

@ -32,7 +32,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1540 lib1541 \ lib1540 lib1541 \
lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \ lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \
lib1558 lib1559 lib1560 \ lib1558 lib1559 lib1560 \
lib1591 lib1592 lib1593 \ lib1591 lib1592 lib1593 lib1594 lib1596 \
lib1900 lib1905 lib1906 \ lib1900 lib1905 lib1906 \
lib2033 lib2033
@ -544,6 +544,13 @@ lib1592_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1592
lib1593_SOURCES = lib1593.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1593_SOURCES = lib1593.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1593_LDADD = $(TESTUTIL_LIBS) lib1593_LDADD = $(TESTUTIL_LIBS)
lib1594_SOURCES = lib1594.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1594_LDADD = $(TESTUTIL_LIBS)
lib1596_SOURCES = lib1594.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1596_LDADD = $(TESTUTIL_LIBS)
lib1596_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1596
lib1900_SOURCES = lib1900.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1900_SOURCES = lib1900.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1900_LDADD = $(TESTUTIL_LIBS) lib1900_LDADD = $(TESTUTIL_LIBS)
lib1900_CPPFLAGS = $(AM_CPPFLAGS) lib1900_CPPFLAGS = $(AM_CPPFLAGS)

66
tests/libtest/lib1594.c Normal file
View File

@ -0,0 +1,66 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, 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.haxx.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.
*
***************************************************************************/
/* Testing Retry-After header parser */
#include "test.h"
#include "memdebug.h"
int test(char *URL)
{
struct curl_slist *header = NULL;
curl_off_t retry;
CURL *curl = NULL;
int res = 0;
global_init(CURL_GLOBAL_ALL);
easy_init(curl);
easy_setopt(curl, CURLOPT_URL, URL);
res = curl_easy_perform(curl);
if(res)
goto test_cleanup;
res = curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry);
if(res)
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);
test_cleanup:
/* always cleanup */
curl_easy_cleanup(curl);
curl_slist_free_all(header);
curl_global_cleanup();
return res;
}