gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] branch master updated (bb2e08b6 -> 22752968)


From: gnunet
Subject: [libmicrohttpd] branch master updated (bb2e08b6 -> 22752968)
Date: Wed, 24 Feb 2021 17:48:45 +0100

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from bb2e08b6 bump MHD_VERSION
     new 7bbbcd63 SIGPIPE macros minor refactoring
     new d50159bf Implemented new function MHD_create_response_from_iovec()
     new a22b8cdf mhd_send: re-use MHD_VECT_SEND macro
     new f85f8592 mhd_send: use MSG_MORE for iovec
     new b8aa7636 Bump MHD_VERSION
     new 22752968 ChangeLog updated

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 ChangeLog                                          |  16 ++
 doc/libmicrohttpd.texi                             |  27 ++
 src/include/microhttpd.h                           |  40 ++-
 src/microhttpd/connection.c                        |  45 +++-
 src/microhttpd/daemon.c                            |  14 +-
 src/microhttpd/internal.h                          |  69 +++++
 src/microhttpd/mhd_send.c                          | 279 +++++++++++++++++++--
 src/microhttpd/mhd_send.h                          |  22 ++
 src/microhttpd/mhd_sockets.h                       |  13 +-
 src/microhttpd/response.c                          | 153 +++++++++++
 src/testcurl/.gitignore                            |   2 +
 src/testcurl/Makefile.am                           |  16 +-
 src/testcurl/https/.gitignore                      |   1 +
 src/testcurl/https/Makefile.am                     |  16 +-
 .../{test_https_get.c => test_https_get_iovec.c}   | 171 ++++++++++++-
 .../{test_get_sendfile.c => test_get_iovec.c}      | 272 ++++++++++++++------
 16 files changed, 1032 insertions(+), 124 deletions(-)
 copy src/testcurl/https/{test_https_get.c => test_https_get_iovec.c} (62%)
 copy src/testcurl/{test_get_sendfile.c => test_get_iovec.c} (75%)

diff --git a/ChangeLog b/ChangeLog
index bdbdc15c..60e474b6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,10 +1,26 @@
+Wed 24 Feb 2021 19:23:00 MSK
+    SIGPIPE-related macro minor refactoring for readability.
+    Added new response iov function (and related framework), based on the patch
+    provided by Lawrence Sebald and Damon N. Earp from NASA. -EG
+
 Thu 04 Feb 2021 06:41:34 PM CET
     Fix PostProcessor to always properly stop iteration when application 
callback
     tells it to do so. -CG
 
+Sun 24 Jan 2021 21:30:00 MSK
+    Added '--enable-heavy-tests' configure parameter.
+    Minor configure.ac and Makefiles fixes. -EG
+
+Tue 19 Jan 2021 17:59:00 MSK
+    Fixed compatibility with autoconf. 2.70
+    Updated M4 macros. -EG
+
 Wed 06 Jan 2021 08:39:58 PM CET
     Return timeout of zero also for connections awaiting cleanup. -CG
 
+Tue 29 Dec 2020 15:39:00 MSK
+    Improved speed of TLS handshake by pre-enabling TCP_NODELAY. -EG
+
 Mon 28 Dec 2020 21:36:00 MSK
     Releasing libmicrohttpd 0.9.72. -EG
 
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi
index 461ba3ac..2a4f09d2 100644
--- a/doc/libmicrohttpd.texi
+++ b/doc/libmicrohttpd.texi
@@ -1270,6 +1270,11 @@ Handle for a response.
 @end deftp
 
 
+@deftp {C Struct} MHD_IoVec
+An element of an array of memory buffers.
+@end deftp
+
+
 @deftp {C Struct} MHD_PostProcessor
 @cindex POST method
 Handle for @code{POST} processing.
@@ -2170,6 +2175,28 @@ MHD_destroy_response(response);
 @end example
 
 
+@deftypefun {struct MHD_Response *} MHD_create_response_from_iovec (const 
struct MHD_IoVec *iov, int iovcnt, MHD_ContentReaderFreeCallback crfc, void 
*cls)
+Create a response object from an array of memory buffers.
+The response object can be extended with header information and then be used
+any number of times.
+@table @var
+@item iov
+the array for response data buffers, an internal copy of this will be made;
+
+@item iovcnt
+the number of elements in @var{iov};
+
+@item crfc
+the callback to call to free resources associated with @var{iov};
+
+@item cls
+the argument to @var{crfc};
+@end table
+
+Return @code{NULL} on error (i.e. invalid arguments, out of memory).
+@end deftypefun
+
+
 
 @c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index d4f6c139..b416e62a 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -135,7 +135,7 @@ typedef intptr_t ssize_t;
  * they are parsed as decimal numbers.
  * Example: 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00097203
+#define MHD_VERSION 0x00097204
 
 /**
  * Operational results from MHD calls.
@@ -1973,6 +1973,23 @@ union MHD_ConnectionInfo
 };
 
 
+/**
+ * I/O vector type. Provided for use with MHD_create_response_from_iovec.
+ */
+struct MHD_IoVec
+{
+  /**
+   * The pointer to the memory region for I/O.
+   */
+  void *iov_base;
+
+  /**
+   * The size in bytes of the memory region for I/O.
+   */
+  size_t iov_len;
+};
+
+
 /**
  * Values of this enum are used to specify what
  * information about a connection is desired.
@@ -3229,6 +3246,27 @@ MHD_create_response_from_fd_at_offset64 (uint64_t size,
                                          uint64_t offset);
 
 
+/**
+ * Create a response object from an array of memory buffers.
+ * The response object can be extended with header information and then be used
+ * any number of times.
+ *
+ * @param iov the array for response data buffers, an internal copy of this
+ *        will be made
+ * @param iovcnt the number of elements in @a iov
+ * @param free_cb the callback to clean up any data associated with @a iov when
+ *        the response is destroyed.
+ * @param cls the argument passed to @a free_cb
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_iovec (const struct MHD_IoVec *iov,
+                                int iovcnt,
+                                MHD_ContentReaderFreeCallback free_cb,
+                                void *cls);
+
+
 /**
  * Enumeration for actions MHD should perform on the underlying socket
  * of the upgrade.  This API is not finalized, and in particular
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index f7d25962..7f2dcd75 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -794,11 +794,33 @@ try_ready_normal_body (struct MHD_Connection *connection)
   struct MHD_Response *response;
 
   response = connection->response;
-  if (NULL == response->crc)
-    return MHD_YES;
   if ( (0 == response->total_size) ||
        (connection->response_write_position == response->total_size) )
     return MHD_YES; /* 0-byte response is always ready */
