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:
Родитель
2bdb26a507
Коммит
f933449d3b
|
@ -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;
|
||||
}
|
||||
|
|
13
lib/http.c
13
lib/http.c
|
@ -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,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;
|
||||
}
|
Загрузка…
Ссылка в новой задаче