Coverage Report

Created: 2024-09-08 06:32

/src/curl/lib/conncache.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
9
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
10
 *
11
 * This software is licensed as described in the file COPYING, which
12
 * you should have received as part of this distribution. The terms
13
 * are also available at https://curl.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 "sendf.h"
36
#include "conncache.h"
37
#include "http_negotiate.h"
38
#include "http_ntlm.h"
39
#include "share.h"
40
#include "sigpipe.h"
41
#include "connect.h"
42
#include "select.h"
43
#include "strcase.h"
44
45
/* The last 3 #include files should be in this order */
46
#include "curl_printf.h"
47
#include "curl_memory.h"
48
#include "memdebug.h"
49
50
51
65.2k
#define CPOOL_IS_LOCKED(c)    ((c) && (c)->locked)
52
53
#define CPOOL_LOCK(c)                                                   \
54
61.6M
  do {                                                                  \
55
61.6M
    if((c)) {                                                           \
56
61.6M
      if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
57
61.6M
        Curl_share_lock(((c)->idata), CURL_LOCK_DATA_CONNECT,           \
58
0
                        CURL_LOCK_ACCESS_SINGLE);                       \
59
61.6M
      DEBUGASSERT(!(c)->locked);                                        \
60
61.6M
      (c)->locked = TRUE;                                               \
61
61.6M
    }                                                                   \
62
61.6M
  } while(0)
63
64
#define CPOOL_UNLOCK(c)                                                 \
65
61.6M
  do {                                                                  \
66
61.6M
    if((c)) {                                                           \
67
61.6M
      DEBUGASSERT((c)->locked);                                         \
68
61.6M
      (c)->locked = FALSE;                                              \
69
61.6M
      if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
70
61.6M
        Curl_share_unlock((c)->idata, CURL_LOCK_DATA_CONNECT);          \
71
61.6M
    }                                                                   \
72
61.6M
  } while(0)
73
74
75
/* A list of connections to the same destinationn. */
76
struct cpool_bundle {
77
  struct Curl_llist conns; /* connections in the bundle */
78
  size_t dest_len; /* total length of destination, including NUL */
79
  char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
80
};
81
82
83
static void cpool_discard_conn(struct cpool *cpool,
84
                               struct Curl_easy *data,
85
                               struct connectdata *conn,
86
                               bool aborted);
87
static void cpool_close_and_destroy(struct cpool *cpool,
88
                                    struct connectdata *conn,
89
                                    struct Curl_easy *data,
90
                                    bool do_shutdown);
91
static void cpool_run_conn_shutdown(struct Curl_easy *data,
92
                                    struct connectdata *conn,
93
                                    bool *done);
94
static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
95
                                            struct connectdata *conn);
96
static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
97
                                          struct Curl_easy *data,
98
                                          struct connectdata *conn);
99
static void cpool_shutdown_all(struct cpool *cpool,
100
                               struct Curl_easy *data, int timeout_ms);
101
static void cpool_close_and_destroy_all(struct cpool *cpool);
102
static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool);
103
104
static struct cpool_bundle *cpool_bundle_create(const char *dest,
105
                                                size_t dest_len)
106
44.6k
{
107
44.6k
  struct cpool_bundle *bundle;
108
44.6k
  bundle = calloc(1, sizeof(*bundle) + dest_len);
109
44.6k
  if(!bundle)
110
0
    return NULL;
111
44.6k
  Curl_llist_init(&bundle->conns, NULL);
112
44.6k
  bundle->dest_len = dest_len;
113
44.6k
  memcpy(bundle->dest, dest, dest_len);
114
44.6k
  return bundle;
115
44.6k
}
116
117
static void cpool_bundle_destroy(struct cpool_bundle *bundle)
118
44.6k
{
119
44.6k
  DEBUGASSERT(!Curl_llist_count(&bundle->conns));
120
44.6k
  free(bundle);
121
44.6k
}
122
123
/* Add a connection to a bundle */
124
static void cpool_bundle_add(struct cpool_bundle *bundle,
125
                             struct connectdata *conn)
126
45.0k
{
127
45.0k
  DEBUGASSERT(!Curl_node_llist(&conn->cpool_node));
128
45.0k
  Curl_llist_append(&bundle->conns, conn, &conn->cpool_node);
129
45.0k
  conn->bits.in_cpool = TRUE;
130
45.0k
}
131
132
/* Remove a connection from a bundle */
133
static void cpool_bundle_remove(struct cpool_bundle *bundle,
134
                                struct connectdata *conn)
135
45.0k
{
136
45.0k
  (void)bundle;
137
45.0k
  DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns);
138
45.0k
  Curl_node_remove(&conn->cpool_node);
139
45.0k
  conn->bits.in_cpool = FALSE;
140
45.0k
}
141
142
static void cpool_bundle_free_entry(void *freethis)
143
44.6k
{
144
44.6k
  cpool_bundle_destroy((struct cpool_bundle *)freethis);
145
44.6k
}
146
147
int Curl_cpool_init(struct cpool *cpool,
148
                        Curl_cpool_disconnect_cb *disconnect_cb,
149
                        struct Curl_multi *multi,
150
                        struct Curl_share *share,
151
                        size_t size)
152
66.8k
{
153
66.8k
  DEBUGASSERT(!!multi != !!share); /* either one */
154
66.8k
  Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
155
66.8k
                 Curl_str_key_compare, cpool_bundle_free_entry);
156
66.8k
  Curl_llist_init(&cpool->shutdowns, NULL);
157
158
66.8k
  DEBUGASSERT(disconnect_cb);
159
66.8k
  if(!disconnect_cb)
160
0
    return 1;
161
162
  /* allocate a new easy handle to use when closing cached connections */
163
66.8k
  cpool->idata = curl_easy_init();
164
66.8k
  if(!cpool->idata)
165
0
    return 1; /* bad */
166
66.8k
  cpool->idata->state.internal = true;
167
  /* TODO: this is quirky. We need an internal handle for certain
168
   * operations, but we do not add it to the multi (if there is one).
169
   * But we give it the multi so that socket event operations can work.
170
   * Probably better to have an internal handle owned by the multi that
171
   * can be used for cpool operations. */
172
66.8k
  cpool->idata->multi = multi;
173
66.8k
 #ifdef DEBUGBUILD
174
66.8k
  if(getenv("CURL_DEBUG"))
175
0
    cpool->idata->set.verbose = true;
176
66.8k
#endif
177
178
66.8k
  cpool->disconnect_cb = disconnect_cb;
179
66.8k
  cpool->idata->multi = cpool->multi = multi;
180
66.8k
  cpool->idata->share = cpool->share = share;
181
182
66.8k
  return 0; /* good */
