Base code merged to SPIN 2.4
[platform/upstream/curl.git] / lib / multi.c
index ca975a0..97c9e65 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -30,6 +30,7 @@
 #include "connect.h"
 #include "progress.h"
 #include "easyif.h"
+#include "share.h"
 #include "multiif.h"
 #include "sendf.h"
 #include "timeval.h"
@@ -112,20 +113,23 @@ static void mstate(struct SessionHandle *data, CURLMstate state
 #endif
 )
 {
-#ifdef DEBUGBUILD
-  long connection_id = -5000;
-#endif
   CURLMstate oldstate = data->mstate;
 
+#if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS)
+  (void) lineno;
+#endif
+
   if(oldstate == state)
     /* don't bother when the new state is the same as the old state */
     return;
 
   data->mstate = state;
 
-#ifdef DEBUGBUILD
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   if(data->mstate >= CURLM_STATE_CONNECT_PEND &&
      data->mstate < CURLM_STATE_COMPLETED) {
+    long connection_id = -5000;
+
     if(data->easy_conn)
       connection_id = data->easy_conn->connection_id;
 
@@ -135,6 +139,7 @@ static void mstate(struct SessionHandle *data, CURLMstate state
           (void *)data, lineno, connection_id);
   }
 #endif
+
   if(state == CURLM_STATE_COMPLETED)
     /* changing to COMPLETED means there's one less easy handle 'alive' */
     data->multi->num_alive--;
@@ -179,11 +184,12 @@ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh,
   check = calloc(1, sizeof(struct Curl_sh_entry));
   if(!check)
     return NULL; /* major failure */
+
   check->easy = data;
   check->socket = s;
 
   /* make/add new hash entry */
-  if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
+  if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
     free(check);
     return NULL; /* major failure */
   }
@@ -308,6 +314,10 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
   if(!multi->msglist)
     goto error;
 
+  multi->pending = Curl_llist_alloc(multi_freeamsg);
+  if(!multi->pending)
+    goto error;
+
   /* allocate a new easy handle to use when closing cached connections */
   multi->closure_handle = curl_easy_init();
   if(!multi->closure_handle)
@@ -333,6 +343,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
   Curl_close(multi->closure_handle);
   multi->closure_handle = NULL;
   Curl_llist_destroy(multi->msglist, NULL);
+  Curl_llist_destroy(multi->pending, NULL);
 
   free(multi);
   return NULL;
@@ -481,6 +492,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
   struct SessionHandle *easy = curl_handle;
   struct SessionHandle *data = easy;
+  bool premature;
+  bool easy_owns_conn;
+  struct curl_llist_element *e;
 
   /* First, make some basic checks that the CURLM handle is a good handle */
   if(!GOOD_MULTI_HANDLE(multi))
@@ -494,126 +508,117 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
   if(!data->multi)
     return CURLM_OK; /* it is already removed so let's say it is fine! */
 
-  if(easy) {
-    bool premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
-    bool easy_owns_conn = (data->easy_conn &&
-                           (data->easy_conn->data == easy)) ?
-                           TRUE : FALSE;
 
-    /* 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--;
+  premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
+  easy_owns_conn = (data->easy_conn && (data->easy_conn->data == easy)) ?
+    TRUE : FALSE;
 
-    if(data->easy_conn &&
-       (data->easy_conn->send_pipe->size +
-        data->easy_conn->recv_pipe->size > 1) &&
-       data->mstate > CURLM_STATE_WAITDO &&
-       data->mstate < CURLM_STATE_COMPLETED) {
-      /* If the handle is in a pipeline and has started sending off its
-         request but not received its response yet, we need to close
-         connection. */
-      connclose(data->easy_conn, "Removed with partial response");
-      /* Set connection owner so that Curl_done() closes it.
-         We can sefely do this here since connection is killed. */
-      data->easy_conn->data = easy;
-    }
+  /* 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--;
 
-    /* The timer must be shut down before data->multi is set to NULL,
-       else the timenode will remain in the splay tree after
-       curl_easy_cleanup is called. */
-    Curl_expire(data, 0);
+  if(data->easy_conn &&
+     data->mstate > CURLM_STATE_DO &&
+     data->mstate < CURLM_STATE_COMPLETED) {
+    /* If the handle is in a pipeline and has started sending off its
+       request but not received its response yet, we need to close
+       connection. */
+    connclose(data->easy_conn, "Removed with partial response");
+    /* Set connection owner so that Curl_done() closes it.
+       We can safely do this here since connection is killed. */
+    data->easy_conn->data = easy;
+    easy_owns_conn = TRUE;
+  }
 
-    /* destroy the timeout list that is held in the easy handle */
-    if(data->state.timeoutlist) {
-      Curl_llist_destroy(data->state.timeoutlist, NULL);
-      data->state.timeoutlist = NULL;
-    }
+  /* The timer must be shut down before data->multi is set to NULL,
+     else the timenode will remain in the splay tree after
+     curl_easy_cleanup is called. */
+  Curl_expire(data, 0);
 
-    if(data->dns.hostcachetype == HCACHE_MULTI) {
-      /* stop using the multi handle's DNS cache */
-      data->dns.hostcache = NULL;
-      data->dns.hostcachetype = HCACHE_NONE;
-    }
+  /* destroy the timeout list that is held in the easy handle */
+  if(data->state.timeoutlist) {
+    Curl_llist_destroy(data->state.timeoutlist, NULL);
+    data->state.timeoutlist = NULL;
+  }
 
