diff -r -U20 curl.orig/lib/llist.c curl/lib/llist.c
--- curl.orig/lib/llist.c	2007-11-07 12:21:35.000000000 +0300
+++ curl/lib/llist.c	2008-01-13 21:35:30.000000000 +0300
@@ -119,20 +119,68 @@
 
   return 1;
 }
 
 void
 Curl_llist_destroy(struct curl_llist *list, void *user)
 {
   if(list) {
     while(list->size > 0)
       Curl_llist_remove(list, list->tail, user);
 
     free(list);
   }
 }
 
 size_t
 Curl_llist_count(struct curl_llist *list)
 {
   return list->size;
 }
+
+int Curl_llist_move(struct curl_llist *list, struct curl_llist_element *e,
+                    struct curl_llist *to_list, struct curl_llist_element *to_e)
+{
+  /* Remove element from list */
+  if(e == NULL || list->size == 0)
+    return 0;
+
+  if(e == list->head) {
+    list->head = e->next;
+
+    if(list->head == NULL)
+      list->tail = NULL;
+    else
+      e->next->prev = NULL;
+  } else {
+    e->prev->next = e->next;
+    if(!e->next)
+      list->tail = e->prev;
+    else
+      e->next->prev = e->prev;
+  }
+
+  --list->size;
+
+  /* Add element to to_list after to_e */
+  if(to_list->size == 0) {
+    to_list->head = e;
+    to_list->head->prev = NULL;
+    to_list->head->next = NULL;
+    to_list->tail = e;
+  }
+  else {
+    e->next = to_e->next;
+    e->prev = to_e;
+    if(to_e->next) {
+      to_e->next->prev = e;
+    }
+    else {
+      to_list->tail = e;
+    }
+    to_e->next = e;
+  }
+
+  ++to_list->size;
+
+  return 1;
+}
diff -r -U20 curl.orig/lib/llist.h curl/lib/llist.h
--- curl.orig/lib/llist.h	2005-01-25 03:06:29.000000000 +0300
+++ curl/lib/llist.h	2008-01-13 21:24:29.000000000 +0300
@@ -39,22 +39,24 @@
   struct curl_llist_element *head;
   struct curl_llist_element *tail;
 
   curl_llist_dtor dtor;
 
   size_t size;
 };
 
 void Curl_llist_init(struct curl_llist *, curl_llist_dtor);
 struct curl_llist *Curl_llist_alloc(curl_llist_dtor);
 int Curl_llist_insert_next(struct curl_llist *, struct curl_llist_element *,
                            const void *);
 int Curl_llist_insert_prev(struct curl_llist *, struct curl_llist_element *,
                            const void *);
 int Curl_llist_remove(struct curl_llist *, struct curl_llist_element *,
                       void *);
 int Curl_llist_remove_next(struct curl_llist *, struct curl_llist_element *,
                            void *);
 size_t Curl_llist_count(struct curl_llist *);
 void Curl_llist_destroy(struct curl_llist *, void *);
+int Curl_llist_move(struct curl_llist *, struct curl_llist_element *,
+                    struct curl_llist *, struct curl_llist_element *);
 
 #endif
diff -r -U20 curl.orig/lib/multi.c curl/lib/multi.c
--- curl.orig/lib/multi.c	2008-01-08 17:52:07.000000000 +0300
+++ curl/lib/multi.c	2008-01-14 11:46:08.000000000 +0300
@@ -915,62 +915,66 @@
     case CURLM_STATE_INIT:
       /* init this transfer. */
       easy->result=Curl_pretransfer(easy->easy_handle);
 
       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 pipeline */
-        easy->result = Curl_addHandleToPipeline(easy->easy_handle,
-                                                easy->easy_conn->send_pipe);
+        /* Add this handle to the send or pend pipeline */
+        easy->result = Curl_addHandleToSendOrPendPipeline(easy->easy_handle,
+                                                          easy->easy_conn);
         if(CURLE_OK == easy->result) {
-          if(async)
-            /* We're now waiting for an asynchronous name lookup */
-            multistate(easy, CURLM_STATE_WAITRESOLVE);
+          if (easy->easy_handle->state.is_in_pipeline)
+            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);
+            if(async)
+              /* We're now waiting for an asynchronous name lookup */
+              multistate(easy, CURLM_STATE_WAITRESOLVE);
             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);
 
