gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [libmicrohttpd] branch master updated (316f6ab3 -> e82de750


From: gnunet
Subject: [GNUnet-SVN] [libmicrohttpd] branch master updated (316f6ab3 -> e82de750)
Date: Mon, 19 Aug 2019 10:15:12 +0200

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

ng0 pushed a change to branch master
in repository libmicrohttpd.

    from 316f6ab3 add compiler/linker hardnening
     new fedd9045 mhd_send: Add initial version.
     new 99142213 mhd_send
     new 5181cf5a mhd_send: fix switch.
     new f824f035 mhd_send: minor typo
     new b474adda mhd_send: remove unnecessary comments.
     new 883c72f9 mhd_send: Move return_bytes related code into the right place.
     new 8289ed69 provide example for use of getsockopt to get MSS
     new 77db4d16 fix syntax
     new 0d394e2a startingpoint
     new 43c21d59 mhd_send: Restructure.
     new 4f9522fa restructure a bit to reuse existing send function
     new 56676ae7 Add mhd_send to Makefile, more fixes in mhd_send.
     new beae0da3 mhd_send.c: fix compiler error about MSG_MORE when MSG_MORE 
is undefined.
     new 9af4a482 mhd_send.c: Try to guess the right branch to close.
     new ee0942b2 iAdd headerfile for mhd_send.
     new 09ef9de1 mhd_send: Use MHD_SCKT_OPT_BOOL_ for setsockopt optval.
     new 5e99e18f mhd_send: start adding logic from send_param_adapter.
     new 67792e0c more from connection.c, without checks so far.
     new 8edc3b91 adjust
     new 2a08acaa mhd_send.c: variable declarations (style).
     new 843b844d mhd_send.c: Use daemon from connection struct.
     new 3100e3e3 mhd_send.c: call send_tls_adapter() when TLS is used.
     new 1f08febc mhd_send: change send_tls_adapter() to non-static and export 
its prototype, use it in mhd_send.c
     new f5170975 move TLS branch to right position
     new 5847d13f inline TLS logic
     new a2fefb3b indentation, comments, issue
     new b0d40648 fixes
     new 45a1764c fixes
     new e9c09cf9 fix err logic
     new 61b84b6a comments
     new 34c88c22 combine ifs, fix uninitialized var error
     new 23a7502e setsockopt(): check return value.
     new b854bbba replace connection->send_cls()
     new d7a3f826 Use s in setsockopt()
     new ffd6e4c4 use using_tls
     new 068cf22c implement TCP_NOPUSH branch content
     new 5c197db3 flatten if statements, add initial TCP_NOPUSH to 
MHD_send_on_connection2_
     new 15672269 check getsockopt's.
     new 6722cf7d indent
     new 23b0752b doxygen for _send2
     new 4b924674 doxygen for send_
     new 52a76e50 indent
     new 640a37e2 move comment above function.
     new 9d1dddc0 remove verbose comments in mhd_send.c
     new bc251e7f fix compiler error.
     new 7054ad4d Merge remote-tracking branch 'origin/master' into 
dev/ng0/gsoc2019
     new e3352886 Merge remote-tracking branch 'origin/master' into 
dev/ng0/gsoc2019
     new 6d1993c5 connection.c: preprocessor ifdef some setsockopts.
     new 4ebff9dc more OLDSOCK.
     new a0588fc0 do use MSG_NOSIGNAL in send() if available
     new 5304f73f gnutls cork integration
     new 066f17f2 reminder
     new ee67443d doxygen
     new 30404c34 Merge branch 'dev/ng0/gsoc2019' of 
git+ssh://gnunet.org/libmicrohttpd into dev/ng0/gsoc2019
     new 6c03b75c doxygen: MHD_SSO
     new b5784559 doxygen, links
     new e87675bf cover the case of TCP_NOPUSH and TCP_CORK coexisting on the 
same OS.
     new 42e98f38 start SENDMSG/WRITEV.
     new 1cc593e3 sendfile and netbsd, comment.
     new 1b013c78 doxygen fix.
     new aad43f23 incomplete commit, adding 2 new helper functions and more.
     new 2525c5bc fix errors.
     new 0b9d7d3b attempt fix.
     new 0c40dc48 move sendfile function work to connection.c
     new 23b35cb8 remove old function name.
     new 7bfe5411 doxygen.
     new 304002d6 remove commented code, replaced by functions.
     new 9a088e23 Add MHD_send_socket_state_cork_nodelay_ and use it.
     new a1a0bf1a function replace.
     new cf1206ff conditionally return and setsockopt.
     new 3d6e8187 lisp sneeks in.
     new 51cfb23f first attempt to add MHD_send_on_connection2_
     new e072dbba Start reworking into generic setsockopt wrapper.
     new e1807005 fix failure to build.
     new eb3035f6 switch functions.
     new d67b93a2 indent
     new db64241b fix
     new 81a47b20 buffersize
     new 9c8c1e7b fix
     new 30c4d657 post_cork function first draft
     new cd41be40 fix regression introduced in cc5032b85
     new eca0f287 Merge branch 'dev/ng0/gsoc2019' of 
git+ssh://gnunet.org/libmicrohttpd into dev/ng0/gsoc2019
     new db800351 revert last patch, should be only on master for now
     new adba1139 Merge branch 'dev/ng0/gsoc2019' of gnunet.org:libmicrohttpd 
into dev/ng0/gsoc2019
     new c2915ac7 pre_cork socket..
     new 5bfd85fc initial move code sendfile.
     new 3dfbaa2a remove code, add prototype to mhd_send.h
     new 9ec1b5df attempt fix
     new 78d30bd5 attempt fix
     new 6a9e9a52 show not tell
     new 9ed35bc8 show not tell
     new 20c16b79 simplify
     new 8a68b235 simplify
     new 16d0ccc4 simplify
     new 497c1d68 simplify
     new 68721add simplify
     new be5ba476 simplify
     new d5c3f00f simplify
     new b76dadc0 remove dead code, comment new code.
     new 5880cd0c incomplete
     new af387e7c don't cast
     new 2c1b1f14 for tests
     new 7a9f4c60 test
     new afe3f5ae skcork
     new 9e8418b9 remove commented code
     new be86a916 switch post/pre function definition to a logical appearance 
in the file (pre before post), purely for orientation.
     new 43ee3a30 test for MGS_MORE existing, not its non-existince.
     new 8567afc5 have_cork->want_cork
     new 1bcbd2c0 declare ret
     new abc998d2 check before post_ function if we need to set want_cork to 
false
     new 02a9ae28 synt
     new 379da4ce configure.ac: define a check for HAVE_SENDMSG
     new 8ce24c2a _len -> _size
     new 654d20bc writev check
     new f17794b5 gitignore build-aux.
     new 45bcf020 ack other authors
     new 57054d04 .
     new 5b83823e Add MSG_MORE detection.
     new 4ab7ea5d attempt configure fix
     new 46829218 configure
     new 919c6d4e MSG_MORE fix
     new 2220c3f7 partial fix to sendmsg code
     new 8c6c3d45 fix behavior on NetBSD, use the right sizeof.
     new 9edfc8f8 mhd_send commented,
     new 8ad5fab2 Merge remote-tracking branch 'origin/master' into 
dev/ng0/gsoc2019
     new b8f3cf79 fixes, comments, FIXMEs
     new 33f02e62 remove commentblock
     new e099a6ea TCP_NOPUSH
     new da7cb682 swap _NODELAY and _NOPUSH
     new c5081fb2 always set nodelay, except if we cannot cork
     new 4b62caea toggle Naggle if and only if corking is not possible by other 
means
     new d21cf2c1 reduce variable scope
     new 8a4aec4d properly handle return value from send_on_connection2
     new 97fd0a65 handle TLS case in send_on_connectin2
     new 26368ac8 add ways for application to control corking for upgraded 
sockets
     new 2754ba51 do it in both tests
     new 68295550 remove obsolete OLD_SOCK if'd blocks.
     new 7d7d55d1 mhd_send: fix failure to build
     new b98ad354 Rename senfile_adapter to MHD_send_sendfile_ and remove 
duplicate prototype.
     new dd81c5f8 Mark function calls which could be removed as dead code.
     new 0359c0bb Document MHD_UPGRADE_ACTION_CORK_ON and 
HD_UPGRADE_ACTION_CORK_OFF.
     new 818b0a79 Add draft of Changelog.
     new 8b1e8793 fix a couple of fixmes: add log messages.
     new bdb2e3ea connection.c: remove dead code
     new 8e06dbed remove more code.
     new f8070814 remove code
     new 5a2872c1 Merge remote-tracking branch 'origin/master' into 
dev/ng0/gsoc2019
     new 86832243 Merge remote-tracking branch 'origin/master' into 
dev/ng0/gsoc2019
     new d72f4184 connection.c: remove 2 more calls of dead code.
     new b8e36827 mhd_send.c: log EINVAL
     new c51869af mhd_send.c: for now, let EINVAL and EBADF fail hard.
     new 39ddaf98 connection.c: remove dead code.
     new 3242129e Revert "mhd_send.c: for now, let EINVAL and EBADF fail hard."
     new e82de750 Revert "connection.c: remove dead code."

The 154 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:
 .gitignore                          |   1 +
 ChangeLog                           |  11 +
 configure.ac                        |  22 ++
 doc/libmicrohttpd.texi              |   4 +
 src/include/microhttpd.h            |  12 +-
 src/microhttpd/Makefile.am          |   1 +
 src/microhttpd/connection.c         | 470 +++++---------------------
 src/microhttpd/connection_https.c   |   2 +-
 src/microhttpd/connection_https.h   |  14 +
 src/microhttpd/daemon.c             |  14 +-
 src/microhttpd/internal.h           |  14 +-
 src/microhttpd/mhd_send.c           | 647 ++++++++++++++++++++++++++++++++++++
 src/microhttpd/mhd_send.h           |  97 ++++++
 src/microhttpd/mhd_sockets.c        |  88 +++--
 src/microhttpd/mhd_sockets.h        |  38 +++
 src/microhttpd/response.c           |  44 ++-
 src/microhttpd/test_upgrade.c       |   2 +
 src/microhttpd/test_upgrade_large.c |   2 +
 18 files changed, 1053 insertions(+), 430 deletions(-)
 create mode 100644 src/microhttpd/mhd_send.c
 create mode 100644 src/microhttpd/mhd_send.h

diff --git a/.gitignore b/.gitignore
index ff6afe00..88f8ab32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ config.rpath
 /config.log
 /app.info
 /debug
+/build-aux
 /exclude
 /autom4te.cache
 /scripts
diff --git a/ChangeLog b/ChangeLog
index 01889a7e..b25b9365 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Fri 18 Aug 2019 00:00:00 PM UTC
+    Fixes and optimizations for the setsockopt handling:
+    * Added: MHD_UPGRADE_ACTION_CORK_ON and MHD_UPGRADE_ACTION_CORK_OFF
+      to enum MHD_UpgradeAction (turn corking on/off on the underlying
+      socket).
+    * Use calls and flags native to the system for corking and
+      other operations, tested with performance improvements on
+      FreeBSD, Debian Linux, NetBSD, and cygwin. In particular,
+      this adds selective usage of MSG_MORE, NODELAY, TCP_NOPUSH,
+      TCP_CORK. -ng0
+
 Fri 09 Aug 2019 10:07:27 AM CEST
     Copy compiler and linker hardening flags from GNUnet (updating
     configure.ac). -CG
diff --git a/configure.ac b/configure.ac
index 086481a2..37ae0fe6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -670,6 +670,28 @@ AM_CONDITIONAL(USE_MS_LIB_TOOL, [test 
"x$ac_cv_use_ms_lib_tool" = "xyes"])
 MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([AC_DEFINE([HAVE_LISTEN_SHUTDOWN],[1],[can 
use shutdown on listen sockets])])
 AM_CONDITIONAL([HAVE_LISTEN_SHUTDOWN], [test 
"x$mhd_cv_host_shtdwn_trgr_select" = "xyes"])
 
+# SENDMSG. Should we check for SCM_RIGHTS instead?
+# https://lists.x.org/archives/xorg-devel/2013-November/038687.html
+AC_MSG_CHECKING([whether sendmsg is available])
+AC_SEARCH_LIBS(sendmsg, socket, AC_DEFINE([HAVE_SENDMSG],1,[Define if your 
platform supports sendmsg]))
+AC_MSG_CHECKING([whether writev is available])
+AC_CHECK_FUNCS([writev])
+
+# check MSG_MORE defined
+AC_MSG_CHECKING([whether MSG_MORE is defined])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+]],[[return MSG_MORE;]]
+    )],
+    [
+      AC_MSG_RESULT(yes)
+      AC_DEFINE(HAVE_MSG_MORE, [1], [have MSG_MORE])
+    ],
+    [
+      AC_MSG_RESULT(no)
+    ])
+
 # set GCC options
 # use '-fno-strict-aliasing', but only if the compiler
 # and linker can take it
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi
index 4424337f..d9d53dd7 100644
--- a/doc/libmicrohttpd.texi
+++ b/doc/libmicrohttpd.texi
@@ -2329,6 +2329,10 @@ Set of actions to be performed on upgraded connections.  
Passed as an argument t
 @table @code
 @item MHD_UPGRADE_ACTION_CLOSE
 Closes the connection.  Must be called once the application is done with the 