183
66.8k
}
184
185
void Curl_cpool_destroy(struct cpool *cpool)
186
66.8k
{
187
66.8k
  if(cpool) {
188
66.8k
    if(cpool->idata) {
189
66.8k
      cpool_close_and_destroy_all(cpool);
190
      /* The internal closure handle is special and we need to
191
       * disconnect it from multi/share before closing it down. */
192
66.8k
      cpool->idata->multi = NULL;
193
66.8k
      cpool->idata->share = NULL;
194
66.8k
      Curl_close(&cpool->idata);
195
66.8k
    }
196
66.8k
    Curl_hash_destroy(&cpool->dest2bundle);
197
66.8k
    cpool->multi = NULL;
198
66.8k
  }
199
66.8k
}
200
201
static struct cpool *cpool_get_instance(struct Curl_easy *data)
202
369k
{
203
369k
  if(data) {
204
369k
    if(CURL_SHARE_KEEP_CONNECT(data->share))
205
0
      return &data->share->cpool;
206
369k
    else if(data->multi_easy)
207
0
      return &data->multi_easy->cpool;
208
369k
    else if(data->multi)
209
369k
      return &data->multi->cpool;
210
369k
  }
211
0
  return NULL;
212
369k
}
213
214
void Curl_cpool_xfer_init(struct Curl_easy *data)
215
66.8k
{
216
66.8k
  struct cpool *cpool = cpool_get_instance(data);
217
218
66.8k
  DEBUGASSERT(cpool);
219
66.8k
  if(cpool) {
220
66.8k
    CPOOL_LOCK(cpool);
221
    /* the identifier inside the connection cache */
222
66.8k
    data->id = cpool->next_easy_id++;
223
66.8k
    if(cpool->next_easy_id <= 0)
224
0
      cpool->next_easy_id = 0;
225
66.8k
    data->state.lastconnect_id = -1;
226
227
    /* The closure handle only ever has default timeouts set. To improve the
228
       state somewhat we clone the timeouts from each added handle so that the
229
       closure handle always has the same timeouts as the most recently added
230
       easy handle. */
231
66.8k
    cpool->idata->set.timeout = data->set.timeout;
232
66.8k
    cpool->idata->set.server_response_timeout =
233
66.8k
      data->set.server_response_timeout;
234
66.8k
    cpool->idata->set.no_signal = data->set.no_signal;
235
236
66.8k
    CPOOL_UNLOCK(cpool);
237
66.8k
  }
238
0
  else {
239
    /* We should not get here, but in a non-debug build, do something */
240
0
    data->id = 0;
241
0
    data->state.lastconnect_id = -1;
242
0
  }
243
66.8k
}
244
245
static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
246
                                              struct connectdata *conn)
247
90.1k
{
248
90.1k
  return Curl_hash_pick(&cpool->dest2bundle,
249
90.1k
                        conn->destination, conn->destination_len);
250
90.1k
}
251
252
static struct cpool_bundle *
253
cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
254
44.6k
{
255
44.6k
  struct cpool_bundle *bundle;
256
257
44.6k
  bundle = cpool_bundle_create(conn->destination, conn->destination_len);
258
44.6k
  if(!bundle)
259
0
    return NULL;
260
261
44.6k
  if(!Curl_hash_add(&cpool->dest2bundle,
262
44.6k
                    bundle->dest, bundle->dest_len, bundle)) {
263
0
    cpool_bundle_destroy(bundle);
264
0
    return NULL;
265
0
  }
266
44.6k
  return bundle;
267
44.6k
}
268
269
static void cpool_remove_bundle(struct cpool *cpool,
270
                                struct cpool_bundle *bundle)
271
44.6k
{
272
44.6k
  struct Curl_hash_iterator iter;
273
44.6k
  struct Curl_hash_element *he;
274
275
44.6k
  if(!cpool)
276
0
    return;
277
278
44.6k
  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
279
280
44.6k
  he = Curl_hash_next_element(&iter);
281
44.7k
  while(he) {
282
44.7k
    if(he->ptr == bundle) {
283
      /* The bundle is destroyed by the hash destructor function,
284
         free_bundle_hash_entry() */
285
44.6k
      Curl_hash_delete(&cpool->dest2bundle, he->key, he->key_len);
286
44.6k
      return;
287
44.6k
    }
288
289
21
    he = Curl_hash_next_element(&iter);
290
21
  }
291
44.6k
}
292
293
static struct connectdata *
294
cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle);
295
296
int Curl_cpool_check_limits(struct Curl_easy *data,
297
                            struct connectdata *conn)
298
43.8k
{
299
43.8k
  struct cpool *cpool = cpool_get_instance(data);
300
43.8k
  struct cpool_bundle *bundle;
301
43.8k
  size_t dest_limit = 0;
302
43.8k
  size_t total_limit = 0;
303
43.8k
  int result = CPOOL_LIMIT_OK;
304
305
43.8k
  if(!cpool)
306
0
    return CPOOL_LIMIT_OK;
307
308
43.8k
  if(data && data->multi) {
309
43.8k
    dest_limit = data->multi->max_host_connections;
310
43.8k
    total_limit = data->multi->max_total_connections;
311
43.8k
  }
312
313
43.8k
  if(!dest_limit && !total_limit)
314
43.8k
    return CPOOL_LIMIT_OK;
315
316
0
  CPOOL_LOCK(cpool);
317
0
  if(dest_limit) {
318
0
    bundle = cpool_find_bundle(cpool, conn);
319
0
    while(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
320
0
      struct connectdata *oldest_idle = NULL;
321
      /* The bundle is full. Extract the oldest connection that may
322
       * be removed now, if there is one. */
323
0
      oldest_idle = cpool_bundle_get_oldest_idle(bundle);
324
0
      if(!oldest_idle)
325
0
        break;
326
      /* disconnect the old conn and continue */
327
0
      DEBUGF(infof(data, "Discarding connection #%"
328
0
                   FMT_OFF_T " from %zu to reach destination "
329
0
                   "limit of %zu", oldest_idle->connection_id,
330
0
                   Curl_llist_count(&bundle->conns), dest_limit));
331
0
      Curl_cpool_disconnect(data, oldest_idle, FALSE);
332
0
    }
333
0
    if(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
334
0
      result = CPOOL_LIMIT_DEST;
335
0
      goto out;
336
0
    }
337
0
  }
338
339
0
  if(total_limit) {
340
0
    while(cpool->num_conn >= total_limit) {
341
0
      struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
342
0
      if(!oldest_idle)
343
0
        break;
344
      /* disconnect the old conn and continue */
345
0
      DEBUGF(infof(data, "Discarding connection #%"
346
0
                   FMT_OFF_T " from %zu to reach total "
347
0
                   "limit of %zu",
348
0
                   oldest_idle->connection_id, cpool->num_conn, total_limit));
349
0
      Curl_cpool_disconnect(data, oldest_idle, FALSE);
350
0
    }
351
0
    if(cpool->num_conn >= total_limit) {
352
0
      result = CPOOL_LIMIT_TOTAL;
353
0
      goto out;
354
0
    }
355
0
  }
356
357
0
out:
358
0
  CPOOL_UNLOCK(cpool);
359
0
  return result;
360
0
}
361
362
CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
363
                             struct connectdata *conn)
