mirror of
https://github.com/curl/curl.git
synced 2025-09-01 09:55:00 +03:00
Compare commits
3 Commits
b23e0f01af
...
54098af721
Author | SHA1 | Date | |
---|---|---|---|
|
54098af721 | ||
|
4a3ed6fc16 | ||
|
f92d9c0c5f |
|
@ -332,6 +332,12 @@ Callback for wildcard matching. See CURLOPT_FNMATCH_FUNCTION(3)
|
|||
|
||||
Follow HTTP redirects. See CURLOPT_FOLLOWLOCATION(3)
|
||||
|
||||
## CURLOPT_FORBID_RETRY_ON_REUSE
|
||||
|
||||
Prevent curl from transparently retry a transfer on a new connection when a
|
||||
previously reused connection was reset or otherwise no longer unavailable.
|
||||
See CURLOPT_FORBID_RETRY_ON_REUSE(3)
|
||||
|
||||
## CURLOPT_FORBID_REUSE
|
||||
|
||||
Prevent subsequent connections from reusing this. See CURLOPT_FORBID_REUSE(3)
|
||||
|
|
106
docs/libcurl/opts/CURLOPT_FORBID_RETRY_ON_REUSE.md
Normal file
106
docs/libcurl/opts/CURLOPT_FORBID_RETRY_ON_REUSE.md
Normal file
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
c: Copyright (C) 2025 Hewlett Packard Enterprise Development LP
|
||||
SPDX-License-Identifier: curl
|
||||
Title: CURLOPT_FORBID_RETRY_ON_REUSE
|
||||
Section: 3
|
||||
Source: libcurl
|
||||
See-also:
|
||||
- CURLOPT_FRESH_CONNECT (3)
|
||||
- CURLOPT_FORBID_REUSE (3)
|
||||
Protocol:
|
||||
- All
|
||||
Added-in: 8.16.0
|
||||
---
|
||||
|
||||
# NAME
|
||||
|
||||
CURLOPT_FORBID_RETRY_ON_REUSE - prevent retry in case of reused connection is reset
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
~~~c
|
||||
#include <curl/curl.h>
|
||||
|
||||
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_FORBID_RETRY_ON_REUSE,
|
||||
long forbid_retry);
|
||||
~~~
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Pass a long. Set *forbid_retry* to 1 to prevent libcurl from resubmitting a
|
||||
transfer on a new connection when the previous reused connection was reset.
|
||||
Instead, the reset is returned to the caller as a `CURLE_RECV_ERROR`.
|
||||
|
||||
This option only affects retry decision if the connection used to perform
|
||||
the request (and from which CURL received a TCP reset) is reused.
|
||||
|
||||
Normally, libcurl considers connection resets on reused connections as a
|
||||
transient timing issue. The reset event is only visible to curl at the time
|
||||
libcurl attempts to issue transfer on that connection. As a result,
|
||||
sometimes libcurl may issue more than 1 identical transfers in a row on
|
||||
different connections with a single `curl_easy_perform()` call. Caller is
|
||||
not informed about any previous transfer attempts that may or may not have
|
||||
arrived at the server before the reset happened.
|
||||
|
||||
This can break transactional application-level protocols if the protocol
|
||||
state machine considers connection state changes as part of state
|
||||
transition edges, or the protocol involves non-idempotent requests with
|
||||
side effects.
|
||||
|
||||
Before introduction of this option, the only way to avoid unobservable
|
||||
retries was to set CURLOPT_FORBID_REUSE(3) to 1. However, without
|
||||
connection reuse and keepalive, the application pays significant
|
||||
overhead from the TCP and TLS handshake for every transfer. This option
|
||||
decouples implicit retry behavior from connection reuse, allowing the
|
||||
application to benefit from connection reuse without risking unobservable
|
||||
retries.
|
||||
|
||||
Set to 0 to have libcurl transparently retry the transfer on a new
|
||||
connection if the reused connection was reset (default behavior).
|
||||
|
||||
# DEFAULT
|
||||
|
||||
0
|
||||
|
||||
# %PROTOCOLS%
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
~~~c
|
||||
#include <unistd.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
if(curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
|
||||
curl_easy_setopt(curl, CURLOPT_FORBID_RETRY_ON_REUSE, 1L);
|
||||
|
||||
/*
|
||||
* This request will establish a connection and retained it for reuse
|
||||
* after the transfer is done.
|
||||
*/
|
||||
curl_easy_perform(curl);
|
||||
|
||||
/* Wait long enough for the server to drop connection. */
|
||||
sleep(60);
|
||||
|
||||
/*
|
||||
* curl will fail this request from the reset instead of
|
||||
* transmitting the same transfer again over a new connection.
|
||||
*/
|
||||
curl_easy_perform(curl);
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
# %AVAILABILITY%
|
||||
|
||||
# RETURN VALUE
|
||||
|
||||
curl_easy_setopt(3) returns a CURLcode indicating success or error.
|
||||
|
||||
CURLE_OK (0) means everything was OK, non-zero means an error occurred, see
|
||||
libcurl-errors(3).
|
|
@ -185,6 +185,7 @@ man_MANS = \
|
|||
CURLOPT_FNMATCH_DATA.3 \
|
||||
CURLOPT_FNMATCH_FUNCTION.3 \
|
||||
CURLOPT_FOLLOWLOCATION.3 \
|
||||
CURLOPT_FORBID_RETRY_ON_REUSE.3 \
|
||||
CURLOPT_FORBID_REUSE.3 \
|
||||
CURLOPT_FRESH_CONNECT.3 \
|
||||
CURLOPT_FTP_ACCOUNT.3 \
|
||||
|
|
|
@ -646,6 +646,7 @@ CURLOPT_FILETIME 7.5
|
|||
CURLOPT_FNMATCH_DATA 7.21.0
|
||||
CURLOPT_FNMATCH_FUNCTION 7.21.0
|
||||
CURLOPT_FOLLOWLOCATION 7.1
|
||||
CURLOPT_FORBID_RETRY_ON_REUSE 8.16.0
|
||||
CURLOPT_FORBID_REUSE 7.7
|
||||
CURLOPT_FRESH_CONNECT 7.7
|
||||
CURLOPT_FTP_ACCOUNT 7.13.0
|
||||
|
|
|
@ -2258,6 +2258,10 @@ typedef enum {
|
|||
/* set TLS supported signature algorithms */
|
||||
CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328),
|
||||
|
||||
/* prevent transparent retries on a new connection when a previously
|
||||
* reused connection was reset or otherwise no longer unavailable. */
|
||||
CURLOPT(CURLOPT_FORBID_RETRY_ON_REUSE, CURLOPTTYPE_LONG, 329),
|
||||
|
||||
CURLOPT_LASTENTRY /* the last unused */
|
||||
} CURLoption;
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@ const struct curl_easyoption Curl_easyopts[] = {
|
|||
{"FNMATCH_DATA", CURLOPT_FNMATCH_DATA, CURLOT_CBPTR, 0},
|
||||
{"FNMATCH_FUNCTION", CURLOPT_FNMATCH_FUNCTION, CURLOT_FUNCTION, 0},
|
||||
{"FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION, CURLOT_LONG, 0},
|
||||
{"FORBID_RETRY_ON_REUSE", CURLOPT_FORBID_RETRY_ON_REUSE, CURLOT_LONG, 0},
|
||||
{"FORBID_REUSE", CURLOPT_FORBID_REUSE, CURLOT_LONG, 0},
|
||||
{"FRESH_CONNECT", CURLOPT_FRESH_CONNECT, CURLOT_LONG, 0},
|
||||
{"FTPAPPEND", CURLOPT_APPEND, CURLOT_LONG, CURLOT_FLAG_ALIAS},
|
||||
|
@ -380,6 +381,6 @@ const struct curl_easyoption Curl_easyopts[] = {
|
|||
*/
|
||||
int Curl_easyopts_check(void)
|
||||
{
|
||||
return (CURLOPT_LASTENTRY % 10000) != (328 + 1);
|
||||
return (CURLOPT_LASTENTRY % 10000) != (329 + 1);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -452,6 +452,13 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option,
|
|||
*/
|
||||
s->reuse_forbid = enabled;
|
||||
break;
|
||||
case CURLOPT_FORBID_RETRY_ON_REUSE:
|
||||
/**
|
||||
* No retry under any circumstances even when a reset happens on a reused
|
||||
* connection.
|
||||
*/
|
||||
s->retry_on_reuse_forbid = enabled;
|
||||
break;
|
||||
case CURLOPT_FRESH_CONNECT:
|
||||
/*
|
||||
* This transfer shall not use a previously cached connection but
|
||||
|
|
|
@ -667,7 +667,7 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
|
|||
return CURLE_OK;
|
||||
|
||||
if((data->req.bytecount + data->req.headerbytecount == 0) &&
|
||||
conn->bits.reuse &&
|
||||
conn->bits.reuse && conn->bits.retry_on_reuse &&
|
||||
(!data->req.no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP))
|
||||
#ifndef CURL_DISABLE_RTSP
|
||||
&& (data->set.rtspreq != RTSPREQ_RECEIVE)
|
||||
|
|
|
@ -3514,6 +3514,10 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||
*************************************************************/
|
||||
if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy)
|
||||
conn->bits.tunnel_proxy = TRUE;
|
||||
|
||||
/* Carry over the no-retry-on-reuse into connection bits. */
|
||||
conn->bits.retry_on_reuse = !data->set.retry_on_reuse_forbid;
|
||||
|
||||
#endif
|
||||
|
||||
/*************************************************************
|
||||
|
|
|
@ -379,6 +379,8 @@ struct ConnectBits {
|
|||
/* always modify bits.close with the connclose() and connkeep() macros! */
|
||||
BIT(close); /* if set, we close the connection after this request */
|
||||
BIT(reuse); /* if set, this is a reused connection */
|
||||
BIT(retry_on_reuse); /* if set, a reused connection after connection reset
|
||||
is allowed to retry the request */
|
||||
BIT(altused); /* this is an alt-svc "redirect" */
|
||||
BIT(conn_to_host); /* if set, this connection has a "connect to host"
|
||||
that overrides the host in the URL */
|
||||
|
@ -1605,6 +1607,7 @@ struct UserDefined {
|
|||
#endif
|
||||
BIT(reuse_forbid); /* forbidden to be reused, close after use */
|
||||
BIT(reuse_fresh); /* do not reuse an existing connection */
|
||||
BIT(retry_on_reuse_forbid); /* do not retry under connection reset */
|
||||
BIT(no_signal); /* do not use any signal/alarm handler */
|
||||
BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */
|
||||
BIT(ignorecl); /* ignore content length */
|
||||
|
|
|
@ -1398,6 +1398,8 @@ static CURLcode add_parallel_transfers(CURLM *multi, CURLSH *share,
|
|||
#ifdef DEBUGBUILD
|
||||
if(getenv("CURL_FORBID_REUSE"))
|
||||
(void)curl_easy_setopt(per->curl, CURLOPT_FORBID_REUSE, 1L);
|
||||
if(getenv("CURL_FORBID_RETRY_ON_REUSE"))
|
||||
(void)curl_easy_setopt(per->curl, CURLOPT_FORBID_RETRY_ON_REUSE, 1L);
|
||||
#endif
|
||||
|
||||
mcode = curl_multi_add_handle(multi, per->curl);
|
||||
|
@ -1868,6 +1870,8 @@ static CURLcode serial_transfers(CURLSH *share)
|
|||
#ifdef DEBUGBUILD
|
||||
if(getenv("CURL_FORBID_REUSE"))
|
||||
(void)curl_easy_setopt(per->curl, CURLOPT_FORBID_REUSE, 1L);
|
||||
if(getenv("CURL_FORBID_RETRY_ON_REUSE"))
|
||||
(void)curl_easy_setopt(per->curl, CURLOPT_FORBID_RETRY_ON_REUSE, 1L);
|
||||
|
||||
if(global->test_duphandle) {
|
||||
CURL *dup = curl_easy_duphandle(per->curl);
|
||||
|
|
|
@ -423,10 +423,13 @@ static CURLcode glob_parse(struct URLGlob *glob, const char *pattern,
|
|||
if(++glob->size >= glob->palloc) {
|
||||
struct URLPattern *np = NULL;
|
||||
glob->palloc *= 2;
|
||||
if(glob->size < 10000) /* avoid ridiculous amounts */
|
||||
if(glob->size < 255) { /* avoid ridiculous amounts */
|
||||
np = realloc(glob->pattern, glob->palloc * sizeof(struct URLPattern));
|
||||
if(!np)
|
||||
return globerror(glob, NULL, pos, CURLE_OUT_OF_MEMORY);
|
||||
if(!np)
|
||||
return globerror(glob, NULL, pos, CURLE_OUT_OF_MEMORY);
|
||||
}
|
||||
else
|
||||
return globerror(glob, "too many {} sets", pos, CURLE_URL_MALFORMAT);
|
||||
glob->pattern = np;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ test718 test719 test720 test721 test722 test723 test724 test725 test726 \
|
|||
test727 test728 test729 test730 test731 test732 test733 test734 test735 \
|
||||
test736 test737 test738 test739 test740 test741 test742 test743 test744 \
|
||||
test745 test746 test747 test748 test749 test750 test751 test752 test753 \
|
||||
test754 test755 test756 test757 test758 test759 test760 \
|
||||
test754 test755 test756 test757 test758 test759 test760 test761 \
|
||||
test780 test781 test782 test783 test784 test785 test786 test787 test788 \
|
||||
test789 test790 test791 test792 test793 test794 test795 test796 test797 \
|
||||
\
|
||||
|
|
33
tests/data/test761
Normal file
33
tests/data/test761
Normal file
|
@ -0,0 +1,33 @@
|
|||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
globbing
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
none
|
||||
</server>
|
||||
<name>
|
||||
too many {} globs
|
||||
</name>
|
||||
<command>
|
||||
http://testingthis/{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<errorcode>
|
||||
3
|
||||
</errorcode>
|
||||
<stderr mode="text">
|
||||
curl: (3) too many {} sets in URL position 403:
|
||||
http://testingthis/{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a}b{a
|
||||
</stderr>
|
||||
</verify>
|
||||
</testcase>
|
Loading…
Reference in New Issue
Block a user