aboutsummaryrefslogtreecommitdiff
path: root/lib
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 /lib
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.
Diffstat (limited to 'lib')
-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() */
};
/*