1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2012, 2016, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
10 *
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.
14 *
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.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ***************************************************************************/
23
24 #include "curl_setup.h"
25
26 #include <curl/curl.h>
27
28 #include "urldata.h"
29 #include "url.h"
30 #include "progress.h"
31 #include "multiif.h"
32 #include "sendf.h"
33 #include "rawstr.h"
34 #include "conncache.h"
35 /* The last 3 #include files should be in this order */
36 #include "curl_printf.h"
37 #include "curl_memory.h"
38 #include "memdebug.h"
39
conn_llist_dtor(void * user,void * element)40 static void conn_llist_dtor(void *user, void *element)
41 {
42 struct connectdata *data = element;
43 (void)user;
44
45 data->bundle = NULL;
46 }
47
bundle_create(struct Curl_easy * data,struct connectbundle ** cb_ptr)48 static CURLcode bundle_create(struct Curl_easy *data,
49 struct connectbundle **cb_ptr)
50 {
51 (void)data;
52 DEBUGASSERT(*cb_ptr == NULL);
53 *cb_ptr = malloc(sizeof(struct connectbundle));
54 if(!*cb_ptr)
55 return CURLE_OUT_OF_MEMORY;
56
57 (*cb_ptr)->num_connections = 0;
58 (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
59
60 (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor);
61 if(!(*cb_ptr)->conn_list) {
62 Curl_safefree(*cb_ptr);
63 return CURLE_OUT_OF_MEMORY;
64 }
65 return CURLE_OK;
66 }
67
bundle_destroy(struct connectbundle * cb_ptr)68 static void bundle_destroy(struct connectbundle *cb_ptr)
69 {
70 if(!cb_ptr)
71 return;
72
73 if(cb_ptr->conn_list) {
74 Curl_llist_destroy(cb_ptr->conn_list, NULL);
75 cb_ptr->conn_list = NULL;
76 }
77 free(cb_ptr);
78 }
79
80 /* Add a connection to a bundle */
bundle_add_conn(struct connectbundle * cb_ptr,struct connectdata * conn)81 static CURLcode bundle_add_conn(struct connectbundle *cb_ptr,
82 struct connectdata *conn)
83 {
84 if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn))
85 return CURLE_OUT_OF_MEMORY;
86
87 conn->bundle = cb_ptr;
88
89 cb_ptr->num_connections++;
90 return CURLE_OK;
91 }
92
93 /* Remove a connection from a bundle */
bundle_remove_conn(struct connectbundle * cb_ptr,struct connectdata * conn)94 static int bundle_remove_conn(struct connectbundle *cb_ptr,
95 struct connectdata *conn)
96 {
97 struct curl_llist_element *curr;
98
99 curr = cb_ptr->conn_list->head;
100 while(curr) {
101 if(curr->ptr == conn) {
102 Curl_llist_remove(cb_ptr->conn_list, curr, NULL);
103 cb_ptr->num_connections--;
104 conn->bundle = NULL;
105 return 1; /* we removed a handle */
106 }
107 curr = curr->next;
108 }
109 return 0;
110 }
111
free_bundle_hash_entry(void * freethis)112 static void free_bundle_hash_entry(void *freethis)
113 {
114 struct connectbundle *b = (struct connectbundle *) freethis;
115
116 bundle_destroy(b);
117 }
118
Curl_conncache_init(struct conncache * connc,int size)119 int Curl_conncache_init(struct conncache *connc, int size)
120 {
121 return Curl_hash_init(&connc->hash, size, Curl_hash_str,
122 Curl_str_key_compare, free_bundle_hash_entry);
123 }
124
Curl_conncache_destroy(struct conncache * connc)125 void Curl_conncache_destroy(struct conncache *connc)
126 {
127 if(connc)
128 Curl_hash_destroy(&connc->hash);
129 }
130
131 /* returns an allocated key to find a bundle for this connection */
hashkey(struct connectdata * conn)132 static char *hashkey(struct connectdata *conn)
133 {
134 const char *hostname;
135
136 if(conn->bits.proxy)
137 hostname = conn->proxy.name;
138 else if(conn->bits.conn_to_host)
139 hostname = conn->conn_to_host.name;
140 else
141 hostname = conn->host.name;
142
143 return aprintf("%s:%d", hostname, conn->port);
144 }
145
146 /* Look up the bundle with all the connections to the same host this
147 connectdata struct is setup to use. */
Curl_conncache_find_bundle(struct connectdata * conn,struct conncache * connc)148 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
149 struct conncache *connc)
150 {
151 struct connectbundle *bundle = NULL;
152 if(connc) {
153 char *key = hashkey(conn);
154 if(key) {
155 bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
156 free(key);
157 }
158 }
159
160 return bundle;
161 }
162
conncache_add_bundle(struct conncache * connc,char * key,struct connectbundle * bundle)163 static bool conncache_add_bundle(struct conncache *connc,
164 char *key,
165 struct connectbundle *bundle)
166 {
167 void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
168
169 return p?TRUE:FALSE;
170 }
171
conncache_remove_bundle(struct conncache * connc,struct connectbundle * bundle)172 static void conncache_remove_bundle(struct conncache *connc,
173 struct connectbundle *bundle)
174 {
175 struct curl_hash_iterator iter;
176 struct curl_hash_element *he;
177
178 if(!connc)
179 return;
180
181 Curl_hash_start_iterate(&connc->hash, &iter);
182
183 he = Curl_hash_next_element(&iter);
184 while(he) {
185 if(he->ptr == bundle) {
186 /* The bundle is destroyed by the hash destructor function,
187 free_bundle_hash_entry() */
188 Curl_hash_delete(&connc->hash, he->key, he->key_len);
189 return;
190 }
191
192 he = Curl_hash_next_element(&iter);
193 }
194 }
195
Curl_conncache_add_conn(struct conncache * connc,struct connectdata * conn)196 CURLcode Curl_conncache_add_conn(struct conncache *connc,
197 struct connectdata *conn)
198 {
199 CURLcode result;
200 struct connectbundle *bundle;
201 struct connectbundle *new_bundle = NULL;
202 struct Curl_easy *data = conn->data;
203
204 bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
205 if(!bundle) {
206 char *key;
207 int rc;
208
209 result = bundle_create(data, &new_bundle);
210 if(result)
211 return result;
212
213 key = hashkey(conn);
214 if(!key) {
215 bundle_destroy(new_bundle);
216 return CURLE_OUT_OF_MEMORY;
217 }
218
219 rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
220 free(key);
221 if(!rc) {
222 bundle_destroy(new_bundle);
223 return CURLE_OUT_OF_MEMORY;
224 }
225 bundle = new_bundle;
226 }
227
228 result = bundle_add_conn(bundle, conn);
229 if(result) {
230 if(new_bundle)
231 conncache_remove_bundle(data->state.conn_cache, new_bundle);
232 return result;
233 }
234
235 conn->connection_id = connc->next_connection_id++;
236 connc->num_connections++;
237
238 DEBUGF(infof(conn->data, "Added connection %ld. "
239 "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
240 conn->connection_id, (curl_off_t) connc->num_connections));
241
242 return CURLE_OK;
243 }
244
Curl_conncache_remove_conn(struct conncache * connc,struct connectdata * conn)245 void Curl_conncache_remove_conn(struct conncache *connc,
246 struct connectdata *conn)
247 {
248 struct connectbundle *bundle = conn->bundle;
249
250 /* The bundle pointer can be NULL, since this function can be called
251 due to a failed connection attempt, before being added to a bundle */
252 if(bundle) {
253 bundle_remove_conn(bundle, conn);
254 if(bundle->num_connections == 0) {
255 conncache_remove_bundle(connc, bundle);
256 }
257
258 if(connc) {
259 connc->num_connections--;
260
261 DEBUGF(infof(conn->data, "The cache now contains %"
262 CURL_FORMAT_CURL_OFF_TU " members\n",
263 (curl_off_t) connc->num_connections));
264 }
265 }
266 }
267
268 /* This function iterates the entire connection cache and calls the
269 function func() with the connection pointer as the first argument
270 and the supplied 'param' argument as the other,
271
272 Return 0 from func() to continue the loop, return 1 to abort it.
273 */
Curl_conncache_foreach(struct conncache * connc,void * param,int (* func)(struct connectdata * conn,void * param))274 void Curl_conncache_foreach(struct conncache *connc,
275 void *param,
276 int (*func)(struct connectdata *conn, void *param))
277 {
278 struct curl_hash_iterator iter;
279 struct curl_llist_element *curr;
280 struct curl_hash_element *he;
281
282 if(!connc)
283 return;
284
285 Curl_hash_start_iterate(&connc->hash, &iter);
286
287 he = Curl_hash_next_element(&iter);
288 while(he) {
289 struct connectbundle *bundle;
290
291 bundle = he->ptr;
292 he = Curl_hash_next_element(&iter);
293
294 curr = bundle->conn_list->head;
295 while(curr) {
296 /* Yes, we need to update curr before calling func(), because func()
297 might decide to remove the connection */
298 struct connectdata *conn = curr->ptr;
299 curr = curr->next;
300
301 if(1 == func(conn, param))
302 return;
303 }
304 }
305 }
306
307 /* Return the first connection found in the cache. Used when closing all
308 connections */
309 struct connectdata *
Curl_conncache_find_first_connection(struct conncache * connc)310 Curl_conncache_find_first_connection(struct conncache *connc)
311 {
312 struct curl_hash_iterator iter;
313 struct curl_hash_element *he;
314 struct connectbundle *bundle;
315
316 Curl_hash_start_iterate(&connc->hash, &iter);
317
318 he = Curl_hash_next_element(&iter);
319 while(he) {
320 struct curl_llist_element *curr;
321 bundle = he->ptr;
322
323 curr = bundle->conn_list->head;
324 if(curr) {
325 return curr->ptr;
326 }
327
328 he = Curl_hash_next_element(&iter);
329 }
330
331 return NULL;
332 }
333
334
335 #if 0
336 /* Useful for debugging the connection cache */
337 void Curl_conncache_print(struct conncache *connc)
338 {
339 struct curl_hash_iterator iter;
340 struct curl_llist_element *curr;
341 struct curl_hash_element *he;
342
343 if(!connc)
344 return;
345
346 fprintf(stderr, "=Bundle cache=\n");
347
348 Curl_hash_start_iterate(connc->hash, &iter);
349
350 he = Curl_hash_next_element(&iter);
351 while(he) {
352 struct connectbundle *bundle;
353 struct connectdata *conn;
354
355 bundle = he->ptr;
356
357 fprintf(stderr, "%s -", he->key);
358 curr = bundle->conn_list->head;
359 while(curr) {
360 conn = curr->ptr;
361
362 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
363 curr = curr->next;
364 }
365 fprintf(stderr, "\n");
366
367 he = Curl_hash_next_element(&iter);
368 }
369 }
370 #endif
371