summaryrefslogtreecommitdiff
path: root/plugins/FTPFileYM/curl/lib/multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/FTPFileYM/curl/lib/multi.c')
-rw-r--r--plugins/FTPFileYM/curl/lib/multi.c1229
1 files changed, 619 insertions, 610 deletions
diff --git a/plugins/FTPFileYM/curl/lib/multi.c b/plugins/FTPFileYM/curl/lib/multi.c
index fa0afb9f83..e723a3ebf0 100644
--- a/plugins/FTPFileYM/curl/lib/multi.c
+++ b/plugins/FTPFileYM/curl/lib/multi.c
@@ -40,6 +40,7 @@
#include "conncache.h"
#include "bundles.h"
#include "multihandle.h"
+#include "pipeline.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -57,6 +58,7 @@
#define CURL_SOCKET_HASH_TABLE_SIZE 911
#endif
+#define CURL_CONNECTION_HASH_SIZE 97
#define CURL_MULTI_HANDLE 0x000bab1e
@@ -66,25 +68,21 @@
((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER))
static void singlesocket(struct Curl_multi *multi,
- struct Curl_one_easy *easy);
+ struct SessionHandle *data);
static int update_timer(struct Curl_multi *multi);
-static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
- struct connectdata *conn);
-static int checkPendPipeline(struct connectdata *conn);
-static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
- struct connectdata *conn);
-static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle,
- struct connectdata *conn);
static bool isHandleAtHead(struct SessionHandle *handle,
struct curl_llist *pipeline);
static CURLMcode add_next_timeout(struct timeval now,
struct Curl_multi *multi,
struct SessionHandle *d);
+static CURLMcode multi_timeout(struct Curl_multi *multi,
+ long *timeout_ms);
#ifdef DEBUGBUILD
static const char * const statename[]={
"INIT",
+ "CONNECT_PEND",
"CONNECT",
"WAITRESOLVE",
"WAITCONNECT",
@@ -107,7 +105,7 @@ static const char * const statename[]={
static void multi_freetimeout(void *a, void *b);
/* always use this function to change state, to make debugging easier */
-static void mstate(struct Curl_one_easy *easy, CURLMstate state
+static void mstate(struct SessionHandle *data, CURLMstate state
#ifdef DEBUGBUILD
, int lineno
#endif
@@ -116,29 +114,29 @@ static void mstate(struct Curl_one_easy *easy, CURLMstate state
#ifdef DEBUGBUILD
long connection_id = -5000;
#endif
- CURLMstate oldstate = easy->state;
+ CURLMstate oldstate = data->mstate;
if(oldstate == state)
/* don't bother when the new state is the same as the old state */
return;
- easy->state = state;
+ data->mstate = state;
#ifdef DEBUGBUILD
- if(easy->easy_conn) {
- if(easy->state > CURLM_STATE_CONNECT &&
- easy->state < CURLM_STATE_COMPLETED)
- connection_id = easy->easy_conn->connection_id;
+ if(data->mstate >= CURLM_STATE_CONNECT_PEND &&
+ data->mstate < CURLM_STATE_COMPLETED) {
+ if(data->easy_conn)
+ connection_id = data->easy_conn->connection_id;
- infof(easy->easy_handle,
+ infof(data,
"STATE: %s => %s handle %p; line %d (connection #%ld) \n",
- statename[oldstate], statename[easy->state],
- (char *)easy, lineno, connection_id);
+ 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' */
- easy->easy_handle->multi->num_alive--;
+ data->multi->num_alive--;
}
#ifndef DEBUGBUILD
@@ -218,16 +216,16 @@ static void sh_freeentry(void *freethis)
free(p);
}
-static size_t fd_key_compare(void*k1, size_t k1_len, void*k2, size_t 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 (*((int* ) k1)) == (*((int* ) k2));
+ return (*((int *) k1)) == (*((int *) 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)
{
- int fd = * ((int* ) key);
+ int fd = *((int *) key);
(void) key_length;
return (fd % (int)slots_num);
@@ -251,9 +249,9 @@ static size_t hash_fd(void* key, size_t key_length, size_t slots_num)
* per call."
*
*/
-static struct curl_hash *sh_init(void)
+static struct curl_hash *sh_init(int hashsize)
{
- return Curl_hash_alloc(CURL_SOCKET_HASH_TABLE_SIZE, hash_fd, fd_key_compare,
+ return Curl_hash_alloc(hashsize, hash_fd, fd_key_compare,
sh_freeentry);
}
@@ -283,7 +281,8 @@ static void multi_freeamsg(void *a, void *b)
(void)b;
}
-CURLM *curl_multi_init(void)
+struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
+ int chashsize) /* connection hash */
{
struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
@@ -296,11 +295,11 @@ CURLM *curl_multi_init(void)
if(!multi->hostcache)
goto error;
- multi->sockhash = sh_init();
+ multi->sockhash = sh_init(hashsize);
if(!multi->sockhash)
goto error;
- multi->conn_cache = Curl_conncache_init();
+ multi->conn_cache = Curl_conncache_init(chashsize);
if(!multi->conn_cache)
goto error;
@@ -308,12 +307,15 @@ CURLM *curl_multi_init(void)
if(!multi->msglist)
goto error;
- /* Let's make the doubly-linked list a circular list. This makes
- the linked list code simpler and allows inserting at the end
- with less work (we didn't keep a tail pointer before). */
- multi->easy.next = &multi->easy;
- multi->easy.prev = &multi->easy;
+ /* allocate a new easy handle to use when closing cached connections */
+ multi->closure_handle = curl_easy_init();
+ if(!multi->closure_handle)
+ goto error;
+
+ multi->closure_handle->multi = multi;
+ multi->closure_handle->state.conn_cache = multi->conn_cache;
+ multi->max_pipeline_length = 5;
return (CURLM *) multi;
error:
@@ -324,20 +326,25 @@ CURLM *curl_multi_init(void)
multi->hostcache = NULL;
Curl_conncache_destroy(multi->conn_cache);
multi->conn_cache = NULL;
+ Curl_close(multi->closure_handle);
+ multi->closure_handle = NULL;
free(multi);
return NULL;
}
+CURLM *curl_multi_init(void)
+{
+ return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE,
+ CURL_CONNECTION_HASH_SIZE);
+}
+
CURLMcode curl_multi_add_handle(CURLM *multi_handle,
CURL *easy_handle)
{
struct curl_llist *timeoutlist;
- struct Curl_one_easy *easy;
struct Curl_multi *multi = (struct Curl_multi *)multi_handle;
struct SessionHandle *data = (struct SessionHandle *)easy_handle;
- struct SessionHandle *new_closure = NULL;
- struct curl_hash *hostcache = NULL;
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
@@ -347,110 +354,73 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
if(!GOOD_EASY_HANDLE(easy_handle))
return CURLM_BAD_EASY_HANDLE;
- /* Prevent users from adding same easy handle more than
- once and prevent adding to more than one multi stack */
+ /* Prevent users from adding same easy handle more than once and prevent
+ adding to more than one multi stack */
if(data->multi)
- /* possibly we should create a new unique error code for this condition */
- return CURLM_BAD_EASY_HANDLE;
+ return CURLM_ADDED_ALREADY;
/* Allocate and initialize timeout list for easy handle */
timeoutlist = Curl_llist_alloc(multi_freetimeout);
if(!timeoutlist)
return CURLM_OUT_OF_MEMORY;
- /* Allocate new node for the doubly-linked circular list of
- Curl_one_easy structs that holds pointers to easy handles */
- easy = calloc(1, sizeof(struct Curl_one_easy));
- if(!easy) {
- Curl_llist_destroy(timeoutlist, NULL);
- return CURLM_OUT_OF_MEMORY;
- }
-
- /* In case multi handle has no hostcache yet, allocate one */
- if(!multi->hostcache) {
- hostcache = Curl_mk_dnscache();
- if(!hostcache) {
- free(easy);
- Curl_llist_destroy(timeoutlist, NULL);
- return CURLM_OUT_OF_MEMORY;
- }
- }
-
- /* In case multi handle has no closure_handle yet, allocate
- a new easy handle to use when closing cached connections */
- if(!multi->closure_handle) {
- new_closure = (struct SessionHandle *)curl_easy_init();
- if(!new_closure) {
- Curl_hash_destroy(hostcache);
- free(easy);
- Curl_llist_destroy(timeoutlist, NULL);
- return CURLM_OUT_OF_MEMORY;
- }
- }
-
/*
- ** 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.
- */
-
- /* In case a new closure handle has been initialized above, it
- is associated now with the multi handle which lacked one. */
- if(new_closure) {
- multi->closure_handle = new_closure;
- Curl_easy_addmulti(multi->closure_handle, multi_handle);
- multi->closure_handle->state.conn_cache = multi->conn_cache;
- }
-
- /* In case hostcache has been allocated above,
- it is associated now with the multi handle. */
- if(hostcache)
- multi->hostcache = hostcache;
+ * 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.
+ */
/* Make easy handle use timeout list initialized above */
data->state.timeoutlist = timeoutlist;
timeoutlist = NULL;
/* set the easy handle */
- easy->easy_handle = data;
- multistate(easy, CURLM_STATE_INIT);
-
- /* set the back pointer to one_easy to assist in removal */
- easy->easy_handle->multi_pos = easy;
-
+ multistate(data, CURLM_STATE_INIT);
+
+ if((data->set.global_dns_cache) &&
+ (data->dns.hostcachetype != HCACHE_GLOBAL)) {
+ /* global dns cache was requested but still isn't */
+ struct curl_hash *global = Curl_global_host_cache_init();
+ if(global) {
+ /* only do this if the global cache init works */
+ data->dns.hostcache = global;
+ data->dns.hostcachetype = HCACHE_GLOBAL;
+ }
+ }
/* for multi interface connections, we share DNS cache automatically if the
easy handle's one is currently not set. */
- if(!easy->easy_handle->dns.hostcache ||
- (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) {
- easy->easy_handle->dns.hostcache = multi->hostcache;
- easy->easy_handle->dns.hostcachetype = HCACHE_MULTI;
+ else if(!data->dns.hostcache ||
+ (data->dns.hostcachetype == HCACHE_NONE)) {
+ data->dns.hostcache = multi->hostcache;
+ data->dns.hostcachetype = HCACHE_MULTI;
}
/* Point to the multi's connection cache */
- easy->easy_handle->state.conn_cache = multi->conn_cache;
+ data->state.conn_cache = multi->conn_cache;
/* This adds the new entry at the 'end' of the doubly-linked circular
- list of Curl_one_easy structs to try and maintain a FIFO queue so
+ list of SessionHandle structs to try and maintain a FIFO queue so
the pipelined requests are in order. */
- /* We add this new entry last in the list. We make our 'next' point to the
- 'first' struct and our 'prev' point to the previous 'prev' */
- easy->next = &multi->easy;
- easy->prev = multi->easy.prev;
-
- /* make 'easy' the last node in the chain */
- multi->easy.prev = easy;
+ /* We add this new entry last in the list. */
- /* if there was a prev node, make sure its 'next' pointer links to
- the new node */
- easy->prev->next = easy;
+ data->next = NULL; /* end of the line */
+ if(multi->easyp) {
+ struct SessionHandle *last = multi->easylp;
+ last->next = data;
+ data->prev = last;
+ multi->easylp = data; /* the new last node */
+ }
+ else {
+ /* first node, make both prev and next be NULL! */
+ data->next = NULL;
+ data->prev = NULL;
+ multi->easylp = multi->easyp = data; /* both first and last */
+ }
/* make the SessionHandle refer back to this multi handle */
- Curl_easy_addmulti(easy_handle, multi_handle);
-
- /* make the SessionHandle struct refer back to this struct */
- easy->easy_handle->set.one_easy = easy;
+ data->multi = multi_handle;
/* 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
@@ -458,7 +428,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
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(easy->easy_handle, 1);
+ Curl_expire(data, 1);
/* increase the node-counter */
multi->num_easy++;
@@ -494,7 +464,7 @@ 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->easy, sh->easy->magic, (int)sh->socket);
+ (void *)sh->data, sh->data->magic, (int)sh->socket);
}
#endif
@@ -502,8 +472,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
CURL *curl_handle)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
- struct Curl_one_easy *easy;
- struct SessionHandle *data = curl_handle;
+ struct SessionHandle *easy = curl_handle;
+ struct SessionHandle *data = easy;
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
@@ -513,13 +483,14 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
if(!GOOD_EASY_HANDLE(curl_handle))
return CURLM_BAD_EASY_HANDLE;
- /* pick-up from the 'curl_handle' the kept position in the list */
- easy = data->multi_pos;
+ /* 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! */
if(easy) {
- bool premature = (easy->state < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
- bool easy_owns_conn = (easy->easy_conn &&
- (easy->easy_conn->data == easy->easy_handle)) ?
+ bool premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
+ bool easy_owns_conn = (data->easy_conn &&
+ (data->easy_conn->data == easy)) ?
TRUE : FALSE;
/* If the 'state' is not INIT or COMPLETED, we might need to do something
@@ -529,24 +500,24 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
alive connections when this is removed */
multi->num_alive--;
- if(easy->easy_conn &&
- (easy->easy_conn->send_pipe->size +
- easy->easy_conn->recv_pipe->size > 1) &&
- easy->state > CURLM_STATE_WAITDO &&
- easy->state < CURLM_STATE_COMPLETED) {
+ if(data->easy_conn &&
+ (data->easy_conn->send_pipe->size +
+ data->easy_conn->recv_pipe->size > 1) &&
+ data->mstate > CURLM_STATE_WAITDO &&
+ data->mstate < CURLM_STATE_COMPLETED) {
/* If the handle is in a pipeline and has started sending off its
request but not received its response yet, we need to close
connection. */
- easy->easy_conn->bits.close = TRUE;
+ data->easy_conn->bits.close = TRUE;
/* Set connection owner so that Curl_done() closes it.
We can sefely do this here since connection is killed. */
- easy->easy_conn->data = easy->easy_handle;
+ data->easy_conn->data = easy;
}
- /* The timer must be shut down before easy->multi is set to NULL,
+ /* 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. */
- Curl_expire(easy->easy_handle, 0);
+ Curl_expire(data, 0);
/* destroy the timeout list that is held in the easy handle */
if(data->state.timeoutlist) {
@@ -554,13 +525,13 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
data->state.timeoutlist = NULL;
}
- if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
+ if(data->dns.hostcachetype == HCACHE_MULTI) {
/* stop using the multi handle's DNS cache */
- easy->easy_handle->dns.hostcache = NULL;
- easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+ data->dns.hostcache = NULL;
+ data->dns.hostcachetype = HCACHE_NONE;
}
- if(easy->easy_conn) {
+ if(data->easy_conn) {
/* we must call Curl_done() here (if we still "own it") so that we don't
leave a half-baked one around */
@@ -571,31 +542,30 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
Note that this ignores the return code simply because there's
nothing really useful to do with it anyway! */
- (void)Curl_done(&easy->easy_conn, easy->result, premature);
+ (void)Curl_done(&data->easy_conn, data->result, premature);
}
else
/* Clear connection pipelines, if Curl_done above was not called */
- Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn);
+ Curl_getoff_all_pipelines(data, data->easy_conn);
}
/* as this was using a shared connection cache we clear the pointer
to that since we're not part of that multi handle anymore */
- easy->easy_handle->state.conn_cache = NULL;
+ data->state.conn_cache = NULL;
/* change state without using multistate(), only to make singlesocket() do
what we want */
- easy->state = CURLM_STATE_COMPLETED;
+ 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 */
- if(easy->easy_conn) {
- easy->easy_conn->data = NULL;
- easy->easy_conn = NULL;
+ if(data->easy_conn) {
+ data->easy_conn->data = NULL;
+ data->easy_conn = NULL;
}
- Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association
- to this multi handle */
+ data->multi = NULL; /* clear the association to this multi handle */
{
/* make sure there's no pending message in the queue sent from this easy
@@ -605,7 +575,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
for(e = multi->msglist->head; e; e = e->next) {
struct Curl_message *msg = e->ptr;
- if(msg->extmsg.easy_handle == easy->easy_handle) {
+ if(msg->extmsg.easy_handle == easy) {
Curl_llist_remove(multi->msglist, e, NULL);
/* there can only be one from this specific handle */
break;
@@ -614,20 +584,19 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
}
/* make the previous node point to our next */
- if(easy->prev)
- easy->prev->next = easy->next;
- /* make our next point to our previous node */
- if(easy->next)
- easy->next->prev = easy->prev;
-
- easy->easy_handle->set.one_easy = NULL; /* detached */
+ if(data->prev)
+ data->prev->next = data->next;
+ else
+ multi->easyp = data->next; /* point to first node */
- /* Null the position in the controlling structure */
- easy->easy_handle->multi_pos = NULL;
+ /* make our next point to our previous node */
+ if(data->next)
+ data->next->prev = data->prev;
+ else
+ multi->easylp = data->prev; /* point to last node */
/* NOTE NOTE NOTE
We do not touch the easy handle here! */
- free(easy);
multi->num_easy--; /* one less to care about now */
@@ -638,17 +607,14 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
return CURLM_BAD_EASY_HANDLE; /* twasn't found */
}
-bool Curl_multi_canPipeline(const struct Curl_multi* multi)
+bool Curl_multi_pipeline_enabled(const struct Curl_multi *multi)
{
- return multi->pipelining_enabled;
+ return (multi && multi->pipelining_enabled) ? TRUE : FALSE;
}
void Curl_multi_handlePipeBreak(struct SessionHandle *data)
{
- struct Curl_one_easy *one_easy = data->set.one_easy;
-
- if(one_easy)
- one_easy->easy_conn = NULL;
+ data->easy_conn = NULL;
}
static int waitconnect_getsock(struct connectdata *conn,
@@ -678,7 +644,7 @@ static int domore_getsock(struct connectdata *conn,
}
/* returns bitmapped flags for this handle and its sockets */
-static int multi_getsock(struct Curl_one_easy *easy,
+static int multi_getsock(struct SessionHandle *data,
curl_socket_t *socks, /* points to numsocks number
of sockets */
int numsocks)
@@ -688,16 +654,16 @@ static int multi_getsock(struct Curl_one_easy *easy,
happen when this is called from curl_multi_remove_handle() =>
singlesocket() => multi_getsock().
*/
- if(easy->easy_handle->state.pipe_broke || !easy->easy_conn)
+ if(data->state.pipe_broke || !data->easy_conn)
return 0;
- if(easy->state > CURLM_STATE_CONNECT &&
- easy->state < CURLM_STATE_COMPLETED) {
+ if(data->mstate > CURLM_STATE_CONNECT &&
+ data->mstate < CURLM_STATE_COMPLETED) {
/* Set up ownership correctly */
- easy->easy_conn->data = easy->easy_handle;
+ data->easy_conn->data = data;
}
- switch(easy->state) {
+ switch(data->mstate) {
default:
#if 0 /* switch back on these cases to get the compiler to check for all enums
to be present */
@@ -715,28 +681,28 @@ static int multi_getsock(struct Curl_one_easy *easy,
return 0;
case CURLM_STATE_WAITRESOLVE:
- return Curl_resolver_getsock(easy->easy_conn, socks, numsocks);
+ return Curl_resolver_getsock(data->easy_conn, socks, numsocks);
case CURLM_STATE_PROTOCONNECT:
- return Curl_protocol_getsock(easy->easy_conn, socks, numsocks);
+ return Curl_protocol_getsock(data->easy_conn, socks, numsocks);
case CURLM_STATE_DO:
case CURLM_STATE_DOING:
- return Curl_doing_getsock(easy->easy_conn, socks, numsocks);
+ return Curl_doing_getsock(data->easy_conn, socks, numsocks);
case CURLM_STATE_WAITPROXYCONNECT:
case CURLM_STATE_WAITCONNECT:
- return waitconnect_getsock(easy->easy_conn, socks, numsocks);
+ return waitconnect_getsock(data->easy_conn, socks, numsocks);
case CURLM_STATE_DO_MORE:
- return domore_getsock(easy->easy_conn, socks, numsocks);
+ return domore_getsock(data->easy_conn, socks, numsocks);
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_WAITPERFORM:
- return Curl_single_getsock(easy->easy_conn, socks, numsocks);
+ return Curl_single_getsock(data->easy_conn, socks, numsocks);
}
}
@@ -749,7 +715,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
Some easy handles may not have connected to the remote host yet,
and then we must make sure that is done. */
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
- struct Curl_one_easy *easy;
+ struct SessionHandle *data;
int this_max_fd=-1;
curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
int bitmap;
@@ -759,9 +725,9 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
- easy=multi->easy.next;
- while(easy != &multi->easy) {
- bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
+ data=multi->easyp;
+ while(data) {
+ bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE);
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
curl_socket_t s = CURL_SOCKET_BAD;
@@ -783,7 +749,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
}
}
- easy = easy->next; /* check next handle */
+ data = data->next; /* check next handle */
}
*max_fd = this_max_fd;
@@ -798,20 +764,29 @@ CURLMcode curl_multi_wait(CURLM *multi_handle,
int *ret)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
- struct Curl_one_easy *easy;
+ struct SessionHandle *data;
curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
int bitmap;
unsigned int i;
- unsigned int nfds = extra_nfds;
+ unsigned int nfds = 0;
+ unsigned int curlfds;
struct pollfd *ufds = NULL;
+ long timeout_internal;
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_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 0! */
+ (void)multi_timeout(multi, &timeout_internal);
+ if((timeout_internal > 0) && (timeout_internal < (long)timeout_ms))
+ timeout_ms = (int)timeout_internal;
+
/* Count up how many fds we have from the multi handle */
- easy=multi->easy.next;
- while(easy != &multi->easy) {
- bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
+ data=multi->easyp;
+ while(data) {
+ bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE);
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
curl_socket_t s = CURL_SOCKET_BAD;
@@ -829,9 +804,12 @@ CURLMcode curl_multi_wait(CURLM *multi_handle,
}
}
- easy = easy->next; /* check next handle */
+ data = data->next; /* check next handle */
}
+ curlfds = nfds; /* number of internal file descriptors */
+ nfds += extra_nfds; /* add the externally provided ones */
+
if(nfds) {
ufds = malloc(nfds * sizeof(struct pollfd));
if(!ufds)
@@ -839,32 +817,37 @@ CURLMcode curl_multi_wait(CURLM *multi_handle,
}
nfds = 0;
- /* Add the curl handles to our pollfds first */
- easy=multi->easy.next;
- while(easy != &multi->easy) {
- bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
+ /* only do the second loop if we found descriptors in the first stage run
+ above */
- for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
- curl_socket_t s = CURL_SOCKET_BAD;
+ if(curlfds) {
+ /* Add the curl handles to our pollfds first */
+ data=multi->easyp;
+ while(data) {
+ bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE);
- 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;
+ 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;
+ }
}
- }
- easy = easy->next; /* check next handle */
+ data = data->next; /* check next handle */
+ }
}
/* Add external file descriptions from poll-like struct curl_waitfd */
@@ -880,9 +863,30 @@ CURLMcode curl_multi_wait(CURLM *multi_handle,
++nfds;
}
- if(nfds)
+ if(nfds) {
/* wait... */
i = Curl_poll(ufds, nfds, timeout_ms);
+
+ if(i) {
+ unsigned int j;
+ /* 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(j = 0; j < extra_nfds; j++) {
+ unsigned short mask = 0;
+ unsigned r = ufds[curlfds + j].revents;
+
+ if(r & POLLIN)
+ mask |= CURL_WAIT_POLLIN;
+ if(r & POLLOUT)
+ mask |= CURL_WAIT_POLLOUT;
+ if(r & POLLPRI)
+ mask |= CURL_WAIT_POLLPRI;
+
+ extra_fds[j].revents = mask;
+ }
+ }
+ }
else
i = 0;
@@ -894,7 +898,7 @@ CURLMcode curl_multi_wait(CURLM *multi_handle,
static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct timeval now,
- struct Curl_one_easy *easy)
+ struct SessionHandle *data)
{
struct Curl_message *msg = NULL;
bool connected;
@@ -904,14 +908,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
bool done = FALSE;
CURLMcode result = CURLM_OK;
struct SingleRequest *k;
- struct SessionHandle *data;
long timeout_ms;
+ int control;
- if(!GOOD_EASY_HANDLE(easy->easy_handle))
+ if(!GOOD_EASY_HANDLE(data))
return CURLM_BAD_EASY_HANDLE;
- data = easy->easy_handle;
-
do {
/* this is a single-iteration do-while loop just to allow a
break to skip to the end of it */
@@ -921,51 +923,51 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
we're using gets cleaned up and we're left with nothing. */
if(data->state.pipe_broke) {
infof(data, "Pipe broke: handle 0x%p, url = %s\n",
- easy, data->state.path);
+ (void *)data, data->state.path);
- if(easy->state < CURLM_STATE_COMPLETED) {
+ if(data->mstate < CURLM_STATE_COMPLETED) {
/* Head back to the CONNECT state */
- multistate(easy, CURLM_STATE_CONNECT);
+ multistate(data, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM;
- easy->result = CURLE_OK;
+ data->result = CURLE_OK;
}
data->state.pipe_broke = FALSE;
- easy->easy_conn = NULL;
+ data->easy_conn = NULL;
break;
}
- if(!easy->easy_conn &&
- easy->state > CURLM_STATE_CONNECT &&
- easy->state < CURLM_STATE_DONE) {
- /* In all these states, the code will blindly access 'easy->easy_conn'
+ if(!data->easy_conn &&
+ data->mstate > CURLM_STATE_CONNECT &&
+ data->mstate < CURLM_STATE_DONE) {
+ /* In all these states, the code will blindly access 'data->easy_conn'
so this is precaution that it isn't NULL. And it silences static
analyzers. */
- failf(data, "In state %d with no easy_conn, bail out!\n", easy->state);
+ failf(data, "In state %d with no easy_conn, bail out!\n", data->mstate);
return CURLM_INTERNAL_ERROR;
}
- if(easy->easy_conn && easy->state > CURLM_STATE_CONNECT &&
- easy->state < CURLM_STATE_COMPLETED)
+ if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT &&
+ data->mstate < CURLM_STATE_COMPLETED)
/* Make sure we set the connection's current owner */
- easy->easy_conn->data = data;
+ data->easy_conn->data = data;
- if(easy->easy_conn &&
- (easy->state >= CURLM_STATE_CONNECT) &&
- (easy->state < CURLM_STATE_COMPLETED)) {
+ if(data->easy_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 */
timeout_ms = Curl_timeleft(data, &now,
- (easy->state <= CURLM_STATE_WAITDO)?
+ (data->mstate <= CURLM_STATE_WAITDO)?
TRUE:FALSE);
if(timeout_ms < 0) {
/* Handle timed out */
- if(easy->state == CURLM_STATE_WAITRESOLVE)
+ if(data->mstate == CURLM_STATE_WAITRESOLVE)
failf(data, "Resolving timed out after %ld milliseconds",
Curl_tvdiff(now, data->progress.t_startsingle));
- else if(easy->state == CURLM_STATE_WAITCONNECT)
+ else if(data->mstate == CURLM_STATE_WAITCONNECT)
failf(data, "Connection timed out after %ld milliseconds",
Curl_tvdiff(now, data->progress.t_startsingle));
else {
@@ -979,41 +981,52 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Force the connection closed because the server could continue to
send us stuff at any time. (The disconnect_conn logic used below
doesn't work at this point). */
- easy->easy_conn->bits.close = TRUE;
- easy->result = CURLE_OPERATION_TIMEDOUT;
- multistate(easy, CURLM_STATE_COMPLETED);
+ data->easy_conn->bits.close = TRUE;
+ data->result = CURLE_OPERATION_TIMEDOUT;
+ multistate(data, CURLM_STATE_COMPLETED);
break;
}
}
- switch(easy->state) {
+ switch(data->mstate) {
case CURLM_STATE_INIT:
/* init this transfer. */
- easy->result=Curl_pretransfer(data);
+ data->result=Curl_pretransfer(data);
- if(CURLE_OK == easy->result) {
+ if(CURLE_OK == data->result) {
/* after init, go CONNECT */
- multistate(easy, CURLM_STATE_CONNECT);
+ multistate(data, CURLM_STATE_CONNECT);
result = 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 get a connection identifier filled in. */
+ /* Connect. We want to get a connection identifier filled in. */
Curl_pgrsTime(data, TIMER_STARTSINGLE);
- easy->result = Curl_connect(data, &easy->easy_conn,
+ data->result = Curl_connect(data, &data->easy_conn,
&async, &protocol_connect);
+ if(CURLE_NO_CONNECTION_AVAILABLE == data->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);
+ data->result = CURLE_OK;
+ break;
+ }
- if(CURLE_OK == easy->result) {
+ if(CURLE_OK == data->result) {
/* Add this handle to the send or pend pipeline */
- easy->result = addHandleToSendOrPendPipeline(data,
- easy->easy_conn);
- if(CURLE_OK != easy->result)
+ data->result = Curl_add_handle_to_pipeline(data, data->easy_conn);
+ if(CURLE_OK != data->result)
disconnect_conn = TRUE;
else {
if(async)
/* We're now waiting for an asynchronous name lookup */
- multistate(easy, CURLM_STATE_WAITRESOLVE);
+ multistate(data, CURLM_STATE_WAITRESOLVE);
else {
/* after the connect has been sent off, go WAITCONNECT unless the
protocol connect is already done and we can go directly to
@@ -1021,15 +1034,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect)
- multistate(easy, multi->pipelining_enabled?
+ multistate(data, multi->pipelining_enabled?
CURLM_STATE_WAITDO:CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
- if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
- multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
+ if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+ multistate(data, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
- multistate(easy, CURLM_STATE_WAITCONNECT);
+ multistate(data, CURLM_STATE_WAITCONNECT);
}
}
}
@@ -1042,7 +1055,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct Curl_dns_entry *dns = NULL;
/* check if we have the name resolved by now */
- easy->result = Curl_resolver_is_resolved(easy->easy_conn, &dns);
+ data->result = Curl_resolver_is_resolved(data->easy_conn, &dns);
/* Update sockets here, because the socket(s) may have been
closed and the application thus needs to be told, even if it
@@ -1050,36 +1063,36 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
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, easy);
+ singlesocket(multi, data);
if(dns) {
/* Perform the next step in the connection phase, and then move on
to the WAITCONNECT state */
- easy->result = Curl_async_resolved(easy->easy_conn,
+ data->result = Curl_async_resolved(data->easy_conn,
&protocol_connect);
- if(CURLE_OK != easy->result)
+ if(CURLE_OK != data->result)
/* if Curl_async_resolved() returns failure, the connection struct
is already freed and gone */
- easy->easy_conn = NULL; /* no more connection */
+ data->easy_conn = NULL; /* no more connection */
else {
/* call again please so that we get the next socket setup */
result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect)
- multistate(easy, multi->pipelining_enabled?
+ multistate(data, multi->pipelining_enabled?
CURLM_STATE_WAITDO:CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
- if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
- multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
+ if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+ multistate(data, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
- multistate(easy, CURLM_STATE_WAITCONNECT);
+ multistate(data, CURLM_STATE_WAITCONNECT);
}
}
}
- if(CURLE_OK != easy->result) {
+ if(CURLE_OK != data->result) {
/* failure detected */
disconnect_conn = TRUE;
break;
@@ -1090,40 +1103,40 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
#ifndef CURL_DISABLE_HTTP
case CURLM_STATE_WAITPROXYCONNECT:
/* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
- easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect);
+ data->result = Curl_http_connect(data->easy_conn, &protocol_connect);
- if(easy->easy_conn->bits.proxy_connect_closed) {
+ if(data->easy_conn->bits.proxy_connect_closed) {
/* reset the error buffer */
if(data->set.errorbuffer)
data->set.errorbuffer[0] = '\0';
data->state.errorbuf = FALSE;
- easy->result = CURLE_OK;
+ data->result = CURLE_OK;
result = CURLM_CALL_MULTI_PERFORM;
- multistate(easy, CURLM_STATE_CONNECT);
+ multistate(data, CURLM_STATE_CONNECT);
}
- else if(CURLE_OK == easy->result) {
- if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
- multistate(easy, CURLM_STATE_WAITCONNECT);
+ else if(CURLE_OK == data->result) {
+ if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
+ multistate(data, CURLM_STATE_WAITCONNECT);
}
break;
#endif
case CURLM_STATE_WAITCONNECT:
/* awaiting a completion of an asynch connect */
- easy->result = Curl_is_connected(easy->easy_conn,
+ data->result = Curl_is_connected(data->easy_conn,
FIRSTSOCKET,
&connected);
if(connected) {
- if(!easy->result)
+ if(!data->result)
/* if everything is still fine we do the protocol-specific connect
setup */
- easy->result = Curl_protocol_connect(easy->easy_conn,
+ data->result = Curl_protocol_connect(data->easy_conn,
&protocol_connect);
}
- if(CURLE_OK != easy->result) {
+ if(CURLE_OK != data->result) {
/* failure detected */
/* Just break, the cleaning up is handled all in one place */
disconnect_conn = TRUE;
@@ -1138,16 +1151,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
BUT if we are using a proxy we must change to WAITPROXYCONNECT
*/
#ifndef CURL_DISABLE_HTTP
- if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
- multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
+ if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+ multistate(data, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
- multistate(easy, CURLM_STATE_PROTOCONNECT);
+ multistate(data, CURLM_STATE_PROTOCONNECT);
}
else
/* after the connect has completed, go WAITDO or DO */
- multistate(easy, multi->pipelining_enabled?
+ multistate(data, multi->pipelining_enabled?
CURLM_STATE_WAITDO:CURLM_STATE_DO);
result = CURLM_CALL_MULTI_PERFORM;
@@ -1156,18 +1169,18 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_PROTOCONNECT:
/* protocol-specific connect phase */
- easy->result = Curl_protocol_connecting(easy->easy_conn,
+ data->result = Curl_protocol_connecting(data->easy_conn,
&protocol_connect);
- if((easy->result == CURLE_OK) && protocol_connect) {
+ if((data->result == CURLE_OK) && protocol_connect) {
/* after the connect has completed, go WAITDO or DO */
- multistate(easy, multi->pipelining_enabled?
+ multistate(data, multi->pipelining_enabled?
CURLM_STATE_WAITDO:CURLM_STATE_DO);
result = CURLM_CALL_MULTI_PERFORM;
}
- else if(easy->result) {
+ else if(data->result) {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&easy->easy_conn, easy->result, TRUE);
+ Curl_done(&data->easy_conn, data->result, TRUE);
disconnect_conn = TRUE;
}
break;
@@ -1175,19 +1188,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_WAITDO:
/* Wait for our turn to DO when we're pipelining requests */
#ifdef DEBUGBUILD
- infof(data, "WAITDO: Conn %ld send pipe %zu inuse %d athead %d\n",
- easy->easy_conn->connection_id,
- easy->easy_conn->send_pipe->size,
- easy->easy_conn->writechannel_inuse?1:0,
+ infof(data, "WAITDO: Conn %ld send pipe %zu inuse %s athead %s\n",
+ data->easy_conn->connection_id,
+ data->easy_conn->send_pipe->size,
+ data->easy_conn->writechannel_inuse?"TRUE":"FALSE",
isHandleAtHead(data,
- easy->easy_conn->send_pipe)?1:0);
+ data->easy_conn->send_pipe)?"TRUE":"FALSE");
#endif
- if(!easy->easy_conn->writechannel_inuse &&
+ if(!data->easy_conn->writechannel_inuse &&
isHandleAtHead(data,
- easy->easy_conn->send_pipe)) {
+ data->easy_conn->send_pipe)) {
/* Grab the channel */
- easy->easy_conn->writechannel_inuse = TRUE;
- multistate(easy, CURLM_STATE_DO);
+ data->easy_conn->writechannel_inuse = TRUE;
+ multistate(data, CURLM_STATE_DO);
result = CURLM_CALL_MULTI_PERFORM;
}
break;
@@ -1195,50 +1208,51 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_DO:
if(data->set.connect_only) {
/* keep connection open for application to use the socket */
- easy->easy_conn->bits.close = FALSE;
- multistate(easy, CURLM_STATE_DONE);
- easy->result = CURLE_OK;
+ data->easy_conn->bits.close = FALSE;
+ multistate(data, CURLM_STATE_DONE);
+ data->result = CURLE_OK;
result = CURLM_CALL_MULTI_PERFORM;
}
else {
/* Perform the protocol's DO action */
- easy->result = Curl_do(&easy->easy_conn,
- &dophase_done);
+ data->result = Curl_do(&data->easy_conn, &dophase_done);
+
+ /* When Curl_do() returns failure, data->easy_conn might be NULL! */
- if(CURLE_OK == easy->result) {
+ if(CURLE_OK == data->result) {
if(!dophase_done) {
/* some steps needed for wildcard matching */
if(data->set.wildcardmatch) {
struct WildcardData *wc = &data->wildcard;
if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
/* skip some states if it is important */
- Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
- multistate(easy, CURLM_STATE_DONE);
+ Curl_done(&data->easy_conn, CURLE_OK, FALSE);
+ multistate(data, CURLM_STATE_DONE);
result = CURLM_CALL_MULTI_PERFORM;
break;
}
}
/* DO was not completed in one function call, we must continue
DOING... */
- multistate(easy, CURLM_STATE_DOING);
+ multistate(data, CURLM_STATE_DOING);
result = CURLM_OK;
}
/* after DO, go DO_DONE... or DO_MORE */
- else if(easy->easy_conn->bits.do_more) {
+ else if(data->easy_conn->bits.do_more) {
/* we're supposed to do more, but we need to sit down, relax
and wait a little while first */
- multistate(easy, CURLM_STATE_DO_MORE);
+ multistate(data, CURLM_STATE_DO_MORE);
result = CURLM_OK;
}
else {
/* we're done with the DO, now DO_DONE */
- multistate(easy, CURLM_STATE_DO_DONE);
+ multistate(data, CURLM_STATE_DO_DONE);
result = CURLM_CALL_MULTI_PERFORM;
}
}
- else if((CURLE_SEND_ERROR == easy->result) &&
- easy->easy_conn->bits.reuse) {
+ else if((CURLE_SEND_ERROR == data->result) &&
+ data->easy_conn->bits.reuse) {
/*
* In this situation, a connection that we were trying to use
* may have unexpectedly died. If possible, send the connection
@@ -1249,17 +1263,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
CURLcode drc;
bool retry = FALSE;
- drc = Curl_retry_request(easy->easy_conn, &newurl);
+ drc = Curl_retry_request(data->easy_conn, &newurl);
if(drc) {
/* a failure here pretty much implies an out of memory */
- easy->result = drc;
+ data->result = drc;
disconnect_conn = TRUE;
}
else
retry = (newurl)?TRUE:FALSE;
Curl_posttransfer(data);
- drc = Curl_done(&easy->easy_conn, easy->result, FALSE);
+ drc = Curl_done(&data->easy_conn, data->result, FALSE);
/* When set to retry the connection, we must to go back to
* the CONNECT state */
@@ -1268,19 +1282,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
follow = FOLLOW_RETRY;
drc = Curl_follow(data, newurl, follow);
if(drc == CURLE_OK) {
- multistate(easy, CURLM_STATE_CONNECT);
+ multistate(data, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM;
- easy->result = CURLE_OK;
+ data->result = CURLE_OK;
}
else {
/* Follow failed */
- easy->result = drc;
+ data->result = drc;
free(newurl);
}
}
else {
/* done didn't return OK or SEND_ERROR */
- easy->result = drc;
+ data->result = drc;
free(newurl);
}
}
@@ -1292,7 +1306,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&easy->easy_conn, easy->result, FALSE);
+ if(data->easy_conn)
+ Curl_done(&data->easy_conn, data->result, FALSE);
disconnect_conn = TRUE;
}
}
@@ -1300,12 +1315,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_DOING:
/* we continue DOING until the DO phase is complete */
- easy->result = Curl_protocol_doing(easy->easy_conn,
+ data->result = Curl_protocol_doing(data->easy_conn,
&dophase_done);
- if(CURLE_OK == easy->result) {
+ if(CURLE_OK == data->result) {
if(dophase_done) {
/* after DO, go DO_DONE or DO_MORE */
- multistate(easy, easy->easy_conn->bits.do_more?
+ multistate(data, data->easy_conn->bits.do_more?
CURLM_STATE_DO_MORE:
CURLM_STATE_DO_DONE);
result = CURLM_CALL_MULTI_PERFORM;
@@ -1314,7 +1329,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&easy->easy_conn, easy->result, FALSE);
+ Curl_done(&data->easy_conn, data->result, FALSE);
disconnect_conn = TRUE;
}
break;
@@ -1323,13 +1338,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/*
* When we are connected, DO MORE and then go DO_DONE
*/
- easy->result = Curl_do_more(easy->easy_conn, &dophase_done);
+ data->result = Curl_do_more(data->easy_conn, &control);
/* No need to remove this handle from the send pipeline here since that
is done in Curl_done() */
- if(CURLE_OK == easy->result) {
- if(dophase_done) {
- multistate(easy, CURLM_STATE_DO_DONE);
+ if(CURLE_OK == data->result) {
+ 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);
result = CURLM_CALL_MULTI_PERFORM;
}
else
@@ -1339,54 +1358,54 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&easy->easy_conn, easy->result, FALSE);
+ Curl_done(&data->easy_conn, data->result, FALSE);
disconnect_conn = TRUE;
}
break;
case CURLM_STATE_DO_DONE:
/* Move ourselves from the send to recv pipeline */
- moveHandleFromSendToRecvPipeline(data, easy->easy_conn);
+ Curl_move_handle_from_send_to_recv_pipe(data, data->easy_conn);
/* Check if we can move pending requests to send pipe */
- checkPendPipeline(easy->easy_conn);
- multistate(easy, CURLM_STATE_WAITPERFORM);
+ Curl_multi_process_pending_handles(multi);
+ multistate(data, CURLM_STATE_WAITPERFORM);
result = CURLM_CALL_MULTI_PERFORM;
break;
case CURLM_STATE_WAITPERFORM:
/* Wait for our turn to PERFORM */
- if(!easy->easy_conn->readchannel_inuse &&
+ if(!data->easy_conn->readchannel_inuse &&
isHandleAtHead(data,
- easy->easy_conn->recv_pipe)) {
+ data->easy_conn->recv_pipe)) {
/* Grab the channel */
- easy->easy_conn->readchannel_inuse = TRUE;
- multistate(easy, CURLM_STATE_PERFORM);
+ data->easy_conn->readchannel_inuse = TRUE;
+ multistate(data, CURLM_STATE_PERFORM);
result = CURLM_CALL_MULTI_PERFORM;
}
#ifdef DEBUGBUILD
else {
- infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %d athead %d\n",
- easy->easy_conn->connection_id,
- easy->easy_conn->recv_pipe->size,
- easy->easy_conn->readchannel_inuse?1:0,
+ infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %s athead %s\n",
+ data->easy_conn->connection_id,
+ data->easy_conn->recv_pipe->size,
+ data->easy_conn->readchannel_inuse?"TRUE":"FALSE",
isHandleAtHead(data,
- easy->easy_conn->recv_pipe)?1:0);
+ data->easy_conn->recv_pipe)?"TRUE":"FALSE");
}
#endif
break;
case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
/* if both rates are within spec, resume transfer */
- if(Curl_pgrsUpdate(easy->easy_conn))
- easy->result = CURLE_ABORTED_BY_CALLBACK;
+ if(Curl_pgrsUpdate(data->easy_conn))
+ data->result = CURLE_ABORTED_BY_CALLBACK;
else
- easy->result = Curl_speedcheck(data, now);
+ data->result = Curl_speedcheck(data, now);
if(( (data->set.max_send_speed == 0) ||
(data->progress.ulspeed < data->set.max_send_speed )) &&
( (data->set.max_recv_speed == 0) ||
(data->progress.dlspeed < data->set.max_recv_speed)))
- multistate(easy, CURLM_STATE_PERFORM);
+ multistate(data, CURLM_STATE_PERFORM);
break;
case CURLM_STATE_PERFORM:
@@ -1399,7 +1418,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
(data->progress.ulspeed > data->set.max_send_speed)) {
int buffersize;
- multistate(easy, CURLM_STATE_TOOFAST);
+ multistate(data, CURLM_STATE_TOOFAST);
/* calculate upload rate-limitation timeout. */
buffersize = (int)(data->set.buffer_size ?
@@ -1415,7 +1434,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
(data->progress.dlspeed > data->set.max_recv_speed)) {
int buffersize;
- multistate(easy, CURLM_STATE_TOOFAST);
+ multistate(data, CURLM_STATE_TOOFAST);
/* Calculate download rate-limitation timeout. */
buffersize = (int)(data->set.buffer_size ?
@@ -1427,38 +1446,38 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
/* read/write data if it is ready to do so */
- easy->result = Curl_readwrite(easy->easy_conn, &done);
+ data->result = Curl_readwrite(data->easy_conn, &done);
k = &data->req;
if(!(k->keepon & KEEP_RECV)) {
/* We're done receiving */
- easy->easy_conn->readchannel_inuse = FALSE;
+ data->easy_conn->readchannel_inuse = FALSE;
}
if(!(k->keepon & KEEP_SEND)) {
/* We're done sending */
- easy->easy_conn->writechannel_inuse = FALSE;
+ data->easy_conn->writechannel_inuse = FALSE;
}
- if(done || (easy->result == CURLE_RECV_ERROR)) {
+ if(done || (data->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.
*/
- CURLcode ret = Curl_retry_request(easy->easy_conn, &newurl);
+ CURLcode ret = Curl_retry_request(data->easy_conn, &newurl);
if(!ret)
retry = (newurl)?TRUE:FALSE;
if(retry) {
/* if we are to retry, set the result to OK and consider the
request as done */
- easy->result = CURLE_OK;
+ data->result = CURLE_OK;
done = TRUE;
}
}
- if(easy->result) {
+ if(data->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
@@ -1467,11 +1486,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
* happened in the data connection.
*/
- if(!(easy->easy_conn->handler->flags & PROTOPT_DUAL))
- easy->easy_conn->bits.close = TRUE;
+ if(!(data->easy_conn->handler->flags & PROTOPT_DUAL))
+ data->easy_conn->bits.close = TRUE;
Curl_posttransfer(data);
- Curl_done(&easy->easy_conn, easy->result, FALSE);
+ Curl_done(&data->easy_conn, data->result, FALSE);
}
else if(done) {
followtype follow=FOLLOW_NONE;
@@ -1480,15 +1499,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
Curl_posttransfer(data);
/* we're no longer receiving */
- moveHandleFromRecvToDonePipeline(data,
- easy->easy_conn);
+ Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
/* expire the new receiving pipeline head */
- if(easy->easy_conn->recv_pipe->head)
- Curl_expire(easy->easy_conn->recv_pipe->head->ptr, 1);
+ if(data->easy_conn->recv_pipe->head)
+ Curl_expire(data->easy_conn->recv_pipe->head->ptr, 1);
/* Check if we can move pending requests to send pipe */
- checkPendPipeline(easy->easy_conn);
+ Curl_multi_process_pending_handles(multi);
/* When we follow redirects or is set to retry the connection, we must
to go back to the CONNECT state */
@@ -1502,15 +1520,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else
follow = FOLLOW_RETRY;
- easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
- if(easy->result == CURLE_OK)
- easy->result = Curl_follow(data, newurl, follow);
- if(CURLE_OK == easy->result) {
- multistate(easy, CURLM_STATE_CONNECT);
- result = CURLM_CALL_MULTI_PERFORM;
- newurl = NULL; /* handed over the memory ownership to
- Curl_follow(), make sure we don't free() it
- here */
+ data->result = Curl_done(&data->easy_conn, CURLE_OK, FALSE);
+ if(CURLE_OK == data->result) {
+ data->result = Curl_follow(data, newurl, follow);
+ if(CURLE_OK == data->result) {
+ multistate(data, CURLM_STATE_CONNECT);
+ result = CURLM_CALL_MULTI_PERFORM;
+ newurl = NULL; /* handed over the memory ownership to
+ Curl_follow(), make sure we don't free() it
+ here */
+ }
}
}
else {
@@ -1523,14 +1542,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
free(newurl);
newurl = data->req.location;
data->req.location = NULL;
- easy->result = Curl_follow(data, newurl, FOLLOW_FAKE);
- if(CURLE_OK == easy->result)
+ data->result = Curl_follow(data, newurl, FOLLOW_FAKE);
+ if(CURLE_OK == data->result)
newurl = NULL; /* allocation was handed over Curl_follow() */
else
disconnect_conn = TRUE;
}
- multistate(easy, CURLM_STATE_DONE);
+ multistate(data, CURLM_STATE_DONE);
result = CURLM_CALL_MULTI_PERFORM;
}
}
@@ -1541,50 +1560,39 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
case CURLM_STATE_DONE:
+ /* this state is highly transient, so run another loop after this */
+ result = CURLM_CALL_MULTI_PERFORM;
- if(easy->easy_conn) {
- /* Remove ourselves from the receive and done pipelines. Handle
- should be on one of these lists, depending upon how we got here. */
- Curl_removeHandleFromPipeline(data,
- easy->easy_conn->recv_pipe);
- Curl_removeHandleFromPipeline(data,
- easy->easy_conn->done_pipe);
+ if(data->easy_conn) {
+ /* Remove ourselves from the receive pipeline, if we are there. */
+ Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
/* Check if we can move pending requests to send pipe */
- checkPendPipeline(easy->easy_conn);
-
- if(easy->easy_conn->bits.stream_was_rewound) {
- /* This request read past its response boundary so we quickly let
- the other requests consume those bytes since there is no
- guarantee that the socket will become active again */
- result = CURLM_CALL_MULTI_PERFORM;
- }
+ Curl_multi_process_pending_handles(multi);
/* post-transfer command */
- easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
+ data->result = Curl_done(&data->easy_conn, CURLE_OK, FALSE);
/*
* If there are other handles on the pipeline, Curl_done won't set
* easy_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
*/
- if(easy->easy_conn)
- easy->easy_conn = NULL;
+ if(data->easy_conn)
+ data->easy_conn = NULL;
}
if(data->set.wildcardmatch) {
if(data->wildcard.state != CURLWC_DONE) {
/* if a wildcard is set and we are not ending -> lets start again
with CURLM_STATE_INIT */
- result = CURLM_CALL_MULTI_PERFORM;
- multistate(easy, CURLM_STATE_INIT);
+ multistate(data, CURLM_STATE_INIT);
break;
}
}
/* after we have DONE what we're supposed to do, go COMPLETED, and
it doesn't matter what the Curl_done() returned! */
- multistate(easy, CURLM_STATE_COMPLETED);
-
+ multistate(data, CURLM_STATE_COMPLETED);
break;
case CURLM_STATE_COMPLETED:
@@ -1595,7 +1603,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Important: reset the conn pointer so that we don't point to memory
that could be freed anytime */
- easy->easy_conn = NULL;
+ data->easy_conn = NULL;
Curl_expire(data, 0); /* stop all timers */
break;
@@ -1607,8 +1615,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
return CURLM_INTERNAL_ERROR;
}
- if(easy->state < CURLM_STATE_COMPLETED) {
- if(CURLE_OK != easy->result) {
+ if(data->mstate < CURLM_STATE_COMPLETED) {
+ if(CURLE_OK != data->result) {
/*
* If an error was returned, and we aren't in completed state now,
* then we go to completed and consider this transfer aborted.
@@ -1619,61 +1627,59 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
data->state.pipe_broke = FALSE;
- if(easy->easy_conn) {
+ if(data->easy_conn) {
/* if this has a connection, unsubscribe from the pipelines */
- easy->easy_conn->writechannel_inuse = FALSE;
- easy->easy_conn->readchannel_inuse = FALSE;
+ data->easy_conn->writechannel_inuse = FALSE;
+ data->easy_conn->readchannel_inuse = FALSE;
Curl_removeHandleFromPipeline(data,
- easy->easy_conn->send_pipe);
+ data->easy_conn->send_pipe);
Curl_removeHandleFromPipeline(data,
- easy->easy_conn->recv_pipe);
- Curl_removeHandleFromPipeline(data,
- easy->easy_conn->done_pipe);
+ data->easy_conn->recv_pipe);
/* Check if we can move pending requests to send pipe */
- checkPendPipeline(easy->easy_conn);
+ Curl_multi_process_pending_handles(multi);
if(disconnect_conn) {
/* disconnect properly */
- Curl_disconnect(easy->easy_conn, /* dead_connection */ FALSE);
+ Curl_disconnect(data->easy_conn, /* dead_connection */ FALSE);
/* This is where we make sure that the easy_conn pointer is reset.
We don't have to do this in every case block above where a
failure is detected */
- easy->easy_conn = NULL;
+ data->easy_conn = NULL;
}
}
- else if(easy->state == CURLM_STATE_CONNECT) {
+ else if(data->mstate == CURLM_STATE_CONNECT) {
/* Curl_connect() failed */
(void)Curl_posttransfer(data);
}
- multistate(easy, CURLM_STATE_COMPLETED);
+ multistate(data, CURLM_STATE_COMPLETED);
}
/* if there's still a connection to use, call the progress function */
- else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) {
+ else if(data->easy_conn && Curl_pgrsUpdate(data->easy_conn)) {
/* aborted due to progress callback return code must close the
connection */
- easy->easy_conn->bits.close = TRUE;
+ data->easy_conn->bits.close = TRUE;
/* if not yet in DONE state, go there, otherwise COMPLETED */
- multistate(easy, (easy->state < CURLM_STATE_DONE)?
+ multistate(data, (data->mstate < CURLM_STATE_DONE)?
CURLM_STATE_DONE: CURLM_STATE_COMPLETED);
result = CURLM_CALL_MULTI_PERFORM;
}
}
} WHILE_FALSE; /* just to break out from! */
- if(CURLM_STATE_COMPLETED == easy->state) {
+ if(CURLM_STATE_COMPLETED == data->mstate) {
/* now fill in the Curl_message with this info */
- msg = &easy->msg;
+ msg = &data->msg;
msg->extmsg.msg = CURLMSG_DONE;
msg->extmsg.easy_handle = data;
- msg->extmsg.data.result = easy->result;
+ msg->extmsg.data.result = data->result;
result = multi_addmsg(multi, msg);
- multistate(easy, CURLM_STATE_MSGSENT);
+ multistate(data, CURLM_STATE_MSGSENT);
}
return result;
@@ -1683,7 +1689,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
- struct Curl_one_easy *easy;
+ struct SessionHandle *data;
CURLMcode returncode=CURLM_OK;
struct Curl_tree *t;
struct timeval now = Curl_tvnow();
@@ -1691,12 +1697,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
- easy=multi->easy.next;
- while(easy != &multi->easy) {
+ data=multi->easyp;
+ while(data) {
CURLMcode result;
- struct WildcardData *wc = &easy->easy_handle->wildcard;
+ struct WildcardData *wc = &data->wildcard;
- if(easy->easy_handle->set.wildcardmatch) {
+ if(data->set.wildcardmatch) {
if(!wc->filelist) {
CURLcode ret = Curl_wildcard_init(wc); /* init wildcard structures */
if(ret)
@@ -1705,10 +1711,10 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
}
do
- result = multi_runsingle(multi, now, easy);
+ result = multi_runsingle(multi, now, data);
while(CURLM_CALL_MULTI_PERFORM == result);
- if(easy->easy_handle->set.wildcardmatch) {
+ if(data->set.wildcardmatch) {
/* destruct wildcard structures if it is needed */
if(wc->state == CURLWC_DONE || result)
Curl_wildcard_dtor(wc);
@@ -1717,7 +1723,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
if(result)
returncode = result;
- easy = easy->next; /* operate on next handle */
+ data = data->next; /* operate on next handle */
}
/*
@@ -1764,8 +1770,8 @@ static void close_all_connections(struct Curl_multi *multi)
CURLMcode curl_multi_cleanup(CURLM *multi_handle)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
- struct Curl_one_easy *easy;
- struct Curl_one_easy *nexteasy;
+ struct SessionHandle *data;
+ struct SessionHandle *nextdata;
if(GOOD_MULTI_HANDLE(multi)) {
multi->type = 0; /* not good anymore */
@@ -1773,11 +1779,14 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
/* Close all the connections in the connection cache */
close_all_connections(multi);
- multi->closure_handle->dns.hostcache = multi->hostcache;
- Curl_hostcache_clean(multi->closure_handle);
+ if(multi->closure_handle) {
+ multi->closure_handle->dns.hostcache = multi->hostcache;
+ Curl_hostcache_clean(multi->closure_handle,
+ multi->closure_handle->dns.hostcache);
- Curl_close(multi->closure_handle);
- multi->closure_handle = NULL;
+ Curl_close(multi->closure_handle);
+ multi->closure_handle = NULL;
+ }
Curl_hash_destroy(multi->sockhash);
multi->sockhash = NULL;
@@ -1790,28 +1799,30 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
multi->msglist = NULL;
/* remove all easy handles */
- easy = multi->easy.next;
- while(easy != &multi->easy) {
- nexteasy=easy->next;
- if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
+ data = multi->easyp;
+ while(data) {
+ nextdata=data->next;
+ if(data->dns.hostcachetype == HCACHE_MULTI) {
/* clear out the usage of the shared DNS cache */
- Curl_hostcache_clean(easy->easy_handle);
- easy->easy_handle->dns.hostcache = NULL;
- easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+ Curl_hostcache_clean(data, data->dns.hostcache);
+ data->dns.hostcache = NULL;
+ data->dns.hostcachetype = HCACHE_NONE;
}
/* Clear the pointer to the connection cache */
- easy->easy_handle->state.conn_cache = NULL;
-
- Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */
+ data->state.conn_cache = NULL;
+ data->multi = NULL; /* clear the association */
- free(easy);
- easy = nexteasy;
+ data = nextdata;
}
Curl_hash_destroy(multi->hostcache);
multi->hostcache = NULL;
+ /* Free the blacklists by setting them to NULL */
+ Curl_pipeline_set_site_blacklist(NULL, &multi->pipelining_site_bl);
+ Curl_pipeline_set_server_blacklist(NULL, &multi->pipelining_server_bl);
+
free(multi);
return CURLM_OK;
@@ -1863,7 +1874,7 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
* call the callback accordingly.
*/
static void singlesocket(struct Curl_multi *multi,
- struct Curl_one_easy *easy)
+ struct SessionHandle *data)
{
curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
int i;
@@ -1871,7 +1882,6 @@ static void singlesocket(struct Curl_multi *multi,
curl_socket_t s;
int num;
unsigned int curraction;
- struct Curl_one_easy *easy_by_hash;
bool remove_sock_from_hash;
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
@@ -1879,7 +1889,7 @@ static void singlesocket(struct Curl_multi *multi,
/* Fill in the 'current' struct with the state as it is now: what sockets to
supervise and for what actions */
- curraction = multi_getsock(easy, socks, MAX_SOCKSPEREASYHANDLE);
+ curraction = multi_getsock(data, socks, MAX_SOCKSPEREASYHANDLE);
/* 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
@@ -1909,7 +1919,7 @@ static void singlesocket(struct Curl_multi *multi,
}
else {
/* this is a socket we didn't have before, add it! */
- entry = sh_addentry(multi->sockhash, s, easy->easy_handle);
+ entry = sh_addentry(multi->sockhash, s, data);
if(!entry)
/* fatal */
return;
@@ -1917,7 +1927,7 @@ static void singlesocket(struct Curl_multi *multi,
/* we know (entry != NULL) at this point, see the logic above */
if(multi->socket_cb)
- multi->socket_cb(easy->easy_handle,
+ multi->socket_cb(data,
s,
action,
multi->socket_userp,
@@ -1930,9 +1940,9 @@ static void singlesocket(struct Curl_multi *multi,
/* 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< easy->numsocks; i++) {
+ for(i=0; i< data->numsocks; i++) {
int j;
- s = easy->sockets[i];
+ s = data->sockets[i];
for(j=0; j<num; j++) {
if(s == socks[j]) {
/* this is still supervised */
@@ -1950,10 +1960,7 @@ static void singlesocket(struct Curl_multi *multi,
/* check if the socket to be removed serves a connection which has
other easy-s in a pipeline. In this case the socket should not be
removed. */
- struct connectdata *easy_conn;
-
- easy_by_hash = entry->easy->multi_pos;
- easy_conn = easy_by_hash->easy_conn;
+ struct connectdata *easy_conn = data->easy_conn;
if(easy_conn) {
if(easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) {
/* the handle should not be removed from the pipe yet */
@@ -1962,8 +1969,8 @@ static void singlesocket(struct Curl_multi *multi,
/* Update the sockhash entry to instead point to the next in line
for the recv_pipe, or the first (in case this particular easy
isn't already) */
- if(entry->easy == easy->easy_handle) {
- if(isHandleAtHead(easy->easy_handle, easy_conn->recv_pipe))
+ if(entry->easy == data) {
+ if(isHandleAtHead(data, easy_conn->recv_pipe))
entry->easy = easy_conn->recv_pipe->head->next->ptr;
else
entry->easy = easy_conn->recv_pipe->head->ptr;
@@ -1976,8 +1983,8 @@ static void singlesocket(struct Curl_multi *multi,
/* Update the sockhash entry to instead point to the next in line
for the send_pipe, or the first (in case this particular easy
isn't already) */
- if(entry->easy == easy->easy_handle) {
- if(isHandleAtHead(easy->easy_handle, easy_conn->send_pipe))
+ if(entry->easy == data) {
+ if(isHandleAtHead(data, easy_conn->send_pipe))
entry->easy = easy_conn->send_pipe->head->next->ptr;
else
entry->easy = easy_conn->send_pipe->head->ptr;
@@ -1998,7 +2005,7 @@ static void singlesocket(struct Curl_multi *multi,
if(remove_sock_from_hash) {
/* in this case 'entry' is always non-NULL */
if(multi->socket_cb)
- multi->socket_cb(easy->easy_handle,
+ multi->socket_cb(data,
s,
CURL_POLL_REMOVE,
multi->socket_userp,
@@ -2009,11 +2016,44 @@ static void singlesocket(struct Curl_multi *multi,
}
}
- memcpy(easy->sockets, socks, num*sizeof(curl_socket_t));
- easy->numsocks = num;
+ memcpy(data->sockets, socks, num*sizeof(curl_socket_t));
+ data->numsocks = num;
}
/*
+ * Curl_multi_closed()
+ *
+ * Used by the connect code to tell the multi_socket code that one of the
+ * sockets we were using have just been 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.
+ */
+
+void Curl_multi_closed(struct connectdata *conn, curl_socket_t s)
+{
+ struct Curl_multi *multi = conn->data->multi;
+ if(multi) {
+ /* 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 =
+ Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+
+ if(entry) {
+ if(multi->socket_cb)
+ multi->socket_cb(conn->data, s, CURL_POLL_REMOVE,
+ multi->socket_userp,
+ entry->socketp);
+
+ /* now remove it from the socket hash */
+ sh_delentry(multi->sockhash, s);
+ }
+ }
+}
+
+
+
+/*
* add_next_timeout()
*
* Each SessionHandle has a list of timeouts. The add_next_timeout() is called
@@ -2068,7 +2108,6 @@ static CURLMcode add_next_timeout(struct timeval now,
return CURLM_OK;
}
-
static CURLMcode multi_socket(struct Curl_multi *multi,
bool checkall,
curl_socket_t s,
@@ -2081,16 +2120,15 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
struct timeval now = Curl_tvnow();
if(checkall) {
- struct Curl_one_easy *easyp;
/* *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 */
- easyp=multi->easy.next;
- while(easyp != &multi->easy) {
- singlesocket(multi, easyp);
- easyp = easyp->next;
+ data=multi->easyp;
+ while(data) {
+ singlesocket(multi, data);
+ data = data->next;
}
/* or should we fall-through and do the timer-based stuff? */
@@ -2118,35 +2156,35 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
/* If the pipeline is enabled, take the handle which is in the head of
the pipeline. If we should write into the socket, take the send_pipe
head. If we should read from the socket, take the recv_pipe head. */
- if(data->set.one_easy->easy_conn) {
+ if(data->easy_conn) {
if((ev_bitmask & CURL_POLL_OUT) &&
- data->set.one_easy->easy_conn->send_pipe &&
- data->set.one_easy->easy_conn->send_pipe->head)
- data = data->set.one_easy->easy_conn->send_pipe->head->ptr;
+ data->easy_conn->send_pipe &&
+ data->easy_conn->send_pipe->head)
+ data = data->easy_conn->send_pipe->head->ptr;
else if((ev_bitmask & CURL_POLL_IN) &&
- data->set.one_easy->easy_conn->recv_pipe &&
- data->set.one_easy->easy_conn->recv_pipe->head)
- data = data->set.one_easy->easy_conn->recv_pipe->head->ptr;
+ data->easy_conn->recv_pipe &&
+ data->easy_conn->recv_pipe->head)
+ data = data->easy_conn->recv_pipe->head->ptr;
}
- if(data->set.one_easy->easy_conn &&
- !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK))
+ if(data->easy_conn &&
+ !(data->easy_conn->handler->flags & PROTOPT_DIRLOCK))
/* set socket event bitmask if they're not locked */
- data->set.one_easy->easy_conn->cselect_bits = ev_bitmask;
+ data->easy_conn->cselect_bits = ev_bitmask;
do
- result = multi_runsingle(multi, now, data->set.one_easy);
+ result = multi_runsingle(multi, now, data);
while(CURLM_CALL_MULTI_PERFORM == result);
- if(data->set.one_easy->easy_conn &&
- !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK))
+ if(data->easy_conn &&
+ !(data->easy_conn->handler->flags & PROTOPT_DIRLOCK))
/* clear the bitmask only if not locked */
- data->set.one_easy->easy_conn->cselect_bits = 0;
+ data->easy_conn->cselect_bits = 0;
if(CURLM_OK >= result)
/* get the socket(s) and check if the state has been changed since
last */
- singlesocket(multi, data->set.one_easy);
+ singlesocket(multi, data);
/* 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
@@ -2157,8 +2195,25 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
}
}
- now.tv_usec += 40000; /* compensate for bad precision timers that might've
- triggered too early */
+ /* Compensate for bad precision timers that might've triggered too early.
+
+ This precaution was added in commit 2c72732ebf3da5e as a result of bad
+ resolution in the windows function use(d).
+
+ The problematic case here is when using the multi_socket API and libcurl
+ has told the application about a timeout, and that timeout is what fires
+ off a bit early. As we don't have any IDs associated with the timeout we
+ can't tell which timeout that fired off but we only have the times to use
+ to check what to do. If it fires off too early, we don't run the correct
+ actions and we don't tell the application again about the same timeout as
+ was already first in the queue...
+
+ Originally we made the timeouts run 40 milliseconds early on all systems,
+ but now we have an #ifdef setup to provide a decent precaution inaccuracy
+ margin.
+ */
+
+ now.tv_usec += MULTI_TIMEOUT_INACCURACY;
if(now.tv_usec >= 1000000) {
now.tv_sec++;
now.tv_usec -= 1000000;
@@ -2173,13 +2228,13 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
/* the first loop lap 'data' can be NULL */
if(data) {
do
- result = multi_runsingle(multi, now, data->set.one_easy);
+ result = multi_runsingle(multi, now, data);
while(CURLM_CALL_MULTI_PERFORM == result);
if(CURLM_OK >= result)
/* get the socket(s) and check if the state has been changed since
last */
- singlesocket(multi, data->set.one_easy);
+ singlesocket(multi, data);
}
/* Check if there's one (more) expired timer to deal with! This function
@@ -2229,6 +2284,29 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle,
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_PIPELINE_LENGTH:
+ multi->max_pipeline_length = va_arg(param, long);
+ break;
+ case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
+ multi->content_length_penalty_size = va_arg(param, long);
+ break;
+ case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
+ multi->chunk_length_penalty_size = va_arg(param, long);
+ break;
+ case CURLMOPT_PIPELINING_SITE_BL:
+ res = Curl_pipeline_set_site_blacklist(va_arg(param, char **),
+ &multi->pipelining_site_bl);
+ break;
+ case CURLMOPT_PIPELINING_SERVER_BL:
+ res = Curl_pipeline_set_server_blacklist(va_arg(param, char **),
+ &multi->pipelining_server_bl);
+ break;
+ case CURLMOPT_MAX_TOTAL_CONNECTIONS:
+ multi->max_total_connections = va_arg(param, long);
+ break;
default:
res = CURLM_UNKNOWN_OPTION;
break;
@@ -2353,131 +2431,12 @@ static int update_timer(struct Curl_multi *multi)
return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp);
}
-static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
- struct connectdata *conn)
-{
- size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
- struct curl_llist_element *sendhead = conn->send_pipe->head;
- struct curl_llist *pipeline;
- CURLcode rc;
-
- if(!Curl_isPipeliningEnabled(handle) ||
- pipeLen == 0)
- pipeline = conn->send_pipe;
- else {
- if(conn->server_supports_pipelining &&
- pipeLen < MAX_PIPELINE_LENGTH)
- pipeline = conn->send_pipe;
- else
- pipeline = conn->pend_pipe;
- }
-
- rc = Curl_addHandleToPipeline(handle, pipeline);
-
- if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
- /* this is a new one as head, expire it */
- conn->writechannel_inuse = FALSE; /* not in use yet */
-#ifdef DEBUGBUILD
- infof(conn->data, "%p is at send pipe head!\n",
- conn->send_pipe->head->ptr);
-#endif
- Curl_expire(conn->send_pipe->head->ptr, 1);
- }
-
- return rc;
-}
-
-static int checkPendPipeline(struct connectdata *conn)
-{
- int result = 0;
- struct curl_llist_element *sendhead = conn->send_pipe->head;
-
- size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
- if(conn->server_supports_pipelining || pipeLen == 0) {
- struct curl_llist_element *curr = conn->pend_pipe->head;
- const size_t maxPipeLen =
- conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1;
-
- while(pipeLen < maxPipeLen && curr) {
- Curl_llist_move(conn->pend_pipe, curr,
- conn->send_pipe, conn->send_pipe->tail);
- Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER);
- ++result; /* count how many handles we moved */
- curr = conn->pend_pipe->head;
- ++pipeLen;
- }
- }
-
- if(result) {
- conn->now = Curl_tvnow();
- /* something moved, check for a new send pipeline leader */
- if(sendhead != conn->send_pipe->head) {
- /* this is a new one as head, expire it */
- conn->writechannel_inuse = FALSE; /* not in use yet */
-#ifdef DEBUGBUILD
- infof(conn->data, "%p is at send pipe head!\n",
- conn->send_pipe->head->ptr);
-#endif
- Curl_expire(conn->send_pipe->head->ptr, 1);
- }
- }
-
- return result;
-}
-
-/* Move this transfer from the sending list to the receiving list.
-
- Pay special attention to the new sending list "leader" as it needs to get
- checked to update what sockets it acts on.
-
-*/
-static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
- struct connectdata *conn)
+void Curl_multi_set_easy_connection(struct SessionHandle *handle,
+ struct connectdata *conn)
{
- struct curl_llist_element *curr;
-
- curr = conn->send_pipe->head;
- while(curr) {
- if(curr->ptr == handle) {
- Curl_llist_move(conn->send_pipe, curr,
- conn->recv_pipe, conn->recv_pipe->tail);
-
- if(conn->send_pipe->head) {
- /* Since there's a new easy handle at the start of the send pipeline,
- set its timeout value to 1ms to make it trigger instantly */
- conn->writechannel_inuse = FALSE; /* not used now */
-#ifdef DEBUGBUILD
- infof(conn->data, "%p is at send pipe head B!\n",
- conn->send_pipe->head->ptr);
-#endif
- Curl_expire(conn->send_pipe->head->ptr, 1);
- }
-
- /* The receiver's list is not really interesting here since either this
- handle is now first in the list and we'll deal with it soon, or
- another handle is already first and thus is already taken care of */
-
- break; /* we're done! */
- }
- curr = curr->next;
- }
+ handle->easy_conn = conn;
}
-static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle,
- struct connectdata *conn)
-{
- struct curl_llist_element *curr;
-
- curr = conn->recv_pipe->head;
- while(curr) {
- if(curr->ptr == handle) {
- Curl_llist_move(conn->recv_pipe, curr,
- conn->done_pipe, conn->done_pipe->tail);
- break;
- }
- curr = curr->next;
- }
-}
static bool isHandleAtHead(struct SessionHandle *handle,
struct curl_llist *pipeline)
{
@@ -2563,8 +2522,8 @@ void Curl_expire(struct SessionHandle *data, long milli)
struct timeval *nowp = &data->state.expiretime;
int rc;
- /* this is only interesting for multi-interface using libcurl, and only
- while there is still a multi interface struct remaining! */
+ /* this is only interesting while there is still an associated multi struct
+ remaining! */
if(!multi)
return;
@@ -2657,22 +2616,72 @@ CURLMcode curl_multi_assign(CURLM *multi_handle,
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;
+}
+
+size_t Curl_multi_max_pipeline_length(struct Curl_multi *multi)
+{
+ return multi ? multi->max_pipeline_length : 0;
+}
+
+curl_off_t Curl_multi_content_length_penalty_size(struct Curl_multi *multi)
+{
+ return multi ? multi->content_length_penalty_size : 0;
+}
+
+curl_off_t Curl_multi_chunk_length_penalty_size(struct Curl_multi *multi)
+{
+ return multi ? multi->chunk_length_penalty_size : 0;
+}
+
+struct curl_llist *Curl_multi_pipelining_site_bl(struct Curl_multi *multi)
+{
+ return multi->pipelining_site_bl;
+}
+
+struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi)
+{
+ return multi->pipelining_server_bl;
+}
+
+void Curl_multi_process_pending_handles(struct Curl_multi *multi)
+{
+ struct SessionHandle *data;
+
+ data=multi->easyp;
+ while(data) {
+ if(data->mstate == CURLM_STATE_CONNECT_PEND) {
+ multistate(data, CURLM_STATE_CONNECT);
+ /* Make sure that the handle will be processed soonish. */
+ Curl_expire(data, 1);
+ }
+ data = data->next; /* operate on next handle */
+ }
+}
+
#ifdef DEBUGBUILD
void Curl_multi_dump(const struct Curl_multi *multi_handle)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
- struct Curl_one_easy *easy;
+ struct SessionHandle *data;
int i;
fprintf(stderr, "* Multi status: %d handles, %d alive\n",
multi->num_easy, multi->num_alive);
- for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) {
- if(easy->state < CURLM_STATE_COMPLETED) {
+ 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 *)easy->easy_handle,
- statename[easy->state], easy->numsocks);
- for(i=0; i < easy->numsocks; i++) {
- curl_socket_t s = easy->sockets[i];
+ (void *)data,
+ statename[data->mstate], data->numsocks);
+ for(i=0; i < data->numsocks; i++) {
+ curl_socket_t s = data->sockets[i];
struct Curl_sh_entry *entry =
Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
@@ -2685,7 +2694,7 @@ void Curl_multi_dump(const struct Curl_multi *multi_handle)
entry->action&CURL_POLL_IN?"RECVING":"",
entry->action&CURL_POLL_OUT?"SENDING":"");
}
- if(easy->numsocks)
+ if(data->numsocks)
fprintf(stderr, "\n");
}
}