1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012 - 2018, 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"
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; \
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); \
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)
64 static void conn_llist_dtor(void *user, void *element)
66 struct connectdata *conn = element;
71 static CURLcode bundle_create(struct Curl_easy *data,
72 struct connectbundle **cb_ptr)
75 DEBUGASSERT(*cb_ptr == NULL);
76 *cb_ptr = malloc(sizeof(struct connectbundle));
78 return CURLE_OUT_OF_MEMORY;
80 (*cb_ptr)->num_connections = 0;
81 (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
83 Curl_llist_init(&(*cb_ptr)->conn_list, (curl_llist_dtor) conn_llist_dtor);
87 static void bundle_destroy(struct connectbundle *cb_ptr)
92 Curl_llist_destroy(&cb_ptr->conn_list, NULL);
97 /* Add a connection to a bundle */
98 static void bundle_add_conn(struct connectbundle *cb_ptr,
99 struct connectdata *conn)
101 Curl_llist_insert_next(&cb_ptr->conn_list, cb_ptr->conn_list.tail, conn,
103 conn->bundle = cb_ptr;
104 cb_ptr->num_connections++;
107 /* Remove a connection from a bundle */
108 static int bundle_remove_conn(struct connectbundle *cb_ptr,
109 struct connectdata *conn)
111 struct curl_llist_element *curr;
113 curr = cb_ptr->conn_list.head;
115 if(curr->ptr == conn) {
116 Curl_llist_remove(&cb_ptr->conn_list, curr, NULL);
117 cb_ptr->num_connections--;
119 return 1; /* we removed a handle */
126 static void free_bundle_hash_entry(void *freethis)
128 struct connectbundle *b = (struct connectbundle *) freethis;
133 int Curl_conncache_init(struct conncache *connc, int size)
137 /* allocate a new easy handle to use when closing cached connections */
138 connc->closure_handle = curl_easy_init();
139 if(!connc->closure_handle)
142 rc = Curl_hash_init(&connc->hash, size, Curl_hash_str,
143 Curl_str_key_compare, free_bundle_hash_entry);
145 Curl_close(connc->closure_handle);
146 connc->closure_handle = NULL;
149 connc->closure_handle->state.conn_cache = connc;
154 void Curl_conncache_destroy(struct conncache *connc)
157 Curl_hash_destroy(&connc->hash);
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 */
164 const char *hostname;
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;
173 hostname = conn->host.name;
175 DEBUGASSERT(len > 32);
177 /* put the number first so that the hostname gets cut off if too long */
178 snprintf(buf, len, "%ld%s", conn->port, hostname);
181 void Curl_conncache_unlock(struct connectdata *conn)
183 CONN_UNLOCK(conn->data);
186 /* Returns number of connections currently held in the connection cache.
187 Locks/unlocks the cache itself!
189 size_t Curl_conncache_size(struct Curl_easy *data)
193 num = data->state.conn_cache->num_conn;
198 /* Returns number of connections currently held in the connections's bundle
199 Locks/unlocks the cache itself!
201 size_t Curl_conncache_bundle_size(struct connectdata *conn)
204 CONN_LOCK(conn->data);
205 num = conn->bundle->num_connections;
206 CONN_UNLOCK(conn->data);
210 /* Look up the bundle with all the connections to the same host this
211 connectdata struct is setup to use.
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)
217 struct connectbundle *bundle = NULL;
218 CONN_LOCK(conn->data);
221 hashkey(conn, key, sizeof(key));
222 bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
228 static bool conncache_add_bundle(struct conncache *connc,
230 struct connectbundle *bundle)
232 void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
237 static void conncache_remove_bundle(struct conncache *connc,
238 struct connectbundle *bundle)
240 struct curl_hash_iterator iter;
241 struct curl_hash_element *he;
246 Curl_hash_start_iterate(&connc->hash, &iter);
248 he = Curl_hash_next_element(&iter);
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);
257 he = Curl_hash_next_element(&iter);
261 CURLcode Curl_conncache_add_conn(struct conncache *connc,
262 struct connectdata *conn)
264 CURLcode result = CURLE_OK;
265 struct connectbundle *bundle;
266 struct connectbundle *new_bundle = NULL;
267 struct Curl_easy *data = conn->data;
269 /* *find_bundle() locks the connection cache */
270 bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
275 result = bundle_create(data, &new_bundle);
280 hashkey(conn, key, sizeof(key));
281 rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
284 bundle_destroy(new_bundle);
285 result = CURLE_OUT_OF_MEMORY;
291 bundle_add_conn(bundle, conn);
292 conn->connection_id = connc->next_connection_id++;
295 DEBUGF(infof(conn->data, "Added connection %ld. "
296 "The cache now contains %zu members\n",
297 conn->connection_id, connc->num_conn));
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.
310 void Curl_conncache_remove_conn(struct Curl_easy *data,
311 struct connectdata *conn, bool lock)
313 struct connectbundle *bundle = conn->bundle;
314 struct conncache *connc = data->state.conn_cache;
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 */
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 */
328 DEBUGF(infof(data, "The cache now contains %zu members\n",
331 conn->data = NULL; /* clear the association */
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.
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
346 Returns 1 if the loop was aborted due to the callback's return code.
348 Return 0 from func() to continue the loop, return 1 to abort it.
350 bool Curl_conncache_foreach(struct Curl_easy *data,
351 struct conncache *connc,
353 int (*func)(struct connectdata *conn, void *param))
355 struct curl_hash_iterator iter;
356 struct curl_llist_element *curr;
357 struct curl_hash_element *he;
363 Curl_hash_start_iterate(&connc->hash, &iter);
365 he = Curl_hash_next_element(&iter);
367 struct connectbundle *bundle;
370 he = Curl_hash_next_element(&iter);
372 curr = bundle->conn_list.head;
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;
379 if(1 == func(conn, param)) {
389 /* Return the first connection found in the cache. Used when closing all
392 NOTE: no locking is done here as this is presumably only done when cleaning
396 Curl_conncache_find_first_connection(struct conncache *connc)
398 struct curl_hash_iterator iter;
399 struct curl_hash_element *he;
400 struct connectbundle *bundle;
402 Curl_hash_start_iterate(&connc->hash, &iter);
404 he = Curl_hash_next_element(&iter);
406 struct curl_llist_element *curr;
409 curr = bundle->conn_list.head;
414 he = Curl_hash_next_element(&iter);
421 * Give ownership of a connection back to the connection cache. Might
422 * disconnect the oldest existing in there to make space.
424 * Return TRUE if stored, FALSE if closed.
426 bool Curl_conncache_return_conn(struct connectdata *conn)
428 struct Curl_easy *data = conn->data;
430 /* data->multi->maxconnects can be negative, deal with it. */
432 (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
433 data->multi->maxconnects;
434 struct connectdata *conn_candidate = NULL;
436 if(maxconnects > 0 &&
437 Curl_conncache_size(data) > maxconnects) {
438 infof(data, "Connection cache is full, closing the oldest one.\n");
440 conn_candidate = Curl_conncache_extract_oldest(data);
442 /* the winner gets the honour of being disconnected */
443 (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE);
447 return (conn_candidate == conn) ? FALSE : TRUE;
452 * This function finds the connection in the connection bundle that has been
453 * unused for the longest time.
455 * Does not lock the connection cache!
457 * Returns the pointer to the oldest idle connection, or NULL if none was
461 Curl_conncache_extract_bundle(struct Curl_easy *data,
462 struct connectbundle *bundle)
464 struct curl_llist_element *curr;
465 timediff_t highscore = -1;
468 struct connectdata *conn_candidate = NULL;
469 struct connectdata *conn;
475 curr = bundle->conn_list.head;
479 if(!CONN_INUSE(conn)) {
480 /* Set higher score for the age passed since the connection was used */
481 score = Curl_timediff(now, conn->now);
483 if(score > highscore) {
485 conn_candidate = conn;
491 /* remove it to prevent another thread from nicking it */
492 bundle_remove_conn(bundle, conn_candidate);
493 data->state.conn_cache->num_conn--;
494 DEBUGF(infof(data, "The cache now contains %zu members\n",
495 data->state.conn_cache->num_conn));
496 conn_candidate->data = data; /* associate! */
499 return conn_candidate;
503 * This function finds the connection in the connection cache that has been
504 * unused for the longest time and extracts that from the bundle.
506 * Returns the pointer to the connection, or NULL if none was found.
509 Curl_conncache_extract_oldest(struct Curl_easy *data)
511 struct conncache *connc = data->state.conn_cache;
512 struct curl_hash_iterator iter;
513 struct curl_llist_element *curr;
514 struct curl_hash_element *he;
515 timediff_t highscore =- 1;
518 struct connectdata *conn_candidate = NULL;
519 struct connectbundle *bundle;
520 struct connectbundle *bundle_candidate = NULL;
525 Curl_hash_start_iterate(&connc->hash, &iter);
527 he = Curl_hash_next_element(&iter);
529 struct connectdata *conn;
533 curr = bundle->conn_list.head;
537 if(!CONN_INUSE(conn)) {
538 /* Set higher score for the age passed since the connection was used */
539 score = Curl_timediff(now, conn->now);
541 if(score > highscore) {
543 conn_candidate = conn;
544 bundle_candidate = bundle;
550 he = Curl_hash_next_element(&iter);
553 /* remove it to prevent another thread from nicking it */
554 bundle_remove_conn(bundle_candidate, conn_candidate);
556 DEBUGF(infof(data, "The cache now contains %zu members\n",
558 conn_candidate->data = data; /* associate! */
562 return conn_candidate;
565 void Curl_conncache_close_all_connections(struct conncache *connc)
567 struct connectdata *conn;
569 conn = Curl_conncache_find_first_connection(connc);
571 SIGPIPE_VARIABLE(pipe_st);
572 conn->data = connc->closure_handle;
574 sigpipe_ignore(conn->data, &pipe_st);
575 conn->data->easy_conn = NULL; /* clear the easy handle's connection
577 /* This will remove the connection from the cache */
578 connclose(conn, "kill all");
579 (void)Curl_disconnect(connc->closure_handle, conn, FALSE);
580 sigpipe_restore(&pipe_st);
582 conn = Curl_conncache_find_first_connection(connc);
585 if(connc->closure_handle) {
586 SIGPIPE_VARIABLE(pipe_st);
587 sigpipe_ignore(connc->closure_handle, &pipe_st);
589 Curl_hostcache_clean(connc->closure_handle,
590 connc->closure_handle->dns.hostcache);
591 Curl_close(connc->closure_handle);
592 sigpipe_restore(&pipe_st);
597 /* Useful for debugging the connection cache */
598 void Curl_conncache_print(struct conncache *connc)
600 struct curl_hash_iterator iter;
601 struct curl_llist_element *curr;
602 struct curl_hash_element *he;
607 fprintf(stderr, "=Bundle cache=\n");
609 Curl_hash_start_iterate(connc->hash, &iter);
611 he = Curl_hash_next_element(&iter);
613 struct connectbundle *bundle;
614 struct connectdata *conn;
618 fprintf(stderr, "%s -", he->key);
619 curr = bundle->conn_list->head;
623 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
626 fprintf(stderr, "\n");
628 he = Curl_hash_next_element(&iter);