libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

mhd_send.c (58285B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2017-2025 Karlson2k (Evgeny Grin), Full re-write of buffering
      5                           and pushing, many bugs fixes, optimisations,
      6                           sendfile() porting
      7   Copyright (C) 2019 ng0 <ng0@n0.is>, Initial version of send() wrappers
      8 
      9   GNU libmicrohttpd is free software; you can redistribute it and/or
     10   modify it under the terms of the GNU Lesser General Public
     11   License as published by the Free Software Foundation; either
     12   version 2.1 of the License, or (at your option) any later version.
     13 
     14   GNU libmicrohttpd is distributed in the hope that it will be useful,
     15   but WITHOUT ANY WARRANTY; without even the implied warranty of
     16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17   Lesser General Public License for more details.
     18 
     19   Alternatively, you can redistribute GNU libmicrohttpd and/or
     20   modify it under the terms of the GNU General Public License as
     21   published by the Free Software Foundation; either version 2 of
     22   the License, or (at your option) any later version, together
     23   with the eCos exception, as follows:
     24 
     25     As a special exception, if other files instantiate templates or
     26     use macros or inline functions from this file, or you compile this
     27     file and link it with other works to produce a work based on this
     28     file, this file does not by itself cause the resulting work to be
     29     covered by the GNU General Public License. However the source code
     30     for this file must still be made available in accordance with
     31     section (3) of the GNU General Public License v2.
     32 
     33     This exception does not invalidate any other reasons why a work
     34     based on this file might be covered by the GNU General Public
     35     License.
     36 
     37   You should have received copies of the GNU Lesser General Public
     38   License and the GNU General Public License along with this library;
     39   if not, see <https://www.gnu.org/licenses/>.
     40 */
     41 
     42 /**
     43  * @file src/mhd2/mhd_send.c
     44  * @brief Implementation of send() wrappers and helper functions.
     45  * @author Karlson2k (Evgeny Grin)
     46  * @author ng0 (N. Gillmann)
     47  * @author Christian Grothoff
     48  */
     49 
     50 /* Worth considering for future improvements and additions:
     51  * NetBSD has no sendfile or sendfile64. The way to work
     52  * with this seems to be to mmap the file and write(2) as
     53  * large a chunk as possible to the socket. Alternatively,
     54  * use madvise(..., MADV_SEQUENTIAL). */
     55 
     56 #include "mhd_sys_options.h"
     57 
     58 #include <string.h>
     59 
     60 #include "sys_sockets_headers.h"
     61 #include "sys_ip_headers.h"
     62 #include "mhd_sockets_macros.h"
     63 #include "daemon_logger.h"
     64 #include "mhd_socket_error_funcs.h"
     65 
     66 #include "mhd_daemon.h"
     67 #include "mhd_connection.h"
     68 #include "mhd_response.h"
     69 
     70 #include "mhd_iovec.h"
     71 #ifdef HAVE_LINUX_SENDFILE
     72 #  include <sys/sendfile.h>
     73 #endif /* HAVE_LINUX_SENDFILE */
     74 
     75 #if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
     76 #  include <sys/types.h>
     77 #  include <sys/socket.h>
     78 #  include <sys/uio.h>
     79 #endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
     80 #ifdef HAVE_SYS_PARAM_H
     81 /* For FreeBSD version identification */
     82 #  include <sys/param.h>
     83 #endif /* HAVE_SYS_PARAM_H */
     84 #ifdef HAVE_SYSCONF
     85 #  include <unistd.h>
     86 #endif /* HAVE_SYSCONF */
     87 #include "mhd_assert.h"
     88 
     89 #include "mhd_limits.h"
     90 
     91 #ifdef MHD_SUPPORT_HTTPS
     92 #  include "mhd_tls_funcs.h"
     93 #endif
     94 
     95 #include "sckt_send.h"
     96 #include "mhd_send.h"
     97 
     98 #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \
     99   defined(MHD_SOCKETS_KIND_WINSOCK)
    100 #  define mhd_USE_VECT_SEND 1
    101 #endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_SOCKETS_KIND_WINSOCK */
    102 
    103 
    104 #ifdef mhd_USE_VECT_SEND
    105 #  if (!defined(HAVE_SENDMSG) || !defined(HAVE_DCLR_MSG_NOSIGNAL)) && \
    106   defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
    107   defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED)
    108 #    define mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED 1
    109 #  endif /* (!HAVE_SENDMSG || !HAVE_DCLR_MSG_NOSIGNAL) &&
    110             mhd_SEND_SPIPE_SUPPRESS_POSSIBLE && mhd_SEND_SPIPE_SUPPRESS_NEEDED */
    111 #endif /* mhd_USE_VECT_SEND */
    112 
    113 /**
    114  * sendfile() chuck size
    115  */
    116 #define mhd_SENFILE_CHUNK_SIZE         (0x20000)
    117 
    118 /**
    119  * sendfile() chuck size for thread-per-connection
    120  */
    121 #define mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C (0x200000)
    122 
    123 #if defined(HAVE_FREEBSD_SENDFILE) && defined(SF_FLAGS)
    124 /**
    125  * FreeBSD sendfile() flags
    126  */
    127 static int freebsd_sendfile_flags_;
    128 
    129 /**
    130  * FreeBSD sendfile() flags for thread-per-connection
    131  */
    132 static int freebsd_sendfile_flags_thd_p_c_;
    133 
    134 
    135 /**
    136  * Initialises variables for FreeBSD's sendfile()
    137  */
    138 static void
    139 freebsd_sendfile_init_ (void)
    140 {
    141   long sys_page_size = sysconf (_SC_PAGESIZE);
    142   if (0 >= sys_page_size)
    143   {   /* Failed to get page size. */
    144     freebsd_sendfile_flags_ = SF_NODISKIO;
    145     freebsd_sendfile_flags_thd_p_c_ = SF_NODISKIO;
    146   }
    147   else
    148   {
    149     freebsd_sendfile_flags_ =
    150       SF_FLAGS ((uint_least16_t) \
    151                 ((mhd_SENFILE_CHUNK_SIZE + sys_page_size - 1) / sys_page_size) \
    152                 & 0xFFFFU, SF_NODISKIO);
    153     freebsd_sendfile_flags_thd_p_c_ =
    154       SF_FLAGS ((uint_least16_t) \
    155                 ((mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C + sys_page_size - 1) \
    156                  / sys_page_size) & 0xFFFFU, SF_NODISKIO);
    157   }
    158 }
    159 
    160 
    161 #else  /* ! HAVE_FREEBSD_SENDFILE || ! SF_FLAGS */
    162 #  define freebsd_sendfile_init_() (void) 0
    163 #endif /* HAVE_FREEBSD_SENDFILE */
    164 
    165 
    166 #if defined(HAVE_SYSCONF) && defined(_SC_IOV_MAX)
    167 /**
    168  * Current IOV_MAX system value
    169  */
    170 static unsigned long mhd_iov_max_ = 0;
    171 
    172 static void
    173 iov_max_init_ (void)
    174 {
    175   long res = sysconf (_SC_IOV_MAX);
    176   if (res >= 0)
    177     mhd_iov_max_ = (unsigned long)res;
    178   else
    179   {
    180 #  if defined(IOV_MAX)
    181     mhd_iov_max_ = IOV_MAX;
    182 #  else  /* ! IOV_MAX */
    183     mhd_iov_max_ = 8; /* Should be the safe limit */
    184 #  endif /* ! IOV_MAX */
    185   }
    186 }
    187 
    188 
    189 /**
    190  * IOV_MAX (run-time) value
    191  */
    192 #  define mhd_IOV_MAX    mhd_iov_max_
    193 #else  /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */
    194 #  define iov_max_init_() ((void) 0)
    195 #  if defined(IOV_MAX)
    196 
    197 /**
    198  * IOV_MAX (static) value
    199  */
    200 #    define mhd_IOV_MAX    IOV_MAX
    201 #  endif /* IOV_MAX */
    202 #endif /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */
    203 
    204 
    205 void
    206 mhd_send_init_once (void)
    207 {
    208   /* FreeBSD 11 and later allow to specify read-ahead size
    209    * and handles SF_NODISKIO differently.
    210    * SF_FLAGS defined only on FreeBSD 11 and later. */
    211   freebsd_sendfile_init_ ();
    212 
    213   iov_max_init_ ();
    214 }
    215 
    216 
    217 /**
    218  * Set required TCP_NODELAY state for connection socket
    219  *
    220  * The function automatically updates sk.nodelay state.
    221  * @param sk the socket data
    222  * @param nodelay_state the requested new state of socket
    223  * @return true if succeed, false if failed or not supported
    224  *         by the current platform / kernel.
    225  */
    226 static MHD_FN_PAR_NONNULL_ALL_ bool
    227 mhd_connection_set_nodelay_state (struct mhd_ConnSocket *restrict sk,
    228                                   bool nodelay_state)
    229 {
    230 #ifdef HAVE_DCLR_TCP_NODELAY
    231   static const mhd_SCKT_OPT_BOOL off_val = 0;
    232   static const mhd_SCKT_OPT_BOOL on_val = 1;
    233   int err_code;
    234 
    235   if (mhd_T_IS_YES (sk->props.is_nonip))
    236     return false;
    237 
    238 #  ifndef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
    239   mhd_assert ((!nodelay_state) || (mhd_T_IS_NOT_YES (sk->state.nodelay)));
    240 #  endif /* mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
    241   mhd_assert ((nodelay_state) || (mhd_T_IS_NOT_NO (sk->state.nodelay)));
    242 
    243   if (0 == mhd_setsockopt (sk->fd,
    244                            IPPROTO_TCP,
    245                            TCP_NODELAY,
    246                            (const void *)(nodelay_state ? &on_val : &off_val),
    247                            sizeof (off_val)))
    248   {
    249     sk->state.nodelay = nodelay_state ? mhd_T_YES : mhd_T_NO;
    250     return true;
    251   }
    252 
    253   err_code = mhd_SCKT_GET_LERR ();
    254   if ((mhd_T_IS_NOT_YES (sk->props.is_nonip))
    255       && (mhd_SCKT_ERR_IS_EINVAL (err_code)
    256           || mhd_SCKT_ERR_IS_NOPROTOOPT (err_code)
    257           || mhd_SCKT_ERR_IS_NOTSOCK (err_code)))
    258   {
    259     sk->props.is_nonip = mhd_T_YES;
    260   }
    261 #  if 0 /* No messages, avoid potential message flood in the log */
    262   else
    263   {
    264     mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_NODELAY_FAILED, \
    265                  "Failed to set required TCP_NODELAY option for the socket.");
    266   }
    267 #  endif /* No messages */
    268 #else  /* ! TCP_NODELAY */
    269   (void)nodelay_state;  /* Mute compiler warnings */
    270   sk->state.nodelay = mhd_T_NO;
    271 #endif /* ! TCP_NODELAY */
    272   return false;
    273 }
    274 
    275 
    276 /**
    277  * Set required cork state for connection socket
    278  *
    279  * The function automatically updates sk.corked state.
    280  *
    281  * @param sk the socket data
    282  * @param cork_state the requested new state of socket
    283  * @return true if succeed, false if failed or not supported
    284  *         by the current platform / kernel.
    285  */
    286 static MHD_FN_PAR_NONNULL_ALL_ bool
    287 mhd_connection_set_cork_state (struct mhd_ConnSocket *restrict sk,
    288                                bool cork_state)
    289 {
    290 #if defined(mhd_TCP_CORK_NOPUSH)
    291   static const mhd_SCKT_OPT_BOOL off_val = 0;
    292   static const mhd_SCKT_OPT_BOOL on_val = 1;
    293   int err_code;
    294 
    295   if (mhd_T_IS_YES (sk->props.is_nonip))
    296     return false;
    297 
    298   mhd_assert ((!cork_state) || (mhd_T_IS_NOT_YES (sk->state.corked)));
    299 #  ifndef mhd_CORK_RESET_PUSH_DATA_ALWAYS
    300   mhd_assert ((cork_state) || (mhd_T_IS_NOT_NO (sk->state.corked)));
    301 #  endif /* mhd_CORK_RESET_PUSH_DATA_ALWAYS */
    302 
    303   if (0 == mhd_setsockopt (sk->fd,
    304                            IPPROTO_TCP,
    305                            mhd_TCP_CORK_NOPUSH,
    306                            (const void *)(cork_state ? &on_val : &off_val),
    307                            sizeof (off_val)))
    308   {
    309     sk->state.corked = cork_state ? mhd_T_YES : mhd_T_NO;
    310     return true;
    311   }
    312 
    313   err_code = mhd_SCKT_GET_LERR ();
    314   if ((mhd_T_IS_NOT_YES (sk->props.is_nonip))
    315       && (mhd_SCKT_ERR_IS_EINVAL (err_code)
    316           || mhd_SCKT_ERR_IS_NOPROTOOPT (err_code)
    317           || mhd_SCKT_ERR_IS_NOTSOCK (err_code)))
    318   {
    319     sk->props.is_nonip = mhd_T_YES;
    320   }
    321 #  if 0 /* No messages, avoid potential message flood in the log */
    322   else
    323   {
    324 #    ifdef TCP_CORK
    325     mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
    326                  "Failed to set required TCP_CORK option for the socket.");
    327 #    else
    328     mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
    329                  "Failed to set required TCP_NOPUSH option for the socket.");
    330 #    endif
    331   }
    332 #  endif /* No messages */
    333 
    334 #else  /* ! mhd_TCP_CORK_NOPUSH */
    335   (void)cork_state;  /* Mute compiler warnings. */
    336   sk->state.corked = mhd_T_NO;
    337 #endif /* ! mhd_TCP_CORK_NOPUSH */
    338   return false;
    339 }
    340 
    341 
    342 /**
    343  * Handle pre-send setsockopt calls.
    344  *
    345  * @param sk the socket data
    346  * @param plain_send set to true if plain send() or sendmsg() will be called,
    347  *                   set to false if TLS socket send(), sendfile() or
    348  *                   writev() will be called.
    349  * @param push_data whether to push data to the network from buffers after
    350  *                  the next call of send function.
    351  */
    352 static void
    353 pre_send_setopt (struct mhd_ConnSocket *restrict sk,
    354                  bool plain_send,
    355                  bool push_data)
    356 {
    357   /* Try to buffer data if not sending the final piece.
    358    * Final piece is indicated by push_data == true. */
    359   const bool buffer_data = (!push_data);
    360 
    361   if (mhd_T_IS_YES (sk->props.is_nonip))
    362     return;
    363 
    364   // TODO: support inheriting of TCP_NODELAY and TCP_NOPUSH from accept()
    365 
    366   /* The goal is to minimise the total number of additional sys-calls
    367    * before and after send().
    368    * The following tricky (over-)complicated algorithm typically use zero,
    369    * one or two additional sys-calls (depending on OS) for each response. */
    370 
    371   if (buffer_data)
    372   {
    373     /* Need to buffer data if possible. */
    374 #ifdef mhd_USE_MSG_MORE
    375     if (plain_send)
    376       return; /* Data is buffered by send() with MSG_MORE flag.
    377                * No need to check or change anything. */
    378 #else  /* ! mhd_USE_MSG_MORE */
    379     (void)plain_send;  /* Mute compiler warning. */
    380 #endif /* ! mhd_USE_MSG_MORE */
    381 
    382 #ifdef mhd_TCP_CORK_NOPUSH
    383     if (mhd_T_IS_YES (sk->state.corked))
    384       return; /* The connection was already corked. */
    385 
    386     /* Prefer 'cork' over 'no delay' as the 'cork' buffers better, regardless
    387      * of the number of received ACKs. */
    388     if (mhd_connection_set_cork_state (sk, true))
    389       return; /* The connection has been corked. */
    390 
    391     /* Failed to cork the connection.
    392      * Really unlikely to happen on TCP connections. */
    393 #endif /* mhd_TCP_CORK_NOPUSH */
    394     if (mhd_T_IS_NO (sk->state.nodelay))
    395       return; /* TCP_NODELAY was not set for the socket.
    396                * Nagle's algorithm will buffer some data. */
    397 
    398     /* Try to reset TCP_NODELAY state for the socket.
    399      * Ignore possible error as no other options exist to
    400      * buffer data. */
    401     mhd_connection_set_nodelay_state (sk, false);
    402     /* TCP_NODELAY has been (hopefully) reset for the socket.
    403      * Nagle's algorithm will buffer some data. */
    404     return;
    405   }
    406 
    407   /* Need to push data after the next send() */
    408   /* If additional sys-call is required, prefer to make it only after the send()
    409    * (if possible) as this send() may consume only part of the prepared data and
    410    * more send() calls will be used. */
    411 #ifdef mhd_TCP_CORK_NOPUSH
    412 #  ifdef mhd_CORK_RESET_PUSH_DATA
    413 #    ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS
    414   /* Data can be pushed immediately by uncorking socket regardless of
    415    * cork state before. */
    416   /* This is typical for Linux, no other kernel with
    417    * such behaviour are known so far. */
    418 
    419   /* No need to check the current state of TCP_CORK / TCP_NOPUSH
    420    * as reset of cork will push the data anyway. */
    421   return; /* Data may be pushed by resetting of
    422            * TCP_CORK / TCP_NOPUSH after send() */
    423 #    else  /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
    424   /* Reset of TCP_CORK / TCP_NOPUSH will push the data
    425    * only if socket is corked. */
    426 
    427 #      ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
    428   /* Data can be pushed immediately by setting TCP_NODELAY regardless
    429    * of TCP_NODDELAY or corking state before. */
    430 
    431   /* Dead code currently, no known kernels with such behaviour and without
    432    * pushing by uncorking. */
    433   return; /* Data may be pushed by setting of TCP_NODELAY after send().
    434              No need to make extra sys-calls before send().*/
    435 #      else  /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
    436 
    437 /* These next comment blocks are just generic description for the possible
    438  * choices for the code below. */
    439 #        ifdef mhd_NODELAY_SET_PUSH_DATA
    440   /* Setting of TCP_NODELAY will push the data only if
    441    * both TCP_NODELAY and TCP_CORK / TCP_NOPUSH were not set. */
    442 
    443   /* Data can be pushed immediately by uncorking socket if
    444    * socket was corked before or by setting TCP_NODELAY if
    445    * socket was not corked and TCP_NODELAY was not set before. */
    446 
    447   /* This combination not possible currently as Linux is the only kernel that
    448    * pushes data by setting of TCP_NODELAY and Linux pushes data always
    449    * by TCP_NODELAY, regardless previous TCP_NODELAY state. */
    450 #        else  /* ! mhd_NODELAY_SET_PUSH_DATA */
    451   /* Data can be pushed immediately by uncorking socket or
    452    * can be pushed by send() on uncorked socket if
    453    * TCP_NODELAY was set *before*. */
    454 
    455   /* This is typical modern FreeBSD and OpenBSD behaviour. */
    456 #        endif /* ! mhd_NODELAY_SET_PUSH_DATA */
    457 
    458   if (mhd_T_IS_YES (sk->state.corked))
    459     return; /* Socket is corked. Data can be pushed by resetting of
    460              * TCP_CORK / TCP_NOPUSH after send() */
    461   else if (mhd_T_IS_NO (sk->state.corked))
    462   {
    463     /* The socket is not corked. */
    464     if (mhd_T_IS_YES (sk->state.nodelay))
    465       return; /* TCP_NODELAY was already set,
    466                * data will be pushed automatically by the next send() */
    467 #        ifdef mhd_NODELAY_SET_PUSH_DATA
    468     else if (mhd_T_IS_MAYBE (sk->state.nodelay))
    469     {
    470       /* Setting TCP_NODELAY may push data NOW.
    471        * Cork socket here and uncork after send(). */
    472       if (mhd_connection_set_cork_state (sk, true))
    473         return; /* The connection has been corked.
    474                  * Data can be pushed by resetting of
    475                  * TCP_CORK / TCP_NOPUSH after send() */
    476       else
    477       {
    478         /* The socket cannot be corked.
    479          * Really unlikely to happen on TCP connections */
    480         /* Have to set TCP_NODELAY.
    481          * If TCP_NODELAY real system state was OFF then
    482          * already buffered data may be pushed NOW, but it is unlikely
    483          * to happen as this is only a backup solution when corking has failed.
    484          * Ignore possible error here as no other options exist to
    485          * push data. */
    486         mhd_connection_set_nodelay_state (sk, true);
    487         /* TCP_NODELAY has been (hopefully) set for the socket.
    488          * The data will be pushed by the next send(). */
    489         return;
    490       }
    491     }
    492 #        endif /* mhd_NODELAY_SET_PUSH_DATA */
    493     else
    494     {
    495 #        ifdef mhd_NODELAY_SET_PUSH_DATA
    496       /* The socket is not corked and TCP_NODELAY is switched off. */
    497 #        else  /* ! mhd_NODELAY_SET_PUSH_DATA */
    498       /* The socket is not corked and TCP_NODELAY is not set or unknown. */
    499 #        endif /* ! mhd_NODELAY_SET_PUSH_DATA */
    500 
    501       /* At least one additional sys-call before send() is required. */
    502       /* Setting TCP_NODELAY is optimal here as data will be pushed
    503        * automatically by the next send() and no additional
    504        * sys-call are needed after the send(). */
    505       if (mhd_connection_set_nodelay_state (sk, true))
    506         return;
    507       else
    508       {
    509         /* Failed to set TCP_NODELAY for the socket.
    510          * Really unlikely to happen on TCP connections. */
    511         /* Cork the socket here and make additional sys-call
    512          * to uncork the socket after send(). This will push the data. */
    513         /* Ignore possible error here as no other options exist to
    514          * push data. */
    515         mhd_connection_set_cork_state (sk, true);
    516         /* The connection has been (hopefully) corked.
    517          * Data can be pushed by resetting of TCP_CORK / TCP_NOPUSH
    518          * after send() */
    519         return;
    520       }
    521     }
    522   }
    523   /* Corked state is unknown. Need to make a sys-call here otherwise
    524    * data may not be pushed. */
    525   if (mhd_connection_set_cork_state (sk, true))
    526     return; /* The connection has been corked.
    527              * Data can be pushed by resetting of
    528              * TCP_CORK / TCP_NOPUSH after send() */
    529   /* The socket cannot be corked.
    530    * Really unlikely to happen on TCP connections */
    531   if (mhd_T_IS_YES (sk->state.nodelay))
    532     return; /* TCP_NODELAY was already set,
    533              * data will be pushed by the next send() */
    534 
    535   /* Have to set TCP_NODELAY. */
    536 #        ifdef mhd_NODELAY_SET_PUSH_DATA
    537   /* If TCP_NODELAY state was unknown (external connection) then
    538    * already buffered data may be pushed here, but this is unlikely
    539    * to happen as it is only a backup solution when corking has failed. */
    540 #        endif /* mhd_NODELAY_SET_PUSH_DATA */
    541   /* Ignore possible error here as no other options exist to
    542    * push data. */
    543   mhd_connection_set_nodelay_state (sk, true);
    544   /* TCP_NODELAY has been (hopefully) set for the socket.
    545    * The data will be pushed by the next send(). */
    546   return;
    547 #      endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
    548 #    endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
    549 #  else  /* ! mhd_CORK_RESET_PUSH_DATA */
    550 
    551 #    ifndef mhd_NODELAY_SET_PUSH_DATA
    552   /* Neither uncorking the socket or setting TCP_NODELAY
    553    * push the data immediately. */
    554   /* The only way to push the data is to use send() on uncorked
    555    * socket with TCP_NODELAY switched on . */
    556 
    557   /* This is old FreeBSD and Darwin behaviour. */
    558 
    559   /* Uncork socket if socket wasn't uncorked. */
    560   if (mhd_T_IS_NOT_NO (sk->state.corked))
    561     mhd_connection_set_cork_state (sk, false);
    562 
    563   /* Set TCP_NODELAY if it wasn't set. */
    564   if (mhd_T_IS_NOT_YES (sk->state.nodelay))
    565     mhd_connection_set_nodelay_state (sk, true);
    566 
    567   return;
    568 #    else  /* mhd_NODELAY_SET_PUSH_DATA */
    569   /* Setting TCP_NODELAY push the data immediately. */
    570 
    571   /* Dead code currently as Linux kernel is only kernel which push by
    572    * setting TCP_NODELAY. The same kernel push data by resetting TCP_CORK. */
    573 #      ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
    574   return; /* Data may be pushed by setting of TCP_NODELAY after send().
    575              No need to make extra sys-calls before send().*/
    576 #      else  /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
    577   /* Cannot set TCP_NODELAY here as it would push data NOW.
    578    * Set TCP_NODELAY after the send(), together if uncorking if necessary. */
    579 #      endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
    580 #    endif /* mhd_NODELAY_SET_PUSH_DATA */
    581 #  endif /* ! mhd_CORK_RESET_PUSH_DATA */
    582 #else  /* ! mhd_TCP_CORK_NOPUSH */
    583   /* Buffering of data is controlled only by
    584    * Nagel's algorithm. */
    585   /* Set TCP_NODELAY if it wasn't set. */
    586   if (mhd_T_IS_NOT_YES (sk->state.nodelay))
    587     mhd_connection_set_nodelay_state (sk, true);
    588 #endif /* ! mhd_TCP_CORK_NOPUSH */
    589 }
    590 
    591 
    592 #ifndef mhd_CORK_RESET_PUSH_DATA_ALWAYS
    593 /**
    594  * Send zero-sized data
    595  *
    596  * This function use send of zero-sized data to kick data from the socket
    597  * buffers to the network. The socket must not be corked and must have
    598  * TCP_NODELAY switched on.
    599  * Used only as last resort option, when other options are failed due to
    600  * some errors.
    601  * Should not be called on typical data processing.
    602  * @return true if succeed, false if failed
    603  */
    604 static bool
    605 zero_send (struct mhd_ConnSocket *restrict sk)
    606 {
    607   static const int dummy = 0;
    608 
    609   if (mhd_T_IS_YES (sk->props.is_nonip))
    610     return false;
    611   mhd_assert (mhd_T_IS_NO (sk->state.corked));
    612   mhd_assert (mhd_T_IS_YES (sk->state.nodelay));
    613   if (0 == mhd_sys_send (sk->fd, &dummy, 0))
    614     return true;
    615 #  if 0 /* No messages, avoid potential message flood in the log */
    616   mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_ZERO_SEND_FAILED, \
    617                "Failed to push the data by zero-sized send.");
    618 #  endif /* No messages */
    619   return false;
    620 }
    621 
    622 
    623 #endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
    624 
    625 /**
    626  * Handle post-send setsockopt calls.
    627  *
    628  * @param sk the socket data
    629  * @param plain_send_next set to true if plain send() or sendmsg() will be
    630  *                        called next,
    631  *                        set to false if TLS socket send(), sendfile() or
    632  *                        writev() will be called next.
    633  * @param push_data whether to push data to the network from buffers
    634  */
    635 static void
    636 post_send_setopt (struct mhd_ConnSocket *restrict sk,
    637                   bool plain_send_next,
    638                   bool push_data)
    639 {
    640   /* Try to buffer data if not sending the final piece.
    641    * Final piece is indicated by push_data == true. */
    642   const bool buffer_data = (!push_data);
    643 
    644   if (mhd_T_IS_YES (sk->props.is_nonip))
    645     return;
    646   if (buffer_data)
    647     return; /* Nothing to do after the send(). */
    648 
    649 #ifndef mhd_USE_MSG_MORE
    650   (void)plain_send_next;  /* Mute compiler warning */
    651 #endif /* ! mhd_USE_MSG_MORE */
    652 
    653   /* Need to push data. */
    654 #ifdef mhd_TCP_CORK_NOPUSH
    655   if (mhd_T_IS_YES (sk->state.nodelay) \
    656       && mhd_T_IS_NO (sk->state.corked))
    657     return; /* Data has been already pushed by last send(). */
    658 
    659 #  ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS
    660 #    ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
    661 #      ifdef mhd_USE_MSG_MORE
    662   /* This is Linux kernel.
    663    * The socket is corked (or unknown) or 'no delay' is not set (or unknown).
    664    * There are options:
    665    * * Push the data by setting of TCP_NODELAY (without change
    666    *   of the cork on the socket),
    667    * * Push the data by resetting of TCP_CORK.
    668    * The optimal choice depends on the next final send functions
    669    * used on the same socket.
    670    *
    671    * In general on Linux kernel TCP_NODELAY always enabled is preferred,
    672    * as buffering is controlled by MSG_MORE or cork/uncork.
    673    *
    674    * If next send function will not support MSG_MORE (like sendfile()
    675    * or TLS-connection) than push data by setting TCP_NODELAY
    676    * so the socket may remain corked (no additional sys-call before
    677    * next send()).
    678    *
    679    * If send()/sendmsg() will be used next than push data by
    680    * resetting of TCP_CORK so next final send without MSG_MORE will push
    681    * data to the network (without additional sys-call to push data).  */
    682 
    683   if (mhd_T_IS_NOT_YES (sk->state.nodelay)
    684       || (!plain_send_next))
    685   {
    686     if (mhd_connection_set_nodelay_state (sk, true))
    687       return; /* Data has been pushed by TCP_NODELAY. */
    688     /* Failed to set TCP_NODELAY for the socket.
    689      * Really unlikely to happen on TCP connections. */
    690     if (mhd_connection_set_cork_state (sk, false))
    691       return; /* Data has been pushed by uncorking the socket. */
    692     /* Failed to uncork the socket.
    693      * Really unlikely to happen on TCP connections. */
    694 
    695     /* The socket cannot be uncorked, no way to push data */
    696   }
    697   else
    698   {
    699     if (mhd_connection_set_cork_state (sk, false))
    700       return; /* Data has been pushed by uncorking the socket. */
    701     /* Failed to uncork the socket.
    702      * Really unlikely to happen on TCP connections. */
    703     if (mhd_connection_set_nodelay_state (sk, true))
    704       return; /* Data has been pushed by TCP_NODELAY. */
    705     /* Failed to set TCP_NODELAY for the socket.
    706      * Really unlikely to happen on TCP connections. */
    707 
    708     /* The socket cannot be uncorked, no way to push data */
    709   }
    710 #      else  /* ! mhd_USE_MSG_MORE */
    711   /* Push data by setting TCP_NODELAY here as uncorking here
    712    * would require corking the socket before sending the next response. */
    713   if (mhd_connection_set_nodelay_state (sk, true))
    714     return; /* Data was pushed by TCP_NODELAY. */
    715   /* Failed to set TCP_NODELAY for the socket.
    716    * Really unlikely to happen on TCP connections. */
    717   if (mhd_connection_set_cork_state (sk, false))
    718     return; /* Data was pushed by uncorking the socket. */
    719   /* Failed to uncork the socket.
    720    * Really unlikely to happen on TCP connections. */
    721 
    722   /* The socket remains corked, no way to push data */
    723 #      endif /* ! mhd_USE_MSG_MORE */
    724 #    else  /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
    725   if (mhd_connection_set_cork_state (sk, false))
    726     return; /* Data was pushed by uncorking the socket. */
    727   /* Failed to uncork the socket.
    728    * Really unlikely to happen on TCP connections. */
    729 
    730   /* Socket remains corked, no way to push data */
    731 #    endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
    732 #  else  /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
    733   /* This is old FreeBSD or Darwin kernel. */
    734 
    735   if (mhd_T_IS_NO (sk->state.corked))
    736   {
    737     mhd_assert (mhd_T_IS_NOT_YES (sk->state.nodelay));
    738 
    739     /* Unlikely to reach this code.
    740      * TCP_NODELAY should be turned on before send(). */
    741     if (mhd_connection_set_nodelay_state (sk, true))
    742     {
    743       /* TCP_NODELAY has been set on uncorked socket.
    744        * Use zero-send to push the data. */
    745       if (zero_send (sk))
    746         return; /* The data has been pushed by zero-send. */
    747     }
    748 
    749     /* Failed to push the data by all means. */
    750     /* There is nothing left to try. */
    751   }
    752   else
    753   {
    754 #    ifdef mhd_CORK_RESET_PUSH_DATA
    755     enum mhd_Tristate old_cork_state = sk->state.corked;
    756 #    endif /* mhd_CORK_RESET_PUSH_DATA */
    757     /* The socket is corked or cork state is unknown. */
    758 
    759     if (mhd_connection_set_cork_state (sk, false))
    760     {
    761 #    ifdef mhd_CORK_RESET_PUSH_DATA
    762       /* Modern FreeBSD or OpenBSD kernel */
    763       if (mhd_T_IS_YES (old_cork_state))
    764         return; /* Data has been pushed by uncorking the socket. */
    765 #    endif /* mhd_CORK_RESET_PUSH_DATA */
    766 
    767       /* Unlikely to reach this code.
    768        * The data should be pushed by uncorking (FreeBSD) or
    769        * the socket should be uncorked before send(). */
    770       if (mhd_T_IS_YES (sk->state.nodelay)
    771           || (mhd_connection_set_nodelay_state (sk, true)))
    772       {
    773         /* TCP_NODELAY is turned ON on uncorked socket.
    774          * Use zero-send to push the data. */
    775         if (zero_send (sk))
    776           return; /* The data has been pushed by zero-send. */
    777       }
    778     }
    779     /* Data cannot be pushed. */
    780   }
    781 #  endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
    782 #else  /* ! mhd_TCP_CORK_NOPUSH */
    783   /* Corking is not supported. Buffering is controlled
    784    * by TCP_NODELAY only. */
    785   mhd_assert (mhd_T_IS_NOT_YES (sk->state.corked));
    786   if (mhd_T_IS_YES (sk->state.nodelay))
    787     return; /* Data was already pushed by send(). */
    788 
    789   /* Unlikely to reach this code.
    790    * TCP_NODELAY should be turned on before send(). */
    791   if (mhd_connection_set_nodelay_state (sk, true))
    792   {
    793     /* TCP_NODELAY has been set.
    794      * Use zero-send to try to push the data. */
    795     if (zero_send (sk))
    796       return; /* The data has been pushed by zero-send. */
    797   }
    798 
    799   /* Failed to push the data. */
    800 #endif /* ! mhd_TCP_CORK_NOPUSH */
    801 
    802 #if 0 /* No messages, avoid potential message flood in the log */
    803   mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_FLUSH_LAST_PART_FAILED, \
    804                "Failed to force flush the last part of the response header " \
    805                "or the response content that might have been buffered by " \
    806                "the kernel. The client may experience some delay (usually " \
    807                "in range 200ms - 5 sec).");
    808 #endif /* No messages */
    809   return;
    810 }
    811 
    812 
    813 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    814 MHD_FN_PAR_IN_SIZE_ (3, 2)
    815 MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
    816 mhd_sckt_send (struct mhd_ConnSocket *restrict sk,
    817                size_t buf_size,
    818                const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
    819                bool push_data,
    820                size_t *restrict sent)
    821 {
    822   /* plaintext transmission */
    823   ssize_t res;
    824   bool full_buf_sent;
    825 
    826   if (buf_size > MHD_SCKT_SEND_MAX_SIZE_)
    827   {
    828     buf_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */
    829     push_data = false; /* Incomplete send */
    830   }
    831 
    832   pre_send_setopt (sk, true, push_data);
    833 #ifdef mhd_USE_MSG_MORE
    834   res = mhd_sys_send4 (sk->fd,
    835                        buf,
    836                        buf_size,
    837                        push_data ? 0 : MSG_MORE);
    838 #else
    839   res = mhd_sys_send4 (sk->fd,
    840                        buf,
    841                        buf_size,
    842                        0);
    843 #endif
    844 
    845   if (0 >= res)
    846   {
    847     enum mhd_SocketError err;
    848 
    849     err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
    850 
    851     if (mhd_SOCKET_ERR_AGAIN == err)
    852       mhd_SCKT_NET_ST_CLEAR_FLAG (&(sk->ready),
    853                                   mhd_SOCKET_NET_STATE_SEND_READY);
    854 
    855     return err;
    856   }
    857   *sent = (size_t)res;
    858 
    859   full_buf_sent = (buf_size == (size_t)res);
    860 
    861   if (!full_buf_sent || !sk->props.is_nonblck)
    862     mhd_SCKT_NET_ST_CLEAR_FLAG (&(sk->ready),
    863                                 mhd_SOCKET_NET_STATE_SEND_READY);
    864 
    865   /* If there is a need to push the data from network buffers
    866    * call post_send_setopt(). */
    867   /* It's unknown whether sendfile() (or other send function without
    868    * MSG_MORE support) will be used for the next reply so assume
    869    * that next sending will be the same, like this call. */
    870   if (push_data && full_buf_sent)
    871     post_send_setopt (sk, true, push_data);
    872 
    873   return mhd_SOCKET_ERR_NO_ERROR;
    874 }
    875 
    876 
    877 #ifdef MHD_SUPPORT_HTTPS
    878 
    879 static MHD_FN_PAR_NONNULL_ALL_
    880 MHD_FN_PAR_IN_SIZE_ (3, 2)
    881 MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
    882 mhd_send_tls (struct MHD_Connection *restrict c,
    883               size_t buf_size,
    884               const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
    885               bool push_data,
    886               size_t *restrict sent)
    887 {
    888   /* TLS connection */
    889   const bool custm_trnsp = mhd_tls_conn_has_cstm_tr (c->tls);
    890   enum mhd_SocketError res;
    891 
    892   mhd_assert (mhd_C_HAS_TLS (c));
    893   mhd_assert (mhd_D_HAS_TLS (c->daemon));
    894   mhd_assert (0 != buf_size);
    895 
    896   if (!custm_trnsp)
    897     pre_send_setopt (&(c->sk), false, push_data);
    898 
    899   res = mhd_tls_conn_send (c->tls,
    900                            buf_size,
    901                            buf,
    902                            push_data,
    903                            sent);
    904 
    905   if (mhd_SOCKET_ERR_NO_ERROR != res)
    906   {
    907     if (mhd_SOCKET_ERR_AGAIN == res)
    908       c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
    909                     (((unsigned int)c->sk.ready)
    910                      & (~(enum mhd_SocketNetState)
    911                         mhd_SOCKET_NET_STATE_SEND_READY));
    912     return res;
    913   }
    914 
    915   if (!c->sk.props.is_nonblck)
    916     c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
    917                   (((unsigned int)c->sk.ready)
    918                    & (~(enum mhd_SocketNetState)
    919                       mhd_SOCKET_NET_STATE_SEND_READY));
    920 
    921   /* If there is a need to push the data from network buffers
    922    * call post_send_setopt(). */
    923   if ((!custm_trnsp) && push_data && (buf_size == *sent))
    924     post_send_setopt (&(c->sk), false, true);
    925 
    926   return mhd_SOCKET_ERR_NO_ERROR;
    927 }
    928 
    929 
    930 #endif /* MHD_SUPPORT_HTTPS */
    931 
    932 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    933 MHD_FN_PAR_IN_SIZE_ (3, 2)
    934 MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
    935 mhd_send_data (struct MHD_Connection *restrict connection,
    936                size_t buf_size,
    937                const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
    938                bool push_data,
    939                size_t *restrict sent)
    940 {
    941   mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd);
    942   mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage);
    943 
    944 #ifdef MHD_SUPPORT_HTTPS
    945   if (mhd_C_HAS_TLS (connection))
    946     return mhd_send_tls (connection,
    947                          buf_size,
    948                          buf,
    949                          push_data,
    950                          sent);
    951 #endif /* MHD_SUPPORT_HTTPS */
    952 
    953   return mhd_sckt_send (&(connection->sk),
    954                         buf_size,
    955                         buf,
    956                         push_data,
    957                         sent);
    958 }
    959 
    960 
    961 MHD_INTERNAL
    962 MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (3)
    963 MHD_FN_PAR_NONNULL_IF_NONZERO_ (6, 5)
    964 MHD_FN_PAR_NONNULL_ (8) MHD_FN_PAR_OUT_ (8)
    965 MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (6, 5) enum mhd_SocketError
    966 mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
    967                        size_t header_size,
    968                        const char *restrict header,
    969                        bool never_push_hdr,
    970                        size_t body_size,
    971                        const char *restrict body,
    972                        bool complete_response,
    973                        size_t *restrict sent)
    974 {
    975   mhd_iov_ret_type res;
    976   bool send_error;
    977   bool push_hdr;
    978   bool push_body;
    979   MHD_Socket s = connection->sk.fd;
    980 #ifdef mhd_USE_VECT_SEND
    981 #  if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
    982   struct iovec vector[2];
    983 #    ifdef HAVE_SENDMSG
    984   struct msghdr msg;
    985 #    endif /* HAVE_SENDMSG */
    986 #  endif /* HAVE_SENDMSG || HAVE_WRITEV */
    987 #  ifdef _WIN32
    988   WSABUF vector[2];
    989   DWORD vec_sent;
    990 #  endif /* _WIN32 */
    991   bool no_vec; /* Is vector-send() disallowed? */
    992 
    993   no_vec = false;
    994   no_vec = no_vec || (mhd_C_HAS_TLS (connection));
    995 #  if (!defined(HAVE_SENDMSG) || !defined(HAVE_DCLR_MSG_NOSIGNAL)) && \
    996   defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
    997   defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED)
    998   no_vec = no_vec || (!connection->daemon->sigpipe_blocked
    999                       && !connection->sk.props.has_spipe_supp);
   1000 #  endif /* (!HAVE_SENDMSG || ! HAVE_DCLR_MSG_NOSIGNAL) &&
   1001           mhd_SEND_SPIPE_SUPPRESS_POSSIBLE &&
   1002           mhd_SEND_SPIPE_SUPPRESS_NEEDED */
   1003 #endif /* mhd_USE_VECT_SEND */
   1004 
   1005   mhd_assert ((NULL != body) || (0 == body_size));
   1006 
   1007   mhd_assert (MHD_INVALID_SOCKET != s);
   1008   mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage);
   1009 
   1010   push_body = complete_response;
   1011 
   1012   if (!never_push_hdr)
   1013   {
   1014     if (!complete_response)
   1015       push_hdr = true; /* Push the header as the client may react
   1016                         * on header alone while the body data is
   1017                         * being prepared. */
   1018     else
   1019     {
   1020       if (1400 > (header_size + body_size))
   1021         push_hdr = false; /* Do not push the header as complete
   1022                            * reply is already ready and the whole
   1023                            * reply most probably will fit into
   1024                            * the single IP packet. */
   1025       else
   1026         push_hdr = true;   /* Push header alone so client may react
   1027                            * on it while reply body is being delivered. */
   1028     }
   1029   }
   1030   else
   1031     push_hdr = false;
   1032 
   1033   if (complete_response && (0 == body_size))
   1034     push_hdr = true; /* The header alone is equal to the whole response. */
   1035 
   1036 #ifndef mhd_USE_VECT_SEND
   1037   no_vec = (no_vec || true);
   1038 #else  /* mhd_USE_VECT_SEND */
   1039   no_vec = (no_vec || (0 == body_size));
   1040   no_vec = (no_vec || ((sizeof(mhd_iov_elmn_size) <= sizeof(size_t))
   1041                        && (((size_t)mhd_IOV_ELMN_MAX_SIZE) < header_size)));
   1042 #endif /* mhd_USE_VECT_SEND */
   1043 
   1044 
   1045   if (no_vec)
   1046   {
   1047     enum mhd_SocketError ret;
   1048     ret = mhd_send_data (connection,
   1049                          header_size,
   1050                          header,
   1051                          push_hdr,
   1052                          sent);
   1053 
   1054     if ((mhd_SOCKET_ERR_NO_ERROR == ret)
   1055         && (header_size == *sent)
   1056         && (0 != body_size)
   1057         && (header_size < header_size + body_size)
   1058         && (connection->sk.props.is_nonblck))
   1059     {
   1060       size_t sent_b;
   1061       /* The header has been sent completely.
   1062        * Try to send the reply body without waiting for
   1063        * the next round. */
   1064 
   1065       ret = mhd_send_data (connection,
   1066                            body_size,
   1067                            body,
   1068                            push_body,
   1069                            &sent_b);
   1070 
   1071       if (mhd_SOCKET_ERR_NO_ERROR == ret)
   1072         *sent += sent_b;
   1073       else if (mhd_SOCKET_ERR_IS_HARD (ret))
   1074         return ret; /* Unrecoverable error */
   1075 
   1076       return mhd_SOCKET_ERR_NO_ERROR; /* The header has been sent successfully */
   1077     }
   1078     return ret;
   1079   }
   1080 #ifdef mhd_USE_VECT_SEND
   1081 
   1082   mhd_assert (!mhd_C_HAS_TLS (connection));
   1083 
   1084   if (header_size > (header_size + body_size))
   1085   {
   1086     /* Return value limit */
   1087     body_size = SIZE_MAX - header_size;
   1088     complete_response = false;
   1089     push_body = complete_response;
   1090   }
   1091   if ((((mhd_iov_ret_type)(header_size + body_size)) < 0)
   1092       || (((size_t)(mhd_iov_ret_type)(header_size + body_size)) !=
   1093           (header_size + body_size)))
   1094   {
   1095     /* Send sys-call total amount limit */
   1096     body_size = mhd_IOV_RET_MAX_SIZE - header_size;
   1097     complete_response = false;
   1098     push_body = complete_response;
   1099   }
   1100 
   1101   pre_send_setopt (&(connection->sk),
   1102 #  ifdef HAVE_SENDMSG
   1103                    true,
   1104 #  else  /* ! HAVE_SENDMSG */
   1105                    false,
   1106 #  endif /* ! HAVE_SENDMSG */
   1107                    push_hdr || push_body);
   1108   send_error = false;
   1109 #  if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
   1110   vector[0].iov_base = mhd_DROP_CONST (header);
   1111   vector[0].iov_len = header_size;
   1112   vector[1].iov_base = mhd_DROP_CONST (body);
   1113   vector[1].iov_len = body_size;
   1114 
   1115 #    if defined(HAVE_SENDMSG)
   1116   memset (&msg, 0, sizeof(msg));
   1117   msg.msg_name = NULL;
   1118   msg.msg_namelen = 0;
   1119   msg.msg_iov = vector;
   1120   msg.msg_iovlen = 2;
   1121   msg.msg_control = NULL;
   1122   msg.msg_controllen = 0;
   1123   msg.msg_flags = 0;
   1124 
   1125   res = sendmsg (s, &msg, mhd_MSG_NOSIGNAL
   1126 #      ifdef mhd_USE_MSG_MORE
   1127                  | ((push_hdr || push_body) ? 0 : mhd_MSG_MORE)
   1128 #      endif
   1129                  );
   1130 #    elif defined(HAVE_WRITEV)
   1131   res = writev (s, vector, 2);
   1132 #    endif /* HAVE_WRITEV */
   1133   if (0 < res)
   1134     *sent = (size_t)res;
   1135   else
   1136     send_error = true;
   1137 #  endif /* HAVE_SENDMSG || HAVE_WRITEV */
   1138 #  ifdef _WIN32
   1139   if (((mhd_iov_elmn_size)body_size) != body_size)
   1140   {
   1141     /* Send item size limit */
   1142     body_size = mhd_IOV_ELMN_MAX_SIZE;
   1143     complete_response = false;
   1144     push_body = complete_response;
   1145   }
   1146   vector[0].buf = (char *)mhd_DROP_CONST (header);
   1147   vector[0].len = (unsigned long)header_size;
   1148   vector[1].buf = (char *)mhd_DROP_CONST (body);
   1149   vector[1].len = (unsigned long)body_size;
   1150 
   1151   res = WSASend (s, vector, 2, &vec_sent, 0, NULL, NULL);
   1152   if (0 == res)
   1153     *sent = (size_t)vec_sent;
   1154   else
   1155     send_error = true;
   1156 #  endif /* _WIN32 */
   1157 
   1158   if (send_error)
   1159   {
   1160     enum mhd_SocketError err;
   1161 
   1162     err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
   1163 
   1164     if (mhd_SOCKET_ERR_AGAIN == err)
   1165       connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1166                              (((unsigned int)connection->sk.ready)
   1167                               & (~(enum mhd_SocketNetState)
   1168                                  mhd_SOCKET_NET_STATE_SEND_READY));
   1169 
   1170     return err;
   1171   }
   1172   if (((header_size + body_size) > *sent)
   1173       || !connection->sk.props.is_nonblck)
   1174     connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1175                            (((unsigned int)connection->sk.ready)
   1176                             & (~(enum mhd_SocketNetState)
   1177                                mhd_SOCKET_NET_STATE_SEND_READY));
   1178 
   1179   /* If there is a need to push the data from network buffers
   1180    * call post_send_setopt(). */
   1181   if ((push_body)
   1182       && ((header_size + body_size) == *sent))
   1183   {
   1184     /* Complete reply has been sent. */
   1185     /* If TLS connection is used then next final send() will be
   1186      * without MSG_MORE support. If non-TLS connection is used
   1187      * it's unknown whether next 'send' will be plain send() / sendmsg() or
   1188      * sendfile() will be used so assume that next final send() will be
   1189      * the same, like for this response. */
   1190     post_send_setopt (&(connection->sk),
   1191 #  ifdef HAVE_SENDMSG
   1192                       true,  /* Assume the same type of the send function */
   1193 #  else  /* ! HAVE_SENDMSG */
   1194                       false, /* Assume the same type of the send function */
   1195 #  endif /* ! HAVE_SENDMSG */
   1196                       true);
   1197   }
   1198   else if ((push_hdr)
   1199            && (header_size <= *sent))
   1200   {
   1201     /* The header has been sent completely and there is a
   1202      * need to push the header data. */
   1203     /* Luckily the type of send function will be used next is known. */
   1204     post_send_setopt (&(connection->sk),
   1205                       true,
   1206                       true);
   1207   }
   1208 
   1209   return mhd_SOCKET_ERR_NO_ERROR;
   1210 #else  /* ! mhd_USE_VECT_SEND */
   1211   mhd_assert (0 && "Should be unreachable");
   1212   return mhd_SOCKET_ERR_INTERNAL; /* Unreachable. Mute warnings. */
   1213 #endif /* ! mhd_USE_VECT_SEND */
   1214 }
   1215 
   1216 
   1217 #if defined(mhd_USE_SENDFILE)
   1218 
   1219 #  if defined(HAVE_LINUX_SENDFILE) && defined(HAVE_SENDFILE64)
   1220 #    define mhd_off_t off64_t
   1221 #  else
   1222 #    define mhd_off_t off_t
   1223 #  endif
   1224 
   1225 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   1226 MHD_FN_PAR_OUT_ (2) enum mhd_SocketError
   1227 mhd_send_sendfile (struct MHD_Connection *restrict c,
   1228                    size_t *restrict sent)
   1229 {
   1230   enum mhd_SocketError ret;
   1231   const bool used_thr_p_c =
   1232     mhd_D_HAS_THR_PER_CONN (c->daemon);
   1233   const size_t chunk_size =
   1234     used_thr_p_c ?
   1235     mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C : mhd_SENFILE_CHUNK_SIZE;
   1236   const int file_fd = c->rp.response->cntn.file.fd;
   1237   mhd_off_t offset;
   1238   size_t send_size = 0; /* Initialised to mute compiler warning */
   1239   size_t sent_bytes;
   1240   bool push_data;
   1241   bool fallback_to_filereader;
   1242   mhd_assert (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc);
   1243   mhd_assert (MHD_SIZE_UNKNOWN != c->rp.response->cntn_size);
   1244   mhd_assert (chunk_size <= (size_t)SSIZE_MAX);
   1245   mhd_assert (!mhd_C_HAS_TLS (c));
   1246 
   1247   mhd_assert (0 == send_size); /* Mute analyser warning */
   1248   push_data = true;
   1249   if (1)
   1250   {
   1251     bool too_large;
   1252     uint_fast64_t left;
   1253 
   1254     offset = (mhd_off_t)
   1255              (c->rp.rsp_cntn_read_pos + c->rp.response->cntn.file.offset);
   1256     too_large = (((uint_fast64_t)offset) < c->rp.rsp_cntn_read_pos);
   1257     too_large = too_large
   1258                 || (((uint_fast64_t)offset) !=
   1259                     (c->rp.rsp_cntn_read_pos
   1260                      + c->rp.response->cntn.file.offset));
   1261     too_large = too_large || (0 > offset);
   1262     if (too_large)
   1263     {   /* Retry to send with file reader and standard 'send()'. */
   1264       c->rp.response->cntn.file.use_sf = false;
   1265       return mhd_SOCKET_ERR_INTR;
   1266     }
   1267 
   1268     left = c->rp.response->cntn_size - c->rp.rsp_cntn_read_pos;
   1269 
   1270     /* Do not allow system to stick sending on single fast connection:
   1271      * use 128KiB chunks (2MiB for thread-per-connection). */
   1272     if (chunk_size < left) /* This also limit to SSIZE_MAX automatically */
   1273     {
   1274       send_size = chunk_size;
   1275       push_data = false; /* No need to push data, there is more to send. */
   1276     }
   1277     else
   1278       send_size = (size_t)left;
   1279   }
   1280   mhd_assert (0 != send_size);
   1281 
   1282   pre_send_setopt (&(c->sk), false, push_data);
   1283 
   1284   sent_bytes = 0;
   1285   ret = mhd_SOCKET_ERR_NO_ERROR;
   1286   fallback_to_filereader = false;
   1287 #  if defined(HAVE_LINUX_SENDFILE)
   1288   if (1)
   1289   {
   1290     ssize_t res;
   1291 #    ifndef HAVE_SENDFILE64
   1292     ret = sendfile (c->sk.fd,
   1293                     file_fd,
   1294                     &offset,
   1295                     send_size);
   1296 #    else  /* HAVE_SENDFILE64 */
   1297     res = sendfile64 (c->sk.fd,
   1298                       file_fd,
   1299                       &offset,
   1300                       send_size);
   1301 #    endif /* HAVE_SENDFILE64 */
   1302     if (0 > res)
   1303     {
   1304       const int sk_err = mhd_SCKT_GET_LERR ();
   1305 
   1306       if ((EINVAL == sk_err)
   1307           || (EOVERFLOW == sk_err) ||
   1308 #    ifdef EIO
   1309           (EIO == sk_err) ||
   1310 #    endif
   1311 #    ifdef EAFNOSUPPORT
   1312           (EAFNOSUPPORT == sk_err) ||
   1313 #    endif
   1314           (EOPNOTSUPP == sk_err))
   1315         fallback_to_filereader = true;
   1316       else
   1317         ret = mhd_socket_error_get_from_sys_err (sk_err);
   1318     }
   1319     else
   1320       sent_bytes = (size_t)res;
   1321   }
   1322 #  elif defined(HAVE_FREEBSD_SENDFILE)
   1323   if (1)
   1324   {
   1325     off_t sent_bytes_offt = 0;
   1326     int flags = 0;
   1327     bool sent_something = false;
   1328 #    ifdef SF_FLAGS
   1329     flags = used_thr_p_c ?
   1330             freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
   1331 #    endif /* SF_FLAGS */
   1332     if (0 != sendfile (file_fd,
   1333                        c->sk.fd,
   1334                        offset,
   1335                        send_size,
   1336                        NULL,
   1337                        &sent_bytes_offt,
   1338                        flags))
   1339     {
   1340       const int sk_err = mhd_SCKT_GET_LERR ();
   1341 
   1342       sent_something =
   1343         (((EAGAIN == sk_err) || (EBUSY == sk_err) || (EINTR == sk_err))
   1344          && (0 != sent_bytes_offt));
   1345 
   1346       if (!sent_something)
   1347       {
   1348         enum mhd_SocketError err;
   1349         if ((EINVAL == sk_err)
   1350             || (EIO == sk_err)
   1351             || (EOPNOTSUPP == sk_err))
   1352           fallback_to_filereader = true;
   1353         else
   1354           ret = mhd_socket_error_get_from_sys_err (sk_err);
   1355       }
   1356     }
   1357     else
   1358       sent_something = true;
   1359 
   1360     if (sent_something)
   1361     {
   1362       mhd_assert (0 <= sent_bytes_offt);
   1363       mhd_assert (SIZE_MAX >= sent_bytes_offt);
   1364       sent_bytes = (size_t)sent_bytes_offt;
   1365     }
   1366   }
   1367 #  elif defined(HAVE_DARWIN_SENDFILE)
   1368   if (1)
   1369   {
   1370     off_t len;
   1371     bool sent_something;
   1372 
   1373     sent_something = false;
   1374     len = (off_t)send_size;  /* chunk always fit */
   1375 
   1376     if (0 != sendfile (file_fd,
   1377                        c->sk.fd,
   1378                        offset,
   1379                        &len,
   1380                        NULL,
   1381                        0))
   1382     {
   1383       const int sk_err = mhd_SCKT_GET_LERR ();
   1384 
   1385       sent_something =
   1386         ((EAGAIN == sk_err) || (EINTR == sk_err))
   1387         && (0 != len);
   1388 
   1389       if (!sent_something)
   1390       {
   1391         if ((ENOTSUP == sk_err)
   1392             || (EOPNOTSUPP == sk_err))
   1393           fallback_to_filereader = true;
   1394         else
   1395           ret = mhd_socket_error_get_from_sys_err (sk_err);
   1396       }
   1397     }
   1398     else
   1399       sent_something = true;
   1400 
   1401     if (sent_something)
   1402     {
   1403       mhd_assert (0 <= len);
   1404       mhd_assert (SIZE_MAX >= len);
   1405       sent_bytes = (size_t)len;
   1406     }
   1407   }
   1408 #  else
   1409 #    error No sendfile() function
   1410 #  endif
   1411 
   1412   mhd_assert (send_size >= sent_bytes);
   1413 
   1414   /* Some platforms indicate "beyond of the end of the file" by returning
   1415    * success with zero bytes. Let filereader to re-detect this kind of error. */
   1416   if ((fallback_to_filereader)
   1417       || ((mhd_SOCKET_ERR_NO_ERROR == ret) && (0 == sent_bytes)))
   1418   {   /* Retry to send with file reader and standard 'send()'. */
   1419     c->rp.response->cntn.file.use_sf = false;
   1420     return mhd_SOCKET_ERR_INTR;
   1421   }
   1422 
   1423   if ((mhd_SOCKET_ERR_AGAIN == ret) || !c->sk.props.is_nonblck
   1424       || ((mhd_SOCKET_ERR_NO_ERROR == ret) && (send_size > sent_bytes)))
   1425     c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1426                   (((unsigned int)c->sk.ready)
   1427                    & (~(enum mhd_SocketNetState)
   1428                       mhd_SOCKET_NET_STATE_SEND_READY));
   1429 
   1430   if (mhd_SOCKET_ERR_NO_ERROR != ret)
   1431     return ret;
   1432 
   1433   /* If there is a need to push the data from network buffers
   1434    * call post_send_setopt(). */
   1435   /* It's unknown whether sendfile() will be used in the next
   1436    * response so assume that next response will be the same. */
   1437   if ((push_data)
   1438       && (send_size == sent_bytes))
   1439     post_send_setopt (&(c->sk), true, push_data);
   1440 
   1441   *sent = sent_bytes;
   1442   return ret;
   1443 }
   1444 
   1445 
   1446 #endif /* mhd_USE_SENDFILE */
   1447 
   1448 #if defined(mhd_USE_VECT_SEND)
   1449 
   1450 
   1451 /**
   1452  * Function sends iov data by system sendmsg or writev function.
   1453  *
   1454  * Connection must be in non-TLS (non-HTTPS) mode.
   1455  *
   1456  * @param connection the MHD connection structure
   1457  * @param r_iov the pointer to iov data structure with tracking
   1458  * @param push_data set to true to force push the data to the network from
   1459  *                  system buffers (usually set for the last piece of data),
   1460  *                  set to false to prefer holding incomplete network packets
   1461  *                  (more data will be send for the same reply).
   1462  * @param[out] sent the pointer to get amount of actually sent bytes
   1463  * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
   1464  *         the sent size) or socket error
   1465  */
   1466 static MHD_FN_PAR_NONNULL_ALL_
   1467 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
   1468 send_iov_nontls (struct MHD_Connection *restrict connection,
   1469                  struct mhd_iovec_track *const restrict r_iov,
   1470                  bool push_data,
   1471                  size_t *restrict sent)
   1472 {
   1473   bool send_error;
   1474   size_t items_to_send;
   1475 #  ifndef MHD_SOCKETS_KIND_WINSOCK
   1476   ssize_t res;
   1477 #  endif
   1478 #  ifdef HAVE_SENDMSG
   1479   struct msghdr msg;
   1480 #  elif defined(MHD_SOCKETS_KIND_WINSOCK)
   1481   DWORD bytes_sent;
   1482   DWORD cnt_w;
   1483 #  endif /* MHD_SOCKETS_KIND_WINSOCK */
   1484 
   1485   mhd_assert (!mhd_C_HAS_TLS (connection));
   1486   mhd_assert (!mhd_D_HAS_TLS (connection->daemon));
   1487 
   1488   mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd);
   1489   mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage);
   1490 
   1491   send_error = false;
   1492   items_to_send = r_iov->cnt - r_iov->sent;
   1493 #  ifdef mhd_IOV_MAX
   1494   if (mhd_IOV_MAX < items_to_send)
   1495   {
   1496     mhd_assert (0 < mhd_IOV_MAX);
   1497     if (0 == mhd_IOV_MAX)
   1498       return mhd_SOCKET_ERR_INTERNAL; /* Should never happen */
   1499     items_to_send = mhd_IOV_MAX;
   1500     push_data = false; /* Incomplete response */
   1501   }
   1502 #  endif /* mhd_IOV_MAX */
   1503 #  ifdef HAVE_SENDMSG
   1504   memset (&msg, 0, sizeof(struct msghdr));
   1505   msg.msg_name = NULL;
   1506   msg.msg_namelen = 0;
   1507   msg.msg_iov = r_iov->iov + r_iov->sent;
   1508   msg.msg_iovlen = items_to_send;
   1509   msg.msg_control = NULL;
   1510   msg.msg_controllen = 0;
   1511   msg.msg_flags = 0;
   1512 
   1513   pre_send_setopt (&(connection->sk), true, push_data);
   1514   res = sendmsg (connection->sk.fd, &msg,
   1515                  mhd_MSG_NOSIGNAL
   1516 #    ifdef mhd_USE_MSG_MORE
   1517                  | (push_data ? 0 : MSG_MORE)
   1518 #    endif
   1519                  );
   1520   if (0 < res)
   1521     *sent = (size_t)res;
   1522   else
   1523     send_error = true;
   1524 #  elif defined(HAVE_WRITEV)
   1525   pre_send_setopt (&(connection->sk), false, push_data);
   1526   res = writev (connection->sk.fd, r_iov->iov + r_iov->sent,
   1527                 items_to_send);
   1528   if (0 < res)
   1529     *sent = (size_t)res;
   1530   else
   1531     send_error = true;
   1532 #  elif defined(MHD_SOCKETS_KIND_WINSOCK)
   1533 #    ifdef _WIN64
   1534   if (items_to_send > ULONG_MAX)
   1535   {
   1536     cnt_w = ULONG_MAX;
   1537     push_data = false; /* Incomplete response */
   1538   }
   1539   else
   1540     cnt_w = (DWORD)items_to_send;
   1541 #    else  /* ! _WIN64 */
   1542   cnt_w = (DWORD)items_to_send;
   1543 #    endif /* ! _WIN64 */
   1544   pre_send_setopt (&(connection->sk), true, push_data);
   1545   if (0 == WSASend (connection->sk.fd,
   1546                     (LPWSABUF)(r_iov->iov + r_iov->sent),
   1547                     cnt_w,
   1548                     &bytes_sent, 0, NULL, NULL))
   1549     *sent = (size_t)bytes_sent;
   1550   else
   1551     send_error = true;
   1552 #  else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_SOCKETS_KIND_WINSOCK */
   1553 #    error No vector-send function available
   1554 #  endif
   1555 
   1556   if (send_error)
   1557   {
   1558     enum mhd_SocketError err;
   1559 
   1560     err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
   1561 
   1562     if (mhd_SOCKET_ERR_AGAIN == err)
   1563       connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1564                              (((unsigned int)connection->sk.ready)
   1565                               & (~(enum mhd_SocketNetState)
   1566                                  mhd_SOCKET_NET_STATE_SEND_READY));
   1567 
   1568     return err;
   1569   }
   1570 
   1571   /* Some data has been sent */
   1572   if (1)
   1573   {
   1574     size_t track_sent = (size_t)*sent;
   1575 
   1576     if (!connection->sk.props.is_nonblck)
   1577       connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1578                              (((unsigned int)connection->sk.ready)
   1579                               & (~(enum mhd_SocketNetState)
   1580                                  mhd_SOCKET_NET_STATE_SEND_READY));
   1581 
   1582     /* Adjust the internal tracking information for the iovec to
   1583      * take this last send into account. */
   1584     while ((0 != track_sent) && (r_iov->iov[r_iov->sent].iov_len <= track_sent))
   1585     {
   1586       track_sent -= r_iov->iov[r_iov->sent].iov_len;
   1587       r_iov->sent++; /* The iov element has been completely sent */
   1588       mhd_assert ((r_iov->cnt > r_iov->sent) || (0 == track_sent));
   1589     }
   1590 
   1591     if (r_iov->cnt == r_iov->sent)
   1592       post_send_setopt (&(connection->sk), true, push_data);
   1593     else
   1594     {
   1595       connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1596                              (((unsigned int)connection->sk.ready)
   1597                               & (~(enum mhd_SocketNetState)
   1598                                  mhd_SOCKET_NET_STATE_SEND_READY));
   1599       if (0 != track_sent)
   1600       {
   1601         mhd_assert (r_iov->cnt > r_iov->sent);
   1602         /* The last iov element has been partially sent */
   1603         r_iov->iov[r_iov->sent].iov_base =
   1604           (mhd_IOV_ELMN_PTR_TYPE)
   1605           (((uint8_t *)r_iov->iov[r_iov->sent].iov_base)
   1606            + track_sent);
   1607         r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size)track_sent;
   1608       }
   1609     }
   1610   }
   1611 
   1612   return mhd_SOCKET_ERR_NO_ERROR;
   1613 }
   1614 
   1615 
   1616 #endif /* mhd_USE_VECT_SEND */
   1617 
   1618 #if !defined(mhd_USE_VECT_SEND) || defined(MHD_SUPPORT_HTTPS) || \
   1619   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   1620 
   1621 
   1622 /**
   1623  * Function sends iov data by sending buffers one-by-one by standard
   1624  * data send function.
   1625  *
   1626  * Connection could be in HTTPS or non-HTTPS mode.
   1627  *
   1628  * @param connection the MHD connection structure
   1629  * @param r_iov the pointer to iov data structure with tracking
   1630  * @param push_data set to true to force push the data to the network from
   1631  *                  system buffers (usually set for the last piece of data),
   1632  *                  set to false to prefer holding incomplete network packets
   1633  *                  (more data will be send for the same reply).
   1634  * @param[out] sent the pointer to get amount of actually sent bytes
   1635  * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
   1636  *         the sent size) or socket error
   1637  */
   1638 static MHD_FN_PAR_NONNULL_ALL_
   1639 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
   1640 send_iov_emu (struct MHD_Connection *restrict connection,
   1641               struct mhd_iovec_track *const restrict r_iov,
   1642               bool push_data,
   1643               size_t *restrict sent)
   1644 {
   1645   const bool non_blk = connection->sk.props.is_nonblck;
   1646   size_t total_sent;
   1647   size_t max_elelements_to_sent;
   1648 
   1649   mhd_assert (NULL != r_iov->iov);
   1650   total_sent = 0;
   1651   max_elelements_to_sent = 8; /* Do not make too many sys-calls for just one connection */
   1652   do
   1653   {
   1654     enum mhd_SocketError res;
   1655     size_t sent_el_size;
   1656 
   1657     if (total_sent > (size_t)(r_iov->iov[r_iov->sent].iov_len + total_sent))
   1658       break; /* return value would overflow */
   1659 
   1660     res = mhd_send_data (connection,
   1661                          r_iov->iov[r_iov->sent].iov_len,
   1662                          (const char *)r_iov->iov[r_iov->sent].iov_base,
   1663                          push_data && (r_iov->cnt == r_iov->sent + 1),
   1664                          &sent_el_size);
   1665     if (mhd_SOCKET_ERR_NO_ERROR == res)
   1666     {
   1667       /* Result is an error */
   1668       if (0 == total_sent)
   1669         return res; /* Nothing was sent, return error as is */
   1670 
   1671       if (mhd_SOCKET_ERR_IS_HARD (res))
   1672         return res; /* Any kind of a hard error */
   1673 
   1674       break; /* Return the amount of the sent data */
   1675     }
   1676 
   1677     total_sent += sent_el_size;
   1678 
   1679     if (r_iov->iov[r_iov->sent].iov_len != sent_el_size)
   1680     {
   1681       /* Incomplete buffer has been sent.
   1682        * Adjust buffer of the last element. */
   1683       r_iov->iov[r_iov->sent].iov_base =
   1684         (mhd_IOV_ELMN_PTR_TYPE)
   1685         (((uint8_t *)r_iov->iov[r_iov->sent].iov_base) + sent_el_size);
   1686       r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size)sent_el_size;
   1687 
   1688       break; /* Return the amount of the sent data */
   1689     }
   1690     /* The iov element has been completely sent */
   1691     r_iov->sent++;
   1692   } while ((r_iov->cnt > r_iov->sent) && 0 != (--max_elelements_to_sent)
   1693            && (non_blk));
   1694 
   1695   mhd_assert (0 != total_sent);
   1696   *sent = total_sent;
   1697   return mhd_SOCKET_ERR_NO_ERROR;
   1698 }
   1699 
   1700 
   1701 #endif /* !mhd_USE_VECT_SEND || MHD_SUPPORT_HTTPS
   1702           || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1703 
   1704 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   1705 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
   1706 mhd_send_iovec (struct MHD_Connection *restrict connection,
   1707                 struct mhd_iovec_track *const restrict r_iov,
   1708                 bool push_data,
   1709                 size_t *restrict sent)
   1710 {
   1711 #ifdef mhd_USE_VECT_SEND
   1712 #  if defined(MHD_SUPPORT_HTTPS) || \
   1713   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   1714   bool use_iov_send = true;
   1715 #  endif /* MHD_SUPPORT_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1716 #endif /* mhd_USE_VECT_SEND */
   1717 
   1718   mhd_assert (NULL != connection->rp.resp_iov.iov);
   1719   mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == \
   1720               connection->rp.response->cntn_dtype);
   1721   mhd_assert (connection->rp.resp_iov.cnt > connection->rp.resp_iov.sent);
   1722 #ifdef mhd_USE_VECT_SEND
   1723 #  if defined(MHD_SUPPORT_HTTPS) || \
   1724   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   1725   use_iov_send = use_iov_send
   1726                  && (!mhd_C_HAS_TLS (connection));
   1727 #    ifdef mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
   1728   use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked
   1729                                   || connection->sk.props.has_spipe_supp);
   1730 #    endif /* mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1731   if (use_iov_send)
   1732 #  endif /* MHD_SUPPORT_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1733   return send_iov_nontls (connection, r_iov, push_data, sent);
   1734 #endif /* mhd_USE_VECT_SEND */
   1735 
   1736 #if !defined(mhd_USE_VECT_SEND) || defined(MHD_SUPPORT_HTTPS) || \
   1737   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   1738   return send_iov_emu (connection, r_iov, push_data, sent);
   1739 #endif /* !mhd_USE_VECT_SEND || MHD_SUPPORT_HTTPS
   1740           || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1741 }