multi: add CURLMOPT_NETWORK_CHANGED to signal network changed

New multi option CURLMOPT_NETWORK_CHANGED with a long bitmask value:

- CURLM_NWCOPT_CLEAR_CONNS: do not reuse existing connections, close all
  idle connections.

- CURLM_NWCOPT_CLEAR_DNS: clear the multi's DNS cache.

All other bits reserved for future extensions.

Fixes #17225
Reported-by: ウさん
Closes #17613
This commit is contained in:
Stefan Eissing 2025-06-13 12:38:44 +02:00 committed by Daniel Stenberg
parent 7b8594176d
commit 55c045c863
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
19 changed files with 354 additions and 4 deletions

View file

@ -709,7 +709,8 @@ static int cpool_reap_dead_cb(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
struct cpool_reaper_ctx *rctx = param;
if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
if((!CONN_INUSE(conn) && conn->bits.no_reuse) ||
Curl_conn_seems_dead(conn, data, &rctx->now)) {
/* stop the iteration here, pass back the connection that was pruned */
Curl_conn_terminate(data, conn, FALSE);
return 1;
@ -849,6 +850,40 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
cb(conn, data, cbdata);
}
static int cpool_mark_stale(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
(void)data;
(void)param;
conn->bits.no_reuse = TRUE;
return 0;
}
static int cpool_reap_no_reuse(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
(void)data;
(void)param;
if(!CONN_INUSE(conn) && conn->bits.no_reuse) {
Curl_conn_terminate(data, conn, FALSE);
return 1;
}
return 0; /* continue iteration */
}
void Curl_cpool_nw_changed(struct Curl_easy *data)
{
struct cpool *cpool = cpool_get_instance(data);
if(cpool) {
CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, NULL, cpool_mark_stale);
while(cpool_foreach(data, cpool, NULL, cpool_reap_no_reuse))
;
CPOOL_UNLOCK(cpool, data);
}
}
#if 0
/* Useful for debugging the connection pool */
void Curl_cpool_print(struct cpool *cpool)

View file

@ -163,4 +163,8 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
struct connectdata *conn,
Curl_cpool_conn_do_cb *cb, void *cbdata);
/* Close all unused connections, prevent reuse of existing ones. */
void Curl_cpool_nw_changed(struct Curl_easy *data);
#endif /* HEADER_CURL_CONNCACHE_H */

View file

@ -1102,6 +1102,7 @@ void curl_easy_reset(CURL *d)
data->progress.hide = TRUE;
data->state.current_speed = -1; /* init to negative == impossible */
data->state.retrycount = 0; /* reset the retry counter */
data->state.recent_conn_id = -1; /* clear remembered connection id */
/* zero out authentication data: */
memset(&data->state.authhost, 0, sizeof(struct auth));

View file

@ -290,6 +290,16 @@ void Curl_dnscache_prune(struct Curl_easy *data)
dnscache_unlock(data, dnscache);
}
void Curl_dnscache_clear(struct Curl_easy *data)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
if(dnscache) {
dnscache_lock(data, dnscache);
Curl_hash_clean(&dnscache->entries);
dnscache_unlock(data, dnscache);
}
}
#ifdef USE_ALARM_TIMEOUT
/* Beware this is a global and unique instance. This is used to store the
return address that we can jump back to from inside a signal handler. This

View file

@ -130,6 +130,9 @@ void Curl_dnscache_destroy(struct Curl_dnscache *dns);
/* prune old entries from the DNS cache */
void Curl_dnscache_prune(struct Curl_easy *data);
/* clear the DNS cache */
void Curl_dnscache_clear(struct Curl_easy *data);
/* IPv4 threadsafe resolve function used for synch and asynch builds */
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);

View file

@ -3235,6 +3235,16 @@ CURLMcode curl_multi_setopt(CURLM *m,
multi->max_concurrent_streams = (unsigned int)streams;
}
break;
case CURLMOPT_NETWORK_CHANGED: {
long val = va_arg(param, long);
if(val & CURLM_NWCOPT_CLEAR_DNS) {
Curl_dnscache_clear(multi->admin);
}
if(val & CURLM_NWCOPT_CLEAR_CONNS) {
Curl_cpool_nw_changed(multi->admin);
}
break;
}
default:
res = CURLM_UNKNOWN_OPTION;
break;

View file

@ -843,7 +843,7 @@ static bool url_match_connect_config(struct connectdata *conn,
struct url_conn_match *m)
{
/* connect-only or to-be-closed connections will not be reused */
if(conn->connect_only || conn->bits.close)
if(conn->connect_only || conn->bits.close || conn->bits.no_reuse)
return FALSE;
/* ip_version must match */

View file

@ -423,6 +423,7 @@ struct ConnectBits {
BIT(parallel_connect); /* set TRUE when a parallel connect attempt has
started (happy eyeballs) */
BIT(aborted); /* connection was aborted, e.g. in unclean state */
BIT(no_reuse); /* connection should not be reused */
BIT(shutdown_handler); /* connection shutdown: handler shut down */
BIT(shutdown_filters); /* connection shutdown: filters shut down */
BIT(in_cpool); /* connection is kept in a connection pool */