-    if(data->easy_conn) {
+  if(data->dns.hostcachetype == HCACHE_MULTI) {
+    /* stop using the multi handle's DNS cache */
+    data->dns.hostcache = NULL;
+    data->dns.hostcachetype = HCACHE_NONE;
+  }
 
-      /* we must call Curl_done() here (if we still "own it") so that we don't
-         leave a half-baked one around */
-      if(easy_owns_conn) {
+  if(data->easy_conn) {
 
-        /* Curl_done() clears the conn->data field to lose the association
-           between the easy handle and the connection
+    /* we must call Curl_done() here (if we still "own it") so that we don't
+       leave a half-baked one around */
+    if(easy_owns_conn) {
 
-           Note that this ignores the return code simply because there's
-           nothing really useful to do with it anyway! */
-        (void)Curl_done(&data->easy_conn, data->result, premature);
-      }
-      else
-        /* Clear connection pipelines, if Curl_done above was not called */
-        Curl_getoff_all_pipelines(data, data->easy_conn);
+      /* Curl_done() clears the conn->data field to lose the association
+         between the easy handle and the connection
+
+         Note that this ignores the return code simply because there's
+         nothing really useful to do with it anyway! */
+      (void)Curl_done(&data->easy_conn, data->result, premature);
     }
+    else
+      /* Clear connection pipelines, if Curl_done above was not called */
+      Curl_getoff_all_pipelines(data, data->easy_conn);
+  }
 
-    Curl_wildcard_dtor(&data->wildcard);
+  Curl_wildcard_dtor(&data->wildcard);
 
-    /* as this was using a shared connection cache we clear the pointer
-       to that since we're not part of that multi handle anymore */
-    data->state.conn_cache = NULL;
+  /* as this was using a shared connection cache we clear the pointer to that
+     since we're not part of that multi handle anymore */
+  data->state.conn_cache = NULL;
 
-    /* change state without using multistate(), only to make singlesocket() do
-       what we want */
-    data->mstate = CURLM_STATE_COMPLETED;
-    singlesocket(multi, easy); /* to let the application know what sockets
-                                  that vanish with this handle */
+  /* change state without using multistate(), only to make singlesocket() do
+     what we want */
+  data->mstate = CURLM_STATE_COMPLETED;
+  singlesocket(multi, easy); /* to let the application know what sockets that
+                                vanish with this handle */
 
-    /* Remove the association between the connection and the handle */
-    if(data->easy_conn) {
-      data->easy_conn->data = NULL;
-      data->easy_conn = NULL;
-    }
+  /* Remove the association between the connection and the handle */
+  if(data->easy_conn) {
+    data->easy_conn->data = NULL;
+    data->easy_conn = NULL;
+  }
 
-    data->multi = NULL; /* clear the association to this multi handle */
+  data->multi = NULL; /* clear the association to this multi handle */
 
-    {
-      /* make sure there's no pending message in the queue sent from this easy
-         handle */
-      struct curl_llist_element *e;
+  /* make sure there's no pending message in the queue sent from this easy
+     handle */
 
-      for(e = multi->msglist->head; e; e = e->next) {
-        struct Curl_message *msg = e->ptr;
+  for(e = multi->msglist->head; e; e = e->next) {
+    struct Curl_message *msg = e->ptr;
 
-        if(msg->extmsg.easy_handle == easy) {
-          Curl_llist_remove(multi->msglist, e, NULL);
-          /* there can only be one from this specific handle */
-          break;
-        }
-      }
+    if(msg->extmsg.easy_handle == easy) {
+      Curl_llist_remove(multi->msglist, e, NULL);
+      /* there can only be one from this specific handle */
+      break;
     }
+  }
 
-    /* make the previous node point to our next */
-    if(data->prev)
-      data->prev->next = data->next;
-    else
-      multi->easyp = data->next; /* point to first node */
-
-    /* make our next point to our previous node */
-    if(data->next)
-      data->next->prev = data->prev;
-    else
-      multi->easylp = data->prev; /* point to last node */
+  /* make the previous node point to our next */
+  if(data->prev)
+    data->prev->next = data->next;
+  else
+    multi->easyp = data->next; /* point to first node */
 
-    /* NOTE NOTE NOTE
-       We do not touch the easy handle here! */
+  /* make our next point to our previous node */
+  if(data->next)
+    data->next->prev = data->prev;
+  else
+    multi->easylp = data->prev; /* point to last node */
 
-    multi->num_easy--; /* one less to care about now */
+  /* NOTE NOTE NOTE
+     We do not touch the easy handle here! */
+  multi->num_easy--; /* one less to care about now */
 
-    update_timer(multi);
-    return CURLM_OK;
-  }
-  else
-    return CURLM_BAD_EASY_HANDLE; /* twasn't found */
+  update_timer(multi);
+  return CURLM_OK;
 }
 
 bool Curl_multi_pipeline_enabled(const struct Curl_multi *multi)
@@ -927,7 +932,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
   bool protocol_connect = FALSE;
   bool dophase_done = FALSE;
   bool done = FALSE;
