mirror of
https://github.com/curl/curl.git
synced 2026-07-30 23:38:04 +03:00
conncache: conn upkeep/alive: move and enhance
- move `Curl_conn_seems_dead()` into conncache.c - move `Curl_conn_upkeep()` into conncache.c - when upkeep gives an error on a connection not in use, terminate it Closes #21806
This commit is contained in:
parent
c5ad90bf9a
commit
c9ead9bd1c
6 changed files with 122 additions and 143 deletions
|
|
@ -450,9 +450,9 @@ static CURLcode cf_cntrl_all(struct connectdata *conn,
|
|||
int event, int arg1, void *arg2)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
size_t i;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
|
||||
for(i = 0; i < (int)CURL_ARRAYSIZE(conn->cfilter); ++i) {
|
||||
result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
|
||||
event, arg1, arg2);
|
||||
if(!ignore_result && result)
|
||||
|
|
@ -753,7 +753,7 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data,
|
|||
* connect or shutdown does not add poll events for the other. Check
|
||||
* against the transfer's own interest, before any chain added sockets
|
||||
* of its own. */
|
||||
for(i = 0; (i < 2) && !result; ++i) {
|
||||
for(i = 0; (i < (int)CURL_ARRAYSIZE(conn->cfilter)) && !result; ++i) {
|
||||
if(conn->cfilter[i] &&
|
||||
(want_io || !Curl_conn_is_connected(conn, i) ||
|
||||
Curl_shutdown_started(conn, i)))
|
||||
|
|
@ -978,15 +978,17 @@ bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
|
|||
}
|
||||
|
||||
CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex)
|
||||
struct connectdata *conn)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
int i;
|
||||
|
||||
if(!CONN_SOCK_IDX_VALID(sockindex))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
cf = conn->cfilter[sockindex];
|
||||
return cf ? cf->cft->keep_alive(cf, data) : CURLE_OK;
|
||||
for(i = 0; (i < (int)CURL_ARRAYSIZE(conn->cfilter)) && !result; ++i) {
|
||||
struct Curl_cfilter *cf = conn->cfilter[i];
|
||||
if(cf)
|
||||
result = cf->cft->keep_alive(cf, data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
|
||||
|
|
|
|||
|
|
@ -558,11 +558,10 @@ bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
|
|||
bool *input_pending);
|
||||
|
||||
/**
|
||||
* Try to upkeep the connection filters at sockindex.
|
||||
* Try to upkeep the connection filters.
|
||||
*/
|
||||
CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex);
|
||||
struct connectdata *conn);
|
||||
|
||||
/**
|
||||
* Get the remote hostname and port that the connection is currently
|
||||
|
|
|
|||
|
|
@ -715,7 +715,7 @@ static int cpool_reap_dead_cb(struct Curl_easy *data,
|
|||
|
||||
if(!terminate) {
|
||||
reaper->checked++;
|
||||
terminate = Curl_conn_seems_dead(conn, data, &reaper->now);
|
||||
terminate = Curl_cpool_conn_seems_dead(conn, data, &reaper->now);
|
||||
}
|
||||
if(terminate) {
|
||||
/* stop the iteration here, pass back the connection that was pruned */
|
||||
|
|
@ -761,7 +761,21 @@ static int conn_upkeep(struct Curl_easy *data,
|
|||
void *param)
|
||||
{
|
||||
(void)param;
|
||||
Curl_conn_upkeep(data, conn);
|
||||
if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->keepalive) >=
|
||||
data->set.upkeep_interval_ms) {
|
||||
CURLcode result;
|
||||
|
||||
/* briefly attach for action */
|
||||
Curl_attach_connection(data, conn);
|
||||
result = Curl_conn_keep_alive(data, conn);
|
||||
conn->keepalive = *Curl_pgrs_now(data);
|
||||
Curl_detach_connection(data);
|
||||
|
||||
if(result && !CONN_INUSE(conn)) {
|
||||
Curl_conn_terminate(data, conn, FALSE);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0; /* continue iteration */
|
||||
}
|
||||
|
||||
|
|
@ -773,7 +787,8 @@ CURLcode Curl_cpool_upkeep(struct Curl_easy *data)
|
|||
return CURLE_OK;
|
||||
|
||||
CPOOL_LOCK(cpool, data);
|
||||
cpool_foreach(data, cpool, NULL, conn_upkeep);
|
||||
while(cpool_foreach(data, cpool, NULL, conn_upkeep))
|
||||
;
|
||||
CPOOL_UNLOCK(cpool, data);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
|
@ -858,6 +873,84 @@ void Curl_cpool_nw_changed(struct Curl_easy *data)
|
|||
}
|
||||
}
|
||||
|
||||
/* A connection has to have been idle for less than 'conn_max_idle_ms'
|
||||
(the success rate is too low after this), or created less than
|
||||
'conn_max_age_ms' ago, to be subject for reuse. */
|
||||
static bool cpool_conn_maxage(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
const struct curltime *pnow)
|
||||
{
|
||||
timediff_t age_ms;
|
||||
|
||||
if(data->set.conn_max_idle_ms) {
|
||||
age_ms = curlx_ptimediff_ms(pnow, &conn->lastused);
|
||||
if(age_ms > data->set.conn_max_idle_ms) {
|
||||
infof(data, "Too old connection (%" FMT_TIMEDIFF_T
|
||||
" ms idle, max idle is %" FMT_TIMEDIFF_T " ms), disconnect it",
|
||||
age_ms, data->set.conn_max_idle_ms);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(data->set.conn_max_age_ms) {
|
||||
age_ms = curlx_ptimediff_ms(pnow, &conn->created);
|
||||
if(age_ms > data->set.conn_max_age_ms) {
|
||||
infof(data,
|
||||
"Too old connection (created %" FMT_TIMEDIFF_T
|
||||
" ms ago, max lifetime is %" FMT_TIMEDIFF_T " ms), disconnect it",
|
||||
age_ms, data->set.conn_max_age_ms);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE iff the given connection is considered dead.
|
||||
*/
|
||||
bool Curl_cpool_conn_seems_dead(struct connectdata *conn,
|
||||
struct Curl_easy *data,
|
||||
const struct curltime *pnow)
|
||||
{
|
||||
bool input_pending = FALSE;
|
||||
bool dead = FALSE;
|
||||
|
||||
DEBUGASSERT(!data->conn);
|
||||
/* The check only makes sense only if the connection is not in use */
|
||||
if(CONN_INUSE(conn))
|
||||
return FALSE;
|
||||
else if(cpool_conn_maxage(data, conn, pnow)) /* too old? */
|
||||
return TRUE;
|
||||
else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000)
|
||||
return FALSE;
|
||||
else if(conn->scheme->run->connection_is_dead) {
|
||||
Curl_attach_connection(data, conn);
|
||||
dead = conn->scheme->run->connection_is_dead(data, conn);
|
||||
Curl_detach_connection(data);
|
||||
}
|
||||
else {
|
||||
Curl_attach_connection(data, conn);
|
||||
dead = !Curl_conn_is_alive(data, conn, &input_pending);
|
||||
Curl_detach_connection(data);
|
||||
}
|
||||
|
||||
if(input_pending) {
|
||||
/* For reuse, we want a "clean" connection state. This includes
|
||||
* that we expect - in general - no waiting input data. Input
|
||||
* waiting might be a TLS Notify Close, for example. We reject
|
||||
* that.
|
||||
* For protocols where data from other end may arrive at
|
||||
* any time (HTTP/2 PING for example), the protocol handler needs
|
||||
* to install its own `connection_check` callback.
|
||||
*/
|
||||
DEBUGF(infof(data, "connection has input pending, not reusable"));
|
||||
dead = TRUE;
|
||||
}
|
||||
|
||||
return dead;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Useful for debugging the connection pool */
|
||||
void Curl_cpool_print(struct cpool *cpool)
|
||||
|
|
|
|||
|
|
@ -156,4 +156,11 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
|
|||
/* Close all unused connections, prevent reuse of existing ones. */
|
||||
void Curl_cpool_nw_changed(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Return TRUE iff the given connection is considered dead.
|
||||
*/
|
||||
bool Curl_cpool_conn_seems_dead(struct connectdata *conn,
|
||||
struct Curl_easy *data,
|
||||
const struct curltime *pnow);
|
||||
|
||||
#endif /* HEADER_CURL_CONNCACHE_H */
|
||||
|
|
|
|||
113
lib/url.c
113
lib/url.c
|
|
@ -564,115 +564,6 @@ static bool proxy_info_matches(const struct proxy_info *data,
|
|||
}
|
||||
#endif
|
||||
|
||||
/* A connection has to have been idle for less than 'conn_max_idle_ms'
|
||||
(the success rate is too low after this), or created less than
|
||||
'conn_max_age_ms' ago, to be subject for reuse. */
|
||||
static bool conn_maxage(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
struct curltime now)
|
||||
{
|
||||
timediff_t age_ms;
|
||||
|
||||
if(data->set.conn_max_idle_ms) {
|
||||
age_ms = curlx_ptimediff_ms(&now, &conn->lastused);
|
||||
if(age_ms > data->set.conn_max_idle_ms) {
|
||||
infof(data, "Too old connection (%" FMT_TIMEDIFF_T
|
||||
" ms idle, max idle is %" FMT_TIMEDIFF_T " ms), disconnect it",
|
||||
age_ms, data->set.conn_max_idle_ms);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(data->set.conn_max_age_ms) {
|
||||
age_ms = curlx_ptimediff_ms(&now, &conn->created);
|
||||
if(age_ms > data->set.conn_max_age_ms) {
|
||||
infof(data,
|
||||
"Too old connection (created %" FMT_TIMEDIFF_T
|
||||
" ms ago, max lifetime is %" FMT_TIMEDIFF_T " ms), disconnect it",
|
||||
age_ms, data->set.conn_max_age_ms);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE iff the given connection is considered dead.
|
||||
*/
|
||||
bool Curl_conn_seems_dead(struct connectdata *conn,
|
||||
struct Curl_easy *data,
|
||||
const struct curltime *pnow)
|
||||
{
|
||||
DEBUGASSERT(!data->conn);
|
||||
if(!CONN_INUSE(conn)) {
|
||||
/* The check for a dead socket makes sense only if the connection is not in
|
||||
use */
|
||||
bool dead;
|
||||
|
||||
if(conn_maxage(data, conn, *pnow)) {
|
||||
/* avoid check if already too old */
|
||||
dead = TRUE;
|
||||
}
|
||||
else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000)
|
||||
dead = FALSE;
|
||||
else if(conn->scheme->run->connection_is_dead) {
|
||||
/* The protocol has a special method for checking the state of the
|
||||
connection. Use it to check if the connection is dead. */
|
||||
/* briefly attach the connection for the check */
|
||||
Curl_attach_connection(data, conn);
|
||||
dead = conn->scheme->run->connection_is_dead(data, conn);
|
||||
Curl_detach_connection(data);
|
||||
conn->lastchecked = *pnow;
|
||||
}
|
||||
else {
|
||||
bool input_pending = FALSE;
|
||||
|
||||
Curl_attach_connection(data, conn);
|
||||
dead = !Curl_conn_is_alive(data, conn, &input_pending);
|
||||
if(input_pending) {
|
||||
/* For reuse, we want a "clean" connection state. This includes
|
||||
* that we expect - in general - no waiting input data. Input
|
||||
* waiting might be a TLS Notify Close, for example. We reject
|
||||
* that.
|
||||
* For protocols where data from other end may arrive at
|
||||
* any time (HTTP/2 PING for example), the protocol handler needs
|
||||
* to install its own `connection_check` callback.
|
||||
*/
|
||||
DEBUGF(infof(data, "connection has input pending, not reusable"));
|
||||
dead = TRUE;
|
||||
}
|
||||
Curl_detach_connection(data);
|
||||
conn->lastchecked = *pnow;
|
||||
}
|
||||
|
||||
if(dead) {
|
||||
/* remove connection from cpool */
|
||||
infof(data, "Connection %" FMT_OFF_T " seems to be dead",
|
||||
conn->connection_id);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_upkeep(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->keepalive) <=
|
||||
data->set.upkeep_interval_ms)
|
||||
return result;
|
||||
|
||||
/* briefly attach for action */
|
||||
Curl_attach_connection(data, conn);
|
||||
result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
|
||||
Curl_detach_connection(data);
|
||||
|
||||
conn->keepalive = *Curl_pgrs_now(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef USE_SSH
|
||||
static bool ssh_config_matches(struct connectdata *one,
|
||||
struct connectdata *two)
|
||||
|
|
@ -1172,8 +1063,10 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
|
|||
if(!url_match_multiplex_limits(conn, m))
|
||||
return FALSE;
|
||||
|
||||
if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data, &m->now)) {
|
||||
if(Curl_cpool_conn_seems_dead(conn, m->data, &m->now)) {
|
||||
/* remove and disconnect. */
|
||||
infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating",
|
||||
conn->connection_id);
|
||||
Curl_conn_terminate(m->data, conn, FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
|||
19
lib/url.h
19
lib/url.h
|
|
@ -71,23 +71,8 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key);
|
|||
#define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless
|
||||
specified */
|
||||
|
||||
/**
|
||||
* Return TRUE iff the given connection is considered dead.
|
||||
*/
|
||||
bool Curl_conn_seems_dead(struct connectdata *conn,
|
||||
struct Curl_easy *data,
|
||||
const struct curltime *pnow);
|
||||
|
||||
/**
|
||||
* Perform upkeep operations on the connection.
|
||||
*/
|
||||
CURLcode Curl_conn_upkeep(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
/**
|
||||
* Always eval all arguments, return the first
|
||||
* result != (CURLE_OK | CURLE_AGAIN) or `r1`.
|
||||
*/
|
||||
/* Always eval all arguments, return the first
|
||||
* result != (CURLE_OK | CURLE_AGAIN) or `r1`. */
|
||||
CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2);
|
||||
|
||||
#if defined(USE_HTTP2) || defined(USE_HTTP3)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue