tizen 2.3.1 release
[external/curl.git] / lib / transfer.c
index e4e3405..75aaedf 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2010, 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
  *
  ***************************************************************************/
 
-#include "setup.h"
-
-/* -- WIN32 approved -- */
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <errno.h>
+#include "curl_setup.h"
 
 #include "strtoofft.h"
 #include "strequal.h"
 #include "rawstr.h"
 
-#ifdef WIN32
-#include <time.h>
-#include <io.h>
-#else
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
@@ -78,8 +57,6 @@
 #error "We can't compile without socket() support!"
 #endif
 
-#endif  /* WIN32 */
-
 #include "urldata.h"
 #include <curl/curl.h>
 #include "netrc.h"
 #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"
 #include "select.h"
 #include "multiif.h"
-#include "easyif.h" /* for Curl_convert_to_network prototype */
-#include "rtsp.h"
 #include "connect.h"
+#include "non-ascii.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
 /* 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.
@@ -125,11 +99,13 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
 #ifdef CURL_DOES_CONVERSIONS
   bool sending_http_headers = FALSE;
 
-  if((conn->protocol&(PROT_HTTP|PROT_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
 
@@ -150,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) {
@@ -187,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";
@@ -216,7 +203,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
            strlen(endofline_network));
 
 #ifdef CURL_DOES_CONVERSIONS
-    CURLcode res;
+    CURLcode result;
     int length;
     if(data->set.prefer_ascii) {
       /* translate the protocol and data */
@@ -226,25 +213,25 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
       /* just translate the protocol portion */
       length = strlen(hexbuffer);
     }
-    res = Curl_convert_to_network(data, data->req.upload_fromhere, length);
+    result = Curl_convert_to_network(data, data->req.upload_fromhere, length);
     /* Curl_convert_to_network calls failf if unsuccessful */
-    if(res)
-      return(res);
+    if(result)
+      return(result);
 #endif /* CURL_DOES_CONVERSIONS */
 
     if((nread - hexlen) == 0)
-      /* mark this as done once this chunk is transfered */
+      /* mark this as done once this chunk is transferred */
       data->req.upload_done = TRUE;
 
     nread+=(int)strlen(endofline_native); /* for the added end of line */
   }
 #ifdef CURL_DOES_CONVERSIONS
   else if((data->set.prefer_ascii) && (!sending_http_headers)) {
-    CURLcode res;
-    res = Curl_convert_to_network(data, data->req.upload_fromhere, nread);
+    CURLcode result;
+    result = Curl_convert_to_network(data, data->req.upload_fromhere, nread);
     /* Curl_convert_to_network calls failf if unsuccessful */
-    if(res != CURLE_OK)
-      return(res);
+    if(result)
+      return result;
   }
 #endif /* CURL_DOES_CONVERSIONS */
 
@@ -303,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 */
@@ -322,8 +309,21 @@ 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->protocol&(PROT_SCP|PROT_SFTP) ||
+  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,
@@ -349,12 +349,43 @@ 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
 }
 
+/*
+ * Check to see if CURLOPT_TIMECONDITION was met by comparing the time of the
+ * remote document with the time provided by CURLOPT_TIMEVAL
+ */
+bool Curl_meets_timecondition(struct SessionHandle *data, time_t timeofdoc)
+{
+  if((timeofdoc == 0) || (data->set.timevalue == 0))
+    return TRUE;
+
+  switch(data->set.timecondition) {
+  case CURL_TIMECOND_IFMODSINCE:
+  default:
+    if(timeofdoc <= data->set.timevalue) {
+      infof(data,
+            "The requested document is not new enough\n");
+      data->info.timecond = TRUE;
+      return FALSE;
+    }
+    break;
+  case CURL_TIMECOND_IFUNMODSINCE:
+    if(timeofdoc >= data->set.timevalue) {
+      infof(data,
+            "The requested document is not old enough\n");
+      data->info.timecond = TRUE;
+      return FALSE;
+    }
+    break;
+  }
+
+  return TRUE;
+}
 
 /*
  * Go ahead and do a read if we have a readable socket or if
@@ -370,9 +401,7 @@ static CURLcode readwrite_data(struct SessionHandle *data,
   ssize_t nread; /* number of bytes read */
   size_t excess = 0; /* excess bytes read */
   bool is_empty_data = FALSE;
-#ifndef CURL_DISABLE_RTSP
   bool readmore = FALSE; /* used by RTP to signal for more data */
-#endif
 
   *done = FALSE;
 