@@ -1173,46 +1177,44 @@
          */
         easy->result = Curl_do_more(easy->easy_conn);
 
         /* No need to remove ourselves from the send pipeline here since that
            is done for us in Curl_done() */
 
         if(CURLE_OK == easy->result) {
           multistate(easy, CURLM_STATE_DO_DONE);
           result = CURLM_CALL_MULTI_PERFORM;
         }
         else {
           /* failure detected */
           Curl_posttransfer(easy->easy_handle);
           Curl_done(&easy->easy_conn, easy->result, FALSE);
           disconnect_conn = TRUE;
         }
       }
       break;
 
     case CURLM_STATE_DO_DONE:
-      /* Remove ourselves from the send pipeline */
-      Curl_removeHandleFromPipeline(easy->easy_handle,
-                                    easy->easy_conn->send_pipe);
-      /* Add ourselves to the recv pipeline */
-      easy->result = Curl_addHandleToPipeline(easy->easy_handle,
-                                              easy->easy_conn->recv_pipe);
+      /* Move ourselves from the send to recv pipeline */
+      Curl_moveHandleFromSendToRecvPipeline(easy->easy_handle, easy->easy_conn);
+      /* Check if we can move pending requests to send pipe */
+      Curl_checkPendPipeline(easy->easy_conn);
       multistate(easy, CURLM_STATE_WAITPERFORM);
       result = CURLM_CALL_MULTI_PERFORM;
       break;
 
     case CURLM_STATE_WAITPERFORM:
 #ifdef CURLDEBUG
       infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n",
             easy->easy_conn->connectindex,
             easy->easy_conn->recv_pipe->size,
             easy->easy_conn->readchannel_inuse,
             Curl_isHandleAtHead(easy->easy_handle,
                                 easy->easy_conn->recv_pipe));
 #endif
       /* Wait for our turn to PERFORM */
       if(!easy->easy_conn->readchannel_inuse &&
           Curl_isHandleAtHead(easy->easy_handle,
                               easy->easy_conn->recv_pipe)) {
         /* Grab the channel */
         easy->easy_conn->readchannel_inuse = TRUE;
         multistate(easy, CURLM_STATE_PERFORM);
@@ -1275,71 +1277,75 @@
         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 */
+          Curl_checkPendPipeline(easy->easy_conn);
           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 */
+      Curl_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;
       }
 
       if(!easy->easy_handle->state.cancelled) {
         /* 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;
 
@@ -1373,40 +1379,42 @@
       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 */
+          Curl_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 */
           easy->easy_conn = NULL;
         }
 
         multistate(easy, CURLM_STATE_COMPLETED);
       }
     }
 
   } while(easy->easy_handle->change.url_changed);
 
   if((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
     if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
       /* clear out the usage of the shared DNS cache */
diff -r -U20 curl.orig/lib/transfer.c curl/lib/transfer.c
--- curl.orig/lib/transfer.c	2008-01-10 13:30:20.000000000 +0300
+++ curl/lib/transfer.c	2008-01-12 21:33:46.000000000 +0300
@@ -813,40 +813,48 @@
                       (data->set.httpreq==HTTPREQ_GET) &&
                       (k->httpcode == 416)) {
                     /* "Requested Range Not Satisfiable", just proceed and
                        pretend this is no error */
                   }
                   else {
                     /* serious error, go home! */
                     failf (data, "The requested URL returned error: %d",
                            k->httpcode);
                     return CURLE_HTTP_RETURNED_ERROR;
                   }
                 }
 
                 if(k->httpversion == 10) {
                   /* Default action for HTTP/1.0 must be to close, unless
                      we get one of those fancy headers that tell us the
                      server keeps it open for us! */
                   infof(data, "HTTP 1.0, assume close after body\n");
                   conn->bits.close = TRUE;
                 }
