Skip to content
Permalink
Browse files

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
  • Loading branch information...
bagder committed Aug 6, 2019
1 parent 2bdb26a commit f933449d3b6c24e66d4a0c590bca3e7b2a39140d
@@ -157,6 +157,9 @@ Upload size. See \fICURLINFO_CONTENT_LENGTH_UPLOAD_T(3)\fP
.IP CURLINFO_CONTENT_TYPE
Content type from the Content-Type header.
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
User's private data pointer.
See \fICURLINFO_PRIVATE(3)\fP
@@ -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), "
@@ -43,6 +43,7 @@ man_MANS = \
CURLINFO_REDIRECT_URL.3 \
CURLINFO_REQUEST_SIZE.3 \
CURLINFO_RESPONSE_CODE.3 \
CURLINFO_RETRY_AFTER.3 \
CURLINFO_RTSP_CLIENT_CSEQ.3 \
CURLINFO_RTSP_CSEQ_RECV.3 \
CURLINFO_RTSP_SERVER_CSEQ.3 \
@@ -267,6 +267,7 @@ CURLINFO_REDIRECT_TIME_T 7.61.0
CURLINFO_REDIRECT_URL 7.18.2
CURLINFO_REQUEST_SIZE 7.4.1
CURLINFO_RESPONSE_CODE 7.10.8
CURLINFO_RETRY_AFTER 7.66.0
CURLINFO_RTSP_CLIENT_CSEQ 7.20.0
CURLINFO_RTSP_CSEQ_RECV 7.20.0
CURLINFO_RTSP_SERVER_CSEQ 7.20.0
@@ -2623,8 +2623,9 @@ typedef enum {
CURLINFO_STARTTRANSFER_TIME_T = CURLINFO_OFF_T + 54,
CURLINFO_REDIRECT_TIME_T = CURLINFO_OFF_T + 55,
CURLINFO_APPCONNECT_TIME_T = CURLINFO_OFF_T + 56,
CURLINFO_RETRY_AFTER = CURLINFO_OFF_T + 57,

CURLINFO_LASTONE = 56
CURLINFO_LASTONE = 57
} CURLINFO;

/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
@@ -246,7 +246,6 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
case CURLINFO_PROTOCOL:
*param_longp = data->info.conn_protocol;
break;

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

case CURLINFO_RETRY_AFTER:
*param_offt = data->info.retry_after;
break;
default:
return CURLE_UNKNOWN_OPTION;
}
@@ -3953,6 +3953,19 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(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)) {
/* Content-Range: bytes [num]-
Content-Range: bytes: [num]-
@@ -1071,6 +1071,7 @@ struct PureInfo {
long numconnects; /* how many new connection did libcurl created */
char *contenttype; /* the content type of the object */
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'
and, 'conn_local_port' are copied over from the connectdata struct in
@@ -1090,7 +1091,6 @@ struct PureInfo {
OpenSSL, GnuTLS, Schannel, NSS and GSKit
builds. Asked for with CURLOPT_CERTINFO
/ CURLINFO_CERTINFO */

bit timecond:1; /* set to TRUE if the time condition didn't match, which
thus made the document NOT get fetched */
};
@@ -178,7 +178,7 @@ test1540 test1541 \
test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \
test1558 test1559 test1560 test1561 test1562 test1563 \
\
test1590 test1591 test1592 test1593 \
test1590 test1591 test1592 test1593 test1594 \
\
test1600 test1601 test1602 test1603 test1604 test1605 test1606 test1607 \
test1608 test1609 test1620 test1621 \
@@ -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>
@@ -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>
@@ -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>
@@ -32,7 +32,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1540 lib1541 \
lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \
lib1558 lib1559 lib1560 \
lib1591 lib1592 lib1593 \
lib1591 lib1592 lib1593 lib1594 lib1596 \
lib1900 lib1905 lib1906 \
lib2033

@@ -544,6 +544,13 @@ lib1592_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1592
lib1593_SOURCES = lib1593.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
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_LDADD = $(TESTUTIL_LIBS)
lib1900_CPPFLAGS = $(AM_CPPFLAGS)

0 comments on commit f933449

Please sign in to comment.
You can’t perform that action at this time.