364
45.0k
{
365
45.0k
  CURLcode result = CURLE_OK;
366
45.0k
  struct cpool_bundle *bundle = NULL;
367
45.0k
  struct cpool *cpool = cpool_get_instance(data);
368
45.0k
  DEBUGASSERT(conn);
369
370
45.0k
  DEBUGASSERT(cpool);
371
45.0k
  if(!cpool)
372
0
    return CURLE_FAILED_INIT;
373
374
45.0k
  CPOOL_LOCK(cpool);
375
45.0k
  bundle = cpool_find_bundle(cpool, conn);
376
45.0k
  if(!bundle) {
377
44.6k
    bundle = cpool_add_bundle(cpool, conn);
378
44.6k
    if(!bundle) {
379
0
      result = CURLE_OUT_OF_MEMORY;
380
0
      goto out;
381
0
    }
382
44.6k
  }
383
384
45.0k
  cpool_bundle_add(bundle, conn);
385
45.0k
  conn->connection_id = cpool->next_connection_id++;
386
45.0k
  cpool->num_conn++;
387
45.0k
  DEBUGF(infof(data, "Added connection %" FMT_OFF_T ". "
388
45.0k
               "The cache now contains %zu members",
389
45.0k
               conn->connection_id, cpool->num_conn));
390
45.0k
out:
391
45.0k
  CPOOL_UNLOCK(cpool);
392
393
45.0k
  return result;
394
45.0k
}
395
396
static void cpool_remove_conn(struct cpool *cpool,
397
                              struct connectdata *conn)
398
45.0k
{
399
45.0k
  struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
400
45.0k
  DEBUGASSERT(cpool);
401
45.0k
  if(list) {
402
    /* The connection is certainly in the pool, but where? */
403
45.0k
    struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
404
45.0k
    if(bundle && (list == &bundle->conns)) {
405
45.0k
      cpool_bundle_remove(bundle, conn);
406
45.0k
      if(!Curl_llist_count(&bundle->conns))
407
44.6k
        cpool_remove_bundle(cpool, bundle);
408
45.0k
      conn->bits.in_cpool = FALSE;
409
45.0k
      cpool->num_conn--;
410
45.0k
    }
411
0
    else {
412
      /* Not in  a bundle, already in the shutdown list? */
413
0
      DEBUGASSERT(list == &cpool->shutdowns);
414
0
    }
415
45.0k
  }
416
45.0k
}
417
418
/* This function iterates the entire connection pool and calls the function
419
   func() with the connection pointer as the first argument and the supplied
420
   'param' argument as the other.
421
422
   The cpool lock is still held when the callback is called. It needs it,
423
   so that it can safely continue traversing the lists once the callback
424
   returns.
425
426
   Returns TRUE if the loop was aborted due to the callback's return code.
427
428
   Return 0 from func() to continue the loop, return 1 to abort it.
429
 */
430
static bool cpool_foreach(struct Curl_easy *data,
431
                          struct cpool *cpool,
432
                          void *param,
433
                          int (*func)(struct Curl_easy *data,
434
                                      struct connectdata *conn, void *param))
435
50.6k
{
436
50.6k
  struct Curl_hash_iterator iter;
437
50.6k
  struct Curl_hash_element *he;
438
439
50.6k
  if(!cpool)
440
0
    return FALSE;
441
442
50.6k
  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
443
444
50.6k
  he = Curl_hash_next_element(&iter);
445
50.8k
  while(he) {
446
8.01k
    struct Curl_llist_node *curr;
447
8.01k
    struct cpool_bundle *bundle = he->ptr;
448
8.01k
    he = Curl_hash_next_element(&iter);
449
450
8.01k
    curr = Curl_llist_head(&bundle->conns);
451
8.23k
    while(curr) {
452
      /* Yes, we need to update curr before calling func(), because func()
453
         might decide to remove the connection */
454
8.11k
      struct connectdata *conn = Curl_node_elem(curr);
455
8.11k
      curr = Curl_node_next(curr);
456
457
8.11k
      if(1 == func(data, conn, param)) {
458
7.88k
        return TRUE;
459
7.88k
      }
460
8.11k
    }
461
8.01k
  }
462
42.8k
  return FALSE;
463
50.6k
}
464
465
/* Return a live connection in the pool or NULL. */
466
static struct connectdata *cpool_get_live_conn(struct cpool *cpool)
467
141k
{
468
141k
  struct Curl_hash_iterator iter;
469
141k
  struct Curl_hash_element *he;
470
141k
  struct cpool_bundle *bundle;
471
141k
  struct Curl_llist_node *conn_node;
472
473
141k
  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
474
141k
  for(he = Curl_hash_next_element(&iter); he;
475
141k
      he = Curl_hash_next_element(&iter)) {
476
7.80k
    bundle = he->ptr;
477
7.80k
    conn_node = Curl_llist_head(&bundle->conns);
478
7.80k
    if(conn_node)
479
7.80k
      return Curl_node_elem(conn_node);
480
7.80k
  }
481
133k
  return NULL;
482
141k
}
483
484
/*
485
 * A connection (already in the pool) has become idle. Do any
486
 * cleanups in regard to the pool's limits.
487
 *
488
 * Return TRUE if idle connection kept in pool, FALSE if closed.
489
 */
490
bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
491
                              struct connectdata *conn)
492
10.5k
{
493
10.5k
  unsigned int maxconnects = !data->multi->maxconnects ?
494
10.5k
    data->multi->num_easy * 4: data->multi->maxconnects;
495
10.5k
  struct connectdata *oldest_idle = NULL;
496
10.5k
  struct cpool *cpool = cpool_get_instance(data);
497
10.5k
  bool kept = TRUE;
498
499
10.5k
  conn->lastused = Curl_now(); /* it was used up until now */
500
10.5k
  if(cpool && maxconnects) {
501
    /* may be called form a callback already under lock */
502
10.5k
    bool do_lock = !CPOOL_IS_LOCKED(cpool);
503
10.5k
    if(do_lock)
504
0
      CPOOL_LOCK(cpool);
505
10.5k
    if(cpool->num_conn > maxconnects) {
506
0
      infof(data, "Connection pool is full, closing the oldest one");
507
508
0
      oldest_idle = cpool_get_oldest_idle(cpool);
509
0
      kept = (oldest_idle != conn);
510
0
      if(oldest_idle) {
511
0
        Curl_cpool_disconnect(cpool->idata, oldest_idle, FALSE);
512
0
      }
513
0
    }
514
10.5k
    if(do_lock)
515
0
      CPOOL_UNLOCK(cpool);
516
10.5k
  }
517
518
10.5k
  return kept;
519
10.5k
}
520
521
/*
522
 * This function finds the connection in the connection bundle that has been
523
 * unused for the longest time.
524
 */
