source: EcnlProtoTool/trunk/curl-7.57.0/lib/conncache.c@ 331

Last change on this file since 331 was 331, checked in by coas-nagasima, 6 years ago

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc
File size: 12.5 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
9 * Copyright (C) 2012 - 2017, 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 https://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 "conncache.h"
34#include "share.h"
35#include "sigpipe.h"
36#include "connect.h"
37
38/* The last 3 #include files should be in this order */
39#include "curl_printf.h"
40#include "curl_memory.h"
41#include "memdebug.h"
42
43#define CONN_LOCK(x) if((x)->share) \
44 Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE)
45#define CONN_UNLOCK(x) if((x)->share) \
46 Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT)
47
48
49static void conn_llist_dtor(void *user, void *element)
50{
51 struct connectdata *data = element;
52 (void)user;
53
54 data->bundle = NULL;
55}
56
57static CURLcode bundle_create(struct Curl_easy *data,
58 struct connectbundle **cb_ptr)
59{
60 (void)data;
61 DEBUGASSERT(*cb_ptr == NULL);
62 *cb_ptr = malloc(sizeof(struct connectbundle));
63 if(!*cb_ptr)
64 return CURLE_OUT_OF_MEMORY;
65
66 (*cb_ptr)->num_connections = 0;
67 (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
68
69 Curl_llist_init(&(*cb_ptr)->conn_list, (curl_llist_dtor) conn_llist_dtor);
70 return CURLE_OK;
71}
72
73static void bundle_destroy(struct connectbundle *cb_ptr)
74{
75 if(!cb_ptr)
76 return;
77
78 Curl_llist_destroy(&cb_ptr->conn_list, NULL);
79
80 free(cb_ptr);
81}
82
83/* Add a connection to a bundle */
84static CURLcode bundle_add_conn(struct connectbundle *cb_ptr,
85 struct connectdata *conn)
86{
87 Curl_llist_insert_next(&cb_ptr->conn_list, cb_ptr->conn_list.tail, conn,
88 &conn->bundle_node);
89 conn->bundle = cb_ptr;
90 cb_ptr->num_connections++;
91 return CURLE_OK;
92}
93
94/* Remove a connection from a bundle */
95static 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
113static void free_bundle_hash_entry(void *freethis)
114{
115 struct connectbundle *b = (struct connectbundle *) freethis;
116
117 bundle_destroy(b);
118}
119
120int Curl_conncache_init(struct conncache *connc, int size)
121{
122 int rc;
123
124 /* allocate a new easy handle to use when closing cached connections */
125 connc->closure_handle = curl_easy_init();
126 if(!connc->closure_handle)
127 return 1; /* bad */
128
129 rc = Curl_hash_init(&connc->hash, size, Curl_hash_str,
130 Curl_str_key_compare, free_bundle_hash_entry);
131 if(rc) {
132 Curl_close(connc->closure_handle);
133 connc->closure_handle = NULL;
134 }
135 else
136 connc->closure_handle->state.conn_cache = connc;
137
138 return rc;
139}
140
141void Curl_conncache_destroy(struct conncache *connc)
142{
143 if(connc)
144 Curl_hash_destroy(&connc->hash);
145}
146
147/* creates a key to find a bundle for this connection */
148static void hashkey(struct connectdata *conn, char *buf,
149 size_t len) /* something like 128 is fine */
150{
151 const char *hostname;
152
153 if(conn->bits.socksproxy)
154 hostname = conn->socks_proxy.host.name;
155 else if(conn->bits.httpproxy)
156 hostname = conn->http_proxy.host.name;
157 else if(conn->bits.conn_to_host)
158 hostname = conn->conn_to_host.name;
159 else
160 hostname = conn->host.name;
161
162 DEBUGASSERT(len > 32);
163
164 /* put the number first so that the hostname gets cut off if too long */
165 snprintf(buf, len, "%ld%s", conn->port, hostname);
166}
167
168/* Look up the bundle with all the connections to the same host this
169 connectdata struct is setup to use. */
170struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
171 struct conncache *connc)
172{
173 struct connectbundle *bundle = NULL;
174 if(connc) {
175 char key[128];
176 hashkey(conn, key, sizeof(key));
177 CONN_LOCK(conn->data);
178 bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
179 CONN_UNLOCK(conn->data);
180 }
181
182 return bundle;
183}
184
185static bool conncache_add_bundle(struct conncache *connc,
186 char *key,
187 struct connectbundle *bundle)
188{
189 void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
190
191 return p?TRUE:FALSE;
192}
193
194static void conncache_remove_bundle(struct conncache *connc,
195 struct connectbundle *bundle)
196{
197 struct curl_hash_iterator iter;
198 struct curl_hash_element *he;
199
200 if(!connc)
201 return;
202
203 Curl_hash_start_iterate(&connc->hash, &iter);
204
205 he = Curl_hash_next_element(&iter);
206 while(he) {
207 if(he->ptr == bundle) {
208 /* The bundle is destroyed by the hash destructor function,
209 free_bundle_hash_entry() */
210 Curl_hash_delete(&connc->hash, he->key, he->key_len);
211 return;
212 }
213
214 he = Curl_hash_next_element(&iter);
215 }
216}
217
218CURLcode Curl_conncache_add_conn(struct conncache *connc,
219 struct connectdata *conn)
220{
221 CURLcode result;
222 struct connectbundle *bundle;
223 struct connectbundle *new_bundle = NULL;
224 struct Curl_easy *data = conn->data;
225
226 bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
227 if(!bundle) {
228 int rc;
229 char key[128];
230
231 result = bundle_create(data, &new_bundle);
232 if(result)
233 return result;
234
235 hashkey(conn, key, sizeof(key));
236 CONN_LOCK(data);
237 rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
238 CONN_UNLOCK(data);
239
240 if(!rc) {
241 bundle_destroy(new_bundle);
242 return CURLE_OUT_OF_MEMORY;
243 }
244 bundle = new_bundle;
245 }
246
247 CONN_LOCK(data);
248 result = bundle_add_conn(bundle, conn);
249 if(result) {
250 if(new_bundle)
251 conncache_remove_bundle(data->state.conn_cache, new_bundle);
252 CONN_UNLOCK(data);
253 return result;
254 }
255 CONN_UNLOCK(data);
256
257 conn->connection_id = connc->next_connection_id++;
258 connc->num_connections++;
259
260 DEBUGF(infof(conn->data, "Added connection %ld. "
261 "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
262 conn->connection_id, (curl_off_t) connc->num_connections));
263
264 return CURLE_OK;
265}
266
267void Curl_conncache_remove_conn(struct conncache *connc,
268 struct connectdata *conn)
269{
270 struct connectbundle *bundle = conn->bundle;
271
272 /* The bundle pointer can be NULL, since this function can be called
273 due to a failed connection attempt, before being added to a bundle */
274 if(bundle) {
275 CONN_LOCK(conn->data);
276 bundle_remove_conn(bundle, conn);
277 if(bundle->num_connections == 0)
278 conncache_remove_bundle(connc, bundle);
279 CONN_UNLOCK(conn->data);
280 if(connc) {
281 connc->num_connections--;
282
283 DEBUGF(infof(conn->data, "The cache now contains %"
284 CURL_FORMAT_CURL_OFF_TU " members\n",
285 (curl_off_t) connc->num_connections));
286 }
287 }
288}
289
290/* This function iterates the entire connection cache and calls the
291 function func() with the connection pointer as the first argument
292 and the supplied 'param' argument as the other,
293
294 Return 0 from func() to continue the loop, return 1 to abort it.
295 */
296void Curl_conncache_foreach(struct Curl_easy *data,
297 struct conncache *connc,
298 void *param,
299 int (*func)(struct connectdata *conn, void *param))
300{
301 struct curl_hash_iterator iter;
302 struct curl_llist_element *curr;
303 struct curl_hash_element *he;
304
305 if(!connc)
306 return;
307
308 CONN_LOCK(data);
309 Curl_hash_start_iterate(&connc->hash, &iter);
310
311 he = Curl_hash_next_element(&iter);
312 while(he) {
313 struct connectbundle *bundle;
314
315 bundle = he->ptr;
316 he = Curl_hash_next_element(&iter);
317
318 curr = bundle->conn_list.head;
319 while(curr) {
320 /* Yes, we need to update curr before calling func(), because func()
321 might decide to remove the connection */
322 struct connectdata *conn = curr->ptr;
323 curr = curr->next;
324
325 if(1 == func(conn, param)) {
326 CONN_UNLOCK(data);
327 return;
328 }
329 }
330 }
331 CONN_UNLOCK(data);
332}
333
334/* Return the first connection found in the cache. Used when closing all
335 connections.
336
337 NOTE: no locking is done here as this is presumably only done when cleaning
338 up a cache!
339*/
340struct connectdata *
341Curl_conncache_find_first_connection(struct conncache *connc)
342{
343 struct curl_hash_iterator iter;
344 struct curl_hash_element *he;
345 struct connectbundle *bundle;
346
347 Curl_hash_start_iterate(&connc->hash, &iter);
348
349 he = Curl_hash_next_element(&iter);
350 while(he) {
351 struct curl_llist_element *curr;
352 bundle = he->ptr;
353
354 curr = bundle->conn_list.head;
355 if(curr) {
356 return curr->ptr;
357 }
358
359 he = Curl_hash_next_element(&iter);
360 }
361
362 return NULL;
363}
364
365/*
366 * This function finds the connection in the connection
367 * cache that has been unused for the longest time.
368 *
369 * Returns the pointer to the oldest idle connection, or NULL if none was
370 * found.
371 */
372struct connectdata *
373Curl_conncache_oldest_idle(struct Curl_easy *data)
374{
375 struct conncache *bc = data->state.conn_cache;
376 struct curl_hash_iterator iter;
377 struct curl_llist_element *curr;
378 struct curl_hash_element *he;
379 timediff_t highscore =- 1;
380 timediff_t score;
381 struct curltime now;
382 struct connectdata *conn_candidate = NULL;
383 struct connectbundle *bundle;
384
385 now = Curl_now();
386
387 CONN_LOCK(data);
388 Curl_hash_start_iterate(&bc->hash, &iter);
389
390 he = Curl_hash_next_element(&iter);
391 while(he) {
392 struct connectdata *conn;
393
394 bundle = he->ptr;
395
396 curr = bundle->conn_list.head;
397 while(curr) {
398 conn = curr->ptr;
399
400 if(!conn->inuse) {
401 /* Set higher score for the age passed since the connection was used */
402 score = Curl_timediff(now, conn->now);
403
404 if(score > highscore) {
405 highscore = score;
406 conn_candidate = conn;
407 }
408 }
409 curr = curr->next;
410 }
411
412 he = Curl_hash_next_element(&iter);
413 }
414 CONN_UNLOCK(data);
415
416 return conn_candidate;
417}
418
419void Curl_conncache_close_all_connections(struct conncache *connc)
420{
421 struct connectdata *conn;
422
423 conn = Curl_conncache_find_first_connection(connc);
424 while(conn) {
425 SIGPIPE_VARIABLE(pipe_st);
426 conn->data = connc->closure_handle;
427
428 sigpipe_ignore(conn->data, &pipe_st);
429 conn->data->easy_conn = NULL; /* clear the easy handle's connection
430 pointer */
431 /* This will remove the connection from the cache */
432 connclose(conn, "kill all");
433 (void)Curl_disconnect(conn, FALSE);
434 sigpipe_restore(&pipe_st);
435
436 conn = Curl_conncache_find_first_connection(connc);
437 }
438
439 if(connc->closure_handle) {
440 SIGPIPE_VARIABLE(pipe_st);
441 sigpipe_ignore(connc->closure_handle, &pipe_st);
442
443 Curl_hostcache_clean(connc->closure_handle,
444 connc->closure_handle->dns.hostcache);
445 Curl_close(connc->closure_handle);
446 sigpipe_restore(&pipe_st);
447 }
448}
449
450#if 0
451/* Useful for debugging the connection cache */
452void Curl_conncache_print(struct conncache *connc)
453{
454 struct curl_hash_iterator iter;
455 struct curl_llist_element *curr;
456 struct curl_hash_element *he;
457
458 if(!connc)
459 return;
460
461 fprintf(stderr, "=Bundle cache=\n");
462
463 Curl_hash_start_iterate(connc->hash, &iter);
464
465 he = Curl_hash_next_element(&iter);
466 while(he) {
467 struct connectbundle *bundle;
468 struct connectdata *conn;
469
470 bundle = he->ptr;
471
472 fprintf(stderr, "%s -", he->key);
473 curr = bundle->conn_list->head;
474 while(curr) {
475 conn = curr->ptr;
476
477 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
478 curr = curr->next;
479 }
480 fprintf(stderr, "\n");
481
482 he = Curl_hash_next_element(&iter);
483 }
484}
485#endif
Note: See TracBrowser for help on using the repository browser.