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