Karl M added the CURLOPT_CONNECT_ONLY and CURLINFO_LASTSOCKET options that
authorDaniel Stenberg <daniel@haxx.se>
Sat, 11 Feb 2006 22:35:16 +0000 (22:35 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 11 Feb 2006 22:35:16 +0000 (22:35 +0000)
an app can use to let libcurl only connect to a remote host and then extract
the socket from libcurl. libcurl will then not attempt to do any transfer at
all after the connect is done.

13 files changed:
CHANGES
RELEASE-NOTES
docs/libcurl/curl_easy_getinfo.3
docs/libcurl/curl_easy_setopt.3
include/curl/curl.h
lib/easy.c
lib/ftp.c
lib/getinfo.c
lib/http.c
lib/multi.c
lib/transfer.c
lib/url.c
lib/urldata.h

diff --git a/CHANGES b/CHANGES
index 88961d32d6849f5b4b63ff9c19722a7d626e7452..826b0210010dd73371f74f8f3b00d64770a35486 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,11 @@
                                   Changelog
 
 Daniel (11 February 2006)
+- Karl M added the CURLOPT_CONNECT_ONLY and CURLINFO_LASTSOCKET options that
+  an app can use to let libcurl only connect to a remote host and then extract
+  the socket from libcurl. libcurl will then not attempt to do any transfer at
+  all after the connect is done.
+
 - Kent Boortz improved the configure check for GnuTLS to properly set LIBS
   instead of LDFLAGS.
 
index d7babaab237a8735b87360f42a7870a30b41e56a..5cd89a4643a02ef125ad5c2415daa992508a6fac 100644 (file)
@@ -2,21 +2,22 @@ Curl and libcurl 7.15.2
 
  Public curl release number:               92
  Releases counted from the very beginning: 119
- Available command line options:           109
- Available curl_easy_setopt() options:     125
+ Available command line options:           111
+ Available curl_easy_setopt() options:     129
  Number of public functions in libcurl:    46
- Amount of public web site mirrors:        30
+ Amount of public web site mirrors:        31
  Number of known libcurl bindings:         32
  Number of contributors:                   474
 
 This release includes the following changes:
 
+ o CURLOPT_CONNECT_ONLY and CURLINFO_LASTSOCKET added
  o CURLOPT_LOCALPORT and CURLOPT_LOCALPORTRANGE (--local-port) added
  o Dropped support for the LPRT ftp command
- o Gopher is now officially abandoned as a protocol (lib)curl tries to support.
+ o Gopher is now officially abandoned as a protocol (lib)curl tries to support
  o curl_global_init() and curl_global_cleanup() are now using a refcount so
    that it is now legal to call them multiple times. See updated info for
-   details.
+   details
 
 This release includes the following bugfixes:
 
@@ -60,7 +61,7 @@ advice from friends like these:
 
  Dov Murik, Jean Jacques Drouin, Andres Garcia, Yang Tse, Gisle Vanem, Dan
  Fandrich, Alexander Lazic, Michael Jahn, Andrew Benham, Bryan Henderson,
- David Shaw, Jon Turner, Duane Cathey, Michal Marek, Philippe Vaucher,
- Kent Boortz
+ David Shaw, Jon Turner, Duane Cathey, Michal Marek, Philippe Vaucher, Kent
+ Boortz, Karl M
  
         Thanks! (and sorry if I forgot to mention someone)
index bc4f2063bf880e77f08680c1f915a4535ff0245f..2119ea21115fe42a3fa3fa308edc6b3d77e3181d 100644 (file)
@@ -141,6 +141,12 @@ cookies cURL knows (expired ones, too). Don't forget to
 cookies (cookies for the handle have not been enabled or simply none have been
 received) 'struct curl_slist *' will be set to point to NULL. (Added in
 7.14.1)
+.IP CURLINFO_LASTSOCKET
+Pass a pointer to a long to receive the last socket used by this curl
+session. If the socket is no longer valid, -1 is returned. When you finish
+working with the socket, you must call curl_easy_cleanup() as usual and let
+libcurl close the socket and cleanup other resources associated with the
+handle. (Added in 7.15.2)
 .SH TIMES
 .NF
 An overview of the six time values available from curl_easy_getinfo()
index 25e7e77073fe493c8ff4ec35602a831781381a85..8a152947ca2449202e1985aadc2ee6b82dc332f1 100644 (file)
@@ -1044,6 +1044,14 @@ Resolve to ipv4 addresses.
 .IP CURL_IPRESOLVE_V6
 Resolve to ipv6 addresses.
 .RE
+.SH CURLOPT_CONNECT_ONLY
+Pass a long. A non-zero parameter tells the library to perform any required
+proxy authentication and connection setup, but no data transfer.
+
+This option is useful with the \fICURLINFO_LASTSOCKET\fP option to
+\fIcurl_easy_getinfo(3)\fP. The library can set up the connection and then the
+application can obtain the most recently used socket for special data
+transfers. (Added in 7.15.2)
 .SH SSL and SECURITY OPTIONS
 .IP CURLOPT_SSLCERT
 Pass a pointer to a zero terminated string as parameter. The string should be
index 95c478a0708b6302ef40b66ecf3cd3afe8148ad4..1ad282df88479fc0eac092f063f29d4b8a3f8eff 100644 (file)
@@ -923,6 +923,10 @@ typedef enum {
   */
   CINIT(LOCALPORTRANGE, LONG, 140),
 
+  /* no transfer, set up connection and let application use the socket by
+     extracting it with CURLINFO_LASTSOCKET */
+  CINIT(CONNECT_ONLY, LONG, 141),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -1277,9 +1281,10 @@ typedef enum {
   CURLINFO_NUM_CONNECTS     = CURLINFO_LONG   + 26,
   CURLINFO_SSL_ENGINES      = CURLINFO_SLIST  + 27,
   CURLINFO_COOKIELIST       = CURLINFO_SLIST  + 28,
+  CURLINFO_LASTSOCKET       = CURLINFO_LONG   + 29,
   /* Fill in new entries below here! */
 
-  CURLINFO_LASTONE          = 28
+  CURLINFO_LASTONE          = 29
 } CURLINFO;
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
index 2de18037f4edc51fdd45182eb7714a80c444a5cb..d86b755fb25e20241b5c876486b69abe547a8835 100644 (file)
@@ -527,6 +527,8 @@ CURL *curl_easy_duphandle(CURL *incurl)
     memset(outcurl->state.connects, 0,
            sizeof(struct connectdata *)*outcurl->state.numconnects);
 
+    outcurl->state.lastconnect = -1;
+
     outcurl->progress.flags    = data->progress.flags;
     outcurl->progress.callback = data->progress.callback;
 
index ecb717696a13ec1d16b4c46618f115264dd386f5..02732f4528fa6af660cfcc38c7e775dea939f365 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -1689,7 +1689,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
     ftp_pasv_verbose(conn, conninfo, newhost, connectport);
 
 #ifndef CURL_DISABLE_HTTP
-  if(conn->bits.tunnel_proxy) {
+  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
     /* FIX: this MUST wait for a proper connect first if 'connected' is
      * FALSE */
 
@@ -2786,7 +2786,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn,
   ftp->response_time = 3600; /* set default response time-out */
 
 #ifndef CURL_DISABLE_HTTP
-  if (conn->bits.tunnel_proxy) {
+  if (conn->bits.tunnel_proxy && conn->bits.httpproxy) {
     /* BLOCKING */
     /* We want "seamless" FTP operations through HTTP proxy tunnel */
 
index 47828212b5c793721b7853c113e5a2655cb93852..2a4c3aac42e7af0f157ab2408a854c4869b34c4e 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -187,6 +187,14 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...)
   case CURLINFO_COOKIELIST:
     *param_slistp = Curl_cookie_list(data);
     break;
+  case CURLINFO_LASTSOCKET:
+    if((data->state.lastconnect != -1) &&
+       (data->state.connects[data->state.lastconnect] != NULL))
+      *param_longp = data->state.connects[data->state.lastconnect]->
+        sock[FIRSTSOCKET];
+    else
+      *param_longp = -1;
+    break;
   default:
     return CURLE_BAD_FUNCTION_ARGUMENT;
   }
index 39e2940ff9b2481f36df1df02345c94daff86096..e15054f57e8bf79bb6e9118a2bb5ac88ed9d62f4 100644 (file)
@@ -1361,7 +1361,7 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
    * after the connect has occured, can we start talking SSL
    */
 
-  if(conn->bits.tunnel_proxy) {
+  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
 
     /* either SSL over proxy, or explicitly asked for */
     result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
index a60e1f5a93f1f179592a9a42f83c830a2550e94a..a7d1988d6062c5e59012a036ccc80bf1577591ab 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -522,41 +522,50 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
         break;
 
       case CURLM_STATE_DO:
-        /* Perform the protocol's DO action */
-        easy->result = Curl_do(&easy->easy_conn, &dophase_done);
+        if(easy->easy_handle->set.connect_only) {
+          /* keep connection open for application to use the socket */
+          easy->easy_conn->bits.close = FALSE;
+          multistate(easy, CURLM_STATE_DONE);
+          easy->result = CURLE_OK;
+          result = CURLM_OK;
+        }
+        else {
+          /* Perform the protocol's DO action */
+          easy->result = Curl_do(&easy->easy_conn, &dophase_done);
 
-        if(CURLE_OK == easy->result) {
+          if(CURLE_OK == easy->result) {
 
-          if(!dophase_done) {
-            /* DO was not completed in one function call, we must continue
-               DOING... */
-            multistate(easy, CURLM_STATE_DOING);
-            result = CURLM_OK;
-          }
+            if(!dophase_done) {
+              /* DO was not completed in one function call, we must continue
+                 DOING... */
+              multistate(easy, CURLM_STATE_DOING);
+              result = CURLM_OK;
+            }
 
-          /* after DO, go PERFORM... or DO_MORE */
-          else if(easy->easy_conn->bits.do_more) {
-            /* we're supposed to do more, but we need to sit down, relax
-               and wait a little while first */
-            multistate(easy, CURLM_STATE_DO_MORE);
-            result = CURLM_OK;
+            /* after DO, go PERFORM... or DO_MORE */
+            else if(easy->easy_conn->bits.do_more) {
+              /* we're supposed to do more, but we need to sit down, relax
+                 and wait a little while first */
+              multistate(easy, 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) {
+                multistate(easy, CURLM_STATE_PERFORM);
+                result = CURLM_CALL_MULTI_PERFORM;
+              }
+            }
           }
           else {
-            /* we're done with the DO, now PERFORM */
-            easy->result = Curl_readwrite_init(easy->easy_conn);
-            if(CURLE_OK == easy->result) {
-              multistate(easy, CURLM_STATE_PERFORM);
-              result = CURLM_CALL_MULTI_PERFORM;
-            }
+            /* failure detected */
+            Curl_posttransfer(easy->easy_handle);
+            Curl_done(&easy->easy_conn, easy->result);
+            Curl_disconnect(easy->easy_conn); /* close the connection */
+            easy->easy_conn = NULL;           /* no more connection */
           }
         }
-        else {
-          /* failure detected */
-          Curl_posttransfer(easy->easy_handle);
-          Curl_done(&easy->easy_conn, easy->result);
-          Curl_disconnect(easy->easy_conn); /* close the connection */
-          easy->easy_conn = NULL;           /* no more connection */
-        }
         break;
 
       case CURLM_STATE_DOING:
index 50a8bae98ace945a649abfc9bcd3291563c1a4f8..2fbc2c6156e2e48cd4c90f4856c25c15e02e7f4a 100644 (file)
@@ -2159,6 +2159,12 @@ CURLcode Curl_perform(struct SessionHandle *data)
 
     if(res == CURLE_OK) {
       bool do_done;
+      if(data->set.connect_only) {
+        /* keep connection open for application to use the socket */
+        conn->bits.close = FALSE;
+        res = Curl_done(&conn, CURLE_OK);
+        break;
+      }
       res = Curl_do(&conn, &do_done);
 
       /* for non 3rd party transfer only */
index f17213f79839b6339d1e52f7b606678e990bce29..49e7c1cbf9e066e43bbc20a8f5aae7cbebb8146d 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -352,6 +352,9 @@ CURLcode Curl_open(struct SessionHandle **curl)
       memset(data->state.connects, 0,
              sizeof(struct connectdata *)*data->state.numconnects);
 
+    /* most recent connection is not yet defined */
+    data->state.lastconnect = -1;
+
     /*
      * libcurl 7.10 introduced SSL verification *by default*! This needs to be
      * switched off unless wanted.
@@ -432,6 +435,10 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
            are being removed! */
         for(i=newconnects; i< data->state.numconnects; i++)
           Curl_disconnect(data->state.connects[i]);
+
+        /* If the most recent connection is no longer valid, mark it invalid. */
+        if(data->state.lastconnect <= newconnects)
+          data->state.lastconnect = -1;
       }
       if(newconnects) {
         newptr= (struct connectdata **)
@@ -453,8 +460,9 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
         /* zero makes NO cache at all */
         if(data->state.connects)
           free(data->state.connects);
-        data->state.connects=NULL;
-        data->state.numconnects=0;
+        data->state.connects = NULL;
+        data->state.numconnects = 0;
+        data->state.lastconnect = -1;
       }
     }
     break;
@@ -1471,6 +1479,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     data->set.ignorecl = va_arg(param, long)?TRUE:FALSE;
     break;
 
+  case CURLOPT_CONNECT_ONLY:
+    /*
+     * No data transfer, set up connection and let application use the socket
+     */
+    data->set.connect_only = va_arg(param, long)?TRUE:FALSE;
+    break;
+
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_FAILED_INIT; /* correct this */
@@ -3811,10 +3826,14 @@ CURLcode Curl_done(struct connectdata **connp,
     if(!result && res2)
       result = res2;
   }
-  else
+  else {
+    /* remember the most recently used connection */
+    data->state.lastconnect = conn->connectindex;
+
     infof(data, "Connection #%ld to host %s left intact\n",
           conn->connectindex,
           conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
+  }
 
   return result;
 }
index 96494a7cee8efbb077e1f7d64dabddb4cc4ba9cc..a3802b7c3906af2d9b24703e88ae7fb54c008185 100644 (file)
@@ -856,6 +856,7 @@ struct UrlState {
      set, it holds an allocated connection. */
   struct connectdata **connects;
   long numconnects; /* size of the 'connects' array */
+  int lastconnect;  /* index of most recent connect or -1 if undefined */
 
   char *headerbuff; /* allocated buffer to store headers in */
   size_t headersize;   /* size of the allocation */
@@ -1083,6 +1084,7 @@ struct UserDefined {
   bool ignorecl;         /* ignore content length */
   bool ftp_skip_ip;      /* skip the IP address the FTP server passes on to
                             us */
+  bool connect_only;     /* make connection, let application use the socket */
 };
 
 /*