Revert "Update to 7.40.1"
[platform/upstream/curl.git] / lib / transfer.c
index f1896b0..dc817a6 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, 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
  *
  ***************************************************************************/
 
-#include "setup.h"
+#include "curl_setup.h"
 
 #include "strtoofft.h"
 #include "strequal.h"
 #include "rawstr.h"
 
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
@@ -76,9 +70,9 @@
 #include "http.h"
 #include "url.h"
 #include "getinfo.h"
-#include "sslgen.h"
+#include "vtls/vtls.h"
 #include "http_digest.h"
-#include "http_ntlm.h"
+#include "curl_ntlm.h"
 #include "http_negotiate.h"
 #include "share.h"
 #include "curl_memory.h"
@@ -93,8 +87,6 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-#define CURL_TIMEOUT_EXPECT_100 1000 /* counting ms here */
-
 /*
  * This function will call the read callback to fill our buffer with data
  * to upload.
@@ -107,11 +99,13 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
 #ifdef CURL_DOES_CONVERSIONS
   bool sending_http_headers = FALSE;
 
-  if((conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) &&
-     (data->state.proto.http->sending == HTTPSEND_REQUEST)) {
-    /* We're sending the HTTP request headers, not the data.
-       Remember that so we don't re-translate them into garbage. */
-    sending_http_headers = TRUE;
+  if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
+    const struct HTTP *http = data->req.protop;
+
+    if(http->sending == HTTPSEND_REQUEST)
+      /* We're sending the HTTP request headers, not the data.
+         Remember that so we don't re-translate them into garbage. */
+      sending_http_headers = TRUE;
   }
 #endif
 
