Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmcurl/lib/multi.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#include "urldata.h"
27
#include "transfer.h"
28
#include "url.h"
29
#include "cfilters.h"
30
#include "connect.h"
31
#include "progress.h"
32
#include "curl_share.h"
33
#include "psl.h"
34
#include "multiif.h"
35
#include "multi_ev.h"
36
#include "sendf.h"
37
#include "curl_trc.h"
38
#include "http.h"
39
#include "select.h"
40
#include "curlx/wait.h"
41
#include "conncache.h"
42
#include "multihandle.h"
43
#include "sigpipe.h"
44
#include "vtls/vtls.h"
45
#include "vtls/vtls_scache.h"
46
#include "http_proxy.h"
47
#include "http2.h"
48
#include "socketpair.h"
49
#include "bufref.h"
50
51
/* initial multi->xfers table size for a full multi */
52
0
#define CURL_XFER_TABLE_SIZE 512
53
54
/*
55
  CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
56
  to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every
57
  curl handle takes 6K memory, therefore this 3K are not significant.
58
*/
59
#ifndef CURL_SOCKET_HASH_TABLE_SIZE
60
0
#define CURL_SOCKET_HASH_TABLE_SIZE 911
61
#endif
62
63
#ifndef CURL_CONNECTION_HASH_SIZE
64
0
#define CURL_CONNECTION_HASH_SIZE 97
65
#endif
66
67
#ifndef CURL_DNS_HASH_SIZE
68
0
#define CURL_DNS_HASH_SIZE 71
69
#endif
70
71
#ifndef CURL_TLS_SESSION_SIZE
72
0
#define CURL_TLS_SESSION_SIZE 25
73
#endif
74
75
0
#define CURL_MULTI_HANDLE 0x000bab1e
76
77
78
#ifdef DEBUGBUILD
79
/* On a debug build, we want to fail hard on multi handles that
80
 * are not NULL, but no longer have the MAGIC touch. This gives
81
 * us early warning on things only discovered by valgrind otherwise. */
82
#define GOOD_MULTI_HANDLE(x) \
83
  (((x) && (x)->magic == CURL_MULTI_HANDLE)? TRUE:      \
84
  (DEBUGASSERT(!(x)), FALSE))
85
#else
86
#define GOOD_MULTI_HANDLE(x) \
87
0
  ((x) && (x)->magic == CURL_MULTI_HANDLE)
88
#endif
89
90
static void move_pending_to_connect(struct Curl_multi *multi,
91
                                    struct Curl_easy *data);
92
static CURLMcode add_next_timeout(const struct curltime *pnow,
93
                                  struct Curl_multi *multi,
94
                                  struct Curl_easy *d);
95
static void multi_timeout(struct Curl_multi *multi,
96
                          struct curltime *expire_time,
97
                          long *timeout_ms);
98
static void process_pending_handles(struct Curl_multi *multi);
99
static void multi_xfer_bufs_free(struct Curl_multi *multi);
100
#ifdef DEBUGBUILD
101
static void multi_xfer_tbl_dump(struct Curl_multi *multi);
102
#endif
103
104
static const struct curltime *multi_now(struct Curl_multi *multi)
105
0
{
106
0
  curlx_pnow(&multi->now);
107
0
  return &multi->now;
108
0
}
109
110
/* function pointer called once when switching TO a state */
111
typedef void (*init_multistate_func)(struct Curl_easy *data);
112
113
/* called in DID state, before PERFORMING state */
114
static void before_perform(struct Curl_easy *data)
115
0
{
116
0
  data->req.chunk = FALSE;
117
0
  Curl_pgrsTime(data, TIMER_PRETRANSFER);
118
0
}
119
120
static void init_completed(struct Curl_easy *data)
121
0
{
122
  /* this is a completed transfer */
123
124
  /* Important: reset the conn pointer so that we do not point to memory
125
     that could be freed anytime */
126
0
  Curl_detach_connection(data);
127
0
  Curl_expire_clear(data); /* stop all timers */
128
0
}
129
130
/* always use this function to change state, to make debugging easier */
131
static void mstate(struct Curl_easy *data, CURLMstate state
132
#ifdef DEBUGBUILD
133
                   , int lineno
134
#endif
135
)
136
0
{
137
0
  CURLMstate oldstate = data->mstate;
138
0
  static const init_multistate_func finit[MSTATE_LAST] = {
139
0
    NULL,              /* INIT */
140
0
    NULL,              /* PENDING */
141
0
    NULL,              /* SETUP */
142
0
    Curl_init_CONNECT, /* CONNECT */
143
0
    NULL,              /* RESOLVING */
144
0
    NULL,              /* CONNECTING */
145
0
    NULL,              /* PROTOCONNECT */
146
0
    NULL,              /* PROTOCONNECTING */
147
0
    NULL,              /* DO */
148
0
    NULL,              /* DOING */
149
0
    NULL,              /* DOING_MORE */
150
0
    before_perform,    /* DID */
151
0
    NULL,              /* PERFORMING */
152
0
    NULL,              /* RATELIMITING */
153
0
    NULL,              /* DONE */
154
0
    init_completed,    /* COMPLETED */
155
    NULL               /* MSGSENT */
156
0
  };
157
158
0
  if(oldstate == state)
159
    /* do not bother when the new state is the same as the old state */
160
0
    return;
161
162
#ifdef DEBUGBUILD
163
  NOVERBOSE((void)lineno);
164
  CURL_TRC_M(data, "-> [%s] (line %d)", CURL_MSTATE_NAME(state), lineno);
165
#else
166
0
  CURL_TRC_M(data, "-> [%s]", CURL_MSTATE_NAME(state));
167
0
#endif
168
169
  /* really switching state */
170
0
  data->mstate = state;
171
0
  switch(state) {
172
0
  case MSTATE_DONE:
173
0
    CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE);
174
0
    break;
175
0
  case MSTATE_COMPLETED:
176
    /* we sometimes directly jump to COMPLETED, trigger also a notification
177
     * in that case. */
178
0
    if(oldstate < MSTATE_DONE)
179
0
      CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE);
180
    /* changing to COMPLETED means it is in process and needs to go */
181
0
    DEBUGASSERT(Curl_uint32_bset_contains(&data->multi->process, data->mid));
182
0
    Curl_uint32_bset_remove(&data->multi->process, data->mid);
183
0
    Curl_uint32_bset_remove(&data->multi->pending, data->mid); /* to be sure */
184
185
0
    if(Curl_uint32_bset_empty(&data->multi->process)) {
186
      /* free the transfer buffer when we have no more active transfers */
187
0
      multi_xfer_bufs_free(data->multi);
188
0
    }
189
0
    break;
190
0
  default:
191
0
    break;
192
0
  }
193
194
  /* if this state has an init-function, run it */
195
0
  if(finit[state])
196
0
    finit[state](data);
197
0
}
198
199
#ifndef DEBUGBUILD
200
0
#define multistate(x, y) mstate(x, y)
201
#else
202
#define multistate(x, y) mstate(x, y, __LINE__)
203
#endif
204
205
/* multi->proto_hash destructor. Should never be called as elements
206
 * MUST be added with their own destructor */
207
static void ph_freeentry(void *p)
208
0
{
209
0
  (void)p;
210
  /* Will always be FALSE. Cannot use a 0 assert here since compilers
211
   * are not in agreement if they then want a NORETURN attribute or
212
   * not. *sigh* */
213
0
  DEBUGASSERT(p == NULL);
214
0
}
215
216
/*
217
 * multi_addmsg()
218
 *
219
 * Called when a transfer is completed. Adds the given msg pointer to
220
 * the list kept in the multi handle.
221
 */
222
static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg)
223
0
{
224
0
  if(!Curl_llist_count(&multi->msglist))
225
0
    CURLM_NTFY(multi->admin, CURLMNOTIFY_INFO_READ);
226
0
  Curl_llist_append(&multi->msglist, msg, &msg->list);
227
0
}
228
229
struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size,
230
                                     size_t ev_hashsize,  /* event hash */
231
                                     size_t chashsize, /* connection hash */
232
                                     size_t dnssize,   /* dns hash */
233
                                     size_t sesssize)  /* TLS session cache */
234
0
{
235
0
  struct Curl_multi *multi = curlx_calloc(1, sizeof(struct Curl_multi));
236
237
0
  if(!multi)
238
0
    return NULL;
239
240
0
  multi->magic = CURL_MULTI_HANDLE;
241
242
0
  Curl_dnscache_init(&multi->dnscache, dnssize);
243
0
  Curl_mntfy_init(multi);
244
0
  Curl_multi_ev_init(multi, ev_hashsize);
245
0
  Curl_uint32_tbl_init(&multi->xfers, NULL);
246
0
  Curl_uint32_bset_init(&multi->process);
247
0
  Curl_uint32_bset_init(&multi->dirty);
248
0
  Curl_uint32_bset_init(&multi->pending);
249
0
  Curl_uint32_bset_init(&multi->msgsent);
250
0
  Curl_hash_init(&multi->proto_hash, 23,
251
0
                 Curl_hash_str, curlx_str_key_compare, ph_freeentry);
252
0
  Curl_llist_init(&multi->msglist, NULL);
253
254
0
  multi->multiplexing = TRUE;
255
0
  multi->max_concurrent_streams = 100;
256
0
  multi->last_timeout_ms = -1;
257
258
0
  if(Curl_mntfy_resize(multi) ||
259
0
     Curl_uint32_bset_resize(&multi->process, xfer_table_size) ||
260
0
     Curl_uint32_bset_resize(&multi->pending, xfer_table_size) ||
261
0
     Curl_uint32_bset_resize(&multi->dirty, xfer_table_size) ||
262
0
     Curl_uint32_bset_resize(&multi->msgsent, xfer_table_size) ||
263
0
     Curl_uint32_tbl_resize(&multi->xfers, xfer_table_size))
264
0
    goto error;
265
266
0
  multi->admin = curl_easy_init();
267
0
  if(!multi->admin)
268
0
    goto error;
269
  /* Initialize admin handle to operate inside this multi */
270
0
  multi->admin->multi = multi;
271
0
  multi->admin->state.internal = TRUE;
272
0
  Curl_llist_init(&multi->admin->state.timeoutlist, NULL);
273
274
#ifdef DEBUGBUILD
275
  if(getenv("CURL_DEBUG"))
276
    multi->admin->set.verbose = TRUE;
277
#endif
278
0
  Curl_uint32_tbl_add(&multi->xfers, multi->admin, &multi->admin->mid);
279
0
  Curl_uint32_bset_add(&multi->process, multi->admin->mid);
280
281
0
  if(Curl_cshutdn_init(&multi->cshutdn, multi))
282
0
    goto error;
283
284
0
  Curl_cpool_init(&multi->cpool, multi->admin, NULL, chashsize);
285
286
#ifdef USE_SSL
287
  if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
288
    goto error;
289
#else
290
0
  (void)sesssize;
291
0
#endif
292
293
#ifdef USE_WINSOCK
294
  multi->wsa_event = WSACreateEvent();
295
  if(multi->wsa_event == WSA_INVALID_EVENT)
296
    goto error;
297
#elif defined(ENABLE_WAKEUP)
298
0
  if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) {
299
0
    multi->wakeup_pair[0] = CURL_SOCKET_BAD;
300
0
    multi->wakeup_pair[1] = CURL_SOCKET_BAD;
301
0
  }
302
0
#endif
303
304
0
#ifdef USE_IPV6
305
0
  if(Curl_probeipv6(multi))
306
0
    goto error;
307
0
#endif
308
309
0
  return multi;
310
311
0
error:
312
313
0
  Curl_multi_ev_cleanup(multi);
314
0
  Curl_hash_destroy(&multi->proto_hash);
315
0
  Curl_dnscache_destroy(&multi->dnscache);
316
0
  Curl_cpool_destroy(&multi->cpool);
317
0
  Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
318
#ifdef USE_SSL
319
  Curl_ssl_scache_destroy(multi->ssl_scache);
320
#endif
321
0
  if(multi->admin) {
322
0
    multi->admin->multi = NULL;
323
0
    Curl_close(&multi->admin);
324
0
  }
325
0
  Curl_mntfy_cleanup(multi);
326
327
0
  Curl_uint32_bset_destroy(&multi->process);
328
0
  Curl_uint32_bset_destroy(&multi->dirty);
329
0
  Curl_uint32_bset_destroy(&multi->pending);
330
0
  Curl_uint32_bset_destroy(&multi->msgsent);
331
0
  Curl_uint32_tbl_destroy(&multi->xfers);
332
333
0
  curlx_free(multi);
334
0
  return NULL;
335
0
}
336
337
CURLM *curl_multi_init(void)
338
0
{
339
0
  return Curl_multi_handle(CURL_XFER_TABLE_SIZE,
340
0
                           CURL_SOCKET_HASH_TABLE_SIZE,
341
0
                           CURL_CONNECTION_HASH_SIZE,
342
0
                           CURL_DNS_HASH_SIZE,
343
0
                           CURL_TLS_SESSION_SIZE);
344
0
}
345
346
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
347
static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data)
348
{
349
  if(!multi->warned) {
350
    infof(data, "!!! WARNING !!!");
351
    infof(data, "This is a debug build of libcurl, "
352
                "do not use in production.");
353
    multi->warned = TRUE;
354
  }
355
}
356
#else
357
0
#define multi_warn_debug(x, y) Curl_nop_stmt
358
#endif
359
360
bool Curl_is_connecting(struct Curl_easy *data)
361
0
{
362
0
  return data->mstate < MSTATE_DO;
363
0
}
364
365
static CURLMcode multi_xfers_add(struct Curl_multi *multi,
366
                                 struct Curl_easy *data)
367
0
{
368
0
  uint32_t capacity = Curl_uint32_tbl_capacity(&multi->xfers);
369
0
  uint32_t new_size = 0;
370
  /* Prepare to make this into a CURLMOPT_MAX_TRANSFERS, because some
371
   * applications may want to prevent a run-away of their memory use. */
372
  /* UINT_MAX is our "invalid" id, do not let the table grow up to that. */
373
0
  const uint32_t max_capacity = UINT_MAX - 1;
374
375
0
  if(capacity < max_capacity) {
376
    /* We want `multi->xfers` to have "sufficient" free rows, so that we do
377
     * have to reuse the `mid` from a removed easy right away.
378
     * Since uint_tbl and uint_bset are memory efficient,
379
     * regard less than 25% free as insufficient.
380
     * (for low capacities, e.g. multi_easy, 4 or less). */
381
0
    uint32_t used = Curl_uint32_tbl_count(&multi->xfers);
382
0
    uint32_t unused = capacity - used;
383
0
    uint32_t min_unused = CURLMAX(capacity >> 2, 4);
384
0
    if(unused <= min_unused) {
385
      /* Make sure the uint arithmetic here works on the corner
386
       * cases where we are close to max_capacity or UINT_MAX */
387
0
      if((min_unused >= max_capacity) ||
388
0
         ((max_capacity - min_unused) <= capacity) ||
389
0
         ((UINT_MAX - min_unused - 63) <= capacity)) {
390
0
        new_size = max_capacity; /* can not be larger than this */
391
0
      }
392
0
      else {
393
        /* make it a 64 multiple, since our bitsets frow by that and
394
         * small (easy_multi) grows to at least 64 on first resize. */
395
0
        new_size = (((used + min_unused) + 63) / 64) * 64;
396
0
      }
397
0
    }
398
0
  }
399
400
0
  if(new_size > capacity) {
401
    /* Grow the bitsets first. Should one fail, we do not need
402
     * to downsize the already resized ones. The sets continue
403
     * to work properly when larger than the table, but not
404
     * the other way around. */
405
0
    CURL_TRC_M(data, "increasing xfer table size to %u", new_size);
406
0
    if(Curl_uint32_bset_resize(&multi->process, new_size) ||
407
0
       Curl_uint32_bset_resize(&multi->dirty, new_size) ||
408
0
       Curl_uint32_bset_resize(&multi->pending, new_size) ||
409
0
       Curl_uint32_bset_resize(&multi->msgsent, new_size) ||
410
0
       Curl_uint32_tbl_resize(&multi->xfers, new_size))
411
0
      return CURLM_OUT_OF_MEMORY;
412
0
  }
413
414
  /* Insert the easy into the table now */
415
0
  if(!Curl_uint32_tbl_add(&multi->xfers, data, &data->mid)) {
416
    /* MUST only happen when table is full */
417
0
    DEBUGASSERT(Curl_uint32_tbl_capacity(&multi->xfers) <=
418
0
                Curl_uint32_tbl_count(&multi->xfers));
419
0
    return CURLM_OUT_OF_MEMORY;
420
0
  }
421
0
  return CURLM_OK;
422
0
}
423
424
CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
425
0
{
426
0
  CURLMcode mresult;
427
0
  struct Curl_multi *multi = m;
428
0
  struct Curl_easy *data = d;
429
430
  /* First, make some basic checks that the CURLM handle is a good handle */
431
0
  if(!GOOD_MULTI_HANDLE(multi))
432
0
    return CURLM_BAD_HANDLE;
433
434
  /* Verify that we got a somewhat good easy handle too */
435
0
  if(!GOOD_EASY_HANDLE(data))
436
0
    return CURLM_BAD_EASY_HANDLE;
437
438
  /* Prevent users from adding same easy handle more than once and prevent
439
     adding to more than one multi stack */
440
0
  if(data->multi)
441
0
    return CURLM_ADDED_ALREADY;
442
443
0
  if(multi->in_callback)
444
0
    return CURLM_RECURSIVE_API_CALL;
445
446
0
  if(multi->dead) {
447
    /* a "dead" handle cannot get added transfers while any existing easy
448
       handles are still alive - but if there are none alive anymore, it is
449
       fine to start over and unmark the "deadness" of this handle.
450
       This means only the admin handle MUST be present. */
451
0
    if((Curl_uint32_tbl_count(&multi->xfers) != 1) ||
452
0
       !Curl_uint32_tbl_contains(&multi->xfers, 0))
453
0
      return CURLM_ABORTED_BY_CALLBACK;
454
0
    multi->dead = FALSE;
455
0
    Curl_uint32_bset_clear(&multi->process);
456
0
    Curl_uint32_bset_clear(&multi->dirty);
457
0
    Curl_uint32_bset_clear(&multi->pending);
458
0
    Curl_uint32_bset_clear(&multi->msgsent);
459
0
  }
460
461
0
  if(data->multi_easy) {
462
    /* if this easy handle was previously used for curl_easy_perform(), there
463
       is a private multi handle here that we can kill */
464
0
    curl_multi_cleanup(data->multi_easy);
465
0
    data->multi_easy = NULL;
466
0
  }
467
468
  /* Insert the easy into the multi->xfers table, assigning it a `mid`. */
469
0
  if(multi_xfers_add(multi, data))
470
0
    return CURLM_OUT_OF_MEMORY;
471
472
  /* Initialize timeout list for this handle */
473
0
  Curl_llist_init(&data->state.timeoutlist, NULL);
474
475
  /*
476
   * No failure allowed in this function beyond this point. No modification of
477
   * easy nor multi handle allowed before this except for potential multi's
478
   * connection pool growing which will not be undone in this function no
479
   * matter what.
480
   */
481
0
  if(data->set.errorbuffer)
482
0
    data->set.errorbuffer[0] = 0;
483
484
0
  data->state.os_errno = 0;
485
486
  /* make the Curl_easy refer back to this multi handle - before Curl_expire()
487
     is called. */
488
0
  data->multi = multi;
489
490
  /* set the easy handle */
491
0
  multistate(data, MSTATE_INIT);
492
493
#ifdef USE_LIBPSL
494
  /* Do the same for PSL. */
495
  if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL)))
496
    data->psl = &data->share->psl;
497
  else
498
    data->psl = &multi->psl;
499
#endif
500
501
  /* add the easy handle to the process set */
502
0
  Curl_uint32_bset_add(&multi->process, data->mid);
503
0
  ++multi->xfers_alive;
504
0
  ++multi->xfers_total_ever;
505
506
0
  Curl_cpool_xfer_init(data);
507
0
  multi_warn_debug(multi, data);
508
509
  /* Make sure the new handle will run */
510
0
  Curl_multi_mark_dirty(data);
511
512
  /* Necessary in event based processing, where dirty handles trigger
513
   * a timeout callback invocation. */
514
0
  mresult = Curl_update_timer(multi);
515
0
  if(mresult) {
516
0
    data->multi = NULL; /* not anymore */
517
0
    Curl_uint32_tbl_remove(&multi->xfers, data->mid);
518
0
    data->mid = UINT32_MAX;
519
0
    return mresult;
520
0
  }