client.  Takes no additional arguments.
+@item MHD_UPGRADE_ACTION_CORK_ON
+Enable corking on the underlying socket.
+@item MHD_UPGRADE_ACTION_CORK_OFF
+Disable corking on the underlying socket.
 
 @end table
 @end deftp
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index c2fc90a4..00288696 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -3132,7 +3132,17 @@ enum MHD_UpgradeAction
    *
    * Takes no extra arguments.
    */
-  MHD_UPGRADE_ACTION_CLOSE = 0
+  MHD_UPGRADE_ACTION_CLOSE = 0,
+
+  /**
+   * Enable CORKing on the underlying socket.
+   */
+  MHD_UPGRADE_ACTION_CORK_ON = 1,
+
+  /**
+   * Disable CORKing on the underlying socket.
+   */
+  MHD_UPGRADE_ACTION_CORK_OFF = 2
 
 };
 
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index 8bc60879..597a2d56 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -62,6 +62,7 @@ libmicrohttpd_la_SOURCES = \
   mhd_limits.h mhd_byteorder.h \
   sysfdsetsize.c sysfdsetsize.h \
   mhd_str.c mhd_str.h \
+  mhd_send.h mhd_send.c \
   mhd_assert.h \
   mhd_sockets.c mhd_sockets.h \
   mhd_itc.c mhd_itc.h mhd_itc_types.h \
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 6f33dbc1..ab760e4a 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -53,7 +53,7 @@
 /* For FreeBSD version identification */
 #include <sys/param.h>
 #endif /* HAVE_SYS_PARAM_H */