+  if (NULL != response->data_iov)
+  {
+    size_t copy_size;
+    if (NULL != connection->resp_iov.iov)
+      return MHD_YES;
+
+    copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
+    connection->resp_iov.iov = MHD_pool_allocate (connection->pool, copy_size,
+                                                  true);
+    if (NULL == connection->resp_iov.iov)
+    {
+      MHD_mutex_unlock_chk_ (&response->mutex);
+      /* not enough memory */
+      CONNECTION_CLOSE_ERROR (connection,
+                              _ ("Closing connection (out of memory).\n"));
+      return MHD_NO;
+    }
+    memcpy (connection->resp_iov.iov, response->data_iov, copy_size);
+    connection->resp_iov.cnt = response->data_iovcnt;
+    connection->resp_iov.sent = 0;
+    return MHD_YES;
+  }
+  if (NULL == response->crc)
+    return MHD_YES;
   if ( (response->data_start <=
         connection->response_write_position) &&
        (response->data_size + response->data_start >
@@ -2935,6 +2957,7 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
                     connection->response_write_position) );
 
       if ( (NULL == resp->crc) &&
+           (NULL == resp->data_iov) &&
            (0 == connection->response_write_position) )
       {
         mhd_assert (resp->total_size >= resp->data_size);
@@ -3017,12 +3040,16 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
 #if defined(_MHD_HAVE_SENDFILE)
       if (MHD_resp_sender_sendfile == connection->resp_sender)
       {
+        mhd_assert (NULL == response->data_iov);
         ret = MHD_send_sendfile_ (connection);
       }
+      else /* combined with the next 'if' */
+#endif /* _MHD_HAVE_SENDFILE */
+      if (NULL != response->data_iov)
+      {
+        ret = MHD_send_iovec_ (connection, &connection->resp_iov, true);
+      }
       else
-#else  /* ! _MHD_HAVE_SENDFILE */
-      if (1)
-#endif /* ! _MHD_HAVE_SENDFILE */
       {
         data_write_offset = connection->response_write_position
                             - response->data_start;
@@ -3674,6 +3701,8 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
       connection->write_buffer_size = 0;
       connection->write_buffer_send_offset = 0;
       connection->write_buffer_append_offset = 0;
+      /* iov (if any) was deallocated by MHD_pool_reset */
+      memset (&connection->resp_iov, 0, sizeof(connection->resp_iov));
       continue;
     case MHD_CONNECTION_CLOSED:
       cleanup_connection (connection);
@@ -3977,9 +4006,11 @@ MHD_queue_response (struct MHD_Connection *connection,
   if ( (response->fd == -1) ||
        (response->is_pipe) ||
        (0 != (connection->daemon->options & MHD_USE_TLS))
-#if ! defined(MHD_WINSOCK_SOCKETS) && defined(HAVE_SEND_SIGPIPE_SUPPRESS)
+#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
+       defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
        || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
-#endif /* ! MHD_WINSOCK_SOCKETS && ! HAVE_SEND_SIGPIPE_SUPPRESS */
+#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
+          MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
        )
     connection->resp_sender = MHD_resp_sender_std;
   else
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index cae031bd..bcf1a014 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -2243,7 +2243,9 @@ static void
 MHD_cleanup_connections (struct MHD_Daemon *daemon);
 
 #if defined(HTTPS_SUPPORT)
-#if ! defined(MHD_WINSOCK_SOCKETS) && ! defined(MHD_socket_nosignal_) && \
+#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
+  defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
+  ! defined(MHD_socket_nosignal_) && \
   (GNUTLS_VERSION_NUMBER + 0 < 0x030402) && defined(MSG_NOSIGNAL)
 /**
  * Older version of GnuTLS do not support suppressing of SIGPIPE signal.
@@ -2251,8 +2253,10 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon);
  * and if possible.
  */
 #define MHD_TLSLIB_NEED_PUSH_FUNC 1
-#endif \
-  /* !MHD_WINSOCK_SOCKETS && !MHD_socket_nosignal_ && (GNUTLS_VERSION_NUMBER+0 
< 0x030402) */
+#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
+          MHD_SEND_SPIPE_SUPPRESS_POSSIBLE &&
+          ! MHD_socket_nosignal_ && (GNUTLS_VERSION_NUMBER+0 < 0x030402) &&
+          MSG_NOSIGNAL */
 
 #ifdef MHD_TLSLIB_NEED_PUSH_FUNC
 /**
@@ -7740,8 +7744,8 @@ MHD_is_feature_supported (enum MHD_FEATURE feature)
     return MHD_NO;
 #endif
   case MHD_FEATURE_AUTOSUPPRESS_SIGPIPE:
-#if defined(MHD_WINSOCK_SOCKETS) || defined(MHD_socket_nosignal_) || \
-    defined (MSG_NOSIGNAL)
+#if defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
+    defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED)
     return MHD_YES;
 #else
     return MHD_NO;
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index f3c4cb1e..ba71d1eb 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -343,6 +343,57 @@ struct MHD_HTTP_Header
 };
 
 
+#if defined(MHD_WINSOCK_SOCKETS)
+/**
+ * Internally used I/O vector type for use with winsock.
+ * Binary matches system "WSABUF".
+ */
+typedef struct _MHD_W32_iovec
+{
+  unsigned long iov_len;
+  char *iov_base;
+} MHD_iovec_;
+#define MHD_IOV_ELMN_MAX_SIZE    ULONG_MAX
+#elif defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+/**
+ * Internally used I/O vector type for use when writev or sendmsg
+ * is available. Matches system "struct iovec".
+ */
+typedef struct iovec MHD_iovec_;
+#define MHD_IOV_ELMN_MAX_SIZE    SIZE_MAX
+#else
+/**
+ * Internally used I/O vector type for use when writev or sendmsg
+ * is not available.
+ */
+typedef struct MHD_IoVec MHD_iovec_;
+#define MHD_IOV_ELMN_MAX_SIZE    SIZE_MAX
+#endif
+
+
+struct MHD_iovec_track_
+{
+  /**
+   * The copy of array of iovec elements.
+   * The copy of elements are updated during sending.
+   * The number of elements is not changed during lifetime.
+   */
+  MHD_iovec_ *iov;
+
+  /**
+   * The number of elements in @iov.
+   * This value is not changed during lifetime.
+   */
+  size_t cnt;
+
+  /**
+   * The number of sent elements.
+   * At the same time, it is the index of the next (or current) element
+   * to send.
+   */
+  size_t sent;
+};
+
 /**
  * Representation of a response.
  */
@@ -450,6 +501,15 @@ struct MHD_Response
    */
   bool is_pipe;
 
+  /**
+   * I/O vector used with MHD_create_response_from_iovec.
+   */
+  MHD_iovec_ *data_iov;
+
+  /**
+   * Number of elements in data_iov.
+   */
+  size_t data_iovcnt;
 };
 
 
@@ -873,6 +933,15 @@ struct MHD_Connection
    */
   uint64_t response_write_position;
 
+  /**
+   * The copy of iov response.
+   * Valid if iovec response is used.
+   * Updated during send.
+   * Members are allocated in the pool.
+   */
+  struct MHD_iovec_track_ resp_iov;
+
+
 #if defined(_MHD_HAVE_SENDFILE)
   enum MHD_resp_sender_
   {
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c
index ac11b693..f3028293 100644
--- a/src/microhttpd/mhd_send.c
+++ b/src/microhttpd/mhd_send.c
@@ -51,6 +51,15 @@
 
 #include "mhd_limits.h"
 
+#ifdef MHD_VECT_SEND
+#if (! defined (HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL)) && \
+  defined (MHD_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
+  defined (MHD_SEND_SPIPE_SUPPRESS_NEEDED)
+#define _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED 1
+#endif /* (!HAVE_SENDMSG || !MSG_NOSIGNAL) &&
+          MHD_SEND_SPIPE_SUPPRESS_POSSIBLE && MHD_SEND_SPIPE_SUPPRESS_NEEDED */
+#endif /* MHD_VECT_SEND */
+
 /**
  * sendfile() chuck size
  */
@@ -773,10 +782,6 @@ MHD_send_data_ (struct MHD_Connection *connection,
 }
 
 
-#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || defined(_WIN32)
-#define _MHD_USE_SEND_VEC          1
-#endif /* HAVE_SENDMSG || HAVE_WRITEV || _WIN32*/
-
 ssize_t
 MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
                         const char *header,
@@ -795,7 +800,7 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
 #else  /* ! _WIN32 */
 #define _MHD_SEND_VEC_MAX   UINT32_MAX
 #endif /* ! _WIN32 */
-#ifdef _MHD_USE_SEND_VEC
+#ifdef MHD_VECT_SEND
 #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
   struct iovec vector[2];
 #ifdef HAVE_SENDMSG
@@ -812,14 +817,15 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
 #ifdef HTTPS_SUPPORT
   no_vec = no_vec || (connection->daemon->options & MHD_USE_TLS);
 #endif /* HTTPS_SUPPORT */
-#if ! defined(MHD_WINSOCK_SOCKETS) && \
-  (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL)) && \
-  defined(HAVE_SEND_SIGPIPE_SUPPRESS)
+#if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL) ) && \
+  defined(MHD_SEND_SPIPE_SEND_SUPPRESS_POSSIBLE) && \
+  defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED)
   no_vec = no_vec || (! connection->daemon->sigpipe_blocked &&
                       ! connection->sk_spipe_suppress);
-#endif /* !MHD_WINSOCK_SOCKETS && (!HAVE_SENDMSG || ! MSG_NOSIGNAL)
-          && !HAVE_SEND_SIGPIPE_SUPPRESS */
-#endif /* _MHD_USE_SEND_VEC */
+#endif /* (!HAVE_SENDMSG || ! MSG_NOSIGNAL) &&
+          MHD_SEND_SPIPE_SEND_SUPPRESS_POSSIBLE &&
+          MHD_SEND_SPIPE_SUPPRESS_NEEDED */
+#endif /* MHD_VECT_SEND */
 
   mhd_assert ( (NULL != body) || (0 == body_size) );
 
@@ -856,7 +862,7 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
     push_hdr = true; /* The header alone is equal to the whole response. */
 
   if (
-#ifdef _MHD_USE_SEND_VEC
+#ifdef MHD_VECT_SEND
     (no_vec) ||
     (0 == body_size) ||
     ((size_t) SSIZE_MAX <= header_size) ||
@@ -864,9 +870,9 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
 #ifdef _WIN32
     || ((size_t) UINT_MAX < header_size)
 #endif /* _WIN32 */
-#else  /* ! _MHD_USE_SEND_VEC */
+#else  /* ! MHD_VECT_SEND */
     true
-#endif /* ! _MHD_USE_SEND_VEC */
+#endif /* ! MHD_VECT_SEND */
     )
   {
     ret = MHD_send_data_ (connection,
@@ -905,7 +911,7 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
     }
     return ret;
   }
-#ifdef _MHD_USE_SEND_VEC
+#ifdef MHD_VECT_SEND
 
   if ( ((size_t) SSIZE_MAX <= body_size) ||
        ((size_t) SSIZE_MAX < (header_size + body_size)) )
@@ -1030,10 +1036,10 @@ MHD_send_hdr_and_body_ (struct MHD_Connection 
*connection,
   }
 
   return ret;
-#else  /* ! _MHD_USE_SEND_VEC */
+#else  /* ! MHD_VECT_SEND */
   mhd_assert (false);
   return MHD_ERR_CONNRESET_; /* Unreachable. Mute warnings. */
-#endif /* ! _MHD_USE_SEND_VEC */
+#endif /* ! MHD_VECT_SEND */
 }
 
 
@@ -1242,3 +1248,242 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
 
 
 #endif /* _MHD_HAVE_SENDFILE */
+
+#if defined(MHD_VECT_SEND)
+
+
+/**
+ * Function sends iov data by system sendmsg or writev function.
+ *
+ * Connection must be in non-TLS (non-HTTPS) mode.
+ *
+ * @param connection the MHD connection structure
+ * @param r_iov the pointer to iov data structure with tracking
+ * @param push_data set to true to force push the data to the network from
+ *                  system buffers (usually set for the last piece of data),
+ *                  set to false to prefer holding incomplete network packets
+ *                  (more data will be send for the same reply).
+ * @return actual number of bytes sent
+ */
+static ssize_t
+send_iov_nontls (struct MHD_Connection *connection,
+                 struct MHD_iovec_track_ *const r_iov,
+                 bool push_data)
+{
+  ssize_t res;
+  ssize_t total_sent;
+  size_t items_to_send;
+#ifdef HAVE_SENDMSG
+  struct msghdr msg;
+#elif defined(MHD_WINSOCK_SOCKETS)
+  DWORD bytes_sent;
+  DWORD cnt_w;
+#endif /* MHD_WINSOCK_SOCKETS */
+
+  mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS));
+
+  if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
+       (MHD_CONNECTION_CLOSED == connection->state) )
+  {
+    return MHD_ERR_NOTCONN_;
+  }
+
+  pre_send_setopt (connection, true, push_data);
+
+  items_to_send = r_iov->cnt - r_iov->sent;
+#ifdef HAVE_SENDMSG
+  memset (&msg, 0, sizeof(struct msghdr));
+  msg.msg_iov = r_iov->iov + r_iov->sent;
+  msg.msg_iovlen = items_to_send;
+
+#ifdef MHD_USE_MSG_MORE
+  res = sendmsg (connection->socket_fd, &msg,
+                 MSG_NOSIGNAL_OR_ZERO | (push_data ? 0 : MSG_MORE));
+#else  /* ! MHD_USE_MSG_MORE */
+  res = sendmsg (connection->socket_fd, &msg, MSG_NOSIGNAL_OR_ZERO);
+#endif /* ! MHD_USE_MSG_MORE */
+#elif defined(HAVE_WRITEV)
+  res = writev (connection->socket_fd, r_iov->iov + r_iov->sent,
+                items_to_send);
+#elif defined(MHD_WINSOCK_SOCKETS)
+#ifdef _WIN64
+  cnt_w = (items_to_send > UINT32_MAX) ? UINT32_MAX : (DWORD) items_to_send;
+#else  /* ! _WIN64 */
+  cnt_w = (DWORD) items_to_send;
+#endif /* ! _WIN64 */
+  if (0 == WSASend (connection->socket_fd,
+                    (LPWSABUF) (r_iov->iov + r_iov->sent),
+                    cnt_w,
+                    &bytes_sent, 0, NULL, NULL))
+    res = (ssize_t) bytes_sent;
+  else
+    res = -1;
+#else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_WINSOCK_SOCKETS */
+#error No vector-send function available
+#endif
+
+  if (0 > res)
+  {
+    const int err = MHD_socket_get_error_ ();
+
+    if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
+    {
+#ifdef EPOLL_SUPPORT
+      /* EAGAIN --- no longer write-ready */
+      connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+#endif /* EPOLL_SUPPORT */
+      return MHD_ERR_AGAIN_;
+    }
+    if (MHD_SCKT_ERR_IS_EINTR_ (err))
+      return MHD_ERR_AGAIN_;
+    if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ECONNRESET_))
+      return MHD_ERR_CONNRESET_;
+    /* Treat any other error as hard error. */
+    return MHD_ERR_NOTCONN_;
+  }
+
+  /* Some data has been sent */
+  total_sent = res;
+  /* Adjust the internal tracking information for the iovec to
+   * take this last send into account. */
+  while ((0 != res) && (r_iov->iov[r_iov->sent].iov_len <= (size_t) res))
+  {
+    res -= r_iov->iov[r_iov->sent].iov_len;
+    r_iov->sent++; /* The iov element has been completely sent */
+    mhd_assert ((r_iov->cnt > r_iov->sent) || (0 == res));
+  }
+
+  if (r_iov->cnt == r_iov->sent)
+    post_send_setopt (connection, true, push_data);
+  else
+  {
+#ifdef EPOLL_SUPPORT
+    connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+#endif /* EPOLL_SUPPORT */
+    if (0 != res)
+    {
+      mhd_assert (r_iov->cnt > r_iov->sent);
+      /* The last iov element has been partially sent */
+      r_iov->iov[r_iov->sent].iov_base =
+        (void*) ((uint8_t*) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
+      r_iov->iov[r_iov->sent].iov_len -= res;
+    }
+  }
+
+  return total_sent;
+}
+
+
+#endif /* MHD_VECT_SEND */
+
+#if ! defined(MHD_VECT_SEND) || defined(HTTPS_SUPPORT) || \
+  defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
+
+
+/**
+ * Function sends iov data by sending buffers one-by-one by standard
+ * data send function.
+ *
+ * Connection could be in HTTPS or non-HTTPS mode.
+ *
+ * @param connection the MHD connection structure
+ * @param r_iov the pointer to iov data structure with tracking
+ * @param push_data set to true to force push the data to the network from
+ *                  system buffers (usually set for the last piece of data),
+ *                  set to false to prefer holding incomplete network packets
+ *                  (more data will be send for the same reply).
+ * @return actual number of bytes sent
+ */
+static ssize_t
+send_iov_emu (struct MHD_Connection *connection,
+              struct MHD_iovec_track_ *const r_iov,
+              bool push_data)
+{
+  const bool non_blk = connection->sk_nonblck;
+  size_t total_sent;
+  ssize_t res;
+
+  mhd_assert (NULL != r_iov->iov);
+  total_sent = 0;
+  do
+  {
+    if ((size_t) SSIZE_MAX - total_sent < r_iov->iov[r_iov->sent].iov_len)
+      return total_sent; /* return value would overflow */
+
+    res = MHD_send_data_ (connection,
+                          r_iov->iov[r_iov->sent].iov_base,
+                          r_iov->iov[r_iov->sent].iov_len,
+                          push_data && (r_iov->cnt == r_iov->sent + 1));
+    if (0 > res)
+    {
+      /* Result is an error */
+      if (0 == total_sent)
+        return res; /* Nothing was sent, return result as is */
+
+      if (MHD_ERR_AGAIN_ == res)
+        return total_sent; /* Some data has been sent, return the amount */
+
+      return res; /* Any kind of a hard error */
+    }
+
+    total_sent += (size_t) res;
+
+    if (r_iov->iov[r_iov->sent].iov_len != (size_t) res)
+    {
+      /* Incomplete buffer has been sent.
+       * Adjust buffer of the last element. */
+      r_iov->iov[r_iov->sent].iov_base =
+        (void*) ((uint8_t*) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
+      r_iov->iov[r_iov->sent].iov_len -= res;
+
+      return total_sent;
+    }
+    /* The iov element has been completely sent */
+    r_iov->sent++;
+  } while ((r_iov->cnt > r_iov->sent) && (non_blk));
+
+  return (ssize_t) total_sent;
+}
+
+
+#endif /* !MHD_VECT_SEND || HTTPS_SUPPORT
+          || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+
+
+ssize_t
+MHD_send_iovec_ (struct MHD_Connection *connection,
+                 struct MHD_iovec_track_ *const r_iov,
+                 bool push_data)
+{
+#ifdef MHD_VECT_SEND
+#if defined(HTTPS_SUPPORT) || \
+  defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
+  bool use_iov_send = true;
+#endif /* HTTPS_SUPPORT || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+#endif /* MHD_VECT_SEND */
+
+  mhd_assert (NULL != connection->resp_iov.iov);
+  mhd_assert (NULL != connection->response->data_iov);
+  mhd_assert (connection->resp_iov.cnt > connection->resp_iov.sent);
+#ifdef MHD_VECT_SEND
+#if defined(HTTPS_SUPPORT) || \
+  defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
+#ifdef HTTPS_SUPPORT
+  use_iov_send = use_iov_send &&
+                 (0 == (connection->daemon->options & MHD_USE_TLS));
+#endif /* HTTPS_SUPPORT */
+#ifdef _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
+  use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked ||
+                                  connection->sk_spipe_suppress);
+#endif /* _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+  if (use_iov_send)
+#endif /* HTTPS_SUPPORT || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+  return send_iov_nontls (connection, r_iov, push_data);
+#endif /* MHD_VECT_SEND */
+
+#if ! defined(MHD_VECT_SEND) || defined(HTTPS_SUPPORT) || \
+  defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
+  return send_iov_emu (connection, r_iov, push_data);
+#endif /* !MHD_VECT_SEND || HTTPS_SUPPORT
+          || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+}
diff --git a/src/microhttpd/mhd_send.h b/src/microhttpd/mhd_send.h
index 54b5c1fe..d4ee8de7 100644
--- a/src/microhttpd/mhd_send.h
+++ b/src/microhttpd/mhd_send.h
@@ -41,6 +41,11 @@
 #include "connection_https.h"
 #endif
 
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \
+  defined(MHD_WINSOCK_SOCKETS)
+#define MHD_VECT_SEND 1
+#endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_WINSOCK_SOCKETS */
+
 #ifdef HAVE_FREEBSD_SENDFILE
 /**
  * Initialises static variables
@@ -125,4 +130,21 @@ MHD_connection_set_nodelay_state_ (struct MHD_Connection 
*connection,
                                    bool nodelay_state);
 
 
+/**
+ * Function for sending responses backed by a an array of memory buffers.
+ *
+ * @param connection the MHD connection structure
+ * @param r_iov the pointer to iov response structure with tracking
+ * @param push_data set to true to force push the data to the network from
+ *                  system buffers (usually set for the last piece of data),
+ *                  set to false to prefer holding incomplete network packets
+ *                  (more data will be send for the same reply).
+ * @return actual number of bytes sent
+ */
+ssize_t
+MHD_send_iovec_ (struct MHD_Connection *connection,
+                 struct MHD_iovec_track_ *r_iov,
+                 bool push_data);
+
+
 #endif /* MHD_SEND_H */