+                else if(k->httpversion >= 11 &&
+                         !conn->bits.close) {
+                  /* If HTTP version is >= 1.1 and connection is persistent
+                     server supports pipelining. */
+                  infof(data, "HTTP 1.1 or later with persistent connection, "
+                              "pipelining supported\n");
+                  conn->server_supports_pipelining = TRUE;
+                }
 
                 switch(k->httpcode) {
                 case 204:
                   /* (quote from RFC2616, section 10.2.5): The server has
                    * fulfilled the request but does not need to return an
                    * entity-body ... The 204 response MUST NOT include a
                    * message-body, and thus is always terminated by the first
                    * empty line after the header fields. */
                   /* FALLTHROUGH */
                 case 416: /* Requested Range Not Satisfiable, it has the
                              Content-Length: set as the "real" document but no
                              actual response is sent. */
                 case 304:
                   /* (quote from RFC2616, section 10.3.5): The 304 response
                    * MUST NOT contain a message-body, and thus is always
                    * terminated by the first empty line after the header
                    * fields.  */
                   k->size=0;
                   k->maxdownload=0;
                   k->ignorecl = TRUE; /* ignore Content-Length headers */
diff -r -U20 curl.orig/lib/url.c curl/lib/url.c
--- curl.orig/lib/url.c	2008-01-13 06:13:56.000000000 +0300
+++ curl/lib/url.c	2008-01-14 12:38:58.000000000 +0300
@@ -408,40 +408,50 @@
       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);
           }
         }
       }
       pipeline = connptr->recv_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 recv pipe for %p done %d\n",
                     data, connptr, connptr->bits.done);
           }
         }
       }
+      pipeline = connptr->pend_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 pend pipe for %p done %d\n",
+                    data, connptr, connptr->bits.done);
+          }
+        }
+      }
     }
   }
 #endif
 
   if(m)
     /* This handle is still part of a multi handle, take care of this first
        and detach this handle from there. */
     Curl_multi_rmeasy(data->multi, data);
 
   data->magic = 0; /* force a clear AFTER the possibly enforced removal from
                       the multi handle, since that function uses the magic
                       field! */
 
   if(data->state.connc) {
 
     if(data->state.connc->type == CONNCACHE_PRIVATE) {
       /* close all connections still alive that are in the private connection
          cache, as we no longer have the pointer left to the shared one. */
       close_connections(data);
 
@@ -2093,40 +2103,41 @@
   Curl_safefree(conn->user);
   Curl_safefree(conn->passwd);
   Curl_safefree(conn->proxyuser);
   Curl_safefree(conn->proxypasswd);
   Curl_safefree(conn->allocptr.proxyuserpwd);
   Curl_safefree(conn->allocptr.uagent);
   Curl_safefree(conn->allocptr.userpwd);
   Curl_safefree(conn->allocptr.accept_encoding);
   Curl_safefree(conn->allocptr.rangeline);
   Curl_safefree(conn->allocptr.ref);
   Curl_safefree(conn->allocptr.host);
   Curl_safefree(conn->allocptr.cookiehost);
   Curl_safefree(conn->ip_addr_str);
   Curl_safefree(conn->trailer);
   Curl_safefree(conn->host.rawalloc); /* host name buffer */
   Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
   Curl_safefree(conn->master_buffer);
 
   Curl_llist_destroy(conn->send_pipe, NULL);
   Curl_llist_destroy(conn->recv_pipe, NULL);
+  Curl_llist_destroy(conn->pend_pipe, NULL);
 
   /* possible left-overs from the async name resolvers */
 #if defined(USE_ARES)
   Curl_safefree(conn->async.hostname);
   Curl_safefree(conn->async.os_specific);
 #elif defined(CURLRES_THREADED)
   Curl_destroy_thread_data(&conn->async);
 #endif
 
   Curl_ssl_close(conn, FIRSTSOCKET);
   Curl_ssl_close(conn, SECONDARYSOCKET);
 
   Curl_free_ssl_config(&conn->ssl_config);
 
   free(conn); /* free all the connection oriented data */
 }
 
 CURLcode Curl_disconnect(struct connectdata *conn)
 {
   struct SessionHandle *data;
@@ -2179,40 +2190,41 @@
       data->state.connc->connects[conn->connectindex] = NULL;
   }
 
 #ifdef USE_LIBIDN
   if(conn->host.encalloc)
     idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed
                                       with idn_free() since this was allocated
                                       by libidn */
   if(conn->proxy.encalloc)
     idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be
                                        freed with idn_free() since this was
                                        allocated by libidn */
 #endif
 
   Curl_ssl_close(conn, FIRSTSOCKET);
 
   /* Indicate to all handles on the pipe that we're dead */
   if(IsPipeliningEnabled(data)) {
     signalPipeClose(conn->send_pipe);
     signalPipeClose(conn->recv_pipe);
+    signalPipeClose(conn->pend_pipe);
   }
 
   conn_free(conn);
   data->state.current_conn = NULL;
 
   return CURLE_OK;
 }
 
 /*
  * This function should return TRUE if the socket is to be assumed to
  * be dead. Most commonly this happens when the server has closed the
  * connection due to inactivity.
  */
 static bool SocketIsDead(curl_socket_t sock)
 {
   int sval;
   bool ret_val = TRUE;
 
   sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0);
   if(sval == 0)
