... 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
.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
Returns CURLE_OK
.SH "SEE ALSO"
.BR CURLOPT_FRESH_CONNECT "(3), " CURLOPT_MAXCONNECTS "(3), "
+.BR CURLOPT_MAXLIFETIME_CONN "(3), "
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
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), "
--- /dev/null
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * 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), "
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 \
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
/* 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 */
/* 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;
{"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},
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != (313 + 1));
+ return ((CURLOPT_LASTENTRY%10000) != (314 + 1));
}
#endif
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);
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
#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;
}
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 */
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
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 \
--- /dev/null
+<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\r
+Content-Length: 0\r
+\r
+</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\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+GET /%TESTNUMBER HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+GET /%TESTNUMBER HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+GET /%TESTNUMBER HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+</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>
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 \
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
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * 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;
+}