diff --git a/src/microhttpd/mhd_sockets.h b/src/microhttpd/mhd_sockets.h
index d4f21690..c22a1b12 100644
--- a/src/microhttpd/mhd_sockets.h
+++ b/src/microhttpd/mhd_sockets.h
@@ -934,17 +934,22 @@ static const int _MHD_socket_int_one = 1;
 #endif /* SOL_SOCKET && SO_NOSIGPIPE */
 
 
-#if defined(MHD_WINSOCK_SOCKETS) || defined(MHD_socket_nosignal_) || \
-  defined(MSG_NOSIGNAL)
+#if defined(MHD_socket_nosignal_) || defined(MSG_NOSIGNAL)
 /**
- * Indicate that SIGPIPE can be suppressed for normal send() by flags
+ * Indicate that SIGPIPE can be suppressed by MHD for normal send() by flags
  * or socket options.
  * If this macro is undefined, MHD cannot suppress SIGPIPE for normal
  * processing so sendfile() or writev() calls is not avoided.
  */
-#define HAVE_SEND_SIGPIPE_SUPPRESS      1
+#define MHD_SEND_SPIPE_SUPPRESS_POSSIBLE   1
 #endif /* MHD_WINSOCK_SOCKETS || MHD_socket_nosignal_ || MSG_NOSIGNAL */
 