-
+#include "mhd_send.h"
 
 /**
  * Message to transmit when http 1.1 request is received
@@ -278,187 +278,6 @@ send_param_adapter (struct MHD_Connection *connection,
 }
 
 
-#if defined(_MHD_HAVE_SENDFILE)
-/**
- * Function for sending responses backed by file FD.
- *
- * @param connection the MHD connection structure
- * @return actual number of bytes sent
- */
-static ssize_t
-sendfile_adapter (struct MHD_Connection *connection)
-{
-  ssize_t ret;
-  const int file_fd = connection->response->fd;
-  uint64_t left;
-  uint64_t offsetu64;
-#ifndef HAVE_SENDFILE64
-  const uint64_t max_off_t = (uint64_t)OFF_T_MAX;
-#else  /* HAVE_SENDFILE64 */
-  const uint64_t max_off_t = (uint64_t)OFF64_T_MAX;
-#endif /* HAVE_SENDFILE64 */
-#ifdef MHD_LINUX_SOLARIS_SENDFILE
-#ifndef HAVE_SENDFILE64
-  off_t offset;
-#else  /* HAVE_SENDFILE64 */
-  off64_t offset;
-#endif /* HAVE_SENDFILE64 */
-#endif /* MHD_LINUX_SOLARIS_SENDFILE */
-#ifdef HAVE_FREEBSD_SENDFILE
-  off_t sent_bytes;
-  int flags = 0;
-#endif
-#ifdef HAVE_DARWIN_SENDFILE
-  off_t len;
-#endif /* HAVE_DARWIN_SENDFILE */
-  const bool used_thr_p_c = (0 != (connection->daemon->options & 
MHD_USE_THREAD_PER_CONNECTION));
-  const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : 
MHD_SENFILE_CHUNK_;
-  size_t send_size = 0;
-  mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
-
-  offsetu64 = connection->response_write_position + 
connection->response->fd_off;
-  left = connection->response->total_size - 
connection->response_write_position;
-  /* Do not allow system to stick sending on single fast connection:
-   * use 128KiB chunks (2MiB for thread-per-connection). */
-  send_size = (left > chunk_size) ? chunk_size : (size_t) left;
-  if (max_off_t < offsetu64)
-    { /* Retry to send with standard 'send()'. */
-      connection->resp_sender = MHD_resp_sender_std;
-      return MHD_ERR_AGAIN_;
-    }
-#ifdef MHD_LINUX_SOLARIS_SENDFILE
-#ifndef HAVE_SENDFILE64
-  offset = (off_t) offsetu64;
-  ret = sendfile (connection->socket_fd,
-                  file_fd,
-                  &offset,
-                  send_size);
-#else  /* HAVE_SENDFILE64 */
-  offset = (off64_t) offsetu64;
-  ret = sendfile64 (connection->socket_fd,
-                    file_fd,
-                    &offset,
-                    send_size);
-#endif /* HAVE_SENDFILE64 */
-  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_;
-#ifdef HAVE_LINUX_SENDFILE
-      if (MHD_SCKT_ERR_IS_(err,
-                           MHD_SCKT_EBADF_))
-        return MHD_ERR_BADF_;
-      /* sendfile() failed with EINVAL if mmap()-like operations are not
-         supported for FD or other 'unusual' errors occurred, so we should try
-         to fall back to 'SEND'; see also this thread for info on
-         odd libc/Linux behavior with sendfile:
-         http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html 
*/
-      connection->resp_sender = MHD_resp_sender_std;
-      return MHD_ERR_AGAIN_;
-#else  /* HAVE_SOLARIS_SENDFILE */
-      if ( (EAFNOSUPPORT == err) ||
-           (EINVAL == err) ||
-           (EOPNOTSUPP == err) )
-        { /* Retry with standard file reader. */
-          connection->resp_sender = MHD_resp_sender_std;
-          return MHD_ERR_AGAIN_;
-        }
-      if ( (ENOTCONN == err) ||
-           (EPIPE == err) )
-        {
-          return MHD_ERR_CONNRESET_;
-        }
-      return MHD_ERR_BADF_; /* Fail hard */
-#endif /* HAVE_SOLARIS_SENDFILE */
-    }
-#ifdef EPOLL_SUPPORT
-  else if (send_size > (size_t)ret)
-        connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
-#endif /* EPOLL_SUPPORT */
-#elif defined(HAVE_FREEBSD_SENDFILE)
-#ifdef SF_FLAGS
-  flags = used_thr_p_c ?
-      freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
-#endif /* SF_FLAGS */
-  if (0 != sendfile (file_fd,
-                     connection->socket_fd,
-                     (off_t) offsetu64,
-                     send_size,
-                     NULL,
-                     &sent_bytes,
-                     flags))
-    {
-      const int err = MHD_socket_get_error_();
-      if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
-          MHD_SCKT_ERR_IS_EINTR_(err) ||
-          EBUSY == err)
-        {
-          mhd_assert (SSIZE_MAX >= sent_bytes);
-          if (0 != sent_bytes)
-            return (ssize_t)sent_bytes;
-
-          return MHD_ERR_AGAIN_;
-        }
-      /* Some unrecoverable error. Possibly file FD is not suitable
-       * for sendfile(). Retry with standard send(). */
-      connection->resp_sender = MHD_resp_sender_std;
-      return MHD_ERR_AGAIN_;
-    }
-  mhd_assert (0 < sent_bytes);
-  mhd_assert (SSIZE_MAX >= sent_bytes);
-  ret = (ssize_t)sent_bytes;
-#elif defined(HAVE_DARWIN_SENDFILE)
-  len = (off_t)send_size; /* chunk always fit */
-  if (0 != sendfile (file_fd,
-                     connection->socket_fd,
-                     (off_t) offsetu64,
-                     &len,
-                     NULL,
-                     0))
-    {
-      const int err = MHD_socket_get_error_();
-      if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
-          MHD_SCKT_ERR_IS_EINTR_(err))
-        {
-          mhd_assert (0 <= len);
-          mhd_assert (SSIZE_MAX >= len);
-          mhd_assert (send_size >= (size_t)len);
-          if (0 != len)
-            return (ssize_t)len;
-
-          return MHD_ERR_AGAIN_;
-        }
-      if (ENOTCONN == err ||
-          EPIPE == err)
-        return MHD_ERR_CONNRESET_;
-      if (ENOTSUP == err ||
-          EOPNOTSUPP == err)
-        { /* This file FD is not suitable for sendfile().
-           * Retry with standard send(). */
-          connection->resp_sender = MHD_resp_sender_std;
-          return MHD_ERR_AGAIN_;
-        }
-      return MHD_ERR_BADF_; /* Return hard error. */
-    }
-  mhd_assert (0 <= len);
-  mhd_assert (SSIZE_MAX >= len);
-  mhd_assert (send_size >= (size_t)len);
-  ret = (ssize_t)len;
-#endif /* HAVE_FREEBSD_SENDFILE */
-  return ret;
-}
-#endif /* _MHD_HAVE_SENDFILE */
-
-
 /**
  * Check whether is possible to force push socket buffer content as
  * partial packet.
@@ -501,89 +320,6 @@ _MHD_static_inline bool
 socket_start_extra_buffering (struct MHD_Connection *connection)
 {
   mhd_assert(NULL != connection);
-#if defined(TCP_NODELAY)
-  if (connection->sk_tcp_nodelay_on)
-    {
-      const MHD_SCKT_OPT_BOOL_ off_val = 0;
-      /* Enable Nagle's algorithm */
-      /* TCP_NODELAY may interfere with TCP_NOPUSH */
-      if (0 == setsockopt (connection->socket_fd,
-                           IPPROTO_TCP,
-                           TCP_NODELAY,
-                           (const void *) &off_val,
-                           sizeof (off_val)))
-        {
-          connection->sk_tcp_nodelay_on = false;
-        }
-    }
-#endif /* TCP_NODELAY */
-
-#if defined(MHD_TCP_CORK_NOPUSH)
-  if (!connection->sk_tcp_cork_nopush_on)
-    {
-      const MHD_SCKT_OPT_BOOL_ on_val = 1;
-      /* Buffer data before sending (TCP_CORK) or
-       * Send only full frames (TCP_NOPUSH) */
-      if (0 == setsockopt (connection->socket_fd,
-                           IPPROTO_TCP,
-                           MHD_TCP_CORK_NOPUSH,
-                           (const void *) &on_val,
-                           sizeof (on_val)))
-        {
-          connection->sk_tcp_cork_nopush_on = true;
-        }
-    }
-#endif /* MHD_TCP_CORK_NOPUSH */
-
-#if defined(TCP_NODELAY)
-   return connection->sk_tcp_cork_nopush_on && !connection->sk_tcp_nodelay_on;
-#else  /* ! TCP_NODELAY */
-   return connection->sk_tcp_cork_nopush_on;
-#endif /* ! TCP_NODELAY */
-}
-
-
-/**
- * Activate no buffering mode (no delay sending) on connection socket.
- *
- * @param connection connection to be processed
- * @return true on success, false otherwise
- */
-_MHD_static_inline bool
-socket_start_no_buffering (struct MHD_Connection *connection)
-{
-#if defined(MHD_TCP_CORK_NOPUSH)
-  if (connection->sk_tcp_cork_nopush_on)
-    {
-      const MHD_SCKT_OPT_BOOL_ off_val = 0;
-      /* Disable extra buffering */
-      if (0 == setsockopt (connection->socket_fd,
-                           IPPROTO_TCP,
-                           MHD_TCP_CORK_NOPUSH,
-                           (const void *) &off_val,
-                           sizeof (off_val)))
-        {
-          connection->sk_tcp_cork_nopush_on = false;
-        }
-    }
-#endif /* MHD_TCP_CORK_NOPUSH */
-
-#if defined(TCP_NODELAY)
-  if (!connection->sk_tcp_nodelay_on)
-    {
-      const MHD_SCKT_OPT_BOOL_ on_val = 1;
-      /* Enable sending without delay */
-      if (0 == setsockopt (connection->socket_fd,
-                           IPPROTO_TCP,
-                           TCP_NODELAY,
-                           (const void *) &on_val,
-                           sizeof (on_val)))
-        {
-          connection->sk_tcp_nodelay_on = true;
-        }
-    }
-#endif /* TCP_NODELAY */
-  return connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on;
 }
 
 
@@ -601,21 +337,7 @@ socket_start_no_buffering_flush (struct MHD_Connection 
*connection)
 #if defined(TCP_NOPUSH) && !defined(TCP_CORK)
   const int dummy = 0;
 #endif /* !TCP_CORK */
-#if defined(TCP_CORK) || (defined(__FreeBSD__) &&  __FreeBSD__+0 >= 9)
-  const MHD_SCKT_OPT_BOOL_ off_val = 0;
-  /* Switching off TCP_CORK flush buffer even
-   * if TCP_CORK was not enabled */
-  if (0 == setsockopt (connection->socket_fd,
-                       IPPROTO_TCP,
-                       MHD_TCP_CORK_NOPUSH,
-                       (const void *) &off_val,
-                       sizeof (off_val)))
-    {
-      connection->sk_tcp_cork_nopush_on = false;
-    }
-#endif /* MHD_TCP_CORK_NOPUSH */
 
-  res = socket_start_no_buffering (connection);
 #if defined(__FreeBSD__) &&  __FreeBSD__+0 >= 9
   /* FreeBSD do not need zero-send for flushing starting from version 9 */
 #elif defined(TCP_NOPUSH) && !defined(TCP_CORK)
@@ -630,51 +352,6 @@ socket_start_no_buffering_flush (struct MHD_Connection 
*connection)
 }
 
 
