GNU libmicrohttpd 0.9.73
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,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 &= ~MHD_EPOLL_STATE_WRITE_READY;
785#endif
786 return MHD_ERR_AGAIN_;
787 }
788 if (GNUTLS_E_INTERRUPTED == ret)
789 return MHD_ERR_AGAIN_;
790 if ( (GNUTLS_E_ENCRYPTION_FAILED == ret) ||
791 (GNUTLS_E_INVALID_SESSION == ret) ||
792 (GNUTLS_E_COMPRESSION_FAILED == ret) ||
793 (GNUTLS_E_EXPIRED == ret) ||
794 (GNUTLS_E_HASH_FAILED == ret) )
795 return MHD_ERR_TLS_;
796 if ( (GNUTLS_E_PUSH_ERROR == ret) ||
797 (GNUTLS_E_INTERNAL_ERROR == ret) ||
798 (GNUTLS_E_CRYPTODEV_IOCTL_ERROR == ret) ||
799 (GNUTLS_E_CRYPTODEV_DEVICE_ERROR == ret) )
800 return MHD_ERR_PIPE_;
801 if (GNUTLS_E_PREMATURE_TERMINATION == ret)
802 return MHD_ERR_CONNRESET_;
803 if (GNUTLS_E_MEMORY_ERROR == ret)
804 return MHD_ERR_NOMEM_;
805 if (ret < 0)
806 {
807 /* Treat any other error as hard error. */
808 return MHD_ERR_NOTCONN_;
809 }
810#ifdef EPOLL_SUPPORT
811 /* Unlike non-TLS connections, do not reset "write-ready" if
812 * sent amount smaller than provided amount, as TLS
813 * connections may break data into smaller parts for sending. */
814#endif /* EPOLL_SUPPORT */
815#endif /* HTTPS_SUPPORT */
816 (void) 0; /* Mute compiler warning for non-TLS builds. */
817 }
818 else
819 {
820 /* plaintext transmission */
821 if (buffer_size > MHD_SCKT_SEND_MAX_SIZE_)
822 {
823 buffer_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */
824 push_data = false; /* Incomplete send */
825 }
826
827 pre_send_setopt (connection, (! tls_conn), push_data);
828#ifdef MHD_USE_MSG_MORE
829 ret = MHD_send4_ (s,
830 buffer,
831 buffer_size,
832 push_data ? 0 : MSG_MORE);
833#else
834 ret = MHD_send4_ (s,
835 buffer,
836 buffer_size,
837 0);
838#endif
839
840 if (0 > ret)
841 {
842 const int err = MHD_socket_get_error_ ();
843
844 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
845 {
846#if EPOLL_SUPPORT
847 /* EAGAIN, no longer write-ready */
848 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
849#endif /* EPOLL_SUPPORT */
850 return MHD_ERR_AGAIN_;
851 }
852 if (MHD_SCKT_ERR_IS_EINTR_ (err))
853 return MHD_ERR_AGAIN_;
855 return MHD_ERR_CONNRESET_;
857 return MHD_ERR_PIPE_;
859 return MHD_ERR_OPNOTSUPP_;
861 return MHD_ERR_NOTCONN_;
863 return MHD_ERR_INVAL_;
865 return MHD_ERR_NOMEM_;
867 return MHD_ERR_BADF_;
868 /* Treat any other error as a hard error. */
869 return MHD_ERR_NOTCONN_;
870 }
871#if EPOLL_SUPPORT
872 else if (buffer_size > (size_t) ret)
873 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
874#endif /* EPOLL_SUPPORT */
875 }
876
877 /* If there is a need to push the data from network buffers
878 * call post_send_setopt(). */
879 /* If TLS connection is used then next final send() will be
880 * without MSG_MORE support. If non-TLS connection is used
881 * it's unknown whether sendfile() will be used or not so
882 * assume that next call will be the same, like this call. */
883 if ( (push_data) &&
884 (buffer_size == (size_t) ret) )
885 post_send_setopt (connection, (! tls_conn), push_data);
886
887 return ret;
888}
889
890
891ssize_t
893 const char *header,
894 size_t header_size,
895 bool never_push_hdr,
896 const char *body,
897 size_t body_size,
898 bool complete_response)
899{
900 ssize_t ret;
901 bool push_hdr;
902 bool push_body;
903 MHD_socket s = connection->socket_fd;
904#ifndef _WIN32
905#define _MHD_SEND_VEC_MAX MHD_SCKT_SEND_MAX_SIZE_
906#else /* ! _WIN32 */
907#define _MHD_SEND_VEC_MAX UINT32_MAX
908#endif /* ! _WIN32 */
909#ifdef MHD_VECT_SEND
910#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
911 struct iovec vector[2];
912#ifdef HAVE_SENDMSG
913 struct msghdr msg;
914#endif /* HAVE_SENDMSG */
915#endif /* HAVE_SENDMSG || HAVE_WRITEV */
916#ifdef _WIN32
917 WSABUF vector[2];
918 DWORD vec_sent;
919#endif /* _WIN32 */
920 bool no_vec; /* Is vector-send() disallowed? */
921
922 no_vec = false;
923#ifdef HTTPS_SUPPORT
924 no_vec = no_vec || (connection->daemon->options & MHD_USE_TLS);
925#endif /* HTTPS_SUPPORT */
926#if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL) ) && \
927 defined(MHD_SEND_SPIPE_SEND_SUPPRESS_POSSIBLE) && \
928 defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED)
929 no_vec = no_vec || (! connection->daemon->sigpipe_blocked &&
930 ! connection->sk_spipe_suppress);
931#endif /* (!HAVE_SENDMSG || ! MSG_NOSIGNAL) &&
932 MHD_SEND_SPIPE_SEND_SUPPRESS_POSSIBLE &&
933 MHD_SEND_SPIPE_SUPPRESS_NEEDED */
934#endif /* MHD_VECT_SEND */
935
936 mhd_assert ( (NULL != body) || (0 == body_size) );
937
938 if ( (MHD_INVALID_SOCKET == s) ||
939 (MHD_CONNECTION_CLOSED == connection->state) )
940 {
941 return MHD_ERR_NOTCONN_;
942 }
943
944 push_body = complete_response;
945
946 if (! never_push_hdr)
947 {
948 if (! complete_response)
949 push_hdr = true; /* Push the header as the client may react
950 * on header alone while the body data is
951 * being prepared. */
952 else
953 {
954 if (1400 > (header_size + body_size))
955 push_hdr = false; /* Do not push the header as complete
956 * reply is already ready and the whole
957 * reply most probably will fit into
958 * the single IP packet. */
959 else
960 push_hdr = true; /* Push header alone so client may react
961 * on it while reply body is being delivered. */
962 }
963 }
964 else
965 push_hdr = false;
966
967 if (complete_response && (0 == body_size))
968 push_hdr = true; /* The header alone is equal to the whole response. */
969
970 if (
971#ifdef MHD_VECT_SEND
972 (no_vec) ||
973 (0 == body_size) ||
974 ((size_t) SSIZE_MAX <= header_size) ||
975 ((size_t) _MHD_SEND_VEC_MAX < header_size)
976#ifdef _WIN32
977 || ((size_t) UINT_MAX < header_size)
978#endif /* _WIN32 */
979#else /* ! MHD_VECT_SEND */
980 true
981#endif /* ! MHD_VECT_SEND */
982 )
983 {
984 ret = MHD_send_data_ (connection,
985 header,
986 header_size,
987 push_hdr);
988
989 if ( (header_size == (size_t) ret) &&
990 ((size_t) SSIZE_MAX > header_size) &&
991 (0 != body_size) &&
992 (connection->sk_nonblck) )
993 {
994 ssize_t ret2;
995 /* The header has been sent completely.
996 * Try to send the reply body without waiting for
997 * the next round. */
998 /* Make sure that sum of ret + ret2 will not exceed SSIZE_MAX as
999 * function needs to return positive value if succeed. */
1000 if ( (((size_t) SSIZE_MAX) - ((size_t) ret)) < body_size)
1001 {
1002 body_size = (((size_t) SSIZE_MAX) - ((size_t) ret));
1003 complete_response = false;
1004 push_body = complete_response;
1005 }
1006
1007 ret2 = MHD_send_data_ (connection,
1008 body,
1009 body_size,
1010 push_body);
1011 if (0 < ret2)
1012 return ret + ret2; /* Total data sent */
1013 if (MHD_ERR_AGAIN_ == ret2)
1014 return ret;
1015
1016 return ret2; /* Error code */
1017 }
1018 return ret;
1019 }
1020#ifdef MHD_VECT_SEND
1021
1022 if ( ((size_t) SSIZE_MAX <= body_size) ||
1023 ((size_t) SSIZE_MAX < (header_size + body_size)) )
1024 {
1025 /* Return value limit */
1026 body_size = SSIZE_MAX - header_size;
1027 complete_response = false;
1028 push_body = complete_response;
1029 }
1030#if (SSIZE_MAX != _MHD_SEND_VEC_MAX) || (_MHD_SEND_VEC_MAX + 0 == 0)
1031 if (((size_t) _MHD_SEND_VEC_MAX <= body_size) ||
1032 ((size_t) _MHD_SEND_VEC_MAX < (header_size + body_size)))
1033 {
1034 /* Send total amount limit */
1035 body_size = _MHD_SEND_VEC_MAX - header_size;
1036 complete_response = false;
1037 push_body = complete_response;
1038 }
1039#endif /* SSIZE_MAX != _MHD_SEND_VEC_MAX */
1040
1041 pre_send_setopt (connection,
1042#ifdef HAVE_SENDMSG
1043 true,
1044#else /* ! HAVE_SENDMSG */
1045 false,
1046#endif /* ! HAVE_SENDMSG */
1047 push_hdr || push_body);
1048#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
1049 vector[0].iov_base = (void *) header;
1050 vector[0].iov_len = header_size;
1051 vector[1].iov_base = (void *) body;
1052 vector[1].iov_len = body_size;
1053
1054#if defined(HAVE_SENDMSG)
1055 memset (&msg, 0, sizeof(msg));
1056 msg.msg_iov = vector;
1057 msg.msg_iovlen = 2;
1058
1059 ret = sendmsg (s, &msg, MSG_NOSIGNAL_OR_ZERO);
1060#elif defined (HAVE_WRITEV)
1061 ret = writev (s, vector, 2);
1062#endif /* HAVE_WRITEV */
1063#endif /* HAVE_SENDMSG || HAVE_WRITEV */
1064#ifdef _WIN32
1065 if ((size_t) UINT_MAX < body_size)
1066 {
1067 /* Send item size limit */
1068 body_size = UINT_MAX;
1069 complete_response = false;
1070 push_body = complete_response;
1071 }
1072 vector[0].buf = (char *) header;
1073 vector[0].len = (unsigned long) header_size;
1074 vector[1].buf = (char *) body;
1075 vector[1].len = (unsigned long) body_size;
1076
1077 ret = WSASend (s, vector, 2, &vec_sent, 0, NULL, NULL);
1078 if (0 == ret)
1079 ret = (ssize_t) vec_sent;
1080 else
1081 ret = -1;
1082#endif /* _WIN32 */
1083
1084 if (0 > ret)
1085 {
1086 const int err = MHD_socket_get_error_ ();
1087
1088 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
1089 {
1090#if EPOLL_SUPPORT
1091 /* EAGAIN, no longer write-ready */
1092 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
1093#endif /* EPOLL_SUPPORT */
1094 return MHD_ERR_AGAIN_;
1095 }
1096 if (MHD_SCKT_ERR_IS_EINTR_ (err))
1097 return MHD_ERR_AGAIN_;
1099 return MHD_ERR_CONNRESET_;
1101 return MHD_ERR_PIPE_;
1103 return MHD_ERR_OPNOTSUPP_;
1105 return MHD_ERR_NOTCONN_;
1107 return MHD_ERR_INVAL_;
1109 return MHD_ERR_NOMEM_;
1111 return MHD_ERR_BADF_;
1112 /* Treat any other error as a hard error. */
1113 return MHD_ERR_NOTCONN_;
1114 }
1115#if EPOLL_SUPPORT
1116 else if ((header_size + body_size) > (size_t) ret)
1117 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
1118#endif /* EPOLL_SUPPORT */
1119
1120 /* If there is a need to push the data from network buffers
1121 * call post_send_setopt(). */
1122 if ( (push_body) &&
1123 ((header_size + body_size) == (size_t) ret) )
1124 {
1125 /* Complete reply has been sent. */
1126 /* If TLS connection is used then next final send() will be
1127 * without MSG_MORE support. If non-TLS connection is used
1128 * it's unknown whether next 'send' will be plain send() / sendmsg() or
1129 * sendfile() will be used so assume that next final send() will be
1130 * the same, like for this response. */
1131 post_send_setopt (connection,
1132#ifdef HAVE_SENDMSG
1133 true,
1134#else /* ! HAVE_SENDMSG */
1135 false,
1136#endif /* ! HAVE_SENDMSG */
1137 true);
1138 }
1139 else if ( (push_hdr) &&
1140 (header_size <= (size_t) ret))
1141 {
1142 /* The header has been sent completely and there is a
1143 * need to push the header data. */
1144 /* Luckily the type of send function will be used next is known. */
1145 post_send_setopt (connection,
1146#if defined(_MHD_HAVE_SENDFILE)
1147 MHD_resp_sender_std == connection->resp_sender,
1148#else /* ! _MHD_HAVE_SENDFILE */
1149 true,
1150#endif /* ! _MHD_HAVE_SENDFILE */
1151 true);
1152 }
1153
1154 return ret;
1155#else /* ! MHD_VECT_SEND */
1156 mhd_assert (false);
1157 return MHD_ERR_CONNRESET_; /* Unreachable. Mute warnings. */
1158#endif /* ! MHD_VECT_SEND */
1159}
1160
1161
1162#if defined(_MHD_HAVE_SENDFILE)
1163ssize_t
1164MHD_send_sendfile_ (struct MHD_Connection *connection)
1165{
1166 ssize_t ret;
1167 const int file_fd = connection->response->fd;
1168 uint64_t left;
1169 uint64_t offsetu64;
1170#ifndef HAVE_SENDFILE64
1171 const uint64_t max_off_t = (uint64_t) OFF_T_MAX;
1172#else /* HAVE_SENDFILE64 */
1173 const uint64_t max_off_t = (uint64_t) OFF64_T_MAX;
1174#endif /* HAVE_SENDFILE64 */
1175#ifdef MHD_LINUX_SOLARIS_SENDFILE
1176#ifndef HAVE_SENDFILE64
1177 off_t offset;
1178#else /* HAVE_SENDFILE64 */
1179 off64_t offset;
1180#endif /* HAVE_SENDFILE64 */
1181#endif /* MHD_LINUX_SOLARIS_SENDFILE */
1182#ifdef HAVE_FREEBSD_SENDFILE
1183 off_t sent_bytes;
1184 int flags = 0;
1185#endif
1186#ifdef HAVE_DARWIN_SENDFILE
1187 off_t len;
1188#endif /* HAVE_DARWIN_SENDFILE */
1189 const bool used_thr_p_c = (0 != (connection->daemon->options
1191 const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ :
1193 size_t send_size = 0;
1194 bool push_data;
1195 mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
1196 mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS));
1197
1198 offsetu64 = connection->response_write_position
1199 + connection->response->fd_off;
1200 if (max_off_t < offsetu64)
1201 { /* Retry to send with standard 'send()'. */
1202 connection->resp_sender = MHD_resp_sender_std;
1203 return MHD_ERR_AGAIN_;
1204 }
1205
1206 left = connection->response->total_size - connection->response_write_position;
1207
1208 if ( (uint64_t) SSIZE_MAX < left)
1209 left = SSIZE_MAX;
1210
1211 /* Do not allow system to stick sending on single fast connection:
1212 * use 128KiB chunks (2MiB for thread-per-connection). */
1213 if (chunk_size < left)
1214 {
1215 send_size = chunk_size;
1216 push_data = false; /* No need to push data, there is more to send. */
1217 }
1218 else
1219 {
1220 send_size = (size_t) left;
1221 push_data = true; /* Final piece of data, need to push to the network. */
1222 }
1223 pre_send_setopt (connection, false, push_data);
1224
1225#ifdef MHD_LINUX_SOLARIS_SENDFILE
1226#ifndef HAVE_SENDFILE64
1227 offset = (off_t) offsetu64;
1228 ret = sendfile (connection->socket_fd,
1229 file_fd,
1230 &offset,
1231 send_size);
1232#else /* HAVE_SENDFILE64 */
1233 offset = (off64_t) offsetu64;
1234 ret = sendfile64 (connection->socket_fd,
1235 file_fd,
1236 &offset,
1237 send_size);
1238#endif /* HAVE_SENDFILE64 */
1239 if (0 > ret)
1240 {
1241 const int err = MHD_socket_get_error_ ();
1242 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
1243 {
1244#ifdef EPOLL_SUPPORT
1245 /* EAGAIN --- no longer write-ready */
1246 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
1247#endif /* EPOLL_SUPPORT */
1248 return MHD_ERR_AGAIN_;
1249 }
1250 if (MHD_SCKT_ERR_IS_EINTR_ (err))
1251 return MHD_ERR_AGAIN_;
1252#ifdef HAVE_LINUX_SENDFILE
1253 if (MHD_SCKT_ERR_IS_ (err,
1255 return MHD_ERR_BADF_;
1256 /* sendfile() failed with EINVAL if mmap()-like operations are not
1257 supported for FD or other 'unusual' errors occurred, so we should try
1258 to fall back to 'SEND'; see also this thread for info on
1259 odd libc/Linux behavior with sendfile:
1260 http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
1261 connection->resp_sender = MHD_resp_sender_std;
1262 return MHD_ERR_AGAIN_;
1263#else /* HAVE_SOLARIS_SENDFILE */
1264 if ( (EAFNOSUPPORT == err) ||
1265 (EINVAL == err) ||
1266 (EOPNOTSUPP == err) )
1267 { /* Retry with standard file reader. */
1268 connection->resp_sender = MHD_resp_sender_std;
1269 return MHD_ERR_AGAIN_;
1270 }
1271 if ( (ENOTCONN == err) ||
1272 (EPIPE == err) )
1273 {
1274 return MHD_ERR_CONNRESET_;
1275 }
1276 return MHD_ERR_BADF_; /* Fail hard */
1277#endif /* HAVE_SOLARIS_SENDFILE */
1278 }
1279#ifdef EPOLL_SUPPORT
1280 else if (send_size > (size_t) ret)
1281 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
1282#endif /* EPOLL_SUPPORT */
1283#elif defined(HAVE_FREEBSD_SENDFILE)
1284#ifdef SF_FLAGS
1285 flags = used_thr_p_c ?
1286 freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
1287#endif /* SF_FLAGS */
1288 if (0 != sendfile (file_fd,
1289 connection->socket_fd,
1290 (off_t) offsetu64,
1291 send_size,
1292 NULL,
1293 &sent_bytes,
1294 flags))
1295 {
1296 const int err = MHD_socket_get_error_ ();
1297 if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ||
1298 MHD_SCKT_ERR_IS_EINTR_ (err) ||
1299 (EBUSY == err) )
1300 {
1301 mhd_assert (SSIZE_MAX >= sent_bytes);
1302 if (0 != sent_bytes)
1303 return (ssize_t) sent_bytes;
1304
1305 return MHD_ERR_AGAIN_;
1306 }
1307 /* Some unrecoverable error. Possibly file FD is not suitable
1308 * for sendfile(). Retry with standard send(). */
1309 connection->resp_sender = MHD_resp_sender_std;
1310 return MHD_ERR_AGAIN_;
1311 }
1312 mhd_assert (0 < sent_bytes);
1313 mhd_assert (SSIZE_MAX >= sent_bytes);
1314 ret = (ssize_t) sent_bytes;
1315#elif defined(HAVE_DARWIN_SENDFILE)
1316 len = (off_t) send_size; /* chunk always fit */
1317 if (0 != sendfile (file_fd,
1318 connection->socket_fd,
1319 (off_t) offsetu64,
1320 &len,
1321 NULL,
1322 0))
1323 {
1324 const int err = MHD_socket_get_error_ ();
1325 if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ||
1327 {
1328 mhd_assert (0 <= len);
1329 mhd_assert (SSIZE_MAX >= len);
1330 mhd_assert (send_size >= (size_t) len);
1331 if (0 != len)
1332 return (ssize_t) len;
1333
1334 return MHD_ERR_AGAIN_;
1335 }
1336 if ((ENOTCONN == err) ||
1337 (EPIPE == err) )
1338 return MHD_ERR_CONNRESET_;
1339 if ((ENOTSUP == err) ||
1340 (EOPNOTSUPP == err) )
1341 { /* This file FD is not suitable for sendfile().
1342 * Retry with standard send(). */
1343 connection->resp_sender = MHD_resp_sender_std;
1344 return MHD_ERR_AGAIN_;
1345 }
1346 return MHD_ERR_BADF_; /* Return hard error. */
1347 }
1348 mhd_assert (0 <= len);
1349 mhd_assert (SSIZE_MAX >= len);
1350 mhd_assert (send_size >= (size_t) len);
1351 ret = (ssize_t) len;
1352#endif /* HAVE_FREEBSD_SENDFILE */
1353
1354 /* If there is a need to push the data from network buffers
1355 * call post_send_setopt(). */
1356 /* It's unknown whether sendfile() will be used in the next
1357 * response so assume that next response will be the same. */
1358 if ( (push_data) &&
1359 (send_size == (size_t) ret) )
1360 post_send_setopt (connection, false, push_data);
1361
1362 return ret;
1363}
1364
1365
1366#endif /* _MHD_HAVE_SENDFILE */
1367
1368#if defined(MHD_VECT_SEND)
1369
1370
1384static ssize_t
1385send_iov_nontls (struct MHD_Connection *connection,
1386 struct MHD_iovec_track_ *const r_iov,
1387 bool push_data)
1388{
1389 ssize_t res;
1390 ssize_t total_sent;
1391 size_t items_to_send;
1392#ifdef HAVE_SENDMSG
1393 struct msghdr msg;
1394#elif defined(MHD_WINSOCK_SOCKETS)
1395 DWORD bytes_sent;
1396 DWORD cnt_w;
1397#endif /* MHD_WINSOCK_SOCKETS */
1398
1399 mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS));
1400
1401 if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
1402 (MHD_CONNECTION_CLOSED == connection->state) )
1403 {
1404 return MHD_ERR_NOTCONN_;
1405 }
1406
1407 items_to_send = r_iov->cnt - r_iov->sent;
1408#ifdef _MHD_IOV_MAX
1409 if (_MHD_IOV_MAX < items_to_send)
1410 {
1411 mhd_assert (0 < _MHD_IOV_MAX);
1412 if (0 == _MHD_IOV_MAX)
1413 return MHD_ERR_NOTCONN_; /* Should never happen */
1414 items_to_send = _MHD_IOV_MAX;
1415 push_data = false; /* Incomplete response */
1416 }
1417#endif /* _MHD_IOV_MAX */
1418#ifdef HAVE_SENDMSG
1419 memset (&msg, 0, sizeof(struct msghdr));
1420 msg.msg_iov = r_iov->iov + r_iov->sent;
1421 msg.msg_iovlen = items_to_send;
1422
1423 pre_send_setopt (connection, true, push_data);
1424#ifdef MHD_USE_MSG_MORE
1425 res = sendmsg (connection->socket_fd, &msg,
1426 MSG_NOSIGNAL_OR_ZERO | (push_data ? 0 : MSG_MORE));
1427#else /* ! MHD_USE_MSG_MORE */
1428 res = sendmsg (connection->socket_fd, &msg, MSG_NOSIGNAL_OR_ZERO);
1429#endif /* ! MHD_USE_MSG_MORE */
1430#elif defined(HAVE_WRITEV)
1431 pre_send_setopt (connection, true, push_data);
1432 res = writev (connection->socket_fd, r_iov->iov + r_iov->sent,
1433 items_to_send);
1434#elif defined(MHD_WINSOCK_SOCKETS)
1435#ifdef _WIN64
1436 if (items_to_send > UINT32_MAX)
1437 {
1438 cnt_w = UINT32_MAX;
1439 push_data = false; /* Incomplete response */
1440 }
1441 else
1442 cnt_w = (DWORD) items_to_send;
1443#else /* ! _WIN64 */
1444 cnt_w = (DWORD) items_to_send;
1445#endif /* ! _WIN64 */
1446 pre_send_setopt (connection, true, push_data);
1447 if (0 == WSASend (connection->socket_fd,
1448 (LPWSABUF) (r_iov->iov + r_iov->sent),
1449 cnt_w,
1450 &bytes_sent, 0, NULL, NULL))
1451 res = (ssize_t) bytes_sent;
1452 else
1453 res = -1;
1454#else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_WINSOCK_SOCKETS */
1455#error No vector-send function available
1456#endif
1457
1458 if (0 > res)
1459 {
1460 const int err = MHD_socket_get_error_ ();
1461
1462 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
1463 {
1464#ifdef EPOLL_SUPPORT
1465 /* EAGAIN --- no longer write-ready */
1466 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
1467#endif /* EPOLL_SUPPORT */
1468 return MHD_ERR_AGAIN_;
1469 }
1470 if (MHD_SCKT_ERR_IS_EINTR_ (err))
1471 return MHD_ERR_AGAIN_;
1473 return MHD_ERR_CONNRESET_;
1475 return MHD_ERR_PIPE_;
1477 return MHD_ERR_OPNOTSUPP_;
1479 return MHD_ERR_NOTCONN_;
1481 return MHD_ERR_INVAL_;
1483 return MHD_ERR_NOMEM_;
1485 return MHD_ERR_BADF_;
1486 /* Treat any other error as a hard error. */
1487 return MHD_ERR_NOTCONN_;
1488 }
1489
1490 /* Some data has been sent */
1491 total_sent = res;
1492 /* Adjust the internal tracking information for the iovec to
1493 * take this last send into account. */
1494 while ((0 != res) && (r_iov->iov[r_iov->sent].iov_len <= (size_t) res))
1495 {
1496 res -= r_iov->iov[r_iov->sent].iov_len;
1497 r_iov->sent++; /* The iov element has been completely sent */
1498 mhd_assert ((r_iov->cnt > r_iov->sent) || (0 == res));
1499 }
1500
1501 if (r_iov->cnt == r_iov->sent)
1502 post_send_setopt (connection, true, push_data);
1503 else
1504 {
1505#ifdef EPOLL_SUPPORT
1506 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
1507#endif /* EPOLL_SUPPORT */
1508 if (0 != res)
1509 {
1510 mhd_assert (r_iov->cnt > r_iov->sent);
1511 /* The last iov element has been partially sent */
1512 r_iov->iov[r_iov->sent].iov_base =
1513 (void*) ((uint8_t*) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
1514 r_iov->iov[r_iov->sent].iov_len -= (MHD_iov_size_) res;
1515 }
1516 }
1517
1518 return total_sent;
1519}
1520
1521
1522#endif /* MHD_VECT_SEND */
1523
1524#if ! defined(MHD_VECT_SEND) || defined(HTTPS_SUPPORT) || \
1525 defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
1526
1527
1542static ssize_t
1543send_iov_emu (struct MHD_Connection *connection,
1544 struct MHD_iovec_track_ *const r_iov,
1545 bool push_data)
1546{
1547 const bool non_blk = connection->sk_nonblck;
1548 size_t total_sent;
1549 ssize_t res;
1550
1551 mhd_assert (NULL != r_iov->iov);
1552 total_sent = 0;
1553 do
1554 {
1555 if ((size_t) SSIZE_MAX - total_sent < r_iov->iov[r_iov->sent].iov_len)
1556 return total_sent; /* return value would overflow */
1557
1558 res = MHD_send_data_ (connection,
1559 r_iov->iov[r_iov->sent].iov_base,
1560 r_iov->iov[r_iov->sent].iov_len,
1561 push_data && (r_iov->cnt == r_iov->sent + 1));
1562 if (0 > res)
1563 {
1564 /* Result is an error */
1565 if (0 == total_sent)
1566 return res; /* Nothing was sent, return result as is */
1567
1568 if (MHD_ERR_AGAIN_ == res)
1569 return total_sent; /* Some data has been sent, return the amount */
1570
1571 return res; /* Any kind of a hard error */
1572 }
1573
1574 total_sent += (size_t) res;
1575
1576 if (r_iov->iov[r_iov->sent].iov_len != (size_t) res)
1577 {
1578 /* Incomplete buffer has been sent.
1579 * Adjust buffer of the last element. */
1580 r_iov->iov[r_iov->sent].iov_base =
1581 (void*) ((uint8_t*) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
1582 r_iov->iov[r_iov->sent].iov_len -= res;
1583
1584 return total_sent;
1585 }
1586 /* The iov element has been completely sent */
1587 r_iov->sent++;
1588 } while ((r_iov->cnt > r_iov->sent) && (non_blk));
1589
1590 return (ssize_t) total_sent;
1591}
1592
1593
1594#endif /* !MHD_VECT_SEND || HTTPS_SUPPORT
1595 || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1596
1597
1598ssize_t
1600 struct MHD_iovec_track_ *const r_iov,
1601 bool push_data)
1602{
1603#ifdef MHD_VECT_SEND
1604#if defined(HTTPS_SUPPORT) || \
1605 defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
1606 bool use_iov_send = true;
1607#endif /* HTTPS_SUPPORT || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1608#endif /* MHD_VECT_SEND */
1609
1610 mhd_assert (NULL != connection->resp_iov.iov);
1611 mhd_assert (NULL != connection->response->data_iov);
1612 mhd_assert (connection->resp_iov.cnt > connection->resp_iov.sent);
1613#ifdef MHD_VECT_SEND
1614#if defined(HTTPS_SUPPORT) || \
1615 defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
1616#ifdef HTTPS_SUPPORT
1617 use_iov_send = use_iov_send &&
1618 (0 == (connection->daemon->options & MHD_USE_TLS));
1619#endif /* HTTPS_SUPPORT */
1620#ifdef _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
1621 use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked ||
1622 connection->sk_spipe_suppress);
1623#endif /* _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1624 if (use_iov_send)
1625#endif /* HTTPS_SUPPORT || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1626 return send_iov_nontls (connection, r_iov, push_data);
1627#endif /* MHD_VECT_SEND */
1628
1629#if ! defined(MHD_VECT_SEND) || defined(HTTPS_SUPPORT) || \
1630 defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
1631 return send_iov_emu (connection, r_iov, push_data);
1632#endif /* !MHD_VECT_SEND || HTTPS_SUPPORT
1633 || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
1634}
#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
#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:892
ssize_t MHD_send_iovec_(struct MHD_Connection *connection, struct MHD_iovec_track_ *const r_iov, bool push_data)
Definition: mhd_send.c:1599
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:1543
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:642
size_t MHD_iov_size_
Definition: internal.h:376
MHD_tristate
Definition: internal.h:174
@ _MHD_ON
Definition: internal.h:178
@ _MHD_UNKNOWN
Definition: internal.h:175
@ _MHD_YES
Definition: internal.h:179
@ _MHD_OFF
Definition: internal.h:176
macros for mhd_assert()
limits values definitions
#define SSIZE_MAX
Definition: mhd_limits.h:111
#define MHD_SCKT_ENOPROTOOPT_
Definition: mhd_sockets.h:578
#define MHD_send4_(s, b, l, f)
Definition: mhd_sockets.h:345
#define MSG_NOSIGNAL_OR_ZERO
Definition: mhd_sockets.h:184
#define MHD_SCKT_EPIPE_
Definition: mhd_sockets.h:563
int MHD_socket
Definition: microhttpd.h:193
int off_t offset
Definition: microhttpd.h:3270
#define MHD_INVALID_SOCKET
Definition: microhttpd.h:194
@ MHD_USE_THREAD_PER_CONNECTION
Definition: microhttpd.h:1087
@ MHD_USE_TLS
Definition: microhttpd.h:1072
MHD_socket socket_fd
Definition: internal.h:752
enum MHD_tristate sk_nodelay
Definition: internal.h:1025
enum MHD_tristate is_nonip
Definition: internal.h:1005
struct MHD_Response * response
Definition: internal.h:796
bool sk_nonblck
Definition: internal.h:784
uint64_t response_write_position
Definition: internal.h:940
struct MHD_iovec_track_ resp_iov
Definition: internal.h:948
enum MHD_CONNECTION_STATE state
Definition: internal.h:1064
struct MHD_Daemon * daemon
Definition: internal.h:675
bool sk_spipe_suppress
Definition: internal.h:1015
enum MHD_tristate sk_corked
Definition: internal.h:1020
enum MHD_FLAG options
Definition: internal.h:1411
bool sigpipe_blocked
Definition: internal.h:1794
const void * iov_base
Definition: microhttpd.h:2002
size_t iov_len
Definition: microhttpd.h:2007
MHD_iovec_ * data_iov
Definition: internal.h:513
uint64_t total_size
Definition: internal.h:1642
uint64_t fd_off
Definition: internal.h:1653
MHD_iovec_ * iov
Definition: internal.h:387