xref: /curl/lib/conncache.c (revision 9acecc92)
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 "sendf.h"
36 #include "conncache.h"
37 #include "http_negotiate.h"
38 #include "http_ntlm.h"
39 #include "share.h"
40 #include "sigpipe.h"
41 #include "connect.h"
42 #include "select.h"
43 #include "strcase.h"
44 
45 /* The last 3 #include files should be in this order */
46 #include "curl_printf.h"
47 #include "curl_memory.h"
48 #include "memdebug.h"
49 
50 
51 #define CPOOL_IS_LOCKED(c)    ((c) && (c)->locked)
52 
53 #define CPOOL_LOCK(c)                                                   \
54   do {                                                                  \
55     if((c)) {                                                           \
56       if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
57         Curl_share_lock(((c)->idata), CURL_LOCK_DATA_CONNECT,           \
58                         CURL_LOCK_ACCESS_SINGLE);                       \
59       DEBUGASSERT(!(c)->locked);                                        \
60       (c)->locked = TRUE;                                               \
61     }                                                                   \
62   } while(0)
63 
64 #define CPOOL_UNLOCK(c)                                                 \
65   do {                                                                  \
66     if((c)) {                                                           \
67       DEBUGASSERT((c)->locked);                                         \
68       (c)->locked = FALSE;                                              \
69       if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
70         Curl_share_unlock((c)->idata, CURL_LOCK_DATA_CONNECT);          \
71     }                                                                   \
72   } while(0)
73 
74 
75 /* A list of connections to the same destination. */
76 struct cpool_bundle {
77   struct Curl_llist conns; /* connections in the bundle */
78   size_t dest_len; /* total length of destination, including NUL */
79   char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
80 };
81 
82 
83 static void cpool_discard_conn(struct cpool *cpool,
84                                struct Curl_easy *data,
85                                struct connectdata *conn,
86                                bool aborted);
87 static void cpool_close_and_destroy(struct cpool *cpool,
88                                     struct connectdata *conn,
89                                     struct Curl_easy *data,
90                                     bool do_shutdown);
91 static void cpool_run_conn_shutdown(struct Curl_easy *data,
92                                     struct connectdata *conn,
93                                     bool *done);
94 static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
95                                             struct connectdata *conn);
96 static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
97                                           struct Curl_easy *data,
98                                           struct connectdata *conn);
99 static void cpool_shutdown_all(struct cpool *cpool,
100                                struct Curl_easy *data, int timeout_ms);
101 static void cpool_close_and_destroy_all(struct cpool *cpool);
102 static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool);
103 
cpool_bundle_create(const char * dest,size_t dest_len)104 static struct cpool_bundle *cpool_bundle_create(const char *dest,
105                                                 size_t dest_len)
106 {
107   struct cpool_bundle *bundle;
108   bundle = calloc(1, sizeof(*bundle) + dest_len);
109   if(!bundle)
110     return NULL;
111   Curl_llist_init(&bundle->conns, NULL);
112   bundle->dest_len = dest_len;
113   memcpy(bundle->dest, dest, dest_len);
114   return bundle;
115 }
116 
cpool_bundle_destroy(struct cpool_bundle * bundle)117 static void cpool_bundle_destroy(struct cpool_bundle *bundle)
118 {
119   DEBUGASSERT(!Curl_llist_count(&bundle->conns));
120   free(bundle);
121 }
122 
123 /* Add a connection to a bundle */
cpool_bundle_add(struct cpool_bundle * bundle,struct connectdata * conn)124 static void cpool_bundle_add(struct cpool_bundle *bundle,
125                              struct connectdata *conn)
126 {
127   DEBUGASSERT(!Curl_node_llist(&conn->cpool_node));
128   Curl_llist_append(&bundle->conns, conn, &conn->cpool_node);
129   conn->bits.in_cpool = TRUE;
130 }
131 
132 /* Remove a connection from a bundle */
cpool_bundle_remove(struct cpool_bundle * bundle,struct connectdata * conn)133 static void cpool_bundle_remove(struct cpool_bundle *bundle,
134                                 struct connectdata *conn)
135 {
136   (void)bundle;
137   DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns);
138   Curl_node_remove(&conn->cpool_node);
139   conn->bits.in_cpool = FALSE;
140 }
141 
cpool_bundle_free_entry(void * freethis)142 static void cpool_bundle_free_entry(void *freethis)
143 {
144   cpool_bundle_destroy((struct cpool_bundle *)freethis);
145 }
146 
Curl_cpool_init(struct cpool * cpool,Curl_cpool_disconnect_cb * disconnect_cb,struct Curl_multi * multi,struct Curl_share * share,size_t size)147 int Curl_cpool_init(struct cpool *cpool,
148                         Curl_cpool_disconnect_cb *disconnect_cb,
149                         struct Curl_multi *multi,
150                         struct Curl_share *share,
151                         size_t size)
152 {
153   DEBUGASSERT(!!multi != !!share); /* either one */
154   Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
155                  Curl_str_key_compare, cpool_bundle_free_entry);
156   Curl_llist_init(&cpool->shutdowns, NULL);
157 
158   DEBUGASSERT(disconnect_cb);
159   if(!disconnect_cb)
160     return 1;
161 
162   /* allocate a new easy handle to use when closing cached connections */
163   cpool->idata = curl_easy_init();
164   if(!cpool->idata)
165     return 1; /* bad */
166   cpool->idata->state.internal = TRUE;
167   /* TODO: this is quirky. We need an internal handle for certain
168    * operations, but we do not add it to the multi (if there is one).
169    * But we give it the multi so that socket event operations can work.
170    * Probably better to have an internal handle owned by the multi that
171    * can be used for cpool operations. */
172   cpool->idata->multi = multi;
173 #ifdef DEBUGBUILD
174   if(getenv("CURL_DEBUG"))
175     cpool->idata->set.verbose = TRUE;
176 #endif
177 
178   cpool->disconnect_cb = disconnect_cb;
179   cpool->idata->multi = cpool->multi = multi;
180   cpool->idata->share = cpool->share = share;
181 
182   return 0; /* good */
183 }
184 
Curl_cpool_destroy(struct cpool * cpool)185 void Curl_cpool_destroy(struct cpool *cpool)
186 {
187   if(cpool) {
188     if(cpool->idata) {
189       cpool_close_and_destroy_all(cpool);
190       /* The internal closure handle is special and we need to
191        * disconnect it from multi/share before closing it down. */
192       cpool->idata->multi = NULL;
193       cpool->idata->share = NULL;
194       Curl_close(&cpool->idata);
195     }
196     Curl_hash_destroy(&cpool->dest2bundle);
197     cpool->multi = NULL;
198   }
199 }
200 
cpool_get_instance(struct Curl_easy * data)201 static struct cpool *cpool_get_instance(struct Curl_easy *data)
202 {
203   if(data) {
204     if(CURL_SHARE_KEEP_CONNECT(data->share))
205       return &data->share->cpool;
206     else if(data->multi_easy)
207       return &data->multi_easy->cpool;
208     else if(data->multi)
209       return &data->multi->cpool;
210   }
211   return NULL;
212 }
213 
Curl_cpool_xfer_init(struct Curl_easy * data)214 void Curl_cpool_xfer_init(struct Curl_easy *data)
215 {
216   struct cpool *cpool = cpool_get_instance(data);
217 
218   DEBUGASSERT(cpool);
219   if(cpool) {
220     CPOOL_LOCK(cpool);
221     /* the identifier inside the connection cache */
222     data->id = cpool->next_easy_id++;
223     if(cpool->next_easy_id <= 0)
224       cpool->next_easy_id = 0;
225     data->state.lastconnect_id = -1;
226 
227     /* The closure handle only ever has default timeouts set. To improve the
228        state somewhat we clone the timeouts from each added handle so that the
229        closure handle always has the same timeouts as the most recently added
230        easy handle. */
231     cpool->idata->set.timeout = data->set.timeout;
232     cpool->idata->set.server_response_timeout =
233       data->set.server_response_timeout;
234     cpool->idata->set.no_signal = data->set.no_signal;
235 
236     CPOOL_UNLOCK(cpool);
237   }
238   else {
239     /* We should not get here, but in a non-debug build, do something */
240     data->id = 0;
241     data->state.lastconnect_id = -1;
242   }
243 }
244 
cpool_find_bundle(struct cpool * cpool,struct connectdata * conn)245 static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
246                                               struct connectdata *conn)
247 {
248   return Curl_hash_pick(&cpool->dest2bundle,
249                         conn->destination, conn->destination_len);
250 }
251 
252 static struct cpool_bundle *
cpool_add_bundle(struct cpool * cpool,struct connectdata * conn)253 cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
254 {
255   struct cpool_bundle *bundle;
256 
257   bundle = cpool_bundle_create(conn->destination, conn->destination_len);
258   if(!bundle)
259     return NULL;
260 
261   if(!Curl_hash_add(&cpool->dest2bundle,
262                     bundle->dest, bundle->dest_len, bundle)) {
263     cpool_bundle_destroy(bundle);
264     return NULL;
265   }
266   return bundle;
267 }
268 
cpool_remove_bundle(struct cpool * cpool,struct cpool_bundle * bundle)269 static void cpool_remove_bundle(struct cpool *cpool,
270                                 struct cpool_bundle *bundle)
271 {
272   if(!cpool)
273     return;
274 
275   Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
276 }
277 
278 static struct connectdata *
279 cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle);
280 
Curl_cpool_check_limits(struct Curl_easy * data,struct connectdata * conn)281 int Curl_cpool_check_limits(struct Curl_easy *data,
282                             struct connectdata *conn)
283 {
284   struct cpool *cpool = cpool_get_instance(data);
285   struct cpool_bundle *bundle;
286   size_t dest_limit = 0;
287   size_t total_limit = 0;
288   int result = CPOOL_LIMIT_OK;
289 
290   if(!cpool)
291     return CPOOL_LIMIT_OK;
292 
293   if(data && data->multi) {
294     dest_limit = data->multi->max_host_connections;
295     total_limit = data->multi->max_total_connections;
296   }
297 
298   if(!dest_limit && !total_limit)
299     return CPOOL_LIMIT_OK;
300 
301   CPOOL_LOCK(cpool);
302   if(dest_limit) {
303     bundle = cpool_find_bundle(cpool, conn);
304     while(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
305       struct connectdata *oldest_idle = NULL;
306       /* The bundle is full. Extract the oldest connection that may
307        * be removed now, if there is one. */
308       oldest_idle = cpool_bundle_get_oldest_idle(bundle);
309       if(!oldest_idle)
310         break;
311       /* disconnect the old conn and continue */
312       DEBUGF(infof(data, "Discarding connection #%"
313                    FMT_OFF_T " from %zu to reach destination "
314                    "limit of %zu", oldest_idle->connection_id,
315                    Curl_llist_count(&bundle->conns), dest_limit));
316       Curl_cpool_disconnect(data, oldest_idle, FALSE);
317 
318       /* in case the bundle was destroyed in disconnect, look it up again */
319       bundle = cpool_find_bundle(cpool, conn);
320     }
321     if(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
322       result = CPOOL_LIMIT_DEST;
323       goto out;
324     }
325   }
326 
327   if(total_limit) {
328     while(cpool->num_conn >= total_limit) {
329       struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
330       if(!oldest_idle)
331         break;
332       /* disconnect the old conn and continue */
333       DEBUGF(infof(data, "Discarding connection #%"
334                    FMT_OFF_T " from %zu to reach total "
335                    "limit of %zu",
336                    oldest_idle->connection_id, cpool->num_conn, total_limit));
337       Curl_cpool_disconnect(data, oldest_idle, FALSE);
338     }
339     if(cpool->num_conn >= total_limit) {
340       result = CPOOL_LIMIT_TOTAL;
341       goto out;
342     }
343   }
344 
345 out:
346   CPOOL_UNLOCK(cpool);
347   return result;
348 }
349 
Curl_cpool_add_conn(struct Curl_easy * data,struct connectdata * conn)350 CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
351                              struct connectdata *conn)
352 {
353   CURLcode result = CURLE_OK;
354   struct cpool_bundle *bundle = NULL;
355   struct cpool *cpool = cpool_get_instance(data);
356   DEBUGASSERT(conn);
357 
358   DEBUGASSERT(cpool);
359   if(!cpool)
360     return CURLE_FAILED_INIT;
361 
362   CPOOL_LOCK(cpool);
363   bundle = cpool_find_bundle(cpool, conn);
364   if(!bundle) {
365     bundle = cpool_add_bundle(cpool, conn);
366     if(!bundle) {
367       result = CURLE_OUT_OF_MEMORY;
368       goto out;
369     }
370   }
371 
372   cpool_bundle_add(bundle, conn);
373   conn->connection_id = cpool->next_connection_id++;
374   cpool->num_conn++;
375   DEBUGF(infof(data, "Added connection %" FMT_OFF_T ". "
376                "The cache now contains %zu members",
377                conn->connection_id, cpool->num_conn));
378 out:
379   CPOOL_UNLOCK(cpool);
380 
381   return result;
382 }
383 
cpool_remove_conn(struct cpool * cpool,struct connectdata * conn)384 static void cpool_remove_conn(struct cpool *cpool,
385                               struct connectdata *conn)
386 {
387   struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
388   DEBUGASSERT(cpool);
389   if(list) {
390     /* The connection is certainly in the pool, but where? */
391     struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
392     if(bundle && (list == &bundle->conns)) {
393       cpool_bundle_remove(bundle, conn);
394       if(!Curl_llist_count(&bundle->conns))
395         cpool_remove_bundle(cpool, bundle);
396       conn->bits.in_cpool = FALSE;
397       cpool->num_conn--;
398     }
399     else {
400       /* Not in a bundle, already in the shutdown list? */
401       DEBUGASSERT(list == &cpool->shutdowns);
402     }
403   }
404 }
405 
406 /* This function iterates the entire connection pool and calls the function
407    func() with the connection pointer as the first argument and the supplied
408    'param' argument as the other.
409 
410    The cpool lock is still held when the callback is called. It needs it,
411    so that it can safely continue traversing the lists once the callback
412    returns.
413 
414    Returns TRUE if the loop was aborted due to the callback's return code.
415 
416    Return 0 from func() to continue the loop, return 1 to abort it.
417  */
cpool_foreach(struct Curl_easy * data,struct cpool * cpool,void * param,int (* func)(struct Curl_easy * data,struct connectdata * conn,void * param))418 static bool cpool_foreach(struct Curl_easy *data,
419                           struct cpool *cpool,
420                           void *param,
421                           int (*func)(struct Curl_easy *data,
422                                       struct connectdata *conn, void *param))
423 {
424   struct Curl_hash_iterator iter;
425   struct Curl_hash_element *he;
426 
427   if(!cpool)
428     return FALSE;
429 
430   Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
431 
432   he = Curl_hash_next_element(&iter);
433   while(he) {
434     struct Curl_llist_node *curr;
435     struct cpool_bundle *bundle = he->ptr;
436     he = Curl_hash_next_element(&iter);
437 
438     curr = Curl_llist_head(&bundle->conns);
439     while(curr) {
440       /* Yes, we need to update curr before calling func(), because func()
441          might decide to remove the connection */
442       struct connectdata *conn = Curl_node_elem(curr);
443       curr = Curl_node_next(curr);
444 
445       if(1 == func(data, conn, param)) {
446         return TRUE;
447       }
448     }
449   }
450   return FALSE;
451 }
452 
453 /* Return a live connection in the pool or NULL. */
cpool_get_live_conn(struct cpool * cpool)454 static struct connectdata *cpool_get_live_conn(struct cpool *cpool)
455 {
456   struct Curl_hash_iterator iter;
457   struct Curl_hash_element *he;
458   struct cpool_bundle *bundle;
459   struct Curl_llist_node *conn_node;
460 
461   Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
462   for(he = Curl_hash_next_element(&iter); he;
463       he = Curl_hash_next_element(&iter)) {
464     bundle = he->ptr;
465     conn_node = Curl_llist_head(&bundle->conns);
466     if(conn_node)
467       return Curl_node_elem(conn_node);
468   }
469   return NULL;
470 }
471 
472 /*
473  * A connection (already in the pool) has become idle. Do any
474  * cleanups in regard to the pool's limits.
475  *
476  * Return TRUE if idle connection kept in pool, FALSE if closed.
477  */
Curl_cpool_conn_now_idle(struct Curl_easy * data,struct connectdata * conn)478 bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
479                               struct connectdata *conn)
480 {
481   unsigned int maxconnects = !data->multi->maxconnects ?
482     data->multi->num_easy * 4 : data->multi->maxconnects;
483   struct connectdata *oldest_idle = NULL;
484   struct cpool *cpool = cpool_get_instance(data);
485   bool kept = TRUE;
486 
487   conn->lastused = Curl_now(); /* it was used up until now */
488   if(cpool && maxconnects) {
489     /* may be called form a callback already under lock */
490     bool do_lock = !CPOOL_IS_LOCKED(cpool);
491     if(do_lock)
492       CPOOL_LOCK(cpool);
493     if(cpool->num_conn > maxconnects) {
494       infof(data, "Connection pool is full, closing the oldest one");
495 
496       oldest_idle = cpool_get_oldest_idle(cpool);
497       kept = (oldest_idle != conn);
498       if(oldest_idle) {
499         Curl_cpool_disconnect(cpool->idata, oldest_idle, FALSE);
500       }
501     }
502     if(do_lock)
503       CPOOL_UNLOCK(cpool);
504   }
505 
506   return kept;
507 }
508 
509 /*
510  * This function finds the connection in the connection bundle that has been
511  * unused for the longest time.
512  */
513 static struct connectdata *
cpool_bundle_get_oldest_idle(struct cpool_bundle * bundle)514 cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
515 {
516   struct Curl_llist_node *curr;
517   timediff_t highscore = -1;
518   timediff_t score;
519   struct curltime now;
520   struct connectdata *oldest_idle = NULL;
521   struct connectdata *conn;
522 
523   now = Curl_now();
524   curr = Curl_llist_head(&bundle->conns);
525   while(curr) {
526     conn = Curl_node_elem(curr);
527 
528     if(!CONN_INUSE(conn)) {
529       /* Set higher score for the age passed since the connection was used */
530       score = Curl_timediff(now, conn->lastused);
531 
532       if(score > highscore) {
533         highscore = score;
534         oldest_idle = conn;
535       }
536     }
537     curr = Curl_node_next(curr);
538   }
539   return oldest_idle;
540 }
541 
cpool_get_oldest_idle(struct cpool * cpool)542 static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
543 {
544   struct Curl_hash_iterator iter;
545   struct Curl_llist_node *curr;
546   struct Curl_hash_element *he;
547   struct connectdata *oldest_idle = NULL;
548   struct cpool_bundle *bundle;
549   struct curltime now;
550   timediff_t highscore =- 1;
551   timediff_t score;
552 
553   now = Curl_now();
554   Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
555 
556   for(he = Curl_hash_next_element(&iter); he;
557       he = Curl_hash_next_element(&iter)) {
558     struct connectdata *conn;
559     bundle = he->ptr;
560 
561     for(curr = Curl_llist_head(&bundle->conns); curr;
562         curr = Curl_node_next(curr)) {
563       conn = Curl_node_elem(curr);
564       if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
565         continue;
566       /* Set higher score for the age passed since the connection was used */
567       score = Curl_timediff(now, conn->lastused);
568       if(score > highscore) {
569         highscore = score;
570         oldest_idle = conn;
571       }
572     }
573   }
574   return oldest_idle;
575 }
576 
Curl_cpool_find(struct Curl_easy * data,const char * destination,size_t dest_len,Curl_cpool_conn_match_cb * conn_cb,Curl_cpool_done_match_cb * done_cb,void * userdata)577 bool Curl_cpool_find(struct Curl_easy *data,
578                      const char *destination, size_t dest_len,
579                      Curl_cpool_conn_match_cb *conn_cb,
580                      Curl_cpool_done_match_cb *done_cb,
581                      void *userdata)
582 {
583   struct cpool *cpool = cpool_get_instance(data);
584   struct cpool_bundle *bundle;
585   bool result = FALSE;
586 
587   DEBUGASSERT(cpool);
588   DEBUGASSERT(conn_cb);
589   if(!cpool)
590     return FALSE;
591 
592   CPOOL_LOCK(cpool);
593   bundle = Curl_hash_pick(&cpool->dest2bundle, (void *)destination, dest_len);
594   if(bundle) {
595     struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
596     while(curr) {
597       struct connectdata *conn = Curl_node_elem(curr);
598       /* Get next node now. callback might discard current */
599       curr = Curl_node_next(curr);
600 
601       if(conn_cb(conn, userdata)) {
602         result = TRUE;
603         break;
604       }
605     }
606   }
607 
608   if(done_cb) {
609     result = done_cb(result, userdata);
610   }
611   CPOOL_UNLOCK(cpool);
612   return result;
613 }
614 
cpool_shutdown_discard_all(struct cpool * cpool)615 static void cpool_shutdown_discard_all(struct cpool *cpool)
616 {
617   struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
618   struct connectdata *conn;
619 
620   if(!e)
621     return;
622 
623   DEBUGF(infof(cpool->idata, "cpool_shutdown_discard_all"));
624   while(e) {
625     conn = Curl_node_elem(e);
626     Curl_node_remove(e);
627     DEBUGF(infof(cpool->idata, "discard connection #%" FMT_OFF_T,
628                  conn->connection_id));
629     cpool_close_and_destroy(cpool, conn, NULL, FALSE);
630     e = Curl_llist_head(&cpool->shutdowns);
631   }
632 }
633 
cpool_close_and_destroy_all(struct cpool * cpool)634 static void cpool_close_and_destroy_all(struct cpool *cpool)
635 {
636   struct connectdata *conn;
637   int timeout_ms = 0;
638   SIGPIPE_VARIABLE(pipe_st);
639 
640   DEBUGASSERT(cpool);
641   /* Move all connections to the shutdown list */
642   sigpipe_init(&pipe_st);
643   CPOOL_LOCK(cpool);
644   conn = cpool_get_live_conn(cpool);
645   while(conn) {
646     cpool_remove_conn(cpool, conn);
647     sigpipe_apply(cpool->idata, &pipe_st);
648     connclose(conn, "kill all");
649     cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
650 
651     conn = cpool_get_live_conn(cpool);
652   }
653   CPOOL_UNLOCK(cpool);
654 
655     /* Just for testing, run graceful shutdown */
656 #ifdef DEBUGBUILD
657   {
658     char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
659     if(p) {
660       long l = strtol(p, NULL, 10);
661       if(l > 0 && l < INT_MAX)
662         timeout_ms = (int)l;
663     }
664   }
665 #endif
666   sigpipe_apply(cpool->idata, &pipe_st);
667   cpool_shutdown_all(cpool, cpool->idata, timeout_ms);
668 
669   /* discard all connections in the shutdown list */
670   cpool_shutdown_discard_all(cpool);
671 
672   Curl_hostcache_clean(cpool->idata, cpool->idata->dns.hostcache);
673   sigpipe_restore(&pipe_st);
674 }
675 
676 
cpool_shutdown_destroy_oldest(struct cpool * cpool)677 static void cpool_shutdown_destroy_oldest(struct cpool *cpool)
678 {
679   struct Curl_llist_node *e;
680   struct connectdata *conn;
681 
682   e = Curl_llist_head(&cpool->shutdowns);
683   if(e) {
684     SIGPIPE_VARIABLE(pipe_st);
685     conn = Curl_node_elem(e);
686     Curl_node_remove(e);
687     sigpipe_init(&pipe_st);
688     sigpipe_apply(cpool->idata, &pipe_st);
689     cpool_close_and_destroy(cpool, conn, NULL, FALSE);
690     sigpipe_restore(&pipe_st);
691   }
692 }
693 
cpool_discard_conn(struct cpool * cpool,struct Curl_easy * data,struct connectdata * conn,bool aborted)694 static void cpool_discard_conn(struct cpool *cpool,
695                                struct Curl_easy *data,
696                                struct connectdata *conn,
697                                bool aborted)
698 {
699   bool done = FALSE;
700 
701   DEBUGASSERT(data);
702   DEBUGASSERT(cpool);
703   DEBUGASSERT(!conn->bits.in_cpool);
704 
705   /*
706    * If this connection is not marked to force-close, leave it open if there
707    * are other users of it
708    */
709   if(CONN_INUSE(conn) && !aborted) {
710     DEBUGF(infof(data, "[CCACHE] not discarding #%" FMT_OFF_T
711                  " still in use by %zu transfers", conn->connection_id,
712                  CONN_INUSE(conn)));
713     return;
714   }
715 
716   /* treat the connection as aborted in CONNECT_ONLY situations, we do
717    * not know what the APP did with it. */
718   if(conn->connect_only)
719     aborted = TRUE;
720   conn->bits.aborted = aborted;
721 
722   /* We do not shutdown dead connections. The term 'dead' can be misleading
723    * here, as we also mark errored connections/transfers as 'dead'.
724    * If we do a shutdown for an aborted transfer, the server might think
725    * it was successful otherwise (for example an ftps: upload). This is
726    * not what we want. */
727   if(aborted)
728     done = TRUE;
729   if(!done) {
730     /* Attempt to shutdown the connection right away. */
731     Curl_attach_connection(data, conn);
732     cpool_run_conn_shutdown(data, conn, &done);
733     DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
734                  conn->connection_id, done));
735     Curl_detach_connection(data);
736   }
737 
738   if(done) {
739     cpool_close_and_destroy(cpool, conn, data, FALSE);
740     return;
741   }
742 
743   /* Add the connection to our shutdown list for non-blocking shutdown
744    * during multi processing. */
745   if(data->multi && data->multi->max_shutdown_connections > 0 &&
746      (data->multi->max_shutdown_connections >=
747       (long)Curl_llist_count(&cpool->shutdowns))) {
748     DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection "
749                        "due to limit of %ld",
750                        data->multi->max_shutdown_connections));
751     cpool_shutdown_destroy_oldest(cpool);
752   }
753 
754   if(data->multi && data->multi->socket_cb) {
755     DEBUGASSERT(cpool == &data->multi->cpool);
756     /* Start with an empty shutdown pollset, so out internal closure handle
757      * is added to the sockets. */
758     memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll));
759     if(cpool_update_shutdown_ev(data->multi, cpool->idata, conn)) {
760       DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, "
761                    "discarding #%" FMT_OFF_T,
762                    conn->connection_id));
763       cpool_close_and_destroy(cpool, conn, data, FALSE);
764       return;
765     }
766   }
767 
768   Curl_llist_append(&cpool->shutdowns, conn, &conn->cpool_node);
769   DEBUGF(infof(data, "[CCACHE] added #%" FMT_OFF_T
770                " to shutdown list of length %zu", conn->connection_id,
771                Curl_llist_count(&cpool->shutdowns)));
772 }
773 
Curl_cpool_disconnect(struct Curl_easy * data,struct connectdata * conn,bool aborted)774 void Curl_cpool_disconnect(struct Curl_easy *data,
775                            struct connectdata *conn,
776                            bool aborted)
777 {
778   struct cpool *cpool = cpool_get_instance(data);
779   bool do_lock;
780 
781   DEBUGASSERT(cpool);
782   DEBUGASSERT(data && !data->conn);
783   if(!cpool)
784     return;
785 
786   /* If this connection is not marked to force-close, leave it open if there
787    * are other users of it */
788   if(CONN_INUSE(conn) && !aborted) {
789     DEBUGASSERT(0); /* does this ever happen? */
790     DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
791     return;
792   }
793 
794   /* This method may be called while we are under lock, e.g. from a
795    * user callback in find. */
796   do_lock = !CPOOL_IS_LOCKED(cpool);
797   if(do_lock)
798     CPOOL_LOCK(cpool);
799 
800   if(conn->bits.in_cpool) {
801     cpool_remove_conn(cpool, conn);
802     DEBUGASSERT(!conn->bits.in_cpool);
803   }
804 
805   /* Run the callback to let it clean up anything it wants to. */
806   aborted = cpool->disconnect_cb(data, conn, aborted);
807 
808   if(data->multi) {
809     /* Add it to the multi's cpool for shutdown handling */
810     infof(data, "%s connection #%" FMT_OFF_T,
811           aborted ? "closing" : "shutting down", conn->connection_id);
812     cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
813   }
814   else {
815     /* No multi available. Make a best-effort shutdown + close */
816     infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
817     cpool_close_and_destroy(NULL, conn, data, !aborted);
818   }
819 
820   if(do_lock)
821     CPOOL_UNLOCK(cpool);
822 }
823 
cpool_run_conn_shutdown_handler(struct Curl_easy * data,struct connectdata * conn)824 static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
825                                             struct connectdata *conn)
826 {
827   if(!conn->bits.shutdown_handler) {
828     if(conn->dns_entry)
829       Curl_resolv_unlink(data, &conn->dns_entry);
830 
831     /* Cleanup NTLM connection-related data */
832     Curl_http_auth_cleanup_ntlm(conn);
833 
834     /* Cleanup NEGOTIATE connection-related data */
835     Curl_http_auth_cleanup_negotiate(conn);
836 
837     if(conn->handler && conn->handler->disconnect) {
838       /* This is set if protocol-specific cleanups should be made */
839       DEBUGF(infof(data, "connection #%" FMT_OFF_T
840                    ", shutdown protocol handler (aborted=%d)",
841                    conn->connection_id, conn->bits.aborted));
842 
843       conn->handler->disconnect(data, conn, conn->bits.aborted);
844     }
845 
846     /* possible left-overs from the async name resolvers */
847     Curl_resolver_cancel(data);
848 
849     conn->bits.shutdown_handler = TRUE;
850   }
851 }
852 
cpool_run_conn_shutdown(struct Curl_easy * data,struct connectdata * conn,bool * done)853 static void cpool_run_conn_shutdown(struct Curl_easy *data,
854                                     struct connectdata *conn,
855                                     bool *done)
856 {
857   CURLcode r1, r2;
858   bool done1, done2;
859 
860   /* We expect to be attached when called */
861   DEBUGASSERT(data->conn == conn);
862 
863   cpool_run_conn_shutdown_handler(data, conn);
864 
865   if(conn->bits.shutdown_filters) {
866     *done = TRUE;
867     return;
868   }
869 
870   if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
871     r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
872   else {
873     r1 = CURLE_OK;
874     done1 = TRUE;
875   }
876 
877   if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
878     r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
879   else {
880     r2 = CURLE_OK;
881     done2 = TRUE;
882   }
883 
884   /* we are done when any failed or both report success */
885   *done = (r1 || r2 || (done1 && done2));
886   if(*done)
887     conn->bits.shutdown_filters = TRUE;
888 }
889 
cpool_add_pollfds(struct cpool * cpool,struct curl_pollfds * cpfds)890 static CURLcode cpool_add_pollfds(struct cpool *cpool,
891                                   struct curl_pollfds *cpfds)
892 {
893   CURLcode result = CURLE_OK;
894 
895   if(Curl_llist_head(&cpool->shutdowns)) {
896     struct Curl_llist_node *e;
897     struct easy_pollset ps;
898     struct connectdata *conn;
899 
900     for(e = Curl_llist_head(&cpool->shutdowns); e;
901         e = Curl_node_next(e)) {
902       conn = Curl_node_elem(e);
903       memset(&ps, 0, sizeof(ps));
904       Curl_attach_connection(cpool->idata, conn);
905       Curl_conn_adjust_pollset(cpool->idata, &ps);
906       Curl_detach_connection(cpool->idata);
907 
908       result = Curl_pollfds_add_ps(cpfds, &ps);
909       if(result) {
910         Curl_pollfds_cleanup(cpfds);
911         goto out;
912       }
913     }
914   }
915 out:
916   return result;
917 }
918 
Curl_cpool_add_pollfds(struct cpool * cpool,struct curl_pollfds * cpfds)919 CURLcode Curl_cpool_add_pollfds(struct cpool *cpool,
920                                 struct curl_pollfds *cpfds)
921 {
922   CURLcode result;
923   CPOOL_LOCK(cpool);
924   result = cpool_add_pollfds(cpool, cpfds);
925   CPOOL_UNLOCK(cpool);
926   return result;
927 }
928 
Curl_cpool_add_waitfds(struct cpool * cpool,struct curl_waitfds * cwfds)929 CURLcode Curl_cpool_add_waitfds(struct cpool *cpool,
930                                 struct curl_waitfds *cwfds)
931 {
932   CURLcode result = CURLE_OK;
933 
934   CPOOL_LOCK(cpool);
935   if(Curl_llist_head(&cpool->shutdowns)) {
936     struct Curl_llist_node *e;
937     struct easy_pollset ps;
938     struct connectdata *conn;
939 
940     for(e = Curl_llist_head(&cpool->shutdowns); e;
941         e = Curl_node_next(e)) {
942       conn = Curl_node_elem(e);
943       memset(&ps, 0, sizeof(ps));
944       Curl_attach_connection(cpool->idata, conn);
945       Curl_conn_adjust_pollset(cpool->idata, &ps);
946       Curl_detach_connection(cpool->idata);
947 
948       result = Curl_waitfds_add_ps(cwfds, &ps);
949       if(result)
950         goto out;
951     }
952   }
953 out:
954   CPOOL_UNLOCK(cpool);
955   return result;
956 }
957 
cpool_perform(struct cpool * cpool)958 static void cpool_perform(struct cpool *cpool)
959 {
960   struct Curl_easy *data = cpool->idata;
961   struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
962   struct Curl_llist_node *enext;
963   struct connectdata *conn;
964   struct curltime *nowp = NULL;
965   struct curltime now;
966   timediff_t next_from_now_ms = 0, ms;
967   bool done;
968 
969   if(!e)
970     return;
971 
972   DEBUGASSERT(data);
973   DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown",
974                Curl_llist_count(&cpool->shutdowns)));
975   while(e) {
976     enext = Curl_node_next(e);
977     conn = Curl_node_elem(e);
978     Curl_attach_connection(data, conn);
979     cpool_run_conn_shutdown(data, conn, &done);
980     DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
981                  conn->connection_id, done));
982     Curl_detach_connection(data);
983     if(done) {
984       Curl_node_remove(e);
985       cpool_close_and_destroy(cpool, conn, NULL, FALSE);
986     }
987     else {
988       /* Not done, when does this connection time out? */
989       if(!nowp) {
990         now = Curl_now();
991         nowp = &now;
992       }
993       ms = Curl_conn_shutdown_timeleft(conn, nowp);
994       if(ms && ms < next_from_now_ms)
995         next_from_now_ms = ms;
996     }
997     e = enext;
998   }
999 
1000   if(next_from_now_ms)
1001     Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW);
1002 }
1003 
Curl_cpool_multi_perform(struct Curl_multi * multi)1004 void Curl_cpool_multi_perform(struct Curl_multi *multi)
1005 {
1006   CPOOL_LOCK(&multi->cpool);
1007   cpool_perform(&multi->cpool);
1008   CPOOL_UNLOCK(&multi->cpool);
1009 }
1010 
1011 
1012 /*
1013  * Close and destroy the connection. Run the shutdown sequence once,
1014  * of so requested.
1015  */
cpool_close_and_destroy(struct cpool * cpool,struct connectdata * conn,struct Curl_easy * data,bool do_shutdown)1016 static void cpool_close_and_destroy(struct cpool *cpool,
1017                                     struct connectdata *conn,
1018                                     struct Curl_easy *data,
1019                                     bool do_shutdown)
1020 {
1021   bool done;
1022 
1023   /* there must be a connection to close */
1024   DEBUGASSERT(conn);
1025   /* it must be removed from the connection pool */
1026   DEBUGASSERT(!conn->bits.in_cpool);
1027   /* there must be an associated transfer */
1028   DEBUGASSERT(data || cpool);
1029   if(!data)
1030     data = cpool->idata;
1031 
1032   /* the transfer must be detached from the connection */
1033   DEBUGASSERT(data && !data->conn);
1034 
1035   Curl_attach_connection(data, conn);
1036 
1037   cpool_run_conn_shutdown_handler(data, conn);
1038   if(do_shutdown) {
1039     /* Make a last attempt to shutdown handlers and filters, if
1040      * not done so already. */
1041     cpool_run_conn_shutdown(data, conn, &done);
1042   }
1043 
1044   if(cpool)
1045     DEBUGF(infof(data, "[CCACHE] closing #%" FMT_OFF_T,
1046                  conn->connection_id));
1047   else
1048     DEBUGF(infof(data, "closing connection #%" FMT_OFF_T,
1049                  conn->connection_id));
1050   Curl_conn_close(data, SECONDARYSOCKET);
1051   Curl_conn_close(data, FIRSTSOCKET);
1052   Curl_detach_connection(data);
1053 
1054   Curl_conn_free(data, conn);
1055 }
1056 
1057 
cpool_update_shutdown_ev(struct Curl_multi * multi,struct Curl_easy * data,struct connectdata * conn)1058 static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
1059                                           struct Curl_easy *data,
1060                                           struct connectdata *conn)
1061 {
1062   struct easy_pollset ps;
1063   CURLMcode mresult;
1064 
1065   DEBUGASSERT(data);
1066   DEBUGASSERT(multi);
1067   DEBUGASSERT(multi->socket_cb);
1068 
1069   memset(&ps, 0, sizeof(ps));
1070   Curl_attach_connection(data, conn);
1071   Curl_conn_adjust_pollset(data, &ps);
1072   Curl_detach_connection(data);
1073 
1074   mresult = Curl_multi_pollset_ev(multi, data, &ps, &conn->shutdown_poll);
1075 
1076   if(!mresult) /* Remember for next time */
1077     memcpy(&conn->shutdown_poll, &ps, sizeof(ps));
1078   return mresult;
1079 }
1080 
Curl_cpool_multi_socket(struct Curl_multi * multi,curl_socket_t s,int ev_bitmask)1081 void Curl_cpool_multi_socket(struct Curl_multi *multi,
1082                              curl_socket_t s, int ev_bitmask)
1083 {
1084   struct cpool *cpool = &multi->cpool;
1085   struct Curl_easy *data = cpool->idata;
1086   struct Curl_llist_node *e;
1087   struct connectdata *conn;
1088   bool done;
1089 
1090   (void)ev_bitmask;
1091   DEBUGASSERT(multi->socket_cb);
1092   CPOOL_LOCK(cpool);
1093   e = Curl_llist_head(&cpool->shutdowns);
1094   while(e) {
1095     conn = Curl_node_elem(e);
1096     if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
1097       Curl_attach_connection(data, conn);
1098       cpool_run_conn_shutdown(data, conn, &done);
1099       DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
1100                    conn->connection_id, done));
1101       Curl_detach_connection(data);
1102       if(done || cpool_update_shutdown_ev(multi, data, conn)) {
1103         Curl_node_remove(e);
1104         cpool_close_and_destroy(cpool, conn, NULL, FALSE);
1105       }
1106       break;
1107     }
1108     e = Curl_node_next(e);
1109   }
1110   CPOOL_UNLOCK(cpool);
1111 }
1112 
1113 #define NUM_POLLS_ON_STACK 10
1114 
cpool_shutdown_wait(struct cpool * cpool,int timeout_ms)1115 static CURLcode cpool_shutdown_wait(struct cpool *cpool, int timeout_ms)
1116 {
1117   struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
1118   struct curl_pollfds cpfds;
1119   CURLcode result;
1120 
1121   Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
1122 
1123   result = cpool_add_pollfds(cpool, &cpfds);
1124   if(result)
1125     goto out;
1126 
1127   Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
1128 
1129 out:
1130   Curl_pollfds_cleanup(&cpfds);
1131   return result;
1132 }
1133 
cpool_shutdown_all(struct cpool * cpool,struct Curl_easy * data,int timeout_ms)1134 static void cpool_shutdown_all(struct cpool *cpool,
1135                                struct Curl_easy *data, int timeout_ms)
1136 {
1137   struct connectdata *conn;
1138   struct curltime started = Curl_now();
1139 
1140   if(!data)
1141     return;
1142   (void)data;
1143 
1144   DEBUGF(infof(data, "cpool shutdown all"));
1145 
1146   /* Move all connections into the shutdown queue */
1147   for(conn = cpool_get_live_conn(cpool); conn;
1148       conn = cpool_get_live_conn(cpool)) {
1149     /* Move conn from live set to shutdown or destroy right away */
1150     DEBUGF(infof(data, "moving connection #%" FMT_OFF_T
1151                  " to shutdown queue", conn->connection_id));
1152     cpool_remove_conn(cpool, conn);
1153     cpool_discard_conn(cpool, data, conn, FALSE);
1154   }
1155 
1156   while(Curl_llist_head(&cpool->shutdowns)) {
1157     timediff_t timespent;
1158     int remain_ms;
1159 
1160     cpool_perform(cpool);
1161 
1162     if(!Curl_llist_head(&cpool->shutdowns)) {
1163       DEBUGF(infof(data, "cpool shutdown ok"));
1164       break;
1165     }
1166 
1167     /* wait for activity, timeout or "nothing" */
1168     timespent = Curl_timediff(Curl_now(), started);
1169     if(timespent >= (timediff_t)timeout_ms) {
1170       DEBUGF(infof(data, "cpool shutdown %s",
1171                    (timeout_ms > 0) ? "timeout" : "best effort done"));
1172       break;
1173     }
1174 
1175     remain_ms = timeout_ms - (int)timespent;
1176     if(cpool_shutdown_wait(cpool, remain_ms)) {
1177       DEBUGF(infof(data, "cpool shutdown all, abort"));
1178       break;
1179     }
1180   }
1181 
1182   /* Due to errors/timeout, we might come here without being done. */
1183   cpool_shutdown_discard_all(cpool);
1184 }
1185 
1186 struct cpool_reaper_ctx {
1187   struct curltime now;
1188 };
1189 
cpool_reap_dead_cb(struct Curl_easy * data,struct connectdata * conn,void * param)1190 static int cpool_reap_dead_cb(struct Curl_easy *data,
1191                               struct connectdata *conn, void *param)
1192 {
1193   struct cpool_reaper_ctx *rctx = param;
1194   if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
1195     /* stop the iteration here, pass back the connection that was pruned */
1196     Curl_cpool_disconnect(data, conn, FALSE);
1197     return 1;
1198   }
1199   return 0; /* continue iteration */
1200 }
1201 
1202 /*
1203  * This function scans the data's connection pool for half-open/dead
1204  * connections, closes and removes them.
1205  * The cleanup is done at most once per second.
1206  *
1207  * When called, this transfer has no connection attached.
1208  */
Curl_cpool_prune_dead(struct Curl_easy * data)1209 void Curl_cpool_prune_dead(struct Curl_easy *data)
1210 {
1211   struct cpool *cpool = cpool_get_instance(data);
1212   struct cpool_reaper_ctx rctx;
1213   timediff_t elapsed;
1214 
1215   if(!cpool)
1216     return;
1217 
1218   rctx.now = Curl_now();
1219   CPOOL_LOCK(cpool);
1220   elapsed = Curl_timediff(rctx.now, cpool->last_cleanup);
1221 
1222   if(elapsed >= 1000L) {
1223     while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
1224       ;
1225     cpool->last_cleanup = rctx.now;
1226   }
1227   CPOOL_UNLOCK(cpool);
1228 }
1229 
conn_upkeep(struct Curl_easy * data,struct connectdata * conn,void * param)1230 static int conn_upkeep(struct Curl_easy *data,
1231                        struct connectdata *conn,
1232                        void *param)
1233 {
1234   struct curltime *now = param;
1235   /* TODO, shall we reap connections that return an error here? */
1236   Curl_conn_upkeep(data, conn, now);
1237   return 0; /* continue iteration */
1238 }
1239 
Curl_cpool_upkeep(void * data)1240 CURLcode Curl_cpool_upkeep(void *data)
1241 {
1242   struct cpool *cpool = cpool_get_instance(data);
1243   struct curltime now = Curl_now();
1244 
1245   if(!cpool)
1246     return CURLE_OK;
1247 
1248   CPOOL_LOCK(cpool);
1249   cpool_foreach(data, cpool, &now, conn_upkeep);
1250   CPOOL_UNLOCK(cpool);
1251   return CURLE_OK;
1252 }
1253 
1254 struct cpool_find_ctx {
1255   curl_off_t id;
1256   struct connectdata *conn;
1257 };
1258 
cpool_find_conn(struct Curl_easy * data,struct connectdata * conn,void * param)1259 static int cpool_find_conn(struct Curl_easy *data,
1260                            struct connectdata *conn, void *param)
1261 {
1262   struct cpool_find_ctx *fctx = param;
1263   (void)data;
1264   if(conn->connection_id == fctx->id) {
1265     fctx->conn = conn;
1266     return 1;
1267   }
1268   return 0;
1269 }
1270 
Curl_cpool_get_conn(struct Curl_easy * data,curl_off_t conn_id)1271 struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
1272                                         curl_off_t conn_id)
1273 {
1274   struct cpool *cpool = cpool_get_instance(data);
1275   struct cpool_find_ctx fctx;
1276 
1277   if(!cpool)
1278     return NULL;
1279   fctx.id = conn_id;
1280   fctx.conn = NULL;
1281   CPOOL_LOCK(cpool);
1282   cpool_foreach(cpool->idata, cpool, &fctx, cpool_find_conn);
1283   CPOOL_UNLOCK(cpool);
1284   return fctx.conn;
1285 }
1286 
1287 struct cpool_do_conn_ctx {
1288   curl_off_t id;
1289   Curl_cpool_conn_do_cb *cb;
1290   void *cbdata;
1291 };
1292 
cpool_do_conn(struct Curl_easy * data,struct connectdata * conn,void * param)1293 static int cpool_do_conn(struct Curl_easy *data,
1294                          struct connectdata *conn, void *param)
1295 {
1296   struct cpool_do_conn_ctx *dctx = param;
1297   (void)data;
1298   if(conn->connection_id == dctx->id) {
1299     dctx->cb(conn, data, dctx->cbdata);
1300     return 1;
1301   }
1302   return 0;
1303 }
1304 
Curl_cpool_do_by_id(struct Curl_easy * data,curl_off_t conn_id,Curl_cpool_conn_do_cb * cb,void * cbdata)1305 void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
1306                          Curl_cpool_conn_do_cb *cb, void *cbdata)
1307 {
1308   struct cpool *cpool = cpool_get_instance(data);
1309   struct cpool_do_conn_ctx dctx;
1310 
1311   if(!cpool)
1312     return;
1313   dctx.id = conn_id;
1314   dctx.cb = cb;
1315   dctx.cbdata = cbdata;
1316   CPOOL_LOCK(cpool);
1317   cpool_foreach(data, cpool, &dctx, cpool_do_conn);
1318   CPOOL_UNLOCK(cpool);
1319 }
1320 
Curl_cpool_do_locked(struct Curl_easy * data,struct connectdata * conn,Curl_cpool_conn_do_cb * cb,void * cbdata)1321 void Curl_cpool_do_locked(struct Curl_easy *data,
1322                           struct connectdata *conn,
1323                           Curl_cpool_conn_do_cb *cb, void *cbdata)
1324 {
1325   struct cpool *cpool = cpool_get_instance(data);
1326   if(cpool) {
1327     CPOOL_LOCK(cpool);
1328     cb(conn, data, cbdata);
1329     CPOOL_UNLOCK(cpool);
1330   }
1331   else
1332     cb(conn, data, cbdata);
1333 }
1334 
1335 #if 0
1336 /* Useful for debugging the connection pool */
1337 void Curl_cpool_print(struct cpool *cpool)
1338 {
1339   struct Curl_hash_iterator iter;
1340   struct Curl_llist_node *curr;
1341   struct Curl_hash_element *he;
1342 
1343   if(!cpool)
1344     return;
1345 
1346   fprintf(stderr, "=Bundle cache=\n");
1347 
1348   Curl_hash_start_iterate(cpool->dest2bundle, &iter);
1349 
1350   he = Curl_hash_next_element(&iter);
1351   while(he) {
1352     struct cpool_bundle *bundle;
1353     struct connectdata *conn;
1354 
1355     bundle = he->ptr;
1356 
1357     fprintf(stderr, "%s -", he->key);
1358     curr = Curl_llist_head(bundle->conns);
1359     while(curr) {
1360       conn = Curl_node_elem(curr);
1361 
1362       fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
1363       curr = Curl_node_next(curr);
1364     }
1365     fprintf(stderr, "\n");
1366 
1367     he = Curl_hash_next_element(&iter);
1368   }
1369 }
1370 #endif
1371