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