aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2002-08-12 09:43:20 +0000
committerDaniel Stenberg <daniel@haxx.se>2002-08-12 09:43:20 +0000
commitcb895ec3356822df72eb91171a1cc63ad1845d93 (patch)
tree77a33f251fb9ac6a963d2a31cf722aa37867adb7
parent2df4866cfa24920675520a5ccf72340e8e93b757 (diff)
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.
-rw-r--r--lib/connect.c91
-rw-r--r--lib/connect.h9
-rw-r--r--lib/ftp.c282
-rw-r--r--lib/ftp.h10
-rw-r--r--lib/multi.c111
-rw-r--r--lib/url.c85
-rw-r--r--lib/url.h4
-rw-r--r--lib/urldata.h11
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
@@ -338,6 +338,70 @@ int socketerror(int sockfd)
}
/*
+ * 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
* pointer with the connected socket.
@@ -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;
}
-/***********************************************************************
- *
- * ftp_perform()
+/*
+ * Curl_ftp_nextconnect()
*
- * 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) {
@@ -1993,6 +1900,128 @@ CURLcode ftp_perform(struct connectdata *conn)
/***********************************************************************
*
+ * 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()
*
* This function is registered as 'curl_do' function. It decodes the path
@@ -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);
-
- /* clean up here, success or error doesn't matter */
- if(ftp->file)
- free(ftp->file);
- if(ftp->dir)
- free(ftp->dir);
+ retcode = ftp_perform(conn, &connected);
- 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() */
};
/*