Revert "Imported Upstream version 7.44.0"
[platform/upstream/curl.git] / lib / url.c
index 406c1f0..d3bb5e0 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2015, 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
@@ -124,12 +124,16 @@ int curl_win32_idn_to_ascii(const char *in, char **out);
 #include "curl_rtmp.h"
 #include "gopher.h"
 #include "http_proxy.h"
+#include "bundles.h"
 #include "conncache.h"
 #include "multihandle.h"
 #include "pipeline.h"
 #include "dotdot.h"
 #include "strdup.h"
-#include "curl_printf.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
 #include "curl_memory.h"
 /* The last #include file should be: */
 #include "memdebug.h"
@@ -142,6 +146,7 @@ find_oldest_idle_connection_in_bundle(struct SessionHandle *data,
                                       struct connectbundle *bundle);
 static void conn_free(struct connectdata *conn);
 static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
+static CURLcode do_init(struct connectdata *conn);
 static CURLcode parse_url_login(struct SessionHandle *data,
                                 struct connectdata *conn,
                                 char **userptr, char **passwdptr,
@@ -446,8 +451,10 @@ CURLcode Curl_close(struct SessionHandle *data)
   Curl_ssl_free_certinfo(data);
 
   /* Cleanup possible redirect junk */
-  free(data->req.newurl);
-  data->req.newurl = NULL;
+  if(data->req.newurl) {
+    free(data->req.newurl);
+    data->req.newurl = NULL;
+  }
 
   if(data->change.referer_alloc) {
     Curl_safefree(data->change.referer);
@@ -574,30 +581,13 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
                      (char *) CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE);
   if(result)
     return result;
-
-  /* set default negotiate proxy service name */
-  result = setstropt(&set->str[STRING_PROXY_SERVICE_NAME],
-                     (char *) CURL_DEFAULT_PROXY_SERVICE_NAME);
-  if(result)
-    return result;
-
-  /* set default negotiate service name */
-  result = setstropt(&set->str[STRING_SERVICE_NAME],
-                     (char *) CURL_DEFAULT_SERVICE_NAME);
-  if(result)
-    return result;
 #endif
 
   /* This is our preferred CA cert bundle/path since install time */
 #if defined(CURL_CA_BUNDLE)
   result = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE);
-  if(result)
-    return result;
-#endif
-#if defined(CURL_CA_PATH)
+#elif defined(CURL_CA_PATH)
   result = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH);
-  if(result)
-    return result;
 #endif
 
   set->wildcardmatch  = FALSE;
@@ -615,7 +605,6 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
   set->ssl_enable_alpn = TRUE;
 
   set->expect_100_timeout = 1000L; /* Wait for a second by default. */
-  set->sep_headers = TRUE; /* separated header lists by default */
   return result;
 }
 
@@ -677,7 +666,8 @@ CURLcode Curl_open(struct SessionHandle **curl)
 
   if(result) {
     Curl_resolver_cleanup(data->state.resolver);
-    free(data->state.headerbuff);
+    if(data->state.headerbuff)
+      free(data->state.headerbuff);
     Curl_freeset(data);
     free(data);
     data = NULL;
@@ -903,11 +893,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
      * Set explicit SSL version to try to connect with, as some SSL
      * implementations are lame.
      */
-#ifdef USE_SSL
     data->set.ssl.version = va_arg(param, long);
-#else
-    result = CURLE_UNKNOWN_OPTION;
-#endif
     break;
 
 #ifndef CURL_DISABLE_HTTP
@@ -1250,7 +1236,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
       argptr = strdup(argptr);
       if(!argptr || !data->cookies) {
         result = CURLE_OUT_OF_MEMORY;
-        free(argptr);
+        Curl_safefree(argptr);
       }
       else {
         Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
@@ -1483,29 +1469,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
                        va_arg(param, char *));
     break;
 
-  case CURLOPT_PROXY_SERVICE_NAME:
-    /*
-     * Set negotiate proxy service name
-     */
-    result = setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME],
-                       va_arg(param, char *));
-    break;
-
   case CURLOPT_SOCKS5_GSSAPI_NEC:
     /*
      * set flag for nec socks5 support
      */
     data->set.socks5_gssapi_nec = (0 != va_arg(param, long))?TRUE:FALSE;
     break;
