1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus (at) haxx.se> 9 * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel (at) 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.haxx.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 ***************************************************************************/ 23 24 #include "curl_setup.h" 25 26 #include <curl/curl.h> 27 28 #include "urldata.h" 29 #include "url.h" 30 #include "progress.h" 31 #include "multiif.h" 32 #include "sendf.h" 33 #include "conncache.h" 34 #include "share.h" 35 #include "sigpipe.h" 36 #include "connect.h" 37 38 /* The last 3 #include files should be in this order */ 39 #include "curl_printf.h" 40 #include "curl_memory.h" 41 #include "memdebug.h" 42 43 #ifdef CURLDEBUG 44 /* the debug versions of these macros make extra certain that the lock is 45 never doubly locked or unlocked */ 46 #define CONN_LOCK(x) if((x)->share) { \ 47 Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE); \ 48 DEBUGASSERT(!(x)->state.conncache_lock); \ 49 (x)->state.conncache_lock = TRUE; \ 50 } 51 52 #define CONN_UNLOCK(x) if((x)->share) { \ 53 DEBUGASSERT((x)->state.conncache_lock); \ 54 (x)->state.conncache_lock = FALSE; \ 55 Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \ 56 } 57 #else 58 #define CONN_LOCK(x) if((x)->share) \ 59 Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE) 60 #define CONN_UNLOCK(x) if((x)->share) \ 61 Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT) 62 #endif 63 64 static void conn_llist_dtor(void *user, void *element) 65 { 66 struct connectdata *conn = element; 67 (void)user; 68 conn->bundle = NULL; 69 } 70 71 static CURLcode bundle_create(struct Curl_easy *data, 72 struct connectbundle **cb_ptr) 73 { 74 (void)data; 75 DEBUGASSERT(*cb_ptr == NULL); 76 *cb_ptr = malloc(sizeof(struct connectbundle)); 77 if(!*cb_ptr) 78 return CURLE_OUT_OF_MEMORY; 79 80 (*cb_ptr)->num_connections = 0; 81 (*cb_ptr)->multiuse = BUNDLE_UNKNOWN; 82 83 Curl_llist_init(&(*cb_ptr)->conn_list, (curl_llist_dtor) conn_llist_dtor); 84 return CURLE_OK; 85 } 86 87 static void bundle_destroy(struct connectbundle *cb_ptr) 88 { 89 if(!cb_ptr) 90 return; 91 92 Curl_llist_destroy(&cb_ptr->conn_list, NULL); 93 94 free(cb_ptr); 95 } 96 97 /* Add a connection to a bundle */ 98 static void bundle_add_conn(struct connectbundle *cb_ptr, 99 struct connectdata *conn) 100 { 101 Curl_llist_insert_next(&cb_ptr->conn_list, cb_ptr->conn_list.tail, conn, 102 &conn->bundle_node); 103 conn->bundle = cb_ptr; 104 cb_ptr->num_connections++; 105 } 106 107 /* Remove a connection from a bundle */ 108 static int bundle_remove_conn(struct connectbundle *cb_ptr, 109 struct connectdata *conn) 110 { 111 struct curl_llist_element *curr; 112 113 curr = cb_ptr->conn_list.head; 114 while(curr) { 115 if(curr->ptr == conn) { 116 Curl_llist_remove(&cb_ptr->conn_list, curr, NULL); 117 cb_ptr->num_connections--; 118 conn->bundle = NULL; 119 return 1; /* we removed a handle */ 120 } 121 curr = curr->next; 122 } 123 return 0; 124 } 125 126 static void free_bundle_hash_entry(void *freethis) 127 { 128 struct connectbundle *b = (struct connectbundle *) freethis; 129 130 bundle_destroy(b); 131 } 132 133 int Curl_conncache_init(struct conncache *connc, int size) 134 { 135 int rc; 136 137 /* allocate a new easy handle to use when closing cached connections */ 138 connc->closure_handle = curl_easy_init(); 139 if(!connc->closure_handle) 140 return 1; /* bad */ 141 142 rc = Curl_hash_init(&connc->hash, size, Curl_hash_str, 143 Curl_str_key_compare, free_bundle_hash_entry); 144 if(rc) { 145 Curl_close(connc->closure_handle); 146 connc->closure_handle = NULL; 147 } 148 else 149 connc->closure_handle->state.conn_cache = connc; 150 151 return rc; 152 } 153 154 void Curl_conncache_destroy(struct conncache *connc) 155 { 156 if(connc) 157 Curl_hash_destroy(&connc->hash); 158 } 159 160 /* creates a key to find a bundle for this connection */ 161 static void hashkey(struct connectdata *conn, char *buf, 162 size_t len) /* something like 128 is fine */ 163 { 164 const char *hostname; 165 166 if(conn->bits.socksproxy) 167 hostname = conn->socks_proxy.host.name; 168 else if(conn->bits.httpproxy) 169 hostname = conn->http_proxy.host.name; 170 else if(conn->bits.conn_to_host) 171 hostname = conn->conn_to_host.name; 172 else 173 hostname = conn->host.name; 174 175 DEBUGASSERT(len > 32); 176 177 /* put the number first so that the hostname gets cut off if too long */ 178 msnprintf(buf, len, "%ld%s", conn->port, hostname); 179 } 180 181 void Curl_conncache_unlock(struct Curl_easy *data) 182 { 183 CONN_UNLOCK(data); 184 } 185 186 /* Returns number of connections currently held in the connection cache. 187 Locks/unlocks the cache itself! 188 */ 189 size_t Curl_conncache_size(struct Curl_easy *data) 190 { 191 size_t num; 192 CONN_LOCK(data); 193 num = data->state.conn_cache->num_conn; 194 CONN_UNLOCK(data); 195 return num; 196 } 197 198 /* Returns number of connections currently held in the connections's bundle 199 Locks/unlocks the cache itself! 200 */ 201 size_t Curl_conncache_bundle_size(struct connectdata *conn) 202 { 203 size_t num; 204 CONN_LOCK(conn->data); 205 num = conn->bundle->num_connections; 206 CONN_UNLOCK(conn->data); 207 return num; 208 } 209 210 /* Look up the bundle with all the connections to the same host this 211 connectdata struct is setup to use. 212 213 **NOTE**: When it returns, it holds the connection cache lock! */ 214 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, 215 struct conncache *connc) 216 { 217 struct connectbundle *bundle = NULL; 218 CONN_LOCK(conn->data); 219 if(connc) { 220 char key[128]; 221 hashkey(conn, key, sizeof(key)); 222 bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); 223 } 224 225 return bundle; 226 } 227 228 static bool conncache_add_bundle(struct conncache *connc, 229 char *key, 230 struct connectbundle *bundle) 231 { 232 void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle); 233 234 return p?TRUE:FALSE; 235 } 236 237 static void conncache_remove_bundle(struct conncache *connc, 238 struct connectbundle *bundle) 239 { 240 struct curl_hash_iterator iter; 241 struct curl_hash_element *he; 242 243 if(!connc) 244 return; 245 246 Curl_hash_start_iterate(&connc->hash, &iter); 247 248 he = Curl_hash_next_element(&iter); 249 while(he) { 250 if(he->ptr == bundle) { 251 /* The bundle is destroyed by the hash destructor function, 252 free_bundle_hash_entry() */ 253 Curl_hash_delete(&connc->hash, he->key, he->key_len); 254 return; 255 } 256 257 he = Curl_hash_next_element(&iter); 258 } 259 } 260 261 CURLcode Curl_conncache_add_conn(struct conncache *connc, 262 struct connectdata *conn) 263 { 264 CURLcode result = CURLE_OK; 265 struct connectbundle *bundle; 266 struct connectbundle *new_bundle = NULL; 267 struct Curl_easy *data = conn->data; 268 269 /* *find_bundle() locks the connection cache */ 270 bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache); 271 if(!bundle) { 272 int rc; 273 char key[128]; 274 275 result = bundle_create(data, &new_bundle); 276 if(result) { 277 goto unlock; 278 } 279 280 hashkey(conn, key, sizeof(key)); 281 rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle); 282 283 if(!rc) { 284 bundle_destroy(new_bundle); 285 result = CURLE_OUT_OF_MEMORY; 286 goto unlock; 287 } 288 bundle = new_bundle; 289 } 290 291 bundle_add_conn(bundle, conn); 292 conn->connection_id = connc->next_connection_id++; 293 connc->num_conn++; 294 295 DEBUGF(infof(conn->data, "Added connection %ld. " 296 "The cache now contains %zu members\n", 297 conn->connection_id, connc->num_conn)); 298 299 unlock: 300 CONN_UNLOCK(data); 301 302 return result; 303 } 304 305 /* 306 * Removes the connectdata object from the connection cache *and* clears the 307 * ->data pointer association. Pass TRUE/FALSE in the 'lock' argument 308 * depending on if the parent function already holds the lock or not. 309 */ 310 void Curl_conncache_remove_conn(struct Curl_easy *data, 311 struct connectdata *conn, bool lock) 312 { 313 struct connectbundle *bundle = conn->bundle; 314 struct conncache *connc = data->state.conn_cache; 315 316 /* The bundle pointer can be NULL, since this function can be called 317 due to a failed connection attempt, before being added to a bundle */ 318 if(bundle) { 319 if(lock) { 320 CONN_LOCK(data); 321 } 322 bundle_remove_conn(bundle, conn); 323 if(bundle->num_connections == 0) 324 conncache_remove_bundle(connc, bundle); 325 conn->bundle = NULL; /* removed from it */ 326 if(connc) { 327 connc->num_conn--; 328 DEBUGF(infof(data, "The cache now contains %zu members\n", 329 connc->num_conn)); 330 } 331 conn->data = NULL; /* clear the association */ 332 if(lock) { 333 CONN_UNLOCK(data); 334 } 335 } 336 } 337 338 /* This function iterates the entire connection cache and calls the function 339 func() with the connection pointer as the first argument and the supplied 340 'param' argument as the other. 341 342 The conncache lock is still held when the callback is called. It needs it, 343 so that it can safely continue traversing the lists once the callback 344 returns. 345 346 Returns 1 if the loop was aborted due to the callback's return code. 347 348 Return 0 from func() to continue the loop, return 1 to abort it. 349 */ 350 bool Curl_conncache_foreach(struct Curl_easy *data, 351 struct conncache *connc, 352 void *param, 353 int (*func)(struct connectdata *conn, void *param)) 354 { 355 struct curl_hash_iterator iter; 356 struct curl_llist_element *curr; 357 struct curl_hash_element *he; 358 359 if(!connc) 360 return FALSE; 361 362 CONN_LOCK(data); 363 Curl_hash_start_iterate(&connc->hash, &iter); 364 365 he = Curl_hash_next_element(&iter); 366 while(he) { 367 struct connectbundle *bundle; 368 369 bundle = he->ptr; 370 he = Curl_hash_next_element(&iter); 371 372 curr = bundle->conn_list.head; 373 while(curr) { 374 /* Yes, we need to update curr before calling func(), because func() 375 might decide to remove the connection */ 376 struct connectdata *conn = curr->ptr; 377 curr = curr->next; 378 379 if(1 == func(conn, param)) { 380 CONN_UNLOCK(data); 381 return TRUE; 382 } 383 } 384 } 385 CONN_UNLOCK(data); 386 return FALSE; 387 } 388 389 /* Return the first connection found in the cache. Used when closing all 390 connections. 391 392 NOTE: no locking is done here as this is presumably only done when cleaning 393 up a cache! 394 */ 395 static struct connectdata * 396 conncache_find_first_connection(struct conncache *connc) 397 { 398 struct curl_hash_iterator iter; 399 struct curl_hash_element *he; 400 struct connectbundle *bundle; 401 402 Curl_hash_start_iterate(&connc->hash, &iter); 403 404 he = Curl_hash_next_element(&iter); 405 while(he) { 406 struct curl_llist_element *curr; 407 bundle = he->ptr; 408 409 curr = bundle->conn_list.head; 410 if(curr) { 411 return curr->ptr; 412 } 413 414 he = Curl_hash_next_element(&iter); 415 } 416 417 return NULL; 418 } 419 420 /* 421 * Give ownership of a connection back to the connection cache. Might 422 * disconnect the oldest existing in there to make space. 423 * 424 * Return TRUE if stored, FALSE if closed. 425 */ 426 bool Curl_conncache_return_conn(struct connectdata *conn) 427 { 428 struct Curl_easy *data = conn->data; 429 430 /* data->multi->maxconnects can be negative, deal with it. */ 431 size_t maxconnects = 432 (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: 433 data->multi->maxconnects; 434 struct connectdata *conn_candidate = NULL; 435 436 conn->data = NULL; /* no owner anymore */ 437 if(maxconnects > 0 && 438 Curl_conncache_size(data) > maxconnects) { 439 infof(data, "Connection cache is full, closing the oldest one.\n"); 440 441 conn_candidate = Curl_conncache_extract_oldest(data); 442 if(conn_candidate) { 443 /* the winner gets the honour of being disconnected */ 444 (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); 445 } 446 } 447 448 return (conn_candidate == conn) ? FALSE : TRUE; 449 450 } 451 452 /* 453 * This function finds the connection in the connection bundle that has been 454 * unused for the longest time. 455 * 456 * Does not lock the connection cache! 457 * 458 * Returns the pointer to the oldest idle connection, or NULL if none was 459 * found. 460 */ 461 struct connectdata * 462 Curl_conncache_extract_bundle(struct Curl_easy *data, 463 struct connectbundle *bundle) 464 { 465 struct curl_llist_element *curr; 466 timediff_t highscore = -1; 467 timediff_t score; 468 struct curltime now; 469 struct connectdata *conn_candidate = NULL; 470 struct connectdata *conn; 471 472 (void)data; 473 474 now = Curl_now(); 475 476 curr = bundle->conn_list.head; 477 while(curr) { 478 conn = curr->ptr; 479 480 if(!CONN_INUSE(conn) && !conn->data) { 481 /* Set higher score for the age passed since the connection was used */ 482 score = Curl_timediff(now, conn->now); 483 484 if(score > highscore) { 485 highscore = score; 486 conn_candidate = conn; 487 } 488 } 489 curr = curr->next; 490 } 491 if(conn_candidate) { 492 /* remove it to prevent another thread from nicking it */ 493 bundle_remove_conn(bundle, conn_candidate); 494 data->state.conn_cache->num_conn--; 495 DEBUGF(infof(data, "The cache now contains %zu members\n", 496 data->state.conn_cache->num_conn)); 497 conn_candidate->data = data; /* associate! */ 498 } 499 500 return conn_candidate; 501 } 502 503 /* 504 * This function finds the connection in the connection cache that has been 505 * unused for the longest time and extracts that from the bundle. 506 * 507 * Returns the pointer to the connection, or NULL if none was found. 508 */ 509 struct connectdata * 510 Curl_conncache_extract_oldest(struct Curl_easy *data) 511 { 512 struct conncache *connc = data->state.conn_cache; 513 struct curl_hash_iterator iter; 514 struct curl_llist_element *curr; 515 struct curl_hash_element *he; 516 timediff_t highscore =- 1; 517 timediff_t score; 518 struct curltime now; 519 struct connectdata *conn_candidate = NULL; 520 struct connectbundle *bundle; 521 struct connectbundle *bundle_candidate = NULL; 522 523 now = Curl_now(); 524 525 CONN_LOCK(data); 526 Curl_hash_start_iterate(&connc->hash, &iter); 527 528 he = Curl_hash_next_element(&iter); 529 while(he) { 530 struct connectdata *conn; 531 532 bundle = he->ptr; 533 534 curr = bundle->conn_list.head; 535 while(curr) { 536 conn = curr->ptr; 537 538 if(!CONN_INUSE(conn) && !conn->data) { 539 /* Set higher score for the age passed since the connection was used */ 540 score = Curl_timediff(now, conn->now); 541 542 if(score > highscore) { 543 highscore = score; 544 conn_candidate = conn; 545 bundle_candidate = bundle; 546 } 547 } 548 curr = curr->next; 549 } 550 551 he = Curl_hash_next_element(&iter); 552 } 553 if(conn_candidate) { 554 /* remove it to prevent another thread from nicking it */ 555 bundle_remove_conn(bundle_candidate, conn_candidate); 556 connc->num_conn--; 557 DEBUGF(infof(data, "The cache now contains %zu members\n", 558 connc->num_conn)); 559 conn_candidate->data = data; /* associate! */ 560 } 561 CONN_UNLOCK(data); 562 563 return conn_candidate; 564 } 565 566 void Curl_conncache_close_all_connections(struct conncache *connc) 567 { 568 struct connectdata *conn; 569 570 conn = conncache_find_first_connection(connc); 571 while(conn) { 572 SIGPIPE_VARIABLE(pipe_st); 573 conn->data = connc->closure_handle; 574 575 sigpipe_ignore(conn->data, &pipe_st); 576 /* This will remove the connection from the cache */ 577 connclose(conn, "kill all"); 578 (void)Curl_disconnect(connc->closure_handle, conn, FALSE); 579 sigpipe_restore(&pipe_st); 580 581 conn = conncache_find_first_connection(connc); 582 } 583 584 if(connc->closure_handle) { 585 SIGPIPE_VARIABLE(pipe_st); 586 sigpipe_ignore(connc->closure_handle, &pipe_st); 587 588 Curl_hostcache_clean(connc->closure_handle, 589 connc->closure_handle->dns.hostcache); 590 Curl_close(connc->closure_handle); 591 sigpipe_restore(&pipe_st); 592 } 593 } 594 595 #if 0 596 /* Useful for debugging the connection cache */ 597 void Curl_conncache_print(struct conncache *connc) 598 { 599 struct curl_hash_iterator iter; 600 struct curl_llist_element *curr; 601 struct curl_hash_element *he; 602 603 if(!connc) 604 return; 605 606 fprintf(stderr, "=Bundle cache=\n"); 607 608 Curl_hash_start_iterate(connc->hash, &iter); 609 610 he = Curl_hash_next_element(&iter); 611 while(he) { 612 struct connectbundle *bundle; 613 struct connectdata *conn; 614 615 bundle = he->ptr; 616 617 fprintf(stderr, "%s -", he->key); 618 curr = bundle->conn_list->head; 619 while(curr) { 620 conn = curr->ptr; 621 622 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse); 623 curr = curr->next; 624 } 625 fprintf(stderr, "\n"); 626 627 he = Curl_hash_next_element(&iter); 628 } 629 } 630 #endif 631