521
522
  /* The admin handle only ever has default timeouts set. To improve the
523
     state somewhat we clone the timeouts from each added handle so that the
524
     admin handle always has the same timeouts as the most recently added
525
     easy handle. */
526
0
  multi->admin->set.timeout = data->set.timeout;
527
0
  multi->admin->set.server_response_timeout =
528
0
    data->set.server_response_timeout;
529
0
  multi->admin->set.no_signal = data->set.no_signal;
530
531
0
  CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u",
532
0
             data->mid, Curl_multi_xfers_running(multi),
533
0
             Curl_uint32_tbl_count(&multi->xfers));
534
0
  return CURLM_OK;
535
0
}
536
537
#if 0
538
/* Debug-function, used like this:
539
 *
540
 * Curl_hash_print(&multi->sockhash, debug_print_sock_hash);
541
 *
542
 * Enable the hash print function first by editing hash.c
543
 */
544
static void debug_print_sock_hash(void *p)
545
{
546
  struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p;
547
548
  curl_mfprintf(stderr, " [readers %u][writers %u]",
549
                sh->readers, sh->writers);
550
}
551
#endif
552
553
struct multi_done_ctx {
554
  BIT(premature);
555
};
556
557
static bool multi_conn_should_close(struct connectdata *conn,
558
                                    struct Curl_easy *data,
559
                                    bool premature)
560
0
{
561
  /* if conn->bits.close is TRUE, it means that the connection should be
562
     closed in spite of everything else. */
563
0
  if(conn->bits.close)
564
0
    return TRUE;
565
566
  /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
567
     forced us to close this connection. This is ignored for requests taking
568
     place in a NTLM/NEGOTIATE authentication handshake. */
569
0
  if(data->set.reuse_forbid
570
#ifdef USE_NTLM
571
     && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 ||
572
          conn->proxy_ntlm_state == NTLMSTATE_TYPE2)
573
#endif
574
#ifdef USE_SPNEGO
575
     && !(conn->http_negotiate_state == GSS_AUTHRECV ||
576
          conn->proxy_negotiate_state == GSS_AUTHRECV)
577
#endif
578
0
    )
579
0
    return TRUE;
580
581
  /* Unless this connection is for a "connect-only" transfer, it
582
   * needs to be closed if the protocol handler does not support reuse. */
583
0
  if(!data->set.connect_only && conn->scheme &&
584
0
     !(conn->scheme->flags & PROTOPT_CONN_REUSE))
585
0
    return TRUE;
586
587
  /* if premature is TRUE, it means this connection was said to be DONE before
588
     the entire request operation is complete and thus we cannot know in what
589
     state it is for reusing, so we are forced to close it. In a perfect world
590
     we can add code that keep track of if we really must close it here or not,
591
     but currently we have no such detail knowledge. */
592
0
  if(premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))
593
0
    return TRUE;
594
595
0
  return FALSE;
596
0
}
597
598
static void multi_done_locked(struct connectdata *conn,
599
                              struct Curl_easy *data,
600
                              void *userdata)
601
0
{
602
0
  struct multi_done_ctx *mdctx = userdata;
603
0
#ifdef CURLVERBOSE
604
0
  const char *host =
605
0
#ifndef CURL_DISABLE_PROXY
606
0
        conn->bits.socksproxy ?
607
0
        conn->socks_proxy.host.dispname :
608
0
        conn->bits.httpproxy ? conn->http_proxy.host.dispname :
609
0
#endif
610
0
        conn->bits.conn_to_host ? conn->conn_to_host.dispname :
611
0
        conn->host.dispname;
612
0
  int port =
613
0
#ifndef CURL_DISABLE_PROXY
614
0
        conn->bits.httpproxy ? conn->http_proxy.port :
615
0
#endif
616
0
        conn->bits.conn_to_port ? conn->conn_to_port :
617
0
        conn->remote_port;
618
0
#endif /* CURLVERBOSE */
619
620
0
  Curl_detach_connection(data);
621
622
0
  CURL_TRC_M(data, "multi_done_locked, in use=%u", conn->attached_xfers);
623
0
  if(CONN_INUSE(conn)) {
624
    /* Stop if still used. */
625
0
    CURL_TRC_M(data, "Connection still in use %u, no more multi_done now!",
626
0
               conn->attached_xfers);
627
0
    return;
628
0
  }
629
630
0
  data->state.done = TRUE; /* called now! */
631
0
  data->state.recent_conn_id = conn->connection_id;
632
633
0
  Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
634
0
  Curl_resolv_unlink(data, &data->state.dns[1]);
635
0
  Curl_dnscache_prune(data);
636
637
0
  if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) {
638
0
    CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%d, "
639
0
               "forbid=%d, close=%d, premature=%d, conn_multiplex=%d",
640
0
               conn->connection_id, host, port, data->set.reuse_forbid,
641
0
               conn->bits.close, mdctx->premature,
642
0
               Curl_conn_is_multiplex(conn, FIRSTSOCKET));
643
0
    connclose(conn, "disconnecting");
644
0
    Curl_conn_terminate(data, conn, (bool)mdctx->premature);
645
0
  }
646
0
  else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
647
0
    CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s:%d was shutdown"
648
0
               " by server, not reusing", conn->connection_id, host, port);
649
0
    connclose(conn, "server shutdown");
650
0
    Curl_conn_terminate(data, conn, (bool)mdctx->premature);
651
0
  }
652
0
  else {
653
    /* the connection is no longer in use by any transfer */
654
0
    if(Curl_cpool_conn_now_idle(data, conn)) {
655
      /* connection kept in the cpool */
656
0
      data->state.lastconnect_id = conn->connection_id;
657
0
      infof(data, "Connection #%" FMT_OFF_T " to host %s:%d left intact",
658
0
            conn->connection_id, host, port);
659
0
    }
660
0
    else {
661
      /* connection was removed from the cpool and destroyed. */
662
0
      data->state.lastconnect_id = -1;
663
0
    }
664
0
  }
665
0
}
666
667
static CURLcode multi_done(struct Curl_easy *data,
668
                           CURLcode status,  /* an error if this is called
669
                                                after an error was detected */
670
                           bool premature)
671
0
{
672
0
  CURLcode result;
673
0
  struct connectdata *conn = data->conn;
674
0
  struct multi_done_ctx mdctx;
675
676
0
  memset(&mdctx, 0, sizeof(mdctx));
677
678
0
  CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d",
679
0
             (int)status, (int)premature, data->state.done);
680
681
0
  if(data->state.done)
682
    /* Stop if multi_done() has already been called */
683
0
    return CURLE_OK;
684
685
  /* Shut down any ongoing async resolver operation. */
686
0
  Curl_async_shutdown(data);
687
688
  /* Cleanup possible redirect junk */
689
0
  Curl_safefree(data->req.newurl);
690
0
  Curl_safefree(data->req.location);
691
692
0
  switch(status) {
693
0
  case CURLE_ABORTED_BY_CALLBACK:
694
0
  case CURLE_READ_ERROR:
695
0
  case CURLE_WRITE_ERROR:
696
    /* When we are aborted due to a callback return code it has to be counted
697
       as premature as there is trouble ahead if we do not. We have many
698
       callbacks and protocols work differently, we could potentially do this
699
       more fine-grained in the future. */
700
0
    premature = TRUE;
701
0
    FALLTHROUGH();
702
0
  default:
703
0
    break;
704
0
  }
705
706
  /* this calls the protocol-specific function pointer previously set */
707
0
  if(conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT))
708
0
    result = conn->scheme->run->done(data, status, premature);
709
0
  else
710
0
    result = status;
711
712
0
  if(result != CURLE_ABORTED_BY_CALLBACK) {
713
    /* avoid this if we already aborted by callback to avoid this calling
714
       another callback */
715
0
    int rc = Curl_pgrsDone(data);
716
0
    if(!result && rc)
717
0
      result = CURLE_ABORTED_BY_CALLBACK;
718
0
  }
719
720
  /* Make sure that transfer client writes are really done now. */
721
0
  result = Curl_1st_err(result, Curl_xfer_write_done(data, premature));
722
723
  /* Inform connection filters that this transfer is done */
724
0
  Curl_conn_ev_data_done(data, premature);
725
726
0
  process_pending_handles(data->multi); /* connection / multiplex */
727
728
0
  if(!result)
729
0
    result = Curl_req_done(&data->req, data, premature);
730
731
  /* Under the potential connection pool's share lock, decide what to
732
   * do with the transfer's connection. */
733
0
  mdctx.premature = premature;
734
0
  Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx);
735
736
  /* flush the netrc cache */
737
0
  Curl_netrc_cleanup(&data->state.netrc);
738
0
  return result;
739
0
}
740
741
static void close_connect_only(struct connectdata *conn,
742
                               struct Curl_easy *data,
743
                               void *userdata)
744
0
{
745
0
  (void)userdata;
746
0
  (void)data;
747
0
  if(conn->connect_only)
748
0
    connclose(conn, "Removing connect-only easy handle");
749
0
}
750
751
CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
752
0
{
753
0
  struct Curl_multi *multi = m;
754
0
  struct Curl_easy *data = d;
755
0
  bool premature;
756
0
  struct Curl_llist_node *e;
757
0
  CURLMcode mresult;
758
0
  uint32_t mid;
759
760
  /* First, make some basic checks that the CURLM handle is a good handle */
761
0
  if(!GOOD_MULTI_HANDLE(multi))
762
0
    return CURLM_BAD_HANDLE;
763
764
  /* Verify that we got a somewhat good easy handle too */
765
0
  if(!GOOD_EASY_HANDLE(data))
766
0
    return CURLM_BAD_EASY_HANDLE;
767
768
  /* Prevent users from trying to remove same easy handle more than once */
769
0
  if(!data->multi)
770
0
    return CURLM_OK; /* it is already removed so let's say it is fine! */
771
772
  /* Prevent users from trying to remove an easy handle from the wrong multi */
773
0
  if(data->multi != multi)
774
0
    return CURLM_BAD_EASY_HANDLE;
775
776
0
  if(data->mid == UINT32_MAX) {
777
0
    DEBUGASSERT(0);
778
0
    return CURLM_INTERNAL_ERROR;
779
0
  }
780
0
  if(Curl_uint32_tbl_get(&multi->xfers, data->mid) != data) {
781
0
    DEBUGASSERT(0);
782
0
    return CURLM_INTERNAL_ERROR;
783
0
  }
784
785
0
  if(multi->in_callback)
786
0
    return CURLM_RECURSIVE_API_CALL;
787
788
0
  premature = (data->mstate < MSTATE_COMPLETED);
789
790
  /* If the 'state' is not INIT or COMPLETED, we might need to do something
791
     nice to put the easy_handle in a good known state when this returns. */
792
0
  if(data->conn &&
793
0
     data->mstate > MSTATE_DO &&
794
0
     data->mstate < MSTATE_COMPLETED) {
795
    /* Set connection owner so that the DONE function closes it. We can
796
       safely do this here since connection is killed. */
797
0
    streamclose(data->conn, "Removed with partial response");
798
0
  }
799
800
0
  if(data->conn) {
801
    /* multi_done() clears the association between the easy handle and the
802
       connection.
803
804
       Note that this ignores the return code because there is
805
       nothing really useful to do with it anyway! */
806
0
    (void)multi_done(data, data->result, premature);
807
0
  }
808
809
  /* The timer must be shut down before data->multi is set to NULL, else the
810
     timenode will remain in the splay tree after curl_easy_cleanup is
811
     called. Do it after multi_done() in case that sets another time! */
812
0
  Curl_expire_clear(data);
813
814
  /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */
815
0
  if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid))
816
0
    --multi->xfers_alive;
817
818
0
  Curl_wildcard_dtor(&data->wildcard);
819
820
0
  data->mstate = MSTATE_COMPLETED;
821
822
  /* Remove the association between the connection and the handle */
823
0
  Curl_detach_connection(data);
824
825
  /* Tell event handling that this transfer is definitely going away */
826
0
  Curl_multi_ev_xfer_done(multi, data);
827
828
0
  if(data->set.connect_only && !data->multi_easy) {
829
    /* This removes a handle that was part the multi interface that used
830
       CONNECT_ONLY, that connection is now left alive but since this handle
831
       has bits.close set nothing can use that transfer anymore and it is
832
       forbidden from reuse. This easy handle cannot find the connection
833
       anymore once removed from the multi handle
834
835
       Better close the connection here, at once.
836
    */
837
0
    struct connectdata *c;
838
0
    curl_socket_t s;
839
0
    s = Curl_getconnectinfo(data, &c);
840
0
    if((s != CURL_SOCKET_BAD) && c) {
841
0
      Curl_conn_terminate(data, c, TRUE);
842
0
    }
843
0
  }
844
845
0
  if(data->state.lastconnect_id != -1) {
846
    /* Mark any connect-only connection for closure */
847
0
    Curl_cpool_do_by_id(data, data->state.lastconnect_id,
848
0
                        close_connect_only, NULL);
849
0
  }
850
851
#ifdef USE_LIBPSL
852
  /* Remove the PSL association. */
853
  if(data->psl == &multi->psl)
854
    data->psl = NULL;
855
#endif
856
857
  /* make sure there is no pending message in the queue sent from this easy
858
     handle */
859
0
  for(e = Curl_llist_head(&multi->msglist); e; e = Curl_node_next(e)) {
860
0
    struct Curl_message *msg = Curl_node_elem(e);
861
862
0
    if(msg->extmsg.easy_handle == data) {
863
0
      Curl_node_remove(e);
864
      /* there can only be one from this specific handle */
865
0
      break;
866
0
    }
867
0
  }
868
869
  /* clear the association to this multi handle */
870
0
  mid = data->mid;
871
0
  DEBUGASSERT(Curl_uint32_tbl_contains(&multi->xfers, mid));
872
0
  Curl_uint32_tbl_remove(&multi->xfers, mid);
873
0
  Curl_uint32_bset_remove(&multi->process, mid);
874
0
  Curl_uint32_bset_remove(&multi->dirty, mid);
875
0
  Curl_uint32_bset_remove(&multi->pending, mid);
876
0
  Curl_uint32_bset_remove(&multi->msgsent, mid);
877
0
  data->multi = NULL;
878
0
  data->mid = UINT32_MAX;
879
0
  data->master_mid = UINT32_MAX;
880
881
  /* NOTE NOTE NOTE
882
     We do not touch the easy handle here! */
883
0
  process_pending_handles(multi);
884
885
0
  mresult = Curl_update_timer(multi);
886
0
  if(mresult)
887
0
    return mresult;
888
889
0
  CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u",
890
0
             mid, Curl_multi_xfers_running(multi),
891
0
             Curl_uint32_tbl_count(&multi->xfers));
892
0
  return CURLM_OK;
893
0
}
894
895
/* Return TRUE if the application asked for multiplexing */
896
bool Curl_multiplex_wanted(const struct Curl_multi *multi)
897
0
{
898
0
  return multi && multi->multiplexing;
899
0
}
900
901
/*
902
 * Curl_detach_connection() removes the given transfer from the connection.
903
 *
904
 * This is the only function that should clear data->conn. This will
905
 * occasionally be called with the data->conn pointer already cleared.
906
 */
907
void Curl_detach_connection(struct Curl_easy *data)
908
0
{
909
0
  struct connectdata *conn = data->conn;
910
0
  if(conn) {
911
    /* this should never happen, prevent underflow */
912
0
    DEBUGASSERT(conn->attached_xfers);
913
0
    if(conn->attached_xfers) {
914
0
      conn->attached_xfers--;
915
0
      if(!conn->attached_xfers)
916
0
        conn->attached_multi = NULL;
917
0
    }
918
0
  }
919
0
  data->conn = NULL;
920
0
}
921
922
/*
923
 * Curl_attach_connection() attaches this transfer to this connection.
924
 *
925
 * This is the only function that should assign data->conn
926
 */
927
void Curl_attach_connection(struct Curl_easy *data,
928
                            struct connectdata *conn)
929
0
{
930
0
  DEBUGASSERT(data);
931
0
  DEBUGASSERT(!data->conn);
932
0
  DEBUGASSERT(conn);
933
0
  DEBUGASSERT(conn->attached_xfers < UINT32_MAX);
934
0
  data->conn = conn;
935
0
  conn->attached_xfers++;
936
  /* all attached transfers must be from the same multi */
937
0
  if(!conn->attached_multi)
938
0
    conn->attached_multi = data->multi;
939
0
  DEBUGASSERT(conn->attached_multi == data->multi);
940
941
0
  if(conn->scheme && conn->scheme->run->attach)
942
0
    conn->scheme->run->attach(data, conn);
943
0
}
944
945
/* adjust pollset for rate limits/pauses */
946
static CURLcode multi_adjust_pollset(struct Curl_easy *data,
947
                                     struct easy_pollset *ps)
948
0
{
949
0
  CURLcode result = CURLE_OK;
950
951
0
  if(ps->n) {
952
0
    const struct curltime *pnow = Curl_pgrs_now(data);
953
0
    bool send_blocked, recv_blocked;
954
955
0
    recv_blocked = (Curl_rlimit_avail(&data->progress.dl.rlimit, pnow) <= 0);
956
0
    send_blocked = (Curl_rlimit_avail(&data->progress.ul.rlimit, pnow) <= 0);
957
0
    if(send_blocked || recv_blocked) {
958
0
      int i;
959
0
      for(i = 0; i <= SECONDARYSOCKET; ++i) {
960
0
        curl_socket_t sock = data->conn->sock[i];
961
0
        if(sock == CURL_SOCKET_BAD)
962
0
          continue;
963
0
        if(recv_blocked && Curl_pollset_want_recv(data, ps, sock)) {
964
0
          result = Curl_pollset_remove_in(data, ps, sock);
965
0
          if(result)
966
0
            break;
967
0
        }
968
0
        if(send_blocked && Curl_pollset_want_send(data, ps, sock)) {
969
0
          result = Curl_pollset_remove_out(data, ps, sock);
970
0
          if(result)
971
0
            break;
972
0
        }
973
0
      }
974
0
    }
975
976
    /* Not blocked and wanting to receive. If there is data pending
977
     * in the connection filters, make transfer run again. */
978
0
    if(!recv_blocked &&
979
0
       ((Curl_pollset_want_recv(data, ps, data->conn->sock[FIRSTSOCKET]) &&
980
0
         Curl_conn_data_pending(data, FIRSTSOCKET)) ||
981
0
        (Curl_pollset_want_recv(data, ps, data->conn->sock[SECONDARYSOCKET]) &&
982
0
         Curl_conn_data_pending(data, SECONDARYSOCKET)))) {
983
0
      CURL_TRC_M(data, "pollset[] has POLLIN, but there is still "
984
0
                 "buffered input -> mark as dirty");
985
0
      Curl_multi_mark_dirty(data);
986
0
    }
987
0
  }
988
0
  return result;
989
0
}
990
991
static CURLcode mstate_connecting_pollset(struct Curl_easy *data,
992
                                          struct easy_pollset *ps)
993
0
{
994
0
  struct connectdata *conn = data->conn;
995
0
  curl_socket_t sockfd;
996
0
  CURLcode result = CURLE_OK;
997
998
0
  if(Curl_xfer_recv_is_paused(data))
999
0
    return CURLE_OK;
1000
  /* If a socket is set, receiving is default. If the socket
1001
   * has not been determined yet (eyeballing), always ask the
1002
   * connection filters for what to monitor. */
1003
0
  sockfd = Curl_conn_get_first_socket(data);
1004
0
  if(sockfd != CURL_SOCKET_BAD) {
1005
0
    result = Curl_pollset_change(data, ps, sockfd, CURL_POLL_IN, 0);
1006
0
    if(!result)
1007
0
      result = multi_adjust_pollset(data, ps);
1008
0
  }
1009
0
  if(!result)
1010
0
    result = Curl_conn_adjust_pollset(data, conn, ps);
1011
0
  return result;
1012
0
}
1013
1014
static CURLcode mstate_protocol_pollset(struct Curl_easy *data,
1015
                                        struct easy_pollset *ps)
1016
0
{
1017
0
  struct connectdata *conn = data->conn;
1018
0
  CURLcode result = CURLE_OK;
1019
1020
0
  if(conn->scheme->run->proto_pollset)
1021
0
    result = conn->scheme->run->proto_pollset(data, ps);
1022
0
  else {
1023
0
    curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1024
0
    if(sockfd != CURL_SOCKET_BAD) {
1025
      /* Default is to wait to something from the server */
1026
0
      result = Curl_pollset_change(data, ps, sockfd, CURL_POLL_IN, 0);
1027
0
    }
1028
0
  }
1029
0
  if(!result)
1030
0
    result = multi_adjust_pollset(data, ps);
1031
0
  if(!result)
1032
0
    result = Curl_conn_adjust_pollset(data, conn, ps);
1033
0
  return result;
1034
0
}
1035
1036
static CURLcode mstate_do_pollset(struct Curl_easy *data,
1037
                                  struct easy_pollset *ps)