525
static struct connectdata *
526
cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
527
0
{
528
0
  struct Curl_llist_node *curr;
529
0
  timediff_t highscore = -1;
530
0
  timediff_t score;
531
0
  struct curltime now;
532
0
  struct connectdata *oldest_idle = NULL;
533
0
  struct connectdata *conn;
534
535
0
  now = Curl_now();
536
0
  curr = Curl_llist_head(&bundle->conns);
537
0
  while(curr) {
538
0
    conn = Curl_node_elem(curr);
539
540
0
    if(!CONN_INUSE(conn)) {
541
      /* Set higher score for the age passed since the connection was used */
542
0
      score = Curl_timediff(now, conn->lastused);
543
544
0
      if(score > highscore) {
545
0
        highscore = score;
546
0
        oldest_idle = conn;
547
0
      }
548
0
    }
549
0
    curr = Curl_node_next(curr);
550
0
  }
551
0
  return oldest_idle;
552
0
}
553
554
static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
555
0
{
556
0
  struct Curl_hash_iterator iter;
557
0
  struct Curl_llist_node *curr;
558
0
  struct Curl_hash_element *he;
559
0
  struct connectdata *oldest_idle = NULL;
560
0
  struct cpool_bundle *bundle;
561
0
  struct curltime now;
562
0
  timediff_t highscore =- 1;
563
0
  timediff_t score;
564
565
0
  now = Curl_now();
566
0
  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
567
568
0
  for(he = Curl_hash_next_element(&iter); he;
569
0
      he = Curl_hash_next_element(&iter)) {
570
0
    struct connectdata *conn;
571
0
    bundle = he->ptr;
572
573
0
    for(curr = Curl_llist_head(&bundle->conns); curr;
574
0
        curr = Curl_node_next(curr)) {
575
0
      conn = Curl_node_elem(curr);
576
0
      if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
577
0
        continue;
578
      /* Set higher score for the age passed since the connection was used */
579
0
      score = Curl_timediff(now, conn->lastused);
580
0
      if(score > highscore) {
581
0
        highscore = score;
582
0
        oldest_idle = conn;
583
0
      }
584
0
    }
585
0
  }
586
0
  return oldest_idle;
587
0
}
588
589
bool Curl_cpool_find(struct Curl_easy *data,
590
                     const char *destination, size_t dest_len,
591
                     Curl_cpool_conn_match_cb *conn_cb,
592
                     Curl_cpool_done_match_cb *done_cb,
593
                     void *userdata)
594
45.4k
{
595
45.4k
  struct cpool *cpool = cpool_get_instance(data);
596
45.4k
  struct cpool_bundle *bundle;
597
45.4k
  bool result = FALSE;
598
599
45.4k
  DEBUGASSERT(cpool);
600
45.4k
  DEBUGASSERT(conn_cb);
601
45.4k
  if(!cpool)
602
0
    return FALSE;
603
604
45.4k
  CPOOL_LOCK(cpool);
605
45.4k
  bundle = Curl_hash_pick(&cpool->dest2bundle, (void *)destination, dest_len);
606
45.4k
  if(bundle) {
607
2.67k
    struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
608
3.77k
    while(curr) {
609
2.71k
      struct connectdata *conn = Curl_node_elem(curr);
610
      /* Get next node now. callback might discard current */
611
2.71k
      curr = Curl_node_next(curr);
612
613
2.71k
      if(conn_cb(conn, userdata)) {
614
1.61k
        result = TRUE;
615
1.61k
        break;
616
1.61k
      }
617
2.71k
    }
618
2.67k
  }
619
620
45.4k
  if(done_cb) {
621
45.4k
    result = done_cb(result, userdata);
622
45.4k
  }
623
45.4k
  CPOOL_UNLOCK(cpool);
624
45.4k
  return result;
625
45.4k
}
626
627
static void cpool_shutdown_discard_all(struct cpool *cpool)
628
133k
{
629
133k
  struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
630
133k
  struct connectdata *conn;
631
632
133k
  if(!e)
633
133k
    return;
634
635
0
  DEBUGF(infof(cpool->idata, "cpool_shutdown_discard_all"));
636
0
  while(e) {
637
0
    conn = Curl_node_elem(e);
638
0
    Curl_node_remove(e);
639
0
    DEBUGF(infof(cpool->idata, "discard connection #%" FMT_OFF_T,
640
0
                 conn->connection_id));
641
0
    cpool_close_and_destroy(cpool, conn, NULL, FALSE);
642
0
    e = Curl_llist_head(&cpool->shutdowns);
643
0
  }
644
0
}
645
646
static void cpool_close_and_destroy_all(struct cpool *cpool)
647
66.8k
{
648
66.8k
  struct connectdata *conn;
649
66.8k
  int timeout_ms = 0;
650
66.8k
  SIGPIPE_VARIABLE(pipe_st);
651
652
66.8k
  DEBUGASSERT(cpool);
653
  /* Move all connections to the shutdown list */
654
66.8k
  sigpipe_init(&pipe_st);
655
66.8k
  CPOOL_LOCK(cpool);
656
66.8k
  conn = cpool_get_live_conn(cpool);
657
74.6k
  while(conn) {
658
7.80k
    cpool_remove_conn(cpool, conn);
659
7.80k
    sigpipe_apply(cpool->idata, &pipe_st);
660
7.80k
    connclose(conn, "kill all");
661
7.80k
    cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
662
663
7.80k
    conn = cpool_get_live_conn(cpool);
664
7.80k
  }
665
66.8k
  CPOOL_UNLOCK(cpool);
666
667
    /* Just for testing, run graceful shutdown */
668
66.8k
#ifdef DEBUGBUILD
669
66.8k
  {
670
66.8k
    char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
671
66.8k
    if(p) {
672
0
      long l = strtol(p, NULL, 10);
673
0
      if(l > 0 && l < INT_MAX)
674
0
        timeout_ms = (int)l;
675
0
    }
676
66.8k
  }
677
66.8k
#endif
678
66.8k
  sigpipe_apply(cpool->idata, &pipe_st);
679
66.8k
  cpool_shutdown_all(cpool, cpool->idata, timeout_ms);
680
681
  /* discard all connections in the shutdown list */
682
66.8k
  cpool_shutdown_discard_all(cpool);
683
684
66.8k
  Curl_hostcache_clean(cpool->idata, cpool->idata->dns.hostcache);
685
66.8k
  sigpipe_restore(&pipe_st);
686
66.8k
}
687
688
689
static void cpool_shutdown_destroy_oldest(struct cpool *cpool)
690
0
{
691
0
  struct Curl_llist_node *e;
692
0
  struct connectdata *conn;
693
694
0
  e = Curl_llist_head(&cpool->shutdowns);
695
0
  if(e) {
696
0
    SIGPIPE_VARIABLE(pipe_st);
697
0
    conn = Curl_node_elem(e);
698
0
    Curl_node_remove(e);
699
0
    sigpipe_init(&pipe_st);
700
0
    sigpipe_apply(cpool->idata, &pipe_st);
701
0
    cpool_close_and_destroy(cpool, conn, NULL, FALSE);
702
0
    sigpipe_restore(&pipe_st);
703
0
  }
704
0
}
705
706
static void cpool_discard_conn(struct cpool *cpool,
707
                               struct Curl_easy *data,
708
                               struct connectdata *conn,
709
                               bool aborted)
