1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012 - 2014, 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 static void free_bundle_hash_entry(void *freethis)
43 struct connectbundle *b = (struct connectbundle *) freethis;
45 Curl_bundle_destroy(b);
48 struct conncache *Curl_conncache_init(int size)
50 struct conncache *connc;
52 connc = calloc(1, sizeof(struct conncache));
56 connc->hash = Curl_hash_alloc(size, Curl_hash_str,
57 Curl_str_key_compare, free_bundle_hash_entry);
67 void Curl_conncache_destroy(struct conncache *connc)
70 Curl_hash_destroy(connc->hash);
76 struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc,
79 struct connectbundle *bundle = NULL;
82 bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1);
87 static bool conncache_add_bundle(struct conncache *connc,
89 struct connectbundle *bundle)
93 p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle);
98 static void conncache_remove_bundle(struct conncache *connc,
99 struct connectbundle *bundle)
101 struct curl_hash_iterator iter;
102 struct curl_hash_element *he;
107 Curl_hash_start_iterate(connc->hash, &iter);
109 he = Curl_hash_next_element(&iter);
111 if(he->ptr == bundle) {
112 /* The bundle is destroyed by the hash destructor function,
113 free_bundle_hash_entry() */
114 Curl_hash_delete(connc->hash, he->key, he->key_len);
118 he = Curl_hash_next_element(&iter);
122 CURLcode Curl_conncache_add_conn(struct conncache *connc,
123 struct connectdata *conn)
126 struct connectbundle *bundle;
127 struct connectbundle *new_bundle = NULL;
128 struct SessionHandle *data = conn->data;
130 bundle = Curl_conncache_find_bundle(data->state.conn_cache,
133 result = Curl_bundle_create(data, &new_bundle);
137 if(!conncache_add_bundle(data->state.conn_cache,
138 conn->host.name, new_bundle)) {
139 Curl_bundle_destroy(new_bundle);
140 return CURLE_OUT_OF_MEMORY;
145 result = Curl_bundle_add_conn(bundle, conn);
148 conncache_remove_bundle(data->state.conn_cache, new_bundle);
152 conn->connection_id = connc->next_connection_id++;
153 connc->num_connections++;
155 DEBUGF(infof(conn->data, "Added connection %ld. "
156 "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
157 conn->connection_id, (curl_off_t) 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);
176 connc->num_connections--;
178 DEBUGF(infof(conn->data, "The cache now contains %"
179 CURL_FORMAT_CURL_OFF_TU " members\n",
180 (curl_off_t) connc->num_connections));
185 /* This function iterates the entire connection cache and calls the
186 function func() with the connection pointer as the first argument
187 and the supplied 'param' argument as the other,
189 Return 0 from func() to continue the loop, return 1 to abort it.
191 void Curl_conncache_foreach(struct conncache *connc,
193 int (*func)(struct connectdata *conn, void *param))
195 struct curl_hash_iterator iter;
196 struct curl_llist_element *curr;
197 struct curl_hash_element *he;
202 Curl_hash_start_iterate(connc->hash, &iter);
204 he = Curl_hash_next_element(&iter);
206 struct connectbundle *bundle;
209 he = Curl_hash_next_element(&iter);
211 curr = bundle->conn_list->head;
213 /* Yes, we need to update curr before calling func(), because func()
214 might decide to remove the connection */
215 struct connectdata *conn = curr->ptr;
218 if(1 == func(conn, param))
224 /* Return the first connection found in the cache. Used when closing all
227 Curl_conncache_find_first_connection(struct conncache *connc)
229 struct curl_hash_iterator iter;
230 struct curl_hash_element *he;
231 struct connectbundle *bundle;
233 Curl_hash_start_iterate(connc->hash, &iter);
235 he = Curl_hash_next_element(&iter);
237 struct curl_llist_element *curr;
240 curr = bundle->conn_list->head;
245 he = Curl_hash_next_element(&iter);
253 /* Useful for debugging the connection cache */
254 void Curl_conncache_print(struct conncache *connc)
256 struct curl_hash_iterator iter;
257 struct curl_llist_element *curr;
258 struct curl_hash_element *he;
263 fprintf(stderr, "=Bundle cache=\n");
265 Curl_hash_start_iterate(connc->hash, &iter);
267 he = Curl_hash_next_element(&iter);
269 struct connectbundle *bundle;
270 struct connectdata *conn;
274 fprintf(stderr, "%s -", he->key);
275 curr = bundle->conn_list->head;
279 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
282 fprintf(stderr, "\n");
284 he = Curl_hash_next_element(&iter);