diff -u -r libmicrohttpd-0.9.72-orig/src/include/microhttpd.h libmicrohttpd-0.9.72/src/include/microhttpd.h
--- libmicrohttpd-0.9.72-orig/src/include/microhttpd.h	2020-12-28 18:41:28.000000000 +0000
+++ libmicrohttpd-0.9.72/src/include/microhttpd.h	2021-01-08 15:29:04.254639951 +0000
@@ -108,6 +108,7 @@
 #include <stdarg.h>
 #include <stdint.h>
 #include <sys/types.h>
+#include <sys/uio.h>
 #if ! defined(_WIN32) || defined(__CYGWIN__)
 #include <unistd.h>
 #include <sys/time.h>
@@ -2356,6 +2357,23 @@


 /**
+ * This method is called by libmicrohttpd if we are done with an
+ * iovec-based content reader.  It should  be used to free resources
+ * associated with the content reader.
+ *
+ * @param iov internal copy of I/O vector (will be an exact copy of
+ *            the originally passed I/O vector)
+ * @param iovcnt number of elements in the I/O vector
+ * @param cls extra argument to the callback
+ * @ingroup response
+ */
+typedef void
+(*MHD_IOVContentReaderFreeCallback) (const struct iovec *iov,
+                                     int iovcnt,
+                                     void *cls);
+
+
+/**
  * Iterator over key-value pairs where the value
  * maybe made available in increments and/or may
  * not be zero-terminated.  Used for processing
@@ -3228,6 +3246,26 @@


 /**
+ * Create a response object from an iovec.  The response object can be
+ * extended with header information and then be used any number of times.
+ *
+ * @param iov I/O vector for response data -- an internal copy of this
+ *        will be made
+ * @param iovcnt number of elements in iov
+ * @param cls extra argument passed to free_cb
+ * @param free_cb callback to clean up any data associated with iov when
+ *        the response is destroyed.
+ * @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 iovec *iov,
+                                int iovcnt,
+                                void *cls,
+                                MHD_IOVContentReaderFreeCallback free_cb);
+
+
+/**
  * Enumeration for actions MHD should perform on the underlying socket
  * of the upgrade.  This API is not finalized, and in particular
  * the final set of actions is yet to be decided. This is just an
diff -u -r libmicrohttpd-0.9.72-orig/src/microhttpd/connection.c libmicrohttpd-0.9.72/src/microhttpd/connection.c
--- libmicrohttpd-0.9.72-orig/src/microhttpd/connection.c	2020-12-28 18:41:28.000000000 +0000
+++ libmicrohttpd-0.9.72/src/microhttpd/connection.c	2021-01-08 15:29:04.310642072 +0000
@@ -2935,7 +2935,11 @@
                     connection->response_write_position) );

       if ( (NULL == resp->crc) &&
-           (0 == connection->response_write_position) )
+           (0 == connection->response_write_position)
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+           && (NULL == resp->data_iov)
+#endif
+         )
       {
         mhd_assert (resp->total_size >= resp->data_size);
         /* Send response headers alongside the response body, if the body
@@ -2949,6 +2953,19 @@
                                       resp->data_size,
                                       (resp->total_size == resp->data_size));
       }
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+      else if (NULL != resp->data_iov)
+      {
+        ret = MHD_send_hdr_and_body_ (connection,
+                                      &connection->write_buffer
+                                      [connection->write_buffer_send_offset],
+                                      wb_ready,
+                                      false,
+                                      NULL,
+                                      0,
+                                      0);
+      }
+#endif
       else
       {
         /* This is response for HEAD request or reply body is not allowed
@@ -3018,9 +3035,16 @@
         ret = MHD_send_sendfile_ (connection);
       }
       else
-#else  /* ! _MHD_HAVE_SENDFILE */
+#endif /* _MHD_HAVE_SENDFILE */
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+      if (NULL != response->data_iov)
+      {
+        ret = MHD_send_iovec_ (connection);
+      }
+      else
+#else /* !(HAVE_SENDMSG || HAVE_WRITEV) */
       if (1)
-#endif /* ! _MHD_HAVE_SENDFILE */
+#endif /* HAVE_SENDMSG || HAVE_WRITEV */
       {
         data_write_offset = connection->response_write_position
                             - response->data_start;
diff -u -r libmicrohttpd-0.9.72-orig/src/microhttpd/internal.h libmicrohttpd-0.9.72/src/microhttpd/internal.h
--- libmicrohttpd-0.9.72-orig/src/microhttpd/internal.h	2020-12-28 12:17:34.000000000 +0000
+++ libmicrohttpd-0.9.72/src/microhttpd/internal.h	2021-01-08 15:29:04.310642072 +0000
@@ -43,6 +43,10 @@
 #include <stdbool.h>
 #endif

+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+#include <sys/uio.h>
+#endif
+

 #ifdef MHD_PANIC
 /* Override any defined MHD_PANIC macro with proper one */
@@ -450,6 +454,14 @@
    */
   bool is_pipe;

+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+  struct iovec *data_iov;
+  int data_iovcnt;
+  struct iovec data_iov_tr;
+  struct iovec *data_iov_left;
+  int data_iovcnt_left;
+  MHD_IOVContentReaderFreeCallback data_iov_free;
+#endif
 };


