GNU libmicrohttpd  0.9.73
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
78 
81 static int freebsd_sendfile_flags_;
82 
86 static int freebsd_sendfile_flags_thd_p_c_;
87 
88 
92 static void
93 freebsd_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)
118 
121 static unsigned long mhd_iov_max_ = 0;
122 
123 static void
124 iov_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 
152 void
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 
169 bool
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 
239 bool
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 
316 static void
317 pre_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
533 
544 static bool
545 zero_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 
577 static void
578 post_send_setopt (struct MHD_Connection *connection,
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 
748 ssize_t
749 MHD_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 
891 ssize_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_;
1100  if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_))
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_;
1110  if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EBADF_))
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)
1163 ssize_t
1164 MHD_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,
1254  MHD_SCKT_EBADF_))
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) ||
1326  MHD_SCKT_ERR_IS_EINTR_ (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 
1384 static ssize_t
1385 send_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_;
1474  if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_))
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_;
1484  if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EBADF_))
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 
1542 static ssize_t
1543 send_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 
1598 ssize_t
1599 MHD_send_iovec_ (struct MHD_Connection *connection,
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 }
MHD_socket
int MHD_socket
Definition: microhttpd.h:193
MHD_SCKT_ERR_IS_REMOTE_DISCNN_
#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err)
Definition: mhd_sockets.h:688
MHD_SENFILE_CHUNK_
#define MHD_SENFILE_CHUNK_
Definition: mhd_send.c:69
MHD_Connection::sk_corked
enum MHD_tristate sk_corked
Definition: internal.h:1020
_
#define _(String)
Definition: mhd_options.h:42
MHD_Daemon::options
enum MHD_FLAG options
Definition: internal.h:1411
MHD_iovec_track_
Definition: internal.h:381
MHD_ERR_PIPE_
#define MHD_ERR_PIPE_
Definition: connection.h:72
mhd_limits.h
limits values definitions
_MHD_ON
@ _MHD_ON
Definition: internal.h:178
_MHD_SEND_VEC_MAX
#define _MHD_SEND_VEC_MAX
MHD_EPOLL_STATE_WRITE_READY
@ MHD_EPOLL_STATE_WRITE_READY
Definition: internal.h:606
MHD_Connection::resp_iov
struct MHD_iovec_track_ resp_iov
Definition: internal.h:948
MHD_ERR_INVAL_
#define MHD_ERR_INVAL_
Definition: internal.h:1889
MHD_SCKT_OPT_BOOL_
int MHD_SCKT_OPT_BOOL_
Definition: mhd_sockets.h:203
MHD_Response::fd_off
uint64_t fd_off
Definition: internal.h:1653
MHD_Connection::sk_nonblck
bool sk_nonblck
Definition: internal.h:784
MHD_SCKT_EOPNOTSUPP_
#define MHD_SCKT_EOPNOTSUPP_
Definition: mhd_sockets.h:484
MHD_ERR_AGAIN_
#define MHD_ERR_AGAIN_
Definition: internal.h:1863
MHD_SCKT_ENOTSOCK_
#define MHD_SCKT_ENOTSOCK_
Definition: mhd_sockets.h:459
MHD_CONNECTION_CLOSED
@ MHD_CONNECTION_CLOSED
Definition: internal.h:642
MHD_SCKT_ENOPROTOOPT_
#define MHD_SCKT_ENOPROTOOPT_
Definition: mhd_sockets.h:578
OFF_T_MAX
#define OFF_T_MAX
Definition: mhd_limits.h:123
MHD_Connection::is_nonip
enum MHD_tristate is_nonip
Definition: internal.h:1005
MHD_send_data_
ssize_t MHD_send_data_(struct MHD_Connection *connection, const char *buffer, size_t buffer_size, bool push_data)
Definition: mhd_send.c:749
MHD_tristate
MHD_tristate
Definition: internal.h:174
MHD_Connection::sk_nodelay
enum MHD_tristate sk_nodelay
Definition: internal.h:1025
MHD_IoVec::iov_base
const void * iov_base
Definition: microhttpd.h:2002
MHD_connection_set_nodelay_state_
bool MHD_connection_set_nodelay_state_(struct MHD_Connection *connection, bool nodelay_state)
Definition: mhd_send.c:170
zero_send_
static bool zero_send_(struct MHD_Connection *connection)
Definition: mhd_send.c:545
MHD_INVALID_SOCKET
#define MHD_INVALID_SOCKET
Definition: microhttpd.h:194
MHD_ERR_TLS_
#define MHD_ERR_TLS_
Definition: connection.h:77
MHD_iov_size_
size_t MHD_iov_size_
Definition: internal.h:376
MHD_Response::total_size
uint64_t total_size
Definition: internal.h:1642
_MHD_YES
@ _MHD_YES
Definition: internal.h:179
MHD_Daemon::sigpipe_blocked
bool sigpipe_blocked
Definition: internal.h:1794
MHD_Response::data_iov
MHD_iovec_ * data_iov
Definition: internal.h:513
MHD_SCKT_EPIPE_
#define MHD_SCKT_EPIPE_
Definition: mhd_sockets.h:563
MHD_IoVec::iov_len
size_t iov_len
Definition: microhttpd.h:2007
NULL
#define NULL
Definition: reason_phrase.c:30
MHD_socket_get_error_
#define MHD_socket_get_error_()
Definition: mhd_sockets.h:523
mhd_assert.h
macros for mhd_assert()
MHD_SCKT_ERR_IS_LOW_RESOURCES_
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
Definition: mhd_sockets.h:656
MHD_SCKT_ERR_IS_
#define MHD_SCKT_ERR_IS_(err, code)
Definition: mhd_sockets.h:611
MHD_Response::fd
int fd
Definition: internal.h:1680
MHD_ERR_CONNRESET_
#define MHD_ERR_CONNRESET_
Definition: internal.h:1868
MHD_ERR_NOMEM_
#define MHD_ERR_NOMEM_
Definition: internal.h:1879
send_iov_emu
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
MHD_ERR_OPNOTSUPP_
#define MHD_ERR_OPNOTSUPP_
Definition: connection.h:67
MHD_SENFILE_CHUNK_THR_P_C_
#define MHD_SENFILE_CHUNK_THR_P_C_
Definition: mhd_send.c:74
MHD_connection_set_cork_state_
bool MHD_connection_set_cork_state_(struct MHD_Connection *connection, bool cork_state)
Definition: mhd_send.c:240
UINT_MAX
#define UINT_MAX
Definition: mhd_limits.h:45
MHD_iovec_track_::iov
MHD_iovec_ * iov
Definition: internal.h:387
MHD_Connection::state
enum MHD_CONNECTION_STATE state
Definition: internal.h:1064
MHD_USE_TLS
@ MHD_USE_TLS
Definition: microhttpd.h:1072
MHD_socket_last_strerr_
#define MHD_socket_last_strerr_()
Definition: mhd_sockets.h:549
post_send_setopt
static void post_send_setopt(struct MHD_Connection *connection, bool plain_send_next, bool push_data)
Definition: mhd_send.c:578
MHD_SCKT_ENOTCONN_
#define MHD_SCKT_ENOTCONN_
Definition: mhd_sockets.h:429
_MHD_UNKNOWN
@ _MHD_UNKNOWN
Definition: internal.h:175
MHD_send_hdr_and_body_
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
MHD_send_init_static_vars_
void MHD_send_init_static_vars_(void)
Definition: mhd_send.c:153
MHD_Connection::response_write_position
uint64_t response_write_position
Definition: internal.h:940
mhd_assert
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
MHD_send4_
#define MHD_send4_(s, b, l, f)
Definition: mhd_sockets.h:345
SSIZE_MAX
#define SSIZE_MAX
Definition: mhd_limits.h:111
MHD_Connection::response
struct MHD_Response * response
Definition: internal.h:796
pre_send_setopt
static void pre_send_setopt(struct MHD_Connection *connection, bool plain_send, bool push_data)
Definition: mhd_send.c:317
MHD_SCKT_ERR_IS_EAGAIN_
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
Definition: mhd_sockets.h:643
MHD_Connection::daemon
struct MHD_Daemon * daemon
Definition: internal.h:675
_MHD_OFF
@ _MHD_OFF
Definition: internal.h:176
MHD_send_iovec_
ssize_t MHD_send_iovec_(struct MHD_Connection *connection, struct MHD_iovec_track_ *const r_iov, bool push_data)
Definition: mhd_send.c:1599
MHD_ERR_BADF_
#define MHD_ERR_BADF_
Definition: internal.h:1884
MHD_SCKT_EINVAL_
#define MHD_SCKT_EINVAL_
Definition: mhd_sockets.h:464
MHD_USE_THREAD_PER_CONNECTION
@ MHD_USE_THREAD_PER_CONNECTION
Definition: microhttpd.h:1087
MHD_iovec_track_::sent
size_t sent
Definition: internal.h:400
MHD_Connection
Definition: internal.h:634
MHD_ERR_NOTCONN_
#define MHD_ERR_NOTCONN_
Definition: internal.h:1874
MHD_iovec_track_::cnt
size_t cnt
Definition: internal.h:393
MHD_send_
#define MHD_send_(s, b, l)
Definition: mhd_sockets.h:261
offset
int off_t offset
Definition: microhttpd.h:3270
MHD_socket_strerr_
#define MHD_socket_strerr_(err)
Definition: mhd_sockets.h:542
mhd_send.h
Declarations of send() wrappers.
MHD_SCKT_EBADF_
#define MHD_SCKT_EBADF_
Definition: mhd_sockets.h:454
UINT32_MAX
#define UINT32_MAX
Definition: mhd_limits.h:73
MHD_Connection::sk_spipe_suppress
bool sk_spipe_suppress
Definition: internal.h:1015
MSG_NOSIGNAL_OR_ZERO
#define MSG_NOSIGNAL_OR_ZERO
Definition: mhd_sockets.h:184
MHD_SCKT_ERR_IS_EINTR_
#define MHD_SCKT_ERR_IS_EINTR_(err)
Definition: mhd_sockets.h:634
MHD_SCKT_SEND_MAX_SIZE_
#define MHD_SCKT_SEND_MAX_SIZE_
Definition: mhd_sockets.h:222
MHD_Connection::socket_fd
MHD_socket socket_fd
Definition: internal.h:752