GNU libmicrohttpd  0.9.71
mhd_send.c
Go to the documentation of this file.
1 /*
2  This file is part of libmicrohttpd
3  Copyright (C) 2019 ng0 <ng0@n0.is>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19  */
20 
29 /* Worth considering for future improvements and additions:
30  * NetBSD has no sendfile or sendfile64. The way to work
31  * with this seems to be to mmap the file and write(2) as
32  * large a chunk as possible to the socket. Alternatively,
33  * use madvise(..., MADV_SEQUENTIAL). */
34 
35 /* Functions to be used in: send_param_adapter, MHD_send_
36  * and every place where sendfile(), sendfile64(), setsockopt()
37  * are used. */
38 
39 #include "mhd_send.h"
40 
47 static void
48 pre_cork_setsockopt (struct MHD_Connection *connection,
49  bool want_cork)
50 {
51 #if HAVE_MSG_MORE
52  /* We use the MSG_MORE option for corking, no need for extra syscalls! */
53 
54  (void) connection; /* Mute compiler warning. */
55  (void) want_cork; /* Mute compiler warning. */
56 
57 #elif defined(MHD_TCP_CORK_NOPUSH)
58  int ret;
59 
60  /* If sk_cork_on is already what we pass in, return. */
61  if (connection->sk_cork_on == want_cork)
62  {
63  /* nothing to do, success! */
64  return;
65  }
66  if (! want_cork)
67  return; /* nothing to do *pre* syscall! */
68  ret = MHD_socket_cork_ (connection->socket_fd,
69  true);
70  if (0 != ret)
71  {
72  connection->sk_cork_on = true;
73  return;
74  }
75  switch (errno)
76  {
77  case ENOTSOCK:
78  /* FIXME: Could be we are talking to a pipe, maybe remember this
79  and avoid all setsockopt() in the future? */
80  break;
81  case EBADF:
82  /* FIXME: should we die hard here? */
83  break;
84  case EINVAL:
85  /* FIXME: optlen invalid, should at least log this, maybe die */
86 #ifdef HAVE_MESSAGES
87  MHD_DLOG (connection->daemon,
88  _ ("optlen invalid: %s\n"),
90 #endif
91  break;
92  case EFAULT:
93  /* wopsie, should at least log this, FIXME: maybe die */
94 #ifdef HAVE_MESSAGES
95  MHD_DLOG (connection->daemon,
96  _ (
97  "The address pointed to by optval is not a valid part of the process address space: %s\n"),
99 #endif
100  break;
101  case ENOPROTOOPT:
102  /* optlen unknown, should at least log this */
103 #ifdef HAVE_MESSAGES
104  MHD_DLOG (connection->daemon,
105  _ ("The option is unknown: %s\n"),
107 #endif
108  break;
109  default:
110  /* any others? man page does not list more... */
111  break;
112  }
113 #else
114  /* CORK/NOPUSH/MSG_MORE do not exist on this platform,
115  so we must toggle Naggle's algorithm on/off instead
116  (otherwise we keep it always off) */
117  if (connection->sk_cork_on == want_cork)
118  {
119  /* nothing to do, success! */
120  return;
121  }
122  if ( (want_cork) &&
123  (0 == MHD_socket_set_nodelay_ (connection->socket_fd,
124  false)) )
125  connection->sk_cork_on = true;
126 #endif
127 }
128 
129 
136 static void
138  bool want_cork)
139 {
140 #if HAVE_MSG_MORE
141  /* We use the MSG_MORE option for corking, no need for extra syscalls! */
142 
143  (void) connection; /* Mute compiler warning. */
144  (void) want_cork; /* Mute compiler warning. */
145 
146 #elif defined(MHD_TCP_CORK_NOPUSH)
147  int ret;
148 
149  /* If sk_cork_on is already what we pass in, return. */
150  if (connection->sk_cork_on == want_cork)
151  {
152  /* nothing to do, success! */
153  return;
154  }
155  if (want_cork)
156  return; /* nothing to do *post* syscall (in fact, we should never
157  get here, as sk_cork_on should have succeeded in the
158  pre-syscall) */
159  ret = MHD_socket_cork_ (connection->socket_fd,
160  false);
161  if (0 != ret)
162  {
163  connection->sk_cork_on = false;
164  return;
165  }
166  switch (errno)
167  {
168  case ENOTSOCK:
169  /* FIXME: Could be we are talking to a pipe, maybe remember this
170  and avoid all setsockopt() in the future? */
171  break;
172  case EBADF:
173  /* FIXME: should we die hard here? */
174  break;
175  case EINVAL:
176  /* FIXME: optlen invalid, should at least log this, maybe die */
177 #ifdef HAVE_MESSAGES
178  MHD_DLOG (connection->daemon,
179  _ ("optlen invalid: %s\n"),
181 #endif
182  break;
183  case EFAULT:
184  /* wopsie, should at least log this, FIXME: maybe die */
185 #ifdef HAVE_MESSAGES
186  MHD_DLOG (connection->daemon,
187  _ (
188  "The address pointed to by optval is not a valid part of the process address space: %s\n"),
190 #endif
191  break;
192  case ENOPROTOOPT:
193  /* optlen unknown, should at least log this */
194 #ifdef HAVE_MESSAGES
195  MHD_DLOG (connection->daemon,
196  _ ("The option is unknown: %s\n"),
198 #endif
199  break;
200  default:
201  /* any others? man page does not list more... */
202  break;
203  }
204 #else
205  /* CORK/NOPUSH/MSG_MORE do not exist on this platform,
206  so we must toggle Naggle's algorithm on/off instead
207  (otherwise we keep it always off) */
208  if (connection->sk_cork_on == want_cork)
209  {
210  /* nothing to do, success! */
211  return;
212  }
213  if ( (! want_cork) &&
214  (0 == MHD_socket_set_nodelay_ (connection->socket_fd,
215  true)) )
216  connection->sk_cork_on = false;
217 #endif
218 }
219 
220 
240 ssize_t
242  const char *buffer,
243  size_t buffer_size,
244  enum MHD_SendSocketOptions options)
245 {
246  bool want_cork;
247  MHD_socket s = connection->socket_fd;
248  ssize_t ret;
249 
250  /* error handling from send_param_adapter() */
251  if ( (MHD_INVALID_SOCKET == s) ||
252  (MHD_CONNECTION_CLOSED == connection->state) )
253  {
254  return MHD_ERR_NOTCONN_;
255  }
256 
257  /* from send_param_adapter() */
258  if (buffer_size > MHD_SCKT_SEND_MAX_SIZE_)
259  buffer_size = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */
260 
261  /* Get socket options, change/set options if necessary. */
262  switch (options)
263  {
264  /* No corking */
265  case MHD_SSO_NO_CORK:
266  want_cork = false;
267  break;
268  /* Do corking, consider MSG_MORE instead if available. */
269  case MHD_SSO_MAY_CORK:
270  want_cork = true;
271  break;
272  /* Cork the header. */
273  case MHD_SSO_HDR_CORK:
274  want_cork = (buffer_size <= 1024);
275  break;
276  }
277 
278 #ifdef HTTPS_SUPPORT
279  if (0 != (connection->daemon->options & MHD_USE_TLS))
280  {
281  bool have_cork = connection->sk_cork_on;
282 
283  if (want_cork && ! have_cork)
284  {
285  gnutls_record_cork (connection->tls_session);
286  connection->sk_cork_on = true;
287  }
288  if (buffer_size > SSIZE_MAX)
289  buffer_size = SSIZE_MAX;
290  ret = gnutls_record_send (connection->tls_session,
291  buffer,
292  buffer_size);
293  if ( (GNUTLS_E_AGAIN == ret) ||
294  (GNUTLS_E_INTERRUPTED == ret) )
295  {
296 #ifdef EPOLL_SUPPORT
297  if (GNUTLS_E_AGAIN == ret)
298  connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
299 #endif
300  return MHD_ERR_AGAIN_;
301  }
302  if (ret < 0)
303  {
304  /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
305  disrupted); interpret as a hard error */
306  return MHD_ERR_NOTCONN_;
307  }
308 #ifdef EPOLL_SUPPORT
309  /* Unlike non-TLS connections, do not reset "write-ready" if
310  * sent amount smaller than provided amount, as TLS
311  * connections may break data into smaller parts for sending. */
312 #endif /* EPOLL_SUPPORT */
313 
314  if (! want_cork && have_cork)
315  {
316  int err = gnutls_record_uncork (connection->tls_session, 0);
317 
318  if (0 > err)
319  return MHD_ERR_AGAIN_;
320  connection->sk_cork_on = false;
321  }
322  }
323  else
324 #endif /* HTTPS_SUPPORT */
325  {
326  /* plaintext transmission */
327  pre_cork_setsockopt (connection, want_cork);
328 #if HAVE_MSG_MORE
329  ret = send (s,
330  buffer,
331  buffer_size,
332  MAYBE_MSG_NOSIGNAL | (want_cork ? MSG_MORE : 0));
333 #else
334  ret = send (connection->socket_fd,
335  buffer,
336  buffer_size,
338 #endif
339 
340  if (0 > ret)
341  {
342  const int err = MHD_socket_get_error_ ();
343 
344  if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
345  {
346 #if EPOLL_SUPPORT
347  /* EAGAIN, no longer write-ready */
348  connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
349 #endif /* EPOLL_SUPPORT */
350  return MHD_ERR_AGAIN_;
351  }
352  if (MHD_SCKT_ERR_IS_EINTR_ (err))
353  return MHD_ERR_AGAIN_;
355  return MHD_ERR_CONNRESET_;
356  /* Treat any other error as hard error. */
357  return MHD_ERR_NOTCONN_;
358  }
359 #if EPOLL_SUPPORT
360  else if (buffer_size > (size_t) ret)
361  connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
362 #endif /* EPOLL_SUPPORT */
363  if (buffer_size == (size_t) ret)
364  post_cork_setsockopt (connection, want_cork);
365  }
366 
367  return ret;
368 }
369 
370 
388 ssize_t
390  const char *header,
391  size_t header_size,
392  const char *buffer,
393  size_t buffer_size)
394 {
395 #ifdef HTTPS_SUPPORT
396  if (0 != (connection->daemon->options & MHD_USE_TLS))
397  {
398  ssize_t ret;
399 
400  ret = MHD_send_on_connection_ (connection,
401  header,
402  header_size,
404  if ( (header_size == (size_t) ret) &&
405  (0 == buffer_size) &&
406  connection->sk_cork_on)
407  {
408  (void) gnutls_record_uncork (connection->tls_session, 0);
409  connection->sk_cork_on = false;
410  }
411  return ret;
412  }
413 #endif
414 #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
415  MHD_socket s = connection->socket_fd;
416  ssize_t ret;
417  struct iovec vector[2];
418 
419  /* Since we generally give the fully answer, we do not want
420  corking to happen */
421  pre_cork_setsockopt (connection, false);
422 
423  vector[0].iov_base = (void *) header;
424  vector[0].iov_len = header_size;
425  vector[1].iov_base = (void *) buffer;
426  vector[1].iov_len = buffer_size;
427 
428 #if HAVE_SENDMSG
429  {
430  struct msghdr msg;
431 
432  memset (&msg, 0, sizeof(struct msghdr));
433  msg.msg_iov = vector;
434  msg.msg_iovlen = 2;
435 
436  ret = sendmsg (s, &msg, MAYBE_MSG_NOSIGNAL);
437  }
438 #elif HAVE_WRITEV
439  {
440  int iovcnt;
441 
442  iovcnt = sizeof (vector) / sizeof (struct iovec);
443  ret = writev (s, vector, iovcnt);
444  }
445 #endif
446 
447  /* Only if we succeeded sending the full buffer, we need to make sure that
448  the OS flushes at the end */
449  if (header_size + buffer_size == (size_t) ret)
450  post_cork_setsockopt (connection, false);
451 
452  return ret;
453 
454 #else
455  return MHD_send_on_connection_ (connection,
456  header,
457  header_size,
459 #endif
460 }
461 
462 
466 #define MHD_SENFILE_CHUNK_ (0x20000)
467 
471 #define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
472 
473 #ifdef HAVE_FREEBSD_SENDFILE
474 #ifdef SF_FLAGS
475 
478 static int freebsd_sendfile_flags_;
479 
483 static int freebsd_sendfile_flags_thd_p_c_;
484 #endif /* SF_FLAGS */
485 
486 #endif /* HAVE_FREEBSD_SENDFILE */
487 
488 #if defined(_MHD_HAVE_SENDFILE)
489 
495 ssize_t
496 MHD_send_sendfile_ (struct MHD_Connection *connection)
497 {
498  ssize_t ret;
499  const int file_fd = connection->response->fd;
500  uint64_t left;
501  uint64_t offsetu64;
502 #ifndef HAVE_SENDFILE64
503  const uint64_t max_off_t = (uint64_t) OFF_T_MAX;
504 #else /* HAVE_SENDFILE64 */
505  const uint64_t max_off_t = (uint64_t) OFF64_T_MAX;
506 #endif /* HAVE_SENDFILE64 */
507 #ifdef MHD_LINUX_SOLARIS_SENDFILE
508 #ifndef HAVE_SENDFILE64
509  off_t offset;
510 #else /* HAVE_SENDFILE64 */
511  off64_t offset;
512 #endif /* HAVE_SENDFILE64 */
513 #endif /* MHD_LINUX_SOLARIS_SENDFILE */
514 #ifdef HAVE_FREEBSD_SENDFILE
515  off_t sent_bytes;
516  int flags = 0;
517 #endif
518 #ifdef HAVE_DARWIN_SENDFILE
519  off_t len;
520 #endif /* HAVE_DARWIN_SENDFILE */
521  const bool used_thr_p_c = (0 != (connection->daemon->options
523  const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ :
525  size_t send_size = 0;
526  mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
527 
528  pre_cork_setsockopt (connection, false);
529 
530  offsetu64 = connection->response_write_position
531  + connection->response->fd_off;
532  left = connection->response->total_size - connection->response_write_position;
533  /* Do not allow system to stick sending on single fast connection:
534  * use 128KiB chunks (2MiB for thread-per-connection). */
535  send_size = (left > chunk_size) ? chunk_size : (size_t) left;
536  if (max_off_t < offsetu64)
537  { /* Retry to send with standard 'send()'. */
538  connection->resp_sender = MHD_resp_sender_std;
539  return MHD_ERR_AGAIN_;
540  }
541 #ifdef MHD_LINUX_SOLARIS_SENDFILE
542 #ifndef HAVE_SENDFILE64
543  offset = (off_t) offsetu64;
544  ret = sendfile (connection->socket_fd,
545  file_fd,
546  &offset,
547  send_size);
548 #else /* HAVE_SENDFILE64 */
549  offset = (off64_t) offsetu64;
550  ret = sendfile64 (connection->socket_fd,
551  file_fd,
552  &offset,
553  send_size);
554 #endif /* HAVE_SENDFILE64 */
555  if (0 > ret)
556  {
557  const int err = MHD_socket_get_error_ ();
558  if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
559  {
560 #ifdef EPOLL_SUPPORT
561  /* EAGAIN --- no longer write-ready */
562  connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
563 #endif /* EPOLL_SUPPORT */
564  return MHD_ERR_AGAIN_;
565  }
566  if (MHD_SCKT_ERR_IS_EINTR_ (err))
567  return MHD_ERR_AGAIN_;
568 #ifdef HAVE_LINUX_SENDFILE
569  if (MHD_SCKT_ERR_IS_ (err,
571  return MHD_ERR_BADF_;
572  /* sendfile() failed with EINVAL if mmap()-like operations are not
573  supported for FD or other 'unusual' errors occurred, so we should try
574  to fall back to 'SEND'; see also this thread for info on
575  odd libc/Linux behavior with sendfile:
576  http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */connection->resp_sender = MHD_resp_sender_std;
577  return MHD_ERR_AGAIN_;
578 #else /* HAVE_SOLARIS_SENDFILE */
579  if ( (EAFNOSUPPORT == err) ||
580  (EINVAL == err) ||
581  (EOPNOTSUPP == err) )
582  { /* Retry with standard file reader. */
583  connection->resp_sender = MHD_resp_sender_std;
584  return MHD_ERR_AGAIN_;
585  }
586  if ( (ENOTCONN == err) ||
587  (EPIPE == err) )
588  {
589  return MHD_ERR_CONNRESET_;
590  }
591  return MHD_ERR_BADF_; /* Fail hard */
592 #endif /* HAVE_SOLARIS_SENDFILE */
593  }
594 #ifdef EPOLL_SUPPORT
595  else if (send_size > (size_t) ret)
596  connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
597 #endif /* EPOLL_SUPPORT */
598 #elif defined(HAVE_FREEBSD_SENDFILE)
599 #ifdef SF_FLAGS
600  flags = used_thr_p_c ?
601  freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
602 #endif /* SF_FLAGS */
603  if (0 != sendfile (file_fd,
604  connection->socket_fd,
605  (off_t) offsetu64,
606  send_size,
607  NULL,
608  &sent_bytes,
609  flags))
610  {
611  const int err = MHD_socket_get_error_ ();
612  if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ||
613  MHD_SCKT_ERR_IS_EINTR_ (err) ||
614  (EBUSY == err) )
615  {
616  mhd_assert (SSIZE_MAX >= sent_bytes);
617  if (0 != sent_bytes)
618  return (ssize_t) sent_bytes;
619 
620  return MHD_ERR_AGAIN_;
621  }
622  /* Some unrecoverable error. Possibly file FD is not suitable
623  * for sendfile(). Retry with standard send(). */
624  connection->resp_sender = MHD_resp_sender_std;
625  return MHD_ERR_AGAIN_;
626  }
627  mhd_assert (0 < sent_bytes);
628  mhd_assert (SSIZE_MAX >= sent_bytes);
629  ret = (ssize_t) sent_bytes;
630 #elif defined(HAVE_DARWIN_SENDFILE)
631  len = (off_t) send_size; /* chunk always fit */
632  if (0 != sendfile (file_fd,
633  connection->socket_fd,
634  (off_t) offsetu64,
635  &len,
636  NULL,
637  0))
638  {
639  const int err = MHD_socket_get_error_ ();
640  if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ||
642  {
643  mhd_assert (0 <= len);
644  mhd_assert (SSIZE_MAX >= len);
645  mhd_assert (send_size >= (size_t) len);
646  if (0 != len)
647  return (ssize_t) len;
648 
649  return MHD_ERR_AGAIN_;
650  }
651  if ((ENOTCONN == err) ||
652  (EPIPE == err) )
653  return MHD_ERR_CONNRESET_;
654  if ((ENOTSUP == err) ||
655  (EOPNOTSUPP == err) )
656  { /* This file FD is not suitable for sendfile().
657  * Retry with standard send(). */
658  connection->resp_sender = MHD_resp_sender_std;
659  return MHD_ERR_AGAIN_;
660  }
661  return MHD_ERR_BADF_; /* Return hard error. */
662  }
663  mhd_assert (0 <= len);
664  mhd_assert (SSIZE_MAX >= len);
665  mhd_assert (send_size >= (size_t) len);
666  ret = (ssize_t) len;
667 #endif /* HAVE_FREEBSD_SENDFILE */
668 
669  /* Make sure we send the data without delay ONLY if we
670  provided the complete response (not on partial write) */
671  if (left == (uint64_t) ret)
672  post_cork_setsockopt (connection, false);
673 
674  return ret;
675 }
676 
677 
678 #endif /* _MHD_HAVE_SENDFILE */
#define SSIZE_MAX
Definition: mhd_limits.h:111
uint64_t total_size
Definition: internal.h:1642
uint64_t fd_off
Definition: internal.h:1653
static void pre_cork_setsockopt(struct MHD_Connection *connection, bool want_cork)
Definition: mhd_send.c:48
enum MHD_CONNECTION_STATE state
Definition: internal.h:924
uint64_t response_write_position
Definition: internal.h:824
#define OFF_T_MAX
Definition: mhd_limits.h:123
#define MHD_socket_get_error_()
Definition: mhd_sockets.h:523
struct MHD_Response * response
Definition: internal.h:680
#define MHD_SENFILE_CHUNK_
Definition: mhd_send.c:466
#define MHD_ERR_AGAIN_
Definition: internal.h:1863
int MHD_socket
Definition: microhttpd.h:195
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
Definition: mhd_sockets.h:643
#define MAYBE_MSG_NOSIGNAL
Definition: mhd_sockets.h:169
int MHD_socket_cork_(MHD_socket sock, bool on)
Definition: mhd_sockets.c:500
#define MHD_ERR_CONNRESET_
Definition: internal.h:1868
ssize_t MHD_send_on_connection2_(struct MHD_Connection *connection, const char *header, size_t header_size, const char *buffer, size_t buffer_size)
Definition: mhd_send.c:389
struct MHD_Daemon * daemon
Definition: internal.h:675
#define MHD_socket_last_strerr_()
Definition: mhd_sockets.h:549
#define MHD_SCKT_EBADF_
Definition: mhd_sockets.h:454
#define MHD_INVALID_SOCKET
Definition: microhttpd.h:196
MHD_socket socket_fd
Definition: internal.h:752
#define MHD_SCKT_SEND_MAX_SIZE_
Definition: mhd_sockets.h:222
enum MHD_FLAG options
Definition: internal.h:1600
#define MHD_SCKT_ERR_IS_(err, code)
Definition: mhd_sockets.h:611
ssize_t MHD_send_on_connection_(struct MHD_Connection *connection, const char *buffer, size_t buffer_size, enum MHD_SendSocketOptions options)
Definition: mhd_send.c:241
#define NULL
Definition: reason_phrase.c:30
bool sk_cork_on
Definition: internal.h:885
Implementation of send() wrappers.
int off_t offset
Definition: microhttpd.h:3166
MHD_SendSocketOptions
Definition: mhd_send.h:59
#define MHD_ERR_BADF_
Definition: internal.h:1884
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
int MHD_socket_set_nodelay_(MHD_socket sock, bool on)
Definition: mhd_sockets.c:471
#define MHD_ERR_NOTCONN_
Definition: internal.h:1874
#define _(String)
Definition: mhd_options.h:42
#define MHD_SCKT_ECONNRESET_
Definition: mhd_sockets.h:419
#define MHD_SCKT_ERR_IS_EINTR_(err)
Definition: mhd_sockets.h:634
#define MHD_SENFILE_CHUNK_THR_P_C_
Definition: mhd_send.c:471
static void post_cork_setsockopt(struct MHD_Connection *connection, bool want_cork)
Definition: mhd_send.c:137