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));
305 void Curl_conncache_remove_conn(struct connectdata *conn, bool lock)
307 struct Curl_easy *data = conn->data;
308 struct connectbundle *bundle = conn->bundle;
309 struct conncache *connc = data->state.conn_cache;
311 /* The bundle pointer can be NULL, since this function can be called
312 due to a failed connection attempt, before being added to a bundle */
317 bundle_remove_conn(bundle, conn);
318 if(bundle->num_connections == 0)
319 conncache_remove_bundle(connc, bundle);
320 conn->bundle = NULL; /* removed from it */
323 DEBUGF(infof(data, "The cache now contains %zu members\n",
332 /* This function iterates the entire connection cache and calls the function
333 func() with the connection pointer as the first argument and the supplied
334 'param' argument as the other.
336 The conncache lock is still held when the callback is called. It needs it,
337 so that it can safely continue traversing the lists once the callback
340 Returns 1 if the loop was aborted due to the callback's return code.
342 Return 0 from func() to continue the loop, return 1 to abort it.
344 bool Curl_conncache_foreach(struct Curl_easy *data,
345 struct conncache *connc,
347 int (*func)(struct connectdata *conn, void *param))
349 struct curl_hash_iterator iter;
350 struct curl_llist_element *curr;
351 struct curl_hash_element *he;
357 Curl_hash_start_iterate(&connc->hash, &iter);
359 he = Curl_hash_next_element(&iter);
361 struct connectbundle *bundle;
364 he = Curl_hash_next_element(&iter);
366 curr = bundle->conn_list.head;
368 /* Yes, we need to update curr before calling func(), because func()
369 might decide to remove the connection */
370 struct connectdata *conn = curr->ptr;
373 if(1 == func(conn, param)) {
383 /* Return the first connection found in the cache. Used when closing all
386 NOTE: no locking is done here as this is presumably only done when cleaning
390 Curl_conncache_find_first_connection(struct conncache *connc)
392 struct curl_hash_iterator iter;
393 struct curl_hash_element *he;
394 struct connectbundle *bundle;
396 Curl_hash_start_iterate(&connc->hash, &iter);
398 he = Curl_hash_next_element(&iter);
400 struct curl_llist_element *curr;
403 curr = bundle->conn_list.head;
408 he = Curl_hash_next_element(&iter);
415 * Give ownership of a connection back to the connection cache. Might
416 * disconnect the oldest existing in there to make space.
418 * Return TRUE if stored, FALSE if closed.
420 bool Curl_conncache_return_conn(struct connectdata *conn)
422 struct Curl_easy *data = conn->data;
424 /* data->multi->maxconnects can be negative, deal with it. */
426 (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
427 data->multi->maxconnects;
428 struct connectdata *conn_candidate = NULL;
430 if(maxconnects > 0 &&
431 Curl_conncache_size(data) > maxconnects) {
432 infof(data, "Connection cache is full, closing the oldest one.\n");
434 conn_candidate = Curl_conncache_extract_oldest(data);
436 /* the winner gets the honour of being disconnected */
437 (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE);
441 return (conn_candidate == conn) ? FALSE : TRUE;
446 * This function finds the connection in the connection bundle that has been
447 * unused for the longest time.
449 * Does not lock the connection cache!
451 * Returns the pointer to the oldest idle connection, or NULL if none was
455 Curl_conncache_extract_bundle(struct Curl_easy *data,
456 struct connectbundle *bundle)
458 struct curl_llist_element *curr;
459 timediff_t highscore = -1;
462 struct connectdata *conn_candidate = NULL;
463 struct connectdata *conn;
469 curr = bundle->conn_list.head;
473 if(!CONN_INUSE(conn)) {
474 /* Set higher score for the age passed since the connection was used */
475 score = Curl_timediff(now, conn->now);
477 if(score > highscore) {
479 conn_candidate = conn;
485 /* remove it to prevent another thread from nicking it */
486 bundle_remove_conn(bundle, conn_candidate);
487 data->state.conn_cache->num_conn--;
488 DEBUGF(infof(data, "The cache now contains %zu members\n",
489 data->state.conn_cache->num_conn));
490 conn_candidate->data = data; /* associate! */
493 return conn_candidate;
497 * This function finds the connection in the connection cache that has been
498 * unused for the longest time and extracts that from the bundle.
500 * Returns the pointer to the connection, or NULL if none was found.
503 Curl_conncache_extract_oldest(struct Curl_easy *data)
505 struct conncache *connc = data->state.conn_cache;
506 struct curl_hash_iterator iter;
507 struct curl_llist_element *curr;
508 struct curl_hash_element *he;
509 timediff_t highscore =- 1;
512 struct connectdata *conn_candidate = NULL;
513 struct connectbundle *bundle;
514 struct connectbundle *bundle_candidate = NULL;
519 Curl_hash_start_iterate(&connc->hash, &iter);
521 he = Curl_hash_next_element(&iter);
523 struct connectdata *conn;
527 curr = bundle->conn_list.head;
531 if(!CONN_INUSE(conn)) {
532 /* Set higher score for the age passed since the connection was used */
533 score = Curl_timediff(now, conn->now);
535 if(score > highscore) {
537 conn_candidate = conn;
538 bundle_candidate = bundle;
544 he = Curl_hash_next_element(&iter);
547 /* remove it to prevent another thread from nicking it */
548 bundle_remove_conn(bundle_candidate, conn_candidate);
550 DEBUGF(infof(data, "The cache now contains %zu members\n",
552 conn_candidate->data = data; /* associate! */
556 return conn_candidate;
559 void Curl_conncache_close_all_connections(struct conncache *connc)
561 struct connectdata *conn;
563 conn = Curl_conncache_find_first_connection(connc);
565 SIGPIPE_VARIABLE(pipe_st);
566 conn->data = connc->closure_handle;
568 sigpipe_ignore(conn->data, &pipe_st);
569 conn->data->easy_conn = NULL; /* clear the easy handle's connection
571 /* This will remove the connection from the cache */
572 connclose(conn, "kill all");
573 (void)Curl_disconnect(connc->closure_handle, conn, FALSE);
574 sigpipe_restore(&pipe_st);
576 conn = Curl_conncache_find_first_connection(connc);
579 if(connc->closure_handle) {
580 SIGPIPE_VARIABLE(pipe_st);
581 sigpipe_ignore(connc->closure_handle, &pipe_st);
583 Curl_hostcache_clean(connc->closure_handle,
584 connc->closure_handle->dns.hostcache);
585 Curl_close(connc->closure_handle);
586 sigpipe_restore(&pipe_st);
591 /* Useful for debugging the connection cache */
592 void Curl_conncache_print(struct conncache *connc)
594 struct curl_hash_iterator iter;
595 struct curl_llist_element *curr;
596 struct curl_hash_element *he;
601 fprintf(stderr, "=Bundle cache=\n");
603 Curl_hash_start_iterate(connc->hash, &iter);
605 he = Curl_hash_next_element(&iter);
607 struct connectbundle *bundle;
608 struct connectdata *conn;
612 fprintf(stderr, "%s -", he->key);
613 curr = bundle->conn_list->head;
617 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
620 fprintf(stderr, "\n");
622 he = Curl_hash_next_element(&iter);