Coverage Report

Created: 2025-11-09 06:43

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