710
62.4k
{
711
62.4k
  bool done = FALSE;
712
713
62.4k
  DEBUGASSERT(data);
714
62.4k
  DEBUGASSERT(cpool);
715
62.4k
  DEBUGASSERT(!conn->bits.in_cpool);
716
717
  /*
718
   * If this connection is not marked to force-close, leave it open if there
719
   * are other users of it
720
   */
721
62.4k
  if(CONN_INUSE(conn) && !aborted) {
722
0
    DEBUGF(infof(data, "[CCACHE] not discarding #%" FMT_OFF_T
723
0
                 " still in use by %zu transfers", conn->connection_id,
724
0
                 CONN_INUSE(conn)));
725
0
    return;
726
0
  }
727
728
  /* treat the connection as aborted in CONNECT_ONLY situations, we do
729
   * not know what the APP did with it. */
730
62.4k
  if(conn->connect_only)
731
490
    aborted = TRUE;
732
62.4k
  conn->bits.aborted = aborted;
733
734
  /* We do not shutdown dead connections. The term 'dead' can be misleading
735
   * here, as we also mark errored connections/transfers as 'dead'.
736
   * If we do a shutdown for an aborted transfer, the server might think
737
   * it was successful otherwise (for example an ftps: upload). This is
738
   * not what we want. */
739
62.4k
  if(aborted)
740
36.6k
    done = TRUE;
741
62.4k
  if(!done) {
742
    /* Attempt to shutdown the connection right away. */
743
25.7k
    Curl_attach_connection(data, conn);
744
25.7k
    cpool_run_conn_shutdown(data, conn, &done);
745
25.7k
    DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
746
25.7k
                 conn->connection_id, done));
747
25.7k
    Curl_detach_connection(data);
748
25.7k
  }
749
750
62.4k
  if(done) {
751
62.4k
    cpool_close_and_destroy(cpool, conn, data, FALSE);
752
62.4k
    return;
753
62.4k
  }
754
755
  /* Add the connection to our shutdown list for non-blocking shutdown
756
   * during multi processing. */
757
0
  if(data->multi && data->multi->max_shutdown_connections > 0 &&
758
0
     (data->multi->max_shutdown_connections >=
759
0
      (long)Curl_llist_count(&cpool->shutdowns))) {
760
0
    DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection "
761
0
                       "due to limit of %ld",
762
0
                       data->multi->max_shutdown_connections));
763
0
    cpool_shutdown_destroy_oldest(cpool);
764
0
  }
765
766
0
  if(data->multi && data->multi->socket_cb) {
767
0
    DEBUGASSERT(cpool == &data->multi->cpool);
768
    /* Start with an empty shutdown pollset, so out internal closure handle
769
     * is added to the sockets. */
770
0
    memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll));
771
0
    if(cpool_update_shutdown_ev(data->multi, cpool->idata, conn)) {
772
0
      DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, "
773
0
                   "discarding #%" FMT_OFF_T,
774
0
                   conn->connection_id));
775
0
      cpool_close_and_destroy(cpool, conn, data, FALSE);
776
0
      return;
777
0
    }
778
0
  }
779
780
0
  Curl_llist_append(&cpool->shutdowns, conn, &conn->cpool_node);
781
0
  DEBUGF(infof(data, "[CCACHE] added #%" FMT_OFF_T
782
0
               " to shutdown list of length %zu", conn->connection_id,
783
0
               Curl_llist_count(&cpool->shutdowns)));
784
0
}
785
786
void Curl_cpool_disconnect(struct Curl_easy *data,
787
                           struct connectdata *conn,
788
                           bool aborted)
789
54.6k
{
790
54.6k
  struct cpool *cpool = cpool_get_instance(data);
791
54.6k
  bool do_lock;
792
793
54.6k
  DEBUGASSERT(cpool);
794
54.6k
  DEBUGASSERT(data && !data->conn);
795
54.6k
  if(!cpool)
796
0
    return;
797
798
  /* If this connection is not marked to force-close, leave it open if there
799
   * are other users of it */
800
54.6k
  if(CONN_INUSE(conn) && !aborted) {
801
0
    DEBUGASSERT(0); /* does this ever happen? */
802
0
    DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
803
0
    return;
804
0
  }
805
806
  /* This method may be called while we are under lock, e.g. from a
807
   * user callback in find. */
808
54.6k
  do_lock = !CPOOL_IS_LOCKED(cpool);
809
54.6k
  if(do_lock)
810
17.6k
    CPOOL_LOCK(cpool);
811
812
54.6k
  if(conn->bits.in_cpool) {
813
37.2k
    cpool_remove_conn(cpool, conn);
814
37.2k
    DEBUGASSERT(!conn->bits.in_cpool);
815
37.2k
  }
816
817
  /* Run the callback to let it clean up anything it wants to. */
818
54.6k
  aborted = cpool->disconnect_cb(data, conn, aborted);
819
820
54.6k
  if(data->multi) {
821
    /* Add it to the multi's cpool for shutdown handling */
822
54.6k
    infof(data, "%s connection #%" FMT_OFF_T,
823
54.6k
          aborted? "closing" : "shutting down", conn->connection_id);
824
54.6k
    cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
825
54.6k
  }
826
0
  else {
827
    /* No multi available. Make a best-effort shutdown + close */
828
0
    infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
829
0
    cpool_close_and_destroy(NULL, conn, data, !aborted);
830
0
  }
831
832
54.6k
  if(do_lock)
833
17.6k
    CPOOL_UNLOCK(cpool);
834
54.6k
}
835
836
static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
837
                                            struct connectdata *conn)
838
88.2k
{
839
88.2k
  if(!conn->bits.shutdown_handler) {
840
62.4k
    if(conn->dns_entry)
841
0
      Curl_resolv_unlink(data, &conn->dns_entry);
842
843
    /* Cleanup NTLM connection-related data */
844
62.4k
    Curl_http_auth_cleanup_ntlm(conn);
845
846
    /* Cleanup NEGOTIATE connection-related data */
847
62.4k
    Curl_http_auth_cleanup_negotiate(conn);
848
849
62.4k
    if(conn->handler && conn->handler->disconnect) {
850
      /* This is set if protocol-specific cleanups should be made */
851
26.7k
      DEBUGF(infof(data, "connection #%" FMT_OFF_T
852
26.7k
                   ", shutdown protocol handler (aborted=%d)",
853
26.7k
                   conn->connection_id, conn->bits.aborted));
854
855
26.7k
      conn->handler->disconnect(data, conn, conn->bits.aborted);
856
26.7k
    }
857
858
    /* possible left-overs from the async name resolvers */
859
62.4k
    Curl_resolver_cancel(data);
860
861
62.4k
    conn->bits.shutdown_handler = TRUE;
862
62.4k
  }
863
88.2k
}
864
865
static void cpool_run_conn_shutdown(struct Curl_easy *data,
866
                                    struct connectdata *conn,
867
                                    bool *done)