-/**
- * Activate normal buffering mode on connection socket.
- *
- * @param connection connection to be processed
- * @return true on success, false otherwise
- */
-_MHD_static_inline bool
-socket_start_normal_buffering (struct MHD_Connection *connection)
-{
-  mhd_assert(NULL != connection);
-#if defined(MHD_TCP_CORK_NOPUSH)
-  if (connection->sk_tcp_cork_nopush_on)
-    {
-      const MHD_SCKT_OPT_BOOL_ off_val = 0;
-      /* Disable extra buffering */
-      if (0 == setsockopt (connection->socket_fd,
-                           IPPROTO_TCP,
-                           MHD_TCP_CORK_NOPUSH,
-                           (const void *) &off_val,
-                           sizeof (off_val)))
-        {
-          connection->sk_tcp_cork_nopush_on = false;
-        }
-    }
-#endif /* MHD_TCP_CORK_NOPUSH */
-
-#if defined(TCP_NODELAY)
-  if (connection->sk_tcp_nodelay_on)
-    {
-      const MHD_SCKT_OPT_BOOL_ off_val = 0;
-      /* Enable Nagle's algorithm */
-      if (0 == setsockopt (connection->socket_fd,
-                           IPPROTO_TCP,
-                           TCP_NODELAY,
-                           (const void *) &off_val,
-                           sizeof (off_val)))
-        {
-          connection->sk_tcp_nodelay_on = false;
-        }
-    }
-#endif /* TCP_NODELAY */
-  return !connection->sk_tcp_nodelay_on && !connection->sk_tcp_cork_nopush_on;
-}
-
-
 /**
  * Get all of the headers from the request.
  *
@@ -3315,11 +2992,12 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
     case MHD_CONNECTION_HEADERS_PROCESSED:
       return;
     case MHD_CONNECTION_CONTINUE_SENDING:
-      ret = connection->send_cls (connection,
-                                  &HTTP_100_CONTINUE
-                                  [connection->continue_message_write_offset],
-                                  MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE) -
-                                  connection->continue_message_write_offset);
+      ret = MHD_send_on_connection_ (connection,
+                                     &HTTP_100_CONTINUE
+                                     
[connection->continue_message_write_offset],
+                                     MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE) -
+                                     connection->continue_message_write_offset,
+                                     MHD_SSO_NO_CORK);
       if (ret < 0)
         {
           if (MHD_ERR_AGAIN_ == ret)
@@ -3349,26 +3027,55 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
       mhd_assert (0);
       return;
     case MHD_CONNECTION_HEADERS_SENDING:
-      ret = connection->send_cls (connection,
-                                  &connection->write_buffer
-                                  [connection->write_buffer_send_offset],
-                                  connection->write_buffer_append_offset -
-                                    connection->write_buffer_send_offset);
-      if (ret < 0)
-        {
-          if (MHD_ERR_AGAIN_ == ret)
+      {
+        const size_t wb_ready = connection->write_buffer_append_offset -
+          connection->write_buffer_send_offset;
+
+        /* if the response body is not available, we use 
MHD_send_on_connection_() */
+        if (NULL != connection->response->crc)
+          {
+            ret = MHD_send_on_connection_ (connection,
+                                           &connection->write_buffer
+                                           
[connection->write_buffer_send_offset],
+                                           wb_ready,
+                                           MHD_SSO_MAY_CORK);
+          }
+        else
+          {
+            ret = MHD_send_on_connection2_ (connection,
+                                            &connection->write_buffer
+                                            
[connection->write_buffer_send_offset],
+                                            wb_ready,
+                                            connection->response->data,
+                                            
connection->response->data_buffer_size);
+          }
+
+        if (ret < 0)
+          {
+            if (MHD_ERR_AGAIN_ == ret)
+              return;
+            CONNECTION_CLOSE_ERROR (connection,
+                                    _("Connection was closed while sending 
response headers.\n"));
             return;
-          CONNECTION_CLOSE_ERROR (connection,
-                                  _("Connection was closed while sending 
response headers.\n"));
+          }
+        if (ret > wb_ready)
+          {
+            mhd_assert (NULL == connection->repsonse->crc);
+            /* We sent not just header data but also some response data,
+               update both offsets! */
+            connection->write_buffer_send_offset += wb_ready;
+            ret -= wb_ready;
+            connection->response_write_position += ret;
+          }
+        else
+          connection->write_buffer_send_offset += ret;
+        MHD_update_last_activity_ (connection);
+        if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
           return;
-        }
-      connection->write_buffer_send_offset += ret;
-      MHD_update_last_activity_ (connection);
-      if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
+        check_write_done (connection,
+                          MHD_CONNECTION_HEADERS_SENT);
         return;
-      check_write_done (connection,
-                        MHD_CONNECTION_HEADERS_SENT);
-      return;
+      }
     case MHD_CONNECTION_HEADERS_SENT:
       return;
     case MHD_CONNECTION_NORMAL_BODY_READY:
@@ -3390,7 +3097,7 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
 #if defined(_MHD_HAVE_SENDFILE)
           if (MHD_resp_sender_sendfile == connection->resp_sender)
             {
-              ret = sendfile_adapter (connection);
+              ret = MHD_send_sendfile_ (connection);
             }
           else
 #else  /* ! _MHD_HAVE_SENDFILE */
@@ -3401,11 +3108,12 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
                                   - response->data_start;
               if (data_write_offset > (uint64_t)SIZE_MAX)
                 MHD_PANIC (_("Data offset exceeds limit"));
-              ret = connection->send_cls (connection,
-                                          &response->data
-                                          [(size_t)data_write_offset],
-                                          response->data_size -
-                                          (size_t)data_write_offset);
+              ret = MHD_send_on_connection_ (connection,
+                                             &response->data
+                                             [(size_t)data_write_offset],
+                                             response->data_size -
+                                             (size_t)data_write_offset,
+                                             MHD_SSO_NO_CORK);
 #if DEBUG_SEND_DATA
               if (ret > 0)
                 fprintf (stderr,
@@ -3444,11 +3152,12 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
       mhd_assert (0);
       return;
     case MHD_CONNECTION_CHUNKED_BODY_READY:
-      ret = connection->send_cls (connection,
-                                  &connection->write_buffer
-                                  [connection->write_buffer_send_offset],
-                                  connection->write_buffer_append_offset -
-                                    connection->write_buffer_send_offset);
+      ret = MHD_send_on_connection_ (connection,
+                                     &connection->write_buffer
+                                     [connection->write_buffer_send_offset],
+                                     connection->write_buffer_append_offset -
+                                     connection->write_buffer_send_offset,
+                                     MHD_SSO_NO_CORK);
       if (ret < 0)
         {
           if (MHD_ERR_AGAIN_ == ret)
@@ -3472,11 +3181,12 @@ MHD_connection_handle_write (struct MHD_Connection 
*connection)
       mhd_assert (0);
       return;
     case MHD_CONNECTION_FOOTERS_SENDING:
-      ret = connection->send_cls (connection,
-                                  &connection->write_buffer
-                                  [connection->write_buffer_send_offset],
-                                  connection->write_buffer_append_offset -
-                                    connection->write_buffer_send_offset);
+      ret = MHD_send_on_connection_ (connection,
+                                     &connection->write_buffer
+                                     [connection->write_buffer_send_offset],
+                                     connection->write_buffer_append_offset -
+                                     connection->write_buffer_send_offset,
+                                     MHD_SSO_HDR_CORK);
       if (ret < 0)
         {
           if (MHD_ERR_AGAIN_ == ret)
@@ -3723,11 +3433,6 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
           if (need_100_continue (connection))
             {
               connection->state = MHD_CONNECTION_CONTINUE_SENDING;
-              if (socket_flush_possible (connection))
-                socket_start_extra_buffering (connection);
-              else
-                socket_start_no_buffering (connection);
-
               break;
             }
           if ( (NULL != connection->response) &&
@@ -3751,11 +3456,6 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
               MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE))
             {
               connection->state = MHD_CONNECTION_CONTINUE_SENT;
-              if (socket_flush_possible (connection))
-                socket_start_no_buffering_flush (connection);
-              else
-                socket_start_normal_buffering (connection);
-
               continue;
             }
           break;
@@ -3855,11 +3555,6 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
               continue;
             }
           connection->state = MHD_CONNECTION_HEADERS_SENDING;
-          if (socket_flush_possible (connection))
-            socket_start_extra_buffering (connection);
-          else
-            socket_start_no_buffering (connection);
-
           break;
         case MHD_CONNECTION_HEADERS_SENDING:
           /* no default action */
@@ -3867,12 +3562,11 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
         case MHD_CONNECTION_HEADERS_SENT:
           /* Some clients may take some actions right after header receive */
           if (socket_flush_possible (connection))
-            socket_start_no_buffering_flush (connection);
+            socket_start_no_buffering_flush (connection); /* REMOVE: Dead */
 
 #ifdef UPGRADE_SUPPORT
           if (NULL != connection->response->upgrade_handler)
             {
-              socket_start_normal_buffering (connection);
               connection->state = MHD_CONNECTION_UPGRADE;
               /* This connection is "upgraded".  Pass socket to application. */
               if (MHD_YES !=
@@ -3894,10 +3588,6 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
               continue;
             }
 #endif /* UPGRADE_SUPPORT */
-          if (socket_flush_possible (connection))
-            socket_start_extra_buffering (connection);
-          else
-            socket_start_normal_buffering (connection);
 
           if (connection->have_chunked_upload)
             connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
@@ -3929,8 +3619,7 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
 #endif
               connection->state = MHD_CONNECTION_NORMAL_BODY_READY;
               /* Buffering for flushable socket was already enabled*/
-              if (socket_flush_possible (connection))
-                socket_start_no_buffering (connection);
+
               break;
             }
           /* mutex was already unlocked by "try_ready_normal_body */
@@ -3963,8 +3652,7 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
 #endif
               connection->state = MHD_CONNECTION_CHUNKED_BODY_READY;
               /* Buffering for flushable socket was already enabled */
-              if (socket_flush_possible (connection))
-                socket_start_no_buffering (connection);
+
               continue;
             }
           /* mutex was already unlocked by try_ready_chunked_body */
@@ -3997,11 +3685,6 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
            /* FIXME: maybe partially reset memory pool? */
            continue;
          }
-          if (socket_flush_possible (connection))
-            socket_start_no_buffering_flush (connection);
-          else
-            socket_start_normal_buffering (connection);
-
           MHD_destroy_response (connection->response);
           connection->response = NULL;
           if ( (NULL != daemon->notify_completed) &&
@@ -4028,8 +3711,7 @@ MHD_connection_handle_idle (struct MHD_Connection 
*connection)
           else
             {
               /* can try to keep-alive */
-              if (socket_flush_possible (connection))
-                socket_start_normal_buffering (connection);
+
               connection->version = NULL;
               connection->state = MHD_CONNECTION_INIT;
               connection->last = NULL;
diff --git a/src/microhttpd/connection_https.c 
b/src/microhttpd/connection_https.c
index 5efced33..8202329b 100644
--- a/src/microhttpd/connection_https.c
+++ b/src/microhttpd/connection_https.c
@@ -98,7 +98,7 @@ recv_tls_adapter (struct MHD_Connection *connection,
  * @return positive value for number of bytes actually sent or
  *         negative value for error number MHD_ERR_xxx_
  */
-static ssize_t
+ssize_t
 send_tls_adapter (struct MHD_Connection *connection,
                   const void *other,
                   size_t i)
diff --git a/src/microhttpd/connection_https.h 
b/src/microhttpd/connection_https.h
index 1c12ea9f..e91a84d3 100644
--- a/src/microhttpd/connection_https.h
+++ b/src/microhttpd/connection_https.h
@@ -60,6 +60,20 @@ MHD_run_tls_handshake_ (struct MHD_Connection *connection);
  */
 bool
 MHD_tls_connection_shutdown (struct MHD_Connection *connection);
+
+/**
+ * Callback for writing data to the socket.
+ *
+ * @param connection the MHD connection structure
+ * @param other data to write
+ * @param i number of bytes to write
+ * @return positive value for number of bytes actually sent or
+ *         negative value for error number MHD_ERR_xxx_
+ */
+ssize_t
+send_tls_adapter (struct MHD_Connection *connection,
+                  const void *other,
+                  size_t i);
 #endif /* HTTPS_SUPPORT */
 
 #endif
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index a8fc98c6..fb4abd5d 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -3229,6 +3229,19 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
         }
       return MHD_NO;
     }
+#if defined(MHD_TCP_CORK_NOPUSH) || defined(HAVE_MSG_MORE)
+  /* We will use TCP_CORK or TCP_NOPUSH or MSG_MORE to control
+     transmission, disable Nagle's algorithm (always) */
+  if (0 != MHD_socket_set_nodelay_ (s,
+                                    true))
+    {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("Failed to disable TCP Nagle on socket: %s\n"),
+                MHD_socket_last_strerr_());
+    }
+#endif
+#endif
 #if !defined(USE_ACCEPT4) || !defined(HAVE_SOCK_NONBLOCK)
   if (! MHD_socket_nonblocking_ (s))
     {
@@ -6228,7 +6241,6 @@ MHD_start_daemon_va (unsigned int flags,
         }
     }
 #endif /* HAVE_GETSOCKNAME */
-
   if ( (MHD_INVALID_SOCKET != listen_fd) &&
        (! MHD_socket_nonblocking_ (listen_fd)) )
     {
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 1f5aeaf3..ac43d819 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -871,19 +871,13 @@ struct MHD_Connection
   /**
    * true if #socket_fd is non-blocking, false otherwise.
    */
-  bool sk_nonblck;
+  bool sk_nonblck; // FIXME: hopefully dead?
 
   /**
-   * Indicate whether connection socket has TCP_NODELAY turned on / Nagle’s 
algorithm turned off.
-   * TCP_NODELAY should not be turned on when TCP_CORK/TCP_NOPUSH is turned 
off.
+   * Indicate whether connection socket has TCP_CORK / Nagle’s algorithm 
turned on/off
+   * on this socket.
    */
-  bool sk_tcp_nodelay_on;
-
-  /**
-   * Indicate whether connection socket has TCP_CORK/TCP_NOPUSH turned on.
-   * TCP_CORK/TCP_NOPUSH should not be turned on when TCP_NODELAY is turned 
off.
-   */
-  bool sk_tcp_cork_nopush_on;
+  bool sk_cork_on;
 
   /**
    * Has this socket been closed for reading (i.e.  other side closed
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c
new file mode 100644
index 00000000..b3fb25da
--- /dev/null
+++ b/src/microhttpd/mhd_send.c
@@ -0,0 +1,647 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2019 ng0 <address@hidden>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+
+ */
+
+/**
+ * @file microhttpd/mhd_send.c
+ * @brief Implementation of send() wrappers.
+ * @author ng0 (N. Gillmann)
+ * @author Christian Grothoff
+ * @author Evgeny Grin
+ */
+
+/* Worth considering for future improvements and additions:
+ * NetBSD has no sendfile or sendfile64. The way to work
+ * with this seems to be to mmap the file and write(2) as
+ * large a chunk as possible to the socket. Alternatively,
+ * use madvise(..., MADV_SEQUENTIAL). */
+
+/* Functions to be used in: send_param_adapter, MHD_send_
+ * and every place where sendfile(), sendfile64(), setsockopt()
+ * are used. */
+
+#include "mhd_send.h"
+
+/**
+ * Handle setsockopt calls.
+ *
+ * @param connection the MHD_Connection structure
+ * @param want_cork cork state, boolean
+ */
+static void
+pre_cork_setsockopt (struct MHD_Connection *connection,
+                     bool want_cork)
+{
+#if HAVE_MSG_MORE
+  /* We use the MSG_MORE option for corking, no need for extra syscalls! */
+#elif defined(MHD_TCP_CORK_NOPUSH)
+  int ret;
+
+  /* If sk_cork_on is already what we pass in, return. */
+  if (connection->sk_cork_on == want_cork)
+    {
+      /* nothing to do, success! */
+      return;
+    }
+  if (! want_cork)
+    return; /* nothing to do *pre* syscall! */
+  ret = MHD_socket_cork_ (connection->socket_fd,
+                          true);
+  if (0 == ret)
+    {
+      connection->sk_cork_on = true;
+      return;
+    }
+  switch (errno)
+    {
+    case ENOTSOCK:
+      /* FIXME: Could be we are talking to a pipe, maybe remember this
+         and avoid all setsockopt() in the future? */
+      break;
+    case EBADF:
+      /* FIXME: should we die hard here? */
+      break;
+    case EINVAL:
+      /* FIXME: optlen invalid, should at least log this, maybe die */
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("optlen invalid: %s\n"),
+                MHD_socket_last_strerr_());
+#endif
+      break;
+    case EFAULT:
+      /* wopsie, should at leats log this, FIXME: maybe die */
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("The addresss pointed to by optval is not a valid part of 
the process address space: %s\n"),
+                MHD_socket_last_strerr_());
+#endif
+      break;
+    case ENOPROTOOPT:
+      /* optlen unknown, should at least log this */
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("The option is unknown: %s\n"),
+                MHD_socket_last_strerr_());
+#endif
+      break;
+    default:
+      /* any others? man page does not list more... */
+      break;
+    }
+#else
+  /* CORK/NOPUSH/MSG_MORE do not exist on this platform,
+     so we must toggle Naggle's algorithm on/off instead
+     (otherwise we keep it always off) */
+  if (connection->sk_cork_on == want_cork)
+    {
+      /* nothing to do, success! */
+      return;
+    }
+  if ( (want_cork) &&
+       (0 == MHD_socket_set_nodelay_ (connection->socket_fd,
+                                     false)) )
+    connection->sk_cork_on = true;
+#endif
+}
+
+
+/**
+ * Handle setsockopt calls.
+ *
+ * @param connection the MHD_Connection structure
+ * @param want_cork cork state, boolean
+ */
+static void
+post_cork_setsockopt (struct MHD_Connection *connection,
+                      bool want_cork)
+{
+#if HAVE_MSG_MORE
+  /* We use the MSG_MORE option for corking, no need for extra syscalls! */
+#elif defined(MHD_TCP_CORK_NOPUSH)
+  int ret;
+
+  /* If sk_cork_on is already what we pass in, return. */
+  if (connection->sk_cork_on == want_cork)
+    {
+      /* nothing to do, success! */
+      return;
+    }
+  if (want_cork)
+    return; /* nothing to do *post* syscall (in fact, we should never
+               get here, as sk_cork_on should have succeeded in the
+               pre-syscall) */
+  ret = MHD_socket_cork_ (connection->socket_fd,
+                          false);
+  if (0 == ret)
+    {
+      connection->sk_cork_on = false;
+      return;
+    }
+  switch (errno)
+    {
+    case ENOTSOCK:
+      /* FIXME: Could be we are talking to a pipe, maybe remember this
+         and avoid all setsockopt() in the future? */
+      break;
+    case EBADF:
+      /* FIXME: should we die hard here? */
+      break;
+    case EINVAL:
+      /* FIXME: optlen invalid, should at least log this, maybe die */
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("optlen invalid: %s\n"),
+                MHD_socket_last_strerr_());
+#endif
+      break;
+    case EFAULT:
+      /* wopsie, should at leats log this, FIXME: maybe die */
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("The addresss pointed to by optval is not a valid part of 
the process address space: %s\n"),
+                MHD_socket_last_strerr_());
+#endif
+      break;
+    case ENOPROTOOPT:
+      /* optlen unknown, should at least log this */
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                _("The option is unknown: %s\n"),
+                MHD_socket_last_strerr_());
+#endif
+      break;
+    default:
+      /* any others? man page does not list more... */
+      break;
+    }
+#else
+  /* CORK/NOPUSH/MSG_MORE do not exist on this platform,
+     so we must toggle Naggle's algorithm on/off instead
+     (otherwise we keep it always off) */
+  if (connection->sk_cork_on == want_cork)
+    {
+      /* nothing to do, success! */
+      return;
+    }
+  if ( (! want_cork) &&
+       (0 == MHD_socket_set_nodelay_ (connection->socket_fd,
+                                     true)) )
+    connection->sk_cork_on = false;
+#endif
+}
+
+
+/**
+ * Send buffer on connection, and remember the current state of
+ * the socket options; only call setsockopt when absolutely
+ * necessary.
+ *
+ * @param connection the MHD_Connection structure
+ * @param buffer content of the buffer to send
+ * @param buffer_size the size of the buffer (in bytes)
+ * @param options the #MHD_SendSocketOptions enum,
+ *         #MHD_SSO_NO_CORK: definitely no corking (use NODELAY, or explicitly 
disable cork),
+ *         #MHD_SSO_MAY_CORK: should enable corking (use MSG_MORE, or 
explicitly enable cork),
+ *         #MHD_SSO_HDR_CORK: consider tcpi_snd_mss and consider not corking 
for the header
+ *         part if the size of the header is close to the MSS.
+ *         Only used if we are NOT doing 100 Continue and are still sending the
+ *         header (provided in full as the buffer to #MHD_send_on_connection_ 
or as
+ *         the header to #MHD_send_on_connection2_).
+ * @return sum of the number of bytes sent from both buffers or
+ *         -1 on error
+ */
+ssize_t
+MHD_send_on_connection_ (struct MHD_Connection *connection,
+                         const char *buffer,
+                         size_t buffer_size,
+                         enum MHD_SendSocketOptions options)
+{
+  bool want_cork;
+  MHD_socket s = connection->socket_fd;
+  ssize_t ret;
+
+  /* error handling from send_param_adapter() */
+  if ((MHD_INVALID_SOCKET == s) || (MHD_CONNECTION_CLOSED == 
connection->state))
+  {
+    return MHD_ERR_NOTCONN_;
+  }
+
+  /* from send_param_adapter() */
+  if (buffer_size > MHD_SCKT_SEND_MAX_SIZE_)
+    buffer_size = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */
+
+  /* Get socket options, change/set options if necessary. */
+  switch (options)
+  {
+  /* No corking */
+  case MHD_SSO_NO_CORK:
+    want_cork = false;
+    break;
+  /* Do corking, consider MSG_MORE instead if available. */
+  case MHD_SSO_MAY_CORK:
+    want_cork = true;
+    break;
+  /* Cork the header. */
+  case MHD_SSO_HDR_CORK:
+    want_cork = (buffer_size <= 1024);
+    break;
+  }
+
+#ifdef HTTPS_SUPPORT
+  if (0 != (connection->daemon->options & MHD_USE_TLS))
+  {
+    bool have_cork = connection->sk_cork_on;
+
+    if (want_cork && ! have_cork)
+    {
+      gnutls_record_cork (connection->tls_session);
+      connection->sk_cork_on = true;
+    }
+    if (buffer_size > SSIZE_MAX)
+      buffer_size = SSIZE_MAX;
+    ret = gnutls_record_send (connection->tls_session,
+                              buffer,
+                              buffer_size);
+    if ( (GNUTLS_E_AGAIN == ret) ||
+         (GNUTLS_E_INTERRUPTED == ret) )
+    {
+#ifdef EPOLL_SUPPORT
+      if (GNUTLS_E_AGAIN == ret)
+        connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+#endif
+      return MHD_ERR_AGAIN_;
+    }
+    if (ret < 0)
+    {
+      /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
+         disrupted); interpret as a hard error */
+      return MHD_ERR_NOTCONN_;
+    }
+#ifdef EPOLL_SUPPORT
+    /* Unlike non-TLS connections, do not reset "write-ready" if
+     * sent amount smaller than provided amount, as TLS
+     * connections may break data into smaller parts for sending. */
+#endif /* EPOLL_SUPPORT */
+
+    if (! want_cork && have_cork)
+    {
+      (void) gnutls_record_uncork (connection->tls_session, 0);
+      connection->sk_cork_on = false;
+    }
+  }
+  else
+#endif /* HTTPS_SUPPORT  */
+  {
+    /* plaintext transmission */
+    pre_cork_setsockopt (connection, want_cork);
+#if HAVE_MSG_MORE
+    ret = send (s,
+                buffer,
+                buffer_size,
+                MAYBE_MSG_NOSIGNAL | (want_cork ? MSG_MORE : 0));
+#else
+    ret = send (connection->socket_fd,
+                buffer,
+                buffer_size,
+                MAYBE_MSG_NOSIGNAL);
+#endif
+
+    if (0 > ret)
+    {
+      const int err = MHD_socket_get_error_ ();
+
+      if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
+      {
+#if 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_;
+    }
+#if EPOLL_SUPPORT
+    else if (buffer_size > (size_t) ret)
+      connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+#endif /* EPOLL_SUPPORT */
+    if (ret == buffer_size)
+      post_cork_setsockopt (connection, want_cork);
+  }
+
+  return ret;
+}
+
+
+/**
+ * Send header followed by buffer on connection.
+ * Uses writev if possible to send both at once
+ * and returns the sum of the number of bytes sent from
+ * both buffers, or -1 on error;
+ * if writev is unavailable, this call MUST only send from 'header'
+ * (as we cannot handle the case that the first write
+ * succeeds and the 2nd fails!).
+ *
+ * @param connection the MHD_Connection structure
+ * @param header content of header to send
+ * @param header_size the size of the header (in bytes)
+ * @param buffer content of the buffer to send
+ * @param buffer_size the size of the buffer (in bytes)
+ * @return sum of the number of bytes sent from both buffers or
+ *         -1 on error
+ */
+ssize_t
+MHD_send_on_connection2_ (struct MHD_Connection *connection,
+                          const char *header,
+                          size_t header_size,
+                          const char *buffer,
+                          size_t buffer_size)
+{
+#ifdef HTTPS_SUPPORT
+  if (0 != (connection->daemon->options & MHD_USE_TLS))
+    return MHD_send_on_connection_ (connection,
+                                    header,
+                                    header_size,
+                                    MHD_SSO_HDR_CORK);
+#endif
+#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
+  MHD_socket s = connection->socket_fd;
+  ssize_t ret;
+  struct iovec vector[2];
+
+  /* Since we generally give the fully answer, we do not want
+     corking to happen */
+  pre_cork_setsockopt (connection, false);
+
+  vector[0].iov_base = (void *) header;
+  vector[0].iov_len = header_size;
+  vector[1].iov_base = (void *) buffer;
+  vector[1].iov_len = buffer_size;
+
+#if HAVE_SENDMSG
+  {
+    struct msghdr msg;
+
+    memset(&msg, 0, sizeof(struct msghdr));
+    msg.msg_iov = vector;
+    msg.msg_iovlen = 2;
+
+    ret = sendmsg (s, &msg, MAYBE_MSG_NOSIGNAL);
+  }
+#elif HAVE_WRITEV
+  {
+    int iovcnt;
+
+    iovcnt = sizeof (vector) / sizeof (struct iovec);
+    ret = writev (s, vector, iovcnt);
+  }
+#endif
+
+  /* Only if we succeeded sending the full buffer, we need to make sure that
+     the OS flushes at the end */
+  if (ret == header_size + buffer_size)
+    post_cork_setsockopt (connection, false);
+
+  return ret;
+
+#else
+  return MHD_send_on_connection_ (connection,
+                                  header,
+                                  header_size,
+                                  MHD_SSO_HDR_CORK);
+#endif
+}
+
+/**
+ * sendfile() chuck size
+ */
+#define MHD_SENFILE_CHUNK_         (0x20000)
+
+/**
+ * sendfile() chuck size for thread-per-connection
+ */
+#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
+
+#ifdef HAVE_FREEBSD_SENDFILE
+#ifdef SF_FLAGS
+/**
+ * FreeBSD sendfile() flags
+ */
+static int freebsd_sendfile_flags_;
+
+/**
+ * FreeBSD sendfile() flags for thread-per-connection
+ */
+static int freebsd_sendfile_flags_thd_p_c_;
+#endif /* SF_FLAGS */
+
+#endif /* HAVE_FREEBSD_SENDFILE */
+
+#if defined(_MHD_HAVE_SENDFILE)
+/**
+ * Function for sending responses backed by file FD.
+ *
+ * @param connection the MHD connection structure
+ * @return actual number of bytes sent
+ */
+ssize_t
+MHD_send_sendfile_ (struct MHD_Connection *connection)
+{
+  ssize_t ret;
+  const int file_fd = connection->response->fd;
+  uint64_t left;
+  uint64_t offsetu64;
+#ifndef HAVE_SENDFILE64
+  const uint64_t max_off_t = (uint64_t)OFF_T_MAX;
+#else  /* HAVE_SENDFILE64 */
+  const uint64_t max_off_t = (uint64_t)OFF64_T_MAX;
+#endif /* HAVE_SENDFILE64 */
+#ifdef MHD_LINUX_SOLARIS_SENDFILE
+#ifndef HAVE_SENDFILE64
+  off_t offset;
+#else  /* HAVE_SENDFILE64 */
+  off64_t offset;
+#endif /* HAVE_SENDFILE64 */
+#endif /* MHD_LINUX_SOLARIS_SENDFILE */
+#ifdef HAVE_FREEBSD_SENDFILE
+  off_t sent_bytes;
+  int flags = 0;
+#endif
+#ifdef HAVE_DARWIN_SENDFILE
+  off_t len;
+#endif /* HAVE_DARWIN_SENDFILE */
+  const bool used_thr_p_c = (0 != (connection->daemon->options & 
MHD_USE_THREAD_PER_CONNECTION));
+  const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : 
MHD_SENFILE_CHUNK_;
+  size_t send_size = 0;
+  mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
+
+  pre_cork_setsockopt (connection, false);
+
+  offsetu64 = connection->response_write_position + 
connection->response->fd_off;
+  left = connection->response->total_size - 
connection->response_write_position;
+  /* Do not allow system to stick sending on single fast connection:
+   * use 128KiB chunks (2MiB for thread-per-connection). */
+  send_size = (left > chunk_size) ? chunk_size : (size_t) left;
+  if (max_off_t < offsetu64)
+    { /* Retry to send with standard 'send()'. */
+      connection->resp_sender = MHD_resp_sender_std;
+      return MHD_ERR_AGAIN_;
+    }
+#ifdef MHD_LINUX_SOLARIS_SENDFILE
+#ifndef HAVE_SENDFILE64
+  offset = (off_t) offsetu64;
+  ret = sendfile (connection->socket_fd,
+                  file_fd,
+                  &offset,
+                  send_size);
+#else  /* HAVE_SENDFILE64 */
+  offset = (off64_t) offsetu64;
+  ret = sendfile64 (connection->socket_fd,
+                    file_fd,
+                    &offset,
+                    send_size);
+#endif /* HAVE_SENDFILE64 */
+  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_;
+#ifdef HAVE_LINUX_SENDFILE
+      if (MHD_SCKT_ERR_IS_(err,
+                           MHD_SCKT_EBADF_))
+        return MHD_ERR_BADF_;
+      /* sendfile() failed with EINVAL if mmap()-like operations are not
+         supported for FD or other 'unusual' errors occurred, so we should try
+         to fall back to 'SEND'; see also this thread for info on
+         odd libc/Linux behavior with sendfile:
+         http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html 
*/
+      connection->resp_sender = MHD_resp_sender_std;
+      return MHD_ERR_AGAIN_;
+#else  /* HAVE_SOLARIS_SENDFILE */
+      if ( (EAFNOSUPPORT == err) ||
+           (EINVAL == err) ||
+           (EOPNOTSUPP == err) )
+        { /* Retry with standard file reader. */
+          connection->resp_sender = MHD_resp_sender_std;
+          return MHD_ERR_AGAIN_;
+        }
+      if ( (ENOTCONN == err) ||
+           (EPIPE == err) )
+        {
+          return MHD_ERR_CONNRESET_;
+        }
+      return MHD_ERR_BADF_; /* Fail hard */
+#endif /* HAVE_SOLARIS_SENDFILE */
+    }
+#ifdef EPOLL_SUPPORT
+  else if (send_size > (size_t)ret)
+        connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+#endif /* EPOLL_SUPPORT */
+#elif defined(HAVE_FREEBSD_SENDFILE)
+#ifdef SF_FLAGS
+  flags = used_thr_p_c ?
+      freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
+#endif /* SF_FLAGS */
+  if (0 != sendfile (file_fd,
+                     connection->socket_fd,
+                     (off_t) offsetu64,
+                     send_size,
+                     NULL,
+                     &sent_bytes,
+                     flags))
+    {
+      const int err = MHD_socket_get_error_();
+      if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
+          MHD_SCKT_ERR_IS_EINTR_(err) ||
+          EBUSY == err)
+        {
+          mhd_assert (SSIZE_MAX >= sent_bytes);
+          if (0 != sent_bytes)
+            return (ssize_t)sent_bytes;
+
+          return MHD_ERR_AGAIN_;
+        }
+      /* Some unrecoverable error. Possibly file FD is not suitable
+       * for sendfile(). Retry with standard send(). */
+      connection->resp_sender = MHD_resp_sender_std;
+      return MHD_ERR_AGAIN_;
+    }
+  mhd_assert (0 < sent_bytes);
+  mhd_assert (SSIZE_MAX >= sent_bytes);
+  ret = (ssize_t)sent_bytes;
+#elif defined(HAVE_DARWIN_SENDFILE)
+  len = (off_t)send_size; /* chunk always fit */
+  if (0 != sendfile (file_fd,
+                     connection->socket_fd,
+                     (off_t) offsetu64,
+                     &len,
+                     NULL,
+                     0))
+    {
+      const int err = MHD_socket_get_error_();
+      if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
+          MHD_SCKT_ERR_IS_EINTR_(err))
+        {
+          mhd_assert (0 <= len);
+          mhd_assert (SSIZE_MAX >= len);
+          mhd_assert (send_size >= (size_t)len);
+          if (0 != len)
+            return (ssize_t)len;
+
+          return MHD_ERR_AGAIN_;
+        }
+      if (ENOTCONN == err ||
+          EPIPE == err)
+        return MHD_ERR_CONNRESET_;
+      if (ENOTSUP == err ||
+          EOPNOTSUPP == err)
+        { /* This file FD is not suitable for sendfile().
+           * Retry with standard send(). */
+          connection->resp_sender = MHD_resp_sender_std;
+          return MHD_ERR_AGAIN_;
+        }
+      return MHD_ERR_BADF_; /* Return hard error. */
+    }
+  mhd_assert (0 <= len);
+  mhd_assert (SSIZE_MAX >= len);
+  mhd_assert (send_size >= (size_t)len);
+  ret = (ssize_t)len;
+#endif /* HAVE_FREEBSD_SENDFILE */
+
+  /* Make sure we send the data without delay ONLY if we
+     provided the complete response (not on partial write) */
+  if (ret == left)
+    post_cork_setsockopt (connection, false);
+
+  return ret;
+}
+#endif /* _MHD_HAVE_SENDFILE */
diff --git a/src/microhttpd/mhd_send.h b/src/microhttpd/mhd_send.h
new file mode 100644
index 00000000..a766c8c0
--- /dev/null
+++ b/src/microhttpd/mhd_send.h
@@ -0,0 +1,97 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2019 ng0
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+
+*/
+
+/**
+ * @file mhd_send.h
+ * @brief Implementation of send() wrappers.
+ * @author ng0
+ */
+
+#ifndef MHD_SEND_H
+#define MHD_SEND_H
+
+#include "platform.h"
+#include "internal.h"
+#if defined(HAVE_STDBOOL_H)
+#include <stdbool.h>
+#endif /* HAVE_STDBOOL_H */
+#include <errno.h>
+#include "mhd_sockets.h"
+#include "connection.h"
+#include "connection_https.h"
+
+#ifdef MHD_LINUX_SOLARIS_SENDFILE
+#include <sys/sendfile.h>
+#endif /* MHD_LINUX_SOLARIS_SENDFILE */
+#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
+
+#ifdef HAVE_SYS_PARAM_H
+/* For FreeBSD version identification */
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+/**
+ * The enumeration of send socket options.
+ */
+enum MHD_SendSocketOptions
+{
+ /**
+  * definitely no corking (use NODELAY, or explicitly disable cork)
+  */
+  MHD_SSO_NO_CORK = 0,
+  /**
+   * should enable corking (use MSG_MORE, or explicitly enable cork)
+   */
+  MHD_SSO_MAY_CORK = 1,
+  /**
+   * consider tcpi_snd_mss and consider not corking for the header
+   * part if the size of the header is close to the MSS.
+   * Only used if we are NOT doing 100 Continue and are still
+   * sending the header (provided in full as the buffer to
+   * MHD_send_on_connection_ or as the header to
+   * MHD_send_on_connection2_).
+   */
+  MHD_SSO_HDR_CORK = 2
+};
+
+
+ssize_t
+MHD_send_on_connection_ (struct MHD_Connection *connection,
+                         const char *buffer,
+                         size_t buffer_size,
+                         enum MHD_SendSocketOptions options);
+
+ssize_t
+MHD_send_on_connection2_ (struct MHD_Connection *connection,
+                          const char *header,
+                          size_t header_size,
+                          const char *buffer,
+                          size_t buffer_size);
+
+#if defined(_MHD_HAVE_SENDFILE)
+ssize_t
+MHD_send_sendfile_ (struct MHD_Connection *connection);
+#endif
+
+#endif /* MHD_SEND_H */
diff --git a/src/microhttpd/mhd_sockets.c b/src/microhttpd/mhd_sockets.c
index 04405945..b4e73480 100644
--- a/src/microhttpd/mhd_sockets.c
+++ b/src/microhttpd/mhd_sockets.c
@@ -461,6 +461,64 @@ MHD_socket_noninheritable_ (MHD_socket sock)
 }
 
 