1038
0
{
1039
0
  struct connectdata *conn = data->conn;
1040
0
  CURLcode result = CURLE_OK;
1041
1042
0
  if(conn->scheme->run->doing_pollset)
1043
0
    result = conn->scheme->run->doing_pollset(data, ps);
1044
0
  else if(CONN_SOCK_IDX_VALID(conn->send_idx)) {
1045
    /* Default is that we want to send something to the server */
1046
0
    result = Curl_pollset_add_out(data, ps, conn->sock[conn->send_idx]);
1047
0
  }
1048
0
  if(!result)
1049
0
    result = multi_adjust_pollset(data, ps);
1050
0
  if(!result)
1051
0
    result = Curl_conn_adjust_pollset(data, conn, ps);
1052
0
  return result;
1053
0
}
1054
1055
static CURLcode mstate_domore_pollset(struct Curl_easy *data,
1056
                                      struct easy_pollset *ps)
1057
0
{
1058
0
  struct connectdata *conn = data->conn;
1059
0
  CURLcode result = CURLE_OK;
1060
1061
0
  if(conn->scheme->run->domore_pollset)
1062
0
    result = conn->scheme->run->domore_pollset(data, ps);
1063
0
  else if(CONN_SOCK_IDX_VALID(conn->send_idx)) {
1064
    /* Default is that we want to send something to the server */
1065
0
    result = Curl_pollset_add_out(data, ps, conn->sock[conn->send_idx]);
1066
0
  }
1067
0
  if(!result)
1068
0
    result = multi_adjust_pollset(data, ps);
1069
0
  if(!result)
1070
0
    result = Curl_conn_adjust_pollset(data, conn, ps);
1071
0
  return result;
1072
0
}
1073
1074
static CURLcode mstate_perform_pollset(struct Curl_easy *data,
1075
                                       struct easy_pollset *ps)
1076
0
{
1077
0
  struct connectdata *conn = data->conn;
1078
0
  CURLcode result = CURLE_OK;
1079
1080
0
  if(conn->scheme->run->perform_pollset)
1081
0
    result = conn->scheme->run->perform_pollset(data, ps);
1082
0
  else {
1083
    /* Default is to obey the data->req.keepon flags for send/recv */
1084
0
    if(Curl_req_want_recv(data) && CONN_SOCK_IDX_VALID(conn->recv_idx)) {
1085
0
      result = Curl_pollset_add_in(data, ps, conn->sock[conn->recv_idx]);
1086
0
    }
1087
0
    if(!result && Curl_req_want_send(data) &&
1088
0
       CONN_SOCK_IDX_VALID(conn->send_idx)) {
1089
0
      result = Curl_pollset_add_out(data, ps, conn->sock[conn->send_idx]);
1090
0
    }
1091
0
  }
1092
0
  if(!result)
1093
0
    result = multi_adjust_pollset(data, ps);
1094
0
  if(!result)
1095
0
    result = Curl_conn_adjust_pollset(data, conn, ps);
1096
0
  return result;
1097
0
}
1098
1099
/* Initializes `poll_set` with the current socket poll actions needed
1100
 * for transfer `data`. */
1101
CURLMcode Curl_multi_pollset(struct Curl_easy *data,
1102
                             struct easy_pollset *ps)
1103
0
{
1104
0
  CURLMcode mresult = CURLM_OK;
1105
0
  CURLcode result = CURLE_OK;
1106
1107
  /* If the transfer has no connection, this is fine. Happens when
1108
     called via curl_multi_remove_handle() => Curl_multi_ev_assess() =>
1109
     Curl_multi_pollset(). */
1110
0
  Curl_pollset_reset(ps);
1111
0
  if(!data->conn)
1112
0
    return CURLM_OK;
1113
1114
0
  switch(data->mstate) {
1115
0
  case MSTATE_INIT:
1116
0
  case MSTATE_PENDING:
1117
0
  case MSTATE_SETUP:
1118
0
  case MSTATE_CONNECT:
1119
    /* nothing to poll for yet */
1120
0
    break;
1121
1122
0
  case MSTATE_RESOLVING:
1123
0
    result = Curl_resolv_pollset(data, ps);
1124
0
    break;
1125
1126
0
  case MSTATE_CONNECTING:
1127
0
    result = mstate_connecting_pollset(data, ps);
1128
0
    break;
1129
1130
0
  case MSTATE_PROTOCONNECT:
1131
0
  case MSTATE_PROTOCONNECTING:
1132
0
    result = mstate_protocol_pollset(data, ps);
1133
0
    break;
1134
1135
0
  case MSTATE_DO:
1136
0
  case MSTATE_DOING:
1137
0
    result = mstate_do_pollset(data, ps);
1138
0
    break;
1139
1140
0
  case MSTATE_DOING_MORE:
1141
0
    result = mstate_domore_pollset(data, ps);
1142
0
    break;
1143
1144
0
  case MSTATE_DID: /* same as PERFORMING in regard to polling */
1145
0
  case MSTATE_PERFORMING:
1146
0
    result = mstate_perform_pollset(data, ps);
1147
0
    break;
1148
1149
0
  case MSTATE_RATELIMITING:
1150
    /* we need to let time pass, ignore socket(s) */
1151
0
    break;
1152
1153
0
  case MSTATE_DONE:
1154
0
  case MSTATE_COMPLETED:
1155
0
  case MSTATE_MSGSENT:
1156
    /* nothing more to poll for */
1157
0
    break;
1158
1159
0
  default:
1160
0
    failf(data, "multi_getsock: unexpected multi state %d", data->mstate);
1161
0
    DEBUGASSERT(0);
1162
0
    break;
1163
0
  }
1164
1165
0
  if(result) {
1166
0
    if(result == CURLE_OUT_OF_MEMORY)
1167
0
      mresult = CURLM_OUT_OF_MEMORY;
1168
0
    else {
1169
0
      failf(data, "error determining pollset: %d", result);
1170
0
      mresult = CURLM_INTERNAL_ERROR;
1171
0
    }
1172
0
    goto out;
1173
0
  }
1174
1175
0
#ifdef CURLVERBOSE
1176
0
  if(CURL_TRC_M_is_verbose(data)) {
1177
0
    size_t timeout_count = Curl_llist_count(&data->state.timeoutlist);
1178
0
    switch(ps->n) {
1179
0
    case 0:
1180
0
      CURL_TRC_M(data, "pollset[], timeouts=%zu, paused %d/%d (r/w)",
1181
0
                 timeout_count,
1182
0
                 Curl_xfer_send_is_paused(data),
1183
0
                 Curl_xfer_recv_is_paused(data));
1184
0
      break;
1185
0
    case 1:
1186
0
      CURL_TRC_M(data, "pollset[fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu",
1187
0
                 ps->sockets[0],
1188
0
                 (ps->actions[0] & CURL_POLL_IN) ? "IN" : "",
1189
0
                 (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "",
1190
0
                 timeout_count);
1191
0
      break;
1192
0
    case 2:
1193
0
      CURL_TRC_M(data, "pollset[fd=%" FMT_SOCKET_T " %s%s, "
1194
0
                 "fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu",
1195
0
                 ps->sockets[0],
1196
0
                 (ps->actions[0] & CURL_POLL_IN) ? "IN" : "",
1197
0
                 (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "",
1198
0
                 ps->sockets[1],
1199
0
                 (ps->actions[1] & CURL_POLL_IN) ? "IN" : "",
1200
0
                 (ps->actions[1] & CURL_POLL_OUT) ? "OUT" : "",
1201
0
                 timeout_count);
1202
0
      break;
1203
0
    default:
1204
0
      CURL_TRC_M(data, "pollset[fds=%u], timeouts=%zu", ps->n, timeout_count);
1205
0
      break;
1206
0
    }
1207
0
    CURL_TRC_EASY_TIMERS(data);
1208
0
  }
1209
0
#endif
1210
1211
0
out:
1212
0
  return mresult;
1213
0
}
1214
1215
CURLMcode curl_multi_fdset(CURLM *m,
1216
                           fd_set *read_fd_set, fd_set *write_fd_set,
1217
                           fd_set *exc_fd_set, int *max_fd)
1218
0
{
1219
  /* Scan through all the easy handles to get the file descriptors set.
1220
     Some easy handles may not have connected to the remote host yet,
1221
     and then we must make sure that is done. */
1222
0
  int this_max_fd = -1;
1223
0
  struct Curl_multi *multi = m;
1224
0
  struct easy_pollset ps;
1225
0
  unsigned int i;
1226
0
  uint32_t mid;
1227
0
  (void)exc_fd_set;
1228
1229
0
  if(!GOOD_MULTI_HANDLE(multi))
1230
0
    return CURLM_BAD_HANDLE;
1231
1232
0
  if(multi->in_callback)
1233
0
    return CURLM_RECURSIVE_API_CALL;
1234
1235
0
  Curl_pollset_init(&ps);
1236
0
  if(Curl_uint32_bset_first(&multi->process, &mid)) {
1237
0
    do {
1238
0
      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
1239
1240
0
      if(!data) {
1241
0
        DEBUGASSERT(0);
1242
0
        continue;
1243
0
      }
1244
1245
0
      Curl_multi_pollset(data, &ps);
1246
0
      for(i = 0; i < ps.n; i++) {
1247
0
        if(!FDSET_SOCK(ps.sockets[i]))
1248
          /* pretend it does not exist */
1249
0
          continue;
1250
0
        if(ps.actions[i] & CURL_POLL_IN)
1251
0
          FD_SET(ps.sockets[i], read_fd_set);
1252
0
        if(ps.actions[i] & CURL_POLL_OUT)
1253
0
          FD_SET(ps.sockets[i], write_fd_set);
1254
0
        if((int)ps.sockets[i] > this_max_fd)
1255
0
          this_max_fd = (int)ps.sockets[i];
1256
0
      }
1257
0
    } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
1258
0
  }
1259
1260
0
  Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
1261
0
                      read_fd_set, write_fd_set, &this_max_fd);
1262
1263
0
  *max_fd = this_max_fd;
1264
0
  Curl_pollset_cleanup(&ps);
1265
1266
0
  return CURLM_OK;
1267
0
}
1268
1269
CURLMcode curl_multi_waitfds(CURLM *m,
1270
                             struct curl_waitfd *ufds,
1271
                             unsigned int size,
1272
                             unsigned int *fd_count)
1273
0
{
1274
0
  struct Curl_waitfds cwfds;
1275
0
  CURLMcode mresult = CURLM_OK;
1276
0
  struct Curl_multi *multi = m;
1277
0
  struct easy_pollset ps;
1278
0
  unsigned int need = 0;
1279
0
  uint32_t mid;
1280
1281
0
  if(!ufds && (size || !fd_count))
1282
0
    return CURLM_BAD_FUNCTION_ARGUMENT;
1283
1284
0
  if(!GOOD_MULTI_HANDLE(multi))
1285
0
    return CURLM_BAD_HANDLE;
1286
1287
0
  if(multi->in_callback)
1288
0
    return CURLM_RECURSIVE_API_CALL;
1289
1290
0
  Curl_pollset_init(&ps);
1291
0
  Curl_waitfds_init(&cwfds, ufds, size);
1292
0
  if(Curl_uint32_bset_first(&multi->process, &mid)) {
1293
0
    do {
1294
0
      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
1295
0
      if(!data) {
1296
0
        DEBUGASSERT(0);
1297
0
        Curl_uint32_bset_remove(&multi->process, mid);
1298
0
        Curl_uint32_bset_remove(&multi->dirty, mid);
1299
0
        continue;
1300
0
      }
1301
0
      Curl_multi_pollset(data, &ps);
1302
0
      need += Curl_waitfds_add_ps(&cwfds, &ps);
1303
0
    } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
1304
0
  }
1305
1306
0
  need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
1307
1308
0
  if(need != cwfds.n && ufds)
1309
0
    mresult = CURLM_OUT_OF_MEMORY;
1310
1311
0
  if(fd_count)
1312
0
    *fd_count = need;
1313
0
  Curl_pollset_cleanup(&ps);
1314
0
  return mresult;
1315
0
}
1316
1317
#ifdef USE_WINSOCK
1318
/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets cannot
1319
 * be reset this way because an empty datagram would be sent. #9203
1320
 *
1321
 * "On Windows the internal state of FD_WRITE as returned from
1322
 * WSAEnumNetworkEvents is only reset after successful send()."
1323
 */
1324
static void reset_socket_fdwrite(curl_socket_t s)
1325
{
1326
  int t;
1327
  int l = (int)sizeof(t);
1328
  if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM)
1329
    swrite(s, NULL, 0);
1330
}
1331
#endif
1332
1333
0
#define NUM_POLLS_ON_STACK 10
1334
1335
static CURLMcode multi_wait(struct Curl_multi *multi,
1336
                            struct curl_waitfd extra_fds[],
1337
                            unsigned int extra_nfds,
1338
                            int timeout_ms,
1339
                            int *ret,
1340
                            bool extrawait, /* when no socket, wait */
1341
                            bool use_wakeup)
1342
0
{
1343
0
  size_t i;
1344
0
  struct curltime expire_time;
1345
0
  long timeout_internal;
1346
0
  int retcode = 0;
1347
0
  struct easy_pollset ps;
1348
0
  struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
1349
0
  struct curl_pollfds cpfds;
1350
0
  unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */
1351
0
  struct Curl_easy *data = NULL;
1352
0
  CURLMcode mresult = CURLM_OK;
1353
0
  uint32_t mid;
1354
1355
#ifdef USE_WINSOCK
1356
  WSANETWORKEVENTS wsa_events;
1357
  DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
1358
#endif
1359
#ifndef ENABLE_WAKEUP
1360
  (void)use_wakeup;
1361
#endif
1362
1363
0
  if(!GOOD_MULTI_HANDLE(multi))
1364
0
    return CURLM_BAD_HANDLE;
1365
1366
0
  if(multi->in_callback)
1367
0
    return CURLM_RECURSIVE_API_CALL;
1368
1369
0
  if(timeout_ms < 0)
1370
0
    return CURLM_BAD_FUNCTION_ARGUMENT;
1371
1372
0
  Curl_pollset_init(&ps);
1373
0
  Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
1374
1375
  /* Add the curl handles to our pollfds first */
1376
0
  if(Curl_uint32_bset_first(&multi->process, &mid)) {
1377
0
    do {
1378
0
      data = Curl_multi_get_easy(multi, mid);
1379
0
      if(!data) {
1380
0
        DEBUGASSERT(0);
1381
0
        Curl_uint32_bset_remove(&multi->process, mid);
1382
0
        Curl_uint32_bset_remove(&multi->dirty, mid);
1383
0
        continue;
1384
0
      }
1385
0
      Curl_multi_pollset(data, &ps);
1386
0
      if(Curl_pollfds_add_ps(&cpfds, &ps)) {
1387
0
        mresult = CURLM_OUT_OF_MEMORY;
1388
0
        goto out;
1389
0
      }
1390
0
    } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
1391
0
  }
1392
1393
0
  if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) {
1394
0
    mresult = CURLM_OUT_OF_MEMORY;
1395
0
    goto out;
1396
0
  }
1397
1398
0
  curl_nfds = cpfds.n; /* what curl internally uses in cpfds */
1399
  /* Add external file descriptions from poll-like struct curl_waitfd */
1400
0
  for(i = 0; i < extra_nfds; i++) {
1401
0
    unsigned short events = 0;
1402
0
    if(extra_fds[i].events & CURL_WAIT_POLLIN)
1403
0
      events |= POLLIN;
1404
0
    if(extra_fds[i].events & CURL_WAIT_POLLPRI)
1405
0
      events |= POLLPRI;
1406
0
    if(extra_fds[i].events & CURL_WAIT_POLLOUT)
1407
0
      events |= POLLOUT;
1408
0
    if(Curl_pollfds_add_sock(&cpfds, extra_fds[i].fd, events)) {
1409
0
      mresult = CURLM_OUT_OF_MEMORY;
1410
0
      goto out;
1411
0
    }
1412
0
  }
1413
1414
#ifdef USE_WINSOCK
1415
  /* Set the WSA events based on the collected pollds */
1416
  for(i = 0; i < cpfds.n; i++) {
1417
    long mask = 0;
1418
    if(cpfds.pfds[i].events & POLLIN)
1419
      mask |= FD_READ | FD_ACCEPT | FD_CLOSE;
1420
    if(cpfds.pfds[i].events & POLLPRI)
1421
      mask |= FD_OOB;
1422
    if(cpfds.pfds[i].events & POLLOUT) {
1423
      mask |= FD_WRITE | FD_CONNECT | FD_CLOSE;
1424
      reset_socket_fdwrite(cpfds.pfds[i].fd);
1425
    }
1426
    if(mask) {
1427
      if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) {
1428
        mresult = CURLM_OUT_OF_MEMORY;
1429
        goto out;
1430
      }
1431
    }
1432
  }
1433
#endif
1434
1435
0
#ifdef ENABLE_WAKEUP
1436
0
#ifndef USE_WINSOCK
1437
0
  if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
1438
0
    if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) {
1439
0
      mresult = CURLM_OUT_OF_MEMORY;
1440
0
      goto out;
1441
0
    }
1442
0
  }
1443
0
#endif
1444
0
#endif
1445
1446
  /* We check the internal timeout *AFTER* we collected all sockets to
1447
   * poll. Collecting the sockets may install new timers by protocols
1448
   * and connection filters.
1449
   * Use the shorter one of the internal and the caller requested timeout. */
1450
0
  multi_timeout(multi, &expire_time, &timeout_internal);
1451
0
  if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
1452
0
    timeout_ms = (int)timeout_internal;
1453
1454
0
  if(data)
1455
0
    CURL_TRC_M(data, "multi_wait(fds=%d, timeout=%d) tinternal=%ld",
1456
0
               cpfds.n, timeout_ms, timeout_internal);
1457
#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
1458
  if(cpfds.n || use_wakeup) {
1459
#else
1460
0
  if(cpfds.n) {
1461
0
#endif
1462
0
    int pollrc;
1463
#ifdef USE_WINSOCK
1464
    if(cpfds.n)         /* pre-check with Winsock */
1465
      pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0);
1466
    else
1467
      pollrc = 0;
1468
#else
1469
0
    pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */
1470
0
#endif
1471
0
    if(pollrc < 0) {
1472
0
      mresult = CURLM_UNRECOVERABLE_POLL;
1473
0
      goto out;
1474
0
    }
1475
1476
0
    if(pollrc > 0) {
1477
0
      retcode = pollrc;
1478
#ifdef USE_WINSOCK
1479
    }
1480
    else { /* now wait... if not ready during the pre-check (pollrc == 0) */
1481
      WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms,
1482
                               FALSE);
1483
    }
1484
    /* With Winsock, we have to run the following section unconditionally
1485
       to call WSAEventSelect(fd, event, 0) on all the sockets */
1486
    {
1487
#endif
1488
      /* copy revents results from the poll to the curl_multi_wait poll
1489
         struct, the bit values of the actual underlying poll() implementation
1490
         may not be the same as the ones in the public libcurl API! */
1491
0
      for(i = 0; i < extra_nfds; i++) {
1492
0
        unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents;
1493
0
        unsigned short mask = 0;
1494
#ifdef USE_WINSOCK
1495
        curl_socket_t s = extra_fds[i].fd;
1496
        wsa_events.lNetworkEvents = 0;
1497
        if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) {
1498
          if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
1499
            mask |= CURL_WAIT_POLLIN;
1500
          if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE))
1501
            mask |= CURL_WAIT_POLLOUT;
1502
          if(wsa_events.lNetworkEvents & FD_OOB)
1503
            mask |= CURL_WAIT_POLLPRI;
1504
          if(ret && !pollrc && wsa_events.lNetworkEvents)
1505
            retcode++;
1506
        }
1507
        WSAEventSelect(s, multi->wsa_event, 0);
1508
        if(!pollrc) {
1509
          extra_fds[i].revents = (short)mask;
1510
          continue;
1511
        }
1512
#endif
1513
0
        if(r & POLLIN)
1514
0
          mask |= CURL_WAIT_POLLIN;
1515
0
        if(r & POLLOUT)
1516
0
          mask |= CURL_WAIT_POLLOUT;
1517
0
        if(r & POLLPRI)
1518
0
          mask |= CURL_WAIT_POLLPRI;
1519
0
        extra_fds[i].revents = (short)mask;
1520
0
      }
1521
1522
#ifdef USE_WINSOCK
1523
      /* Count up all our own sockets that had activity,
1524
         and remove them from the event. */
