Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmcurl/lib/conncache.c
3153 views
1
/***************************************************************************
2
* _ _ ____ _
3
* Project ___| | | | _ \| |
4
* / __| | | | |_) | |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
7
*
8
* Copyright (C) Linus Nielsen Feltzing, <[email protected]>
9
* Copyright (C) Daniel Stenberg, <[email protected]>, 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.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
* SPDX-License-Identifier: curl
23
*
24
***************************************************************************/
25
26
#include "curl_setup.h"
27
28
#include <curl/curl.h>
29
30
#include "urldata.h"
31
#include "url.h"
32
#include "cfilters.h"
33
#include "progress.h"
34
#include "multiif.h"
35
#include "multi_ev.h"
36
#include "sendf.h"
37
#include "cshutdn.h"
38
#include "conncache.h"
39
#include "http_negotiate.h"
40
#include "http_ntlm.h"
41
#include "share.h"
42
#include "sigpipe.h"
43
#include "connect.h"
44
#include "select.h"
45
#include "curlx/strparse.h"
46
#include "uint-table.h"
47
48
/* The last 3 #include files should be in this order */
49
#include "curl_printf.h"
50
#include "curl_memory.h"
51
#include "memdebug.h"
52
53
54
#define CPOOL_IS_LOCKED(c) ((c) && (c)->locked)
55
56
#define CPOOL_LOCK(c,d) \
57
do { \
58
if((c)) { \
59
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
60
Curl_share_lock((d), CURL_LOCK_DATA_CONNECT, \
61
CURL_LOCK_ACCESS_SINGLE); \
62
DEBUGASSERT(!(c)->locked); \
63
(c)->locked = TRUE; \
64
} \
65
} while(0)
66
67
#define CPOOL_UNLOCK(c,d) \
68
do { \
69
if((c)) { \
70
DEBUGASSERT((c)->locked); \
71
(c)->locked = FALSE; \
72
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
73
Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT); \
74
} \
75
} while(0)
76
77
78
/* A list of connections to the same destination. */
79
struct cpool_bundle {
80
struct Curl_llist conns; /* connections in the bundle */
81
size_t dest_len; /* total length of destination, including NUL */
82
char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
83
};
84
85
86
static void cpool_discard_conn(struct cpool *cpool,
87
struct Curl_easy *data,
88
struct connectdata *conn,
89
bool aborted);
90
91
static struct cpool_bundle *cpool_bundle_create(const char *dest)
92
{
93
struct cpool_bundle *bundle;
94
size_t dest_len = strlen(dest);
95
96
bundle = calloc(1, sizeof(*bundle) + dest_len);
97
if(!bundle)
98
return NULL;
99
Curl_llist_init(&bundle->conns, NULL);
100
bundle->dest_len = dest_len + 1;
101
memcpy(bundle->dest, dest, bundle->dest_len);
102
return bundle;
103
}
104
105
static void cpool_bundle_destroy(struct cpool_bundle *bundle)
106
{
107
DEBUGASSERT(!Curl_llist_count(&bundle->conns));
108
free(bundle);
109
}
110
111
/* Add a connection to a bundle */
112
static void cpool_bundle_add(struct cpool_bundle *bundle,
113
struct connectdata *conn)
114
{
115
DEBUGASSERT(!Curl_node_llist(&conn->cpool_node));
116
Curl_llist_append(&bundle->conns, conn, &conn->cpool_node);
117
conn->bits.in_cpool = TRUE;
118
}
119
120
/* Remove a connection from a bundle */
121
static void cpool_bundle_remove(struct cpool_bundle *bundle,
122
struct connectdata *conn)
123
{
124
(void)bundle;
125
DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns);
126
Curl_node_remove(&conn->cpool_node);
127
conn->bits.in_cpool = FALSE;
128
}
129
130
static void cpool_bundle_free_entry(void *freethis)
131
{
132
cpool_bundle_destroy((struct cpool_bundle *)freethis);
133
}
134
135
void Curl_cpool_init(struct cpool *cpool,
136
struct Curl_easy *idata,
137
struct Curl_share *share,
138
size_t size)
139
{
140
Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
141
curlx_str_key_compare, cpool_bundle_free_entry);
142
143
DEBUGASSERT(idata);
144
145
cpool->idata = idata;
146
cpool->share = share;
147
cpool->initialised = TRUE;
148
}
149
150
/* Return the "first" connection in the pool or NULL. */
151
static struct connectdata *cpool_get_first(struct cpool *cpool)
152
{
153
struct Curl_hash_iterator iter;
154
struct Curl_hash_element *he;
155
struct cpool_bundle *bundle;
156
struct Curl_llist_node *conn_node;
157
158
Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
159
for(he = Curl_hash_next_element(&iter); he;
160
he = Curl_hash_next_element(&iter)) {
161
bundle = he->ptr;
162
conn_node = Curl_llist_head(&bundle->conns);
163
if(conn_node)
164
return Curl_node_elem(conn_node);
165
}
166
return NULL;
167
}
168
169
170
static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
171
struct connectdata *conn)
172
{
173
return Curl_hash_pick(&cpool->dest2bundle,
174
conn->destination, strlen(conn->destination) + 1);
175
}
176
177
178
static void cpool_remove_bundle(struct cpool *cpool,
179
struct cpool_bundle *bundle)
180
{
181
if(!cpool)
182
return;
183
Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
184
}
185
186
187
static void cpool_remove_conn(struct cpool *cpool,
188
struct connectdata *conn)
189
{
190
struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
191
DEBUGASSERT(cpool);
192
if(list) {
193
/* The connection is certainly in the pool, but where? */
194
struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
195
if(bundle && (list == &bundle->conns)) {
196
cpool_bundle_remove(bundle, conn);
197
if(!Curl_llist_count(&bundle->conns))
198
cpool_remove_bundle(cpool, bundle);
199
conn->bits.in_cpool = FALSE;
200
cpool->num_conn--;
201
}
202
else {
203
/* Should have been in the bundle list */
204
DEBUGASSERT(NULL);
205
}
206
}
207
}
208
209
void Curl_cpool_destroy(struct cpool *cpool)
210
{
211
if(cpool && cpool->initialised && cpool->idata) {
212
struct connectdata *conn;
213
SIGPIPE_VARIABLE(pipe_st);
214
215
CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections",
216
cpool->share ? "[SHARE] " : "", cpool->num_conn);
217
/* Move all connections to the shutdown list */
218
sigpipe_init(&pipe_st);
219
CPOOL_LOCK(cpool, cpool->idata);
220
conn = cpool_get_first(cpool);
221
while(conn) {
222
cpool_remove_conn(cpool, conn);
223
sigpipe_apply(cpool->idata, &pipe_st);
224
connclose(conn, "kill all");
225
cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
226
conn = cpool_get_first(cpool);
227
}
228
CPOOL_UNLOCK(cpool, cpool->idata);
229
sigpipe_restore(&pipe_st);
230
Curl_hash_destroy(&cpool->dest2bundle);
231
}
232
}
233
234
static struct cpool *cpool_get_instance(struct Curl_easy *data)
235
{
236
if(data) {
237
if(CURL_SHARE_KEEP_CONNECT(data->share))
238
return &data->share->cpool;
239
else if(data->multi_easy)
240
return &data->multi_easy->cpool;
241
else if(data->multi)
242
return &data->multi->cpool;
243
}
244
return NULL;
245
}
246
247
void Curl_cpool_xfer_init(struct Curl_easy *data)
248
{
249
struct cpool *cpool = cpool_get_instance(data);
250
251
DEBUGASSERT(cpool);
252
if(cpool) {
253
CPOOL_LOCK(cpool, data);
254
/* the identifier inside the connection cache */
255
data->id = cpool->next_easy_id++;
256
if(cpool->next_easy_id <= 0)
257
cpool->next_easy_id = 0;
258
data->state.lastconnect_id = -1;
259
260
CPOOL_UNLOCK(cpool, data);
261
}
262
else {
263
/* We should not get here, but in a non-debug build, do something */
264
data->id = 0;
265
data->state.lastconnect_id = -1;
266
}
267
}
268
269
static struct cpool_bundle *
270
cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
271
{
272
struct cpool_bundle *bundle;
273
274
bundle = cpool_bundle_create(conn->destination);
275
if(!bundle)
276
return NULL;
277
278
if(!Curl_hash_add(&cpool->dest2bundle,
279
bundle->dest, bundle->dest_len, bundle)) {
280
cpool_bundle_destroy(bundle);
281
return NULL;
282
}
283
return bundle;
284
}
285
286
static struct connectdata *
287
cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
288
{
289
struct Curl_llist_node *curr;
290
timediff_t highscore = -1;
291
timediff_t score;
292
struct curltime now;
293
struct connectdata *oldest_idle = NULL;
294
struct connectdata *conn;
295
296
now = curlx_now();
297
curr = Curl_llist_head(&bundle->conns);
298
while(curr) {
299
conn = Curl_node_elem(curr);
300
301
if(!CONN_INUSE(conn)) {
302
/* Set higher score for the age passed since the connection was used */
303
score = curlx_timediff(now, conn->lastused);
304
305
if(score > highscore) {
306
highscore = score;
307
oldest_idle = conn;
308
}
309
}
310
curr = Curl_node_next(curr);
311
}
312
return oldest_idle;
313
}
314
315
static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
316
{
317
struct Curl_hash_iterator iter;
318
struct Curl_llist_node *curr;
319
struct Curl_hash_element *he;
320
struct connectdata *oldest_idle = NULL;
321
struct cpool_bundle *bundle;
322
struct curltime now;
323
timediff_t highscore =- 1;
324
timediff_t score;
325
326
now = curlx_now();
327
Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
328
329
for(he = Curl_hash_next_element(&iter); he;
330
he = Curl_hash_next_element(&iter)) {
331
struct connectdata *conn;
332
bundle = he->ptr;
333
334
for(curr = Curl_llist_head(&bundle->conns); curr;
335
curr = Curl_node_next(curr)) {
336
conn = Curl_node_elem(curr);
337
if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
338
continue;
339
/* Set higher score for the age passed since the connection was used */
340
score = curlx_timediff(now, conn->lastused);
341
if(score > highscore) {
342
highscore = score;
343
oldest_idle = conn;
344
}
345
}
346
}
347
return oldest_idle;
348
}
349
350
351
int Curl_cpool_check_limits(struct Curl_easy *data,
352
struct connectdata *conn)
353
{
354
struct cpool *cpool = cpool_get_instance(data);
355
struct cpool_bundle *bundle;
356
size_t dest_limit = 0;
357
size_t total_limit = 0;
358
size_t shutdowns;
359
int result = CPOOL_LIMIT_OK;
360
361
if(!cpool)
362
return CPOOL_LIMIT_OK;
363
364
if(cpool->idata->multi) {
365
dest_limit = cpool->idata->multi->max_host_connections;
366
total_limit = cpool->idata->multi->max_total_connections;
367
}
368
369
if(!dest_limit && !total_limit)
370
return CPOOL_LIMIT_OK;
371
372
CPOOL_LOCK(cpool, cpool->idata);
373
if(dest_limit) {
374
size_t live;
375
376
bundle = cpool_find_bundle(cpool, conn);
377
live = bundle ? Curl_llist_count(&bundle->conns) : 0;
378
shutdowns = Curl_cshutdn_dest_count(data, conn->destination);
379
while((live + shutdowns) >= dest_limit) {
380
if(shutdowns) {
381
/* close one connection in shutdown right away, if we can */
382
if(!Curl_cshutdn_close_oldest(data, conn->destination))
383
break;
384
}
385
else if(!bundle)
386
break;
387
else {
388
struct connectdata *oldest_idle = NULL;
389
/* The bundle is full. Extract the oldest connection that may
390
* be removed now, if there is one. */
391
oldest_idle = cpool_bundle_get_oldest_idle(bundle);
392
if(!oldest_idle)
393
break;
394
/* disconnect the old conn and continue */
395
CURL_TRC_M(data, "Discarding connection #%"
396
FMT_OFF_T " from %zu to reach destination "
397
"limit of %zu", oldest_idle->connection_id,
398
Curl_llist_count(&bundle->conns), dest_limit);
399
Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
400
401
/* in case the bundle was destroyed in disconnect, look it up again */
402
bundle = cpool_find_bundle(cpool, conn);
403
live = bundle ? Curl_llist_count(&bundle->conns) : 0;
404
}
405
shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination);
406
}
407
if((live + shutdowns) >= dest_limit) {
408
result = CPOOL_LIMIT_DEST;
409
goto out;
410
}
411
}
412
413
if(total_limit) {
414
shutdowns = Curl_cshutdn_count(cpool->idata);
415
while((cpool->num_conn + shutdowns) >= total_limit) {
416
if(shutdowns) {
417
/* close one connection in shutdown right away, if we can */
418
if(!Curl_cshutdn_close_oldest(data, NULL))
419
break;
420
}
421
else {
422
struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
423
if(!oldest_idle)
424
break;
425
/* disconnect the old conn and continue */
426
CURL_TRC_M(data, "Discarding connection #%"
427
FMT_OFF_T " from %zu to reach total "
428
"limit of %zu",
429
oldest_idle->connection_id, cpool->num_conn, total_limit);
430
Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
431
}
432
shutdowns = Curl_cshutdn_count(cpool->idata);
433
}
434
if((cpool->num_conn + shutdowns) >= total_limit) {
435
result = CPOOL_LIMIT_TOTAL;
436
goto out;
437
}
438
}
439
440
out:
441
CPOOL_UNLOCK(cpool, cpool->idata);
442
return result;
443
}
444
445
CURLcode Curl_cpool_add(struct Curl_easy *data,
446
struct connectdata *conn)
447
{
448
CURLcode result = CURLE_OK;
449
struct cpool_bundle *bundle = NULL;
450
struct cpool *cpool = cpool_get_instance(data);
451
DEBUGASSERT(conn);
452
453
DEBUGASSERT(cpool);
454
if(!cpool)
455
return CURLE_FAILED_INIT;
456
457
CPOOL_LOCK(cpool, data);
458
bundle = cpool_find_bundle(cpool, conn);
459
if(!bundle) {
460
bundle = cpool_add_bundle(cpool, conn);
461
if(!bundle) {
462
result = CURLE_OUT_OF_MEMORY;
463
goto out;
464
}
465
}
466
467
cpool_bundle_add(bundle, conn);
468
conn->connection_id = cpool->next_connection_id++;
469
cpool->num_conn++;
470
CURL_TRC_M(data, "[CPOOL] added connection %" FMT_OFF_T ". "
471
"The cache now contains %zu members",
472
conn->connection_id, cpool->num_conn);
473
out:
474
CPOOL_UNLOCK(cpool, data);
475
476
return result;
477
}
478
479
/* This function iterates the entire connection pool and calls the function
480
func() with the connection pointer as the first argument and the supplied
481
'param' argument as the other.
482
483
The cpool lock is still held when the callback is called. It needs it,
484
so that it can safely continue traversing the lists once the callback
485
returns.
486
487
Returns TRUE if the loop was aborted due to the callback's return code.
488
489
Return 0 from func() to continue the loop, return 1 to abort it.
490
*/
491
static bool cpool_foreach(struct Curl_easy *data,
492
struct cpool *cpool,
493
void *param,
494
int (*func)(struct Curl_easy *data,
495
struct connectdata *conn, void *param))
496
{
497
struct Curl_hash_iterator iter;
498
struct Curl_hash_element *he;
499
500
if(!cpool)
501
return FALSE;
502
503
Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
504
505
he = Curl_hash_next_element(&iter);
506
while(he) {
507
struct Curl_llist_node *curr;
508
struct cpool_bundle *bundle = he->ptr;
509
he = Curl_hash_next_element(&iter);
510
511
curr = Curl_llist_head(&bundle->conns);
512
while(curr) {
513
/* Yes, we need to update curr before calling func(), because func()
514
might decide to remove the connection */
515
struct connectdata *conn = Curl_node_elem(curr);
516
curr = Curl_node_next(curr);
517
518
if(1 == func(data, conn, param)) {
519
return TRUE;
520
}
521
}
522
}
523
return FALSE;
524
}
525
526
/*
527
* A connection (already in the pool) has become idle. Do any
528
* cleanups in regard to the pool's limits.
529
*
530
* Return TRUE if idle connection kept in pool, FALSE if closed.
531
*/
532
bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
533
struct connectdata *conn)
534
{
535
unsigned int maxconnects = !data->multi->maxconnects ?
536
(Curl_multi_xfers_running(data->multi) * 4) : data->multi->maxconnects;
537
struct connectdata *oldest_idle = NULL;
538
struct cpool *cpool = cpool_get_instance(data);
539
bool kept = TRUE;
540
541
conn->lastused = curlx_now(); /* it was used up until now */
542
if(cpool && maxconnects) {
543
/* may be called form a callback already under lock */
544
bool do_lock = !CPOOL_IS_LOCKED(cpool);
545
if(do_lock)
546
CPOOL_LOCK(cpool, data);
547
if(cpool->num_conn > maxconnects) {
548
infof(data, "Connection pool is full, closing the oldest of %zu/%u",
549
cpool->num_conn, maxconnects);
550
551
oldest_idle = cpool_get_oldest_idle(cpool);
552
kept = (oldest_idle != conn);
553
if(oldest_idle) {
554
Curl_conn_terminate(data, oldest_idle, FALSE);
555
}
556
}
557
if(do_lock)
558
CPOOL_UNLOCK(cpool, data);
559
}
560
561
return kept;
562
}
563
564
bool Curl_cpool_find(struct Curl_easy *data,
565
const char *destination,
566
Curl_cpool_conn_match_cb *conn_cb,
567
Curl_cpool_done_match_cb *done_cb,
568
void *userdata)
569
{
570
struct cpool *cpool = cpool_get_instance(data);
571
struct cpool_bundle *bundle;
572
bool result = FALSE;
573
574
DEBUGASSERT(cpool);
575
DEBUGASSERT(conn_cb);
576
if(!cpool)
577
return FALSE;
578
579
CPOOL_LOCK(cpool, data);
580
bundle = Curl_hash_pick(&cpool->dest2bundle,
581
CURL_UNCONST(destination),
582
strlen(destination) + 1);
583
if(bundle) {
584
struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
585
while(curr) {
586
struct connectdata *conn = Curl_node_elem(curr);
587
/* Get next node now. callback might discard current */
588
curr = Curl_node_next(curr);
589
590
if(conn_cb(conn, userdata)) {
591
result = TRUE;
592
break;
593
}
594
}
595
}
596
597
if(done_cb) {
598
result = done_cb(result, userdata);
599
}
600
CPOOL_UNLOCK(cpool, data);
601
return result;
602
}
603
604
static void cpool_discard_conn(struct cpool *cpool,
605
struct Curl_easy *data,
606
struct connectdata *conn,
607
bool aborted)
608
{
609
bool done = FALSE;
610
611
DEBUGASSERT(data);
612
DEBUGASSERT(!data->conn);
613
DEBUGASSERT(cpool);
614
DEBUGASSERT(!conn->bits.in_cpool);
615
616
/*
617
* If this connection is not marked to force-close, leave it open if there
618
* are other users of it
619
*/
620
if(CONN_INUSE(conn) && !aborted) {
621
CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
622
" still in use by %u transfers", conn->connection_id,
623
CONN_ATTACHED(conn));
624
return;
625
}
626
627
/* treat the connection as aborted in CONNECT_ONLY situations, we do
628
* not know what the APP did with it. */
629
if(conn->connect_only)
630
aborted = TRUE;
631
conn->bits.aborted = aborted;
632
633
/* We do not shutdown dead connections. The term 'dead' can be misleading
634
* here, as we also mark errored connections/transfers as 'dead'.
635
* If we do a shutdown for an aborted transfer, the server might think
636
* it was successful otherwise (for example an ftps: upload). This is
637
* not what we want. */
638
if(aborted)
639
done = TRUE;
640
if(!done) {
641
/* Attempt to shutdown the connection right away. */
642
Curl_cshutdn_run_once(cpool->idata, conn, &done);
643
}
644
645
if(done || !data->multi)
646
Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
647
else
648
Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
649
}
650
651
void Curl_conn_terminate(struct Curl_easy *data,
652
struct connectdata *conn,
653
bool aborted)
654
{
655
struct cpool *cpool = cpool_get_instance(data);
656
bool do_lock;
657
658
DEBUGASSERT(cpool);
659
DEBUGASSERT(data && !data->conn);
660
if(!cpool)
661
return;
662
663
/* If this connection is not marked to force-close, leave it open if there
664
* are other users of it */
665
if(CONN_INUSE(conn) && !aborted) {
666
DEBUGASSERT(0); /* does this ever happen? */
667
DEBUGF(infof(data, "Curl_disconnect when inuse: %u", CONN_ATTACHED(conn)));
668
return;
669
}
670
671
/* This method may be called while we are under lock, e.g. from a
672
* user callback in find. */
673
do_lock = !CPOOL_IS_LOCKED(cpool);
674
if(do_lock)
675
CPOOL_LOCK(cpool, data);
676
677
if(conn->bits.in_cpool) {
678
cpool_remove_conn(cpool, conn);
679
DEBUGASSERT(!conn->bits.in_cpool);
680
}
681
682
/* treat the connection as aborted in CONNECT_ONLY situations,
683
* so no graceful shutdown is attempted. */
684
if(conn->connect_only)
685
aborted = TRUE;
686
687
if(data->multi) {
688
/* Add it to the multi's cpool for shutdown handling */
689
infof(data, "%s connection #%" FMT_OFF_T,
690
aborted ? "closing" : "shutting down", conn->connection_id);
691
cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
692
}
693
else {
694
/* No multi available, terminate */
695
infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
696
Curl_cshutdn_terminate(cpool->idata, conn, !aborted);
697
}
698
699
if(do_lock)
700
CPOOL_UNLOCK(cpool, data);
701
}
702
703
704
struct cpool_reaper_ctx {
705
struct curltime now;
706
};
707
708
static int cpool_reap_dead_cb(struct Curl_easy *data,
709
struct connectdata *conn, void *param)
710
{
711
struct cpool_reaper_ctx *rctx = param;
712
if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
713
/* stop the iteration here, pass back the connection that was pruned */
714
Curl_conn_terminate(data, conn, FALSE);
715
return 1;
716
}
717
return 0; /* continue iteration */
718
}
719
720
/*
721
* This function scans the data's connection pool for half-open/dead
722
* connections, closes and removes them.
723
* The cleanup is done at most once per second.
724
*
725
* When called, this transfer has no connection attached.
726
*/
727
void Curl_cpool_prune_dead(struct Curl_easy *data)
728
{
729
struct cpool *cpool = cpool_get_instance(data);
730
struct cpool_reaper_ctx rctx;
731
timediff_t elapsed;
732
733
if(!cpool)
734
return;
735
736
rctx.now = curlx_now();
737
CPOOL_LOCK(cpool, data);
738
elapsed = curlx_timediff(rctx.now, cpool->last_cleanup);
739
740
if(elapsed >= 1000L) {
741
while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
742
;
743
cpool->last_cleanup = rctx.now;
744
}
745
CPOOL_UNLOCK(cpool, data);
746
}
747
748
static int conn_upkeep(struct Curl_easy *data,
749
struct connectdata *conn,
750
void *param)
751
{
752
struct curltime *now = param;
753
Curl_conn_upkeep(data, conn, now);
754
return 0; /* continue iteration */
755
}
756
757
CURLcode Curl_cpool_upkeep(void *data)
758
{
759
struct cpool *cpool = cpool_get_instance(data);
760
struct curltime now = curlx_now();
761
762
if(!cpool)
763
return CURLE_OK;
764
765
CPOOL_LOCK(cpool, data);
766
cpool_foreach(data, cpool, &now, conn_upkeep);
767
CPOOL_UNLOCK(cpool, data);
768
return CURLE_OK;
769
}
770
771
struct cpool_find_ctx {
772
curl_off_t id;
773
struct connectdata *conn;
774
};
775
776
static int cpool_find_conn(struct Curl_easy *data,
777
struct connectdata *conn, void *param)
778
{
779
struct cpool_find_ctx *fctx = param;
780
(void)data;
781
if(conn->connection_id == fctx->id) {
782
fctx->conn = conn;
783
return 1;
784
}
785
return 0;
786
}
787
788
struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
789
curl_off_t conn_id)
790
{
791
struct cpool *cpool = cpool_get_instance(data);
792
struct cpool_find_ctx fctx;
793
794
if(!cpool)
795
return NULL;
796
fctx.id = conn_id;
797
fctx.conn = NULL;
798
CPOOL_LOCK(cpool, data);
799
cpool_foreach(data, cpool, &fctx, cpool_find_conn);
800
CPOOL_UNLOCK(cpool, data);
801
return fctx.conn;
802
}
803
804
struct cpool_do_conn_ctx {
805
curl_off_t id;
806
Curl_cpool_conn_do_cb *cb;
807
void *cbdata;
808
};
809
810
static int cpool_do_conn(struct Curl_easy *data,
811
struct connectdata *conn, void *param)
812
{
813
struct cpool_do_conn_ctx *dctx = param;
814
(void)data;
815
if(conn->connection_id == dctx->id) {
816
dctx->cb(conn, data, dctx->cbdata);
817
return 1;
818
}
819
return 0;
820
}
821
822
void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
823
Curl_cpool_conn_do_cb *cb, void *cbdata)
824
{
825
struct cpool *cpool = cpool_get_instance(data);
826
struct cpool_do_conn_ctx dctx;
827
828
if(!cpool)
829
return;
830
dctx.id = conn_id;
831
dctx.cb = cb;
832
dctx.cbdata = cbdata;
833
CPOOL_LOCK(cpool, data);
834
cpool_foreach(data, cpool, &dctx, cpool_do_conn);
835
CPOOL_UNLOCK(cpool, data);
836
}
837
838
void Curl_cpool_do_locked(struct Curl_easy *data,
839
struct connectdata *conn,
840
Curl_cpool_conn_do_cb *cb, void *cbdata)
841
{
842
struct cpool *cpool = cpool_get_instance(data);
843
if(cpool) {
844
CPOOL_LOCK(cpool, data);
845
cb(conn, data, cbdata);
846
CPOOL_UNLOCK(cpool, data);
847
}
848
else
849
cb(conn, data, cbdata);
850
}
851
852
#if 0
853
/* Useful for debugging the connection pool */
854
void Curl_cpool_print(struct cpool *cpool)
855
{
856
struct Curl_hash_iterator iter;
857
struct Curl_llist_node *curr;
858
struct Curl_hash_element *he;
859
860
if(!cpool)
861
return;
862
863
fprintf(stderr, "=Bundle cache=\n");
864
865
Curl_hash_start_iterate(cpool->dest2bundle, &iter);
866
867
he = Curl_hash_next_element(&iter);
868
while(he) {
869
struct cpool_bundle *bundle;
870
struct connectdata *conn;
871
872
bundle = he->ptr;
873
874
fprintf(stderr, "%s -", he->key);
875
curr = Curl_llist_head(bundle->conns);
876
while(curr) {
877
conn = Curl_node_elem(curr);
878
879
fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
880
curr = Curl_node_next(curr);
881
}
882
fprintf(stderr, "\n");
883
884
he = Curl_hash_next_element(&iter);
885
}
886
}
887
#endif
888
889