Index: CHANGES
===================================================================
RCS file: /cvsroot/curl/curl/CHANGES,v
retrieving revision 1.735
diff -u -r1.735 CHANGES
--- CHANGES	9 Aug 2005 21:59:31 -0000	1.735
+++ CHANGES	10 Aug 2005 22:04:24 -0000
@@ -7,6 +7,21 @@
                                   Changelog
 
 
+Daniel
+- HTTP CONNECT cleanup. The CONNECT request is now issued exactly like other
+  requests and the CONNECT response is read and handled exactly like other
+  request responses. This not only simplifies the code, but also improves the
+  CONNECT code to better deal with for example proxies that close the
+  connection in a 407 response. This caused libcurl to fail previously, like
+  when using 'anyauth' with a proxy when NTLM is selected/attempted.
+
+  This should also make the multi interface a lot better during CONNECT. The
+  biggest impact is for non-HTTP protocols that "tunnel" through a HTTP proxy
+  with CONNECT as I had to rewrite that handling quite a bit.
+
+  10 test cases were modified accordingly, mostly due to the CONNECT request
+  now being sent as HTTP 1.1 and a modified request header order.
+
 Daniel (9 August 2005)
 - Christopher R. Palmer fixed the offsets used for date parsings when the time
   zone name of a daylight savings time was used. For example, PDT vs PDS. This
Index: lib/ftp.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/ftp.c,v
retrieving revision 1.327
diff -u -r1.327 ftp.c
--- lib/ftp.c	21 Jul 2005 22:18:35 -0000	1.327
+++ lib/ftp.c	10 Aug 2005 22:04:26 -0000
@@ -2720,6 +2720,19 @@
 
   *done = FALSE; /* default to not done yet */
 
+#ifndef CURL_DISABLE_HTTP
+  if (conn->bits.tunnel_proxy) {
+    /* BLOCKING */
+    /* We want "seamless" FTP operations through HTTP proxy tunnel */
+    result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
+                               conn->host.name, conn->remote_port);
+    if(CURLE_OK != result)
+      return result;
+
+    /* I guess we must free the conn->proto.http pointer here */
+  }
+#endif   /* CURL_DISABLE_HTTP */
+
   ftp = (struct FTP *)calloc(sizeof(struct FTP), 1);
   if(!ftp)
     return CURLE_OUT_OF_MEMORY;
@@ -2740,17 +2753,6 @@
 
   ftp->response_time = 3600; /* set default response time-out */
 