1525
      for(i = 0; i < curl_nfds; ++i) {
1526
        wsa_events.lNetworkEvents = 0;
1527
        if(WSAEnumNetworkEvents(cpfds.pfds[i].fd, NULL, &wsa_events) == 0) {
1528
          if(ret && !pollrc && wsa_events.lNetworkEvents)
1529
            retcode++;
1530
        }
1531
        WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, 0);
1532
      }
1533
      WSAResetEvent(multi->wsa_event);
1534
#else
1535
0
#ifdef ENABLE_WAKEUP
1536
0
      if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
1537
0
        if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) {
1538
0
          (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE);
1539
          /* do not count the wakeup socket into the returned value */
1540
0
          retcode--;
1541
0
        }
1542
0
      }
1543
0
#endif
1544
0
#endif
1545
0
    }
1546
0
  }
1547
1548
0
  if(ret)
1549
0
    *ret = retcode;
1550
#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
1551
  if(extrawait && !cpfds.n && !use_wakeup) {
1552
#else
1553
0
  if(extrawait && !cpfds.n) {
1554
0
#endif
1555
0
    long sleep_ms = 0;
1556
1557
    /* Avoid busy-looping when there is nothing particular to wait for */
1558
0
    multi_timeout(multi, &expire_time, &sleep_ms);
1559
0
    if(sleep_ms) {
1560
0
      if(sleep_ms > timeout_ms)
1561
0
        sleep_ms = timeout_ms;
1562
      /* when there are no easy handles in the multi, this holds a -1
1563
         timeout */
1564
0
      else if(sleep_ms < 0)
1565
0
        sleep_ms = timeout_ms;
1566
0
      curlx_wait_ms(sleep_ms);
1567
0
    }
1568
0
  }
1569
1570
0
out:
1571
0
  Curl_pollset_cleanup(&ps);
1572
0
  Curl_pollfds_cleanup(&cpfds);
1573
0
  return mresult;
1574
0
}
1575
1576
CURLMcode curl_multi_wait(CURLM *multi,
1577
                          struct curl_waitfd extra_fds[],
1578
                          unsigned int extra_nfds,
1579
                          int timeout_ms,
1580
                          int *ret)
1581
0
{
1582
0
  return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE,
1583
0
                    FALSE);
1584
0
}
1585
1586
CURLMcode curl_multi_poll(CURLM *multi,
1587
                          struct curl_waitfd extra_fds[],
1588
                          unsigned int extra_nfds,
1589
                          int timeout_ms,
1590
                          int *ret)
1591
0
{
1592
0
  return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, TRUE);
1593
0
}
1594
1595
CURLMcode curl_multi_wakeup(CURLM *m)
1596
0
{
1597
  /* this function is usually called from another thread,
1598
     it has to be careful only to access parts of the
1599
     Curl_multi struct that are constant */
1600
0
  struct Curl_multi *multi = m;
1601
1602
  /* GOOD_MULTI_HANDLE can be safely called */
1603
0
  if(!GOOD_MULTI_HANDLE(multi))
1604
0
    return CURLM_BAD_HANDLE;
1605
1606
0
#ifdef ENABLE_WAKEUP
1607
#ifdef USE_WINSOCK
1608
  if(WSASetEvent(multi->wsa_event))
1609
    return CURLM_OK;
1610
#else
1611
  /* the wakeup_pair variable is only written during init and cleanup,
1612
     making it safe to access from another thread after the init part
1613
     and before cleanup */
1614
0
  if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
1615
0
    if(Curl_wakeup_signal(multi->wakeup_pair))
1616
0
      return CURLM_WAKEUP_FAILURE;
1617
0
    return CURLM_OK;
1618
0
  }
1619
0
#endif
1620
0
#endif
1621
0
  return CURLM_WAKEUP_FAILURE;
1622
0
}
1623
1624
/*
1625
 * multi_ischanged() is called
1626
 *
1627
 * Returns TRUE/FALSE whether the state is changed to trigger a CONNECT_PEND
1628
 * => CONNECT action.
1629
 *
1630
 * Set 'clear' to TRUE to have it also clear the state variable.
1631
 */
1632
static bool multi_ischanged(struct Curl_multi *multi, bool clear)
1633
0
{
1634
0
  bool retval = (bool)multi->recheckstate;
1635
0
  if(clear)
1636
0
    multi->recheckstate = FALSE;
1637
0
  return retval;
1638
0
}
1639
1640
/*
1641
 * Curl_multi_connchanged() is called to tell that there is a connection in
1642
 * this multi handle that has changed state (multiplexing become possible, the
1643
 * number of allowed streams changed or similar), and a subsequent use of this
1644
 * multi handle should move CONNECT_PEND handles back to CONNECT to have them
1645
 * retry.
1646
 */
1647
void Curl_multi_connchanged(struct Curl_multi *multi)
1648
0
{
1649
0
  multi->recheckstate = TRUE;
1650
0
}
1651
1652
CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
1653
                                 struct Curl_easy *data,
1654
                                 struct connectdata *conn)
1655
0
{
1656
0
  CURLMcode mresult;
1657
1658
0
  if(multi->in_callback)
1659
0
    return CURLM_RECURSIVE_API_CALL;
1660
1661
0
  mresult = curl_multi_add_handle(multi, data);
1662
0
  if(!mresult) {
1663
0
    struct SingleRequest *k = &data->req;
1664
0
    CURLcode result;
1665
1666
    /* pass in NULL for 'conn' here since we do not want to init the
1667
       connection, only this transfer */
1668
0
    result = Curl_init_do(data, NULL);
1669
0
    if(result) {
1670
0
      curl_multi_remove_handle(multi, data);
1671
0
      return CURLM_INTERNAL_ERROR;
1672
0
    }
1673
1674
    /* take this handle to the perform state right away */
1675
0
    multistate(data, MSTATE_PERFORMING);
1676
0
    Curl_attach_connection(data, conn);
1677
0
    k->keepon |= KEEP_RECV; /* setup to receive! */
1678
0
  }
1679
0
  return mresult;
1680
0
}
1681
1682
static CURLcode multi_do(struct Curl_easy *data, bool *done)
1683
0
{
1684
0
  CURLcode result = CURLE_OK;
1685
0
  struct connectdata *conn = data->conn;
1686
1687
0
  DEBUGASSERT(conn);
1688
0
  DEBUGASSERT(conn->scheme);
1689
1690
0
  if(conn->scheme->run->do_it)
1691
0
    result = conn->scheme->run->do_it(data, done);
1692
1693
0
  return result;
1694
0
}
1695
1696
/*
1697
 * multi_do_more() is called during the DO_MORE multi state. It is a second
1698
 * stage DO state which (wrongly) was introduced to support FTP's second
1699
 * connection.
1700
 *
1701
 * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to
1702
 * DOING state there is more work to do!
1703
 */
1704
1705
static CURLcode multi_do_more(struct Curl_easy *data, int *complete)
1706
0
{
1707
0
  CURLcode result = CURLE_OK;
1708
0
  struct connectdata *conn = data->conn;
1709
1710
0
  *complete = 0;
1711
1712
0
  if(conn->scheme->run->do_more)
1713
0
    result = conn->scheme->run->do_more(data, complete);
1714
1715
0
  return result;
1716
0
}
1717
1718
/*
1719
 * Check whether a timeout occurred, and handle it if it did
1720
 */
1721
static bool multi_handle_timeout(struct Curl_easy *data,
1722
                                 bool *stream_error,
1723
                                 CURLcode *result)
1724
0
{
1725
0
  timediff_t timeout_ms;
1726
1727
0
  timeout_ms = Curl_timeleft_ms(data);
1728
0
  if(timeout_ms < 0) {
1729
    /* Handle timed out */
1730
0
    struct curltime since;
1731
0
    if(Curl_is_connecting(data))
1732
0
      since = data->progress.t_startsingle;
1733
0
    else
1734
0
      since = data->progress.t_startop;
1735
0
    if(data->mstate == MSTATE_RESOLVING)
1736
0
      failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T
1737
0
            " milliseconds",
1738
0
            curlx_ptimediff_ms(Curl_pgrs_now(data), &since));
1739
0
    else if(data->mstate == MSTATE_CONNECTING)
1740
0
      failf(data, "Connection timed out after %" FMT_TIMEDIFF_T
1741
0
            " milliseconds",
1742
0
            curlx_ptimediff_ms(Curl_pgrs_now(data), &since));
1743
0
    else {
1744
0
      struct SingleRequest *k = &data->req;
1745
0
      if(k->size != -1) {
1746
0
        failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
1747
0
              " milliseconds with %" FMT_OFF_T " out of %"
1748
0
              FMT_OFF_T " bytes received",
1749
0
              curlx_ptimediff_ms(Curl_pgrs_now(data), &since),
1750
0
              k->bytecount, k->size);
1751
0
      }
1752
0
      else {
1753
0
        failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
1754
0
              " milliseconds with %" FMT_OFF_T " bytes received",
1755
0
              curlx_ptimediff_ms(Curl_pgrs_now(data), &since),
1756
0
              k->bytecount);
1757
0
      }
1758
0
    }
1759
0
    *result = CURLE_OPERATION_TIMEDOUT;
1760
0
    if(data->conn) {
1761
      /* Force connection closed if the connection has indeed been used */
1762
0
      if(data->mstate > MSTATE_DO) {
1763
0
        streamclose(data->conn, "Disconnect due to timeout");
1764
0
        *stream_error = TRUE;
1765
0
      }
1766
0
      (void)multi_done(data, *result, TRUE);
1767
0
    }
1768
0
    return TRUE;
1769
0
  }
1770
1771
0
  return FALSE;
1772
0
}
1773
1774
/*
1775
 * We are doing protocol-specific connecting and this is being called over and
1776
 * over from the multi interface until the connection phase is done on
1777
 * protocol layer.
1778
 */
1779
1780
static CURLcode protocol_connecting(struct Curl_easy *data, bool *done)
1781
0
{
1782
0
  CURLcode result = CURLE_OK;
1783
0
  struct connectdata *conn = data->conn;
1784
1785
0
  if(conn && conn->scheme->run->connecting) {
1786
0
    *done = FALSE;
1787
0
    result = conn->scheme->run->connecting(data, done);
1788
0
  }
1789
0
  else
1790
0
    *done = TRUE;
1791
1792
0
  return result;
1793
0
}
1794
1795
/*
1796
 * We are DOING this is being called over and over from the multi interface
1797
 * until the DOING phase is done on protocol layer.
1798
 */
1799
1800
static CURLcode protocol_doing(struct Curl_easy *data, bool *done)
1801
0
{
1802
0
  CURLcode result = CURLE_OK;
1803
0
  struct connectdata *conn = data->conn;
1804
1805
0
  if(conn && conn->scheme->run->doing) {
1806
0
    *done = FALSE;
1807
0
    result = conn->scheme->run->doing(data, done);
1808
0
  }
1809
0
  else
1810
0
    *done = TRUE;
1811
1812
0
  return result;
1813
0
}
1814
1815
/*
1816
 * We have discovered that the TCP connection has been successful, we can now
1817
 * proceed with some action.
1818
 *
1819
 */
1820
static CURLcode protocol_connect(struct Curl_easy *data, bool *protocol_done)
1821
0
{
1822
0
  struct connectdata *conn = data->conn;
1823
0
  CURLcode result = CURLE_OK;
1824
1825
0
  DEBUGASSERT(conn);
1826
0
  DEBUGASSERT(protocol_done);
1827
0
  DEBUGASSERT(Curl_conn_is_connected(conn, FIRSTSOCKET));
1828
1829
0
  *protocol_done = FALSE;
1830
0
  if(!conn->bits.protoconnstart) {
1831
0
    if(conn->scheme->run->connect_it) {
1832
      /* Call the protocol-specific connect function */
1833
0
      result = conn->scheme->run->connect_it(data, protocol_done);
1834
0
      if(result)
1835
0
        return result;
1836
0
    }
1837
0
    conn->bits.protoconnstart = TRUE;
1838
0
  }
1839
1840
  /* Unless this protocol does not have any protocol-connect callback, as
1841
     then we know we are done. */
1842
0
  if(!conn->scheme->run->connecting)
1843
0
    *protocol_done = TRUE;
1844
0
  return CURLE_OK;
1845
0
}
1846
1847
static void set_in_callback(struct Curl_multi *multi, bool value)
1848
0
{
1849
0
  multi->in_callback = value;
1850
0
}
1851
1852
/*
1853
 * posttransfer() is called immediately after a transfer ends
1854
 */
1855
static void multi_posttransfer(struct Curl_easy *data)
1856
0
{
1857
#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(MSG_NOSIGNAL)
1858
  /* restore the signal handler for SIGPIPE before we get back */
1859
  if(!data->set.no_signal)
1860
    signal(SIGPIPE, data->state.prev_signal);
1861
#else
1862
0
  (void)data;
1863
0
#endif
1864
0
}
1865
1866
/*
1867
 * multi_follow() handles the URL redirect magic. Pass in the 'newurl' string
1868
 * as given by the remote server and set up the new URL to request.
1869
 *
1870
 * This function DOES NOT FREE the given url.
1871
 */
1872
static CURLcode multi_follow(struct Curl_easy *data,
1873
                             const struct Curl_scheme *handler,
1874
                             const char *newurl, /* the Location: string */
1875
                             followtype type) /* see transfer.h */
1876
0
{
1877
0
  if(handler && handler->run->follow)
1878
0
    return handler->run->follow(data, newurl, type);
1879
0
  return CURLE_TOO_MANY_REDIRECTS;
1880
0
}
1881
1882
static CURLcode mspeed_check(struct Curl_easy *data)
1883
0
{
1884
0
  if(Curl_rlimit_active(&data->progress.dl.rlimit) ||
1885
0
     Curl_rlimit_active(&data->progress.ul.rlimit)) {
1886
    /* check if our send/recv limits require idle waits */
1887
0
    const struct curltime *pnow = Curl_pgrs_now(data);
1888
0
    timediff_t recv_ms, send_ms;
1889
1890
0
    send_ms = Curl_rlimit_wait_ms(&data->progress.ul.rlimit, pnow);
1891
0
    recv_ms = Curl_rlimit_wait_ms(&data->progress.dl.rlimit, pnow);
1892
1893
0
    if(send_ms || recv_ms) {
1894
0
      if(data->mstate != MSTATE_RATELIMITING) {
1895
0
        multistate(data, MSTATE_RATELIMITING);
1896
0
      }
1897
0
      Curl_expire(data, CURLMAX(send_ms, recv_ms), EXPIRE_TOOFAST);
1898
0
      Curl_multi_clear_dirty(data);
1899
0
      CURL_TRC_M(data, "[RLIMIT] waiting %" FMT_TIMEDIFF_T "ms",
1900
0
                 CURLMAX(send_ms, recv_ms));
1901
0
      return CURLE_AGAIN;
1902
0
    }
1903
0
    else {
1904
      /* when will the rate limits increase next? The transfer needs
1905
       * to run again at that time or it may stall. */
1906
0
      send_ms = Curl_rlimit_next_step_ms(&data->progress.ul.rlimit, pnow);
1907
0
      recv_ms = Curl_rlimit_next_step_ms(&data->progress.dl.rlimit, pnow);
1908
0
      if(send_ms || recv_ms) {
1909
0
        timediff_t next_ms = CURLMIN(send_ms, recv_ms);
1910
0
        if(!next_ms)
1911
0
          next_ms = CURLMAX(send_ms, recv_ms);
1912
0
        Curl_expire(data, next_ms, EXPIRE_TOOFAST);
1913
0
        CURL_TRC_M(data, "[RLIMIT] next token update in %" FMT_TIMEDIFF_T "ms",
1914
0
                   next_ms);
1915
0
      }
1916
0
    }
1917
0
  }
1918
1919
0
  if(data->mstate != MSTATE_PERFORMING) {
1920
0
    CURL_TRC_M(data, "[RLIMIT] wait over, continue");
1921
0
    multistate(data, MSTATE_PERFORMING);
1922
0
  }
1923
0
  return CURLE_OK;
1924
0
}
1925
1926
static CURLMcode state_performing(struct Curl_easy *data,
1927
                                  bool *stream_errorp,
1928
                                  CURLcode *resultp)
1929
0
{
1930
0
  char *newurl = NULL;
1931
0
  bool retry = FALSE;
1932
0
  CURLMcode mresult = CURLM_OK;
1933
0
  CURLcode result = *resultp = CURLE_OK;
1934
0
  *stream_errorp = FALSE;
1935
1936
0
  if(mspeed_check(data) == CURLE_AGAIN)
1937
0
    return CURLM_OK;
1938
1939
  /* read/write data if it is ready to do so */
1940
0
  result = Curl_sendrecv(data);
1941
1942
0
  if(data->req.done || (result == CURLE_RECV_ERROR)) {
1943
    /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
1944
     * condition and the server closed the reused connection exactly when we
1945
     * wanted to use it, so figure out if that is indeed the case.
1946
     */
1947
0
    CURLcode ret = Curl_retry_request(data, &newurl);
1948
0
    if(!ret)
1949
0
      retry = !!newurl;
1950
0
    else if(!result)
1951
0
      result = ret;
1952
1953
0
    if(retry) {
1954
      /* if we are to retry, set the result to OK and consider the
1955
         request as done */
1956
0
      result = CURLE_OK;
1957
0
      data->req.done = TRUE;
1958
0
    }
1959
0
  }
1960
0
#ifndef CURL_DISABLE_HTTP
1961
0
  else if((result == CURLE_HTTP2_STREAM) &&
1962
0
          Curl_h2_http_1_1_error(data)) {
1963
0
    CURLcode ret = Curl_retry_request(data, &newurl);
1964
1965
0
    if(!ret) {
1966
0
      infof(data, "Downgrades to HTTP/1.1");
1967
0
      streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1");
1968
0
      data->state.http_neg.wanted = CURL_HTTP_V1x;
1969
0
      data->state.http_neg.allowed = CURL_HTTP_V1x;
1970
      /* clear the error message bit too as we ignore the one we got */
1971
0
      data->state.errorbuf = FALSE;
1972
0
      if(!newurl)
1973
        /* typically for HTTP_1_1_REQUIRED error on first flight */
1974
0
        newurl = Curl_bufref_dup(&data->state.url);
1975
0
      if(!newurl) {
1976
0
        result = CURLE_OUT_OF_MEMORY;
1977
0
      }
1978
0
      else {
1979
        /* if we are to retry, set the result to OK and consider the request
1980
          as done */
1981
0
        retry = TRUE;
1982
0
        result = CURLE_OK;
1983
0
        data->req.done = TRUE;
1984
0
      }
1985
0
    }
1986
0
    else
1987
0
      result = ret;
1988
0
  }
1989
0
#endif
1990
1991
0
  if(result) {
1992
    /*
1993
     * The transfer phase returned error, we mark the connection to get closed
1994
     * to prevent being reused. This is because we cannot possibly know if the
1995
     * connection is in a good shape or not now. Unless it is a protocol which
1996
     * uses two "channels" like FTP, as then the error happened in the data
1997
     * connection.
1998
     */
1999
2000
0
    if(!(data->conn->scheme->flags & PROTOPT_DUAL) &&
2001
0
       result != CURLE_HTTP2_STREAM)
2002
0
      streamclose(data->conn, "Transfer returned error");
2003
2004
0
    multi_posttransfer(data);
2005
0
    multi_done(data, result, TRUE);
2006
0
  }
2007
0
  else if(data->req.done && !Curl_cwriter_is_paused(data)) {
2008
0
    const struct Curl_scheme *handler = data->conn->scheme;
2009
2010
    /* call this even if the readwrite function returned error */
2011
0
    multi_posttransfer(data);
2012
2013
    /* When we follow redirects or is set to retry the connection, we must to
2014
       go back to the CONNECT state */
2015
0
    if(data->req.newurl || retry) {
2016
0
      followtype follow = FOLLOW_NONE;
2017
0
      if(!retry) {
2018
        /* if the URL is a follow-location and not a retried request then
2019
           figure out the URL here */
2020
0
        curlx_free(newurl);
2021
0
        newurl = data->req.newurl;
2022
0
        data->req.newurl = NULL;
2023
0
        follow = FOLLOW_REDIR;
2024
0
      }
2025
0
      else
2026
0
        follow = FOLLOW_RETRY;
2027
0
      (void)multi_done(data, CURLE_OK, FALSE);
2028
      /* multi_done() might return CURLE_GOT_NOTHING */
2029
0
      result = multi_follow(data, handler, newurl, follow);
2030
0
      if(!result) {
2031
0
        multistate(data, MSTATE_SETUP);
2032
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2033
0
      }
2034
0
    }
2035
0
    else {
2036
      /* after the transfer is done, go DONE */
2037
2038
      /* but first check to see if we got a location info even though we are
2039
         not following redirects */
2040
0
      if(data->req.location) {
2041
0
        curlx_free(newurl);
2042
0
        newurl = data->req.location;
2043
0
        data->req.location = NULL;
2044
0
        result = multi_follow(data, handler, newurl, FOLLOW_FAKE);
2045
0
        if(result) {
2046
0
          *stream_errorp = TRUE;
2047
0
          result = multi_done(data, result, TRUE);
2048
0
        }
2049
0
      }
2050
2051
0
      if(!result) {
2052
0
        multistate(data, MSTATE_DONE);
2053
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2054
0
      }
2055
0
    }
2056
0
  }
2057
0
  else { /* not errored, not done */
2058
0
    mspeed_check(data);
2059
0
  }
2060
0
  curlx_free(newurl);
2061
0
  *resultp = result;
2062
0
  return mresult;
2063
0
}
2064
2065
static CURLMcode state_do(struct Curl_easy *data,
2066
                          bool *stream_errorp,
2067
                          CURLcode *resultp)