-  CURLMcode result = CURLM_OK;
+  CURLMcode rc;
+  CURLcode result = CURLE_OK;
   struct SingleRequest *k;
   long timeout_ms;
   int control;
@@ -936,26 +942,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     return CURLM_BAD_EASY_HANDLE;
 
   do {
-    /* this is a single-iteration do-while loop just to allow a
-       break to skip to the end of it */
     bool disconnect_conn = FALSE;
+    rc = CURLM_OK;
 
     /* Handle the case when the pipe breaks, i.e., the connection
        we're using gets cleaned up and we're left with nothing. */
     if(data->state.pipe_broke) {
-      infof(data, "Pipe broke: handle 0x%p, url = %s\n",
+      infof(data, "Pipe broke: handle %p, url = %s\n",
             (void *)data, data->state.path);
 
       if(data->mstate < CURLM_STATE_COMPLETED) {
         /* Head back to the CONNECT state */
         multistate(data, CURLM_STATE_CONNECT);
-        result = CURLM_CALL_MULTI_PERFORM;
-        data->result = CURLE_OK;
+        rc = CURLM_CALL_MULTI_PERFORM;
+        result = CURLE_OK;
       }
 
       data->state.pipe_broke = FALSE;
       data->easy_conn = NULL;
-      break;
+      continue;
     }
 
     if(!data->easy_conn &&
@@ -1008,26 +1013,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
           }
         }
 
-        /* Force the connection closed because the server could continue to
-           send us stuff at any time. (The disconnect_conn logic used below
-           doesn't work at this point). */
-        connclose(data->easy_conn, "Disconnected with pending data");
-        data->result = CURLE_OPERATION_TIMEDOUT;
-        multistate(data, CURLM_STATE_COMPLETED);
-        break;
+        /* Force connection closed if the connection has indeed been used */
+        if(data->mstate > CURLM_STATE_DO) {
+          connclose(data->easy_conn, "Disconnected with pending data");
+          disconnect_conn = TRUE;
+        }
+        result = CURLE_OPERATION_TIMEDOUT;
+        /* Skip the statemachine and go directly to error handling section. */
+        goto statemachine_end;
       }
     }
 
     switch(data->mstate) {
     case CURLM_STATE_INIT:
       /* init this transfer. */
-      data->result=Curl_pretransfer(data);
+      result=Curl_pretransfer(data);
 
-      if(CURLE_OK == data->result) {
+      if(!result) {
         /* after init, go CONNECT */
         multistate(data, CURLM_STATE_CONNECT);
         Curl_pgrsTime(data, TIMER_STARTOP);
-        result = CURLM_CALL_MULTI_PERFORM;
+        rc = CURLM_CALL_MULTI_PERFORM;
       }
       break;
 
@@ -1039,20 +1045,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     case CURLM_STATE_CONNECT:
       /* Connect. We want to get a connection identifier filled in. */
       Curl_pgrsTime(data, TIMER_STARTSINGLE);
-      data->result = Curl_connect(data, &data->easy_conn,
-                                  &async, &protocol_connect);
-      if(CURLE_NO_CONNECTION_AVAILABLE == data->result) {
+      result = Curl_connect(data, &data->easy_conn,
+                            &async, &protocol_connect);
+      if(CURLE_NO_CONNECTION_AVAILABLE == result) {
         /* There was no connection available. We will go to the pending
            state and wait for an available connection. */
         multistate(data, CURLM_STATE_CONNECT_PEND);
-        data->result = CURLE_OK;
+
+        /* add this handle to the list of connect-pending handles */
+        if(!Curl_llist_insert_next(multi->pending, multi->pending->tail, data))
+          result = CURLE_OUT_OF_MEMORY;
+        else
+          result = CURLE_OK;
         break;
       }
 
-      if(CURLE_OK == data->result) {
+      if(!result) {
         /* Add this handle to the send or pend pipeline */
-        data->result = Curl_add_handle_to_pipeline(data, data->easy_conn);
-        if(CURLE_OK != data->result)
+        result = Curl_add_handle_to_pipeline(data, data->easy_conn);
+        if(result)
           disconnect_conn = TRUE;
         else {
           if(async)
@@ -1062,7 +1073,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
             /* after the connect has been sent off, go WAITCONNECT unless the
                protocol connect is already done and we can go directly to
                WAITDO or DO! */
-            result = CURLM_CALL_MULTI_PERFORM;
+            rc = CURLM_CALL_MULTI_PERFORM;
 
             if(protocol_connect)
               multistate(data, multi->pipelining_enabled?
@@ -1084,9 +1095,29 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       /* awaiting an asynch name resolve to complete */
     {
       struct Curl_dns_entry *dns = NULL;
+      struct connectdata *conn = data->easy_conn;
 
       /* check if we have the name resolved by now */
-      data->result = Curl_resolver_is_resolved(data->easy_conn, &dns);
+      if(data->share)
+        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+      dns = Curl_fetch_addr(conn, conn->host.name, (int)conn->port);
+
+      if(dns) {
+        dns->inuse++; /* we use it! */
+#ifdef CURLRES_ASYNCH
+        conn->async.dns = dns;
+        conn->async.done = TRUE;
+#endif
+        result = CURLE_OK;
+        infof(data, "Hostname was found in DNS cache\n");
+      }
+
+      if(data->share)
+        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+      if(!dns)
+        result = Curl_resolver_is_resolved(data->easy_conn, &dns);
 
       /* Update sockets here, because the socket(s) may have been
          closed and the application thus needs to be told, even if it
@@ -1099,16 +1130,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       if(dns) {
         /* Perform the next step in the connection phase, and then move on
            to the WAITCONNECT state */
-        data->result = Curl_async_resolved(data->easy_conn,
-                                           &protocol_connect);
+        result = Curl_async_resolved(data->easy_conn, &protocol_connect);
 
-        if(CURLE_OK != data->result)
+        if(result)
           /* if Curl_async_resolved() returns failure, the connection struct
              is already freed and gone */
           data->easy_conn = NULL;           /* no more connection */
         else {
           /* call again please so that we get the next socket setup */
-          result = CURLM_CALL_MULTI_PERFORM;
+          rc = CURLM_CALL_MULTI_PERFORM;
           if(protocol_connect)
             multistate(data, multi->pipelining_enabled?
                        CURLM_STATE_WAITDO:CURLM_STATE_DO);
@@ -1123,7 +1153,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         }
       }
 
-      if(CURLE_OK != data->result) {
+      if(result) {
         /* failure detected */
         disconnect_conn = TRUE;
         break;
@@ -1134,19 +1164,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
 #ifndef CURL_DISABLE_HTTP
     case CURLM_STATE_WAITPROXYCONNECT:
       /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
-      data->result = Curl_http_connect(data->easy_conn, &protocol_connect);
+      result = Curl_http_connect(data->easy_conn, &protocol_connect);
 
       if(data->easy_conn->bits.proxy_connect_closed) {
-        /* reset the error buffer */
-        if(data->set.errorbuffer)
-          data->set.errorbuffer[0] = '\0';
-        data->state.errorbuf = FALSE;
-
-        data->result = CURLE_OK;
-        result = CURLM_CALL_MULTI_PERFORM;
+        /* connect back to proxy again */
+        result = CURLE_OK;
+        rc = CURLM_CALL_MULTI_PERFORM;
         multistate(data, CURLM_STATE_CONNECT);
       }
-      else if(CURLE_OK == data->result) {
+      else if(!result) {
         if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
           multistate(data, CURLM_STATE_WAITCONNECT);
       }
@@ -1155,19 +1181,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
 
     case CURLM_STATE_WAITCONNECT:
       /* awaiting a completion of an asynch connect */
-      data->result = Curl_is_connected(data->easy_conn,
-                                       FIRSTSOCKET,
-                                       &connected);
+      result = Curl_is_connected(data->easy_conn,
+                                 FIRSTSOCKET,
+                                 &connected);
       if(connected) {
 
-        if(!data->result)
+        if(!result)
           /* if everything is still fine we do the protocol-specific connect
              setup */
-          data->result = Curl_protocol_connect(data->easy_conn,
-                                               &protocol_connect);
+          result = Curl_protocol_connect(data->easy_conn,
+                                         &protocol_connect);
       }
 
-      if(CURLE_OK != data->result) {
+      if(data->easy_conn->bits.proxy_connect_closed) {
+        /* connect back to proxy again since it was closed in a proxy CONNECT
+           setup */
+        result = CURLE_OK;
+        rc = CURLM_CALL_MULTI_PERFORM;
+        multistate(data, CURLM_STATE_CONNECT);
+        break;
+      }
+      else if(result) {
         /* failure detected */
         /* Just break, the cleaning up is handled all in one place */
         disconnect_conn = TRUE;
@@ -1194,24 +1228,23 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
           multistate(data, multi->pipelining_enabled?
                      CURLM_STATE_WAITDO:CURLM_STATE_DO);
 
-        result = CURLM_CALL_MULTI_PERFORM;
+        rc = CURLM_CALL_MULTI_PERFORM;
       }
       break;
 
     case CURLM_STATE_PROTOCONNECT:
       /* protocol-specific connect phase */
-      data->result = Curl_protocol_connecting(data->easy_conn,
-                                              &protocol_connect);
-      if((data->result == CURLE_OK) && protocol_connect) {
+      result = Curl_protocol_connecting(data->easy_conn, &protocol_connect);
+      if(!result && protocol_connect) {
         /* after the connect has completed, go WAITDO or DO */
         multistate(data, multi->pipelining_enabled?
                    CURLM_STATE_WAITDO:CURLM_STATE_DO);
-        result = CURLM_CALL_MULTI_PERFORM;
+        rc = CURLM_CALL_MULTI_PERFORM;
       }
-      else if(data->result) {
+      else if(result) {
         /* failure detected */
         Curl_posttransfer(data);
-        Curl_done(&data->easy_conn, data->result, TRUE);
+        Curl_done(&data->easy_conn, result, TRUE);
         disconnect_conn = TRUE;
       }
       break;
@@ -1232,7 +1265,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         /* Grab the channel */
         data->easy_conn->writechannel_inuse = TRUE;
         multistate(data, CURLM_STATE_DO);
-        result = CURLM_CALL_MULTI_PERFORM;
+        rc = CURLM_CALL_MULTI_PERFORM;
       }
       break;
 
@@ -1241,16 +1274,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         /* keep connection open for application to use the socket */
         connkeep(data->easy_conn, "CONNECT_ONLY");
         multistate(data, CURLM_STATE_DONE);
-        data->result = CURLE_OK;
-        result = CURLM_CALL_MULTI_PERFORM;
+        result = CURLE_OK;
+        rc = CURLM_CALL_MULTI_PERFORM;
       }
       else {
         /* Perform the protocol's DO action */
-        data->result = Curl_do(&data->easy_conn, &dophase_done);
+        result = Curl_do(&data->easy_conn, &dophase_done);
 
         /* When Curl_do() returns failure, data->easy_conn might be NULL! */
 
-        if(CURLE_OK == data->result) {
+        if(!result) {
           if(!dophase_done) {
             /* some steps needed for wildcard matching */
             if(data->set.wildcardmatch) {
@@ -1259,14 +1292,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
                 /* skip some states if it is important */
                 Curl_done(&data->easy_conn, CURLE_OK, FALSE);
                 multistate(data, CURLM_STATE_DONE);
-                result = CURLM_CALL_MULTI_PERFORM;
+                rc = CURLM_CALL_MULTI_PERFORM;
                 break;
               }
             }
             /* DO was not completed in one function call, we must continue
                DOING... */
             multistate(data, CURLM_STATE_DOING);
-            result = CURLM_OK;
+            rc = CURLM_OK;
           }
 
           /* after DO, go DO_DONE... or DO_MORE */
@@ -1274,15 +1307,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
             /* we're supposed to do more, but we need to sit down, relax
                and wait a little while first */
             multistate(data, CURLM_STATE_DO_MORE);
-            result = CURLM_OK;
+            rc = CURLM_OK;
           }
           else {
             /* we're done with the DO, now DO_DONE */
             multistate(data, CURLM_STATE_DO_DONE);
-            result = CURLM_CALL_MULTI_PERFORM;
+            rc = CURLM_CALL_MULTI_PERFORM;
           }
         }
-        else if((CURLE_SEND_ERROR == data->result) &&
+        else if((CURLE_SEND_ERROR == result) &&
                 data->easy_conn->bits.reuse) {
           /*
            * In this situation, a connection that we were trying to use
@@ -1297,48 +1330,49 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
           drc = Curl_retry_request(data->easy_conn, &newurl);
           if(drc) {
             /* a failure here pretty much implies an out of memory */
-            data->result = drc;
+            result = drc;
             disconnect_conn = TRUE;
           }
           else
             retry = (newurl)?TRUE:FALSE;
 
           Curl_posttransfer(data);
-          drc = Curl_done(&data->easy_conn, data->result, FALSE);
+          drc = Curl_done(&data->easy_conn, result, FALSE);
 
           /* When set to retry the connection, we must to go back to
            * the CONNECT state */
           if(retry) {
-            if((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) {
+            if(!drc || (drc == CURLE_SEND_ERROR)) {
               follow = FOLLOW_RETRY;
               drc = Curl_follow(data, newurl, follow);
-              if(drc == CURLE_OK) {
+              if(!drc) {
                 multistate(data, CURLM_STATE_CONNECT);
-                result = CURLM_CALL_MULTI_PERFORM;
-                data->result = CURLE_OK;
+                rc = CURLM_CALL_MULTI_PERFORM;
+                result = CURLE_OK;
               }
               else {
                 /* Follow failed */
-                data->result = drc;
+                result = drc;
                 free(newurl);
               }
             }
             else {
               /* done didn't return OK or SEND_ERROR */
-              data->result = drc;
+              result = drc;
               free(newurl);
             }
           }
           else {
             /* Have error handler disconnect conn if we can't retry */
             disconnect_conn = TRUE;
+            free(newurl);
           }
         }
         else {
           /* failure detected */
           Curl_posttransfer(data);
           if(data->easy_conn)
-            Curl_done(&data->easy_conn, data->result, FALSE);
+            Curl_done(&data->easy_conn, result, FALSE);
           disconnect_conn = TRUE;
         }
       }
@@ -1346,21 +1380,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
 
     case CURLM_STATE_DOING:
       /* we continue DOING until the DO phase is complete */
-      data->result = Curl_protocol_doing(data->easy_conn,
-                                         &dophase_done);
-      if(CURLE_OK == data->result) {
+      result = Curl_protocol_doing(data->easy_conn,
+                                   &dophase_done);
+      if(!result) {
         if(dophase_done) {
           /* after DO, go DO_DONE or DO_MORE */
           multistate(data, data->easy_conn->bits.do_more?
                      CURLM_STATE_DO_MORE:
                      CURLM_STATE_DO_DONE);
-          result = CURLM_CALL_MULTI_PERFORM;
+          rc = CURLM_CALL_MULTI_PERFORM;
         } /* dophase_done */
       }
       else {
         /* failure detected */
         Curl_posttransfer(data);
-        Curl_done(&data->easy_conn, data->result, FALSE);
+        Curl_done(&data->easy_conn, result, FALSE);
         disconnect_conn = TRUE;
       }
       break;
@@ -1369,27 +1403,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       /*
        * When we are connected, DO MORE and then go DO_DONE
        */
-      data->result = Curl_do_more(data->easy_conn, &control);
+      result = Curl_do_more(data->easy_conn, &control);
 
       /* No need to remove this handle from the send pipeline here since that
          is done in Curl_done() */
-      if(CURLE_OK == data->result) {
+      if(!result) {
         if(control) {
           /* if positive, advance to DO_DONE
              if negative, go back to DOING */
           multistate(data, control==1?
                      CURLM_STATE_DO_DONE:
                      CURLM_STATE_DOING);
-          result = CURLM_CALL_MULTI_PERFORM;
+          rc = CURLM_CALL_MULTI_PERFORM;
         }
         else
           /* stay in DO_MORE */
-          result = CURLM_OK;
+          rc = CURLM_OK;
       }
       else {
         /* failure detected */
         Curl_posttransfer(data);
-        Curl_done(&data->easy_conn, data->result, FALSE);
+        Curl_done(&data->easy_conn, result, FALSE);
         disconnect_conn = TRUE;
       }
       break;
@@ -1407,7 +1441,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         multistate(data, CURLM_STATE_WAITPERFORM);
       else
         multistate(data, CURLM_STATE_DONE);
-      result = CURLM_CALL_MULTI_PERFORM;
+      rc = CURLM_CALL_MULTI_PERFORM;
       break;
 
     case CURLM_STATE_WAITPERFORM:
@@ -1418,7 +1452,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         /* Grab the channel */
         data->easy_conn->readchannel_inuse = TRUE;
         multistate(data, CURLM_STATE_PERFORM);
-        result = CURLM_CALL_MULTI_PERFORM;
+        rc = CURLM_CALL_MULTI_PERFORM;
       }
 #ifdef DEBUGBUILD
       else {
@@ -1435,9 +1469,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
       /* if both rates are within spec, resume transfer */
       if(Curl_pgrsUpdate(data->easy_conn))
-        data->result = CURLE_ABORTED_BY_CALLBACK;
+        result = CURLE_ABORTED_BY_CALLBACK;
       else
-        data->result = Curl_speedcheck(data, now);
+        result = Curl_speedcheck(data, now);
 
       if(( (data->set.max_send_speed == 0) ||
            (data->progress.ulspeed < data->set.max_send_speed ))  &&
@@ -1447,7 +1481,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       break;
 
     case CURLM_STATE_PERFORM:
-      {
+    {
       char *newurl = NULL;
       bool retry = FALSE;
 
@@ -1463,7 +1497,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
                            data->set.buffer_size : BUFSIZE);
         timeout_ms = Curl_sleep_time(data->set.max_send_speed,
                                      data->progress.ulspeed, buffersize);
-        Curl_expire(data, timeout_ms);
+        Curl_expire_latest(data, timeout_ms);
         break;
       }
 
@@ -1474,17 +1508,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
 
         multistate(data, CURLM_STATE_TOOFAST);
 
-         /* Calculate download rate-limitation timeout. */
+        /* Calculate download rate-limitation timeout. */
         buffersize = (int)(data->set.buffer_size ?
                            data->set.buffer_size : BUFSIZE);
         timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
                                      data->progress.dlspeed, buffersize);
-        Curl_expire(data, timeout_ms);
+        Curl_expire_latest(data, timeout_ms);
         break;
       }
 
       /* read/write data if it is ready to do so */
-      data->result = Curl_readwrite(data->easy_conn, &done);
+      result = Curl_readwrite(data->easy_conn, &done);
 
       k = &data->req;
 
@@ -1498,7 +1532,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         data->easy_conn->writechannel_inuse = FALSE;
       }
 
-      if(done || (data->result == CURLE_RECV_ERROR)) {
+      if(done || (result == CURLE_RECV_ERROR)) {
         /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
          * condition and the server closed the re-used connection exactly when
          * we wanted to use it, so figure out if that is indeed the case.
@@ -1510,12 +1544,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         if(retry) {
           /* if we are to retry, set the result to OK and consider the
              request as done */
-          data->result = CURLE_OK;
+          result = CURLE_OK;
           done = TRUE;
         }
       }
 
-      if(data->result) {
+      if(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
@@ -1528,7 +1562,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
           connclose(data->easy_conn, "Transfer returned error");
 
         Curl_posttransfer(data);
-        Curl_done(&data->easy_conn, data->result, FALSE);
+        Curl_done(&data->easy_conn, result, FALSE);
       }
       else if(done) {
         followtype follow=FOLLOW_NONE;
@@ -1541,7 +1575,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
 
         /* expire the new receiving pipeline head */
         if(data->easy_conn->recv_pipe->head)
-          Curl_expire(data->easy_conn->recv_pipe->head->ptr, 1);
+          Curl_expire_latest(data->easy_conn->recv_pipe->head->ptr, 1);
 
         /* Check if we can move pending requests to send pipe */
         Curl_multi_process_pending_handles(multi);
@@ -1552,18 +1586,20 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
           if(!retry) {
             /* if the URL is a follow-location and not just a retried request
                then figure out the URL here */
+            if(newurl)
+              free(newurl);
             newurl = data->req.newurl;
             data->req.newurl = NULL;
             follow = FOLLOW_REDIR;
           }
           else
             follow = FOLLOW_RETRY;
-          data->result = Curl_done(&data->easy_conn, CURLE_OK, FALSE);
-          if(CURLE_OK == data->result) {
-            data->result = Curl_follow(data, newurl, follow);
-            if(CURLE_OK == data->result) {
+          result = Curl_done(&data->easy_conn, CURLE_OK, FALSE);
+          if(!result) {
+            result = Curl_follow(data, newurl, follow);
+            if(!result) {
               multistate(data, CURLM_STATE_CONNECT);
-              result = CURLM_CALL_MULTI_PERFORM;
+              rc = CURLM_CALL_MULTI_PERFORM;
               newurl = NULL; /* handed over the memory ownership to
                                 Curl_follow(), make sure we don't free() it
                                 here */
@@ -1580,26 +1616,26 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
               free(newurl);
             newurl = data->req.location;
             data->req.location = NULL;
-            data->result = Curl_follow(data, newurl, FOLLOW_FAKE);
-            if(CURLE_OK == data->result)
+            result = Curl_follow(data, newurl, FOLLOW_FAKE);
+            if(!result)
               newurl = NULL; /* allocation was handed over Curl_follow() */
             else
               disconnect_conn = TRUE;
           }
 
           multistate(data, CURLM_STATE_DONE);
-          result = CURLM_CALL_MULTI_PERFORM;
+          rc = CURLM_CALL_MULTI_PERFORM;
         }
       }
 
       if(newurl)
         free(newurl);
       break;
-      }
+    }
 
     case CURLM_STATE_DONE:
       /* this state is highly transient, so run another loop after this */
-      result = CURLM_CALL_MULTI_PERFORM;
+      rc = CURLM_CALL_MULTI_PERFORM;
 
       if(data->easy_conn) {
         CURLcode res;
@@ -1610,11 +1646,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         Curl_multi_process_pending_handles(multi);
 
         /* post-transfer command */
-        res = Curl_done(&data->easy_conn, data->result, FALSE);
+        res = Curl_done(&data->easy_conn, result, FALSE);
 
         /* allow a previously set error code take precedence */
-        if(!data->result)
-          data->result = res;
+        if(!result)
+          result = res;
 
         /*
          * If there are other handles on the pipeline, Curl_done won't set
@@ -1654,14 +1690,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       break;
 
     case CURLM_STATE_MSGSENT:
+      data->result = result;
       return CURLM_OK; /* do nothing */
 
     default:
       return CURLM_INTERNAL_ERROR;
     }
+    statemachine_end:
 
     if(data->mstate < CURLM_STATE_COMPLETED) {
-      if(CURLE_OK != data->result) {
+      if(result) {
         /*
          * If an error was returned, and we aren't in completed state now,
          * then we go to completed and consider this transfer aborted.
@@ -1676,16 +1714,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
           /* if this has a connection, unsubscribe from the pipelines */
           data->easy_conn->writechannel_inuse = FALSE;
           data->easy_conn->readchannel_inuse = FALSE;
-          Curl_removeHandleFromPipeline(data,
-                                        data->easy_conn->send_pipe);
-          Curl_removeHandleFromPipeline(data,
-                                        data->easy_conn->recv_pipe);
+          Curl_removeHandleFromPipeline(data, data->easy_conn->send_pipe);
+          Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
           /* Check if we can move pending requests to send pipe */
           Curl_multi_process_pending_handles(multi);
 
           if(disconnect_conn) {
+            /* Don't attempt to send data over a connection that timed out */
+            bool dead_connection = result == CURLE_OPERATION_TIMEDOUT;
             /* disconnect properly */
-            Curl_disconnect(data->easy_conn, /* dead_connection */ FALSE);
+            Curl_disconnect(data->easy_conn, dead_connection);
 
             /* 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
@@ -1704,31 +1742,34 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       else if(data->easy_conn && Curl_pgrsUpdate(data->easy_conn)) {
         /* aborted due to progress callback return code must close the
            connection */
-        data->result = CURLE_ABORTED_BY_CALLBACK;
+        result = CURLE_ABORTED_BY_CALLBACK;
         connclose(data->easy_conn, "Aborted by callback");
 
         /* if not yet in DONE state, go there, otherwise COMPLETED */
         multistate(data, (data->mstate < CURLM_STATE_DONE)?
                    CURLM_STATE_DONE: CURLM_STATE_COMPLETED);
-        result = CURLM_CALL_MULTI_PERFORM;
+        rc = CURLM_CALL_MULTI_PERFORM;
       }
     }
-  } WHILE_FALSE; /* just to break out from! */
 
-  if(CURLM_STATE_COMPLETED == data->mstate) {
-    /* now fill in the Curl_message with this info */
-    msg = &data->msg;
+    if(CURLM_STATE_COMPLETED == data->mstate) {
+      /* now fill in the Curl_message with this info */
+      msg = &data->msg;
 
-    msg->extmsg.msg = CURLMSG_DONE;
-    msg->extmsg.easy_handle = data;
-    msg->extmsg.data.result = data->result;
+      msg->extmsg.msg = CURLMSG_DONE;
+      msg->extmsg.easy_handle = data;
+      msg->extmsg.data.result = result;
 
-    result = multi_addmsg(multi, msg);
+      rc = multi_addmsg(multi, msg);
 
-    multistate(data, CURLM_STATE_MSGSENT);
-  }
+      multistate(data, CURLM_STATE_MSGSENT);
+    }
+  } while(rc == CURLM_CALL_MULTI_PERFORM);
 
-  return result;
+  data->result = result;
+
+
+  return rc;
 }
 
 
@@ -1758,9 +1799,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
     }
 
     sigpipe_ignore(data, &pipe_st);
-    do
-      result = multi_runsingle(multi, now, data);
-    while(CURLM_CALL_MULTI_PERFORM == result);
+    result = multi_runsingle(multi, now, data);
     sigpipe_restore(&pipe_st);
 
     if(data->set.wildcardmatch) {
@@ -1843,18 +1882,12 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
                            multi->closure_handle->dns.hostcache);
 
       Curl_close(multi->closure_handle);
-      multi->closure_handle = NULL;
     }
 
     Curl_hash_destroy(multi->sockhash);
-    multi->sockhash = NULL;
-
     Curl_conncache_destroy(multi->conn_cache);
-    multi->conn_cache = NULL;
-
-    /* remove the pending list of messages */
     Curl_llist_destroy(multi->msglist, NULL);
-    multi->msglist = NULL;
+    Curl_llist_destroy(multi->pending, NULL);
 
     /* remove all easy handles */
     data = multi->easyp;
@@ -1875,7 +1908,6 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
     }
 
     Curl_hash_destroy(multi->hostcache);
-    multi->hostcache = NULL;
 
     /* Free the blacklists by setting them to NULL */
     Curl_pipeline_set_site_blacklist(NULL, &multi->pipelining_site_bl);
@@ -2084,7 +2116,7 @@ static void singlesocket(struct Curl_multi *multi,
  * Curl_multi_closed()
  *
  * Used by the connect code to tell the multi_socket code that one of the
- * sockets we were using have just been closed.  This function will then
+ * sockets we were using is about to be closed.  This function will then
  * remove it from the sockethash for this handle to make the multi_socket API
  * behave properly, especially for the case when libcurl will create another
  * socket again and it gets the same file descriptor number.
@@ -2237,9 +2269,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
         data->easy_conn->cselect_bits = ev_bitmask;
 
       sigpipe_ignore(data, &pipe_st);
-      do
-        result = multi_runsingle(multi, now, data);
-      while(CURLM_CALL_MULTI_PERFORM == result);
+      result = multi_runsingle(multi, now, data);
       sigpipe_restore(&pipe_st);
 
       if(data->easy_conn &&
@@ -2281,9 +2311,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
       SIGPIPE_VARIABLE(pipe_st);
 
       sigpipe_ignore(data, &pipe_st);
-      do
-        result = multi_runsingle(multi, now, data);
-      while(CURLM_CALL_MULTI_PERFORM == result);
+      result = multi_runsingle(multi, now, data);
       sigpipe_restore(&pipe_st);
 
       if(CURLM_OK >= result)
@@ -2486,12 +2514,6 @@ static int update_timer(struct Curl_multi *multi)
   return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp);
 }
 
-void Curl_multi_set_easy_connection(struct SessionHandle *handle,
-                                    struct connectdata *conn)
-{
-  handle->easy_conn = conn;
-}
-
 static bool isHandleAtHead(struct SessionHandle *handle,
                            struct curl_llist *pipeline)
 {
@@ -2654,6 +2676,46 @@ void Curl_expire(struct SessionHandle *data, long milli)
 #endif
 }
 
+/*
+ * Curl_expire_latest()
+ *
+ * This is like Curl_expire() but will only add a timeout node to the list of
+ * timers if there is no timeout that will expire before the given time.
+ *
+ * Use this function if the code logic risks calling this function many times
+ * or if there's no particular conditional wait in the code for this specific
+ * time-out period to expire.
+ *
+ */
+void Curl_expire_latest(struct SessionHandle *data, long milli)
+{
+  struct timeval *expire = &data->state.expiretime;
+
+  struct timeval set;
+
+  set = Curl_tvnow();
+  set.tv_sec += milli / 1000;
+  set.tv_usec += (milli % 1000) * 1000;
+
+  if(set.tv_usec >= 1000000) {
+    set.tv_sec++;
+    set.tv_usec -= 1000000;
+  }
+
+  if(expire->tv_sec || expire->tv_usec) {
+    /* This means that the struct is added as a node in the splay tree.
+       Compare if the new time is earlier, and only remove-old/add-new if it
+         is. */
+    long diff = curlx_tvdiff(set, *expire);
+    if(diff > 0)
+      /* the new expire time was later than the top time, so just skip this */
+      return;
+  }
+
+  /* Just add the timeout like normal */
+  Curl_expire(data, milli);
+}
+
 CURLMcode curl_multi_assign(CURLM *multi_handle,
                             curl_socket_t s, void *hashp)
 {
@@ -2708,16 +2770,23 @@ struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi)
 
 void Curl_multi_process_pending_handles(struct Curl_multi *multi)
 {
-  struct SessionHandle *data;
+  struct curl_llist_element *e = multi->pending->head;
+
+  while(e) {
+    struct SessionHandle *data = e->ptr;
+    struct curl_llist_element *next = e->next;
 
-  data=multi->easyp;
-  while(data) {
     if(data->mstate == CURLM_STATE_CONNECT_PEND) {
       multistate(data, CURLM_STATE_CONNECT);
+
+      /* Remove this node from the list */
+      Curl_llist_remove(multi->pending, e, NULL);
+
       /* Make sure that the handle will be processed soonish. */
-      Curl_expire(data, 1);
+      Curl_expire_latest(data, 1);
     }
-    data = data->next; /* operate on next handle */
+
+    e = next; /* operate on next handle */
   }
 }