diff -u -r libmicrohttpd-0.9.72-orig/src/microhttpd/mhd_send.c libmicrohttpd-0.9.72/src/microhttpd/mhd_send.c
--- libmicrohttpd-0.9.72-orig/src/microhttpd/mhd_send.c	2020-12-28 12:17:34.000000000 +0000
+++ libmicrohttpd-0.9.72/src/microhttpd/mhd_send.c	2021-01-08 15:29:04.310642072 +0000
@@ -1250,3 +1250,171 @@


 #endif /* _MHD_HAVE_SENDFILE */
+
+
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+static ssize_t
+send_iov_nontls (struct MHD_Connection *connection,
+                 struct iovec *iov,
+                 int iovcnt)
+{
+  ssize_t ret;
+#ifdef HAVE_SENDMSG
+  struct msghdr msg;
+#endif
+
+  if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
+       (MHD_CONNECTION_CLOSED == connection->state) )
+  {
+    return MHD_ERR_NOTCONN_;
+  }
+
+#ifdef HAVE_SENDMSG
+  /* Copy in the iovec, offsetting as needed for previous partial sends. */
+  memset(&msg, 0, sizeof(struct msghdr));
+  msg.msg_iov = iov;
+  msg.msg_iovlen = iovcnt;
+  ret = sendmsg (connection->socket_fd, &msg, MSG_NOSIGNAL);
+#else
+  ret = writev (connection->socket_fd, iov, iovcnt);
+#endif
+
+  if (0 > ret)
+  {
+    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_;
+  }
+#ifdef EPOLL_SUPPORT
+  connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+#endif /* EPOLL_SUPPORT */
+  return ret;
+}
+
+#ifdef HTTPS_SUPPORT
+
+static ssize_t
+send_iov_tls (struct MHD_Connection *connection,
+              struct iovec *iov,
+              int iovcnt)
+{
+  ssize_t res;
+  int i;
+
+  /* Send one iovec element. */
+  res = gnutls_record_send (connection->tls_session,
+                            iov[0].iov_base,
+                            iov[0].iov_len);
+  if ( (GNUTLS_E_AGAIN == res) ||
+       (GNUTLS_E_INTERRUPTED == res) )
+  {
+#ifdef EPOLL_SUPPORT
+    if (GNUTLS_E_AGAIN == res)
+      connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+#endif
+    return MHD_ERR_AGAIN_;
+  }
+  if (res < 0)
+  {
+    /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
+       disrupted); interpret as a hard error */
+    return MHD_ERR_NOTCONN_;
+  }
+  return res;
+}
+
+#endif /* HTTP_SUPPORT */
+
+ssize_t
+MHD_send_iovec_ (struct MHD_Connection *connection) {
+  struct MHD_Response *response = connection->response;
+  ssize_t ret, sz;
+  int i;
+#ifdef HTTPS_SUPPORT
+  const bool tls_conn = (connection->daemon->options & MHD_USE_TLS);
+#else  /* ! HTTPS_SUPPORT */
+  const bool tls_conn = false;
+#endif /* ! HTTPS_SUPPORT */
+
+  pre_send_setopt (connection, !tls_conn, false);
+
+#ifdef HTTPS_SUPPORT
+  if (0 != (connection->daemon->options & MHD_USE_TLS))
+  {
+    ret = send_iov_tls (connection,
+                        response->data_iov_left,
+                        response->data_iovcnt_left);
+  }
+  else
+#endif /* HTTPS_SUPPORT */
+  {
+    ret = send_iov_nontls(connection,
+                          response->data_iov_left,
+                          response->data_iovcnt_left);
+  }
+
+  /* This does not spark joy. */
+  if (ret >= 0 && ret < response->data_iov_left[0].iov_len)
+  {
+    if (!response->data_iov_tr.iov_base)
+    {
+      response->data_iov_tr = response->data_iov_left[0];
+    }
+
+    response->data_iov_left[0].iov_len -= ret;
+    response->data_iov_left[0].iov_base =
+      ((char *)response->data_iov_left[0].iov_base) + ret;
+  }
+  else if (ret >= 0)
+  {
+    sz = ret - response->data_iov_left[0].iov_len;
+
+    if (response->data_iov_tr.iov_base)
+    {
+      response->data_iov_left[0] = response->data_iov_tr;
+      memset (&response->data_iov_tr, 0, sizeof(struct iovec));
+    }
+
+    for (i = 1; i < response->data_iovcnt_left; ++i)
+    {
+      if (sz < response->data_iov_left[i].iov_len)
+      {
+        response->data_iov_tr = response->data_iov_left[0];
+      }
+
+      if (sz <= response->data_iov_left[i].iov_len)
+      {
+        response->data_iov_left[i].iov_len -= sz;
+        response->data_iov_left[i].iov_base =
+          ((char *)response->data_iov_left[i].iov_base) + sz;
+        response->data_iov_left += i;
+        response->data_iovcnt_left -= i;
+        break;
+      }
+
+      sz -= response->data_iov_left[i].iov_len;
+    }
+
+    if (0 == response->data_iovcnt_left)
+    {
+        post_send_setopt (connection, !tls_conn, true);
+    }
+  }
+
+  return ret;
+}
+
+#endif /* HAVE_SENDMSG || HAVE_WRITEV */
diff -u -r libmicrohttpd-0.9.72-orig/src/microhttpd/mhd_send.h libmicrohttpd-0.9.72/src/microhttpd/mhd_send.h
--- libmicrohttpd-0.9.72-orig/src/microhttpd/mhd_send.h	2020-12-26 14:02:37.000000000 +0000
+++ libmicrohttpd-0.9.72/src/microhttpd/mhd_send.h	2021-01-08 15:29:04.314642224 +0000
@@ -111,4 +111,11 @@

 #endif