2068
0
{
2069
0
  CURLMcode mresult = CURLM_OK;
2070
0
  CURLcode result = CURLE_OK;
2071
0
  if(data->set.fprereq) {
2072
0
    int prereq_rc;
2073
2074
    /* call the prerequest callback function */
2075
0
    Curl_set_in_callback(data, TRUE);
2076
0
    prereq_rc = data->set.fprereq(data->set.prereq_userp,
2077
0
                                  data->info.primary.remote_ip,
2078
0
                                  data->info.primary.local_ip,
2079
0
                                  data->info.primary.remote_port,
2080
0
                                  data->info.primary.local_port);
2081
0
    Curl_set_in_callback(data, FALSE);
2082
0
    if(prereq_rc != CURL_PREREQFUNC_OK) {
2083
0
      failf(data, "operation aborted by pre-request callback");
2084
      /* failure in pre-request callback - do not do any other processing */
2085
0
      result = CURLE_ABORTED_BY_CALLBACK;
2086
0
      multi_posttransfer(data);
2087
0
      multi_done(data, result, FALSE);
2088
0
      *stream_errorp = TRUE;
2089
0
      goto end;
2090
0
    }
2091
0
  }
2092
2093
0
  if(data->set.connect_only && !data->set.connect_only_ws) {
2094
0
    multistate(data, MSTATE_DONE);
2095
0
    mresult = CURLM_CALL_MULTI_PERFORM;
2096
0
  }
2097
0
  else {
2098
0
    bool dophase_done = FALSE;
2099
    /* Perform the protocol's DO action */
2100
0
    result = multi_do(data, &dophase_done);
2101
2102
    /* When multi_do() returns failure, data->conn might be NULL! */
2103
2104
0
    if(!result) {
2105
0
      if(!dophase_done) {
2106
0
#ifndef CURL_DISABLE_FTP
2107
        /* some steps needed for wildcard matching */
2108
0
        if(data->state.wildcardmatch) {
2109
0
          struct WildcardData *wc = data->wildcard;
2110
0
          if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
2111
            /* skip some states if it is important */
2112
0
            multi_done(data, CURLE_OK, FALSE);
2113
2114
            /* if there is no connection left, skip the DONE state */
2115
0
            multistate(data, data->conn ? MSTATE_DONE : MSTATE_COMPLETED);
2116
0
            mresult = CURLM_CALL_MULTI_PERFORM;
2117
0
            goto end;
2118
0
          }
2119
0
        }
2120
0
#endif
2121
        /* DO was not completed in one function call, we must continue
2122
           DOING... */
2123
0
        multistate(data, MSTATE_DOING);
2124
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2125
0
      }
2126
2127
      /* after DO, go DO_DONE... or DO_MORE */
2128
0
      else if(data->conn->bits.do_more) {
2129
        /* we are supposed to do more, but we need to sit down, relax and wait
2130
           a little while first */
2131
0
        multistate(data, MSTATE_DOING_MORE);
2132
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2133
0
      }
2134
0
      else {
2135
        /* we are done with the DO, now DID */
2136
0
        multistate(data, MSTATE_DID);
2137
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2138
0
      }
2139
0
    }
2140
0
    else if((result == CURLE_SEND_ERROR) &&
2141
0
            data->conn->bits.reuse) {
2142
      /*
2143
       * In this situation, a connection that we were trying to use may have
2144
       * unexpectedly died. If possible, send the connection back to the
2145
       * CONNECT phase so we can try again.
2146
       */
2147
0
      const struct Curl_scheme *handler = data->conn->scheme;
2148
0
      char *newurl = NULL;
2149
0
      followtype follow = FOLLOW_NONE;
2150
0
      CURLcode drc;
2151
2152
0
      drc = Curl_retry_request(data, &newurl);
2153
0
      if(drc) {
2154
        /* a failure here pretty much implies an out of memory */
2155
0
        result = drc;
2156
0
        *stream_errorp = TRUE;
2157
0
      }
2158
2159
0
      multi_posttransfer(data);
2160
0
      drc = multi_done(data, result, FALSE);
2161
2162
      /* When set to retry the connection, we must go back to the CONNECT
2163
       * state */
2164
0
      if(newurl) {
2165
0
        if(!drc || (drc == CURLE_SEND_ERROR)) {
2166
0
          follow = FOLLOW_RETRY;
2167
0
          drc = multi_follow(data, handler, newurl, follow);
2168
0
          if(!drc) {
2169
0
            multistate(data, MSTATE_SETUP);
2170
0
            mresult = CURLM_CALL_MULTI_PERFORM;
2171
0
            result = CURLE_OK;
2172
0
          }
2173
0
          else {
2174
            /* Follow failed */
2175
0
            result = drc;
2176
0
          }
2177
0
        }
2178
0
        else {
2179
          /* done did not return OK or SEND_ERROR */
2180
0
          result = drc;
2181
0
        }
2182
0
      }
2183
0
      else {
2184
        /* Have error handler disconnect conn if we cannot retry */
2185
0
        *stream_errorp = TRUE;
2186
0
      }
2187
0
      curlx_free(newurl);
2188
0
    }
2189
0
    else {
2190
      /* failure detected */
2191
0
      multi_posttransfer(data);
2192
0
      if(data->conn)
2193
0
        multi_done(data, result, FALSE);
2194
0
      *stream_errorp = TRUE;
2195
0
    }
2196
0
  }
2197
0
end:
2198
0
  *resultp = result;
2199
0
  return mresult;
2200
0
}
2201
2202
static CURLMcode state_ratelimiting(struct Curl_easy *data,
2203
                                    CURLcode *resultp)
2204
0
{
2205
0
  CURLcode result = CURLE_OK;
2206
0
  CURLMcode mresult = CURLM_OK;
2207
0
  DEBUGASSERT(data->conn);
2208
  /* if both rates are within spec, resume transfer */
2209
0
  result = Curl_pgrsCheck(data);
2210
2211
0
  if(result) {
2212
0
    if(!(data->conn->scheme->flags & PROTOPT_DUAL) &&
2213
0
       result != CURLE_HTTP2_STREAM)
2214
0
      streamclose(data->conn, "Transfer returned error");
2215
2216
0
    multi_posttransfer(data);
2217
0
    multi_done(data, result, TRUE);
2218
0
  }
2219
0
  else {
2220
0
    if(!mspeed_check(data))
2221
0
      mresult = CURLM_CALL_MULTI_PERFORM;
2222
0
  }
2223
0
  *resultp = result;
2224
0
  return mresult;
2225
0
}
2226
2227
static CURLMcode state_resolving(struct Curl_multi *multi,
2228
                                 struct Curl_easy *data,
2229
                                 bool *stream_errorp,
2230
                                 CURLcode *resultp)
2231
0
{
2232
0
  struct Curl_dns_entry *dns = NULL;
2233
0
  CURLcode result;
2234
0
  CURLMcode mresult = CURLM_OK;
2235
2236
0
  result = Curl_resolv_check(data, &dns);
2237
0
  CURL_TRC_DNS(data, "Curl_resolv_check() -> %d, %s",
2238
0
               result, dns ? "found" : "missing");
2239
  /* Update sockets here, because the socket(s) may have been closed and the
2240
     application thus needs to be told, even if it is likely that the same
2241
     socket(s) will again be used further down. If the name has not yet been
2242
     resolved, it is likely that new sockets have been opened in an attempt to
2243
     contact another resolver. */
2244
0
  mresult = Curl_multi_ev_assess_xfer(multi, data);
2245
0
  if(mresult)
2246
0
    return mresult;
2247
2248
0
  if(dns) {
2249
0
    bool connected;
2250
    /* Perform the next step in the connection phase, and then move on to the
2251
       WAITCONNECT state */
2252
0
    result = Curl_once_resolved(data, dns, &connected);
2253
2254
0
    if(result)
2255
      /* if Curl_once_resolved() returns failure, the connection struct is
2256
         already freed and gone */
2257
0
      data->conn = NULL; /* no more connection */
2258
0
    else {
2259
      /* call again please so that we get the next socket setup */
2260
0
      mresult = CURLM_CALL_MULTI_PERFORM;
2261
0
      if(connected)
2262
0
        multistate(data, MSTATE_PROTOCONNECT);
2263
0
      else {
2264
0
        multistate(data, MSTATE_CONNECTING);
2265
0
      }
2266
0
    }
2267
0
  }
2268
2269
0
  if(result)
2270
    /* failure detected */
2271
0
    *stream_errorp = TRUE;
2272
2273
0
  *resultp = result;
2274
0
  return mresult;
2275
0
}
2276
2277
static CURLMcode state_connect(struct Curl_multi *multi,
2278
                               struct Curl_easy *data,
2279
                               CURLcode *resultp)
2280
0
{
2281
  /* Connect. We want to get a connection identifier filled in. This state can
2282
     be entered from SETUP and from PENDING. */
2283
0
  bool connected;
2284
0
  bool async;
2285
0
  CURLMcode mresult = CURLM_OK;
2286
0
  CURLcode result = Curl_connect(data, &async, &connected);
2287
0
  if(result == CURLE_NO_CONNECTION_AVAILABLE) {
2288
    /* There was no connection available. We will go to the pending state and
2289
       wait for an available connection. */
2290
0
    multistate(data, MSTATE_PENDING);
2291
    /* move from process to pending set */
2292
0
    Curl_uint32_bset_remove(&multi->process, data->mid);
2293
0
    Curl_uint32_bset_remove(&multi->dirty, data->mid);
2294
0
    Curl_uint32_bset_add(&multi->pending, data->mid);
2295
0
    *resultp = CURLE_OK;
2296
0
    return mresult;
2297
0
  }
2298
0
  else
2299
0
    process_pending_handles(data->multi);
2300
2301
0
  if(!result) {
2302
0
    if(async)
2303
      /* We are now waiting for an asynchronous name lookup */
2304
0
      multistate(data, MSTATE_RESOLVING);
2305
0
    else {
2306
      /* after the connect has been sent off, go WAITCONNECT unless the
2307
         protocol connect is already done and we can go directly to WAITDO or
2308
         DO! */
2309
0
      mresult = CURLM_CALL_MULTI_PERFORM;
2310
2311
0
      if(connected) {
2312
0
        if(!data->conn->bits.reuse &&
2313
0
           Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) {
2314
          /* new connection, can multiplex, wake pending handles */
2315
0
          process_pending_handles(data->multi);
2316
0
        }
2317
0
        multistate(data, MSTATE_PROTOCONNECT);
2318
0
      }
2319
0
      else {
2320
0
        multistate(data, MSTATE_CONNECTING);
2321
0
      }
2322
0
    }
2323
0
  }
2324
0
  *resultp = result;
2325
0
  return mresult;
2326
0
}
2327
2328
/* returns the possibly updated result */
2329
static CURLcode is_finished(struct Curl_multi *multi,
2330
                            struct Curl_easy *data,
2331
                            bool stream_error,
2332
                            CURLcode result)
2333
0
{
2334
0
  if(data->mstate < MSTATE_COMPLETED) {
2335
0
    if(result) {
2336
      /*
2337
       * If an error was returned, and we are not in completed state now,
2338
       * then we go to completed and consider this transfer aborted.
2339
       */
2340
2341
      /* No attempt to disconnect connections must be made before this -
2342
         connection detach and termination happens only here */
2343
2344
      /* Check if we can move pending requests to send pipe */
2345
0
      process_pending_handles(multi); /* connection */
2346
2347
0
      if(data->conn) {
2348
0
        if(stream_error) {
2349
          /* Do not attempt to send data over a connection that timed out */
2350
0
          bool dead_connection = result == CURLE_OPERATION_TIMEDOUT;
2351
0
          struct connectdata *conn = data->conn;
2352
2353
          /* This is where we make sure that the conn pointer is reset.
2354
             We do not have to do this in every case block above where a
2355
             failure is detected */
2356
0
          Curl_detach_connection(data);
2357
0
          Curl_conn_terminate(data, conn, dead_connection);
2358
0
        }
2359
0
      }
2360
0
      else if(data->mstate == MSTATE_CONNECT) {
2361
        /* Curl_connect() failed */
2362
0
        multi_posttransfer(data);
2363
0
        Curl_pgrsUpdate_nometer(data);
2364
0
      }
2365
2366
0
      multistate(data, MSTATE_COMPLETED);
2367
0
      return result;
2368
0
    }
2369
    /* if there is still a connection to use, call the progress function */
2370
0
    else if(data->conn) {
2371
0
      result = Curl_pgrsUpdate(data);
2372
0
      if(result) {
2373
        /* aborted due to progress callback return code must close the
2374
           connection */
2375
0
        streamclose(data->conn, "Aborted by callback");
2376
2377
        /* if not yet in DONE state, go there, otherwise COMPLETED */
2378
0
        multistate(data, (data->mstate < MSTATE_DONE) ?
2379
0
                   MSTATE_DONE : MSTATE_COMPLETED);
2380
0
        return result;
2381
0
      }
2382
0
    }
2383
0
  }
2384
0
  return result;
2385
0
}
2386
2387
static void handle_completed(struct Curl_multi *multi,
2388
                             struct Curl_easy *data,
2389
                             CURLcode result)
2390
0
{
2391
0
  if(data->master_mid != UINT32_MAX) {
2392
    /* A sub transfer, not for msgsent to application */
2393
0
    struct Curl_easy *mdata;
2394
2395
0
    CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid);
2396
0
    mdata = Curl_multi_get_easy(multi, data->master_mid);
2397
0
    if(mdata) {
2398
0
      if(mdata->sub_xfer_done)
2399
0
        mdata->sub_xfer_done(mdata, data, result);
2400
0
      else
2401
0
        CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.",
2402
0
                   data->master_mid);
2403
0
    }
2404
0
    else {
2405
0
      CURL_TRC_M(data, "master easy %u already gone.", data->master_mid);
2406
0
    }
2407
0
  }
2408
0
  else {
2409
    /* now fill in the Curl_message with this info */
2410
0
    struct Curl_message *msg = &data->msg;
2411
2412
0
    msg->extmsg.msg = CURLMSG_DONE;
2413
0
    msg->extmsg.easy_handle = data;
2414
0
    msg->extmsg.data.result = result;
2415
2416
0
    multi_addmsg(multi, msg);
2417
0
    DEBUGASSERT(!data->conn);
2418
0
  }
2419
0
  multistate(data, MSTATE_MSGSENT);
2420
2421
  /* remove from the other sets, add to msgsent */
2422
0
  Curl_uint32_bset_remove(&multi->process, data->mid);
2423
0
  Curl_uint32_bset_remove(&multi->dirty, data->mid);
2424
0
  Curl_uint32_bset_remove(&multi->pending, data->mid);
2425
0
  Curl_uint32_bset_add(&multi->msgsent, data->mid);
2426
0
  --multi->xfers_alive;
2427
0
}
2428
2429
static CURLMcode multi_runsingle(struct Curl_multi *multi,
2430
                                 struct Curl_easy *data,
2431
                                 struct Curl_sigpipe_ctx *sigpipe_ctx)
2432
0
{
2433
0
  bool connected;
2434
0
  bool protocol_connected = FALSE;
2435
0
  bool dophase_done = FALSE;
2436
0
  CURLMcode mresult;
2437
0
  CURLcode result = CURLE_OK;
2438
0
  int control;
2439
2440
0
  if(!GOOD_EASY_HANDLE(data))
2441
0
    return CURLM_BAD_EASY_HANDLE;
2442
2443
0
  if(multi->dead) {
2444
    /* a multi-level callback returned error before, meaning every individual
2445
     transfer now has failed */
2446
0
    result = CURLE_ABORTED_BY_CALLBACK;
2447
0
    multi_posttransfer(data);
2448
0
    multi_done(data, result, FALSE);
2449
0
    multistate(data, MSTATE_COMPLETED);
2450
0
  }
2451
2452
0
  multi_warn_debug(multi, data);
2453
2454
  /* transfer runs now, clear the dirty bit. This may be set
2455
   * again during processing, triggering a re-run later. */
2456
0
  Curl_uint32_bset_remove(&multi->dirty, data->mid);
2457
2458
0
  if(data == multi->admin) {
2459
0
    Curl_cshutdn_perform(&multi->cshutdn, multi->admin, sigpipe_ctx);
2460
0
    return CURLM_OK;
2461
0
  }
2462
2463
0
  sigpipe_apply(data, sigpipe_ctx);
2464
0
  do {
2465
    /* A "stream" here is a logical stream if the protocol can handle that
2466
       (HTTP/2), or the full connection for older protocols */
2467
0
    bool stream_error = FALSE;
2468
0
    mresult = CURLM_OK;
2469
2470
0
    if(multi_ischanged(multi, TRUE)) {
2471
0
      CURL_TRC_M(data, "multi changed, check CONNECT_PEND queue");
2472
0
      process_pending_handles(multi); /* multiplexed */
2473
0
    }
2474
2475
0
    if(data->mstate > MSTATE_CONNECT &&
2476
0
       data->mstate < MSTATE_COMPLETED) {
2477
      /* Make sure we set the connection's current owner */
2478
0
      DEBUGASSERT(data->conn);
2479
0
      if(!data->conn)
2480
0
        return CURLM_INTERNAL_ERROR;
2481
0
    }
2482
2483
    /* Wait for the connect state as only then is the start time stored, but
2484
       we must not check already completed handles */
2485
0
    if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) &&
2486
0
       multi_handle_timeout(data, &stream_error, &result))
2487
      /* Skip the statemachine and go directly to error handling section. */
2488
0
      goto statemachine_end;
2489
2490
0
    switch(data->mstate) {
2491
0
    case MSTATE_INIT:
2492
      /* Transitional state. init this transfer. A handle never comes back to
2493
         this state. */
2494
0
      result = Curl_pretransfer(data);
2495
0
      if(result)
2496
0
        break;
2497
2498
      /* after init, go SETUP */
2499
0
      multistate(data, MSTATE_SETUP);
2500
0
      Curl_pgrsTime(data, TIMER_STARTOP);
2501
0
      FALLTHROUGH();
2502
2503
0
    case MSTATE_SETUP:
2504
      /* Transitional state. Setup things for a new transfer. The handle
2505
         can come back to this state on a redirect. */
2506
0
      Curl_pgrsTime(data, TIMER_STARTSINGLE);
2507
0
      if(data->set.timeout)
2508
0
        Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
2509
0
      if(data->set.connecttimeout)
2510
        /* Since a connection might go to pending and back to CONNECT several
2511
           times before it actually takes off, we need to set the timeout once
2512
           in SETUP before we enter CONNECT the first time. */
2513
0
        Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
2514
2515
0
      multistate(data, MSTATE_CONNECT);
2516
0
      FALLTHROUGH();
2517
2518
0
    case MSTATE_CONNECT:
2519
0
      mresult = state_connect(multi, data, &result);
2520
0
      break;
2521
2522
0
    case MSTATE_RESOLVING:
2523
      /* awaiting an asynch name resolve to complete */
2524
0
      mresult = state_resolving(multi, data, &stream_error, &result);
2525
0
      break;
2526
2527
0
    case MSTATE_CONNECTING:
2528
      /* awaiting a completion of an asynch TCP connect */
2529
0
      DEBUGASSERT(data->conn);
2530
0
      if(!Curl_xfer_recv_is_paused(data)) {
2531
0
        result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected);
2532
0
        if(connected && !result) {
2533
0
          if(!data->conn->bits.reuse &&
2534
0
             Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) {
2535
            /* new connection, can multiplex, wake pending handles */
2536
0
            process_pending_handles(data->multi);
2537
0
          }
2538
0
          mresult = CURLM_CALL_MULTI_PERFORM;
2539
0
          multistate(data, MSTATE_PROTOCONNECT);
2540
0
        }
2541
0
        else if(result) {
2542
          /* failure detected */
2543
0
          multi_posttransfer(data);
2544
0
          multi_done(data, result, TRUE);
2545
0
          stream_error = TRUE;
2546
0
          break;
2547
0
        }
2548
0
      }
