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