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