From cb895ec3356822df72eb91171a1cc63ad1845d93 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 12 Aug 2002 09:43:20 +0000 Subject: [PATCH] Initial fix to make the multi interface return control while waiting for the initial connect to "come through". This should work fine for connect and for FTP-PASV connects. Needs massive testing. --- lib/connect.c | 91 +++++++++++++++- lib/connect.h | 9 +- lib/ftp.c | 282 ++++++++++++++++++++++++++++---------------------- lib/ftp.h | 10 +- lib/multi.c | 111 ++++++++++++++++++-- lib/url.c | 85 +++++++++++---- lib/url.h | 4 +- lib/urldata.h | 11 +- 8 files changed, 433 insertions(+), 170 deletions(-) diff --git a/lib/connect.c b/lib/connect.c index ba00b73e9..545062c66 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -337,6 +337,70 @@ int socketerror(int sockfd) return err; } +/* + * Curl_is_connected() is used from the multi interface to check if the + * firstsocket has connected. + */ + +CURLcode Curl_is_connected(struct connectdata *conn, + int sockfd, + bool *connected) +{ + int rc; + struct SessionHandle *data = conn->data; + + *connected = FALSE; /* a very negative world view is best */ + + if(data->set.timeout || data->set.connecttimeout) { + /* there is a timeout set */ + + /* Evaluate in milliseconds how much time that has passed */ + long has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); + + /* subtract the most strict timeout of the ones */ + if(data->set.timeout && data->set.connecttimeout) { + if (data->set.timeout < data->set.connecttimeout) + has_passed -= data->set.timeout*1000; + else + has_passed -= data->set.connecttimeout*1000; + } + else if(data->set.timeout) + has_passed -= data->set.timeout*1000; + else + has_passed -= data->set.connecttimeout*1000; + + if(has_passed > 0 ) { + /* time-out, bail out, go home */ + failf(data, "Connection time-out"); + return CURLE_OPERATION_TIMEOUTED; + } + } + + /* check for connect without timeout as we want to return immediately */ + rc = waitconnect(sockfd, 0); + + if(0 == rc) { + int err = socketerror(sockfd); + if ((0 == err) || (EISCONN == err)) { + /* we are connected, awesome! */ + *connected = TRUE; + return CURLE_OK; + } + /* nope, not connected for real */ + } + + /* + * If the connection phase is "done" here, we should attempt to connect + * to the "next address" in the Curl_hostaddr structure that we resolved + * before. But we don't have that struct around anymore and we can't just + * keep a pointer since the cache might in fact have gotten pruned by the + * time we want to read this... Alas, we don't do this yet. + */ + + return CURLE_OK; +} + + /* * TCP connect to the given host with timeout, proxy or remote doesn't matter. * There might be more than one IP address to try out. Fill in the passed @@ -347,7 +411,8 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ Curl_addrinfo *remotehost, /* use one in here */ int port, /* connect to this */ int *sockconn, /* the connected socket */ - Curl_ipconnect **addr) /* the one we used */ + Curl_ipconnect **addr, /* the one we used */ + bool *connected) /* really connected? */ { struct SessionHandle *data = conn->data; int rc; @@ -437,8 +502,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ case EAGAIN: #endif case EINTR: - /* asynchronous connect, wait for connect or timeout */ + if(data->state.used_interface == Curl_if_multi) + /* don't hang when doing multi */ + timeout_ms = 0; + rc = waitconnect(sockfd, timeout_ms); break; case ECONNREFUSED: /* no one listening */ @@ -448,6 +516,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ break; } } + if(0 == rc) { /* we might be connected, if the socket says it is OK! Ask it! */ int err; @@ -455,11 +524,17 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ + *connected = TRUE; /* this is truly a connect */ break; } failf(data, "socket error: %d", err); /* we are _not_ connected, it was a false alert, continue please */ } + else if(data->state.used_interface == Curl_if_multi) { + /* When running the multi interface, we bail out here */ + rc = 0; + break; + } /* connect failed or timed out */ sclose(sockfd); @@ -542,8 +617,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ */ case EAGAIN: #endif - /* asynchronous connect, wait for connect or timeout */ + if(data->state.used_interface == Curl_if_multi) + /* don't hang when doing multi */ + timeout_ms = 0; + rc = waitconnect(sockfd, timeout_ms); break; default: @@ -558,6 +636,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ int err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ + *connected = TRUE; /* this is a true connect */ break; } /* nope, not connected for real */ @@ -565,6 +644,12 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ } if(0 != rc) { + if(data->state.used_interface == Curl_if_multi) { + /* When running the multi interface, we bail out here */ + rc = 0; + break; + } + /* get a new timeout for next attempt */ after = Curl_tvnow(); timeout_ms -= Curl_tvdiff(after, before); diff --git a/lib/connect.h b/lib/connect.h index f44252346..5a6b8e63c 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -26,10 +26,15 @@ int Curl_nonblock(int socket, /* operate on this */ int nonblock /* TRUE or FALSE */); +CURLcode Curl_is_connected(struct connectdata *conn, + int sockfd, + bool *connected); + CURLcode Curl_connecthost(struct connectdata *conn, Curl_addrinfo *host, /* connect to this */ int port, /* connect to this port number */ int *sockconn, /* not set if error is returned */ - Curl_ipconnect **addr /* the one we used */ - ); /* index we used */ + Curl_ipconnect **addr, /* the one we used */ + bool *connected /* truly connected? */ + ); #endif diff --git a/lib/ftp.c b/lib/ftp.c index 50d10a0ba..dd10ad41c 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -387,8 +387,10 @@ int Curl_GetFTPResponse(char *buf, return nread; /* total amount of bytes read */ } -/* ftp_connect() should do everything that is to be considered a part - of the connection phase. */ +/* + * Curl_ftp_connect() should do everything that is to be considered a part of + * the connection phase. + */ CURLcode Curl_ftp_connect(struct connectdata *conn) { /* this is FTP and no proxy */ @@ -1321,7 +1323,8 @@ CURLcode ftp_use_port(struct connectdata *conn) */ static -CURLcode ftp_use_pasv(struct connectdata *conn) +CURLcode ftp_use_pasv(struct connectdata *conn, + bool *connected) { struct SessionHandle *data = conn->data; ssize_t nread; @@ -1473,7 +1476,14 @@ CURLcode ftp_use_pasv(struct connectdata *conn) addr, connectport, &conn->secondarysocket, - &conninfo); + &conninfo, + connected); + + /* + * When this is used from the multi interface, this might've returned with + * the 'connected' set to FALSE and thus we are now awaiting a non-blocking + * connect to connect and we should not be "hanging" here waiting. + */ if((CURLE_OK == result) && data->set.verbose) @@ -1494,127 +1504,24 @@ CURLcode ftp_use_pasv(struct connectdata *conn) return CURLE_OK; } -/*********************************************************************** +/* + * Curl_ftp_nextconnect() * - * ftp_perform() - * - * This is the actual DO function for FTP. Get a file/directory according to - * the options previously setup. + * This function shall be called when the second FTP connection has been + * established and is confirmed connected. */ -static -CURLcode ftp_perform(struct connectdata *conn) +CURLcode Curl_ftp_nextconnect(struct connectdata *conn) { - /* this is FTP and no proxy */ - ssize_t nread; - CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; char *buf = data->state.buffer; /* this is our buffer */ + CURLcode result; + ssize_t nread; + int ftpcode; /* for ftp status */ /* the ftp struct is already inited in ftp_connect() */ struct FTP *ftp = conn->proto.ftp; - long *bytecountp = ftp->bytecountp; - int ftpcode; /* for ftp status */ - - /* Send any QUOTE strings? */ - if(data->set.quote) { - if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) - return result; - } - - /* This is a re-used connection. Since we change directory to where the - transfer is taking place, we must now get back to the original dir - where we ended up after login: */ - if (conn->bits.reuse) { - if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) - return result; - } - - /* change directory first! */ - if(ftp->dir && ftp->dir[0]) { - if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) - return result; - } - - /* Requested time of file? */ - if(data->set.get_filetime && ftp->file) { - result = ftp_getfiletime(conn, ftp->file); - if(result) - return result; - } - - /* If we have selected NOBODY and HEADER, it means that we only want file - information. Which in FTP can't be much more than the file size and - date. */ - if(data->set.no_body && data->set.include_header && ftp->file) { - /* The SIZE command is _not_ RFC 959 specified, and therefor many servers - may not support it! It is however the only way we have to get a file's - size! */ - ssize_t filesize; - - ftp->no_transfer = TRUE; /* this means no actual transfer is made */ - - /* Some servers return different sizes for different modes, and thus we - must set the proper type before we check the size */ - result = ftp_transfertype(conn, data->set.ftp_ascii); - if(result) - return result; - - /* failing to get size is not a serious error */ - result = ftp_getsize(conn, ftp->file, &filesize); - - if(CURLE_OK == result) { - sprintf(buf, "Content-Length: %d\r\n", filesize); - result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); - if(result) - return result; - } - - /* If we asked for a time of the file and we actually got one as - well, we "emulate" a HTTP-style header in our output. */ - -#ifdef HAVE_STRFTIME - if(data->set.get_filetime && data->info.filetime) { - struct tm *tm; -#ifdef HAVE_LOCALTIME_R - struct tm buffer; - tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); -#else - tm = localtime((unsigned long *)&data->info.filetime); -#endif - /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", - tm); - result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); - if(result) - return result; - } -#endif - - return CURLE_OK; - } - - if(data->set.no_body) - /* doesn't really transfer any data */ - ftp->no_transfer = TRUE; - /* Get us a second connection up and connected */ - else if(data->set.ftp_use_port) { - /* We have chosen to use the PORT command */ - result = ftp_use_port(conn); - if(CURLE_OK == result) - /* we have the data connection ready */ - infof(data, "Connected the data stream with PORT!\n"); - } - else { - /* We have chosen (this is default) to use the PASV command */ - result = ftp_use_pasv(conn); - if(CURLE_OK == result) - infof(data, "Connected the data stream with PASV!\n"); - } - - if(result) - return result; if(data->set.upload) { @@ -1991,6 +1898,128 @@ CURLcode ftp_perform(struct connectdata *conn) return CURLE_OK; } +/*********************************************************************** + * + * ftp_perform() + * + * This is the actual DO function for FTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode ftp_perform(struct connectdata *conn, + bool *connected) /* for the TCP connect status after + PASV / PORT */ +{ + /* this is FTP and no proxy */ + CURLcode result=CURLE_OK; + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is our buffer */ + + /* the ftp struct is already inited in ftp_connect() */ + struct FTP *ftp = conn->proto.ftp; + + /* Send any QUOTE strings? */ + if(data->set.quote) { + if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) + return result; + } + + /* This is a re-used connection. Since we change directory to where the + transfer is taking place, we must now get back to the original dir + where we ended up after login: */ + if (conn->bits.reuse) { + if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) + return result; + } + + /* change directory first! */ + if(ftp->dir && ftp->dir[0]) { + if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) + return result; + } + + /* Requested time of file? */ + if(data->set.get_filetime && ftp->file) { + result = ftp_getfiletime(conn, ftp->file); + if(result) + return result; + } + + /* If we have selected NOBODY and HEADER, it means that we only want file + information. Which in FTP can't be much more than the file size and + date. */ + if(data->set.no_body && data->set.include_header && ftp->file) { + /* The SIZE command is _not_ RFC 959 specified, and therefor many servers + may not support it! It is however the only way we have to get a file's + size! */ + ssize_t filesize; + + ftp->no_transfer = TRUE; /* this means no actual transfer is made */ + + /* Some servers return different sizes for different modes, and thus we + must set the proper type before we check the size */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if(result) + return result; + + /* failing to get size is not a serious error */ + result = ftp_getsize(conn, ftp->file, &filesize); + + if(CURLE_OK == result) { + sprintf(buf, "Content-Length: %d\r\n", filesize); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } + + /* If we asked for a time of the file and we actually got one as + well, we "emulate" a HTTP-style header in our output. */ + +#ifdef HAVE_STRFTIME + if(data->set.get_filetime && data->info.filetime) { + struct tm *tm; +#ifdef HAVE_LOCALTIME_R + struct tm buffer; + tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); +#else + tm = localtime((unsigned long *)&data->info.filetime); +#endif + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", + tm); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } +#endif + + return CURLE_OK; + } + + if(data->set.no_body) + /* doesn't really transfer any data */ + ftp->no_transfer = TRUE; + /* Get us a second connection up and connected */ + else if(data->set.ftp_use_port) { + /* We have chosen to use the PORT command */ + result = ftp_use_port(conn); + if(CURLE_OK == result) { + /* we have the data connection ready */ + infof(data, "Ordered connect of the data stream with PORT!\n"); + *connected = TRUE; /* mark us "still connected" */ + } + } + else { + /* We have chosen (this is default) to use the PASV command */ + result = ftp_use_pasv(conn, connected); + if(connected) + infof(data, "Connected the data stream with PASV!\n"); + } + + return result; +} + /*********************************************************************** * * Curl_ftp() @@ -2003,6 +2032,7 @@ CURLcode ftp_perform(struct connectdata *conn) CURLcode Curl_ftp(struct connectdata *conn) { CURLcode retcode; + bool connected; struct SessionHandle *data = conn->data; struct FTP *ftp; @@ -2049,15 +2079,15 @@ CURLcode Curl_ftp(struct connectdata *conn) else ftp->dir = NULL; - retcode = ftp_perform(conn); + retcode = ftp_perform(conn, &connected); - /* clean up here, success or error doesn't matter */ - if(ftp->file) - free(ftp->file); - if(ftp->dir) - free(ftp->dir); - - ftp->file = ftp->dir = NULL; /* zero */ + if(CURLE_OK == retcode) { + if(connected) + retcode = Curl_ftp_nextconnect(conn); + else + /* since we didn't connect now, we want do_more to get called */ + conn->do_more = TRUE; + } return retcode; } @@ -2128,6 +2158,12 @@ CURLcode Curl_ftp_disconnect(struct connectdata *conn) free(ftp->entrypath); if(ftp->cache) free(ftp->cache); + if(ftp->file) + free(ftp->file); + if(ftp->dir) + free(ftp->dir); + + ftp->file = ftp->dir = NULL; /* zero */ } return CURLE_OK; } diff --git a/lib/ftp.h b/lib/ftp.h index baf9f40e6..0ed2d9abe 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -1,6 +1,5 @@ #ifndef __FTP_H #define __FTP_H - /***************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -24,22 +23,15 @@ * $Id$ *****************************************************************************/ -/* MN 06/07/02 */ #ifndef CURL_DISABLE_FTP - CURLcode Curl_ftp(struct connectdata *conn); CURLcode Curl_ftp_done(struct connectdata *conn); CURLcode Curl_ftp_connect(struct connectdata *conn); CURLcode Curl_ftp_disconnect(struct connectdata *conn); - CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...); - -/* The kerberos stuff needs this: */ int Curl_GetFTPResponse(char *buf, struct connectdata *conn, int *ftpcode); - -/* MN 06/07/02 */ +CURLcode Curl_ftp_nextconnect(struct connectdata *conn); #endif - #endif diff --git a/lib/multi.c b/lib/multi.c index 2e6a408b4..0830aa4ae 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -29,6 +29,7 @@ #include "urldata.h" #include "transfer.h" #include "url.h" +#include "connect.h" /* The last #include file should be: */ #ifdef MALLOCDEBUG @@ -43,11 +44,13 @@ struct Curl_message { typedef enum { CURLM_STATE_INIT, - CURLM_STATE_CONNECT, - CURLM_STATE_DO, - CURLM_STATE_PERFORM, - CURLM_STATE_DONE, - CURLM_STATE_COMPLETED, + CURLM_STATE_CONNECT, /* connect has been sent off */ + CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ + CURLM_STATE_DO, /* send off the request (part 1) */ + CURLM_STATE_DO_MORE, /* send off the request (part 2) */ + CURLM_STATE_PERFORM, /* transfer data */ + CURLM_STATE_DONE, /* post data transfer operation */ + CURLM_STATE_COMPLETED, /* operation complete */ CURLM_STATE_LAST /* not a true state, never use this */ } CURLMstate; @@ -224,6 +227,32 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, switch(easy->state) { default: break; + case CURLM_STATE_WAITCONNECT: + case CURLM_STATE_DO_MORE: + { + /* when we're waiting for a connect, we wait for the socket to + become writable */ + struct connectdata *conn = easy->easy_conn; + int sockfd; + + if(CURLM_STATE_WAITCONNECT == easy->state) { + sockfd = conn->firstsocket; + FD_SET(sockfd, write_fd_set); + } + else { + /* When in DO_MORE state, we could be either waiting for us + to connect to a remote site, or we could wait for that site + to connect to us. It makes a difference in the way: if we + connect to the site we wait for the socket to become writable, if + the site connects to us we wait for it to become readable */ + sockfd = conn->secondarysocket; + FD_SET(sockfd, write_fd_set); + } + + if(sockfd > *max_fd) + *max_fd = sockfd; + } + break; case CURLM_STATE_PERFORM: /* This should have a set of file descriptors for us to set. */ /* after the transfer is done, go DONE */ @@ -251,6 +280,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) bool done; CURLMcode result=CURLM_OK; struct Curl_message *msg = NULL; + bool connected; *running_handles = 0; /* bump this once for every living handle */ @@ -259,6 +289,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { + +#ifdef MALLOCDEBUG + fprintf(stderr, "HANDLE %p: State: %x\n", + (char *)easy, easy->state); +#endif + switch(easy->state) { case CURLM_STATE_INIT: /* init this transfer. */ @@ -287,23 +323,80 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* Connect. We get a connection identifier filled in. */ easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); - /* after connect, go DO */ + /* after the connect has been sent off, go WAITCONNECT */ if(CURLE_OK == easy->result) { - easy->state = CURLM_STATE_DO; + easy->state = CURLM_STATE_WAITCONNECT; result = CURLM_CALL_MULTI_PERFORM; } break; + + case CURLM_STATE_WAITCONNECT: + { + bool connected; + easy->result = Curl_is_connected(easy->easy_conn, + easy->easy_conn->firstsocket, + &connected); + if(connected) + easy->result = Curl_protocol_connect(easy->easy_conn, NULL); + + if(CURLE_OK != easy->result) + /* failure detected */ + break; + + if(connected) { + /* after the connect has completed, go DO */ + easy->state = CURLM_STATE_DO; + result = CURLM_CALL_MULTI_PERFORM; + } + } + break; + case CURLM_STATE_DO: /* Do the fetch or put request */ easy->result = Curl_do(&easy->easy_conn); - /* after do, go PERFORM */ if(CURLE_OK == easy->result) { - if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) { + + /* after do, go PERFORM... or DO_MORE */ + if(easy->easy_conn->do_more) { + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + easy->state = CURLM_STATE_DO_MORE; + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ + easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) { + easy->state = CURLM_STATE_PERFORM; + result = CURLM_CALL_MULTI_PERFORM; + } + } + } + break; + + case CURLM_STATE_DO_MORE: + /* + * First, check if we really are ready to do more. + */ + easy->result = Curl_is_connected(easy->easy_conn, + easy->easy_conn->secondarysocket, + &connected); + if(connected) { + /* + * When we are connected, DO MORE and then go PERFORM + */ + easy->result = Curl_do_more(easy->easy_conn); + + if(CURLE_OK == easy->result) + easy->result = Curl_readwrite_init(easy->easy_conn); + + if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } break; + case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); diff --git a/lib/url.c b/lib/url.c index 071ec07f7..ead5eb494 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1307,7 +1307,8 @@ ConnectionStore(struct SessionHandle *data, } static CURLcode ConnectPlease(struct connectdata *conn, - Curl_addrinfo *hostaddr) + Curl_addrinfo *hostaddr, + bool *connected) { CURLcode result; Curl_ipconnect *addr; @@ -1319,7 +1320,8 @@ static CURLcode ConnectPlease(struct connectdata *conn, hostaddr, conn->port, &conn->firstsocket, - &addr); + &addr, + connected); if(CURLE_OK == result) { /* All is cool, then we store the current information from the hostaddr struct to the serv_addr, as it might be needed later. The address @@ -1374,7 +1376,7 @@ static void verboseconnect(struct connectdata *conn, struct in_addr in; (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr)); infof(data, "Connected to %s (%s) port %d\n", - hostaddr?hostaddr->h_name:"[re-used]", + hostaddr?hostaddr->h_name:"", #if defined(HAVE_INET_NTOA_R) inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)), #else @@ -1385,6 +1387,42 @@ static void verboseconnect(struct connectdata *conn, #endif } +/* + * We have discovered that the TCP connection has been successful, we can now + * proceed with some action. + * + * If we're using the multi interface, this host address pointer is most + * likely NULL at this point as we can't keep the resolved info around. This + * may call for some reworking, like a reference counter in the struct or + * something. The hostaddr is not used for very much though, we have the + * 'serv_addr' field in the connectdata struct for most of it. + */ +CURLcode Curl_protocol_connect(struct connectdata *conn, + Curl_addrinfo *hostaddr) +{ + struct SessionHandle *data = conn->data; + CURLcode result; + + Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ + + if(data->set.verbose) + verboseconnect(conn, hostaddr); + + if(conn->curl_connect) { + /* is there a protocol-specific connect() procedure? */ + + /* set start time here for timeout purposes in the + * connect procedure, it is later set again for the + * progress meter purpose */ + conn->now = Curl_tvnow(); + + /* Call the protocol-specific connect function */ + result = conn->curl_connect(conn); + } + + return result; /* pass back status */ +} + static CURLcode CreateConnection(struct SessionHandle *data, struct connectdata **in_connect) { @@ -1780,6 +1818,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->remote_port = PORT_HTTP; conn->protocol |= PROT_HTTP; conn->curl_do = Curl_http; + conn->curl_do_more = NULL; conn->curl_done = Curl_http_done; conn->curl_connect = Curl_http_connect; #else @@ -1797,6 +1836,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL; conn->curl_do = Curl_http; + conn->curl_do_more = NULL; conn->curl_done = Curl_http_done; conn->curl_connect = Curl_http_connect; @@ -1819,6 +1859,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, } conn->protocol |= PROT_GOPHER; conn->curl_do = Curl_http; + conn->curl_do_more = NULL; conn->curl_done = Curl_http_done; #else failf(data, LIBCURL_NAME @@ -1867,6 +1908,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, } else { conn->curl_do = Curl_ftp; + conn->curl_do_more = Curl_ftp_nextconnect; conn->curl_done = Curl_ftp_done; conn->curl_connect = Curl_ftp_connect; conn->curl_disconnect = Curl_ftp_disconnect; @@ -2441,29 +2483,16 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->headerbytecount = 0; if(-1 == conn->firstsocket) { + bool connected; + /* Connect only if not already connected! */ - result = ConnectPlease(conn, hostaddr); - Curl_pgrsTime(data, TIMER_CONNECT); /* connect done, good or bad */ + result = ConnectPlease(conn, hostaddr, &connected); + + if(connected) + result = Curl_protocol_connect(conn, hostaddr); if(CURLE_OK != result) return result; - - if(data->set.verbose) - verboseconnect(conn, hostaddr); - - if(conn->curl_connect) { - /* is there a protocol-specific connect() procedure? */ - - /* set start time here for timeout purposes in the - * connect procedure, it is later set again for the - * progress meter purpose */ - conn->now = Curl_tvnow(); - - /* Call the protocol-specific connect function */ - result = conn->curl_connect(conn); - if(result != CURLE_OK) - return result; /* pass back errors */ - } } else { Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ @@ -2558,6 +2587,8 @@ CURLcode Curl_do(struct connectdata **connp) struct connectdata *conn = *connp; struct SessionHandle *data=conn->data; + conn->do_more = FALSE; /* by default there's no curl_do_more() to use */ + if(conn->curl_do) { /* generic protocol-specific function pointer set in curl_connect() */ result = conn->curl_do(conn); @@ -2587,6 +2618,16 @@ CURLcode Curl_do(struct connectdata **connp) return result; } +CURLcode Curl_do_more(struct connectdata *conn) +{ + CURLcode result=CURLE_OK; + + if(conn->curl_do_more) + result = conn->curl_do_more(conn); + + return result; +} + /* * local variables: * eval: (load-file "../curl-mode.el") diff --git a/lib/url.h b/lib/url.h index 630cd1c19..c7c6503b3 100644 --- a/lib/url.h +++ b/lib/url.h @@ -32,7 +32,9 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...); CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */ CURLcode Curl_connect(struct SessionHandle *, struct connectdata **); CURLcode Curl_do(struct connectdata **); +CURLcode Curl_do_more(struct connectdata *); CURLcode Curl_done(struct connectdata *); CURLcode Curl_disconnect(struct connectdata *); - +CURLcode Curl_protocol_connect(struct connectdata *conn, + Curl_addrinfo *hostaddr); #endif diff --git a/lib/urldata.h b/lib/urldata.h index 9c7b18b1b..44a4a7200 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -328,6 +328,12 @@ struct connectdata { CURLcode (*curl_do)(struct connectdata *connect); CURLcode (*curl_done)(struct connectdata *connect); + /* If the curl_do() function is better made in two halves, this + * curl_do_more() function will be called afterwards, if set. For example + * for doing the FTP stuff after the PASV/PORT command. + */ + CURLcode (*curl_do_more)(struct connectdata *connect); + /* This function *MAY* be set to a protocol-dependent function that is run * after the connect() and everything is done, as a step in the connection. */ @@ -414,7 +420,10 @@ struct connectdata { buffer, so the next read should read from where this pointer points to, and the 'upload_present' contains the number of bytes available at this position */ - char *upload_fromhere; + char *upload_fromhere; + + bool do_more; /* this is set TRUE if the ->curl_do_more() function is + supposed to be called, after ->curl_do() */ }; /*