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