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