+/**
+ * Disable Nagle's algorithm on @a sock.  This is what we do by default for
+ * all TCP sockets in MHD, unless the platform does not support the MSG_MORE
+ * or MSG_CORK or MSG_NOPUSH options.
+ *
+ * @param sock socket to manipulate
+ * @param on value to use
+ * @return 0 on success
+ */
+int
+MHD_socket_set_nodelay_ (MHD_socket sock,
+                         bool on)
+{
+#ifdef TCP_NODELAY
+  {
+    const MHD_SCKT_OPT_BOOL_ off_val = 0;
+    const MHD_SCKT_OPT_BOOL_ on_val = 1;
+    /* Disable Nagle's algorithm for normal buffering */
+    return setsockopt (sock,
+                       IPPROTO_TCP,
+                       TCP_NODELAY,
+                       (const void *) (on) ? &on_val : &off_val,
+                       sizeof (on_val));
+  }
+#else
+  (void) sock;
+  return 0;
+#endif /* TCP_NODELAY */
+}
+
+
+/**
+ * Enable/disable the cork option.
+ *
+ * @param sock socket to manipulate
+ * @param on set to true to enable CORK, false to disable
+ * @return non-zero if succeeded, zero otherwise
+ */
+int
+MHD_socket_cork_ (MHD_socket sock,
+                  bool on)
+{
+#if defined(MHD_TCP_CORK_NOPUSH)
+  const MHD_SCKT_OPT_BOOL_ off_val = 0;
+  const MHD_SCKT_OPT_BOOL_ on_val = 1;
+
+  /* Disable extra buffering */
+  return (0 == setsockopt (sock,
+                           IPPROTO_TCP,
+                           MHD_TCP_CORK_NOPUSH,
+                           (const void *) (on ? &on_val : &off_val),
+                           sizeof (off_val)));
+#else
+  return 0;
+#endif /* MHD_TCP_CORK_NOPUSH */
+}
+
+
 /**
  * Change socket buffering mode to default.
  *
@@ -470,29 +528,19 @@ MHD_socket_noninheritable_ (MHD_socket sock)
 int
 MHD_socket_buffering_reset_ (MHD_socket sock)
 {
-  int res = !0;
-#if defined(TCP_NODELAY) || defined(MHD_TCP_CORK_NOPUSH)
-  const MHD_SCKT_OPT_BOOL_ off_val = 0;
 #if defined(MHD_TCP_CORK_NOPUSH)
+  int res = ! MHD_socket_set_nodelay_ (sock,
+                                       true);
   /* Disable extra buffering */
