Base code merged to SPIN 2.4
[platform/upstream/curl.git] / lib / sendf.c
index 922550b..2d1a166 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2007, 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
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
- * $Id$
  ***************************************************************************/
 
-#include "setup.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h> /* required for send() & recv() prototypes */
-#endif
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
+#include "curl_setup.h"
 
 #include <curl/curl.h>
+
 #include "urldata.h"
 #include "sendf.h"
 #include "connect.h"
-#include "sslgen.h"
+#include "vtls/vtls.h"
 #include "ssh.h"
 #include "multiif.h"
+#include "non-ascii.h"
 
 #define _MPRINTF_REPLACE /* use the internal *printf() functions */
 #include <curl/mprintf.h>
 
-#ifdef HAVE_KRB4
-#include "krb4.h"
-#else
-#define Curl_sec_send(a,b,c,d) -1
-#define Curl_sec_read(a,b,c,d) -1
-#endif
-
-#include <string.h>
-#include "memory.h"
+#include "curl_memory.h"
 #include "strerror.h"
-#include "easyif.h" /* for the Curl_convert_from_network prototype */
+
 /* The last #include file should be: */
 #include "memdebug.h"
 
-/* returns last node in linked list */
-static struct curl_slist *slist_get_last(struct curl_slist *list)
-{
-  struct curl_slist     *item;
-
-  /* if caller passed us a NULL, return now */
-  if (!list)
-    return NULL;
-
-  /* loop through to find the last item */
-  item = list;
-  while (item->next) {
-    item = item->next;
-  }
-  return item;
-}
-
-/*
- * curl_slist_append() appends a string to the linked list. It always retunrs
- * the address of the first record, so that you can sure this function as an
- * initialization function as well as an append function. If you find this
- * bothersome, then simply create a separate _init function and call it
- * appropriately from within the proram.
- */
-struct curl_slist *curl_slist_append(struct curl_slist *list,
-                                     const char *data)
-{
-  struct curl_slist     *last;
-  struct curl_slist     *new_item;
-
-  new_item = (struct curl_slist *) malloc(sizeof(struct curl_slist));
-  if (new_item) {
-    char *dup = strdup(data);
-    if(dup) {
-      new_item->next = NULL;
-      new_item->data = dup;
-    }
-    else {
-      free(new_item);
-      return NULL;
-    }
-  }
-  else
-    return NULL;
-
-  if (list) {
-    last = slist_get_last(list);
-    last->next = new_item;
-    return list;
-  }
-
-  /* if this is the first item, then new_item *is* the list */
-  return new_item;
-}
-
-/* be nice and clean up resources */
-void curl_slist_free_all(struct curl_slist *list)
-{
-  struct curl_slist     *next;
-  struct curl_slist     *item;
-
-  if (!list)
-    return;
-
-  item = list;
-  do {
-    next = item->next;
-
-    if (item->data) {
-      free(item->data);
-    }
-    free(item);
-    item = next;
-  } while (next);
-}
-
 #ifdef CURL_DO_LINEEND_CONV
 /*
  * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
@@ -150,17 +54,17 @@ static size_t convert_lineends(struct SessionHandle *data,
   char *inPtr, *outPtr;
 
   /* sanity check */
