1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ***************************************************************************/
24 #include "curl_setup.h"
26 #include <curl/curl.h>
33 #include "conncache.h"
38 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
43 #define HASHKEY_SIZE 128
45 static void conn_llist_dtor(void *user, void *element)
47 struct connectdata *conn = element;
52 static CURLcode bundle_create(struct connectbundle **bundlep)
54 DEBUGASSERT(*bundlep == NULL);
55 *bundlep = malloc(sizeof(struct connectbundle));
57 return CURLE_OUT_OF_MEMORY;
59 (*bundlep)->num_connections = 0;
60 (*bundlep)->multiuse = BUNDLE_UNKNOWN;
62 Curl_llist_init(&(*bundlep)->conn_list, (Curl_llist_dtor) conn_llist_dtor);
66 static void bundle_destroy(struct connectbundle *bundle)
71 Curl_llist_destroy(&bundle->conn_list, NULL);
76 /* Add a connection to a bundle */
77 static void bundle_add_conn(struct connectbundle *bundle,
78 struct connectdata *conn)
80 Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn,
82 conn->bundle = bundle;
83 bundle->num_connections++;
86 /* Remove a connection from a bundle */
87 static int bundle_remove_conn(struct connectbundle *bundle,
88 struct connectdata *conn)
90 struct Curl_llist_element *curr;
92 curr = bundle->conn_list.head;
94 if(curr->ptr == conn) {
95 Curl_llist_remove(&bundle->conn_list, curr, NULL);
96 bundle->num_connections--;
98 return 1; /* we removed a handle */
106 static void free_bundle_hash_entry(void *freethis)
108 struct connectbundle *b = (struct connectbundle *) freethis;
113 int Curl_conncache_init(struct conncache *connc, int size)
117 /* allocate a new easy handle to use when closing cached connections */
118 connc->closure_handle = curl_easy_init();
119 if(!connc->closure_handle)
122 rc = Curl_hash_init(&connc->hash, size, Curl_hash_str,
123 Curl_str_key_compare, free_bundle_hash_entry);
125 Curl_close(&connc->closure_handle);
127 connc->closure_handle->state.conn_cache = connc;
132 void Curl_conncache_destroy(struct conncache *connc)
135 Curl_hash_destroy(&connc->hash);
138 /* creates a key to find a bundle for this connection */
139 static void hashkey(struct connectdata *conn, char *buf,
140 size_t len, /* something like 128 is fine */
143 const char *hostname;
144 long port = conn->remote_port;
146 #ifndef CURL_DISABLE_PROXY
147 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
148 hostname = conn->http_proxy.host.name;
153 if(conn->bits.conn_to_host)
154 hostname = conn->conn_to_host.name;
156 hostname = conn->host.name;
159 /* report back which name we used */
162 /* put the number first so that the hostname gets cut off if too long */
163 msnprintf(buf, len, "%ld%s", port, hostname);
166 /* Returns number of connections currently held in the connection cache.
167 Locks/unlocks the cache itself!
169 size_t Curl_conncache_size(struct Curl_easy *data)
172 CONNCACHE_LOCK(data);
173 num = data->state.conn_cache->num_conn;
174 CONNCACHE_UNLOCK(data);
178 /* Look up the bundle with all the connections to the same host this
179 connectdata struct is setup to use.
181 **NOTE**: When it returns, it holds the connection cache lock! */
182 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
183 struct conncache *connc,
186 struct connectbundle *bundle = NULL;
187 CONNCACHE_LOCK(conn->data);
189 char key[HASHKEY_SIZE];
190 hashkey(conn, key, sizeof(key), hostp);
191 bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
197 static bool conncache_add_bundle(struct conncache *connc,
199 struct connectbundle *bundle)
201 void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
206 static void conncache_remove_bundle(struct conncache *connc,
207 struct connectbundle *bundle)
209 struct Curl_hash_iterator iter;
210 struct Curl_hash_element *he;
215 Curl_hash_start_iterate(&connc->hash, &iter);
217 he = Curl_hash_next_element(&iter);
219 if(he->ptr == bundle) {
220 /* The bundle is destroyed by the hash destructor function,
221 free_bundle_hash_entry() */
222 Curl_hash_delete(&connc->hash, he->key, he->key_len);
226 he = Curl_hash_next_element(&iter);
230 CURLcode Curl_conncache_add_conn(struct conncache *connc,
231 struct connectdata *conn)
233 CURLcode result = CURLE_OK;
234 struct connectbundle *bundle = NULL;
235 struct Curl_easy *data = conn->data;
237 /* *find_bundle() locks the connection cache */
238 bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache, NULL);
241 char key[HASHKEY_SIZE];
243 result = bundle_create(&bundle);
248 hashkey(conn, key, sizeof(key), NULL);
249 rc = conncache_add_bundle(data->state.conn_cache, key, bundle);
252 bundle_destroy(bundle);
253 result = CURLE_OUT_OF_MEMORY;
258 bundle_add_conn(bundle, conn);
259 conn->connection_id = connc->next_connection_id++;
262 DEBUGF(infof(conn->data, "Added connection %ld. "
263 "The cache now contains %zu members\n",
264 conn->connection_id, connc->num_conn));
267 CONNCACHE_UNLOCK(data);
273 * Removes the connectdata object from the connection cache, but does *not*
274 * clear the conn->data association. The transfer still owns this connection.
276 * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function
277 * already holds the lock or not.
279 void Curl_conncache_remove_conn(struct Curl_easy *data,
280 struct connectdata *conn, bool lock)
282 struct connectbundle *bundle = conn->bundle;
283 struct conncache *connc = data->state.conn_cache;
285 /* The bundle pointer can be NULL, since this function can be called
286 due to a failed connection attempt, before being added to a bundle */
289 CONNCACHE_LOCK(data);
291 bundle_remove_conn(bundle, conn);
292 if(bundle->num_connections == 0)
293 conncache_remove_bundle(connc, bundle);
294 conn->bundle = NULL; /* removed from it */
297 DEBUGF(infof(data, "The cache now contains %zu members\n",
301 CONNCACHE_UNLOCK(data);
306 /* This function iterates the entire connection cache and calls the function
307 func() with the connection pointer as the first argument and the supplied
308 'param' argument as the other.
310 The conncache lock is still held when the callback is called. It needs it,
311 so that it can safely continue traversing the lists once the callback
314 Returns 1 if the loop was aborted due to the callback's return code.
316 Return 0 from func() to continue the loop, return 1 to abort it.
318 bool Curl_conncache_foreach(struct Curl_easy *data,
319 struct conncache *connc,
321 int (*func)(struct connectdata *conn, void *param))
323 struct Curl_hash_iterator iter;
324 struct Curl_llist_element *curr;
325 struct Curl_hash_element *he;
330 CONNCACHE_LOCK(data);
331 Curl_hash_start_iterate(&connc->hash, &iter);
333 he = Curl_hash_next_element(&iter);
335 struct connectbundle *bundle;
338 he = Curl_hash_next_element(&iter);
340 curr = bundle->conn_list.head;
342 /* Yes, we need to update curr before calling func(), because func()
343 might decide to remove the connection */
344 struct connectdata *conn = curr->ptr;
347 if(1 == func(conn, param)) {
348 CONNCACHE_UNLOCK(data);
353 CONNCACHE_UNLOCK(data);
357 /* Return the first connection found in the cache. Used when closing all
360 NOTE: no locking is done here as this is presumably only done when cleaning
363 static struct connectdata *
364 conncache_find_first_connection(struct conncache *connc)
366 struct Curl_hash_iterator iter;
367 struct Curl_hash_element *he;
368 struct connectbundle *bundle;
370 Curl_hash_start_iterate(&connc->hash, &iter);
372 he = Curl_hash_next_element(&iter);
374 struct Curl_llist_element *curr;
377 curr = bundle->conn_list.head;
382 he = Curl_hash_next_element(&iter);
389 * Give ownership of a connection back to the connection cache. Might
390 * disconnect the oldest existing in there to make space.
392 * Return TRUE if stored, FALSE if closed.
394 bool Curl_conncache_return_conn(struct Curl_easy *data,
395 struct connectdata *conn)
397 /* data->multi->maxconnects can be negative, deal with it. */
399 (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
400 data->multi->maxconnects;
401 struct connectdata *conn_candidate = NULL;
403 conn->lastused = Curl_now(); /* it was used up until now */
404 if(maxconnects > 0 &&
405 Curl_conncache_size(data) > maxconnects) {
406 infof(data, "Connection cache is full, closing the oldest one.\n");
408 conn_candidate = Curl_conncache_extract_oldest(data);
410 /* the winner gets the honour of being disconnected */
411 (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE);
415 return (conn_candidate == conn) ? FALSE : TRUE;
420 * This function finds the connection in the connection bundle that has been
421 * unused for the longest time.
423 * Does not lock the connection cache!
425 * Returns the pointer to the oldest idle connection, or NULL if none was
429 Curl_conncache_extract_bundle(struct Curl_easy *data,
430 struct connectbundle *bundle)
432 struct Curl_llist_element *curr;
433 timediff_t highscore = -1;
436 struct connectdata *conn_candidate = NULL;
437 struct connectdata *conn;
443 curr = bundle->conn_list.head;
447 if(!CONN_INUSE(conn) && !conn->data) {
448 /* Set higher score for the age passed since the connection was used */
449 score = Curl_timediff(now, conn->lastused);
451 if(score > highscore) {
453 conn_candidate = conn;
459 /* remove it to prevent another thread from nicking it */
460 bundle_remove_conn(bundle, conn_candidate);
461 data->state.conn_cache->num_conn--;
462 DEBUGF(infof(data, "The cache now contains %zu members\n",
463 data->state.conn_cache->num_conn));
464 conn_candidate->data = data; /* associate! */
467 return conn_candidate;
471 * This function finds the connection in the connection cache that has been
472 * unused for the longest time and extracts that from the bundle.
474 * Returns the pointer to the connection, or NULL if none was found.
477 Curl_conncache_extract_oldest(struct Curl_easy *data)
479 struct conncache *connc = data->state.conn_cache;
480 struct Curl_hash_iterator iter;
481 struct Curl_llist_element *curr;
482 struct Curl_hash_element *he;
483 timediff_t highscore =- 1;
486 struct connectdata *conn_candidate = NULL;
487 struct connectbundle *bundle;
488 struct connectbundle *bundle_candidate = NULL;
492 CONNCACHE_LOCK(data);
493 Curl_hash_start_iterate(&connc->hash, &iter);
495 he = Curl_hash_next_element(&iter);
497 struct connectdata *conn;
501 curr = bundle->conn_list.head;
505 if(!CONN_INUSE(conn) && !conn->data && !conn->bits.close &&
506 !conn->bits.connect_only) {
507 /* Set higher score for the age passed since the connection was used */
508 score = Curl_timediff(now, conn->lastused);
510 if(score > highscore) {
512 conn_candidate = conn;
513 bundle_candidate = bundle;
519 he = Curl_hash_next_element(&iter);
522 /* remove it to prevent another thread from nicking it */
523 bundle_remove_conn(bundle_candidate, conn_candidate);
525 DEBUGF(infof(data, "The cache now contains %zu members\n",
527 conn_candidate->data = data; /* associate! */
529 CONNCACHE_UNLOCK(data);
531 return conn_candidate;
534 void Curl_conncache_close_all_connections(struct conncache *connc)
536 struct connectdata *conn;
537 char buffer[READBUFFER_MIN + 1];
538 if(!connc->closure_handle)
540 connc->closure_handle->state.buffer = buffer;
541 connc->closure_handle->set.buffer_size = READBUFFER_MIN;
543 conn = conncache_find_first_connection(connc);
545 SIGPIPE_VARIABLE(pipe_st);
546 conn->data = connc->closure_handle;
548 sigpipe_ignore(conn->data, &pipe_st);
549 /* This will remove the connection from the cache */
550 connclose(conn, "kill all");
551 Curl_conncache_remove_conn(conn->data, conn, TRUE);
552 (void)Curl_disconnect(connc->closure_handle, conn, FALSE);
553 sigpipe_restore(&pipe_st);
555 conn = conncache_find_first_connection(connc);
558 connc->closure_handle->state.buffer = NULL;
559 if(connc->closure_handle) {
560 SIGPIPE_VARIABLE(pipe_st);
561 sigpipe_ignore(connc->closure_handle, &pipe_st);
563 Curl_hostcache_clean(connc->closure_handle,
564 connc->closure_handle->dns.hostcache);
565 Curl_close(&connc->closure_handle);
566 sigpipe_restore(&pipe_st);
571 /* Useful for debugging the connection cache */
572 void Curl_conncache_print(struct conncache *connc)
574 struct Curl_hash_iterator iter;
575 struct Curl_llist_element *curr;
576 struct Curl_hash_element *he;
581 fprintf(stderr, "=Bundle cache=\n");
583 Curl_hash_start_iterate(connc->hash, &iter);
585 he = Curl_hash_next_element(&iter);
587 struct connectbundle *bundle;
588 struct connectdata *conn;
592 fprintf(stderr, "%s -", he->key);
593 curr = bundle->conn_list->head;
597 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
600 fprintf(stderr, "\n");
602 he = Curl_hash_next_element(&iter);