868
25.7k
{
869
25.7k
  CURLcode r1, r2;
870
25.7k
  bool done1, done2;
871
872
  /* We expect to be attached when called */
873
25.7k
  DEBUGASSERT(data->conn == conn);
874
875
25.7k
  cpool_run_conn_shutdown_handler(data, conn);
876
877
25.7k
  if(conn->bits.shutdown_filters) {
878
0
    *done = TRUE;
879
0
    return;
880
0
  }
881
882
25.7k
  if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
883
24.5k
    r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
884
1.24k
  else {
885
1.24k
    r1 = CURLE_OK;
886
1.24k
    done1 = TRUE;
887
1.24k
  }
888
889
25.7k
  if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
890
0
    r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
891
25.7k
  else {
892
25.7k
    r2 = CURLE_OK;
893
25.7k
    done2 = TRUE;
894
25.7k
  }
895
896
  /* we are done when any failed or both report success */
897
25.7k
  *done = (r1 || r2 || (done1 && done2));
898
25.7k
  if(*done)
899
25.7k
    conn->bits.shutdown_filters = TRUE;
900
25.7k
}
901
902
static CURLcode cpool_add_pollfds(struct cpool *cpool,
903
                                  struct curl_pollfds *cpfds)
904
0
{
905
0
  CURLcode result = CURLE_OK;
906
907
0
  if(Curl_llist_head(&cpool->shutdowns)) {
908
0
    struct Curl_llist_node *e;
909
0
    struct easy_pollset ps;
910
0
    struct connectdata *conn;
911
912
0
    for(e = Curl_llist_head(&cpool->shutdowns); e;
913
0
        e = Curl_node_next(e)) {
914
0
      conn = Curl_node_elem(e);
915
0
      memset(&ps, 0, sizeof(ps));
916
0
      Curl_attach_connection(cpool->idata, conn);
917
0
      Curl_conn_adjust_pollset(cpool->idata, &ps);
918
0
      Curl_detach_connection(cpool->idata);
919
920
0
      result = Curl_pollfds_add_ps(cpfds, &ps);
921
0
      if(result) {
922
0
        Curl_pollfds_cleanup(cpfds);
923
0
        goto out;
924
0
      }
925
0
    }
926
0
  }
927
0
out:
928
0
  return result;
929
0
}
930
931
CURLcode Curl_cpool_add_pollfds(struct cpool *cpool,
932
                                struct curl_pollfds *cpfds)
933
0
{
934
0
  CURLcode result;
935
0
  CPOOL_LOCK(cpool);
936
0
  result = cpool_add_pollfds(cpool, cpfds);
937
0
  CPOOL_UNLOCK(cpool);
938
0
  return result;
939
0
}
940
941
CURLcode Curl_cpool_add_waitfds(struct cpool *cpool,
942
                                struct curl_waitfds *cwfds)
943
0
{
944
0
  CURLcode result = CURLE_OK;
945
946
0
  CPOOL_LOCK(cpool);
947
0
  if(Curl_llist_head(&cpool->shutdowns)) {
948
0
    struct Curl_llist_node *e;
949
0
    struct easy_pollset ps;
950
0
    struct connectdata *conn;
951
952
0
    for(e = Curl_llist_head(&cpool->shutdowns); e;
953
0
        e = Curl_node_next(e)) {
954
0
      conn = Curl_node_elem(e);
955
0
      memset(&ps, 0, sizeof(ps));
956
0
      Curl_attach_connection(cpool->idata, conn);
957
0
      Curl_conn_adjust_pollset(cpool->idata, &ps);
958
0
      Curl_detach_connection(cpool->idata);
959
960
0
      result = Curl_waitfds_add_ps(cwfds, &ps);
961
0
      if(result)
962
0
        goto out;
963
0
    }
964
0
  }
965
0
out:
966
0
  CPOOL_UNLOCK(cpool);
967
0
  return result;
968
0
}
969
970
static void cpool_perform(struct cpool *cpool)
971
61.2M
{
972
61.2M
  struct Curl_easy *data = cpool->idata;
973
61.2M
  struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
974
61.2M
  struct Curl_llist_node *enext;
975
61.2M
  struct connectdata *conn;
976
61.2M
  struct curltime *nowp = NULL;
977
61.2M
  struct curltime now;
978
61.2M
  timediff_t next_from_now_ms = 0, ms;
979
61.2M
  bool done;
980
981
61.2M
  if(!e)
982
61.2M
    return;
983
984
0
  DEBUGASSERT(data);
985
0
  DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown",
986
0
               Curl_llist_count(&cpool->shutdowns)));
987
0
  while(e) {
988
0
    enext = Curl_node_next(e);
989
0
    conn = Curl_node_elem(e);
990
0
    Curl_attach_connection(data, conn);
991
0
    cpool_run_conn_shutdown(data, conn, &done);
992
0
    DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
993
0
                 conn->connection_id, done));
994
0
    Curl_detach_connection(data);
995
0
    if(done) {
996
0
      Curl_node_remove(e);
997
0
      cpool_close_and_destroy(cpool, conn, NULL, FALSE);
998
0
    }
999
0
    else {
1000
      /* Not done, when does this connection time out? */
1001
0
      if(!nowp) {
1002
0
        now = Curl_now();
1003
0
        nowp = &now;
1004
0
      }
1005
0
      ms = Curl_conn_shutdown_timeleft(conn, nowp);
1006
0
      if(ms && ms < next_from_now_ms)
1007
0
        next_from_now_ms = ms;
1008
0
    }
1009
0
    e = enext;
1010
0
  }
1011
1012
0
  if(next_from_now_ms)
1013
0
    Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW);
1014
0
}
1015
1016
void Curl_cpool_multi_perform(struct Curl_multi *multi)
1017
61.2M
{
1018
61.2M
  CPOOL_LOCK(&multi->cpool);
1019
61.2M
  cpool_perform(&multi->cpool);
1020
61.2M
  CPOOL_UNLOCK(&multi->cpool);
1021
61.2M
}
1022
1023
1024
/*
1025
 * Close and destroy the connection. Run the shutdown sequence once,
1026
 * of so requested.
1027
 */
1028
static void cpool_close_and_destroy(struct cpool *cpool,
1029
                                    struct connectdata *conn,
1030
                                    struct Curl_easy *data,
1031
                                    bool do_shutdown)
