1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012 - 2017, 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 *data = element;
72 static CURLcode bundle_create(struct Curl_easy *data,
73 struct connectbundle **cb_ptr)
76 DEBUGASSERT(*cb_ptr == NULL);
77 *cb_ptr = malloc(sizeof(struct connectbundle));
79 return CURLE_OUT_OF_MEMORY;
81 (*cb_ptr)->num_connections = 0;
82 (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
84 Curl_llist_init(&(*cb_ptr)->conn_list, (curl_llist_dtor) conn_llist_dtor);
88 static void bundle_destroy(struct connectbundle *cb_ptr)
93 Curl_llist_destroy(&cb_ptr->conn_list, NULL);
98 /* Add a connection to a bundle */
99 static CURLcode bundle_add_conn(struct connectbundle *cb_ptr,
100 struct connectdata *conn)
102 Curl_llist_insert_next(&cb_ptr->conn_list, cb_ptr->conn_list.tail, conn,
104 conn->bundle = cb_ptr;
105 cb_ptr->num_connections++;
109 /* Remove a connection from a bundle */
110 static int bundle_remove_conn(struct connectbundle *cb_ptr,
111 struct connectdata *conn)
113 struct curl_llist_element *curr;
115 curr = cb_ptr->conn_list.head;
117 if(curr->ptr == conn) {
118 Curl_llist_remove(&cb_ptr->conn_list, curr, NULL);
119 cb_ptr->num_connections--;
121 return 1; /* we removed a handle */
128 static void free_bundle_hash_entry(void *freethis)
130 struct connectbundle *b = (struct connectbundle *) freethis;
135 int Curl_conncache_init(struct conncache *connc, int size)
139 /* allocate a new easy handle to use when closing cached connections */
140 connc->closure_handle = curl_easy_init();
141 if(!connc->closure_handle)
144 rc = Curl_hash_init(&connc->hash, size, Curl_hash_str,
145 Curl_str_key_compare, free_bundle_hash_entry);
147 Curl_close(connc->closure_handle);
148 connc->closure_handle = NULL;
151 connc->closure_handle->state.conn_cache = connc;
156 void Curl_conncache_destroy(struct conncache *connc)
159 Curl_hash_destroy(&connc->hash);
162 /* creates a key to find a bundle for this connection */
163 static void hashkey(struct connectdata *conn, char *buf,
164 size_t len) /* something like 128 is fine */
166 const char *hostname;
168 if(conn->bits.socksproxy)
169 hostname = conn->socks_proxy.host.name;
170 else if(conn->bits.httpproxy)
171 hostname = conn->http_proxy.host.name;
172 else if(conn->bits.conn_to_host)
173 hostname = conn->conn_to_host.name;
175 hostname = conn->host.name;
177 DEBUGASSERT(len > 32);
179 /* put the number first so that the hostname gets cut off if too long */
180 snprintf(buf, len, "%ld%s", conn->port, hostname);
183 void Curl_conncache_unlock(struct connectdata *conn)
185 CONN_UNLOCK(conn->data);
188 /* Returns number of connections currently held in the connection cache.
189 Locks/unlocks the cache itself!
191 size_t Curl_conncache_size(struct Curl_easy *data)
195 num = data->state.conn_cache->num_conn;
200 /* Returns number of connections currently held in the connections's bundle
201 Locks/unlocks the cache itself!
203 size_t Curl_conncache_bundle_size(struct connectdata *conn)
206 CONN_LOCK(conn->data);
207 num = conn->bundle->num_connections;
208 CONN_UNLOCK(conn->data);
212 /* Look up the bundle with all the connections to the same host this
213 connectdata struct is setup to use.
215 **NOTE**: When it returns, it holds the connection cache lock! */
216 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
217 struct conncache *connc)
219 struct connectbundle *bundle = NULL;
220 CONN_LOCK(conn->data);
223 hashkey(conn, key, sizeof(key));
224 bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
230 static bool conncache_add_bundle(struct conncache *connc,
232 struct connectbundle *bundle)
234 void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
239 static void conncache_remove_bundle(struct conncache *connc,
240 struct connectbundle *bundle)
242 struct curl_hash_iterator iter;
243 struct curl_hash_element *he;
248 Curl_hash_start_iterate(&connc->hash, &iter);
250 he = Curl_hash_next_element(&iter);
252 if(he->ptr == bundle) {
253 /* The bundle is destroyed by the hash destructor function,
254 free_bundle_hash_entry() */
255 Curl_hash_delete(&connc->hash, he->key, he->key_len);
259 he = Curl_hash_next_element(&iter);
263 CURLcode Curl_conncache_add_conn(struct conncache *connc,
264 struct connectdata *conn)
267 struct connectbundle *bundle;
268 struct connectbundle *new_bundle = NULL;
269 struct Curl_easy *data = conn->data;
271 /* *find_bundle() locks the connection cache */
272 bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
277 result = bundle_create(data, &new_bundle);
282 hashkey(conn, key, sizeof(key));
283 rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
286 bundle_destroy(new_bundle);
287 result = CURLE_OUT_OF_MEMORY;
293 result = bundle_add_conn(bundle, conn);
296 conncache_remove_bundle(data->state.conn_cache, new_bundle);
300 conn->connection_id = connc->next_connection_id++;
303 DEBUGF(infof(conn->data, "Added connection %ld. "
304 "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
305 conn->connection_id, (curl_off_t) connc->num_conn));
313 void Curl_conncache_remove_conn(struct connectdata *conn, bool lock)
315 struct Curl_easy *data = conn->data;
316 struct connectbundle *bundle = conn->bundle;
317 struct conncache *connc = data->state.conn_cache;
319 /* The bundle pointer can be NULL, since this function can be called
320 due to a failed connection attempt, before being added to a bundle */
323 CONN_LOCK(conn->data);
325 bundle_remove_conn(bundle, conn);
326 if(bundle->num_connections == 0)
327 conncache_remove_bundle(connc, bundle);
328 conn->bundle = NULL; /* removed from it */
331 DEBUGF(infof(conn->data, "The cache now contains %"
332 CURL_FORMAT_CURL_OFF_TU " members\n",
333 (curl_off_t) connc->num_conn));
336 CONN_UNLOCK(conn->data);
341 /* This function iterates the entire connection cache and calls the function
342 func() with the connection pointer as the first argument and the supplied
343 'param' argument as the other.
345 The conncache lock is still held when the callback is called. It needs it,
346 so that it can safely continue traversing the lists once the callback
349 Returns 1 if the loop was aborted due to the callback's return code.
351 Return 0 from func() to continue the loop, return 1 to abort it.
353 bool Curl_conncache_foreach(struct Curl_easy *data,
354 struct conncache *connc,
356 int (*func)(struct connectdata *conn, void *param))
358 struct curl_hash_iterator iter;
359 struct curl_llist_element *curr;
360 struct curl_hash_element *he;
366 Curl_hash_start_iterate(&connc->hash, &iter);
368 he = Curl_hash_next_element(&iter);
370 struct connectbundle *bundle;
373 he = Curl_hash_next_element(&iter);
375 curr = bundle->conn_list.head;
377 /* Yes, we need to update curr before calling func(), because func()
378 might decide to remove the connection */
379 struct connectdata *conn = curr->ptr;
382 if(1 == func(conn, param)) {
392 /* Return the first connection found in the cache. Used when closing all
395 NOTE: no locking is done here as this is presumably only done when cleaning
399 Curl_conncache_find_first_connection(struct conncache *connc)
401 struct curl_hash_iterator iter;
402 struct curl_hash_element *he;
403 struct connectbundle *bundle;
405 Curl_hash_start_iterate(&connc->hash, &iter);
407 he = Curl_hash_next_element(&iter);
409 struct curl_llist_element *curr;
412 curr = bundle->conn_list.head;
417 he = Curl_hash_next_element(&iter);
424 * Give ownership of a connection back to the connection cache. Might
425 * disconnect the oldest existing in there to make space.
427 * Return TRUE if stored, FALSE if closed.
429 bool Curl_conncache_return_conn(struct connectdata *conn)
431 struct Curl_easy *data = conn->data;
433 /* data->multi->maxconnects can be negative, deal with it. */
435 (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
436 data->multi->maxconnects;
437 struct connectdata *conn_candidate = NULL;
439 if(maxconnects > 0 &&
440 Curl_conncache_size(data) > maxconnects) {
441 infof(data, "Connection cache is full, closing the oldest one.\n");
443 conn_candidate = Curl_conncache_extract_oldest(data);
446 /* Set the connection's owner correctly */
447 conn_candidate->data = data;
449 /* the winner gets the honour of being disconnected */
450 (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
454 conn->inuse = FALSE; /* Mark the connection unused */
457 return (conn_candidate == conn) ? FALSE : TRUE;
462 * This function finds the connection in the connection bundle that has been
463 * unused for the longest time.
465 * Does not lock the connection cache!
467 * Returns the pointer to the oldest idle connection, or NULL if none was
471 Curl_conncache_extract_bundle(struct Curl_easy *data,
472 struct connectbundle *bundle)
474 struct curl_llist_element *curr;
475 timediff_t highscore = -1;
478 struct connectdata *conn_candidate = NULL;
479 struct connectdata *conn;
485 curr = bundle->conn_list.head;
490 /* Set higher score for the age passed since the connection was used */
491 score = Curl_timediff(now, conn->now);
493 if(score > highscore) {
495 conn_candidate = conn;
501 /* remove it to prevent another thread from nicking it */
502 bundle_remove_conn(bundle, conn_candidate);
503 data->state.conn_cache->num_conn--;
504 DEBUGF(infof(data, "The cache now contains %"
505 CURL_FORMAT_CURL_OFF_TU " members\n",
506 (curl_off_t) data->state.conn_cache->num_conn));
509 return conn_candidate;
513 * This function finds the connection in the connection cache that has been
514 * unused for the longest time and extracts that from the bundle.
516 * Returns the pointer to the connection, or NULL if none was found.
519 Curl_conncache_extract_oldest(struct Curl_easy *data)
521 struct conncache *connc = data->state.conn_cache;
522 struct curl_hash_iterator iter;
523 struct curl_llist_element *curr;
524 struct curl_hash_element *he;
525 timediff_t highscore =- 1;
528 struct connectdata *conn_candidate = NULL;
529 struct connectbundle *bundle;
530 struct connectbundle *bundle_candidate = NULL;
535 Curl_hash_start_iterate(&connc->hash, &iter);
537 he = Curl_hash_next_element(&iter);
539 struct connectdata *conn;
543 curr = bundle->conn_list.head;
548 /* Set higher score for the age passed since the connection was used */
549 score = Curl_timediff(now, conn->now);
551 if(score > highscore) {
553 conn_candidate = conn;
554 bundle_candidate = bundle;
560 he = Curl_hash_next_element(&iter);
563 /* remove it to prevent another thread from nicking it */
564 bundle_remove_conn(bundle_candidate, conn_candidate);
566 DEBUGF(infof(data, "The cache now contains %"
567 CURL_FORMAT_CURL_OFF_TU " members\n",
568 (curl_off_t) connc->num_conn));
572 return conn_candidate;
575 void Curl_conncache_close_all_connections(struct conncache *connc)
577 struct connectdata *conn;
579 conn = Curl_conncache_find_first_connection(connc);
581 SIGPIPE_VARIABLE(pipe_st);
582 conn->data = connc->closure_handle;
584 sigpipe_ignore(conn->data, &pipe_st);
585 conn->data->easy_conn = NULL; /* clear the easy handle's connection
587 /* This will remove the connection from the cache */
588 connclose(conn, "kill all");
589 (void)Curl_disconnect(conn, FALSE);
590 sigpipe_restore(&pipe_st);
592 conn = Curl_conncache_find_first_connection(connc);
595 if(connc->closure_handle) {
596 SIGPIPE_VARIABLE(pipe_st);
597 sigpipe_ignore(connc->closure_handle, &pipe_st);
599 Curl_hostcache_clean(connc->closure_handle,
600 connc->closure_handle->dns.hostcache);
601 Curl_close(connc->closure_handle);
602 sigpipe_restore(&pipe_st);
607 /* Useful for debugging the connection cache */
608 void Curl_conncache_print(struct conncache *connc)
610 struct curl_hash_iterator iter;
611 struct curl_llist_element *curr;
612 struct curl_hash_element *he;
617 fprintf(stderr, "=Bundle cache=\n");
619 Curl_hash_start_iterate(connc->hash, &iter);
621 he = Curl_hash_next_element(&iter);
623 struct connectbundle *bundle;
624 struct connectdata *conn;
628 fprintf(stderr, "%s -", he->key);
629 curr = bundle->conn_list->head;
633 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
636 fprintf(stderr, "\n");
638 he = Curl_hash_next_element(&iter);