+#if ! defined(MHD_WINSOCK_SOCKETS)
+/**
+ * Indicate that suppression of SIGPIPE is required.
+ */
+#define MHD_SEND_SPIPE_SUPPRESS_NEEDED     1
+#endif
 
 /**
  * Create a listen socket, with noninheritable flag if possible.
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index 4ae46bf3..3dbcd245 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -846,6 +846,153 @@ MHD_create_response_from_buffer_with_free_callback 
(size_t size,
 }
 
 
+/**
+ * Create a response object from an array of memory buffers.
+ * The response object can be extended with header information and then be used
+ * any number of times.
+ *
+ * @param iov the array for response data buffers, an internal copy of this
+ *        will be made
+ * @param iovcnt the number of elements in @a iov
+ * @param free_cb the callback to clean up any data associated with @a iov when
+ *        the response is destroyed.
+ * @param cls the argument passed to @a free_cb
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_iovec (const struct MHD_IoVec *iov,
+                                int iovcnt,
+                                MHD_ContentReaderFreeCallback free_cb,
+                                void *cls)
+{
+  struct MHD_Response *response;
+
+  if ((NULL == iov) && (0 < iovcnt))
+    return NULL;
+
+  response = MHD_calloc_ (1, sizeof (struct MHD_Response));
+  if (NULL != response)
+  {
+    if (MHD_mutex_init_ (&response->mutex))
+    {
+      int i;
+      int i_cp; /**< Index in the copy of iov */
+      uint64_t total_size;
+      void *last_valid_buffer;
+
+      i_cp = 0;
+      total_size = 0;
+      last_valid_buffer = NULL;
+      /* Calculate final size, number of valid elements, and check 'iov' */
+      for (i = 0; iovcnt > i; ++i)
+      {
+#if defined(MHD_WINSOCK_SOCKETS) && defined(_WIN64)
+        int64_t i_add;
+#endif /* ! MHD_WINSOCK_SOCKETS && _WIN64 */
+        if (0 == iov[i].iov_len)
+          continue; /* skip zero-sized elements */
+
+        if (NULL == iov[i].iov_base)
+        {
+          i_cp = -1; /* error */
+          break;
+        }
+        if ( (total_size > (total_size + iov[i].iov_len)) ||
+             (INT_MAX == i_cp) ||
+             (SSIZE_MAX < iov[i].iov_len) )
+        {
+          i_cp = -1; /* overflow */
+          break;
+        }
+        last_valid_buffer = iov[i].iov_base;
+        total_size += iov[i].iov_len;
+#if defined(MHD_POSIX_SOCKETS) || ! defined(_WIN64)
+        i_cp++;
+#else  /* ! MHD_POSIX_SOCKETS && _WIN64 */
+        i_add = iov[i].iov_len / ULONG_MAX;
+        if (0 != iov[i].iov_len % ULONG_MAX)
+          i_add++;
+        if (INT_MAX < (i_add + i_cp))
+        {
+          i_cp = -1; /* overflow */
+          break;
+        }
+        i_cp += (int) i_add;
+#endif /* ! MHD_POSIX_SOCKETS && _WIN64 */
+      }
+      if (0 <= i_cp)
+      {
+        response->fd = -1;
+        response->reference_count = 1;
+        response->total_size = total_size;
+        response->crc_cls = cls;
+        response->crfc = free_cb;
+        if (1 < i_cp)
+        {
+          MHD_iovec_ *iov_copy;
+          int num_copy_elements = i_cp;
+
+          iov_copy = MHD_calloc_ (num_copy_elements, sizeof(MHD_iovec_));
+          if (NULL != iov_copy)
+          {
+            i_cp = 0;
+            for (i = 0; iovcnt > i; ++i)
+            {
+              size_t element_size;
+              uint8_t *buf;
+
+              if (0 == iov[i].iov_len)
+                continue; /* skip zero-sized elements */
+
+              buf = (uint8_t*) iov[i].iov_base;
+              element_size = iov[i].iov_len;
+#if defined(MHD_WINSOCK_SOCKETS) && defined(_WIN64)
+              while (ULONG_MAX < element_size)
+              {
+                iov_copy[i_cp].iov_base = (void*) buf;
+                iov_copy[i_cp].iov_len = ULONG_MAX;
+                buf += ULONG_MAX;
+                element_size -= ULONG_MAX;
+                i_cp++;
+              }
+#endif /* MHD_WINSOCK_SOCKETS && _WIN64 */
+              iov_copy[i_cp].iov_base = (void*) buf;
+              iov_copy[i_cp].iov_len = element_size;
+              i_cp++;
+            }
+
+            mhd_assert (num_copy_elements == i_cp);
+            response->data_iov = iov_copy;
+            response->data_iovcnt = i_cp;
+
+            return response;
+          }
+
+        }
+        else if (1 == i_cp)
+        {
+          mhd_assert (NULL != last_valid_buffer);
+          response->data = last_valid_buffer;
+          response->data_size = total_size;
+
+          return response;
+        }
+        else /* if (0 == i_nz) */
+        {
+          mhd_assert (0 == total_size);
+
+          return response;
+        }
+      }
+      /* Some error condition */
+      MHD_mutex_destroy_chk_ (&response->mutex);
+    }
+    free (response);
+  }
+  return NULL;
+}
+
+
 #ifdef UPGRADE_SUPPORT
 /**
  * This connection-specific callback is provided by MHD to
@@ -1287,6 +1434,12 @@ MHD_destroy_response (struct MHD_Response *response)
 #endif
   if (NULL != response->crfc)
     response->crfc (response->crc_cls);
+
+  if (NULL != response->data_iov)
+  {
+    free (response->data_iov);
+  }
+
   while (NULL != response->first_header)
   {
     pos = response->first_header;
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore
index 0ca3ac3c..008262bd 100644
--- a/src/testcurl/.gitignore
+++ b/src/testcurl/.gitignore
@@ -116,3 +116,5 @@ test_patch11
 /test_add_conn_cleanup
 /test_add_conn_cleanup_nolisten
 core
+/test_get_iovec
+/test_get_iovec11
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index 78f2a076..d0580554 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -78,6 +78,7 @@ endif
 if HAVE_CURL
 check_PROGRAMS = \
   test_get \
+  test_get_iovec \
   test_get_sendfile \
   test_delete \
   test_patch \
@@ -89,6 +90,7 @@ check_PROGRAMS = \
   test_parse_cookies \
   test_large_put \
   test_get11 \
+  test_get_iovec11 \
   test_get_sendfile11 \
   test_patch11 \
   test_put11 \
@@ -218,6 +220,12 @@ test_digestauth_with_arguments_LDADD = \
   $(top_builddir)/src/microhttpd/libmicrohttpd.la \
   @LIBGCRYPT_LIBS@ @LIBCURL@
 
+test_get_iovec_SOURCES = \
+  test_get_iovec.c mhd_has_in_name.h
+test_get_iovec_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
 test_get_sendfile_SOURCES = \
   test_get_sendfile.c mhd_has_in_name.h
 test_get_sendfile_LDADD = \
@@ -306,7 +314,7 @@ test_put_chunked_SOURCES = \
 test_put_chunked_LDADD = \
   $(top_builddir)/src/microhttpd/libmicrohttpd.la \
   @LIBCURL@
-  
+
 test_add_conn_SOURCES = \
   test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
 test_add_conn_CFLAGS = \
@@ -345,6 +353,12 @@ test_get11_LDADD = \
   $(top_builddir)/src/microhttpd/libmicrohttpd.la \
   @LIBCURL@
 
+test_get_iovec11_SOURCES = \
+  test_get_iovec.c mhd_has_in_name.h
+test_get_iovec11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
 test_get_sendfile11_SOURCES = \
   test_get_sendfile.c mhd_has_in_name.h
 test_get_sendfile11_LDADD = \
diff --git a/src/testcurl/https/.gitignore b/src/testcurl/https/.gitignore
index 966e1215..7175906e 100644
--- a/src/testcurl/https/.gitignore
+++ b/src/testcurl/https/.gitignore
@@ -53,4 +53,5 @@
 /mhds_multi_daemon_test
 /tls_authentication_test
 /tmp_ca_cert.pem
+/test_https_get_iovec
 *.exe
diff --git a/src/testcurl/https/Makefile.am b/src/testcurl/https/Makefile.am
index 7d395ed1..7f568b33 100644
--- a/src/testcurl/https/Makefile.am
+++ b/src/testcurl/https/Makefile.am
@@ -1,4 +1,6 @@
 # This Makefile.am is in the public domain
+EMPTY_ITEM =
+
 SUBDIRS = .
 
 if USE_COVERAGE
@@ -34,7 +36,9 @@ THREAD_ONLY_TESTS = \
   test_https_time_out \
   test_https_multi_daemon \
   test_https_get \
-  test_empty_response
+  test_empty_response \
+  test_https_get_iovec \
+  $(EMPTY_ITEM)
 
 check_PROGRAMS = \
   test_https_get_select
@@ -155,6 +159,16 @@ test_https_get_LDADD  = \
   $(top_builddir)/src/microhttpd/libmicrohttpd.la \
   $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
 
+test_https_get_iovec_SOURCES = \
+  test_https_get_iovec.c \
+  tls_test_keys.h \
+  tls_test_common.h \
+  tls_test_common.c
+test_https_get_iovec_LDADD  = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
 if HAVE_GNUTLS_SNI
 test_https_sni_SOURCES = \
   test_https_sni.c \
diff --git a/src/testcurl/https/test_https_get.c 
b/src/testcurl/https/test_https_get_iovec.c
similarity index 62%
copy from src/testcurl/https/test_https_get.c
copy to src/testcurl/https/test_https_get_iovec.c
index b3f2fbf7..28d5cbfc 100644
--- a/src/testcurl/https/test_https_get.c
+++ b/src/testcurl/https/test_https_get_iovec.c
@@ -1,6 +1,7 @@
 /*
   This file is part of libmicrohttpd
-  Copyright (C) 2007 Christian Grothoff
+  Copyright (C) 2007-2021 Christian Grothoff
+  Copyright (C) 2016-2021 Evgeny Grin
 
   libmicrohttpd is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published
@@ -19,9 +20,16 @@
 */
 
 /**
- * @file test_https_get.c
- * @brief  Testcase for libmicrohttpd HTTPS GET operations
+ * @file test_https_get_iovec.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations using an iovec
  * @author Sagie Amir
+ * @author Karlson2k (Evgeny Grin)
+ * @author Lawrence Sebald
+ */
+
+/*
+ * This testcase is derived from the test_https_get.c testcase. This version
+ * adds the usage of a scatter/gather array for storing the response data.
  */
 
 #include "platform.h"
