48271f7510ff545106ab8fb130526925fc584b60
[platform/upstream/curl.git] / lib / conncache.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
9  * Copyright (C) 2012 - 2013, 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 "bundles.h"
35 #include "conncache.h"
36
37 #include "curl_memory.h"
38 /* The last #include file should be: */
39 #include "memdebug.h"
40
41 static void free_bundle_hash_entry(void *freethis)
42 {
43   struct connectbundle *b = (struct connectbundle *) freethis;
44
45   Curl_bundle_destroy(b);
46 }
47
48 struct conncache *Curl_conncache_init(int size)
49 {
50   struct conncache *connc;
51
52   connc = calloc(1, sizeof(struct conncache));
53   if(!connc)
54     return NULL;
55
56   connc->hash = Curl_hash_alloc(size, Curl_hash_str,
57                                 Curl_str_key_compare, free_bundle_hash_entry);
58
59   if(!connc->hash) {
60     free(connc);
61     return NULL;
62   }
63
64   return connc;
65 }
66
67 void Curl_conncache_destroy(struct conncache *connc)
68 {
69   if(connc) {
70     Curl_hash_destroy(connc->hash);
71     connc->hash = NULL;
72     free(connc);
73   }
74 }
75
76 struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc,
77                                                  char *hostname)
78 {
79   struct connectbundle *bundle = NULL;
80
81   if(connc)
82     bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1);
83
84   return bundle;
85 }
86
87 static bool conncache_add_bundle(struct conncache *connc,
88                                  char *hostname,
89                                  struct connectbundle *bundle)
90 {
91   void *p;
92
93   p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle);
94
95   return p?TRUE:FALSE;
96 }
97
98 static void conncache_remove_bundle(struct conncache *connc,
99                                     struct connectbundle *bundle)
100 {
101   struct curl_hash_iterator iter;
102   struct curl_hash_element *he;
103
104   if(!connc)
105     return;
106
107   Curl_hash_start_iterate(connc->hash, &iter);
108
109   he = Curl_hash_next_element(&iter);
110   while(he) {
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);
115       return;
116     }
117
118     he = Curl_hash_next_element(&iter);
119   }
120 }
121
122 CURLcode Curl_conncache_add_conn(struct conncache *connc,
123                                  struct connectdata *conn)
124 {
125   CURLcode result;
126   struct connectbundle *bundle;
127   struct connectbundle *new_bundle = NULL;
128   struct SessionHandle *data = conn->data;
129
130   bundle = Curl_conncache_find_bundle(data->state.conn_cache,
131                                       conn->host.name);
132   if(!bundle) {
133     result = Curl_bundle_create(data, &new_bundle);
134     if(result != CURLE_OK)
135       return result;
136
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;
141     }
142     bundle = new_bundle;
143   }
144
145   result = Curl_bundle_add_conn(bundle, conn);
146   if(result != CURLE_OK) {
147     if(new_bundle)
148       conncache_remove_bundle(data->state.conn_cache, new_bundle);
149     return result;
150   }
151
152   connc->num_connections++;
153
154   return CURLE_OK;
155 }
156
157 void Curl_conncache_remove_conn(struct conncache *connc,
158                                 struct connectdata *conn)
159 {
160   struct connectbundle *bundle = conn->bundle;
161
162   /* The bundle pointer can be NULL, since this function can be called
163      due to a failed connection attempt, before being added to a bundle */
164   if(bundle) {
165     Curl_bundle_remove_conn(bundle, conn);
166     if(bundle->num_connections == 0) {
167       conncache_remove_bundle(connc, bundle);
168     }
169     connc->num_connections--;
170
171     DEBUGF(infof(conn->data, "The cache now contains %d members\n",
172                  connc->num_connections));
173   }
174 }
175
176 /* This function iterates the entire connection cache and calls the
177    function func() with the connection pointer as the first argument
178    and the supplied 'param' argument as the other,
179
180    Return 0 from func() to continue the loop, return 1 to abort it.
181  */
182 void Curl_conncache_foreach(struct conncache *connc,
183                             void *param,
184                             int (*func)(struct connectdata *conn, void *param))
185 {
186   struct curl_hash_iterator iter;
187   struct curl_llist_element *curr;
188   struct curl_hash_element *he;
189
190   if(!connc)
191     return;
192
193   Curl_hash_start_iterate(connc->hash, &iter);
194
195   he = Curl_hash_next_element(&iter);
196   while(he) {
197     struct connectbundle *bundle;
198     struct connectdata *conn;
199
200     bundle = he->ptr;
201
202     curr = bundle->conn_list->head;
203     while(curr) {
204       /* Yes, we need to update curr before calling func(), because func()
205          might decide to remove the connection */
206       conn = curr->ptr;
207       curr = curr->next;
208
209       if(1 == func(conn, param))
210         return;
211     }
212
213     he = Curl_hash_next_element(&iter);
214   }
215 }
216
217 /* Return the first connection found in the cache. Used when closing all
218    connections */
219 struct connectdata *
220 Curl_conncache_find_first_connection(struct conncache *connc)
221 {
222   struct curl_hash_iterator iter;
223   struct curl_llist_element *curr;
224   struct curl_hash_element *he;
225   struct connectbundle *bundle;
226
227   Curl_hash_start_iterate(connc->hash, &iter);
228
229   he = Curl_hash_next_element(&iter);
230   while(he) {
231     bundle = he->ptr;
232
233     curr = bundle->conn_list->head;
234     if(curr) {
235       return curr->ptr;
236     }
237
238     he = Curl_hash_next_element(&iter);
239   }
240
241   return NULL;
242 }
243
244
245 #if 0
246 /* Useful for debugging the connection cache */
247 void Curl_conncache_print(struct conncache *connc)
248 {
249   struct curl_hash_iterator iter;
250   struct curl_llist_element *curr;
251   struct curl_hash_element *he;
252
253   if(!connc)
254     return;
255
256   fprintf(stderr, "=Bundle cache=\n");
257
258   Curl_hash_start_iterate(connc->hash, &iter);
259
260   he = Curl_hash_next_element(&iter);
261   while(he) {
262     struct connectbundle *bundle;
263     struct connectdata *conn;
264
265     bundle = he->ptr;
266
267     fprintf(stderr, "%s -", he->key);
268     curr = bundle->conn_list->head;
269     while(curr) {
270       conn = curr->ptr;
271
272       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
273       curr = curr->next;
274     }
275     fprintf(stderr, "\n");
276
277     he = Curl_hash_next_element(&iter);
278   }
279 }
280 #endif