-
-  case CURLOPT_SERVICE_NAME:
-    /*
-     * Set negotiate service identity
-     */
-    result = setstropt(&data->set.str[STRING_SERVICE_NAME],
-                       va_arg(param, char *));
-    break;
-
 #endif
 
   case CURLOPT_HEADERDATA:
@@ -2028,17 +1997,6 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
 
     data->set.ssl.verifyhost = (0 != arg)?TRUE:FALSE;
     break;
-  case CURLOPT_SSL_VERIFYSTATUS:
-    /*
-     * Enable certificate status verifying.
-     */
-    if(!Curl_ssl_cert_status_request()) {
-      result = CURLE_NOT_BUILT_IN;
-      break;
-    }
-
-    data->set.ssl.verifystatus = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
   case CURLOPT_SSL_CTX_FUNCTION:
 #ifdef have_curlssl_ssl_ctx
     /*
@@ -2059,17 +2017,6 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     result = CURLE_NOT_BUILT_IN;
 #endif
     break;
-  case CURLOPT_SSL_FALSESTART:
-    /*
-     * Enable TLS false start.
-     */
-    if(!Curl_ssl_false_start()) {
-      result = CURLE_NOT_BUILT_IN;
-      break;
-    }
-
-    data->set.ssl.falsestart = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
   case CURLOPT_CERTINFO:
 #ifdef have_curlssl_certinfo
     data->set.ssl.certinfo = (0 != va_arg(param, long))?TRUE:FALSE;
@@ -2185,15 +2132,16 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
 
       data->share->dirty++;
 
-      if(data->share->specifier & (1<< CURL_LOCK_DATA_DNS)) {
+      if(data->share->hostcache) {
         /* use shared host cache */
-        data->dns.hostcache = &data->share->hostcache;
+        data->dns.hostcache = data->share->hostcache;
         data->dns.hostcachetype = HCACHE_SHARED;
       }
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
       if(data->share->cookies) {
         /* use shared cookie list, first free own one if any */
-        Curl_cookie_cleanup(data->cookies);
+        if(data->cookies)
+          Curl_cookie_cleanup(data->cookies);
         /* enable cookies since we now use a share that uses cookies! */
         data->cookies = data->share->cookies;
       }
@@ -2234,8 +2182,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
 
   case CURLOPT_SSL_OPTIONS:
     arg = va_arg(param, long);
-    data->set.ssl_enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
-    data->set.ssl_no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
+    data->set.ssl_enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE;
     break;
 
 #endif
@@ -2646,12 +2593,6 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     break;
 #endif
 
-  case CURLOPT_PATH_AS_IS:
-    data->set.path_as_is = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_PIPEWAIT:
-    data->set.pipewait = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_UNKNOWN_OPTION;
@@ -2777,15 +2718,16 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection)
   free(conn->host.encalloc); /* encoded host name buffer, must be freed with
                                 idn_free() since this was allocated by
                                 curl_win32_idn_to_ascii */
-  free(conn->proxy.encalloc); /* encoded proxy name buffer, must be freed
-                                 with idn_free() since this was allocated by
-                                 curl_win32_idn_to_ascii */
+  if(conn->proxy.encalloc)
+    free(conn->proxy.encalloc); /* encoded proxy name buffer, must be freed
+                                   with idn_free() since this was allocated by
+                                   curl_win32_idn_to_ascii */
 #endif
 
   Curl_ssl_close(conn, FIRSTSOCKET);
 
   /* Indicate to all handles on the pipe that we're dead */
-  if(Curl_pipeline_wanted(data->multi, CURLPIPE_ANY)) {
+  if(Curl_multi_pipeline_enabled(data->multi)) {
     signalPipeClose(conn->send_pipe, TRUE);
     signalPipeClose(conn->recv_pipe, TRUE);
   }
@@ -2813,31 +2755,32 @@ static bool SocketIsDead(curl_socket_t sock)
   return ret_val;
 }
 