@@ -2239,43 +2251,103 @@
     return TRUE;
 
   return FALSE;
 }
 
 CURLcode Curl_addHandleToPipeline(struct SessionHandle *data,
                                   struct curl_llist *pipeline)
 {
 #ifdef CURLDEBUG
   if(!IsPipeliningPossible(data)) {
     /* when not pipelined, there MUST be no handle in the list already */
     if(pipeline->head)
       infof(data, "PIPE when no PIPE supposed!\n");
   }
 #endif
   if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
     return CURLE_OUT_OF_MEMORY;
   return CURLE_OK;
 }
 
+CURLcode Curl_addHandleToSendOrPendPipeline(struct SessionHandle *data,
+                                            struct connectdata *conn)
+{
+  size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
+  struct curl_llist *pipeline;
+
+  if(!IsPipeliningEnabled(data) ||
+      pipeLen == 0)
+     pipeline = conn->send_pipe;
+  else {
+    if(conn->server_supports_pipelining &&
+        pipeLen < MAX_PIPELINE_LENGTH)
+      pipeline = conn->send_pipe;
+    else
+      pipeline = conn->pend_pipe;
+  }
+
+  return Curl_addHandleToPipeline(data, pipeline);
+}
+
+int Curl_checkPendPipeline(struct connectdata *conn)
+{
+  int result = 0;
+
+  if (conn->server_supports_pipelining) {
+    size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
+    struct curl_llist_element *curr = conn->pend_pipe->head;
+
+    while(pipeLen < MAX_PIPELINE_LENGTH && curr) {
+      Curl_llist_move(conn->pend_pipe, curr,
+                      conn->send_pipe, conn->send_pipe->tail);
+      Curl_pgrsTime(curr->ptr, TIMER_CONNECT);
+      ++result; /* count how many handles we moved */
+      curr = conn->pend_pipe->head;
+      ++pipeLen;
+    }
+    if (result > 0)
+      conn->now = Curl_tvnow();
+  }
+
+  return result;
+}
+
+int Curl_moveHandleFromSendToRecvPipeline(struct SessionHandle *data,
+                                          struct connectdata *conn)
+{
+  struct curl_llist_element *curr;
+
+  curr = conn->send_pipe->head;
+  while(curr) {
+    if(curr->ptr == data) {
+      Curl_llist_move(conn->send_pipe, curr,
+                      conn->recv_pipe, conn->recv_pipe->tail);
+      return 1; /* we moved a handle */
+    }
+    curr = curr->next;
+  }
+
+  return 0;
+}
 
 int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