2549
0
      break;
2550
2551
0
    case MSTATE_PROTOCONNECT:
2552
0
      if(!result && data->conn->bits.reuse) {
2553
        /* ftp seems to hang when protoconnect on reused connection since we
2554
         * handle PROTOCONNECT in general inside the filers, it seems wrong to
2555
         * restart this on a reused connection.
2556
         */
2557
0
        multistate(data, MSTATE_DO);
2558
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2559
0
        break;
2560
0
      }
2561
0
      if(!result)
2562
0
        result = protocol_connect(data, &protocol_connected);
2563
0
      if(!result && !protocol_connected) {
2564
        /* switch to waiting state */
2565
0
        multistate(data, MSTATE_PROTOCONNECTING);
2566
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2567
0
      }
2568
0
      else if(!result) {
2569
        /* protocol connect has completed, go WAITDO or DO */
2570
0
        multistate(data, MSTATE_DO);
2571
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2572
0
      }
2573
0
      else {
2574
        /* failure detected */
2575
0
        multi_posttransfer(data);
2576
0
        multi_done(data, result, TRUE);
2577
0
        stream_error = TRUE;
2578
0
      }
2579
0
      break;
2580
2581
0
    case MSTATE_PROTOCONNECTING:
2582
      /* protocol-specific connect phase */
2583
0
      result = protocol_connecting(data, &protocol_connected);
2584
0
      if(!result && protocol_connected) {
2585
        /* after the connect has completed, go WAITDO or DO */
2586
0
        multistate(data, MSTATE_DO);
2587
0
        mresult = CURLM_CALL_MULTI_PERFORM;
2588
0
      }
2589
0
      else if(result) {
2590
        /* failure detected */
2591
0
        multi_posttransfer(data);
2592
0
        multi_done(data, result, TRUE);
2593
0
        stream_error = TRUE;
2594
0
      }
2595
0
      break;
2596
2597
0
    case MSTATE_DO:
2598
0
      mresult = state_do(data, &stream_error, &result);
2599
0
      break;
2600
2601
0
    case MSTATE_DOING:
2602
      /* we continue DOING until the DO phase is complete */
2603
0
      DEBUGASSERT(data->conn);
2604
0
      result = protocol_doing(data, &dophase_done);
2605
0
      if(!result) {
2606
0
        if(dophase_done) {
2607
          /* after DO, go DO_DONE or DO_MORE */
2608
0
          multistate(data, data->conn->bits.do_more ?
2609
0
                     MSTATE_DOING_MORE : MSTATE_DID);
2610
0
          mresult = CURLM_CALL_MULTI_PERFORM;
2611
0
        } /* dophase_done */
2612
0
      }
2613
0
      else {
2614
        /* failure detected */
2615
0
        multi_posttransfer(data);
2616
0
        multi_done(data, result, FALSE);
2617
0
        stream_error = TRUE;
2618
0
      }
2619
0
      break;
2620
2621
0
    case MSTATE_DOING_MORE:
2622
      /*
2623
       * When we are connected, DOING MORE and then go DID
2624
       */
2625
0
      DEBUGASSERT(data->conn);
2626
0
      result = multi_do_more(data, &control);
2627
2628
0
      if(!result) {
2629
0
        if(control) {
2630
          /* if positive, advance to DO_DONE
2631
             if negative, go back to DOING */
2632
0
          multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING);
2633
0
          mresult = CURLM_CALL_MULTI_PERFORM;
2634
0
        }
2635
        /* else
2636
           stay in DO_MORE */
2637
0
      }
2638
0
      else {
2639
        /* failure detected */
2640
0
        multi_posttransfer(data);
2641
0
        multi_done(data, result, FALSE);
2642
0
        stream_error = TRUE;
2643
0
      }
2644
0
      break;
2645
2646
0
    case MSTATE_DID:
2647
0
      DEBUGASSERT(data->conn);
2648
0
      if(data->conn->bits.multiplex)
2649
        /* Check if we can move pending requests to send pipe */
2650
0
        process_pending_handles(multi); /* multiplexed */
2651
2652
      /* Only perform the transfer if there is a good socket to work with.
2653
         Having both BAD is a signal to skip immediately to DONE */
2654
0
      if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) ||
2655
0
         CONN_SOCK_IDX_VALID(data->conn->send_idx))
2656
0
        multistate(data, MSTATE_PERFORMING);
2657
0
      else {
2658
0
#ifndef CURL_DISABLE_FTP
2659
0
        if(data->state.wildcardmatch &&
2660
0
           ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) {
2661
0
          data->wildcard->state = CURLWC_DONE;
2662
0
        }
2663
0
#endif
2664
0
        multistate(data, MSTATE_DONE);
2665
0
      }
2666
0
      mresult = CURLM_CALL_MULTI_PERFORM;
2667
0
      break;
2668
2669
0
    case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */
2670
0
      mresult = state_ratelimiting(data, &result);
2671
0
      break;
2672
2673
0
    case MSTATE_PERFORMING:
2674
0
      mresult = state_performing(data, &stream_error, &result);
2675
0
      break;
2676
2677
0
    case MSTATE_DONE:
2678
      /* this state is highly transient, so run another loop after this */
2679
0
      mresult = CURLM_CALL_MULTI_PERFORM;
2680
2681
0
      if(data->conn) {
2682
0
        CURLcode res;
2683
2684
        /* post-transfer command */
2685
0
        res = multi_done(data, result, FALSE);
2686
2687
        /* allow a previously set error code take precedence */
2688
0
        if(!result)
2689
0
          result = res;
2690
0
      }
2691
2692
0
#ifndef CURL_DISABLE_FTP
2693
0
      if(data->state.wildcardmatch) {
2694
0
        if(data->wildcard->state != CURLWC_DONE) {
2695
          /* if a wildcard is set and we are not ending -> lets start again
2696
             with MSTATE_INIT */
2697
0
          multistate(data, MSTATE_INIT);
2698
0
          break;
2699
0
        }
2700
0
      }
2701
0
#endif
2702
      /* after we have DONE what we are supposed to do, go COMPLETED, and
2703
         it does not matter what the multi_done() returned! */
2704
0
      multistate(data, MSTATE_COMPLETED);
2705
0
      break;
2706
2707
0
    case MSTATE_COMPLETED:
2708
0
      break;
2709
2710
0
    case MSTATE_PENDING:
2711
0
    case MSTATE_MSGSENT:
2712
      /* handles in these states should NOT be in this list */
2713
0
      break;
2714
2715
0
    default:
2716
0
      return CURLM_INTERNAL_ERROR;
2717
0
    }
2718
2719
0
    if(data->mstate >= MSTATE_CONNECT &&
2720
0
       data->mstate < MSTATE_DO &&
2721
0
       mresult != CURLM_CALL_MULTI_PERFORM &&
2722
0
       !multi_ischanged(multi, FALSE)) {
2723
      /* We now handle stream timeouts if and only if this will be the last
2724
       * loop iteration. We only check this on the last iteration to ensure
2725
       * that if we know we have additional work to do immediately
2726
       * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before
2727
       * declaring the connection timed out as we may almost have a completed
2728
       * connection. */
2729
0
      multi_handle_timeout(data, &stream_error, &result);
2730
0
    }
2731
2732
0
statemachine_end:
2733
2734
0
    result = is_finished(multi, data, stream_error, result);
2735
0
    if(result)
2736
0
      mresult = CURLM_CALL_MULTI_PERFORM;
2737
2738
0
    if(MSTATE_COMPLETED == data->mstate) {
2739
0
      handle_completed(multi, data, result);
2740
0
      return CURLM_OK;
2741
0
    }
2742
0
  } while((mresult == CURLM_CALL_MULTI_PERFORM) ||
2743
0
          multi_ischanged(multi, FALSE));
2744
2745
0
  data->result = result;
2746
0
  return mresult;
2747
0
}
2748
2749
static CURLMcode multi_perform(struct Curl_multi *multi,
2750
                               int *running_handles)
2751
0
{
2752
0
  CURLMcode returncode = CURLM_OK;
2753
0
  struct curltime start = *multi_now(multi);
2754
0
  uint32_t mid;
2755
0
  struct Curl_sigpipe_ctx sigpipe_ctx;
2756
2757
0
  if(multi->in_callback)
2758
0
    return CURLM_RECURSIVE_API_CALL;
2759
2760
0
  if(multi->in_ntfy_callback)
2761
0
    return CURLM_RECURSIVE_API_CALL;
2762
2763
0
  sigpipe_init(&sigpipe_ctx);
2764
2765
0
  if(Curl_uint32_bset_first(&multi->process, &mid)) {
2766
0
    CURL_TRC_M(multi->admin, "multi_perform(running=%u)",
2767
0
               Curl_multi_xfers_running(multi));
2768
0
    do {
2769
0
      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
2770
0
      CURLMcode mresult;
2771
0
      if(!data) {
2772
0
        DEBUGASSERT(0);
2773
0
        Curl_uint32_bset_remove(&multi->process, mid);
2774
0
        Curl_uint32_bset_remove(&multi->dirty, mid);
2775
0
        continue;
2776
0
      }
2777
0
      mresult = multi_runsingle(multi, data, &sigpipe_ctx);
2778
0
      if(mresult)
2779
0
        returncode = mresult;
2780
0
    } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
2781
0
  }
2782
0
  sigpipe_restore(&sigpipe_ctx);
2783
2784
0
  if(multi_ischanged(multi, TRUE))
2785
0
    process_pending_handles(multi);
2786
2787
0
  if(!returncode && CURL_MNTFY_HAS_ENTRIES(multi))
2788
0
    returncode = Curl_mntfy_dispatch_all(multi);
2789
2790
  /*
2791
   * Remove all expired timers from the splay since handles are dealt
2792
   * with unconditionally by this function and curl_multi_timeout() requires
2793
   * that already passed/handled expire times are removed from the splay.
2794
   *
2795
   * It is important that the 'now' value is set at the entry of this function
2796
   * and not for the current time as it may have ticked a little while since
2797
   * then and then we risk this loop to remove timers that actually have not
2798
   * been handled!
2799
   */
2800
0
  if(multi->timetree) {
2801
0
    struct Curl_tree *t = NULL;
2802
0
    do {
2803
0
      multi->timetree = Curl_splaygetbest(&start, multi->timetree, &t);
2804
0
      if(t) {
2805
        /* the removed may have another timeout in queue */
2806
0
        struct Curl_easy *data = Curl_splayget(t);
2807
0
        (void)add_next_timeout(&start, multi, data);
2808
0
        if(data->mstate == MSTATE_PENDING) {
2809
0
          bool stream_unused;
2810
0
          CURLcode result_unused;
2811
0
          if(multi_handle_timeout(data, &stream_unused, &result_unused)) {
2812
0
            infof(data, "PENDING handle timeout");
2813
0
            move_pending_to_connect(multi, data);
2814
0
          }
2815
0
        }
2816
0
      }
2817
0
    } while(t);
2818
0
  }
2819
2820
0
  if(running_handles) {
2821
0
    unsigned int running = Curl_multi_xfers_running(multi);
2822
0
    *running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
2823
0
  }
2824
2825
0
  if(CURLM_OK >= returncode)
2826
0
    returncode = Curl_update_timer(multi);
2827
2828
0
  return returncode;
2829
0
}
2830
2831
CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
2832
0
{
2833
0
  struct Curl_multi *multi = m;
2834
2835
0
  if(!GOOD_MULTI_HANDLE(multi))
2836
0
    return CURLM_BAD_HANDLE;
2837
2838
0
  return multi_perform(multi, running_handles);
2839
0
}
2840
2841
CURLMcode curl_multi_cleanup(CURLM *m)
2842
0
{
2843
0
  struct Curl_multi *multi = m;
2844
0
  if(GOOD_MULTI_HANDLE(multi)) {
2845
0
    void *entry;
2846
0
    uint32_t mid;
2847
0
    if(multi->in_callback)
2848
0
      return CURLM_RECURSIVE_API_CALL;
2849
0
    if(multi->in_ntfy_callback)
2850
0
      return CURLM_RECURSIVE_API_CALL;
2851
2852
    /* First remove all remaining easy handles,
2853
     * close internal ones. admin handle is special */
2854
0
    if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) {
2855
0
      do {
2856
0
        struct Curl_easy *data = entry;
2857
0
        if(!GOOD_EASY_HANDLE(data))
2858
0
          return CURLM_BAD_HANDLE;
2859
2860
#ifdef DEBUGBUILD
2861
        if(mid != data->mid) {
2862
          CURL_TRC_M(data, "multi_cleanup: still present with mid=%u, "
2863
                     "but unexpected data->mid=%u\n", mid, data->mid);
2864
          DEBUGASSERT(0);
2865
        }
2866
#endif
2867
2868
0
        if(data == multi->admin)
2869
0
          continue;
2870
2871
0
        if(!data->state.done && data->conn)
2872
          /* if DONE was never called for this handle */
2873
0
          (void)multi_done(data, CURLE_OK, TRUE);
2874
2875
0
        data->multi = NULL; /* clear the association */
2876
0
        Curl_uint32_tbl_remove(&multi->xfers, mid);
2877
0
        data->mid = UINT32_MAX;
2878
2879
#ifdef USE_LIBPSL
2880
        if(data->psl == &multi->psl)
2881
          data->psl = NULL;
2882
#endif
2883
0
        if(data->state.internal)
2884
0
          Curl_close(&data);
2885
0
      } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry));
2886
0
    }
2887
2888
0
    Curl_cpool_destroy(&multi->cpool);
2889
0
    Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
2890
0
    if(multi->admin) {
2891
0
      CURL_TRC_M(multi->admin, "multi_cleanup, closing admin handle, done");
2892
0
      multi->admin->multi = NULL;
2893
0
      Curl_uint32_tbl_remove(&multi->xfers, multi->admin->mid);
2894
0
      Curl_close(&multi->admin);
2895
0
    }
2896
2897
0
    multi->magic = 0; /* not good anymore */
2898
2899
0
    Curl_multi_ev_cleanup(multi);
2900
0
    Curl_hash_destroy(&multi->proto_hash);
2901
0
    Curl_dnscache_destroy(&multi->dnscache);
2902
0
    Curl_psl_destroy(&multi->psl);
2903
#ifdef USE_SSL
2904
    Curl_ssl_scache_destroy(multi->ssl_scache);
2905
#endif
2906
2907
#ifdef USE_WINSOCK
2908
    WSACloseEvent(multi->wsa_event);
2909
#else
2910
0
#ifdef ENABLE_WAKEUP
2911
0
  Curl_wakeup_destroy(multi->wakeup_pair);
2912
0
#endif
2913
0
#endif
2914
2915
0
    multi_xfer_bufs_free(multi);
2916
0
    Curl_mntfy_cleanup(multi);
2917
#ifdef DEBUGBUILD
2918
    if(Curl_uint32_tbl_count(&multi->xfers)) {
2919
      multi_xfer_tbl_dump(multi);
2920
      DEBUGASSERT(0);
2921
    }
2922
#endif
2923
0
    Curl_uint32_bset_destroy(&multi->process);
2924
0
    Curl_uint32_bset_destroy(&multi->dirty);
2925
0
    Curl_uint32_bset_destroy(&multi->pending);
2926
0
    Curl_uint32_bset_destroy(&multi->msgsent);
2927
0
    Curl_uint32_tbl_destroy(&multi->xfers);
2928
0
    curlx_free(multi);
2929
2930
0
    return CURLM_OK;
2931
0
  }
2932
0
  return CURLM_BAD_HANDLE;
2933
0
}
2934
2935
/*
2936
 * curl_multi_info_read()
2937
 *
2938
 * This function is the primary way for a multi/multi_socket application to
2939
 * figure out if a transfer has ended. We MUST make this function as fast as
2940
 * possible as it will be polled frequently and we MUST NOT scan any lists in
2941
 * here to figure out things. We must scale fine to thousands of handles and
2942
 * beyond. The current design is fully O(1).
2943
 */
2944
2945
CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue)
2946
0
{
2947
0
  struct Curl_message *msg;
2948
0
  struct Curl_multi *multi = m;
2949
2950
0
  *msgs_in_queue = 0; /* default to none */
2951
2952
0
  if(GOOD_MULTI_HANDLE(multi) &&
2953
0
     !multi->in_callback &&
2954
0
     Curl_llist_count(&multi->msglist)) {
2955
    /* there is one or more messages in the list */
2956
0
    struct Curl_llist_node *e;
2957
2958
    /* extract the head of the list to return */
2959
0
    e = Curl_llist_head(&multi->msglist);
2960
2961
0
    msg = Curl_node_elem(e);
2962
2963
    /* remove the extracted entry */
2964
0
    Curl_node_remove(e);
2965
2966
0
    *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist));
2967
2968
0
    return &msg->extmsg;
2969
0
  }
2970
0
  return NULL;
2971
0
}
2972
2973
void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s)
2974
0
{
2975
0
  if(data) {
2976
0
    struct Curl_multi *multi = data->multi;
2977
0
    if(multi) {
2978
0
      CURL_TRC_M(data, "Curl_multi_will_close fd=%" FMT_SOCKET_T, s);
2979
0
      Curl_multi_ev_socket_done(multi, data, s);
2980
0
    }
2981
0
  }
2982
0
}
2983
2984
/*
2985
 * add_next_timeout()
2986
 *
2987
 * Each Curl_easy has a list of timeouts. The add_next_timeout() is called
2988
 * when it has been removed from the splay tree because the timeout has
2989
 * expired. This function is then to advance in the list to pick the next
2990
 * timeout to use (skip the already expired ones) and add this node back to
2991
 * the splay tree again.
2992
 *
2993
 * The splay tree only has each sessionhandle as a single node and the nearest
2994
 * timeout is used to sort it on.
2995
 */
2996
static CURLMcode add_next_timeout(const struct curltime *pnow,
2997
                                  struct Curl_multi *multi,
2998
                                  struct Curl_easy *d)
2999
0
{
3000
0
  struct curltime *tv = &d->state.expiretime;
3001
0
  struct Curl_llist *list = &d->state.timeoutlist;
3002
0
  struct Curl_llist_node *e;
3003
3004
  /* move over the timeout list for this specific handle and remove all
3005
     timeouts that are now passed tense and store the next pending
3006
     timeout in *tv */
3007
0
  for(e = Curl_llist_head(list); e;) {
3008
0
    struct Curl_llist_node *n = Curl_node_next(e);
3009
0
    struct time_node *node = Curl_node_elem(e);
3010
0
    timediff_t diff = curlx_ptimediff_us(&node->time, pnow);
3011
0
    if(diff <= 0)
3012
      /* remove outdated entry */
3013
0
      Curl_node_remove(e);
3014
0
    else
3015
      /* the list is sorted so get out on the first mismatch */
3016
0
      break;
3017
0
    e = n;
3018
0
  }
3019
0
  e = Curl_llist_head(list);
3020
0
  if(!e) {
3021
    /* clear the expire times within the handles that we remove from the
3022
       splay tree */
3023
0
    tv->tv_sec = 0;
3024
0
    tv->tv_usec = 0;
3025
0
  }
3026
0
  else {
3027
0
    struct time_node *node = Curl_node_elem(e);
3028
    /* copy the first entry to 'tv' */
3029
0
    memcpy(tv, &node->time, sizeof(*tv));
3030
3031
    /* Insert this node again into the splay. Keep the timer in the list in
3032
       case we need to recompute future timers. */
3033
0
    multi->timetree = Curl_splayinsert(tv, multi->timetree,
3034
0
                                       &d->state.timenode);
3035
0
  }
3036
0
  return CURLM_OK;
3037
0
}
3038
3039
static void multi_mark_expired_as_dirty(struct Curl_multi *multi,
3040
                                        const struct curltime *ts)
3041
0
{
3042
0
  struct Curl_easy *data = NULL;
3043
0
  struct Curl_tree *t = NULL;
3044
3045
  /*
3046
   * The loop following here will go on as long as there are expire-times left
3047
   * to process (compared to `ts`) in the splay and 'data' will be
3048
   * re-assigned for every expired handle we deal with.
3049
   */
3050
0
  while(1) {
3051
    /* Check if there is one (more) expired timer to deal with! This function
3052
       extracts a matching node if there is one */
3053
0
    multi->timetree = Curl_splaygetbest(ts, multi->timetree, &t);
3054
0
    if(!t)
3055
0
      return;
3056
3057
0
    data = Curl_splayget(t); /* assign this for next loop */
3058
0
    if(!data)
3059
0
      continue;
3060
0
#ifdef CURLVERBOSE
3061
0
    if(CURL_TRC_TIMER_is_verbose(data)) {
3062
0
      struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist);
3063
0
      if(e) {
3064
0
        struct time_node *n = Curl_node_elem(e);
3065
0
        CURL_TRC_TIMER(data, n->eid, "has expired");
3066
0
      }
3067
0
    }
3068
0
#endif
3069
0
    (void)add_next_timeout(ts, multi, data);
3070
0
    Curl_multi_mark_dirty(data);
3071
0
  }
