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