Index: lib/multi.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/multi.c,v
retrieving revision 1.163
diff -U 20 -r1.163 multi.c
--- lib/multi.c	27 Jan 2008 22:53:10 -0000	1.163
+++ lib/multi.c	2 Feb 2008 15:54:55 -0000
@@ -568,41 +568,42 @@
     return CURLM_BAD_HANDLE;
 
   /* Verify that we got a somewhat good easy handle too */
   if(!GOOD_EASY_HANDLE(curl_handle))
     return CURLM_BAD_EASY_HANDLE;
 
   /* pick-up from the 'curl_handle' the kept position in the list */
   easy = ((struct SessionHandle *)curl_handle)->multi_pos;
 
   if(easy) {
     bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED);
 
     /* If the 'state' is not INIT or COMPLETED, we might need to do something
        nice to put the easy_handle in a good known state when this returns. */
     if(premature)
       /* this handle is "alive" so we need to count down the total number of
          alive connections when this is removed */
       multi->num_alive--;
 
     if(easy->easy_conn &&
-        easy->easy_handle->state.is_in_pipeline &&
+        (easy->easy_conn->send_pipe->size +
+         easy->easy_conn->recv_pipe->size > 1) &&
         easy->state > CURLM_STATE_WAITDO &&
         easy->state < CURLM_STATE_COMPLETED) {
       /* If the handle is in a pipeline and has started sending off its
          request but not received its reponse yet, we need to close
          connection. */
       easy->easy_conn->bits.close = TRUE;
       /* Set connection owner so that Curl_done() closes it.
          We can sefely do this here since connection is killed. */
       easy->easy_conn->data = easy->easy_handle;
     }
 
     /* The timer must be shut down before easy->multi is set to NULL,
        else the timenode will remain in the splay tree after
        curl_easy_cleanup is called. */
     Curl_expire(easy->easy_handle, 0);
 
     if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
       /* clear out the usage of the shared DNS cache */
       easy->easy_handle->dns.hostcache = NULL;
       easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