@@ -40,6 +48,147 @@ extern const char srv_signed_key_pem[];
 
 static int global_port;
 
+/* Use large enough pieces (>16KB) to test partially consumed
+ * data as TLS doesn't take more than 16KB by a single call. */
+#define TESTSTR_IOVLEN 20480
+#define TESTSTR_IOVCNT 30
+#define TESTSTR_SIZE   (TESTSTR_IOVCNT * TESTSTR_IOVLEN)
+
+
+static void
+iov_free_callback (void *cls)
+{
+  free (cls);
+}
+
+
+static int
+check_read_data (const void *ptr, size_t len)
+{
+  const int *buf;
+  size_t i;
+
+  if (len % sizeof(int))
+    return -1;
+
+  buf = (const int *) ptr;
+
+  for (i = 0; i < len / sizeof(int); ++i)
+  {
+    if (buf[i] != (int) i)
+      return -1;
+  }
+
+  return 0;
+}
+
+
+static enum MHD_Result
+iovec_ahc (void *cls,
+           struct MHD_Connection *connection,
+           const char *url,
+           const char *method,
+           const char *version,
+           const char *upload_data,
+           size_t *upload_data_size,
+           void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  enum MHD_Result ret;
+  int *data;
+  struct MHD_IoVec iov[TESTSTR_IOVCNT];
+  int i;
+  int j;
+  (void) cls; (void) url; (void) version;          /* Unused. Silent compiler 
warning. */
+  (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler 
warning. */
+
+  if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+  {
+    /* do never respond on first call */
+    *ptr = &aptr;
+    return MHD_YES;
+  }
+  *ptr = NULL;                  /* reset when done */
+
+  /* Create some test data. */
+  if (NULL == (data = malloc (TESTSTR_SIZE)))
+    return MHD_NO;
+
+  for (j = 0; j < TESTSTR_IOVCNT; ++j)
+  {
+    /* Assign chunks of memory area in the reverse order
+     * to make non-continous set of data therefore
+     * possible buffer overruns could be detected */
+    iov[j].iov_base = data + (((TESTSTR_IOVCNT - 1) - j)
+                              * (TESTSTR_SIZE / TESTSTR_IOVCNT
+                                 / sizeof(int)));
+    iov[j].iov_len = TESTSTR_SIZE / TESTSTR_IOVCNT;
+
+    for (i = 0; i < (int) (TESTSTR_IOVLEN / sizeof(int)); ++i)
+      ((int*) iov[j].iov_base)[i] = i + (j * TESTSTR_IOVLEN / sizeof(int));
+  }
+
+  response = MHD_create_response_from_iovec (iov,
+                                             TESTSTR_IOVCNT,
+                                             &iov_free_callback,
+                                             data);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+test_iovec_transfer (void *cls,
+                     int port,
+                     const char *cipher_suite,
+                     int proto_version)
+{
+  int len;
+  int ret = 0;
+  struct CBC cbc;
+  char url[255];
+  (void) cls;    /* Unused. Silent compiler warning. */
+
+  len = TESTSTR_SIZE;
+  if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
+  {
+    fprintf (stderr, MHD_E_MEM);
+    return -1;
+  }
+  cbc.size = len;
+  cbc.pos = 0;
+
+  if (gen_test_file_url (url,
+                         sizeof (url),
+                         port))
+  {
+    ret = -1;
+    goto cleanup;
+  }
+
+  if (CURLE_OK !=
+      send_curl_req (url, &cbc, cipher_suite, proto_version))
+  {
+    ret = -1;
+    goto cleanup;
+  }
+
+  /* compare test file & daemon response */
+  if ((cbc.pos != TESTSTR_SIZE) ||
+      (0 != check_read_data (cbc.buf, cbc.pos)))
+  {
+    fprintf (stderr, "Error: local file & received file differ.\n");
+    ret = -1;
+  }
+cleanup:
+  free (cbc.buf);
+  return ret;
+}
+
 
 /* perform a HTTP GET request via SSL/TLS */
 static int
@@ -60,7 +209,7 @@ test_secure_get (FILE *test_fd,
                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS
                         | MHD_USE_ERROR_LOG, port,
                         NULL, NULL,
-                        &http_ahc, NULL,
+                        &iovec_ahc, NULL,
                         MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
                         MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
                         MHD_OPTION_END);
@@ -81,7 +230,7 @@ test_secure_get (FILE *test_fd,
     port = (int) dinfo->port;
   }
 
-  ret = test_https_transfer (test_fd,
+  ret = test_iovec_transfer (test_fd,
                              port,
                              cipher_suite,
                              proto_version);
@@ -104,6 +253,7 @@ ahc_empty (void *cls,
   static int ptr;
   struct MHD_Response *response;
   enum MHD_Result ret;
+  struct MHD_IoVec iov;
   (void) cls;
   (void) url;
   (void) url;
@@ -120,9 +270,14 @@ ahc_empty (void *cls,
     return MHD_YES;
   }
   *unused = NULL;
-  response = MHD_create_response_from_buffer (0,
-                                              NULL,
-                                              MHD_RESPMEM_PERSISTENT);
+
+  iov.iov_base = NULL;
+  iov.iov_len = 0;
+
+  response = MHD_create_response_from_iovec (&iov,
+                                             1,
+                                             NULL,
+                                             NULL);
   ret = MHD_queue_response (connection,
                             MHD_HTTP_OK,
                             response);
diff --git a/src/testcurl/test_get_sendfile.c b/src/testcurl/test_get_iovec.c
similarity index 75%
copy from src/testcurl/test_get_sendfile.c
copy to src/testcurl/test_get_iovec.c
index 1a444607..1d817851 100644
--- a/src/testcurl/test_get_sendfile.c
+++ b/src/testcurl/test_get_iovec.c
@@ -1,6 +1,7 @@
 /*
      This file is part of libmicrohttpd
-     Copyright (C) 2007, 2009 Christian Grothoff
+     Copyright (C) 2007-2021 Christian Grothoff
+     Copyright (C) 2014-2021 Evgeny Grin
 
      libmicrohttpd is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -17,10 +18,18 @@
      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
      Boston, MA 02110-1301, USA.
 */
+
 /**
- * @file test_get_sendfile.c
- * @brief  Testcase for libmicrohttpd response from FD
+ * @file test_get_iovec.c
+ * @brief  Testcase for libmicrohttpd response from scatter/gather array
  * @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
+ * @author Lawrence Sebald
+ */
+
+/*
+ * This test is largely derived from the test_get_sendfile.c file, with the
+ * daemon using MHD_create_response_from_iovec instead of working from an fd.
  */
 
 #include "MHD_config.h"
@@ -47,13 +56,14 @@
 #define MHD_CPU_COUNT 2
 #endif
 
-#define TESTSTR \
-  "This is the content of the test file we are sending using sendfile (if 
available)"
-
-static char *sourcefile;
+#define TESTSTR_IOVLEN 20480
+#define TESTSTR_IOVCNT 20
+#define TESTSTR_SIZE   (TESTSTR_IOVCNT * TESTSTR_IOVLEN)
 
 static int oneone;
 
+static int readbuf[TESTSTR_SIZE * 2 / sizeof(int)];
+
 struct CBC
 {
   char *buf;
@@ -68,13 +78,56 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
   struct CBC *cbc = ctx;
 
   if (cbc->pos + size * nmemb > cbc->size)
-    return 0;                   /* overflow */
+    _exit (7);                   /* overflow */
   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
   cbc->pos += size * nmemb;
   return size * nmemb;
 }
 
 
+static void
+iov_free_callback (void *cls)
+{
+  free (cls);
+}
+
+
+static void
+iovncont_free_callback (void *cls)
+{
+  struct MHD_IoVec *iov = (struct MHD_IoVec *) cls;
+  int i;
+
+  for (i = 0; i < TESTSTR_IOVCNT; ++i)
+  {
+    free (iov[i].iov_base);
+  }
+
+  free (iov);
+}
+
+
+static int
+check_read_data (const void *ptr, size_t len)
+{
+  const int *buf;
+  size_t i;
+
+  if (len % sizeof(int))
+    return -1;
+
+  buf = (const int *) ptr;
+
+  for (i = 0; i < len / sizeof(int); ++i)
+  {
+    if (buf[i] != (int) i)
+      return -1;
+  }
+
+  return 0;
+}
+
+
 static enum MHD_Result
 ahc_echo (void *cls,
           struct MHD_Connection *connection,
@@ -88,7 +141,65 @@ ahc_echo (void *cls,
   const char *me = cls;
   struct MHD_Response *response;
   enum MHD_Result ret;
-  int fd;
+  int *data;
+  struct MHD_IoVec iov[TESTSTR_IOVCNT];
+  int i;
+  (void) url; (void) version;                      /* Unused. Silent compiler 
warning. */
+  (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler 
warning. */
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+  {
+    *unused = &ptr;
+    return MHD_YES;
+  }
+  *unused = NULL;
+
+  /* Create some test data. */
+  if (NULL == (data = malloc (TESTSTR_SIZE)))
+    return MHD_NO;
+
+  for (i = 0; i < (int) (TESTSTR_SIZE / sizeof(int)); ++i)
+  {
+    data[i] = i;
+  }
+
+  for (i = 0; i < TESTSTR_IOVCNT; ++i)
+  {
+    iov[i].iov_base = data + (i * (TESTSTR_SIZE / TESTSTR_IOVCNT
+                                   / sizeof(int)));
+    iov[i].iov_len = TESTSTR_SIZE / TESTSTR_IOVCNT;
+  }
+
+  response = MHD_create_response_from_iovec (iov,
+                                             TESTSTR_IOVCNT,
+                                             &iov_free_callback,
+                                             data);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static enum MHD_Result
+ncont_echo (void *cls,
+            struct MHD_Connection *connection,
+            const char *url,
+            const char *method,
+            const char *version,
+            const char *upload_data, size_t *upload_data_size,
+            void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  enum MHD_Result ret;
+  int *data;
+  struct MHD_IoVec *iov;
+  int i, j;
   (void) url; (void) version;                      /* Unused. Silent compiler 
warning. */
   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler 
warning. */
 
@@ -100,29 +211,53 @@ ahc_echo (void *cls,
     return MHD_YES;
   }
   *unused = NULL;
-  fd = open (sourcefile, O_RDONLY);
-  if (fd == -1)
+
+  if (NULL == (iov = malloc (sizeof(struct MHD_IoVec) * TESTSTR_IOVCNT)))
+    return MHD_NO;
+
+  memset (iov, 0, sizeof(struct MHD_IoVec) * TESTSTR_IOVCNT);
+
+  /* Create some test data. */
+  for (j = TESTSTR_IOVCNT - 1; j >= 0; --j)
   {
-    fprintf (stderr, "Failed to open `%s': %s\n",
-             sourcefile,
-             strerror (errno));
-    exit (1);
+    if (NULL == (data = malloc (TESTSTR_IOVLEN)))
+      goto err_out;
+
+    iov[j].iov_base = data;
+    iov[j].iov_len = TESTSTR_IOVLEN;
+
+    for (i = 0; i < (int) (TESTSTR_IOVLEN / sizeof(int)); ++i)
+    {
+      data[i] = i + (j * TESTSTR_IOVLEN / sizeof(int));
+    }
   }
-  response = MHD_create_response_from_fd (strlen (TESTSTR), fd);
+
+  response = MHD_create_response_from_iovec (iov,
+                                             TESTSTR_IOVCNT,
+                                             &iovncont_free_callback,
+                                             iov);
   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
   MHD_destroy_response (response);
   if (ret == MHD_NO)
     abort ();
   return ret;
+
+err_out:
+  for (j = 0; j < TESTSTR_IOVCNT; ++j)
+  {
+    if (NULL != iov[j].iov_base)
+      free (iov[j].iov_base);
+  }
+
+  return MHD_NO;
 }
 
 
 static int
-testInternalGet ()
+testInternalGet (bool contiguous)
 {
   struct MHD_Daemon *d;
   CURL *c;
-  char buf[2048];
   struct CBC cbc;
   CURLcode errornum;
   int port;
@@ -136,11 +271,21 @@ testInternalGet ()
       port += 10;
   }
 
-  cbc.buf = buf;
-  cbc.size = 2048;
+  cbc.buf = (char*) readbuf;
+  cbc.size = sizeof(readbuf);
   cbc.pos = 0;
-  d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
-                        port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+
+  if (contiguous)
+  {
+    d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
+                          port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  }
+  else
+  {
+    d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
+                          port, NULL, NULL, &ncont_echo, "GET", 
MHD_OPTION_END);
+  }
+
   if (d == NULL)
     return 1;
   if (0 == port)
@@ -180,9 +325,9 @@ testInternalGet ()
   }
   curl_easy_cleanup (c);
   MHD_stop_daemon (d);
-  if (cbc.pos != strlen (TESTSTR))
+  if (cbc.pos != TESTSTR_SIZE)
     return 4;
-  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+  if (0 != check_read_data (cbc.buf, cbc.pos))
     return 8;
   return 0;
 }
@@ -193,7 +338,6 @@ testMultithreadedGet ()
 {
   struct MHD_Daemon *d;
   CURL *c;
-  char buf[2048];
   struct CBC cbc;
   CURLcode errornum;
   int port;
@@ -207,11 +351,12 @@ testMultithreadedGet ()
       port += 10;
   }
 
-  cbc.buf = buf;
-  cbc.size = 2048;
+  cbc.buf = (char*) readbuf;
+  cbc.size = sizeof(readbuf);
   cbc.pos = 0;
   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
-                        | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
+                        | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
+                        | MHD_USE_AUTO,
                         port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
   if (d == NULL)
     return 16;
@@ -252,9 +397,9 @@ testMultithreadedGet ()
   }
   curl_easy_cleanup (c);
   MHD_stop_daemon (d);
-  if (cbc.pos != strlen (TESTSTR))
+  if (cbc.pos != TESTSTR_SIZE)
     return 64;
-  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+  if (0 != check_read_data (cbc.buf, cbc.pos))
     return 128;
   return 0;
 }
@@ -265,7 +410,6 @@ testMultithreadedPoolGet ()
 {
   struct MHD_Daemon *d;
   CURL *c;
-  char buf[2048];
   struct CBC cbc;
   CURLcode errornum;
   int port;
@@ -279,10 +423,11 @@ testMultithreadedPoolGet ()
       port += 10;
   }
 
-  cbc.buf = buf;
-  cbc.size = 2048;
+  cbc.buf = (char*) readbuf;
+  cbc.size = sizeof(readbuf);
   cbc.pos = 0;
-  d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
+  d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
+                        | MHD_USE_AUTO,
                         port, NULL, NULL, &ahc_echo, "GET",
                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
                         MHD_OPTION_END);
