1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012 - 2013, 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 http://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>
35 #include "conncache.h"
37 #include "curl_memory.h"
38 /* The last #include file should be: */
41 #define CONNECTION_HASH_SIZE 97
43 static void free_bundle_hash_entry(void *freethis)
45 struct connectbundle *b = (struct connectbundle *) freethis;
47 Curl_bundle_destroy(b);
50 struct conncache *Curl_conncache_init(void)
52 struct conncache *connc;
54 connc = calloc(1, sizeof(struct conncache));
58 connc->hash = Curl_hash_alloc(CONNECTION_HASH_SIZE, Curl_hash_str,
59 Curl_str_key_compare, free_bundle_hash_entry);
69 void Curl_conncache_destroy(struct conncache *connc)
72 Curl_hash_destroy(connc->hash);
78 struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc,
81 struct connectbundle *bundle = NULL;
84 bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1);
89 static bool conncache_add_bundle(struct conncache *connc,
91 struct connectbundle *bundle)
95 p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle);
100 static void conncache_remove_bundle(struct conncache *connc,
101 struct connectbundle *bundle)
103 struct curl_hash_iterator iter;
104 struct curl_hash_element *he;
109 Curl_hash_start_iterate(connc->hash, &iter);
111 he = Curl_hash_next_element(&iter);
113 if(he->ptr == bundle) {
114 /* The bundle is destroyed by the hash destructor function,
115 free_bundle_hash_entry() */
116 Curl_hash_delete(connc->hash, he->key, he->key_len);
120 he = Curl_hash_next_element(&iter);
124 CURLcode Curl_conncache_add_conn(struct conncache *connc,
125 struct connectdata *conn)
128 struct connectbundle *bundle;
129 struct connectbundle *new_bundle = NULL;
130 struct SessionHandle *data = conn->data;
132 bundle = Curl_conncache_find_bundle(data->state.conn_cache,
135 result = Curl_bundle_create(data, &new_bundle);
136 if(result != CURLE_OK)
139 if(!conncache_add_bundle(data->state.conn_cache,
140 conn->host.name, new_bundle)) {
141 Curl_bundle_destroy(new_bundle);
142 return CURLE_OUT_OF_MEMORY;
147 result = Curl_bundle_add_conn(bundle, conn);
148 if(result != CURLE_OK) {
150 conncache_remove_bundle(data->state.conn_cache, new_bundle);
154 connc->num_connections++;
159 void Curl_conncache_remove_conn(struct conncache *connc,
160 struct connectdata *conn)
162 struct connectbundle *bundle = conn->bundle;
164 /* The bundle pointer can be NULL, since this function can be called
165 due to a failed connection attempt, before being added to a bundle */
167 Curl_bundle_remove_conn(bundle, conn);
168 if(bundle->num_connections == 0) {
169 conncache_remove_bundle(connc, bundle);
171 connc->num_connections--;
173 DEBUGF(infof(conn->data, "The cache now contains %d members\n",
174 connc->num_connections));
178 /* This function iterates the entire connection cache and calls the
179 function func() with the connection pointer as the first argument
180 and the supplied 'param' argument as the other,
182 Return 0 from func() to continue the loop, return 1 to abort it.
184 void Curl_conncache_foreach(struct conncache *connc,
186 int (*func)(struct connectdata *conn, void *param))
188 struct curl_hash_iterator iter;
189 struct curl_llist_element *curr;
190 struct curl_hash_element *he;
195 Curl_hash_start_iterate(connc->hash, &iter);
197 he = Curl_hash_next_element(&iter);
199 struct connectbundle *bundle;
200 struct connectdata *conn;
204 curr = bundle->conn_list->head;
206 /* Yes, we need to update curr before calling func(), because func()
207 might decide to remove the connection */
211 if(1 == func(conn, param))
215 he = Curl_hash_next_element(&iter);
219 /* Return the first connection found in the cache. Used when closing all
222 Curl_conncache_find_first_connection(struct conncache *connc)
224 struct curl_hash_iterator iter;
225 struct curl_llist_element *curr;
226 struct curl_hash_element *he;
227 struct connectbundle *bundle;
229 Curl_hash_start_iterate(connc->hash, &iter);
231 he = Curl_hash_next_element(&iter);
235 curr = bundle->conn_list->head;
240 he = Curl_hash_next_element(&iter);
248 /* Useful for debugging the connection cache */
249 void Curl_conncache_print(struct conncache *connc)
251 struct curl_hash_iterator iter;
252 struct curl_llist_element *curr;
253 struct curl_hash_element *he;
258 fprintf(stderr, "=Bundle cache=\n");
260 Curl_hash_start_iterate(connc->hash, &iter);
262 he = Curl_hash_next_element(&iter);
264 struct connectbundle *bundle;
265 struct connectdata *conn;
269 fprintf(stderr, "%s -", he->key);
270 curr = bundle->conn_list->head;
274 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
277 fprintf(stderr, "\n");
279 he = Curl_hash_next_element(&iter);