-#ifndef CURL_DISABLE_HTTP
-  if (conn->bits.tunnel_proxy) {
-    /* BLOCKING */
-    /* We want "seamless" FTP operations through HTTP proxy tunnel */
-    result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
-                               conn->host.name, conn->remote_port);
-    if(CURLE_OK != result)
-      return result;
-  }
-#endif   /* CURL_DISABLE_HTTP */
-
   if(conn->protocol & PROT_FTPS) {
     /* BLOCKING */
     /* FTPS is simply ftp with SSL for the control channel */
Index: lib/http.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/http.c,v
retrieving revision 1.274
diff -u -r1.274 http.c
--- lib/http.c	5 Jul 2005 22:07:34 -0000	1.274
+++ lib/http.c	10 Aug 2005 22:04:26 -0000
@@ -219,7 +219,7 @@
  */
 static CURLcode perhapsrewind(struct connectdata *conn)
 {
-  struct HTTP *http = conn->proto.http;
+  struct HTTP *http = conn->http;
   struct SessionHandle *data = conn->data;
   curl_off_t bytessent;
   curl_off_t expectsend = -1; /* default is unknown */
@@ -760,7 +760,7 @@
                            void *userp)
 {
   struct connectdata *conn = (struct connectdata *)userp;
-  struct HTTP *http = conn->proto.http;
+  struct HTTP *http = conn->http;
   size_t fullsize = size * nitems;
 
   if(0 == http->postsize)
@@ -847,7 +847,7 @@
   CURLcode res;
   char *ptr;
   size_t size;
-  struct HTTP *http = conn->proto.http;
+  struct HTTP *http = conn->http;
   size_t sendsize;
   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
 
@@ -1054,14 +1054,8 @@
 }
 
 /*
- * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
- * function will issue the necessary commands to get a seamless tunnel through
- * this proxy. After that, the socket can be used just as a normal socket.
+ * Curl_proxyCONNECT() sends a CONNECT request.
  *
- * This badly needs to be rewritten. CONNECT should be sent and dealt with
- * like any ordinary HTTP request, and not specially crafted like this. This
- * function only remains here like this for now since the rewrite is a bit too
- * much work to do at the moment.
  */
 
 CURLcode Curl_proxyCONNECT(struct connectdata *conn,
@@ -1069,264 +1063,51 @@
                            char *hostname,
                            int remote_port)
 {
-  int subversion=0;
+  CURLcode res;
   struct SessionHandle *data=conn->data;
-  struct Curl_transfer_keeper *k = &conn->keep;
-  CURLcode result;
-  int res;
-  size_t nread;   /* total size read */
-  int perline; /* count bytes per line */
-  int keepon=TRUE;
-  ssize_t gotbytes;
-  char *ptr;
-  long timeout =
-    data->set.timeout?data->set.timeout:3600; /* in seconds */
-  char *line_start;
-  char *host_port;
-  curl_socket_t tunnelsocket = conn->sock[sockindex];
-  send_buffer *req_buffer;
-  curl_off_t cl=0;
-
-#define SELECT_OK      0
-#define SELECT_ERROR   1
-#define SELECT_TIMEOUT 2
-  int error = SELECT_OK;
-
-  infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
-
-  do {
-    if(conn->newurl) {
-      /* This only happens if we've looped here due to authentication reasons,
-         and we don't really use the newly cloned URL here then. Just free()
-         it. */
-      free(conn->newurl);
-      conn->newurl = NULL;
-    }
-
-    /* initialize a dynamic send-buffer */
-    req_buffer = add_buffer_init();
-
-    if(!req_buffer)
-      return CURLE_OUT_OF_MEMORY;
-
-    host_port = aprintf("%s:%d", hostname, remote_port);
-    if(!host_port)
-      return CURLE_OUT_OF_MEMORY;
 
-    /* Setup the proxy-authorization header, if any */
-    result = Curl_http_output_auth(conn, (char *)"CONNECT", host_port, TRUE);
+  char buffer[256]; /* FIX TODO MUST-DO: this can't be fixed-size, get a
+                       suitable size by checking string lengths */
 
-    if(CURLE_OK == result) {
-      char *host=(char *)"";
-      const char *proxyconn="";
-      char *ptr;
-
-      ptr = checkheaders(data, "Host:");
-      if(!ptr) {
-        host = aprintf("Host: %s\r\n", host_port);
-        if(!host)
-          result = CURLE_OUT_OF_MEMORY;
-      }
-      ptr = checkheaders(data, "Proxy-Connection:");
-      if(!ptr)
-        proxyconn = "Proxy-Connection: Keep-Alive\r\n";
-
-      if(CURLE_OK == result) {
-        /* Send the connect request to the proxy */
-        /* BLOCKING */
-        result =
-          add_bufferf(req_buffer,
-                      "CONNECT %s:%d HTTP/1.0\r\n"
-                      "%s"  /* Host: */
-                      "%s"  /* Proxy-Authorization */
-                      "%s"  /* User-Agent */
-                      "%s", /* Proxy-Connection */
-                      hostname, remote_port,
-                      host,
-                      conn->allocptr.proxyuserpwd?
-                      conn->allocptr.proxyuserpwd:"",
-                      data->set.useragent?conn->allocptr.uagent:"",
-                      proxyconn);
-
-        if(CURLE_OK == result)
-          result = add_custom_headers(conn, req_buffer);
-
-        if(host && *host)
-          free(host);
-
-        if(CURLE_OK == result)
-          /* CRLF terminate the request */
-          result = add_bufferf(req_buffer, "\r\n");
-
-        if(CURLE_OK == result)
-          /* Now send off the request */
-          result = add_buffer_send(req_buffer, conn,
-                                   &data->info.request_size);
-      }
-      if(result)
-        failf(data, "Failed sending CONNECT to proxy");
-    }
-    free(host_port);
-    if(result)
-      return result;
+  /* if these were all gathered in a single struct this would be less
+     cluttered */
 
-    ptr=data->state.buffer;
-    line_start = ptr;
+  CURLcode (*curl_do)(struct connectdata *, bool *done);
+  CURLcode (*curl_done)(struct connectdata *, CURLcode);
+  CURLcode (*curl_do_more)(struct connectdata *);
+  CURLcode (*curl_connect)(struct connectdata *, bool *done);
 
-    nread=0;
-    perline=0;
-    keepon=TRUE;
-
-    while((nread<BUFSIZE) && (keepon && !error)) {
-
-      /* if timeout is requested, find out how much remaining time we have */
-      long check = timeout - /* timeout time */
-        Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
-      if(check <=0 ) {
-        failf(data, "Proxy CONNECT aborted due to timeout");
-        error = SELECT_TIMEOUT; /* already too little time */
-        break;
-      }
-
-      /* timeout each second and check the timeout */
-      switch (Curl_select(tunnelsocket, CURL_SOCKET_BAD, 1000)) {
-      case -1: /* select() error, stop reading */
-        error = SELECT_ERROR;
-        failf(data, "Proxy CONNECT aborted due to select() error");
-        break;
-      case 0: /* timeout */
-        break;
-      default:
-        res = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, &gotbytes);
-        if(res< 0)
-          /* EWOULDBLOCK */
-          continue; /* go loop yourself */
-        else if(res)
-          keepon = FALSE;
-        else if(gotbytes <= 0) {
-          keepon = FALSE;
-          error = SELECT_ERROR;
-          failf(data, "Proxy CONNECT aborted");
-        }
-        else {
-          /*
-           * We got a whole chunk of data, which can be anything from one byte
-           * to a set of lines and possibly just a piece of the last line.
-           */
-          int i;
-
-          nread += gotbytes;
-
-          if(keepon > TRUE) {
-            /* This means we are currently ignoring a response-body, so we
-               simply count down our counter and make sure to break out of the
-               loop when we're done! */
-            cl -= gotbytes;
-            if(cl<=0) {
-              keepon = FALSE;
-              break;
-            }
-          }
-          else
-          for(i = 0; i < gotbytes; ptr++, i++) {
-            perline++; /* amount of bytes in this line so far */
-            if(*ptr=='\n') {
-              char letter;
-              int writetype;
-
-              /* output debug if that is requested */
-              if(data->set.verbose)
-                Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline,
-                           conn);
-
-              /* send the header to the callback */
-              writetype = CLIENTWRITE_HEADER;
-              if(data->set.include_header)
-                writetype |= CLIENTWRITE_BODY;
-
-              result = Curl_client_write(data, writetype, line_start, perline);
-              if(result)
-                return result;
-
-              /* Newlines are CRLF, so the CR is ignored as the line isn't
-                 really terminated until the LF comes. Treat a following CR
-                 as end-of-headers as well.*/
-
-              if(('\r' == line_start[0]) ||
-                 ('\n' == line_start[0])) {
-                /* end of response-headers from the proxy */
-                if(cl && (407 == k->httpcode) && !data->state.authproblem) {
-                  /* If we get a 407 response code with content length when we
-                   * have no auth problem, we must ignore the whole
-                   * response-body */
-                  keepon = 2;
-                  infof(data, "Ignore %" FORMAT_OFF_T
-                        " bytes of response-body\n", cl);
-                  cl -= (gotbytes - i);/* remove the remaining chunk of what
-                                          we already read */
-                  if(cl<=0)
-                    /* if the whole thing was already read, we are done! */
-                    keepon=FALSE;
-                }
-                else
-                  keepon = FALSE;
-                break; /* breaks out of for-loop, not switch() */
-              }
-
-              /* keep a backup of the position we are about to blank */
-              letter = line_start[perline];
-              line_start[perline]=0; /* zero terminate the buffer */
-              if((checkprefix("WWW-Authenticate:", line_start) &&
-                  (401 == k->httpcode)) ||
-                 (checkprefix("Proxy-authenticate:", line_start) &&
-                  (407 == k->httpcode))) {
-                result = Curl_http_input_auth(conn, k->httpcode, line_start);
-                if(result)
-                  return result;
-              }
-              else if(checkprefix("Content-Length:", line_start)) {
-                cl = curlx_strtoofft(line_start + strlen("Content-Length:"),
-                                     NULL, 10);
-              }
-              else if(2 == sscanf(line_start, "HTTP/1.%d %d",
-                                  &subversion,
-                                  &k->httpcode)) {
-                /* store the HTTP code from the proxy */
-                data->info.httpproxycode = k->httpcode;
-              }
-              /* put back the letter we blanked out before */
-              line_start[perline]= letter;
-
-              perline=0; /* line starts over here */
-              line_start = ptr+1; /* this skips the zero byte we wrote */
-            }
-          }
-        }
-        break;
-      } /* switch */
-    } /* while there's buffer left and loop is requested */
+  /* Back these up and restore after Curl_perform().
+   *
+   * The only reason setting these here matters, is because we force
+   * Curl_perform() to re-use the same connectdata as we're already using!
+   */
 
-    if(error)
-      return CURLE_RECV_ERROR;
+  snprintf(buffer, sizeof(buffer), "http://%s:%d/", hostname, remote_port);
+  data->change.url = buffer;
 
-    if(data->info.httpproxycode != 200)
-      /* Deal with the possibly already received authenticate
-         headers. 'newurl' is set to a new URL if we must loop. */
-      Curl_http_auth_act(conn);
-
-  } while(conn->newurl);
-
-  if(200 != k->httpcode) {
-    failf(data, "Received HTTP code %d from proxy after CONNECT",
-          k->httpcode);
-    return CURLE_RECV_ERROR;
-  }
-
-  /* If a proxy-authorization header was used for the proxy, then we should
-     make sure that it isn't accidentally used for the document request
-     after we've connected. So let's free and clear it here. */
-  Curl_safefree(conn->allocptr.proxyuserpwd);
-  conn->allocptr.proxyuserpwd = NULL;
+  curl_do = conn->curl_do;
+  curl_do_more = conn->curl_do_more;
+  curl_done = conn->curl_done;
+  curl_connect = conn->curl_connect;
+
+  conn->curl_do = Curl_http;
+  conn->curl_do_more = NULL;
+  conn->curl_done = Curl_http_done;
+  conn->curl_connect = Curl_http_connect;
+
+  infof(data, "proxyCONNECT now\n");
+
+  data->state.reuseconn = conn; /* force reusage of this struct, bypass the
+                                   normal checks. The ConnectionExists()
+                                   function itself clears this after use. */
+
+  res = Curl_perform(data);
+
+  conn->curl_do = curl_do;
+  conn->curl_do_more = curl_do_more;
+  conn->curl_done = curl_done;
+  conn->curl_connect = curl_connect;
 
   data->state.authproxy.done = TRUE;
 