+
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+ssize_t
+MHD_send_iovec_ (struct MHD_Connection *connection);
+
+#endif
+
 #endif /* MHD_SEND_H */
diff -u -r libmicrohttpd-0.9.72-orig/src/microhttpd/response.c libmicrohttpd-0.9.72/src/microhttpd/response.c
--- libmicrohttpd-0.9.72-orig/src/microhttpd/response.c	2020-12-28 12:17:34.000000000 +0000
+++ libmicrohttpd-0.9.72/src/microhttpd/response.c	2021-01-08 15:29:04.314642224 +0000
@@ -56,6 +56,11 @@
 #endif /* _WIN32 */


+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+#include <sys/uio.h>
+#endif
+
+
 /**
  * Size of single file read operation for
  * file-backed responses.
@@ -846,6 +851,74 @@
 }


+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+/**
+ * Create a response object from an iovec.  The response object can be
+ * extended with header information and then be used any number of times.
+ *
+ * @param iov I/O vector for response data
+ * @param iovcnt number of elements in iov
+ * @param cls extra argument passed to free_cb
+ * @param free_cb callback to clean up any data associated with iov when
+ *        the response is destroyed.
+ * @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 iovec *iov,
+                                int iovcnt,
+                                void *cls,
+                                MHD_IOVContentReaderFreeCallback free_cb)
+{
+  struct MHD_Response *response;
+  void *tmp;
+  int i;
+  size_t dsz = 0;
+
+  if ((NULL == iov) && (iovcnt > 0))
+    return NULL;
+  if (NULL == (response = MHD_calloc_ (1, sizeof (struct MHD_Response))))
+    return NULL;
+  response->fd = -1;
+#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
+  if (! MHD_mutex_init_ (&response->mutex))
+  {
+    free (response);
+    return NULL;
+  }
+#endif
+  if (NULL == (tmp = malloc(iovcnt * sizeof(struct iovec))))
+  {
+#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
+    MHD_mutex_destroy_chk_ (&response->mutex);
+#endif
+    free(response);
+    return NULL;
+  }
+
+  response->data_iov = (struct iovec *)tmp;
+  response->data_iovcnt = iovcnt;
+  response->data_iov_left = tmp;
+  response->data_iovcnt_left = iovcnt;
+  response->crc_cls = cls;
+  response->data_iov_free = free_cb;
+  memcpy(response->data_iov, iov, iovcnt * sizeof(struct iovec));
+
+  for(i = 0; i < iovcnt; ++i)
+  {
+    dsz += iov[i].iov_len;
+  }
+
+  response->data_size = dsz;
+  response->total_size = dsz;
+  response->reference_count = 1;
+  return response;
+}
+
+
+#endif /* HAVE_SENDMSG || HAVE_WRITEV */
+
+
 #ifdef UPGRADE_SUPPORT
 /**
  * This connection-specific callback is provided by MHD to
@@ -1287,6 +1360,18 @@
 #endif
   if (NULL != response->crfc)
     response->crfc (response->crc_cls);
+
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+  if (NULL != response->data_iov)
+  {
+    if (NULL != response->data_iov_free)
+      response->data_iov_free (response->data_iov,
+                               response->data_iovcnt,
+                               response->crc_cls);
+    free(response->data_iov);
+  }
+#endif
+
   while (NULL != response->first_header)
   {
     pos = response->first_header;