@@ -418,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) {
@@ -436,16 +465,13 @@ static CURLcode readwrite_data(struct SessionHandle *data,
        in the flow below before the actual storing is done. */
     k->str = k->buf;
 
-#ifndef CURL_DISABLE_RTSP
-    /* Check for RTP at the beginning of the data */
-    if(conn->protocol & PROT_RTSP) {
-      result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
+    if(conn->handler->readwrite) {
+      result = conn->handler->readwrite(data, conn, &nread, &readmore);
       if(result)
         return result;
       if(readmore)
         break;
     }
-#endif
 
 #ifndef CURL_DISABLE_HTTP
     /* Since this is a two-state thing, we check if we are parsing
@@ -457,20 +483,37 @@ static CURLcode readwrite_data(struct SessionHandle *data,
       if(result)
         return result;
 
-#ifndef CURL_DISABLE_RTSP
-      /* Check for RTP after the headers if there is no Content */
-      if(k->maxdownload <= 0 && nread > 0 && (conn->protocol & PROT_RTSP)) {
-        result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
+      if(conn->handler->readwrite &&
+         (k->maxdownload <= 0 && nread > 0)) {
+        result = conn->handler->readwrite(data, conn, &nread, &readmore);
         if(result)
           return result;
         if(readmore)
           break;
       }
-#endif
 
-      if(stop_reading)
+      if(stop_reading) {
         /* We've stopped dealing with input, get out of the do-while loop */
+
+        if(nread > 0) {
+          if(Curl_multi_pipeline_enabled(conn->data->multi)) {
+            infof(data,
+                  "Rewinding stream by : %zd"
+                  " bytes on url %s (zero-length body)\n",
+                  nread, data->state.path);
+            read_rewind(conn, (size_t)nread);
+          }
+          else {
+            infof(data,
+                  "Excess found in a non pipelined read:"
+                  " excess = %zd"
+                  " url = %s (zero-length body)\n",
+                  nread, data->state.path);
+          }
+        }
+
         break;
+      }
     }
 #endif /* CURL_DISABLE_HTTP */
 
@@ -480,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->protocol&(PROT_HTTP|PROT_RTSP)) {
+        if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
           /* HTTP-only checks */
 
           if(data->req.newurl) {
@@ -505,6 +547,18 @@ static CURLcode readwrite_data(struct SessionHandle *data,
           if(data->state.resume_from && !k->content_range &&
              (data->set.httpreq==HTTPREQ_GET) &&
              !k->ignorebody) {
+
+            if(k->size == data->state.resume_from) {
+              /* The resume point is at the end of file, consider this fine
+                 even if it doesn't allow resume from here. */
+              infof(data, "The entire document is already downloaded");
+              connclose(conn, "already downloaded");
+              /* Abort download */
+              k->keepon &= ~KEEP_RECV;
+              *done = TRUE;
+              return CURLE_OK;
+            }
+
             /* we wanted to resume a download, although the server doesn't
              * seem to support this and we did this with a GET (if it
              * wasn't a GET we did a POST or PUT resume) */
@@ -518,34 +572,24 @@ static CURLcode readwrite_data(struct SessionHandle *data,
                requested. This seems to be what chapter 13.3.4 of
                RFC 2616 defines to be the correct action for a
                HTTP/1.1 client */
-            if((k->timeofdoc > 0) && (data->set.timevalue > 0)) {
-              switch(data->set.timecondition) {
-              case CURL_TIMECOND_IFMODSINCE:
-              default:
-                if(k->timeofdoc < data->set.timevalue) {
-                  infof(data,
-                        "The requested document is not new enough\n");
-                  *done = TRUE;
-                  data->info.timecond = TRUE;
-                  return CURLE_OK;
-                }
-                break;
-              case CURL_TIMECOND_IFUNMODSINCE:
-                if(k->timeofdoc > data->set.timevalue) {
-                  infof(data,
-                        "The requested document is not old enough\n");
-                  *done = TRUE;
-                  data->info.timecond = TRUE;
-                  return CURLE_OK;
-                }
-                break;
-              } /* switch */
-            } /* two valid time strings */
+
+            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 */
+              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" */
@@ -579,7 +623,7 @@ static CURLcode readwrite_data(struct SessionHandle *data,
             failf(data, "Failed writing data");
             return CURLE_WRITE_ERROR;
           }
-          failf(data, "Received problem %d in the chunky parser", (int)res);
+          failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res));
           return CURLE_RECV_ERROR;
         }
         else if(CHUNKE_STOP == res) {
@@ -595,8 +639,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);
@@ -619,14 +664,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);
@@ -635,9 +680,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);
           }
         }
