• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012, 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 http://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 #include "curl_printf.h"
36 
37 #include "curl_memory.h"
38 /* The last #include file should be: */
39 #include "memdebug.h"
40 
conn_llist_dtor(void * user,void * element)41 static void conn_llist_dtor(void *user, void *element)
42 {
43   struct connectdata *data = element;
44   (void)user;
45 
46   data->bundle = NULL;
47 }
48 
bundle_create(struct SessionHandle * data,struct connectbundle ** cb_ptr)49 static CURLcode bundle_create(struct SessionHandle *data,
50                               struct connectbundle **cb_ptr)
51 {
52   (void)data;
53   DEBUGASSERT(*cb_ptr == NULL);
54   *cb_ptr = malloc(sizeof(struct connectbundle));
55   if(!*cb_ptr)
56     return CURLE_OUT_OF_MEMORY;
57 
58   (*cb_ptr)->num_connections = 0;
59   (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
60 
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;
65   }
66   return CURLE_OK;
67 }
68 
bundle_destroy(struct connectbundle * cb_ptr)69 static void bundle_destroy(struct connectbundle *cb_ptr)
70 {
71   if(!cb_ptr)
72     return;
73 
74   if(cb_ptr->conn_list) {
75     Curl_llist_destroy(cb_ptr->conn_list, NULL);
76     cb_ptr->conn_list = NULL;
77   }
78   free(cb_ptr);
79 }
80 
81 /* Add a connection to a bundle */
bundle_add_conn(struct connectbundle * cb_ptr,struct connectdata * conn)82 static CURLcode bundle_add_conn(struct connectbundle *cb_ptr,
83                               struct connectdata *conn)
84 {
85   if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn))
86     return CURLE_OUT_OF_MEMORY;
87 
88   conn->bundle = cb_ptr;
89 
90   cb_ptr->num_connections++;
91   return CURLE_OK;
92 }
93 
94 /* Remove a connection from a bundle */
bundle_remove_conn(struct connectbundle * cb_ptr,struct connectdata * conn)95 static int bundle_remove_conn(struct connectbundle *cb_ptr,
96                               struct connectdata *conn)
97 {
98   struct curl_llist_element *curr;
99 
100   curr = cb_ptr->conn_list->head;
101   while(curr) {
102     if(curr->ptr == conn) {
103       Curl_llist_remove(cb_ptr->conn_list, curr, NULL);
104       cb_ptr->num_connections--;
105       conn->bundle = NULL;
106       return 1; /* we removed a handle */
107     }
108     curr = curr->next;
109   }
110   return 0;
111 }
112 
free_bundle_hash_entry(void * freethis)113 static void free_bundle_hash_entry(void *freethis)
114 {
115   struct connectbundle *b = (struct connectbundle *) freethis;
116 
117   bundle_destroy(b);
118 }
119 
Curl_conncache_init(struct conncache * connc,int size)120 int Curl_conncache_init(struct conncache *connc, int size)
121 {
122   return Curl_hash_init(&connc->hash, size, Curl_hash_str,
123                         Curl_str_key_compare, free_bundle_hash_entry);
124 }
125 
Curl_conncache_destroy(struct conncache * connc)126 void Curl_conncache_destroy(struct conncache *connc)
127 {
128   if(connc)
129     Curl_hash_destroy(&connc->hash);
130 }
131 
132 /* returns an allocated key to find a bundle for this connection */
hashkey(struct connectdata * conn)133 static char *hashkey(struct connectdata *conn)
134 {
135   return aprintf("%s:%d",
136                  conn->bits.proxy?conn->proxy.name:conn->host.name,
137                  conn->localport);
138 }
139 
140 /* Look up the bundle with all the connections to the same host this
141    connectdata struct is setup to use. */
Curl_conncache_find_bundle(struct connectdata * conn,struct conncache * connc)142 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
143                                                  struct conncache *connc)
144 {
145   struct connectbundle *bundle = NULL;
146   if(connc) {
147     char *key = hashkey(conn);
148     if(key) {
149       bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
150       free(key);
151     }
152   }
153 
154   return bundle;
155 }
156 
conncache_add_bundle(struct conncache * connc,char * key,struct connectbundle * bundle)157 static bool conncache_add_bundle(struct conncache *connc,
158                                  char *key,
159                                  struct connectbundle *bundle)
160 {
161   void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
162 
163   return p?TRUE:FALSE;
164 }
165 
conncache_remove_bundle(struct conncache * connc,struct connectbundle * bundle)166 static void conncache_remove_bundle(struct conncache *connc,
167                                     struct connectbundle *bundle)
168 {
169   struct curl_hash_iterator iter;
170   struct curl_hash_element *he;
171 
172   if(!connc)
173     return;
174 
175   Curl_hash_start_iterate(&connc->hash, &iter);
176 
177   he = Curl_hash_next_element(&iter);
178   while(he) {
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);
183       return;
184     }
185 
186     he = Curl_hash_next_element(&iter);
187   }
188 }
189 
Curl_conncache_add_conn(struct conncache * connc,struct connectdata * conn)190 CURLcode Curl_conncache_add_conn(struct conncache *connc,
191                                  struct connectdata *conn)
192 {
193   CURLcode result;
194   struct connectbundle *bundle;
195   struct connectbundle *new_bundle = NULL;
196   struct SessionHandle *data = conn->data;
197 
198   bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
199   if(!bundle) {
200     char *key;
201     int rc;
202 
203     result = bundle_create(data, &new_bundle);
204     if(result)
205       return result;
206 
207     key = hashkey(conn);
208     if(!key) {
209       bundle_destroy(new_bundle);
210       return CURLE_OUT_OF_MEMORY;
211     }
212 
213     rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
214     free(key);
215     if(!rc) {
216       bundle_destroy(new_bundle);
217       return CURLE_OUT_OF_MEMORY;
218     }
219     bundle = new_bundle;
220   }
221 
222   result = bundle_add_conn(bundle, conn);
223   if(result) {
224     if(new_bundle)
225       conncache_remove_bundle(data->state.conn_cache, new_bundle);
226     return result;
227   }
228 
229   conn->connection_id = connc->next_connection_id++;
230   connc->num_connections++;
231 
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));
235 
236   return CURLE_OK;
237 }
238 
Curl_conncache_remove_conn(struct conncache * connc,struct connectdata * conn)239 void Curl_conncache_remove_conn(struct conncache *connc,
240                                 struct connectdata *conn)
241 {
242   struct connectbundle *bundle = conn->bundle;
243 
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 */
246   if(bundle) {
247     bundle_remove_conn(bundle, conn);
248     if(bundle->num_connections == 0) {
249       conncache_remove_bundle(connc, bundle);
250     }
251 
252     if(connc) {
253       connc->num_connections--;
254 
255       DEBUGF(infof(conn->data, "The cache now contains %"
256                    CURL_FORMAT_CURL_OFF_TU " members\n",
257                    (curl_off_t) connc->num_connections));
258     }
259   }
260 }
261 
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,
265 
266    Return 0 from func() to continue the loop, return 1 to abort it.
267  */
Curl_conncache_foreach(struct conncache * connc,void * param,int (* func)(struct connectdata * conn,void * param))268 void Curl_conncache_foreach(struct conncache *connc,
269                             void *param,
270                             int (*func)(struct connectdata *conn, void *param))
271 {
272   struct curl_hash_iterator iter;
273   struct curl_llist_element *curr;
274   struct curl_hash_element *he;
275 
276   if(!connc)
277     return;
278 
279   Curl_hash_start_iterate(&connc->hash, &iter);
280 
281   he = Curl_hash_next_element(&iter);
282   while(he) {
283     struct connectbundle *bundle;
284 
285     bundle = he->ptr;
286     he = Curl_hash_next_element(&iter);
287 
288     curr = bundle->conn_list->head;
289     while(curr) {
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;
293       curr = curr->next;
294 
295       if(1 == func(conn, param))
296         return;
297     }
298   }
299 }
300 
301 /* Return the first connection found in the cache. Used when closing all
302    connections */
303 struct connectdata *
Curl_conncache_find_first_connection(struct conncache * connc)304 Curl_conncache_find_first_connection(struct conncache *connc)
305 {
306   struct curl_hash_iterator iter;
307   struct curl_hash_element *he;
308   struct connectbundle *bundle;
309 
310   Curl_hash_start_iterate(&connc->hash, &iter);
311 
312   he = Curl_hash_next_element(&iter);
313   while(he) {
314     struct curl_llist_element *curr;
315     bundle = he->ptr;
316 
317     curr = bundle->conn_list->head;
318     if(curr) {
319       return curr->ptr;
320     }
321 
322     he = Curl_hash_next_element(&iter);
323   }
324 
325   return NULL;
326 }
327 
328 
329 #if 0
330 /* Useful for debugging the connection cache */
331 void Curl_conncache_print(struct conncache *connc)
332 {
333   struct curl_hash_iterator iter;
334   struct curl_llist_element *curr;
335   struct curl_hash_element *he;
336 
337   if(!connc)
338     return;
339 
340   fprintf(stderr, "=Bundle cache=\n");
341 
342   Curl_hash_start_iterate(connc->hash, &iter);
343 
344   he = Curl_hash_next_element(&iter);
345   while(he) {
346     struct connectbundle *bundle;
347     struct connectdata *conn;
348 
349     bundle = he->ptr;
350 
351     fprintf(stderr, "%s -", he->key);
352     curr = bundle->conn_list->head;
353     while(curr) {
354       conn = curr->ptr;
355 
356       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
357       curr = curr->next;
358     }
359     fprintf(stderr, "\n");
360 
361     he = Curl_hash_next_element(&iter);
362   }
363 }
364 #endif
365