@@ -132,14 +126,24 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
     return CURLE_ABORTED_BY_CALLBACK;
   }
   else if(nread == CURL_READFUNC_PAUSE) {
-    struct SingleRequest *k = &data->req;
-    /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
-    k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
-    if(data->req.upload_chunky) {
-      /* Back out the preallocation done above */
-      data->req.upload_fromhere -= (8 + 2);
+
+    if(conn->handler->flags & PROTOPT_NONETWORK) {
+      /* protocols that work without network cannot be paused. This is
+         actually only FILE:// just now, and it can't pause since the transfer
+         isn't done using the "normal" procedure. */
+      failf(data, "Read callback asked for PAUSE when not supported!");
+      return CURLE_READ_ERROR;
+    }
+    else {
+      struct SingleRequest *k = &data->req;
+      /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
+      k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
+      if(data->req.upload_chunky) {
+        /* Back out the preallocation done above */
+        data->req.upload_fromhere -= (8 + 2);
+      }
+      *nreadp = 0;
     }
-    *nreadp = 0;
     return CURLE_OK; /* nothing was read */
   }
   else if((size_t)nread > buffersize) {
@@ -169,11 +173,12 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
     const char *endofline_native;
     const char *endofline_network;
     int hexlen;
+
+    if(
 #ifdef CURL_DO_LINEEND_CONV
-    if((data->set.crlf) || (data->set.prefer_ascii)) {
-#else
-    if(data->set.crlf) {
-#endif /* CURL_DO_LINEEND_CONV */
+       (data->set.prefer_ascii) ||
+#endif
+       (data->set.crlf)) {
       /* \n will become \r\n later on */
       endofline_native  = "\n";
       endofline_network = "\x0a";
@@ -285,7 +290,7 @@ CURLcode Curl_readrewind(struct connectdata *conn)
     else {
       /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
          given FILE * stream and we can actually attempt to rewind that
-         ourself with fseek() */
+         ourselves with fseek() */
       if(data->set.fread_func == (curl_read_callback)fread) {
         if(-1 != fseek(data->set.in, 0, SEEK_SET))
           /* successful rewind */
@@ -305,7 +310,20 @@ static int data_pending(const struct connectdata *conn)
   /* in the case of libssh2, we can never be really sure that we have emptied
      its internal buffers so we MUST always try until we get EAGAIN back */
   return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
+#if defined(USE_NGHTTP2)
+    Curl_ssl_data_pending(conn, FIRSTSOCKET) ||
+    /* For HTTP/2, we may read up everything including responde body
+       with header fields in Curl_http_readwrite_headers. If no
+       content-length is provided, curl waits for the connection
+       close, which we emulate it using conn->proto.httpc.closed =
+       TRUE. The thing is if we read everything, then http2_recv won't
+       be called and we cannot signal the HTTP/2 stream has closed. As
+       a workaround, we return nonzero here to call http2_recv. */
+    ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20 &&
+     conn->proto.httpc.closed);
+#else
     Curl_ssl_data_pending(conn, FIRSTSOCKET);
+#endif
 }
 
 static void read_rewind(struct connectdata *conn,
@@ -331,7 +349,7 @@ static void read_rewind(struct connectdata *conn,
     }
 
     DEBUGF(infof(conn->data,
-                 "Buffer after stream rewind (read_pos = %zu): [%s]",
+                 "Buffer after stream rewind (read_pos = %zu): [%s]\n",
                  conn->read_pos, buf));
   }
 #endif
@@ -429,7 +447,7 @@ static CURLcode readwrite_data(struct SessionHandle *data,
 
     *didwhat |= KEEP_RECV;
     /* indicates data of zero size, i.e. empty file */
-    is_empty_data = (bool)((nread == 0) && (k->bodywrites == 0));
+    is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
 
     /* NUL terminate, allowing string ops to be used */
     if(0 < nread || is_empty_data) {
@@ -478,7 +496,7 @@ static CURLcode readwrite_data(struct SessionHandle *data,
         /* We've stopped dealing with input, get out of the do-while loop */
 
         if(nread > 0) {
-          if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
+          if(Curl_multi_pipeline_enabled(conn->data->multi)) {
             infof(data,
                   "Rewinding stream by : %zd"
                   " bytes on url %s (zero-length body)\n",
@@ -505,12 +523,11 @@ static CURLcode readwrite_data(struct SessionHandle *data,
        is non-headers. */
     if(k->str && !k->header && (nread > 0 || is_empty_data)) {
 
-
 #ifndef CURL_DISABLE_HTTP
       if(0 == k->bodywrites && !is_empty_data) {
         /* These checks are only made the first time we are about to
            write a piece of the body */
-        if(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) {
+        if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
           /* HTTP-only checks */
 
           if(data->req.newurl) {
@@ -546,16 +563,21 @@ static CURLcode readwrite_data(struct SessionHandle *data,
 
             if(!Curl_meets_timecondition(data, k->timeofdoc)) {
               *done = TRUE;
+              /* We're simulating a http 304 from server so we return
+                 what should have been returned from the server */
+              data->info.httpcode = 304;
+              infof(data, "Simulate a HTTP 304 response!\n");
               /* we abort the transfer before it is completed == we ruin the
                  re-use ability. Close the connection */
-              conn->bits.close = TRUE;
+              connclose(conn, "Simulated 304 handling");
               return CURLE_OK;
             }
           } /* we have a time condition */
 
-        } /* this is HTTP */
+        } /* this is HTTP or RTSP */
       } /* this is the first time we write a body part */
 #endif /* CURL_DISABLE_HTTP */
+
       k->bodywrites++;
 
       /* pass data to the debug function before it gets "dechunked" */
@@ -589,7 +611,7 @@ static CURLcode readwrite_data(struct SessionHandle *data,
             failf(data, "Failed writing data");
             return CURLE_WRITE_ERROR;
           }
-          failf(data, "Problem (%d) in the Chunked-Encoded data", (int)res);
+          failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res));
           return CURLE_RECV_ERROR;
         }
         else if(CHUNKE_STOP == res) {
@@ -605,9 +627,9 @@ static CURLcode readwrite_data(struct SessionHandle *data,
 
           dataleft = conn->chunk.dataleft;
           if(dataleft != 0) {
-            infof(conn->data, "Leftovers after chunking: %zu bytes", dataleft);
-            if(conn->data->multi &&
-               Curl_multi_canPipeline(conn->data->multi)) {
+            infof(conn->data, "Leftovers after chunking: %zu bytes\n",
+                  dataleft);
+            if(Curl_multi_pipeline_enabled(conn->data->multi)) {
               /* only attempt the rewind if we truly are pipelining */
               infof(conn->data, "Rewinding %zu bytes\n",dataleft);
               read_rewind(conn, dataleft);
@@ -630,14 +652,14 @@ static CURLcode readwrite_data(struct SessionHandle *data,
 
         excess = (size_t)(k->bytecount + nread - k->maxdownload);
         if(excess > 0 && !k->ignorebody) {
-          if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
+          if(Curl_multi_pipeline_enabled(conn->data->multi)) {
             /* The 'excess' amount below can't be more than BUFSIZE which
                always will fit in a size_t */
             infof(data,
                   "Rewinding stream by : %zu"
-                  " bytes on url %s (size = %" FORMAT_OFF_T
-                  ", maxdownload = %" FORMAT_OFF_T
-                  ", bytecount = %" FORMAT_OFF_T ", nread = %zd)\n",
+                  " bytes on url %s (size = %" CURL_FORMAT_CURL_OFF_T
+                  ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+                  ", bytecount = %" CURL_FORMAT_CURL_OFF_T ", nread = %zd)\n",
                   excess, data->state.path,
                   k->size, k->maxdownload, k->bytecount, nread);
             read_rewind(conn, excess);
@@ -646,9 +668,9 @@ static CURLcode readwrite_data(struct SessionHandle *data,
             infof(data,
                   "Excess found in a non pipelined read:"
                   " excess = %zu"
-                  ", size = %" FORMAT_OFF_T
-                  ", maxdownload = %" FORMAT_OFF_T
-                  ", bytecount = %" FORMAT_OFF_T "\n",
+                  ", size = %" CURL_FORMAT_CURL_OFF_T
+                  ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+                  ", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n",
                   excess, k->size, k->maxdownload, k->bytecount);
           }
         }
@@ -701,7 +723,7 @@ static CURLcode readwrite_data(struct SessionHandle *data,
             if(!k->ignorebody) {
 
 #ifndef CURL_DISABLE_POP3
-              if(conn->handler->protocol&CURLPROTO_POP3)
+              if(conn->handler->protocol&PROTO_FAMILY_POP3)
                 result = Curl_pop3_write(conn, k->str, nread);
               else
 #endif /* CURL_DISABLE_POP3 */
@@ -799,6 +821,9 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
   /*
    * We loop here to do the READ and SEND loop until we run out of
    * data to send or until we get EWOULDBLOCK back
+   *
+   * FIXME: above comment is misleading. Currently no looping is
+   * actually done in do-while loop below.
    */
   do {
 
@@ -812,9 +837,10 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
         /* HTTP pollution, this should be written nicer to become more
            protocol agnostic. */
         int fillcount;
+        struct HTTP *http = data->req.protop;
 
         if((k->exp100 == EXP100_SENDING_REQUEST) &&
-           (data->state.proto.http->sending == HTTPSEND_BODY)) {
+           (http->sending == HTTPSEND_BODY)) {
           /* If this call is to send body data, we must take some action:
              We have sent off the full HTTP 1.1 request, and we shall now
              go into the Expect: 100 state and await such a header */
@@ -824,12 +850,12 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
           *didwhat &= ~KEEP_SEND;  /* we didn't write anything actually */
 
           /* set a timeout for the multi interface */
-          Curl_expire(data, CURL_TIMEOUT_EXPECT_100);
+          Curl_expire(data, data->set.expect_100_timeout);
           break;
         }
 
-        if(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) {
-          if(data->state.proto.http->sending == HTTPSEND_REQUEST)
+        if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
+          if(http->sending == HTTPSEND_REQUEST)
             /* We're sending the HTTP request headers, not the data.
                Remember that so we don't change the line endings. */
             sending_http_headers = TRUE;
@@ -866,7 +892,7 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
       data->req.upload_present = nread;
 
 #ifndef CURL_DISABLE_SMTP
-      if(conn->handler->protocol & CURLPROTO_SMTP) {
+      if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
         result = Curl_smtp_escape_eob(conn, nread);
         if(result)
           return result;
@@ -875,13 +901,12 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
 #endif /* CURL_DISABLE_SMTP */
 
       /* convert LF to CRLF if so asked */
-      if((!sending_http_headers) &&
+      if((!sending_http_headers) && (
 #ifdef CURL_DO_LINEEND_CONV
-        /* always convert if we're FTPing in ASCII mode */
-         ((data->set.crlf) || (data->set.prefer_ascii))) {
-#else
-         (data->set.crlf)) {
+         /* always convert if we're FTPing in ASCII mode */
+         (data->set.prefer_ascii) ||
 #endif
+         (data->set.crlf))) {
         if(data->state.scratch == NULL)
           data->state.scratch = malloc(2*BUFSIZE);
         if(data->state.scratch == NULL) {
@@ -901,7 +926,7 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
             if(!data->set.crlf) {
               /* we're here only because FTP is in ASCII mode...
                  bump infilesize for the LF we just added */
-              data->set.infilesize++;
+              data->state.infilesize++;
             }
           }
           else
@@ -942,7 +967,7 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
 
     k->writebytecount += bytes_written;
 
-    if(k->writebytecount == data->set.infilesize) {
+    if(k->writebytecount == data->state.infilesize) {
       /* we have sent all data we were supposed to */
       k->upload_done = TRUE;
       infof(data, "We are completely uploaded and fine\n");
@@ -971,7 +996,7 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
 
     Curl_pgrsSetUploadCounter(data, k->writebytecount);
 
-  } while(0); /* just to break out from! */
+  } WHILE_FALSE; /* just to break out from! */
 
   return CURLE_OK;
 }
@@ -1026,12 +1051,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
     if(result || *done)
       return result;
   }
-  else if(k->keepon & KEEP_RECV) {
-    DEBUGF(infof(data, "additional stuff not fine %s:%d: %d %d\n",
-                 __FILE__, __LINE__,
-                 select_res & CURL_CSELECT_IN,
-                 conn->bits.stream_was_rewound));
-  }
 
   /* If we still have writing to do, we check if we have a writable socket. */
   if((k->keepon & KEEP_SEND) && (select_res & CURL_CSELECT_OUT)) {
@@ -1067,7 +1086,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
       */
 
       long ms = Curl_tvdiff(k->now, k->start100);
-      if(ms > CURL_TIMEOUT_EXPECT_100) {
+      if(ms >= data->set.expect_100_timeout) {
         /* we've waited long enough, continue anyway */
         k->exp100 = EXP100_SEND_DATA;
         k->keepon |= KEEP_SEND;
@@ -1087,13 +1106,14 @@ CURLcode Curl_readwrite(struct connectdata *conn,
     if(0 > Curl_timeleft(data, &k->now, FALSE)) {
       if(k->size != -1) {
         failf(data, "Operation timed out after %ld milliseconds with %"
-              FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
+              CURL_FORMAT_CURL_OFF_T " out of %"
+              CURL_FORMAT_CURL_OFF_T " bytes received",
               Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount,
               k->size);
       }
       else {
         failf(data, "Operation timed out after %ld milliseconds with %"
-              FORMAT_OFF_T " bytes received",
+              CURL_FORMAT_CURL_OFF_T " bytes received",
               Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount);
       }
       return CURLE_OPERATION_TIMEDOUT;
@@ -1115,7 +1135,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
        (k->bytecount != (k->size + data->state.crlf_conversions)) &&
 #endif /* CURL_DO_LINEEND_CONV */
        !data->req.newurl) {
-      failf(data, "transfer closed with %" FORMAT_OFF_T
+      failf(data, "transfer closed with %" CURL_FORMAT_CURL_OFF_T
             " bytes remaining to read",
             k->size - k->bytecount);
       return CURLE_PARTIAL_FILE;
@@ -1125,7 +1145,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
             (conn->chunk.state != CHUNK_STOP)) {
       /*
        * In chunked mode, return an error if the connection is closed prior to
-       * the empty (terminiating) chunk is read.
+       * the empty (terminating) chunk is read.
        *
        * The condition above used to check for
        * conn->proto.http->chunk.datasize != 0 which is true after reading
@@ -1140,8 +1160,8 @@ CURLcode Curl_readwrite(struct connectdata *conn,
   }
 
   /* Now update the "done" boolean we return */
-  *done = (bool)(0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
-                                  KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)));
+  *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
+                            KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;
 
   return CURLE_OK;
 }
@@ -1202,11 +1222,11 @@ int Curl_single_getsock(const struct connectdata *conn,
 /*
  * Determine optimum sleep time based on configured rate, current rate,
  * and packet size.
- * Returns value in mili-seconds.
+ * Returns value in milliseconds.
  *
  * The basic idea is to adjust the desired rate up/down in this method
  * based on whether we are running too slow or too fast.  Then, calculate
- * how many miliseconds to wait for the next packet to achieve this new
+ * how many milliseconds to wait for the next packet to achieve this new
  * rate.
  */
 long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
@@ -1232,7 +1252,7 @@ long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
     rate_bps += rate_bps >> 6;
   }
 
-  /* Determine number of miliseconds to wait until we do
+  /* Determine number of milliseconds to wait until we do
    * the next packet at the adjusted rate.  We should wait
    * longer when using larger packets, for instance.
    */
@@ -1253,200 +1273,6 @@ long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
   return (long)rv;
 }
 
-
-/*
- * Transfer()
- *
- * This function is what performs the actual transfer. It is capable of doing
- * both ways simultaneously.  The transfer must already have been setup by a
- * call to Curl_setup_transfer().
- *
- * Note that headers are created in a preallocated buffer of a default size.
- * That buffer can be enlarged on demand, but it is never shrunken again.
- *
- */
-
-static CURLcode
-Transfer(struct connectdata *conn)
-{
-  CURLcode result;
-  struct SessionHandle *data = conn->data;
-  struct SingleRequest *k = &data->req;
-  bool done=FALSE;
-  bool first=TRUE;
-  long timeout_ms;
-  int buffersize;
-  long totmp;
-
-  if((conn->sockfd == CURL_SOCKET_BAD) &&
-     (conn->writesockfd == CURL_SOCKET_BAD))
-    /* nothing to read, nothing to write, we're already OK! */
-    return CURLE_OK;
-
-  /* we want header and/or body, if neither then don't do this! */
-  if(!k->getheader && data->set.opt_no_body)
-    return CURLE_OK;
-
-  while(!done) {
-    curl_socket_t fd_read = conn->sockfd;
-    curl_socket_t fd_write = conn->writesockfd;
-    int keepon = k->keepon;
-    timeout_ms = 1000;
-
-    if(conn->waitfor) {
-      /* if waitfor is set, get the RECV and SEND bits from that but keep the
-         other bits */
-      keepon &= ~ (KEEP_RECV|KEEP_SEND);
-      keepon |= conn->waitfor & (KEEP_RECV|KEEP_SEND);
-    }
-
-    /* limit-rate logic: if speed exceeds threshold, then do not include fd in
-       select set. The current speed is recalculated in each Curl_readwrite()
-       call */
-    if((keepon & KEEP_SEND) &&
-        (!data->set.max_send_speed ||
-         (data->progress.ulspeed < data->set.max_send_speed) )) {
-      k->keepon &= ~KEEP_SEND_HOLD;
-    }
-    else {
-      if(data->set.upload && data->set.max_send_speed &&
-         (data->progress.ulspeed > data->set.max_send_speed) ) {
-        /* calculate upload rate-limitation timeout. */
-        buffersize = (int)(data->set.buffer_size ?
-                           data->set.buffer_size : BUFSIZE);
-        totmp = Curl_sleep_time(data->set.max_send_speed,
-                                data->progress.ulspeed, buffersize);
-        if(totmp < timeout_ms)
-          timeout_ms = totmp;
-      }
-      fd_write = CURL_SOCKET_BAD;
-      if(keepon & KEEP_SEND)
-        k->keepon |= KEEP_SEND_HOLD; /* hold it */
-    }
-
-    if((keepon & KEEP_RECV) &&
-        (!data->set.max_recv_speed ||
-         (data->progress.dlspeed < data->set.max_recv_speed)) ) {
-      k->keepon &= ~KEEP_RECV_HOLD;
-    }
-    else {
-      if((!data->set.upload) && data->set.max_recv_speed &&
-         (data->progress.dlspeed > data->set.max_recv_speed)) {
-        /* Calculate download rate-limitation timeout. */
-        buffersize = (int)(data->set.buffer_size ?
-                           data->set.buffer_size : BUFSIZE);
-        totmp = Curl_sleep_time(data->set.max_recv_speed,
-                                data->progress.dlspeed, buffersize);
-        if(totmp < timeout_ms)
-          timeout_ms = totmp;
-      }
-      fd_read = CURL_SOCKET_BAD;
-      if(keepon & KEEP_RECV)
-        k->keepon |= KEEP_RECV_HOLD; /* hold it */
-    }
-
-    /* pause logic. Don't check descriptors for paused connections */
-    if(k->keepon & KEEP_RECV_PAUSE)
-      fd_read = CURL_SOCKET_BAD;
-    if(k->keepon & KEEP_SEND_PAUSE)
-      fd_write = CURL_SOCKET_BAD;
-
-    /* The *_HOLD and *_PAUSE logic is necessary since even though there might
-       be no traffic during the select interval, we still call
-       Curl_readwrite() for the timeout case and if we limit transfer speed we
-       must make sure that this function doesn't transfer anything while in
-       HOLD status.
-
-       The no timeout for the first round is for the protocols for which data
-       has already been slurped off the socket and thus waiting for action
-       won't work since it'll wait even though there is already data present
-       to work with. */
-    if(first &&
-       ((fd_read != CURL_SOCKET_BAD) || (fd_write != CURL_SOCKET_BAD)))
-      /* if this is the first lap and one of the file descriptors is fine
-         to work with, skip the timeout */
-      timeout_ms = 0;
-    else {
-      totmp = Curl_timeleft(data, &k->now, FALSE);
-      if(totmp < 0)
-        return CURLE_OPERATION_TIMEDOUT;
-      else if(!totmp)
-        totmp = 1000;
-
-      if(totmp < timeout_ms)
-        timeout_ms = totmp;
-    }
-
-    switch (Curl_socket_ready(fd_read, fd_write, timeout_ms)) {
-    case -1: /* select() error, stop reading */
-#ifdef EINTR
-      /* The EINTR is not serious, and it seems you might get this more
-         often when using the lib in a multi-threaded environment! */
-      if(SOCKERRNO == EINTR)
-        continue;
-#endif
-      return CURLE_RECV_ERROR;  /* indicate a network problem */
-    case 0:  /* timeout */
-    default: /* readable descriptors */
-
-      result = Curl_readwrite(conn, &done);
-      /* "done" signals to us if the transfer(s) are ready */
-      break;
-    }
-    if(result)
-      return result;
-
-    first = FALSE; /* not the first lap anymore */
-  }
-
-  return CURLE_OK;
-}
-
-static CURLcode loadhostpairs(struct SessionHandle *data)
-{
-  struct curl_slist *hostp;
-  char hostname[256];
-  char address[256];
-  int port;
-
-  for(hostp = data->change.resolve; hostp; hostp = hostp->next ) {
-    if(!hostp->data)
-      continue;
-    if(hostp->data[0] == '-') {
-      /* TODO: mark an entry for removal */
-    }
-    else if(3 == sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
-                        address)) {
-      struct Curl_dns_entry *dns;
-      Curl_addrinfo *addr;
-
-      addr = Curl_str2addr(address, port);
-      if(!addr) {
-        infof(data, "Resolve %s found illegal!\n", hostp->data);
-        continue;
-      }
-      infof(data, "Added %s:%d:%s to DNS cache\n",
-            hostname, port, address);
-
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-      /* put this host in the cache */
-      dns = Curl_cache_addr(data, addr, hostname, port);
-
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
-      if(!dns)
-        return CURLE_OUT_OF_MEMORY;
-    }
-  }
-  data->change.resolve = NULL; /* dealt with now */
-
-  return CURLE_OK;
-}
-
-
 /*
  * Curl_pretransfer() is called immediately before a transfer starts.
  */
@@ -1460,9 +1286,9 @@ CURLcode Curl_pretransfer(struct SessionHandle *data)
   }
 
   /* Init the SSL session ID cache here. We do it here since we want to do it
-     after the *_setopt() calls (that could change the size of the cache) but
+     after the *_setopt() calls (that could specify the size of the cache) but
      before any transfer takes place. */
-  res = Curl_ssl_initsessions(data, data->set.ssl.numsessions);
+  res = Curl_ssl_initsessions(data, data->set.ssl.max_ssl_sessions);
   if(res)
     return res;
 
@@ -1485,7 +1311,7 @@ CURLcode Curl_pretransfer(struct SessionHandle *data)
 
   /* If there is a list of host pairs to deal with */
   if(data->change.resolve)
-    res = loadhostpairs(data);
+    res = Curl_loadhostpairs(data);
 
   if(!res) {
     /* Allow data->set.use_port to set which port to use. This needs to be
@@ -1509,6 +1335,12 @@ CURLcode Curl_pretransfer(struct SessionHandle *data)
 
     if(data->set.connecttimeout)
       Curl_expire(data, data->set.connecttimeout);
+
+    /* In case the handle is re-used and an authentication method was picked
+       in the session we need to make sure we only use the one(s) we now
+       consider to be fine */
+    data->state.authhost.picked &= data->state.authhost.want;
+    data->state.authproxy.picked &= data->state.authproxy.want;
   }
 
   return res;
@@ -1602,7 +1434,7 @@ static bool is_absolute_url(const char *url)
   char prot[16]; /* URL protocol string storage */
   char letter;   /* used for a silly sscanf */
 
-  return (bool)(2 == sscanf(url, "%15[^?&/:]://%c", prot, &letter));
+  return (2 == sscanf(url, "%15[^?&/:]://%c", prot, &letter)) ? TRUE : FALSE;
 }
 
 /*
@@ -1694,26 +1526,37 @@ static char *concat_url(const char *base, const char *relurl)
     }
   }
   else {
-    /* We got a new absolute path for this server, cut off from the
-       first slash */
-    pathsep = strchr(protsep, '/');
-    if(pathsep) {
-      /* When people use badly formatted URLs, such as
-         "http://www.url.com?dir=/home/daniel" we must not use the first
-         slash, if there's a ?-letter before it! */
-      char *sep = strchr(protsep, '?');
-      if(sep && (sep < pathsep))
-        pathsep = sep;
-      *pathsep=0;
+    /* We got a new absolute path for this server */
+
+    if((relurl[0] == '/') && (relurl[1] == '/')) {
+      /* the new URL starts with //, just keep the protocol part from the
+         original one */
+      *protsep=0;
+      useurl = &relurl[2]; /* we keep the slashes from the original, so we
+                              skip the new ones */
     }
     else {
-      /* There was no slash. Now, since we might be operating on a badly
-         formatted URL, such as "http://www.url.com?id=2380" which doesn't
-         use a slash separator as it is supposed to, we need to check for a
-         ?-letter as well! */
-      pathsep = strchr(protsep, '?');
-      if(pathsep)
+      /* cut off the original URL from the first slash, or deal with URLs
+         without slash */
+      pathsep = strchr(protsep, '/');
+      if(pathsep) {
+        /* When people use badly formatted URLs, such as
+           "http://www.url.com?dir=/home/daniel" we must not use the first
+           slash, if there's a ?-letter before it! */
+        char *sep = strchr(protsep, '?');
+        if(sep && (sep < pathsep))
+          pathsep = sep;
         *pathsep=0;
+      }
+      else {
+        /* There was no slash. Now, since we might be operating on a badly
+           formatted URL, such as "http://www.url.com?id=2380" which doesn't
+           use a slash separator as it is supposed to, we need to check for a
+           ?-letter as well! */
+        pathsep = strchr(protsep, '?');
+        if(pathsep)
+          *pathsep=0;
+      }
     }
   }
 
@@ -1726,8 +1569,8 @@ static char *concat_url(const char *base, const char *relurl)
 
   urllen = strlen(url_clone);
 
-  newest = malloc( urllen + 1 + /* possible slash */
-                         newlen + 1 /* zero byte */);
+  newest = malloc(urllen + 1 + /* possible slash */
+                  newlen + 1 /* zero byte */);
 
   if(!newest) {
     free(url_clone); /* don't leak this */
@@ -1790,15 +1633,14 @@ CURLcode Curl_follow(struct SessionHandle *data,
          when we get the next URL. We pick the ->url field, which may or may
          not be 100% correct */
 
-      if(data->change.referer_alloc)
-        /* If we already have an allocated referer, free this first */
-        free(data->change.referer);
+      if(data->change.referer_alloc) {
+        Curl_safefree(data->change.referer);
+        data->change.referer_alloc = FALSE;
+      }
 
       data->change.referer = strdup(data->change.url);
-      if(!data->change.referer) {
-        data->change.referer_alloc = FALSE;
+      if(!data->change.referer)
         return CURLE_OUT_OF_MEMORY;
-      }
       data->change.referer_alloc = TRUE; /* yes, free this later */
     }
   }
@@ -1845,12 +1687,13 @@ CURLcode Curl_follow(struct SessionHandle *data,
   if(disallowport)
     data->state.allow_port = FALSE;
 
-  if(data->change.url_alloc)
-    free(data->change.url);
-  else
-    data->change.url_alloc = TRUE; /* the URL is allocated */
+  if(data->change.url_alloc) {
+    Curl_safefree(data->change.url);
+    data->change.url_alloc = FALSE;
+  }
 
   data->change.url = newurl;
+  data->change.url_alloc = TRUE;
   newurl = NULL; /* don't free! */
 
   infof(data, "Issue another request to this URL: '%s'\n", data->change.url);
@@ -1876,63 +1719,59 @@ CURLcode Curl_follow(struct SessionHandle *data,
      */
     break;
   case 301: /* Moved Permanently */
-    /* (quote from RFC2616, section 10.3.2):
+    /* (quote from RFC7231, section 6.4.2)
      *
-     * Note: When automatically redirecting a POST request after receiving a
-     * 301 status code, some existing HTTP/1.0 user agents will erroneously
-     * change it into a GET request.
+     * Note: For historical reasons, a user agent MAY change the request
+     * method from POST to GET for the subsequent request.  If this
+     * behavior is undesired, the 307 (Temporary Redirect) status code
+     * can be used instead.
      *
      * ----
      *
-     * Warning: Because most of importants user agents do this obvious RFC2616
-     * violation, many webservers expect this misbehavior. So these servers
-     * often answers to a POST request with an error page.  To be sure that
-     * libcurl gets the page that most user agents would get, libcurl has to
-     * force GET.
+     * Many webservers expect this, so these servers often answers to a POST
+     * request with an error page. To be sure that libcurl gets the page that
+     * most user agents would get, libcurl has to force GET.
      *
-     * This behaviour can be overridden with CURLOPT_POSTREDIR.
+     * This behaviour is forbidden by RFC1945 and the obsolete RFC2616, and
+     * can be overridden with CURLOPT_POSTREDIR.
      */
     if((data->set.httpreq == HTTPREQ_POST
         || data->set.httpreq == HTTPREQ_POST_FORM)
-       && !data->set.post301) {
-      infof(data,
-            "Violate RFC 2616/10.3.2 and switch from POST to GET\n");
+       && !(data->set.keep_post & CURL_REDIR_POST_301)) {
+      infof(data, "Switch from POST to GET\n");
       data->set.httpreq = HTTPREQ_GET;
     }
     break;
   case 302: /* Found */
-    /* (From 10.3.3)
-
-    Note: RFC 1945 and RFC 2068 specify that the client is not allowed
-    to change the method on the redirected request.  However, most
-    existing user agent implementations treat 302 as if it were a 303
-    response, performing a GET on the Location field-value regardless
-    of the original request method. The status codes 303 and 307 have
-    been added for servers that wish to make unambiguously clear which
-    kind of reaction is expected of the client.
-
-    (From 10.3.4)
-
-    Note: Many pre-HTTP/1.1 user agents do not understand the 303
-    status. When interoperability with such clients is a concern, the
-    302 status code may be used instead, since most user agents react
-    to a 302 response as described here for 303.
-
-    This behaviour can be overriden with CURLOPT_POSTREDIR
-    */
+    /* (quote from RFC7231, section 6.4.3)
+     *
+     * Note: For historical reasons, a user agent MAY change the request
+     * method from POST to GET for the subsequent request.  If this
+     * behavior is undesired, the 307 (Temporary Redirect) status code
+     * can be used instead.
+     *
+     * ----
+     *
+     * Many webservers expect this, so these servers often answers to a POST
+     * request with an error page. To be sure that libcurl gets the page that
+     * most user agents would get, libcurl has to force GET.
+     *
+     * This behaviour is forbidden by RFC1945 and the obsolete RFC2616, and
+     * can be overridden with CURLOPT_POSTREDIR.
+     */
     if((data->set.httpreq == HTTPREQ_POST
         || data->set.httpreq == HTTPREQ_POST_FORM)
-       && !data->set.post302) {
-      infof(data,
-            "Violate RFC 2616/10.3.3 and switch from POST to GET\n");
+       && !(data->set.keep_post & CURL_REDIR_POST_302)) {
+      infof(data, "Switch from POST to GET\n");
       data->set.httpreq = HTTPREQ_GET;
     }
     break;
 
   case 303: /* See Other */
-    /* Disable both types of POSTs, since doing a second POST when
-     * following isn't what anyone would want! */
-    if(data->set.httpreq != HTTPREQ_GET) {
+    /* Disable both types of POSTs, unless the user explicitely
+       asks for POST after POST */
+    if(data->set.httpreq != HTTPREQ_GET
+      && !(data->set.keep_post & CURL_REDIR_POST_303)) {
       data->set.httpreq = HTTPREQ_GET; /* enforce GET request */
       infof(data, "Disables POST, goes with %s\n",
             data->set.opt_no_body?"HEAD":"GET");
@@ -1954,39 +1793,12 @@ CURLcode Curl_follow(struct SessionHandle *data,
     break;
   }
   Curl_pgrsTime(data, TIMER_REDIRECT);
-  Curl_pgrsResetTimes(data);
+  Curl_pgrsResetTimesSizes(data);
 
   return CURLE_OK;
 #endif /* CURL_DISABLE_HTTP */
 }
 
-static CURLcode
-connect_host(struct SessionHandle *data,
-             struct connectdata **conn)
-{
-  CURLcode res = CURLE_OK;
-
-  bool async;
-  bool protocol_done=TRUE; /* will be TRUE always since this is only used
-                                within the easy interface */
-  Curl_pgrsTime(data, TIMER_STARTSINGLE);
-  res = Curl_connect(data, conn, &async, &protocol_done);
-
-  if((CURLE_OK == res) && async) {
-    /* Now, if async is TRUE here, we need to wait for the name
-       to resolve */
-    res = Curl_resolver_wait_resolv(*conn, NULL);
-    if(CURLE_OK == res)
-      /* Resolved, continue with the connection */
-      res = Curl_async_resolved(*conn, &protocol_done);
-    else
-      /* if we can't resolve, we kill this "connection" now */
-      (void)Curl_disconnect(*conn, /* dead_connection */ FALSE);
-  }
-
-  return res;
-}
-
 CURLcode
 Curl_reconnect_request(struct connectdata **connp)
 {
@@ -2002,10 +1814,12 @@ Curl_reconnect_request(struct connectdata **connp)
 
   infof(data, "Re-used connection seems dead, get a new one\n");
 
-  conn->bits.close = TRUE; /* enforce close of this connection */
+  connclose(conn, "Reconnect dead connection"); /* enforce close */
   result = Curl_done(&conn, result, FALSE); /* we are so done with this */
 
-  /* conn may no longer be a good pointer */
+  /* conn may no longer be a good pointer, clear it to avoid mistakes by
+     parent functions */
+  *connp = NULL;
 
   /*
    * According to bug report #1330310. We need to check for CURLE_SEND_ERROR
@@ -2055,7 +1869,7 @@ CURLcode Curl_retry_request(struct connectdata *conn,
   /* if we're talking upload, we can't do the checks below, unless the protocol
      is HTTP as when uploading over HTTP we will still get a response */
   if(data->set.upload &&
-     !(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)))
+     !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)))
     return CURLE_OK;
 
   if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry ||
@@ -2063,7 +1877,7 @@ CURLcode Curl_retry_request(struct connectdata *conn,
         data->req.headerbytecount == 0) &&
         conn->bits.reuse &&
         !data->set.opt_no_body &&
-        data->set.rtspreq != RTSPREQ_RECEIVE)) {
+       data->set.rtspreq != RTSPREQ_RECEIVE)) {
     /* We got no data, we attempted to re-use a connection and yet we want a
        "body". This might happen if the connection was left alive when we were
        done using it before, but that was closed when we wanted to read from
@@ -2073,209 +1887,21 @@ CURLcode Curl_retry_request(struct connectdata *conn,
     if(!*url)
       return CURLE_OUT_OF_MEMORY;
 
-    conn->bits.close = TRUE; /* close this connection */
+    connclose(conn, "retry"); /* close this connection */
     conn->bits.retry = TRUE; /* mark this as a connection we're about
                                 to retry. Marking it this way should
                                 prevent i.e HTTP transfers to return
                                 error just because nothing has been
                                 transferred! */
 
-    if(data->state.proto.http->writebytecount)
-      return Curl_readrewind(conn);
-  }
-  return CURLE_OK;
-}
-
-static CURLcode Curl_do_perform(struct SessionHandle *data)
-{
-  CURLcode res;
-  CURLcode res2;
-  struct connectdata *conn=NULL;
-  char *newurl = NULL; /* possibly a new URL to follow to! */
-  followtype follow = FOLLOW_NONE;
-
-  data->state.used_interface = Curl_if_easy;
-
-  res = Curl_pretransfer(data);
-  if(res)
-    return res;
-
-  /*
-   * It is important that there is NO 'return' from this function at any other
-   * place than falling down to the end of the function! This is because we
-   * have cleanup stuff that must be done before we get back, and that is only
-   * performed after this do-while loop.
-   */
-
-  for(;;) {
-    res = connect_host(data, &conn);   /* primary connection */
 
-    if(res == CURLE_OK) {
-      bool do_done;
-      if(data->set.connect_only) {
-        /* keep connection open for application to use the socket */
-        conn->bits.close = FALSE;
-        res = Curl_done(&conn, CURLE_OK, FALSE);
-        break;
-      }
-      res = Curl_do(&conn, &do_done);
-
-      if(res == CURLE_OK) {
-        if(conn->data->set.wildcardmatch) {
-          if(conn->data->wildcard.state == CURLWC_DONE ||
-             conn->data->wildcard.state == CURLWC_SKIP) {
-            /* keep connection open for application to use the socket */
-            conn->bits.close = FALSE;
-            res = Curl_done(&conn, CURLE_OK, FALSE);
-            break;
-          }
-        }
-        res = Transfer(conn); /* now fetch that URL please */
-        if((res == CURLE_OK) || (res == CURLE_RECV_ERROR)) {
-          bool retry = FALSE;
-          CURLcode rc = Curl_retry_request(conn, &newurl);
-          if(rc)
-            res = rc;
-          else
-            retry = (newurl?TRUE:FALSE);
-
-          if(retry) {
-            /* we know (newurl != NULL) at this point */
-            res = CURLE_OK;
-            follow = FOLLOW_RETRY;
-          }
-          else if(res == CURLE_OK) {
-            /*
-             * We must duplicate the new URL here as the connection data may
-             * be free()ed in the Curl_done() function. We prefer the newurl
-             * one since that's used for redirects or just further requests
-             * for retries or multi-stage HTTP auth methods etc.
-             */
-            if(data->req.newurl) {
-              follow = FOLLOW_REDIR;
-              newurl = strdup(data->req.newurl);
-              if(!newurl)
-                res = CURLE_OUT_OF_MEMORY;
-            }
-            else if(data->req.location) {
-              follow = FOLLOW_FAKE;
-              newurl = strdup(data->req.location);
-              if(!newurl)
-                res = CURLE_OUT_OF_MEMORY;
-            }
-          }
-
-          /* in the above cases where 'newurl' gets assigned, we have a fresh
-           * allocated memory pointed to */
-        }
-        if(res != CURLE_OK) {
-          /* The transfer phase returned error, we mark the connection to get
-           * closed to prevent being re-used. This is because we can't
-           * possibly know if the connection is in a good shape or not now. */
-          conn->bits.close = TRUE;
-
-          if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
-            /* if we failed anywhere, we must clean up the secondary socket if
-               it was used */
-            Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
-            conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
-          }
-        }
-
-        /* Always run Curl_done(), even if some of the previous calls
-           failed, but return the previous (original) error code */
-        res2 = Curl_done(&conn, res, FALSE);
-
-        if(CURLE_OK == res)
-          res = res2;
-      }
-      else if(conn)
-        /* Curl_do() failed, clean up left-overs in the done-call, but note
-           that at some cases the conn pointer is NULL when Curl_do() failed
-           and the connection cache is very small so only call Curl_done() if
-           conn is still "alive". */
-        /* ignore return code since we already have an error to return */
-        (void)Curl_done(&conn, res, FALSE);
-
-      /*
-       * Important: 'conn' cannot be used here, since it may have been closed
-       * in 'Curl_done' or other functions.
-       */
-
-      if((res == CURLE_OK) && follow) {
-        res = Curl_follow(data, newurl, follow);
-        if(CURLE_OK == res) {
-          /* if things went fine, Curl_follow() freed or otherwise took
-             responsibility for the newurl pointer */
-          newurl = NULL;
-          if(follow >= FOLLOW_RETRY) {
-            follow = FOLLOW_NONE;
-            continue;
-          }
-          /* else we break out of the loop below */
-        }
-      }
+    if(conn->handler->protocol&PROTO_FAMILY_HTTP) {
+      struct HTTP *http = data->req.protop;
+      if(http->writebytecount)
+        return Curl_readrewind(conn);
     }
-    break; /* it only reaches here when this shouldn't loop */
-
-  } /* loop if Location: */
-
-  if(newurl)
-    free(newurl);
-
-  if(res && !data->state.errorbuf) {
-    /*
-     * As an extra precaution: if no error string has been set and there was
-     * an error, use the strerror() string or if things are so bad that not
-     * even that is good, set a bad string that mentions the error code.
-     */
-    const char *str = curl_easy_strerror(res);
-    if(!str)
-      failf(data, "unspecified error %d", (int)res);
-    else
-      failf(data, "%s", str);
-  }
-
-  /* run post-transfer unconditionally, but don't clobber the return code if
-     we already have an error code recorder */
-  res2 = Curl_posttransfer(data);
-  if(!res && res2)
-    res = res2;
-
-  return res;
-}
-
-/*
- * Curl_perform() is the internal high-level function that gets called by the
- * external curl_easy_perform() function. It inits, performs and cleans up a
- * single file transfer.
- */
-CURLcode Curl_perform(struct SessionHandle *data)
-{
-  CURLcode res;
-  if(!data->set.wildcardmatch)
-    return Curl_do_perform(data);
-
-  /* init main wildcard structures */
-  res = Curl_wildcard_init(&data->wildcard);
-  if(res)
-    return res;
-
-  res = Curl_do_perform(data);
-  if(res) {
-    Curl_wildcard_dtor(&data->wildcard);
-    return res;
   }
-
-  /* wildcard loop */
-  while(!res && data->wildcard.state != CURLWC_DONE)
-    res = Curl_do_perform(data);
-
-  Curl_wildcard_dtor(&data->wildcard);
-
-  /* wildcard download finished or failed */
-  data->wildcard.state = CURLWC_INIT;
-  return res;
+  return CURLE_OK;
 }
 
 /*
@@ -2331,6 +1957,7 @@ Curl_setup_transfer(
       k->keepon |= KEEP_RECV;
 
     if(conn->writesockfd != CURL_SOCKET_BAD) {
+      struct HTTP *http = data->req.protop;
       /* HTTP 1.1 magic:
 
          Even if we require a 100-return code before uploading data, we might
@@ -2341,13 +1968,15 @@ Curl_setup_transfer(
          state info where we wait for the 100-return code
       */
       if((data->state.expect100header) &&
-         (data->state.proto.http->sending == HTTPSEND_BODY)) {
+         (conn->handler->protocol&PROTO_FAMILY_HTTP) &&
+         (http->sending == HTTPSEND_BODY)) {
         /* wait with write until we either got 100-continue or a timeout */
         k->exp100 = EXP100_AWAITING_CONTINUE;
-        k->start100 = k->start;
+        k->start100 = Curl_tvnow();
 
-        /* set a timeout for the multi interface */
-        Curl_expire(data, CURL_TIMEOUT_EXPECT_100);
+        /* Set a timeout for the multi interface. Add the inaccuracy margin so
+           that we don't fire slightly too early and get denied to run. */
+        Curl_expire(data, data->set.expect_100_timeout);
       }
       else {
         if(data->state.expect100header)