@@ -663,12 +708,12 @@ static CURLcode readwrite_data(struct SessionHandle *data,
           /* Don't let excess data pollute body writes */
           if(k->maxdownload == -1 || (curl_off_t)k->hbuflen <= k->maxdownload)
             result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                data->state.headerbuff,
-                k->hbuflen);
+                                       data->state.headerbuff,
+                                       k->hbuflen);
           else
             result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                data->state.headerbuff,
-                (size_t)k->maxdownload);
+                                       data->state.headerbuff,
+                                       (size_t)k->maxdownload);
 
           if(result)
             return result;
@@ -681,22 +726,22 @@ static CURLcode readwrite_data(struct SessionHandle *data,
              encodings handled here. */
 #ifdef HAVE_LIBZ
           switch (conn->data->set.http_ce_skip ?
-                  IDENTITY : k->content_encoding) {
+                  IDENTITY : k->auto_decoding) {
           case IDENTITY:
 #endif
             /* This is the default when the server sends no
                Content-Encoding header. See Curl_readwrite_init; the
-               memset() call initializes k->content_encoding to zero. */
+               memset() call initializes k->auto_decoding to zero. */
             if(!k->ignorebody) {
 
 #ifndef CURL_DISABLE_POP3
-              if(conn->protocol&PROT_POP3)
+              if(conn->handler->protocol&PROTO_FAMILY_POP3)
                 result = Curl_pop3_write(conn, k->str, nread);
               else
 #endif /* CURL_DISABLE_POP3 */
 
-              result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
-                                         nread);
+                result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
+                                           nread);
             }
 #ifdef HAVE_LIBZ
             break;
@@ -731,24 +776,20 @@ static CURLcode readwrite_data(struct SessionHandle *data,
 
     } /* if(! header and data to read ) */
 
-#ifndef CURL_DISABLE_RTSP
-    if(excess > 0 && !conn->bits.stream_was_rewound &&
-        (conn->protocol & PROT_RTSP)) {
-      /* Check for RTP after the content if there is unrewound excess */
-
+    if(conn->handler->readwrite &&
+       (excess > 0 && !conn->bits.stream_was_rewound)) {
       /* Parse the excess data */
       k->str += nread;
       nread = (ssize_t)excess;
 
-      result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
+      result = conn->handler->readwrite(data, conn, &nread, &readmore);
       if(result)
         return result;
 
       if(readmore)
         k->keepon |= KEEP_RECV; /* we're not done reading */
-        break;
+      break;
     }
-#endif
 
     if(is_empty_data) {
       /* if we received nothing, the server closed the connection and we
@@ -792,6 +833,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 {
 
@@ -805,9 +849,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 */
@@ -817,12 +862,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->protocol&(PROT_HTTP|PROT_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;
@@ -858,29 +903,23 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
       /* store number of bytes available for upload */
       data->req.upload_present = nread;
 
-#ifndef CURL_DISABLE_SMTP
-      if(conn->protocol & PROT_SMTP) {
-        result = Curl_smtp_escape_eob(conn, nread);
-        if(result)
-          return result;
-      }
-      else
-#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
-        if(data->state.scratch == NULL)
-          data->state.scratch = malloc(2*BUFSIZE);
-        if(data->state.scratch == NULL) {
-          failf (data, "Failed to alloc scratch buffer!");
-          return CURLE_OUT_OF_MEMORY;
+         (data->set.crlf))) {
+        /* Do we need to allocate a scratch buffer? */
+        if(!data->state.scratch) {
+          data->state.scratch = malloc(2 * BUFSIZE);
+          if(!data->state.scratch) {
+            failf(data, "Failed to alloc scratch buffer!");
+
+            return CURLE_OUT_OF_MEMORY;
+          }
         }
+
         /*
          * ASCII/EBCDIC Note: This is presumably a text (not binary)
          * transfer so the data should already be in ASCII.
@@ -894,12 +933,13 @@ 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
             data->state.scratch[si] = data->req.upload_fromhere[i];
         }
+
         if(si != nread) {
           /* only perform the special operation if we really did replace
              anything */
@@ -912,6 +952,14 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
           data->req.upload_present = nread;
         }
       }
+
+#ifndef CURL_DISABLE_SMTP
+      if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
+        result = Curl_smtp_escape_eob(conn, nread);
+        if(result)
+          return result;
+      }
+#endif /* CURL_DISABLE_SMTP */
     } /* if 0 == data->req.upload_present */
     else {
       /* We have a partial buffer left from a previous "round". Use
@@ -933,6 +981,14 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
       Curl_debug(data, CURLINFO_DATA_OUT, data->req.upload_fromhere,
                  (size_t)bytes_written, conn);
 
+    k->writebytecount += bytes_written;
+
+    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");
+    }
+
     if(data->req.upload_present != bytes_written) {
       /* we only wrote a part of the buffer (if anything), deal with it! */
 
@@ -954,10 +1010,9 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
       }
     }
 