@@ -851,51 +852,46 @@
   bool connected;
   bool async;
   bool protocol_connect = FALSE;
   bool dophase_done;
   bool done;
   CURLMcode result = CURLM_OK;
   struct SingleRequest *k;
 
   do {
     bool disconnect_conn = FALSE;
 
     if(!GOOD_EASY_HANDLE(easy->easy_handle))
       return CURLM_BAD_EASY_HANDLE;
 
     /* Handle the case when the pipe breaks, i.e., the connection
        we're using gets cleaned up and we're left with nothing. */
     if(easy->easy_handle->state.pipe_broke) {
       infof(easy->easy_handle, "Pipe broke: handle 0x%x, url = %s\n",
             easy, easy->easy_handle->state.path);
 
-      if(easy->easy_handle->state.is_in_pipeline) {
+      if(easy->state != CURLM_STATE_COMPLETED) {
         /* Head back to the CONNECT state */
         multistate(easy, CURLM_STATE_CONNECT);
-        easy->easy_handle->state.is_in_pipeline = FALSE;
         result = CURLM_CALL_MULTI_PERFORM;
         easy->result = CURLE_OK;
       }
-      else {
-        easy->result = CURLE_COULDNT_CONNECT;
-        multistate(easy, CURLM_STATE_COMPLETED);
-      }
 
       easy->easy_handle->state.pipe_broke = FALSE;
       easy->easy_conn = NULL;
       break;
     }
 
     if(easy->state > CURLM_STATE_CONNECT &&
         easy->state < CURLM_STATE_COMPLETED) {
       /* Make sure we set the connection's current owner */
       easy->easy_conn->data = easy->easy_handle;
     }
 
     if(CURLM_STATE_WAITCONNECT <= easy->state &&
         easy->state <= CURLM_STATE_DO &&
         easy->easy_handle->change.url_changed) {
       char *gotourl;
       Curl_posttransfer(easy->easy_handle);
 
       easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
       /* We make sure that the pipe broken flag is reset
@@ -929,66 +925,58 @@
       if(CURLE_OK == easy->result) {
         /* after init, go CONNECT */
         multistate(easy, CURLM_STATE_CONNECT);
         result = CURLM_CALL_MULTI_PERFORM;
 
         easy->easy_handle->state.used_interface = Curl_if_multi;
       }
       break;
 
     case CURLM_STATE_CONNECT:
       /* Connect. We get a connection identifier filled in. */
       Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
       easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
                                   &async, &protocol_connect);
 
       if(CURLE_OK == easy->result) {
         /* Add this handle to the send or pend pipeline */
         easy->result = addHandleToSendOrPendPipeline(easy->easy_handle,
                                                      easy->easy_conn);
         if(CURLE_OK == easy->result) {
-          if (easy->easy_handle->state.is_in_pipeline) {
-            multistate(easy, CURLM_STATE_WAITDO);
-            if(isHandleAtHead(easy->easy_handle,
-                              easy->easy_conn->send_pipe))
-              result = CURLM_CALL_MULTI_PERFORM;
-          }
+          if(async)
+            /* We're now waiting for an asynchronous name lookup */
+            multistate(easy, CURLM_STATE_WAITRESOLVE);
           else {
-            if(async)
-              /* We're now waiting for an asynchronous name lookup */
-              multistate(easy, CURLM_STATE_WAITRESOLVE);
+            /* after the connect has been sent off, go WAITCONNECT unless the
+               protocol connect is already done and we can go directly to
+               WAITDO! */
+            result = CURLM_CALL_MULTI_PERFORM;
+
+            if(protocol_connect)
+              multistate(easy, CURLM_STATE_WAITDO);
             else {
-              /* after the connect has been sent off, go WAITCONNECT unless the
-                 protocol connect is already done and we can go directly to
-                 WAITDO! */
-              result = CURLM_CALL_MULTI_PERFORM;
-
-              if(protocol_connect)
-                multistate(easy, CURLM_STATE_WAITDO);
-              else {
 #ifndef CURL_DISABLE_HTTP
-                if(easy->easy_conn->bits.tunnel_connecting)
-                  multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
-                else
+              if(easy->easy_conn->bits.tunnel_connecting)
+                multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
+              else
 #endif
-                  multistate(easy, CURLM_STATE_WAITCONNECT);
-              }
+                multistate(easy, CURLM_STATE_WAITCONNECT);
             }
           }
         }
       }
       break;
 
     case CURLM_STATE_WAITRESOLVE:
       /* awaiting an asynch name resolve to complete */
     {
       struct Curl_dns_entry *dns = NULL;
 
       /* check if we have the name resolved by now */
       easy->result = Curl_is_resolved(easy->easy_conn, &dns);
 
       if(dns) {
         /* Perform the next step in the connection phase, and then move on
            to the WAITCONNECT state */
         easy->result = Curl_async_resolved(easy->easy_conn,
                                            &protocol_connect);
 
@@ -1270,99 +1258,97 @@
 
       k = &easy->easy_handle->req;
 
       if(!(k->keepon & KEEP_READ)) {
         /* We're done reading */
         easy->easy_conn->readchannel_inuse = FALSE;
       }
 
       if(!(k->keepon & KEEP_WRITE)) {
         /* We're done writing */
         easy->easy_conn->writechannel_inuse = FALSE;
       }
 
       if(easy->result)  {
         /* The transfer phase returned error, we mark the connection to get
          * closed to prevent being re-used. This is because we can't
          * possibly know if the connection is in a good shape or not now. */
         easy->easy_conn->bits.close = TRUE;
         Curl_removeHandleFromPipeline(easy->easy_handle,
                                       easy->easy_conn->recv_pipe);
-        easy->easy_handle->state.is_in_pipeline = FALSE;
 
         if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
           /* if we failed anywhere, we must clean up the secondary socket if
              it was used */
           sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
           easy->easy_conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
         }
         Curl_posttransfer(easy->easy_handle);
         Curl_done(&easy->easy_conn, easy->result, FALSE);
       }
       else if(TRUE == done) {
         char *newurl;
         bool retry = Curl_retry_request(easy->easy_conn, &newurl);
 
         /* call this even if the readwrite function returned error */
         Curl_posttransfer(easy->easy_handle);
 
         /* When we follow redirects, must to go back to the CONNECT state */
         if(easy->easy_handle->req.newurl || retry) {
           Curl_removeHandleFromPipeline(easy->easy_handle,
                                         easy->easy_conn->recv_pipe);
           /* Check if we can move pending requests to send pipe */
           checkPendPipeline(easy->easy_conn);
-          easy->easy_handle->state.is_in_pipeline = FALSE;
+
           if(!retry) {
             /* if the URL is a follow-location and not just a retried request
                then figure out the URL here */
             newurl = easy->easy_handle->req.newurl;
             easy->easy_handle->req.newurl = NULL;
           }
           easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
           if(easy->result == CURLE_OK)
             easy->result = Curl_follow(easy->easy_handle, newurl, retry);
           if(CURLE_OK == easy->result) {
             multistate(easy, CURLM_STATE_CONNECT);
             result = CURLM_CALL_MULTI_PERFORM;
           }
           else
             /* Since we "took it", we are in charge of freeing this on
                failure */
             free(newurl);
         }
         else {
           /* after the transfer is done, go DONE */
           multistate(easy, CURLM_STATE_DONE);
           result = CURLM_CALL_MULTI_PERFORM;
         }
       }
 
       break;
 
     case CURLM_STATE_DONE:
       /* Remove ourselves from the receive pipeline */
       Curl_removeHandleFromPipeline(easy->easy_handle,
                                     easy->easy_conn->recv_pipe);
       /* Check if we can move pending requests to send pipe */
       checkPendPipeline(easy->easy_conn);
-      easy->easy_handle->state.is_in_pipeline = FALSE;
 
       if(easy->easy_conn->bits.stream_was_rewound) {
           /* This request read past its response boundary so we quickly
              let the other requests consume those bytes since there is no
              guarantee that the socket will become active again */
           result = CURLM_CALL_MULTI_PERFORM;
       }
 
       /* post-transfer command */
       easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
 
       /* after we have DONE what we're supposed to do, go COMPLETED, and
          it doesn't matter what the Curl_done() returned! */
       multistate(easy, CURLM_STATE_COMPLETED);
 
       break;
 
     case CURLM_STATE_COMPLETED:
       /* this is a completed transfer, it is likely to still be connected */
 
@@ -1371,41 +1357,40 @@
 
       /* Important: reset the conn pointer so that we don't point to memory
          that could be freed anytime */
       easy->easy_conn = NULL;
       break;
 
     default:
       return CURLM_INTERNAL_ERROR;
     }
 
     if(CURLM_STATE_COMPLETED != easy->state) {
       if(CURLE_OK != easy->result) {
         /*
          * If an error was returned, and we aren't in completed state now,
          * then we go to completed and consider this transfer aborted.
          */
 
         /* NOTE: no attempt to disconnect connections must be made
            in the case blocks above - cleanup happens only here */
 
-        easy->easy_handle->state.is_in_pipeline = FALSE;
         easy->easy_handle->state.pipe_broke = FALSE;
 
         if(easy->easy_conn) {
           /* if this has a connection, unsubscribe from the pipelines */
           easy->easy_conn->writechannel_inuse = FALSE;
           easy->easy_conn->readchannel_inuse = FALSE;
           Curl_removeHandleFromPipeline(easy->easy_handle,
                                         easy->easy_conn->send_pipe);
           Curl_removeHandleFromPipeline(easy->easy_handle,
                                         easy->easy_conn->recv_pipe);
           /* Check if we can move pending requests to send pipe */
           checkPendPipeline(easy->easy_conn);
         }
 
         if(disconnect_conn) {
           Curl_disconnect(easy->easy_conn); /* disconnect properly */
 
           /* This is where we make sure that the easy_conn pointer is reset.
              We don't have to do this in every case block above where a
              failure is detected */
Index: lib/url.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.c,v
retrieving revision 1.698
diff -U 20 -r1.698 url.c
--- lib/url.c	31 Jan 2008 12:04:33 -0000	1.698
+++ lib/url.c	2 Feb 2008 15:55:15 -0000
@@ -370,43 +370,40 @@
 }
 #endif
 
 /*
  * This is the internal function curl_easy_cleanup() calls. This should
  * cleanup and free all resources associated with this sessionhandle.
  *
  * NOTE: if we ever add something that attempts to write to a socket or
  * similar here, we must ignore SIGPIPE first. It is currently only done
  * when curl_easy_perform() is invoked.
  */
 
 CURLcode Curl_close(struct SessionHandle *data)
 {
   struct Curl_multi *m = data->multi;
 
 #ifdef CURLDEBUG
   /* only for debugging, scan through all connections and see if there's a
      pipe reference still identifying this handle */
 
-  if(data->state.is_in_pipeline)
-    fprintf(stderr, "CLOSED when in pipeline!\n");
-
   if(data->state.connc && data->state.connc->type == CONNCACHE_MULTI) {
     struct conncache *c = data->state.connc;
     long i;
     struct curl_llist *pipeline;
     struct curl_llist_element *curr;
     struct connectdata *connptr;
 
     for(i=0; i< c->num; i++) {
       connptr = c->connects[i];
       if(!connptr)
         continue;
 
       pipeline = connptr->send_pipe;
       if(pipeline) {
         for (curr = pipeline->head; curr; curr=curr->next) {
           if(data == (struct SessionHandle *) curr->ptr) {
             fprintf(stderr,
                     "MAJOR problem we %p are still in send pipe for %p done %d\n",
                     data, connptr, connptr->bits.done);
           }
@@ -2460,61 +2457,57 @@
              !strequal(needle->passwd, check->passwd)) {
             /* one of them was different */
             continue;
           }
         }
         match = TRUE;
       }
     }
     else { /* The requested needle connection is using a proxy,
               is the checked one using the same host, port and type? */
       if(check->bits.proxy &&
          (needle->proxytype == check->proxytype) &&
          strequal(needle->proxy.name, check->proxy.name) &&
          needle->port == check->port) {
         /* This is the same proxy connection, use it! */
         match = TRUE;
       }
     }
 
     if(match) {
-      if(!check->is_in_pipeline) {
-        /* The check for a dead socket makes sense only in the
-           non-pipelining case */
+      if(pipeLen == 0) {
+        /* The check for a dead socket makes sense only if there
+           are no handles in pipeline */
         bool dead = SocketIsDead(check->sock[FIRSTSOCKET]);
         if(dead) {
           check->data = data;
           infof(data, "Connection #%d seems to be dead!\n", i);
 
           Curl_disconnect(check); /* disconnect resources */
           data->state.connc->connects[i]=NULL; /* nothing here */
 
           return FALSE;
         }
       }
 
       check->inuse = TRUE; /* mark this as being in use so that no other
                               handle in a multi stack may nick it */
-      if(canPipeline) {
-        /* Mark the connection as being in a pipeline */
-        check->is_in_pipeline = TRUE;
-      }
 
       *usethis = check;
       return TRUE; /* yes, we found one to use! */
     }
   }
 
   return FALSE; /* no matching connecting exists */
 }
 
 
 
 /*
  * This function frees/closes a connection in the connection cache. This
  * should take the previously set policy into account when deciding which
  * of the connections to kill.
  */
 static long
 ConnectionKillOne(struct SessionHandle *data)
 {
   long i;
@@ -2543,42 +2536,40 @@
   if(connindex >= 0) {
     /* Set the connection's owner correctly */
     conn = data->state.connc->connects[connindex];
     conn->data = data;
 
     /* the winner gets the honour of being disconnected */
     (void)Curl_disconnect(conn);
 
     /* clean the array entry */
     data->state.connc->connects[connindex] = NULL;
   }
 
   return connindex; /* return the available index or -1 */
 }
 
 /* this connection can now be marked 'idle' */
 static void
 ConnectionDone(struct connectdata *conn)
 {
   conn->inuse = FALSE;
-  if(!conn->send_pipe && !conn->recv_pipe && !conn->pend_pipe)
-    conn->is_in_pipeline = FALSE;
 }
 
 /*
  * The given input connection struct pointer is to be stored. If the "cache"
  * is already full, we must clean out the most suitable using the previously
  * set policy.
  *
  * The given connection should be unique. That must've been checked prior to
  * this call.
  */
 static long
 ConnectionStore(struct SessionHandle *data,
                 struct connectdata *conn)
 {
   long i;
   for(i=0; i< data->state.connc->num; i++) {
     if(!data->state.connc->connects[i])
       break;
   }
   if(i == data->state.connc->num) {
@@ -4321,42 +4312,44 @@
 #endif
 
   return CURLE_OK;
 }
 
 CURLcode Curl_connect(struct SessionHandle *data,
                       struct connectdata **in_connect,
                       bool *asyncp,
                       bool *protocol_done)
 {
   CURLcode code;
   struct Curl_dns_entry *dns;
 
   *asyncp = FALSE; /* assume synchronous resolves by default */
 
   /* call the stuff that needs to be called */
   code = CreateConnection(data, in_connect, &dns, asyncp);
 
   if(CURLE_OK == code) {
     /* no error */
-    if((*in_connect)->is_in_pipeline)
-      data->state.is_in_pipeline = TRUE;
+    if((*in_connect)->send_pipe->size +
+       (*in_connect)->recv_pipe->size != 0)
+      /* pipelining */
+      *protocol_done = TRUE;
     else {
       if(dns || !*asyncp)
         /* If an address is available it means that we already have the name
            resolved, OR it isn't async. if this is a re-used connection 'dns'
            will be NULL here. Continue connecting from here */
         code = SetupConnection(*in_connect, dns, protocol_done);
       /* else
          response will be received and treated async wise */
     }
   }
 
   if(CURLE_OK != code && *in_connect) {
     /* We're not allowed to return failure with memory left allocated
        in the connectdata struct, free those here */
     Curl_disconnect(*in_connect); /* close the connection */
     *in_connect = NULL;           /* return a NULL */
   }
 
   return code;
 }
Index: lib/urldata.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/urldata.h,v
retrieving revision 1.373
diff -U 20 -r1.373 urldata.h
--- lib/urldata.h	31 Jan 2008 12:04:33 -0000	1.373
+++ lib/urldata.h	2 Feb 2008 15:55:17 -0000
@@ -933,41 +933,40 @@
     char *host; /* free later if not NULL */
     char *cookiehost; /* free later if not NULL */
   } allocptr;
 
   int sec_complete; /* if kerberos is enabled for this connection */
 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
   enum protection_level command_prot;
   enum protection_level data_prot;
   enum protection_level request_data_prot;
   size_t buffer_size;
   struct krb4buffer in_buffer, out_buffer;
   void *app_data;
   const struct Curl_sec_client_mech *mech;
   struct sockaddr_in local_addr;
 #endif
 
   bool readchannel_inuse;  /* whether the read channel is in use by an easy
                               handle */
   bool writechannel_inuse; /* whether the write channel is in use by an easy
                               handle */
-  bool is_in_pipeline;     /* TRUE if this connection is in a pipeline */
   bool server_supports_pipelining; /* TRUE if server supports pipelining,
                                       set after first response */
 
   struct curl_llist *send_pipe; /* List of handles waiting to
                                    send on this pipeline */
   struct curl_llist *recv_pipe; /* List of handles waiting to read
                                    their responses on this pipeline */
   struct curl_llist *pend_pipe; /* List of pending handles on
                                    this pipeline */
 #define MAX_PIPELINE_LENGTH 5
 
   char* master_buffer; /* The master buffer allocated on-demand;
                           used for pipelining. */
   size_t read_pos; /* Current read position in the master buffer */
   size_t buf_len; /* Length of the buffer?? */
 
 
   curl_seek_callback seek_func; /* function that seeks the input */
   void *seek_client;            /* pointer to pass to the seek() above */
 
@@ -1118,42 +1117,40 @@
     Curl_if_multi
   } used_interface;
 
   struct conncache *connc; /* points to the connection cache this handle
                               uses */
 
   /* buffers to store authentication data in, as parsed from input options */
   struct timeval keeps_speed; /* for the progress meter really */
 
   long 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 */
 
   char buffer[BUFSIZE+1]; /* download buffer */
   char uploadbuffer[BUFSIZE+1]; /* upload buffer */
   curl_off_t current_speed;  /* the ProgressShow() funcion sets this,
                                 bytes / second */
   bool this_is_a_follow; /* this is a followed Location: request */
 
-  bool is_in_pipeline; /* Indicates whether this handle is part of a pipeline */
-
   char *first_host; /* if set, this should be the host name that we will
                        sent authorization to, no else. Used to make Location:
                        following not keep sending user+password... This is
                        strdup() data.
                     */
   struct curl_ssl_session *session; /* array of 'numsessions' size */
   long sessionage;                  /* number of the most recent session */
   char *tempwrite;      /* allocated buffer to keep data in when a write
                            callback returns to make the connection paused */
   size_t tempwritesize; /* size of the 'tempwrite' allocated buffer */
   int tempwritetype;    /* type of the 'tempwrite' buffer as a bitmask that is
                            used with Curl_client_write() */
   char *scratch; /* huge buffer[BUFSIZE*2] when doing upload CRLF replacing */
   bool errorbuf; /* Set to TRUE if the error buffer is already filled in.
                     This must be set to FALSE every time _easy_perform() is
                     called. */
   int os_errno;  /* filled in with errno whenever an error occurs */
 #ifdef HAVE_SIGNAL
   /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */
   void (*prev_signal)(int sig);