3072
0
}
3073
3074
static CURLMcode multi_run_dirty(struct Curl_multi *multi,
3075
                                 struct Curl_sigpipe_ctx *sigpipe_ctx,
3076
                                 uint32_t *pnum)
3077
0
{
3078
0
  CURLMcode mresult = CURLM_OK;
3079
0
  uint32_t mid;
3080
3081
0
  *pnum = 0;
3082
0
  if(Curl_uint32_bset_first(&multi->dirty, &mid)) {
3083
0
    do {
3084
0
      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
3085
0
      if(data) {
3086
0
        CURL_TRC_M(data, "multi_run_dirty");
3087
3088
0
        if(!Curl_uint32_bset_contains(&multi->process, mid)) {
3089
          /* We are no longer processing this transfer */
3090
0
          Curl_uint32_bset_remove(&multi->dirty, mid);
3091
0
          continue;
3092
0
        }
3093
3094
0
        (*pnum)++;
3095
        /* runsingle() clears the dirty mid */
3096
0
        mresult = multi_runsingle(multi, data, sigpipe_ctx);
3097
3098
0
        if(CURLM_OK >= mresult) {
3099
          /* reassess event handling of data */
3100
0
          mresult = Curl_multi_ev_assess_xfer(multi, data);
3101
0
          if(mresult)
3102
0
            goto out;
3103
0
        }
3104
0
      }
3105
0
      else {
3106
0
        CURL_TRC_M(multi->admin, "multi_run_dirty, %u no longer found", mid);
3107
0
        Curl_uint32_bset_remove(&multi->dirty, mid);
3108
0
      }
3109
0
    } while(Curl_uint32_bset_next(&multi->dirty, mid, &mid));
3110
0
  }
3111
3112
0
out:
3113
0
  return mresult;
3114
0
}
3115
3116
static CURLMcode multi_socket(struct Curl_multi *multi,
3117
                              bool checkall,
3118
                              curl_socket_t s,
3119
                              int ev_bitmask,
3120
                              int *running_handles)
3121
0
{
3122
0
  CURLMcode mresult = CURLM_OK;
3123
0
  struct Curl_sigpipe_ctx pipe_ctx;
3124
0
  uint32_t run_xfers;
3125
3126
0
  (void)ev_bitmask;
3127
0
  sigpipe_init(&pipe_ctx);
3128
3129
0
  if(checkall) {
3130
    /* *perform() deals with running_handles on its own */
3131
0
    mresult = multi_perform(multi, running_handles);
3132
3133
0
    if(mresult != CURLM_BAD_HANDLE) {
3134
      /* Reassess event status of all active transfers */
3135
0
      mresult = Curl_multi_ev_assess_xfer_bset(multi, &multi->process);
3136
0
    }
3137
0
    goto out;
3138
0
  }
3139
3140
0
  if(s != CURL_SOCKET_TIMEOUT) {
3141
    /* Mark all transfers of that socket as dirty */
3142
0
    Curl_multi_ev_dirty_xfers(multi, s);
3143
0
  }
3144
0
  else {
3145
    /* Asked to run due to time-out. Clear the 'last_expire_ts' variable to
3146
       force Curl_update_timer() to trigger a callback to the app again even
3147
       if the same timeout is still the one to run after this call. That
3148
       handles the case when the application asks libcurl to run the timeout
3149
       prematurely. */
3150
0
    memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts));
3151
0
  }
3152
3153
0
  multi_mark_expired_as_dirty(multi, multi_now(multi));
3154
0
  mresult = multi_run_dirty(multi, &pipe_ctx, &run_xfers);
3155
0
  if(mresult)
3156
0
    goto out;
3157
3158
0
  if(run_xfers) {
3159
    /* Running transfers takes time. With a new timestamp, we might catch
3160
     * other expires which are due now. Instead of telling the application
3161
     * to set a 0 timeout and call us again, we run them here.
3162
     * Do that only once or it might be unfair to transfers on other
3163
     * sockets. */
3164
0
    multi_mark_expired_as_dirty(multi, &multi->now);
3165
0
    mresult = multi_run_dirty(multi, &pipe_ctx, &run_xfers);
3166
0
  }
3167
3168
0
out:
3169
0
  sigpipe_restore(&pipe_ctx);
3170
3171
0
  if(multi_ischanged(multi, TRUE))
3172
0
    process_pending_handles(multi);
3173
3174
0
  if(!mresult && CURL_MNTFY_HAS_ENTRIES(multi))
3175
0
    mresult = Curl_mntfy_dispatch_all(multi);
3176
3177
0
  if(running_handles) {
3178
0
    unsigned int running = Curl_multi_xfers_running(multi);
3179
0
    *running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
3180
0
  }
3181
3182
0
  if(CURLM_OK >= mresult)
3183
0
    mresult = Curl_update_timer(multi);
3184
0
  return mresult;
3185
0
}
3186
3187
#undef curl_multi_setopt
3188
CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...)
3189
0
{
3190
0
  CURLMcode mresult = CURLM_OK;
3191
0
  va_list param;
3192
0
  unsigned long uarg;
3193
0
  struct Curl_multi *multi = m;
3194
3195
0
  if(!GOOD_MULTI_HANDLE(multi))
3196
0
    return CURLM_BAD_HANDLE;
3197
3198
0
  if(multi->in_callback)
3199
0
    return CURLM_RECURSIVE_API_CALL;
3200
3201
0
  va_start(param, option);
3202
3203
0
  switch(option) {
3204
0
  case CURLMOPT_SOCKETFUNCTION:
3205
0
    multi->socket_cb = va_arg(param, curl_socket_callback);
3206
0
    break;
3207
0
  case CURLMOPT_SOCKETDATA:
3208
0
    multi->socket_userp = va_arg(param, void *);
3209
0
    break;
3210
0
  case CURLMOPT_PUSHFUNCTION:
3211
0
    multi->push_cb = va_arg(param, curl_push_callback);
3212
0
    break;
3213
0
  case CURLMOPT_PUSHDATA:
3214
0
    multi->push_userp = va_arg(param, void *);
3215
0
    break;
3216
0
  case CURLMOPT_PIPELINING:
3217
0
    multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0;
3218
0
    break;
3219
0
  case CURLMOPT_TIMERFUNCTION:
3220
0
    multi->timer_cb = va_arg(param, curl_multi_timer_callback);
3221
0
    break;
3222
0
  case CURLMOPT_TIMERDATA:
3223
0
    multi->timer_userp = va_arg(param, void *);
3224
0
    break;
3225
0
  case CURLMOPT_MAXCONNECTS:
3226
0
    uarg = va_arg(param, unsigned long);
3227
0
    if(uarg <= UINT_MAX)
3228
0
      multi->maxconnects = (unsigned int)uarg;
3229
0
    break;
3230
0
  case CURLMOPT_MAX_HOST_CONNECTIONS:
3231
0
    if(!curlx_sltouz(va_arg(param, long), &multi->max_host_connections))
3232
0
      mresult = CURLM_BAD_FUNCTION_ARGUMENT;
3233
0
    break;
3234
0
  case CURLMOPT_MAX_TOTAL_CONNECTIONS:
3235
0
    if(!curlx_sltouz(va_arg(param, long), &multi->max_total_connections))
3236
0
      mresult = CURLM_BAD_FUNCTION_ARGUMENT;
3237
0
    break;
3238
    /* options formerly used for pipelining */
3239
0
  case CURLMOPT_MAX_PIPELINE_LENGTH:
3240
0
    break;
3241
0
  case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
3242
0
    break;
3243
0
  case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
3244
0
    break;
3245
0
  case CURLMOPT_PIPELINING_SITE_BL:
3246
0
    break;
3247
0
  case CURLMOPT_PIPELINING_SERVER_BL:
3248
0
    break;
3249
0
  case CURLMOPT_MAX_CONCURRENT_STREAMS: {
3250
0
    long streams = va_arg(param, long);
3251
0
    if((streams < 1) || (streams > INT_MAX))
3252
0
      streams = 100;
3253
0
    multi->max_concurrent_streams = (unsigned int)streams;
3254
0
    break;
3255
0
  }
3256
0
  case CURLMOPT_NETWORK_CHANGED: {
3257
0
    long val = va_arg(param, long);
3258
0
    if(val & CURLMNWC_CLEAR_DNS) {
3259
0
      Curl_dnscache_clear(multi->admin);
3260
0
    }
3261
0
    if(val & CURLMNWC_CLEAR_CONNS) {
3262
0
      Curl_cpool_nw_changed(multi->admin);
3263
0
    }
3264
0
    break;
3265
0
  }
3266
0
  case CURLMOPT_NOTIFYFUNCTION:
3267
0
    multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback);
3268
0
    break;
3269
0
  case CURLMOPT_NOTIFYDATA:
3270
0
    multi->ntfy.ntfy_cb_data = va_arg(param, void *);
3271
0
    break;
3272
0
  default:
3273
0
    mresult = CURLM_UNKNOWN_OPTION;
3274
0
    break;
3275
0
  }
3276
0
  va_end(param);
3277
0
  return mresult;
3278
0
}
3279
3280
/* we define curl_multi_socket() in the public multi.h header */
3281
#undef curl_multi_socket
3282
3283
CURLMcode curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles)
3284
0
{
3285
0
  struct Curl_multi *multi = m;
3286
0
  if(multi->in_callback)
3287
0
    return CURLM_RECURSIVE_API_CALL;
3288
0
  if(multi->in_ntfy_callback)
3289
0
    return CURLM_RECURSIVE_API_CALL;
3290
0
  return multi_socket(multi, FALSE, s, 0, running_handles);
3291
0
}
3292
3293
CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s,
3294
                                   int ev_bitmask, int *running_handles)
3295
0
{
3296
0
  struct Curl_multi *multi = m;
3297
0
  if(multi->in_callback)
3298
0
    return CURLM_RECURSIVE_API_CALL;
3299
0
  if(multi->in_ntfy_callback)
3300
0
    return CURLM_RECURSIVE_API_CALL;
3301
0
  return multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
3302
0
}
3303
3304
CURLMcode curl_multi_socket_all(CURLM *m, int *running_handles)
3305
0
{
3306
0
  struct Curl_multi *multi = m;
3307
0
  if(multi->in_callback)
3308
0
    return CURLM_RECURSIVE_API_CALL;
3309
0
  if(multi->in_ntfy_callback)
3310
0
    return CURLM_RECURSIVE_API_CALL;
3311
0
  return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
3312
0
}
3313
3314
static bool multi_has_dirties(struct Curl_multi *multi)
3315
0
{
3316
0
  uint32_t mid;
3317
0
  if(Curl_uint32_bset_first(&multi->dirty, &mid)) {
3318
0
    do {
3319
0
      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
3320
0
      if(data) {
3321
0
        if(Curl_uint32_bset_contains(&multi->process, mid))
3322
0
          return TRUE;
3323
        /* We are no longer processing this transfer */
3324
0
        Curl_uint32_bset_remove(&multi->dirty, mid);
3325
0
      }
3326
0
      else {
3327
0
        CURL_TRC_M(multi->admin, "dirty transfer %u no longer found", mid);
3328
0
        Curl_uint32_bset_remove(&multi->dirty, mid);
3329
0
      }
3330
0
    } while(Curl_uint32_bset_next(&multi->dirty, mid, &mid));
3331
0
  }
3332
0
  return FALSE;
3333
0
}
3334
3335
static void multi_timeout(struct Curl_multi *multi,
3336
                          struct curltime *expire_time,
3337
                          long *timeout_ms)
3338
0
{
3339
0
  static const struct curltime tv_zero = { 0, 0 };
3340
0
  VERBOSE(struct Curl_easy *data = NULL);
3341
3342
0
  if(multi->dead) {
3343
0
    *timeout_ms = 0;
3344
0
    return;
3345
0
  }
3346
3347
0
  if(multi_has_dirties(multi)) {
3348
0
    *expire_time = *multi_now(multi);
3349
0
    *timeout_ms = 0;
3350
0
    return;
3351
0
  }
3352
0
  else if(multi->timetree) {
3353
0
    const struct curltime *pnow = multi_now(multi);
3354
    /* splay the lowest to the bottom */
3355
0
    multi->timetree = Curl_splay(&tv_zero, multi->timetree);
3356
    /* this will not return NULL from a non-empty tree, but some compilers
3357
     * are not convinced of that. Analyzers are hard. */
3358
0
    *expire_time = multi->timetree ? multi->timetree->key : tv_zero;
3359
3360
    /* 'multi->timetree' will be non-NULL here but the compilers sometimes
3361
       yell at us if we assume so */
3362
0
    if(multi->timetree &&
3363
0
       curlx_ptimediff_us(&multi->timetree->key, pnow) > 0) {
3364
      /* some time left before expiration */
3365
0
      timediff_t diff_ms =
3366
0
        curlx_timediff_ceil_ms(multi->timetree->key, *pnow);
3367
0
      VERBOSE(data = Curl_splayget(multi->timetree));
3368
      /* this should be safe even on 32-bit archs, as we do not use that
3369
         overly long timeouts */
3370
0
      *timeout_ms = (long)diff_ms;
3371
0
    }
3372
0
    else {
3373
0
      if(multi->timetree)
3374
0
        VERBOSE(data = Curl_splayget(multi->timetree));
3375
      /* 0 means immediately */
3376
0
      *timeout_ms = 0;
3377
0
    }
3378
0
  }
3379
0
  else {
3380
0
    *expire_time = tv_zero;
3381
0
    *timeout_ms = -1;
3382
0
  }
3383
3384
0
#ifdef CURLVERBOSE
3385
0
  if(CURL_TRC_TIMER_is_verbose(data)) {
3386
0
    struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist);
3387
0
    if(e) {
3388
0
      struct time_node *n = Curl_node_elem(e);
3389
0
      CURL_TRC_TIMER(data, n->eid, "gives multi timeout in %ldms",
3390
0
                     *timeout_ms);
3391
0
    }
3392
0
  }
3393
0
#endif
3394
0
}
3395
3396
CURLMcode curl_multi_timeout(CURLM *m,
3397
                             long *timeout_ms)
3398
0
{
3399
0
  struct curltime expire_time;
3400
0
  struct Curl_multi *multi = m;
3401
3402
  /* First, make some basic checks that the CURLM handle is a good handle */
3403
0
  if(!GOOD_MULTI_HANDLE(multi))
3404
0
    return CURLM_BAD_HANDLE;
3405
3406
0
  if(multi->in_callback)
3407
0
    return CURLM_RECURSIVE_API_CALL;
3408
3409
0
  multi_timeout(multi, &expire_time, timeout_ms);
3410
0
  return CURLM_OK;
3411
0
}
3412
3413
/*
3414
 * Tell the application it should update its timers, if it subscribes to the
3415
 * update timer callback.
3416
 */
3417
CURLMcode Curl_update_timer(struct Curl_multi *multi)
3418
0
{
3419
0
  struct curltime expire_ts;
3420
0
  long timeout_ms;
3421
0
  int rc;
3422
0
  bool set_value = FALSE;
3423
3424
0
  if(!multi->timer_cb || multi->dead)
3425
0
    return CURLM_OK;
3426
0
  multi_timeout(multi, &expire_ts, &timeout_ms);
3427
3428
0
  if(timeout_ms < 0 && multi->last_timeout_ms < 0) {
3429
    /* nothing to do */
3430
0
  }
3431
0
  else if(timeout_ms < 0) {
3432
    /* there is no timeout now but there was one previously */
3433
0
    CURL_TRC_M(multi->admin, "[TIMER] clear");
3434
0
    timeout_ms = -1; /* normalize */
3435
0
    set_value = TRUE;
3436
0
  }
3437
0
  else if(multi->last_timeout_ms < 0) {
3438
0
    CURL_TRC_M(multi->admin, "[TIMER] set %ldms, none before", timeout_ms);
3439
0
    set_value = TRUE;
3440
0
  }
3441
0
  else if(curlx_ptimediff_us(&multi->last_expire_ts, &expire_ts)) {
3442
    /* We had a timeout before and have one now, the absolute timestamp
3443
     * differs. The relative timeout_ms may be the same, but the starting
3444
     * point differs. Let the application restart its timer. */
3445
0
    CURL_TRC_M(multi->admin, "[TIMER] set %ldms, replace previous",
3446
0
               timeout_ms);
3447
0
    set_value = TRUE;
3448
0
  }
3449
0
  else {
3450
    /* We have same expire time as previously. Our relative 'timeout_ms'
3451
     * may be different now, but the application has the timer running
3452
     * and we do not to tell it to start this again. */
3453
0
  }
3454
3455
0
  if(set_value) {
3456
0
    multi->last_expire_ts = expire_ts;
3457
0
    multi->last_timeout_ms = timeout_ms;
3458
0
    set_in_callback(multi, TRUE);
3459
0
    rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp);
3460
0
    set_in_callback(multi, FALSE);
3461
0
    if(rc == -1) {
3462
0
      multi->dead = TRUE;
3463
0
      return CURLM_ABORTED_BY_CALLBACK;
3464
0
    }
3465
0
  }
3466
0
  return CURLM_OK;
3467
0
}
3468
3469
/*
3470
 * multi_deltimeout()
3471
 *
3472
 * Remove a given timestamp from the list of timeouts.
3473
 */
3474
static void multi_deltimeout(struct Curl_easy *data, expire_id eid)
3475
0
{
3476
0
  struct Curl_llist_node *e;
3477
0
  struct Curl_llist *timeoutlist = &data->state.timeoutlist;
3478
  /* find and remove the specific node from the list */
3479
0
  for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) {
3480
0
    struct time_node *n = Curl_node_elem(e);
3481
0
    if(n->eid == eid) {
3482
0
      Curl_node_remove(e);
3483
0
      return;
3484
0
    }
3485
0
  }
3486
0
}
3487
3488
/*
3489
 * multi_addtimeout()
3490
 *
3491
 * Add a timestamp to the list of timeouts. Keep the list sorted so that head
3492
 * of list is always the timeout nearest in time.
3493
 *
3494
 */
3495
static CURLMcode multi_addtimeout(struct Curl_easy *data,
3496
                                  struct curltime *stamp,
3497
                                  expire_id eid)
3498
0
{
3499
0
  struct Curl_llist_node *e;
3500
0
  struct time_node *node;
3501
0
  struct Curl_llist_node *prev = NULL;
3502
0
  size_t n;
3503
0
  struct Curl_llist *timeoutlist = &data->state.timeoutlist;
3504
3505
0
  node = &data->state.expires[eid];
3506
3507
  /* copy the timestamp and id */
3508
0
  memcpy(&node->time, stamp, sizeof(*stamp));
3509
0
  node->eid = eid; /* also marks it as in use */
3510
3511
0
  n = Curl_llist_count(timeoutlist);
3512
0
  if(n) {
3513
    /* find the correct spot in the list */
3514
0
    for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) {
3515
0
      struct time_node *check = Curl_node_elem(e);
3516
0
      timediff_t diff = curlx_ptimediff_ms(&check->time, &node->time);
3517
0
      if(diff > 0)
3518
0
        break;
3519
0
      prev = e;
3520
0
    }
3521
0
  }
3522
  /* else
3523
     this is the first timeout on the list */
3524
3525
0
  Curl_llist_insert_next(timeoutlist, prev, node, &node->list);
3526
0
  CURL_TRC_TIMER(data, eid, "set for %" FMT_TIMEDIFF_T "ns",
3527
0
                 curlx_ptimediff_us(&node->time, Curl_pgrs_now(data)));
3528
0
  return CURLM_OK;
3529
0
}
3530
3531
void Curl_expire_ex(struct Curl_easy *data,
3532
                    timediff_t milli, expire_id id)