1032
62.4k
{
1033
62.4k
  bool done;
1034
1035
  /* there must be a connection to close */
1036
62.4k
  DEBUGASSERT(conn);
1037
  /* it must be removed from the connection pool */
1038
62.4k
  DEBUGASSERT(!conn->bits.in_cpool);
1039
  /* there must be an associated transfer */
1040
62.4k
  DEBUGASSERT(data || cpool);
1041
62.4k
  if(!data)
1042
0
    data = cpool->idata;
1043
1044
  /* the transfer must be detached from the connection */
1045
62.4k
  DEBUGASSERT(data && !data->conn);
1046
1047
62.4k
  Curl_attach_connection(data, conn);
1048
1049
62.4k
  cpool_run_conn_shutdown_handler(data, conn);
1050
62.4k
  if(do_shutdown) {
1051
    /* Make a last attempt to shutdown handlers and filters, if
1052
     * not done so already. */
1053
0
    cpool_run_conn_shutdown(data, conn, &done);
1054
0
  }
1055
1056
62.4k
  if(cpool)
1057
62.4k
    DEBUGF(infof(data, "[CCACHE] closing #%" FMT_OFF_T,
1058
62.4k
                 conn->connection_id));
1059
0
  else
1060
0
    DEBUGF(infof(data, "closing connection #%" FMT_OFF_T,
1061
62.4k
                 conn->connection_id));
1062
62.4k
  Curl_conn_close(data, SECONDARYSOCKET);
1063
62.4k
  Curl_conn_close(data, FIRSTSOCKET);
1064
62.4k
  Curl_detach_connection(data);
1065
1066
62.4k
  Curl_conn_free(data, conn);
1067
62.4k
}
1068
1069
1070
static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
1071
                                          struct Curl_easy *data,
1072
                                          struct connectdata *conn)
1073
0
{
1074
0
  struct easy_pollset ps;
1075
0
  CURLMcode mresult;
1076
1077
0
  DEBUGASSERT(data);
1078
0
  DEBUGASSERT(multi);
1079
0
  DEBUGASSERT(multi->socket_cb);
1080
1081
0
  memset(&ps, 0, sizeof(ps));
1082
0
  Curl_attach_connection(data, conn);
1083
0
  Curl_conn_adjust_pollset(data, &ps);
1084
0
  Curl_detach_connection(data);
1085
1086
0
  mresult = Curl_multi_pollset_ev(multi, data, &ps, &conn->shutdown_poll);
1087
1088
0
  if(!mresult) /* Remember for next time */
1089
0
    memcpy(&conn->shutdown_poll, &ps, sizeof(ps));
1090
0
  return mresult;
1091
0
}
1092
1093
void Curl_cpool_multi_socket(struct Curl_multi *multi,
1094
                             curl_socket_t s, int ev_bitmask)
1095
0
{
1096
0
  struct cpool *cpool = &multi->cpool;
1097
0
  struct Curl_easy *data = cpool->idata;
1098
0
  struct Curl_llist_node *e;
1099
0
  struct connectdata *conn;
1100
0
  bool done;
1101
1102
0
  (void)ev_bitmask;
1103
0
  DEBUGASSERT(multi->socket_cb);
1104
0
  CPOOL_LOCK(cpool);
1105
0
  e = Curl_llist_head(&cpool->shutdowns);
1106
0
  while(e) {
1107
0
    conn = Curl_node_elem(e);
1108
0
    if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
1109
0
      Curl_attach_connection(data, conn);
1110
0
      cpool_run_conn_shutdown(data, conn, &done);
1111
0
      DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
1112
0
                   conn->connection_id, done));
1113
0
      Curl_detach_connection(data);
1114
0
      if(done || cpool_update_shutdown_ev(multi, data, conn)) {
1115
0
        Curl_node_remove(e);
1116
0
        cpool_close_and_destroy(cpool, conn, NULL, FALSE);
1117
0
      }
1118
0
      break;
1119
0
    }
1120
0
    e = Curl_node_next(e);
1121
0
  }
1122
0
  CPOOL_UNLOCK(cpool);
1123
0
}
1124
1125
0
#define NUM_POLLS_ON_STACK 10
1126
1127
static CURLcode cpool_shutdown_wait(struct cpool *cpool, int timeout_ms)
1128
0
{
1129
0
  struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
1130
0
  struct curl_pollfds cpfds;
1131
0
  CURLcode result;
1132
1133
0
  Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
1134
1135
0
  result = cpool_add_pollfds(cpool, &cpfds);
1136
0
  if(result)
1137
0
    goto out;
1138
1139
0
  Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
1140
1141
0
out:
1142
0
  Curl_pollfds_cleanup(&cpfds);
1143
0
  return result;
1144
0
}
1145
1146
static void cpool_shutdown_all(struct cpool *cpool,
1147
                               struct Curl_easy *data, int timeout_ms)
1148
66.8k
{
1149
66.8k
  struct connectdata *conn;
1150
66.8k
  struct curltime started = Curl_now();
1151
1152
66.8k
  if(!data)
1153
0
    return;
1154
66.8k
  (void)data;
1155
1156
66.8k
  DEBUGF(infof(data, "cpool shutdown all"));
1157
1158
  /* Move all connections into the shutdown queue */
1159
66.8k
  for(conn = cpool_get_live_conn(cpool); conn;
1160
66.8k
      conn = cpool_get_live_conn(cpool)) {
1161
    /* Move conn from live set to shutdown or destroy right away */
1162
0
    DEBUGF(infof(data, "moving connection #%" FMT_OFF_T
1163
0
                 " to shutdown queue", conn->connection_id));
1164
0
    cpool_remove_conn(cpool, conn);
1165
0
    cpool_discard_conn(cpool, data, conn, FALSE);
1166
0
  }
1167
1168
66.8k
  while(Curl_llist_head(&cpool->shutdowns)) {
1169
0
    timediff_t timespent;
1170
0
    int remain_ms;
1171
1172
0
    cpool_perform(cpool);
1173
1174
0
    if(!Curl_llist_head(&cpool->shutdowns)) {
1175
0
      DEBUGF(infof(data, "cpool shutdown ok"));
1176
0
      break;
1177
0
    }
1178
1179
    /* wait for activity, timeout or "nothing" */
1180
0
    timespent = Curl_timediff(Curl_now(), started);
1181
0
    if(timespent >= (timediff_t)timeout_ms) {
1182
0
      DEBUGF(infof(data, "cpool shutdown %s",
1183
0
                   (timeout_ms > 0)? "timeout" : "best effort done"));
1184
0
      break;
1185
0
    }
1186
1187
0
    remain_ms = timeout_ms - (int)timespent;
1188
0
    if(cpool_shutdown_wait(cpool, remain_ms)) {
1189
0
      DEBUGF(infof(data, "cpool shutdown all, abort"));
1190
0
      break;
1191
0
    }
1192
0
  }
1193
1194
  /* Due to errors/timeout, we might come here without being done. */
1195
66.8k
  cpool_shutdown_discard_all(cpool);
1196
66.8k
}
1197
1198
struct cpool_reaper_ctx {
1199
  struct curltime now;
1200
};
1201
1202
static int cpool_reap_dead_cb(struct Curl_easy *data,
1203
                              struct connectdata *conn, void *param)
