quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

conncache.c (25630B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
      9  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
     10  *
     11  * This software is licensed as described in the file COPYING, which
     12  * you should have received as part of this distribution. The terms
     13  * are also available at https://curl.se/docs/copyright.html.
     14  *
     15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     16  * copies of the Software, and permit persons to whom the Software is
     17  * furnished to do so, under the terms of the COPYING file.
     18  *
     19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     20  * KIND, either express or implied.
     21  *
     22  * SPDX-License-Identifier: curl
     23  *
     24  ***************************************************************************/
     25 
     26 #include "curl_setup.h"
     27 
     28 #include <curl/curl.h>
     29 
     30 #include "urldata.h"
     31 #include "url.h"
     32 #include "cfilters.h"
     33 #include "progress.h"
     34 #include "multiif.h"
     35 #include "multi_ev.h"
     36 #include "sendf.h"
     37 #include "cshutdn.h"
     38 #include "conncache.h"
     39 #include "http_negotiate.h"
     40 #include "http_ntlm.h"
     41 #include "share.h"
     42 #include "sigpipe.h"
     43 #include "connect.h"
     44 #include "select.h"
     45 #include "curlx/strparse.h"
     46 #include "uint-table.h"
     47 
     48 /* The last 3 #include files should be in this order */
     49 #include "curl_printf.h"
     50 #include "curl_memory.h"
     51 #include "memdebug.h"
     52 
     53 
     54 #define CPOOL_IS_LOCKED(c)    ((c) && (c)->locked)
     55 
     56 #define CPOOL_LOCK(c,d)                                                 \
     57   do {                                                                  \
     58     if((c)) {                                                           \
     59       if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
     60         Curl_share_lock((d), CURL_LOCK_DATA_CONNECT,                    \
     61                         CURL_LOCK_ACCESS_SINGLE);                       \
     62       DEBUGASSERT(!(c)->locked);                                        \
     63       (c)->locked = TRUE;                                               \
     64     }                                                                   \
     65   } while(0)
     66 
     67 #define CPOOL_UNLOCK(c,d)                                                 \
     68   do {                                                                  \
     69     if((c)) {                                                           \
     70       DEBUGASSERT((c)->locked);                                         \
     71       (c)->locked = FALSE;                                              \
     72       if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
     73         Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT);                 \
     74     }                                                                   \
     75   } while(0)
     76 
     77 
     78 /* A list of connections to the same destination. */
     79 struct cpool_bundle {
     80   struct Curl_llist conns; /* connections in the bundle */
     81   size_t dest_len; /* total length of destination, including NUL */
     82   char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
     83 };
     84 
     85 
     86 static void cpool_discard_conn(struct cpool *cpool,
     87                                struct Curl_easy *data,
     88                                struct connectdata *conn,
     89                                bool aborted);
     90 
     91 static struct cpool_bundle *cpool_bundle_create(const char *dest)
     92 {
     93   struct cpool_bundle *bundle;
     94   size_t dest_len = strlen(dest);
     95 
     96   bundle = calloc(1, sizeof(*bundle) + dest_len);
     97   if(!bundle)
     98     return NULL;
     99   Curl_llist_init(&bundle->conns, NULL);
    100   bundle->dest_len = dest_len + 1;
    101   memcpy(bundle->dest, dest, bundle->dest_len);
    102   return bundle;
    103 }
    104 
    105 static void cpool_bundle_destroy(struct cpool_bundle *bundle)
    106 {
    107   DEBUGASSERT(!Curl_llist_count(&bundle->conns));
    108   free(bundle);
    109 }
    110 
    111 /* Add a connection to a bundle */
    112 static void cpool_bundle_add(struct cpool_bundle *bundle,
    113                              struct connectdata *conn)
    114 {
    115   DEBUGASSERT(!Curl_node_llist(&conn->cpool_node));
    116   Curl_llist_append(&bundle->conns, conn, &conn->cpool_node);
    117   conn->bits.in_cpool = TRUE;
    118 }
    119 
    120 /* Remove a connection from a bundle */
    121 static void cpool_bundle_remove(struct cpool_bundle *bundle,
    122                                 struct connectdata *conn)
    123 {
    124   (void)bundle;
    125   DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns);
    126   Curl_node_remove(&conn->cpool_node);
    127   conn->bits.in_cpool = FALSE;
    128 }
    129 
    130 static void cpool_bundle_free_entry(void *freethis)
    131 {
    132   cpool_bundle_destroy((struct cpool_bundle *)freethis);
    133 }
    134 
    135 void Curl_cpool_init(struct cpool *cpool,
    136                      struct Curl_easy *idata,
    137                      struct Curl_share *share,
    138                      size_t size)
    139 {
    140   Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
    141                  curlx_str_key_compare, cpool_bundle_free_entry);
    142 
    143   DEBUGASSERT(idata);
    144 
    145   cpool->idata = idata;
    146   cpool->share = share;
    147   cpool->initialised = TRUE;
    148 }
    149 
    150 /* Return the "first" connection in the pool or NULL. */
    151 static struct connectdata *cpool_get_first(struct cpool *cpool)
    152 {
    153   struct Curl_hash_iterator iter;
    154   struct Curl_hash_element *he;
    155   struct cpool_bundle *bundle;
    156   struct Curl_llist_node *conn_node;
    157 
    158   Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
    159   for(he = Curl_hash_next_element(&iter); he;
    160       he = Curl_hash_next_element(&iter)) {
    161     bundle = he->ptr;
    162     conn_node = Curl_llist_head(&bundle->conns);
    163     if(conn_node)
    164       return Curl_node_elem(conn_node);
    165   }
    166   return NULL;
    167 }
    168 
    169 
    170 static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
    171                                               struct connectdata *conn)
    172 {
    173   return Curl_hash_pick(&cpool->dest2bundle,
    174                         conn->destination, strlen(conn->destination) + 1);
    175 }
    176 
    177 
    178 static void cpool_remove_bundle(struct cpool *cpool,
    179                                 struct cpool_bundle *bundle)
    180 {
    181   if(!cpool)
    182     return;
    183   Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
    184 }
    185 
    186 
    187 static void cpool_remove_conn(struct cpool *cpool,
    188                               struct connectdata *conn)
    189 {
    190   struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
    191   DEBUGASSERT(cpool);
    192   if(list) {
    193     /* The connection is certainly in the pool, but where? */
    194     struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
    195     if(bundle && (list == &bundle->conns)) {
    196       cpool_bundle_remove(bundle, conn);
    197       if(!Curl_llist_count(&bundle->conns))
    198         cpool_remove_bundle(cpool, bundle);
    199       conn->bits.in_cpool = FALSE;
    200       cpool->num_conn--;
    201     }
    202     else {
    203       /* Should have been in the bundle list */
    204       DEBUGASSERT(NULL);
    205     }
    206   }
    207 }
    208 
    209 void Curl_cpool_destroy(struct cpool *cpool)
    210 {
    211   if(cpool && cpool->initialised && cpool->idata) {
    212     struct connectdata *conn;
    213     SIGPIPE_VARIABLE(pipe_st);
    214 
    215     CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections",
    216                cpool->share ? "[SHARE] " : "", cpool->num_conn);
    217     /* Move all connections to the shutdown list */
    218     sigpipe_init(&pipe_st);
    219     CPOOL_LOCK(cpool, cpool->idata);
    220     conn = cpool_get_first(cpool);
    221     while(conn) {
    222       cpool_remove_conn(cpool, conn);
    223       sigpipe_apply(cpool->idata, &pipe_st);
    224       connclose(conn, "kill all");
    225       cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
    226       conn = cpool_get_first(cpool);
    227     }
    228     CPOOL_UNLOCK(cpool, cpool->idata);
    229     sigpipe_restore(&pipe_st);
    230     Curl_hash_destroy(&cpool->dest2bundle);
    231   }
    232 }
    233 
    234 static struct cpool *cpool_get_instance(struct Curl_easy *data)
    235 {
    236   if(data) {
    237     if(CURL_SHARE_KEEP_CONNECT(data->share))
    238       return &data->share->cpool;
    239     else if(data->multi_easy)
    240       return &data->multi_easy->cpool;
    241     else if(data->multi)
    242       return &data->multi->cpool;
    243   }
    244   return NULL;
    245 }
    246 
    247 void Curl_cpool_xfer_init(struct Curl_easy *data)
    248 {
    249   struct cpool *cpool = cpool_get_instance(data);
    250 
    251   DEBUGASSERT(cpool);
    252   if(cpool) {
    253     CPOOL_LOCK(cpool, data);
    254     /* the identifier inside the connection cache */
    255     data->id = cpool->next_easy_id++;
    256     if(cpool->next_easy_id <= 0)
    257       cpool->next_easy_id = 0;
    258     data->state.lastconnect_id = -1;
    259 
    260     CPOOL_UNLOCK(cpool, data);
    261   }
    262   else {
    263     /* We should not get here, but in a non-debug build, do something */
    264     data->id = 0;
    265     data->state.lastconnect_id = -1;
    266   }
    267 }
    268 
    269 static struct cpool_bundle *
    270 cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
    271 {
    272   struct cpool_bundle *bundle;
    273 
    274   bundle = cpool_bundle_create(conn->destination);
    275   if(!bundle)
    276     return NULL;
    277 
    278   if(!Curl_hash_add(&cpool->dest2bundle,
    279                     bundle->dest, bundle->dest_len, bundle)) {
    280     cpool_bundle_destroy(bundle);
    281     return NULL;
    282   }
    283   return bundle;
    284 }
    285 
    286 static struct connectdata *
    287 cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
    288 {
    289   struct Curl_llist_node *curr;
    290   timediff_t highscore = -1;
    291   timediff_t score;
    292   struct curltime now;
    293   struct connectdata *oldest_idle = NULL;
    294   struct connectdata *conn;
    295 
    296   now = curlx_now();
    297   curr = Curl_llist_head(&bundle->conns);
    298   while(curr) {
    299     conn = Curl_node_elem(curr);
    300 
    301     if(!CONN_INUSE(conn)) {
    302       /* Set higher score for the age passed since the connection was used */
    303       score = curlx_timediff(now, conn->lastused);
    304 
    305       if(score > highscore) {
    306         highscore = score;
    307         oldest_idle = conn;
    308       }
    309     }
    310     curr = Curl_node_next(curr);
    311   }
    312   return oldest_idle;
    313 }
    314 
    315 static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
    316 {
    317   struct Curl_hash_iterator iter;
    318   struct Curl_llist_node *curr;
    319   struct Curl_hash_element *he;
    320   struct connectdata *oldest_idle = NULL;
    321   struct cpool_bundle *bundle;
    322   struct curltime now;
    323   timediff_t highscore =- 1;
    324   timediff_t score;
    325 
    326   now = curlx_now();
    327   Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
    328 
    329   for(he = Curl_hash_next_element(&iter); he;
    330       he = Curl_hash_next_element(&iter)) {
    331     struct connectdata *conn;
    332     bundle = he->ptr;
    333 
    334     for(curr = Curl_llist_head(&bundle->conns); curr;
    335         curr = Curl_node_next(curr)) {
    336       conn = Curl_node_elem(curr);
    337       if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
    338         continue;
    339       /* Set higher score for the age passed since the connection was used */
    340       score = curlx_timediff(now, conn->lastused);
    341       if(score > highscore) {
    342         highscore = score;
    343         oldest_idle = conn;
    344       }
    345     }
    346   }
    347   return oldest_idle;
    348 }
    349 
    350 
    351 int Curl_cpool_check_limits(struct Curl_easy *data,
    352                             struct connectdata *conn)
    353 {
    354   struct cpool *cpool = cpool_get_instance(data);
    355   struct cpool_bundle *bundle;
    356   size_t dest_limit = 0;
    357   size_t total_limit = 0;
    358   size_t shutdowns;
    359   int result = CPOOL_LIMIT_OK;
    360 
    361   if(!cpool)
    362     return CPOOL_LIMIT_OK;
    363 
    364   if(cpool->idata->multi) {
    365     dest_limit = cpool->idata->multi->max_host_connections;
    366     total_limit = cpool->idata->multi->max_total_connections;
    367   }
    368 
    369   if(!dest_limit && !total_limit)
    370     return CPOOL_LIMIT_OK;
    371 
    372   CPOOL_LOCK(cpool, cpool->idata);
    373   if(dest_limit) {
    374     size_t live;
    375 
    376     bundle = cpool_find_bundle(cpool, conn);
    377     live = bundle ? Curl_llist_count(&bundle->conns) : 0;
    378     shutdowns = Curl_cshutdn_dest_count(data, conn->destination);
    379     while((live  + shutdowns) >= dest_limit) {
    380       if(shutdowns) {
    381         /* close one connection in shutdown right away, if we can */
    382         if(!Curl_cshutdn_close_oldest(data, conn->destination))
    383           break;
    384       }
    385       else if(!bundle)
    386         break;
    387       else {
    388         struct connectdata *oldest_idle = NULL;
    389         /* The bundle is full. Extract the oldest connection that may
    390          * be removed now, if there is one. */
    391         oldest_idle = cpool_bundle_get_oldest_idle(bundle);
    392         if(!oldest_idle)
    393           break;
    394         /* disconnect the old conn and continue */
    395         CURL_TRC_M(data, "Discarding connection #%"
    396                      FMT_OFF_T " from %zu to reach destination "
    397                      "limit of %zu", oldest_idle->connection_id,
    398                      Curl_llist_count(&bundle->conns), dest_limit);
    399         Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
    400 
    401         /* in case the bundle was destroyed in disconnect, look it up again */
    402         bundle = cpool_find_bundle(cpool, conn);
    403         live = bundle ? Curl_llist_count(&bundle->conns) : 0;
    404       }
    405       shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination);
    406     }
    407     if((live + shutdowns) >= dest_limit) {
    408       result = CPOOL_LIMIT_DEST;
    409       goto out;
    410     }
    411   }
    412 
    413   if(total_limit) {
    414     shutdowns = Curl_cshutdn_count(cpool->idata);
    415     while((cpool->num_conn + shutdowns) >= total_limit) {
    416       if(shutdowns) {
    417         /* close one connection in shutdown right away, if we can */
    418         if(!Curl_cshutdn_close_oldest(data, NULL))
    419           break;
    420       }
    421       else {
    422         struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
    423         if(!oldest_idle)
    424           break;
    425         /* disconnect the old conn and continue */
    426         CURL_TRC_M(data, "Discarding connection #%"
    427                    FMT_OFF_T " from %zu to reach total "
    428                    "limit of %zu",
    429                    oldest_idle->connection_id, cpool->num_conn, total_limit);
    430         Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
    431       }
    432       shutdowns = Curl_cshutdn_count(cpool->idata);
    433     }
    434     if((cpool->num_conn + shutdowns) >= total_limit) {
    435       result = CPOOL_LIMIT_TOTAL;
    436       goto out;
    437     }
    438   }
    439 
    440 out:
    441   CPOOL_UNLOCK(cpool, cpool->idata);
    442   return result;
    443 }
    444 
    445 CURLcode Curl_cpool_add(struct Curl_easy *data,
    446                         struct connectdata *conn)
    447 {
    448   CURLcode result = CURLE_OK;
    449   struct cpool_bundle *bundle = NULL;
    450   struct cpool *cpool = cpool_get_instance(data);
    451   DEBUGASSERT(conn);
    452 
    453   DEBUGASSERT(cpool);
    454   if(!cpool)
    455     return CURLE_FAILED_INIT;
    456 
    457   CPOOL_LOCK(cpool, data);
    458   bundle = cpool_find_bundle(cpool, conn);
    459   if(!bundle) {
    460     bundle = cpool_add_bundle(cpool, conn);
    461     if(!bundle) {
    462       result = CURLE_OUT_OF_MEMORY;
    463       goto out;
    464     }
    465   }
    466 
    467   cpool_bundle_add(bundle, conn);
    468   conn->connection_id = cpool->next_connection_id++;
    469   cpool->num_conn++;
    470   CURL_TRC_M(data, "[CPOOL] added connection %" FMT_OFF_T ". "
    471              "The cache now contains %zu members",
    472              conn->connection_id, cpool->num_conn);
    473 out:
    474   CPOOL_UNLOCK(cpool, data);
    475 
    476   return result;
    477 }
    478 
    479 /* This function iterates the entire connection pool and calls the function
    480    func() with the connection pointer as the first argument and the supplied
    481    'param' argument as the other.
    482 
    483    The cpool lock is still held when the callback is called. It needs it,
    484    so that it can safely continue traversing the lists once the callback
    485    returns.
    486 
    487    Returns TRUE if the loop was aborted due to the callback's return code.
    488 
    489    Return 0 from func() to continue the loop, return 1 to abort it.
    490  */
    491 static bool cpool_foreach(struct Curl_easy *data,
    492                           struct cpool *cpool,
    493                           void *param,
    494                           int (*func)(struct Curl_easy *data,
    495                                       struct connectdata *conn, void *param))
    496 {
    497   struct Curl_hash_iterator iter;
    498   struct Curl_hash_element *he;
    499 
    500   if(!cpool)
    501     return FALSE;
    502 
    503   Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
    504 
    505   he = Curl_hash_next_element(&iter);
    506   while(he) {
    507     struct Curl_llist_node *curr;
    508     struct cpool_bundle *bundle = he->ptr;
    509     he = Curl_hash_next_element(&iter);
    510 
    511     curr = Curl_llist_head(&bundle->conns);
    512     while(curr) {
    513       /* Yes, we need to update curr before calling func(), because func()
    514          might decide to remove the connection */
    515       struct connectdata *conn = Curl_node_elem(curr);
    516       curr = Curl_node_next(curr);
    517 
    518       if(1 == func(data, conn, param)) {
    519         return TRUE;
    520       }
    521     }
    522   }
    523   return FALSE;
    524 }
    525 
    526 /*
    527  * A connection (already in the pool) has become idle. Do any
    528  * cleanups in regard to the pool's limits.
    529  *
    530  * Return TRUE if idle connection kept in pool, FALSE if closed.
    531  */
    532 bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
    533                               struct connectdata *conn)
    534 {
    535   unsigned int maxconnects = !data->multi->maxconnects ?
    536     (Curl_multi_xfers_running(data->multi) * 4) : data->multi->maxconnects;
    537   struct connectdata *oldest_idle = NULL;
    538   struct cpool *cpool = cpool_get_instance(data);
    539   bool kept = TRUE;
    540 
    541   conn->lastused = curlx_now(); /* it was used up until now */
    542   if(cpool && maxconnects) {
    543     /* may be called form a callback already under lock */
    544     bool do_lock = !CPOOL_IS_LOCKED(cpool);
    545     if(do_lock)
    546       CPOOL_LOCK(cpool, data);
    547     if(cpool->num_conn > maxconnects) {
    548       infof(data, "Connection pool is full, closing the oldest of %zu/%u",
    549             cpool->num_conn, maxconnects);
    550 
    551       oldest_idle = cpool_get_oldest_idle(cpool);
    552       kept = (oldest_idle != conn);
    553       if(oldest_idle) {
    554         Curl_conn_terminate(data, oldest_idle, FALSE);
    555       }
    556     }
    557     if(do_lock)
    558       CPOOL_UNLOCK(cpool, data);
    559   }
    560 
    561   return kept;
    562 }
    563 
    564 bool Curl_cpool_find(struct Curl_easy *data,
    565                      const char *destination,
    566                      Curl_cpool_conn_match_cb *conn_cb,
    567                      Curl_cpool_done_match_cb *done_cb,
    568                      void *userdata)
    569 {
    570   struct cpool *cpool = cpool_get_instance(data);
    571   struct cpool_bundle *bundle;
    572   bool result = FALSE;
    573 
    574   DEBUGASSERT(cpool);
    575   DEBUGASSERT(conn_cb);
    576   if(!cpool)
    577     return FALSE;
    578 
    579   CPOOL_LOCK(cpool, data);
    580   bundle = Curl_hash_pick(&cpool->dest2bundle,
    581                           CURL_UNCONST(destination),
    582                           strlen(destination) + 1);
    583   if(bundle) {
    584     struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
    585     while(curr) {
    586       struct connectdata *conn = Curl_node_elem(curr);
    587       /* Get next node now. callback might discard current */
    588       curr = Curl_node_next(curr);
    589 
    590       if(conn_cb(conn, userdata)) {
    591         result = TRUE;
    592         break;
    593       }
    594     }
    595   }
    596 
    597   if(done_cb) {
    598     result = done_cb(result, userdata);
    599   }
    600   CPOOL_UNLOCK(cpool, data);
    601   return result;
    602 }
    603 
    604 static void cpool_discard_conn(struct cpool *cpool,
    605                                struct Curl_easy *data,
    606                                struct connectdata *conn,
    607                                bool aborted)
    608 {
    609   bool done = FALSE;
    610 
    611   DEBUGASSERT(data);
    612   DEBUGASSERT(!data->conn);
    613   DEBUGASSERT(cpool);
    614   DEBUGASSERT(!conn->bits.in_cpool);
    615 
    616   /*
    617    * If this connection is not marked to force-close, leave it open if there
    618    * are other users of it
    619    */
    620   if(CONN_INUSE(conn) && !aborted) {
    621     CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
    622                " still in use by %u transfers", conn->connection_id,
    623                CONN_ATTACHED(conn));
    624     return;
    625   }
    626 
    627   /* treat the connection as aborted in CONNECT_ONLY situations, we do
    628    * not know what the APP did with it. */
    629   if(conn->connect_only)
    630     aborted = TRUE;
    631   conn->bits.aborted = aborted;
    632 
    633   /* We do not shutdown dead connections. The term 'dead' can be misleading
    634    * here, as we also mark errored connections/transfers as 'dead'.
    635    * If we do a shutdown for an aborted transfer, the server might think
    636    * it was successful otherwise (for example an ftps: upload). This is
    637    * not what we want. */
    638   if(aborted)
    639     done = TRUE;
    640   if(!done) {
    641     /* Attempt to shutdown the connection right away. */
    642     Curl_cshutdn_run_once(cpool->idata, conn, &done);
    643   }
    644 
    645   if(done || !data->multi)
    646     Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
    647   else
    648     Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
    649 }
    650 
    651 void Curl_conn_terminate(struct Curl_easy *data,
    652                          struct connectdata *conn,
    653                          bool aborted)
    654 {
    655   struct cpool *cpool = cpool_get_instance(data);
    656   bool do_lock;
    657 
    658   DEBUGASSERT(cpool);
    659   DEBUGASSERT(data && !data->conn);
    660   if(!cpool)
    661     return;
    662 
    663   /* If this connection is not marked to force-close, leave it open if there
    664    * are other users of it */
    665   if(CONN_INUSE(conn) && !aborted) {
    666     DEBUGASSERT(0); /* does this ever happen? */
    667     DEBUGF(infof(data, "Curl_disconnect when inuse: %u", CONN_ATTACHED(conn)));
    668     return;
    669   }
    670 
    671   /* This method may be called while we are under lock, e.g. from a
    672    * user callback in find. */
    673   do_lock = !CPOOL_IS_LOCKED(cpool);
    674   if(do_lock)
    675     CPOOL_LOCK(cpool, data);
    676 
    677   if(conn->bits.in_cpool) {
    678     cpool_remove_conn(cpool, conn);
    679     DEBUGASSERT(!conn->bits.in_cpool);
    680   }
    681 
    682   /* treat the connection as aborted in CONNECT_ONLY situations,
    683    * so no graceful shutdown is attempted. */
    684   if(conn->connect_only)
    685     aborted = TRUE;
    686 
    687   if(data->multi) {
    688     /* Add it to the multi's cpool for shutdown handling */
    689     infof(data, "%s connection #%" FMT_OFF_T,
    690           aborted ? "closing" : "shutting down", conn->connection_id);
    691     cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
    692   }
    693   else {
    694     /* No multi available, terminate */
    695     infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
    696     Curl_cshutdn_terminate(cpool->idata, conn, !aborted);
    697   }
    698 
    699   if(do_lock)
    700     CPOOL_UNLOCK(cpool, data);
    701 }
    702 
    703 
    704 struct cpool_reaper_ctx {
    705   struct curltime now;
    706 };
    707 
    708 static int cpool_reap_dead_cb(struct Curl_easy *data,
    709                               struct connectdata *conn, void *param)
    710 {
    711   struct cpool_reaper_ctx *rctx = param;
    712   if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
    713     /* stop the iteration here, pass back the connection that was pruned */
    714     Curl_conn_terminate(data, conn, FALSE);
    715     return 1;
    716   }
    717   return 0; /* continue iteration */
    718 }
    719 
    720 /*
    721  * This function scans the data's connection pool for half-open/dead
    722  * connections, closes and removes them.
    723  * The cleanup is done at most once per second.
    724  *
    725  * When called, this transfer has no connection attached.
    726  */
    727 void Curl_cpool_prune_dead(struct Curl_easy *data)
    728 {
    729   struct cpool *cpool = cpool_get_instance(data);
    730   struct cpool_reaper_ctx rctx;
    731   timediff_t elapsed;
    732 
    733   if(!cpool)
    734     return;
    735 
    736   rctx.now = curlx_now();
    737   CPOOL_LOCK(cpool, data);
    738   elapsed = curlx_timediff(rctx.now, cpool->last_cleanup);
    739 
    740   if(elapsed >= 1000L) {
    741     while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
    742       ;
    743     cpool->last_cleanup = rctx.now;
    744   }
    745   CPOOL_UNLOCK(cpool, data);
    746 }
    747 
    748 static int conn_upkeep(struct Curl_easy *data,
    749                        struct connectdata *conn,
    750                        void *param)
    751 {
    752   struct curltime *now = param;
    753   Curl_conn_upkeep(data, conn, now);
    754   return 0; /* continue iteration */
    755 }
    756 
    757 CURLcode Curl_cpool_upkeep(void *data)
    758 {
    759   struct cpool *cpool = cpool_get_instance(data);
    760   struct curltime now = curlx_now();
    761 
    762   if(!cpool)
    763     return CURLE_OK;
    764 
    765   CPOOL_LOCK(cpool, data);
    766   cpool_foreach(data, cpool, &now, conn_upkeep);
    767   CPOOL_UNLOCK(cpool, data);
    768   return CURLE_OK;
    769 }
    770 
    771 struct cpool_find_ctx {
    772   curl_off_t id;
    773   struct connectdata *conn;
    774 };
    775 
    776 static int cpool_find_conn(struct Curl_easy *data,
    777                            struct connectdata *conn, void *param)
    778 {
    779   struct cpool_find_ctx *fctx = param;
    780   (void)data;
    781   if(conn->connection_id == fctx->id) {
    782     fctx->conn = conn;
    783     return 1;
    784   }
    785   return 0;
    786 }
    787 
    788 struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
    789                                         curl_off_t conn_id)
    790 {
    791   struct cpool *cpool = cpool_get_instance(data);
    792   struct cpool_find_ctx fctx;
    793 
    794   if(!cpool)
    795     return NULL;
    796   fctx.id = conn_id;
    797   fctx.conn = NULL;
    798   CPOOL_LOCK(cpool, data);
    799   cpool_foreach(data, cpool, &fctx, cpool_find_conn);
    800   CPOOL_UNLOCK(cpool, data);
    801   return fctx.conn;
    802 }
    803 
    804 struct cpool_do_conn_ctx {
    805   curl_off_t id;
    806   Curl_cpool_conn_do_cb *cb;
    807   void *cbdata;
    808 };
    809 
    810 static int cpool_do_conn(struct Curl_easy *data,
    811                          struct connectdata *conn, void *param)
    812 {
    813   struct cpool_do_conn_ctx *dctx = param;
    814   (void)data;
    815   if(conn->connection_id == dctx->id) {
    816     dctx->cb(conn, data, dctx->cbdata);
    817     return 1;
    818   }
    819   return 0;
    820 }
    821 
    822 void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
    823                          Curl_cpool_conn_do_cb *cb, void *cbdata)
    824 {
    825   struct cpool *cpool = cpool_get_instance(data);
    826   struct cpool_do_conn_ctx dctx;
    827 
    828   if(!cpool)
    829     return;
    830   dctx.id = conn_id;
    831   dctx.cb = cb;
    832   dctx.cbdata = cbdata;
    833   CPOOL_LOCK(cpool, data);
    834   cpool_foreach(data, cpool, &dctx, cpool_do_conn);
    835   CPOOL_UNLOCK(cpool, data);
    836 }
    837 
    838 void Curl_cpool_do_locked(struct Curl_easy *data,
    839                           struct connectdata *conn,
    840                           Curl_cpool_conn_do_cb *cb, void *cbdata)
    841 {
    842   struct cpool *cpool = cpool_get_instance(data);
    843   if(cpool) {
    844     CPOOL_LOCK(cpool, data);
    845     cb(conn, data, cbdata);
    846     CPOOL_UNLOCK(cpool, data);
    847   }
    848   else
    849     cb(conn, data, cbdata);
    850 }
    851 
    852 #if 0
    853 /* Useful for debugging the connection pool */
    854 void Curl_cpool_print(struct cpool *cpool)
    855 {
    856   struct Curl_hash_iterator iter;
    857   struct Curl_llist_node *curr;
    858   struct Curl_hash_element *he;
    859 
    860   if(!cpool)
    861     return;
    862 
    863   fprintf(stderr, "=Bundle cache=\n");
    864 
    865   Curl_hash_start_iterate(cpool->dest2bundle, &iter);
    866 
    867   he = Curl_hash_next_element(&iter);
    868   while(he) {
    869     struct cpool_bundle *bundle;
    870     struct connectdata *conn;
    871 
    872     bundle = he->ptr;
    873 
    874     fprintf(stderr, "%s -", he->key);
    875     curr = Curl_llist_head(bundle->conns);
    876     while(curr) {
    877       conn = Curl_node_elem(curr);
    878 
    879       fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
    880       curr = Curl_node_next(curr);
    881     }
    882     fprintf(stderr, "\n");
    883 
    884     he = Curl_hash_next_element(&iter);
    885   }
    886 }
    887 #endif