-                                   struct curl_llist *pipeline)
+                                  struct curl_llist *pipeline)
 {
   struct curl_llist_element *curr;
 
   curr = pipeline->head;
   while(curr) {
     if(curr->ptr == handle) {
       Curl_llist_remove(pipeline, curr, NULL);
       return 1; /* we removed a handle */
     }
     curr = curr->next;
   }
 
   return 0;
 }
 
 #if 0 /* this code is saved here as it is useful for debugging purposes */
 static void Curl_printPipeline(struct curl_llist *pipeline)
 {
   struct curl_llist_element *curr;
 
@@ -2357,89 +2429,91 @@
   for(i=0; i< data->state.connc->num; i++) {
     bool match = FALSE;
     size_t pipeLen = 0;
     /*
      * Note that if we use a HTTP proxy, we check connections to that
      * proxy and not to the actual remote server.
      */
     check = data->state.connc->connects[i];
     if(!check)
       /* NULL pointer means not filled-in entry */
       continue;
 
     pipeLen = check->send_pipe->size + check->recv_pipe->size;
 
     if(check->connectindex == -1) {
       check->connectindex = i; /* Set this appropriately since it might have
                                   been set to -1 when the easy was removed
                                   from the multi */
     }
 
-    if(pipeLen > 0 && !canPipeline) {
-      /* can only happen within multi handles, and means that another easy
-         handle is using this connection */
-      continue;
-    }
-
-#ifdef CURLRES_ASYNCH
-    /* ip_addr_str is NULL only if the resolving of the name hasn't completed
-       yet and until then we don't re-use this connection */
-    if(!check->ip_addr_str) {
-      infof(data,
-            "Connection #%ld hasn't finished name resolve, can't reuse\n",
-            check->connectindex);
-      continue;
-    }
-#endif
-
-    if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) {
-      /* Don't pick a connection that hasn't connected yet or that is going to
-         get closed. */
-      infof(data, "Connection #%ld isn't open enough, can't reuse\n",
-            check->connectindex);
-#ifdef CURLDEBUG
-      if(check->recv_pipe->size > 0) {
-        infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
-              check->connectindex);
-      }
-#endif
-      continue;
-    }
-
-    if(pipeLen >= MAX_PIPELINE_LENGTH) {
-      infof(data, "Connection #%ld has its pipeline full, can't reuse\n",
-            check->connectindex);
-      continue;
-    }
-
     if(canPipeline) {
       /* Make sure the pipe has only GET requests */
       struct SessionHandle* sh = gethandleathead(check->send_pipe);
       struct SessionHandle* rh = gethandleathead(check->recv_pipe);
       if(sh) {
         if(!IsPipeliningPossible(sh))
           continue;
       }
       else if(rh) {
         if(!IsPipeliningPossible(rh))
           continue;
       }
+
+#ifdef CURLDEBUG
+      if(pipeLen > MAX_PIPELINE_LENGTH) {
+        infof(data, "BAD! Connection #%ld has too big pipeline!\n",
+              check->connectindex);
+      }
+#endif
+    }
+    else {
+      if(pipeLen > 0) {
+        /* can only happen within multi handles, and means that another easy
+           handle is using this connection */
+        continue;
+      }
+
+#ifdef CURLRES_ASYNCH
+      /* ip_addr_str is NULL only if the resolving of the name hasn't completed
+         yet and until then we don't re-use this connection */
+      if(!check->ip_addr_str) {
+        infof(data,
+              "Connection #%ld hasn't finished name resolve, can't reuse\n",
+              check->connectindex);
+        continue;
+      }
+#endif
+
+      if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) {
+        /* Don't pick a connection that hasn't connected yet or that is going to
+           get closed. */
+        infof(data, "Connection #%ld isn't open enough, can't reuse\n",
+              check->connectindex);
+#ifdef CURLDEBUG
+        if(check->recv_pipe->size > 0) {
+          infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
+                check->connectindex);
+        }
+#endif
+        continue;
+      }
     }
 
     if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
       /* don't do mixed SSL and non-SSL connections */
       continue;
 
     if(needle->bits.proxy != check->bits.proxy)
       /* don't do mixed proxy and non-proxy connections */
       continue;
 
     if(!needle->bits.httpproxy || needle->protocol&PROT_SSL ||
        (needle->bits.httpproxy && check->bits.httpproxy &&
         needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
         strequal(needle->proxy.name, check->proxy.name) &&
         (needle->port == check->port))) {
       /* The requested connection does not use a HTTP proxy or it uses SSL or
          it is a non-SSL protocol tunneled over the same http proxy name and
          port number */
 
       if(strequal(needle->protostr, check->protostr) &&
@@ -2549,41 +2623,41 @@
   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)
+  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;
   }
@@ -3543,41 +3617,42 @@
   conn->created = Curl_tvnow();
 
   conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERPWD]);
   conn->bits.proxy_user_passwd = (bool)(NULL != data->set.str[STRING_PROXYUSERPWD]);
   conn->bits.no_body = data->set.opt_no_body;
   conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
   conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
   conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
 
   if(data->multi && Curl_multi_canPipeline(data->multi) &&
       !conn->master_buffer) {
     /* Allocate master_buffer to be used for pipelining */
     conn->master_buffer = calloc(BUFSIZE, sizeof (char));
     if(!conn->master_buffer)
       return CURLE_OUT_OF_MEMORY;
   }
 
   /* Initialize the pipeline lists */
   conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
   conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
