Initial fix to make the multi interface return control while waiting for
authorDaniel Stenberg <daniel@haxx.se>
Mon, 12 Aug 2002 09:43:20 +0000 (09:43 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 12 Aug 2002 09:43:20 +0000 (09:43 +0000)
the initial connect to "come through".

This should work fine for connect and for FTP-PASV connects. Needs massive
testing.

lib/connect.c
lib/connect.h
lib/ftp.c
lib/ftp.h
lib/multi.c
lib/url.c
lib/url.h
lib/urldata.h

index ba00b73..545062c 100644 (file)
@@ -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);
index f442523..5a6b8e6 100644 (file)
 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
index 50d10a0..dd10ad4 100644 (file)
--- 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;
 }
index baf9f40..0ed2d9a 100644 (file)
--- a/lib/ftp.h
+++ b/lib/ftp.h
@@ -1,6 +1,5 @@
 #ifndef __FTP_H
 #define __FTP_H
-
 /*****************************************************************************
  *                                  _   _ ____  _     
  *  Project                     ___| | | |  _ \| |    
  * $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
index 2e6a408..0830aa4 100644 (file)
@@ -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);
index 071ec07..ead5eb4 100644 (file)
--- 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")
index 630cd1c..c7c6503 100644 (file)
--- 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
index 9c7b18b..44a4a72 100644 (file)
@@ -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() */
 };
 
 /*