aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2013-01-17 12:59:23 +0100
committerDaniel Stenberg <daniel@haxx.se>2013-01-17 19:40:35 +0100
commitc43127414d89ccb9ef6517081f68986d991bcfb3 (patch)
treef6a639061f5e199089a923b052904aa24901243c /lib
parent9fd88abb7032346e88636165e688232e36f5c336 (diff)
always-multi: always use non-blocking internals
Remove internal separated behavior of the easy vs multi intercace. curl_easy_perform() is now using the multi interface itself. Several minor multi interface quirks and bugs have been fixed in the process. Much help with debugging this has been provided by: Yang Tse
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.inc2
-rw-r--r--lib/conncache.c7
-rw-r--r--lib/conncache.h10
-rw-r--r--lib/connect.c42
-rw-r--r--lib/easy.c161
-rw-r--r--lib/ftp.c168
-rw-r--r--lib/http.c34
-rw-r--r--lib/http_proxy.c33
-rw-r--r--lib/imap.c45
-rw-r--r--lib/multi.c183
-rw-r--r--lib/multihandle.h134
-rw-r--r--lib/openldap.c25
-rw-r--r--lib/pop3.c30
-rw-r--r--lib/smtp.c41
-rw-r--r--lib/ssh.c28
-rw-r--r--lib/ssluse.c12
-rw-r--r--lib/tftp.c132
-rw-r--r--lib/transfer.c4
-rw-r--r--lib/url.c98
-rw-r--r--lib/urldata.h19
20 files changed, 410 insertions, 798 deletions
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index b116b3932..e970edd90 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -43,4 +43,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
gopher.h axtls.h cyassl.h http_proxy.h non-ascii.h asyn.h curl_ntlm.h \
curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h curl_ntlm_msgs.h \
curl_sasl.h curl_schannel.h curl_multibyte.h curl_darwinssl.h \
- hostcheck.h bundles.h conncache.h curl_setup_once.h
+ hostcheck.h bundles.h conncache.h curl_setup_once.h multihandle.h
diff --git a/lib/conncache.c b/lib/conncache.c
index 165a26fb4..530cdc2ec 100644
--- a/lib/conncache.c
+++ b/lib/conncache.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -47,7 +47,7 @@ static void free_bundle_hash_entry(void *freethis)
Curl_bundle_destroy(b);
}
-struct conncache *Curl_conncache_init(conncachetype type)
+struct conncache *Curl_conncache_init(void)
{
struct conncache *connc;
@@ -63,9 +63,6 @@ struct conncache *Curl_conncache_init(conncachetype type)
return NULL;
}
- connc->type = type;
- connc->num_connections = 0;
-
return connc;
}
diff --git a/lib/conncache.h b/lib/conncache.h
index 03b129d4e..fad17d8f7 100644
--- a/lib/conncache.h
+++ b/lib/conncache.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) 2012, 2013, Linus Nielsen Feltzing, <linus@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,18 +22,12 @@
*
***************************************************************************/
-typedef enum {
- CONNCACHE_PRIVATE, /* used for an easy handle alone */
- CONNCACHE_MULTI /* shared within a multi handle */
-} conncachetype;
-
struct conncache {
struct curl_hash *hash;
- conncachetype type;
size_t num_connections;
};
-struct conncache *Curl_conncache_init(conncachetype type);
+struct conncache *Curl_conncache_init(void);
void Curl_conncache_destroy(struct conncache *connc);
diff --git a/lib/connect.c b/lib/connect.c
index acaf3b485..c411a0eb5 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -74,6 +74,8 @@
#include "sslgen.h" /* for Curl_ssl_check_cxn() */
#include "progress.h"
#include "warnless.h"
+#include "conncache.h"
+#include "multihandle.h"
/* The last #include file should be: */
#include "memdebug.h"
@@ -980,8 +982,7 @@ singleipconnect(struct connectdata *conn,
/* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
connect(). We can be sure of this since connect() cannot return 1. */
- if((WAITCONN_TIMEOUT == rc) &&
- (data->state.used_interface == Curl_if_multi)) {
+ if(WAITCONN_TIMEOUT == rc) {
/* Timeout when running the multi interface */
*sockp = sockfd;
return CURLE_OK;
@@ -1072,9 +1073,8 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
/* start connecting to the IP curr_addr points to */
res = singleipconnect(conn, curr_addr,
- /* don't hang when doing multi */
- (data->state.used_interface == Curl_if_multi)?0:
- conn->timeoutms_per_addr, &sockfd, connected);
+ 0, /* don't hang when doing multi */
+ &sockfd, connected);
if(res)
return res;
@@ -1112,6 +1112,21 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
return CURLE_OK;
}
+struct connfind {
+ struct connectdata *tofind;
+ bool found;
+};
+
+static int conn_is_conn(struct connectdata *conn, void *param)
+{
+ struct connfind *f = (struct connfind *)param;
+ if(conn == f->tofind) {
+ f->found = TRUE;
+ return 1;
+ }
+ return 0;
+}
+
/*
* Used to extract socket and connectdata struct for the most recent
* transfer on the given SessionHandle.
@@ -1125,8 +1140,21 @@ curl_socket_t Curl_getconnectinfo(struct SessionHandle *data,
DEBUGASSERT(data);
- if(data->state.lastconnect) {
+ /* this only works for an easy handle that has been used for
+ curl_easy_perform()! */
+ if(data->state.lastconnect && data->multi_easy) {
struct connectdata *c = data->state.lastconnect;
+ struct connfind find;
+ find.tofind = data->state.lastconnect;
+ find.found = FALSE;
+
+ Curl_conncache_foreach(data->multi_easy->conn_cache, &find, conn_is_conn);
+
+ if(!find.found) {
+ data->state.lastconnect = NULL;
+ return CURL_SOCKET_BAD;
+ }
+
if(connp)
/* only store this if the caller cares for it */
*connp = c;
diff --git a/lib/easy.c b/lib/easy.c
index 700fb327b..2f4b48b80 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -385,40 +385,46 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...)
return ret;
}
-#ifdef CURL_MULTIEASY
-/***************************************************************************
- * This function is still only for testing purposes. It makes a great way
- * to run the full test suite on the multi interface instead of the easy one.
- ***************************************************************************
- *
- * The *new* curl_easy_perform() is the external interface that performs a
- * transfer previously setup.
+/*
+ * curl_easy_perform() is the external interface that performs a blocking
+ * transfer as previously setup.
*
- * Wrapper-function that: creates a multi handle, adds the easy handle to it,
+ * CONCEPT: This function creates a multi handle, adds the easy handle to it,
* runs curl_multi_perform() until the transfer is done, then detaches the
* easy handle, destroys the multi handle and returns the easy handle's return
- * code. This will make everything internally use and assume multi interface.
+ * code.
+ *
+ * REALITY: it can't just create and destroy the multi handle that easily. It
+ * needs to keep it around since if this easy handle is used again by this
+ * function, the same multi handle must be re-used so that the same pools and
+ * caches can be used.
*/
CURLcode curl_easy_perform(CURL *easy)
{
CURLM *multi;
CURLMcode mcode;
CURLcode code = CURLE_OK;
- int still_running;
- struct timeval timeout;
- int rc;
CURLMsg *msg;
- fd_set fdread;
- fd_set fdwrite;
- fd_set fdexcep;
- int maxfd;
+ bool done = FALSE;
+ int rc;
+ struct SessionHandle *data = easy;
if(!easy)
return CURLE_BAD_FUNCTION_ARGUMENT;
- multi = curl_multi_init();
- if(!multi)
- return CURLE_OUT_OF_MEMORY;
+ if(data->multi) {
+ failf(data, "easy handled already used in multi handle");
+ return CURLE_FAILED_INIT;
+ }
+
+ if(data->multi_easy)
+ multi = data->multi_easy;
+ else {
+ multi = curl_multi_init();
+ if(!multi)
+ return CURLE_OUT_OF_MEMORY;
+ data->multi_easy = multi;
+ }
mcode = curl_multi_add_handle(multi, easy);
if(mcode) {
@@ -429,108 +435,33 @@ CURLcode curl_easy_perform(CURL *easy)
return CURLE_FAILED_INIT;
}
- /* we start some action by calling perform right away */
-
- do {
- while(CURLM_CALL_MULTI_PERFORM ==
- curl_multi_perform(multi, &still_running));
-
- if(!still_running)
- break;
-
- FD_ZERO(&fdread);
- FD_ZERO(&fdwrite);
- FD_ZERO(&fdexcep);
-
- /* timeout once per second */
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
-
- /* Old deprecated style: get file descriptors from the transfers */
- curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd);
- rc = Curl_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
-
- /* The way is to extract the sockets and wait for them without using
- select. This whole alternative version should probably rather use the
- curl_multi_socket() approach. */
-
- if(rc == -1)
- /* select error */
- break;
-
- /* timeout or data to send/receive => loop! */
- } while(still_running);
-
- msg = curl_multi_info_read(multi, &rc);
- if(msg)
- code = msg->data.result;
-
- mcode = curl_multi_remove_handle(multi, easy);
- /* what to do if it fails? */
-
- mcode = curl_multi_cleanup(multi);
- /* what to do if it fails? */
-
- return code;
-}
-#else
-/*
- * curl_easy_perform() is the external interface that performs a transfer
- * previously setup.
- */
-CURLcode curl_easy_perform(CURL *curl)
-{
- struct SessionHandle *data = (struct SessionHandle *)curl;
-
- if(!data)
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ /* assign this after curl_multi_add_handle() since that function checks for
+ it and rejects this handle otherwise */
+ data->multi = multi;
- if(! (data->share && data->share->hostcache)) {
- /* this handle is not using a shared dns cache */
+ while(!done && !mcode) {
+ int still_running;
- if(data->set.global_dns_cache &&
- (data->dns.hostcachetype != HCACHE_GLOBAL)) {
- /* global dns cache was requested but still isn't */
- struct curl_hash *ptr;
+ mcode = curl_multi_wait(multi, NULL, 0, 1000, NULL);
- if(data->dns.hostcachetype == HCACHE_PRIVATE) {
- /* if the current cache is private, kill it first */
- Curl_hash_destroy(data->dns.hostcache);
- data->dns.hostcachetype = HCACHE_NONE;
- data->dns.hostcache = NULL;
- }
+ if(mcode == CURLM_OK)
+ mcode = curl_multi_perform(multi, &still_running);
- ptr = Curl_global_host_cache_init();
- if(ptr) {
- /* only do this if the global cache init works */
- data->dns.hostcache = ptr;
- data->dns.hostcachetype = HCACHE_GLOBAL;
+ /* only read 'still_running' if curl_multi_perform() return OK */
+ if((mcode == CURLM_OK) && !still_running) {
+ msg = curl_multi_info_read(multi, &rc);
+ if(msg) {
+ code = msg->data.result;
+ done = TRUE;
}
}
-
- if(!data->dns.hostcache) {
- data->dns.hostcachetype = HCACHE_PRIVATE;
- data->dns.hostcache = Curl_mk_dnscache();
-
- if(!data->dns.hostcache)
- /* While we possibly could survive and do good without a host cache,
- the fact that creating it failed indicates that things are truly
- screwed up and we should bail out! */
- return CURLE_OUT_OF_MEMORY;
- }
-
}
- if(!data->state.conn_cache) {
- /* Oops, no connection cache, create one */
- data->state.conn_cache = Curl_conncache_init(CONNCACHE_PRIVATE);
- if(!data->state.conn_cache)
- return CURLE_OUT_OF_MEMORY;
- }
+ mcode = curl_multi_remove_handle(multi, easy);
- return Curl_perform(data);
+ /* The multi handle is kept alive, owned by the easy handle */
+ return code;
}
-#endif
/*
* curl_easy_cleanup() is the external interface to cleaning/freeing the given
@@ -553,10 +484,6 @@ void Curl_easy_addmulti(struct SessionHandle *data,
void *multi)
{
data->multi = multi;
- if(multi == NULL)
- /* the association is cleared, mark the easy handle as not used by an
- interface */
- data->state.used_interface = Curl_if_none;
}
void Curl_easy_initHandleData(struct SessionHandle *data)
diff --git a/lib/ftp.c b/lib/ftp.c
index 352f12f44..469b88749 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -101,8 +101,17 @@
#endif
/* Local API functions */
-static void state(struct connectdata *conn,
- ftpstate newstate);
+#ifndef DEBUGBUILD
+static void _state(struct connectdata *conn,
+ ftpstate newstate);
+#define state(x,y) _state(x,y)
+#else
+static void _state(struct connectdata *conn,
+ ftpstate newstate,
+ int lineno);
+#define state(x,y) _state(x,y,__LINE__)
+#endif
+
static CURLcode ftp_sendquote(struct connectdata *conn,
struct curl_slist *quote);
static CURLcode ftp_quit(struct connectdata *conn);
@@ -149,6 +158,8 @@ static CURLcode ftp_readresp(curl_socket_t sockfd,
struct pingpong *pp,
int *ftpcode,
size_t *size);
+static CURLcode ftp_dophase_done(struct connectdata *conn,
+ bool connected);
/* easy-to-use macro: */
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
@@ -411,7 +422,7 @@ static long ftp_timeleft_accept(struct SessionHandle *data)
* connection for a negative response regarding a failure in connecting
*
*/
-static CURLcode ReceivedServerConnect(struct connectdata* conn, bool* received)
+static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received)
{
struct SessionHandle *data = conn->data;
curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
@@ -514,8 +525,8 @@ static CURLcode InitiateTransfer(struct connectdata *conn)
else {
/* FTP download: */
Curl_setup_transfer(conn, SECONDARYSOCKET,
- conn->proto.ftpc.retr_size_saved, FALSE,
- ftp->bytecountp, -1, NULL); /* no upload here */
+ conn->proto.ftpc.retr_size_saved, FALSE,
+ ftp->bytecountp, -1, NULL); /* no upload here */
}
conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
@@ -528,18 +539,15 @@ static CURLcode InitiateTransfer(struct connectdata *conn)
*
* AllowServerConnect()
*
- * When we've issue the PORT command, we have told the server to connect
- * to us. This function
- * - will sit and wait here until the server has connected for easy interface
- * - will check whether data connection is established if so it is accepted
- * for multi interface
+ * When we've issue the PORT command, we have told the server to connect to
+ * us. This function checks whether data connection is established if so it is
+ * accepted.
*
*/
static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
{
struct SessionHandle *data = conn->data;
long timeout_ms;
- long interval_ms;
CURLcode ret = CURLE_OK;
*connected = FALSE;
@@ -548,50 +556,34 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
/* Save the time we start accepting server connect */
Curl_pgrsTime(data, TIMER_STARTACCEPT);
- for(;;) {
- timeout_ms = ftp_timeleft_accept(data);
- if(timeout_ms < 0) {
- /* if a timeout was already reached, bail out */
- failf(data, "Accept timeout occurred while waiting server connect");
- return CURLE_FTP_ACCEPT_TIMEOUT;
- }
+ timeout_ms = ftp_timeleft_accept(data);
+ if(timeout_ms < 0) {
+ /* if a timeout was already reached, bail out */
+ failf(data, "Accept timeout occurred while waiting server connect");
+ return CURLE_FTP_ACCEPT_TIMEOUT;
+ }
+
+ /* see if the connection request is already here */
+ ret = ReceivedServerConnect(conn, connected);
+ if(ret)
+ return ret;
- /* see if the connection request is already here */
- ret = ReceivedServerConnect(conn, connected);
+ if(*connected) {
+ ret = AcceptServerConnect(conn);
if(ret)
return ret;
- if(*connected) {
- ret = AcceptServerConnect(conn);
- if(ret)
- return ret;
-
- ret = InitiateTransfer(conn);
- if(ret)
- return ret;
-
- break; /* connection is accepted, break the loop */
- }
- else {
- if(data->state.used_interface == Curl_if_easy) {
- interval_ms = 1000;
- if(timeout_ms < interval_ms)
- interval_ms = timeout_ms;
-
- /* sleep for 1 second and then continue */
- Curl_socket_ready(CURL_SOCKET_BAD, CURL_SOCKET_BAD, interval_ms);
- }
- else {
- /* Add timeout to multi handle and break out of the loop */
- if(ret == CURLE_OK && *connected == FALSE) {
- if(data->set.accepttimeout > 0)
- Curl_expire(data, data->set.accepttimeout);
- else
- Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
- }
-
- break; /* connection was not accepted immediately */
- }
+ ret = InitiateTransfer(conn);
+ if(ret)
+ return ret;
+ }
+ else {
+ /* Add timeout to multi handle and break out of the loop */
+ if(ret == CURLE_OK && *connected == FALSE) {
+ if(data->set.accepttimeout > 0)
+ Curl_expire(data, data->set.accepttimeout);
+ else
+ Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
}
}
@@ -787,8 +779,12 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
}
/* This is the ONLY way to change FTP state! */
-static void state(struct connectdata *conn,
- ftpstate newstate)
+static void _state(struct connectdata *conn,
+ ftpstate newstate
+#ifdef DEBUGBUILD
+ , int lineno
+#endif
+ )
{
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
@@ -833,8 +829,8 @@ static void state(struct connectdata *conn,
struct ftp_conn *ftpc = &conn->proto.ftpc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
if(ftpc->state != newstate)
- infof(conn->data, "FTP %p state change from %s to %s\n",
- ftpc, names[ftpc->state], names[newstate]);
+ infof(conn->data, "FTP %p (line %d) state change from %s to %s\n",
+ ftpc, lineno, names[ftpc->state], names[newstate]);
#endif
ftpc->state = newstate;
}
@@ -2071,10 +2067,19 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
if(result)
return result;
+
+ if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) {
+ /* the CONNECT procedure is not complete, the tunnel is not yet up */
+ state(conn, FTP_STOP); /* this phase is completed */
+ conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
+
+ return result;
+ }
}
conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
+ conn->bits.do_more = TRUE;
state(conn, FTP_STOP); /* this phase is completed */
return result;
@@ -2108,6 +2113,7 @@ static CURLcode ftp_state_port_resp(struct connectdata *conn,
else {
infof(data, "Connect data stream actively\n");
state(conn, FTP_STOP); /* end of DO phase */
+ result = ftp_dophase_done(conn, FALSE);
}
return result;
@@ -3206,7 +3212,6 @@ static CURLcode ftp_connect(struct connectdata *conn,
{
CURLcode result;
struct ftp_conn *ftpc = &conn->proto.ftpc;
- struct SessionHandle *data=conn->data;
struct pingpong *pp = &ftpc->pp;
*done = FALSE; /* default to not done yet */
@@ -3240,13 +3245,7 @@ static CURLcode ftp_connect(struct connectdata *conn,
response */
state(conn, FTP_WAIT220);
- if(data->state.used_interface == Curl_if_multi)
- result = ftp_multi_statemach(conn, done);
- else {
- result = ftp_easy_statemach(conn);
- if(!result)
- *done = TRUE;
- }
+ result = ftp_multi_statemach(conn, done);
return result;
}
@@ -3681,6 +3680,14 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
/* if the second connection isn't done yet, wait for it */
if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
+ if(conn->tunnel_state[SECONDARYSOCKET] == TUNNEL_CONNECT) {
+ /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
+ aren't used so we blank their arguments. TODO: make this nicer */
+ result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0);
+
+ return result;
+ }
+
result = Curl_is_connected(conn, SECONDARYSOCKET, &connected);
/* Ready to do more? */
@@ -3691,9 +3698,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
return result;
}
- if((data->state.used_interface == Curl_if_multi) &&
- ftpc->state) {
- /* multi interface and already in a state so skip the intial commands.
+ if(ftpc->state) {
+ /* already in a state so skip the intial commands.
They are only done to kickstart the do_more state */
result = ftp_multi_statemach(conn, complete);
@@ -3701,6 +3707,12 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
immediately */
if(result || (ftpc->wait_data_conn != TRUE))
return result;
+
+ if(ftpc->wait_data_conn)
+ /* if we reach the end of the FTP state machine here, *complete will be
+ TRUE but so is ftpc->wait_data_conn, which says we need to wait for
+ the data connection and therefore we're not actually complete */
+ *complete = FALSE;
}
if(ftp->transfer <= FTPTRANSFER_INFO) {
@@ -3729,6 +3741,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
if(result)
return result;
+
+ result = ftp_multi_statemach(conn, complete);
}
else {
/* download */
@@ -3755,14 +3769,10 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
if(result)
return result;
}
- }
- if(data->state.used_interface == Curl_if_multi) {
- result = ftp_multi_statemach(conn, complete);
- return result;
+ result = ftp_multi_statemach(conn, complete);
}
- else
- result = ftp_easy_statemach(conn);
+ return result;
}
if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
@@ -3805,7 +3815,6 @@ CURLcode ftp_perform(struct connectdata *conn,
ftp->transfer = FTPTRANSFER_INFO;
}
-
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
@@ -3814,16 +3823,12 @@ CURLcode ftp_perform(struct connectdata *conn,
return result;
/* run the state-machine */
- if(conn->data->state.used_interface == Curl_if_multi)
- result = ftp_multi_statemach(conn, dophase_done);
- else {
- result = ftp_easy_statemach(conn);
- *dophase_done = TRUE; /* with the easy interface we are done here */
- }
+ result = ftp_multi_statemach(conn, dophase_done);
+
*connected = conn->bits.tcpconnect[FIRSTSOCKET];
if(*dophase_done)
- DEBUGF(infof(conn->data, "DO phase is complete\n"));
+ DEBUGF(infof(conn->data, "DO phase is complete1\n"));
return result;
}
@@ -4476,7 +4481,7 @@ static CURLcode ftp_doing(struct connectdata *conn,
else if(*dophase_done) {
result = ftp_dophase_done(conn, FALSE /* not connected */);
- DEBUGF(infof(conn->data, "DO phase is complete\n"));
+ DEBUGF(infof(conn->data, "DO phase is complete2\n"));
}
return result;
}
@@ -4521,6 +4526,7 @@ CURLcode ftp_regular_transfer(struct connectdata *conn,
return CURLE_OK;
result = ftp_dophase_done(conn, connected);
+
if(result)
return result;
}
diff --git a/lib/http.c b/lib/http.c
index 21f7c21e6..daaafe317 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -1295,22 +1295,16 @@ Curl_compareheader(const char *headerline, /* line to check */
*/
CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
{
- struct SessionHandle *data;
CURLcode result;
- data=conn->data;
-
/* We default to persistent connections. We set this already in this connect
function to make the re-use checks properly be able to check this bit. */
conn->bits.close = FALSE;
- if(data->state.used_interface == Curl_if_multi) {
- /* when the multi interface is used, the CONNECT procedure might not have
- been completed */
- result = Curl_proxy_connect(conn);
- if(result)
- return result;
- }
+ /* the CONNECT procedure might not have been completed */
+ result = Curl_proxy_connect(conn);
+ if(result)
+ return result;
if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
/* nothing else to do except wait right now - we're not done here. */
@@ -1318,22 +1312,12 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
if(conn->given->flags & PROTOPT_SSL) {
/* perform SSL initialization */
- if(data->state.used_interface == Curl_if_multi) {
- result = https_connecting(conn, done);
- if(result)
- return result;
- }
- else {
- /* BLOCKING */
- result = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(result)
- return result;
- *done = TRUE;
- }
+ result = https_connecting(conn, done);
+ if(result)
+ return result;
}
- else {
+ else
*done = TRUE;
- }
return CURLE_OK;
}
diff --git a/lib/http_proxy.c b/lib/http_proxy.c
index ada89576e..ed0fe9503 100644
--- a/lib/http_proxy.c
+++ b/lib/http_proxy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -87,13 +87,6 @@ CURLcode Curl_proxy_connect(struct connectdata *conn)
* Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
* function will issue the necessary commands to get a seamless tunnel through
* this proxy. After that, the socket can be used just as a normal socket.
- *
- * This badly needs to be rewritten. CONNECT should be sent and dealt with
- * like any ordinary HTTP request, and not specially crafted like this. This
- * function only remains here like this for now since the rewrite is a bit too
- * much work to do at the moment.
- *
- * This function is BLOCKING which is nasty for all multi interface using apps.
*/
CURLcode Curl_proxyCONNECT(struct connectdata *conn,
@@ -244,19 +237,13 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
return CURLE_RECV_ERROR;
}
- /* if we're in multi-mode and we would block, return instead for a retry */
- if(Curl_if_multi == data->state.used_interface) {
- if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
- /* return so we'll be called again polling-style */
- return CURLE_OK;
- else {
- DEBUGF(infof(data,
- "Multi mode finished polling for response from "
- "proxy CONNECT\n"));
- }
- }
+ if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
+ /* return so we'll be called again polling-style */
+ return CURLE_OK;
else {
- DEBUGF(infof(data, "Easy mode waiting response from proxy CONNECT\n"));
+ DEBUGF(infof(data,
+ "Multi mode finished polling for response from "
+ "proxy CONNECT\n"));
}
/* at this point, either:
@@ -572,6 +559,12 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(closeConnection && data->req.newurl)
conn->bits.proxy_connect_closed = TRUE;
+ if(data->req.newurl) {
+ /* this won't be used anymore for the CONNECT so free it now */
+ free(data->req.newurl);
+ data->req.newurl = NULL;
+ }
+
/* to back to init state */
conn->tunnel_state[sockindex] = TUNNEL_INIT;
diff --git a/lib/imap.c b/lib/imap.c
index a53f2e9d2..c7198d0b1 100644
--- a/lib/imap.c
+++ b/lib/imap.c
@@ -633,19 +633,9 @@ static CURLcode imap_state_starttls_resp(struct connectdata *conn,
result = imap_state_capability(conn);
}
else {
- if(data->state.used_interface == Curl_if_multi) {
- state(conn, IMAP_UPGRADETLS);
- result = imap_state_upgrade_tls(conn);
- }
- else {
- result = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(CURLE_OK == result) {
- imap_to_imaps(conn);
- result = imap_state_capability(conn);
- }
- }
+ state(conn, IMAP_UPGRADETLS);
+ return imap_state_upgrade_tls(conn);
}
-
return result;
}
@@ -1358,7 +1348,6 @@ static CURLcode imap_connect(struct connectdata *conn,
{
CURLcode result;
struct imap_conn *imapc = &conn->proto.imapc;
- struct SessionHandle *data=conn->data;
struct pingpong *pp = &imapc->pp;
*done = FALSE; /* default to not done yet */
@@ -1379,17 +1368,7 @@ static CURLcode imap_connect(struct connectdata *conn,
pp->endofresp = imap_endofresp;
pp->conn = conn;
- if((conn->handler->flags & PROTOPT_SSL) &&
- data->state.used_interface != Curl_if_multi) {
- /* IMAPS is simply imap with SSL for the control channel */
- /* so perform the SSL initialization for this socket */
- result = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(result)
- return result;
- }
-
- /* Initialise the response reader stuff */
- Curl_pp_init(pp);
+ Curl_pp_init(pp); /* init generic pingpong data */
/* Start off waiting for the server greeting response */
state(conn, IMAP_SERVERGREET);
@@ -1397,13 +1376,7 @@ static CURLcode imap_connect(struct connectdata *conn,
/* Start off with an id of '*' */
imapc->idstr = "*";
- if(data->state.used_interface == Curl_if_multi)
- result = imap_multi_statemach(conn, done);
- else {
- result = imap_easy_statemach(conn);
- if(!result)
- *done = TRUE;
- }
+ result = imap_multi_statemach(conn, done);
return result;
}
@@ -1473,13 +1446,9 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected,
if(result)
return result;
- /* Run the state-machine */
- if(conn->data->state.used_interface == Curl_if_multi)
- result = imap_multi_statemach(conn, dophase_done);
- else {
- result = imap_easy_statemach(conn);
- *dophase_done = TRUE; /* with the easy interface we are done here */
- }
+ /* run the state-machine */
+ result = imap_multi_statemach(conn, dophase_done);
+
*connected = conn->bits.tcpconnect[FIRSTSOCKET];
if(*dophase_done)
diff --git a/lib/multi.c b/lib/multi.c
index de1fc7d6e..fa0afb9f8 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -39,6 +39,7 @@
#include "speedcheck.h"
#include "conncache.h"
#include "bundles.h"
+#include "multihandle.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -56,62 +57,6 @@
#define CURL_SOCKET_HASH_TABLE_SIZE 911
#endif
-struct Curl_message {
- /* the 'CURLMsg' is the part that is visible to the external user */
- struct CURLMsg extmsg;
-};
-
-/* NOTE: if you add a state here, add the name to the statename[] array as
- well!
-*/
-typedef enum {
- CURLM_STATE_INIT, /* 0 - start in this state */
- CURLM_STATE_CONNECT, /* 1 - resolve/connect has been sent off */
- CURLM_STATE_WAITRESOLVE, /* 2 - awaiting the resolve to finalize */
- CURLM_STATE_WAITCONNECT, /* 3 - awaiting the connect to finalize */
- CURLM_STATE_WAITPROXYCONNECT, /* 4 - awaiting proxy CONNECT to finalize */
- CURLM_STATE_PROTOCONNECT, /* 5 - completing the protocol-specific connect
- phase */
- CURLM_STATE_WAITDO, /* 6 - wait for our turn to send the request */
- CURLM_STATE_DO, /* 7 - start send off the request (part 1) */
- CURLM_STATE_DOING, /* 8 - sending off the request (part 1) */
- CURLM_STATE_DO_MORE, /* 9 - send off the request (part 2) */
- CURLM_STATE_DO_DONE, /* 10 - done sending off request */
- CURLM_STATE_WAITPERFORM, /* 11 - wait for our turn to read the response */
- CURLM_STATE_PERFORM, /* 12 - transfer data */
- CURLM_STATE_TOOFAST, /* 13 - wait because limit-rate exceeded */
- CURLM_STATE_DONE, /* 14 - post data transfer operation */
- CURLM_STATE_COMPLETED, /* 15 - operation complete */
- CURLM_STATE_MSGSENT, /* 16 - the operation complete message is sent */
- CURLM_STATE_LAST /* 17 - not a true state, never use this */
-} CURLMstate;
-
-/* we support N sockets per easy handle. Set the corresponding bit to what
- action we should wait for */
-#define MAX_SOCKSPEREASYHANDLE 5
-#define GETSOCK_READABLE (0x00ff)
-#define GETSOCK_WRITABLE (0xff00)
-
-struct Curl_one_easy {
- /* first, two fields for the linked list of these */
- struct Curl_one_easy *next;
- struct Curl_one_easy *prev;
-
- struct SessionHandle *easy_handle; /* the easy handle for this unit */
- struct connectdata *easy_conn; /* the "unit's" connection */
-
- CURLMstate state; /* the handle's state */
- CURLcode result; /* previous result */
-
- struct Curl_message msg; /* A single posted message. */
-
- /* Array with the plain socket numbers this handle takes care of, in no
- particular order. Note that all sockets are added to the sockhash, where
- the state etc are also kept. This array is mostly used to detect when a
- socket is to be removed from the hash. See singlesocket(). */
- curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
- int numsocks;
-};
#define CURL_MULTI_HANDLE 0x000bab1e
@@ -120,57 +65,6 @@ struct Curl_one_easy {
#define GOOD_EASY_HANDLE(x) \
((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER))
-/* This is the struct known as CURLM on the outside */
-struct Curl_multi {
- /* First a simple identifier to easier detect if a user mix up
- this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
- long type;
-
- /* We have a doubly-linked circular list with easy handles */
- struct Curl_one_easy easy;
-
- int num_easy; /* amount of entries in the linked list above. */
- int num_alive; /* amount of easy handles that are added but have not yet
- reached COMPLETE state */
-
- struct curl_llist *msglist; /* a list of messages from completed transfers */
-
- /* callback function and user data pointer for the *socket() API */
- curl_socket_callback socket_cb;
- void *socket_userp;
-
- /* Hostname cache */
- struct curl_hash *hostcache;
-
- /* timetree points to the splay-tree of time nodes to figure out expire
- times of all currently set timers */
- struct Curl_tree *timetree;
-
- /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
- the pluralis form, there can be more than one easy handle waiting on the
- same actual socket) */
- struct curl_hash *sockhash;
-
- /* Whether pipelining is enabled for this multi handle */
- bool pipelining_enabled;
-
- /* Shared connection cache (bundles)*/
- struct conncache *conn_cache;
-
- /* This handle will be used for closing the cached connections in
- curl_multi_cleanup() */
- struct SessionHandle *closure_handle;
-
- long maxconnects; /* if >0, a fixed limit of the maximum number of entries
- we're allowed to grow the connection cache to */
-
- /* timer callback and user data pointer for the *socket() API */
- curl_multi_timer_callback timer_cb;
- void *timer_userp;
- struct timeval timer_lastcall; /* the fixed time for the timeout for the
- previous callback */
-};
-
static void singlesocket(struct Curl_multi *multi,
struct Curl_one_easy *easy);
static int update_timer(struct Curl_multi *multi);
@@ -213,7 +107,11 @@ 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 multistate(struct Curl_one_easy *easy, CURLMstate state)
+static void mstate(struct Curl_one_easy *easy, CURLMstate state
+#ifdef DEBUGBUILD
+ , int lineno
+#endif
+)
{
#ifdef DEBUGBUILD
long connection_id = -5000;
@@ -233,9 +131,9 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
connection_id = easy->easy_conn->connection_id;
infof(easy->easy_handle,
- "STATE: %s => %s handle %p; (connection #%ld) \n",
+ "STATE: %s => %s handle %p; line %d (connection #%ld) \n",
statename[oldstate], statename[easy->state],
- (char *)easy, connection_id);
+ (char *)easy, lineno, connection_id);
}
#endif
if(state == CURLM_STATE_COMPLETED)
@@ -243,6 +141,12 @@ static void multistate(struct Curl_one_easy *easy, CURLMstate state)
easy->easy_handle->multi->num_alive--;
}
+#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 a particular socket
*/
@@ -396,7 +300,7 @@ CURLM *curl_multi_init(void)
if(!multi->sockhash)
goto error;
- multi->conn_cache = Curl_conncache_init(CONNCACHE_MULTI);
+ multi->conn_cache = Curl_conncache_init();
if(!multi->conn_cache)
goto error;
@@ -516,29 +420,14 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle,
easy->easy_handle->multi_pos = easy;
/* for multi interface connections, we share DNS cache automatically if the
- easy handle's one is currently private. */
- if(easy->easy_handle->dns.hostcache &&
- (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) {
- Curl_hash_destroy(easy->easy_handle->dns.hostcache);
- easy->easy_handle->dns.hostcache = NULL;
- easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
- }
-
+ 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;
}
- /* On a multi stack the connection cache, owned by the multi handle,
- is shared between all easy handles within the multi handle.
- Therefore we free the private connection cache if there is one */
- if(easy->easy_handle->state.conn_cache &&
- easy->easy_handle->state.conn_cache->type == CONNCACHE_PRIVATE) {
- Curl_conncache_destroy(easy->easy_handle->state.conn_cache);
- }
-
- /* Point now to this multi's connection cache */
+ /* Point to the multi's connection cache */
easy->easy_handle->state.conn_cache = multi->conn_cache;
/* This adds the new entry at the 'end' of the doubly-linked circular
@@ -666,18 +555,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
}
if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
- if(multi->num_easy == 1) {
- if(easy_owns_conn) {
- Curl_resolver_cancel(easy->easy_conn);
- if(easy->easy_conn->dns_entry) {
- Curl_resolv_unlock(easy->easy_handle, easy->easy_conn->dns_entry);
- easy->easy_conn->dns_entry = NULL;
- }
- }
- Curl_hostcache_destroy(easy->easy_handle);
- multi->hostcache = NULL;
- }
- /* clear out the usage of the shared DNS cache */
+ /* stop using the multi handle's DNS cache */
easy->easy_handle->dns.hostcache = NULL;
easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
}
@@ -700,12 +578,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn);
}
- if(easy->easy_handle->state.conn_cache->type == CONNCACHE_MULTI) {
- /* if this was using the shared connection cache we clear the pointer
- to that since we're not part of that handle anymore */
- easy->easy_handle->state.conn_cache = NULL;
- easy->easy_handle->state.lastconnect = NULL;
- }
+ /* 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;
/* change state without using multistate(), only to make singlesocket() do
what we want */
@@ -1025,7 +900,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
bool connected;
bool async;
bool protocol_connect = FALSE;
- bool dophase_done;
+ bool dophase_done = FALSE;
bool done = FALSE;
CURLMcode result = CURLM_OK;
struct SingleRequest *k;
@@ -1120,8 +995,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* after init, go CONNECT */
multistate(easy, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM;
-
- data->state.used_interface = Curl_if_multi;
}
break;
@@ -1577,9 +1450,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(!ret)
retry = (newurl)?TRUE:FALSE;
- if(retry)
- /* if we are to retry, set the result to OK */
+ if(retry) {
+ /* if we are to retry, set the result to OK and consider the
+ request as done */
easy->result = CURLE_OK;
+ done = TRUE;
+ }
}
if(easy->result) {
@@ -1897,6 +1773,9 @@ 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);
+
Curl_close(multi->closure_handle);
multi->closure_handle = NULL;
diff --git a/lib/multihandle.h b/lib/multihandle.h
new file mode 100644
index 000000000..941835941
--- /dev/null
+++ b/lib/multihandle.h
@@ -0,0 +1,134 @@
+#ifndef HEADER_CURL_MULTIHANDLE_H
+#define HEADER_CURL_MULTIHANDLE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * 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 http://curl.haxx.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.
+ *
+ ***************************************************************************/
+
+struct Curl_message {
+ /* the 'CURLMsg' is the part that is visible to the external user */
+ struct CURLMsg extmsg;
+};
+
+/* NOTE: if you add a state here, add the name to the statename[] array as
+ well!
+*/
+typedef enum {
+ CURLM_STATE_INIT, /* 0 - start in this state */
+ CURLM_STATE_CONNECT, /* 1 - resolve/connect has been sent off */
+ CURLM_STATE_WAITRESOLVE, /* 2 - awaiting the resolve to finalize */
+ CURLM_STATE_WAITCONNECT, /* 3 - awaiting the connect to finalize */
+ CURLM_STATE_WAITPROXYCONNECT, /* 4 - awaiting proxy CONNECT to finalize */
+ CURLM_STATE_PROTOCONNECT, /* 5 - completing the protocol-specific connect
+ phase */
+ CURLM_STATE_WAITDO, /* 6 - wait for our turn to send the request */
+ CURLM_STATE_DO, /* 7 - start send off the request (part 1) */
+ CURLM_STATE_DOING, /* 8 - sending off the request (part 1) */
+ CURLM_STATE_DO_MORE, /* 9 - send off the request (part 2) */
+ CURLM_STATE_DO_DONE, /* 10 - done sending off request */
+ CURLM_STATE_WAITPERFORM, /* 11 - wait for our turn to read the response */
+ CURLM_STATE_PERFORM, /* 12 - transfer data */
+ CURLM_STATE_TOOFAST, /* 13 - wait because limit-rate exceeded */
+ CURLM_STATE_DONE, /* 14 - post data transfer operation */
+ CURLM_STATE_COMPLETED, /* 15 - operation complete */
+ CURLM_STATE_MSGSENT, /* 16 - the operation complete message is sent */
+ CURLM_STATE_LAST /* 17 - not a true state, never use this */
+} CURLMstate;
+
+/* we support N sockets per easy handle. Set the corresponding bit to what
+ action we should wait for */
+#define MAX_SOCKSPEREASYHANDLE 5
+#define GETSOCK_READABLE (0x00ff)
+#define GETSOCK_WRITABLE (0xff00)
+
+struct Curl_one_easy {
+ /* first, two fields for the linked list of these */
+ struct Curl_one_easy *next;
+ struct Curl_one_easy *prev;
+
+ struct SessionHandle *easy_handle; /* the easy handle for this unit */
+ struct connectdata *easy_conn; /* the "unit's" connection */
+
+ CURLMstate state; /* the handle's state */
+ CURLcode result; /* previous result */
+
+ struct Curl_message msg; /* A single posted message. */
+
+ /* Array with the plain socket numbers this handle takes care of, in no
+ particular order. Note that all sockets are added to the sockhash, where
+ the state etc are also kept. This array is mostly used to detect when a
+ socket is to be removed from the hash. See singlesocket(). */
+ curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
+ int numsocks;
+};
+
+/* This is the struct known as CURLM on the outside */
+struct Curl_multi {
+ /* First a simple identifier to easier detect if a user mix up
+ this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
+ long type;
+
+ /* We have a doubly-linked circular list with easy handles */
+ struct Curl_one_easy easy;
+
+ int num_easy; /* amount of entries in the linked list above. */
+ int num_alive; /* amount of easy handles that are added but have not yet
+ reached COMPLETE state */
+
+ struct curl_llist *msglist; /* a list of messages from completed transfers */
+
+ /* callback function and user data pointer for the *socket() API */
+ curl_socket_callback socket_cb;
+ void *socket_userp;
+
+ /* Hostname cache */
+ struct curl_hash *hostcache;
+
+ /* timetree points to the splay-tree of time nodes to figure out expire
+ times of all currently set timers */
+ struct Curl_tree *timetree;
+
+ /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
+ the pluralis form, there can be more than one easy handle waiting on the
+ same actual socket) */
+ struct curl_hash *sockhash;
+
+ /* Whether pipelining is enabled for this multi handle */
+ bool pipelining_enabled;
+
+ /* Shared connection cache (bundles)*/
+ struct conncache *conn_cache;
+
+ /* This handle will be used for closing the cached connections in
+ curl_multi_cleanup() */
+ struct SessionHandle *closure_handle;
+
+ long maxconnects; /* if >0, a fixed limit of the maximum number of entries
+ we're allowed to grow the connection cache to */
+
+ /* timer callback and user data pointer for the *socket() API */
+ curl_multi_timer_callback timer_cb;
+ void *timer_userp;
+ struct timeval timer_lastcall; /* the fixed time for the timeout for the
+ previous callback */
+};
+
+#endif /* HEADER_CURL_MULTIHANDLE_H */
+
diff --git a/lib/openldap.c b/lib/openldap.c
index 4c395fadc..cb3b42020 100644
--- a/lib/openldap.c
+++ b/lib/openldap.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
- * Copyright (C) 2011 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2011 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -192,6 +192,7 @@ static CURLcode ldap_connect(struct connectdata *conn, bool *done)
struct SessionHandle *data=conn->data;
int rc, proto = LDAP_VERSION3;
char hosturl[1024], *ptr;
+ (void)done;
strcpy(hosturl, "ldap");
ptr = hosturl+4;
@@ -212,23 +213,12 @@ static CURLcode ldap_connect(struct connectdata *conn, bool *done)
#ifdef USE_SSL
if(conn->handler->flags & PROTOPT_SSL) {
CURLcode res;
- if(data->state.used_interface == Curl_if_easy) {
- res = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(res)
- return res;
- li->ssldone = TRUE;
- }
- else {
- res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
- if(res)
- return res;
- }
+ res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
+ if(res)
+ return res;
}
#endif
- if(data->state.used_interface == Curl_if_easy)
- return ldap_connecting(conn, done);
-
return CURLE_OK;
}
@@ -262,10 +252,7 @@ static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
}
#endif
- if(data->state.used_interface == Curl_if_easy)
- tvp = NULL; /* let ldap_result block indefinitely */
- else
- tvp = &tv;
+ tvp = &tv;
retry:
if(!li->didbind) {
diff --git a/lib/pop3.c b/lib/pop3.c
index e8fd9c52c..b5ea7c23f 100644
--- a/lib/pop3.c
+++ b/lib/pop3.c
@@ -570,17 +570,8 @@ static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
result = pop3_state_capa(conn);
}
else {
- if(data->state.used_interface == Curl_if_multi) {
- state(conn, POP3_UPGRADETLS);
- result = pop3_state_upgrade_tls(conn);
- }
- else {
- result = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(CURLE_OK == result) {
- pop3_to_pop3s(conn);
- result = pop3_state_capa(conn);
- }
- }
+ state(conn, POP3_UPGRADETLS);
+ result = pop3_state_upgrade_tls(conn);
}
return result;
@@ -1301,7 +1292,6 @@ static CURLcode pop3_connect(struct connectdata *conn, bool *done)
{
CURLcode result;
struct pop3_conn *pop3c = &conn->proto.pop3c;
- struct SessionHandle *data = conn->data;
struct pingpong *pp = &pop3c->pp;
*done = FALSE; /* default to not done yet */
@@ -1336,13 +1326,7 @@ static CURLcode pop3_connect(struct connectdata *conn, bool *done)
/* Start off waiting for the server greeting response */
state(conn, POP3_SERVERGREET);
- if(data->state.used_interface == Curl_if_multi)
- result = pop3_multi_statemach(conn, done);
- else {
- result = pop3_easy_statemach(conn);
- if(!result)
- *done = TRUE;
- }
+ result = pop3_multi_statemach(conn, done);
return result;
}
@@ -1418,12 +1402,8 @@ static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
return result;
/* Run the state-machine */
- if(conn->data->state.used_interface == Curl_if_multi)
- result = pop3_multi_statemach(conn, dophase_done);
- else {
- result = pop3_easy_statemach(conn);
- *dophase_done = TRUE; /* with the easy interface we are done here */
- }
+ result = pop3_multi_statemach(conn, dophase_done);
+
*connected = conn->bits.tcpconnect[FIRSTSOCKET];
if(*dophase_done)
diff --git a/lib/smtp.c b/lib/smtp.c
index 2ad2ac691..8de9af9c3 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -493,17 +493,8 @@ static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
result = smtp_authenticate(conn);
}
else {
- if(data->state.used_interface == Curl_if_multi) {
- state(conn, SMTP_UPGRADETLS);
- result = smtp_state_upgrade_tls(conn);
- }
- else {
- result = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(CURLE_OK == result) {
- smtp_to_smtps(conn);
- result = smtp_state_ehlo(conn);
- }
- }
+ state(conn, SMTP_UPGRADETLS);
+ return smtp_state_upgrade_tls(conn);
}
return result;
@@ -1300,7 +1291,6 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done)
{
CURLcode result;
struct smtp_conn *smtpc = &conn->proto.smtpc;
- struct SessionHandle *data = conn->data;
struct pingpong *pp = &smtpc->pp;
const char *path = conn->data->state.path;
char localhost[HOSTNAME_MAX + 1];
@@ -1323,15 +1313,6 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done)
pp->endofresp = smtp_endofresp;
pp->conn = conn;
- if((conn->handler->protocol & CURLPROTO_SMTPS) &&
- data->state.used_interface != Curl_if_multi) {
- /* SMTPS is simply smtp with SSL for the control channel */
- /* so perform the SSL initialization for this socket */
- result = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(result)
- return result;
- }
-
/* Initialise the response reader stuff */
Curl_pp_init(pp);
@@ -1357,13 +1338,7 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done)
/* Start off waiting for the server greeting response */
state(conn, SMTP_SERVERGREET);
- if(data->state.used_interface == Curl_if_multi)
- result = smtp_multi_statemach(conn, done);
- else {
- result = smtp_easy_statemach(conn);
- if(!result)
- *done = TRUE;
- }
+ result = smtp_multi_statemach(conn, done);
return result;
}
@@ -1470,13 +1445,9 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
if(result)
return result;
- /* Run the state-machine */
- if(conn->data->state.used_interface == Curl_if_multi)
- result = smtp_multi_statemach(conn, dophase_done);
- else {
- result = smtp_easy_statemach(conn);
- *dophase_done = TRUE; /* with the easy interface we are done here */
- }
+ /* run the state-machine */
+ result = smtp_multi_statemach(conn, dophase_done);
+
*connected = conn->bits.tcpconnect[FIRSTSOCKET];
if(*dophase_done)
diff --git a/lib/ssh.c b/lib/ssh.c
index 8eb2e9fc9..0c7d36ecc 100644
--- a/lib/ssh.c
+++ b/lib/ssh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -2793,13 +2793,7 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done)
state(conn, SSH_INIT);
- if(data->state.used_interface == Curl_if_multi)
- result = ssh_multi_statemach(conn, done);
- else {
- result = ssh_easy_statemach(conn, TRUE);
- if(!result)
- *done = TRUE;
- }
+ result = ssh_multi_statemach(conn, done);
return result;
}
@@ -2828,13 +2822,8 @@ CURLcode scp_perform(struct connectdata *conn,
state(conn, SSH_SCP_TRANS_INIT);
/* run the state-machine */
- if(conn->data->state.used_interface == Curl_if_multi) {
- result = ssh_multi_statemach(conn, dophase_done);
- }
- else {
- result = ssh_easy_statemach(conn, FALSE);
- *dophase_done = TRUE; /* with the easy interface we are done here */
- }
+ result = ssh_multi_statemach(conn, dophase_done);
+
*connected = conn->bits.tcpconnect[FIRSTSOCKET];
if(*dophase_done) {
@@ -3037,13 +3026,8 @@ CURLcode sftp_perform(struct connectdata *conn,
state(conn, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
- if(conn->data->state.used_interface == Curl_if_multi) {
- result = ssh_multi_statemach(conn, dophase_done);
- }
- else {
- result = ssh_easy_statemach(conn, FALSE);
- *dophase_done = TRUE; /* with the easy interface we are done here */
- }
+ result = ssh_multi_statemach(conn, dophase_done);
+
*connected = conn->bits.tcpconnect[FIRSTSOCKET];
if(*dophase_done) {
diff --git a/lib/ssluse.c b/lib/ssluse.c
index c4c465c2c..4a0dba734 100644
--- a/lib/ssluse.c
+++ b/lib/ssluse.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -1520,16 +1520,6 @@ ossl_connect_step1(struct connectdata *conn,
SSL_CTX_set_options(connssl->ctx, ctx_options);
-#if 0
- /*
- * Not sure it's needed to tell SSL_connect() that socket is
- * non-blocking. It doesn't seem to care, but just return with
- * SSL_ERROR_WANT_x.
- */
- if(data->state.used_interface == Curl_if_multi)
- SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
-#endif
-
if(data->set.str[STRING_CERT] || data->set.str[STRING_CERT_TYPE]) {
if(!cert_stuff(conn,
connssl->ctx,
diff --git a/lib/tftp.c b/lib/tftp.c
index 282460bda..ef740b856 100644
--- a/lib/tftp.c
+++ b/lib/tftp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -1185,129 +1185,6 @@ static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event)
return (long)(state->max_time - current);
}
-
-/**********************************************************
- *
- * tftp_easy_statemach
- *
- * Handle easy request until completion
- *
- **********************************************************/
-static CURLcode tftp_easy_statemach(struct connectdata *conn)
-{
- int rc;
- int check_time = 0;
- CURLcode result = CURLE_OK;
- struct SessionHandle *data = conn->data;
- tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
- curl_socket_t fd_read;
- long timeout_ms;
- struct SingleRequest *k = &data->req;
- struct timeval transaction_start = Curl_tvnow();
-
- k->start = transaction_start;
- k->now = transaction_start;
-
- /* Run the TFTP State Machine */
- for(; (state->state != TFTP_STATE_FIN) && (result == CURLE_OK); ) {
-
- timeout_ms = state->retry_time * 1000;
-
- if(data->set.upload) {
- if(data->set.max_send_speed &&
- (data->progress.ulspeed > data->set.max_send_speed)) {
- fd_read = CURL_SOCKET_BAD;
- timeout_ms = Curl_sleep_time(data->set.max_send_speed,
- data->progress.ulspeed, state->blksize);
- }
- else {
- fd_read = state->sockfd;
- }
- }
- else {
- if(data->set.max_recv_speed &&
- (data->progress.dlspeed > data->set.max_recv_speed)) {
- fd_read = CURL_SOCKET_BAD;
- timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
- data->progress.dlspeed, state->blksize);
- }
- else
- fd_read = state->sockfd;
- }
-
- if(data->set.timeout) {
- timeout_ms = data->set.timeout - Curl_tvdiff(k->now, k->start);
- if(timeout_ms > state->retry_time * 1000)
- timeout_ms = state->retry_time * 1000;
- else if(timeout_ms < 0)
- timeout_ms = 0;
- }
-
-
- /* Wait until ready to read or timeout occurs */
- rc = Curl_socket_ready(fd_read, CURL_SOCKET_BAD, timeout_ms);
-
- k->now = Curl_tvnow();
-
- /* Force a progress callback if it's been too long */
- if(Curl_tvdiff(k->now, k->start) >= data->set.timeout) {
- if(Curl_pgrsUpdate(conn)) {
- tftp_state_machine(state, TFTP_EVENT_ERROR);
- return CURLE_ABORTED_BY_CALLBACK;
- }
- k->start = k->now;
- }
-
- if(rc == -1) {
- /* bail out */
- int error = SOCKERRNO;
- failf(data, "%s", Curl_strerror(conn, error));
- state->event = TFTP_EVENT_ERROR;
- }
- else {
-
- if(rc==0) {
- /* A timeout occurred, but our timeout is variable, so maybe
- just continue? */
- long rtms = state->retry_time * 1000;
- if(Curl_tvdiff(k->now, transaction_start) > rtms) {
- state->event = TFTP_EVENT_TIMEOUT;
- /* Force a look at transfer timeouts */
- check_time = 1;
- }
- else {
- continue; /* skip state machine */
- }
- }
- else {
- result = tftp_receive_packet(conn);
- if(result == CURLE_OK)
- transaction_start = Curl_tvnow();
-
- if(k->bytecountp)
- *k->bytecountp = k->bytecount; /* read count */
- if(k->writebytecountp)
- *k->writebytecountp = k->writebytecount; /* write count */
- }
- }
-
- if(check_time) {
- tftp_state_timeout(conn, NULL);
- check_time = 0;
- }
-
- if(result)
- return(result);
-
- result = tftp_state_machine(state, state->event);
- }
-
- /* Tell curl we're done */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
- return(result);
-}
-
/**********************************************************
*
* tftp_multi_statemach
@@ -1404,12 +1281,7 @@ static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done)
if(state->state == TFTP_STATE_FIN || result != CURLE_OK)
return(result);
- if(conn->data->state.used_interface == Curl_if_multi)
- tftp_multi_statemach(conn, dophase_done);
- else {
- result = tftp_easy_statemach(conn);
- *dophase_done = TRUE; /* with the easy interface we are done here */
- }
+ tftp_multi_statemach(conn, dophase_done);
if(*dophase_done)
DEBUGF(infof(conn->data, "DO phase is complete\n"));
diff --git a/lib/transfer.c b/lib/transfer.c
index 6be43ea34..5e861f9fe 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -2068,8 +2068,6 @@ static CURLcode Curl_do_perform(struct SessionHandle *data)
char *newurl = NULL; /* possibly a new URL to follow to! */
followtype follow = FOLLOW_NONE;
- data->state.used_interface = Curl_if_easy;
-
res = Curl_pretransfer(data);
if(res)
return res;
diff --git a/lib/url.c b/lib/url.c
index 8e07a791e..80c8a997e 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -131,7 +131,6 @@ int curl_win32_idn_to_ascii(const char *in, char **out);
#include "memdebug.h"
/* Local static prototypes */
-static bool ConnectionKillOne(struct SessionHandle *data);
static void conn_free(struct connectdata *conn);
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
static CURLcode do_init(struct connectdata *conn);
@@ -255,15 +254,6 @@ static const struct Curl_handler Curl_handler_dummy = {
PROTOPT_NONE /* flags */
};
-static void close_connections(struct SessionHandle *data)
-{
- /* Loop through all open connections and kill them one by one */
- bool killed;
- do {
- killed = ConnectionKillOne(data);
- } while(killed);
-}
-
void Curl_freeset(struct SessionHandle * data)
{
/* Free all dynamic strings stored in the data->set substructure. */
@@ -386,6 +376,11 @@ CURLcode Curl_close(struct SessionHandle *data)
and detach this handle from there. */
curl_multi_remove_handle(data->multi, data);
+ if(data->multi_easy)
+ /* when curl_easy_perform() is used, it creates its own multi handle to
+ use and this is the one */
+ curl_multi_cleanup(data->multi_easy);
+
/* Destroy the timeout list that is held in the easy handle. It is
/normally/ done by curl_multi_remove_handle() but this is "just in
case" */
@@ -398,19 +393,6 @@ CURLcode Curl_close(struct SessionHandle *data)
the multi handle, since that function uses the magic
field! */
- if(data->state.conn_cache) {
- if(data->state.conn_cache->type == CONNCACHE_PRIVATE) {
- /* close all connections still alive that are in the private connection
- cache, as we no longer have the pointer left to the shared one. */
- close_connections(data);
- Curl_conncache_destroy(data->state.conn_cache);
- data->state.conn_cache = NULL;
- }
- }
-
- if(data->dns.hostcachetype == HCACHE_PRIVATE)
- Curl_hostcache_destroy(data);
-
if(data->state.rangestringalloc)
free(data->state.range);
@@ -2000,10 +1982,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
data->share->dirty++;
if(data->share->hostcache) {
- /* use shared host cache, first free the private one if any */
- if(data->dns.hostcachetype == HCACHE_PRIVATE)
- Curl_hostcache_destroy(data);
-
+ /* use shared host cache */
data->dns.hostcache = data->share->hostcache;
data->dns.hostcachetype = HCACHE_SHARED;
}
@@ -2982,69 +2961,6 @@ ConnectionExists(struct SessionHandle *data,
return FALSE; /* no matching connecting exists */
}
-/*
- * This function kills and removes an existing connection in the connection
- * cache. The connection that has been unused for the longest time.
- *
- * Returns FALSE if it can't find any unused connection to kill.
- */
-static bool
-ConnectionKillOne(struct SessionHandle *data)
-{
- struct conncache *bc = data->state.conn_cache;
- struct curl_hash_iterator iter;
- struct curl_llist_element *curr;
- struct curl_hash_element *he;
- long highscore=-1;
- long score;
- struct timeval now;
- struct connectdata *conn_candidate = NULL;
- struct connectbundle *bundle;
-
- now = Curl_tvnow();
-
- Curl_hash_start_iterate(bc->hash, &iter);
-
- he = Curl_hash_next_element(&iter);
- while(he) {
- struct connectdata *conn;
-
- bundle = he->ptr;
-
- curr = bundle->conn_list->head;
- while(curr) {
- conn = curr->ptr;
-
- if(!conn->inuse) {
- /* Set higher score for the age passed since the connection was used */
- score = Curl_tvdiff(now, conn->now);
-
- if(score > highscore) {
- highscore = score;
- conn_candidate = conn;
- }
- }
- curr = curr->next;
- }
-
- he = Curl_hash_next_element(&iter);
- }
-
- if(conn_candidate) {
- /* Set the connection's owner correctly */
- conn_candidate->data = data;
-
- bundle = conn_candidate->bundle;
-
- /* the winner gets the honour of being disconnected */
- (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
/* this connection can now be marked 'idle' */
static void
ConnectionDone(struct connectdata *conn)
diff --git a/lib/urldata.h b/lib/urldata.h
index 576872d70..7a275da5e 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -1147,15 +1147,15 @@ struct auth {
};
struct UrlState {
- enum {
- Curl_if_none,
- Curl_if_easy,
- Curl_if_multi
- } used_interface;
/* Points to the connection cache */
struct conncache *conn_cache;
+ /* when curl_easy_perform() is called, the multi handle is "owned" by
+ the easy handle so curl_easy_cleanup() on such an easy handle will
+ also close the multi handle! */
+ bool multi_owned_by_easy;
+
/* buffers to store authentication data in, as parsed from input options */
struct timeval keeps_speed; /* for the progress meter really */
@@ -1588,7 +1588,6 @@ struct Names {
struct curl_hash *hostcache;
enum {
HCACHE_NONE, /* not pointing to anything */
- HCACHE_PRIVATE, /* points to our own */
HCACHE_GLOBAL, /* points to the (shrug) global one */
HCACHE_MULTI, /* points to a shared one in the multi handle */
HCACHE_SHARED /* points to a shared one in a shared object */
@@ -1608,7 +1607,11 @@ struct Names {
struct SessionHandle {
struct Names dns;
struct Curl_multi *multi; /* if non-NULL, points to the multi handle
- struct to which this "belongs" */
+ struct to which this "belongs" when used by
+ the multi interface */
+ struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle
+ struct to which this "belongs" when used
+ by the easy interface */
struct Curl_one_easy *multi_pos; /* if non-NULL, points to its position
in multi controlling structure to assist
in removal. */