Revert "Update to 7.40.1"
[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 - 2014, 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   conn->connection_id = connc->next_connection_id++;
153   connc->num_connections++;
154
155   return CURLE_OK;
156 }
157
158 void Curl_conncache_remove_conn(struct conncache *connc,
159                                 struct connectdata *conn)
160 {
161   struct connectbundle *bundle = conn->bundle;
162
163   /* The bundle pointer can be NULL, since this function can be called
164      due to a failed connection attempt, before being added to a bundle */
165   if(bundle) {
166     Curl_bundle_remove_conn(bundle, conn);
167     if(bundle->num_connections == 0) {
168       conncache_remove_bundle(connc, bundle);
169     }
170
171     if(connc) {
172       connc->num_connections--;
173
174       DEBUGF(infof(conn->data, "The cache now contains %d members\n",
175                    connc->num_connections));
176     }
177   }
178 }
179
180 /* This function iterates the entire connection cache and calls the
181    function func() with the connection pointer as the first argument
182    and the supplied 'param' argument as the other,
183
184    Return 0 from func() to continue the loop, return 1 to abort it.
185  */
186 void Curl_conncache_foreach(struct conncache *connc,
187                             void *param,
188                             int (*func)(struct connectdata *conn, void *param))
189 {
190   struct curl_hash_iterator iter;
191   struct curl_llist_element *curr;
192   struct curl_hash_element *he;
193
194   if(!connc)
195     return;
196
197   Curl_hash_start_iterate(connc->hash, &iter);
198
199   he = Curl_hash_next_element(&iter);
200   while(he) {
201     struct connectbundle *bundle;
202     struct connectdata *conn;
203
204     bundle = he->ptr;
205
206     curr = bundle->conn_list->head;
207     while(curr) {
208       /* Yes, we need to update curr before calling func(), because func()
209          might decide to remove the connection */
210       conn = curr->ptr;
211       curr = curr->next;
212
213       if(1 == func(conn, param))
214         return;
215     }
216
217     he = Curl_hash_next_element(&iter);
218   }
219 }
220
221 /* Return the first connection found in the cache. Used when closing all
222    connections */
223 struct connectdata *
224 Curl_conncache_find_first_connection(struct conncache *connc)
225 {
226   struct curl_hash_iterator iter;
227   struct curl_llist_element *curr;
228   struct curl_hash_element *he;
229   struct connectbundle *bundle;
230
231   Curl_hash_start_iterate(connc->hash, &iter);
232
233   he = Curl_hash_next_element(&iter);
234   while(he) {
235     bundle = he->ptr;
236
237     curr = bundle->conn_list->head;
238     if(curr) {
239       return curr->ptr;
240     }
241
242     he = Curl_hash_next_element(&iter);
243   }
244
245   return NULL;
246 }
247
248
249 #if 0
250 /* Useful for debugging the connection cache */
251 void Curl_conncache_print(struct conncache *connc)
252 {
253   struct curl_hash_iterator iter;
254   struct curl_llist_element *curr;
255   struct curl_hash_element *he;
256
257   if(!connc)
258     return;
259
260   fprintf(stderr, "=Bundle cache=\n");
261
262   Curl_hash_start_iterate(connc->hash, &iter);
263
264   he = Curl_hash_next_element(&iter);
265   while(he) {
266     struct connectbundle *bundle;
267     struct connectdata *conn;
268
269     bundle = he->ptr;
270
271     fprintf(stderr, "%s -", he->key);
272     curr = bundle->conn_list->head;
273     while(curr) {
274       conn = curr->ptr;
275
276       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
277       curr = curr->next;
278     }
279     fprintf(stderr, "\n");
280
281     he = Curl_hash_next_element(&iter);
282   }
283 }
284 #endif