summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Tolar <tolar@yahooinc.com>2021-09-18 11:29:44 -0500
committerDaniel Stenberg <daniel@haxx.se>2021-10-06 14:38:59 +0200
commit5f563495f1f9cf7ef4f23f997e4c2707dd3e74a8 (patch)
treee6d017874795f4c0bfe61105b1a245f44699ae2c
parent013cb2ff7d3a37cab6910cc2e4d255377d341b6e (diff)
downloadcurl-5f563495f1f9cf7ef4f23f997e4c2707dd3e74a8.tar.gz
CURLOPT_MAXLIFETIME_CONN: maximum allowed lifetime for conn reuse
... and close connections that are too old instead of reusing them. By default, this behavior is disabled. Bug: https://curl.se/mail/lib-2021-09/0058.html Closes #7751
-rw-r--r--docs/libcurl/curl_easy_setopt.34
-rw-r--r--docs/libcurl/opts/CURLOPT_FORBID_REUSE.31
-rw-r--r--docs/libcurl/opts/CURLOPT_MAXAGE_CONN.36
-rw-r--r--docs/libcurl/opts/CURLOPT_MAXLIFETIME_CONN.366
-rw-r--r--docs/libcurl/opts/Makefile.inc1
-rw-r--r--docs/libcurl/symbols-in-versions1
-rw-r--r--include/curl/curl.h7
-rw-r--r--lib/easyoptions.c3
-rw-r--r--lib/setopt.c6
-rw-r--r--lib/url.c24
-rw-r--r--lib/urldata.h2
-rw-r--r--packages/OS400/curl.inc.in2
-rw-r--r--tests/data/Makefile.inc2
-rw-r--r--tests/data/test154267
-rw-r--r--tests/libtest/Makefile.inc6
-rw-r--r--tests/libtest/lib1542.c86
16 files changed, 272 insertions, 12 deletions
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index b1abf43e3..feb9a3955 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -492,7 +492,9 @@ Use a new connection. \fICURLOPT_FRESH_CONNECT(3)\fP
.IP CURLOPT_FORBID_REUSE
Prevent subsequent connections from re-using this. See \fICURLOPT_FORBID_REUSE(3)\fP
.IP CURLOPT_MAXAGE_CONN
-Limit the age of connections for reuse. See \fICURLOPT_MAXAGE_CONN(3)\fP
+Limit the age (idle time) of connections for reuse. See \fICURLOPT_MAXAGE_CONN(3)\fP
+.IP CURLOPT_MAXLIFETIME_CONN
+Limit the age (since creation) of connections for reuse. See \fICURLOPT_MAXLIFETIME_CONN(3)\fP
.IP CURLOPT_CONNECTTIMEOUT
Timeout for the connection phase. See \fICURLOPT_CONNECTTIMEOUT(3)\fP
.IP CURLOPT_CONNECTTIMEOUT_MS
diff --git a/docs/libcurl/opts/CURLOPT_FORBID_REUSE.3 b/docs/libcurl/opts/CURLOPT_FORBID_REUSE.3
index a1b2a8272..f2d6dd37e 100644
--- a/docs/libcurl/opts/CURLOPT_FORBID_REUSE.3
+++ b/docs/libcurl/opts/CURLOPT_FORBID_REUSE.3
@@ -57,3 +57,4 @@ Always
Returns CURLE_OK
.SH "SEE ALSO"
.BR CURLOPT_FRESH_CONNECT "(3), " CURLOPT_MAXCONNECTS "(3), "
+.BR CURLOPT_MAXLIFETIME_CONN "(3), "
diff --git a/docs/libcurl/opts/CURLOPT_MAXAGE_CONN.3 b/docs/libcurl/opts/CURLOPT_MAXAGE_CONN.3
index 0624e2e8f..40127c801 100644
--- a/docs/libcurl/opts/CURLOPT_MAXAGE_CONN.3
+++ b/docs/libcurl/opts/CURLOPT_MAXAGE_CONN.3
@@ -29,8 +29,8 @@ CURLOPT_MAXAGE_CONN \- max idle time allowed for reusing a connection
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MAXAGE_CONN, long maxage);
.SH DESCRIPTION
Pass a long as parameter containing \fImaxage\fP - the maximum time in seconds
-that you allow an existing connection to have to be considered for reuse for
-this request.
+that you allow an existing connection to have been idle to be considered for
+reuse for this request.
The "connection cache" that holds previously used connections. When a new
request is to be done, it will consider any connection that matches for
@@ -62,4 +62,4 @@ Added in libcurl 7.65.0
Returns CURLE_OK.
.SH "SEE ALSO"
.BR CURLOPT_TIMEOUT "(3), " CURLOPT_FORBID_REUSE "(3), "
-.BR CURLOPT_FRESH_CONNECT "(3), "
+.BR CURLOPT_FRESH_CONNECT "(3), " CURLOPT_MAXLIFETIME_CONN "(3), "
diff --git a/docs/libcurl/opts/CURLOPT_MAXLIFETIME_CONN.3 b/docs/libcurl/opts/CURLOPT_MAXLIFETIME_CONN.3
new file mode 100644
index 000000000..eeb6c134e
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_MAXLIFETIME_CONN.3
@@ -0,0 +1,66 @@
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 2021, 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.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 CURLOPT_MAXLIFETIME_CONN 3 "10 Nov 2021" "libcurl 7.80.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_MAXLIFETIME_CONN \- max lifetime (since creation) allowed for reusing a connection
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MAXLIFETIME_CONN, long maxlifetime);
+.SH DESCRIPTION
+Pass a long as parameter containing \fImaxlifetime\fP - the maximum time in
+seconds, since the creation of the connection, that you allow an existing
+connection to have to be considered for reuse for this request.
+
+libcurl features a connection cache that holds previously used connections.
+When a new request is to be done, it will consider any connection that matches
+for reuse. The \fICURLOPT_MAXLIFETIME_CONN(3)\fP limit prevents libcurl from
+trying very old connections for reuse. This can be used for client-side load
+balancing. If a connection is found in the cache that is older than this set
+\fImaxlifetime\fP, it will instead be closed once any in-progress transfers
+complete.
+
+If set to 0, this behavior is disabled: all connections are eligible for reuse.
+.SH DEFAULT
+Default \fImaxlifetime\fP is 0 seconds (i.e., disabled).
+.SH PROTOCOLS
+All
+.SH EXAMPLE
+.nf
+CURL *curl = curl_easy_init();
+if(curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
+
+ /* only allow each connection to be reused for 30 seconds */
+ curl_easy_setopt(curl, CURLOPT_MAXLIFETIME_CONN, 30L);
+
+ curl_easy_perform(curl);
+}
+.fi
+.SH AVAILABILITY
+Added in libcurl 7.80.0
+.SH RETURN VALUE
+Returns CURLE_OK.
+.SH "SEE ALSO"
+.BR CURLOPT_TIMEOUT "(3), " CURLOPT_FORBID_REUSE "(3), "
+.BR CURLOPT_FRESH_CONNECT "(3), " CURLOPT_MAXAGE_CONN "(3), "
diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc
index 7bac21024..55a0a3b7c 100644
--- a/docs/libcurl/opts/Makefile.inc
+++ b/docs/libcurl/opts/Makefile.inc
@@ -228,6 +228,7 @@ man_MANS = \
CURLOPT_MAXCONNECTS.3 \
CURLOPT_MAXFILESIZE.3 \
CURLOPT_MAXFILESIZE_LARGE.3 \
+ CURLOPT_MAXLIFETIME_CONN.3 \
CURLOPT_MAXREDIRS.3 \
CURLOPT_MAX_RECV_SPEED_LARGE.3 \
CURLOPT_MAX_SEND_SPEED_LARGE.3 \
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index 1dbcdbd3e..a8f2e08be 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -499,6 +499,7 @@ CURLOPT_MAXAGE_CONN 7.65.0
CURLOPT_MAXCONNECTS 7.7
CURLOPT_MAXFILESIZE 7.10.8
CURLOPT_MAXFILESIZE_LARGE 7.11.0
+CURLOPT_MAXLIFETIME_CONN 7.80.0
CURLOPT_MAXREDIRS 7.5
CURLOPT_MAX_RECV_SPEED_LARGE 7.15.5
CURLOPT_MAX_SEND_SPEED_LARGE 7.15.5
diff --git a/include/curl/curl.h b/include/curl/curl.h
index fb33eeb15..6b6ac8a05 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -2058,7 +2058,8 @@ typedef enum {
/* alt-svc cache file name to possibly read from/write to */
CURLOPT(CURLOPT_ALTSVC, CURLOPTTYPE_STRINGPOINT, 287),
- /* maximum age of a connection to consider it for reuse (in seconds) */
+ /* maximum age (idle time) of a connection to consider it for reuse
+ * (in seconds) */
CURLOPT(CURLOPT_MAXAGE_CONN, CURLOPTTYPE_LONG, 288),
/* SASL authorisation identity */
@@ -2127,6 +2128,10 @@ typedef enum {
/* Data passed to the CURLOPT_PREREQFUNCTION callback */
CURLOPT(CURLOPT_PREREQDATA, CURLOPTTYPE_CBPOINT, 313),
+ /* maximum age (since creation) of a connection to consider it for reuse
+ * (in seconds) */
+ CURLOPT(CURLOPT_MAXLIFETIME_CONN, CURLOPTTYPE_LONG, 314),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
diff --git a/lib/easyoptions.c b/lib/easyoptions.c
index bc149e7be..b6131d432 100644
--- a/lib/easyoptions.c
+++ b/lib/easyoptions.c
@@ -165,6 +165,7 @@ struct curl_easyoption Curl_easyopts[] = {
{"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0},
{"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0},
{"MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CURLOT_OFF_T, 0},
+ {"MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CURLOT_LONG, 0},
{"MAXREDIRS", CURLOPT_MAXREDIRS, CURLOT_LONG, 0},
{"MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CURLOT_OFF_T, 0},
{"MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0},
@@ -358,6 +359,6 @@ struct curl_easyoption Curl_easyopts[] = {
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != (313 + 1));
+ return ((CURLOPT_LASTENTRY%10000) != (314 + 1));
}
#endif
diff --git a/lib/setopt.c b/lib/setopt.c
index 8e19389ae..65fe252f4 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -2938,6 +2938,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.maxage_conn = arg;
break;
+ case CURLOPT_MAXLIFETIME_CONN:
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.maxlifetime_conn = arg;
+ break;
case CURLOPT_TRAILERFUNCTION:
#ifndef CURL_DISABLE_HTTP
data->set.trailer_callback = va_arg(param, curl_trailer_callback);
diff --git a/lib/url.c b/lib/url.c
index 5c31cadd6..1603b3072 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -622,6 +622,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
set->maxage_conn = 118;
+ set->maxlifetime_conn = 0;
set->http09_allowed = FALSE;
set->httpwant =
#ifdef USE_NGHTTP2
@@ -962,21 +963,36 @@ socks_proxy_info_matches(const struct proxy_info *data,
#define socks_proxy_info_matches(x,y) FALSE
#endif
-/* A connection has to have been idle for a shorter time than 'maxage_conn' to
- be subject for reuse. The success rate is just too low after this. */
+/* A connection has to have been idle for a shorter time than 'maxage_conn'
+ (the success rate is just too low after this), or created less than
+ 'maxlifetime_conn' ago, to be subject for reuse. */
static bool conn_maxage(struct Curl_easy *data,
struct connectdata *conn,
struct curltime now)
{
- timediff_t idletime = Curl_timediff(now, conn->lastused);
+ timediff_t idletime, lifetime;
+
+ idletime = Curl_timediff(now, conn->lastused);
idletime /= 1000; /* integer seconds is fine */
if(idletime > data->set.maxage_conn) {
- infof(data, "Too old connection (%ld seconds), disconnect it",
+ infof(data, "Too old connection (%ld seconds idle), disconnect it",
idletime);
return TRUE;
}
+
+ lifetime = Curl_timediff(now, conn->created);
+ lifetime /= 1000; /* integer seconds is fine */
+
+ if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
+ infof(data,
+ "Too old connection (%ld seconds since creation), disconnect it",
+ lifetime);
+ return TRUE;
+ }
+
+
return FALSE;
}
diff --git a/lib/urldata.h b/lib/urldata.h
index 47cb9e282..92df52467 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1678,6 +1678,8 @@ struct UserDefined {
long server_response_timeout; /* in milliseconds, 0 means no timeout */
long maxage_conn; /* in seconds, max idle time to allow a connection that
is to be reused */
+ long maxlifetime_conn; /* in seconds, max time since creation to allow a
+ connection that is to be reused */
long tftp_blksize; /* in bytes, 0 means use default */
curl_off_t filesize; /* size of file to upload, -1 means unknown */
long low_speed_limit; /* bytes/second */
diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in
index 94b2deba9..b6a37a60a 100644
--- a/packages/OS400/curl.inc.in
+++ b/packages/OS400/curl.inc.in
@@ -1583,6 +1583,8 @@
d c 40309
d CURLOPT_PROXY_CAINFO_BLOB...
d c 40310
+ d CURLOPT_MAXLIFETIME_CONN...
+ d c 00314
*
/if not defined(CURL_NO_OLDIES)
d CURLOPT_FILE c 10001
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 41249fdad..57f2abf69 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -189,7 +189,7 @@ test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \
test1516 test1517 test1518 test1519 test1520 test1521 test1522 test1523 \
test1524 test1525 test1526 test1527 test1528 test1529 test1530 test1531 \
test1532 test1533 test1534 test1535 test1536 test1537 test1538 test1539 \
-test1540 \
+test1540 test1542 \
\
test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \
test1558 test1559 test1560 test1561 test1562 test1563 test1564 test1565 \
diff --git a/tests/data/test1542 b/tests/data/test1542
new file mode 100644
index 000000000..6a9b7f0b7
--- /dev/null
+++ b/tests/data/test1542
@@ -0,0 +1,67 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+connection re-use
+persistent connection
+CURLOPT_MAXLIFETIME_CONN
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK
+Content-Length: 0
+
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<tool>
+lib%TESTNUMBER
+</tool>
+ <name>
+connection reuse with CURLOPT_MAXLIFETIME_CONN
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+<file name="log/stderr%TESTNUMBER" mode="text">
+== Info: Connection #0 to host %HOSTIP left intact
+== Info: Connection #0 to host %HOSTIP left intact
+== Info: Connection #0 to host %HOSTIP left intact
+== Info: Closing connection 0
+== Info: Connection #1 to host %HOSTIP left intact
+</file>
+<stripfile>
+$_ = '' if (($_ !~ /left intact/) && ($_ !~ /Closing connection/))
+</stripfile>
+</verify>
+</testcase>
diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
index 0f70ceb4b..ade101290 100644
--- a/tests/libtest/Makefile.inc
+++ b/tests/libtest/Makefile.inc
@@ -55,7 +55,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1518 lib1520 lib1521 lib1522 lib1523 \
lib1525 lib1526 lib1527 lib1528 lib1529 lib1530 lib1531 lib1532 lib1533 \
lib1534 lib1535 lib1536 lib1537 lib1538 lib1539 \
- lib1540 \
+ lib1540 lib1542 \
lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \
lib1558 lib1559 lib1560 lib1564 lib1565 lib1567 lib1568 lib1569 \
lib1591 lib1592 lib1593 lib1594 lib1596 \
@@ -569,6 +569,10 @@ lib1540_SOURCES = lib1540.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1540_LDADD = $(TESTUTIL_LIBS)
lib1540_CPPFLAGS = $(AM_CPPFLAGS)
+lib1542_SOURCES = lib1542.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(WARNLESS)
+lib1542_LDADD = $(TESTUTIL_LIBS)
+lib1542_CPPFLAGS = $(AM_CPPFLAGS)
+
lib1550_SOURCES = lib1550.c $(SUPPORTFILES)
lib1550_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1517
diff --git a/tests/libtest/lib1542.c b/tests/libtest/lib1542.c
new file mode 100644
index 000000000..4e17d9d36
--- /dev/null
+++ b/tests/libtest/lib1542.c
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2021, 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.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.
+ *
+ ***************************************************************************/
+
+/*
+ * Test CURLOPT_MAXLIFETIME_CONN:
+ * Send four requests, sleeping between the second and third and setting
+ * MAXLIFETIME_CONN between the third and fourth. The first three requests
+ * should use the same connection, and the fourth request should close the
+ * first connection and open a second.
+ */
+
+#include "test.h"
+#include "testutil.h"
+#include "testtrace.h"
+#include "warnless.h"
+#include "memdebug.h"
+
+#if defined(WIN32) || defined(_WIN32)
+#define sleep(sec) Sleep ((sec)*1000)
+#endif
+
+int test(char *URL)
+{
+ CURL *easy = NULL;
+ int res = 0;
+
+ global_init(CURL_GLOBAL_ALL);
+
+ res_easy_init(easy);
+
+ easy_setopt(easy, CURLOPT_URL, URL);
+
+ libtest_debug_config.nohex = 1;
+ libtest_debug_config.tracetime = 0;
+ easy_setopt(easy, CURLOPT_DEBUGDATA, &libtest_debug_config);
+ easy_setopt(easy, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);
+ easy_setopt(easy, CURLOPT_VERBOSE, 1L);
+
+ res = curl_easy_perform(easy);
+ if(res)
+ goto test_cleanup;
+
+ res = curl_easy_perform(easy);
+ if(res)
+ goto test_cleanup;
+
+ /* CURLOPT_MAXLIFETIME_CONN is inclusive - the connection needs to be 2
+ * seconds old */
+ sleep(2);
+
+ res = curl_easy_perform(easy);
+ if(res)
+ goto test_cleanup;
+
+ easy_setopt(easy, CURLOPT_MAXLIFETIME_CONN, 1L);
+
+ res = curl_easy_perform(easy);
+ if(res)
+ goto test_cleanup;
+
+test_cleanup:
+
+ curl_easy_cleanup(easy);
+ curl_global_cleanup();
+
+ return (int)res;
+}