Annotation of embedaddon/curl/lib/multi.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2020, 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.haxx.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: ***************************************************************************/
! 22:
! 23: #include "curl_setup.h"
! 24:
! 25: #include <curl/curl.h>
! 26:
! 27: #include "urldata.h"
! 28: #include "transfer.h"
! 29: #include "url.h"
! 30: #include "connect.h"
! 31: #include "progress.h"
! 32: #include "easyif.h"
! 33: #include "share.h"
! 34: #include "psl.h"
! 35: #include "multiif.h"
! 36: #include "sendf.h"
! 37: #include "timeval.h"
! 38: #include "http.h"
! 39: #include "select.h"
! 40: #include "warnless.h"
! 41: #include "speedcheck.h"
! 42: #include "conncache.h"
! 43: #include "multihandle.h"
! 44: #include "sigpipe.h"
! 45: #include "vtls/vtls.h"
! 46: #include "connect.h"
! 47: #include "http_proxy.h"
! 48: #include "http2.h"
! 49: #include "socketpair.h"
! 50: #include "socks.h"
! 51: /* The last 3 #include files should be in this order */
! 52: #include "curl_printf.h"
! 53: #include "curl_memory.h"
! 54: #include "memdebug.h"
! 55:
! 56: /*
! 57: CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
! 58: to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every
! 59: CURL handle takes 45-50 K memory, therefore this 3K are not significant.
! 60: */
! 61: #ifndef CURL_SOCKET_HASH_TABLE_SIZE
! 62: #define CURL_SOCKET_HASH_TABLE_SIZE 911
! 63: #endif
! 64:
! 65: #ifndef CURL_CONNECTION_HASH_SIZE
! 66: #define CURL_CONNECTION_HASH_SIZE 97
! 67: #endif
! 68:
! 69: #define CURL_MULTI_HANDLE 0x000bab1e
! 70:
! 71: #define GOOD_MULTI_HANDLE(x) \
! 72: ((x) && (x)->type == CURL_MULTI_HANDLE)
! 73:
! 74: static CURLMcode singlesocket(struct Curl_multi *multi,
! 75: struct Curl_easy *data);
! 76: static CURLMcode add_next_timeout(struct curltime now,
! 77: struct Curl_multi *multi,
! 78: struct Curl_easy *d);
! 79: static CURLMcode multi_timeout(struct Curl_multi *multi,
! 80: long *timeout_ms);
! 81: static void process_pending_handles(struct Curl_multi *multi);
! 82: static void detach_connnection(struct Curl_easy *data);
! 83:
! 84: #ifdef DEBUGBUILD
! 85: static const char * const statename[]={
! 86: "INIT",
! 87: "CONNECT_PEND",
! 88: "CONNECT",
! 89: "WAITRESOLVE",
! 90: "WAITCONNECT",
! 91: "WAITPROXYCONNECT",
! 92: "SENDPROTOCONNECT",
! 93: "PROTOCONNECT",
! 94: "DO",
! 95: "DOING",
! 96: "DO_MORE",
! 97: "DO_DONE",
! 98: "PERFORM",
! 99: "TOOFAST",
! 100: "DONE",
! 101: "COMPLETED",
! 102: "MSGSENT",
! 103: };
! 104: #endif
! 105:
! 106: /* function pointer called once when switching TO a state */
! 107: typedef void (*init_multistate_func)(struct Curl_easy *data);
! 108:
! 109: static void Curl_init_completed(struct Curl_easy *data)
! 110: {
! 111: /* this is a completed transfer */
! 112:
! 113: /* Important: reset the conn pointer so that we don't point to memory
! 114: that could be freed anytime */
! 115: detach_connnection(data);
! 116: Curl_expire_clear(data); /* stop all timers */
! 117: }
! 118:
! 119: /* always use this function to change state, to make debugging easier */
! 120: static void mstate(struct Curl_easy *data, CURLMstate state
! 121: #ifdef DEBUGBUILD
! 122: , int lineno
! 123: #endif
! 124: )
! 125: {
! 126: CURLMstate oldstate = data->mstate;
! 127: static const init_multistate_func finit[CURLM_STATE_LAST] = {
! 128: NULL, /* INIT */
! 129: NULL, /* CONNECT_PEND */
! 130: Curl_init_CONNECT, /* CONNECT */
! 131: NULL, /* WAITRESOLVE */
! 132: NULL, /* WAITCONNECT */
! 133: NULL, /* WAITPROXYCONNECT */
! 134: NULL, /* SENDPROTOCONNECT */
! 135: NULL, /* PROTOCONNECT */
! 136: Curl_connect_free, /* DO */
! 137: NULL, /* DOING */
! 138: NULL, /* DO_MORE */
! 139: NULL, /* DO_DONE */
! 140: NULL, /* PERFORM */
! 141: NULL, /* TOOFAST */
! 142: NULL, /* DONE */
! 143: Curl_init_completed, /* COMPLETED */
! 144: NULL /* MSGSENT */
! 145: };
! 146:
! 147: #if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS)
! 148: (void) lineno;
! 149: #endif
! 150:
! 151: if(oldstate == state)
! 152: /* don't bother when the new state is the same as the old state */
! 153: return;
! 154:
! 155: data->mstate = state;
! 156:
! 157: #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
! 158: if(data->mstate >= CURLM_STATE_CONNECT_PEND &&
! 159: data->mstate < CURLM_STATE_COMPLETED) {
! 160: long connection_id = -5000;
! 161:
! 162: if(data->conn)
! 163: connection_id = data->conn->connection_id;
! 164:
! 165: infof(data,
! 166: "STATE: %s => %s handle %p; line %d (connection #%ld)\n",
! 167: statename[oldstate], statename[data->mstate],
! 168: (void *)data, lineno, connection_id);
! 169: }
! 170: #endif
! 171:
! 172: if(state == CURLM_STATE_COMPLETED)
! 173: /* changing to COMPLETED means there's one less easy handle 'alive' */
! 174: data->multi->num_alive--;
! 175:
! 176: /* if this state has an init-function, run it */
! 177: if(finit[state])
! 178: finit[state](data);
! 179: }
! 180:
! 181: #ifndef DEBUGBUILD
! 182: #define multistate(x,y) mstate(x,y)
! 183: #else
! 184: #define multistate(x,y) mstate(x,y, __LINE__)
! 185: #endif
! 186:
! 187: /*
! 188: * We add one of these structs to the sockhash for each socket
! 189: */
! 190:
! 191: struct Curl_sh_entry {
! 192: struct curl_hash transfers; /* hash of transfers using this socket */
! 193: unsigned int action; /* what combined action READ/WRITE this socket waits
! 194: for */
! 195: void *socketp; /* settable by users with curl_multi_assign() */
! 196: unsigned int users; /* number of transfers using this */
! 197: unsigned int readers; /* this many transfers want to read */
! 198: unsigned int writers; /* this many transfers want to write */
! 199: };
! 200: /* bits for 'action' having no bits means this socket is not expecting any
! 201: action */
! 202: #define SH_READ 1
! 203: #define SH_WRITE 2
! 204:
! 205: /* look up a given socket in the socket hash, skip invalid sockets */
! 206: static struct Curl_sh_entry *sh_getentry(struct curl_hash *sh,
! 207: curl_socket_t s)
! 208: {
! 209: if(s != CURL_SOCKET_BAD) {
! 210: /* only look for proper sockets */
! 211: return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
! 212: }
! 213: return NULL;
! 214: }
! 215:
! 216: #define TRHASH_SIZE 13
! 217: static size_t trhash(void *key, size_t key_length, size_t slots_num)
! 218: {
! 219: size_t keyval = (size_t)*(struct Curl_easy **)key;
! 220: (void) key_length;
! 221:
! 222: return (keyval % slots_num);
! 223: }
! 224:
! 225: static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
! 226: {
! 227: (void)k1_len;
! 228: (void)k2_len;
! 229:
! 230: return *(struct Curl_easy **)k1 == *(struct Curl_easy **)k2;
! 231: }
! 232:
! 233: static void trhash_dtor(void *nada)
! 234: {
! 235: (void)nada;
! 236: }
! 237:
! 238:
! 239: /* make sure this socket is present in the hash for this handle */
! 240: static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh,
! 241: curl_socket_t s)
! 242: {
! 243: struct Curl_sh_entry *there = sh_getentry(sh, s);
! 244: struct Curl_sh_entry *check;
! 245:
! 246: if(there) {
! 247: /* it is present, return fine */
! 248: return there;
! 249: }
! 250:
! 251: /* not present, add it */
! 252: check = calloc(1, sizeof(struct Curl_sh_entry));
! 253: if(!check)
! 254: return NULL; /* major failure */
! 255:
! 256: if(Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash,
! 257: trhash_compare, trhash_dtor)) {
! 258: free(check);
! 259: return NULL;
! 260: }
! 261:
! 262: /* make/add new hash entry */
! 263: if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
! 264: Curl_hash_destroy(&check->transfers);
! 265: free(check);
! 266: return NULL; /* major failure */
! 267: }
! 268:
! 269: return check; /* things are good in sockhash land */
! 270: }
! 271:
! 272:
! 273: /* delete the given socket + handle from the hash */
! 274: static void sh_delentry(struct Curl_sh_entry *entry,
! 275: struct curl_hash *sh, curl_socket_t s)
! 276: {
! 277: Curl_hash_destroy(&entry->transfers);
! 278:
! 279: /* We remove the hash entry. This will end up in a call to
! 280: sh_freeentry(). */
! 281: Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
! 282: }
! 283:
! 284: /*
! 285: * free a sockhash entry
! 286: */
! 287: static void sh_freeentry(void *freethis)
! 288: {
! 289: struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
! 290:
! 291: free(p);
! 292: }
! 293:
! 294: static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
! 295: {
! 296: (void) k1_len; (void) k2_len;
! 297:
! 298: return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2));
! 299: }
! 300:
! 301: static size_t hash_fd(void *key, size_t key_length, size_t slots_num)
! 302: {
! 303: curl_socket_t fd = *((curl_socket_t *) key);
! 304: (void) key_length;
! 305:
! 306: return (fd % slots_num);
! 307: }
! 308:
! 309: /*
! 310: * sh_init() creates a new socket hash and returns the handle for it.
! 311: *
! 312: * Quote from README.multi_socket:
! 313: *
! 314: * "Some tests at 7000 and 9000 connections showed that the socket hash lookup
! 315: * is somewhat of a bottle neck. Its current implementation may be a bit too
! 316: * limiting. It simply has a fixed-size array, and on each entry in the array
! 317: * it has a linked list with entries. So the hash only checks which list to
! 318: * scan through. The code I had used so for used a list with merely 7 slots
! 319: * (as that is what the DNS hash uses) but with 7000 connections that would
! 320: * make an average of 1000 nodes in each list to run through. I upped that to
! 321: * 97 slots (I believe a prime is suitable) and noticed a significant speed
! 322: * increase. I need to reconsider the hash implementation or use a rather
! 323: * large default value like this. At 9000 connections I was still below 10us
! 324: * per call."
! 325: *
! 326: */
! 327: static int sh_init(struct curl_hash *hash, int hashsize)
! 328: {
! 329: return Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare,
! 330: sh_freeentry);
! 331: }
! 332:
! 333: /*
! 334: * multi_addmsg()
! 335: *
! 336: * Called when a transfer is completed. Adds the given msg pointer to
! 337: * the list kept in the multi handle.
! 338: */
! 339: static CURLMcode multi_addmsg(struct Curl_multi *multi,
! 340: struct Curl_message *msg)
! 341: {
! 342: Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg,
! 343: &msg->list);
! 344: return CURLM_OK;
! 345: }
! 346:
! 347: struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
! 348: int chashsize) /* connection hash */
! 349: {
! 350: struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
! 351:
! 352: if(!multi)
! 353: return NULL;
! 354:
! 355: multi->type = CURL_MULTI_HANDLE;
! 356:
! 357: if(Curl_mk_dnscache(&multi->hostcache))
! 358: goto error;
! 359:
! 360: if(sh_init(&multi->sockhash, hashsize))
! 361: goto error;
! 362:
! 363: if(Curl_conncache_init(&multi->conn_cache, chashsize))
! 364: goto error;
! 365:
! 366: Curl_llist_init(&multi->msglist, NULL);
! 367: Curl_llist_init(&multi->pending, NULL);
! 368:
! 369: multi->multiplexing = TRUE;
! 370:
! 371: /* -1 means it not set by user, use the default value */
! 372: multi->maxconnects = -1;
! 373: multi->max_concurrent_streams = 100;
! 374: multi->ipv6_works = Curl_ipv6works(NULL);
! 375:
! 376: #ifdef ENABLE_WAKEUP
! 377: if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) {
! 378: multi->wakeup_pair[0] = CURL_SOCKET_BAD;
! 379: multi->wakeup_pair[1] = CURL_SOCKET_BAD;
! 380: }
! 381: else if(curlx_nonblock(multi->wakeup_pair[0], TRUE) < 0 ||
! 382: curlx_nonblock(multi->wakeup_pair[1], TRUE) < 0) {
! 383: sclose(multi->wakeup_pair[0]);
! 384: sclose(multi->wakeup_pair[1]);
! 385: multi->wakeup_pair[0] = CURL_SOCKET_BAD;
! 386: multi->wakeup_pair[1] = CURL_SOCKET_BAD;
! 387: }
! 388: #endif
! 389:
! 390: return multi;
! 391:
! 392: error:
! 393:
! 394: Curl_hash_destroy(&multi->sockhash);
! 395: Curl_hash_destroy(&multi->hostcache);
! 396: Curl_conncache_destroy(&multi->conn_cache);
! 397: Curl_llist_destroy(&multi->msglist, NULL);
! 398: Curl_llist_destroy(&multi->pending, NULL);
! 399:
! 400: free(multi);
! 401: return NULL;
! 402: }
! 403:
! 404: struct Curl_multi *curl_multi_init(void)
! 405: {
! 406: return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE,
! 407: CURL_CONNECTION_HASH_SIZE);
! 408: }
! 409:
! 410: CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
! 411: struct Curl_easy *data)
! 412: {
! 413: /* First, make some basic checks that the CURLM handle is a good handle */
! 414: if(!GOOD_MULTI_HANDLE(multi))
! 415: return CURLM_BAD_HANDLE;
! 416:
! 417: /* Verify that we got a somewhat good easy handle too */
! 418: if(!GOOD_EASY_HANDLE(data))
! 419: return CURLM_BAD_EASY_HANDLE;
! 420:
! 421: /* Prevent users from adding same easy handle more than once and prevent
! 422: adding to more than one multi stack */
! 423: if(data->multi)
! 424: return CURLM_ADDED_ALREADY;
! 425:
! 426: if(multi->in_callback)
! 427: return CURLM_RECURSIVE_API_CALL;
! 428:
! 429: /* Initialize timeout list for this handle */
! 430: Curl_llist_init(&data->state.timeoutlist, NULL);
! 431:
! 432: /*
! 433: * No failure allowed in this function beyond this point. And no
! 434: * modification of easy nor multi handle allowed before this except for
! 435: * potential multi's connection cache growing which won't be undone in this
! 436: * function no matter what.
! 437: */
! 438: if(data->set.errorbuffer)
! 439: data->set.errorbuffer[0] = 0;
! 440:
! 441: /* set the easy handle */
! 442: multistate(data, CURLM_STATE_INIT);
! 443:
! 444: /* for multi interface connections, we share DNS cache automatically if the
! 445: easy handle's one is currently not set. */
! 446: if(!data->dns.hostcache ||
! 447: (data->dns.hostcachetype == HCACHE_NONE)) {
! 448: data->dns.hostcache = &multi->hostcache;
! 449: data->dns.hostcachetype = HCACHE_MULTI;
! 450: }
! 451:
! 452: /* Point to the shared or multi handle connection cache */
! 453: if(data->share && (data->share->specifier & (1<< CURL_LOCK_DATA_CONNECT)))
! 454: data->state.conn_cache = &data->share->conn_cache;
! 455: else
! 456: data->state.conn_cache = &multi->conn_cache;
! 457:
! 458: #ifdef USE_LIBPSL
! 459: /* Do the same for PSL. */
! 460: if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL)))
! 461: data->psl = &data->share->psl;
! 462: else
! 463: data->psl = &multi->psl;
! 464: #endif
! 465:
! 466: /* We add the new entry last in the list. */
! 467: data->next = NULL; /* end of the line */
! 468: if(multi->easyp) {
! 469: struct Curl_easy *last = multi->easylp;
! 470: last->next = data;
! 471: data->prev = last;
! 472: multi->easylp = data; /* the new last node */
! 473: }
! 474: else {
! 475: /* first node, make prev NULL! */
! 476: data->prev = NULL;
! 477: multi->easylp = multi->easyp = data; /* both first and last */
! 478: }
! 479:
! 480: /* make the Curl_easy refer back to this multi handle */
! 481: data->multi = multi;
! 482:
! 483: /* Set the timeout for this handle to expire really soon so that it will
! 484: be taken care of even when this handle is added in the midst of operation
! 485: when only the curl_multi_socket() API is used. During that flow, only
! 486: sockets that time-out or have actions will be dealt with. Since this
! 487: handle has no action yet, we make sure it times out to get things to
! 488: happen. */
! 489: Curl_expire(data, 0, EXPIRE_RUN_NOW);
! 490:
! 491: /* increase the node-counter */
! 492: multi->num_easy++;
! 493:
! 494: /* increase the alive-counter */
! 495: multi->num_alive++;
! 496:
! 497: /* A somewhat crude work-around for a little glitch in Curl_update_timer()
! 498: that happens if the lastcall time is set to the same time when the handle
! 499: is removed as when the next handle is added, as then the check in
! 500: Curl_update_timer() that prevents calling the application multiple times
! 501: with the same timer info will not trigger and then the new handle's
! 502: timeout will not be notified to the app.
! 503:
! 504: The work-around is thus simply to clear the 'lastcall' variable to force
! 505: Curl_update_timer() to always trigger a callback to the app when a new
! 506: easy handle is added */
! 507: memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
! 508:
! 509: /* The closure handle only ever has default timeouts set. To improve the
! 510: state somewhat we clone the timeouts from each added handle so that the
! 511: closure handle always has the same timeouts as the most recently added
! 512: easy handle. */
! 513: data->state.conn_cache->closure_handle->set.timeout = data->set.timeout;
! 514: data->state.conn_cache->closure_handle->set.server_response_timeout =
! 515: data->set.server_response_timeout;
! 516: data->state.conn_cache->closure_handle->set.no_signal =
! 517: data->set.no_signal;
! 518:
! 519: Curl_update_timer(multi);
! 520: return CURLM_OK;
! 521: }
! 522:
! 523: #if 0
! 524: /* Debug-function, used like this:
! 525: *
! 526: * Curl_hash_print(multi->sockhash, debug_print_sock_hash);
! 527: *
! 528: * Enable the hash print function first by editing hash.c
! 529: */
! 530: static void debug_print_sock_hash(void *p)
! 531: {
! 532: struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p;
! 533:
! 534: fprintf(stderr, " [easy %p/magic %x/socket %d]",
! 535: (void *)sh->data, sh->data->magic, (int)sh->socket);
! 536: }
! 537: #endif
! 538:
! 539: static CURLcode multi_done(struct Curl_easy *data,
! 540: CURLcode status, /* an error if this is called
! 541: after an error was detected */
! 542: bool premature)
! 543: {
! 544: CURLcode result;
! 545: struct connectdata *conn = data->conn;
! 546: unsigned int i;
! 547:
! 548: DEBUGF(infof(data, "multi_done\n"));
! 549:
! 550: if(data->state.done)
! 551: /* Stop if multi_done() has already been called */
! 552: return CURLE_OK;
! 553:
! 554: conn->data = data; /* ensure the connection uses this transfer now */
! 555:
! 556: /* Stop the resolver and free its own resources (but not dns_entry yet). */
! 557: Curl_resolver_kill(conn);
! 558:
! 559: /* Cleanup possible redirect junk */
! 560: Curl_safefree(data->req.newurl);
! 561: Curl_safefree(data->req.location);
! 562:
! 563: switch(status) {
! 564: case CURLE_ABORTED_BY_CALLBACK:
! 565: case CURLE_READ_ERROR:
! 566: case CURLE_WRITE_ERROR:
! 567: /* When we're aborted due to a callback return code it basically have to
! 568: be counted as premature as there is trouble ahead if we don't. We have
! 569: many callbacks and protocols work differently, we could potentially do
! 570: this more fine-grained in the future. */
! 571: premature = TRUE;
! 572: default:
! 573: break;
! 574: }
! 575:
! 576: /* this calls the protocol-specific function pointer previously set */
! 577: if(conn->handler->done)
! 578: result = conn->handler->done(conn, status, premature);
! 579: else
! 580: result = status;
! 581:
! 582: if(CURLE_ABORTED_BY_CALLBACK != result) {
! 583: /* avoid this if we already aborted by callback to avoid this calling
! 584: another callback */
! 585: CURLcode rc = Curl_pgrsDone(conn);
! 586: if(!result && rc)
! 587: result = CURLE_ABORTED_BY_CALLBACK;
! 588: }
! 589:
! 590: process_pending_handles(data->multi); /* connection / multiplex */
! 591:
! 592: CONN_LOCK(data);
! 593: detach_connnection(data);
! 594: if(CONN_INUSE(conn)) {
! 595: /* Stop if still used. */
! 596: /* conn->data must not remain pointing to this transfer since it is going
! 597: away! Find another to own it! */
! 598: conn->data = conn->easyq.head->ptr;
! 599: CONN_UNLOCK(data);
! 600: DEBUGF(infof(data, "Connection still in use %zu, "
! 601: "no more multi_done now!\n",
! 602: conn->easyq.size));
! 603: return CURLE_OK;
! 604: }
! 605: conn->data = NULL; /* the connection now has no owner */
! 606: data->state.done = TRUE; /* called just now! */
! 607:
! 608: if(conn->dns_entry) {
! 609: Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
! 610: conn->dns_entry = NULL;
! 611: }
! 612: Curl_hostcache_prune(data);
! 613: Curl_safefree(data->state.ulbuf);
! 614:
! 615: /* if the transfer was completed in a paused state there can be buffered
! 616: data left to free */
! 617: for(i = 0; i < data->state.tempcount; i++) {
! 618: free(data->state.tempwrite[i].buf);
! 619: }
! 620: data->state.tempcount = 0;
! 621:
! 622: /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
! 623: forced us to close this connection. This is ignored for requests taking
! 624: place in a NTLM/NEGOTIATE authentication handshake
! 625:
! 626: if conn->bits.close is TRUE, it means that the connection should be
! 627: closed in spite of all our efforts to be nice, due to protocol
! 628: restrictions in our or the server's end
! 629:
! 630: if premature is TRUE, it means this connection was said to be DONE before
! 631: the entire request operation is complete and thus we can't know in what
! 632: state it is for re-using, so we're forced to close it. In a perfect world
! 633: we can add code that keep track of if we really must close it here or not,
! 634: but currently we have no such detail knowledge.
! 635: */
! 636:
! 637: if((data->set.reuse_forbid
! 638: #if defined(USE_NTLM)
! 639: && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 ||
! 640: conn->proxy_ntlm_state == NTLMSTATE_TYPE2)
! 641: #endif
! 642: #if defined(USE_SPNEGO)
! 643: && !(conn->http_negotiate_state == GSS_AUTHRECV ||
! 644: conn->proxy_negotiate_state == GSS_AUTHRECV)
! 645: #endif
! 646: ) || conn->bits.close
! 647: || (premature && !(conn->handler->flags & PROTOPT_STREAM))) {
! 648: CURLcode res2;
! 649: connclose(conn, "disconnecting");
! 650: CONN_UNLOCK(data);
! 651: res2 = Curl_disconnect(data, conn, premature);
! 652:
! 653: /* If we had an error already, make sure we return that one. But
! 654: if we got a new error, return that. */
! 655: if(!result && res2)
! 656: result = res2;
! 657: }
! 658: else {
! 659: char buffer[256];
! 660: /* create string before returning the connection */
! 661: msnprintf(buffer, sizeof(buffer),
! 662: "Connection #%ld to host %s left intact",
! 663: conn->connection_id,
! 664: conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
! 665: conn->bits.httpproxy ? conn->http_proxy.host.dispname :
! 666: conn->bits.conn_to_host ? conn->conn_to_host.dispname :
! 667: conn->host.dispname);
! 668: /* the connection is no longer in use by this transfer */
! 669: CONN_UNLOCK(data);
! 670: if(Curl_conncache_return_conn(data, conn)) {
! 671: /* remember the most recently used connection */
! 672: data->state.lastconnect = conn;
! 673: infof(data, "%s\n", buffer);
! 674: }
! 675: else
! 676: data->state.lastconnect = NULL;
! 677: }
! 678:
! 679: Curl_free_request_state(data);
! 680: return result;
! 681: }
! 682:
! 683: CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
! 684: struct Curl_easy *data)
! 685: {
! 686: struct Curl_easy *easy = data;
! 687: bool premature;
! 688: bool easy_owns_conn;
! 689: struct curl_llist_element *e;
! 690:
! 691: /* First, make some basic checks that the CURLM handle is a good handle */
! 692: if(!GOOD_MULTI_HANDLE(multi))
! 693: return CURLM_BAD_HANDLE;
! 694:
! 695: /* Verify that we got a somewhat good easy handle too */
! 696: if(!GOOD_EASY_HANDLE(data))
! 697: return CURLM_BAD_EASY_HANDLE;
! 698:
! 699: /* Prevent users from trying to remove same easy handle more than once */
! 700: if(!data->multi)
! 701: return CURLM_OK; /* it is already removed so let's say it is fine! */
! 702:
! 703: /* Prevent users from trying to remove an easy handle from the wrong multi */
! 704: if(data->multi != multi)
! 705: return CURLM_BAD_EASY_HANDLE;
! 706:
! 707: if(multi->in_callback)
! 708: return CURLM_RECURSIVE_API_CALL;
! 709:
! 710: premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
! 711: easy_owns_conn = (data->conn && (data->conn->data == easy)) ?
! 712: TRUE : FALSE;
! 713:
! 714: /* If the 'state' is not INIT or COMPLETED, we might need to do something
! 715: nice to put the easy_handle in a good known state when this returns. */
! 716: if(premature) {
! 717: /* this handle is "alive" so we need to count down the total number of
! 718: alive connections when this is removed */
! 719: multi->num_alive--;
! 720: }
! 721:
! 722: if(data->conn &&
! 723: data->mstate > CURLM_STATE_DO &&
! 724: data->mstate < CURLM_STATE_COMPLETED) {
! 725: /* Set connection owner so that the DONE function closes it. We can
! 726: safely do this here since connection is killed. */
! 727: data->conn->data = easy;
! 728: streamclose(data->conn, "Removed with partial response");
! 729: easy_owns_conn = TRUE;
! 730: }
! 731:
! 732: if(data->conn) {
! 733:
! 734: /* we must call multi_done() here (if we still own the connection) so that
! 735: we don't leave a half-baked one around */
! 736: if(easy_owns_conn) {
! 737:
! 738: /* multi_done() clears the association between the easy handle and the
! 739: connection.
! 740:
! 741: Note that this ignores the return code simply because there's
! 742: nothing really useful to do with it anyway! */
! 743: (void)multi_done(data, data->result, premature);
! 744: }
! 745: }
! 746:
! 747: /* The timer must be shut down before data->multi is set to NULL, else the
! 748: timenode will remain in the splay tree after curl_easy_cleanup is
! 749: called. Do it after multi_done() in case that sets another time! */
! 750: Curl_expire_clear(data);
! 751:
! 752: if(data->connect_queue.ptr)
! 753: /* the handle was in the pending list waiting for an available connection,
! 754: so go ahead and remove it */
! 755: Curl_llist_remove(&multi->pending, &data->connect_queue, NULL);
! 756:
! 757: if(data->dns.hostcachetype == HCACHE_MULTI) {
! 758: /* stop using the multi handle's DNS cache, *after* the possible
! 759: multi_done() call above */
! 760: data->dns.hostcache = NULL;
! 761: data->dns.hostcachetype = HCACHE_NONE;
! 762: }
! 763:
! 764: Curl_wildcard_dtor(&data->wildcard);
! 765:
! 766: /* destroy the timeout list that is held in the easy handle, do this *after*
! 767: multi_done() as that may actually call Curl_expire that uses this */
! 768: Curl_llist_destroy(&data->state.timeoutlist, NULL);
! 769:
! 770: /* as this was using a shared connection cache we clear the pointer to that
! 771: since we're not part of that multi handle anymore */
! 772: data->state.conn_cache = NULL;
! 773:
! 774: /* change state without using multistate(), only to make singlesocket() do
! 775: what we want */
! 776: data->mstate = CURLM_STATE_COMPLETED;
! 777: singlesocket(multi, easy); /* to let the application know what sockets that
! 778: vanish with this handle */
! 779:
! 780: /* Remove the association between the connection and the handle */
! 781: if(data->conn)
! 782: detach_connnection(data);
! 783:
! 784: #ifdef USE_LIBPSL
! 785: /* Remove the PSL association. */
! 786: if(data->psl == &multi->psl)
! 787: data->psl = NULL;
! 788: #endif
! 789:
! 790: data->multi = NULL; /* clear the association to this multi handle */
! 791:
! 792: /* make sure there's no pending message in the queue sent from this easy
! 793: handle */
! 794:
! 795: for(e = multi->msglist.head; e; e = e->next) {
! 796: struct Curl_message *msg = e->ptr;
! 797:
! 798: if(msg->extmsg.easy_handle == easy) {
! 799: Curl_llist_remove(&multi->msglist, e, NULL);
! 800: /* there can only be one from this specific handle */
! 801: break;
! 802: }
! 803: }
! 804:
! 805: /* make the previous node point to our next */
! 806: if(data->prev)
! 807: data->prev->next = data->next;
! 808: else
! 809: multi->easyp = data->next; /* point to first node */
! 810:
! 811: /* make our next point to our previous node */
! 812: if(data->next)
! 813: data->next->prev = data->prev;
! 814: else
! 815: multi->easylp = data->prev; /* point to last node */
! 816:
! 817: /* NOTE NOTE NOTE
! 818: We do not touch the easy handle here! */
! 819: multi->num_easy--; /* one less to care about now */
! 820:
! 821: Curl_update_timer(multi);
! 822: return CURLM_OK;
! 823: }
! 824:
! 825: /* Return TRUE if the application asked for multiplexing */
! 826: bool Curl_multiplex_wanted(const struct Curl_multi *multi)
! 827: {
! 828: return (multi && (multi->multiplexing));
! 829: }
! 830:
! 831: /* This is the only function that should clear data->conn. This will
! 832: occasionally be called with the pointer already cleared. */
! 833: static void detach_connnection(struct Curl_easy *data)
! 834: {
! 835: struct connectdata *conn = data->conn;
! 836: if(conn)
! 837: Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
! 838: data->conn = NULL;
! 839: }
! 840:
! 841: /* This is the only function that should assign data->conn */
! 842: void Curl_attach_connnection(struct Curl_easy *data,
! 843: struct connectdata *conn)
! 844: {
! 845: DEBUGASSERT(!data->conn);
! 846: DEBUGASSERT(conn);
! 847: data->conn = conn;
! 848: Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data,
! 849: &data->conn_queue);
! 850: }
! 851:
! 852: static int waitconnect_getsock(struct connectdata *conn,
! 853: curl_socket_t *sock)
! 854: {
! 855: int i;
! 856: int s = 0;
! 857: int rc = 0;
! 858:
! 859: #ifdef USE_SSL
! 860: if(CONNECT_FIRSTSOCKET_PROXY_SSL())
! 861: return Curl_ssl_getsock(conn, sock);
! 862: #endif
! 863:
! 864: if(SOCKS_STATE(conn->cnnct.state))
! 865: return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET);
! 866:
! 867: for(i = 0; i<2; i++) {
! 868: if(conn->tempsock[i] != CURL_SOCKET_BAD) {
! 869: sock[s] = conn->tempsock[i];
! 870: rc |= GETSOCK_WRITESOCK(s);
! 871: #ifdef ENABLE_QUIC
! 872: if(conn->transport == TRNSPRT_QUIC)
! 873: /* when connecting QUIC, we want to read the socket too */
! 874: rc |= GETSOCK_READSOCK(s);
! 875: #endif
! 876: s++;
! 877: }
! 878: }
! 879:
! 880: return rc;
! 881: }
! 882:
! 883: static int waitproxyconnect_getsock(struct connectdata *conn,
! 884: curl_socket_t *sock)
! 885: {
! 886: sock[0] = conn->sock[FIRSTSOCKET];
! 887:
! 888: /* when we've sent a CONNECT to a proxy, we should rather wait for the
! 889: socket to become readable to be able to get the response headers */
! 890: if(conn->connect_state)
! 891: return GETSOCK_READSOCK(0);
! 892:
! 893: return GETSOCK_WRITESOCK(0);
! 894: }
! 895:
! 896: static int domore_getsock(struct connectdata *conn,
! 897: curl_socket_t *socks)
! 898: {
! 899: if(conn && conn->handler->domore_getsock)
! 900: return conn->handler->domore_getsock(conn, socks);
! 901: return GETSOCK_BLANK;
! 902: }
! 903:
! 904: static int doing_getsock(struct connectdata *conn,
! 905: curl_socket_t *socks)
! 906: {
! 907: if(conn && conn->handler->doing_getsock)
! 908: return conn->handler->doing_getsock(conn, socks);
! 909: return GETSOCK_BLANK;
! 910: }
! 911:
! 912: static int protocol_getsock(struct connectdata *conn,
! 913: curl_socket_t *socks)
! 914: {
! 915: if(conn->handler->proto_getsock)
! 916: return conn->handler->proto_getsock(conn, socks);
! 917: /* Backup getsock logic. Since there is a live socket in use, we must wait
! 918: for it or it will be removed from watching when the multi_socket API is
! 919: used. */
! 920: socks[0] = conn->sock[FIRSTSOCKET];
! 921: return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
! 922: }
! 923:
! 924: /* returns bitmapped flags for this handle and its sockets. The 'socks[]'
! 925: array contains MAX_SOCKSPEREASYHANDLE entries. */
! 926: static int multi_getsock(struct Curl_easy *data,
! 927: curl_socket_t *socks)
! 928: {
! 929: /* The no connection case can happen when this is called from
! 930: curl_multi_remove_handle() => singlesocket() => multi_getsock().
! 931: */
! 932: if(!data->conn)
! 933: return 0;
! 934:
! 935: if(data->mstate > CURLM_STATE_CONNECT &&
! 936: data->mstate < CURLM_STATE_COMPLETED) {
! 937: /* Set up ownership correctly */
! 938: data->conn->data = data;
! 939: }
! 940:
! 941: switch(data->mstate) {
! 942: default:
! 943: #if 0 /* switch back on these cases to get the compiler to check for all enums
! 944: to be present */
! 945: case CURLM_STATE_TOOFAST: /* returns 0, so will not select. */
! 946: case CURLM_STATE_COMPLETED:
! 947: case CURLM_STATE_MSGSENT:
! 948: case CURLM_STATE_INIT:
! 949: case CURLM_STATE_CONNECT:
! 950: case CURLM_STATE_WAITDO:
! 951: case CURLM_STATE_DONE:
! 952: case CURLM_STATE_LAST:
! 953: /* this will get called with CURLM_STATE_COMPLETED when a handle is
! 954: removed */
! 955: #endif
! 956: return 0;
! 957:
! 958: case CURLM_STATE_WAITRESOLVE:
! 959: return Curl_resolv_getsock(data->conn, socks);
! 960:
! 961: case CURLM_STATE_PROTOCONNECT:
! 962: case CURLM_STATE_SENDPROTOCONNECT:
! 963: return protocol_getsock(data->conn, socks);
! 964:
! 965: case CURLM_STATE_DO:
! 966: case CURLM_STATE_DOING:
! 967: return doing_getsock(data->conn, socks);
! 968:
! 969: case CURLM_STATE_WAITPROXYCONNECT:
! 970: return waitproxyconnect_getsock(data->conn, socks);
! 971:
! 972: case CURLM_STATE_WAITCONNECT:
! 973: return waitconnect_getsock(data->conn, socks);
! 974:
! 975: case CURLM_STATE_DO_MORE:
! 976: return domore_getsock(data->conn, socks);
! 977:
! 978: case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch
! 979: to waiting for the same as the *PERFORM
! 980: states */
! 981: case CURLM_STATE_PERFORM:
! 982: return Curl_single_getsock(data->conn, socks);
! 983: }
! 984:
! 985: }
! 986:
! 987: CURLMcode curl_multi_fdset(struct Curl_multi *multi,
! 988: fd_set *read_fd_set, fd_set *write_fd_set,
! 989: fd_set *exc_fd_set, int *max_fd)
! 990: {
! 991: /* Scan through all the easy handles to get the file descriptors set.
! 992: Some easy handles may not have connected to the remote host yet,
! 993: and then we must make sure that is done. */
! 994: struct Curl_easy *data;
! 995: int this_max_fd = -1;
! 996: curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
! 997: int i;
! 998: (void)exc_fd_set; /* not used */
! 999:
! 1000: if(!GOOD_MULTI_HANDLE(multi))
! 1001: return CURLM_BAD_HANDLE;
! 1002:
! 1003: if(multi->in_callback)
! 1004: return CURLM_RECURSIVE_API_CALL;
! 1005:
! 1006: data = multi->easyp;
! 1007: while(data) {
! 1008: int bitmap = multi_getsock(data, sockbunch);
! 1009:
! 1010: for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
! 1011: curl_socket_t s = CURL_SOCKET_BAD;
! 1012:
! 1013: if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
! 1014: FD_SET(sockbunch[i], read_fd_set);
! 1015: s = sockbunch[i];
! 1016: }
! 1017: if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
! 1018: FD_SET(sockbunch[i], write_fd_set);
! 1019: s = sockbunch[i];
! 1020: }
! 1021: if(s == CURL_SOCKET_BAD)
! 1022: /* this socket is unused, break out of loop */
! 1023: break;
! 1024: if((int)s > this_max_fd)
! 1025: this_max_fd = (int)s;
! 1026: }
! 1027:
! 1028: data = data->next; /* check next handle */
! 1029: }
! 1030:
! 1031: *max_fd = this_max_fd;
! 1032:
! 1033: return CURLM_OK;
! 1034: }
! 1035:
! 1036: #define NUM_POLLS_ON_STACK 10
! 1037:
! 1038: static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
! 1039: struct curl_waitfd extra_fds[],
! 1040: unsigned int extra_nfds,
! 1041: int timeout_ms,
! 1042: int *ret,
! 1043: bool extrawait, /* when no socket, wait */
! 1044: bool use_wakeup)
! 1045: {
! 1046: struct Curl_easy *data;
! 1047: curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
! 1048: int bitmap;
! 1049: unsigned int i;
! 1050: unsigned int nfds = 0;
! 1051: unsigned int curlfds;
! 1052: bool ufds_malloc = FALSE;
! 1053: long timeout_internal;
! 1054: int retcode = 0;
! 1055: struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
! 1056: struct pollfd *ufds = &a_few_on_stack[0];
! 1057:
! 1058: if(!GOOD_MULTI_HANDLE(multi))
! 1059: return CURLM_BAD_HANDLE;
! 1060:
! 1061: if(multi->in_callback)
! 1062: return CURLM_RECURSIVE_API_CALL;
! 1063:
! 1064: if(timeout_ms < 0)
! 1065: return CURLM_BAD_FUNCTION_ARGUMENT;
! 1066:
! 1067: /* Count up how many fds we have from the multi handle */
! 1068: data = multi->easyp;
! 1069: while(data) {
! 1070: bitmap = multi_getsock(data, sockbunch);
! 1071:
! 1072: for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
! 1073: curl_socket_t s = CURL_SOCKET_BAD;
! 1074:
! 1075: if(bitmap & GETSOCK_READSOCK(i)) {
! 1076: ++nfds;
! 1077: s = sockbunch[i];
! 1078: }
! 1079: if(bitmap & GETSOCK_WRITESOCK(i)) {
! 1080: ++nfds;
! 1081: s = sockbunch[i];
! 1082: }
! 1083: if(s == CURL_SOCKET_BAD) {
! 1084: break;
! 1085: }
! 1086: }
! 1087:
! 1088: data = data->next; /* check next handle */
! 1089: }
! 1090:
! 1091: /* If the internally desired timeout is actually shorter than requested from
! 1092: the outside, then use the shorter time! But only if the internal timer
! 1093: is actually larger than -1! */
! 1094: (void)multi_timeout(multi, &timeout_internal);
! 1095: if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
! 1096: timeout_ms = (int)timeout_internal;
! 1097:
! 1098: curlfds = nfds; /* number of internal file descriptors */
! 1099: nfds += extra_nfds; /* add the externally provided ones */
! 1100:
! 1101: #ifdef ENABLE_WAKEUP
! 1102: if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
! 1103: ++nfds;
! 1104: }
! 1105: #endif
! 1106:
! 1107: if(nfds > NUM_POLLS_ON_STACK) {
! 1108: /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
! 1109: big, so at 2^29 sockets this value might wrap. When a process gets
! 1110: the capability to actually handle over 500 million sockets this
! 1111: calculation needs a integer overflow check. */
! 1112: ufds = malloc(nfds * sizeof(struct pollfd));
! 1113: if(!ufds)
! 1114: return CURLM_OUT_OF_MEMORY;
! 1115: ufds_malloc = TRUE;
! 1116: }
! 1117: nfds = 0;
! 1118:
! 1119: /* only do the second loop if we found descriptors in the first stage run
! 1120: above */
! 1121:
! 1122: if(curlfds) {
! 1123: /* Add the curl handles to our pollfds first */
! 1124: data = multi->easyp;
! 1125: while(data) {
! 1126: bitmap = multi_getsock(data, sockbunch);
! 1127:
! 1128: for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
! 1129: curl_socket_t s = CURL_SOCKET_BAD;
! 1130:
! 1131: if(bitmap & GETSOCK_READSOCK(i)) {
! 1132: ufds[nfds].fd = sockbunch[i];
! 1133: ufds[nfds].events = POLLIN;
! 1134: ++nfds;
! 1135: s = sockbunch[i];
! 1136: }
! 1137: if(bitmap & GETSOCK_WRITESOCK(i)) {
! 1138: ufds[nfds].fd = sockbunch[i];
! 1139: ufds[nfds].events = POLLOUT;
! 1140: ++nfds;
! 1141: s = sockbunch[i];
! 1142: }
! 1143: if(s == CURL_SOCKET_BAD) {
! 1144: break;
! 1145: }
! 1146: }
! 1147:
! 1148: data = data->next; /* check next handle */
! 1149: }
! 1150: }
! 1151:
! 1152: /* Add external file descriptions from poll-like struct curl_waitfd */
! 1153: for(i = 0; i < extra_nfds; i++) {
! 1154: ufds[nfds].fd = extra_fds[i].fd;
! 1155: ufds[nfds].events = 0;
! 1156: if(extra_fds[i].events & CURL_WAIT_POLLIN)
! 1157: ufds[nfds].events |= POLLIN;
! 1158: if(extra_fds[i].events & CURL_WAIT_POLLPRI)
! 1159: ufds[nfds].events |= POLLPRI;
! 1160: if(extra_fds[i].events & CURL_WAIT_POLLOUT)
! 1161: ufds[nfds].events |= POLLOUT;
! 1162: ++nfds;
! 1163: }
! 1164:
! 1165: #ifdef ENABLE_WAKEUP
! 1166: if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
! 1167: ufds[nfds].fd = multi->wakeup_pair[0];
! 1168: ufds[nfds].events = POLLIN;
! 1169: ++nfds;
! 1170: }
! 1171: #endif
! 1172:
! 1173: if(nfds) {
! 1174: int pollrc;
! 1175: /* wait... */
! 1176: pollrc = Curl_poll(ufds, nfds, timeout_ms);
! 1177:
! 1178: if(pollrc > 0) {
! 1179: retcode = pollrc;
! 1180: /* copy revents results from the poll to the curl_multi_wait poll
! 1181: struct, the bit values of the actual underlying poll() implementation
! 1182: may not be the same as the ones in the public libcurl API! */
! 1183: for(i = 0; i < extra_nfds; i++) {
! 1184: unsigned short mask = 0;
! 1185: unsigned r = ufds[curlfds + i].revents;
! 1186:
! 1187: if(r & POLLIN)
! 1188: mask |= CURL_WAIT_POLLIN;
! 1189: if(r & POLLOUT)
! 1190: mask |= CURL_WAIT_POLLOUT;
! 1191: if(r & POLLPRI)
! 1192: mask |= CURL_WAIT_POLLPRI;
! 1193:
! 1194: extra_fds[i].revents = mask;
! 1195: }
! 1196:
! 1197: #ifdef ENABLE_WAKEUP
! 1198: if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
! 1199: if(ufds[curlfds + extra_nfds].revents & POLLIN) {
! 1200: char buf[64];
! 1201: ssize_t nread;
! 1202: while(1) {
! 1203: /* the reading socket is non-blocking, try to read
! 1204: data from it until it receives an error (except EINTR).
! 1205: In normal cases it will get EAGAIN or EWOULDBLOCK
! 1206: when there is no more data, breaking the loop. */
! 1207: nread = sread(multi->wakeup_pair[0], buf, sizeof(buf));
! 1208: if(nread <= 0) {
! 1209: #ifndef USE_WINSOCK
! 1210: if(nread < 0 && EINTR == SOCKERRNO)
! 1211: continue;
! 1212: #endif
! 1213: break;
! 1214: }
! 1215: }
! 1216: /* do not count the wakeup socket into the returned value */
! 1217: retcode--;
! 1218: }
! 1219: }
! 1220: #endif
! 1221: }
! 1222: }
! 1223:
! 1224: if(ufds_malloc)
! 1225: free(ufds);
! 1226: if(ret)
! 1227: *ret = retcode;
! 1228: if(!extrawait || nfds)
! 1229: /* if any socket was checked */
! 1230: ;
! 1231: else {
! 1232: long sleep_ms = 0;
! 1233:
! 1234: /* Avoid busy-looping when there's nothing particular to wait for */
! 1235: if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) {
! 1236: if(sleep_ms > timeout_ms)
! 1237: sleep_ms = timeout_ms;
! 1238: /* when there are no easy handles in the multi, this holds a -1
! 1239: timeout */
! 1240: else if((sleep_ms < 0) && extrawait)
! 1241: sleep_ms = timeout_ms;
! 1242: Curl_wait_ms((int)sleep_ms);
! 1243: }
! 1244: }
! 1245:
! 1246: return CURLM_OK;
! 1247: }
! 1248:
! 1249: CURLMcode curl_multi_wait(struct Curl_multi *multi,
! 1250: struct curl_waitfd extra_fds[],
! 1251: unsigned int extra_nfds,
! 1252: int timeout_ms,
! 1253: int *ret)
! 1254: {
! 1255: return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE,
! 1256: FALSE);
! 1257: }
! 1258:
! 1259: CURLMcode curl_multi_poll(struct Curl_multi *multi,
! 1260: struct curl_waitfd extra_fds[],
! 1261: unsigned int extra_nfds,
! 1262: int timeout_ms,
! 1263: int *ret)
! 1264: {
! 1265: return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE,
! 1266: TRUE);
! 1267: }
! 1268:
! 1269: CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
! 1270: {
! 1271: /* this function is usually called from another thread,
! 1272: it has to be careful only to access parts of the
! 1273: Curl_multi struct that are constant */
! 1274:
! 1275: /* GOOD_MULTI_HANDLE can be safely called */
! 1276: if(!GOOD_MULTI_HANDLE(multi))
! 1277: return CURLM_BAD_HANDLE;
! 1278:
! 1279: #ifdef ENABLE_WAKEUP
! 1280: /* the wakeup_pair variable is only written during init and cleanup,
! 1281: making it safe to access from another thread after the init part
! 1282: and before cleanup */
! 1283: if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
! 1284: char buf[1];
! 1285: buf[0] = 1;
! 1286: while(1) {
! 1287: /* swrite() is not thread-safe in general, because concurrent calls
! 1288: can have their messages interleaved, but in this case the content
! 1289: of the messages does not matter, which makes it ok to call.
! 1290:
! 1291: The write socket is set to non-blocking, this way this function
! 1292: cannot block, making it safe to call even from the same thread
! 1293: that will call Curl_multi_wait(). If swrite() returns that it
! 1294: would block, it's considered successful because it means that
! 1295: previous calls to this function will wake up the poll(). */
! 1296: if(swrite(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) {
! 1297: int err = SOCKERRNO;
! 1298: int return_success;
! 1299: #ifdef USE_WINSOCK
! 1300: return_success = WSAEWOULDBLOCK == err;
! 1301: #else
! 1302: if(EINTR == err)
! 1303: continue;
! 1304: return_success = EWOULDBLOCK == err || EAGAIN == err;
! 1305: #endif
! 1306: if(!return_success)
! 1307: return CURLM_WAKEUP_FAILURE;
! 1308: }
! 1309: return CURLM_OK;
! 1310: }
! 1311: }
! 1312: #endif
! 1313: return CURLM_WAKEUP_FAILURE;
! 1314: }
! 1315:
! 1316: /*
! 1317: * multi_ischanged() is called
! 1318: *
! 1319: * Returns TRUE/FALSE whether the state is changed to trigger a CONNECT_PEND
! 1320: * => CONNECT action.
! 1321: *
! 1322: * Set 'clear' to TRUE to have it also clear the state variable.
! 1323: */
! 1324: static bool multi_ischanged(struct Curl_multi *multi, bool clear)
! 1325: {
! 1326: bool retval = multi->recheckstate;
! 1327: if(clear)
! 1328: multi->recheckstate = FALSE;
! 1329: return retval;
! 1330: }
! 1331:
! 1332: CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
! 1333: struct Curl_easy *data,
! 1334: struct connectdata *conn)
! 1335: {
! 1336: CURLMcode rc;
! 1337:
! 1338: if(multi->in_callback)
! 1339: return CURLM_RECURSIVE_API_CALL;
! 1340:
! 1341: rc = curl_multi_add_handle(multi, data);
! 1342: if(!rc) {
! 1343: struct SingleRequest *k = &data->req;
! 1344:
! 1345: /* pass in NULL for 'conn' here since we don't want to init the
! 1346: connection, only this transfer */
! 1347: Curl_init_do(data, NULL);
! 1348:
! 1349: /* take this handle to the perform state right away */
! 1350: multistate(data, CURLM_STATE_PERFORM);
! 1351: Curl_attach_connnection(data, conn);
! 1352: k->keepon |= KEEP_RECV; /* setup to receive! */
! 1353: }
! 1354: return rc;
! 1355: }
! 1356:
! 1357: /*
! 1358: * do_complete is called when the DO actions are complete.
! 1359: *
! 1360: * We init chunking and trailer bits to their default values here immediately
! 1361: * before receiving any header data for the current request.
! 1362: */
! 1363: static void do_complete(struct connectdata *conn)
! 1364: {
! 1365: conn->data->req.chunk = FALSE;
! 1366: Curl_pgrsTime(conn->data, TIMER_PRETRANSFER);
! 1367: }
! 1368:
! 1369: static CURLcode multi_do(struct Curl_easy *data, bool *done)
! 1370: {
! 1371: CURLcode result = CURLE_OK;
! 1372: struct connectdata *conn = data->conn;
! 1373:
! 1374: DEBUGASSERT(conn);
! 1375: DEBUGASSERT(conn->handler);
! 1376: DEBUGASSERT(conn->data == data);
! 1377:
! 1378: if(conn->handler->do_it) {
! 1379: /* generic protocol-specific function pointer set in curl_connect() */
! 1380: result = conn->handler->do_it(conn, done);
! 1381:
! 1382: if(!result && *done)
! 1383: /* do_complete must be called after the protocol-specific DO function */
! 1384: do_complete(conn);
! 1385: }
! 1386: return result;
! 1387: }
! 1388:
! 1389: /*
! 1390: * multi_do_more() is called during the DO_MORE multi state. It is basically a
! 1391: * second stage DO state which (wrongly) was introduced to support FTP's
! 1392: * second connection.
! 1393: *
! 1394: * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to
! 1395: * DOING state there's more work to do!
! 1396: */
! 1397:
! 1398: static CURLcode multi_do_more(struct connectdata *conn, int *complete)
! 1399: {
! 1400: CURLcode result = CURLE_OK;
! 1401:
! 1402: *complete = 0;
! 1403:
! 1404: if(conn->handler->do_more)
! 1405: result = conn->handler->do_more(conn, complete);
! 1406:
! 1407: if(!result && (*complete == 1))
! 1408: /* do_complete must be called after the protocol-specific DO function */
! 1409: do_complete(conn);
! 1410:
! 1411: return result;
! 1412: }
! 1413:
! 1414: /*
! 1415: * We are doing protocol-specific connecting and this is being called over and
! 1416: * over from the multi interface until the connection phase is done on
! 1417: * protocol layer.
! 1418: */
! 1419:
! 1420: static CURLcode protocol_connecting(struct connectdata *conn,
! 1421: bool *done)
! 1422: {
! 1423: CURLcode result = CURLE_OK;
! 1424:
! 1425: if(conn && conn->handler->connecting) {
! 1426: *done = FALSE;
! 1427: result = conn->handler->connecting(conn, done);
! 1428: }
! 1429: else
! 1430: *done = TRUE;
! 1431:
! 1432: return result;
! 1433: }
! 1434:
! 1435: /*
! 1436: * We are DOING this is being called over and over from the multi interface
! 1437: * until the DOING phase is done on protocol layer.
! 1438: */
! 1439:
! 1440: static CURLcode protocol_doing(struct connectdata *conn, bool *done)
! 1441: {
! 1442: CURLcode result = CURLE_OK;
! 1443:
! 1444: if(conn && conn->handler->doing) {
! 1445: *done = FALSE;
! 1446: result = conn->handler->doing(conn, done);
! 1447: }
! 1448: else
! 1449: *done = TRUE;
! 1450:
! 1451: return result;
! 1452: }
! 1453:
! 1454: /*
! 1455: * We have discovered that the TCP connection has been successful, we can now
! 1456: * proceed with some action.
! 1457: *
! 1458: */
! 1459: static CURLcode protocol_connect(struct connectdata *conn,
! 1460: bool *protocol_done)
! 1461: {
! 1462: CURLcode result = CURLE_OK;
! 1463:
! 1464: DEBUGASSERT(conn);
! 1465: DEBUGASSERT(protocol_done);
! 1466:
! 1467: *protocol_done = FALSE;
! 1468:
! 1469: if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) {
! 1470: /* We already are connected, get back. This may happen when the connect
! 1471: worked fine in the first call, like when we connect to a local server
! 1472: or proxy. Note that we don't know if the protocol is actually done.
! 1473:
! 1474: Unless this protocol doesn't have any protocol-connect callback, as
! 1475: then we know we're done. */
! 1476: if(!conn->handler->connecting)
! 1477: *protocol_done = TRUE;
! 1478:
! 1479: return CURLE_OK;
! 1480: }
! 1481:
! 1482: if(!conn->bits.protoconnstart) {
! 1483:
! 1484: result = Curl_proxy_connect(conn, FIRSTSOCKET);
! 1485: if(result)
! 1486: return result;
! 1487:
! 1488: if(CONNECT_FIRSTSOCKET_PROXY_SSL())
! 1489: /* wait for HTTPS proxy SSL initialization to complete */
! 1490: return CURLE_OK;
! 1491:
! 1492: if(conn->bits.tunnel_proxy && conn->bits.httpproxy &&
! 1493: Curl_connect_ongoing(conn))
! 1494: /* when using an HTTP tunnel proxy, await complete tunnel establishment
! 1495: before proceeding further. Return CURLE_OK so we'll be called again */
! 1496: return CURLE_OK;
! 1497:
! 1498: if(conn->handler->connect_it) {
! 1499: /* is there a protocol-specific connect() procedure? */
! 1500:
! 1501: /* Call the protocol-specific connect function */
! 1502: result = conn->handler->connect_it(conn, protocol_done);
! 1503: }
! 1504: else
! 1505: *protocol_done = TRUE;
! 1506:
! 1507: /* it has started, possibly even completed but that knowledge isn't stored
! 1508: in this bit! */
! 1509: if(!result)
! 1510: conn->bits.protoconnstart = TRUE;
! 1511: }
! 1512:
! 1513: return result; /* pass back status */
! 1514: }
! 1515:
! 1516:
! 1517: static CURLMcode multi_runsingle(struct Curl_multi *multi,
! 1518: struct curltime now,
! 1519: struct Curl_easy *data)
! 1520: {
! 1521: struct Curl_message *msg = NULL;
! 1522: bool connected;
! 1523: bool async;
! 1524: bool protocol_connected = FALSE;
! 1525: bool dophase_done = FALSE;
! 1526: bool done = FALSE;
! 1527: CURLMcode rc;
! 1528: CURLcode result = CURLE_OK;
! 1529: timediff_t timeout_ms;
! 1530: timediff_t recv_timeout_ms;
! 1531: timediff_t send_timeout_ms;
! 1532: int control;
! 1533:
! 1534: if(!GOOD_EASY_HANDLE(data))
! 1535: return CURLM_BAD_EASY_HANDLE;
! 1536:
! 1537: do {
! 1538: /* A "stream" here is a logical stream if the protocol can handle that
! 1539: (HTTP/2), or the full connection for older protocols */
! 1540: bool stream_error = FALSE;
! 1541: rc = CURLM_OK;
! 1542:
! 1543: DEBUGASSERT((data->mstate <= CURLM_STATE_CONNECT) ||
! 1544: (data->mstate >= CURLM_STATE_DONE) ||
! 1545: data->conn);
! 1546: if(!data->conn &&
! 1547: data->mstate > CURLM_STATE_CONNECT &&
! 1548: data->mstate < CURLM_STATE_DONE) {
! 1549: /* In all these states, the code will blindly access 'data->conn'
! 1550: so this is precaution that it isn't NULL. And it silences static
! 1551: analyzers. */
! 1552: failf(data, "In state %d with no conn, bail out!\n", data->mstate);
! 1553: return CURLM_INTERNAL_ERROR;
! 1554: }
! 1555:
! 1556: if(multi_ischanged(multi, TRUE)) {
! 1557: DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n"));
! 1558: process_pending_handles(multi); /* multiplexed */
! 1559: }
! 1560:
! 1561: if(data->conn && data->mstate > CURLM_STATE_CONNECT &&
! 1562: data->mstate < CURLM_STATE_COMPLETED) {
! 1563: /* Make sure we set the connection's current owner */
! 1564: data->conn->data = data;
! 1565: }
! 1566:
! 1567: if(data->conn &&
! 1568: (data->mstate >= CURLM_STATE_CONNECT) &&
! 1569: (data->mstate < CURLM_STATE_COMPLETED)) {
! 1570: /* we need to wait for the connect state as only then is the start time
! 1571: stored, but we must not check already completed handles */
! 1572: timeout_ms = Curl_timeleft(data, &now,
! 1573: (data->mstate <= CURLM_STATE_DO)?
! 1574: TRUE:FALSE);
! 1575:
! 1576: if(timeout_ms < 0) {
! 1577: /* Handle timed out */
! 1578: if(data->mstate == CURLM_STATE_WAITRESOLVE)
! 1579: failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
! 1580: " milliseconds",
! 1581: Curl_timediff(now, data->progress.t_startsingle));
! 1582: else if(data->mstate == CURLM_STATE_WAITCONNECT)
! 1583: failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
! 1584: " milliseconds",
! 1585: Curl_timediff(now, data->progress.t_startsingle));
! 1586: else {
! 1587: struct SingleRequest *k = &data->req;
! 1588: if(k->size != -1) {
! 1589: failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
! 1590: " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
! 1591: CURL_FORMAT_CURL_OFF_T " bytes received",
! 1592: Curl_timediff(now, data->progress.t_startsingle),
! 1593: k->bytecount, k->size);
! 1594: }
! 1595: else {
! 1596: failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
! 1597: " milliseconds with %" CURL_FORMAT_CURL_OFF_T
! 1598: " bytes received",
! 1599: Curl_timediff(now, data->progress.t_startsingle),
! 1600: k->bytecount);
! 1601: }
! 1602: }
! 1603:
! 1604: /* Force connection closed if the connection has indeed been used */
! 1605: if(data->mstate > CURLM_STATE_DO) {
! 1606: streamclose(data->conn, "Disconnected with pending data");
! 1607: stream_error = TRUE;
! 1608: }
! 1609: result = CURLE_OPERATION_TIMEDOUT;
! 1610: (void)multi_done(data, result, TRUE);
! 1611: /* Skip the statemachine and go directly to error handling section. */
! 1612: goto statemachine_end;
! 1613: }
! 1614: }
! 1615:
! 1616: switch(data->mstate) {
! 1617: case CURLM_STATE_INIT:
! 1618: /* init this transfer. */
! 1619: result = Curl_pretransfer(data);
! 1620:
! 1621: if(!result) {
! 1622: /* after init, go CONNECT */
! 1623: multistate(data, CURLM_STATE_CONNECT);
! 1624: Curl_pgrsTime(data, TIMER_STARTOP);
! 1625: rc = CURLM_CALL_MULTI_PERFORM;
! 1626: }
! 1627: break;
! 1628:
! 1629: case CURLM_STATE_CONNECT_PEND:
! 1630: /* We will stay here until there is a connection available. Then
! 1631: we try again in the CURLM_STATE_CONNECT state. */
! 1632: break;
! 1633:
! 1634: case CURLM_STATE_CONNECT:
! 1635: /* Connect. We want to get a connection identifier filled in. */
! 1636: Curl_pgrsTime(data, TIMER_STARTSINGLE);
! 1637: if(data->set.timeout)
! 1638: Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
! 1639:
! 1640: if(data->set.connecttimeout)
! 1641: Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
! 1642:
! 1643: result = Curl_connect(data, &async, &protocol_connected);
! 1644: if(CURLE_NO_CONNECTION_AVAILABLE == result) {
! 1645: /* There was no connection available. We will go to the pending
! 1646: state and wait for an available connection. */
! 1647: multistate(data, CURLM_STATE_CONNECT_PEND);
! 1648:
! 1649: /* add this handle to the list of connect-pending handles */
! 1650: Curl_llist_insert_next(&multi->pending, multi->pending.tail, data,
! 1651: &data->connect_queue);
! 1652: result = CURLE_OK;
! 1653: break;
! 1654: }
! 1655: else if(data->state.previouslypending) {
! 1656: /* this transfer comes from the pending queue so try move another */
! 1657: infof(data, "Transfer was pending, now try another\n");
! 1658: process_pending_handles(data->multi);
! 1659: }
! 1660:
! 1661: if(!result) {
! 1662: if(async)
! 1663: /* We're now waiting for an asynchronous name lookup */
! 1664: multistate(data, CURLM_STATE_WAITRESOLVE);
! 1665: else {
! 1666: /* after the connect has been sent off, go WAITCONNECT unless the
! 1667: protocol connect is already done and we can go directly to
! 1668: WAITDO or DO! */
! 1669: rc = CURLM_CALL_MULTI_PERFORM;
! 1670:
! 1671: if(protocol_connected)
! 1672: multistate(data, CURLM_STATE_DO);
! 1673: else {
! 1674: #ifndef CURL_DISABLE_HTTP
! 1675: if(Curl_connect_ongoing(data->conn))
! 1676: multistate(data, CURLM_STATE_WAITPROXYCONNECT);
! 1677: else
! 1678: #endif
! 1679: multistate(data, CURLM_STATE_WAITCONNECT);
! 1680: }
! 1681: }
! 1682: }
! 1683: break;
! 1684:
! 1685: case CURLM_STATE_WAITRESOLVE:
! 1686: /* awaiting an asynch name resolve to complete */
! 1687: {
! 1688: struct Curl_dns_entry *dns = NULL;
! 1689: struct connectdata *conn = data->conn;
! 1690: const char *hostname;
! 1691:
! 1692: DEBUGASSERT(conn);
! 1693: if(conn->bits.httpproxy)
! 1694: hostname = conn->http_proxy.host.name;
! 1695: else if(conn->bits.conn_to_host)
! 1696: hostname = conn->conn_to_host.name;
! 1697: else
! 1698: hostname = conn->host.name;
! 1699:
! 1700: /* check if we have the name resolved by now */
! 1701: dns = Curl_fetch_addr(conn, hostname, (int)conn->port);
! 1702:
! 1703: if(dns) {
! 1704: #ifdef CURLRES_ASYNCH
! 1705: conn->async.dns = dns;
! 1706: conn->async.done = TRUE;
! 1707: #endif
! 1708: result = CURLE_OK;
! 1709: infof(data, "Hostname '%s' was found in DNS cache\n", hostname);
! 1710: }
! 1711:
! 1712: if(!dns)
! 1713: result = Curl_resolv_check(data->conn, &dns);
! 1714:
! 1715: /* Update sockets here, because the socket(s) may have been
! 1716: closed and the application thus needs to be told, even if it
! 1717: is likely that the same socket(s) will again be used further
! 1718: down. If the name has not yet been resolved, it is likely
! 1719: that new sockets have been opened in an attempt to contact
! 1720: another resolver. */
! 1721: singlesocket(multi, data);
! 1722:
! 1723: if(dns) {
! 1724: /* Perform the next step in the connection phase, and then move on
! 1725: to the WAITCONNECT state */
! 1726: result = Curl_once_resolved(data->conn, &protocol_connected);
! 1727:
! 1728: if(result)
! 1729: /* if Curl_once_resolved() returns failure, the connection struct
! 1730: is already freed and gone */
! 1731: data->conn = NULL; /* no more connection */
! 1732: else {
! 1733: /* call again please so that we get the next socket setup */
! 1734: rc = CURLM_CALL_MULTI_PERFORM;
! 1735: if(protocol_connected)
! 1736: multistate(data, CURLM_STATE_DO);
! 1737: else {
! 1738: #ifndef CURL_DISABLE_HTTP
! 1739: if(Curl_connect_ongoing(data->conn))
! 1740: multistate(data, CURLM_STATE_WAITPROXYCONNECT);
! 1741: else
! 1742: #endif
! 1743: multistate(data, CURLM_STATE_WAITCONNECT);
! 1744: }
! 1745: }
! 1746: }
! 1747:
! 1748: if(result) {
! 1749: /* failure detected */
! 1750: stream_error = TRUE;
! 1751: break;
! 1752: }
! 1753: }
! 1754: break;
! 1755:
! 1756: #ifndef CURL_DISABLE_HTTP
! 1757: case CURLM_STATE_WAITPROXYCONNECT:
! 1758: /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
! 1759: DEBUGASSERT(data->conn);
! 1760: result = Curl_http_connect(data->conn, &protocol_connected);
! 1761:
! 1762: if(data->conn->bits.proxy_connect_closed) {
! 1763: rc = CURLM_CALL_MULTI_PERFORM;
! 1764: /* connect back to proxy again */
! 1765: result = CURLE_OK;
! 1766: multi_done(data, CURLE_OK, FALSE);
! 1767: multistate(data, CURLM_STATE_CONNECT);
! 1768: }
! 1769: else if(!result) {
! 1770: if((data->conn->http_proxy.proxytype != CURLPROXY_HTTPS ||
! 1771: data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) &&
! 1772: Curl_connect_complete(data->conn)) {
! 1773: rc = CURLM_CALL_MULTI_PERFORM;
! 1774: /* initiate protocol connect phase */
! 1775: multistate(data, CURLM_STATE_SENDPROTOCONNECT);
! 1776: }
! 1777: }
! 1778: else if(result)
! 1779: stream_error = TRUE;
! 1780: break;
! 1781: #endif
! 1782:
! 1783: case CURLM_STATE_WAITCONNECT:
! 1784: /* awaiting a completion of an asynch TCP connect */
! 1785: DEBUGASSERT(data->conn);
! 1786: result = Curl_is_connected(data->conn, FIRSTSOCKET, &connected);
! 1787: if(connected && !result) {
! 1788: #ifndef CURL_DISABLE_HTTP
! 1789: if((data->conn->http_proxy.proxytype == CURLPROXY_HTTPS &&
! 1790: !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) ||
! 1791: Curl_connect_ongoing(data->conn)) {
! 1792: multistate(data, CURLM_STATE_WAITPROXYCONNECT);
! 1793: break;
! 1794: }
! 1795: #endif
! 1796: rc = CURLM_CALL_MULTI_PERFORM;
! 1797: multistate(data, data->conn->bits.tunnel_proxy?
! 1798: CURLM_STATE_WAITPROXYCONNECT:
! 1799: CURLM_STATE_SENDPROTOCONNECT);
! 1800: }
! 1801: else if(result) {
! 1802: /* failure detected */
! 1803: Curl_posttransfer(data);
! 1804: multi_done(data, result, TRUE);
! 1805: stream_error = TRUE;
! 1806: break;
! 1807: }
! 1808: break;
! 1809:
! 1810: case CURLM_STATE_SENDPROTOCONNECT:
! 1811: result = protocol_connect(data->conn, &protocol_connected);
! 1812: if(!result && !protocol_connected)
! 1813: /* switch to waiting state */
! 1814: multistate(data, CURLM_STATE_PROTOCONNECT);
! 1815: else if(!result) {
! 1816: /* protocol connect has completed, go WAITDO or DO */
! 1817: multistate(data, CURLM_STATE_DO);
! 1818: rc = CURLM_CALL_MULTI_PERFORM;
! 1819: }
! 1820: else if(result) {
! 1821: /* failure detected */
! 1822: Curl_posttransfer(data);
! 1823: multi_done(data, result, TRUE);
! 1824: stream_error = TRUE;
! 1825: }
! 1826: break;
! 1827:
! 1828: case CURLM_STATE_PROTOCONNECT:
! 1829: /* protocol-specific connect phase */
! 1830: result = protocol_connecting(data->conn, &protocol_connected);
! 1831: if(!result && protocol_connected) {
! 1832: /* after the connect has completed, go WAITDO or DO */
! 1833: multistate(data, CURLM_STATE_DO);
! 1834: rc = CURLM_CALL_MULTI_PERFORM;
! 1835: }
! 1836: else if(result) {
! 1837: /* failure detected */
! 1838: Curl_posttransfer(data);
! 1839: multi_done(data, result, TRUE);
! 1840: stream_error = TRUE;
! 1841: }
! 1842: break;
! 1843:
! 1844: case CURLM_STATE_DO:
! 1845: if(data->set.connect_only) {
! 1846: /* keep connection open for application to use the socket */
! 1847: connkeep(data->conn, "CONNECT_ONLY");
! 1848: multistate(data, CURLM_STATE_DONE);
! 1849: result = CURLE_OK;
! 1850: rc = CURLM_CALL_MULTI_PERFORM;
! 1851: }
! 1852: else {
! 1853: /* Perform the protocol's DO action */
! 1854: result = multi_do(data, &dophase_done);
! 1855:
! 1856: /* When multi_do() returns failure, data->conn might be NULL! */
! 1857:
! 1858: if(!result) {
! 1859: if(!dophase_done) {
! 1860: #ifndef CURL_DISABLE_FTP
! 1861: /* some steps needed for wildcard matching */
! 1862: if(data->state.wildcardmatch) {
! 1863: struct WildcardData *wc = &data->wildcard;
! 1864: if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
! 1865: /* skip some states if it is important */
! 1866: multi_done(data, CURLE_OK, FALSE);
! 1867: multistate(data, CURLM_STATE_DONE);
! 1868: rc = CURLM_CALL_MULTI_PERFORM;
! 1869: break;
! 1870: }
! 1871: }
! 1872: #endif
! 1873: /* DO was not completed in one function call, we must continue
! 1874: DOING... */
! 1875: multistate(data, CURLM_STATE_DOING);
! 1876: rc = CURLM_OK;
! 1877: }
! 1878:
! 1879: /* after DO, go DO_DONE... or DO_MORE */
! 1880: else if(data->conn->bits.do_more) {
! 1881: /* we're supposed to do more, but we need to sit down, relax
! 1882: and wait a little while first */
! 1883: multistate(data, CURLM_STATE_DO_MORE);
! 1884: rc = CURLM_OK;
! 1885: }
! 1886: else {
! 1887: /* we're done with the DO, now DO_DONE */
! 1888: multistate(data, CURLM_STATE_DO_DONE);
! 1889: rc = CURLM_CALL_MULTI_PERFORM;
! 1890: }
! 1891: }
! 1892: else if((CURLE_SEND_ERROR == result) &&
! 1893: data->conn->bits.reuse) {
! 1894: /*
! 1895: * In this situation, a connection that we were trying to use
! 1896: * may have unexpectedly died. If possible, send the connection
! 1897: * back to the CONNECT phase so we can try again.
! 1898: */
! 1899: char *newurl = NULL;
! 1900: followtype follow = FOLLOW_NONE;
! 1901: CURLcode drc;
! 1902:
! 1903: drc = Curl_retry_request(data->conn, &newurl);
! 1904: if(drc) {
! 1905: /* a failure here pretty much implies an out of memory */
! 1906: result = drc;
! 1907: stream_error = TRUE;
! 1908: }
! 1909:
! 1910: Curl_posttransfer(data);
! 1911: drc = multi_done(data, result, FALSE);
! 1912:
! 1913: /* When set to retry the connection, we must to go back to
! 1914: * the CONNECT state */
! 1915: if(newurl) {
! 1916: if(!drc || (drc == CURLE_SEND_ERROR)) {
! 1917: follow = FOLLOW_RETRY;
! 1918: drc = Curl_follow(data, newurl, follow);
! 1919: if(!drc) {
! 1920: multistate(data, CURLM_STATE_CONNECT);
! 1921: rc = CURLM_CALL_MULTI_PERFORM;
! 1922: result = CURLE_OK;
! 1923: }
! 1924: else {
! 1925: /* Follow failed */
! 1926: result = drc;
! 1927: }
! 1928: }
! 1929: else {
! 1930: /* done didn't return OK or SEND_ERROR */
! 1931: result = drc;
! 1932: }
! 1933: }
! 1934: else {
! 1935: /* Have error handler disconnect conn if we can't retry */
! 1936: stream_error = TRUE;
! 1937: }
! 1938: free(newurl);
! 1939: }
! 1940: else {
! 1941: /* failure detected */
! 1942: Curl_posttransfer(data);
! 1943: if(data->conn)
! 1944: multi_done(data, result, FALSE);
! 1945: stream_error = TRUE;
! 1946: }
! 1947: }
! 1948: break;
! 1949:
! 1950: case CURLM_STATE_DOING:
! 1951: /* we continue DOING until the DO phase is complete */
! 1952: DEBUGASSERT(data->conn);
! 1953: result = protocol_doing(data->conn, &dophase_done);
! 1954: if(!result) {
! 1955: if(dophase_done) {
! 1956: /* after DO, go DO_DONE or DO_MORE */
! 1957: multistate(data, data->conn->bits.do_more?
! 1958: CURLM_STATE_DO_MORE:
! 1959: CURLM_STATE_DO_DONE);
! 1960: rc = CURLM_CALL_MULTI_PERFORM;
! 1961: } /* dophase_done */
! 1962: }
! 1963: else {
! 1964: /* failure detected */
! 1965: Curl_posttransfer(data);
! 1966: multi_done(data, result, FALSE);
! 1967: stream_error = TRUE;
! 1968: }
! 1969: break;
! 1970:
! 1971: case CURLM_STATE_DO_MORE:
! 1972: /*
! 1973: * When we are connected, DO MORE and then go DO_DONE
! 1974: */
! 1975: DEBUGASSERT(data->conn);
! 1976: result = multi_do_more(data->conn, &control);
! 1977:
! 1978: if(!result) {
! 1979: if(control) {
! 1980: /* if positive, advance to DO_DONE
! 1981: if negative, go back to DOING */
! 1982: multistate(data, control == 1?
! 1983: CURLM_STATE_DO_DONE:
! 1984: CURLM_STATE_DOING);
! 1985: rc = CURLM_CALL_MULTI_PERFORM;
! 1986: }
! 1987: else
! 1988: /* stay in DO_MORE */
! 1989: rc = CURLM_OK;
! 1990: }
! 1991: else {
! 1992: /* failure detected */
! 1993: Curl_posttransfer(data);
! 1994: multi_done(data, result, FALSE);
! 1995: stream_error = TRUE;
! 1996: }
! 1997: break;
! 1998:
! 1999: case CURLM_STATE_DO_DONE:
! 2000: DEBUGASSERT(data->conn);
! 2001: if(data->conn->bits.multiplex)
! 2002: /* Check if we can move pending requests to send pipe */
! 2003: process_pending_handles(multi); /* multiplexed */
! 2004:
! 2005: /* Only perform the transfer if there's a good socket to work with.
! 2006: Having both BAD is a signal to skip immediately to DONE */
! 2007: if((data->conn->sockfd != CURL_SOCKET_BAD) ||
! 2008: (data->conn->writesockfd != CURL_SOCKET_BAD))
! 2009: multistate(data, CURLM_STATE_PERFORM);
! 2010: else {
! 2011: #ifndef CURL_DISABLE_FTP
! 2012: if(data->state.wildcardmatch &&
! 2013: ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) {
! 2014: data->wildcard.state = CURLWC_DONE;
! 2015: }
! 2016: #endif
! 2017: multistate(data, CURLM_STATE_DONE);
! 2018: }
! 2019: rc = CURLM_CALL_MULTI_PERFORM;
! 2020: break;
! 2021:
! 2022: case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
! 2023: DEBUGASSERT(data->conn);
! 2024: /* if both rates are within spec, resume transfer */
! 2025: if(Curl_pgrsUpdate(data->conn))
! 2026: result = CURLE_ABORTED_BY_CALLBACK;
! 2027: else
! 2028: result = Curl_speedcheck(data, now);
! 2029:
! 2030: if(!result) {
! 2031: send_timeout_ms = 0;
! 2032: if(data->set.max_send_speed > 0)
! 2033: send_timeout_ms =
! 2034: Curl_pgrsLimitWaitTime(data->progress.uploaded,
! 2035: data->progress.ul_limit_size,
! 2036: data->set.max_send_speed,
! 2037: data->progress.ul_limit_start,
! 2038: now);
! 2039:
! 2040: recv_timeout_ms = 0;
! 2041: if(data->set.max_recv_speed > 0)
! 2042: recv_timeout_ms =
! 2043: Curl_pgrsLimitWaitTime(data->progress.downloaded,
! 2044: data->progress.dl_limit_size,
! 2045: data->set.max_recv_speed,
! 2046: data->progress.dl_limit_start,
! 2047: now);
! 2048:
! 2049: if(!send_timeout_ms && !recv_timeout_ms) {
! 2050: multistate(data, CURLM_STATE_PERFORM);
! 2051: Curl_ratelimit(data, now);
! 2052: }
! 2053: else if(send_timeout_ms >= recv_timeout_ms)
! 2054: Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST);
! 2055: else
! 2056: Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST);
! 2057: }
! 2058: break;
! 2059:
! 2060: case CURLM_STATE_PERFORM:
! 2061: {
! 2062: char *newurl = NULL;
! 2063: bool retry = FALSE;
! 2064: bool comeback = FALSE;
! 2065:
! 2066: /* check if over send speed */
! 2067: send_timeout_ms = 0;
! 2068: if(data->set.max_send_speed > 0)
! 2069: send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded,
! 2070: data->progress.ul_limit_size,
! 2071: data->set.max_send_speed,
! 2072: data->progress.ul_limit_start,
! 2073: now);
! 2074:
! 2075: /* check if over recv speed */
! 2076: recv_timeout_ms = 0;
! 2077: if(data->set.max_recv_speed > 0)
! 2078: recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded,
! 2079: data->progress.dl_limit_size,
! 2080: data->set.max_recv_speed,
! 2081: data->progress.dl_limit_start,
! 2082: now);
! 2083:
! 2084: if(send_timeout_ms || recv_timeout_ms) {
! 2085: Curl_ratelimit(data, now);
! 2086: multistate(data, CURLM_STATE_TOOFAST);
! 2087: if(send_timeout_ms >= recv_timeout_ms)
! 2088: Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST);
! 2089: else
! 2090: Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST);
! 2091: break;
! 2092: }
! 2093:
! 2094: /* read/write data if it is ready to do so */
! 2095: result = Curl_readwrite(data->conn, data, &done, &comeback);
! 2096:
! 2097: if(done || (result == CURLE_RECV_ERROR)) {
! 2098: /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
! 2099: * condition and the server closed the re-used connection exactly when
! 2100: * we wanted to use it, so figure out if that is indeed the case.
! 2101: */
! 2102: CURLcode ret = Curl_retry_request(data->conn, &newurl);
! 2103: if(!ret)
! 2104: retry = (newurl)?TRUE:FALSE;
! 2105: else if(!result)
! 2106: result = ret;
! 2107:
! 2108: if(retry) {
! 2109: /* if we are to retry, set the result to OK and consider the
! 2110: request as done */
! 2111: result = CURLE_OK;
! 2112: done = TRUE;
! 2113: }
! 2114: }
! 2115: else if((CURLE_HTTP2_STREAM == result) &&
! 2116: Curl_h2_http_1_1_error(data->conn)) {
! 2117: CURLcode ret = Curl_retry_request(data->conn, &newurl);
! 2118:
! 2119: if(!ret) {
! 2120: infof(data, "Downgrades to HTTP/1.1!\n");
! 2121: data->set.httpversion = CURL_HTTP_VERSION_1_1;
! 2122: /* clear the error message bit too as we ignore the one we got */
! 2123: data->state.errorbuf = FALSE;
! 2124: if(!newurl)
! 2125: /* typically for HTTP_1_1_REQUIRED error on first flight */
! 2126: newurl = strdup(data->change.url);
! 2127: /* if we are to retry, set the result to OK and consider the request
! 2128: as done */
! 2129: retry = TRUE;
! 2130: result = CURLE_OK;
! 2131: done = TRUE;
! 2132: }
! 2133: else
! 2134: result = ret;
! 2135: }
! 2136:
! 2137: if(result) {
! 2138: /*
! 2139: * The transfer phase returned error, we mark the connection to get
! 2140: * closed to prevent being re-used. This is because we can't possibly
! 2141: * know if the connection is in a good shape or not now. Unless it is
! 2142: * a protocol which uses two "channels" like FTP, as then the error
! 2143: * happened in the data connection.
! 2144: */
! 2145:
! 2146: if(!(data->conn->handler->flags & PROTOPT_DUAL) &&
! 2147: result != CURLE_HTTP2_STREAM)
! 2148: streamclose(data->conn, "Transfer returned error");
! 2149:
! 2150: Curl_posttransfer(data);
! 2151: multi_done(data, result, TRUE);
! 2152: }
! 2153: else if(done) {
! 2154: followtype follow = FOLLOW_NONE;
! 2155:
! 2156: /* call this even if the readwrite function returned error */
! 2157: Curl_posttransfer(data);
! 2158:
! 2159: /* When we follow redirects or is set to retry the connection, we must
! 2160: to go back to the CONNECT state */
! 2161: if(data->req.newurl || retry) {
! 2162: if(!retry) {
! 2163: /* if the URL is a follow-location and not just a retried request
! 2164: then figure out the URL here */
! 2165: free(newurl);
! 2166: newurl = data->req.newurl;
! 2167: data->req.newurl = NULL;
! 2168: follow = FOLLOW_REDIR;
! 2169: }
! 2170: else
! 2171: follow = FOLLOW_RETRY;
! 2172: (void)multi_done(data, CURLE_OK, FALSE);
! 2173: /* multi_done() might return CURLE_GOT_NOTHING */
! 2174: result = Curl_follow(data, newurl, follow);
! 2175: if(!result) {
! 2176: multistate(data, CURLM_STATE_CONNECT);
! 2177: rc = CURLM_CALL_MULTI_PERFORM;
! 2178: }
! 2179: free(newurl);
! 2180: }
! 2181: else {
! 2182: /* after the transfer is done, go DONE */
! 2183:
! 2184: /* but first check to see if we got a location info even though we're
! 2185: not following redirects */
! 2186: if(data->req.location) {
! 2187: free(newurl);
! 2188: newurl = data->req.location;
! 2189: data->req.location = NULL;
! 2190: result = Curl_follow(data, newurl, FOLLOW_FAKE);
! 2191: free(newurl);
! 2192: if(result) {
! 2193: stream_error = TRUE;
! 2194: result = multi_done(data, result, TRUE);
! 2195: }
! 2196: }
! 2197:
! 2198: if(!result) {
! 2199: multistate(data, CURLM_STATE_DONE);
! 2200: rc = CURLM_CALL_MULTI_PERFORM;
! 2201: }
! 2202: }
! 2203: }
! 2204: else if(comeback) {
! 2205: /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer
! 2206: won't get stuck on this transfer at the expense of other concurrent
! 2207: transfers */
! 2208: Curl_expire(data, 0, EXPIRE_RUN_NOW);
! 2209: rc = CURLM_OK;
! 2210: }
! 2211: break;
! 2212: }
! 2213:
! 2214: case CURLM_STATE_DONE:
! 2215: /* this state is highly transient, so run another loop after this */
! 2216: rc = CURLM_CALL_MULTI_PERFORM;
! 2217:
! 2218: if(data->conn) {
! 2219: CURLcode res;
! 2220:
! 2221: if(data->conn->bits.multiplex)
! 2222: /* Check if we can move pending requests to connection */
! 2223: process_pending_handles(multi); /* multiplexing */
! 2224:
! 2225: /* post-transfer command */
! 2226: res = multi_done(data, result, FALSE);
! 2227:
! 2228: /* allow a previously set error code take precedence */
! 2229: if(!result)
! 2230: result = res;
! 2231:
! 2232: /*
! 2233: * If there are other handles on the connection, multi_done won't set
! 2234: * conn to NULL. In such a case, curl_multi_remove_handle() can
! 2235: * access free'd data, if the connection is free'd and the handle
! 2236: * removed before we perform the processing in CURLM_STATE_COMPLETED
! 2237: */
! 2238: if(data->conn)
! 2239: detach_connnection(data);
! 2240: }
! 2241:
! 2242: #ifndef CURL_DISABLE_FTP
! 2243: if(data->state.wildcardmatch) {
! 2244: if(data->wildcard.state != CURLWC_DONE) {
! 2245: /* if a wildcard is set and we are not ending -> lets start again
! 2246: with CURLM_STATE_INIT */
! 2247: multistate(data, CURLM_STATE_INIT);
! 2248: break;
! 2249: }
! 2250: }
! 2251: #endif
! 2252: /* after we have DONE what we're supposed to do, go COMPLETED, and
! 2253: it doesn't matter what the multi_done() returned! */
! 2254: multistate(data, CURLM_STATE_COMPLETED);
! 2255: break;
! 2256:
! 2257: case CURLM_STATE_COMPLETED:
! 2258: break;
! 2259:
! 2260: case CURLM_STATE_MSGSENT:
! 2261: data->result = result;
! 2262: return CURLM_OK; /* do nothing */
! 2263:
! 2264: default:
! 2265: return CURLM_INTERNAL_ERROR;
! 2266: }
! 2267: statemachine_end:
! 2268:
! 2269: if(data->mstate < CURLM_STATE_COMPLETED) {
! 2270: if(result) {
! 2271: /*
! 2272: * If an error was returned, and we aren't in completed state now,
! 2273: * then we go to completed and consider this transfer aborted.
! 2274: */
! 2275:
! 2276: /* NOTE: no attempt to disconnect connections must be made
! 2277: in the case blocks above - cleanup happens only here */
! 2278:
! 2279: /* Check if we can move pending requests to send pipe */
! 2280: process_pending_handles(multi); /* connection */
! 2281:
! 2282: if(data->conn) {
! 2283: if(stream_error) {
! 2284: /* Don't attempt to send data over a connection that timed out */
! 2285: bool dead_connection = result == CURLE_OPERATION_TIMEDOUT;
! 2286: struct connectdata *conn = data->conn;
! 2287:
! 2288: /* This is where we make sure that the conn pointer is reset.
! 2289: We don't have to do this in every case block above where a
! 2290: failure is detected */
! 2291: detach_connnection(data);
! 2292:
! 2293: /* disconnect properly */
! 2294: Curl_disconnect(data, conn, dead_connection);
! 2295: }
! 2296: }
! 2297: else if(data->mstate == CURLM_STATE_CONNECT) {
! 2298: /* Curl_connect() failed */
! 2299: (void)Curl_posttransfer(data);
! 2300: }
! 2301:
! 2302: multistate(data, CURLM_STATE_COMPLETED);
! 2303: rc = CURLM_CALL_MULTI_PERFORM;
! 2304: }
! 2305: /* if there's still a connection to use, call the progress function */
! 2306: else if(data->conn && Curl_pgrsUpdate(data->conn)) {
! 2307: /* aborted due to progress callback return code must close the
! 2308: connection */
! 2309: result = CURLE_ABORTED_BY_CALLBACK;
! 2310: streamclose(data->conn, "Aborted by callback");
! 2311:
! 2312: /* if not yet in DONE state, go there, otherwise COMPLETED */
! 2313: multistate(data, (data->mstate < CURLM_STATE_DONE)?
! 2314: CURLM_STATE_DONE: CURLM_STATE_COMPLETED);
! 2315: rc = CURLM_CALL_MULTI_PERFORM;
! 2316: }
! 2317: }
! 2318:
! 2319: if(CURLM_STATE_COMPLETED == data->mstate) {
! 2320: if(data->set.fmultidone) {
! 2321: /* signal via callback instead */
! 2322: data->set.fmultidone(data, result);
! 2323: }
! 2324: else {
! 2325: /* now fill in the Curl_message with this info */
! 2326: msg = &data->msg;
! 2327:
! 2328: msg->extmsg.msg = CURLMSG_DONE;
! 2329: msg->extmsg.easy_handle = data;
! 2330: msg->extmsg.data.result = result;
! 2331:
! 2332: rc = multi_addmsg(multi, msg);
! 2333: DEBUGASSERT(!data->conn);
! 2334: }
! 2335: multistate(data, CURLM_STATE_MSGSENT);
! 2336: }
! 2337: } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
! 2338:
! 2339: data->result = result;
! 2340: return rc;
! 2341: }
! 2342:
! 2343:
! 2344: CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
! 2345: {
! 2346: struct Curl_easy *data;
! 2347: CURLMcode returncode = CURLM_OK;
! 2348: struct Curl_tree *t;
! 2349: struct curltime now = Curl_now();
! 2350:
! 2351: if(!GOOD_MULTI_HANDLE(multi))
! 2352: return CURLM_BAD_HANDLE;
! 2353:
! 2354: if(multi->in_callback)
! 2355: return CURLM_RECURSIVE_API_CALL;
! 2356:
! 2357: data = multi->easyp;
! 2358: while(data) {
! 2359: CURLMcode result;
! 2360: SIGPIPE_VARIABLE(pipe_st);
! 2361:
! 2362: sigpipe_ignore(data, &pipe_st);
! 2363: result = multi_runsingle(multi, now, data);
! 2364: sigpipe_restore(&pipe_st);
! 2365:
! 2366: if(result)
! 2367: returncode = result;
! 2368:
! 2369: data = data->next; /* operate on next handle */
! 2370: }
! 2371:
! 2372: /*
! 2373: * Simply remove all expired timers from the splay since handles are dealt
! 2374: * with unconditionally by this function and curl_multi_timeout() requires
! 2375: * that already passed/handled expire times are removed from the splay.
! 2376: *
! 2377: * It is important that the 'now' value is set at the entry of this function
! 2378: * and not for the current time as it may have ticked a little while since
! 2379: * then and then we risk this loop to remove timers that actually have not
! 2380: * been handled!
! 2381: */
! 2382: do {
! 2383: multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
! 2384: if(t)
! 2385: /* the removed may have another timeout in queue */
! 2386: (void)add_next_timeout(now, multi, t->payload);
! 2387:
! 2388: } while(t);
! 2389:
! 2390: *running_handles = multi->num_alive;
! 2391:
! 2392: if(CURLM_OK >= returncode)
! 2393: Curl_update_timer(multi);
! 2394:
! 2395: return returncode;
! 2396: }
! 2397:
! 2398: CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
! 2399: {
! 2400: struct Curl_easy *data;
! 2401: struct Curl_easy *nextdata;
! 2402:
! 2403: if(GOOD_MULTI_HANDLE(multi)) {
! 2404: if(multi->in_callback)
! 2405: return CURLM_RECURSIVE_API_CALL;
! 2406:
! 2407: multi->type = 0; /* not good anymore */
! 2408:
! 2409: /* Firsrt remove all remaining easy handles */
! 2410: data = multi->easyp;
! 2411: while(data) {
! 2412: nextdata = data->next;
! 2413: if(!data->state.done && data->conn)
! 2414: /* if DONE was never called for this handle */
! 2415: (void)multi_done(data, CURLE_OK, TRUE);
! 2416: if(data->dns.hostcachetype == HCACHE_MULTI) {
! 2417: /* clear out the usage of the shared DNS cache */
! 2418: Curl_hostcache_clean(data, data->dns.hostcache);
! 2419: data->dns.hostcache = NULL;
! 2420: data->dns.hostcachetype = HCACHE_NONE;
! 2421: }
! 2422:
! 2423: /* Clear the pointer to the connection cache */
! 2424: data->state.conn_cache = NULL;
! 2425: data->multi = NULL; /* clear the association */
! 2426:
! 2427: #ifdef USE_LIBPSL
! 2428: if(data->psl == &multi->psl)
! 2429: data->psl = NULL;
! 2430: #endif
! 2431:
! 2432: data = nextdata;
! 2433: }
! 2434:
! 2435: /* Close all the connections in the connection cache */
! 2436: Curl_conncache_close_all_connections(&multi->conn_cache);
! 2437:
! 2438: Curl_hash_destroy(&multi->sockhash);
! 2439: Curl_conncache_destroy(&multi->conn_cache);
! 2440: Curl_llist_destroy(&multi->msglist, NULL);
! 2441: Curl_llist_destroy(&multi->pending, NULL);
! 2442:
! 2443: Curl_hash_destroy(&multi->hostcache);
! 2444: Curl_psl_destroy(&multi->psl);
! 2445:
! 2446: #ifdef ENABLE_WAKEUP
! 2447: sclose(multi->wakeup_pair[0]);
! 2448: sclose(multi->wakeup_pair[1]);
! 2449: #endif
! 2450: free(multi);
! 2451:
! 2452: return CURLM_OK;
! 2453: }
! 2454: return CURLM_BAD_HANDLE;
! 2455: }
! 2456:
! 2457: /*
! 2458: * curl_multi_info_read()
! 2459: *
! 2460: * This function is the primary way for a multi/multi_socket application to
! 2461: * figure out if a transfer has ended. We MUST make this function as fast as
! 2462: * possible as it will be polled frequently and we MUST NOT scan any lists in
! 2463: * here to figure out things. We must scale fine to thousands of handles and
! 2464: * beyond. The current design is fully O(1).
! 2465: */
! 2466:
! 2467: CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue)
! 2468: {
! 2469: struct Curl_message *msg;
! 2470:
! 2471: *msgs_in_queue = 0; /* default to none */
! 2472:
! 2473: if(GOOD_MULTI_HANDLE(multi) &&
! 2474: !multi->in_callback &&
! 2475: Curl_llist_count(&multi->msglist)) {
! 2476: /* there is one or more messages in the list */
! 2477: struct curl_llist_element *e;
! 2478:
! 2479: /* extract the head of the list to return */
! 2480: e = multi->msglist.head;
! 2481:
! 2482: msg = e->ptr;
! 2483:
! 2484: /* remove the extracted entry */
! 2485: Curl_llist_remove(&multi->msglist, e, NULL);
! 2486:
! 2487: *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist));
! 2488:
! 2489: return &msg->extmsg;
! 2490: }
! 2491: return NULL;
! 2492: }
! 2493:
! 2494: /*
! 2495: * singlesocket() checks what sockets we deal with and their "action state"
! 2496: * and if we have a different state in any of those sockets from last time we
! 2497: * call the callback accordingly.
! 2498: */
! 2499: static CURLMcode singlesocket(struct Curl_multi *multi,
! 2500: struct Curl_easy *data)
! 2501: {
! 2502: curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
! 2503: int i;
! 2504: struct Curl_sh_entry *entry;
! 2505: curl_socket_t s;
! 2506: int num;
! 2507: unsigned int curraction;
! 2508: int actions[MAX_SOCKSPEREASYHANDLE];
! 2509:
! 2510: for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++)
! 2511: socks[i] = CURL_SOCKET_BAD;
! 2512:
! 2513: /* Fill in the 'current' struct with the state as it is now: what sockets to
! 2514: supervise and for what actions */
! 2515: curraction = multi_getsock(data, socks);
! 2516:
! 2517: /* We have 0 .. N sockets already and we get to know about the 0 .. M
! 2518: sockets we should have from now on. Detect the differences, remove no
! 2519: longer supervised ones and add new ones */
! 2520:
! 2521: /* walk over the sockets we got right now */
! 2522: for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) &&
! 2523: (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
! 2524: i++) {
! 2525: unsigned int action = CURL_POLL_NONE;
! 2526: unsigned int prevaction = 0;
! 2527: unsigned int comboaction;
! 2528: bool sincebefore = FALSE;
! 2529:
! 2530: s = socks[i];
! 2531:
! 2532: /* get it from the hash */
! 2533: entry = sh_getentry(&multi->sockhash, s);
! 2534:
! 2535: if(curraction & GETSOCK_READSOCK(i))
! 2536: action |= CURL_POLL_IN;
! 2537: if(curraction & GETSOCK_WRITESOCK(i))
! 2538: action |= CURL_POLL_OUT;
! 2539:
! 2540: actions[i] = action;
! 2541: if(entry) {
! 2542: /* check if new for this transfer */
! 2543: int j;
! 2544: for(j = 0; j< data->numsocks; j++) {
! 2545: if(s == data->sockets[j]) {
! 2546: prevaction = data->actions[j];
! 2547: sincebefore = TRUE;
! 2548: break;
! 2549: }
! 2550: }
! 2551: }
! 2552: else {
! 2553: /* this is a socket we didn't have before, add it to the hash! */
! 2554: entry = sh_addentry(&multi->sockhash, s);
! 2555: if(!entry)
! 2556: /* fatal */
! 2557: return CURLM_OUT_OF_MEMORY;
! 2558: }
! 2559: if(sincebefore && (prevaction != action)) {
! 2560: /* Socket was used already, but different action now */
! 2561: if(prevaction & CURL_POLL_IN)
! 2562: entry->readers--;
! 2563: if(prevaction & CURL_POLL_OUT)
! 2564: entry->writers--;
! 2565: if(action & CURL_POLL_IN)
! 2566: entry->readers++;
! 2567: if(action & CURL_POLL_OUT)
! 2568: entry->writers++;
! 2569: }
! 2570: else if(!sincebefore) {
! 2571: /* a new user */
! 2572: entry->users++;
! 2573: if(action & CURL_POLL_IN)
! 2574: entry->readers++;
! 2575: if(action & CURL_POLL_OUT)
! 2576: entry->writers++;
! 2577:
! 2578: /* add 'data' to the transfer hash on this socket! */
! 2579: if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */
! 2580: sizeof(struct Curl_easy *), data))
! 2581: return CURLM_OUT_OF_MEMORY;
! 2582: }
! 2583:
! 2584: comboaction = (entry->writers? CURL_POLL_OUT : 0) |
! 2585: (entry->readers ? CURL_POLL_IN : 0);
! 2586:
! 2587: /* socket existed before and has the same action set as before */
! 2588: if(sincebefore && (entry->action == comboaction))
! 2589: /* same, continue */
! 2590: continue;
! 2591:
! 2592: if(multi->socket_cb)
! 2593: multi->socket_cb(data, s, comboaction, multi->socket_userp,
! 2594: entry->socketp);
! 2595:
! 2596: entry->action = comboaction; /* store the current action state */
! 2597: }
! 2598:
! 2599: num = i; /* number of sockets */
! 2600:
! 2601: /* when we've walked over all the sockets we should have right now, we must
! 2602: make sure to detect sockets that are removed */
! 2603: for(i = 0; i< data->numsocks; i++) {
! 2604: int j;
! 2605: bool stillused = FALSE;
! 2606: s = data->sockets[i];
! 2607: for(j = 0; j < num; j++) {
! 2608: if(s == socks[j]) {
! 2609: /* this is still supervised */
! 2610: stillused = TRUE;
! 2611: break;
! 2612: }
! 2613: }
! 2614: if(stillused)
! 2615: continue;
! 2616:
! 2617: entry = sh_getentry(&multi->sockhash, s);
! 2618: /* if this is NULL here, the socket has been closed and notified so
! 2619: already by Curl_multi_closed() */
! 2620: if(entry) {
! 2621: int oldactions = data->actions[i];
! 2622: /* this socket has been removed. Decrease user count */
! 2623: entry->users--;
! 2624: if(oldactions & CURL_POLL_OUT)
! 2625: entry->writers--;
! 2626: if(oldactions & CURL_POLL_IN)
! 2627: entry->readers--;
! 2628: if(!entry->users) {
! 2629: if(multi->socket_cb)
! 2630: multi->socket_cb(data, s, CURL_POLL_REMOVE,
! 2631: multi->socket_userp,
! 2632: entry->socketp);
! 2633: sh_delentry(entry, &multi->sockhash, s);
! 2634: }
! 2635: else {
! 2636: /* still users, but remove this handle as a user of this socket */
! 2637: if(Curl_hash_delete(&entry->transfers, (char *)&data,
! 2638: sizeof(struct Curl_easy *))) {
! 2639: DEBUGASSERT(NULL);
! 2640: }
! 2641: }
! 2642: }
! 2643: } /* for loop over numsocks */
! 2644:
! 2645: memcpy(data->sockets, socks, num*sizeof(curl_socket_t));
! 2646: memcpy(data->actions, actions, num*sizeof(int));
! 2647: data->numsocks = num;
! 2648: return CURLM_OK;
! 2649: }
! 2650:
! 2651: void Curl_updatesocket(struct Curl_easy *data)
! 2652: {
! 2653: singlesocket(data->multi, data);
! 2654: }
! 2655:
! 2656:
! 2657: /*
! 2658: * Curl_multi_closed()
! 2659: *
! 2660: * Used by the connect code to tell the multi_socket code that one of the
! 2661: * sockets we were using is about to be closed. This function will then
! 2662: * remove it from the sockethash for this handle to make the multi_socket API
! 2663: * behave properly, especially for the case when libcurl will create another
! 2664: * socket again and it gets the same file descriptor number.
! 2665: */
! 2666:
! 2667: void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
! 2668: {
! 2669: if(data) {
! 2670: /* if there's still an easy handle associated with this connection */
! 2671: struct Curl_multi *multi = data->multi;
! 2672: if(multi) {
! 2673: /* this is set if this connection is part of a handle that is added to
! 2674: a multi handle, and only then this is necessary */
! 2675: struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
! 2676:
! 2677: if(entry) {
! 2678: if(multi->socket_cb)
! 2679: multi->socket_cb(data, s, CURL_POLL_REMOVE,
! 2680: multi->socket_userp,
! 2681: entry->socketp);
! 2682:
! 2683: /* now remove it from the socket hash */
! 2684: sh_delentry(entry, &multi->sockhash, s);
! 2685: }
! 2686: }
! 2687: }
! 2688: }
! 2689:
! 2690: /*
! 2691: * add_next_timeout()
! 2692: *
! 2693: * Each Curl_easy has a list of timeouts. The add_next_timeout() is called
! 2694: * when it has just been removed from the splay tree because the timeout has
! 2695: * expired. This function is then to advance in the list to pick the next
! 2696: * timeout to use (skip the already expired ones) and add this node back to
! 2697: * the splay tree again.
! 2698: *
! 2699: * The splay tree only has each sessionhandle as a single node and the nearest
! 2700: * timeout is used to sort it on.
! 2701: */
! 2702: static CURLMcode add_next_timeout(struct curltime now,
! 2703: struct Curl_multi *multi,
! 2704: struct Curl_easy *d)
! 2705: {
! 2706: struct curltime *tv = &d->state.expiretime;
! 2707: struct curl_llist *list = &d->state.timeoutlist;
! 2708: struct curl_llist_element *e;
! 2709: struct time_node *node = NULL;
! 2710:
! 2711: /* move over the timeout list for this specific handle and remove all
! 2712: timeouts that are now passed tense and store the next pending
! 2713: timeout in *tv */
! 2714: for(e = list->head; e;) {
! 2715: struct curl_llist_element *n = e->next;
! 2716: timediff_t diff;
! 2717: node = (struct time_node *)e->ptr;
! 2718: diff = Curl_timediff(node->time, now);
! 2719: if(diff <= 0)
! 2720: /* remove outdated entry */
! 2721: Curl_llist_remove(list, e, NULL);
! 2722: else
! 2723: /* the list is sorted so get out on the first mismatch */
! 2724: break;
! 2725: e = n;
! 2726: }
! 2727: e = list->head;
! 2728: if(!e) {
! 2729: /* clear the expire times within the handles that we remove from the
! 2730: splay tree */
! 2731: tv->tv_sec = 0;
! 2732: tv->tv_usec = 0;
! 2733: }
! 2734: else {
! 2735: /* copy the first entry to 'tv' */
! 2736: memcpy(tv, &node->time, sizeof(*tv));
! 2737:
! 2738: /* Insert this node again into the splay. Keep the timer in the list in
! 2739: case we need to recompute future timers. */
! 2740: multi->timetree = Curl_splayinsert(*tv, multi->timetree,
! 2741: &d->state.timenode);
! 2742: }
! 2743: return CURLM_OK;
! 2744: }
! 2745:
! 2746: static CURLMcode multi_socket(struct Curl_multi *multi,
! 2747: bool checkall,
! 2748: curl_socket_t s,
! 2749: int ev_bitmask,
! 2750: int *running_handles)
! 2751: {
! 2752: CURLMcode result = CURLM_OK;
! 2753: struct Curl_easy *data = NULL;
! 2754: struct Curl_tree *t;
! 2755: struct curltime now = Curl_now();
! 2756:
! 2757: if(checkall) {
! 2758: /* *perform() deals with running_handles on its own */
! 2759: result = curl_multi_perform(multi, running_handles);
! 2760:
! 2761: /* walk through each easy handle and do the socket state change magic
! 2762: and callbacks */
! 2763: if(result != CURLM_BAD_HANDLE) {
! 2764: data = multi->easyp;
! 2765: while(data && !result) {
! 2766: result = singlesocket(multi, data);
! 2767: data = data->next;
! 2768: }
! 2769: }
! 2770:
! 2771: /* or should we fall-through and do the timer-based stuff? */
! 2772: return result;
! 2773: }
! 2774: if(s != CURL_SOCKET_TIMEOUT) {
! 2775: struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
! 2776:
! 2777: if(!entry)
! 2778: /* Unmatched socket, we can't act on it but we ignore this fact. In
! 2779: real-world tests it has been proved that libevent can in fact give
! 2780: the application actions even though the socket was just previously
! 2781: asked to get removed, so thus we better survive stray socket actions
! 2782: and just move on. */
! 2783: ;
! 2784: else {
! 2785: struct curl_hash_iterator iter;
! 2786: struct curl_hash_element *he;
! 2787:
! 2788: /* the socket can be shared by many transfers, iterate */
! 2789: Curl_hash_start_iterate(&entry->transfers, &iter);
! 2790: for(he = Curl_hash_next_element(&iter); he;
! 2791: he = Curl_hash_next_element(&iter)) {
! 2792: data = (struct Curl_easy *)he->ptr;
! 2793: DEBUGASSERT(data);
! 2794: DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
! 2795:
! 2796: if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
! 2797: /* set socket event bitmask if they're not locked */
! 2798: data->conn->cselect_bits = ev_bitmask;
! 2799:
! 2800: Curl_expire(data, 0, EXPIRE_RUN_NOW);
! 2801: }
! 2802:
! 2803: /* Now we fall-through and do the timer-based stuff, since we don't want
! 2804: to force the user to have to deal with timeouts as long as at least
! 2805: one connection in fact has traffic. */
! 2806:
! 2807: data = NULL; /* set data to NULL again to avoid calling
! 2808: multi_runsingle() in case there's no need to */
! 2809: now = Curl_now(); /* get a newer time since the multi_runsingle() loop
! 2810: may have taken some time */
! 2811: }
! 2812: }
! 2813: else {
! 2814: /* Asked to run due to time-out. Clear the 'lastcall' variable to force
! 2815: Curl_update_timer() to trigger a callback to the app again even if the
! 2816: same timeout is still the one to run after this call. That handles the
! 2817: case when the application asks libcurl to run the timeout
! 2818: prematurely. */
! 2819: memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
! 2820: }
! 2821:
! 2822: /*
! 2823: * The loop following here will go on as long as there are expire-times left
! 2824: * to process in the splay and 'data' will be re-assigned for every expired
! 2825: * handle we deal with.
! 2826: */
! 2827: do {
! 2828: /* the first loop lap 'data' can be NULL */
! 2829: if(data) {
! 2830: SIGPIPE_VARIABLE(pipe_st);
! 2831:
! 2832: sigpipe_ignore(data, &pipe_st);
! 2833: result = multi_runsingle(multi, now, data);
! 2834: sigpipe_restore(&pipe_st);
! 2835:
! 2836: if(CURLM_OK >= result) {
! 2837: /* get the socket(s) and check if the state has been changed since
! 2838: last */
! 2839: result = singlesocket(multi, data);
! 2840: if(result)
! 2841: return result;
! 2842: }
! 2843: }
! 2844:
! 2845: /* Check if there's one (more) expired timer to deal with! This function
! 2846: extracts a matching node if there is one */
! 2847:
! 2848: multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
! 2849: if(t) {
! 2850: data = t->payload; /* assign this for next loop */
! 2851: (void)add_next_timeout(now, multi, t->payload);
! 2852: }
! 2853:
! 2854: } while(t);
! 2855:
! 2856: *running_handles = multi->num_alive;
! 2857: return result;
! 2858: }
! 2859:
! 2860: #undef curl_multi_setopt
! 2861: CURLMcode curl_multi_setopt(struct Curl_multi *multi,
! 2862: CURLMoption option, ...)
! 2863: {
! 2864: CURLMcode res = CURLM_OK;
! 2865: va_list param;
! 2866:
! 2867: if(!GOOD_MULTI_HANDLE(multi))
! 2868: return CURLM_BAD_HANDLE;
! 2869:
! 2870: if(multi->in_callback)
! 2871: return CURLM_RECURSIVE_API_CALL;
! 2872:
! 2873: va_start(param, option);
! 2874:
! 2875: switch(option) {
! 2876: case CURLMOPT_SOCKETFUNCTION:
! 2877: multi->socket_cb = va_arg(param, curl_socket_callback);
! 2878: break;
! 2879: case CURLMOPT_SOCKETDATA:
! 2880: multi->socket_userp = va_arg(param, void *);
! 2881: break;
! 2882: case CURLMOPT_PUSHFUNCTION:
! 2883: multi->push_cb = va_arg(param, curl_push_callback);
! 2884: break;
! 2885: case CURLMOPT_PUSHDATA:
! 2886: multi->push_userp = va_arg(param, void *);
! 2887: break;
! 2888: case CURLMOPT_PIPELINING:
! 2889: multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX;
! 2890: break;
! 2891: case CURLMOPT_TIMERFUNCTION:
! 2892: multi->timer_cb = va_arg(param, curl_multi_timer_callback);
! 2893: break;
! 2894: case CURLMOPT_TIMERDATA:
! 2895: multi->timer_userp = va_arg(param, void *);
! 2896: break;
! 2897: case CURLMOPT_MAXCONNECTS:
! 2898: multi->maxconnects = va_arg(param, long);
! 2899: break;
! 2900: case CURLMOPT_MAX_HOST_CONNECTIONS:
! 2901: multi->max_host_connections = va_arg(param, long);
! 2902: break;
! 2903: case CURLMOPT_MAX_TOTAL_CONNECTIONS:
! 2904: multi->max_total_connections = va_arg(param, long);
! 2905: break;
! 2906: /* options formerly used for pipelining */
! 2907: case CURLMOPT_MAX_PIPELINE_LENGTH:
! 2908: break;
! 2909: case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
! 2910: break;
! 2911: case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
! 2912: break;
! 2913: case CURLMOPT_PIPELINING_SITE_BL:
! 2914: break;
! 2915: case CURLMOPT_PIPELINING_SERVER_BL:
! 2916: break;
! 2917: case CURLMOPT_MAX_CONCURRENT_STREAMS:
! 2918: {
! 2919: long streams = va_arg(param, long);
! 2920: if(streams < 1)
! 2921: streams = 100;
! 2922: multi->max_concurrent_streams =
! 2923: (streams > (long)INITIAL_MAX_CONCURRENT_STREAMS)?
! 2924: INITIAL_MAX_CONCURRENT_STREAMS : (unsigned int)streams;
! 2925: }
! 2926: break;
! 2927: default:
! 2928: res = CURLM_UNKNOWN_OPTION;
! 2929: break;
! 2930: }
! 2931: va_end(param);
! 2932: return res;
! 2933: }
! 2934:
! 2935: /* we define curl_multi_socket() in the public multi.h header */
! 2936: #undef curl_multi_socket
! 2937:
! 2938: CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s,
! 2939: int *running_handles)
! 2940: {
! 2941: CURLMcode result;
! 2942: if(multi->in_callback)
! 2943: return CURLM_RECURSIVE_API_CALL;
! 2944: result = multi_socket(multi, FALSE, s, 0, running_handles);
! 2945: if(CURLM_OK >= result)
! 2946: Curl_update_timer(multi);
! 2947: return result;
! 2948: }
! 2949:
! 2950: CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s,
! 2951: int ev_bitmask, int *running_handles)
! 2952: {
! 2953: CURLMcode result;
! 2954: if(multi->in_callback)
! 2955: return CURLM_RECURSIVE_API_CALL;
! 2956: result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
! 2957: if(CURLM_OK >= result)
! 2958: Curl_update_timer(multi);
! 2959: return result;
! 2960: }
! 2961:
! 2962: CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles)
! 2963:
! 2964: {
! 2965: CURLMcode result;
! 2966: if(multi->in_callback)
! 2967: return CURLM_RECURSIVE_API_CALL;
! 2968: result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
! 2969: if(CURLM_OK >= result)
! 2970: Curl_update_timer(multi);
! 2971: return result;
! 2972: }
! 2973:
! 2974: static CURLMcode multi_timeout(struct Curl_multi *multi,
! 2975: long *timeout_ms)
! 2976: {
! 2977: static struct curltime tv_zero = {0, 0};
! 2978:
! 2979: if(multi->timetree) {
! 2980: /* we have a tree of expire times */
! 2981: struct curltime now = Curl_now();
! 2982:
! 2983: /* splay the lowest to the bottom */
! 2984: multi->timetree = Curl_splay(tv_zero, multi->timetree);
! 2985:
! 2986: if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
! 2987: /* some time left before expiration */
! 2988: timediff_t diff = Curl_timediff(multi->timetree->key, now);
! 2989: if(diff <= 0)
! 2990: /*
! 2991: * Since we only provide millisecond resolution on the returned value
! 2992: * and the diff might be less than one millisecond here, we don't
! 2993: * return zero as that may cause short bursts of busyloops on fast
! 2994: * processors while the diff is still present but less than one
! 2995: * millisecond! instead we return 1 until the time is ripe.
! 2996: */
! 2997: *timeout_ms = 1;
! 2998: else
! 2999: /* this should be safe even on 64 bit archs, as we don't use that
! 3000: overly long timeouts */
! 3001: *timeout_ms = (long)diff;
! 3002: }
! 3003: else
! 3004: /* 0 means immediately */
! 3005: *timeout_ms = 0;
! 3006: }
! 3007: else
! 3008: *timeout_ms = -1;
! 3009:
! 3010: return CURLM_OK;
! 3011: }
! 3012:
! 3013: CURLMcode curl_multi_timeout(struct Curl_multi *multi,
! 3014: long *timeout_ms)
! 3015: {
! 3016: /* First, make some basic checks that the CURLM handle is a good handle */
! 3017: if(!GOOD_MULTI_HANDLE(multi))
! 3018: return CURLM_BAD_HANDLE;
! 3019:
! 3020: if(multi->in_callback)
! 3021: return CURLM_RECURSIVE_API_CALL;
! 3022:
! 3023: return multi_timeout(multi, timeout_ms);
! 3024: }
! 3025:
! 3026: /*
! 3027: * Tell the application it should update its timers, if it subscribes to the
! 3028: * update timer callback.
! 3029: */
! 3030: void Curl_update_timer(struct Curl_multi *multi)
! 3031: {
! 3032: long timeout_ms;
! 3033:
! 3034: if(!multi->timer_cb)
! 3035: return;
! 3036: if(multi_timeout(multi, &timeout_ms)) {
! 3037: return;
! 3038: }
! 3039: if(timeout_ms < 0) {
! 3040: static const struct curltime none = {0, 0};
! 3041: if(Curl_splaycomparekeys(none, multi->timer_lastcall)) {
! 3042: multi->timer_lastcall = none;
! 3043: /* there's no timeout now but there was one previously, tell the app to
! 3044: disable it */
! 3045: multi->timer_cb(multi, -1, multi->timer_userp);
! 3046: return;
! 3047: }
! 3048: return;
! 3049: }
! 3050:
! 3051: /* When multi_timeout() is done, multi->timetree points to the node with the
! 3052: * timeout we got the (relative) time-out time for. We can thus easily check
! 3053: * if this is the same (fixed) time as we got in a previous call and then
! 3054: * avoid calling the callback again. */
! 3055: if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0)
! 3056: return;
! 3057:
! 3058: multi->timer_lastcall = multi->timetree->key;
! 3059:
! 3060: multi->timer_cb(multi, timeout_ms, multi->timer_userp);
! 3061: }
! 3062:
! 3063: /*
! 3064: * multi_deltimeout()
! 3065: *
! 3066: * Remove a given timestamp from the list of timeouts.
! 3067: */
! 3068: static void
! 3069: multi_deltimeout(struct Curl_easy *data, expire_id eid)
! 3070: {
! 3071: struct curl_llist_element *e;
! 3072: struct curl_llist *timeoutlist = &data->state.timeoutlist;
! 3073: /* find and remove the specific node from the list */
! 3074: for(e = timeoutlist->head; e; e = e->next) {
! 3075: struct time_node *n = (struct time_node *)e->ptr;
! 3076: if(n->eid == eid) {
! 3077: Curl_llist_remove(timeoutlist, e, NULL);
! 3078: return;
! 3079: }
! 3080: }
! 3081: }
! 3082:
! 3083: /*
! 3084: * multi_addtimeout()
! 3085: *
! 3086: * Add a timestamp to the list of timeouts. Keep the list sorted so that head
! 3087: * of list is always the timeout nearest in time.
! 3088: *
! 3089: */
! 3090: static CURLMcode
! 3091: multi_addtimeout(struct Curl_easy *data,
! 3092: struct curltime *stamp,
! 3093: expire_id eid)
! 3094: {
! 3095: struct curl_llist_element *e;
! 3096: struct time_node *node;
! 3097: struct curl_llist_element *prev = NULL;
! 3098: size_t n;
! 3099: struct curl_llist *timeoutlist = &data->state.timeoutlist;
! 3100:
! 3101: node = &data->state.expires[eid];
! 3102:
! 3103: /* copy the timestamp and id */
! 3104: memcpy(&node->time, stamp, sizeof(*stamp));
! 3105: node->eid = eid; /* also marks it as in use */
! 3106:
! 3107: n = Curl_llist_count(timeoutlist);
! 3108: if(n) {
! 3109: /* find the correct spot in the list */
! 3110: for(e = timeoutlist->head; e; e = e->next) {
! 3111: struct time_node *check = (struct time_node *)e->ptr;
! 3112: timediff_t diff = Curl_timediff(check->time, node->time);
! 3113: if(diff > 0)
! 3114: break;
! 3115: prev = e;
! 3116: }
! 3117:
! 3118: }
! 3119: /* else
! 3120: this is the first timeout on the list */
! 3121:
! 3122: Curl_llist_insert_next(timeoutlist, prev, node, &node->list);
! 3123: return CURLM_OK;
! 3124: }
! 3125:
! 3126: /*
! 3127: * Curl_expire()
! 3128: *
! 3129: * given a number of milliseconds from now to use to set the 'act before
! 3130: * this'-time for the transfer, to be extracted by curl_multi_timeout()
! 3131: *
! 3132: * The timeout will be added to a queue of timeouts if it defines a moment in
! 3133: * time that is later than the current head of queue.
! 3134: *
! 3135: * Expire replaces a former timeout using the same id if already set.
! 3136: */
! 3137: void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
! 3138: {
! 3139: struct Curl_multi *multi = data->multi;
! 3140: struct curltime *nowp = &data->state.expiretime;
! 3141: struct curltime set;
! 3142:
! 3143: /* this is only interesting while there is still an associated multi struct
! 3144: remaining! */
! 3145: if(!multi)
! 3146: return;
! 3147:
! 3148: DEBUGASSERT(id < EXPIRE_LAST);
! 3149:
! 3150: set = Curl_now();
! 3151: set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */
! 3152: set.tv_usec += (unsigned int)(milli%1000)*1000;
! 3153:
! 3154: if(set.tv_usec >= 1000000) {
! 3155: set.tv_sec++;
! 3156: set.tv_usec -= 1000000;
! 3157: }
! 3158:
! 3159: /* Remove any timer with the same id just in case. */
! 3160: multi_deltimeout(data, id);
! 3161:
! 3162: /* Add it to the timer list. It must stay in the list until it has expired
! 3163: in case we need to recompute the minimum timer later. */
! 3164: multi_addtimeout(data, &set, id);
! 3165:
! 3166: if(nowp->tv_sec || nowp->tv_usec) {
! 3167: /* This means that the struct is added as a node in the splay tree.
! 3168: Compare if the new time is earlier, and only remove-old/add-new if it
! 3169: is. */
! 3170: timediff_t diff = Curl_timediff(set, *nowp);
! 3171: int rc;
! 3172:
! 3173: if(diff > 0) {
! 3174: /* The current splay tree entry is sooner than this new expiry time.
! 3175: We don't need to update our splay tree entry. */
! 3176: return;
! 3177: }
! 3178:
! 3179: /* Since this is an updated time, we must remove the previous entry from
! 3180: the splay tree first and then re-add the new value */
! 3181: rc = Curl_splayremovebyaddr(multi->timetree,
! 3182: &data->state.timenode,
! 3183: &multi->timetree);
! 3184: if(rc)
! 3185: infof(data, "Internal error removing splay node = %d\n", rc);
! 3186: }
! 3187:
! 3188: /* Indicate that we are in the splay tree and insert the new timer expiry
! 3189: value since it is our local minimum. */
! 3190: *nowp = set;
! 3191: data->state.timenode.payload = data;
! 3192: multi->timetree = Curl_splayinsert(*nowp, multi->timetree,
! 3193: &data->state.timenode);
! 3194: }
! 3195:
! 3196: /*
! 3197: * Curl_expire_done()
! 3198: *
! 3199: * Removes the expire timer. Marks it as done.
! 3200: *
! 3201: */
! 3202: void Curl_expire_done(struct Curl_easy *data, expire_id id)
! 3203: {
! 3204: /* remove the timer, if there */
! 3205: multi_deltimeout(data, id);
! 3206: }
! 3207:
! 3208: /*
! 3209: * Curl_expire_clear()
! 3210: *
! 3211: * Clear ALL timeout values for this handle.
! 3212: */
! 3213: void Curl_expire_clear(struct Curl_easy *data)
! 3214: {
! 3215: struct Curl_multi *multi = data->multi;
! 3216: struct curltime *nowp = &data->state.expiretime;
! 3217:
! 3218: /* this is only interesting while there is still an associated multi struct
! 3219: remaining! */
! 3220: if(!multi)
! 3221: return;
! 3222:
! 3223: if(nowp->tv_sec || nowp->tv_usec) {
! 3224: /* Since this is an cleared time, we must remove the previous entry from
! 3225: the splay tree */
! 3226: struct curl_llist *list = &data->state.timeoutlist;
! 3227: int rc;
! 3228:
! 3229: rc = Curl_splayremovebyaddr(multi->timetree,
! 3230: &data->state.timenode,
! 3231: &multi->timetree);
! 3232: if(rc)
! 3233: infof(data, "Internal error clearing splay node = %d\n", rc);
! 3234:
! 3235: /* flush the timeout list too */
! 3236: while(list->size > 0) {
! 3237: Curl_llist_remove(list, list->tail, NULL);
! 3238: }
! 3239:
! 3240: #ifdef DEBUGBUILD
! 3241: infof(data, "Expire cleared (transfer %p)\n", data);
! 3242: #endif
! 3243: nowp->tv_sec = 0;
! 3244: nowp->tv_usec = 0;
! 3245: }
! 3246: }
! 3247:
! 3248:
! 3249:
! 3250:
! 3251: CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
! 3252: void *hashp)
! 3253: {
! 3254: struct Curl_sh_entry *there = NULL;
! 3255:
! 3256: if(multi->in_callback)
! 3257: return CURLM_RECURSIVE_API_CALL;
! 3258:
! 3259: there = sh_getentry(&multi->sockhash, s);
! 3260:
! 3261: if(!there)
! 3262: return CURLM_BAD_SOCKET;
! 3263:
! 3264: there->socketp = hashp;
! 3265:
! 3266: return CURLM_OK;
! 3267: }
! 3268:
! 3269: size_t Curl_multi_max_host_connections(struct Curl_multi *multi)
! 3270: {
! 3271: return multi ? multi->max_host_connections : 0;
! 3272: }
! 3273:
! 3274: size_t Curl_multi_max_total_connections(struct Curl_multi *multi)
! 3275: {
! 3276: return multi ? multi->max_total_connections : 0;
! 3277: }
! 3278:
! 3279: /*
! 3280: * When information about a connection has appeared, call this!
! 3281: */
! 3282:
! 3283: void Curl_multiuse_state(struct connectdata *conn,
! 3284: int bundlestate) /* use BUNDLE_* defines */
! 3285: {
! 3286: DEBUGASSERT(conn);
! 3287: DEBUGASSERT(conn->bundle);
! 3288: DEBUGASSERT(conn->data);
! 3289: DEBUGASSERT(conn->data->multi);
! 3290:
! 3291: conn->bundle->multiuse = bundlestate;
! 3292: process_pending_handles(conn->data->multi);
! 3293: }
! 3294:
! 3295: static void process_pending_handles(struct Curl_multi *multi)
! 3296: {
! 3297: struct curl_llist_element *e = multi->pending.head;
! 3298: if(e) {
! 3299: struct Curl_easy *data = e->ptr;
! 3300:
! 3301: DEBUGASSERT(data->mstate == CURLM_STATE_CONNECT_PEND);
! 3302:
! 3303: multistate(data, CURLM_STATE_CONNECT);
! 3304:
! 3305: /* Remove this node from the list */
! 3306: Curl_llist_remove(&multi->pending, e, NULL);
! 3307:
! 3308: /* Make sure that the handle will be processed soonish. */
! 3309: Curl_expire(data, 0, EXPIRE_RUN_NOW);
! 3310:
! 3311: /* mark this as having been in the pending queue */
! 3312: data->state.previouslypending = TRUE;
! 3313: }
! 3314: }
! 3315:
! 3316: void Curl_set_in_callback(struct Curl_easy *data, bool value)
! 3317: {
! 3318: /* might get called when there is no data pointer! */
! 3319: if(data) {
! 3320: if(data->multi_easy)
! 3321: data->multi_easy->in_callback = value;
! 3322: else if(data->multi)
! 3323: data->multi->in_callback = value;
! 3324: }
! 3325: }
! 3326:
! 3327: bool Curl_is_in_callback(struct Curl_easy *easy)
! 3328: {
! 3329: return ((easy->multi && easy->multi->in_callback) ||
! 3330: (easy->multi_easy && easy->multi_easy->in_callback));
! 3331: }
! 3332:
! 3333: #ifdef DEBUGBUILD
! 3334: void Curl_multi_dump(struct Curl_multi *multi)
! 3335: {
! 3336: struct Curl_easy *data;
! 3337: int i;
! 3338: fprintf(stderr, "* Multi status: %d handles, %d alive\n",
! 3339: multi->num_easy, multi->num_alive);
! 3340: for(data = multi->easyp; data; data = data->next) {
! 3341: if(data->mstate < CURLM_STATE_COMPLETED) {
! 3342: /* only display handles that are not completed */
! 3343: fprintf(stderr, "handle %p, state %s, %d sockets\n",
! 3344: (void *)data,
! 3345: statename[data->mstate], data->numsocks);
! 3346: for(i = 0; i < data->numsocks; i++) {
! 3347: curl_socket_t s = data->sockets[i];
! 3348: struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
! 3349:
! 3350: fprintf(stderr, "%d ", (int)s);
! 3351: if(!entry) {
! 3352: fprintf(stderr, "INTERNAL CONFUSION\n");
! 3353: continue;
! 3354: }
! 3355: fprintf(stderr, "[%s %s] ",
! 3356: (entry->action&CURL_POLL_IN)?"RECVING":"",
! 3357: (entry->action&CURL_POLL_OUT)?"SENDING":"");
! 3358: }
! 3359: if(data->numsocks)
! 3360: fprintf(stderr, "\n");
! 3361: }
! 3362: }
! 3363: }
! 3364: #endif
! 3365:
! 3366: unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
! 3367: {
! 3368: DEBUGASSERT(multi);
! 3369: return multi->max_concurrent_streams;
! 3370: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>