-  res = (0 == setsockopt (sock,
-                          IPPROTO_TCP,
-                          MHD_TCP_CORK_NOPUSH,
-                          (const void *) &off_val,
-                          sizeof (off_val))) && res;
+  return MHD_socket_cork_ (sock,
+                           false) && res;
+#elif defined(HAVE_MSG_MORE)
+  return ! MHD_socket_set_nodelay_ (sock,
+                                    true);
+#else
+  return ! MHD_socket_set_nodelay_ (sock,
+                                    false);
 #endif /* MHD_TCP_CORK_NOPUSH */
-#if defined(TCP_NODELAY)
-  /* Enable Nagle's algorithm for normal buffering */
-  res = (0 == setsockopt (sock,
-                          IPPROTO_TCP,
-                          TCP_NODELAY,
-                          (const void *) &off_val,
-                          sizeof (off_val))) && res;
-#endif /* TCP_NODELAY */
-#else  /* !TCP_NODELAY && !MHD_TCP_CORK_NOPUSH */
-  (void) sock;
-#endif /* !TCP_NODELAY && !MHD_TCP_CORK_NOPUSH */
-  return res;
 }
 
 
diff --git a/src/microhttpd/mhd_sockets.h b/src/microhttpd/mhd_sockets.h
index a684d71d..08b01c20 100644
--- a/src/microhttpd/mhd_sockets.h
+++ b/src/microhttpd/mhd_sockets.h
@@ -35,6 +35,7 @@
 #include "mhd_options.h"
 
 #include <errno.h>
