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