From 75c96e58ef2a94e714256acbbcd7c98951c753e5 Mon Sep 17 00:00:00 2001
From: Carlo Wood <carlo@alinoe.com>
Date: Mon, 10 Nov 2014 10:51:54 +0100
Subject: [PATCH] pipelining: Fix connection handling under timeouts.

Bug in multi_runsingle(); when a request times out and then setting
CURLM_STATE_COMPLETED, followed by a break causes the code to continue
at the end of the function where the message is sent to the user with
multi_addmsg() after which the state is set to CURLM_STATE_MSGSENT.

This commit sets the state to CURLM_STATE_DONE (unless already in that
state) and does NOT break out of the loop. As a result the easy handle
is cleaned up as a handle with an error, as it should. It's connection
is closed.

Also, this change makes pipelining not considered for new requests when
the connection is already marked for closure.

Bug: http://curl.haxx.se/bug/view.cgi?id=1420
---
 lib/multi.c | 17 +++++++++++------
 lib/url.c   |  3 ++-
 2 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/lib/multi.c b/lib/multi.c
index fddb4c5..7352342 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -1007,17 +1007,22 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
                   Curl_tvdiff(now, data->progress.t_startsingle),
                   k->bytecount);
           }
         }
 
-        /* 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");
+        /* Force the connection closed when the server could continue to
+           send us stuff at any time. */
+        if((data->mstate >= CURLM_STATE_DO) &&
+           (data->mstate <= CURLM_STATE_DONE))
+          connclose(data->easy_conn, "Disconnected with pending data");
+
         data->result = CURLE_OPERATION_TIMEDOUT;
-        multistate(data, CURLM_STATE_COMPLETED);
-        break;
+        if(data->mstate != CURLM_STATE_DONE)
+          multistate(data, CURLM_STATE_DONE);
+        else
+          /* if already in DONE, go COMPLETED */
+          multistate(data, CURLM_STATE_COMPLETED);
       }
     }
 
     switch(data->mstate) {
     case CURLM_STATE_INIT:
diff --git a/lib/url.c b/lib/url.c
index 5b19f89..a4c24db 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -2728,11 +2728,12 @@ static bool SocketIsDead(curl_socket_t sock)
 }
 
 static bool IsPipeliningPossible(const struct SessionHandle *handle,
                                  const struct connectdata *conn)
 {
-  if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+  if(!conn->bits.close &&
+     (conn->handler->protocol & PROTO_FAMILY_HTTP) &&
      Curl_multi_pipeline_enabled(handle->multi) &&
      (handle->set.httpreq == HTTPREQ_GET ||
       handle->set.httpreq == HTTPREQ_HEAD) &&
      handle->set.httpversion != CURL_HTTP_VERSION_1_0)
     return TRUE;
-- 
2.1.3