-    k->writebytecount += bytes_written;
     Curl_pgrsSetUploadCounter(data, k->writebytecount);
 
-  } while(0); /* just to break out from! */
+  } WHILE_FALSE; /* just to break out from! */
 
   return CURLE_OK;
 }
@@ -1047,7 +1102,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;
@@ -1064,16 +1119,17 @@ CURLcode Curl_readwrite(struct connectdata *conn,
     return result;
 
   if(k->keepon) {
-    if(0 > Curl_timeleft(conn, &k->now, FALSE)) {
+    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;
@@ -1095,7 +1151,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;
@@ -1105,7 +1161,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
@@ -1120,8 +1176,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;
 }
@@ -1162,10 +1218,10 @@ int Curl_single_getsock(const struct connectdata *conn,
   if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
 
     if((conn->sockfd != conn->writesockfd) ||
-       !(data->req.keepon & KEEP_RECV)) {
-      /* only if they are not the same socket or we didn't have a readable
+       bitmap == GETSOCK_BLANK) {
+      /* only if they are not the same socket and we have a readable
          one, we increase index */
-      if(data->req.keepon & KEEP_RECV)
+      if(bitmap != GETSOCK_BLANK)
         sockindex++; /* increase index if we need two entries */
 
       DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
@@ -1182,11 +1238,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,
@@ -1195,24 +1251,24 @@ long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
   curl_off_t min_sleep = 0;
   curl_off_t rv = 0;
 
-  if (rate_bps == 0)
+  if(rate_bps == 0)
     return 0;
 
   /* If running faster than about .1% of the desired speed, slow
    * us down a bit.  Use shift instead of division as the 0.1%
    * cutoff is arbitrary anyway.
    */
-  if (cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
+  if(cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
     /* running too fast, decrease target rate by 1/64th of rate */
     rate_bps -= rate_bps >> 6;
     min_sleep = 1;
   }
-  else if (cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
+  else if(cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
     /* running too slow, increase target rate by 1/64th of rate */
     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.
    */
@@ -1221,7 +1277,7 @@ long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
   /* Catch rounding errors and always slow down at least 1ms if
    * we are running too fast.
    */
-  if (rv < min_sleep)
+  if(rv < min_sleep)
     rv = min_sleep;
 
   /* Bound value to fit in 'long' on 32-bit platform.  That's
@@ -1233,201 +1289,12 @@ 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;
-  int timeout_ms;
-  int buffersize;
-  int 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 = (int)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 = (int)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(conn, &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 void 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] == '-') {
-      /* 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);
-    }
-  }
-  data->change.resolve = NULL; /* dealt with now */
-}
-
-
 /*
  * Curl_pretransfer() is called immediately before a transfer starts.
  */
 CURLcode Curl_pretransfer(struct SessionHandle *data)
 {
-  CURLcode res;
+  CURLcode result;
   if(!data->change.url) {
     /* we can't do anything without URL */
     failf(data, "No URL set!");
@@ -1435,19 +1302,17 @@ 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);
-  if(res)
-    return res;
+  result = Curl_ssl_initsessions(data, data->set.ssl.max_ssl_sessions);
+  if(result)
+    return result;
 
   data->set.followlocation=0; /* reset the location-follow counter */
   data->state.this_is_a_follow = FALSE; /* reset this */
   data->state.errorbuf = FALSE; /* no error has occurred */
   data->state.httpversion = 0; /* don't assume any particular server version */
 
-  data->state.ssl_connect_retry = FALSE;
-
   data->state.authproblem = FALSE;
   data->state.authhost.want = data->set.httpauth;
   data->state.authproxy.want = data->set.proxyauth;
@@ -1460,31 +1325,40 @@ CURLcode Curl_pretransfer(struct SessionHandle *data)
 
   /* If there is a list of host pairs to deal with */
   if(data->change.resolve)
-    loadhostpairs(data);
+    result = Curl_loadhostpairs(data);
 
- /* Allow data->set.use_port to set which port to use. This needs to be
-  * disabled for example when we follow Location: headers to URLs using
-  * different ports! */
-  data->state.allow_port = TRUE;
+  if(!result) {
+    /* Allow data->set.use_port to set which port to use. This needs to be
+     * disabled for example when we follow Location: headers to URLs using
+     * different ports! */
+    data->state.allow_port = TRUE;
 
 #if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
-  /*************************************************************
-   * Tell signal handler to ignore SIGPIPE
-   *************************************************************/
-  if(!data->set.no_signal)
-    data->state.prev_signal = signal(SIGPIPE, SIG_IGN);
+    /*************************************************************
+     * Tell signal handler to ignore SIGPIPE
+     *************************************************************/
+    if(!data->set.no_signal)
+      data->state.prev_signal = signal(SIGPIPE, SIG_IGN);
 #endif
 
-  Curl_initinfo(data); /* reset session-specific information "variables" */
-  Curl_pgrsStartNow(data);
+    Curl_initinfo(data); /* reset session-specific information "variables" */
+    Curl_pgrsResetTimesSizes(data);
+    Curl_pgrsStartNow(data);
 
-  if(data->set.timeout)
-    Curl_expire(data, data->set.timeout);
+    if(data->set.timeout)
+      Curl_expire(data, data->set.timeout);
 
-  if(data->set.connecttimeout)
-    Curl_expire(data, data->set.connecttimeout);
+    if(data->set.connecttimeout)
+      Curl_expire(data, data->set.connecttimeout);
 
-  return CURLE_OK;
+    /* 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 result;
 }
 
 /*
@@ -1500,11 +1374,6 @@ CURLcode Curl_posttransfer(struct SessionHandle *data)
   (void)data; /* unused parameter */
 #endif
 
-  if(!(data->progress.flags & PGRS_HIDE) &&
-     !data->progress.callback)
-    /* only output if we don't use a progress callback and we're not hidden */
-    fprintf(data->set.err, "\n");
-
   return CURLE_OK;
 }
 
@@ -1580,7 +1449,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;
 }
 
 /*
@@ -1672,26 +1541,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;
+      }
     }
   }
 
@@ -1704,8 +1584,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 */
@@ -1768,15 +1648,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 */
     }
   }
@@ -1787,7 +1666,7 @@ CURLcode Curl_follow(struct SessionHandle *data,
      to be absolute and this doesn't seem to be that!
      */
     char *absolute = concat_url(data->change.url, newurl);
-    if (!absolute)
+    if(!absolute)
       return CURLE_OUT_OF_MEMORY;
     free(newurl);
     newurl = absolute;
@@ -1803,7 +1682,7 @@ CURLcode Curl_follow(struct SessionHandle *data,
       size_t newlen = strlen_url(newurl);
 
       newest = malloc(newlen+1); /* get memory for this */
-      if (!newest)
+      if(!newest)
         return CURLE_OUT_OF_MEMORY;
       strcpy_url(newest, newurl); /* create a space-free URL */
 
@@ -1823,12 +1702,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);
@@ -1854,63 +1734,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");
+    if((data->set.httpreq == HTTPREQ_POST
+        || data->set.httpreq == HTTPREQ_POST_FORM)
+       && !(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
-    */
-    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");
+    /* (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.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");
@@ -1932,39 +1808,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_wait_for_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)
 {
@@ -1980,10 +1829,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
@@ -1992,20 +1843,20 @@ Curl_reconnect_request(struct connectdata **connp)
    * (again). Slight Lack of feedback in the report, but I don't think this
    * extra check can do much harm.
    */
-  if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
+  if(!result || (CURLE_SEND_ERROR == result)) {
     bool async;
     bool protocol_done = TRUE;
 
     /* Now, redo the connect and get a new connection */
     result = Curl_connect(data, connp, &async, &protocol_done);
-    if(CURLE_OK == result) {
+    if(!result) {
       /* We have connected or sent away a name resolve query fine */
 
       conn = *connp; /* setup conn to again point to something nice */
       if(async) {
         /* Now, if async is TRUE here, we need to wait for the name
            to resolve */
-        result = Curl_wait_for_resolv(conn, NULL);
+        result = Curl_resolver_wait_resolv(conn, NULL);
         if(result)
           return result;
 
@@ -2032,15 +1883,14 @@ 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->protocol&(PROT_HTTP|PROT_RTSP)))
+  if(data->set.upload &&
+     !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)))
     return CURLE_OK;
 
-  if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry ||
-      ((data->req.bytecount +
-        data->req.headerbytecount == 0) &&
-        conn->bits.reuse &&
-        !data->set.opt_no_body &&
-        data->set.rtspreq != RTSPREQ_RECEIVE)) {
+  if((data->req.bytecount + data->req.headerbytecount == 0) &&
+      conn->bits.reuse &&
+      !data->set.opt_no_body &&
+      (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
@@ -2050,206 +1900,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
-                                transfered! */
-  }
-  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 */
+                                transferred! */
 
-    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 */
-            sclose(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;
 }
 
 /*
@@ -2305,6 +1970,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
@@ -2315,13 +1981,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)