+#include <stdbool.h>
 
 #if !defined(MHD_POSIX_SOCKETS) && !defined(MHD_WINSOCK_SOCKETS)
 #  if !defined(_WIN32) || defined(__CYGWIN__)
@@ -744,6 +745,19 @@ int
 MHD_socket_nonblocking_ (MHD_socket sock);
 
 
+/**
+ * Disable Nagle's algorithm on @a sock.  This is what we do by default for
+ * all TCP sockets in MHD, unless the platform does not support the MSG_MORE
+ * or MSG_CORK or MSG_NOPUSH options.
+ *
+ * @param sock socket to manipulate
+ * @param on value to use
+ * @return 0 on success
+ */
+int
+MHD_socket_set_nodelay_ (MHD_socket sock,
+                         bool on);
+
 /**
  * Change socket options to be non-inheritable.
  *
@@ -755,6 +769,30 @@ int
 MHD_socket_noninheritable_ (MHD_socket sock);
 
 
+/**
+ * Enable/disable the cork option.
+ *
+ * TCP_NOPUSH has the same logic as MSG_MSG_MORE.
+ * The two are more or less equivalent by a source
+ * transformation (ie
+ * send(MSG_MORE) => "set TCP_NOPUSH + send() + clear TCP_NOPUSH".
+ * Both of them are really fairly "local", but TCP_NOPUSH has a
+ * _notion_ of persistency that is entirely lacking in MSG_MORE.
+ * ... with TCP_NOPUSH you basically have to know what your last
+ * write is, and clear the bit _before_ that write if you want
+ * to avoid bad latencies.
+ *
+ * See also: https://yarchive.net/comp/linux/sendfile.html
+ *
+ * @param sock socket to manipulate
+ * @param on set to true to enable CORK, false to disable
+ * @return non-zero if succeeded, zero otherwise
+ */
+int
+MHD_socket_cork_ (MHD_socket sock,
+                  bool on);
+
+
 /**
  * Change socket buffering mode to default.
  *
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index 3e9fb053..65ea7b09 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -604,9 +604,9 @@ MHD_create_response_from_fd_at_offset64 (uint64_t size,
 
   response = MHD_create_response_from_callback (size,
                                                 MHD_FILE_READ_BLOCK_SIZE,
-                                               &file_reader,
-                                               NULL,
-                                               &free_callback);
+                                                &file_reader,
+                                                NULL,
+                                                &free_callback);
   if (NULL == response)
     return NULL;
   response->fd = fd;
@@ -825,6 +825,44 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh,
      * be moved to cleanup list by MHD_resume_connection(). */
     MHD_resume_connection (connection);
     return MHD_YES;
