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
47static void
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
136static 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
240ssize_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
388ssize_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
478static int freebsd_sendfile_flags_;
479
483static int freebsd_sendfile_flags_thd_p_c_;
484#endif /* SF_FLAGS */
485
486#endif /* HAVE_FREEBSD_SENDFILE */
487
488#if defined(_MHD_HAVE_SENDFILE)
495ssize_t
496MHD_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) ||
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 MHD_ERR_CONNRESET_
Definition: internal.h:1868
#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 MHD_SCKT_ERR_IS_(err, code)
Definition: mhd_sockets.h:611
#define MAYBE_MSG_NOSIGNAL
Definition: mhd_sockets.h:169
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
Definition: mhd_sockets.h:643
#define MHD_socket_last_strerr_()
Definition: mhd_sockets.h:549
#define MHD_SCKT_EBADF_
Definition: mhd_sockets.h:454
#define MHD_socket_get_error_()
Definition: mhd_sockets.h:523
#define MHD_SCKT_ERR_IS_EINTR_(err)
Definition: mhd_sockets.h:634
#define MHD_SCKT_SEND_MAX_SIZE_
Definition: mhd_sockets.h:222
#define MHD_SCKT_ECONNRESET_
Definition: mhd_sockets.h:419
#define NULL
Definition: reason_phrase.c:30
#define _(String)
Definition: mhd_options.h:42
#define MHD_SENFILE_CHUNK_THR_P_C_
Definition: mhd_send.c:471
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
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
static void pre_cork_setsockopt(struct MHD_Connection *connection, bool want_cork)
Definition: mhd_send.c:48
static void post_cork_setsockopt(struct MHD_Connection *connection, bool want_cork)
Definition: mhd_send.c:137
#define MHD_SENFILE_CHUNK_
Definition: mhd_send.c:466
Implementation of send() wrappers.
MHD_SendSocketOptions
Definition: mhd_send.h:60
@ MHD_SSO_HDR_CORK
Definition: mhd_send.h:77
@ MHD_SSO_NO_CORK
Definition: mhd_send.h:64
@ MHD_SSO_MAY_CORK
Definition: mhd_send.h:68
@ MHD_CONNECTION_CLOSED
Definition: internal.h:526
#define SSIZE_MAX
Definition: mhd_limits.h:111
int MHD_socket_cork_(MHD_socket sock, bool on)
Definition: mhd_sockets.c:500
int MHD_socket_set_nodelay_(MHD_socket sock, bool on)
Definition: mhd_sockets.c:471
int MHD_socket
Definition: microhttpd.h:195
int off_t offset
Definition: microhttpd.h:3167
#define MHD_INVALID_SOCKET
Definition: microhttpd.h:196
@ MHD_USE_THREAD_PER_CONNECTION
Definition: microhttpd.h:1075
@ MHD_USE_TLS
Definition: microhttpd.h:1060
MHD_socket socket_fd
Definition: internal.h:752
bool sk_cork_on
Definition: internal.h:885
struct MHD_Response * response
Definition: internal.h:680
uint64_t response_write_position
Definition: internal.h:824
enum MHD_CONNECTION_STATE state
Definition: internal.h:924
struct MHD_Daemon * daemon
Definition: internal.h:675
enum MHD_FLAG options
Definition: internal.h:1600
uint64_t total_size
Definition: internal.h:1642
uint64_t fd_off
Definition: internal.h:1653