aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd
diff options
context:
space:
mode:
authorng0 <ng0@n0.is>2019-07-24 10:17:45 +0000
committerng0 <ng0@n0.is>2019-07-24 10:17:45 +0000
commit0c40dc481b3ed4149c9bb82118d5c923fb34dcde (patch)
tree264820d60451da98da40cb54d724103c25d3b268 /src/microhttpd
parent0b9d7d3bf3a26a512efe404c7ac0044bdd486932 (diff)
downloadlibmicrohttpd-0c40dc481b3ed4149c9bb82118d5c923fb34dcde.tar.gz
libmicrohttpd-0c40dc481b3ed4149c9bb82118d5c923fb34dcde.zip
move sendfile function work to connection.c
Diffstat (limited to 'src/microhttpd')
-rw-r--r--src/microhttpd/connection.c72
-rw-r--r--src/microhttpd/mhd_send.c262
-rw-r--r--src/microhttpd/mhd_send.h9
3 files changed, 82 insertions, 261 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 626adb42..3315ad22 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -288,6 +288,12 @@ send_param_adapter (struct MHD_Connection *connection,
288static ssize_t 288static ssize_t
289sendfile_adapter (struct MHD_Connection *connection) 289sendfile_adapter (struct MHD_Connection *connection)
290{ 290{
291 bool want_cork = false;
292 bool have_cork;
293 bool have_more;
294 bool use_corknopush;
295 bool using_tls = false;
296
291 ssize_t ret; 297 ssize_t ret;
292 const int file_fd = connection->response->fd; 298 const int file_fd = connection->response->fd;
293 uint64_t left; 299 uint64_t left;
@@ -454,6 +460,72 @@ sendfile_adapter (struct MHD_Connection *connection)
454 mhd_assert (send_size >= (size_t)len); 460 mhd_assert (send_size >= (size_t)len);
455 ret = (ssize_t)len; 461 ret = (ssize_t)len;
456#endif /* HAVE_FREEBSD_SENDFILE */ 462#endif /* HAVE_FREEBSD_SENDFILE */
463
464 /* ! could be avoided by redefining the variable. */
465 have_cork = ! connection->sk_tcp_nodelay_on;
466
467#ifdef MSG_MORE
468 have_more = true;
469#else
470 have_more = false;
471#endif
472
473#if TCP_NODELAY
474 use_corknopush = false;
475#elif TCP_CORK
476 use_corknopush = true;
477#elif TCP_NOPUSH
478 use_corknopush = true;
479#endif
480
481#ifdef HTTPS_SUPPORT
482 using_tls = (0 != (connection->daemon->options & MHD_USE_TLS));
483#endif
484
485#if TCP_CORK
486 /* When we have CORK, we can have NODELAY on the same system,
487 * at least since Linux 2.2 and both can be combined since
488 * Linux 2.5.71. For more details refer to tcp(7) on Linux.
489 * No other system in 2019-06 has TCP_CORK. */
490 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
491 {
492 if (0 == setsockopt (connection->socket_fd,
493 IPPROTO_TCP,
494 TCP_CORK,
495 (const void *) &off_val,
496 sizeof (off_val)))
497 {
498 connection->sk_tcp_nodelay_on = true;
499 }
500 else if (0 == setsockopt (connection->socket_fd,
501 IPPROTO_TCP,
502 TCP_NODELAY,
503 (const void *) &on_val,
504 sizeof (on_val)))
505 {
506 connection->sk_tcp_nodelay_on = true;
507 }
508 }
509#elif TCP_NOPUSH
510 /* TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the
511 * exception that we know that TCP_NOPUSH will definitely
512 * exist and we can disregard TCP_NODELAY unless requested. */
513 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
514 {
515 MHD_send_socket_state_nopush_ (connection, true, false);
516 /*
517 if (0 == setsockopt (connection->socket_fd,
518 IPPROTO_TCP,
519 TCP_NOPUSH,
520 (const void *) &on_val,
521 sizeof (on_val)))
522 {
523 connection->sk_tcp_nodelay_on = false;
524 }
525 */
526 }
527#endif
528
457 return ret; 529 return ret;
458} 530}
459#endif /* _MHD_HAVE_SENDFILE */ 531#endif /* _MHD_HAVE_SENDFILE */
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c
index 7e312b59..1d3b739a 100644
--- a/src/microhttpd/mhd_send.c
+++ b/src/microhttpd/mhd_send.c
@@ -24,14 +24,13 @@
24 * @author ng0 <ng0@n0.is> 24 * @author ng0 <ng0@n0.is>
25 */ 25 */
26 26
27/* TODO: sendfile() wrappers. */ 27/* TODO: sendfile() wrapper, in connection.c */
28 28
29/* Functions to be used in: send_param_adapter, MHD_send_ 29/* Functions to be used in: send_param_adapter, MHD_send_
30 * and every place where sendfile(), sendfile64(), setsockopt() 30 * and every place where sendfile(), sendfile64(), setsockopt()
31 * are used. */ 31 * are used. */
32 32
33#include "mhd_send.h" 33#include "mhd_send.h"
34#include "connection.h"
35 34
36/** 35/**
37 * Set socket to nodelay, on or off. 36 * Set socket to nodelay, on or off.
@@ -496,262 +495,3 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
496 * with this seems to be to mmap the file and write(2) as 495 * with this seems to be to mmap the file and write(2) as
497 * large a chunk as possible to the socket. Alternatively, 496 * large a chunk as possible to the socket. Alternatively,
498 * use madvise(..., MADV_SEQUENTIAL). */ 497 * use madvise(..., MADV_SEQUENTIAL). */
499
500#if defined(_MHD_HAVE_SENDFILE)
501/**
502 * Function for sending responses backed by file FD.
503 * A sendfile() wrapper which also performs cork/uncork
504 * operations.
505 *
506 * @param connection the #MHD_Connection structure
507 * @return actual number of bytes sent
508 */
509ssize_t
510MHD_sendfile_on_connection_ (struct MHD_Connection *connection)
511{
512 // I'm looking for a version of sendfile_adapter() that *also* performs
513 // cork/uncork operations. Specifically, we want to make sure that the
514 // buffer is flushed after sendfile() is done (so setsockopt() behavior
515 // equivalent to MHD_SSO_NO_CORK), and _also_ of course update the
516 // setsockopt state, i.e. connection->sk_tcp_nodelay_on = true;
517
518 bool want_cork = false;
519 bool have_cork;
520 bool have_more;
521 bool use_corknopush;
522 bool using_tls = false;
523 ssize_t ret;
524 const int file_fd = connection->response->fd;
525 uint64_t left;
526 uint64_t offsetu64;
527#ifndef HAVE_SENDFILE64
528 const uint64_t max_off_t = (uint64_t)OFF_T_MAX;
529#else /* HAVE_SENDFILE64 */
530 const uint64_t max_off_t = (uint64_t)OFF64_T_MAX;
531#endif /* HAVE_SENDFILE64 */
532#ifdef MHD_LINUX_SOLARIS_SENDFILE
533#ifndef HAVE_SENDFILE64
534 off_t offset;
535#else /* HAVE_SENDFILE64 */
536 off64_t offset;
537#endif /* HAVE_SENDFILE64 */
538#endif /* MHD_LINUX_SOLARIS_SENDFILE */
539#ifdef HAVE_FREEBSD_SENDFILE
540 off_t sent_bytes;
541 int flags = 0;
542#endif
543#ifdef HAVE_DARWIN_SENDFILE
544 off_t len;
545#endif /* HAVE_DARWIN_SENDFILE */
546 const bool used_thr_p_c = (0 != (connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION));
547 const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : MHD_SENFILE_CHUNK_;
548 size_t send_size = 0;
549 mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
550
551 offsetu64 = connection->response_write_position + connection->response->fd_off;
552 left = connection->response->total_size - connection->response_write_position;
553 /* Do not allow system to stick sending on single fast connection:
554 * use 128KiB chunks (2MiB for thread-per-connection). */
555 send_size = (left > chunk_size) ? chunk_size : (size_t) left;
556 if (max_off_t < offsetu64)
557 { /* Retry to send with standard 'send()'. */
558 connection->resp_sender = MHD_resp_sender_std;
559 return MHD_ERR_AGAIN_;
560 }
561#ifdef MHD_LINUX_SOLARIS_SENDFILE
562#ifndef HAVE_SENDFILE64
563 offset = (off_t) offsetu64;
564 ret = sendfile (connection->socket_fd,
565 file_fd,
566 &offset,
567 send_size);
568#else /* HAVE_SENDFILE64 */
569 offset = (off64_t) offsetu64;
570 ret = sendfile64 (connection->socket_fd,
571 file_fd,
572 &offset,
573 send_size);
574#endif /* HAVE_SENDFILE64 */
575 if (0 > ret)
576 {
577 const int err = MHD_socket_get_error_();
578 if (MHD_SCKT_ERR_IS_EAGAIN_(err))
579 {
580#ifdef EPOLL_SUPPORT
581 /* EAGAIN --- no longer write-ready */
582 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
583#endif /* EPOLL_SUPPORT */
584 return MHD_ERR_AGAIN_;
585 }
586 if (MHD_SCKT_ERR_IS_EINTR_ (err))
587 return MHD_ERR_AGAIN_;
588#ifdef HAVE_LINUX_SENDFILE
589 if (MHD_SCKT_ERR_IS_(err,
590 MHD_SCKT_EBADF_))
591 return MHD_ERR_BADF_;
592 /* sendfile() failed with EINVAL if mmap()-like operations are not
593 supported for FD or other 'unusual' errors occurred, so we should try
594 to fall back to 'SEND'; see also this thread for info on
595 odd libc/Linux behavior with sendfile:
596 http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
597 connection->resp_sender = MHD_resp_sender_std;
598 return MHD_ERR_AGAIN_;
599#else /* HAVE_SOLARIS_SENDFILE */
600 if ( (EAFNOSUPPORT == err) ||
601 (EINVAL == err) ||
602 (EOPNOTSUPP == err) )
603 { /* Retry with standard file reader. */
604 connection->resp_sender = MHD_resp_sender_std;
605 return MHD_ERR_AGAIN_;
606 }
607 if ( (ENOTCONN == err) ||
608 (EPIPE == err) )
609 {
610 return MHD_ERR_CONNRESET_;
611 }
612 return MHD_ERR_BADF_; /* Fail hard */
613#endif /* HAVE_SOLARIS_SENDFILE */
614 }
615#ifdef EPOLL_SUPPORT
616 else if (send_size > (size_t)ret)
617 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
618#endif /* EPOLL_SUPPORT */
619#elif defined(HAVE_FREEBSD_SENDFILE)
620#ifdef SF_FLAGS
621 flags = used_thr_p_c ?
622 freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
623#endif /* SF_FLAGS */
624 if (0 != sendfile (file_fd,
625 connection->socket_fd,
626 (off_t) offsetu64,
627 send_size,
628 NULL,
629 &sent_bytes,
630 flags))
631 {
632 const int err = MHD_socket_get_error_();
633 if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
634 MHD_SCKT_ERR_IS_EINTR_(err) ||
635 EBUSY == err)
636 {
637 mhd_assert (SSIZE_MAX >= sent_bytes);
638 if (0 != sent_bytes)
639 return (ssize_t)sent_bytes;
640
641 return MHD_ERR_AGAIN_;
642 }
643 /* Some unrecoverable error. Possibly file FD is not suitable
644 * for sendfile(). Retry with standard send(). */
645 connection->resp_sender = MHD_resp_sender_std;
646 return MHD_ERR_AGAIN_;
647 }
648 mhd_assert (0 < sent_bytes);
649 mhd_assert (SSIZE_MAX >= sent_bytes);
650 ret = (ssize_t)sent_bytes;
651#elif defined(HAVE_DARWIN_SENDFILE)
652 len = (off_t)send_size; /* chunk always fit */
653 if (0 != sendfile (file_fd,
654 connection->socket_fd,
655 (off_t) offsetu64,
656 &len,
657 NULL,
658 0))
659 {
660 const int err = MHD_socket_get_error_();
661 if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
662 MHD_SCKT_ERR_IS_EINTR_(err))
663 {
664 mhd_assert (0 <= len);
665 mhd_assert (SSIZE_MAX >= len);
666 mhd_assert (send_size >= (size_t)len);
667 if (0 != len)
668 return (ssize_t)len;
669
670 return MHD_ERR_AGAIN_;
671 }
672 if (ENOTCONN == err ||
673 EPIPE == err)
674 return MHD_ERR_CONNRESET_;
675 if (ENOTSUP == err ||
676 EOPNOTSUPP == err)
677 { /* This file FD is not suitable for sendfile().
678 * Retry with standard send(). */
679 connection->resp_sender = MHD_resp_sender_std;
680 return MHD_ERR_AGAIN_;
681 }
682 return MHD_ERR_BADF_; /* Return hard error. */
683 }
684 mhd_assert (0 <= len);
685 mhd_assert (SSIZE_MAX >= len);
686 mhd_assert (send_size >= (size_t)len);
687 ret = (ssize_t)len;
688#endif /* HAVE_FREEBSD_SENDFILE */
689
690 /* ! could be avoided by redefining the variable. */
691 have_cork = ! connection->sk_tcp_nodelay_on;
692
693#ifdef MSG_MORE
694 have_more = true;
695#else
696 have_more = false;
697#endif
698
699#if TCP_NODELAY
700 use_corknopush = false;
701#elif TCP_CORK
702 use_corknopush = true;
703#elif TCP_NOPUSH
704 use_corknopush = true;
705#endif
706
707#ifdef HTTPS_SUPPORT
708 using_tls = (0 != (connection->daemon->options & MHD_USE_TLS));
709#endif
710
711#if TCP_CORK
712 /* When we have CORK, we can have NODELAY on the same system,
713 * at least since Linux 2.2 and both can be combined since
714 * Linux 2.5.71. For more details refer to tcp(7) on Linux.
715 * No other system in 2019-06 has TCP_CORK. */
716 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
717 {
718 if (0 == setsockopt (connection->socket_fd,
719 IPPROTO_TCP,
720 TCP_CORK,
721 (const void *) &off_val,
722 sizeof (off_val)))
723 {
724 connection->sk_tcp_nodelay_on = true;
725 }
726 else if (0 == setsockopt (connection->socket_fd,
727 IPPROTO_TCP,
728 TCP_NODELAY,
729 (const void *) &on_val,
730 sizeof (on_val)))
731 {
732 connection->sk_tcp_nodelay_on = true;
733 }
734 }
735#elif TCP_NOPUSH
736 /* TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the
737 * exception that we know that TCP_NOPUSH will definitely
738 * exist and we can disregard TCP_NODELAY unless requested. */
739 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
740 {
741 MHD_send_socket_state_nopush_ (connection, true, false);
742 /*
743 if (0 == setsockopt (connection->socket_fd,
744 IPPROTO_TCP,
745 TCP_NOPUSH,
746 (const void *) &on_val,
747 sizeof (on_val)))
748 {
749 connection->sk_tcp_nodelay_on = false;
750 }
751 */
752 }
753#endif
754
755 return ret;
756}
757#endif /* _MHD_HAVE_SENDFILE */
diff --git a/src/microhttpd/mhd_send.h b/src/microhttpd/mhd_send.h
index a6049865..c4a6d705 100644
--- a/src/microhttpd/mhd_send.h
+++ b/src/microhttpd/mhd_send.h
@@ -77,4 +77,13 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
77 77
78ssize_t 78ssize_t
79MHD_sendfile_on_connection_ (struct MHD_Connection *connection); 79MHD_sendfile_on_connection_ (struct MHD_Connection *connection);
80
81void
82MHD_send_socket_state_nopush_ (struct MHD_Connection *connection,
83 bool value,
84 bool state_store);
85
86void
87MHD_send_socket_state_nodelay_ (struct MHD_Connection *connection,
88 bool value);
80#endif /* MHD_SEND_H */ 89#endif /* MHD_SEND_H */