-  if(!conn->send_pipe || !conn->recv_pipe)
+  conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
+  if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe)
     return CURLE_OUT_OF_MEMORY;
 
   /* This initing continues below, see the comment "Continue connectdata
    * initialization here" */
 
   /***********************************************************
    * We need to allocate memory to store the path in. We get the size of the
    * full URL to be sure, and we need to make it at least 256 bytes since
    * other parts of the code will rely on this fact
    ***********************************************************/
 #define LEAST_PATH_ALLOC 256
   urllen=strlen(data->change.url);
   if(urllen < LEAST_PATH_ALLOC)
     urllen=LEAST_PATH_ALLOC;
 
   /* Free the old buffer */
   Curl_safefree(data->state.pathbuffer);
 
   /*
    * We malloc() the buffers below urllen+2 to make room for to possibilities:
@@ -4007,40 +4082,41 @@
     /* host can change, when doing keepalive with a proxy ! */
     if(conn->bits.proxy) {
       free(conn->host.rawalloc);
       conn->host=old_conn->host;
     }
     else
       free(old_conn->host.rawalloc); /* free the newly allocated name buffer */
 
     /* get the newly set value, not the old one */
     conn->bits.no_body = old_conn->bits.no_body;
 
     /* re-use init */
     conn->bits.reuse = TRUE; /* yes, we're re-using here */
 
     Curl_safefree(old_conn->user);
     Curl_safefree(old_conn->passwd);
     Curl_safefree(old_conn->proxyuser);
     Curl_safefree(old_conn->proxypasswd);
     Curl_llist_destroy(old_conn->send_pipe, NULL);
     Curl_llist_destroy(old_conn->recv_pipe, NULL);
+    Curl_llist_destroy(old_conn->pend_pipe, NULL);
     Curl_safefree(old_conn->master_buffer);
 
     free(old_conn);          /* we don't need this anymore */
 
     *in_connect = conn;      /* return this instead! */
 
     infof(data, "Re-using existing connection! (#%ld) with host %s\n",
           conn->connectindex,
           conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
   }
   else {
     /*
      * This is a brand new connection, so let's store it in the connection
      * cache of ours!
      */
     ConnectionStore(data, conn);
   }
 
   result = setup_range(data);
   if(result)
@@ -4341,60 +4417,58 @@
 #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(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((*in_connect)->is_in_pipeline)
+      data->state.is_in_pipeline = 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) {
+  if(CURLE_OK != code && *in_connect) {
     /* We're not allowed to return failure with memory left allocated
        in the connectdata struct, free those here */
-    if(*in_connect) {
-      Curl_disconnect(*in_connect); /* close the connection */
-      *in_connect = NULL;           /* return a NULL */
-    }
-  }
-  else {
-    if((*in_connect)->is_in_pipeline)
-      data->state.is_in_pipeline = TRUE;
+    Curl_disconnect(*in_connect); /* close the connection */
+    *in_connect = NULL;           /* return a NULL */
   }
 
   return code;
 }
 
 /* Call this function after Curl_connect() has returned async=TRUE and
    then a successful name resolve has been received.
 
    Note: this function disconnects and frees the conn data in case of
    resolve failure */
 CURLcode Curl_async_resolved(struct connectdata *conn,
                              bool *protocol_done)
 {
 #if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
     defined(USE_THREADING_GETADDRINFO)
   CURLcode code = SetupConnection(conn, conn->async.dns, protocol_done);
 
   if(code)
     /* We're not allowed to return failure with memory left allocated
        in the connectdata struct, free those here */
@@ -4414,40 +4488,41 @@
                                         error was detected */
                    bool premature)
 {
   CURLcode result;
   struct connectdata *conn = *connp;
   struct SessionHandle *data = conn->data;
 
   Curl_expire(data, 0); /* stop timer */
 
   if(conn->bits.done)
     return CURLE_OK; /* Curl_done() has already been called */
 
   conn->bits.done = TRUE; /* called just now! */
 
   if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) &&
      conn->readchannel_inuse)
     conn->readchannel_inuse = FALSE;
   if(Curl_removeHandleFromPipeline(data, conn->send_pipe) &&
      conn->writechannel_inuse)
     conn->writechannel_inuse = FALSE;