@@ -325,9 +470,9 @@ testMultithreadedPoolGet ()
   }
   curl_easy_cleanup (c);
   MHD_stop_daemon (d);
-  if (cbc.pos != strlen (TESTSTR))
+  if (cbc.pos != TESTSTR_SIZE)
     return 64;
-  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+  if (0 != check_read_data (cbc.buf, cbc.pos))
     return 128;
   return 0;
 }
@@ -338,7 +483,6 @@ testExternalGet ()
 {
   struct MHD_Daemon *d;
   CURL *c;
-  char buf[2048];
   struct CBC cbc;
   CURLM *multi;
   CURLMcode mret;
@@ -367,8 +511,8 @@ testExternalGet ()
   }
 
   multi = NULL;
-  cbc.buf = buf;
-  cbc.size = 2048;
+  cbc.buf = (char*) readbuf;
+  cbc.size = sizeof(readbuf);
   cbc.pos = 0;
   d = MHD_start_daemon (MHD_USE_ERROR_LOG,
                         port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
@@ -488,16 +632,9 @@ testExternalGet ()
     curl_multi_cleanup (multi);
   }
   MHD_stop_daemon (d);
-  if (cbc.pos != strlen (TESTSTR))
-  {
-    fprintf (stderr,
-             "Got %.*s instead of %s!\n",
-             (int) cbc.pos,
-             cbc.buf,
-             TESTSTR);
+  if (cbc.pos != TESTSTR_SIZE)
     return 8192;
-  }
-  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+  if (0 != check_read_data (cbc.buf, cbc.pos))
     return 16384;
   return 0;
 }
