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