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.
This commit is contained in:
Daniel Stenberg 2002-08-12 09:43:20 +00:00
parent 2df4866cfa
commit cb895ec335
8 changed files with 433 additions and 170 deletions

View File

@ -337,6 +337,70 @@ int socketerror(int sockfd)
return err; 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. * 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 * 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 */ Curl_addrinfo *remotehost, /* use one in here */
int port, /* connect to this */ int port, /* connect to this */
int *sockconn, /* the connected socket */ 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; struct SessionHandle *data = conn->data;
int rc; int rc;
@ -437,8 +502,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
case EAGAIN: case EAGAIN:
#endif #endif
case EINTR: case EINTR:
/* asynchronous connect, wait for connect or timeout */ /* 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); rc = waitconnect(sockfd, timeout_ms);
break; break;
case ECONNREFUSED: /* no one listening */ case ECONNREFUSED: /* no one listening */
@ -448,6 +516,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
break; break;
} }
} }
if(0 == rc) { if(0 == rc) {
/* we might be connected, if the socket says it is OK! Ask it! */ /* we might be connected, if the socket says it is OK! Ask it! */
int err; int err;
@ -455,11 +524,17 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
err = socketerror(sockfd); err = socketerror(sockfd);
if ((0 == err) || (EISCONN == err)) { if ((0 == err) || (EISCONN == err)) {
/* we are connected, awesome! */ /* we are connected, awesome! */
*connected = TRUE; /* this is truly a connect */
break; break;
} }
failf(data, "socket error: %d", err); failf(data, "socket error: %d", err);
/* we are _not_ connected, it was a false alert, continue please */ /* 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 */ /* connect failed or timed out */
sclose(sockfd); sclose(sockfd);
@ -542,8 +617,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
*/ */
case EAGAIN: case EAGAIN:
#endif #endif
/* asynchronous connect, wait for connect or timeout */ /* 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); rc = waitconnect(sockfd, timeout_ms);
break; break;
default: default:
@ -558,6 +636,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
int err = socketerror(sockfd); int err = socketerror(sockfd);
if ((0 == err) || (EISCONN == err)) { if ((0 == err) || (EISCONN == err)) {
/* we are connected, awesome! */ /* we are connected, awesome! */
*connected = TRUE; /* this is a true connect */
break; break;
} }
/* nope, not connected for real */ /* nope, not connected for real */
@ -565,6 +644,12 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
} }
if(0 != rc) { 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 */ /* get a new timeout for next attempt */
after = Curl_tvnow(); after = Curl_tvnow();
timeout_ms -= Curl_tvdiff(after, before); timeout_ms -= Curl_tvdiff(after, before);

View File

@ -26,10 +26,15 @@
int Curl_nonblock(int socket, /* operate on this */ int Curl_nonblock(int socket, /* operate on this */
int nonblock /* TRUE or FALSE */); int nonblock /* TRUE or FALSE */);
CURLcode Curl_is_connected(struct connectdata *conn,
int sockfd,
bool *connected);
CURLcode Curl_connecthost(struct connectdata *conn, CURLcode Curl_connecthost(struct connectdata *conn,
Curl_addrinfo *host, /* connect to this */ Curl_addrinfo *host, /* connect to this */
int port, /* connect to this port number */ int port, /* connect to this port number */
int *sockconn, /* not set if error is returned */ int *sockconn, /* not set if error is returned */
Curl_ipconnect **addr /* the one we used */ Curl_ipconnect **addr, /* the one we used */
); /* index we used */ bool *connected /* truly connected? */
);
#endif #endif

282
lib/ftp.c
View File

@ -387,8 +387,10 @@ int Curl_GetFTPResponse(char *buf,
return nread; /* total amount of bytes read */ 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) CURLcode Curl_ftp_connect(struct connectdata *conn)
{ {
/* this is FTP and no proxy */ /* this is FTP and no proxy */
@ -1321,7 +1323,8 @@ CURLcode ftp_use_port(struct connectdata *conn)
*/ */
static static
CURLcode ftp_use_pasv(struct connectdata *conn) CURLcode ftp_use_pasv(struct connectdata *conn,
bool *connected)
{ {
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
ssize_t nread; ssize_t nread;
@ -1473,7 +1476,14 @@ CURLcode ftp_use_pasv(struct connectdata *conn)
addr, addr,
connectport, connectport,
&conn->secondarysocket, &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) && if((CURLE_OK == result) &&
data->set.verbose) data->set.verbose)
@ -1494,127 +1504,24 @@ CURLcode ftp_use_pasv(struct connectdata *conn)
return CURLE_OK; return CURLE_OK;
} }
/*********************************************************************** /*
* Curl_ftp_nextconnect()
* *
* ftp_perform() * This function shall be called when the second FTP connection has been
* * established and is confirmed connected.
* This is the actual DO function for FTP. Get a file/directory according to
* the options previously setup.
*/ */
static CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
CURLcode ftp_perform(struct connectdata *conn)
{ {
/* this is FTP and no proxy */
ssize_t nread;
CURLcode result=CURLE_OK;
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
char *buf = data->state.buffer; /* this is our buffer */ 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() */ /* the ftp struct is already inited in ftp_connect() */
struct FTP *ftp = conn->proto.ftp; struct FTP *ftp = conn->proto.ftp;
long *bytecountp = ftp->bytecountp; 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) { if(data->set.upload) {
@ -1991,6 +1898,128 @@ CURLcode ftp_perform(struct connectdata *conn)
return CURLE_OK; 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() * Curl_ftp()
@ -2003,6 +2032,7 @@ CURLcode ftp_perform(struct connectdata *conn)
CURLcode Curl_ftp(struct connectdata *conn) CURLcode Curl_ftp(struct connectdata *conn)
{ {
CURLcode retcode; CURLcode retcode;
bool connected;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
struct FTP *ftp; struct FTP *ftp;
@ -2049,15 +2079,15 @@ CURLcode Curl_ftp(struct connectdata *conn)
else else
ftp->dir = NULL; ftp->dir = NULL;
retcode = ftp_perform(conn); retcode = ftp_perform(conn, &connected);
/* clean up here, success or error doesn't matter */ if(CURLE_OK == retcode) {
if(ftp->file) if(connected)
free(ftp->file); retcode = Curl_ftp_nextconnect(conn);
if(ftp->dir) else
free(ftp->dir); /* since we didn't connect now, we want do_more to get called */
conn->do_more = TRUE;
ftp->file = ftp->dir = NULL; /* zero */ }
return retcode; return retcode;
} }
@ -2128,6 +2158,12 @@ CURLcode Curl_ftp_disconnect(struct connectdata *conn)
free(ftp->entrypath); free(ftp->entrypath);
if(ftp->cache) if(ftp->cache)
free(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; return CURLE_OK;
} }

View File

@ -1,6 +1,5 @@
#ifndef __FTP_H #ifndef __FTP_H
#define __FTP_H #define __FTP_H
/***************************************************************************** /*****************************************************************************
* _ _ ____ _ * _ _ ____ _
* Project ___| | | | _ \| | * Project ___| | | | _ \| |
@ -24,22 +23,15 @@
* $Id$ * $Id$
*****************************************************************************/ *****************************************************************************/
/* MN 06/07/02 */
#ifndef CURL_DISABLE_FTP #ifndef CURL_DISABLE_FTP
CURLcode Curl_ftp(struct connectdata *conn); CURLcode Curl_ftp(struct connectdata *conn);
CURLcode Curl_ftp_done(struct connectdata *conn); CURLcode Curl_ftp_done(struct connectdata *conn);
CURLcode Curl_ftp_connect(struct connectdata *conn); CURLcode Curl_ftp_connect(struct connectdata *conn);
CURLcode Curl_ftp_disconnect(struct connectdata *conn); CURLcode Curl_ftp_disconnect(struct connectdata *conn);
CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...); CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...);
/* The kerberos stuff needs this: */
int Curl_GetFTPResponse(char *buf, struct connectdata *conn, int Curl_GetFTPResponse(char *buf, struct connectdata *conn,
int *ftpcode); int *ftpcode);
CURLcode Curl_ftp_nextconnect(struct connectdata *conn);
/* MN 06/07/02 */
#endif #endif
#endif #endif

View File

@ -29,6 +29,7 @@
#include "urldata.h" #include "urldata.h"
#include "transfer.h" #include "transfer.h"
#include "url.h" #include "url.h"
#include "connect.h"
/* The last #include file should be: */ /* The last #include file should be: */
#ifdef MALLOCDEBUG #ifdef MALLOCDEBUG
@ -43,11 +44,13 @@ struct Curl_message {
typedef enum { typedef enum {
CURLM_STATE_INIT, CURLM_STATE_INIT,
CURLM_STATE_CONNECT, CURLM_STATE_CONNECT, /* connect has been sent off */
CURLM_STATE_DO, CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */
CURLM_STATE_PERFORM, CURLM_STATE_DO, /* send off the request (part 1) */
CURLM_STATE_DONE, CURLM_STATE_DO_MORE, /* send off the request (part 2) */
CURLM_STATE_COMPLETED, 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 */ CURLM_STATE_LAST /* not a true state, never use this */
} CURLMstate; } CURLMstate;
@ -224,6 +227,32 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
switch(easy->state) { switch(easy->state) {
default: default:
break; 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: case CURLM_STATE_PERFORM:
/* This should have a set of file descriptors for us to set. */ /* This should have a set of file descriptors for us to set. */
/* after the transfer is done, go DONE */ /* after the transfer is done, go DONE */
@ -251,6 +280,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
bool done; bool done;
CURLMcode result=CURLM_OK; CURLMcode result=CURLM_OK;
struct Curl_message *msg = NULL; struct Curl_message *msg = NULL;
bool connected;
*running_handles = 0; /* bump this once for every living handle */ *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; easy=multi->easy.next;
while(easy) { while(easy) {
#ifdef MALLOCDEBUG
fprintf(stderr, "HANDLE %p: State: %x\n",
(char *)easy, easy->state);
#endif
switch(easy->state) { switch(easy->state) {
case CURLM_STATE_INIT: case CURLM_STATE_INIT:
/* init this transfer. */ /* 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. */ /* Connect. We get a connection identifier filled in. */
easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); 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) { if(CURLE_OK == easy->result) {
easy->state = CURLM_STATE_DO; easy->state = CURLM_STATE_WAITCONNECT;
result = CURLM_CALL_MULTI_PERFORM; result = CURLM_CALL_MULTI_PERFORM;
} }
break; 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: case CURLM_STATE_DO:
/* Do the fetch or put request */ /* Do the fetch or put request */
easy->result = Curl_do(&easy->easy_conn); easy->result = Curl_do(&easy->easy_conn);
/* after do, go PERFORM */
if(CURLE_OK == easy->result) { 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; easy->state = CURLM_STATE_PERFORM;
result = CURLM_CALL_MULTI_PERFORM; result = CURLM_CALL_MULTI_PERFORM;
} }
} }
break; break;
case CURLM_STATE_PERFORM: case CURLM_STATE_PERFORM:
/* read/write data if it is ready to do so */ /* read/write data if it is ready to do so */
easy->result = Curl_readwrite(easy->easy_conn, &done); easy->result = Curl_readwrite(easy->easy_conn, &done);

View File

@ -1307,7 +1307,8 @@ ConnectionStore(struct SessionHandle *data,
} }
static CURLcode ConnectPlease(struct connectdata *conn, static CURLcode ConnectPlease(struct connectdata *conn,
Curl_addrinfo *hostaddr) Curl_addrinfo *hostaddr,
bool *connected)
{ {
CURLcode result; CURLcode result;
Curl_ipconnect *addr; Curl_ipconnect *addr;
@ -1319,7 +1320,8 @@ static CURLcode ConnectPlease(struct connectdata *conn,
hostaddr, hostaddr,
conn->port, conn->port,
&conn->firstsocket, &conn->firstsocket,
&addr); &addr,
connected);
if(CURLE_OK == result) { if(CURLE_OK == result) {
/* All is cool, then we store the current information from the hostaddr /* 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 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; struct in_addr in;
(void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr)); (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr));
infof(data, "Connected to %s (%s) port %d\n", 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) #if defined(HAVE_INET_NTOA_R)
inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)), inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
#else #else
@ -1385,6 +1387,42 @@ static void verboseconnect(struct connectdata *conn,
#endif #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, static CURLcode CreateConnection(struct SessionHandle *data,
struct connectdata **in_connect) struct connectdata **in_connect)
{ {
@ -1780,6 +1818,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->remote_port = PORT_HTTP; conn->remote_port = PORT_HTTP;
conn->protocol |= PROT_HTTP; conn->protocol |= PROT_HTTP;
conn->curl_do = Curl_http; conn->curl_do = Curl_http;
conn->curl_do_more = NULL;
conn->curl_done = Curl_http_done; conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect; conn->curl_connect = Curl_http_connect;
#else #else
@ -1797,6 +1836,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL; conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL;
conn->curl_do = Curl_http; conn->curl_do = Curl_http;
conn->curl_do_more = NULL;
conn->curl_done = Curl_http_done; conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect; conn->curl_connect = Curl_http_connect;
@ -1819,6 +1859,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
} }
conn->protocol |= PROT_GOPHER; conn->protocol |= PROT_GOPHER;
conn->curl_do = Curl_http; conn->curl_do = Curl_http;
conn->curl_do_more = NULL;
conn->curl_done = Curl_http_done; conn->curl_done = Curl_http_done;
#else #else
failf(data, LIBCURL_NAME failf(data, LIBCURL_NAME
@ -1867,6 +1908,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
} }
else { else {
conn->curl_do = Curl_ftp; conn->curl_do = Curl_ftp;
conn->curl_do_more = Curl_ftp_nextconnect;
conn->curl_done = Curl_ftp_done; conn->curl_done = Curl_ftp_done;
conn->curl_connect = Curl_ftp_connect; conn->curl_connect = Curl_ftp_connect;
conn->curl_disconnect = Curl_ftp_disconnect; conn->curl_disconnect = Curl_ftp_disconnect;
@ -2441,29 +2483,16 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->headerbytecount = 0; conn->headerbytecount = 0;
if(-1 == conn->firstsocket) { if(-1 == conn->firstsocket) {
bool connected;
/* Connect only if not already connected! */ /* Connect only if not already connected! */
result = ConnectPlease(conn, hostaddr); result = ConnectPlease(conn, hostaddr, &connected);
Curl_pgrsTime(data, TIMER_CONNECT); /* connect done, good or bad */
if(connected)
result = Curl_protocol_connect(conn, hostaddr);
if(CURLE_OK != result) if(CURLE_OK != result)
return 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 { else {
Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
@ -2558,6 +2587,8 @@ CURLcode Curl_do(struct connectdata **connp)
struct connectdata *conn = *connp; struct connectdata *conn = *connp;
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
conn->do_more = FALSE; /* by default there's no curl_do_more() to use */
if(conn->curl_do) { if(conn->curl_do) {
/* generic protocol-specific function pointer set in curl_connect() */ /* generic protocol-specific function pointer set in curl_connect() */
result = conn->curl_do(conn); result = conn->curl_do(conn);
@ -2587,6 +2618,16 @@ CURLcode Curl_do(struct connectdata **connp)
return result; 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: * local variables:
* eval: (load-file "../curl-mode.el") * eval: (load-file "../curl-mode.el")

View File

@ -32,7 +32,9 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...);
CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */ CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */
CURLcode Curl_connect(struct SessionHandle *, struct connectdata **); CURLcode Curl_connect(struct SessionHandle *, struct connectdata **);
CURLcode Curl_do(struct connectdata **); CURLcode Curl_do(struct connectdata **);
CURLcode Curl_do_more(struct connectdata *);
CURLcode Curl_done(struct connectdata *); CURLcode Curl_done(struct connectdata *);
CURLcode Curl_disconnect(struct connectdata *); CURLcode Curl_disconnect(struct connectdata *);
CURLcode Curl_protocol_connect(struct connectdata *conn,
Curl_addrinfo *hostaddr);
#endif #endif

View File

@ -328,6 +328,12 @@ struct connectdata {
CURLcode (*curl_do)(struct connectdata *connect); CURLcode (*curl_do)(struct connectdata *connect);
CURLcode (*curl_done)(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 /* 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. * 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, 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 and the 'upload_present' contains the number of bytes available at this
position */ 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() */
}; };
/* /*