@@ -509,10 +646,10 @@ testUnknownPortGet ()
   struct MHD_Daemon *d;
   const union MHD_DaemonInfo *di;
   CURL *c;
-  char buf[2048];
   struct CBC cbc;
   CURLcode errornum;
   int port;
+  char buf[2048];
 
   struct sockaddr_in addr;
   socklen_t addr_len = sizeof(addr);
@@ -521,8 +658,8 @@ testUnknownPortGet ()
   addr.sin_port = 0;
   addr.sin_addr.s_addr = INADDR_ANY;
 
-  cbc.buf = buf;
-  cbc.size = 2048;
+  cbc.buf = (char*) readbuf;
+  cbc.size = sizeof(readbuf);
   cbc.pos = 0;
   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
                         0, NULL, NULL, &ahc_echo, "GET",
@@ -584,9 +721,9 @@ testUnknownPortGet ()
   }
   curl_easy_cleanup (c);
   MHD_stop_daemon (d);
-  if (cbc.pos != strlen (TESTSTR))
+  if (cbc.pos != TESTSTR_SIZE)
     return 1048576;
-  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+  if (0 != check_read_data (cbc.buf, cbc.pos))
     return 2097152;
   return 0;
 }
@@ -596,41 +733,18 @@ int
 main (int argc, char *const *argv)
 {
   unsigned int errorCount = 0;
-  const char *tmp;
-  FILE *f;
   (void) argc;   /* Unused. Silent compiler warning. */
 
   if ((NULL == argv) || (0 == argv[0]))
     return 99;
   oneone = has_in_name (argv[0], "11");
 
-  if ( (NULL == (tmp = getenv ("TMPDIR"))) &&
-       (NULL == (tmp = getenv ("TMP"))) &&
-       (NULL == (tmp = getenv ("TEMP"))) )
-    tmp = "/tmp";
-  sourcefile = malloc (strlen (tmp) + 32);
-  snprintf (sourcefile,
-            strlen (tmp) + 32,
-            "%s/%s%s",
-            tmp,
-            "test-mhd-sendfile",
-            oneone ? "11" : "");
-  f = fopen (sourcefile, "w");
-  if (NULL == f)
-  {
-    fprintf (stderr, "failed to write test file\n");
-    free (sourcefile);
-    return 1;
-  }
-  if (1 !=
-      fwrite (TESTSTR, strlen (TESTSTR), 1, f))
-    abort ();
-  fclose (f);
   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
     return 2;
   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
   {
-    errorCount += testInternalGet ();
+    errorCount += testInternalGet (true);
+    errorCount += testInternalGet (false);
     errorCount += testMultithreadedGet ();
     errorCount += testMultithreadedPoolGet ();
     errorCount += testUnknownPortGet ();
@@ -639,7 +753,5 @@ main (int argc, char *const *argv)
   if (errorCount != 0)
     fprintf (stderr, "Error (code: %u)\n", errorCount);
   curl_global_cleanup ();
-  unlink (sourcefile);
-  free (sourcefile);
   return errorCount != 0;       /* 0 == pass */
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]