+  Curl_removeHandleFromPipeline(data, conn->pend_pipe);
 
   /* Cleanup possible redirect junk */
   if(data->req.newurl) {
     free(data->req.newurl);
     data->req.newurl = NULL;
   }
 
   if(conn->dns_entry) {
     Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
     conn->dns_entry = NULL;
   }
 
   /* this calls the protocol-specific function pointer previously set */
   if(conn->handler->done)
     result = conn->handler->done(conn, status, premature);
   else
     result = CURLE_OK;
 
   Curl_pgrsDone(conn); /* done with the operation */
 
diff -r -U20 curl.orig/lib/urldata.h curl/lib/urldata.h
--- curl.orig/lib/urldata.h	2008-01-10 13:30:20.000000000 +0300
+++ curl/lib/urldata.h	2008-01-12 21:25:34.000000000 +0300
@@ -932,45 +932,49 @@
     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 */
 
   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 */
 
   /*************** Request - specific items ************/
 
   /* previously this was in the urldata struct */
   curl_read_callback fread_func; /* function that reads the input */
   void *fread_in;           /* pointer to pass to the fread() above */
 
   struct ntlmdata ntlm;     /* NTLM differs from other authentication schemes
                                because it authenticates connections, not
                                single requests! */
   struct ntlmdata proxyntlm; /* NTLM data for proxy */
diff -r -U20 curl.orig/lib/url.h curl/lib/url.h
--- curl.orig/lib/url.h	2007-10-22 19:05:35.000000000 +0400
+++ curl/lib/url.h	2008-01-14 11:45:04.000000000 +0300
@@ -49,40 +49,45 @@
 void Curl_safefree(void *ptr);
 
 /* create a connection cache */
 struct conncache *Curl_mk_connc(int type, long amount);
 /* free a connection cache */
 void Curl_rm_connc(struct conncache *c);
 /* Change number of entries of a connection cache */
 CURLcode Curl_ch_connc(struct SessionHandle *data,
                        struct conncache *c,
                        long newamount);
 
 int Curl_protocol_getsock(struct connectdata *conn,
                           curl_socket_t *socks,
                           int numsocks);
 int Curl_doing_getsock(struct connectdata *conn,
                        curl_socket_t *socks,
                        int numsocks);
 
 CURLcode Curl_addHandleToPipeline(struct SessionHandle *handle,
                                   struct curl_llist *pipeline);
+CURLcode Curl_addHandleToSendOrPendPipeline(struct SessionHandle *handle,
+                                            struct connectdata *conn);
+int Curl_checkPendPipeline(struct connectdata *conn);
+int Curl_moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
+                                          struct connectdata *conn);
 int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
                                   struct curl_llist *pipeline);
 bool Curl_isHandleAtHead(struct SessionHandle *handle,
                          struct curl_llist *pipeline);
 
 void Curl_close_connections(struct SessionHandle *data);
 
 #if 0
 CURLcode Curl_protocol_fdset(struct connectdata *conn,
                              fd_set *read_fd_set,
                              fd_set *write_fd_set,
                              int *max_fdp);
 CURLcode Curl_doing_fdset(struct connectdata *conn,
                           fd_set *read_fd_set,
                           fd_set *write_fd_set,
                           int *max_fdp);
 #endif
 
 /* Called on connect, and if there's already a protocol-specific struct
    allocated for a different connection, this frees it that it can be setup
diff -r -U20 curl.orig/tests/data/test530 curl/tests/data/test530
--- curl.orig/tests/data/test530	2007-04-19 00:22:01.000000000 +0400
+++ curl/tests/data/test530	2008-01-13 07:11:24.000000000 +0300
@@ -1,31 +1,32 @@
 <testcase>
 <info>
 <keywords>
 HTTP
 Pipelining
 </keywords>
 </info>
 # Server-side
 <reply>
 <servercmd>
-pipe: 4
+pipe: 1
+pipe: 3
 </servercmd>
 <data>
 HTTP/1.1 200 OK
 Date: Thu, 09 Nov 2010 14:49:00 GMT
 Server: test-server/fake
 Content-Length: 47
 
 file contents should appear once for each file
 HTTP/1.1 200 OK
 Date: Thu, 09 Nov 2010 14:49:00 GMT
 Server: test-server/fake
 Content-Length: 47
 
 file contents should appear once for each file
 HTTP/1.1 200 OK
 Date: Thu, 09 Nov 2010 14:49:00 GMT
 Server: test-server/fake
 Content-Length: 47
 
 file contents should appear once for each file
