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