libmicrohttpd2

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

mhd_send.c (58016B)


      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_IN_SIZE_ (3,2) MHD_FN_PAR_IN_SIZE_ (6,5) enum mhd_SocketError
    964 mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
    965                        size_t header_size,
    966                        const char *restrict header,
    967                        bool never_push_hdr,
    968                        size_t body_size,
    969                        const char *restrict body,
    970                        bool complete_response,
    971                        size_t *restrict sent)
    972 {
    973   mhd_iov_ret_type res;
    974   bool send_error;
    975   bool push_hdr;
    976   bool push_body;
    977   MHD_Socket s = connection->sk.fd;
    978 #ifdef mhd_USE_VECT_SEND
    979 #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
    980   struct iovec vector[2];
    981 #ifdef HAVE_SENDMSG
    982   struct msghdr msg;
    983 #endif /* HAVE_SENDMSG */
    984 #endif /* HAVE_SENDMSG || HAVE_WRITEV */
    985 #ifdef _WIN32
    986   WSABUF vector[2];
    987   DWORD vec_sent;
    988 #endif /* _WIN32 */
    989   bool no_vec; /* Is vector-send() disallowed? */
    990 
    991   no_vec = false;
    992   no_vec = no_vec || (mhd_C_HAS_TLS (connection));
    993 #if (! defined(HAVE_SENDMSG) || ! defined(HAVE_DCLR_MSG_NOSIGNAL) ) && \
    994   defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
    995   defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED)
    996   no_vec = no_vec || (! connection->daemon->sigpipe_blocked &&
    997                       ! connection->sk.props.has_spipe_supp);
    998 #endif /* (!HAVE_SENDMSG || ! HAVE_DCLR_MSG_NOSIGNAL) &&
    999           mhd_SEND_SPIPE_SUPPRESS_POSSIBLE &&
   1000           mhd_SEND_SPIPE_SUPPRESS_NEEDED */
   1001 #endif /* mhd_USE_VECT_SEND */
   1002 
   1003   mhd_assert ( (NULL != body) || (0 == body_size) );
   1004 
   1005   mhd_assert (MHD_INVALID_SOCKET != s);
   1006   mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage);
   1007 
   1008   push_body = complete_response;
   1009 
   1010   if (! never_push_hdr)
   1011   {
   1012     if (! complete_response)
   1013       push_hdr = true; /* Push the header as the client may react
   1014                         * on header alone while the body data is
   1015                         * being prepared. */
   1016     else
   1017     {
   1018       if (1400 > (header_size + body_size))
   1019         push_hdr = false; /* Do not push the header as complete
   1020                            * reply is already ready and the whole
   1021                            * reply most probably will fit into
   1022                            * the single IP packet. */
   1023       else
   1024         push_hdr = true;   /* Push header alone so client may react
   1025                            * on it while reply body is being delivered. */
   1026     }
   1027   }
   1028   else
   1029     push_hdr = false;
   1030 
   1031   if (complete_response && (0 == body_size))
   1032     push_hdr = true; /* The header alone is equal to the whole response. */
   1033 
   1034 #ifndef mhd_USE_VECT_SEND
   1035   no_vec = (no_vec || true);
   1036 #else  /* mhd_USE_VECT_SEND */
   1037   no_vec = (no_vec || (0 == body_size));
   1038   no_vec = (no_vec || ((sizeof(mhd_iov_elmn_size) <= sizeof(size_t)) &&
   1039                        (((size_t) mhd_IOV_ELMN_MAX_SIZE) < header_size)));
   1040 #endif /* mhd_USE_VECT_SEND */
   1041 
   1042 
   1043   if (no_vec)
   1044   {
   1045     enum mhd_SocketError ret;
   1046     ret = mhd_send_data (connection,
   1047                          header_size,
   1048                          header,
   1049                          push_hdr,
   1050                          sent);
   1051 
   1052     if ((mhd_SOCKET_ERR_NO_ERROR == ret) &&
   1053         (header_size == *sent) &&
   1054         (0 != body_size) &&
   1055         (header_size < header_size + body_size) &&
   1056         (connection->sk.props.is_nonblck))
   1057     {
   1058       size_t sent_b;
   1059       /* The header has been sent completely.
   1060        * Try to send the reply body without waiting for
   1061        * the next round. */
   1062 
   1063       ret = mhd_send_data (connection,
   1064                            body_size,
   1065                            body,
   1066                            push_body,
   1067                            &sent_b);
   1068 
   1069       if (mhd_SOCKET_ERR_NO_ERROR == ret)
   1070         *sent += sent_b;
   1071       else if (mhd_SOCKET_ERR_IS_HARD (ret))
   1072         return ret; /* Unrecoverable error */
   1073 
   1074       return mhd_SOCKET_ERR_NO_ERROR; /* The header has been sent successfully */
   1075     }
   1076     return ret;
   1077   }
   1078 #ifdef mhd_USE_VECT_SEND
   1079 
   1080   mhd_assert (! mhd_C_HAS_TLS (connection));
   1081 
   1082   if (header_size > (header_size + body_size))
   1083   {
   1084     /* Return value limit */
   1085     body_size = SIZE_MAX - header_size;
   1086     complete_response = false;
   1087     push_body = complete_response;
   1088   }
   1089   if (((mhd_iov_ret_type) (header_size + body_size)) < 0 ||
   1090       ((size_t) (mhd_iov_ret_type) (header_size + body_size)) !=
   1091       (header_size + body_size))
   1092   {
   1093     /* Send sys-call total amount limit */
   1094     body_size = mhd_IOV_RET_MAX_SIZE - header_size;
   1095     complete_response = false;
   1096     push_body = complete_response;
   1097   }
   1098 
   1099   pre_send_setopt (&(connection->sk),
   1100 #ifdef HAVE_SENDMSG
   1101                    true,
   1102 #else  /* ! HAVE_SENDMSG */
   1103                    false,
   1104 #endif /* ! HAVE_SENDMSG */
   1105                    push_hdr || push_body);
   1106   send_error = false;
   1107 #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
   1108   vector[0].iov_base = mhd_DROP_CONST (header);
   1109   vector[0].iov_len = header_size;
   1110   vector[1].iov_base = mhd_DROP_CONST (body);
   1111   vector[1].iov_len = body_size;
   1112 
   1113 #if defined(HAVE_SENDMSG)
   1114   memset (&msg, 0, sizeof(msg));
   1115   msg.msg_name = NULL;
   1116   msg.msg_namelen = 0;
   1117   msg.msg_iov = vector;
   1118   msg.msg_iovlen = 2;
   1119   msg.msg_control = NULL;
   1120   msg.msg_controllen = 0;
   1121   msg.msg_flags = 0;
   1122 
   1123   res = sendmsg (s, &msg, mhd_MSG_NOSIGNAL
   1124 #  ifdef mhd_USE_MSG_MORE
   1125                  | ((push_hdr || push_body) ? 0 : mhd_MSG_MORE)
   1126 #  endif
   1127                  );
   1128 #elif defined(HAVE_WRITEV)
   1129   res = writev (s, vector, 2);
   1130 #endif /* HAVE_WRITEV */
   1131   if (0 < res)
   1132     *sent = (size_t) res;
   1133   else
   1134     send_error = true;
   1135 #endif /* HAVE_SENDMSG || HAVE_WRITEV */
   1136 #ifdef _WIN32
   1137   if (((mhd_iov_elmn_size) body_size) != body_size)
   1138   {
   1139     /* Send item size limit */
   1140     body_size = mhd_IOV_ELMN_MAX_SIZE;
   1141     complete_response = false;
   1142     push_body = complete_response;
   1143   }
   1144   vector[0].buf = (char *) mhd_DROP_CONST (header);
   1145   vector[0].len = (unsigned long) header_size;
   1146   vector[1].buf = (char *) mhd_DROP_CONST (body);
   1147   vector[1].len = (unsigned long) body_size;
   1148 
   1149   res = WSASend (s, vector, 2, &vec_sent, 0, NULL, NULL);
   1150   if (0 == res)
   1151     *sent = (size_t) vec_sent;
   1152   else
   1153     send_error = true;
   1154 #endif /* _WIN32 */
   1155 
   1156   if (send_error)
   1157   {
   1158     enum mhd_SocketError err;
   1159 
   1160     err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
   1161 
   1162     if (mhd_SOCKET_ERR_AGAIN == err)
   1163       connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1164                              (((unsigned int) connection->sk.ready)
   1165                               & (~(enum mhd_SocketNetState)
   1166                                  mhd_SOCKET_NET_STATE_SEND_READY));
   1167 
   1168     return err;
   1169   }
   1170   if (((header_size + body_size) > *sent)
   1171       || ! connection->sk.props.is_nonblck)
   1172     connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1173                            (((unsigned int) connection->sk.ready)
   1174                             & (~(enum mhd_SocketNetState)
   1175                                mhd_SOCKET_NET_STATE_SEND_READY));
   1176 
   1177   /* If there is a need to push the data from network buffers
   1178    * call post_send_setopt(). */
   1179   if ( (push_body) &&
   1180        ((header_size + body_size) == *sent) )
   1181   {
   1182     /* Complete reply has been sent. */
   1183     /* If TLS connection is used then next final send() will be
   1184      * without MSG_MORE support. If non-TLS connection is used
   1185      * it's unknown whether next 'send' will be plain send() / sendmsg() or
   1186      * sendfile() will be used so assume that next final send() will be
   1187      * the same, like for this response. */
   1188     post_send_setopt (&(connection->sk),
   1189 #ifdef HAVE_SENDMSG
   1190                       true,  /* Assume the same type of the send function */
   1191 #else  /* ! HAVE_SENDMSG */
   1192                       false, /* Assume the same type of the send function */
   1193 #endif /* ! HAVE_SENDMSG */
   1194                       true);
   1195   }
   1196   else if ( (push_hdr) &&
   1197             (header_size <= *sent))
   1198   {
   1199     /* The header has been sent completely and there is a
   1200      * need to push the header data. */
   1201     /* Luckily the type of send function will be used next is known. */
   1202     post_send_setopt (&(connection->sk),
   1203                       true,
   1204                       true);
   1205   }
   1206 
   1207   return mhd_SOCKET_ERR_NO_ERROR;
   1208 #else  /* ! mhd_USE_VECT_SEND */
   1209   mhd_assert (0 && "Should be unreachable");
   1210   return mhd_SOCKET_ERR_INTERNAL; /* Unreachable. Mute warnings. */
   1211 #endif /* ! mhd_USE_VECT_SEND */
   1212 }
   1213 
   1214 
   1215 #if defined(mhd_USE_SENDFILE)
   1216 
   1217 #if defined(HAVE_LINUX_SENDFILE) && defined(HAVE_SENDFILE64)
   1218 #  define mhd_off_t off64_t
   1219 #else
   1220 #  define mhd_off_t off_t
   1221 #endif
   1222 
   1223 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   1224 MHD_FN_PAR_OUT_ (2) enum mhd_SocketError
   1225 mhd_send_sendfile (struct MHD_Connection *restrict c,
   1226                    size_t *restrict sent)
   1227 {
   1228   enum mhd_SocketError ret;
   1229   const bool used_thr_p_c =
   1230     mhd_D_HAS_THR_PER_CONN (c->daemon);
   1231   const size_t chunk_size =
   1232     used_thr_p_c ?
   1233     mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C : mhd_SENFILE_CHUNK_SIZE;
   1234   const int file_fd = c->rp.response->cntn.file.fd;
   1235   mhd_off_t offset;
   1236   size_t send_size = 0; /* Initialised to mute compiler warning */
   1237   size_t sent_bytes;
   1238   bool push_data;
   1239   bool fallback_to_filereader;
   1240   mhd_assert (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc);
   1241   mhd_assert (MHD_SIZE_UNKNOWN != c->rp.response->cntn_size);
   1242   mhd_assert (chunk_size <= (size_t) SSIZE_MAX);
   1243   mhd_assert (! mhd_C_HAS_TLS (c));
   1244 
   1245   mhd_assert (0 == send_size); /* Mute analyser warning */
   1246   push_data = true;
   1247   if (1)
   1248   {
   1249     bool too_large;
   1250     uint_fast64_t left;
   1251 
   1252     offset = (mhd_off_t)
   1253              (c->rp.rsp_cntn_read_pos + c->rp.response->cntn.file.offset);
   1254     too_large = (((uint_fast64_t) offset) < c->rp.rsp_cntn_read_pos);
   1255     too_large = too_large ||
   1256                 (((uint_fast64_t) offset) !=
   1257                  (c->rp.rsp_cntn_read_pos + c->rp.response->cntn.file.offset));
   1258     too_large = too_large || (0 > offset);
   1259     if (too_large)
   1260     {   /* Retry to send with file reader and standard 'send()'. */
   1261       c->rp.response->cntn.file.use_sf = false;
   1262       return mhd_SOCKET_ERR_INTR;
   1263     }
   1264 
   1265     left = c->rp.response->cntn_size - c->rp.rsp_cntn_read_pos;
   1266 
   1267     /* Do not allow system to stick sending on single fast connection:
   1268      * use 128KiB chunks (2MiB for thread-per-connection). */
   1269     if (chunk_size < left) /* This also limit to SSIZE_MAX automatically */
   1270     {
   1271       send_size = chunk_size;
   1272       push_data = false; /* No need to push data, there is more to send. */
   1273     }
   1274     else
   1275       send_size = (size_t) left;
   1276   }
   1277   mhd_assert (0 != send_size);
   1278 
   1279   pre_send_setopt (&(c->sk), false, push_data);
   1280 
   1281   sent_bytes = 0;
   1282   ret = mhd_SOCKET_ERR_NO_ERROR;
   1283   fallback_to_filereader = false;
   1284 #if defined(HAVE_LINUX_SENDFILE)
   1285   if (1)
   1286   {
   1287     ssize_t res;
   1288 #ifndef HAVE_SENDFILE64
   1289     ret = sendfile (c->sk.fd,
   1290                     file_fd,
   1291                     &offset,
   1292                     send_size);
   1293 #else  /* HAVE_SENDFILE64 */
   1294     res = sendfile64 (c->sk.fd,
   1295                       file_fd,
   1296                       &offset,
   1297                       send_size);
   1298 #endif /* HAVE_SENDFILE64 */
   1299     if (0 > res)
   1300     {
   1301       const int sk_err = mhd_SCKT_GET_LERR ();
   1302 
   1303       if ((EINVAL == sk_err) ||
   1304           (EOVERFLOW == sk_err) ||
   1305 #ifdef EIO
   1306           (EIO == sk_err) ||
   1307 #endif
   1308 #ifdef EAFNOSUPPORT
   1309           (EAFNOSUPPORT == sk_err) ||
   1310 #endif
   1311           (EOPNOTSUPP == sk_err))
   1312         fallback_to_filereader = true;
   1313       else
   1314         ret = mhd_socket_error_get_from_sys_err (sk_err);
   1315     }
   1316     else
   1317       sent_bytes = (size_t) res;
   1318   }
   1319 #elif defined(HAVE_FREEBSD_SENDFILE)
   1320   if (1)
   1321   {
   1322     off_t sent_bytes_offt = 0;
   1323     int flags = 0;
   1324     bool sent_something = false;
   1325 #ifdef SF_FLAGS
   1326     flags = used_thr_p_c ?
   1327             freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
   1328 #endif /* SF_FLAGS */
   1329     if (0 != sendfile (file_fd,
   1330                        c->sk.fd,
   1331                        offset,
   1332                        send_size,
   1333                        NULL,
   1334                        &sent_bytes_offt,
   1335                        flags))
   1336     {
   1337       const int sk_err = mhd_SCKT_GET_LERR ();
   1338 
   1339       sent_something =
   1340         (((EAGAIN == sk_err) || (EBUSY == sk_err) || (EINTR == sk_err)) &&
   1341          (0 != sent_bytes_offt));
   1342 
   1343       if (! sent_something)
   1344       {
   1345         enum mhd_SocketError err;
   1346         if ((EINVAL == sk_err) ||
   1347             (EIO == sk_err) ||
   1348             (EOPNOTSUPP == sk_err))
   1349           fallback_to_filereader = true;
   1350         else
   1351           ret = mhd_socket_error_get_from_sys_err (sk_err);
   1352       }
   1353     }
   1354     else
   1355       sent_something = true;
   1356 
   1357     if (sent_something)
   1358     {
   1359       mhd_assert (0 <= sent_bytes_offt);
   1360       mhd_assert (SIZE_MAX >= sent_bytes_offt);
   1361       sent_bytes = (size_t) sent_bytes_offt;
   1362     }
   1363   }
   1364 #elif defined(HAVE_DARWIN_SENDFILE)
   1365   if (1)
   1366   {
   1367     off_t len;
   1368     bool sent_something;
   1369 
   1370     sent_something = false;
   1371     len = (off_t) send_size; /* chunk always fit */
   1372 
   1373     if (0 != sendfile (file_fd,
   1374                        c->sk.fd,
   1375                        offset,
   1376                        &len,
   1377                        NULL,
   1378                        0))
   1379     {
   1380       const int sk_err = mhd_SCKT_GET_LERR ();
   1381 
   1382       sent_something =
   1383         ((EAGAIN == sk_err) || (EINTR == sk_err)) &&
   1384         (0 != len);
   1385 
   1386       if (! sent_something)
   1387       {
   1388         if ((ENOTSUP == sk_err) ||
   1389             (EOPNOTSUPP == sk_err))
   1390           fallback_to_filereader = true;
   1391         else
   1392           ret = mhd_socket_error_get_from_sys_err (sk_err);
   1393       }
   1394     }
   1395     else
   1396       sent_something = true;
   1397 
   1398     if (sent_something)
   1399     {
   1400       mhd_assert (0 <= len);
   1401       mhd_assert (SIZE_MAX >= len);
   1402       sent_bytes = (size_t) len;
   1403     }
   1404   }
   1405 #else
   1406 #error No sendfile() function
   1407 #endif
   1408 
   1409   mhd_assert (send_size >= sent_bytes);
   1410 
   1411   /* Some platforms indicate "beyond of the end of the file" by returning
   1412    * success with zero bytes. Let filereader to re-detect this kind of error. */
   1413   if ((fallback_to_filereader) ||
   1414       ((mhd_SOCKET_ERR_NO_ERROR == ret) && (0 == sent_bytes)))
   1415   {   /* Retry to send with file reader and standard 'send()'. */
   1416     c->rp.response->cntn.file.use_sf = false;
   1417     return mhd_SOCKET_ERR_INTR;
   1418   }
   1419 
   1420   if ((mhd_SOCKET_ERR_AGAIN == ret) || ! c->sk.props.is_nonblck ||
   1421       ((mhd_SOCKET_ERR_NO_ERROR == ret) && (send_size > sent_bytes)))
   1422     c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1423                   (((unsigned int) c->sk.ready)
   1424                    & (~(enum mhd_SocketNetState)
   1425                       mhd_SOCKET_NET_STATE_SEND_READY));
   1426 
   1427   if (mhd_SOCKET_ERR_NO_ERROR != ret)
   1428     return ret;
   1429 
   1430   /* If there is a need to push the data from network buffers
   1431    * call post_send_setopt(). */
   1432   /* It's unknown whether sendfile() will be used in the next
   1433    * response so assume that next response will be the same. */
   1434   if ((push_data) &&
   1435       (send_size == sent_bytes))
   1436     post_send_setopt (&(c->sk), true, push_data);
   1437 
   1438   *sent = sent_bytes;
   1439   return ret;
   1440 }
   1441 
   1442 
   1443 #endif /* mhd_USE_SENDFILE */
   1444 
   1445 #if defined(mhd_USE_VECT_SEND)
   1446 
   1447 
   1448 /**
   1449  * Function sends iov data by system sendmsg or writev function.
   1450  *
   1451  * Connection must be in non-TLS (non-HTTPS) mode.
   1452  *
   1453  * @param connection the MHD connection structure
   1454  * @param r_iov the pointer to iov data structure with tracking
   1455  * @param push_data set to true to force push the data to the network from
   1456  *                  system buffers (usually set for the last piece of data),
   1457  *                  set to false to prefer holding incomplete network packets
   1458  *                  (more data will be send for the same reply).
   1459  * @param[out] sent the pointer to get amount of actually sent bytes
   1460  * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
   1461  *         the sent size) or socket error
   1462  */
   1463 static MHD_FN_PAR_NONNULL_ALL_
   1464 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
   1465 send_iov_nontls (struct MHD_Connection *restrict connection,
   1466                  struct mhd_iovec_track *const restrict r_iov,
   1467                  bool push_data,
   1468                  size_t *restrict sent)
   1469 {
   1470   bool send_error;
   1471   size_t items_to_send;
   1472 #ifndef MHD_SOCKETS_KIND_WINSOCK
   1473   ssize_t res;
   1474 #endif
   1475 #ifdef HAVE_SENDMSG
   1476   struct msghdr msg;
   1477 #elif defined(MHD_SOCKETS_KIND_WINSOCK)
   1478   DWORD bytes_sent;
   1479   DWORD cnt_w;
   1480 #endif /* MHD_SOCKETS_KIND_WINSOCK */
   1481 
   1482   mhd_assert (! mhd_C_HAS_TLS (connection));
   1483   mhd_assert (! mhd_D_HAS_TLS (connection->daemon));
   1484 
   1485   mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd);
   1486   mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage);
   1487 
   1488   send_error = false;
   1489   items_to_send = r_iov->cnt - r_iov->sent;
   1490 #ifdef mhd_IOV_MAX
   1491   if (mhd_IOV_MAX < items_to_send)
   1492   {
   1493     mhd_assert (0 < mhd_IOV_MAX);
   1494     if (0 == mhd_IOV_MAX)
   1495       return mhd_SOCKET_ERR_INTERNAL; /* Should never happen */
   1496     items_to_send = mhd_IOV_MAX;
   1497     push_data = false; /* Incomplete response */
   1498   }
   1499 #endif /* mhd_IOV_MAX */
   1500 #ifdef HAVE_SENDMSG
   1501   memset (&msg, 0, sizeof(struct msghdr));
   1502   msg.msg_name = NULL;
   1503   msg.msg_namelen = 0;
   1504   msg.msg_iov = r_iov->iov + r_iov->sent;
   1505   msg.msg_iovlen = items_to_send;
   1506   msg.msg_control = NULL;
   1507   msg.msg_controllen = 0;
   1508   msg.msg_flags = 0;
   1509 
   1510   pre_send_setopt (&(connection->sk), true, push_data);
   1511   res = sendmsg (connection->sk.fd, &msg,
   1512                  mhd_MSG_NOSIGNAL
   1513 #  ifdef mhd_USE_MSG_MORE
   1514                  | (push_data ? 0 : MSG_MORE)
   1515 #  endif
   1516                  );
   1517   if (0 < res)
   1518     *sent = (size_t) res;
   1519   else
   1520     send_error = true;
   1521 #elif defined(HAVE_WRITEV)
   1522   pre_send_setopt (&(connection->sk), false, push_data);
   1523   res = writev (connection->sk.fd, r_iov->iov + r_iov->sent,
   1524                 items_to_send);
   1525   if (0 < res)
   1526     *sent = (size_t) res;
   1527   else
   1528     send_error = true;
   1529 #elif defined(MHD_SOCKETS_KIND_WINSOCK)
   1530 #ifdef _WIN64
   1531   if (items_to_send > ULONG_MAX)
   1532   {
   1533     cnt_w = ULONG_MAX;
   1534     push_data = false; /* Incomplete response */
   1535   }
   1536   else
   1537     cnt_w = (DWORD) items_to_send;
   1538 #else  /* ! _WIN64 */
   1539   cnt_w = (DWORD) items_to_send;
   1540 #endif /* ! _WIN64 */
   1541   pre_send_setopt (&(connection->sk), true, push_data);
   1542   if (0 == WSASend (connection->sk.fd,
   1543                     (LPWSABUF) (r_iov->iov + r_iov->sent),
   1544                     cnt_w,
   1545                     &bytes_sent, 0, NULL, NULL))
   1546     *sent = (size_t) bytes_sent;
   1547   else
   1548     send_error = true;
   1549 #else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_SOCKETS_KIND_WINSOCK */
   1550 #error No vector-send function available
   1551 #endif
   1552 
   1553   if (send_error)
   1554   {
   1555     enum mhd_SocketError err;
   1556 
   1557     err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ());
   1558 
   1559     if (mhd_SOCKET_ERR_AGAIN == err)
   1560       connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1561                              (((unsigned int) connection->sk.ready)
   1562                               & (~(enum mhd_SocketNetState)
   1563                                  mhd_SOCKET_NET_STATE_SEND_READY));
   1564 
   1565     return err;
   1566   }
   1567 
   1568   /* Some data has been sent */
   1569   if (1)
   1570   {
   1571     size_t track_sent = (size_t) *sent;
   1572 
   1573     if (! connection->sk.props.is_nonblck)
   1574       connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1575                              (((unsigned int) connection->sk.ready)
   1576                               & (~(enum mhd_SocketNetState)
   1577                                  mhd_SOCKET_NET_STATE_SEND_READY));
   1578 
   1579     /* Adjust the internal tracking information for the iovec to
   1580      * take this last send into account. */
   1581     while ((0 != track_sent) && (r_iov->iov[r_iov->sent].iov_len <= track_sent))
   1582     {
   1583       track_sent -= r_iov->iov[r_iov->sent].iov_len;
   1584       r_iov->sent++; /* The iov element has been completely sent */
   1585       mhd_assert ((r_iov->cnt > r_iov->sent) || (0 == track_sent));
   1586     }
   1587 
   1588     if (r_iov->cnt == r_iov->sent)
   1589       post_send_setopt (&(connection->sk), true, push_data);
   1590     else
   1591     {
   1592       connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
   1593                              (((unsigned int) connection->sk.ready)
   1594                               & (~(enum mhd_SocketNetState)
   1595                                  mhd_SOCKET_NET_STATE_SEND_READY));
   1596       if (0 != track_sent)
   1597       {
   1598         mhd_assert (r_iov->cnt > r_iov->sent);
   1599         /* The last iov element has been partially sent */
   1600         r_iov->iov[r_iov->sent].iov_base =
   1601           (mhd_IOV_ELMN_PTR_TYPE)
   1602           (((uint8_t *) r_iov->iov[r_iov->sent].iov_base)
   1603            + track_sent);
   1604         r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size) track_sent;
   1605       }
   1606     }
   1607   }
   1608 
   1609   return mhd_SOCKET_ERR_NO_ERROR;
   1610 }
   1611 
   1612 
   1613 #endif /* mhd_USE_VECT_SEND */
   1614 
   1615 #if ! defined(mhd_USE_VECT_SEND) || defined(MHD_SUPPORT_HTTPS) || \
   1616   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   1617 
   1618 
   1619 /**
   1620  * Function sends iov data by sending buffers one-by-one by standard
   1621  * data send function.
   1622  *
   1623  * Connection could be in HTTPS or non-HTTPS mode.
   1624  *
   1625  * @param connection the MHD connection structure
   1626  * @param r_iov the pointer to iov data structure with tracking
   1627  * @param push_data set to true to force push the data to the network from
   1628  *                  system buffers (usually set for the last piece of data),
   1629  *                  set to false to prefer holding incomplete network packets
   1630  *                  (more data will be send for the same reply).
   1631  * @param[out] sent the pointer to get amount of actually sent bytes
   1632  * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
   1633  *         the sent size) or socket error
   1634  */
   1635 static MHD_FN_PAR_NONNULL_ALL_
   1636 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
   1637 send_iov_emu (struct MHD_Connection *restrict connection,
   1638               struct mhd_iovec_track *const restrict r_iov,
   1639               bool push_data,
   1640               size_t *restrict sent)
   1641 {
   1642   const bool non_blk = connection->sk.props.is_nonblck;
   1643   size_t total_sent;
   1644   size_t max_elelements_to_sent;
   1645 
   1646   mhd_assert (NULL != r_iov->iov);
   1647   total_sent = 0;
   1648   max_elelements_to_sent = 8; /* Do not make too many sys-calls for just one connection */
   1649   do
   1650   {
   1651     enum mhd_SocketError res;
   1652     size_t sent_el_size;
   1653 
   1654     if (total_sent > (size_t) (r_iov->iov[r_iov->sent].iov_len + total_sent))
   1655       break; /* return value would overflow */
   1656 
   1657     res = mhd_send_data (connection,
   1658                          r_iov->iov[r_iov->sent].iov_len,
   1659                          (const char *) r_iov->iov[r_iov->sent].iov_base,
   1660                          push_data && (r_iov->cnt == r_iov->sent + 1),
   1661                          &sent_el_size);
   1662     if (mhd_SOCKET_ERR_NO_ERROR == res)
   1663     {
   1664       /* Result is an error */
   1665       if (0 == total_sent)
   1666         return res; /* Nothing was sent, return error as is */
   1667 
   1668       if (mhd_SOCKET_ERR_IS_HARD (res))
   1669         return res; /* Any kind of a hard error */
   1670 
   1671       break; /* Return the amount of the sent data */
   1672     }
   1673 
   1674     total_sent += sent_el_size;
   1675 
   1676     if (r_iov->iov[r_iov->sent].iov_len != sent_el_size)
   1677     {
   1678       /* Incomplete buffer has been sent.
   1679        * Adjust buffer of the last element. */
   1680       r_iov->iov[r_iov->sent].iov_base =
   1681         (mhd_IOV_ELMN_PTR_TYPE)
   1682         (((uint8_t *) r_iov->iov[r_iov->sent].iov_base) + sent_el_size);
   1683       r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size) sent_el_size;
   1684 
   1685       break; /* Return the amount of the sent data */
   1686     }
   1687     /* The iov element has been completely sent */
   1688     r_iov->sent++;
   1689   } while ((r_iov->cnt > r_iov->sent) && 0 != (--max_elelements_to_sent) &&
   1690            (non_blk));
   1691 
   1692   mhd_assert (0 != total_sent);
   1693   *sent = total_sent;
   1694   return mhd_SOCKET_ERR_NO_ERROR;
   1695 }
   1696 
   1697 
   1698 #endif /* !mhd_USE_VECT_SEND || MHD_SUPPORT_HTTPS
   1699           || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1700 
   1701 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   1702 MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
   1703 mhd_send_iovec (struct MHD_Connection *restrict connection,
   1704                 struct mhd_iovec_track *const restrict r_iov,
   1705                 bool push_data,
   1706                 size_t *restrict sent)
   1707 {
   1708 #ifdef mhd_USE_VECT_SEND
   1709 #if defined(MHD_SUPPORT_HTTPS) || \
   1710   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   1711   bool use_iov_send = true;
   1712 #endif /* MHD_SUPPORT_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1713 #endif /* mhd_USE_VECT_SEND */
   1714 
   1715   mhd_assert (NULL != connection->rp.resp_iov.iov);
   1716   mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == \
   1717               connection->rp.response->cntn_dtype);
   1718   mhd_assert (connection->rp.resp_iov.cnt > connection->rp.resp_iov.sent);
   1719 #ifdef mhd_USE_VECT_SEND
   1720 #if defined(MHD_SUPPORT_HTTPS) || \
   1721   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   1722   use_iov_send = use_iov_send &&
   1723                  (! mhd_C_HAS_TLS (connection));
   1724 #ifdef mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
   1725   use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked ||
   1726                                   connection->sk.props.has_spipe_supp);
   1727 #endif /* mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1728   if (use_iov_send)
   1729 #endif /* MHD_SUPPORT_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1730   return send_iov_nontls (connection, r_iov, push_data, sent);
   1731 #endif /* mhd_USE_VECT_SEND */
   1732 
   1733 #if ! defined(mhd_USE_VECT_SEND) || defined(MHD_SUPPORT_HTTPS) || \
   1734   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   1735   return send_iov_emu (connection, r_iov, push_data, sent);
   1736 #endif /* !mhd_USE_VECT_SEND || MHD_SUPPORT_HTTPS
   1737           || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   1738 }