1204
0
{
1205
0
  struct cpool_reaper_ctx *rctx = param;
1206
0
  if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
1207
    /* stop the iteration here, pass back the connection that was pruned */
1208
0
    Curl_cpool_disconnect(data, conn, FALSE);
1209
0
    return 1;
1210
0
  }
1211
0
  return 0; /* continue iteration */
1212
0
}
1213
1214
/*
1215
 * This function scans the data's connection pool for half-open/dead
1216
 * connections, closes and removes them.
1217
 * The cleanup is done at most once per second.
1218
 *
1219
 * When called, this transfer has no connection attached.
1220
 */
1221
void Curl_cpool_prune_dead(struct Curl_easy *data)
1222
45.9k
{
1223
45.9k
  struct cpool *cpool = cpool_get_instance(data);
1224
45.9k
  struct cpool_reaper_ctx rctx;
1225
45.9k
  timediff_t elapsed;
1226
1227
45.9k
  if(!cpool)
1228
0
    return;
1229
1230
45.9k
  rctx.now = Curl_now();
1231
45.9k
  CPOOL_LOCK(cpool);
1232
45.9k
  elapsed = Curl_timediff(rctx.now, cpool->last_cleanup);
1233
1234
45.9k
  if(elapsed >= 1000L) {
1235
41.0k
    while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
1236
0
      ;
1237
41.0k
    cpool->last_cleanup = rctx.now;
1238
41.0k
  }
1239
45.9k
  CPOOL_UNLOCK(cpool);
1240
45.9k
}
1241
1242
static int conn_upkeep(struct Curl_easy *data,
1243
                       struct connectdata *conn,
1244
                       void *param)
1245
0
{
1246
0
  struct curltime *now = param;
1247
  /* TODO, shall we reap connections that return an error here? */
1248
0
  Curl_conn_upkeep(data, conn, now);
1249
0
  return 0; /* continue iteration */
1250
0
}
1251
1252
CURLcode Curl_cpool_upkeep(void *data)
1253
0
{
1254
0
  struct cpool *cpool = cpool_get_instance(data);
1255
0
  struct curltime now = Curl_now();
1256
1257
0
  if(!cpool)
1258
0
    return CURLE_OK;
1259
1260
0
  CPOOL_LOCK(cpool);
1261
0
  cpool_foreach(data, cpool, &now, conn_upkeep);
1262
0
  CPOOL_UNLOCK(cpool);
1263
0
  return CURLE_OK;
1264
0
}
1265
1266
struct cpool_find_ctx {
1267
  curl_off_t id;
1268
  struct connectdata *conn;
1269
};
1270
1271
static int cpool_find_conn(struct Curl_easy *data,
1272
                           struct connectdata *conn, void *param)
1273
310
{
1274
310
  struct cpool_find_ctx *fctx = param;
1275
310
  (void)data;
1276
310
  if(conn->connection_id == fctx->id) {
1277
229
    fctx->conn = conn;
1278
229
    return 1;
1279
229
  }
1280
81
  return 0;
1281
310
}
1282
1283
struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
1284
                                        curl_off_t conn_id)
1285
229
{
1286
229
  struct cpool *cpool = cpool_get_instance(data);
1287
229
  struct cpool_find_ctx fctx;
1288
1289
229
  if(!cpool)
1290
0
    return NULL;
1291
229
  fctx.id = conn_id;
1292
229
  fctx.conn = NULL;
1293
229
  CPOOL_LOCK(cpool);
1294
229
  cpool_foreach(cpool->idata, cpool, &fctx, cpool_find_conn);
1295
229
  CPOOL_UNLOCK(cpool);
1296
229
  return fctx.conn;
1297
229
}
1298
1299
struct cpool_do_conn_ctx {
1300
  curl_off_t id;
1301
  Curl_cpool_conn_do_cb *cb;
1302
  void *cbdata;
1303
};
1304
1305
static int cpool_do_conn(struct Curl_easy *data,
1306
                         struct connectdata *conn, void *param)
1307
7.80k
{
1308
7.80k
  struct cpool_do_conn_ctx *dctx = param;
1309
7.80k
  (void)data;
1310
7.80k
  if(conn->connection_id == dctx->id) {
1311
7.66k
    dctx->cb(conn, data, dctx->cbdata);
1312
7.66k
    return 1;
1313
7.66k
  }
1314
142
  return 0;
1315
7.80k
}
1316
1317
void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
1318
                         Curl_cpool_conn_do_cb *cb, void *cbdata)
1319
9.37k
{
1320
9.37k
  struct cpool *cpool = cpool_get_instance(data);
1321
9.37k
  struct cpool_do_conn_ctx dctx;
1322
1323
9.37k
  if(!cpool)
1324
0
    return;
1325
9.37k
  dctx.id = conn_id;
1326
9.37k
  dctx.cb = cb;
1327
9.37k
  dctx.cbdata = cbdata;
1328
9.37k
  CPOOL_LOCK(cpool);
1329
9.37k
  cpool_foreach(data, cpool, &dctx, cpool_do_conn);
1330
9.37k
  CPOOL_UNLOCK(cpool);
1331
9.37k
}
1332
1333
void Curl_cpool_do_locked(struct Curl_easy *data,
1334
                          struct connectdata *conn,
1335
                          Curl_cpool_conn_do_cb *cb, void *cbdata)
1336
47.1k
{
1337
47.1k
  struct cpool *cpool = cpool_get_instance(data);
1338
47.1k
  if(cpool) {
1339
47.1k
    CPOOL_LOCK(cpool);
1340
47.1k
    cb(conn, data, cbdata);
1341
47.1k
    CPOOL_UNLOCK(cpool);
1342
47.1k
  }
1343
0
  else
1344
0
    cb(conn, data, cbdata);
1345
47.1k
}
1346
1347
#if 0
1348
/* Useful for debugging the connection pool */
1349
void Curl_cpool_print(struct cpool *cpool)
1350
{
1351
  struct Curl_hash_iterator iter;
1352
  struct Curl_llist_node *curr;
1353
  struct Curl_hash_element *he;
1354
1355
  if(!cpool)
1356
    return;
1357
1358
  fprintf(stderr, "=Bundle cache=\n");
1359
1360
  Curl_hash_start_iterate(cpool->dest2bundle, &iter);
1361
1362
  he = Curl_hash_next_element(&iter);
1363
  while(he) {
1364
    struct cpool_bundle *bundle;
1365
    struct connectdata *conn;
1366
1367
    bundle = he->ptr;
1368
1369
    fprintf(stderr, "%s -", he->key);
1370
    curr = Curl_llist_head(bundle->conns);
1371
    while(curr) {
1372
      conn = Curl_node_elem(curr);
1373
1374
      fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
1375
      curr = Curl_node_next(curr);
1376
    }
1377
    fprintf(stderr, "\n");
1378
1379
    he = Curl_hash_next_element(&iter);
1380
  }
1381
}
1382
#endif