1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012, 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 ***************************************************************************/
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(conncachetype type)
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);
67 connc->num_connections = 0;
72 void Curl_conncache_destroy(struct conncache *connc)
75 Curl_hash_destroy(connc->hash);
81 struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc,
84 struct connectbundle *bundle = NULL;
87 bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1);
92 static bool conncache_add_bundle(struct conncache *connc,
94 struct connectbundle *bundle)
98 p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle);
103 static void conncache_remove_bundle(struct conncache *connc,
104 struct connectbundle *bundle)
106 struct curl_hash_iterator iter;
107 struct curl_hash_element *he;
112 Curl_hash_start_iterate(connc->hash, &iter);
114 he = Curl_hash_next_element(&iter);
116 if(he->ptr == bundle) {
117 /* The bundle is destroyed by the hash destructor function,
118 free_bundle_hash_entry() */
119 Curl_hash_delete(connc->hash, he->key, he->key_len);
123 he = Curl_hash_next_element(&iter);
127 CURLcode Curl_conncache_add_conn(struct conncache *connc,
128 struct connectdata *conn)
131 struct connectbundle *bundle;
132 struct connectbundle *new_bundle = NULL;
133 struct SessionHandle *data = conn->data;
135 bundle = Curl_conncache_find_bundle(data->state.conn_cache,
138 result = Curl_bundle_create(data, &new_bundle);
139 if(result != CURLE_OK)
142 if(!conncache_add_bundle(data->state.conn_cache,
143 conn->host.name, new_bundle)) {
144 Curl_bundle_destroy(new_bundle);
145 return CURLE_OUT_OF_MEMORY;
150 result = Curl_bundle_add_conn(bundle, conn);
151 if(result != CURLE_OK) {
153 conncache_remove_bundle(data->state.conn_cache, new_bundle);
157 connc->num_connections++;
162 void Curl_conncache_remove_conn(struct conncache *connc,
163 struct connectdata *conn)
165 struct connectbundle *bundle = conn->bundle;
167 /* The bundle pointer can be NULL, since this function can be called
168 due to a failed connection attempt, before being added to a bundle */
170 Curl_bundle_remove_conn(bundle, conn);
171 if(bundle->num_connections == 0) {
172 conncache_remove_bundle(connc, bundle);
174 connc->num_connections--;
176 DEBUGF(infof(conn->data, "The cache now contains %d members\n",
177 connc->num_connections));
181 /* This function iterates the entire connection cache and calls the
182 function func() with the connection pointer as the first argument
183 and the supplied 'param' argument as the other,
185 Return 0 from func() to continue the loop, return 1 to abort it.
187 void Curl_conncache_foreach(struct conncache *connc,
189 int (*func)(struct connectdata *conn, void *param))
191 struct curl_hash_iterator iter;
192 struct curl_llist_element *curr;
193 struct curl_hash_element *he;
198 Curl_hash_start_iterate(connc->hash, &iter);
200 he = Curl_hash_next_element(&iter);
202 struct connectbundle *bundle;
203 struct connectdata *conn;
207 curr = bundle->conn_list->head;
209 /* Yes, we need to update curr before calling func(), because func()
210 might decide to remove the connection */
214 if(1 == func(conn, param))
218 he = Curl_hash_next_element(&iter);
222 /* Return the first connection found in the cache. Used when closing all
225 Curl_conncache_find_first_connection(struct conncache *connc)
227 struct curl_hash_iterator iter;
228 struct curl_llist_element *curr;
229 struct curl_hash_element *he;
230 struct connectbundle *bundle;
232 Curl_hash_start_iterate(connc->hash, &iter);
234 he = Curl_hash_next_element(&iter);
238 curr = bundle->conn_list->head;
243 he = Curl_hash_next_element(&iter);
251 /* Useful for debugging the connection cache */
252 void Curl_conncache_print(struct conncache *connc)
254 struct curl_hash_iterator iter;
255 struct curl_llist_element *curr;
256 struct curl_hash_element *he;
261 fprintf(stderr, "=Bundle cache=\n");
263 Curl_hash_start_iterate(connc->hash, &iter);
265 he = Curl_hash_next_element(&iter);
267 struct connectbundle *bundle;
268 struct connectdata *conn;
272 fprintf(stderr, "%s -", he->key);
273 curr = bundle->conn_list->head;
277 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
280 fprintf(stderr, "\n");
282 he = Curl_hash_next_element(&iter);