-/*
- * IsPipeliningPossible() returns TRUE if the options set would allow
- * pipelining/multiplexing and the connection is using a HTTP protocol.
- */
 static bool IsPipeliningPossible(const struct SessionHandle *handle,
                                  const struct connectdata *conn)
 {
-  /* If a HTTP protocol and pipelining is enabled */
-  if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
-
-    if(Curl_pipeline_wanted(handle->multi, CURLPIPE_HTTP1) &&
-       (handle->set.httpversion != CURL_HTTP_VERSION_1_0) &&
-       (handle->set.httpreq == HTTPREQ_GET ||
-        handle->set.httpreq == HTTPREQ_HEAD))
-      /* didn't ask for HTTP/1.0 and a GET or HEAD */
-      return TRUE;
+  if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+     Curl_multi_pipeline_enabled(handle->multi) &&
+     (handle->set.httpreq == HTTPREQ_GET ||
+      handle->set.httpreq == HTTPREQ_HEAD) &&
+     handle->set.httpversion != CURL_HTTP_VERSION_1_0)
+    return TRUE;
 
-    if(Curl_pipeline_wanted(handle->multi, CURLPIPE_MULTIPLEX) &&
-       (handle->set.httpversion == CURL_HTTP_VERSION_2_0))
-      /* allows HTTP/2 */
-      return TRUE;
-  }
   return FALSE;
 }
 
+bool Curl_isPipeliningEnabled(const struct SessionHandle *handle)
+{
+  return Curl_multi_pipeline_enabled(handle->multi);
+}
+
+CURLcode Curl_addHandleToPipeline(struct SessionHandle *data,
+                                  struct curl_llist *pipeline)
+{
+  if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
+    return CURLE_OUT_OF_MEMORY;
+  return CURLE_OK;
+}
+
 int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
                                   struct curl_llist *pipeline)
 {
@@ -2885,14 +2828,15 @@ void Curl_getoff_all_pipelines(struct SessionHandle *data,
                                struct connectdata *conn)
 {
   bool recv_head = (conn->readchannel_inuse &&
-                    Curl_recvpipe_head(data, conn));
+    (gethandleathead(conn->recv_pipe) == data)) ? TRUE : FALSE;
+
   bool send_head = (conn->writechannel_inuse &&
-                    Curl_sendpipe_head(data, conn));
+    (gethandleathead(conn->send_pipe) == data)) ? TRUE : FALSE;
 
   if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) && recv_head)
-    Curl_pipeline_leave_read(conn);
+    conn->readchannel_inuse = FALSE;
   if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head)
-    Curl_pipeline_leave_write(conn);
+    conn->writechannel_inuse = FALSE;
 }
 
 static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke)
@@ -2944,7 +2888,7 @@ find_oldest_idle_connection(struct SessionHandle *data)
 
   now = Curl_tvnow();
 
-  Curl_hash_start_iterate(&bc->hash, &iter);
+  Curl_hash_start_iterate(bc->hash, &iter);
 
   he = Curl_hash_next_element(&iter);
   while(he) {
@@ -3078,13 +3022,6 @@ static void prune_dead_connections(struct SessionHandle *data)
   }
 }
 
