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