+  case MHD_UPGRADE_ACTION_CORK_ON:
+    if (connection->sk_cork_on)
+      return MHD_YES;
+#ifdef HTTPS_SUPPORT
+    if (0 != (daemon->options & MHD_USE_TLS) )
+      {
+        gnutls_record_cork (connection->tls_session);
+        connection->sk_cork_on = true;
+        return MHD_YES;
+      }
+    else
+#else
+      {
+        if (0 ==
+            MHD_socket_cork_ (connection->socket_fd,
+                              true))
+          connection->sk_cork_on = true;
+      }
+#endif
+  case MHD_UPGRADE_ACTION_CORK_OFF:
+    if (! connection->sk_cork_on)
+      return MHD_YES;
+#ifdef HTTPS_SUPPORT
+    if (0 != (daemon->options & MHD_USE_TLS) )
+      {
+        gnutls_record_uncork (connection->tls_session, 0);
+        connection->sk_cork_on = false;
+        return MHD_YES;
+      }
+    else
+#else
+      {
+        if (0 ==
+            MHD_socket_cork_ (connection->socket_fd,
+                              false))
+          connection->sk_cork_on = false;
+      }
+#endif
   default:
     /* we don't understand this one */
     return MHD_NO;
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c
index caf12e61..9135187c 100644
--- a/src/microhttpd/test_upgrade.c
+++ b/src/microhttpd/test_upgrade.c
@@ -685,6 +685,8 @@ run_usock (void *cls)
 {
   struct MHD_UpgradeResponseHandle *urh = cls;
 
+  MHD_upgrade_action (urh,
+                      MHD_UPGRADE_ACTION_CORK_OFF);
   send_all (usock,
             "Hello");
   recv_all (usock,
diff --git a/src/microhttpd/test_upgrade_large.c 
b/src/microhttpd/test_upgrade_large.c
index 7eabf82c..3131fdf4 100644
--- a/src/microhttpd/test_upgrade_large.c
+++ b/src/microhttpd/test_upgrade_large.c
@@ -704,6 +704,8 @@ run_usock (void *cls)
 {
   struct MHD_UpgradeResponseHandle *urh = cls;
 
+  MHD_upgrade_action (urh,
+                      MHD_UPGRADE_ACTION_CORK_OFF);
   send_all (usock,
             LARGE_STRING);
   recv_all (usock,

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

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