-
-static size_t max_pipeline_length(struct Curl_multi *multi)
-{
-  return multi ? multi->max_pipeline_length : 0;
-}
-
-
 /*
  * Given one filled in connection struct (named needle), this function should
  * detect if there already is one that has all the significant details
@@ -3101,21 +3038,17 @@ static bool
 ConnectionExists(struct SessionHandle *data,
                  struct connectdata *needle,
                  struct connectdata **usethis,
-                 bool *force_reuse,
-                 bool *waitpipe)
+                 bool *force_reuse)
 {
   struct connectdata *check;
   struct connectdata *chosen = 0;
   bool canPipeline = IsPipeliningPossible(data, needle);
-#ifdef USE_NTLM
   bool wantNTLMhttp = ((data->state.authhost.want & CURLAUTH_NTLM) ||
                        (data->state.authhost.want & CURLAUTH_NTLM_WB)) &&
     (needle->handler->protocol & PROTO_FAMILY_HTTP) ? TRUE : FALSE;
-#endif
   struct connectbundle *bundle;
 
   *force_reuse = FALSE;
-  *waitpipe = FALSE;
 
   /* We can't pipe if the site is blacklisted */
   if(canPipeline && Curl_pipeline_site_blacklisted(data, needle)) {
@@ -3124,11 +3057,10 @@ ConnectionExists(struct SessionHandle *data,
 
   /* Look up the bundle with all the connections to this
      particular host */
-  bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache);
+  bundle = Curl_conncache_find_bundle(data->state.conn_cache,
+                                      needle->host.name);
   if(bundle) {
-    /* Max pipe length is zero (unlimited) for multiplexed connections */
-    size_t max_pipe_len = (bundle->multiuse != BUNDLE_MULTIPLEX)?
-      max_pipeline_length(data->multi):0;
+    size_t max_pipe_len = Curl_multi_max_pipeline_length(data->multi);
     size_t best_pipe_len = max_pipe_len;
     struct curl_llist_element *curr;
 
@@ -3136,17 +3068,9 @@ ConnectionExists(struct SessionHandle *data,
           needle->host.name, (void *)bundle);
 
     /* We can't pipe if we don't know anything about the server */
-    if(canPipeline) {
-      if(bundle->multiuse <= BUNDLE_UNKNOWN) {
-        if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) {
-          infof(data, "Server doesn't support multi-use yet, wait\n");
-          *waitpipe = TRUE;
-          return FALSE; /* no re-use */
-        }
-
-        infof(data, "Server doesn't support multi-use (yet)\n");
-        canPipeline = FALSE;
-      }
+    if(canPipeline && !bundle->server_supports_pipelining) {
+      infof(data, "Server doesn't support pipelining\n");
+      canPipeline = FALSE;
     }
 
     curr = bundle->conn_list->head;
@@ -3170,19 +3094,16 @@ ConnectionExists(struct SessionHandle *data,
       pipeLen = check->send_pipe->size + check->recv_pipe->size;
 
       if(canPipeline) {
-
-        if(!check->bits.multiplex) {
-          /* If not multiplexing, make sure the pipe has only GET requests */
-          struct SessionHandle* sh = gethandleathead(check->send_pipe);
-          struct SessionHandle* rh = gethandleathead(check->recv_pipe);
-          if(sh) {
-            if(!IsPipeliningPossible(sh, check))
-              continue;
-          }
-          else if(rh) {
-            if(!IsPipeliningPossible(rh, check))
-              continue;
-          }
+        /* Make sure the pipe has only GET requests */
+        struct SessionHandle* sh = gethandleathead(check->send_pipe);
+        struct SessionHandle* rh = gethandleathead(check->recv_pipe);
+        if(sh) {
+          if(!IsPipeliningPossible(sh, check))
+            continue;
+        }
+        else if(rh) {
+          if(!IsPipeliningPossible(rh, check))
+            continue;
         }
       }
       else {
@@ -3262,11 +3183,8 @@ ConnectionExists(struct SessionHandle *data,
           continue;
       }
 
-      if((!(needle->handler->flags & PROTOPT_CREDSPERREQUEST))
-#ifdef USE_NTLM
-         || (wantNTLMhttp || check->ntlm.state != NTLMSTATE_NONE)
-#endif
-        ) {
+      if((!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) ||
+         wantNTLMhttp) {
         /* This protocol requires credentials per connection or is HTTP+NTLM,
            so verify that we're using the same name and password as well */
         if(!strequal(needle->user, check->user) ||
@@ -3361,42 +3279,19 @@ ConnectionExists(struct SessionHandle *data,
           }
 
           /* We can't use the connection if the pipe is full */
-          if(max_pipe_len && (pipeLen >= max_pipe_len)) {
-            infof(data, "Pipe is full, skip (%zu)\n", pipeLen);
+          if(pipeLen >= max_pipe_len)
             continue;
-          }
-#ifdef USE_NGHTTP2
-          /* If multiplexed, make sure we don't go over concurrency limit */
-          if(check->bits.multiplex) {
-            /* Multiplexed connections can only be HTTP/2 for now */
-            struct http_conn *httpc = &check->proto.httpc;
-            if(pipeLen >= httpc->settings.max_concurrent_streams) {
-              infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)\n",
-                    pipeLen);
-              continue;
-            }
-          }
-#endif
+
           /* We can't use the connection if the pipe is penalized */
-          if(Curl_pipeline_penalized(data, check)) {
-            infof(data, "Penalized, skip\n");
+          if(Curl_pipeline_penalized(data, check))
             continue;
-          }
 
-          if(max_pipe_len) {
-            if(pipeLen < best_pipe_len) {
-              /* This connection has a shorter pipe so far. We'll pick this
-                 and continue searching */
-              chosen = check;
-              best_pipe_len = pipeLen;
-              continue;
-            }
-          }
-          else {
-            /* When not pipelining (== multiplexed), we have a match here! */
+          if(pipeLen < best_pipe_len) {
+            /* This connection has a shorter pipe so far. We'll pick this
+               and continue searching */
             chosen = check;
-            infof(data, "Multiplexed connection found!\n");
-            break;
+            best_pipe_len = pipeLen;
+            continue;
           }
         }
         else {
@@ -3448,6 +3343,20 @@ ConnectionDone(struct SessionHandle *data, struct connectdata *conn)
   return (conn_candidate == conn) ? FALSE : TRUE;
 }
 
+/*
+ * The given input connection struct pointer is to be stored in the connection
+ * cache. If the cache is already full, least interesting existing connection
+ * (if any) gets closed.
+ *
+ * The given connection should be unique. That must've been checked prior to
+ * this call.
+ */
+static CURLcode ConnectionStore(struct SessionHandle *data,
+                                struct connectdata *conn)
+{
+  return Curl_conncache_add_conn(data->state.conn_cache, conn);
+}
+
 /* after a TCP connection to the proxy has been verified, this function does
    the next magic step.
 
@@ -3693,7 +3602,7 @@ static void fix_hostname(struct SessionHandle *data,
   host->dispname = host->name;
 
   len = strlen(host->name);
-  if(len && (host->name[len-1] == '.'))
+  if(host->name[len-1] == '.')
     /* strip off a single trailing dot if present, primarily for SNI but
        there's no use for it */
     host->name[len-1]=0;
@@ -3710,7 +3619,7 @@ static void fix_hostname(struct SessionHandle *data,
            stringprep_locale_charset ());
     if(rc != IDNA_SUCCESS)
       infof(data, "Failed to convert %s to ACE; %s\n",
-            host->name, Curl_idn_strerror(conn, rc));
+            host->name, Curl_idn_strerror(conn,rc));
     else {
       /* tld_check_name() displays a warning if the host name contains
          "illegal" characters for this TLD */
@@ -3823,9 +3732,9 @@ static struct connectdata *allocate_conn(struct SessionHandle *data)
   conn->response_header = NULL;
 #endif
 
-  if(Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1) &&
-     !conn->master_buffer) {
-    /* Allocate master_buffer to be used for HTTP/1 pipelining */
+  if(Curl_multi_pipeline_enabled(data->multi) &&
+      !conn->master_buffer) {
+    /* Allocate master_buffer to be used for pipelining */
     conn->master_buffer = calloc(BUFSIZE, sizeof (char));
     if(!conn->master_buffer)
       goto error;
@@ -3864,9 +3773,9 @@ static struct connectdata *allocate_conn(struct SessionHandle *data)
   conn->send_pipe = NULL;
   conn->recv_pipe = NULL;
 
-  free(conn->master_buffer);
-  free(conn->localdev);
-  free(conn);
+  Curl_safefree(conn->master_buffer);
+  Curl_safefree(conn->localdev);
+  Curl_safefree(conn);
   return NULL;
 }
 
@@ -4109,7 +4018,7 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data,
     path[0] = '/';
     rebuild_url = TRUE;
   }
-  else if(!data->set.path_as_is) {
+  else {
     /* sanitise paths and remove ../ and ./ sequences according to RFC3986 */
     char *newp = Curl_dedotdotify(path);
     if(!newp)
@@ -4504,7 +4413,8 @@ static char *detect_proxy(struct connectdata *conn)
     }
   } /* if(!check_noproxy(conn->host.name, no_proxy)) - it wasn't specified
        non-proxy */
-  free(no_proxy);
+  if(no_proxy)
+    free(no_proxy);
 
 #else /* !CURL_DISABLE_HTTP */
 
@@ -4518,6 +4428,7 @@ static char *detect_proxy(struct connectdata *conn)
  * If this is supposed to use a proxy, we need to figure out the proxy
  * host name, so that we can re-use an existing connection
  * that may exist registered to the same proxy host.
+ * proxy will be freed before this function returns.
  */
 static CURLcode parse_proxy(struct SessionHandle *data,
                             struct connectdata *conn, char *proxy)
@@ -4591,8 +4502,8 @@ static CURLcode parse_proxy(struct SessionHandle *data,
       }
     }
 
-    free(proxyuser);
-    free(proxypasswd);
+    Curl_safefree(proxyuser);
+    Curl_safefree(proxypasswd);
 
     if(result)
       return result;
@@ -4804,9 +4715,9 @@ static CURLcode parse_url_login(struct SessionHandle *data,
 
   out:
 
-  free(userp);
-  free(passwdp);
-  free(optionsp);
+  Curl_safefree(userp);
+  Curl_safefree(passwdp);
+  Curl_safefree(optionsp);
 
   return result;
 }
@@ -4894,7 +4805,7 @@ static CURLcode parse_login_details(const char *login, const size_t len,
   if(!result && passwdp && plen) {
     pbuf = malloc(plen + 1);
     if(!pbuf) {
-      free(ubuf);
+      Curl_safefree(ubuf);
       result = CURLE_OUT_OF_MEMORY;
     }
   }
@@ -4903,8 +4814,8 @@ static CURLcode parse_login_details(const char *login, const size_t len,
   if(!result && optionsp && olen) {
     obuf = malloc(olen + 1);
     if(!obuf) {
-      free(pbuf);
-      free(ubuf);
+      Curl_safefree(pbuf);
+      Curl_safefree(ubuf);
       result = CURLE_OUT_OF_MEMORY;
     }
   }
@@ -5266,7 +5177,8 @@ static CURLcode resolve_server(struct SessionHandle *data,
 static void reuse_conn(struct connectdata *old_conn,
                        struct connectdata *conn)
 {
-  free(old_conn->proxy.rawalloc);
+  if(old_conn->proxy.rawalloc)
+    free(old_conn->proxy.rawalloc);
 
   /* free the SSL config struct from this connection struct as this was
      allocated in vain and is targeted for destruction */
@@ -5354,9 +5266,8 @@ static CURLcode create_conn(struct SessionHandle *data,
   bool reuse;
   char *proxy = NULL;
   bool prot_missing = FALSE;
-  bool connections_available = TRUE;
+  bool no_connections_available = FALSE;
   bool force_reuse = FALSE;
-  bool waitpipe = FALSE;
   size_t max_host_connections = Curl_multi_max_host_connections(data->multi);
   size_t max_total_connections = Curl_multi_max_total_connections(data->multi);
 
@@ -5516,8 +5427,10 @@ static CURLcode create_conn(struct SessionHandle *data,
 
   if(data->set.str[STRING_NOPROXY] &&
      check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) {
-    free(proxy);  /* proxy is in exception list */
-    proxy = NULL;
+    if(proxy) {
+      free(proxy);  /* proxy is in exception list */
+      proxy = NULL;
+    }
   }
   else if(!proxy)
     proxy = detect_proxy(conn);
@@ -5543,8 +5456,7 @@ static CURLcode create_conn(struct SessionHandle *data,
   if(proxy) {
     result = parse_proxy(data, conn, proxy);
 
-    free(proxy); /* parse_proxy copies the proxy string */
-    proxy = NULL;
+    Curl_safefree(proxy); /* parse_proxy copies the proxy string */
 
     if(result)
       goto out;
@@ -5565,10 +5477,8 @@ static CURLcode create_conn(struct SessionHandle *data,
       conn->bits.httpproxy = TRUE;
 #endif
     }
-    else {
+    else
       conn->bits.httpproxy = FALSE; /* not a HTTP proxy */
-      conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */
-    }
     conn->bits.proxy = TRUE;
   }
   else {
@@ -5633,7 +5543,7 @@ static CURLcode create_conn(struct SessionHandle *data,
       conn->data = data;
       conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */
 
-      Curl_conncache_add_conn(data->state.conn_cache, conn);
+      ConnectionStore(data, conn);
 
       /*
        * Setup whatever necessary for a resumed transfer
@@ -5651,7 +5561,7 @@ static CURLcode create_conn(struct SessionHandle *data,
     }
 
     /* since we skip do_init() */
-    Curl_init_do(data, conn);
+    do_init(conn);
 
     goto out;
   }
@@ -5698,7 +5608,7 @@ static CURLcode create_conn(struct SessionHandle *data,
   if(data->set.reuse_fresh && !data->state.this_is_a_follow)
     reuse = FALSE;
   else
-    reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe);
+    reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse);
 
   /* If we found a reusable connection, we may still want to
      open a new connection if we are pipelining. */
@@ -5735,24 +5645,18 @@ static CURLcode create_conn(struct SessionHandle *data,
     /* set a pointer to the hostname we display */
     fix_hostname(data, conn, &conn->host);
 
-    infof(data, "Re-using existing connection! (#%ld) with %s %s\n",
+    infof(data, "Re-using existing connection! (#%ld) with host %s\n",
           conn->connection_id,
-          conn->bits.proxy?"proxy":"host",
           conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
   }
   else {
     /* We have decided that we want a new connection. However, we may not
        be able to do that if we have reached the limit of how many
        connections we are allowed to open. */
-    struct connectbundle *bundle = NULL;
-
-    if(waitpipe)
-      /* There is a connection that *might* become usable for pipelining
-         "soon", and we wait for that */
-      connections_available = FALSE;
-    else
-      bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
+    struct connectbundle *bundle;
 
+    bundle = Curl_conncache_find_bundle(data->state.conn_cache,
+                                        conn->host.name);
     if(max_host_connections > 0 && bundle &&
        (bundle->num_connections >= max_host_connections)) {
       struct connectdata *conn_candidate;
@@ -5765,15 +5669,11 @@ static CURLcode create_conn(struct SessionHandle *data,
         conn_candidate->data = data;
         (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
       }
-      else {
-        infof(data, "No more connections allowed to host: %d\n",
-              max_host_connections);
-        connections_available = FALSE;
-      }
+      else
+        no_connections_available = TRUE;
     }
 
-    if(connections_available &&
-       (max_total_connections > 0) &&
+    if(max_total_connections > 0 &&
        (data->state.conn_cache->num_connections >= max_total_connections)) {
       struct connectdata *conn_candidate;
 
@@ -5785,13 +5685,12 @@ static CURLcode create_conn(struct SessionHandle *data,
         conn_candidate->data = data;
         (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
       }
-      else {
-        infof(data, "No connections available in cache\n");
-        connections_available = FALSE;
-      }
+      else
+        no_connections_available = TRUE;
     }
 
-    if(!connections_available) {
+
+    if(no_connections_available) {
       infof(data, "No connections available.\n");
 
       conn_free(conn);
@@ -5805,7 +5704,7 @@ static CURLcode create_conn(struct SessionHandle *data,
        * This is a brand new connection, so let's store it in the connection
        * cache of ours!
        */
-      Curl_conncache_add_conn(data->state.conn_cache, conn);
+      ConnectionStore(data, conn);
     }
 
 #if defined(USE_NTLM)
@@ -5830,7 +5729,7 @@ static CURLcode create_conn(struct SessionHandle *data,
   conn->inuse = TRUE;
 
   /* Setup and init stuff before DO starts, in preparing for the transfer. */
-  Curl_init_do(data, conn);
+  do_init(conn);
 
   /*
    * Setup whatever necessary for a resumed transfer
@@ -5845,6 +5744,8 @@ static CURLcode create_conn(struct SessionHandle *data,
    * Inherit the proper values from the urldata struct AFTER we have arranged
    * the persistent connection stuff
    */
+  conn->fread_func = data->set.fread_func;
+  conn->fread_in = data->set.in;
   conn->seek_func = data->set.seek_func;
   conn->seek_client = data->set.seek_client;
 
@@ -5855,10 +5756,10 @@ static CURLcode create_conn(struct SessionHandle *data,
 
   out:
 
-  free(options);
-  free(passwd);
-  free(user);
-  free(proxy);
+  Curl_safefree(options);
+  Curl_safefree(passwd);
+  Curl_safefree(user);
+  Curl_safefree(proxy);
   return result;
 }
 
@@ -6002,19 +5903,37 @@ CURLcode Curl_done(struct connectdata **connp,
   conn = *connp;
   data = conn->data;
 
-  DEBUGF(infof(data, "Curl_done\n"));
-
-  if(data->state.done)
+  if(conn->bits.done)
     /* Stop if Curl_done() has already been called */
     return CURLE_OK;
 
   Curl_getoff_all_pipelines(data, conn);
 
+  if((conn->send_pipe->size + conn->recv_pipe->size != 0 &&
+      !data->set.reuse_forbid &&
+      !conn->bits.close))
+    /* Stop if pipeline is not empty and we do not have to close
+       connection. */
+    return CURLE_OK;
+
+  conn->bits.done = TRUE; /* called just now! */
+
   /* Cleanup possible redirect junk */
-  free(data->req.newurl);
-  data->req.newurl = NULL;
-  free(data->req.location);
-  data->req.location = NULL;
+  if(data->req.newurl) {
+    free(data->req.newurl);
+    data->req.newurl = NULL;
+  }
+  if(data->req.location) {
+    free(data->req.location);
+    data->req.location = NULL;
+  }
+
+  Curl_resolver_cancel(conn);
+
+  if(conn->dns_entry) {
+    Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
+    conn->dns_entry = NULL;
+  }
 
   switch(status) {
   case CURLE_ABORTED_BY_CALLBACK:
@@ -6038,27 +5957,12 @@ CURLcode Curl_done(struct connectdata **connp,
   if(!result && Curl_pgrsDone(conn))
     result = CURLE_ABORTED_BY_CALLBACK;
 
-  if((conn->send_pipe->size + conn->recv_pipe->size != 0 &&
-      !data->set.reuse_forbid &&
-      !conn->bits.close)) {
-    /* Stop if pipeline is not empty and we do not have to close
-       connection. */
-    DEBUGF(infof(data, "Connection still in use, no more Curl_done now!\n"));
-    return CURLE_OK;
-  }
-
-  data->state.done = TRUE; /* called just now! */
-  Curl_resolver_cancel(conn);
-
-  if(conn->dns_entry) {
-    Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
-    conn->dns_entry = NULL;
-  }
-
   /* if the transfer was completed in a paused state there can be buffered
      data left to write and then kill */
-  free(data->state.tempwrite);
-  data->state.tempwrite = NULL;
+  if(data->state.tempwrite) {
+    free(data->state.tempwrite);
+    data->state.tempwrite = NULL;
+  }
 
   /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
      forced us to close this connection. This is ignored for requests taking
@@ -6112,24 +6016,20 @@ CURLcode Curl_done(struct connectdata **connp,
 }
 
 /*
- * Curl_init_do() inits the readwrite session. This is inited each time (in
- * the DO function before the protocol-specific DO functions are invoked) for
- * transfer, sometimes multiple times on the same SessionHandle. Make sure
+ * do_init() inits the readwrite session. This is inited each time (in the DO
+ * function before the protocol-specific DO functions are invoked) for a
+ * transfer, sometimes multiple times on the same SessionHandle. Make sure
  * nothing in here depends on stuff that are setup dynamically for the
  * transfer.
- *
- * Allow this function to get called with 'conn' set to NULL.
  */
 
-CURLcode Curl_init_do(struct SessionHandle *data, struct connectdata *conn)
+static CURLcode do_init(struct connectdata *conn)
 {
+  struct SessionHandle *data = conn->data;
   struct SingleRequest *k = &data->req;
 
-  if(conn)
-    conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to
-                                 * use */
-
-  data->state.done = FALSE; /* Curl_done() is not called yet */
+  conn->bits.done = FALSE; /* Curl_done() is not called yet */
+  conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */
   data->state.expect100header = FALSE;
 
   if(data->set.opt_no_body)