-  if ((startPtr == NULL) || (size < 1)) {
+  if((startPtr == NULL) || (size < 1)) {
     return(size);
   }
 
-  if (data->state.prev_block_had_trailing_cr == TRUE) {
+  if(data->state.prev_block_had_trailing_cr) {
     /* The previous block of incoming data
        had a trailing CR, which was turned into a LF. */
-    if (*startPtr == '\n') {
+    if(*startPtr == '\n') {
       /* This block of incoming data starts with the
          previous block's LF so get rid of it */
-      memcpy(startPtr, startPtr+1, size-1);
+      memmove(startPtr, startPtr+1, size-1);
       size--;
       /* and it wasn't a bare CR but a CRLF conversion instead */
       data->state.crlf_conversions++;
@@ -170,11 +74,11 @@ static size_t convert_lineends(struct SessionHandle *data,
 
   /* find 1st CR, if any */
   inPtr = outPtr = memchr(startPtr, '\r', size);
-  if (inPtr) {
+  if(inPtr) {
     /* at least one CR, now look for CRLF */
-    while (inPtr < (startPtr+size-1)) {
+    while(inPtr < (startPtr+size-1)) {
       /* note that it's size-1, so we'll never look past the last byte */
-      if (memcmp(inPtr, "\r\n", 2) == 0) {
+      if(memcmp(inPtr, "\r\n", 2) == 0) {
         /* CRLF found, bump past the CR and copy the NL */
         inPtr++;
         *outPtr = *inPtr;
@@ -182,7 +86,7 @@ static size_t convert_lineends(struct SessionHandle *data,
         data->state.crlf_conversions++;
       }
       else {
-        if (*inPtr == '\r') {
+        if(*inPtr == '\r') {
           /* lone CR, move LF instead */
           *outPtr = '\n';
         }
@@ -195,9 +99,9 @@ static size_t convert_lineends(struct SessionHandle *data,
       inPtr++;
     } /* end of while loop */
 
-    if (inPtr < startPtr+size) {
+    if(inPtr < startPtr+size) {
       /* handle last byte */
-      if (*inPtr == '\r') {
+      if(*inPtr == '\r') {
         /* deal with a CR at the end of the buffer */
         *outPtr = '\n'; /* copy a NL instead */
         /* note that a CRLF might be split across two blocks */
@@ -208,12 +112,11 @@ static size_t convert_lineends(struct SessionHandle *data,
         *outPtr = *inPtr;
       }
       outPtr++;
-      inPtr++;
     }
-    if (outPtr < startPtr+size) {
+    if(outPtr < startPtr+size)
       /* tidy up by null terminating the now shorter data */
       *outPtr = '\0';
-    }
+
     return(outPtr - startPtr);
   }
   return(size);
@@ -227,9 +130,9 @@ void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
   if(data && data->set.verbose) {
     va_list ap;
     size_t len;
-    char print_buffer[1024 + 1];
+    char print_buffer[2048 + 1];
     va_start(ap, fmt);
-    vsnprintf(print_buffer, 1024, fmt, ap);
+    vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
     va_end(ap);
     len = strlen(print_buffer);
     Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL);
@@ -271,7 +174,7 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
   struct SessionHandle *data = conn->data;
   ssize_t bytes_written;
   size_t write_len;
-  CURLcode res = CURLE_OK;
+  CURLcode result = CURLE_OK;
   char *s;
   char *sptr;
   va_list ap;
@@ -285,11 +188,11 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
   write_len = strlen(s);
   sptr = s;
 
-  while (1) {
+  for(;;) {
     /* Write the buffer to the socket */
-    res = Curl_write(conn, sockfd, sptr, write_len, &bytes_written);
+    result = Curl_write(conn, sockfd, sptr, write_len, &bytes_written);
 
-    if(CURLE_OK != res)
+    if(result)
       break;
 
     if(data->set.verbose)
@@ -307,17 +210,56 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
 
   free(s); /* free the output string */
 
-  return res;
+  return result;
 }
 
-static ssize_t Curl_plain_send(struct connectdata *conn,
-                               int num,
-                               void *mem,
-                               size_t len)
+/*
+ * Curl_write() is an internal write function that sends data to the
+ * server. Works with plain sockets, SCP, SSL or kerberos.
+ *
+ * If the write would block (CURLE_AGAIN), we return CURLE_OK and
+ * (*written == 0). Otherwise we return regular CURLcode value.
+ */
+CURLcode Curl_write(struct connectdata *conn,
+                    curl_socket_t sockfd,
+                    const void *mem,
+                    size_t len,
+                    ssize_t *written)
+{
+  ssize_t bytes_written;
+  CURLcode result = CURLE_OK;
+  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+  bytes_written = conn->send[num](conn, num, mem, len, &result);
+
+  *written = bytes_written;
+  if(bytes_written >= 0)
+    /* we completely ignore the curlcode value when subzero is not returned */
+    return CURLE_OK;
+
+  /* handle CURLE_AGAIN or a send failure */
+  switch(result) {
+  case CURLE_AGAIN:
+    *written = 0;
+    return CURLE_OK;
+
+  case CURLE_OK:
+    /* general send failure */
+    return CURLE_SEND_ERROR;
+
+  default:
+    /* we got a specific curlcode, forward it */
+    return result;
+  }
+}
+
+ssize_t Curl_send_plain(struct connectdata *conn, int num,
+                        const void *mem, size_t len, CURLcode *code)
 {
   curl_socket_t sockfd = conn->sock[num];
   ssize_t bytes_written = swrite(sockfd, mem, len);
 
+  *code = CURLE_OK;
   if(-1 == bytes_written) {
     int err = SOCKERRNO;
 
@@ -331,55 +273,217 @@ static ssize_t Curl_plain_send(struct connectdata *conn,
          treat both error codes the same here */
       (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
 #endif
-      )
+      ) {
       /* this is just a case of EWOULDBLOCK */
       bytes_written=0;
-    else
+      *code = CURLE_AGAIN;
+    }
+    else {
       failf(conn->data, "Send failure: %s",
             Curl_strerror(conn, err));
+      conn->data->state.os_errno = err;
+      *code = CURLE_SEND_ERROR;
+    }
   }
   return bytes_written;
 }
 
 /*
- * Curl_write() is an internal write function that sends data to the
- * server. Works with plain sockets, SCP, SSL or kerberos.
+ * Curl_write_plain() is an internal write function that sends data to the
+ * server using plain sockets only. Otherwise meant to have the exact same
+ * proto as Curl_write()
  */
-CURLcode Curl_write(struct connectdata *conn,
-                    curl_socket_t sockfd,
-                    void *mem,
-                    size_t len,
-                    ssize_t *written)
+CURLcode Curl_write_plain(struct connectdata *conn,
+                          curl_socket_t sockfd,
+                          const void *mem,
+                          size_t len,
+                          ssize_t *written)
 {
   ssize_t bytes_written;
-  CURLcode retcode;
+  CURLcode result;
   int num = (sockfd == conn->sock[SECONDARYSOCKET]);
 
-  if (conn->ssl[num].use)
-    /* only TRUE if SSL enabled */
-    bytes_written = Curl_ssl_send(conn, num, mem, len);
-#ifdef USE_LIBSSH2
-  else if (conn->protocol & PROT_SCP)
-    bytes_written = Curl_scp_send(conn, num, mem, len);
-  else if (conn->protocol & PROT_SFTP)
-    bytes_written = Curl_sftp_send(conn, num, mem, len);
-#endif /* !USE_LIBSSH2 */
-  else if(conn->sec_complete)
-    /* only TRUE if krb4 enabled */
-    bytes_written = Curl_sec_send(conn, num, mem, len);
-  else
-    bytes_written = Curl_plain_send(conn, num, mem, len);
+  bytes_written = Curl_send_plain(conn, num, mem, len, &result);
 
   *written = bytes_written;
-  retcode = (-1 != bytes_written)?CURLE_OK:CURLE_SEND_ERROR;
 
-  return retcode;
+  return result;
+}
+
+ssize_t Curl_recv_plain(struct connectdata *conn, int num, char *buf,
+                        size_t len, CURLcode *code)
+{
+  curl_socket_t sockfd = conn->sock[num];
+  ssize_t nread = sread(sockfd, buf, len);
+
+  *code = CURLE_OK;
+  if(-1 == nread) {
+    int err = SOCKERRNO;
+
+    if(
+#ifdef WSAEWOULDBLOCK
+      /* This is how Windows does it */
+      (WSAEWOULDBLOCK == err)
+#else
+      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+         due to its inability to send off data without blocking. We therefor
+         treat both error codes the same here */
+      (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
+#endif
+      ) {
+      /* this is just a case of EWOULDBLOCK */
+      *code = CURLE_AGAIN;
+    }
+    else {
+      failf(conn->data, "Recv failure: %s",
+            Curl_strerror(conn, err));
+      conn->data->state.os_errno = err;
+      *code = CURLE_RECV_ERROR;
+    }
+  }
+  return nread;
+}
+
+static CURLcode pausewrite(struct SessionHandle *data,
+                           int type, /* what type of data */
+                           const char *ptr,
+                           size_t len)
+{
+  /* signalled to pause sending on this connection, but since we have data
+     we want to send we need to dup it to save a copy for when the sending
+     is again enabled */
+  struct SingleRequest *k = &data->req;
+  char *dupl = malloc(len);
+  if(!dupl)
+    return CURLE_OUT_OF_MEMORY;
+
+  memcpy(dupl, ptr, len);
+
+  /* store this information in the state struct for later use */
+  data->state.tempwrite = dupl;
+  data->state.tempwritesize = len;
+  data->state.tempwritetype = type;
+
+  /* mark the connection as RECV paused */
+  k->keepon |= KEEP_RECV_PAUSE;
+
+  DEBUGF(infof(data, "Pausing with %zu bytes in buffer for type %02x\n",
+               len, type));
+
+  return CURLE_OK;
+}
+
+
+/* Curl_client_chop_write() writes chunks of data not larger than
+ * CURL_MAX_WRITE_SIZE via client write callback(s) and
+ * takes care of pause requests from the callbacks.
+ */
+CURLcode Curl_client_chop_write(struct connectdata *conn,
+                                int type,
+                                char * ptr,
+                                size_t len)
+{
+  struct SessionHandle *data = conn->data;
+  curl_write_callback writeheader = NULL;
+  curl_write_callback writebody = NULL;
+
+  if(!len)
+    return CURLE_OK;
+
+  /* If reading is actually paused, we're forced to append this chunk of data
+     to the already held data, but only if it is the same type as otherwise it
+     can't work and it'll return error instead. */
+  if(data->req.keepon & KEEP_RECV_PAUSE) {
+    size_t newlen;
+    char *newptr;
+    if(type != data->state.tempwritetype)
+      /* major internal confusion */
+      return CURLE_RECV_ERROR;
+
+    DEBUGASSERT(data->state.tempwrite);
+
+    /* figure out the new size of the data to save */
+    newlen = len + data->state.tempwritesize;
+    /* allocate the new memory area */
+    newptr = realloc(data->state.tempwrite, newlen);
+    if(!newptr)
+      return CURLE_OUT_OF_MEMORY;
+    /* copy the new data to the end of the new area */
+    memcpy(newptr + data->state.tempwritesize, ptr, len);
+    /* update the pointer and the size */
+    data->state.tempwrite = newptr;
+    data->state.tempwritesize = newlen;
+    return CURLE_OK;
+  }
+
+  /* Determine the callback(s) to use. */
+  if(type & CLIENTWRITE_BODY)
+    writebody = data->set.fwrite_func;
+  if((type & CLIENTWRITE_HEADER) &&
+     (data->set.fwrite_header || data->set.writeheader)) {
+    /*
+     * Write headers to the same callback or to the especially setup
+     * header callback function (added after version 7.7.1).
+     */
+    writeheader =
+      data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
+  }
+
+  /* Chop data, write chunks. */
+  while(len) {
+    size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
+
+    if(writebody) {
+      size_t wrote = writebody(ptr, 1, chunklen, data->set.out);
+
+      if(CURL_WRITEFUNC_PAUSE == wrote) {
+        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, "Write callback asked for PAUSE when not supported!");
+          return CURLE_WRITE_ERROR;
+        }
+        else
+          return pausewrite(data, type, ptr, len);
+      }
+      else if(wrote != chunklen) {
+        failf(data, "Failed writing body (%zu != %zu)", wrote, chunklen);
+        return CURLE_WRITE_ERROR;
+      }
+    }
+
+    if(writeheader) {
+      size_t wrote = writeheader(ptr, 1, chunklen, data->set.writeheader);
+
+      if(CURL_WRITEFUNC_PAUSE == wrote)
+        /* here we pass in the HEADER bit only since if this was body as well
+           then it was passed already and clearly that didn't trigger the
+           pause, so this is saved for later with the HEADER bit only */
+        return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
+
+      if(wrote != chunklen) {
+        failf (data, "Failed writing header");
+        return CURLE_WRITE_ERROR;
+      }
+    }
+
+    ptr += chunklen;
+    len -= chunklen;
+  }
+
+  return CURLE_OK;
 }
 
-/* client_write() sends data to the write callback(s)
+
+/* Curl_client_write() sends data to the write callback(s)
 
    The bit pattern defines to what "streams" to write to. Body and/or header.
    The defines are in sendf.h of course.
+
+   If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
+   local character encoding.  This is a problem and should be changed in
+   the future to leave the original data alone.
  */
 CURLcode Curl_client_write(struct connectdata *conn,
                            int type,
@@ -387,91 +491,70 @@ CURLcode Curl_client_write(struct connectdata *conn,
                            size_t len)
 {
   struct SessionHandle *data = conn->data;
-  size_t wrote;
-
-  if (data->state.cancelled) {
-      /* We just suck everything into a black hole */
-      return CURLE_OK;
-  }
 
   if(0 == len)
     len = strlen(ptr);
 
-  if(type & CLIENTWRITE_BODY) {
-    if((conn->protocol&PROT_FTP) && conn->proto.ftpc.transfertype == 'A') {
-#ifdef CURL_DOES_CONVERSIONS
-      /* convert from the network encoding */
-      size_t rc;
-      rc = Curl_convert_from_network(data, ptr, len);
-      /* Curl_convert_from_network calls failf if unsuccessful */
-      if(rc != CURLE_OK)
-        return rc;
-#endif /* CURL_DOES_CONVERSIONS */
+  /* FTP data may need conversion. */
+  if((type & CLIENTWRITE_BODY) &&
+    (conn->handler->protocol & PROTO_FAMILY_FTP) &&
+    conn->proto.ftpc.transfertype == 'A') {
+    /* convert from the network encoding */
+    CURLcode result = Curl_convert_from_network(data, ptr, len);
+    /* Curl_convert_from_network calls failf if unsuccessful */
+    if(result)
+      return result;
 
 #ifdef CURL_DO_LINEEND_CONV
-      /* convert end-of-line markers */
-      len = convert_lineends(data, ptr, len);
+    /* convert end-of-line markers */
+    len = convert_lineends(data, ptr, len);
 #endif /* CURL_DO_LINEEND_CONV */
     }
-    /* If the previous block of data ended with CR and this block of data is
-       just a NL, then the length might be zero */
-    if (len) {
-      wrote = data->set.fwrite(ptr, 1, len, data->set.out);
-    }
-    else {
-      wrote = len;
-    }
 
-    if(wrote != len) {
-      failf (data, "Failed writing body");
-      return CURLE_WRITE_ERROR;
-    }
-  }
-
-  if((type & CLIENTWRITE_HEADER) &&
-     (data->set.fwrite_header || data->set.writeheader) ) {
-    /*
-     * Write headers to the same callback or to the especially setup
-     * header callback function (added after version 7.7.1).
-     */
-    curl_write_callback writeit=
-      data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite;
+  return Curl_client_chop_write(conn, type, ptr, len);
+}
 
-    /* Note: The header is in the host encoding
-       regardless of the ftp transfer mode (ASCII/Image) */
+CURLcode Curl_read_plain(curl_socket_t sockfd,
+                         char *buf,
+                         size_t bytesfromsocket,
+                         ssize_t *n)
+{
+  ssize_t nread = sread(sockfd, buf, bytesfromsocket);
 
-    wrote = writeit(ptr, 1, len, data->set.writeheader);
-    if(wrote != len) {
-      failf (data, "Failed writing header");
-      return CURLE_WRITE_ERROR;
-    }
+  if(-1 == nread) {
+    int err = SOCKERRNO;
+#ifdef USE_WINSOCK
+    if(WSAEWOULDBLOCK == err)
+#else
+    if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err))
+#endif
+      return CURLE_AGAIN;
+    else
+      return CURLE_RECV_ERROR;
   }
 
+  /* we only return number of bytes read when we return OK */
+  *n = nread;
   return CURLE_OK;
 }
 
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
 /*
  * Internal read-from-socket function. This is meant to deal with plain
  * sockets, SSL sockets and kerberos sockets.
  *
- * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
- * a regular CURLcode value.
+ * Returns a regular CURLcode value.
  */
-int Curl_read(struct connectdata *conn, /* connection data */
-              curl_socket_t sockfd,     /* read from this socket */
-              char *buf,                /* store read data here */
-              size_t sizerequested,     /* max amount to read */
-              ssize_t *n)               /* amount bytes read */
+CURLcode Curl_read(struct connectdata *conn, /* connection data */
+                   curl_socket_t sockfd,     /* read from this socket */
+                   char *buf,                /* store read data here */
+                   size_t sizerequested,     /* max amount to read */
+                   ssize_t *n)               /* amount bytes read */
 {
+  CURLcode result = CURLE_RECV_ERROR;
   ssize_t nread = 0;
   size_t bytesfromsocket = 0;
   char *buffertofill = NULL;
-  bool pipelining = (bool)(conn->data->multi &&
-                     Curl_multi_canPipeline(conn->data->multi));
+  bool pipelining = Curl_multi_pipeline_enabled(conn->data->multi);
 
   /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
      If it is the second socket, we set num to 1. Otherwise to 0. This lets
@@ -482,10 +565,11 @@ int Curl_read(struct connectdata *conn, /* connection data */
 
   /* If session can pipeline, check connection buffer  */
   if(pipelining) {
-    size_t bytestocopy = MIN(conn->buf_len - conn->read_pos, sizerequested);
+    size_t bytestocopy = CURLMIN(conn->buf_len - conn->read_pos,
+                                 sizerequested);
 
     /* Copy from our master buffer first if we have some unread data there*/
-    if (bytestocopy > 0) {
+    if(bytestocopy > 0) {
       memcpy(buf, conn->master_buffer + conn->read_pos, bytestocopy);
       conn->read_pos += bytestocopy;
       conn->bits.stream_was_rewound = FALSE;
@@ -495,65 +579,27 @@ int Curl_read(struct connectdata *conn, /* connection data */
     }
     /* If we come here, it means that there is no data to read from the buffer,
      * so we read from the socket */
-    bytesfromsocket = MIN(sizerequested, BUFSIZE * sizeof (char));
+    bytesfromsocket = CURLMIN(sizerequested, BUFSIZE * sizeof (char));
     buffertofill = conn->master_buffer;
   }
   else {
-    bytesfromsocket = MIN((long)sizerequested, conn->data->set.buffer_size ?
-                          conn->data->set.buffer_size : BUFSIZE);
+    bytesfromsocket = CURLMIN((long)sizerequested,
+                              conn->data->set.buffer_size ?
+                              conn->data->set.buffer_size : BUFSIZE);
     buffertofill = buf;
   }
 
-  if(conn->ssl[num].use) {
-    nread = Curl_ssl_recv(conn, num, buffertofill, bytesfromsocket);
-
-    if(nread == -1) {
-      return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */
-    }
-  }
-#ifdef USE_LIBSSH2
-  else if (conn->protocol & (PROT_SCP|PROT_SFTP)) {
-    if(conn->protocol & PROT_SCP)
-      nread = Curl_scp_recv(conn, num, buffertofill, bytesfromsocket);
-    else if (conn->protocol & PROT_SFTP)
-      nread = Curl_sftp_recv(conn, num, buffertofill, bytesfromsocket);
-#ifdef LIBSSH2CHANNEL_EAGAIN
-    if((nread == LIBSSH2CHANNEL_EAGAIN) || (nread == 0))
-      /* EWOULDBLOCK */
-      return -1;
-#endif
-    if(nread < 0)
-      /* since it is negative and not EGAIN, it was a protocol-layer error */
-      return CURLE_RECV_ERROR;
-  }
-#endif /* !USE_LIBSSH2 */
-  else {
-    if(conn->sec_complete)
-      nread = Curl_sec_read(conn, sockfd, buffertofill,
-                            bytesfromsocket);
-    else
-      nread = sread(sockfd, buffertofill, bytesfromsocket);
+  nread = conn->recv[num](conn, num, buffertofill, bytesfromsocket, &result);
+  if(nread < 0)
+    return result;
 
-    if(-1 == nread) {
-      int err = SOCKERRNO;
-#ifdef USE_WINSOCK
-      if(WSAEWOULDBLOCK == err)
-#else
-      if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err))
-#endif
-        return -1;
-    }
+  if(pipelining) {
+    memcpy(buf, conn->master_buffer, nread);
+    conn->buf_len = nread;
+    conn->read_pos = nread;
   }
 
-  if (nread >= 0) {
-    if(pipelining) {
-      memcpy(buf, conn->master_buffer, nread);
-      conn->buf_len = nread;
-      conn->read_pos = nread;
-    }
-
-    *n += nread;
-  }
+  *n += nread;
 
   return CURLE_OK;
 }
@@ -562,7 +608,7 @@ int Curl_read(struct connectdata *conn, /* connection data */
 static int showit(struct SessionHandle *data, curl_infotype type,
                   char *ptr, size_t size)
 {
-  static const char * const s_infotype[CURLINFO_END] = {
+  static const char s_infotype[CURLINFO_END][3] = {
     "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
 
 #ifdef CURL_DOES_CONVERSIONS
@@ -573,7 +619,7 @@ static int showit(struct SessionHandle *data, curl_infotype type,
   case CURLINFO_HEADER_OUT:
     /* assume output headers are ASCII */
     /* copy the data into my buffer so the original is unchanged */
-    if (size > BUFSIZE) {
+    if(size > BUFSIZE) {
       size = BUFSIZE; /* truncate if necessary */
       buf[BUFSIZE] = '\0';
     }
@@ -587,7 +633,7 @@ static int showit(struct SessionHandle *data, curl_infotype type,
       size_t i;
       for(i = 0; i < size-4; i++) {
         if(memcmp(&buf[i], "\x0d\x0a\x0d\x0a", 4) == 0) {
-          /* convert everthing through this CRLFCRLF but no further */
+          /* convert everything through this CRLFCRLF but no further */
           conv_size = i + 4;
           break;
         }
@@ -640,11 +686,13 @@ int Curl_debug(struct SessionHandle *data, curl_infotype type,
     switch (type) {
     case CURLINFO_HEADER_IN:
       w = "Header";
+      /* FALLTHROUGH */
     case CURLINFO_DATA_IN:
       t = "from";
       break;
     case CURLINFO_HEADER_OUT:
       w = "Header";
+      /* FALLTHROUGH */
     case CURLINFO_DATA_OUT:
       t = "to";
       break;