3533
0
{
3534
0
  struct Curl_multi *multi = data->multi;
3535
0
  struct curltime *curr_expire = &data->state.expiretime;
3536
0
  struct curltime set;
3537
3538
  /* this is only interesting while there is still an associated multi struct
3539
     remaining! */
3540
0
  if(!multi)
3541
0
    return;
3542
3543
0
  DEBUGASSERT(id < EXPIRE_LAST);
3544
3545
0
  set = *Curl_pgrs_now(data);
3546
0
  set.tv_sec += (time_t)(milli / 1000); /* may be a 64 to 32-bit conversion */
3547
0
  set.tv_usec += (int)(milli % 1000) * 1000;
3548
3549
0
  if(set.tv_usec >= 1000000) {
3550
0
    set.tv_sec++;
3551
0
    set.tv_usec -= 1000000;
3552
0
  }
3553
3554
  /* Remove any timer with the same id */
3555
0
  multi_deltimeout(data, id);
3556
3557
  /* Add it to the timer list. It must stay in the list until it has expired
3558
     in case we need to recompute the minimum timer later. */
3559
0
  multi_addtimeout(data, &set, id);
3560
3561
0
  if(curr_expire->tv_sec || curr_expire->tv_usec) {
3562
    /* This means that the struct is added as a node in the splay tree.
3563
       Compare if the new time is earlier, and only remove-old/add-new if it
3564
       is. */
3565
0
    timediff_t diff = curlx_ptimediff_ms(&set, curr_expire);
3566
0
    int rc;
3567
3568
0
    if(diff > 0) {
3569
      /* The current splay tree entry is sooner than this new expiry time.
3570
         We do not need to update our splay tree entry. */
3571
0
      return;
3572
0
    }
3573
3574
    /* Since this is an updated time, we must remove the previous entry from
3575
       the splay tree first and then re-add the new value */
3576
0
    rc = Curl_splayremove(multi->timetree, &data->state.timenode,
3577
0
                          &multi->timetree);
3578
0
    if(rc)
3579
0
      infof(data, "Internal error removing splay node = %d", rc);
3580
0
  }
3581
3582
  /* Indicate that we are in the splay tree and insert the new timer expiry
3583
     value since it is our local minimum. */
3584
0
  *curr_expire = set;
3585
0
  Curl_splayset(&data->state.timenode, data);
3586
0
  multi->timetree = Curl_splayinsert(curr_expire, multi->timetree,
3587
0
                                     &data->state.timenode);
3588
0
}
3589
3590
/*
3591
 * Curl_expire()
3592
 *
3593
 * given a number of milliseconds from now to use to set the 'act before
3594
 * this'-time for the transfer, to be extracted by curl_multi_timeout()
3595
 *
3596
 * The timeout will be added to a queue of timeouts if it defines a moment in
3597
 * time that is later than the current head of queue.
3598
 *
3599
 * Expire replaces a former timeout using the same id if already set.
3600
 */
3601
void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
3602
0
{
3603
0
  Curl_expire_ex(data, milli, id);
3604
0
}
3605
3606
/*
3607
 * Curl_expire_done()
3608
 *
3609
 * Removes the expire timer. Marks it as done.
3610
 *
3611
 */
3612
void Curl_expire_done(struct Curl_easy *data, expire_id id)
3613
0
{
3614
  /* remove the timer, if there */
3615
0
  multi_deltimeout(data, id);
3616
0
  CURL_TRC_TIMER(data, id, "cleared");
3617
0
}
3618
3619
/*
3620
 * Curl_expire_clear()
3621
 *
3622
 * Clear ALL timeout values for this handle.
3623
 */
3624
void Curl_expire_clear(struct Curl_easy *data)
3625
0
{
3626
0
  struct Curl_multi *multi = data->multi;
3627
0
  struct curltime *nowp = &data->state.expiretime;
3628
3629
  /* this is only interesting while there is still an associated multi struct
3630
     remaining! */
3631
0
  if(!multi)
3632
0
    return;
3633
3634
0
  if(nowp->tv_sec || nowp->tv_usec) {
3635
    /* Since this is an cleared time, we must remove the previous entry from
3636
       the splay tree */
3637
0
    struct Curl_llist *list = &data->state.timeoutlist;
3638
0
    int rc;
3639
3640
0
    rc = Curl_splayremove(multi->timetree, &data->state.timenode,
3641
0
                          &multi->timetree);
3642
0
    if(rc)
3643
0
      infof(data, "Internal error clearing splay node = %d", rc);
3644
3645
    /* clear the timeout list too */
3646
0
    Curl_llist_destroy(list, NULL);
3647
3648
0
    if(data->id >= 0)
3649
0
      CURL_TRC_M(data, "[TIMEOUT] all cleared");
3650
0
    nowp->tv_sec = 0;
3651
0
    nowp->tv_usec = 0;
3652
0
  }
3653
0
}
3654
3655
CURLMcode curl_multi_assign(CURLM *m, curl_socket_t s,
3656
                            void *hashp)
3657
0
{
3658
0
  struct Curl_multi *multi = m;
3659
0
  if(!GOOD_MULTI_HANDLE(multi))
3660
0
    return CURLM_BAD_HANDLE;
3661
3662
0
  return Curl_multi_ev_assign(multi, s, hashp);
3663
0
}
3664
3665
static void move_pending_to_connect(struct Curl_multi *multi,
3666
                                    struct Curl_easy *data)
3667
0
{
3668
0
  DEBUGASSERT(data->mstate == MSTATE_PENDING);
3669
3670
  /* Remove this node from the pending set, add into process set */
3671
0
  Curl_uint32_bset_remove(&multi->pending, data->mid);
3672
0
  Curl_uint32_bset_add(&multi->process, data->mid);
3673
3674
0
  multistate(data, MSTATE_CONNECT);
3675
0
  Curl_multi_mark_dirty(data); /* make it run */
3676
0
}
3677
3678
/* process_pending_handles() moves a handle from PENDING back into the process
3679
   list and change state to CONNECT.
3680
3681
   We do not move all transfers because that can be a significant amount.
3682
   Since this is tried every now and then doing too many too often becomes a
3683
   performance problem.
3684
3685
   When there is a change for connection limits like max host connections etc,
3686
   this likely only allows one new transfer. When there is a pipewait change,
3687
   it can potentially allow hundreds of new transfers.
3688
3689
   We could consider an improvement where we store the queue reason and allow
3690
   more pipewait rechecks than others.
3691
*/
3692
static void process_pending_handles(struct Curl_multi *multi)
3693
0
{
3694
0
  uint32_t mid;
3695
0
  if(Curl_uint32_bset_first(&multi->pending, &mid)) {
3696
0
    do {
3697
0
      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
3698
0
      if(data) {
3699
0
        move_pending_to_connect(multi, data);
3700
0
        break;
3701
0
      }
3702
      /* transfer no longer known, should not happen */
3703
0
      Curl_uint32_bset_remove(&multi->pending, mid);
3704
0
      DEBUGASSERT(0);
3705
0
    } while(Curl_uint32_bset_next(&multi->pending, mid, &mid));
3706
0
  }
3707
0
}
3708
3709
void Curl_set_in_callback(struct Curl_easy *data, bool value)
3710
0
{
3711
0
  if(data && data->multi)
3712
0
    data->multi->in_callback = value;
3713
0
}
3714
3715
bool Curl_is_in_callback(struct Curl_easy *data)
3716
0
{
3717
0
  return data && data->multi && data->multi->in_callback;
3718
0
}
3719
3720
unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
3721
0
{
3722
0
  DEBUGASSERT(multi);
3723
0
  return multi->max_concurrent_streams;
3724
0
}
3725
3726
CURL **curl_multi_get_handles(CURLM *m)
3727
0
{
3728
0
  struct Curl_multi *multi = m;
3729
0
  void *entry;
3730
0
  size_t count = Curl_uint32_tbl_count(&multi->xfers);
3731
0
  CURL **a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1));
3732
0
  if(a) {
3733
0
    unsigned int i = 0;
3734
0
    uint32_t mid;
3735
3736
0
    if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) {
3737
0
      do {
3738
0
        struct Curl_easy *data = entry;
3739
0
        DEBUGASSERT(i < count);
3740
0
        if(!data->state.internal)
3741
0
          a[i++] = data;
3742
0
      } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry));
3743
0
    }
3744
0
    a[i] = NULL; /* last entry is a NULL */
3745
0
  }
3746
0
  return a;
3747
0
}
3748
3749
CURLMcode curl_multi_get_offt(CURLM *m,
3750
                              CURLMinfo_offt info,
3751
                              curl_off_t *pvalue)
3752
0
{
3753
0
  struct Curl_multi *multi = m;
3754
0
  uint32_t n;
3755
3756
0
  if(!GOOD_MULTI_HANDLE(multi))
3757
0
    return CURLM_BAD_HANDLE;
3758
0
  if(!pvalue)
3759
0
    return CURLM_BAD_FUNCTION_ARGUMENT;
3760
3761
0
  switch(info) {
3762
0
  case CURLMINFO_XFERS_CURRENT:
3763
0
    n = Curl_uint32_tbl_count(&multi->xfers);
3764
0
    if(n && multi->admin)
3765
0
      --n;
3766
0
    *pvalue = (curl_off_t)n;
3767
0
    return CURLM_OK;
3768
0
  case CURLMINFO_XFERS_RUNNING:
3769
0
    n = Curl_uint32_bset_count(&multi->process);
3770
0
    if(n && Curl_uint32_bset_contains(&multi->process, multi->admin->mid))
3771
0
      --n;
3772
0
    *pvalue = (curl_off_t)n;
3773
0
    return CURLM_OK;
3774
0
  case CURLMINFO_XFERS_PENDING:
3775
0
    *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->pending);
3776
0
    return CURLM_OK;
3777
0
  case CURLMINFO_XFERS_DONE:
3778
0
    *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->msgsent);
3779
0
    return CURLM_OK;
3780
0
  case CURLMINFO_XFERS_ADDED:
3781
0
    *pvalue = multi->xfers_total_ever;
3782
0
    return CURLM_OK;
3783
0
  default:
3784
0
    *pvalue = -1;
3785
0
    return CURLM_UNKNOWN_OPTION;
3786
0
  }
3787
0
}
3788
3789
CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
3790
                                    char **pbuf, size_t *pbuflen)
3791
0
{
3792
0
  DEBUGASSERT(data);
3793
0
  DEBUGASSERT(data->multi);
3794
0
  *pbuf = NULL;
3795
0
  *pbuflen = 0;
3796
0
  if(!data->multi) {
3797
0
    failf(data, "transfer has no multi handle");
3798
0
    return CURLE_FAILED_INIT;
3799
0
  }
3800
0
  if(!data->set.buffer_size) {
3801
0
    failf(data, "transfer buffer size is 0");
3802
0
    return CURLE_FAILED_INIT;
3803
0
  }
3804
0
  if(data->multi->xfer_buf_borrowed) {
3805
0
    failf(data, "attempt to borrow xfer_buf when already borrowed");
3806
0
    return CURLE_AGAIN;
3807
0
  }
3808
3809
0
  if(data->multi->xfer_buf &&
3810
0
     data->set.buffer_size > data->multi->xfer_buf_len) {
3811
    /* not large enough, get a new one */
3812
0
    curlx_free(data->multi->xfer_buf);
3813
0
    data->multi->xfer_buf = NULL;
3814
0
    data->multi->xfer_buf_len = 0;
3815
0
  }
3816
3817
0
  if(!data->multi->xfer_buf) {
3818
0
    data->multi->xfer_buf = curlx_malloc(curlx_uitouz(data->set.buffer_size));
3819
0
    if(!data->multi->xfer_buf) {
3820
0
      failf(data, "could not allocate xfer_buf of %u bytes",
3821
0
            data->set.buffer_size);
3822
0
      return CURLE_OUT_OF_MEMORY;
3823
0
    }
3824
0
    data->multi->xfer_buf_len = data->set.buffer_size;
3825
0
  }
3826
3827
0
  data->multi->xfer_buf_borrowed = TRUE;
3828
0
  *pbuf = data->multi->xfer_buf;
3829
0
  *pbuflen = data->multi->xfer_buf_len;
3830
0
  return CURLE_OK;
3831
0
}
3832
3833
void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf)
3834
0
{
3835
0
  (void)buf;
3836
0
  DEBUGASSERT(data);
3837
0
  DEBUGASSERT(data->multi);
3838
0
  DEBUGASSERT(!buf || data->multi->xfer_buf == buf);
3839
0
  data->multi->xfer_buf_borrowed = FALSE;
3840
0
}
3841
3842
CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data,
3843
                                      char **pbuf, size_t *pbuflen)
3844
0
{
3845
0
  DEBUGASSERT(data);
3846
0
  DEBUGASSERT(data->multi);
3847
0
  *pbuf = NULL;
3848
0
  *pbuflen = 0;
3849
0
  if(!data->multi) {
3850
0
    failf(data, "transfer has no multi handle");
3851
0
    return CURLE_FAILED_INIT;
3852
0
  }
3853
0
  if(!data->set.upload_buffer_size) {
3854
0
    failf(data, "transfer upload buffer size is 0");
3855
0
    return CURLE_FAILED_INIT;
3856
0
  }
3857
0
  if(data->multi->xfer_ulbuf_borrowed) {
3858
0
    failf(data, "attempt to borrow xfer_ulbuf when already borrowed");
3859
0
    return CURLE_AGAIN;
3860
0
  }
3861
3862
0
  if(data->multi->xfer_ulbuf &&
3863
0
     data->set.upload_buffer_size > data->multi->xfer_ulbuf_len) {
3864
    /* not large enough, get a new one */
3865
0
    curlx_free(data->multi->xfer_ulbuf);
3866
0
    data->multi->xfer_ulbuf = NULL;
3867
0
    data->multi->xfer_ulbuf_len = 0;
3868
0
  }
3869
3870
0
  if(!data->multi->xfer_ulbuf) {
3871
0
    data->multi->xfer_ulbuf =
3872
0
      curlx_malloc(curlx_uitouz(data->set.upload_buffer_size));
3873
0
    if(!data->multi->xfer_ulbuf) {
3874
0
      failf(data, "could not allocate xfer_ulbuf of %u bytes",
3875
0
            data->set.upload_buffer_size);
3876
0
      return CURLE_OUT_OF_MEMORY;
3877
0
    }
3878
0
    data->multi->xfer_ulbuf_len = data->set.upload_buffer_size;
3879
0
  }
3880
3881
0
  data->multi->xfer_ulbuf_borrowed = TRUE;
3882
0
  *pbuf = data->multi->xfer_ulbuf;
3883
0
  *pbuflen = data->multi->xfer_ulbuf_len;
3884
0
  return CURLE_OK;
3885
0
}
3886
3887
void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf)
3888
0
{
3889
0
  (void)buf;
3890
0
  DEBUGASSERT(data);
3891
0
  DEBUGASSERT(data->multi);
3892
0
  DEBUGASSERT(!buf || data->multi->xfer_ulbuf == buf);
3893
0
  data->multi->xfer_ulbuf_borrowed = FALSE;
3894
0
}
3895
3896
CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data,
3897
                                        size_t blen, char **pbuf)
3898
0
{
3899
0
  DEBUGASSERT(data);
3900
0
  DEBUGASSERT(data->multi);
3901
0
  *pbuf = NULL;
3902
0
  if(!data->multi) {
3903
0
    failf(data, "transfer has no multi handle");
3904
0
    return CURLE_FAILED_INIT;
3905
0
  }
3906
0
  if(data->multi->xfer_sockbuf_borrowed) {
3907
0
    failf(data, "attempt to borrow xfer_sockbuf when already borrowed");
3908
0
    return CURLE_AGAIN;
3909
0
  }
3910
3911
0
  if(data->multi->xfer_sockbuf && blen > data->multi->xfer_sockbuf_len) {
3912
    /* not large enough, get a new one */
3913
0
    curlx_free(data->multi->xfer_sockbuf);
3914
0
    data->multi->xfer_sockbuf = NULL;
3915
0
    data->multi->xfer_sockbuf_len = 0;
3916
0
  }
3917
3918
0
  if(!data->multi->xfer_sockbuf) {
3919
0
    data->multi->xfer_sockbuf = curlx_malloc(blen);
3920
0
    if(!data->multi->xfer_sockbuf) {
3921
0
      failf(data, "could not allocate xfer_sockbuf of %zu bytes", blen);
3922
0
      return CURLE_OUT_OF_MEMORY;
3923
0
    }
3924
0
    data->multi->xfer_sockbuf_len = blen;
3925
0
  }
3926
3927
0
  data->multi->xfer_sockbuf_borrowed = TRUE;
3928
0
  *pbuf = data->multi->xfer_sockbuf;
3929
0
  return CURLE_OK;
3930
0
}
3931
3932
void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf)
3933
0
{
3934
0
  (void)buf;
3935
0
  DEBUGASSERT(data);
3936
0
  DEBUGASSERT(data->multi);
3937
0
  DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf);
3938
0
  data->multi->xfer_sockbuf_borrowed = FALSE;
3939
0
}
3940
3941
static void multi_xfer_bufs_free(struct Curl_multi *multi)
3942
0
{
3943
0
  DEBUGASSERT(multi);
3944
0
  Curl_safefree(multi->xfer_buf);
3945
0
  multi->xfer_buf_len = 0;
3946
0
  multi->xfer_buf_borrowed = FALSE;
3947
0
  Curl_safefree(multi->xfer_ulbuf);
3948
0
  multi->xfer_ulbuf_len = 0;
3949
0
  multi->xfer_ulbuf_borrowed = FALSE;
3950
0
  Curl_safefree(multi->xfer_sockbuf);
3951
0
  multi->xfer_sockbuf_len = 0;
3952
0
  multi->xfer_sockbuf_borrowed = FALSE;
3953
0
}
3954
3955
struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi,
3956
                                      uint32_t mid)
3957
0
{
3958
0
  struct Curl_easy *data = Curl_uint32_tbl_get(&multi->xfers, mid);
3959
0
  if(GOOD_EASY_HANDLE(data))
3960
0
    return data;
3961
0
  CURL_TRC_M(multi->admin, "invalid easy handle in xfer table for mid=%u",
3962
0
             mid);
3963
0
  Curl_uint32_tbl_remove(&multi->xfers, mid);
3964
0
  return NULL;
3965
0
}
3966
3967
unsigned int Curl_multi_xfers_running(struct Curl_multi *multi)
3968
0
{
3969
0
  return multi->xfers_alive;
3970
0
}
3971
3972
void Curl_multi_mark_dirty(struct Curl_easy *data)
3973
0
{
3974
0
  if(data->multi && data->mid != UINT32_MAX)
3975
0
    Curl_uint32_bset_add(&data->multi->dirty, data->mid);
3976
0
}
3977
3978
void Curl_multi_clear_dirty(struct Curl_easy *data)
3979
0
{
3980
0
  if(data->multi && data->mid != UINT32_MAX)
3981
0
    Curl_uint32_bset_remove(&data->multi->dirty, data->mid);
3982
0
}
3983
3984
CURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification)
3985
0
{
3986
0
  struct Curl_multi *multi = m;
3987
3988
0
  if(!GOOD_MULTI_HANDLE(multi))
3989
0
    return CURLM_BAD_HANDLE;
3990
0
  return Curl_mntfy_enable(multi, notification);
3991
0
}
3992
3993
CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification)
3994
0
{
3995
0
  struct Curl_multi *multi = m;
3996
3997
0
  if(!GOOD_MULTI_HANDLE(multi))
3998
0
    return CURLM_BAD_HANDLE;
3999
0
  return Curl_mntfy_disable(multi, notification);
4000
0
}
4001
4002
#ifdef DEBUGBUILD
4003
static void multi_xfer_dump(struct Curl_multi *multi, uint32_t mid,
4004
                            void *entry)
4005
{
4006
  struct Curl_easy *data = entry;
4007
4008
  (void)multi;
4009
  if(!data) {
4010
    curl_mfprintf(stderr, "mid=%u, entry=NULL, bug in xfer table?\n", mid);
4011
  }
4012
  else {
4013
    curl_mfprintf(stderr, "mid=%u, magic=%s, p=%p, id=%" FMT_OFF_T
4014
                  ", url=%s\n",
4015
                  mid,
4016
                  (data->magic == CURLEASY_MAGIC_NUMBER) ? "GOOD" : "BAD!",
4017
                  (void *)data, data->id, Curl_bufref_ptr(&data->state.url));
4018
  }
4019
}
4020
4021
static void multi_xfer_tbl_dump(struct Curl_multi *multi)
4022
{
4023
  uint32_t mid;
4024
  void *entry;
4025
  curl_mfprintf(stderr, "=== multi xfer table (count=%u, capacity=%u\n",
4026
                Curl_uint32_tbl_count(&multi->xfers),
4027
                Curl_uint32_tbl_capacity(&multi->xfers));
4028
  if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) {
4029
    multi_xfer_dump(multi, mid, entry);
4030
    while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry))
4031
      multi_xfer_dump(multi, mid, entry);
4032
  }
4033
  curl_mfprintf(stderr, "===\n");
4034
  fflush(stderr);
4035
}
4036
#endif /* DEBUGBUILD */