GNU libmicrohttpd 0.9.75
mhd_send.c
Go to the documentation of this file.
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2017,2020 Karlson2k (Evgeny Grin), Full re-write of buffering and
4 pushing, many bugs fixes, optimisations, sendfile() porting
5 Copyright (C) 2019 ng0 <ng0@n0.is>, Initial version of send() wrappers
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21 */
22
31/* Worth considering for future improvements and additions:
32 * NetBSD has no sendfile or sendfile64. The way to work
33 * with this seems to be to mmap the file and write(2) as
34 * large a chunk as possible to the socket. Alternatively,
35 * use madvise(..., MADV_SEQUENTIAL). */
36
37#include "mhd_send.h"
38#ifdef MHD_LINUX_SOLARIS_SENDFILE
39#include <sys/sendfile.h>
40#endif /* MHD_LINUX_SOLARIS_SENDFILE */
41#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
42#include <sys/types.h>
43#include <sys/socket.h>
44#include <sys/uio.h>
45#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
46#ifdef HAVE_SYS_PARAM_H
47/* For FreeBSD version identification */
48#include <sys/param.h>
49#endif /* HAVE_SYS_PARAM_H */
50#ifdef HAVE_SYSCONF
51#include <unistd.h>
52#endif /* HAVE_SYSCONF */
53#include "mhd_assert.h"
54
55#include "mhd_limits.h"
56
57#ifdef MHD_VECT_SEND
58#if (! defined (HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL)) && \
59 defined (MHD_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
60 defined (MHD_SEND_SPIPE_SUPPRESS_NEEDED)
61#define _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED 1
62#endif /* (!HAVE_SENDMSG || !MSG_NOSIGNAL) &&
63 MHD_SEND_SPIPE_SUPPRESS_POSSIBLE && MHD_SEND_SPIPE_SUPPRESS_NEEDED */
64#endif /* MHD_VECT_SEND */
65
69#define MHD_SENFILE_CHUNK_ (0x20000)
70
74#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
75
76#ifdef HAVE_FREEBSD_SENDFILE
77#ifdef SF_FLAGS
81static int freebsd_sendfile_flags_;
82
86static int freebsd_sendfile_flags_thd_p_c_;
87
88
92static void
93freebsd_sendfile_init_ (void)
94{
95 long sys_page_size = sysconf (_SC_PAGESIZE);
96 if (0 >= sys_page_size)
97 { /* Failed to get page size. */
98 freebsd_sendfile_flags_ = SF_NODISKIO;
99 freebsd_sendfile_flags_thd_p_c_ = SF_NODISKIO;
100 }
101 else
102 {
103 freebsd_sendfile_flags_ =
104 SF_FLAGS ((uint16_t) ((MHD_SENFILE_CHUNK_ + sys_page_size - 1)
105 / sys_page_size), SF_NODISKIO);
106 freebsd_sendfile_flags_thd_p_c_ =
107 SF_FLAGS ((uint16_t) ((MHD_SENFILE_CHUNK_THR_P_C_ + sys_page_size - 1)
108 / sys_page_size), SF_NODISKIO);
109 }
110}
111
112
113#endif /* SF_FLAGS */
114#endif /* HAVE_FREEBSD_SENDFILE */
115
116
117#if defined(HAVE_SYSCONF) && defined(_SC_IOV_MAX)
121static unsigned long mhd_iov_max_ = 0;
122
123static void
124iov_max_init_ (void)
125{
126 long res = sysconf (_SC_IOV_MAX);
127 if (res >= 0)
128 mhd_iov_max_ = res;
129#if defined(IOV_MAX)
130 else
131 mhd_iov_max_ = IOV_MAX;
132#endif /* IOV_MAX */
133}
134
135
139#define _MHD_IOV_MAX mhd_iov_max_
140#elif defined(IOV_MAX)
141
145#define _MHD_IOV_MAX IOV_MAX
146#endif /* HAVE_SYSCONF && _SC_IOV_MAX */
147
148
152void
154{
155#ifdef HAVE_FREEBSD_SENDFILE
156 /* FreeBSD 11 and later allow to specify read-ahead size
157 * and handles SF_NODISKIO differently.
158 * SF_FLAGS defined only on FreeBSD 11 and later. */
159#ifdef SF_FLAGS
160 freebsd_sendfile_init_ ();
161#endif /* SF_FLAGS */
162#endif /* HAVE_FREEBSD_SENDFILE */
163#if defined(HAVE_SYSCONF) && defined(_SC_IOV_MAX)
164 iov_max_init_ ();
165#endif /* HAVE_SYSCONF && _SC_IOV_MAX */
166}
167
168
169bool
171 bool nodelay_state)
172{
173#ifdef TCP_NODELAY
174 const MHD_SCKT_OPT_BOOL_ off_val = 0;
175 const MHD_SCKT_OPT_BOOL_ on_val = 1;
176 int err_code;
177
178 if (_MHD_YES == connection->is_nonip)
179 return false;
180
181 if (0 == setsockopt (connection->socket_fd,
182 IPPROTO_TCP,
183 TCP_NODELAY,
184 (const void *) (nodelay_state ? &on_val : &off_val),
185 sizeof (off_val)))
186 {
187 connection->sk_nodelay = nodelay_state;
188 return true;
189 }
190
191 err_code = MHD_socket_get_error_ ();
192 if (MHD_SCKT_ERR_IS_ (err_code, MHD_SCKT_EINVAL_) ||
195 {
196 if (_MHD_UNKNOWN == connection->is_nonip)
197 connection->is_nonip = _MHD_YES;
198#ifdef HAVE_MESSAGES
199 else
200 {
201 MHD_DLOG (connection->daemon,
202 _ ("Setting %s option to %s state failed "
203 "for TCP/IP socket %d: %s\n"),
204 "TCP_NODELAY",
205 nodelay_state ? _ ("ON") : _ ("OFF"),
206 (int) connection->socket_fd,
207 MHD_socket_strerr_ (err_code));
208 }
209#endif /* HAVE_MESSAGES */
210 }
211#ifdef HAVE_MESSAGES
212 else
213 {
214 MHD_DLOG (connection->daemon,
215 _ ("Setting %s option to %s state failed: %s\n"),
216 "TCP_NODELAY",
217 nodelay_state ? _ ("ON") : _ ("OFF"),
218 MHD_socket_strerr_ (err_code));
219 }
220#endif /* HAVE_MESSAGES */
221
222#else /* ! TCP_NODELAY */
223 (void) connection; (void) nodelay_state; /* Mute compiler warnings */
224#endif /* ! TCP_NODELAY */
225 return false;
226}
227
228
239bool
241 bool cork_state)
242{
243#if defined(MHD_TCP_CORK_NOPUSH)
244 const MHD_SCKT_OPT_BOOL_ off_val = 0;
245 const MHD_SCKT_OPT_BOOL_ on_val = 1;
246 int err_code;
247
248 if (_MHD_YES == connection->is_nonip)
249 return false;
250 if (0 == setsockopt (connection->socket_fd,
251 IPPROTO_TCP,
252 MHD_TCP_CORK_NOPUSH,
253 (const void *) (cork_state ? &on_val : &off_val),
254 sizeof (off_val)))
255 {
256 connection->sk_corked = cork_state;
257 return true;
258 }
259
260 err_code = MHD_socket_get_error_ ();
261 if (MHD_SCKT_ERR_IS_ (err_code, MHD_SCKT_EINVAL_) ||
264 {
265 if (_MHD_UNKNOWN == connection->is_nonip)
266 connection->is_nonip = _MHD_YES;
267#ifdef HAVE_MESSAGES
268 else
269 {
270 MHD_DLOG (connection->daemon,
271 _ ("Setting %s option to %s state failed "
272 "for TCP/IP socket %d: %s\n"),
273#ifdef TCP_CORK
274 "TCP_CORK",
275#else /* ! TCP_CORK */
276 "TCP_NOPUSH",
277#endif /* ! TCP_CORK */
278 cork_state ? _ ("ON") : _ ("OFF"),
279 (int) connection->socket_fd,
280 MHD_socket_strerr_ (err_code));
281 }
282#endif /* HAVE_MESSAGES */
283 }
284#ifdef HAVE_MESSAGES
285 else
286 {
287 MHD_DLOG (connection->daemon,
288 _ ("Setting %s option to %s state failed: %s\n"),
289#ifdef TCP_CORK
290 "TCP_CORK",
291#else /* ! TCP_CORK */
292 "TCP_NOPUSH",
293#endif /* ! TCP_CORK */
294 cork_state ? _ ("ON") : _ ("OFF"),
295 MHD_socket_strerr_ (err_code));
296 }
297#endif /* HAVE_MESSAGES */
298
299#else /* ! MHD_TCP_CORK_NOPUSH */
300 (void) connection; (void) cork_state; /* Mute compiler warnings. */
301#endif /* ! MHD_TCP_CORK_NOPUSH */
302 return false;
303}
304
305
316static void
317pre_send_setopt (struct MHD_Connection *connection,
318 bool plain_send,
319 bool push_data)
320{
321 /* Try to buffer data if not sending the final piece.
322 * Final piece is indicated by push_data == true. */
323 const bool buffer_data = (! push_data);
324
325 if (_MHD_YES == connection->is_nonip)
326 return;
327 /* The goal is to minimise the total number of additional sys-calls
328 * before and after send().
329 * The following tricky (over-)complicated algorithm typically use zero,
330 * one or two additional sys-calls (depending on OS) for each response. */
331
332 if (buffer_data)
333 {
334 /* Need to buffer data if possible. */
335#ifdef MHD_USE_MSG_MORE
336 if (plain_send)
337 return; /* Data is buffered by send() with MSG_MORE flag.
338 * No need to check or change anything. */
339#else /* ! MHD_USE_MSG_MORE */
340 (void) plain_send; /* Mute compiler warning. */
341#endif /* ! MHD_USE_MSG_MORE */
342
343#ifdef MHD_TCP_CORK_NOPUSH
344 if (_MHD_ON == connection->sk_corked)
345 return; /* The connection was already corked. */
346
347 if (MHD_connection_set_cork_state_ (connection, true))
348 return; /* The connection has been corked. */
349
350 /* Failed to cork the connection.
351 * Really unlikely to happen on TCP connections. */
352#endif /* MHD_TCP_CORK_NOPUSH */
353 if (_MHD_OFF == connection->sk_nodelay)
354 return; /* TCP_NODELAY was not set for the socket.
355 * Nagle's algorithm will buffer some data. */
356
357 /* Try to reset TCP_NODELAY state for the socket.
358 * Ignore possible error as no other options exist to
359 * buffer data. */
360 MHD_connection_set_nodelay_state_ (connection, false);
361 /* TCP_NODELAY has been (hopefully) reset for the socket.
362 * Nagle's algorithm will buffer some data. */
363 return;
364 }
365
366 /* Need to push data after send() */
367 /* If additional sys-call is required prefer to make it after the send()
368 * as the next send() may consume only part of the prepared data and
369 * more send() calls will be used. */
370#ifdef MHD_TCP_CORK_NOPUSH
371#ifdef _MHD_CORK_RESET_PUSH_DATA
372#ifdef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
373 /* Data can be pushed immediately by uncorking socket regardless of
374 * cork state before. */
375 /* This is typical for Linux, no other kernel with
376 * such behavior are known so far. */
377
378 /* No need to check the current state of TCP_CORK / TCP_NOPUSH
379 * as reset of cork will push the data anyway. */
380 return; /* Data may be pushed by resetting of
381 * TCP_CORK / TCP_NOPUSH after send() */
382#else /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
383 /* Reset of TCP_CORK / TCP_NOPUSH will push the data
384 * only if socket is corked. */
385
386#ifdef _MHD_NODELAY_SET_PUSH_DATA_ALWAYS
387 /* Data can be pushed immediately by setting TCP_NODELAY regardless
388 * of TCP_NODDELAY or corking state before. */
389
390 /* Dead code currently, no known kernels with such behavior. */
391 return; /* Data may be pushed by setting of TCP_NODELAY after send().
392 No need to make extra sys-calls before send().*/
393#else /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
394
395#ifdef _MHD_NODELAY_SET_PUSH_DATA
396 /* Setting of TCP_NODELAY will push the data only if
397 * both TCP_NODELAY and TCP_CORK / TCP_NOPUSH were not set. */
398
399 /* Data can be pushed immediately by uncorking socket if
400 * socket was corked before or by setting TCP_NODELAY if
401 * socket was not corked and TCP_NODELAY was not set before. */
402
403 /* Dead code currently as Linux is the only kernel that push
404 * data by setting of TCP_NODELAY and Linux push data always. */
405#else /* ! _MHD_NODELAY_SET_PUSH_DATA */
406 /* Data can be pushed immediately by uncorking socket or
407 * can be pushed by send() on uncorked socket if
408 * TCP_NODELAY was set *before*. */
409
410 /* This is typical FreeBSD behavior. */
411#endif /* ! _MHD_NODELAY_SET_PUSH_DATA */
412
413 if (_MHD_ON == connection->sk_corked)
414 return; /* Socket is corked. Data can be pushed by resetting of
415 * TCP_CORK / TCP_NOPUSH after send() */
416 else if (_MHD_OFF == connection->sk_corked)
417 {
418 /* The socket is not corked. */
419 if (_MHD_ON == connection->sk_nodelay)
420 return; /* TCP_NODELAY was already set,
421 * data will be pushed automatically by the next send() */
422#ifdef _MHD_NODELAY_SET_PUSH_DATA
423 else if (_MHD_UNKNOWN == connection->sk_nodelay)
424 {
425 /* Setting TCP_NODELAY may push data.
426 * Cork socket here and uncork after send(). */
427 if (MHD_connection_set_cork_state_ (connection, true))
428 return; /* The connection has been corked.
429 * Data can be pushed by resetting of
430 * TCP_CORK / TCP_NOPUSH after send() */
431 else
432 {
433 /* The socket cannot be corked.
434 * Really unlikely to happen on TCP connections */
435 /* Have to set TCP_NODELAY.
436 * If TCP_NODELAY real system state was OFF then
437 * already buffered data may be pushed here, but this is unlikely
438 * to happen as it is only a backup solution when corking has failed.
439 * Ignore possible error here as no other options exist to
440 * push data. */
441 MHD_connection_set_nodelay_state_ (connection, true);
442 /* TCP_NODELAY has been (hopefully) set for the socket.
443 * The data will be pushed by the next send(). */
444 return;
445 }
446 }
447#endif /* _MHD_NODELAY_SET_PUSH_DATA */
448 else
449 {
450#ifdef _MHD_NODELAY_SET_PUSH_DATA
451 /* TCP_NODELAY was switched off and
452 * the socket is not corked. */
453#else /* ! _MHD_NODELAY_SET_PUSH_DATA */
454 /* Socket is not corked and TCP_NODELAY was not set or unknown. */
455#endif /* ! _MHD_NODELAY_SET_PUSH_DATA */
456
457 /* At least one additional sys-call is required. */
458 /* Setting TCP_NODELAY is optimal here as data will be pushed
459 * automatically by the next send() and no additional
460 * sys-call are needed after the send(). */
461 if (MHD_connection_set_nodelay_state_ (connection, true))
462 return;
463 else
464 {
465 /* Failed to set TCP_NODELAY for the socket.
466 * Really unlikely to happen on TCP connections. */
467 /* Cork the socket here and make additional sys-call
468 * to uncork the socket after send(). */
469 /* Ignore possible error here as no other options exist to
470 * push data. */
471 MHD_connection_set_cork_state_ (connection, true);
472 /* The connection has been (hopefully) corked.
473 * Data can be pushed by resetting of TCP_CORK / TCP_NOPUSH
474 * after send() */
475 return;
476 }
477 }
478 }
479 /* Corked state is unknown. Need to make sys-call here otherwise
480 * data may not be pushed. */
481 if (MHD_connection_set_cork_state_ (connection, true))
482 return; /* The connection has been corked.
483 * Data can be pushed by resetting of
484 * TCP_CORK / TCP_NOPUSH after send() */
485 /* The socket cannot be corked.
486 * Really unlikely to happen on TCP connections */
487 if (_MHD_ON == connection->sk_nodelay)
488 return; /* TCP_NODELAY was already set,
489 * data will be pushed by the next send() */
490 /* Have to set TCP_NODELAY. */
491#ifdef _MHD_NODELAY_SET_PUSH_DATA
492 /* If TCP_NODELAY state was unknown (external connection) then
493 * already buffered data may be pushed here, but this is unlikely
494 * to happen as it is only a backup solution when corking has failed. */
495#endif /* _MHD_NODELAY_SET_PUSH_DATA */
496 /* Ignore possible error here as no other options exist to
497 * push data. */
498 MHD_connection_set_nodelay_state_ (connection, true);
499 /* TCP_NODELAY has been (hopefully) set for the socket.
500 * The data will be pushed by the next send(). */
501 return;
502#endif /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
503#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
504#else /* ! _MHD_CORK_RESET_PUSH_DATA */
505 /* Neither uncorking the socket or setting TCP_NODELAY
506 * push the data immediately. */
507 /* The only way to push the data is to use send() on uncorked
508 * socket with TCP_NODELAY switched on . */
509
510 /* This is a typical *BSD (except FreeBSD) and Darwin behavior. */
511
512 /* Uncork socket if socket wasn't uncorked. */
513 if (_MHD_OFF != connection->sk_corked)
514 MHD_connection_set_cork_state_ (connection, false);
515
516 /* Set TCP_NODELAY if it wasn't set. */
517 if (_MHD_ON != connection->sk_nodelay)
518 MHD_connection_set_nodelay_state_ (connection, true);
519
520 return;
521#endif /* ! _MHD_CORK_RESET_PUSH_DATA */
522#else /* ! MHD_TCP_CORK_NOPUSH */
523 /* Buffering of data is controlled only by
524 * Nagel's algorithm. */
525 /* Set TCP_NODELAY if it wasn't set. */
526 if (_MHD_ON != connection->sk_nodelay)
527 MHD_connection_set_nodelay_state_ (connection, true);
528#endif /* ! MHD_TCP_CORK_NOPUSH */
529}
530
531
532#ifndef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
544static bool
545zero_send_ (struct MHD_Connection *connection)
546{
547 int dummy;
548
549 if (_MHD_YES == connection->is_nonip)
550 return false;
551 mhd_assert (_MHD_OFF == connection->sk_corked);
552 mhd_assert (_MHD_ON == connection->sk_nodelay);
553 dummy = 0; /* Mute compiler and analyzer warnings */
554 if (0 == MHD_send_ (connection->socket_fd, &dummy, 0))
555 return true;
556#ifdef HAVE_MESSAGES
557 MHD_DLOG (connection->daemon,
558 _ ("Zero-send failed: %s\n"),
560#endif /* HAVE_MESSAGES */
561 return false;
562}
563
564
565#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
566
577static void
579 bool plain_send_next,
580 bool push_data)
581{
582 /* Try to buffer data if not sending the final piece.
583 * Final piece is indicated by push_data == true. */
584 const bool buffer_data = (! push_data);
585
586 if (_MHD_YES == connection->is_nonip)
587 return;
588 if (buffer_data)
589 return; /* Nothing to do after send(). */
590
591#ifndef MHD_USE_MSG_MORE
592 (void) plain_send_next; /* Mute compiler warning */
593#endif /* ! MHD_USE_MSG_MORE */
594
595 /* Need to push data. */
596#ifdef MHD_TCP_CORK_NOPUSH
597#ifdef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
598#ifdef _MHD_NODELAY_SET_PUSH_DATA_ALWAYS
599#ifdef MHD_USE_MSG_MORE
600 if (_MHD_OFF == connection->sk_corked)
601 {
602 if (_MHD_ON == connection->sk_nodelay)
603 return; /* Data was already pushed by send(). */
604 }
605 /* This is Linux kernel. There are options:
606 * * Push the data by setting of TCP_NODELAY (without change
607 * of the cork on the socket),
608 * * Push the data by resetting of TCP_CORK.
609 * The optimal choice depends on the next final send functions
610 * used on the same socket. If TCP_NODELAY wasn't set then push
611 * data by setting TCP_NODELAY (TCP_NODELAY will not be removed
612 * and is needed to push the data by send() without MSG_MORE).
613 * If send()/sendmsg() will be used next than push data by
614 * resetting of TCP_CORK so next send without MSG_MORE will push
615 * data to the network (without additional sys-call to push data).
616 * If next final send function will not support MSG_MORE (like
617 * sendfile() or TLS-connection) than push data by setting
618 * TCP_NODELAY so socket will remain corked (no additional
619 * sys-call before next send()). */
620 if ((_MHD_ON != connection->sk_nodelay) ||
621 (! plain_send_next))
622 {
623 if (MHD_connection_set_nodelay_state_ (connection, true))
624 return; /* Data has been pushed by TCP_NODELAY. */
625 /* Failed to set TCP_NODELAY for the socket.
626 * Really unlikely to happen on TCP connections. */
627 if (MHD_connection_set_cork_state_ (connection, false))
628 return; /* Data has been pushed by uncorking the socket. */
629 /* Failed to uncork the socket.
630 * Really unlikely to happen on TCP connections. */
631
632 /* The socket cannot be uncorked, no way to push data */
633 }
634 else
635 {
636 if (MHD_connection_set_cork_state_ (connection, false))
637 return; /* Data has been pushed by uncorking the socket. */
638 /* Failed to uncork the socket.
639 * Really unlikely to happen on TCP connections. */
640 if (MHD_connection_set_nodelay_state_ (connection, true))
641 return; /* Data has been pushed by TCP_NODELAY. */
642 /* Failed to set TCP_NODELAY for the socket.
643 * Really unlikely to happen on TCP connections. */
644
645 /* The socket cannot be uncorked, no way to push data */
646 }
647#else /* ! MHD_USE_MSG_MORE */
648 /* Use setting of TCP_NODELAY here to avoid sys-call
649 * for corking the socket during sending of the next response. */
650 if (MHD_connection_set_nodelay_state_ (connection, true))
651 return; /* Data was pushed by TCP_NODELAY. */
652 /* Failed to set TCP_NODELAY for the socket.
653 * Really unlikely to happen on TCP connections. */
654 if (MHD_connection_set_cork_state_ (connection, false))
655 return; /* Data was pushed by uncorking the socket. */
656 /* Failed to uncork the socket.
657 * Really unlikely to happen on TCP connections. */
658
659 /* The socket remains corked, no way to push data */
660#endif /* ! MHD_USE_MSG_MORE */
661#else /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
662 if (MHD_connection_set_cork_state_ (connection, false))
663 return; /* Data was pushed by uncorking the socket. */
664 /* Failed to uncork the socket.
665 * Really unlikely to happen on TCP connections. */
666 return; /* Socket remains corked, no way to push data */
667#endif /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
668#else /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
669 /* This is a typical *BSD or Darwin kernel. */
670
671 if (_MHD_OFF == connection->sk_corked)
672 {
673 if (_MHD_ON == connection->sk_nodelay)
674 return; /* Data was already pushed by send(). */
675
676 /* Unlikely to reach this code.
677 * TCP_NODELAY should be turned on before send(). */
678 if (MHD_connection_set_nodelay_state_ (connection, true))
679 {
680 /* TCP_NODELAY has been set on uncorked socket.
681 * Use zero-send to push the data. */
682 if (zero_send_ (connection))
683 return; /* The data has been pushed by zero-send. */
684 }
685
686 /* Failed to push the data by all means. */
687 /* There is nothing left to try. */
688 }
689 else
690 {
691#ifdef _MHD_CORK_RESET_PUSH_DATA
692 enum MHD_tristate old_cork_state = connection->sk_corked;
693#endif /* _MHD_CORK_RESET_PUSH_DATA */
694 /* The socket is corked or cork state is unknown. */
695
696 if (MHD_connection_set_cork_state_ (connection, false))
697 {
698#ifdef _MHD_CORK_RESET_PUSH_DATA
699 /* FreeBSD kernel */
700 if (_MHD_OFF == old_cork_state)
701 return; /* Data has been pushed by uncorking the socket. */
702#endif /* _MHD_CORK_RESET_PUSH_DATA */
703
704 /* Unlikely to reach this code.
705 * The data should be pushed by uncorking (FreeBSD) or
706 * the socket should be uncorked before send(). */
707 if ((_MHD_ON == connection->sk_nodelay) ||
708 (MHD_connection_set_nodelay_state_ (connection, true)))
709 {
710 /* TCP_NODELAY is turned ON on uncorked socket.
711 * Use zero-send to push the data. */
712 if (zero_send_ (connection))
713 return; /* The data has been pushed by zero-send. */
714 }
715 }
716 /* The socket remains corked. Data cannot be pushed. */
717 }
718#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
719#else /* ! MHD_TCP_CORK_NOPUSH */
720 /* Corking is not supported. Buffering is controlled
721 * by TCP_NODELAY only. */
722 mhd_assert (_MHD_ON != connection->sk_corked);
723 if (_MHD_ON == connection->sk_nodelay)
724 return; /* Data was already pushed by send(). */
725
726 /* Unlikely to reach this code.
727 * TCP_NODELAY should be turned on before send(). */
728 if (MHD_connection_set_nodelay_state_ (connection, true))
729 {
730 /* TCP_NODELAY has been set.
731 * Use zero-send to push the data. */
732 if (zero_send_ (connection))
733 return; /* The data has been pushed by zero-send. */
734 }
735
736 /* Failed to push the data. */
737#endif /* ! MHD_TCP_CORK_NOPUSH */
738#ifdef HAVE_MESSAGES
739 MHD_DLOG (connection->daemon,
740 _ ("Failed to push the data from buffers to the network. "
741 "Client may experience some delay "
742 "(usually in range 200ms - 5 sec).\n"));
743#endif /* HAVE_MESSAGES */
744 return;
745}
746
747
748ssize_t
749MHD_send_data_ (struct MHD_Connection *connection,
750 const char *buffer,
751 size_t buffer_size,
752 bool push_data)
753{
754 MHD_socket s = connection->socket_fd;
755 ssize_t ret;
756#ifdef HTTPS_SUPPORT
757 const bool tls_conn = (connection->daemon->options & MHD_USE_TLS);
758#else /* ! HTTPS_SUPPORT */
759 const bool tls_conn = false;
760#endif /* ! HTTPS_SUPPORT */
761
762 if ( (MHD_INVALID_SOCKET == s) ||
763 (MHD_CONNECTION_CLOSED == connection->state) )
764 {
765 return MHD_ERR_NOTCONN_;
766 }
767
768 if (buffer_size > SSIZE_MAX)
769 {
770 buffer_size = SSIZE_MAX; /* Max return value */
771 push_data = false; /* Incomplete send */
772 }
773
774 if (tls_conn)
775 {
776#ifdef HTTPS_SUPPORT
777 pre_send_setopt (connection, (! tls_conn), push_data);
778 ret = gnutls_record_send (connection->tls_session,
779 buffer,
780 buffer_size);
781 if (GNUTLS_E_AGAIN == ret)
782 {
783#ifdef EPOLL_SUPPORT
784 connection->epoll_state &=
786#endif
787 return MHD_ERR_AGAIN_;
788 }
789 if (GNUTLS_E_INTERRUPTED == ret)
790 return MHD_ERR_AGAIN_;
791 if ( (GNUTLS_E_ENCRYPTION_FAILED == ret) ||
792 (GNUTLS_E_INVALID_SESSION == ret) ||
793 (GNUTLS_E_COMPRESSION_FAILED == ret) ||
794 (GNUTLS_E_EXPIRED == ret) ||
795 (GNUTLS_E_HASH_FAILED == ret) )
796 return MHD_ERR_TLS_;
797 if ( (GNUTLS_E_PUSH_ERROR == ret) ||
798 (GNUTLS_E_INTERNAL_ERROR == ret) ||
799 (GNUTLS_E_CRYPTODEV_IOCTL_ERROR == ret) ||
800 (GNUTLS_E_CRYPTODEV_DEVICE_ERROR == ret) )
801 return MHD_ERR_PIPE_;
802#if defined(GNUTLS_E_PREMATURE_TERMINATION)
803 if (GNUTLS_E_PREMATURE_TERMINATION == ret)
804 return MHD_ERR_CONNRESET_;
805#elif defined(GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
806 if (GNUTLS_E_UNEXPECTED_PACKET_LENGTH == ret)
807 return MHD_ERR_CONNRESET_;
808#endif /* GNUTLS_E_UNEXPECTED_PACKET_LENGTH */
809 if (GNUTLS_E_MEMORY_ERROR == ret)
810 return MHD_ERR_NOMEM_;
811 if (ret < 0)
812 {
813 /* Treat any other error as hard error. */
814 return MHD_ERR_NOTCONN_;
815 }
816#ifdef EPOLL_SUPPORT
817 /* Unlike non-TLS connections, do not reset "write-ready" if
818 * sent amount smaller than provided amount, as TLS
819 * connections may break data into smaller parts for sending. */
820#endif /* EPOLL_SUPPORT */
821#endif /* HTTPS_SUPPORT */
822 (void) 0; /* Mute compiler warning for non-TLS builds. */
823 }
824 else
825 {
826 /* plaintext transmission */
827 if (buffer_size > MHD_SCKT_SEND_MAX_SIZE_)
828 {
829 buffer_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */
830 push_data = false; /* Incomplete send */
831 }
832
833 pre_send_setopt (connection, (! tls_conn), push_data);
834#ifdef MHD_USE_MSG_MORE
835 ret = MHD_send4_ (s,
836 buffer,
837 buffer_size,
838 push_data ? 0 : MSG_MORE);
839#else
840 ret = MHD_send4_ (s,
841 buffer,
842 buffer_size,
843 0);
844#endif
845
846 if (0 > ret)
847 {
848 const int err = MHD_socket_get_error_ ();
849
850 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
851 {
852#if EPOLL_SUPPORT
853 /* EAGAIN, no longer write-ready */
854 connection->epoll_state &=
856#endif /* EPOLL_SUPPORT */
857 return MHD_ERR_AGAIN_;
858 }
859 if (MHD_SCKT_ERR_IS_EINTR_ (err))
860 return MHD_ERR_AGAIN_;
862 return MHD_ERR_CONNRESET_;
864 return MHD_ERR_PIPE_;
866 return MHD_ERR_OPNOTSUPP_;
868 return MHD_ERR_NOTCONN_;
870 return MHD_ERR_INVAL_;
872 return MHD_ERR_NOMEM_;
874 return MHD_ERR_BADF_;
875 /* Treat any other error as a hard error. */
876 return MHD_ERR_NOTCONN_;
877 }
878#if EPOLL_SUPPORT
879 else if (buffer_size > (size_t) ret)
880 connection->epoll_state &=
882#endif /* EPOLL_SUPPORT */
883 }
884
885 /* If there is a need to push the data from network buffers
886 * call post_send_setopt(). */
887 /* If TLS connection is used then next final send() will be
888 * without MSG_MORE support. If non-TLS connection is used
889 * it's unknown whether sendfile() will be used or not so
890 * assume that next call will be the same, like this call. */
891 if ( (push_data) &&
892 (buffer_size == (size_t) ret) )
893 post_send_setopt (connection, (! tls_conn), push_data);
894
895 return ret;
896}
897
898
899ssize_t
901 const char *header,
902 size_t header_size,
903 bool never_push_hdr,
904 const char *body,
905 size_t body_size,
906 bool complete_response)
907{
908 ssize_t ret;
909 bool push_hdr;
910 bool push_body;
911 MHD_socket s = connection->socket_fd;
912#ifndef _WIN32
913#define _MHD_SEND_VEC_MAX MHD_SCKT_SEND_MAX_SIZE_
914#else /* ! _WIN32 */
915#define _MHD_SEND_VEC_MAX UINT32_MAX
916#endif /* ! _WIN32 */
917#ifdef MHD_VECT_SEND
918#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
919 struct iovec vector[2];
920#ifdef HAVE_SENDMSG
921 struct msghdr msg;
922#endif /* HAVE_SENDMSG */
923#endif /* HAVE_SENDMSG || HAVE_WRITEV */
924#ifdef _WIN32
925 WSABUF vector[2];
926 DWORD vec_sent;
927#endif /* _WIN32 */
928 bool no_vec; /* Is vector-send() disallowed? */
929
930 no_vec = false;
931#ifdef HTTPS_SUPPORT
932 no_vec = no_vec || (connection->daemon->options & MHD_USE_TLS);
933#endif /* HTTPS_SUPPORT */
934#if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL) ) && \
935 defined(MHD_SEND_SPIPE_SEND_SUPPRESS_POSSIBLE) && \
936 defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED)
937 no_vec = no_vec || (! connection->daemon->sigpipe_blocked &&
938 ! connection->sk_spipe_suppress);
939#endif /* (!HAVE_SENDMSG || ! MSG_NOSIGNAL) &&
940 MHD_SEND_SPIPE_SEND_SUPPRESS_POSSIBLE &&
941 MHD_SEND_SPIPE_SUPPRESS_NEEDED */
942#endif /* MHD_VECT_SEND */
943
944 mhd_assert ( (NULL != body) || (0 == body_size) );
945
946 if ( (MHD_INVALID_SOCKET == s) ||
947 (MHD_CONNECTION_CLOSED == connection->state) )
948 {
949 return MHD_ERR_NOTCONN_;
950 }
951
952 push_body = complete_response;
953
954 if (! never_push_hdr)
955 {
956 if (! complete_response)
957 push_hdr = true; /* Push the header as the client may react
958 * on header alone while the body data is
959 * being prepared. */
960 else
961 {
962 if (1400 > (header_size + body_size))
963 push_hdr = false; /* Do not push the header as complete
964 * reply is already ready and the whole
965 * reply most probably will fit into
966 * the single IP packet. */
967 else
968 push_hdr = true; /* Push header alone so client may react
969 * on it while reply body is being delivered. */
970 }
971 }
972 else
973 push_hdr = false;
974
975 if (complete_response && (0 == body_size))
976 push_hdr = true; /* The header alone is equal to the whole response. */
977
978 if (
979#ifdef MHD_VECT_SEND
980 (no_vec) ||
981 (0 == body_size) ||
982 ((size_t) SSIZE_MAX <= header_size) ||
983 ((size_t) _MHD_SEND_VEC_MAX < header_size)
984#ifdef _WIN32
985 || ((size_t) UINT_MAX < header_size)
986#endif /* _WIN32 */
987#else /* ! MHD_VECT_SEND */
988 true
989#endif /* ! MHD_VECT_SEND */
990 )
991 {
992 ret = MHD_send_data_ (connection,
993 header,
994 header_size,
995 push_hdr);
996
997 if ( (header_size == (size_t) ret) &&
998 ((size_t) SSIZE_MAX > header_size) &&
999 (0 != body_size) &&
1000 (connection->sk_nonblck) )
1001 {
1002 ssize_t ret2;
1003 /* The header has been sent completely.
1004 * Try to send the reply body without waiting for
1005 * the next round. */
1006 /* Make sure that sum of ret + ret2 will not exceed SSIZE_MAX as
1007 * function needs to return positive value if succeed. */
1008 if ( (((size_t) SSIZE_MAX) - ((size_t) ret)) < body_size)
1009 {
1010 body_size = (((size_t) SSIZE_MAX) - ((size_t) ret));
1011 complete_response = false;
1012 push_body = complete_response;
1013 }
1014
1015 ret2 = MHD_send_data_ (connection,
1016 body,
1017 body_size,
1018 push_body);
1019 if (0 < ret2)
1020 return ret + ret2; /* Total data sent */
1021 if (MHD_ERR_AGAIN_ == ret2)
1022 return ret;
1023
1024 return ret2; /* Error code */
1025 }
1026 return ret;
1027 }
1028#ifdef MHD_VECT_SEND
1029
1030 if ( ((size_t) SSIZE_MAX <= body_size) ||
1031 ((size_t) SSIZE_MAX < (header_size + body_size)) )
1032 {
1033 /* Return value limit */
1034 body_size = SSIZE_MAX - header_size;
1035 complete_response = false;
1036 push_body = complete_response;
1037 }
1038#if (SSIZE_MAX != _MHD_SEND_VEC_MAX) || (_MHD_SEND_VEC_MAX + 0 == 0)
1039 if (((size_t) _MHD_SEND_VEC_MAX <= body_size) ||
1040 ((size_t) _MHD_SEND_VEC_MAX < (header_size + body_size)))
1041 {
1042 /* Send total amount limit */
1043 body_size = _MHD_SEND_VEC_MAX - header_size;
1044 complete_response = false;
1045 push_body = complete_response;
1046 }
1047#endif /* SSIZE_MAX != _MHD_SEND_VEC_MAX */
1048
1049 pre_send_setopt (connection,
1050#ifdef HAVE_SENDMSG
1051 true,
1052#else /* ! HAVE_SENDMSG */
1053 false,
1054#endif /* ! HAVE_SENDMSG */
1055 push_hdr || push_body);
1056#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
1057 vector[0].iov_base = (void *) header;
1058 vector[0].iov_len = header_size;
1059 vector[1].iov_base = (void *) body;
1060 vector[1].iov_len = body_size;
1061
1062#if defined(HAVE_SENDMSG)
1063 memset (&msg, 0, sizeof(msg));
1064 msg.msg_iov = vector;
1065 msg.msg_iovlen = 2;
1066
1067 ret = sendmsg (s, &msg, MSG_NOSIGNAL_OR_ZERO);
1068#elif defined (HAVE_WRITEV)
1069 ret = writev (s, vector, 2);
1070#endif /* HAVE_WRITEV */
1071#endif /* HAVE_SENDMSG || HAVE_WRITEV */
1072#ifdef _WIN32
1073 if ((size_t) UINT_MAX < body_size)
1074 {
1075 /* Send item size limit */
1076 body_size = UINT_MAX;
1077 complete_response = false;
1078 push_body = complete_response;
1079 }
1080 vector[0].buf = (char *) header;
1081 vector[0].len = (unsigned long) header_size;
1082 vector[1].buf = (char *) body;
1083 vector[1].len = (unsigned long) body_size;
1084
1085 ret = WSASend (s, vector, 2, &vec_sent, 0, NULL, NULL);
1086 if (0 == ret)
1087 ret = (ssize_t) vec_sent;
1088 else
1089 ret = -1;
1090#endif /* _WIN32 */
1091
1092 if (0 > ret)
1093 {
1094 const int err = MHD_socket_get_error_ ();
1095
1096 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
1097 {
1098#if EPOLL_SUPPORT
1099 /* EAGAIN, no longer write-ready */
1100 connection->epoll_state &=
1102#endif /* EPOLL_SUPPORT */
1103 return MHD_ERR_AGAIN_;
1104 }
1105 if (MHD_SCKT_ERR_IS_EINTR_ (err))
1106 return MHD_ERR_AGAIN_;
1108 return MHD_ERR_CONNRESET_;
1110 return MHD_ERR_PIPE_;
1112 return MHD_ERR_OPNOTSUPP_;
1114 return MHD_ERR_NOTCONN_;
1116 return MHD_ERR_INVAL_;
1118 return MHD_ERR_NOMEM_;
1120 return MHD_ERR_BADF_;
1121 /* Treat any other error as a hard error. */
1122 return MHD_ERR_NOTCONN_;
1123 }
1124#if EPOLL_SUPPORT
1125 else if ((header_size + body_size) > (size_t) ret)
1126 connection->epoll_state &=
1128#endif /* EPOLL_SUPPORT */
1129
1130 /* If there is a need to push the data from network buffers
1131 * call post_send_setopt(). */
1132 if ( (push_body) &&
1133 ((header_size + body_size) == (size_t) ret) )
1134 {
1135 /* Complete reply has been sent. */
1136 /* If TLS connection is used then next final send() will be
1137 * without MSG_MORE support. If non-TLS connection is used
1138 * it's unknown whether next 'send' will be plain send() / sendmsg() or
1139 * sendfile() will be used so assume that next final send() will be
1140 * the same, like for this response. */
1141 post_send_setopt (connection,
1142#ifdef HAVE_SENDMSG
1143 true,
1144#else /* ! HAVE_SENDMSG */
1145 false,
1146#endif /* ! HAVE_SENDMSG */
1147 true);
1148 }
1149 else if ( (push_hdr) &&
1150 (header_size <= (size_t) ret))
1151 {
1152 /* The header has been sent completely and there is a
1153 * need to push the header data. */
1154 /* Luckily the type of send function will be used next is known. */
1155 post_send_setopt (connection,
1156#if defined(_MHD_HAVE_SENDFILE)
1157 MHD_resp_sender_std == connection->resp_sender,
1158#else /* ! _MHD_HAVE_SENDFILE */
1159 true,
1160#endif /* ! _MHD_HAVE_SENDFILE */
1161 true);
1162 }
1163
1164 return ret;
1165#else /* ! MHD_VECT_SEND */
1166 mhd_assert (false);
1167 return MHD_ERR_CONNRESET_; /* Unreachable. Mute warnings. */
1168#endif /* ! MHD_VECT_SEND */
1169}
1170
1171
1172#if defined(_MHD_HAVE_SENDFILE)
1173ssize_t
1174MHD_send_sendfile_ (struct MHD_Connection *connection)
1175{
1176 ssize_t ret;
1177 const int file_fd = connection->response->fd;
1178 uint64_t left;
1179 uint64_t offsetu64;
1180#ifndef HAVE_SENDFILE64
1181 const uint64_t max_off_t = (uint64_t) OFF_T_MAX;
1182#else /* HAVE_SENDFILE64 */
1183 const uint64_t max_off_t = (uint64_t) OFF64_T_MAX;
1184#endif /* HAVE_SENDFILE64 */
1185#ifdef MHD_LINUX_SOLARIS_SENDFILE
1186#ifndef HAVE_SENDFILE64
1187 off_t offset;
1188#else /* HAVE_SENDFILE64 */
1189 off64_t offset;
1190#endif /* HAVE_SENDFILE64 */
1191#endif /* MHD_LINUX_SOLARIS_SENDFILE */
1192#ifdef HAVE_FREEBSD_SENDFILE
1193 off_t sent_bytes;
1194 int flags = 0;
1195#endif
1196#ifdef HAVE_DARWIN_SENDFILE
1197 off_t len;
1198#endif /* HAVE_DARWIN_SENDFILE */
1199 const bool used_thr_p_c = (0 != (connection->daemon->options
1201 const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ :
1203 size_t send_size = 0;
1204 bool push_data;
1205 mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
1206 mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS));
1207
1208 offsetu64 = connection->response_write_position
1209 + connection->response->fd_off;
1210 if (max_off_t < offsetu64)
1211 { /* Retry to send with standard 'send()'. */
1212 connection->resp_sender = MHD_resp_sender_std;
1213 return MHD_ERR_AGAIN_;
1214 }
1215
1216 left = connection->response->total_size - connection->response_write_position;
1217
1218 if ( (uint64_t) SSIZE_MAX < left)
1219 left = SSIZE_MAX;
1220
1221 /* Do not allow system to stick sending on single fast connection:
1222 * use 128KiB chunks (2MiB for thread-per-connection). */
1223 if (chunk_size < left)
1224 {
1225 send_size = chunk_size;
1226 push_data = false; /* No need to push data, there is more to send. */
1227 }
1228 else
1229 {
1230 send_size = (size_t) left;
1231 push_data = true; /* Final piece of data, need to push to the network. */
1232 }
1233 pre_send_setopt (connection, false, push_data);
1234
1235#ifdef MHD_LINUX_SOLARIS_SENDFILE
1236#ifndef HAVE_SENDFILE64
1237 offset = (off_t) offsetu64;
1238 ret = sendfile (connection->socket_fd,
1239 file_fd,
1240 &offset,
1241 send_size);
1242#else /* HAVE_SENDFILE64 */
1243 offset = (off64_t) offsetu64;
1244 ret = sendfile64 (connection->socket_fd,
1245 file_fd,
1246 &offset,
1247 send_size);
1248#endif /* HAVE_SENDFILE64 */
1249 if (0 > ret)
1250 {
1251 const int err = MHD_socket_get_error_ ();
1252 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
1253 {
1254#ifdef EPOLL_SUPPORT
1255 /* EAGAIN --- no longer write-ready */
1256 connection->epoll_state &=
1258#endif /* EPOLL_SUPPORT */
1259 return MHD_ERR_AGAIN_;
1260 }
1261 if (MHD_SCKT_ERR_IS_EINTR_ (err))
1262 return MHD_ERR_AGAIN_;
1263#ifdef HAVE_LINUX_SENDFILE
1264 if (MHD_SCKT_ERR_IS_ (err,
1266 return MHD_ERR_BADF_;
1267 /* sendfile() failed with EINVAL if mmap()-like operations are not
1268 supported for FD or other 'unusual' errors occurred, so we should try
1269 to fall back to 'SEND'; see also this thread for info on
1270 odd libc/Linux behavior with sendfile:
1271 http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
1272 connection->resp_sender = MHD_resp_sender_std;
1273 return MHD_ERR_AGAIN_;
1274#else /* HAVE_SOLARIS_SENDFILE */
1275 if ( (EAFNOSUPPORT == err) ||
1276 (EINVAL == err) ||
1277 (EOPNOTSUPP == err) )
1278 { /* Retry with standard file reader. */
1279 connection->resp_sender = MHD_resp_sender_std;
1280 return MHD_ERR_AGAIN_;
1281 }
1282 if ( (ENOTCONN == err) ||
1283 (EPIPE == err) )
1284 {
1285 return MHD_ERR_CONNRESET_;
1286 }
1287 return MHD_ERR_BADF_; /* Fail hard */
1288#endif /* HAVE_SOLARIS_SENDFILE */
1289 }
1290#ifdef EPOLL_SUPPORT
1291 else if (send_size > (size_t) ret)
1292 connection->epoll_state &=
1294#endif /* EPOLL_SUPPORT */
1295#elif defined(HAVE_FREEBSD_SENDFILE)
1296#ifdef SF_FLAGS
1297 flags = used_thr_p_c ?
1298 freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
1299#endif /* SF_FLAGS */
1300 if (0 != sendfile (file_fd,
1301 connection->socket_fd,
1302 (off_t) offsetu64,
1303 send_size,
1304 NULL,
1305 &sent_bytes,
1306 flags))
1307 {
1308 const int err = MHD_socket_get_error_ ();
1309 if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ||
1310 MHD_SCKT_ERR_IS_EINTR_ (err) ||
1311 (EBUSY == err) )
1312 {
1313 mhd_assert (SSIZE_MAX >= sent_bytes);
1314 if (0 != sent_bytes)
1315 return (ssize_t) sent_bytes;
1316
1317 return MHD_ERR_AGAIN_;
1318 }
1319 /* Some unrecoverable error. Possibly file FD is not suitable
1320 * for sendfile(). Retry with standard send(). */
1321 connection->resp_sender = MHD_resp_sender_std;
1322 return MHD_ERR_AGAIN_;
1323 }
1324 mhd_assert (0 < sent_bytes);
1325 mhd_assert (SSIZE_MAX >= sent_bytes);
1326 ret = (ssize_t) sent_bytes;
1327#elif defined(HAVE_DARWIN_SENDFILE)
1328 len = (off_t) send_size; /* chunk always fit */
1329 if (0 != sendfile (file_fd,
1330 connection->socket_fd,
1331 (off_t) offsetu64,
1332 &len,
1333 NULL,
1334 0))
1335 {
1336 const int err = MHD_socket_get_error_ ();
1337 if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ||
1339 {
1340 mhd_assert (0 <= len);
1341 mhd_assert (SSIZE_MAX >= len);
1342 mhd_assert (send_size >= (size_t) len);
1343 if (0 != len)
1344 return (ssize_t) len;
1345
1346 return MHD_ERR_AGAIN_;
1347 }
1348 if ((ENOTCONN == err) ||
1349 (EPIPE == err) )
1350 return MHD_ERR_CONNRESET_;
1351 if ((ENOTSUP == err) ||
1352 (EOPNOTSUPP == err) )
1353 { /* This file FD is not suitable for sendfile().
1354 * Retry with standard send(). */
1355 connection->resp_sender = MHD_resp_sender_std;
1356 return MHD_ERR_AGAIN_;
1357 }
1358 return MHD_ERR_BADF_; /* Return hard error. */
1359 }
1360 mhd_assert (0 <= len);
1361 mhd_assert (SSIZE_MAX >= len);
1362 mhd_assert (send_size >= (size_t) len);
1363 ret = (ssize_t) len;
1364#endif /* HAVE_FREEBSD_SENDFILE */
1365
1366 /* If there is a need to push the data from network buffers
1367 * call post_send_setopt(). */
1368 /* It's unknown whether sendfile() will be used in the next
1369 * response so assume that next response will be the same. */
1370 if ( (push_data) &&
1371 (send_size == (size_t) ret) )
1372 post_send_setopt (connection, false, push_data);
1373
1374 return ret;
1375}
1376
1377
1378#endif /* _MHD_HAVE_SENDFILE */
1379
1380#if defined(MHD_VECT_SEND)
1381
1382
1396static ssize_t
1397send_iov_nontls (struct MHD_Connection *connection,
1398 struct MHD_iovec_track_ *const r_iov,
1399 bool push_data)
1400{
1401 ssize_t res;
1402 ssize_t total_sent;
1403 size_t items_to_send;
1404#ifdef HAVE_SENDMSG
1405 struct msghdr msg;
1406#elif defined(MHD_WINSOCK_SOCKETS)
1407 DWORD bytes_sent;
1408 DWORD cnt_w;
1409#endif /* MHD_WINSOCK_SOCKETS */
1410
1411 mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS));
1412
1413 if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
1414 (MHD_CONNECTION_CLOSED == connection->state) )
1415 {
1416 return MHD_ERR_NOTCONN_;
1417 }
1418
1419 items_to_send = r_iov->cnt - r_iov->sent;
1420#ifdef _MHD_IOV_MAX
1421 if (_MHD_IOV_MAX < items_to_send)
1422 {
1423 mhd_assert (0 < _MHD_IOV_MAX);
1424 if (0 == _MHD_IOV_MAX)
1425 return MHD_ERR_NOTCONN_; /* Should never happen */
1426 items_to_send = _MHD_IOV_MAX;
1427 push_data = false; /* Incomplete response */
1428 }
1429#endif /* _MHD_IOV_MAX */
1430#ifdef HAVE_SENDMSG
1431 memset (&msg, 0, sizeof(struct msghdr));
1432 msg.msg_iov = r_iov->iov + r_iov->sent;
1433 msg.msg_iovlen = items_to_send;
1434
1435 pre_send_setopt (connection, true, push_data);
1436#ifdef MHD_USE_MSG_MORE
1437 res = sendmsg (connection->socket_fd, &msg,
1438 MSG_NOSIGNAL_OR_ZERO | (push_data ? 0 : MSG_MORE));
1439#else /* ! MHD_USE_MSG_MORE */
1440 res = sendmsg (connection->socket_fd, &msg, MSG_NOSIGNAL_OR_ZERO);
1441#endif /* ! MHD_USE_MSG_MORE */
1442#elif defined(HAVE_WRITEV)
1443 pre_send_setopt (connection, true, push_data);
1444 res = writev (connection->socket_fd, r_iov->iov + r_iov->sent,
1445 items_to_send);
1446#elif defined(MHD_WINSOCK_SOCKETS)
1447#ifdef _WIN64
1448 if (items_to_send > UINT32_MAX)
1449 {
1450 cnt_w = UINT32_MAX;
1451 push_data = false; /* Incomplete response */
1452 }
1453 else
1454 cnt_w = (DWORD) items_to_send;
1455#else /* ! _WIN64 */
1456 cnt_w = (DWORD) items_to_send;
1457#endif /* ! _WIN64 */
1458 pre_send_setopt (connection, true, push_data);
1459 if (0 == WSASend (connection->socket_fd,
1460 (LPWSABUF) (r_iov->iov + r_iov->sent),
1461 cnt_w,
1462 &bytes_sent, 0, NULL, NULL))
1463 res = (ssize_t) bytes_sent;
1464 else
1465 res = -1;
1466#else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_WINSOCK_SOCKETS */
1467#error No vector-send function available
1468#endif
1469
1470 if (0 > res)
1471 {
1472 const int err = MHD_socket_get_error_ ();
1473
1474 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
1475 {
1476#ifdef EPOLL_SUPPORT
1477 /* EAGAIN --- no longer write-ready */
1478 connection->epoll_state &=
1480#endif /* EPOLL_SUPPORT */
1481 return MHD_ERR_AGAIN_;
1482 }
1483 if (MHD_SCKT_ERR_IS_EINTR_ (err))
1484 return MHD_ERR_AGAIN_;
1486 return MHD_ERR_CONNRESET_;
1488 return MHD_ERR_PIPE_;
1490 return MHD_ERR_OPNOTSUPP_;
1492 return MHD_ERR_NOTCONN_;
1494 return MHD_ERR_INVAL_;
1496 return MHD_ERR_NOMEM_;
1498 return MHD_ERR_BADF_;
1499 /* Treat any other error as a hard error. */
1500 return MHD_ERR_NOTCONN_;
1501 }
1502
1503 /* Some data has been sent */
1504 total_sent = res;
1505 /* Adjust the internal tracking information for the iovec to
1506 * take this last send into account. */
1507 while ((0 != res) && (r_iov->iov[r_iov->sent].iov_len <= (size_t) res))
1508 {
1509 res -= r_iov->iov[r_iov->sent].iov_len;
1510 r_iov->sent++; /* The iov element has been completely sent */
1511 mhd_assert ((r_iov->cnt > r_iov->sent) || (0 == res));
1512 }
1513
1514 if (r_iov->cnt == r_iov->sent)
1515 post_send_setopt (connection, true, push_data);
1516 else
1517 {
1518#ifdef EPOLL_SUPPORT
1519 connection->epoll_state &=
1521#endif /* EPOLL_SUPPORT */
1522 if (0 != res)
1523 {
1524 mhd_assert (r_iov->cnt > r_iov->sent);
1525 /* The last iov element has been partially sent */
1526 r_iov->iov[r_iov->sent].iov_base =
1527 (void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
1528 r_iov->iov[r_iov->sent].iov_len -= (MHD_iov_size_) res;
1529 }
1530 }
1531
1532 return total_sent;
1533}
1534
1535
1536#endif /* MHD_VECT_SEND */
1537
1538#if ! defined(MHD_VECT_SEND) || defined(HTTPS_SUPPORT) || \
1539 defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
1540
1541
1556static ssize_t
1557send_iov_emu (struct MHD_Connection *connection,
1558 struct MHD_iovec_track_ *const r_iov,
1559 bool push_data)
1560{
1561 const bool non_blk = connection->sk_nonblck;
1562 size_t total_sent;
1563 ssize_t res;
1564
1565 mhd_assert (NULL != r_iov->iov);
1566 total_sent = 0;
1567 do
1568 {
1569 if ((size_t) SSIZE_MAX - total_sent < r_iov->iov[r_iov->sent].iov_len)
1570 return total_sent; /* return value would overflow */
1571
1572 res = MHD_send_data_ (connection,
1573 r_iov->iov[r_iov->sent].iov_base,
1574 r_iov->iov[r_iov->sent].iov_len,
1575 push_data && (r_iov->cnt == r_iov->sent + 1));
1576 if (0 > res)
1577 {
1578 /* Result is an error */
1579 if (0 == total_sent)
1580 return res; /* Nothing was sent, return result as is */
1581
1582 if (MHD_ERR_AGAIN_ == res)
1583 return total_sent; /* Some data has been sent, return the amount */
1584
1585 return res; /* Any kind of a hard error */
1586 }
1587
1588 total_sent += (size_t) res;
1589
1590 if (r_iov->iov[r_iov->sent].iov_len != (size_t) res)
1591 {
1592 /* Incomplete buffer has been sent.
1593 * Adjust buffer of the last element. */
1594 r_iov->iov[r_iov->sent].iov_base =
1595 (void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
1596 r_iov->iov[r_iov->sent].iov_len -= res;
1597
1598 return total_sent;
1599 }
1600 /* The iov element has been completely sent */
1601 r_iov->sent++;
1602 } while ((r_iov->cnt > r_iov->sent) && (non_blk));
1603
1604 return (ssize_t) total_sent;
1605}
1606
1607
1608#endif /* !MHD_VECT_SEND || HTTPS_SUPPORT
1609 || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1610
1611
1612ssize_t
1614 struct MHD_iovec_track_ *const r_iov,
1615 bool push_data)
1616{
1617#ifdef MHD_VECT_SEND
1618#if defined(HTTPS_SUPPORT) || \
1619 defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
1620 bool use_iov_send = true;
1621#endif /* HTTPS_SUPPORT || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1622#endif /* MHD_VECT_SEND */
1623
1624 mhd_assert (NULL != connection->resp_iov.iov);
1625 mhd_assert (NULL != connection->response->data_iov);
1626 mhd_assert (connection->resp_iov.cnt > connection->resp_iov.sent);
1627#ifdef MHD_VECT_SEND
1628#if defined(HTTPS_SUPPORT) || \
1629 defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
1630#ifdef HTTPS_SUPPORT
1631 use_iov_send = use_iov_send &&
1632 (0 == (connection->daemon->options & MHD_USE_TLS));
1633#endif /* HTTPS_SUPPORT */
1634#ifdef _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
1635 use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked ||
1636 connection->sk_spipe_suppress);
1637#endif /* _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1638 if (use_iov_send)
1639#endif /* HTTPS_SUPPORT || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1640 return send_iov_nontls (connection, r_iov, push_data);
1641#endif /* MHD_VECT_SEND */
1642
1643#if ! defined(MHD_VECT_SEND) || defined(HTTPS_SUPPORT) || \
1644 defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
1645 return send_iov_emu (connection, r_iov, push_data);
1646#endif /* !MHD_VECT_SEND || HTTPS_SUPPORT
1647 || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1648}
#define MHD_ERR_TLS_
Definition: connection.h:77
#define MHD_ERR_OPNOTSUPP_
Definition: connection.h:67
#define MHD_ERR_PIPE_
Definition: connection.h:72
#define MHD_ERR_INVAL_
Definition: internal.h:1889
#define MHD_ERR_CONNRESET_
Definition: internal.h:1868
#define MHD_ERR_NOMEM_
Definition: internal.h:1879
MHD_EpollState
Definition: internal.h:588
@ MHD_EPOLL_STATE_WRITE_READY
Definition: internal.h:606
#define MHD_ERR_AGAIN_
Definition: internal.h:1863
#define MHD_ERR_BADF_
Definition: internal.h:1884
#define MHD_ERR_NOTCONN_
Definition: internal.h:1874
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
#define OFF_T_MAX
Definition: mhd_limits.h:123
#define UINT32_MAX
Definition: mhd_limits.h:73
#define UINT_MAX
Definition: mhd_limits.h:45
#define MHD_SCKT_ERR_IS_(err, code)
Definition: mhd_sockets.h:611
int MHD_SCKT_OPT_BOOL_
Definition: mhd_sockets.h:203
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
Definition: mhd_sockets.h:643
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
Definition: mhd_sockets.h:656
#define MHD_socket_strerr_(err)
Definition: mhd_sockets.h:542
#define MHD_socket_last_strerr_()
Definition: mhd_sockets.h:549
#define MHD_SCKT_EOPNOTSUPP_
Definition: mhd_sockets.h:484
#define MHD_SCKT_EBADF_
Definition: mhd_sockets.h:454
#define MHD_socket_get_error_()
Definition: mhd_sockets.h:523
#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err)
Definition: mhd_sockets.h:688
#define MHD_SCKT_EINVAL_
Definition: mhd_sockets.h:464
#define MHD_SCKT_ERR_IS_EINTR_(err)
Definition: mhd_sockets.h:634
#define MHD_SCKT_SEND_MAX_SIZE_
Definition: mhd_sockets.h:222
#define MHD_SCKT_ENOTCONN_
Definition: mhd_sockets.h:429
#define MHD_SCKT_ENOTSOCK_
Definition: mhd_sockets.h:459
#define MHD_send_(s, b, l)
Definition: mhd_sockets.h:261
#define NULL
Definition: reason_phrase.c:30
#define _(String)
Definition: mhd_options.h:42
#define MHD_SENFILE_CHUNK_THR_P_C_
Definition: mhd_send.c:74
ssize_t MHD_send_hdr_and_body_(struct MHD_Connection *connection, const char *header, size_t header_size, bool never_push_hdr, const char *body, size_t body_size, bool complete_response)
Definition: mhd_send.c:900
ssize_t MHD_send_iovec_(struct MHD_Connection *connection, struct MHD_iovec_track_ *const r_iov, bool push_data)
Definition: mhd_send.c:1613
ssize_t MHD_send_data_(struct MHD_Connection *connection, const char *buffer, size_t buffer_size, bool push_data)
Definition: mhd_send.c:749
static bool zero_send_(struct MHD_Connection *connection)
Definition: mhd_send.c:545
bool MHD_connection_set_cork_state_(struct MHD_Connection *connection, bool cork_state)
Definition: mhd_send.c:240
#define _MHD_SEND_VEC_MAX
void MHD_send_init_static_vars_(void)
Definition: mhd_send.c:153
static void pre_send_setopt(struct MHD_Connection *connection, bool plain_send, bool push_data)
Definition: mhd_send.c:317
static void post_send_setopt(struct MHD_Connection *connection, bool plain_send_next, bool push_data)
Definition: mhd_send.c:578
static ssize_t send_iov_emu(struct MHD_Connection *connection, struct MHD_iovec_track_ *const r_iov, bool push_data)
Definition: mhd_send.c:1557
bool MHD_connection_set_nodelay_state_(struct MHD_Connection *connection, bool nodelay_state)
Definition: mhd_send.c:170
#define MHD_SENFILE_CHUNK_
Definition: mhd_send.c:69
Declarations of send() wrappers.
@ MHD_CONNECTION_CLOSED
Definition: internal.h:696
size_t MHD_iov_size_
Definition: internal.h:403
MHD_tristate
Definition: internal.h:183
@ _MHD_ON
Definition: internal.h:187
@ _MHD_UNKNOWN
Definition: internal.h:184
@ _MHD_YES
Definition: internal.h:188
@ _MHD_OFF
Definition: internal.h:185
macros for mhd_assert()
limits values definitions
#define SSIZE_MAX
Definition: mhd_limits.h:113
#define MHD_SCKT_ENOPROTOOPT_
Definition: mhd_sockets.h:584
#define MHD_send4_(s, b, l, f)
Definition: mhd_sockets.h:357
#define MSG_NOSIGNAL_OR_ZERO
Definition: mhd_sockets.h:189
#define MHD_SCKT_EPIPE_
Definition: mhd_sockets.h:569
int MHD_socket
Definition: microhttpd.h:207
int off_t offset
Definition: microhttpd.h:3643
#define MHD_INVALID_SOCKET
Definition: microhttpd.h:208
@ MHD_USE_THREAD_PER_CONNECTION
Definition: microhttpd.h:1261
@ MHD_USE_TLS
Definition: microhttpd.h:1246
MHD_socket socket_fd
Definition: internal.h:752
enum MHD_tristate sk_nodelay
Definition: internal.h:1207
enum MHD_tristate is_nonip
Definition: internal.h:1187
struct MHD_Response * response
Definition: internal.h:968
bool sk_nonblck
Definition: internal.h:784
uint64_t response_write_position
Definition: internal.h:1121
struct MHD_iovec_track_ resp_iov
Definition: internal.h:1129
enum MHD_CONNECTION_STATE state
Definition: internal.h:1263
struct MHD_Daemon * daemon
Definition: internal.h:675
bool sk_spipe_suppress
Definition: internal.h:1197
enum MHD_tristate sk_corked
Definition: internal.h:1202
enum MHD_FLAG options
Definition: internal.h:1619
bool sigpipe_blocked
Definition: internal.h:2008
const void * iov_base
Definition: microhttpd.h:2185
size_t iov_len
Definition: microhttpd.h:2190
MHD_iovec_ * data_iov
Definition: internal.h:549
uint64_t total_size
Definition: internal.h:1642
uint64_t fd_off
Definition: internal.h:1653
MHD_iovec_ * iov
Definition: internal.h:414