@@ -1341,33 +1122,9 @@
 CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
 {
   struct SessionHandle *data;
-  CURLcode result;
 
   data=conn->data;
 
-  /* If we are not using a proxy and we want a secure connection, perform SSL
-   * initialization & connection now.  If using a proxy with https, then we
-   * must tell the proxy to CONNECT to the host we want to talk to.  Only
-   * after the connect has occured, can we start talking SSL
-   */
-
-  if(conn->bits.tunnel_proxy) {
-
-    /* either SSL over proxy, or explicitly asked for */
-    result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
-                               conn->host.name,
-                               conn->remote_port);
-    if(CURLE_OK != result)
-      return result;
-  }
-
-  if(conn->protocol & PROT_HTTPS) {
-    /* perform SSL initialization for this socket */
-    result = Curl_ssl_connect(conn, FIRSTSOCKET);
-    if(result)
-      return result;
-  }
-
   if(!data->state.this_is_a_follow) {
     /* this is not a followed location, get the original host name */
     if (data->state.first_host)
@@ -1394,7 +1151,7 @@
   struct HTTP *http;
 
   data=conn->data;
-  http=conn->proto.http;
+  http=conn->http;
 
   /* set the proper values (possibly modified on POST) */
   conn->fread = data->set.fread; /* restore */
@@ -1521,49 +1278,77 @@
      the rest of the request in the PERFORM phase. */
   *done = TRUE;
 
-  if(!conn->proto.http) {
+  if(!conn->http) {
     /* Only allocate this struct if we don't already have it! */
 
     http = (struct HTTP *)malloc(sizeof(struct HTTP));
     if(!http)
       return CURLE_OUT_OF_MEMORY;
     memset(http, 0, sizeof(struct HTTP));
-    conn->proto.http = http;
+    conn->http = http;
   }
   else
-    http = conn->proto.http;
+    http = conn->http;
 
   /* We default to persistant connections */
   conn->bits.close = FALSE;
 
-  if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
-       data->set.upload) {
-    httpreq = HTTPREQ_PUT;
+  if((conn->protocol & PROT_HTTPS) &&
+     (!conn->bits.tunnel_proxy || (conn->tunnelstate == CONNECT_DONE))) {
+    /* perform SSL initialization for this socket */
+    result = Curl_ssl_connect(conn, FIRSTSOCKET);
+    if(result)
+      return result;
+    conn->tunnelstate = CONNECT_SECURE;
   }
 
-  /* Now set the 'request' pointer to the proper request string */
-  if(data->set.customrequest)
-    request = data->set.customrequest;
+  if(conn->bits.tunnel_proxy && (conn->tunnelstate != CONNECT_DONE)) {
+    /* We CONNECT to a proxy so we must do this first before using any
+       other request. tunnelstate says we are not yet CONNECTed fine. */
+    httpreq = HTTPREQ_CONNECT;
+    request = (char *)"CONNECT";
+
+    conn->allocptr.connect = aprintf("%s%s%s:%d",
+                                     conn->bits.ipv6_ip?"[":"",
+                                     host,
+                                     conn->bits.ipv6_ip?"]":"",
+                                     conn->remote_port);
+    ppath = conn->allocptr.connect;
+    conn->tunnelstate = CONNECT_PROGRESS;
+  }
   else {
-    if(conn->bits.no_body)
-      request = (char *)"HEAD";
+
+    if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
+         data->set.upload) {
+      httpreq = HTTPREQ_PUT;
+    }
+
+    /* Now set the 'request' pointer to the proper request string */
+    if(data->set.customrequest) {
+      httpreq = HTTPREQ_CUSTOM;
+      request = data->set.customrequest;
+    }
     else {
-      curlassert((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
-      switch(httpreq) {
-      case HTTPREQ_POST:
-      case HTTPREQ_POST_FORM:
-        request = (char *)"POST";
-        break;
-      case HTTPREQ_PUT:
-        request = (char *)"PUT";
-        break;
-      default: /* this should never happen */
-      case HTTPREQ_GET:
-        request = (char *)"GET";
-        break;
-      case HTTPREQ_HEAD:
+      if(conn->bits.no_body)
+        request = (char *)"HEAD";
+      else {
+        curlassert((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
+        switch(httpreq) {
+        case HTTPREQ_POST:
+        case HTTPREQ_POST_FORM:
+          request = (char *)"POST";
+          break;
+        case HTTPREQ_PUT:
+          request = (char *)"PUT";
+          break;
+        default: /* this should never happen */
+        case HTTPREQ_GET:
+          request = (char *)"GET";
+          break;
+        case HTTPREQ_HEAD:
         request = (char *)"HEAD";
         break;
+        }
       }
     }
   }
@@ -1578,7 +1363,8 @@
   }
 
   /* setup the authentication headers */
-  result = Curl_http_output_auth(conn, request, ppath, FALSE);
+  result = Curl_http_output_auth(conn, request, ppath,
+                                 conn->allocptr.connect?TRUE:FALSE);
   if(result)
     return result;
 
@@ -1735,6 +1521,15 @@
     }
     ppath = data->change.url;
   }
+
+  http->p_pragma =
+    (!checkheaders(data, "Pragma:") &&
+     (conn->bits.httpproxy && !conn->bits.tunnel_proxy) )?
+    "Pragma: no-cache\r\n":NULL;
+
+  if(!checkheaders(data, "Accept:"))
+    http->p_accept = "Accept: */*\r\n";
+
   if(HTTPREQ_POST_FORM == httpreq) {
     /* we must build the whole darned post sequence first, so that we have
        a size of the whole shebang before we start to send it */
@@ -1747,15 +1542,6 @@
      }
   }
 
-
-  http->p_pragma =
-    (!checkheaders(data, "Pragma:") &&
-     (conn->bits.httpproxy && !conn->bits.tunnel_proxy) )?
-    "Pragma: no-cache\r\n":NULL;
-
-  if(!checkheaders(data, "Accept:"))
-    http->p_accept = "Accept: */*\r\n";
-
   if(( (HTTPREQ_POST == httpreq) ||
        (HTTPREQ_POST_FORM == httpreq) ||
        (HTTPREQ_PUT == httpreq) ) &&
@@ -1885,24 +1671,30 @@
                 request,
                 ppath,
                 httpstring,
-                conn->allocptr.proxyuserpwd?
+                (conn->allocptr.proxyuserpwd &&
+                 (conn->tunnelstate != CONNECT_DONE))?
                 conn->allocptr.proxyuserpwd:"",
-                conn->allocptr.userpwd?conn->allocptr.userpwd:"",
+                (conn->allocptr.userpwd && !conn->allocptr.connect)?conn->allocptr.userpwd:"",
                 (conn->bits.use_range && conn->allocptr.rangeline)?
                 conn->allocptr.rangeline:"",
                 (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)?
                 conn->allocptr.uagent:"",
                 (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
                 http->p_pragma?http->p_pragma:"",
-                http->p_accept?http->p_accept:"",
+                (http->p_accept && !conn->allocptr.connect)? http->p_accept:"",
                 (data->set.encoding && *data->set.encoding && conn->allocptr.accept_encoding)?
                 conn->allocptr.accept_encoding:"",
                 (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> */,
-                (conn->bits.httpproxy && !conn->bits.tunnel_proxy)?
+                (conn->bits.httpproxy && (!conn->bits.tunnel_proxy || conn->allocptr.connect))?
                   "Proxy-Connection: Keep-Alive\r\n":"",
                 te
                 );
 
+    if(conn->allocptr.connect) {
+      free(conn->allocptr.connect);
+      conn->allocptr.connect = NULL;
+    }
+
     if(result)
       return result;
 
Index: lib/http_chunks.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/http_chunks.c,v
retrieving revision 1.28
diff -u -r1.28 http_chunks.c
--- lib/http_chunks.c	12 Jul 2005 18:15:34 -0000	1.28
+++ lib/http_chunks.c	10 Aug 2005 22:04:26 -0000
@@ -83,7 +83,7 @@
 
 void Curl_httpchunk_init(struct connectdata *conn)
 {
-  struct Curl_chunker *chunk = &conn->proto.http->chunk;
+  struct Curl_chunker *chunk = &conn->http->chunk;
   chunk->hexindex=0; /* start at 0 */
   chunk->dataleft=0; /* no data left yet! */
   chunk->state = CHUNK_HEX; /* we get hex first! */
@@ -103,7 +103,7 @@
                               ssize_t *wrotep)
 {
   CURLcode result=CURLE_OK;
-  struct Curl_chunker *ch = &conn->proto.http->chunk;
+  struct Curl_chunker *ch = &conn->http->chunk;
   struct Curl_transfer_keeper *k = &conn->keep;
   size_t piece;
   size_t length = (size_t)datalen;
Index: lib/transfer.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/transfer.c,v
retrieving revision 1.281
diff -u -r1.281 transfer.c
--- lib/transfer.c	12 Jul 2005 18:15:34 -0000	1.281
+++ lib/transfer.c	10 Aug 2005 22:04:27 -0000
@@ -563,6 +563,58 @@
                  * If we requested a "no body", this is a good time to get
                  * out and return home.
                  */
+
+                if(conn->tunnelstate == CONNECT_PROGRESS) {
+                  /*
+                   * This is a request setting up a CONNECT tunnel. If no
+                   * Content-Length or chunked transfer encoding have been set
+                   * in the headers, then return now.
+                   */
+
+                  infof(data, "end of CONNECT headers\n");
+
+                  if(!data->state.authproxy.done)
+                    /* the auth code itself fixes the newurl magic to make
+                       this continue */
+                    ;
+                  else if(k->httpcode == 200) {
+                    /* We set 'newurl' to trigger a continuation if this is
+                       HTTP, since it if is another protocol this is a CONNECT
+                       only to initiate a "tunnel" for the other protocol. */
+                    if(conn->protocol & PROT_HTTP) {
+                      conn->newurl = strdup(data->change.url);
+                      if(!conn->newurl)
+                        return CURLE_OUT_OF_MEMORY;
+                    }
+                    conn->tunnelstate = CONNECT_DONE; /* CONNECT done! */
+
+                    if(!conn->bits.proxyclose && conn->bits.close) {
+                      /*
+                       * This is a work-around: if we get a HTTP/1.0 response
+                       * to a CONNECT request it is assumed to be kept alive
+                       * as long as it isn't *explicitly* told that it is
+                       * closed. This is based on Apache2 behaviour.
+                       */
+                      conn->bits.close = FALSE;
+                    }
+                  }
+                  else {
+                    failf(data,
+                          "Received HTTP code %d from proxy after CONNECT",
+                          k->httpcode);
+                    return CURLE_RECV_ERROR;
+                  }
+                  if((conn->size > 0) || conn->bits.chunk) {
+                    /* if there is a response-body coming, we must read and
+                       ignore all of it */
+                    k->ignorebody = TRUE;
+                    infof(data, "Ignoring the CONNECT response-body\n");
+                  }
+                  else
+                    /* we are done with this response */
+                    stop_reading = TRUE;
+                }
+
                 if(conn->bits.no_body)
                   stop_reading = TRUE;
                 else {
@@ -647,7 +699,11 @@
               }
 
               if (nc) {
-                data->info.httpcode = k->httpcode;
+                if(conn->tunnelstate == CONNECT_PROGRESS)
+                  /* store the HTTP code from the proxy separately */
+                  data->info.httpproxycode = k->httpcode;
+                else
+                  data->info.httpcode = k->httpcode;
                 data->info.httpversion = k->httpversion;
 
                 /*
@@ -796,6 +852,12 @@
                * close down after this transfer.
                */
               conn->bits.close = TRUE; /* close when done */
+              /*
+               * It is not always enough to have the "close" state just binary.
+               * since it seems some CONNECT replies will be kept alive even
+               * when returning HTTP/1.0 without keep-alive header info.
+               */
+              conn->bits.proxyclose = TRUE;
               infof(data, "HTTP/1.1 proxy connection set close!\n");
             }
             else if((k->httpversion == 10) &&
@@ -1224,7 +1286,7 @@
             int fillcount;
 
             if(k->wait100_after_headers &&
-               (conn->proto.http->sending == HTTPSEND_BODY)) {
+               (conn->http->sending == HTTPSEND_BODY)) {
               /* If this call is to send body data, we must take some action:
                  We have sent off the full HTTP 1.1 request, and we shall now
                  go into the Expect: 100 state and await such a header */
@@ -1408,13 +1470,13 @@
     }
     else if(!(conn->bits.no_body) &&
             conn->bits.chunk &&
-            (conn->proto.http->chunk.state != CHUNK_STOP)) {
+            (conn->http->chunk.state != CHUNK_STOP)) {
       /*
        * In chunked mode, return an error if the connection is closed prior to
        * the empty (terminiating) chunk is read.
        *
        * The condition above used to check for
-       * conn->proto.http->chunk.datasize != 0 which is true after reading
+       * conn->http->chunk.datasize != 0 which is true after reading
        * *any* chunk, not just the empty chunk.
        *
        */
@@ -1487,7 +1549,7 @@
          state info where we wait for the 100-return code
       */
       if (data->set.expect100header &&
-          (conn->proto.http->sending == HTTPSEND_BODY)) {
+          (conn->http->sending == HTTPSEND_BODY)) {
         /* wait with write until we either got 100-continue or a timeout */
         k->write_after_100_header = TRUE;
         k->start100 = k->start;
Index: lib/url.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.c,v
retrieving revision 1.473
diff -u -r1.473 url.c
--- lib/url.c	7 Aug 2005 22:59:06 -0000	1.473
+++ lib/url.c	10 Aug 2005 22:04:28 -0000
@@ -1499,6 +1499,7 @@
   }
 
   Curl_safefree(conn->proto.generic);
+  Curl_safefree(conn->http);
   Curl_safefree(conn->newurl);
   Curl_safefree(conn->pathbuffer); /* the URL path buffer */
 
@@ -1534,6 +1535,7 @@
   Curl_safefree(conn->allocptr.ref);
   Curl_safefree(conn->allocptr.host);
   Curl_safefree(conn->allocptr.cookiehost);
+  Curl_safefree(conn->allocptr.connect);
   Curl_safefree(conn->ip_addr_str);
   Curl_safefree(conn->trailer);
 
@@ -1574,6 +1576,8 @@
  * Given one filled in connection struct (named needle), this function should
  * detect if there already is one that have all the significant details
  * exactly the same and thus should be used instead.
+ *
+ * Returns TRUE if a match was found and returned.
  */
 static bool
 ConnectionExists(struct SessionHandle *data,
@@ -1583,6 +1587,11 @@
   long i;
   struct connectdata *check;
 
+  if(data->state.reuseconn) {
+    *usethis = data->state.reuseconn;
+    return TRUE;
+  }
+
   for(i=0; i< data->state.numconnects; i++) {
     bool match = FALSE;
     /*
@@ -1598,12 +1607,13 @@
       /* don't do mixed SSL and non-SSL connections */
       continue;
 
-    if(!needle->bits.httpproxy || needle->protocol&PROT_SSL) {
-      /* The requested connection does not use a HTTP proxy or it
-         uses SSL. */
+    if(!needle->bits.httpproxy || needle->bits.tunnel_proxy) {
+      /* The requested connection does not use a HTTP proxy at all, or it
+         "tunnels" through it (which it also does when using SSL-related
+         servers through a proxy) */
 
-      if(!(needle->protocol&PROT_SSL) && check->bits.httpproxy)
-        /* we don't do SSL but the cached connection has a proxy,
+      if(!(needle->bits.tunnel_proxy) && check->bits.httpproxy)
+        /* we don't do proxytunneling but the cached connection has a proxy,
            then don't match this */
         continue;
 
@@ -1637,7 +1647,7 @@
       if(check->bits.httpproxy &&
          strequal(needle->proxy.name, check->proxy.name) &&
          needle->port == check->port) {
-        /* This is the same proxy connection, use it! */
+        /* This is the same proxy connection */
         match = TRUE;
       }
     }
Index: lib/urldata.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/urldata.h,v
retrieving revision 1.268
diff -u -r1.268 urldata.h
--- lib/urldata.h	12 Jul 2005 18:15:34 -0000	1.268
+++ lib/urldata.h	10 Aug 2005 22:04:29 -0000
@@ -367,6 +367,7 @@
  * Boolean values that concerns this connection.
  */
 struct ConnectBits {
+  bool proxyclose; /* set TRUE if Proxy-Connection: close was received */
   bool close; /* if set, we close the connection after this request */
   bool reuse; /* if set, this is a re-used connection */
   bool chunk; /* if set, this is a chunked transfer-encoding */
@@ -421,10 +422,11 @@
                          LPRT doesn't work we disable it for the forthcoming
                          requests */
   bool netrc;         /* name+password provided by netrc */
-  
+
   bool trailerHdrPresent; /* Set when Trailer: header found in HTTP response.
-                             Required to determine whether to look for trailers 
-                             in case of Transfer-Encoding: chunking */ 
+                             Required to determine whether to look for
+                             trailers in case of Transfer-Encoding:
+                             chunking */
 };
 
 struct hostname {
@@ -518,6 +520,14 @@
 #define FIRSTSOCKET     0
 #define SECONDARYSOCKET 1
 
+typedef enum {
+  CONNECT_NONE,     /* not doing CONNECT at all */
+  CONNECT_PROGRESS, /* currently doing a CONNECT request/response */
+  CONNECT_DONE,     /* we are CONNECTed fine already */
+  CONNECT_SECURE,   /* we have done the SSL/TLS negotiation */
+  CONNECT_LAST  /* never use */
+} Curl_tstate;
+
 /*
  * The connectdata struct contains all fields and variables that should be
  * unique for an entire connection.
@@ -661,14 +671,15 @@
   /** Dynamicly allocated strings, may need to be freed before this **/
   /** struct is killed.                                             **/
   struct dynamically_allocated_data {
-    char *proxyuserpwd; /* free later if not NULL! */
-    char *uagent; /* free later if not NULL! */
-    char *accept_encoding; /* free later if not NULL! */
-    char *userpwd; /* free later if not NULL! */
-    char *rangeline; /* free later if not NULL! */
-    char *ref; /* free later if not NULL! */
-    char *host; /* free later if not NULL */
-    char *cookiehost; /* free later if not NULL */
+    char *proxyuserpwd;
+    char *uagent;
+    char *accept_encoding;
+    char *userpwd;
+    char *rangeline;
+    char *ref;
+    char *host;
+    char *cookiehost;
+    char *connect;
   } allocptr;
 
   char *newurl; /* This can only be set if a Location: was in the
@@ -687,9 +698,7 @@
 #endif
 
   /*************** Request - specific items ************/
-  /* previously this was in the urldata struct */
   union {
-    struct HTTP *http;
     struct HTTP *gopher; /* alias, just for the sake of being more readable */
     struct HTTP *https;  /* alias, just for the sake of being more readable */
     struct FTP *ftp;
@@ -698,6 +707,11 @@
     void *generic;
   } proto;
 
+  struct HTTP *http; /* this is sepearate and not part of the proto union
+                        above since we may have a HTTP proxy CONNECT
+                        connection over which we use one of the other
+                        protocols */
+
   /* This struct is inited when needed */
   struct Curl_transfer_keeper keep;
 
@@ -731,11 +745,12 @@
 
   enum { NORMAL, SOURCE3RD, TARGET3RD } xfertype;
 
+  Curl_tstate tunnelstate;
+
   /* These three are used for chunked-encoding trailer support */
   char *trailer; /* allocated buffer to store trailer in */
   int trlMax;    /* allocated buffer size */
   int trlPos;    /* index of where to store data */
-
 };
 
 /* The end of connectdata. */
@@ -804,6 +819,7 @@
   HTTPREQ_POST_FORM, /* we make a difference internally */
   HTTPREQ_PUT,
   HTTPREQ_HEAD,
+  HTTPREQ_CONNECT,
   HTTPREQ_CUSTOM,
   HTTPREQ_LAST /* last in list */
 } Curl_HttpReq;
@@ -897,6 +913,12 @@
 #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
   ENGINE *engine;
 #endif /* USE_SSLEAY */
+
+  struct connectdata *reuseconn; /* if this is non-NULL when a connect is to
+                                    be made, this will be reused without the
+                                    otherwise used ConnectionExists() call
+                                    logic */
+
 };
 
 
Index: tests/data/test206
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test206,v
retrieving revision 1.3
diff -u -r1.3 test206
--- tests/data/test206	11 May 2005 09:52:59 -0000	1.3
+++ tests/data/test206	10 Aug 2005 22:04:29 -0000
@@ -66,13 +66,13 @@
 ^User-Agent: curl/.*
 </strip>
 <protocol>
-CONNECT test.remote.server.com:206 HTTP/1.0
+CONNECT test.remote.server.com:206 HTTP/1.1
 Host: test.remote.server.com:206
 Proxy-Connection: Keep-Alive
 
-CONNECT test.remote.server.com:206 HTTP/1.0
-Host: test.remote.server.com:206
+CONNECT test.remote.server.com:206 HTTP/1.1
 Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="test.remote.server.com:206", response="5059a96c954981ceb94e17d667c8d3f8"
+Host: test.remote.server.com:206
 Proxy-Connection: Keep-Alive
 
 GET /path/2060002 HTTP/1.1
Index: tests/data/test209
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test209,v
retrieving revision 1.4
diff -u -r1.4 test209
--- tests/data/test209	11 May 2005 09:52:59 -0000	1.4
+++ tests/data/test209	10 Aug 2005 22:04:29 -0000
@@ -79,14 +79,14 @@
 ^User-Agent: curl/.*
 </strip>
 <protocol>
-CONNECT test.remote.server.com:209 HTTP/1.0
-Host: test.remote.server.com:209
+CONNECT test.remote.server.com:209 HTTP/1.1
 Proxy-Authorization: NTLM TlRMTVNTUAABAAAAAgIAAAAAAAAgAAAAAAAAACAAAAA=
+Host: test.remote.server.com:209
 Proxy-Connection: Keep-Alive
 
-CONNECT test.remote.server.com:209 HTTP/1.0
-Host: test.remote.server.com:209
+CONNECT test.remote.server.com:209 HTTP/1.1
 Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAEUAAAAYABgAXQAAAAAAAABAAAAABQAFAEAAAAAAAAAARQAAAAAAAAB1AAAAAYIAAHNpbGx5oB5CPMq0JDu5tbxLow3sHn3jfoYDE+7QJVE7DA0GyDEwvj2BxsBctP9tT4fnCtL1
+Host: test.remote.server.com:209
 Proxy-Connection: Keep-Alive
 
 GET /path/2090002 HTTP/1.1
Index: tests/data/test213
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test213,v
retrieving revision 1.4
diff -u -r1.4 test213
--- tests/data/test213	11 May 2005 09:52:59 -0000	1.4
+++ tests/data/test213	10 Aug 2005 22:04:29 -0000
@@ -79,14 +79,14 @@
 ^User-Agent: curl/.*
 </strip>
 <protocol nonewline=yes>
-CONNECT test.remote.server.com:213 HTTP/1.0
-Host: test.remote.server.com:213
+CONNECT test.remote.server.com:213 HTTP/1.1
 Proxy-Authorization: NTLM TlRMTVNTUAABAAAAAgIAAAAAAAAgAAAAAAAAACAAAAA=
+Host: test.remote.server.com:213
 Proxy-Connection: Keep-Alive
 
-CONNECT test.remote.server.com:213 HTTP/1.0
-Host: test.remote.server.com:213
+CONNECT test.remote.server.com:213 HTTP/1.1
 Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAEUAAAAYABgAXQAAAAAAAABAAAAABQAFAEAAAAAAAAAARQAAAAAAAAB1AAAAAYIAAHNpbGx5oB5CPMq0JDu5tbxLow3sHn3jfoYDE+7QJVE7DA0GyDEwvj2BxsBctP9tT4fnCtL1
+Host: test.remote.server.com:213
 Proxy-Connection: Keep-Alive
 
 POST /path/2130002 HTTP/1.1
Index: tests/data/test217
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test217,v
retrieving revision 1.3
diff -u -r1.3 test217
--- tests/data/test217	11 May 2005 09:52:59 -0000	1.3
+++ tests/data/test217	10 Aug 2005 22:04:29 -0000
@@ -29,7 +29,7 @@
 ^User-Agent: curl/.*
 </strip>
 <protocol>
-CONNECT test.remote.server.com:217 HTTP/1.0
+CONNECT test.remote.server.com:217 HTTP/1.1
 Host: test.remote.server.com:217
 Proxy-Connection: Keep-Alive
 
Index: tests/data/test265
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test265,v
retrieving revision 1.1
diff -u -r1.1 test265
--- tests/data/test265	3 Jul 2005 22:25:15 -0000	1.1
+++ tests/data/test265	10 Aug 2005 22:04:29 -0000
@@ -3,7 +3,7 @@
 
 # this is returned first since we get no proxy-auth
 <data1001>
-HTTP/1.0 407 Authorization Required to proxy me my dear
+HTTP/1.1 407 Authorization Required to proxy me my dear
 Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAgACADAAAAAGgoEAc51AYVDgyNcAAAAAAAAAAG4AbgAyAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==
 Content-Length: 1033
 
@@ -41,7 +41,7 @@
 </data1000>
 
 <datacheck>
-HTTP/1.0 407 Authorization Required to proxy me my dear
+HTTP/1.1 407 Authorization Required to proxy me my dear
 Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAgACADAAAAAGgoEAc51AYVDgyNcAAAAAAAAAAG4AbgAyAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==
 Content-Length: 1033
 
@@ -82,14 +82,14 @@
 ^User-Agent: curl/.*
 </strip>
 <protocol nonewline=yes>
-CONNECT test.remote.server.com:265 HTTP/1.0
-Host: test.remote.server.com:265
+CONNECT test.remote.server.com:265 HTTP/1.1
 Proxy-Authorization: NTLM TlRMTVNTUAABAAAAAgIAAAAAAAAgAAAAAAAAACAAAAA=
+Host: test.remote.server.com:265
 Proxy-Connection: Keep-Alive
 
-CONNECT test.remote.server.com:265 HTTP/1.0
-Host: test.remote.server.com:265
+CONNECT test.remote.server.com:265 HTTP/1.1
 Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAEUAAAAYABgAXQAAAAAAAABAAAAABQAFAEAAAAAAAAAARQAAAAAAAAB1AAAAAYIAAHNpbGx5oB5CPMq0JDu5tbxLow3sHn3jfoYDE+7QJVE7DA0GyDEwvj2BxsBctP9tT4fnCtL1
+Host: test.remote.server.com:265
 Proxy-Connection: Keep-Alive
 
 POST /path/2650002 HTTP/1.1
Index: tests/data/test503
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test503,v
retrieving revision 1.15
diff -u -r1.15 test503
--- tests/data/test503	11 May 2005 09:52:59 -0000	1.15
+++ tests/data/test503	10 Aug 2005 22:04:29 -0000
@@ -48,9 +48,9 @@
 # Verify data after the test has been "shot"
 <verify>
 <protocol>
-CONNECT 127.0.0.1:%HTTPSPORT HTTP/1.0
-Host: 127.0.0.1:%HTTPSPORT
+CONNECT 127.0.0.1:%HTTPSPORT HTTP/1.1
 Proxy-Authorization: Basic dGVzdDppbmc=
+Host: 127.0.0.1:%HTTPSPORT
 Proxy-Connection: Keep-Alive
 
 GET /503 HTTP/1.1
Index: tests/data/test80
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test80,v
retrieving revision 1.10
diff -u -r1.10 test80
--- tests/data/test80	11 May 2005 09:52:59 -0000	1.10
+++ tests/data/test80	10 Aug 2005 22:04:29 -0000
@@ -53,9 +53,9 @@
 ^User-Agent:.*
 </strip>
 <protocol>
-CONNECT 127.0.0.1:%HTTPPORT HTTP/1.0
-Host: 127.0.0.1:%HTTPPORT
+CONNECT 127.0.0.1:%HTTPPORT HTTP/1.1
 Proxy-Authorization: Basic eW91YXJlOnlvdXJzZWxm
+Host: 127.0.0.1:%HTTPPORT
 User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3
 Proxy-Connection: Keep-Alive
 
Index: tests/data/test83
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test83,v
retrieving revision 1.8
diff -u -r1.8 test83
--- tests/data/test83	11 May 2005 09:52:59 -0000	1.8
+++ tests/data/test83	10 Aug 2005 22:04:29 -0000
@@ -53,7 +53,7 @@
 ^User-Agent:.*
 </strip>
 <protocol>
-CONNECT 127.0.0.1:%HTTPPORT HTTP/1.0
+CONNECT 127.0.0.1:%HTTPPORT HTTP/1.1
 User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3
 Host: 127.0.0.1:%HTTPPORT
 Proxy-Connection: Keep-Alive
Index: tests/data/test94
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test94,v
retrieving revision 1.5
diff -u -r1.5 test94
--- tests/data/test94	11 May 2005 09:52:59 -0000	1.5
+++ tests/data/test94	10 Aug 2005 22:04:29 -0000
@@ -46,7 +46,7 @@
 ^User-Agent:.*
 </strip>
 <protocol>
-CONNECT test.anything.really.com:94 HTTP/1.0
+CONNECT test.anything.really.com:94 HTTP/1.1
 User-Agent: curl/7.11.0-CVS (i686-pc-linux-gnu) libcurl/7.11.0-CVS OpenSSL/0.9.6b ipv6 zlib/1.1.4
 Host: test.anything.really.com:94
 Proxy-Connection: Keep-Alive
Index: tests/data/test95
===================================================================
RCS file: /cvsroot/curl/curl/tests/data/test95,v
retrieving revision 1.8
diff -u -r1.8 test95
--- tests/data/test95	11 May 2005 09:52:59 -0000	1.8
+++ tests/data/test95	10 Aug 2005 22:04:29 -0000
@@ -51,7 +51,7 @@
 ^User-Agent:.*
 </strip>
 <protocol nonewline=yes>
-CONNECT 127.0.0.1:%HTTPPORT HTTP/1.0
+CONNECT 127.0.0.1:%HTTPPORT HTTP/1.1
 User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3
 Host: 127.0.0.1:%HTTPPORT
 Proxy-Connection: Keep-Alive
