diff options
author | Nikita Slyusarev <[email protected]> | 2022-02-10 16:46:52 +0300 |
---|---|---|
committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:46:52 +0300 |
commit | cd77cecfc03a3eaf87816af28a33067c4f0cdb59 (patch) | |
tree | 1308e0bae862d52e0020d881fe758080437fe389 /contrib/libs/curl/lib/multi.c | |
parent | cdae02d225fb5b3afbb28990e79a7ac6c9125327 (diff) |
Restoring authorship annotation for Nikita Slyusarev <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/curl/lib/multi.c')
-rw-r--r-- | contrib/libs/curl/lib/multi.c | 2958 |
1 files changed, 1479 insertions, 1479 deletions
diff --git a/contrib/libs/curl/lib/multi.c b/contrib/libs/curl/lib/multi.c index f1c9e4dbfba..9075cd6c49f 100644 --- a/contrib/libs/curl/lib/multi.c +++ b/contrib/libs/curl/lib/multi.c @@ -1,46 +1,46 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#include <curl/curl.h> - -#include "urldata.h" -#include "transfer.h" -#include "url.h" -#include "connect.h" -#include "progress.h" -#include "easyif.h" + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include <curl/curl.h> + +#include "urldata.h" +#include "transfer.h" +#include "url.h" +#include "connect.h" +#include "progress.h" +#include "easyif.h" #include "share.h" #include "psl.h" -#include "multiif.h" -#include "sendf.h" -#include "timeval.h" -#include "http.h" -#include "select.h" -#include "warnless.h" -#include "speedcheck.h" -#include "conncache.h" -#include "multihandle.h" +#include "multiif.h" +#include "sendf.h" +#include "timeval.h" +#include "http.h" +#include "select.h" +#include "warnless.h" +#include "speedcheck.h" +#include "conncache.h" +#include "multihandle.h" #include "sigpipe.h" #include "vtls/vtls.h" #include "connect.h" @@ -50,61 +50,61 @@ #include "socks.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 - to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every - CURL handle takes 45-50 K memory, therefore this 3K are not significant. -*/ -#ifndef CURL_SOCKET_HASH_TABLE_SIZE -#define CURL_SOCKET_HASH_TABLE_SIZE 911 -#endif - +#include "curl_memory.h" +#include "memdebug.h" + +/* + CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 + to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every + CURL handle takes 45-50 K memory, therefore this 3K are not significant. +*/ +#ifndef CURL_SOCKET_HASH_TABLE_SIZE +#define CURL_SOCKET_HASH_TABLE_SIZE 911 +#endif + #ifndef CURL_CONNECTION_HASH_SIZE -#define CURL_CONNECTION_HASH_SIZE 97 +#define CURL_CONNECTION_HASH_SIZE 97 #endif - -#define CURL_MULTI_HANDLE 0x000bab1e - -#define GOOD_MULTI_HANDLE(x) \ + +#define CURL_MULTI_HANDLE 0x000bab1e + +#define GOOD_MULTI_HANDLE(x) \ ((x) && (x)->type == CURL_MULTI_HANDLE) - + static CURLMcode singlesocket(struct Curl_multi *multi, struct Curl_easy *data); static CURLMcode add_next_timeout(struct curltime now, - struct Curl_multi *multi, + struct Curl_multi *multi, struct Curl_easy *d); -static CURLMcode multi_timeout(struct Curl_multi *multi, - long *timeout_ms); +static CURLMcode multi_timeout(struct Curl_multi *multi, + long *timeout_ms); static void process_pending_handles(struct Curl_multi *multi); - -#ifdef DEBUGBUILD -static const char * const statename[]={ - "INIT", - "CONNECT_PEND", - "CONNECT", - "WAITRESOLVE", - "WAITCONNECT", - "WAITPROXYCONNECT", + +#ifdef DEBUGBUILD +static const char * const statename[]={ + "INIT", + "CONNECT_PEND", + "CONNECT", + "WAITRESOLVE", + "WAITCONNECT", + "WAITPROXYCONNECT", "SENDPROTOCONNECT", - "PROTOCONNECT", - "DO", - "DOING", - "DO_MORE", - "DO_DONE", - "PERFORM", - "TOOFAST", - "DONE", - "COMPLETED", - "MSGSENT", -}; -#endif - + "PROTOCONNECT", + "DO", + "DOING", + "DO_MORE", + "DO_DONE", + "PERFORM", + "TOOFAST", + "DONE", + "COMPLETED", + "MSGSENT", +}; +#endif + /* function pointer called once when switching TO a state */ typedef void (*init_multistate_func)(struct Curl_easy *data); - + static void Curl_init_completed(struct Curl_easy *data) { /* this is a completed transfer */ @@ -115,14 +115,14 @@ static void Curl_init_completed(struct Curl_easy *data) Curl_expire_clear(data); /* stop all timers */ } -/* always use this function to change state, to make debugging easier */ +/* always use this function to change state, to make debugging easier */ static void mstate(struct Curl_easy *data, CURLMstate state -#ifdef DEBUGBUILD - , int lineno -#endif -) -{ - CURLMstate oldstate = data->mstate; +#ifdef DEBUGBUILD + , int lineno +#endif +) +{ + CURLMstate oldstate = data->mstate; static const init_multistate_func finit[CURLM_STATE_LAST] = { NULL, /* INIT */ NULL, /* CONNECT_PEND */ @@ -142,67 +142,67 @@ static void mstate(struct Curl_easy *data, CURLMstate state Curl_init_completed, /* COMPLETED */ NULL /* MSGSENT */ }; - + #if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS) (void) lineno; #endif - if(oldstate == state) - /* don't bother when the new state is the same as the old state */ - return; - - data->mstate = state; - + if(oldstate == state) + /* don't bother when the new state is the same as the old state */ + return; + + data->mstate = state; + #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - if(data->mstate >= CURLM_STATE_CONNECT_PEND && - data->mstate < CURLM_STATE_COMPLETED) { + if(data->mstate >= CURLM_STATE_CONNECT_PEND && + data->mstate < CURLM_STATE_COMPLETED) { long connection_id = -5000; if(data->conn) connection_id = data->conn->connection_id; - - infof(data, + + infof(data, "STATE: %s => %s handle %p; line %d (connection #%ld)\n", - statename[oldstate], statename[data->mstate], - (void *)data, lineno, connection_id); - } -#endif + statename[oldstate], statename[data->mstate], + (void *)data, lineno, connection_id); + } +#endif if(state == CURLM_STATE_COMPLETED) { - /* changing to COMPLETED means there's one less easy handle 'alive' */ + /* changing to COMPLETED means there's one less easy handle 'alive' */ DEBUGASSERT(data->multi->num_alive > 0); - data->multi->num_alive--; + data->multi->num_alive--; } /* if this state has an init-function, run it */ if(finit[state]) finit[state](data); -} - -#ifndef DEBUGBUILD -#define multistate(x,y) mstate(x,y) -#else -#define multistate(x,y) mstate(x,y, __LINE__) -#endif - -/* +} + +#ifndef DEBUGBUILD +#define multistate(x,y) mstate(x,y) +#else +#define multistate(x,y) mstate(x,y, __LINE__) +#endif + +/* * We add one of these structs to the sockhash for each socket - */ - -struct Curl_sh_entry { + */ + +struct Curl_sh_entry { struct Curl_hash transfers; /* hash of transfers using this socket */ unsigned int action; /* what combined action READ/WRITE this socket waits for */ - void *socketp; /* settable by users with curl_multi_assign() */ + void *socketp; /* settable by users with curl_multi_assign() */ unsigned int users; /* number of transfers using this */ unsigned int readers; /* this many transfers want to read */ unsigned int writers; /* this many transfers want to write */ -}; -/* bits for 'action' having no bits means this socket is not expecting any - action */ -#define SH_READ 1 -#define SH_WRITE 2 - +}; +/* bits for 'action' having no bits means this socket is not expecting any + action */ +#define SH_READ 1 +#define SH_WRITE 2 + /* look up a given socket in the socket hash, skip invalid sockets */ static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh, curl_socket_t s) @@ -237,138 +237,138 @@ static void trhash_dtor(void *nada) } -/* make sure this socket is present in the hash for this handle */ +/* make sure this socket is present in the hash for this handle */ static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh, curl_socket_t s) -{ +{ struct Curl_sh_entry *there = sh_getentry(sh, s); - struct Curl_sh_entry *check; - + struct Curl_sh_entry *check; + if(there) { - /* it is present, return fine */ - return there; + /* it is present, return fine */ + return there; } - - /* not present, add it */ - check = calloc(1, sizeof(struct Curl_sh_entry)); - if(!check) - return NULL; /* major failure */ + + /* not present, add it */ + check = calloc(1, sizeof(struct Curl_sh_entry)); + if(!check) + return NULL; /* major failure */ if(Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, trhash_compare, trhash_dtor)) { free(check); return NULL; } - - /* make/add new hash entry */ + + /* make/add new hash entry */ if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { Curl_hash_destroy(&check->transfers); - free(check); - return NULL; /* major failure */ - } - - return check; /* things are good in sockhash land */ -} - - -/* delete the given socket + handle from the hash */ + free(check); + return NULL; /* major failure */ + } + + return check; /* things are good in sockhash land */ +} + + +/* delete the given socket + handle from the hash */ static void sh_delentry(struct Curl_sh_entry *entry, struct Curl_hash *sh, curl_socket_t s) -{ +{ Curl_hash_destroy(&entry->transfers); /* We remove the hash entry. This will end up in a call to sh_freeentry(). */ Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t)); -} - -/* - * free a sockhash entry - */ -static void sh_freeentry(void *freethis) -{ - struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis; - +} + +/* + * free a sockhash entry + */ +static void sh_freeentry(void *freethis) +{ + struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis; + free(p); -} - -static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) -{ - (void) k1_len; (void) k2_len; - +} + +static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) +{ + (void) k1_len; (void) k2_len; + return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2)); -} - -static size_t hash_fd(void *key, size_t key_length, size_t slots_num) -{ +} + +static size_t hash_fd(void *key, size_t key_length, size_t slots_num) +{ curl_socket_t fd = *((curl_socket_t *) key); - (void) key_length; - + (void) key_length; + return (fd % slots_num); -} - -/* - * sh_init() creates a new socket hash and returns the handle for it. - * - * Quote from README.multi_socket: - * - * "Some tests at 7000 and 9000 connections showed that the socket hash lookup - * is somewhat of a bottle neck. Its current implementation may be a bit too - * limiting. It simply has a fixed-size array, and on each entry in the array - * it has a linked list with entries. So the hash only checks which list to - * scan through. The code I had used so for used a list with merely 7 slots - * (as that is what the DNS hash uses) but with 7000 connections that would - * make an average of 1000 nodes in each list to run through. I upped that to - * 97 slots (I believe a prime is suitable) and noticed a significant speed - * increase. I need to reconsider the hash implementation or use a rather - * large default value like this. At 9000 connections I was still below 10us - * per call." - * - */ +} + +/* + * sh_init() creates a new socket hash and returns the handle for it. + * + * Quote from README.multi_socket: + * + * "Some tests at 7000 and 9000 connections showed that the socket hash lookup + * is somewhat of a bottle neck. Its current implementation may be a bit too + * limiting. It simply has a fixed-size array, and on each entry in the array + * it has a linked list with entries. So the hash only checks which list to + * scan through. The code I had used so for used a list with merely 7 slots + * (as that is what the DNS hash uses) but with 7000 connections that would + * make an average of 1000 nodes in each list to run through. I upped that to + * 97 slots (I believe a prime is suitable) and noticed a significant speed + * increase. I need to reconsider the hash implementation or use a rather + * large default value like this. At 9000 connections I was still below 10us + * per call." + * + */ static int sh_init(struct Curl_hash *hash, int hashsize) -{ +{ return Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare, sh_freeentry); -} - -/* - * multi_addmsg() - * - * Called when a transfer is completed. Adds the given msg pointer to - * the list kept in the multi handle. - */ -static CURLMcode multi_addmsg(struct Curl_multi *multi, - struct Curl_message *msg) -{ +} + +/* + * multi_addmsg() + * + * Called when a transfer is completed. Adds the given msg pointer to + * the list kept in the multi handle. + */ +static CURLMcode multi_addmsg(struct Curl_multi *multi, + struct Curl_message *msg) +{ Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg, &msg->list); - return CURLM_OK; -} - -struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ - int chashsize) /* connection hash */ -{ - struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); - - if(!multi) - return NULL; - - multi->type = CURL_MULTI_HANDLE; - + return CURLM_OK; +} + +struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ + int chashsize) /* connection hash */ +{ + struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); + + if(!multi) + return NULL; + + multi->type = CURL_MULTI_HANDLE; + if(Curl_mk_dnscache(&multi->hostcache)) - goto error; - + goto error; + if(sh_init(&multi->sockhash, hashsize)) - goto error; - + goto error; + if(Curl_conncache_init(&multi->conn_cache, chashsize)) - goto error; - + goto error; + Curl_llist_init(&multi->msglist, NULL); Curl_llist_init(&multi->pending, NULL); - + multi->multiplexing = TRUE; - + /* -1 means it not set by user, use the default value */ multi->maxconnects = -1; multi->max_concurrent_streams = 100; @@ -389,74 +389,74 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ #endif return multi; - - error: - + + error: + Curl_hash_destroy(&multi->sockhash); Curl_hash_destroy(&multi->hostcache); Curl_conncache_destroy(&multi->conn_cache); Curl_llist_destroy(&multi->msglist, NULL); Curl_llist_destroy(&multi->pending, NULL); - - free(multi); - return NULL; -} - + + free(multi); + return NULL; +} + struct Curl_multi *curl_multi_init(void) -{ - return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE, - CURL_CONNECTION_HASH_SIZE); -} - +{ + return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE, + CURL_CONNECTION_HASH_SIZE); +} + CURLMcode curl_multi_add_handle(struct Curl_multi *multi, struct Curl_easy *data) -{ - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - /* Verify that we got a somewhat good easy handle too */ +{ + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - - /* Prevent users from adding same easy handle more than once and prevent - adding to more than one multi stack */ - if(data->multi) - return CURLM_ADDED_ALREADY; - + return CURLM_BAD_EASY_HANDLE; + + /* Prevent users from adding same easy handle more than once and prevent + adding to more than one multi stack */ + if(data->multi) + return CURLM_ADDED_ALREADY; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - + /* Initialize timeout list for this handle */ Curl_llist_init(&data->state.timeoutlist, NULL); - /* - * No failure allowed in this function beyond this point. And no - * modification of easy nor multi handle allowed before this except for - * potential multi's connection cache growing which won't be undone in this - * function no matter what. - */ + /* + * No failure allowed in this function beyond this point. And no + * modification of easy nor multi handle allowed before this except for + * potential multi's connection cache growing which won't be undone in this + * function no matter what. + */ if(data->set.errorbuffer) data->set.errorbuffer[0] = 0; - - /* set the easy handle */ - multistate(data, CURLM_STATE_INIT); - - /* for multi interface connections, we share DNS cache automatically if the - easy handle's one is currently not set. */ + + /* set the easy handle */ + multistate(data, CURLM_STATE_INIT); + + /* for multi interface connections, we share DNS cache automatically if the + easy handle's one is currently not set. */ if(!data->dns.hostcache || - (data->dns.hostcachetype == HCACHE_NONE)) { + (data->dns.hostcachetype == HCACHE_NONE)) { data->dns.hostcache = &multi->hostcache; - data->dns.hostcachetype = HCACHE_MULTI; - } - + data->dns.hostcachetype = HCACHE_MULTI; + } + /* Point to the shared or multi handle connection cache */ if(data->share && (data->share->specifier & (1<< CURL_LOCK_DATA_CONNECT))) data->state.conn_cache = &data->share->conn_cache; else data->state.conn_cache = &multi->conn_cache; data->state.lastconnect_id = -1; - + #ifdef USE_LIBPSL /* Do the same for PSL. */ if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL))) @@ -466,48 +466,48 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, #endif /* We add the new entry last in the list. */ - data->next = NULL; /* end of the line */ - if(multi->easyp) { + data->next = NULL; /* end of the line */ + if(multi->easyp) { struct Curl_easy *last = multi->easylp; - last->next = data; - data->prev = last; - multi->easylp = data; /* the new last node */ - } - else { + last->next = data; + data->prev = last; + multi->easylp = data; /* the new last node */ + } + else { /* first node, make prev NULL! */ - data->prev = NULL; - multi->easylp = multi->easyp = data; /* both first and last */ - } - + data->prev = NULL; + multi->easylp = multi->easyp = data; /* both first and last */ + } + /* make the Curl_easy refer back to this multi handle */ data->multi = multi; - - /* Set the timeout for this handle to expire really soon so that it will - be taken care of even when this handle is added in the midst of operation - when only the curl_multi_socket() API is used. During that flow, only - sockets that time-out or have actions will be dealt with. Since this - handle has no action yet, we make sure it times out to get things to - happen. */ + + /* Set the timeout for this handle to expire really soon so that it will + be taken care of even when this handle is added in the midst of operation + when only the curl_multi_socket() API is used. During that flow, only + sockets that time-out or have actions will be dealt with. Since this + handle has no action yet, we make sure it times out to get things to + happen. */ Curl_expire(data, 0, EXPIRE_RUN_NOW); - - /* increase the node-counter */ - multi->num_easy++; - - /* increase the alive-counter */ - multi->num_alive++; - + + /* increase the node-counter */ + multi->num_easy++; + + /* increase the alive-counter */ + multi->num_alive++; + /* A somewhat crude work-around for a little glitch in Curl_update_timer() that happens if the lastcall time is set to the same time when the handle is removed as when the next handle is added, as then the check in Curl_update_timer() that prevents calling the application multiple times with the same timer info will not trigger and then the new handle's timeout will not be notified to the app. - - The work-around is thus simply to clear the 'lastcall' variable to force + + The work-around is thus simply to clear the 'lastcall' variable to force Curl_update_timer() to always trigger a callback to the app when a new easy handle is added */ - memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); - + memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); + CONNCACHE_LOCK(data); /* The closure handle only ever has default timeouts set. To improve the state somewhat we clone the timeouts from each added handle so that the @@ -521,34 +521,34 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, CONNCACHE_UNLOCK(data); Curl_update_timer(multi); - return CURLM_OK; -} - -#if 0 -/* Debug-function, used like this: - * - * Curl_hash_print(multi->sockhash, debug_print_sock_hash); - * - * Enable the hash print function first by editing hash.c - */ -static void debug_print_sock_hash(void *p) -{ - struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; - - fprintf(stderr, " [easy %p/magic %x/socket %d]", - (void *)sh->data, sh->data->magic, (int)sh->socket); -} -#endif - + return CURLM_OK; +} + +#if 0 +/* Debug-function, used like this: + * + * Curl_hash_print(multi->sockhash, debug_print_sock_hash); + * + * Enable the hash print function first by editing hash.c + */ +static void debug_print_sock_hash(void *p) +{ + struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; + + fprintf(stderr, " [easy %p/magic %x/socket %d]", + (void *)sh->data, sh->data->magic, (int)sh->socket); +} +#endif + static CURLcode multi_done(struct Curl_easy *data, CURLcode status, /* an error if this is called after an error was detected */ bool premature) -{ +{ CURLcode result; struct connectdata *conn = data->conn; unsigned int i; - + DEBUGF(infof(data, "multi_done\n")); if(data->state.done) @@ -720,29 +720,29 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, bool easy_owns_conn; struct Curl_llist_element *e; - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - /* Verify that we got a somewhat good easy handle too */ + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + /* Verify that we got a somewhat good easy handle too */ if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - - /* Prevent users from trying to remove same easy handle more than once */ - if(!data->multi) - return CURLM_OK; /* it is already removed so let's say it is fine! */ - + return CURLM_BAD_EASY_HANDLE; + + /* Prevent users from trying to remove same easy handle more than once */ + if(!data->multi) + return CURLM_OK; /* it is already removed so let's say it is fine! */ + /* Prevent users from trying to remove an easy handle from the wrong multi */ if(data->multi != multi) return CURLM_BAD_EASY_HANDLE; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - + premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE; easy_owns_conn = (data->conn && (data->conn->data == easy)) ? TRUE : FALSE; - + /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ if(premature) { @@ -750,7 +750,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, alive connections when this is removed */ multi->num_alive--; } - + if(data->conn && data->mstate > CURLM_STATE_DO && data->mstate < CURLM_STATE_COMPLETED) { @@ -760,22 +760,22 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, streamclose(data->conn, "Removed with partial response"); easy_owns_conn = TRUE; } - + if(data->conn) { - + /* we must call multi_done() here (if we still own the connection) so that we don't leave a half-baked one around */ if(easy_owns_conn) { - + /* multi_done() clears the association between the easy handle and the connection. - + Note that this ignores the return code simply because there's nothing really useful to do with it anyway! */ (void)multi_done(data, data->result, premature); - } + } } - + /* The timer must be shut down before data->multi is set to NULL, else the timenode will remain in the splay tree after curl_easy_cleanup is called. Do it after multi_done() in case that sets another time! */ @@ -785,26 +785,26 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, /* the handle was in the pending list waiting for an available connection, so go ahead and remove it */ Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); - + if(data->dns.hostcachetype == HCACHE_MULTI) { /* stop using the multi handle's DNS cache, *after* the possible multi_done() call above */ data->dns.hostcache = NULL; data->dns.hostcachetype = HCACHE_NONE; } - + Curl_wildcard_dtor(&data->wildcard); - + /* destroy the timeout list that is held in the easy handle, do this *after* multi_done() as that may actually call Curl_expire that uses this */ Curl_llist_destroy(&data->state.timeoutlist, NULL); - + /* change state without using multistate(), only to make singlesocket() do what we want */ data->mstate = CURLM_STATE_COMPLETED; singlesocket(multi, easy); /* to let the application know what sockets that vanish with this handle */ - + /* Remove the association between the connection and the handle */ Curl_detach_connnection(data); @@ -813,36 +813,36 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, Curl_conncache_foreach(data, data->state.conn_cache, data, &close_connect_only); } - + #ifdef USE_LIBPSL /* Remove the PSL association. */ if(data->psl == &multi->psl) data->psl = NULL; #endif - + /* as this was using a shared connection cache we clear the pointer to that since we're not part of that multi handle anymore */ data->state.conn_cache = NULL; data->multi = NULL; /* clear the association to this multi handle */ - + /* make sure there's no pending message in the queue sent from this easy handle */ - + for(e = multi->msglist.head; e; e = e->next) { struct Curl_message *msg = e->ptr; - + if(msg->extmsg.easy_handle == easy) { Curl_llist_remove(&multi->msglist, e, NULL); /* there can only be one from this specific handle */ break; } - } + } /* make the previous node point to our next */ if(data->prev) data->prev->next = data->next; - else + else multi->easyp = data->next; /* point to first node */ /* make our next point to our previous node */ @@ -857,14 +857,14 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, Curl_update_timer(multi); return CURLM_OK; -} - +} + /* Return TRUE if the application asked for multiplexing */ bool Curl_multiplex_wanted(const struct Curl_multi *multi) -{ +{ return (multi && (multi->multiplexing)); -} - +} + /* * Curl_detach_connnection() removes the given transfer from the connection. * @@ -872,13 +872,13 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi) * occasionally be called with the data->conn pointer already cleared. */ void Curl_detach_connnection(struct Curl_easy *data) -{ +{ struct connectdata *conn = data->conn; if(conn) Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); data->conn = NULL; -} - +} + /* * Curl_attach_connnection() attaches this transfer to this connection. * @@ -894,9 +894,9 @@ void Curl_attach_connnection(struct Curl_easy *data, &data->conn_queue); } -static int waitconnect_getsock(struct connectdata *conn, +static int waitconnect_getsock(struct connectdata *conn, curl_socket_t *sock) -{ +{ int i; int s = 0; int rc = 0; @@ -930,24 +930,24 @@ static int waitconnect_getsock(struct connectdata *conn, static int waitproxyconnect_getsock(struct connectdata *conn, curl_socket_t *sock) { - sock[0] = conn->sock[FIRSTSOCKET]; - - /* when we've sent a CONNECT to a proxy, we should rather wait for the - socket to become readable to be able to get the response headers */ + sock[0] = conn->sock[FIRSTSOCKET]; + + /* when we've sent a CONNECT to a proxy, we should rather wait for the + socket to become readable to be able to get the response headers */ if(conn->connect_state) - return GETSOCK_READSOCK(0); - - return GETSOCK_WRITESOCK(0); -} - -static int domore_getsock(struct connectdata *conn, + return GETSOCK_READSOCK(0); + + return GETSOCK_WRITESOCK(0); +} + +static int domore_getsock(struct connectdata *conn, curl_socket_t *socks) -{ - if(conn && conn->handler->domore_getsock) +{ + if(conn && conn->handler->domore_getsock) return conn->handler->domore_getsock(conn, socks); - return GETSOCK_BLANK; -} - + return GETSOCK_BLANK; +} + static int doing_getsock(struct connectdata *conn, curl_socket_t *socks) { @@ -972,101 +972,101 @@ static int protocol_getsock(struct connectdata *conn, array contains MAX_SOCKSPEREASYHANDLE entries. */ static int multi_getsock(struct Curl_easy *data, curl_socket_t *socks) -{ +{ /* The no connection case can happen when this is called from curl_multi_remove_handle() => singlesocket() => multi_getsock(). - */ + */ if(!data->conn) - return 0; - - if(data->mstate > CURLM_STATE_CONNECT && - data->mstate < CURLM_STATE_COMPLETED) { - /* Set up ownership correctly */ + return 0; + + if(data->mstate > CURLM_STATE_CONNECT && + data->mstate < CURLM_STATE_COMPLETED) { + /* Set up ownership correctly */ data->conn->data = data; - } - - switch(data->mstate) { - default: - return 0; - - case CURLM_STATE_WAITRESOLVE: + } + + switch(data->mstate) { + default: + return 0; + + case CURLM_STATE_WAITRESOLVE: return Curl_resolv_getsock(data->conn, socks); - - case CURLM_STATE_PROTOCONNECT: + + case CURLM_STATE_PROTOCONNECT: case CURLM_STATE_SENDPROTOCONNECT: return protocol_getsock(data->conn, socks); - - case CURLM_STATE_DO: - case CURLM_STATE_DOING: + + case CURLM_STATE_DO: + case CURLM_STATE_DOING: return doing_getsock(data->conn, socks); - - case CURLM_STATE_WAITPROXYCONNECT: + + case CURLM_STATE_WAITPROXYCONNECT: return waitproxyconnect_getsock(data->conn, socks); - case CURLM_STATE_WAITCONNECT: + case CURLM_STATE_WAITCONNECT: return waitconnect_getsock(data->conn, socks); - - case CURLM_STATE_DO_MORE: + + case CURLM_STATE_DO_MORE: return domore_getsock(data->conn, socks); - - case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch - to waiting for the same as the *PERFORM - states */ - case CURLM_STATE_PERFORM: + + case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch + to waiting for the same as the *PERFORM + states */ + case CURLM_STATE_PERFORM: return Curl_single_getsock(data->conn, socks); - } - -} - + } + +} + CURLMcode curl_multi_fdset(struct Curl_multi *multi, - fd_set *read_fd_set, fd_set *write_fd_set, - fd_set *exc_fd_set, int *max_fd) -{ - /* Scan through all the easy handles to get the file descriptors set. - Some easy handles may not have connected to the remote host yet, - and then we must make sure that is done. */ + fd_set *read_fd_set, fd_set *write_fd_set, + fd_set *exc_fd_set, int *max_fd) +{ + /* Scan through all the easy handles to get the file descriptors set. + Some easy handles may not have connected to the remote host yet, + and then we must make sure that is done. */ struct Curl_easy *data; int this_max_fd = -1; - curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; - int i; - (void)exc_fd_set; /* not used */ - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - + curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; + int i; + (void)exc_fd_set; /* not used */ + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; data = multi->easyp; - while(data) { + while(data) { int bitmap = multi_getsock(data, sockbunch); - + for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { - FD_SET(sockbunch[i], read_fd_set); - s = sockbunch[i]; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { - FD_SET(sockbunch[i], write_fd_set); - s = sockbunch[i]; - } - if(s == CURL_SOCKET_BAD) - /* this socket is unused, break out of loop */ - break; + curl_socket_t s = CURL_SOCKET_BAD; + + if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { + FD_SET(sockbunch[i], read_fd_set); + s = sockbunch[i]; + } + if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { + FD_SET(sockbunch[i], write_fd_set); + s = sockbunch[i]; + } + if(s == CURL_SOCKET_BAD) + /* this socket is unused, break out of loop */ + break; if((int)s > this_max_fd) this_max_fd = (int)s; - } - - data = data->next; /* check next handle */ - } - - *max_fd = this_max_fd; - - return CURLM_OK; -} - + } + + data = data->next; /* check next handle */ + } + + *max_fd = this_max_fd; + + return CURLM_OK; +} + #define NUM_POLLS_ON_STACK 10 static CURLMcode Curl_multi_wait(struct Curl_multi *multi, @@ -1076,52 +1076,52 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, int *ret, bool extrawait, /* when no socket, wait */ bool use_wakeup) -{ +{ struct Curl_easy *data; - curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; - int bitmap; - unsigned int i; - unsigned int nfds = 0; - unsigned int curlfds; - long timeout_internal; + curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; + int bitmap; + unsigned int i; + unsigned int nfds = 0; + unsigned int curlfds; + long timeout_internal; int retcode = 0; struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct pollfd *ufds = &a_few_on_stack[0]; bool ufds_malloc = FALSE; - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - + if(timeout_ms < 0) return CURLM_BAD_FUNCTION_ARGUMENT; - /* Count up how many fds we have from the multi handle */ + /* Count up how many fds we have from the multi handle */ data = multi->easyp; - while(data) { + while(data) { bitmap = multi_getsock(data, sockbunch); - + for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if(bitmap & GETSOCK_READSOCK(i)) { - ++nfds; - s = sockbunch[i]; - } - if(bitmap & GETSOCK_WRITESOCK(i)) { - ++nfds; - s = sockbunch[i]; - } - if(s == CURL_SOCKET_BAD) { - break; - } - } - - data = data->next; /* check next handle */ - } - + curl_socket_t s = CURL_SOCKET_BAD; + + if(bitmap & GETSOCK_READSOCK(i)) { + ++nfds; + s = sockbunch[i]; + } + if(bitmap & GETSOCK_WRITESOCK(i)) { + ++nfds; + s = sockbunch[i]; + } + if(s == CURL_SOCKET_BAD) { + break; + } + } + + data = data->next; /* check next handle */ + } + /* If the internally desired timeout is actually shorter than requested from the outside, then use the shorter time! But only if the internal timer is actually larger than -1! */ @@ -1129,9 +1129,9 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) timeout_ms = (int)timeout_internal; - curlfds = nfds; /* number of internal file descriptors */ - nfds += extra_nfds; /* add the externally provided ones */ - + curlfds = nfds; /* number of internal file descriptors */ + nfds += extra_nfds; /* add the externally provided ones */ + #ifdef ENABLE_WAKEUP if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { ++nfds; @@ -1147,55 +1147,55 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, if(!ufds) return CURLM_OUT_OF_MEMORY; ufds_malloc = TRUE; - } - nfds = 0; - - /* only do the second loop if we found descriptors in the first stage run - above */ - - if(curlfds) { - /* Add the curl handles to our pollfds first */ + } + nfds = 0; + + /* only do the second loop if we found descriptors in the first stage run + above */ + + if(curlfds) { + /* Add the curl handles to our pollfds first */ data = multi->easyp; - while(data) { + while(data) { bitmap = multi_getsock(data, sockbunch); - + for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if(bitmap & GETSOCK_READSOCK(i)) { - ufds[nfds].fd = sockbunch[i]; - ufds[nfds].events = POLLIN; - ++nfds; - s = sockbunch[i]; - } - if(bitmap & GETSOCK_WRITESOCK(i)) { - ufds[nfds].fd = sockbunch[i]; - ufds[nfds].events = POLLOUT; - ++nfds; - s = sockbunch[i]; - } - if(s == CURL_SOCKET_BAD) { - break; - } - } - - data = data->next; /* check next handle */ - } - } - - /* Add external file descriptions from poll-like struct curl_waitfd */ - for(i = 0; i < extra_nfds; i++) { - ufds[nfds].fd = extra_fds[i].fd; - ufds[nfds].events = 0; - if(extra_fds[i].events & CURL_WAIT_POLLIN) - ufds[nfds].events |= POLLIN; - if(extra_fds[i].events & CURL_WAIT_POLLPRI) - ufds[nfds].events |= POLLPRI; - if(extra_fds[i].events & CURL_WAIT_POLLOUT) - ufds[nfds].events |= POLLOUT; - ++nfds; - } - + curl_socket_t s = CURL_SOCKET_BAD; + + if(bitmap & GETSOCK_READSOCK(i)) { + ufds[nfds].fd = sockbunch[i]; + ufds[nfds].events = POLLIN; + ++nfds; + s = sockbunch[i]; + } + if(bitmap & GETSOCK_WRITESOCK(i)) { + ufds[nfds].fd = sockbunch[i]; + ufds[nfds].events = POLLOUT; + ++nfds; + s = sockbunch[i]; + } + if(s == CURL_SOCKET_BAD) { + break; + } + } + + data = data->next; /* check next handle */ + } + } + + /* Add external file descriptions from poll-like struct curl_waitfd */ + for(i = 0; i < extra_nfds; i++) { + ufds[nfds].fd = extra_fds[i].fd; + ufds[nfds].events = 0; + if(extra_fds[i].events & CURL_WAIT_POLLIN) + ufds[nfds].events |= POLLIN; + if(extra_fds[i].events & CURL_WAIT_POLLPRI) + ufds[nfds].events |= POLLPRI; + if(extra_fds[i].events & CURL_WAIT_POLLOUT) + ufds[nfds].events |= POLLOUT; + ++nfds; + } + #ifdef ENABLE_WAKEUP if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { ufds[nfds].fd = multi->wakeup_pair[0]; @@ -1204,26 +1204,26 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, } #endif - if(nfds) { - /* wait... */ + if(nfds) { + /* wait... */ int pollrc = Curl_poll(ufds, nfds, timeout_ms); if(pollrc > 0) { retcode = pollrc; - /* copy revents results from the poll to the curl_multi_wait poll - struct, the bit values of the actual underlying poll() implementation - may not be the same as the ones in the public libcurl API! */ + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ for(i = 0; i < extra_nfds; i++) { - unsigned short mask = 0; + unsigned short mask = 0; unsigned r = ufds[curlfds + i].revents; - - if(r & POLLIN) - mask |= CURL_WAIT_POLLIN; - if(r & POLLOUT) - mask |= CURL_WAIT_POLLOUT; - if(r & POLLPRI) - mask |= CURL_WAIT_POLLPRI; + + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; extra_fds[i].revents = mask; - } + } #ifdef ENABLE_WAKEUP if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { @@ -1249,12 +1249,12 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, } } #endif - } - } - + } + } + if(ufds_malloc) free(ufds); - if(ret) + if(ret) *ret = retcode; if(!extrawait || nfds) /* if any socket was checked */ @@ -1274,9 +1274,9 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi, } } - return CURLM_OK; -} - + return CURLM_OK; +} + CURLMcode curl_multi_wait(struct Curl_multi *multi, struct curl_waitfd extra_fds[], unsigned int extra_nfds, @@ -1560,32 +1560,32 @@ CURLcode Curl_preconnect(struct Curl_easy *data) } -static CURLMcode multi_runsingle(struct Curl_multi *multi, +static CURLMcode multi_runsingle(struct Curl_multi *multi, struct curltime *nowp, struct Curl_easy *data) -{ - struct Curl_message *msg = NULL; - bool connected; - bool async; +{ + struct Curl_message *msg = NULL; + bool connected; + bool async; bool protocol_connected = FALSE; - bool dophase_done = FALSE; - bool done = FALSE; + bool dophase_done = FALSE; + bool done = FALSE; CURLMcode rc; CURLcode result = CURLE_OK; timediff_t timeout_ms; timediff_t recv_timeout_ms; timediff_t send_timeout_ms; - int control; - - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - - do { + int control; + + if(!GOOD_EASY_HANDLE(data)) + return CURLM_BAD_EASY_HANDLE; + + do { /* A "stream" here is a logical stream if the protocol can handle that (HTTP/2), or the full connection for older protocols */ bool stream_error = FALSE; rc = CURLM_OK; - + if(multi_ischanged(multi, TRUE)) { DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n")); process_pending_handles(multi); /* multiplexed */ @@ -1593,30 +1593,30 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(data->conn && data->mstate > CURLM_STATE_CONNECT && data->mstate < CURLM_STATE_COMPLETED) { - /* Make sure we set the connection's current owner */ + /* Make sure we set the connection's current owner */ data->conn->data = data; } - + if(data->conn && - (data->mstate >= CURLM_STATE_CONNECT) && - (data->mstate < CURLM_STATE_COMPLETED)) { - /* we need to wait for the connect state as only then is the start time - stored, but we must not check already completed handles */ + (data->mstate >= CURLM_STATE_CONNECT) && + (data->mstate < CURLM_STATE_COMPLETED)) { + /* we need to wait for the connect state as only then is the start time + stored, but we must not check already completed handles */ timeout_ms = Curl_timeleft(data, nowp, (data->mstate <= CURLM_STATE_DO)? - TRUE:FALSE); - - if(timeout_ms < 0) { - /* Handle timed out */ - if(data->mstate == CURLM_STATE_WAITRESOLVE) + TRUE:FALSE); + + if(timeout_ms < 0) { + /* Handle timed out */ + if(data->mstate == CURLM_STATE_WAITRESOLVE) failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T " milliseconds", Curl_timediff(*nowp, data->progress.t_startsingle)); - else if(data->mstate == CURLM_STATE_WAITCONNECT) + else if(data->mstate == CURLM_STATE_WAITCONNECT) failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T " milliseconds", Curl_timediff(*nowp, data->progress.t_startsingle)); - else { + else { struct SingleRequest *k = &data->req; if(k->size != -1) { failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T @@ -1632,8 +1632,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_timediff(*nowp, data->progress.t_startsingle), k->bytecount); } - } - + } + /* Force connection closed if the connection has indeed been used */ if(data->mstate > CURLM_STATE_DO) { streamclose(data->conn, "Disconnected with pending data"); @@ -1643,29 +1643,29 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, (void)multi_done(data, result, TRUE); /* Skip the statemachine and go directly to error handling section. */ goto statemachine_end; - } - } - - switch(data->mstate) { - case CURLM_STATE_INIT: - /* init this transfer. */ + } + } + + switch(data->mstate) { + case CURLM_STATE_INIT: + /* init this transfer. */ result = Curl_pretransfer(data); - + if(!result) { - /* after init, go CONNECT */ - multistate(data, CURLM_STATE_CONNECT); + /* after init, go CONNECT */ + multistate(data, CURLM_STATE_CONNECT); *nowp = Curl_pgrsTime(data, TIMER_STARTOP); rc = CURLM_CALL_MULTI_PERFORM; - } - break; - - case CURLM_STATE_CONNECT_PEND: - /* We will stay here until there is a connection available. Then - we try again in the CURLM_STATE_CONNECT state. */ - break; - - case CURLM_STATE_CONNECT: - /* Connect. We want to get a connection identifier filled in. */ + } + break; + + case CURLM_STATE_CONNECT_PEND: + /* We will stay here until there is a connection available. Then + we try again in the CURLM_STATE_CONNECT state. */ + break; + + case CURLM_STATE_CONNECT: + /* Connect. We want to get a connection identifier filled in. */ /* init this transfer. */ result = Curl_preconnect(data); if(result) @@ -1680,27 +1680,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = Curl_connect(data, &async, &protocol_connected); if(CURLE_NO_CONNECTION_AVAILABLE == result) { - /* There was no connection available. We will go to the pending - state and wait for an available connection. */ - multistate(data, CURLM_STATE_CONNECT_PEND); + /* There was no connection available. We will go to the pending + state and wait for an available connection. */ + multistate(data, CURLM_STATE_CONNECT_PEND); /* add this handle to the list of connect-pending handles */ Curl_llist_insert_next(&multi->pending, multi->pending.tail, data, &data->connect_queue); result = CURLE_OK; - break; - } + break; + } else if(data->state.previouslypending) { /* this transfer comes from the pending queue so try move another */ infof(data, "Transfer was pending, now try another\n"); process_pending_handles(data->multi); } - + if(!result) { if(async) /* We're now waiting for an asynchronous name lookup */ multistate(data, CURLM_STATE_WAITRESOLVE); - else { + else { /* after the connect has been sent off, go WAITCONNECT unless the protocol connect is already done and we can go directly to WAITDO or DO! */ @@ -1708,25 +1708,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(protocol_connected) multistate(data, CURLM_STATE_DO); - else { -#ifndef CURL_DISABLE_HTTP + else { +#ifndef CURL_DISABLE_HTTP if(Curl_connect_ongoing(data->conn)) multistate(data, CURLM_STATE_WAITPROXYCONNECT); else -#endif +#endif multistate(data, CURLM_STATE_WAITCONNECT); - } - } - } - break; - - case CURLM_STATE_WAITRESOLVE: - /* awaiting an asynch name resolve to complete */ - { - struct Curl_dns_entry *dns = NULL; + } + } + } + break; + + case CURLM_STATE_WAITRESOLVE: + /* awaiting an asynch name resolve to complete */ + { + struct Curl_dns_entry *dns = NULL; struct connectdata *conn = data->conn; const char *hostname; - + DEBUGASSERT(conn); #ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy) @@ -1738,9 +1738,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else hostname = conn->host.name; - /* check if we have the name resolved by now */ + /* check if we have the name resolved by now */ dns = Curl_fetch_addr(conn, hostname, (int)conn->port); - + if(dns) { #ifdef CURLRES_ASYNCH conn->async.dns = dns; @@ -1753,50 +1753,50 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!dns) result = Curl_resolv_check(data->conn, &dns); - /* Update sockets here, because the socket(s) may have been - closed and the application thus needs to be told, even if it - is likely that the same socket(s) will again be used further - down. If the name has not yet been resolved, it is likely - that new sockets have been opened in an attempt to contact - another resolver. */ - singlesocket(multi, data); - - if(dns) { - /* Perform the next step in the connection phase, and then move on - to the WAITCONNECT state */ + /* Update sockets here, because the socket(s) may have been + closed and the application thus needs to be told, even if it + is likely that the same socket(s) will again be used further + down. If the name has not yet been resolved, it is likely + that new sockets have been opened in an attempt to contact + another resolver. */ + singlesocket(multi, data); + + if(dns) { + /* Perform the next step in the connection phase, and then move on + to the WAITCONNECT state */ result = Curl_once_resolved(data->conn, &protocol_connected); - + if(result) /* if Curl_once_resolved() returns failure, the connection struct - is already freed and gone */ + is already freed and gone */ data->conn = NULL; /* no more connection */ - else { - /* call again please so that we get the next socket setup */ + else { + /* call again please so that we get the next socket setup */ rc = CURLM_CALL_MULTI_PERFORM; if(protocol_connected) multistate(data, CURLM_STATE_DO); - else { -#ifndef CURL_DISABLE_HTTP + else { +#ifndef CURL_DISABLE_HTTP if(Curl_connect_ongoing(data->conn)) - multistate(data, CURLM_STATE_WAITPROXYCONNECT); - else -#endif - multistate(data, CURLM_STATE_WAITCONNECT); - } - } - } - + multistate(data, CURLM_STATE_WAITPROXYCONNECT); + else +#endif + multistate(data, CURLM_STATE_WAITCONNECT); + } + } + } + if(result) { - /* failure detected */ + /* failure detected */ stream_error = TRUE; - break; - } - } - break; - -#ifndef CURL_DISABLE_HTTP - case CURLM_STATE_WAITPROXYCONNECT: - /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ + break; + } + } + break; + +#ifndef CURL_DISABLE_HTTP + case CURLM_STATE_WAITPROXYCONNECT: + /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ DEBUGASSERT(data->conn); result = Curl_http_connect(data->conn, &protocol_connected); #ifndef CURL_DISABLE_PROXY @@ -1805,8 +1805,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* connect back to proxy again */ result = CURLE_OK; multi_done(data, CURLE_OK, FALSE); - multistate(data, CURLM_STATE_CONNECT); - } + multistate(data, CURLM_STATE_CONNECT); + } else #endif if(!result) { @@ -1823,10 +1823,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else stream_error = TRUE; - break; -#endif - - case CURLM_STATE_WAITCONNECT: + break; +#endif + + case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch TCP connect */ DEBUGASSERT(data->conn); result = Curl_is_connected(data->conn, FIRSTSOCKET, &connected); @@ -1851,16 +1851,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #else multistate(data, CURLM_STATE_SENDPROTOCONNECT); #endif - } + } else if(result) { - /* failure detected */ + /* failure detected */ Curl_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; - break; - } + break; + } break; - + case CURLM_STATE_SENDPROTOCONNECT: result = protocol_connect(data->conn, &protocol_connected); if(!result && !protocol_connected) @@ -1870,187 +1870,187 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* protocol connect has completed, go WAITDO or DO */ multistate(data, CURLM_STATE_DO); rc = CURLM_CALL_MULTI_PERFORM; - } + } else { /* failure detected */ Curl_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; } - break; - - case CURLM_STATE_PROTOCONNECT: - /* protocol-specific connect phase */ + break; + + case CURLM_STATE_PROTOCONNECT: + /* protocol-specific connect phase */ result = protocol_connecting(data->conn, &protocol_connected); if(!result && protocol_connected) { - /* after the connect has completed, go WAITDO or DO */ + /* after the connect has completed, go WAITDO or DO */ multistate(data, CURLM_STATE_DO); rc = CURLM_CALL_MULTI_PERFORM; - } + } else if(result) { - /* failure detected */ - Curl_posttransfer(data); + /* failure detected */ + Curl_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; - } - break; - - case CURLM_STATE_DO: - if(data->set.connect_only) { - /* keep connection open for application to use the socket */ + } + break; + + case CURLM_STATE_DO: + if(data->set.connect_only) { + /* keep connection open for application to use the socket */ connkeep(data->conn, "CONNECT_ONLY"); - multistate(data, CURLM_STATE_DONE); + multistate(data, CURLM_STATE_DONE); result = CURLE_OK; rc = CURLM_CALL_MULTI_PERFORM; - } - else { - /* Perform the protocol's DO action */ + } + else { + /* Perform the protocol's DO action */ result = multi_do(data, &dophase_done); - + /* When multi_do() returns failure, data->conn might be NULL! */ - + if(!result) { - if(!dophase_done) { + if(!dophase_done) { #ifndef CURL_DISABLE_FTP - /* some steps needed for wildcard matching */ + /* some steps needed for wildcard matching */ if(data->state.wildcardmatch) { - struct WildcardData *wc = &data->wildcard; - if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { - /* skip some states if it is important */ + struct WildcardData *wc = &data->wildcard; + if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { + /* skip some states if it is important */ multi_done(data, CURLE_OK, FALSE); - multistate(data, CURLM_STATE_DONE); + multistate(data, CURLM_STATE_DONE); rc = CURLM_CALL_MULTI_PERFORM; - break; - } - } + break; + } + } #endif - /* DO was not completed in one function call, we must continue - DOING... */ - multistate(data, CURLM_STATE_DOING); + /* DO was not completed in one function call, we must continue + DOING... */ + multistate(data, CURLM_STATE_DOING); rc = CURLM_OK; - } - - /* after DO, go DO_DONE... or DO_MORE */ + } + + /* after DO, go DO_DONE... or DO_MORE */ else if(data->conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax - and wait a little while first */ - multistate(data, CURLM_STATE_DO_MORE); + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + multistate(data, CURLM_STATE_DO_MORE); rc = CURLM_OK; - } - else { - /* we're done with the DO, now DO_DONE */ - multistate(data, CURLM_STATE_DO_DONE); + } + else { + /* we're done with the DO, now DO_DONE */ + multistate(data, CURLM_STATE_DO_DONE); rc = CURLM_CALL_MULTI_PERFORM; - } - } + } + } else if((CURLE_SEND_ERROR == result) && data->conn->bits.reuse) { - /* - * In this situation, a connection that we were trying to use - * may have unexpectedly died. If possible, send the connection - * back to the CONNECT phase so we can try again. - */ - char *newurl = NULL; + /* + * In this situation, a connection that we were trying to use + * may have unexpectedly died. If possible, send the connection + * back to the CONNECT phase so we can try again. + */ + char *newurl = NULL; followtype follow = FOLLOW_NONE; - CURLcode drc; - + CURLcode drc; + drc = Curl_retry_request(data->conn, &newurl); - if(drc) { - /* a failure here pretty much implies an out of memory */ + if(drc) { + /* a failure here pretty much implies an out of memory */ result = drc; stream_error = TRUE; - } - - Curl_posttransfer(data); + } + + Curl_posttransfer(data); drc = multi_done(data, result, FALSE); - - /* When set to retry the connection, we must to go back to - * the CONNECT state */ + + /* When set to retry the connection, we must to go back to + * the CONNECT state */ if(newurl) { if(!drc || (drc == CURLE_SEND_ERROR)) { - follow = FOLLOW_RETRY; - drc = Curl_follow(data, newurl, follow); + follow = FOLLOW_RETRY; + drc = Curl_follow(data, newurl, follow); if(!drc) { - multistate(data, CURLM_STATE_CONNECT); + multistate(data, CURLM_STATE_CONNECT); rc = CURLM_CALL_MULTI_PERFORM; result = CURLE_OK; - } - else { - /* Follow failed */ + } + else { + /* Follow failed */ result = drc; - } - } - else { - /* done didn't return OK or SEND_ERROR */ + } + } + else { + /* done didn't return OK or SEND_ERROR */ result = drc; - } - } - else { - /* Have error handler disconnect conn if we can't retry */ + } + } + else { + /* Have error handler disconnect conn if we can't retry */ stream_error = TRUE; - } + } free(newurl); - } - else { - /* failure detected */ - Curl_posttransfer(data); + } + else { + /* failure detected */ + Curl_posttransfer(data); if(data->conn) multi_done(data, result, FALSE); stream_error = TRUE; - } - } - break; - - case CURLM_STATE_DOING: - /* we continue DOING until the DO phase is complete */ + } + } + break; + + case CURLM_STATE_DOING: + /* we continue DOING until the DO phase is complete */ DEBUGASSERT(data->conn); result = protocol_doing(data->conn, &dophase_done); if(!result) { - if(dophase_done) { - /* after DO, go DO_DONE or DO_MORE */ + if(dophase_done) { + /* after DO, go DO_DONE or DO_MORE */ multistate(data, data->conn->bits.do_more? - CURLM_STATE_DO_MORE: - CURLM_STATE_DO_DONE); + CURLM_STATE_DO_MORE: + CURLM_STATE_DO_DONE); rc = CURLM_CALL_MULTI_PERFORM; - } /* dophase_done */ - } - else { - /* failure detected */ - Curl_posttransfer(data); + } /* dophase_done */ + } + else { + /* failure detected */ + Curl_posttransfer(data); multi_done(data, result, FALSE); stream_error = TRUE; - } - break; - - case CURLM_STATE_DO_MORE: - /* - * When we are connected, DO MORE and then go DO_DONE - */ + } + break; + + case CURLM_STATE_DO_MORE: + /* + * When we are connected, DO MORE and then go DO_DONE + */ DEBUGASSERT(data->conn); result = multi_do_more(data->conn, &control); - + if(!result) { - if(control) { - /* if positive, advance to DO_DONE - if negative, go back to DOING */ + if(control) { + /* if positive, advance to DO_DONE + if negative, go back to DOING */ multistate(data, control == 1? - CURLM_STATE_DO_DONE: - CURLM_STATE_DOING); + CURLM_STATE_DO_DONE: + CURLM_STATE_DOING); rc = CURLM_CALL_MULTI_PERFORM; - } - else - /* stay in DO_MORE */ + } + else + /* stay in DO_MORE */ rc = CURLM_OK; - } - else { - /* failure detected */ - Curl_posttransfer(data); + } + else { + /* failure detected */ + Curl_posttransfer(data); multi_done(data, result, FALSE); stream_error = TRUE; - } - break; - - case CURLM_STATE_DO_DONE: + } + break; + + case CURLM_STATE_DO_DONE: DEBUGASSERT(data->conn); if(data->conn->bits.multiplex) /* Check if we can move pending requests to send pipe */ @@ -2071,16 +2071,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(data, CURLM_STATE_DONE); } rc = CURLM_CALL_MULTI_PERFORM; - break; - - case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ + break; + + case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ DEBUGASSERT(data->conn); - /* if both rates are within spec, resume transfer */ + /* if both rates are within spec, resume transfer */ if(Curl_pgrsUpdate(data->conn)) result = CURLE_ABORTED_BY_CALLBACK; - else + else result = Curl_speedcheck(data, *nowp); - + if(!result) { send_timeout_ms = 0; if(data->set.max_send_speed > 0) @@ -2109,15 +2109,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); } - break; - - case CURLM_STATE_PERFORM: + break; + + case CURLM_STATE_PERFORM: { - char *newurl = NULL; - bool retry = FALSE; + char *newurl = NULL; + bool retry = FALSE; bool comeback = FALSE; DEBUGASSERT(data->state.buffer); - /* check if over send speed */ + /* check if over send speed */ send_timeout_ms = 0; if(data->set.max_send_speed > 0) send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded, @@ -2125,8 +2125,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->set.max_send_speed, data->progress.ul_limit_start, *nowp); - - /* check if over recv speed */ + + /* check if over recv speed */ recv_timeout_ms = 0; if(data->set.max_recv_speed > 0) recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded, @@ -2134,42 +2134,42 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->set.max_recv_speed, data->progress.dl_limit_start, *nowp); - + if(send_timeout_ms || recv_timeout_ms) { Curl_ratelimit(data, *nowp); - multistate(data, CURLM_STATE_TOOFAST); + multistate(data, CURLM_STATE_TOOFAST); if(send_timeout_ms >= recv_timeout_ms) Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); else Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); - break; - } - - /* read/write data if it is ready to do so */ + break; + } + + /* read/write data if it is ready to do so */ result = Curl_readwrite(data->conn, data, &done, &comeback); - + if(done || (result == CURLE_RECV_ERROR)) { - /* If CURLE_RECV_ERROR happens early enough, we assume it was a race - * condition and the server closed the re-used connection exactly when - * we wanted to use it, so figure out if that is indeed the case. - */ + /* If CURLE_RECV_ERROR happens early enough, we assume it was a race + * condition and the server closed the re-used connection exactly when + * we wanted to use it, so figure out if that is indeed the case. + */ CURLcode ret = Curl_retry_request(data->conn, &newurl); - if(!ret) - retry = (newurl)?TRUE:FALSE; + if(!ret) + retry = (newurl)?TRUE:FALSE; else if(!result) result = ret; - - if(retry) { - /* if we are to retry, set the result to OK and consider the - request as done */ + + if(retry) { + /* if we are to retry, set the result to OK and consider the + request as done */ result = CURLE_OK; - done = TRUE; - } - } + done = TRUE; + } + } else if((CURLE_HTTP2_STREAM == result) && Curl_h2_http_1_1_error(data->conn)) { CURLcode ret = Curl_retry_request(data->conn, &newurl); - + if(!ret) { infof(data, "Downgrades to HTTP/1.1!\n"); data->set.httpversion = CURL_HTTP_VERSION_1_1; @@ -2189,72 +2189,72 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } if(result) { - /* - * The transfer phase returned error, we mark the connection to get - * closed to prevent being re-used. This is because we can't possibly - * know if the connection is in a good shape or not now. Unless it is - * a protocol which uses two "channels" like FTP, as then the error - * happened in the data connection. - */ - + /* + * The transfer phase returned error, we mark the connection to get + * closed to prevent being re-used. This is because we can't possibly + * know if the connection is in a good shape or not now. Unless it is + * a protocol which uses two "channels" like FTP, as then the error + * happened in the data connection. + */ + if(!(data->conn->handler->flags & PROTOPT_DUAL) && result != CURLE_HTTP2_STREAM) streamclose(data->conn, "Transfer returned error"); - - Curl_posttransfer(data); + + Curl_posttransfer(data); multi_done(data, result, TRUE); - } - else if(done) { + } + else if(done) { followtype follow = FOLLOW_NONE; - - /* call this even if the readwrite function returned error */ - Curl_posttransfer(data); - - /* When we follow redirects or is set to retry the connection, we must - to go back to the CONNECT state */ - if(data->req.newurl || retry) { - if(!retry) { - /* if the URL is a follow-location and not just a retried request - then figure out the URL here */ + + /* call this even if the readwrite function returned error */ + Curl_posttransfer(data); + + /* When we follow redirects or is set to retry the connection, we must + to go back to the CONNECT state */ + if(data->req.newurl || retry) { + if(!retry) { + /* if the URL is a follow-location and not just a retried request + then figure out the URL here */ free(newurl); - newurl = data->req.newurl; - data->req.newurl = NULL; - follow = FOLLOW_REDIR; - } - else - follow = FOLLOW_RETRY; + newurl = data->req.newurl; + data->req.newurl = NULL; + follow = FOLLOW_REDIR; + } + else + follow = FOLLOW_RETRY; (void)multi_done(data, CURLE_OK, FALSE); /* multi_done() might return CURLE_GOT_NOTHING */ result = Curl_follow(data, newurl, follow); if(!result) { multistate(data, CURLM_STATE_CONNECT); rc = CURLM_CALL_MULTI_PERFORM; - } + } free(newurl); - } - else { - /* after the transfer is done, go DONE */ - - /* but first check to see if we got a location info even though we're - not following redirects */ - if(data->req.location) { + } + else { + /* after the transfer is done, go DONE */ + + /* but first check to see if we got a location info even though we're + not following redirects */ + if(data->req.location) { free(newurl); - newurl = data->req.location; - data->req.location = NULL; + newurl = data->req.location; + data->req.location = NULL; result = Curl_follow(data, newurl, FOLLOW_FAKE); free(newurl); if(result) { stream_error = TRUE; result = multi_done(data, result, TRUE); } - } - + } + if(!result) { multistate(data, CURLM_STATE_DONE); rc = CURLM_CALL_MULTI_PERFORM; } - } - } + } + } else if(comeback) { /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer won't get stuck on this transfer at the expense of other concurrent @@ -2262,13 +2262,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_expire(data, 0, EXPIRE_RUN_NOW); rc = CURLM_OK; } - break; + break; } - - case CURLM_STATE_DONE: - /* this state is highly transient, so run another loop after this */ + + case CURLM_STATE_DONE: + /* this state is highly transient, so run another loop after this */ rc = CURLM_CALL_MULTI_PERFORM; - + if(data->conn) { CURLcode res; @@ -2276,71 +2276,71 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Check if we can move pending requests to connection */ process_pending_handles(multi); /* multiplexing */ - /* post-transfer command */ + /* post-transfer command */ res = multi_done(data, result, FALSE); /* allow a previously set error code take precedence */ if(!result) result = res; - /* + /* * If there are other handles on the connection, multi_done won't set * conn to NULL. In such a case, curl_multi_remove_handle() can - * access free'd data, if the connection is free'd and the handle - * removed before we perform the processing in CURLM_STATE_COMPLETED - */ + * access free'd data, if the connection is free'd and the handle + * removed before we perform the processing in CURLM_STATE_COMPLETED + */ Curl_detach_connnection(data); - } - + } + #ifndef CURL_DISABLE_FTP if(data->state.wildcardmatch) { - if(data->wildcard.state != CURLWC_DONE) { - /* if a wildcard is set and we are not ending -> lets start again - with CURLM_STATE_INIT */ - multistate(data, CURLM_STATE_INIT); - break; - } - } + if(data->wildcard.state != CURLWC_DONE) { + /* if a wildcard is set and we are not ending -> lets start again + with CURLM_STATE_INIT */ + multistate(data, CURLM_STATE_INIT); + break; + } + } #endif - /* after we have DONE what we're supposed to do, go COMPLETED, and + /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the multi_done() returned! */ - multistate(data, CURLM_STATE_COMPLETED); - break; - - case CURLM_STATE_COMPLETED: - break; - - case CURLM_STATE_MSGSENT: + multistate(data, CURLM_STATE_COMPLETED); + break; + + case CURLM_STATE_COMPLETED: + break; + + case CURLM_STATE_MSGSENT: data->result = result; - return CURLM_OK; /* do nothing */ - - default: - return CURLM_INTERNAL_ERROR; - } + return CURLM_OK; /* do nothing */ + + default: + return CURLM_INTERNAL_ERROR; + } statemachine_end: - - if(data->mstate < CURLM_STATE_COMPLETED) { + + if(data->mstate < CURLM_STATE_COMPLETED) { if(result) { - /* - * If an error was returned, and we aren't in completed state now, - * then we go to completed and consider this transfer aborted. - */ - - /* NOTE: no attempt to disconnect connections must be made - in the case blocks above - cleanup happens only here */ - + /* + * If an error was returned, and we aren't in completed state now, + * then we go to completed and consider this transfer aborted. + */ + + /* NOTE: no attempt to disconnect connections must be made + in the case blocks above - cleanup happens only here */ + /* Check if we can move pending requests to send pipe */ process_pending_handles(multi); /* connection */ - + if(data->conn) { if(stream_error) { /* Don't attempt to send data over a connection that timed out */ bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; struct connectdata *conn = data->conn; - + /* This is where we make sure that the conn pointer is reset. - We don't have to do this in every case block above where a - failure is detected */ + We don't have to do this in every case block above where a + failure is detected */ Curl_detach_connnection(data); /* remove connection from cache */ @@ -2348,30 +2348,30 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* disconnect properly */ Curl_disconnect(data, conn, dead_connection); - } - } - else if(data->mstate == CURLM_STATE_CONNECT) { - /* Curl_connect() failed */ - (void)Curl_posttransfer(data); - } - - multistate(data, CURLM_STATE_COMPLETED); + } + } + else if(data->mstate == CURLM_STATE_CONNECT) { + /* Curl_connect() failed */ + (void)Curl_posttransfer(data); + } + + multistate(data, CURLM_STATE_COMPLETED); rc = CURLM_CALL_MULTI_PERFORM; - } - /* if there's still a connection to use, call the progress function */ + } + /* if there's still a connection to use, call the progress function */ else if(data->conn && Curl_pgrsUpdate(data->conn)) { - /* aborted due to progress callback return code must close the - connection */ + /* aborted due to progress callback return code must close the + connection */ result = CURLE_ABORTED_BY_CALLBACK; streamclose(data->conn, "Aborted by callback"); - - /* if not yet in DONE state, go there, otherwise COMPLETED */ - multistate(data, (data->mstate < CURLM_STATE_DONE)? - CURLM_STATE_DONE: CURLM_STATE_COMPLETED); + + /* if not yet in DONE state, go there, otherwise COMPLETED */ + multistate(data, (data->mstate < CURLM_STATE_DONE)? + CURLM_STATE_DONE: CURLM_STATE_COMPLETED); rc = CURLM_CALL_MULTI_PERFORM; - } - } - + } + } + if(CURLM_STATE_COMPLETED == data->mstate) { if(data->set.fmultidone) { /* signal via callback instead */ @@ -2380,117 +2380,117 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* now fill in the Curl_message with this info */ msg = &data->msg; - + msg->extmsg.msg = CURLMSG_DONE; msg->extmsg.easy_handle = data; msg->extmsg.data.result = result; - + rc = multi_addmsg(multi, msg); DEBUGASSERT(!data->conn); } multistate(data, CURLM_STATE_MSGSENT); } } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); - + data->result = result; return rc; -} - - +} + + CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) -{ +{ struct Curl_easy *data; CURLMcode returncode = CURLM_OK; - struct Curl_tree *t; + struct Curl_tree *t; struct curltime now = Curl_now(); - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; data = multi->easyp; - while(data) { - CURLMcode result; + while(data) { + CURLMcode result; SIGPIPE_VARIABLE(pipe_st); - + sigpipe_ignore(data, &pipe_st); result = multi_runsingle(multi, &now, data); sigpipe_restore(&pipe_st); - - if(result) - returncode = result; - - data = data->next; /* operate on next handle */ - } - - /* - * Simply remove all expired timers from the splay since handles are dealt - * with unconditionally by this function and curl_multi_timeout() requires - * that already passed/handled expire times are removed from the splay. - * - * It is important that the 'now' value is set at the entry of this function - * and not for the current time as it may have ticked a little while since - * then and then we risk this loop to remove timers that actually have not - * been handled! - */ - do { - multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) - /* the removed may have another timeout in queue */ - (void)add_next_timeout(now, multi, t->payload); - - } while(t); - - *running_handles = multi->num_alive; - - if(CURLM_OK >= returncode) + + if(result) + returncode = result; + + data = data->next; /* operate on next handle */ + } + + /* + * Simply remove all expired timers from the splay since handles are dealt + * with unconditionally by this function and curl_multi_timeout() requires + * that already passed/handled expire times are removed from the splay. + * + * It is important that the 'now' value is set at the entry of this function + * and not for the current time as it may have ticked a little while since + * then and then we risk this loop to remove timers that actually have not + * been handled! + */ + do { + multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); + if(t) + /* the removed may have another timeout in queue */ + (void)add_next_timeout(now, multi, t->payload); + + } while(t); + + *running_handles = multi->num_alive; + + if(CURLM_OK >= returncode) Curl_update_timer(multi); - - return returncode; -} - + + return returncode; +} + CURLMcode curl_multi_cleanup(struct Curl_multi *multi) -{ +{ struct Curl_easy *data; struct Curl_easy *nextdata; - + if(GOOD_MULTI_HANDLE(multi)) { if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - - multi->type = 0; /* not good anymore */ - + + multi->type = 0; /* not good anymore */ + /* Firsrt remove all remaining easy handles */ - data = multi->easyp; - while(data) { + data = multi->easyp; + while(data) { nextdata = data->next; if(!data->state.done && data->conn) /* if DONE was never called for this handle */ (void)multi_done(data, CURLE_OK, TRUE); - if(data->dns.hostcachetype == HCACHE_MULTI) { - /* clear out the usage of the shared DNS cache */ - Curl_hostcache_clean(data, data->dns.hostcache); - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - - /* Clear the pointer to the connection cache */ - data->state.conn_cache = NULL; - data->multi = NULL; /* clear the association */ - + if(data->dns.hostcachetype == HCACHE_MULTI) { + /* clear out the usage of the shared DNS cache */ + Curl_hostcache_clean(data, data->dns.hostcache); + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } + + /* Clear the pointer to the connection cache */ + data->state.conn_cache = NULL; + data->multi = NULL; /* clear the association */ + #ifdef USE_LIBPSL if(data->psl == &multi->psl) data->psl = NULL; #endif - data = nextdata; - } - + data = nextdata; + } + /* Close all the connections in the connection cache */ Curl_conncache_close_all_connections(&multi->conn_cache); - + Curl_hash_destroy(&multi->sockhash); Curl_conncache_destroy(&multi->conn_cache); Curl_llist_destroy(&multi->msglist, NULL); @@ -2503,98 +2503,98 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) sclose(multi->wakeup_pair[0]); sclose(multi->wakeup_pair[1]); #endif - free(multi); - - return CURLM_OK; - } + free(multi); + + return CURLM_OK; + } return CURLM_BAD_HANDLE; -} - -/* - * curl_multi_info_read() - * - * This function is the primary way for a multi/multi_socket application to - * figure out if a transfer has ended. We MUST make this function as fast as - * possible as it will be polled frequently and we MUST NOT scan any lists in - * here to figure out things. We must scale fine to thousands of handles and - * beyond. The current design is fully O(1). - */ - +} + +/* + * curl_multi_info_read() + * + * This function is the primary way for a multi/multi_socket application to + * figure out if a transfer has ended. We MUST make this function as fast as + * possible as it will be polled frequently and we MUST NOT scan any lists in + * here to figure out things. We must scale fine to thousands of handles and + * beyond. The current design is fully O(1). + */ + CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) -{ - struct Curl_message *msg; - - *msgs_in_queue = 0; /* default to none */ - +{ + struct Curl_message *msg; + + *msgs_in_queue = 0; /* default to none */ + if(GOOD_MULTI_HANDLE(multi) && !multi->in_callback && Curl_llist_count(&multi->msglist)) { - /* there is one or more messages in the list */ + /* there is one or more messages in the list */ struct Curl_llist_element *e; - - /* extract the head of the list to return */ + + /* extract the head of the list to return */ e = multi->msglist.head; - - msg = e->ptr; - - /* remove the extracted entry */ + + msg = e->ptr; + + /* remove the extracted entry */ Curl_llist_remove(&multi->msglist, e, NULL); - + *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); - - return &msg->extmsg; - } + + return &msg->extmsg; + } return NULL; -} - -/* - * singlesocket() checks what sockets we deal with and their "action state" - * and if we have a different state in any of those sockets from last time we - * call the callback accordingly. - */ +} + +/* + * singlesocket() checks what sockets we deal with and their "action state" + * and if we have a different state in any of those sockets from last time we + * call the callback accordingly. + */ static CURLMcode singlesocket(struct Curl_multi *multi, struct Curl_easy *data) -{ - curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; - int i; - struct Curl_sh_entry *entry; - curl_socket_t s; - int num; - unsigned int curraction; +{ + curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; + int i; + struct Curl_sh_entry *entry; + curl_socket_t s; + int num; + unsigned int curraction; int actions[MAX_SOCKSPEREASYHANDLE]; - + for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) - socks[i] = CURL_SOCKET_BAD; - - /* Fill in the 'current' struct with the state as it is now: what sockets to - supervise and for what actions */ + socks[i] = CURL_SOCKET_BAD; + + /* Fill in the 'current' struct with the state as it is now: what sockets to + supervise and for what actions */ curraction = multi_getsock(data, socks); - - /* We have 0 .. N sockets already and we get to know about the 0 .. M - sockets we should have from now on. Detect the differences, remove no - longer supervised ones and add new ones */ - - /* walk over the sockets we got right now */ + + /* We have 0 .. N sockets already and we get to know about the 0 .. M + sockets we should have from now on. Detect the differences, remove no + longer supervised ones and add new ones */ + + /* walk over the sockets we got right now */ for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) && - (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); - i++) { + (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); + i++) { unsigned int action = CURL_POLL_NONE; unsigned int prevaction = 0; unsigned int comboaction; bool sincebefore = FALSE; - - s = socks[i]; - - /* get it from the hash */ + + s = socks[i]; + + /* get it from the hash */ entry = sh_getentry(&multi->sockhash, s); - - if(curraction & GETSOCK_READSOCK(i)) - action |= CURL_POLL_IN; - if(curraction & GETSOCK_WRITESOCK(i)) - action |= CURL_POLL_OUT; - + + if(curraction & GETSOCK_READSOCK(i)) + action |= CURL_POLL_IN; + if(curraction & GETSOCK_WRITESOCK(i)) + action |= CURL_POLL_OUT; + actions[i] = action; - if(entry) { + if(entry) { /* check if new for this transfer */ int j; for(j = 0; j< data->numsocks; j++) { @@ -2604,14 +2604,14 @@ static CURLMcode singlesocket(struct Curl_multi *multi, break; } } - } - else { + } + else { /* this is a socket we didn't have before, add it to the hash! */ entry = sh_addentry(&multi->sockhash, s); - if(!entry) - /* fatal */ + if(!entry) + /* fatal */ return CURLM_OUT_OF_MEMORY; - } + } if(sincebefore && (prevaction != action)) { /* Socket was used already, but different action now */ if(prevaction & CURL_POLL_IN) @@ -2630,7 +2630,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, entry->readers++; if(action & CURL_POLL_OUT) entry->writers++; - + /* add 'data' to the transfer hash on this socket! */ if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */ sizeof(struct Curl_easy *), data)) @@ -2645,31 +2645,31 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* same, continue */ continue; - if(multi->socket_cb) + if(multi->socket_cb) multi->socket_cb(data, s, comboaction, multi->socket_userp, - entry->socketp); - + entry->socketp); + entry->action = comboaction; /* store the current action state */ - } - - num = i; /* number of sockets */ - - /* when we've walked over all the sockets we should have right now, we must - make sure to detect sockets that are removed */ + } + + num = i; /* number of sockets */ + + /* when we've walked over all the sockets we should have right now, we must + make sure to detect sockets that are removed */ for(i = 0; i< data->numsocks; i++) { - int j; + int j; bool stillused = FALSE; - s = data->sockets[i]; + s = data->sockets[i]; for(j = 0; j < num; j++) { - if(s == socks[j]) { - /* this is still supervised */ + if(s == socks[j]) { + /* this is still supervised */ stillused = TRUE; - break; - } - } + break; + } + } if(stillused) continue; - + entry = sh_getentry(&multi->sockhash, s); /* if this is NULL here, the socket has been closed and notified so already by Curl_multi_closed() */ @@ -2682,12 +2682,12 @@ static CURLMcode singlesocket(struct Curl_multi *multi, if(oldactions & CURL_POLL_IN) entry->readers--; if(!entry->users) { - if(multi->socket_cb) + if(multi->socket_cb) multi->socket_cb(data, s, CURL_POLL_REMOVE, - multi->socket_userp, - entry->socketp); + multi->socket_userp, + entry->socketp); sh_delentry(entry, &multi->sockhash, s); - } + } else { /* still users, but remove this handle as a user of this socket */ if(Curl_hash_delete(&entry->transfers, (char *)&data, @@ -2697,31 +2697,31 @@ static CURLMcode singlesocket(struct Curl_multi *multi, } } } /* for loop over numsocks */ - - memcpy(data->sockets, socks, num*sizeof(curl_socket_t)); + + memcpy(data->sockets, socks, num*sizeof(curl_socket_t)); memcpy(data->actions, actions, num*sizeof(int)); - data->numsocks = num; + data->numsocks = num; return CURLM_OK; -} - +} + void Curl_updatesocket(struct Curl_easy *data) { singlesocket(data->multi, data); } -/* - * Curl_multi_closed() - * - * Used by the connect code to tell the multi_socket code that one of the +/* + * Curl_multi_closed() + * + * Used by the connect code to tell the multi_socket code that one of the * sockets we were using is about to be closed. This function will then - * remove it from the sockethash for this handle to make the multi_socket API - * behave properly, especially for the case when libcurl will create another - * socket again and it gets the same file descriptor number. - */ - + * remove it from the sockethash for this handle to make the multi_socket API + * behave properly, especially for the case when libcurl will create another + * socket again and it gets the same file descriptor number. + */ + void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) -{ +{ if(data) { /* if there's still an easy handle associated with this connection */ struct Curl_multi *multi = data->multi; @@ -2729,115 +2729,115 @@ void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) /* this is set if this connection is part of a handle that is added to a multi handle, and only then this is necessary */ struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); - + if(entry) { if(multi->socket_cb) multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->socketp); - + /* now remove it from the socket hash */ sh_delentry(entry, &multi->sockhash, s); } - } - } -} - -/* - * add_next_timeout() - * + } + } +} + +/* + * add_next_timeout() + * * Each Curl_easy has a list of timeouts. The add_next_timeout() is called - * when it has just been removed from the splay tree because the timeout has - * expired. This function is then to advance in the list to pick the next - * timeout to use (skip the already expired ones) and add this node back to - * the splay tree again. - * - * The splay tree only has each sessionhandle as a single node and the nearest - * timeout is used to sort it on. - */ + * when it has just been removed from the splay tree because the timeout has + * expired. This function is then to advance in the list to pick the next + * timeout to use (skip the already expired ones) and add this node back to + * the splay tree again. + * + * The splay tree only has each sessionhandle as a single node and the nearest + * timeout is used to sort it on. + */ static CURLMcode add_next_timeout(struct curltime now, - struct Curl_multi *multi, + struct Curl_multi *multi, struct Curl_easy *d) -{ +{ struct curltime *tv = &d->state.expiretime; struct Curl_llist *list = &d->state.timeoutlist; struct Curl_llist_element *e; struct time_node *node = NULL; - - /* move over the timeout list for this specific handle and remove all - timeouts that are now passed tense and store the next pending - timeout in *tv */ + + /* move over the timeout list for this specific handle and remove all + timeouts that are now passed tense and store the next pending + timeout in *tv */ for(e = list->head; e;) { struct Curl_llist_element *n = e->next; timediff_t diff; node = (struct time_node *)e->ptr; diff = Curl_timediff(node->time, now); - if(diff <= 0) - /* remove outdated entry */ - Curl_llist_remove(list, e, NULL); - else - /* the list is sorted so get out on the first mismatch */ - break; - e = n; - } - e = list->head; - if(!e) { - /* clear the expire times within the handles that we remove from the - splay tree */ - tv->tv_sec = 0; - tv->tv_usec = 0; - } - else { - /* copy the first entry to 'tv' */ + if(diff <= 0) + /* remove outdated entry */ + Curl_llist_remove(list, e, NULL); + else + /* the list is sorted so get out on the first mismatch */ + break; + e = n; + } + e = list->head; + if(!e) { + /* clear the expire times within the handles that we remove from the + splay tree */ + tv->tv_sec = 0; + tv->tv_usec = 0; + } + else { + /* copy the first entry to 'tv' */ memcpy(tv, &node->time, sizeof(*tv)); - + /* Insert this node again into the splay. Keep the timer in the list in case we need to recompute future timers. */ - multi->timetree = Curl_splayinsert(*tv, multi->timetree, - &d->state.timenode); - } - return CURLM_OK; -} - -static CURLMcode multi_socket(struct Curl_multi *multi, - bool checkall, - curl_socket_t s, - int ev_bitmask, - int *running_handles) -{ - CURLMcode result = CURLM_OK; + multi->timetree = Curl_splayinsert(*tv, multi->timetree, + &d->state.timenode); + } + return CURLM_OK; +} + +static CURLMcode multi_socket(struct Curl_multi *multi, + bool checkall, + curl_socket_t s, + int ev_bitmask, + int *running_handles) +{ + CURLMcode result = CURLM_OK; struct Curl_easy *data = NULL; - struct Curl_tree *t; + struct Curl_tree *t; struct curltime now = Curl_now(); - - if(checkall) { - /* *perform() deals with running_handles on its own */ - result = curl_multi_perform(multi, running_handles); - - /* walk through each easy handle and do the socket state change magic - and callbacks */ + + if(checkall) { + /* *perform() deals with running_handles on its own */ + result = curl_multi_perform(multi, running_handles); + + /* walk through each easy handle and do the socket state change magic + and callbacks */ if(result != CURLM_BAD_HANDLE) { data = multi->easyp; while(data && !result) { result = singlesocket(multi, data); data = data->next; } - } - - /* or should we fall-through and do the timer-based stuff? */ - return result; - } + } + + /* or should we fall-through and do the timer-based stuff? */ + return result; + } if(s != CURL_SOCKET_TIMEOUT) { struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); - - if(!entry) - /* Unmatched socket, we can't act on it but we ignore this fact. In - real-world tests it has been proved that libevent can in fact give - the application actions even though the socket was just previously - asked to get removed, so thus we better survive stray socket actions - and just move on. */ - ; - else { + + if(!entry) + /* Unmatched socket, we can't act on it but we ignore this fact. In + real-world tests it has been proved that libevent can in fact give + the application actions even though the socket was just previously + asked to get removed, so thus we better survive stray socket actions + and just move on. */ + ; + else { struct Curl_hash_iterator iter; struct Curl_hash_element *he; @@ -2848,24 +2848,24 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data = (struct Curl_easy *)he->ptr; DEBUGASSERT(data); DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER); - + if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) /* set socket event bitmask if they're not locked */ data->conn->cselect_bits = ev_bitmask; - + Curl_expire(data, 0, EXPIRE_RUN_NOW); - } - - /* Now we fall-through and do the timer-based stuff, since we don't want - to force the user to have to deal with timeouts as long as at least - one connection in fact has traffic. */ - - data = NULL; /* set data to NULL again to avoid calling - multi_runsingle() in case there's no need to */ + } + + /* Now we fall-through and do the timer-based stuff, since we don't want + to force the user to have to deal with timeouts as long as at least + one connection in fact has traffic. */ + + data = NULL; /* set data to NULL again to avoid calling + multi_runsingle() in case there's no need to */ now = Curl_now(); /* get a newer time since the multi_runsingle() loop may have taken some time */ - } - } + } + } else { /* Asked to run due to time-out. Clear the 'lastcall' variable to force Curl_update_timer() to trigger a callback to the app again even if the @@ -2873,103 +2873,103 @@ static CURLMcode multi_socket(struct Curl_multi *multi, case when the application asks libcurl to run the timeout prematurely. */ memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); - } - - /* - * The loop following here will go on as long as there are expire-times left - * to process in the splay and 'data' will be re-assigned for every expired - * handle we deal with. - */ - do { - /* the first loop lap 'data' can be NULL */ - if(data) { + } + + /* + * The loop following here will go on as long as there are expire-times left + * to process in the splay and 'data' will be re-assigned for every expired + * handle we deal with. + */ + do { + /* the first loop lap 'data' can be NULL */ + if(data) { SIGPIPE_VARIABLE(pipe_st); - + sigpipe_ignore(data, &pipe_st); result = multi_runsingle(multi, &now, data); sigpipe_restore(&pipe_st); if(CURLM_OK >= result) { - /* get the socket(s) and check if the state has been changed since - last */ + /* get the socket(s) and check if the state has been changed since + last */ result = singlesocket(multi, data); if(result) return result; } - } - - /* Check if there's one (more) expired timer to deal with! This function - extracts a matching node if there is one */ - - multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) { - data = t->payload; /* assign this for next loop */ - (void)add_next_timeout(now, multi, t->payload); - } - - } while(t); - - *running_handles = multi->num_alive; - return result; -} - -#undef curl_multi_setopt + } + + /* Check if there's one (more) expired timer to deal with! This function + extracts a matching node if there is one */ + + multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); + if(t) { + data = t->payload; /* assign this for next loop */ + (void)add_next_timeout(now, multi, t->payload); + } + + } while(t); + + *running_handles = multi->num_alive; + return result; +} + +#undef curl_multi_setopt CURLMcode curl_multi_setopt(struct Curl_multi *multi, - CURLMoption option, ...) -{ - CURLMcode res = CURLM_OK; - va_list param; - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - + CURLMoption option, ...) +{ + CURLMcode res = CURLM_OK; + va_list param; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - va_start(param, option); - - switch(option) { - case CURLMOPT_SOCKETFUNCTION: - multi->socket_cb = va_arg(param, curl_socket_callback); - break; - case CURLMOPT_SOCKETDATA: - multi->socket_userp = va_arg(param, void *); - break; + va_start(param, option); + + switch(option) { + case CURLMOPT_SOCKETFUNCTION: + multi->socket_cb = va_arg(param, curl_socket_callback); + break; + case CURLMOPT_SOCKETDATA: + multi->socket_userp = va_arg(param, void *); + break; case CURLMOPT_PUSHFUNCTION: multi->push_cb = va_arg(param, curl_push_callback); break; case CURLMOPT_PUSHDATA: multi->push_userp = va_arg(param, void *); break; - case CURLMOPT_PIPELINING: + case CURLMOPT_PIPELINING: multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX; - break; - case CURLMOPT_TIMERFUNCTION: - multi->timer_cb = va_arg(param, curl_multi_timer_callback); - break; - case CURLMOPT_TIMERDATA: - multi->timer_userp = va_arg(param, void *); - break; - case CURLMOPT_MAXCONNECTS: - multi->maxconnects = va_arg(param, long); - break; - case CURLMOPT_MAX_HOST_CONNECTIONS: - multi->max_host_connections = va_arg(param, long); - break; + break; + case CURLMOPT_TIMERFUNCTION: + multi->timer_cb = va_arg(param, curl_multi_timer_callback); + break; + case CURLMOPT_TIMERDATA: + multi->timer_userp = va_arg(param, void *); + break; + case CURLMOPT_MAXCONNECTS: + multi->maxconnects = va_arg(param, long); + break; + case CURLMOPT_MAX_HOST_CONNECTIONS: + multi->max_host_connections = va_arg(param, long); + break; case CURLMOPT_MAX_TOTAL_CONNECTIONS: multi->max_total_connections = va_arg(param, long); break; /* options formerly used for pipelining */ - case CURLMOPT_MAX_PIPELINE_LENGTH: - break; - case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: - break; - case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: - break; - case CURLMOPT_PIPELINING_SITE_BL: - break; - case CURLMOPT_PIPELINING_SERVER_BL: - break; + case CURLMOPT_MAX_PIPELINE_LENGTH: + break; + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + break; + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: + break; + case CURLMOPT_PIPELINING_SITE_BL: + break; + case CURLMOPT_PIPELINING_SERVER_BL: + break; case CURLMOPT_MAX_CONCURRENT_STREAMS: { long streams = va_arg(param, long); @@ -2978,149 +2978,149 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, multi->max_concurrent_streams = curlx_sltoui(streams); } break; - default: - res = CURLM_UNKNOWN_OPTION; - break; - } - va_end(param); - return res; -} - -/* we define curl_multi_socket() in the public multi.h header */ -#undef curl_multi_socket - + default: + res = CURLM_UNKNOWN_OPTION; + break; + } + va_end(param); + return res; +} + +/* we define curl_multi_socket() in the public multi.h header */ +#undef curl_multi_socket + CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, - int *running_handles) -{ + int *running_handles) +{ CURLMcode result; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, FALSE, s, 0, running_handles); - if(CURLM_OK >= result) + if(CURLM_OK >= result) Curl_update_timer(multi); - return result; -} - + return result; +} + CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, - int ev_bitmask, int *running_handles) -{ + int ev_bitmask, int *running_handles) +{ CURLMcode result; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles); - if(CURLM_OK >= result) + if(CURLM_OK >= result) Curl_update_timer(multi); - return result; -} - + return result; +} + CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) -{ +{ CURLMcode result; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); - if(CURLM_OK >= result) + if(CURLM_OK >= result) Curl_update_timer(multi); - return result; -} - -static CURLMcode multi_timeout(struct Curl_multi *multi, - long *timeout_ms) -{ + return result; +} + +static CURLMcode multi_timeout(struct Curl_multi *multi, + long *timeout_ms) +{ static struct curltime tv_zero = {0, 0}; - - if(multi->timetree) { - /* we have a tree of expire times */ + + if(multi->timetree) { + /* we have a tree of expire times */ struct curltime now = Curl_now(); - - /* splay the lowest to the bottom */ - multi->timetree = Curl_splay(tv_zero, multi->timetree); - - if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) { - /* some time left before expiration */ + + /* splay the lowest to the bottom */ + multi->timetree = Curl_splay(tv_zero, multi->timetree); + + if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) { + /* some time left before expiration */ timediff_t diff = Curl_timediff(multi->timetree->key, now); if(diff <= 0) - /* - * Since we only provide millisecond resolution on the returned value - * and the diff might be less than one millisecond here, we don't - * return zero as that may cause short bursts of busyloops on fast - * processors while the diff is still present but less than one - * millisecond! instead we return 1 until the time is ripe. - */ + /* + * Since we only provide millisecond resolution on the returned value + * and the diff might be less than one millisecond here, we don't + * return zero as that may cause short bursts of busyloops on fast + * processors while the diff is still present but less than one + * millisecond! instead we return 1 until the time is ripe. + */ *timeout_ms = 1; else /* this should be safe even on 64 bit archs, as we don't use that overly long timeouts */ *timeout_ms = (long)diff; - } - else - /* 0 means immediately */ - *timeout_ms = 0; - } - else - *timeout_ms = -1; - - return CURLM_OK; -} - + } + else + /* 0 means immediately */ + *timeout_ms = 0; + } + else + *timeout_ms = -1; + + return CURLM_OK; +} + CURLMcode curl_multi_timeout(struct Curl_multi *multi, - long *timeout_ms) -{ - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - + long *timeout_ms) +{ + /* First, make some basic checks that the CURLM handle is a good handle */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - return multi_timeout(multi, timeout_ms); -} - -/* - * Tell the application it should update its timers, if it subscribes to the - * update timer callback. - */ + return multi_timeout(multi, timeout_ms); +} + +/* + * Tell the application it should update its timers, if it subscribes to the + * update timer callback. + */ void Curl_update_timer(struct Curl_multi *multi) -{ - long timeout_ms; - - if(!multi->timer_cb) +{ + long timeout_ms; + + if(!multi->timer_cb) return; - if(multi_timeout(multi, &timeout_ms)) { + if(multi_timeout(multi, &timeout_ms)) { return; - } - if(timeout_ms < 0) { + } + if(timeout_ms < 0) { static const struct curltime none = {0, 0}; - if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { - multi->timer_lastcall = none; - /* there's no timeout now but there was one previously, tell the app to - disable it */ + if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { + multi->timer_lastcall = none; + /* there's no timeout now but there was one previously, tell the app to + disable it */ multi->timer_cb(multi, -1, multi->timer_userp); return; - } + } return; - } - - /* When multi_timeout() is done, multi->timetree points to the node with the - * timeout we got the (relative) time-out time for. We can thus easily check - * if this is the same (fixed) time as we got in a previous call and then - * avoid calling the callback again. */ - if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) + } + + /* When multi_timeout() is done, multi->timetree points to the node with the + * timeout we got the (relative) time-out time for. We can thus easily check + * if this is the same (fixed) time as we got in a previous call and then + * avoid calling the callback again. */ + if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) return; - - multi->timer_lastcall = multi->timetree->key; - + + multi->timer_lastcall = multi->timetree->key; + multi->timer_cb(multi, timeout_ms, multi->timer_userp); -} - -/* +} + +/* * multi_deltimeout() - * + * * Remove a given timestamp from the list of timeouts. - */ + */ static void multi_deltimeout(struct Curl_easy *data, expire_id eid) -{ +{ struct Curl_llist_element *e; struct Curl_llist *timeoutlist = &data->state.timeoutlist; /* find and remove the specific node from the list */ @@ -3131,84 +3131,84 @@ multi_deltimeout(struct Curl_easy *data, expire_id eid) return; } } -} - -/* - * multi_addtimeout() - * - * Add a timestamp to the list of timeouts. Keep the list sorted so that head - * of list is always the timeout nearest in time. - * - */ -static CURLMcode +} + +/* + * multi_addtimeout() + * + * Add a timestamp to the list of timeouts. Keep the list sorted so that head + * of list is always the timeout nearest in time. + * + */ +static CURLMcode multi_addtimeout(struct Curl_easy *data, struct curltime *stamp, expire_id eid) -{ +{ struct Curl_llist_element *e; struct time_node *node; struct Curl_llist_element *prev = NULL; size_t n; struct Curl_llist *timeoutlist = &data->state.timeoutlist; - + node = &data->state.expires[eid]; - + /* copy the timestamp and id */ memcpy(&node->time, stamp, sizeof(*stamp)); node->eid = eid; /* also marks it as in use */ - + n = Curl_llist_count(timeoutlist); if(n) { - /* find the correct spot in the list */ - for(e = timeoutlist->head; e; e = e->next) { + /* find the correct spot in the list */ + for(e = timeoutlist->head; e; e = e->next) { struct time_node *check = (struct time_node *)e->ptr; timediff_t diff = Curl_timediff(check->time, node->time); - if(diff > 0) - break; - prev = e; - } - - } - /* else - this is the first timeout on the list */ - + if(diff > 0) + break; + prev = e; + } + + } + /* else + this is the first timeout on the list */ + Curl_llist_insert_next(timeoutlist, prev, node, &node->list); - return CURLM_OK; -} - -/* - * Curl_expire() - * - * given a number of milliseconds from now to use to set the 'act before - * this'-time for the transfer, to be extracted by curl_multi_timeout() - * + return CURLM_OK; +} + +/* + * Curl_expire() + * + * given a number of milliseconds from now to use to set the 'act before + * this'-time for the transfer, to be extracted by curl_multi_timeout() + * * The timeout will be added to a queue of timeouts if it defines a moment in * time that is later than the current head of queue. - * + * * Expire replaces a former timeout using the same id if already set. */ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) -{ - struct Curl_multi *multi = data->multi; +{ + struct Curl_multi *multi = data->multi; struct curltime *nowp = &data->state.expiretime; struct curltime set; - - /* this is only interesting while there is still an associated multi struct - remaining! */ - if(!multi) - return; - + + /* this is only interesting while there is still an associated multi struct + remaining! */ + if(!multi) + return; + DEBUGASSERT(id < EXPIRE_LAST); - + set = Curl_now(); set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */ set.tv_usec += (unsigned int)(milli%1000)*1000; - + if(set.tv_usec >= 1000000) { set.tv_sec++; set.tv_usec -= 1000000; } - + /* Remove any timer with the same id just in case. */ multi_deltimeout(data, id); @@ -3227,7 +3227,7 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) /* The current splay tree entry is sooner than this new expiry time. We don't need to update our splay tree entry. */ return; - } + } /* Since this is an updated time, we must remove the previous entry from the splay tree first and then re-add the new value */ @@ -3235,8 +3235,8 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) &multi->timetree); if(rc) infof(data, "Internal error removing splay node = %d\n", rc); - } - + } + /* Indicate that we are in the splay tree and insert the new timer expiry value since it is our local minimum. */ *nowp = set; @@ -3244,7 +3244,7 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) multi->timetree = Curl_splayinsert(*nowp, multi->timetree, &data->state.timenode); } - + /* * Curl_expire_done() * @@ -3256,7 +3256,7 @@ void Curl_expire_done(struct Curl_easy *data, expire_id id) /* remove the timer, if there */ multi_deltimeout(data, id); } - + /* * Curl_expire_clear() * @@ -3266,12 +3266,12 @@ void Curl_expire_clear(struct Curl_easy *data) { struct Curl_multi *multi = data->multi; struct curltime *nowp = &data->state.expiretime; - + /* this is only interesting while there is still an associated multi struct remaining! */ if(!multi) return; - + if(nowp->tv_sec || nowp->tv_usec) { /* Since this is an cleared time, we must remove the previous entry from the splay tree */ @@ -3286,69 +3286,69 @@ void Curl_expire_clear(struct Curl_easy *data) /* flush the timeout list too */ while(list->size > 0) { Curl_llist_remove(list, list->tail, NULL); - } - + } + #ifdef DEBUGBUILD infof(data, "Expire cleared (transfer %p)\n", data); #endif nowp->tv_sec = 0; nowp->tv_usec = 0; - } -} - + } +} + CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, void *hashp) -{ - struct Curl_sh_entry *there = NULL; - +{ + struct Curl_sh_entry *there = NULL; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - + there = sh_getentry(&multi->sockhash, s); - if(!there) - return CURLM_BAD_SOCKET; - - there->socketp = hashp; - - return CURLM_OK; -} - -size_t Curl_multi_max_host_connections(struct Curl_multi *multi) -{ - return multi ? multi->max_host_connections : 0; -} - -size_t Curl_multi_max_total_connections(struct Curl_multi *multi) -{ - return multi ? multi->max_total_connections : 0; -} - + if(!there) + return CURLM_BAD_SOCKET; + + there->socketp = hashp; + + return CURLM_OK; +} + +size_t Curl_multi_max_host_connections(struct Curl_multi *multi) +{ + return multi ? multi->max_host_connections : 0; +} + +size_t Curl_multi_max_total_connections(struct Curl_multi *multi) +{ + return multi ? multi->max_total_connections : 0; +} + /* * When information about a connection has appeared, call this! */ - + void Curl_multiuse_state(struct connectdata *conn, int bundlestate) /* use BUNDLE_* defines */ -{ +{ DEBUGASSERT(conn); DEBUGASSERT(conn->bundle); DEBUGASSERT(conn->data); DEBUGASSERT(conn->data->multi); - + conn->bundle->multiuse = bundlestate; process_pending_handles(conn->data->multi); -} - +} + static void process_pending_handles(struct Curl_multi *multi) -{ +{ struct Curl_llist_element *e = multi->pending.head; if(e) { struct Curl_easy *data = e->ptr; - + DEBUGASSERT(data->mstate == CURLM_STATE_CONNECT_PEND); multistate(data, CURLM_STATE_CONNECT); @@ -3361,9 +3361,9 @@ static void process_pending_handles(struct Curl_multi *multi) /* mark this as having been in the pending queue */ data->state.previouslypending = TRUE; - } -} - + } +} + void Curl_set_in_callback(struct Curl_easy *data, bool value) { /* might get called when there is no data pointer! */ @@ -3381,38 +3381,38 @@ bool Curl_is_in_callback(struct Curl_easy *easy) (easy->multi_easy && easy->multi_easy->in_callback)); } -#ifdef DEBUGBUILD +#ifdef DEBUGBUILD void Curl_multi_dump(struct Curl_multi *multi) -{ +{ struct Curl_easy *data; - int i; - fprintf(stderr, "* Multi status: %d handles, %d alive\n", - multi->num_easy, multi->num_alive); + int i; + fprintf(stderr, "* Multi status: %d handles, %d alive\n", + multi->num_easy, multi->num_alive); for(data = multi->easyp; data; data = data->next) { - if(data->mstate < CURLM_STATE_COMPLETED) { - /* only display handles that are not completed */ - fprintf(stderr, "handle %p, state %s, %d sockets\n", - (void *)data, - statename[data->mstate], data->numsocks); + if(data->mstate < CURLM_STATE_COMPLETED) { + /* only display handles that are not completed */ + fprintf(stderr, "handle %p, state %s, %d sockets\n", + (void *)data, + statename[data->mstate], data->numsocks); for(i = 0; i < data->numsocks; i++) { - curl_socket_t s = data->sockets[i]; + curl_socket_t s = data->sockets[i]; struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); - - fprintf(stderr, "%d ", (int)s); - if(!entry) { - fprintf(stderr, "INTERNAL CONFUSION\n"); - continue; - } - fprintf(stderr, "[%s %s] ", + + fprintf(stderr, "%d ", (int)s); + if(!entry) { + fprintf(stderr, "INTERNAL CONFUSION\n"); + continue; + } + fprintf(stderr, "[%s %s] ", (entry->action&CURL_POLL_IN)?"RECVING":"", (entry->action&CURL_POLL_OUT)?"SENDING":""); - } - if(data->numsocks) - fprintf(stderr, "\n"); - } - } -} -#endif + } + if(data->numsocks) + fprintf(stderr, "\n"); + } + } +} +#endif unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) { |