From c43127414d89ccb9ef6517081f68986d991bcfb3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 17 Jan 2013 12:59:23 +0100 Subject: [PATCH] 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 --- docs/TODO | 14 +--- lib/Makefile.inc | 2 +- lib/conncache.c | 7 +- lib/conncache.h | 10 +-- lib/connect.c | 42 ++++++++-- lib/easy.c | 161 ++++++++++-------------------------- lib/ftp.c | 168 +++++++++++++++++++------------------ lib/http.c | 34 ++------ lib/http_proxy.c | 33 +++----- lib/imap.c | 45 ++-------- lib/multi.c | 183 +++++++---------------------------------- lib/multihandle.h | 134 ++++++++++++++++++++++++++++++ lib/openldap.c | 25 ++---- lib/pop3.c | 30 ++----- lib/smtp.c | 41 ++------- lib/ssh.c | 28 ++----- lib/ssluse.c | 12 +-- lib/tftp.c | 132 +---------------------------- lib/transfer.c | 4 +- lib/url.c | 98 ++-------------------- lib/urldata.h | 19 +++-- tests/Makefile.am | 2 +- tests/data/test1208 | 8 +- tests/data/test1349 | 3 +- tests/data/test1350 | 3 +- tests/data/test1351 | 3 +- tests/data/test1352 | 3 +- tests/data/test1353 | 3 +- tests/data/test1354 | 1 - tests/data/test1357 | 1 - tests/data/test1358 | 1 - tests/data/test1359 | 1 - tests/data/test1360 | 1 - tests/data/test1361 | 1 - tests/data/test1362 | 1 - tests/data/test1379 | 1 - tests/data/test1380 | 1 - tests/data/test1381 | 1 - tests/data/test1382 | 1 - tests/data/test1383 | 1 - tests/data/test1384 | 1 - tests/data/test1387 | 1 - tests/data/test1388 | 1 - tests/data/test1389 | 1 - tests/data/test1390 | 1 - tests/data/test1391 | 1 - tests/data/test1392 | 1 - tests/data/test1407 | 2 +- tests/data/test506 | 74 ++++++++--------- tests/data/test808 | 3 +- tests/data/test813 | 4 +- tests/libtest/lib537.c | 4 +- tests/runtests.pl | 3 +- tests/valgrind.supp | 16 ++++ 54 files changed, 489 insertions(+), 883 deletions(-) create mode 100644 lib/multihandle.h create mode 100644 tests/valgrind.supp diff --git a/docs/TODO b/docs/TODO index cfbac2f3b1fa7e..470612d3007024 100644 --- a/docs/TODO +++ b/docs/TODO @@ -21,8 +21,7 @@ 2. libcurl - multi interface 2.1 More non-blocking - 2.2 Remove easy interface internally - 2.4 Fix HTTP Pipelining for PUT + 2.2 Fix HTTP Pipelining for PUT 3. Documentation 3.1 More and better @@ -191,16 +190,7 @@ - The "DONE" operation (post transfer protocol-specific actions) for the protocols SFTP, SMTP, FTP. Fixing Curl_done() for this is a worthy task. -2.2 Remove easy interface internally - - Make curl_easy_perform() a wrapper-function that simply creates a multi - handle, adds the easy handle to it, runs curl_multi_perform() until the - transfer is done, then detach the easy handle, destroy the multi handle and - return the easy handle's return code. This will thus make everything - internally use and assume the multi interface. The select()-loop should use - curl_multi_socket(). - -2.4 Fix HTTP Pipelining for PUT +2.2 Fix HTTP Pipelining for PUT HTTP Pipelining can be a way to greatly enhance performance for multiple serial requests and currently libcurl only supports that for HEAD and GET diff --git a/lib/Makefile.inc b/lib/Makefile.inc index b116b3932fe2ac..e970edd90eac12 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 165a26fb4fd42e..530cdc2ec3f418 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012, Linus Nielsen Feltzing, - * Copyright (C) 2012, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2013, Daniel Stenberg, , 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 03b129d4e9a072..fad17d8f7a76d4 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012, Linus Nielsen Feltzing, + * Copyright (C) 2012, 2013, Linus Nielsen Feltzing, * * 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 acaf3b485ba0ee..c411a0eb5ff2f8 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 700fb327be4e6c..2f4b48b802f78c 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 352f12f44c5ab2..469b8874969e48 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 21f7c21e6e1661..daaafe317e340e 100644 --- a/lib/http.c +++ b/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 ada89576e170ef..ed0fe95037bc78 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 a53f2e9d243126..c7198d0b1435a0 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 de1fc7d6ead8a4..fa0afb9f83ab3a 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 @@ -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 00000000000000..9418359415ee1e --- /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, , 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 4c395fadc33014..cb3b4202058645 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010, Howard Chu, - * Copyright (C) 2011 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 2011 - 2013, Daniel Stenberg, , 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 e8fd9c52c7dc06..b5ea7c23f09b2e 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 2ad2ac691742e9..8de9af9c3e064e 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 8eb2e9fc9becb4..0c7d36ecc94c55 100644 --- a/lib/ssh.c +++ b/lib/ssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 c4c465c2c76a39..4a0dba734d4cad 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 282460bdaa5810..ef740b856986c9 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 6be43ea34d39fa..5e861f9fe34d82 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 8e07a791eabc3f..80c8a997e39891 100644 --- a/lib/url.c +++ b/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 576872d70f84b3..7a275da5eef0ae 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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. */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 65a618ea622d99..7e55d96ab9e095 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,7 +27,7 @@ EXTRA_DIST = ftpserver.pl httpserver.pl secureserver.pl runtests.pl getpart.pm \ FILEFORMAT README stunnel.pem memanalyze.pl testcurl.pl valgrind.pm ftp.pm \ sshserver.pl sshhelp.pm testcurl.1 runtests.1 $(HTMLPAGES) $(PDFPAGES) \ serverhelp.pm tftpserver.pl rtspserver.pl directories.pm symbol-scan.pl \ - CMakeLists.txt mem-include-scan.pl + CMakeLists.txt mem-include-scan.pl valgrind.supp # we have two variables here to make sure DIST_SUBDIRS won't get 'unit' # added twice as then targets such as 'distclean' misbehave and try to diff --git a/tests/data/test1208 b/tests/data/test1208 index a0d428b43e90b7..504f6c7bb3b832 100644 --- a/tests/data/test1208 +++ b/tests/data/test1208 @@ -36,6 +36,11 @@ FTP PORT download, no data conn and no transient negative reply s/^EPRT \|1\|(.*)/EPRT \|1\|/ + +# This test doesn't send a QUIT because the main state machine in multi.c +# triggers the timeout and sets the CURLE_OPERATION_TIMEDOUT error (28) for +# which the FTP disconect code generically has to assume could mean the +# control the connection and thus it cannot send any command. USER anonymous PASS ftp@example.com @@ -44,10 +49,9 @@ EPRT |1| TYPE I SIZE 1208 RETR 1208 -QUIT -12 +28 diff --git a/tests/data/test1349 b/tests/data/test1349 index c88eec9e054adf..28576fe90c31cf 100644 --- a/tests/data/test1349 +++ b/tests/data/test1349 @@ -54,6 +54,8 @@ fooo mooo +# The final "221 bye bye baby" response to QUIT will not be recorded +# since that is not considered part of this particular transfer! 220- _ _ ____ _ 220- ___| | | | _ \| | @@ -69,7 +71,6 @@ mooo 213 10 150 Binary data connection for 1349 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1350 b/tests/data/test1350 index 2142894840f784..df12de59d2448f 100644 --- a/tests/data/test1350 +++ b/tests/data/test1350 @@ -54,6 +54,8 @@ fooo mooo +# The final "221 bye bye baby" response to QUIT will not be recorded +# since that is not considered part of this particular transfer! 220- _ _ ____ _ 220- ___| | | | _ \| | @@ -69,7 +71,6 @@ mooo 213 10 150 Binary data connection for 1350 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1351 b/tests/data/test1351 index fea442fc7fc231..d81265e01c69d2 100644 --- a/tests/data/test1351 +++ b/tests/data/test1351 @@ -55,6 +55,8 @@ fooo mooo +# The final "221 bye bye baby" response to QUIT will not be recorded +# since that is not considered part of this particular transfer! 220- _ _ ____ _ 220- ___| | | | _ \| | @@ -70,7 +72,6 @@ mooo 213 10 150 Binary data connection for 1351 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1352 b/tests/data/test1352 index ca24d4911b270e..fa9d1ce7bdabb9 100644 --- a/tests/data/test1352 +++ b/tests/data/test1352 @@ -55,6 +55,8 @@ fooo mooo +# The final "221 bye bye baby" response to QUIT will not be recorded +# since that is not considered part of this particular transfer! 220- _ _ ____ _ 220- ___| | | | _ \| | @@ -70,7 +72,6 @@ mooo 213 10 150 Binary data connection for 1352 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1353 b/tests/data/test1353 index 015f60a4180626..b2954b14daef16 100644 --- a/tests/data/test1353 +++ b/tests/data/test1353 @@ -54,6 +54,8 @@ fooo mooo +# The final "221 bye bye baby" response to QUIT will not be recorded +# since that is not considered part of this particular transfer! 220- _ _ ____ _ 220- ___| | | | _ \| | @@ -69,7 +71,6 @@ mooo 213 10 150 Binary data connection for 1353 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1354 b/tests/data/test1354 index cf644294cbfcfb..aa54f2d07e700b 100644 --- a/tests/data/test1354 +++ b/tests/data/test1354 @@ -69,7 +69,6 @@ mooo 213 10 150 Binary data connection for 1354 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1357 b/tests/data/test1357 index 4b539ec6d046b2..952572032b250a 100644 --- a/tests/data/test1357 +++ b/tests/data/test1357 @@ -87,7 +87,6 @@ MOOOO 213 214 150 Binary data connection for 1357 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1358 b/tests/data/test1358 index 081b36f1d6d7d8..24cc724ea06690 100644 --- a/tests/data/test1358 +++ b/tests/data/test1358 @@ -87,7 +87,6 @@ MOOOO 213 214 150 Binary data connection for 1358 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1359 b/tests/data/test1359 index bb9747ae489e82..96445bb4e844f9 100644 --- a/tests/data/test1359 +++ b/tests/data/test1359 @@ -88,7 +88,6 @@ MOOOO 213 214 150 Binary data connection for 1359 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1360 b/tests/data/test1360 index d2cb33dfcb7d54..abfbe8c682b5c1 100644 --- a/tests/data/test1360 +++ b/tests/data/test1360 @@ -88,7 +88,6 @@ MOOOO 213 214 150 Binary data connection for 1360 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1361 b/tests/data/test1361 index 8d3f1f53bbf2f1..b97de16cd635c0 100644 --- a/tests/data/test1361 +++ b/tests/data/test1361 @@ -87,7 +87,6 @@ MOOOO 213 214 150 Binary data connection for 1361 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1362 b/tests/data/test1362 index 5dc41811c4b1b7..0ea7461ea66d9c 100644 --- a/tests/data/test1362 +++ b/tests/data/test1362 @@ -87,7 +87,6 @@ MOOOO 213 214 150 Binary data connection for 1362 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1379 b/tests/data/test1379 index 26552c8fab7fb0..0fd06d5c541dea 100644 --- a/tests/data/test1379 +++ b/tests/data/test1379 @@ -64,7 +64,6 @@ mooo 213 10 150 Binary data connection for 1379 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1380 b/tests/data/test1380 index 060b4b0b8e2281..ec7800d5d3741a 100644 --- a/tests/data/test1380 +++ b/tests/data/test1380 @@ -64,7 +64,6 @@ mooo 213 10 150 Binary data connection for 1380 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1381 b/tests/data/test1381 index c82ac745dd2c7f..8f3bc37f08c524 100644 --- a/tests/data/test1381 +++ b/tests/data/test1381 @@ -65,7 +65,6 @@ mooo 213 10 150 Binary data connection for 1381 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1382 b/tests/data/test1382 index 5e5513f3869aef..fbf5195f72838c 100644 --- a/tests/data/test1382 +++ b/tests/data/test1382 @@ -65,7 +65,6 @@ mooo 213 10 150 Binary data connection for 1382 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1383 b/tests/data/test1383 index 2604faf9631acd..0469e72e019e65 100644 --- a/tests/data/test1383 +++ b/tests/data/test1383 @@ -64,7 +64,6 @@ mooo 213 10 150 Binary data connection for 1383 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1384 b/tests/data/test1384 index 289aa9bdbfc9b0..2deeb7c4c6e464 100644 --- a/tests/data/test1384 +++ b/tests/data/test1384 @@ -64,7 +64,6 @@ mooo 213 10 150 Binary data connection for 1384 () (10 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1387 b/tests/data/test1387 index 6628b8963e5e66..a2c5a4d6b056a9 100644 --- a/tests/data/test1387 +++ b/tests/data/test1387 @@ -79,7 +79,6 @@ MOOOO 213 214 150 Binary data connection for 1387 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1388 b/tests/data/test1388 index 478a2e9fa88836..b4662e654b87c4 100644 --- a/tests/data/test1388 +++ b/tests/data/test1388 @@ -79,7 +79,6 @@ MOOOO 213 214 150 Binary data connection for 1388 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1389 b/tests/data/test1389 index 06b9c26b1bf1ca..c2ac3ad9d3044c 100644 --- a/tests/data/test1389 +++ b/tests/data/test1389 @@ -80,7 +80,6 @@ MOOOO 213 214 150 Binary data connection for 1389 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1390 b/tests/data/test1390 index b5980369e63ba6..3cf86e775f2d10 100644 --- a/tests/data/test1390 +++ b/tests/data/test1390 @@ -80,7 +80,6 @@ MOOOO 213 214 150 Binary data connection for 1390 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1391 b/tests/data/test1391 index fa7d89a6695440..913f25afe724f3 100644 --- a/tests/data/test1391 +++ b/tests/data/test1391 @@ -79,7 +79,6 @@ MOOOO 213 214 150 Binary data connection for 1391 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1392 b/tests/data/test1392 index c1e8346a28169a..b05fc317c6fb12 100644 --- a/tests/data/test1392 +++ b/tests/data/test1392 @@ -79,7 +79,6 @@ MOOOO 213 214 150 Binary data connection for 1392 () (214 bytes). 226 File transfer complete -221 bye bye baby s/^(229 Entering Passive Mode \().*(\).*)/${1}stripped${2}/ diff --git a/tests/data/test1407 b/tests/data/test1407 index a0c84057dd4813..842778a7c21441 100644 --- a/tests/data/test1407 +++ b/tests/data/test1407 @@ -12,7 +12,7 @@ LIST # Server-side -REPLY LIST +OK 1407 100 +REPLY LIST +OK 1407 100\r\n. diff --git a/tests/data/test506 b/tests/data/test506 index c1a959a40afb73..a61e2fc0032005 100644 --- a/tests/data/test506 +++ b/tests/data/test506 @@ -101,66 +101,60 @@ run 1: set cookie 1, 2 and 3 lock: dns [Pigs in space]: 14 unlock: dns [Pigs in space]: 15 CLEANUP -lock: dns [Pigs in space]: 16 -unlock: dns [Pigs in space]: 17 -lock: cookie [Pigs in space]: 18 -unlock: cookie [Pigs in space]: 19 -lock: share [Pigs in space]: 20 -unlock: share [Pigs in space]: 21 +lock: cookie [Pigs in space]: 16 +unlock: cookie [Pigs in space]: 17 +lock: share [Pigs in space]: 18 +unlock: share [Pigs in space]: 19 *** run 2 CURLOPT_SHARE -lock: share [Pigs in space]: 22 -unlock: share [Pigs in space]: 23 +lock: share [Pigs in space]: 20 +unlock: share [Pigs in space]: 21 PERFORM -lock: dns [Pigs in space]: 24 -unlock: dns [Pigs in space]: 25 +lock: dns [Pigs in space]: 22 +unlock: dns [Pigs in space]: 23 +lock: cookie [Pigs in space]: 24 +unlock: cookie [Pigs in space]: 25 lock: cookie [Pigs in space]: 26 unlock: cookie [Pigs in space]: 27 lock: cookie [Pigs in space]: 28 unlock: cookie [Pigs in space]: 29 -lock: cookie [Pigs in space]: 30 -unlock: cookie [Pigs in space]: 31 run 2: set cookie 4 and 5 -lock: dns [Pigs in space]: 32 -unlock: dns [Pigs in space]: 33 +lock: dns [Pigs in space]: 30 +unlock: dns [Pigs in space]: 31 CLEANUP -lock: dns [Pigs in space]: 34 -unlock: dns [Pigs in space]: 35 -lock: cookie [Pigs in space]: 36 -unlock: cookie [Pigs in space]: 37 -lock: share [Pigs in space]: 38 -unlock: share [Pigs in space]: 39 +lock: cookie [Pigs in space]: 32 +unlock: cookie [Pigs in space]: 33 +lock: share [Pigs in space]: 34 +unlock: share [Pigs in space]: 35 *** run 3 CURLOPT_SHARE -lock: share [Pigs in space]: 40 -unlock: share [Pigs in space]: 41 +lock: share [Pigs in space]: 36 +unlock: share [Pigs in space]: 37 CURLOPT_COOKIEJAR PERFORM -lock: dns [Pigs in space]: 42 -unlock: dns [Pigs in space]: 43 +lock: dns [Pigs in space]: 38 +unlock: dns [Pigs in space]: 39 +lock: cookie [Pigs in space]: 40 +unlock: cookie [Pigs in space]: 41 +lock: cookie [Pigs in space]: 42 +unlock: cookie [Pigs in space]: 43 lock: cookie [Pigs in space]: 44 unlock: cookie [Pigs in space]: 45 -lock: cookie [Pigs in space]: 46 -unlock: cookie [Pigs in space]: 47 -lock: cookie [Pigs in space]: 48 -unlock: cookie [Pigs in space]: 49 run 3: overwrite cookie 1 and 4 -lock: dns [Pigs in space]: 50 -unlock: dns [Pigs in space]: 51 +lock: dns [Pigs in space]: 46 +unlock: dns [Pigs in space]: 47 try SHARE_CLEANUP... -lock: share [Pigs in space]: 52 -unlock: share [Pigs in space]: 53 +lock: share [Pigs in space]: 48 +unlock: share [Pigs in space]: 49 SHARE_CLEANUP failed, correct CLEANUP -lock: dns [Pigs in space]: 54 -unlock: dns [Pigs in space]: 55 -lock: cookie [Pigs in space]: 56 -unlock: cookie [Pigs in space]: 57 -lock: share [Pigs in space]: 58 -unlock: share [Pigs in space]: 59 +lock: cookie [Pigs in space]: 50 +unlock: cookie [Pigs in space]: 51 +lock: share [Pigs in space]: 52 +unlock: share [Pigs in space]: 53 SHARE_CLEANUP -lock: share [Pigs in space]: 60 -unlock: share [Pigs in space]: 61 +lock: share [Pigs in space]: 54 +unlock: share [Pigs in space]: 55 GLOBAL_CLEANUP diff --git a/tests/data/test808 b/tests/data/test808 index 2c9c11cdd23d3e..9027a5ab99159f 100644 --- a/tests/data/test808 +++ b/tests/data/test808 @@ -10,7 +10,8 @@ LIST # Server-side -REPLY LIST +OK 808 100 +# include the '.\r\n' 3-byte trailer to end the transfer poperly! +REPLY LIST +OK 808 100\r\n. diff --git a/tests/data/test813 b/tests/data/test813 index 1447fa277b5fd5..fac4aa0ef70107 100644 --- a/tests/data/test813 +++ b/tests/data/test813 @@ -34,11 +34,13 @@ pop3://%HOSTIP:%POP3PORT/813 -u user:wrong 67 +# +# The multi interface considers a broken "DO" request as a prematurely broken +# transfer and such a connection will not get a "QUIT" CAPA USER user PASS wrong -QUIT diff --git a/tests/libtest/lib537.c b/tests/libtest/lib537.c index 24d25223530539..5da537995c99db 100644 --- a/tests/libtest/lib537.c +++ b/tests/libtest/lib537.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -41,7 +41,7 @@ #error "this test requires FD_SETSIZE" #endif -#define SAFETY_MARGIN (10) +#define SAFETY_MARGIN (11) #if defined(WIN32) || defined(_WIN32) || defined(MSDOS) #define DEV_NULL "NUL" diff --git a/tests/runtests.pl b/tests/runtests.pl index f6c2016c4b9298..c59d68fb7e8ac7 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -6,7 +6,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. +# Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -3084,6 +3084,7 @@ sub singletest { my $valgrindcmd = "$valgrind "; $valgrindcmd .= "$valgrind_tool " if($valgrind_tool); $valgrindcmd .= "--leak-check=yes "; + $valgrindcmd .= "--suppressions=valgrind.supp "; $valgrindcmd .= "--num-callers=16 "; $valgrindcmd .= "${valgrind_logfile}=$LOGDIR/valgrind$testnum"; $CMDLINE = "$valgrindcmd $CMDLINE"; diff --git a/tests/valgrind.supp b/tests/valgrind.supp new file mode 100644 index 00000000000000..6fb9ce80178907 --- /dev/null +++ b/tests/valgrind.supp @@ -0,0 +1,16 @@ +{ + libidn-idna_to_ascii-error + Memcheck:Addr4 + fun:idna_to_ascii_4z + fun:idna_to_ascii_8z + fun:idna_to_ascii_lz + fun:fix_hostname + fun:resolve_server + fun:create_conn + fun:Curl_connect + fun:multi_runsingle + fun:curl_multi_perform + fun:curl_easy_perform + fun:operate + fun:main +}