1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012 - 2015, 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>
34 #include "conncache.h"
35 #include "curl_printf.h"
37 #include "curl_memory.h"
38 /* The last #include file should be: */
41 static void conn_llist_dtor(void *user, void *element)
43 struct connectdata *data = element;
49 static CURLcode bundle_create(struct SessionHandle *data,
50 struct connectbundle **cb_ptr)
53 DEBUGASSERT(*cb_ptr == NULL);
54 *cb_ptr = malloc(sizeof(struct connectbundle));
56 return CURLE_OUT_OF_MEMORY;
58 (*cb_ptr)->num_connections = 0;
59 (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
61 (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor);
62 if(!(*cb_ptr)->conn_list) {
63 Curl_safefree(*cb_ptr);
64 return CURLE_OUT_OF_MEMORY;
69 static void bundle_destroy(struct connectbundle *cb_ptr)
74 if(cb_ptr->conn_list) {
75 Curl_llist_destroy(cb_ptr->conn_list, NULL);
76 cb_ptr->conn_list = NULL;
81 /* Add a connection to a bundle */
82 static CURLcode bundle_add_conn(struct connectbundle *cb_ptr,
83 struct connectdata *conn)
85 if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn))
86 return CURLE_OUT_OF_MEMORY;
88 conn->bundle = cb_ptr;
90 cb_ptr->num_connections++;
94 /* Remove a connection from a bundle */
95 static int bundle_remove_conn(struct connectbundle *cb_ptr,
96 struct connectdata *conn)
98 struct curl_llist_element *curr;
100 curr = cb_ptr->conn_list->head;
102 if(curr->ptr == conn) {
103 Curl_llist_remove(cb_ptr->conn_list, curr, NULL);
104 cb_ptr->num_connections--;
106 return 1; /* we removed a handle */
113 static void free_bundle_hash_entry(void *freethis)
115 struct connectbundle *b = (struct connectbundle *) freethis;
120 int Curl_conncache_init(struct conncache *connc, int size)
122 return Curl_hash_init(&connc->hash, size, Curl_hash_str,
123 Curl_str_key_compare, free_bundle_hash_entry);
126 void Curl_conncache_destroy(struct conncache *connc)
129 Curl_hash_destroy(&connc->hash);
132 /* returns an allocated key to find a bundle for this connection */
133 static char *hashkey(struct connectdata *conn)
135 return aprintf("%s:%d",
136 conn->bits.proxy?conn->proxy.name:conn->host.name,
140 /* Look up the bundle with all the connections to the same host this
141 connectdata struct is setup to use. */
142 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
143 struct conncache *connc)
145 struct connectbundle *bundle = NULL;
147 char *key = hashkey(conn);
149 bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
157 static bool conncache_add_bundle(struct conncache *connc,
159 struct connectbundle *bundle)
161 void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
166 static void conncache_remove_bundle(struct conncache *connc,
167 struct connectbundle *bundle)
169 struct curl_hash_iterator iter;
170 struct curl_hash_element *he;
175 Curl_hash_start_iterate(&connc->hash, &iter);
177 he = Curl_hash_next_element(&iter);
179 if(he->ptr == bundle) {
180 /* The bundle is destroyed by the hash destructor function,
181 free_bundle_hash_entry() */
182 Curl_hash_delete(&connc->hash, he->key, he->key_len);
186 he = Curl_hash_next_element(&iter);
190 CURLcode Curl_conncache_add_conn(struct conncache *connc,
191 struct connectdata *conn)
194 struct connectbundle *bundle;
195 struct connectbundle *new_bundle = NULL;
196 struct SessionHandle *data = conn->data;
198 bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
203 result = bundle_create(data, &new_bundle);
209 bundle_destroy(new_bundle);
210 return CURLE_OUT_OF_MEMORY;
213 rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
216 bundle_destroy(new_bundle);
217 return CURLE_OUT_OF_MEMORY;
222 result = bundle_add_conn(bundle, conn);
225 conncache_remove_bundle(data->state.conn_cache, new_bundle);
229 conn->connection_id = connc->next_connection_id++;
230 connc->num_connections++;
232 DEBUGF(infof(conn->data, "Added connection %ld. "
233 "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
234 conn->connection_id, (curl_off_t) connc->num_connections));
239 void Curl_conncache_remove_conn(struct conncache *connc,
240 struct connectdata *conn)
242 struct connectbundle *bundle = conn->bundle;
244 /* The bundle pointer can be NULL, since this function can be called
245 due to a failed connection attempt, before being added to a bundle */
247 bundle_remove_conn(bundle, conn);
248 if(bundle->num_connections == 0) {
249 conncache_remove_bundle(connc, bundle);
253 connc->num_connections--;
255 DEBUGF(infof(conn->data, "The cache now contains %"
256 CURL_FORMAT_CURL_OFF_TU " members\n",
257 (curl_off_t) connc->num_connections));
262 /* This function iterates the entire connection cache and calls the
263 function func() with the connection pointer as the first argument
264 and the supplied 'param' argument as the other,
266 Return 0 from func() to continue the loop, return 1 to abort it.
268 void Curl_conncache_foreach(struct conncache *connc,
270 int (*func)(struct connectdata *conn, void *param))
272 struct curl_hash_iterator iter;
273 struct curl_llist_element *curr;
274 struct curl_hash_element *he;
279 Curl_hash_start_iterate(&connc->hash, &iter);
281 he = Curl_hash_next_element(&iter);
283 struct connectbundle *bundle;
286 he = Curl_hash_next_element(&iter);
288 curr = bundle->conn_list->head;
290 /* Yes, we need to update curr before calling func(), because func()
291 might decide to remove the connection */
292 struct connectdata *conn = curr->ptr;
295 if(1 == func(conn, param))
301 /* Return the first connection found in the cache. Used when closing all
304 Curl_conncache_find_first_connection(struct conncache *connc)
306 struct curl_hash_iterator iter;
307 struct curl_hash_element *he;
308 struct connectbundle *bundle;
310 Curl_hash_start_iterate(&connc->hash, &iter);
312 he = Curl_hash_next_element(&iter);
314 struct curl_llist_element *curr;
317 curr = bundle->conn_list->head;
322 he = Curl_hash_next_element(&iter);
330 /* Useful for debugging the connection cache */
331 void Curl_conncache_print(struct conncache *connc)
333 struct curl_hash_iterator iter;
334 struct curl_llist_element *curr;
335 struct curl_hash_element *he;
340 fprintf(stderr, "=Bundle cache=\n");
342 Curl_hash_start_iterate(connc->hash, &iter);
344 he = Curl_hash_next_element(&iter);
346 struct connectbundle *bundle;
347 struct connectdata *conn;
351 fprintf(stderr, "%s -", he->key);
352 curr = bundle->conn_list->head;
356 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
359 fprintf(stderr, "\n");
361 he = Curl_hash_next_element(&iter);