]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
timeout handling: auto-detect effective timeout
authorStefan Eissing <stefan@eissing.org>
Mon, 19 Jan 2026 10:38:35 +0000 (11:38 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 20 Jan 2026 15:43:45 +0000 (16:43 +0100)
When checking a transfer for being expired via `Curl_timeleft_ms()`,
eleminate the `bool connecting` parameter and have the function check
the `mstate` of the transfer instead.

Advantages:
* eleminate the caller needing awareness if the transfer is
  connecting or in a later state
* fix pingpong timeout handling to check the correct timeout
  during "proto_connect" phases
* avoid using "connecting" timeouts during establishing a secondary
  connection (e.g. FTP) since this would use the timestamp from
  the original, primary connect and thus be wrong

Reported-by: Wyuer on github
Fixes #20347
Closes #20354

25 files changed:
lib/asyn-ares.c
lib/cf-h1-proxy.c
lib/cf-h2-proxy.c
lib/cf-ip-happy.c
lib/cf-socket.c
lib/cfilters.c
lib/connect.c
lib/connect.h
lib/cshutdn.c
lib/doh.c
lib/ftp.c
lib/gopher.c
lib/multi.c
lib/multiif.h
lib/pingpong.c
lib/pingpong.h
lib/socks.c
lib/tftp.c
lib/transfer.c
lib/url.c
lib/vssh/libssh.c
lib/vssh/libssh2.c
lib/vtls/schannel.c
lib/ws.c
tests/unit/unit1303.c

index a7af7bbfbb1bca103858837ab58e347010b36a90..526d2cd93632c9abed5073d239f2ededf55d7a08 100644 (file)
@@ -392,7 +392,7 @@ CURLcode Curl_async_await(struct Curl_easy *data,
   DEBUGASSERT(entry);
   *entry = NULL; /* clear on entry */
 
-  timeout_ms = Curl_timeleft_ms(data, TRUE);
+  timeout_ms = Curl_timeleft_ms(data);
   if(timeout_ms < 0) {
     /* already expired! */
     connclose(data->conn, "Timed out before name resolve started");
index fcf2b70ed70627015464990e10c85c8995ebb8f9..ccc8349b1b80ec3cbeaa0f012bd0baa599ffd267 100644 (file)
@@ -571,7 +571,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
 
   do {
 
-    if(Curl_timeleft_ms(data, TRUE) < 0) {
+    if(Curl_timeleft_ms(data) < 0) {
       failf(data, "Proxy CONNECT aborted due to timeout");
       result = CURLE_OPERATION_TIMEDOUT;
       goto out;
index f46a074afa765d2c7d0af68e46bb23d6d0b63550..8a0173c44a4930e9782bc52d8f4a1908fc7a547c 100644 (file)
@@ -1058,7 +1058,7 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
   }
   DEBUGASSERT(ts->authority);
 
-  if(Curl_timeleft_ms(data, TRUE) < 0) {
+  if(Curl_timeleft_ms(data) < 0) {
     failf(data, "Proxy CONNECT aborted due to timeout");
     result = CURLE_OPERATION_TIMEDOUT;
     goto out;
index 8db8c15b9f5b18ea484ffba668cb338ba4f3fb06..3ab349c8f6174a5125ec5d0795131d83f4b9d96a 100644 (file)
@@ -496,8 +496,8 @@ out:
     bool more_possible;
 
     /* when do we need to be called again? */
-    next_expire_ms = Curl_timeleft_ms(data, TRUE);
-    if(next_expire_ms <= 0) {
+    next_expire_ms = Curl_timeleft_ms(data);
+    if(next_expire_ms < 0) {
       failf(data, "Connection timeout after %" FMT_OFF_T " ms",
         curlx_ptimediff_ms(Curl_pgrs_now(data),
                            &data->progress.t_startsingle));
@@ -699,7 +699,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
   if(!dns)
     return CURLE_FAILED_INIT;
 
-  if(Curl_timeleft_ms(data, TRUE) < 0) {
+  if(Curl_timeleft_ms(data) < 0) {
     /* a precaution, no need to continue if time already is up */
     failf(data, "Connection time-out");
     return CURLE_OPERATION_TIMEDOUT;
index c1863d17de442cfee6a8342e074dc01b348e0a53..ebad57f371d0c39d510610eafb2c68453a67d6ed 100644 (file)
@@ -1955,7 +1955,7 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
 #endif
 
   /* check if the generic timeout possibly is set shorter */
-  other_ms = Curl_timeleft_ms(data, FALSE);
+  other_ms = Curl_timeleft_ms(data);
   if(other_ms && (other_ms < timeout_ms))
     /* note that this also works fine for when other_ms happens to be negative
        due to it already having elapsed */
index 47c05c87a29a319b70d0c76aeca5aaba8f7e48f5..8b21cffd5e48a6cd4cf6a74d74fcb6c72c052c76 100644 (file)
@@ -177,8 +177,6 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
 
   *done = FALSE;
   if(!Curl_shutdown_started(data, sockindex)) {
-    CURL_TRC_M(data, "shutdown start on%s connection",
-               sockindex ? " secondary" : "");
     Curl_shutdown_start(data, sockindex, 0);
   }
   else {
@@ -552,7 +550,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
       goto out;
     else {
       /* check allowed time left */
-      const timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
+      const timediff_t timeout_ms = Curl_timeleft_ms(data);
       curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
       int rc;
 
index a13405450bb1f4602cd5d7f43512dd5320d5fb22..09231e3117f66087dac09460d1549d562268dcdd 100644 (file)
@@ -100,25 +100,27 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *cstr)
  * transfer/connection. If the value is 0, there is no timeout (ie there is
  * infinite time left). If the value is negative, the timeout time has already
  * elapsed.
- * @param data the transfer to check on
- * @param duringconnect TRUE iff connect timeout is also taken into account.
  * @unittest: 1303
  */
 timediff_t Curl_timeleft_now_ms(struct Curl_easy *data,
-                                const struct curltime *pnow,
-                                bool duringconnect)
+                                const struct curltime *pnow)
 {
   timediff_t timeleft_ms = 0;
   timediff_t ctimeleft_ms = 0;
-  timediff_t ctimeout_ms;
-
-  /* The duration of a connect and the total transfer are calculated from two
-     different time-stamps. It can end up with the total timeout being reached
-     before the connect timeout expires and we must acknowledge whichever
-     timeout that is reached first. The total timeout is set per entire
-     operation, while the connect timeout is set per connect. */
-  if((!data->set.timeout || data->set.connect_only) && !duringconnect)
+
+  if(Curl_shutdown_started(data, FIRSTSOCKET))
+    return Curl_shutdown_timeleft(data, data->conn, FIRSTSOCKET);
+  else if(Curl_is_connecting(data)) {
+    timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
+      data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
+    ctimeleft_ms = ctimeout_ms -
+      curlx_ptimediff_ms(pnow, &data->progress.t_startsingle);
+    if(!ctimeleft_ms)
+      ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
+  }
+  else if(!data->set.timeout || data->set.connect_only) {
     return 0; /* no timeout in place or checked, return "no limit" */
+  }
 
   if(data->set.timeout) {
     timeleft_ms = data->set.timeout -
@@ -127,25 +129,16 @@ timediff_t Curl_timeleft_now_ms(struct Curl_easy *data,
       timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
   }
 
-  if(!duringconnect)
-    return timeleft_ms; /* no connect check, this is it */
-  ctimeout_ms = (data->set.connecttimeout > 0) ?
-    data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
-  ctimeleft_ms = ctimeout_ms -
-    curlx_ptimediff_ms(pnow, &data->progress.t_startsingle);
   if(!ctimeleft_ms)
-    ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
-  if(!timeleft_ms)
-    return ctimeleft_ms; /* no general timeout, this is it */
-
-  /* return minimal time left or max amount already expired */
-  return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms;
+    return timeleft_ms;
+  else if(!timeleft_ms)
+    return ctimeleft_ms;
+  return CURLMIN(ctimeleft_ms, timeleft_ms);
 }
 
-timediff_t Curl_timeleft_ms(struct Curl_easy *data,
-                            bool duringconnect)
+timediff_t Curl_timeleft_ms(struct Curl_easy *data)
 {
-  return Curl_timeleft_now_ms(data, Curl_pgrs_now(data), duringconnect);
+  return Curl_timeleft_now_ms(data, Curl_pgrs_now(data));
 }
 
 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
@@ -162,6 +155,8 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
   /* Set a timer, unless we operate on the admin handle */
   if(data->mid)
     Curl_expire_ex(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN);
+  CURL_TRC_M(data, "shutdown start on%s connection",
+             sockindex ? " secondary" : "");
 }
 
 timediff_t Curl_shutdown_timeleft(struct Curl_easy *data,
@@ -204,8 +199,11 @@ void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)
 
 bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
 {
-  struct curltime *pt = &data->conn->shutdown.start[sockindex];
-  return (pt->tv_sec > 0) || (pt->tv_usec > 0);
+  if(data->conn) {
+    struct curltime *pt = &data->conn->shutdown.start[sockindex];
+    return (pt->tv_sec > 0) || (pt->tv_usec > 0);
+  }
+  return FALSE;
 }
 
 /* retrieves ip address and port from a sockaddr structure. note it calls
index 01e9dfc029103cd3e35180c65c7a8b7be2208310..bf13d57d42b770b91cc8e7b8e0ece1b5be397a36 100644 (file)
@@ -36,11 +36,9 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *str);
 
 /* generic function that returns how much time there is left to run, according
    to the timeouts set */
-timediff_t Curl_timeleft_ms(struct Curl_easy *data,
-                            bool duringconnect);
+timediff_t Curl_timeleft_ms(struct Curl_easy *data);
 timediff_t Curl_timeleft_now_ms(struct Curl_easy *data,
-                                const struct curltime *pnow,
-                                bool duringconnect);
+                                const struct curltime *pnow);
 
 #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
 
index 1bd62346f3b189a61acc327b72596c62a9f4a490..93182cc357b9346870a3f96fb44c210361199ab8 100644 (file)
@@ -76,6 +76,10 @@ static void cshutdn_run_once(struct Curl_easy *data,
   /* We expect to be attached when called */
   DEBUGASSERT(data->conn == conn);
 
+  if(!Curl_shutdown_started(data, FIRSTSOCKET)) {
+    Curl_shutdown_start(data, FIRSTSOCKET, 0);
+  }
+
   cshutdn_run_conn_handler(data, conn);
 
   if(conn->bits.shutdown_filters) {
index 34801c26677533c9a863809f876ef924c755e294..bcc1a0c9668562b1102f86439243abd84a5b7d04 100644 (file)
--- a/lib/doh.c
+++ b/lib/doh.c
@@ -304,8 +304,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
     goto error;
   }
 
-  timeout_ms = Curl_timeleft_ms(data, TRUE);
-  if(timeout_ms <= 0) {
+  timeout_ms = Curl_timeleft_ms(data);
+  if(timeout_ms < 0) {
     result = CURLE_OPERATION_TIMEDOUT;
     goto error;
   }
index fa08b3d5c6c1f875fa8b3353e2ab7134713e0bb4..a1e8fa00ed9c108dbf1855a6f5196161431cdde4 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -686,7 +686,7 @@ static CURLcode getftpresponse(struct Curl_easy *data,
 
   while(!*ftpcodep && !result) {
     /* check and reset timeout value every lap */
-    timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE);
+    timediff_t timeout = Curl_pp_state_timeout(data, pp);
     timediff_t interval_ms;
 
     if(timeout <= 0) {
index 3ab88ad1532d7778301f835d83b8d962426b681d..49aeb10725ae344609d6832fb125f31849dd4e03 100644 (file)
@@ -125,7 +125,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
     else
       break;
 
-    timeout_ms = Curl_timeleft_ms(data, FALSE);
+    timeout_ms = Curl_timeleft_ms(data);
     if(timeout_ms < 0) {
       result = CURLE_OPERATION_TIMEDOUT;
       break;
index 2f8cb055c60c51f64a43e2128bcd9509333bdc3b..477b0d224388e59605e721552012882f4b96cf8f 100644 (file)
@@ -351,6 +351,11 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data)
 #define multi_warn_debug(x, y) Curl_nop_stmt
 #endif
 
+bool Curl_is_connecting(struct Curl_easy *data)
+{
+  return data->mstate < MSTATE_DO;
+}
+
 static CURLMcode multi_xfers_add(struct Curl_multi *multi,
                                  struct Curl_easy *data)
 {
@@ -1720,14 +1725,13 @@ static bool multi_handle_timeout(struct Curl_easy *data,
                                  bool *stream_error,
                                  CURLcode *result)
 {
-  bool connect_timeout = data->mstate < MSTATE_DO;
   timediff_t timeout_ms;
 
-  timeout_ms = Curl_timeleft_ms(data, connect_timeout);
+  timeout_ms = Curl_timeleft_ms(data);
   if(timeout_ms < 0) {
     /* Handle timed out */
     struct curltime since;
-    if(connect_timeout)
+    if(Curl_is_connecting(data))
       since = data->progress.t_startsingle;
     else
       since = data->progress.t_startop;
index 15fc070341060ce2858ebcb03ef3fb7990586c43..52f799b7c0b5784eb00d928ebfabbe2ae9642f9c 100644 (file)
@@ -40,6 +40,7 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi);
 void Curl_set_in_callback(struct Curl_easy *data, bool value);
 bool Curl_is_in_callback(struct Curl_easy *data);
 CURLcode Curl_preconnect(struct Curl_easy *data);
+bool Curl_is_connecting(struct Curl_easy *data);
 
 void Curl_multi_connchanged(struct Curl_multi *multi);
 
index 5af53052559e8ade2aee22b5c052eb201518fe63..7de902492bcd98ee33791bbaa46c5d6044488710 100644 (file)
@@ -29,6 +29,7 @@
 #include "urldata.h"
 #include "cfilters.h"
 #include "connect.h"
+#include "multiif.h"
 #include "sendf.h"
 #include "curl_trc.h"
 #include "select.h"
@@ -40,9 +41,9 @@
 /* Returns timeout in ms. 0 or negative number means the timeout has already
    triggered */
 timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
-                                 struct pingpong *pp, bool disconnecting)
+                                 struct pingpong *pp)
 {
-  timediff_t timeout_ms; /* in milliseconds */
+  timediff_t timeout_ms, xfer_timeout_ms;
   timediff_t response_time = data->set.server_response_timeout ?
     data->set.server_response_timeout : RESP_TIMEOUT;
 
@@ -55,19 +56,10 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
      full response to arrive before we bail out */
   timeout_ms = response_time -
                curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response);
-
-  if(data->set.timeout && !disconnecting) {
-    /* if timeout is requested, find out how much overall remains */
-    timediff_t timeout2_ms = Curl_timeleft_ms(data, FALSE);
-    /* pick the lowest number */
-    timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
-  }
-
-  if(disconnecting) {
-    timediff_t total_left_ms = Curl_timeleft_ms(data, FALSE);
-    timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
-  }
-
+  /* transfer timeout can be 0, which means no timeout applies */
+  xfer_timeout_ms = Curl_timeleft_ms(data);
+  if(xfer_timeout_ms && (xfer_timeout_ms < timeout_ms))
+    return xfer_timeout_ms;
   return timeout_ms;
 }
 
@@ -82,7 +74,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
   curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int rc;
   timediff_t interval_ms;
-  timediff_t timeout_ms = Curl_pp_state_timeout(data, pp, disconnecting);
+  timediff_t timeout_ms = Curl_pp_state_timeout(data, pp);
   CURLcode result = CURLE_OK;
 
   if(timeout_ms <= 0) {
index a7292e6003213e4f15b9bd114431c1b6dd9d8ed9..33d2a9233a934347334c3c259ee4c3c8431112c2 100644 (file)
@@ -91,7 +91,7 @@ void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow);
 /* Returns timeout in ms. 0 or negative number means the timeout has already
    triggered */
 timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
-                                 struct pingpong *pp, bool disconnecting);
+                                 struct pingpong *pp);
 
 /***********************************************************************
  *
index 35765d7abaa2ccc888b1c3042fabe03e54466a42..686f0437caea41aa822dc7b4dfca2c16d6a6c62f 100644 (file)
@@ -131,7 +131,7 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf,
 
   *pnread = 0;
   for(;;) {
-    timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
+    timediff_t timeout_ms = Curl_timeleft_ms(data);
     if(timeout_ms < 0) {
       /* we already got the timeout */
       return CURLE_OPERATION_TIMEDOUT;
index 1b882efe377d20407f20fa572bf1b61df754528d..544c591cddf15d06b36d7f5cbae5cd4439a5d40f 100644 (file)
@@ -156,10 +156,9 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state)
 {
   time_t timeout;
   timediff_t timeout_ms;
-  bool start = (state->state == TFTP_STATE_START);
 
   /* Compute drop-dead time */
-  timeout_ms = Curl_timeleft_ms(state->data, start);
+  timeout_ms = Curl_timeleft_ms(state->data);
 
   if(timeout_ms < 0) {
     /* time-out, bail out, go home */
@@ -1142,8 +1141,7 @@ static timediff_t tftp_state_timeout(struct tftp_conn *state,
   if(event)
     *event = TFTP_EVENT_NONE;
 
-  timeout_ms = Curl_timeleft_ms(state->data,
-                                (state->state == TFTP_STATE_START));
+  timeout_ms = Curl_timeleft_ms(state->data);
   if(timeout_ms < 0) {
     state->error = TFTP_ERR_TIMEOUT;
     state->state = TFTP_STATE_FIN;
index 63d2e15047d9c26581e48e95078a8d34dbe346f1..ce86d22f5264eda895ad09c55aa69de8e5aab31d 100644 (file)
@@ -386,7 +386,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data)
     goto out;
 
   if(k->keepon) {
-    if(Curl_timeleft_ms(data, FALSE) < 0) {
+    if(Curl_timeleft_ms(data) < 0) {
       if(k->size != -1) {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " out of %"
index 99efe38631b4c400f63f3640b4b7360f8335215e..7e4d499e2a793ac203a31b009e7f52796cb5f3df 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -3039,7 +3039,7 @@ static CURLcode resolve_server(struct Curl_easy *data,
 {
   struct hostname *ehost;
   int eport;
-  timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
+  timediff_t timeout_ms = Curl_timeleft_ms(data);
   const char *peertype = "host";
   CURLcode result;
 
index 5a2c9203f2da97f2bbd1c2770e252fd024d1c524..191d353b03a13387a740ae9d563cfad89abc6c78 100644 (file)
@@ -2420,7 +2420,7 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data,
       if(result)
         break;
 
-      left_ms = Curl_timeleft_ms(data, FALSE);
+      left_ms = Curl_timeleft_ms(data);
       if(left_ms < 0) {
         failf(data, "Operation timed out");
         return CURLE_OPERATION_TIMEDOUT;
index a0f6cd243c050b4aa0850eb11f8e5626fde01c8d..c699a37d0e8d7ad7f9e6157644f4c2b8fb25a31a 100644 (file)
@@ -3205,7 +3205,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
       if(result)
         break;
 
-      left_ms = Curl_timeleft_ms(data, FALSE);
+      left_ms = Curl_timeleft_ms(data);
       if(left_ms < 0) {
         failf(data, "Operation timed out");
         return CURLE_OPERATION_TIMEDOUT;
index 3280fbc4af7bf841666f5b9f86d82190adb99d32..c4f566597ffe93f4d4c712939ad8530cec050f4b 100644 (file)
@@ -1797,7 +1797,7 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data,
       remaining = MAX_RENEG_BLOCK_TIME - elapsed;
 
       if(blocking) {
-        timeout_ms = Curl_timeleft_ms(data, FALSE);
+        timeout_ms = Curl_timeleft_ms(data);
 
         if(timeout_ms < 0) {
           result = CURLE_OPERATION_TIMEDOUT;
@@ -1950,7 +1950,7 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     while(len > *pnwritten) {
       size_t this_write = 0;
       int what;
-      timediff_t timeout_ms = Curl_timeleft_ms(data, FALSE);
+      timediff_t timeout_ms = Curl_timeleft_ms(data);
       if(timeout_ms < 0) {
         /* we already got the timeout */
         failf(data, "schannel: timed out sending data "
index f9075f518f0142441db3ded990ee07c75c23d594..0ebe9726f078e8f85a7829179585821d3dde4eb2 100644 (file)
--- a/lib/ws.c
+++ b/lib/ws.c
@@ -1699,7 +1699,7 @@ static CURLcode ws_send_raw_blocking(struct Curl_easy *data,
 
       CURL_TRC_WS(data, "ws_send_raw_blocking() partial, %zu left to send",
                   buflen);
-      left_ms = Curl_timeleft_ms(data, FALSE);
+      left_ms = Curl_timeleft_ms(data);
       if(left_ms < 0) {
         failf(data, "[WS] Timeout waiting for socket becoming writable");
         return CURLE_SEND_ERROR;
index ca2d349bc454dac5ef9a727e9064a559c0025cd4..2a6b54366d7074013201cb7e1a5985313640da1b 100644 (file)
@@ -148,7 +148,8 @@ static CURLcode test_unit1303(const char *arg)
     NOW(run[i].now_s, run[i].now_us);
     TIMEOUTS(run[i].timeout_ms, run[i].connecttimeout_ms);
     easy->progress.now = now;
-    timeout = Curl_timeleft_now_ms(easy, &now, run[i].connecting);
+    easy->mstate = run[i].connecting ? MSTATE_INIT : MSTATE_DO;
+    timeout = Curl_timeleft_now_ms(easy, &now);
     if(timeout != run[i].result)
       fail(run[i].comment);
   }