113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se> 913498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 1013498266Sopenharmony_ci * 1113498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1213498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1313498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1413498266Sopenharmony_ci * 1513498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1613498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1713498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1813498266Sopenharmony_ci * 1913498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 2013498266Sopenharmony_ci * KIND, either express or implied. 2113498266Sopenharmony_ci * 2213498266Sopenharmony_ci * SPDX-License-Identifier: curl 2313498266Sopenharmony_ci * 2413498266Sopenharmony_ci ***************************************************************************/ 2513498266Sopenharmony_ci 2613498266Sopenharmony_ci#include "curl_setup.h" 2713498266Sopenharmony_ci 2813498266Sopenharmony_ci#include <curl/curl.h> 2913498266Sopenharmony_ci 3013498266Sopenharmony_ci#include "urldata.h" 3113498266Sopenharmony_ci#include "url.h" 3213498266Sopenharmony_ci#include "progress.h" 3313498266Sopenharmony_ci#include "multiif.h" 3413498266Sopenharmony_ci#include "sendf.h" 3513498266Sopenharmony_ci#include "conncache.h" 3613498266Sopenharmony_ci#include "share.h" 3713498266Sopenharmony_ci#include "sigpipe.h" 3813498266Sopenharmony_ci#include "connect.h" 3913498266Sopenharmony_ci#include "strcase.h" 4013498266Sopenharmony_ci 4113498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 4213498266Sopenharmony_ci#include "curl_printf.h" 4313498266Sopenharmony_ci#include "curl_memory.h" 4413498266Sopenharmony_ci#include "memdebug.h" 4513498266Sopenharmony_ci 4613498266Sopenharmony_ci#define HASHKEY_SIZE 128 4713498266Sopenharmony_ci 4813498266Sopenharmony_cistatic CURLcode bundle_create(struct connectbundle **bundlep) 4913498266Sopenharmony_ci{ 5013498266Sopenharmony_ci DEBUGASSERT(*bundlep == NULL); 5113498266Sopenharmony_ci *bundlep = malloc(sizeof(struct connectbundle)); 5213498266Sopenharmony_ci if(!*bundlep) 5313498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 5413498266Sopenharmony_ci 5513498266Sopenharmony_ci (*bundlep)->num_connections = 0; 5613498266Sopenharmony_ci (*bundlep)->multiuse = BUNDLE_UNKNOWN; 5713498266Sopenharmony_ci 5813498266Sopenharmony_ci Curl_llist_init(&(*bundlep)->conn_list, NULL); 5913498266Sopenharmony_ci return CURLE_OK; 6013498266Sopenharmony_ci} 6113498266Sopenharmony_ci 6213498266Sopenharmony_cistatic void bundle_destroy(struct connectbundle *bundle) 6313498266Sopenharmony_ci{ 6413498266Sopenharmony_ci free(bundle); 6513498266Sopenharmony_ci} 6613498266Sopenharmony_ci 6713498266Sopenharmony_ci/* Add a connection to a bundle */ 6813498266Sopenharmony_cistatic void bundle_add_conn(struct connectbundle *bundle, 6913498266Sopenharmony_ci struct connectdata *conn) 7013498266Sopenharmony_ci{ 7113498266Sopenharmony_ci Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn, 7213498266Sopenharmony_ci &conn->bundle_node); 7313498266Sopenharmony_ci conn->bundle = bundle; 7413498266Sopenharmony_ci bundle->num_connections++; 7513498266Sopenharmony_ci} 7613498266Sopenharmony_ci 7713498266Sopenharmony_ci/* Remove a connection from a bundle */ 7813498266Sopenharmony_cistatic int bundle_remove_conn(struct connectbundle *bundle, 7913498266Sopenharmony_ci struct connectdata *conn) 8013498266Sopenharmony_ci{ 8113498266Sopenharmony_ci struct Curl_llist_element *curr; 8213498266Sopenharmony_ci 8313498266Sopenharmony_ci curr = bundle->conn_list.head; 8413498266Sopenharmony_ci while(curr) { 8513498266Sopenharmony_ci if(curr->ptr == conn) { 8613498266Sopenharmony_ci Curl_llist_remove(&bundle->conn_list, curr, NULL); 8713498266Sopenharmony_ci bundle->num_connections--; 8813498266Sopenharmony_ci conn->bundle = NULL; 8913498266Sopenharmony_ci return 1; /* we removed a handle */ 9013498266Sopenharmony_ci } 9113498266Sopenharmony_ci curr = curr->next; 9213498266Sopenharmony_ci } 9313498266Sopenharmony_ci DEBUGASSERT(0); 9413498266Sopenharmony_ci return 0; 9513498266Sopenharmony_ci} 9613498266Sopenharmony_ci 9713498266Sopenharmony_cistatic void free_bundle_hash_entry(void *freethis) 9813498266Sopenharmony_ci{ 9913498266Sopenharmony_ci struct connectbundle *b = (struct connectbundle *) freethis; 10013498266Sopenharmony_ci 10113498266Sopenharmony_ci bundle_destroy(b); 10213498266Sopenharmony_ci} 10313498266Sopenharmony_ci 10413498266Sopenharmony_ciint Curl_conncache_init(struct conncache *connc, int size) 10513498266Sopenharmony_ci{ 10613498266Sopenharmony_ci /* allocate a new easy handle to use when closing cached connections */ 10713498266Sopenharmony_ci connc->closure_handle = curl_easy_init(); 10813498266Sopenharmony_ci if(!connc->closure_handle) 10913498266Sopenharmony_ci return 1; /* bad */ 11013498266Sopenharmony_ci connc->closure_handle->state.internal = true; 11113498266Sopenharmony_ci 11213498266Sopenharmony_ci Curl_hash_init(&connc->hash, size, Curl_hash_str, 11313498266Sopenharmony_ci Curl_str_key_compare, free_bundle_hash_entry); 11413498266Sopenharmony_ci connc->closure_handle->state.conn_cache = connc; 11513498266Sopenharmony_ci 11613498266Sopenharmony_ci return 0; /* good */ 11713498266Sopenharmony_ci} 11813498266Sopenharmony_ci 11913498266Sopenharmony_civoid Curl_conncache_destroy(struct conncache *connc) 12013498266Sopenharmony_ci{ 12113498266Sopenharmony_ci if(connc) 12213498266Sopenharmony_ci Curl_hash_destroy(&connc->hash); 12313498266Sopenharmony_ci} 12413498266Sopenharmony_ci 12513498266Sopenharmony_ci/* creates a key to find a bundle for this connection */ 12613498266Sopenharmony_cistatic void hashkey(struct connectdata *conn, char *buf, size_t len) 12713498266Sopenharmony_ci{ 12813498266Sopenharmony_ci const char *hostname; 12913498266Sopenharmony_ci long port = conn->remote_port; 13013498266Sopenharmony_ci DEBUGASSERT(len >= HASHKEY_SIZE); 13113498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY 13213498266Sopenharmony_ci if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { 13313498266Sopenharmony_ci hostname = conn->http_proxy.host.name; 13413498266Sopenharmony_ci port = conn->port; 13513498266Sopenharmony_ci } 13613498266Sopenharmony_ci else 13713498266Sopenharmony_ci#endif 13813498266Sopenharmony_ci if(conn->bits.conn_to_host) 13913498266Sopenharmony_ci hostname = conn->conn_to_host.name; 14013498266Sopenharmony_ci else 14113498266Sopenharmony_ci hostname = conn->host.name; 14213498266Sopenharmony_ci 14313498266Sopenharmony_ci /* put the numbers first so that the hostname gets cut off if too long */ 14413498266Sopenharmony_ci#ifdef ENABLE_IPV6 14513498266Sopenharmony_ci msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname); 14613498266Sopenharmony_ci#else 14713498266Sopenharmony_ci msnprintf(buf, len, "%ld/%s", port, hostname); 14813498266Sopenharmony_ci#endif 14913498266Sopenharmony_ci Curl_strntolower(buf, buf, len); 15013498266Sopenharmony_ci} 15113498266Sopenharmony_ci 15213498266Sopenharmony_ci/* Returns number of connections currently held in the connection cache. 15313498266Sopenharmony_ci Locks/unlocks the cache itself! 15413498266Sopenharmony_ci*/ 15513498266Sopenharmony_cisize_t Curl_conncache_size(struct Curl_easy *data) 15613498266Sopenharmony_ci{ 15713498266Sopenharmony_ci size_t num; 15813498266Sopenharmony_ci CONNCACHE_LOCK(data); 15913498266Sopenharmony_ci num = data->state.conn_cache->num_conn; 16013498266Sopenharmony_ci CONNCACHE_UNLOCK(data); 16113498266Sopenharmony_ci return num; 16213498266Sopenharmony_ci} 16313498266Sopenharmony_ci 16413498266Sopenharmony_ci/* Look up the bundle with all the connections to the same host this 16513498266Sopenharmony_ci connectdata struct is setup to use. 16613498266Sopenharmony_ci 16713498266Sopenharmony_ci **NOTE**: When it returns, it holds the connection cache lock! */ 16813498266Sopenharmony_cistruct connectbundle * 16913498266Sopenharmony_ciCurl_conncache_find_bundle(struct Curl_easy *data, 17013498266Sopenharmony_ci struct connectdata *conn, 17113498266Sopenharmony_ci struct conncache *connc) 17213498266Sopenharmony_ci{ 17313498266Sopenharmony_ci struct connectbundle *bundle = NULL; 17413498266Sopenharmony_ci CONNCACHE_LOCK(data); 17513498266Sopenharmony_ci if(connc) { 17613498266Sopenharmony_ci char key[HASHKEY_SIZE]; 17713498266Sopenharmony_ci hashkey(conn, key, sizeof(key)); 17813498266Sopenharmony_ci bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); 17913498266Sopenharmony_ci } 18013498266Sopenharmony_ci 18113498266Sopenharmony_ci return bundle; 18213498266Sopenharmony_ci} 18313498266Sopenharmony_ci 18413498266Sopenharmony_cistatic void *conncache_add_bundle(struct conncache *connc, 18513498266Sopenharmony_ci char *key, 18613498266Sopenharmony_ci struct connectbundle *bundle) 18713498266Sopenharmony_ci{ 18813498266Sopenharmony_ci return Curl_hash_add(&connc->hash, key, strlen(key), bundle); 18913498266Sopenharmony_ci} 19013498266Sopenharmony_ci 19113498266Sopenharmony_cistatic void conncache_remove_bundle(struct conncache *connc, 19213498266Sopenharmony_ci struct connectbundle *bundle) 19313498266Sopenharmony_ci{ 19413498266Sopenharmony_ci struct Curl_hash_iterator iter; 19513498266Sopenharmony_ci struct Curl_hash_element *he; 19613498266Sopenharmony_ci 19713498266Sopenharmony_ci if(!connc) 19813498266Sopenharmony_ci return; 19913498266Sopenharmony_ci 20013498266Sopenharmony_ci Curl_hash_start_iterate(&connc->hash, &iter); 20113498266Sopenharmony_ci 20213498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 20313498266Sopenharmony_ci while(he) { 20413498266Sopenharmony_ci if(he->ptr == bundle) { 20513498266Sopenharmony_ci /* The bundle is destroyed by the hash destructor function, 20613498266Sopenharmony_ci free_bundle_hash_entry() */ 20713498266Sopenharmony_ci Curl_hash_delete(&connc->hash, he->key, he->key_len); 20813498266Sopenharmony_ci return; 20913498266Sopenharmony_ci } 21013498266Sopenharmony_ci 21113498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 21213498266Sopenharmony_ci } 21313498266Sopenharmony_ci} 21413498266Sopenharmony_ci 21513498266Sopenharmony_ciCURLcode Curl_conncache_add_conn(struct Curl_easy *data) 21613498266Sopenharmony_ci{ 21713498266Sopenharmony_ci CURLcode result = CURLE_OK; 21813498266Sopenharmony_ci struct connectbundle *bundle = NULL; 21913498266Sopenharmony_ci struct connectdata *conn = data->conn; 22013498266Sopenharmony_ci struct conncache *connc = data->state.conn_cache; 22113498266Sopenharmony_ci DEBUGASSERT(conn); 22213498266Sopenharmony_ci 22313498266Sopenharmony_ci /* *find_bundle() locks the connection cache */ 22413498266Sopenharmony_ci bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache); 22513498266Sopenharmony_ci if(!bundle) { 22613498266Sopenharmony_ci char key[HASHKEY_SIZE]; 22713498266Sopenharmony_ci 22813498266Sopenharmony_ci result = bundle_create(&bundle); 22913498266Sopenharmony_ci if(result) { 23013498266Sopenharmony_ci goto unlock; 23113498266Sopenharmony_ci } 23213498266Sopenharmony_ci 23313498266Sopenharmony_ci hashkey(conn, key, sizeof(key)); 23413498266Sopenharmony_ci 23513498266Sopenharmony_ci if(!conncache_add_bundle(data->state.conn_cache, key, bundle)) { 23613498266Sopenharmony_ci bundle_destroy(bundle); 23713498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 23813498266Sopenharmony_ci goto unlock; 23913498266Sopenharmony_ci } 24013498266Sopenharmony_ci } 24113498266Sopenharmony_ci 24213498266Sopenharmony_ci bundle_add_conn(bundle, conn); 24313498266Sopenharmony_ci conn->connection_id = connc->next_connection_id++; 24413498266Sopenharmony_ci connc->num_conn++; 24513498266Sopenharmony_ci 24613498266Sopenharmony_ci DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". " 24713498266Sopenharmony_ci "The cache now contains %zu members", 24813498266Sopenharmony_ci conn->connection_id, connc->num_conn)); 24913498266Sopenharmony_ci 25013498266Sopenharmony_ciunlock: 25113498266Sopenharmony_ci CONNCACHE_UNLOCK(data); 25213498266Sopenharmony_ci 25313498266Sopenharmony_ci return result; 25413498266Sopenharmony_ci} 25513498266Sopenharmony_ci 25613498266Sopenharmony_ci/* 25713498266Sopenharmony_ci * Removes the connectdata object from the connection cache, but the transfer 25813498266Sopenharmony_ci * still owns this connection. 25913498266Sopenharmony_ci * 26013498266Sopenharmony_ci * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function 26113498266Sopenharmony_ci * already holds the lock or not. 26213498266Sopenharmony_ci */ 26313498266Sopenharmony_civoid Curl_conncache_remove_conn(struct Curl_easy *data, 26413498266Sopenharmony_ci struct connectdata *conn, bool lock) 26513498266Sopenharmony_ci{ 26613498266Sopenharmony_ci struct connectbundle *bundle = conn->bundle; 26713498266Sopenharmony_ci struct conncache *connc = data->state.conn_cache; 26813498266Sopenharmony_ci 26913498266Sopenharmony_ci /* The bundle pointer can be NULL, since this function can be called 27013498266Sopenharmony_ci due to a failed connection attempt, before being added to a bundle */ 27113498266Sopenharmony_ci if(bundle) { 27213498266Sopenharmony_ci if(lock) { 27313498266Sopenharmony_ci CONNCACHE_LOCK(data); 27413498266Sopenharmony_ci } 27513498266Sopenharmony_ci bundle_remove_conn(bundle, conn); 27613498266Sopenharmony_ci if(bundle->num_connections == 0) 27713498266Sopenharmony_ci conncache_remove_bundle(connc, bundle); 27813498266Sopenharmony_ci conn->bundle = NULL; /* removed from it */ 27913498266Sopenharmony_ci if(connc) { 28013498266Sopenharmony_ci connc->num_conn--; 28113498266Sopenharmony_ci DEBUGF(infof(data, "The cache now contains %zu members", 28213498266Sopenharmony_ci connc->num_conn)); 28313498266Sopenharmony_ci } 28413498266Sopenharmony_ci if(lock) { 28513498266Sopenharmony_ci CONNCACHE_UNLOCK(data); 28613498266Sopenharmony_ci } 28713498266Sopenharmony_ci } 28813498266Sopenharmony_ci} 28913498266Sopenharmony_ci 29013498266Sopenharmony_ci/* This function iterates the entire connection cache and calls the function 29113498266Sopenharmony_ci func() with the connection pointer as the first argument and the supplied 29213498266Sopenharmony_ci 'param' argument as the other. 29313498266Sopenharmony_ci 29413498266Sopenharmony_ci The conncache lock is still held when the callback is called. It needs it, 29513498266Sopenharmony_ci so that it can safely continue traversing the lists once the callback 29613498266Sopenharmony_ci returns. 29713498266Sopenharmony_ci 29813498266Sopenharmony_ci Returns 1 if the loop was aborted due to the callback's return code. 29913498266Sopenharmony_ci 30013498266Sopenharmony_ci Return 0 from func() to continue the loop, return 1 to abort it. 30113498266Sopenharmony_ci */ 30213498266Sopenharmony_cibool Curl_conncache_foreach(struct Curl_easy *data, 30313498266Sopenharmony_ci struct conncache *connc, 30413498266Sopenharmony_ci void *param, 30513498266Sopenharmony_ci int (*func)(struct Curl_easy *data, 30613498266Sopenharmony_ci struct connectdata *conn, void *param)) 30713498266Sopenharmony_ci{ 30813498266Sopenharmony_ci struct Curl_hash_iterator iter; 30913498266Sopenharmony_ci struct Curl_llist_element *curr; 31013498266Sopenharmony_ci struct Curl_hash_element *he; 31113498266Sopenharmony_ci 31213498266Sopenharmony_ci if(!connc) 31313498266Sopenharmony_ci return FALSE; 31413498266Sopenharmony_ci 31513498266Sopenharmony_ci CONNCACHE_LOCK(data); 31613498266Sopenharmony_ci Curl_hash_start_iterate(&connc->hash, &iter); 31713498266Sopenharmony_ci 31813498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 31913498266Sopenharmony_ci while(he) { 32013498266Sopenharmony_ci struct connectbundle *bundle; 32113498266Sopenharmony_ci 32213498266Sopenharmony_ci bundle = he->ptr; 32313498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 32413498266Sopenharmony_ci 32513498266Sopenharmony_ci curr = bundle->conn_list.head; 32613498266Sopenharmony_ci while(curr) { 32713498266Sopenharmony_ci /* Yes, we need to update curr before calling func(), because func() 32813498266Sopenharmony_ci might decide to remove the connection */ 32913498266Sopenharmony_ci struct connectdata *conn = curr->ptr; 33013498266Sopenharmony_ci curr = curr->next; 33113498266Sopenharmony_ci 33213498266Sopenharmony_ci if(1 == func(data, conn, param)) { 33313498266Sopenharmony_ci CONNCACHE_UNLOCK(data); 33413498266Sopenharmony_ci return TRUE; 33513498266Sopenharmony_ci } 33613498266Sopenharmony_ci } 33713498266Sopenharmony_ci } 33813498266Sopenharmony_ci CONNCACHE_UNLOCK(data); 33913498266Sopenharmony_ci return FALSE; 34013498266Sopenharmony_ci} 34113498266Sopenharmony_ci 34213498266Sopenharmony_ci/* Return the first connection found in the cache. Used when closing all 34313498266Sopenharmony_ci connections. 34413498266Sopenharmony_ci 34513498266Sopenharmony_ci NOTE: no locking is done here as this is presumably only done when cleaning 34613498266Sopenharmony_ci up a cache! 34713498266Sopenharmony_ci*/ 34813498266Sopenharmony_cistatic struct connectdata * 34913498266Sopenharmony_ciconncache_find_first_connection(struct conncache *connc) 35013498266Sopenharmony_ci{ 35113498266Sopenharmony_ci struct Curl_hash_iterator iter; 35213498266Sopenharmony_ci struct Curl_hash_element *he; 35313498266Sopenharmony_ci struct connectbundle *bundle; 35413498266Sopenharmony_ci 35513498266Sopenharmony_ci Curl_hash_start_iterate(&connc->hash, &iter); 35613498266Sopenharmony_ci 35713498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 35813498266Sopenharmony_ci while(he) { 35913498266Sopenharmony_ci struct Curl_llist_element *curr; 36013498266Sopenharmony_ci bundle = he->ptr; 36113498266Sopenharmony_ci 36213498266Sopenharmony_ci curr = bundle->conn_list.head; 36313498266Sopenharmony_ci if(curr) { 36413498266Sopenharmony_ci return curr->ptr; 36513498266Sopenharmony_ci } 36613498266Sopenharmony_ci 36713498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 36813498266Sopenharmony_ci } 36913498266Sopenharmony_ci 37013498266Sopenharmony_ci return NULL; 37113498266Sopenharmony_ci} 37213498266Sopenharmony_ci 37313498266Sopenharmony_ci/* 37413498266Sopenharmony_ci * Give ownership of a connection back to the connection cache. Might 37513498266Sopenharmony_ci * disconnect the oldest existing in there to make space. 37613498266Sopenharmony_ci * 37713498266Sopenharmony_ci * Return TRUE if stored, FALSE if closed. 37813498266Sopenharmony_ci */ 37913498266Sopenharmony_cibool Curl_conncache_return_conn(struct Curl_easy *data, 38013498266Sopenharmony_ci struct connectdata *conn) 38113498266Sopenharmony_ci{ 38213498266Sopenharmony_ci unsigned int maxconnects = !data->multi->maxconnects ? 38313498266Sopenharmony_ci data->multi->num_easy * 4: data->multi->maxconnects; 38413498266Sopenharmony_ci struct connectdata *conn_candidate = NULL; 38513498266Sopenharmony_ci 38613498266Sopenharmony_ci conn->lastused = Curl_now(); /* it was used up until now */ 38713498266Sopenharmony_ci if(maxconnects && Curl_conncache_size(data) > maxconnects) { 38813498266Sopenharmony_ci infof(data, "Connection cache is full, closing the oldest one"); 38913498266Sopenharmony_ci 39013498266Sopenharmony_ci conn_candidate = Curl_conncache_extract_oldest(data); 39113498266Sopenharmony_ci if(conn_candidate) { 39213498266Sopenharmony_ci /* Use the closure handle for this disconnect so that anything that 39313498266Sopenharmony_ci happens during the disconnect is not stored and associated with the 39413498266Sopenharmony_ci 'data' handle which already just finished a transfer and it is 39513498266Sopenharmony_ci important that details from this (unrelated) disconnect does not 39613498266Sopenharmony_ci taint meta-data in the data handle. */ 39713498266Sopenharmony_ci struct conncache *connc = data->state.conn_cache; 39813498266Sopenharmony_ci connc->closure_handle->state.buffer = data->state.buffer; 39913498266Sopenharmony_ci connc->closure_handle->set.buffer_size = data->set.buffer_size; 40013498266Sopenharmony_ci Curl_disconnect(connc->closure_handle, conn_candidate, 40113498266Sopenharmony_ci /* dead_connection */ FALSE); 40213498266Sopenharmony_ci } 40313498266Sopenharmony_ci } 40413498266Sopenharmony_ci 40513498266Sopenharmony_ci return (conn_candidate == conn) ? FALSE : TRUE; 40613498266Sopenharmony_ci 40713498266Sopenharmony_ci} 40813498266Sopenharmony_ci 40913498266Sopenharmony_ci/* 41013498266Sopenharmony_ci * This function finds the connection in the connection bundle that has been 41113498266Sopenharmony_ci * unused for the longest time. 41213498266Sopenharmony_ci * 41313498266Sopenharmony_ci * Does not lock the connection cache! 41413498266Sopenharmony_ci * 41513498266Sopenharmony_ci * Returns the pointer to the oldest idle connection, or NULL if none was 41613498266Sopenharmony_ci * found. 41713498266Sopenharmony_ci */ 41813498266Sopenharmony_cistruct connectdata * 41913498266Sopenharmony_ciCurl_conncache_extract_bundle(struct Curl_easy *data, 42013498266Sopenharmony_ci struct connectbundle *bundle) 42113498266Sopenharmony_ci{ 42213498266Sopenharmony_ci struct Curl_llist_element *curr; 42313498266Sopenharmony_ci timediff_t highscore = -1; 42413498266Sopenharmony_ci timediff_t score; 42513498266Sopenharmony_ci struct curltime now; 42613498266Sopenharmony_ci struct connectdata *conn_candidate = NULL; 42713498266Sopenharmony_ci struct connectdata *conn; 42813498266Sopenharmony_ci 42913498266Sopenharmony_ci (void)data; 43013498266Sopenharmony_ci 43113498266Sopenharmony_ci now = Curl_now(); 43213498266Sopenharmony_ci 43313498266Sopenharmony_ci curr = bundle->conn_list.head; 43413498266Sopenharmony_ci while(curr) { 43513498266Sopenharmony_ci conn = curr->ptr; 43613498266Sopenharmony_ci 43713498266Sopenharmony_ci if(!CONN_INUSE(conn)) { 43813498266Sopenharmony_ci /* Set higher score for the age passed since the connection was used */ 43913498266Sopenharmony_ci score = Curl_timediff(now, conn->lastused); 44013498266Sopenharmony_ci 44113498266Sopenharmony_ci if(score > highscore) { 44213498266Sopenharmony_ci highscore = score; 44313498266Sopenharmony_ci conn_candidate = conn; 44413498266Sopenharmony_ci } 44513498266Sopenharmony_ci } 44613498266Sopenharmony_ci curr = curr->next; 44713498266Sopenharmony_ci } 44813498266Sopenharmony_ci if(conn_candidate) { 44913498266Sopenharmony_ci /* remove it to prevent another thread from nicking it */ 45013498266Sopenharmony_ci bundle_remove_conn(bundle, conn_candidate); 45113498266Sopenharmony_ci data->state.conn_cache->num_conn--; 45213498266Sopenharmony_ci DEBUGF(infof(data, "The cache now contains %zu members", 45313498266Sopenharmony_ci data->state.conn_cache->num_conn)); 45413498266Sopenharmony_ci } 45513498266Sopenharmony_ci 45613498266Sopenharmony_ci return conn_candidate; 45713498266Sopenharmony_ci} 45813498266Sopenharmony_ci 45913498266Sopenharmony_ci/* 46013498266Sopenharmony_ci * This function finds the connection in the connection cache that has been 46113498266Sopenharmony_ci * unused for the longest time and extracts that from the bundle. 46213498266Sopenharmony_ci * 46313498266Sopenharmony_ci * Returns the pointer to the connection, or NULL if none was found. 46413498266Sopenharmony_ci */ 46513498266Sopenharmony_cistruct connectdata * 46613498266Sopenharmony_ciCurl_conncache_extract_oldest(struct Curl_easy *data) 46713498266Sopenharmony_ci{ 46813498266Sopenharmony_ci struct conncache *connc = data->state.conn_cache; 46913498266Sopenharmony_ci struct Curl_hash_iterator iter; 47013498266Sopenharmony_ci struct Curl_llist_element *curr; 47113498266Sopenharmony_ci struct Curl_hash_element *he; 47213498266Sopenharmony_ci timediff_t highscore =- 1; 47313498266Sopenharmony_ci timediff_t score; 47413498266Sopenharmony_ci struct curltime now; 47513498266Sopenharmony_ci struct connectdata *conn_candidate = NULL; 47613498266Sopenharmony_ci struct connectbundle *bundle; 47713498266Sopenharmony_ci struct connectbundle *bundle_candidate = NULL; 47813498266Sopenharmony_ci 47913498266Sopenharmony_ci now = Curl_now(); 48013498266Sopenharmony_ci 48113498266Sopenharmony_ci CONNCACHE_LOCK(data); 48213498266Sopenharmony_ci Curl_hash_start_iterate(&connc->hash, &iter); 48313498266Sopenharmony_ci 48413498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 48513498266Sopenharmony_ci while(he) { 48613498266Sopenharmony_ci struct connectdata *conn; 48713498266Sopenharmony_ci 48813498266Sopenharmony_ci bundle = he->ptr; 48913498266Sopenharmony_ci 49013498266Sopenharmony_ci curr = bundle->conn_list.head; 49113498266Sopenharmony_ci while(curr) { 49213498266Sopenharmony_ci conn = curr->ptr; 49313498266Sopenharmony_ci 49413498266Sopenharmony_ci if(!CONN_INUSE(conn) && !conn->bits.close && 49513498266Sopenharmony_ci !conn->connect_only) { 49613498266Sopenharmony_ci /* Set higher score for the age passed since the connection was used */ 49713498266Sopenharmony_ci score = Curl_timediff(now, conn->lastused); 49813498266Sopenharmony_ci 49913498266Sopenharmony_ci if(score > highscore) { 50013498266Sopenharmony_ci highscore = score; 50113498266Sopenharmony_ci conn_candidate = conn; 50213498266Sopenharmony_ci bundle_candidate = bundle; 50313498266Sopenharmony_ci } 50413498266Sopenharmony_ci } 50513498266Sopenharmony_ci curr = curr->next; 50613498266Sopenharmony_ci } 50713498266Sopenharmony_ci 50813498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 50913498266Sopenharmony_ci } 51013498266Sopenharmony_ci if(conn_candidate) { 51113498266Sopenharmony_ci /* remove it to prevent another thread from nicking it */ 51213498266Sopenharmony_ci bundle_remove_conn(bundle_candidate, conn_candidate); 51313498266Sopenharmony_ci connc->num_conn--; 51413498266Sopenharmony_ci DEBUGF(infof(data, "The cache now contains %zu members", 51513498266Sopenharmony_ci connc->num_conn)); 51613498266Sopenharmony_ci } 51713498266Sopenharmony_ci CONNCACHE_UNLOCK(data); 51813498266Sopenharmony_ci 51913498266Sopenharmony_ci return conn_candidate; 52013498266Sopenharmony_ci} 52113498266Sopenharmony_ci 52213498266Sopenharmony_civoid Curl_conncache_close_all_connections(struct conncache *connc) 52313498266Sopenharmony_ci{ 52413498266Sopenharmony_ci struct connectdata *conn; 52513498266Sopenharmony_ci char buffer[READBUFFER_MIN + 1]; 52613498266Sopenharmony_ci SIGPIPE_VARIABLE(pipe_st); 52713498266Sopenharmony_ci if(!connc->closure_handle) 52813498266Sopenharmony_ci return; 52913498266Sopenharmony_ci connc->closure_handle->state.buffer = buffer; 53013498266Sopenharmony_ci connc->closure_handle->set.buffer_size = READBUFFER_MIN; 53113498266Sopenharmony_ci 53213498266Sopenharmony_ci conn = conncache_find_first_connection(connc); 53313498266Sopenharmony_ci while(conn) { 53413498266Sopenharmony_ci sigpipe_ignore(connc->closure_handle, &pipe_st); 53513498266Sopenharmony_ci /* This will remove the connection from the cache */ 53613498266Sopenharmony_ci connclose(conn, "kill all"); 53713498266Sopenharmony_ci Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE); 53813498266Sopenharmony_ci Curl_disconnect(connc->closure_handle, conn, FALSE); 53913498266Sopenharmony_ci sigpipe_restore(&pipe_st); 54013498266Sopenharmony_ci 54113498266Sopenharmony_ci conn = conncache_find_first_connection(connc); 54213498266Sopenharmony_ci } 54313498266Sopenharmony_ci 54413498266Sopenharmony_ci connc->closure_handle->state.buffer = NULL; 54513498266Sopenharmony_ci sigpipe_ignore(connc->closure_handle, &pipe_st); 54613498266Sopenharmony_ci 54713498266Sopenharmony_ci Curl_hostcache_clean(connc->closure_handle, 54813498266Sopenharmony_ci connc->closure_handle->dns.hostcache); 54913498266Sopenharmony_ci Curl_close(&connc->closure_handle); 55013498266Sopenharmony_ci sigpipe_restore(&pipe_st); 55113498266Sopenharmony_ci} 55213498266Sopenharmony_ci 55313498266Sopenharmony_ci#if 0 55413498266Sopenharmony_ci/* Useful for debugging the connection cache */ 55513498266Sopenharmony_civoid Curl_conncache_print(struct conncache *connc) 55613498266Sopenharmony_ci{ 55713498266Sopenharmony_ci struct Curl_hash_iterator iter; 55813498266Sopenharmony_ci struct Curl_llist_element *curr; 55913498266Sopenharmony_ci struct Curl_hash_element *he; 56013498266Sopenharmony_ci 56113498266Sopenharmony_ci if(!connc) 56213498266Sopenharmony_ci return; 56313498266Sopenharmony_ci 56413498266Sopenharmony_ci fprintf(stderr, "=Bundle cache=\n"); 56513498266Sopenharmony_ci 56613498266Sopenharmony_ci Curl_hash_start_iterate(connc->hash, &iter); 56713498266Sopenharmony_ci 56813498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 56913498266Sopenharmony_ci while(he) { 57013498266Sopenharmony_ci struct connectbundle *bundle; 57113498266Sopenharmony_ci struct connectdata *conn; 57213498266Sopenharmony_ci 57313498266Sopenharmony_ci bundle = he->ptr; 57413498266Sopenharmony_ci 57513498266Sopenharmony_ci fprintf(stderr, "%s -", he->key); 57613498266Sopenharmony_ci curr = bundle->conn_list->head; 57713498266Sopenharmony_ci while(curr) { 57813498266Sopenharmony_ci conn = curr->ptr; 57913498266Sopenharmony_ci 58013498266Sopenharmony_ci fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse); 58113498266Sopenharmony_ci curr = curr->next; 58213498266Sopenharmony_ci } 58313498266Sopenharmony_ci fprintf(stderr, "\n"); 58413498266Sopenharmony_ci 58513498266Sopenharmony_ci he = Curl_hash_next_element(&iter); 58613498266Sopenharmony_ci } 58713498266Sopenharmony_ci} 58813498266Sopenharmony_ci#endif 589