]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
conncache: conn upkeep/alive: move and enhance
authorStefan Eissing <stefan@eissing.org>
Mon, 27 Jul 2026 07:49:33 +0000 (09:49 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 27 Jul 2026 21:26:57 +0000 (23:26 +0200)
- 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

lib/cfilters.c
lib/cfilters.h
lib/conncache.c
lib/conncache.h
lib/url.c
lib/url.h

index bef39e6f5b62e4c418675834057a904925a28217..0af8c4bede88fa25779a21b886f09a9f9e22c090 100644 (file)
@@ -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,
index f55a572e2e9347b8836e8e04830f575e735fbf2a..cfdb5782ee7a69a26510caed3f1fa3766a0e56df 100644 (file)
@@ -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
index eb88d67dc8f75b6301255303fd9717834d2eac99..897b41cc42514b92b3ac62b01733dcb7e9095f99 100644 (file)
@@ -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)
index 78ccd8282fa24603f7c39f234c1a29b094951fa9..6798a3b8b3e7d5035f8dbbb835ad087349b75479 100644 (file)
@@ -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 */
index 1c837c586e8aae11a299f7579f6ffb324b0eea65..4cb25350338ccb614b59f72356e231ed8817f3b4 100644 (file)
--- a/lib/url.c
+++ b/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;
   }
index 02f1bd3943ab71c37f7a42b24bbb28388526dc01..c1df2fe811937bbb4c5cb6f04815cdf2e4120548 100644 (file)
--- a/lib/url.h
+++ b/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)