aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/mhd_send.c
diff options
context:
space:
mode:
authorng0 <ng0@n0.is>2019-07-25 17:33:14 +0000
committerng0 <ng0@n0.is>2019-07-25 17:33:14 +0000
commit5bfd85fc577faa16598d2d6602057644d3d8e088 (patch)
treea5c637fdc3ff0617d009847193f0f7b654f05d8b /src/microhttpd/mhd_send.c
parentc2915ac74083f5803d7d26de83b65eac4cb341a7 (diff)
downloadlibmicrohttpd-5bfd85fc577faa16598d2d6602057644d3d8e088.tar.gz
libmicrohttpd-5bfd85fc577faa16598d2d6602057644d3d8e088.zip
initial move code sendfile.
Diffstat (limited to 'src/microhttpd/mhd_send.c')
-rw-r--r--src/microhttpd/mhd_send.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c
index b22313df..4758b502 100644
--- a/src/microhttpd/mhd_send.c
+++ b/src/microhttpd/mhd_send.c
@@ -655,3 +655,291 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
655 MHD_SSO_HDR_CORK); 655 MHD_SSO_HDR_CORK);
656#endif 656#endif
657} 657}
658
659/**
660 * sendfile() chuck size
661 */
662#define MHD_SENFILE_CHUNK_ (0x20000)
663
664/**
665 * sendfile() chuck size for thread-per-connection
666 */
667#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
668
669#ifdef HAVE_FREEBSD_SENDFILE
670#ifdef SF_FLAGS
671/**
672 * FreeBSD sendfile() flags
673 */
674static int freebsd_sendfile_flags_;
675
676/**
677 * FreeBSD sendfile() flags for thread-per-connection
678 */
679static int freebsd_sendfile_flags_thd_p_c_;
680#endif /* SF_FLAGS */
681/**
682 * Initialises static variables
683 */
684void
685MHD_conn_init_static_ (void)
686{
687/* FreeBSD 11 and later allow to specify read-ahead size
688 * and handles SF_NODISKIO differently.
689 * SF_FLAGS defined only on FreeBSD 11 and later. */
690#ifdef SF_FLAGS
691 long sys_page_size = sysconf (_SC_PAGESIZE);
692 if (0 > sys_page_size)
693 { /* Failed to get page size. */
694 freebsd_sendfile_flags_ = SF_NODISKIO;
695 freebsd_sendfile_flags_thd_p_c_ = SF_NODISKIO;
696 }
697 else
698 {
699 freebsd_sendfile_flags_ =
700 SF_FLAGS((uint16_t)(MHD_SENFILE_CHUNK_ / sys_page_size), SF_NODISKIO);
701 freebsd_sendfile_flags_thd_p_c_ =
702 SF_FLAGS((uint16_t)(MHD_SENFILE_CHUNK_THR_P_C_ / sys_page_size), SF_NODISKIO);
703 }
704#endif /* SF_FLAGS */
705}
706#endif /* HAVE_FREEBSD_SENDFILE */
707
708#if defined(_MHD_HAVE_SENDFILE)
709/**
710 * Function for sending responses backed by file FD.
711 *
712 * @param connection the MHD connection structure
713 * @return actual number of bytes sent
714 */
715static ssize_t
716sendfile_adapter (struct MHD_Connection *connection)
717{
718 bool want_cork = false;
719 bool have_cork;
720 bool have_more;
721 bool use_corknopush;
722 bool using_tls = false;
723
724 ssize_t ret;
725 ssize_t lo_ret;
726 const int file_fd = connection->response->fd;
727 uint64_t left;
728 uint64_t offsetu64;
729#ifndef HAVE_SENDFILE64
730 const uint64_t max_off_t = (uint64_t)OFF_T_MAX;
731#else /* HAVE_SENDFILE64 */
732 const uint64_t max_off_t = (uint64_t)OFF64_T_MAX;
733#endif /* HAVE_SENDFILE64 */
734#ifdef MHD_LINUX_SOLARIS_SENDFILE
735#ifndef HAVE_SENDFILE64
736 off_t offset;
737#else /* HAVE_SENDFILE64 */
738 off64_t offset;
739#endif /* HAVE_SENDFILE64 */
740#endif /* MHD_LINUX_SOLARIS_SENDFILE */
741#ifdef HAVE_FREEBSD_SENDFILE
742 off_t sent_bytes;
743 int flags = 0;
744#endif
745#ifdef HAVE_DARWIN_SENDFILE
746 off_t len;
747#endif /* HAVE_DARWIN_SENDFILE */
748 const bool used_thr_p_c = (0 != (connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION));
749 const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : MHD_SENFILE_CHUNK_;
750 size_t send_size = 0;
751 mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
752
753 offsetu64 = connection->response_write_position + connection->response->fd_off;
754 left = connection->response->total_size - connection->response_write_position;
755 /* Do not allow system to stick sending on single fast connection:
756 * use 128KiB chunks (2MiB for thread-per-connection). */
757 send_size = (left > chunk_size) ? chunk_size : (size_t) left;
758 if (max_off_t < offsetu64)
759 { /* Retry to send with standard 'send()'. */
760 connection->resp_sender = MHD_resp_sender_std;
761 return MHD_ERR_AGAIN_;
762 }
763#ifdef MHD_LINUX_SOLARIS_SENDFILE
764#ifndef HAVE_SENDFILE64
765 offset = (off_t) offsetu64;
766 ret = sendfile (connection->socket_fd,
767 file_fd,
768 &offset,
769 send_size);
770#else /* HAVE_SENDFILE64 */
771 offset = (off64_t) offsetu64;
772 ret = sendfile64 (connection->socket_fd,
773 file_fd,
774 &offset,
775 send_size);
776#endif /* HAVE_SENDFILE64 */
777 if (0 > ret)
778 {
779 const int err = MHD_socket_get_error_();
780 if (MHD_SCKT_ERR_IS_EAGAIN_(err))
781 {
782#ifdef EPOLL_SUPPORT
783 /* EAGAIN --- no longer write-ready */
784 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
785#endif /* EPOLL_SUPPORT */
786 return MHD_ERR_AGAIN_;
787 }
788 if (MHD_SCKT_ERR_IS_EINTR_ (err))
789 return MHD_ERR_AGAIN_;
790#ifdef HAVE_LINUX_SENDFILE
791 if (MHD_SCKT_ERR_IS_(err,
792 MHD_SCKT_EBADF_))
793 return MHD_ERR_BADF_;
794 /* sendfile() failed with EINVAL if mmap()-like operations are not
795 supported for FD or other 'unusual' errors occurred, so we should try
796 to fall back to 'SEND'; see also this thread for info on
797 odd libc/Linux behavior with sendfile:
798 http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
799 connection->resp_sender = MHD_resp_sender_std;
800 return MHD_ERR_AGAIN_;
801#else /* HAVE_SOLARIS_SENDFILE */
802 if ( (EAFNOSUPPORT == err) ||
803 (EINVAL == err) ||
804 (EOPNOTSUPP == err) )
805 { /* Retry with standard file reader. */
806 connection->resp_sender = MHD_resp_sender_std;
807 return MHD_ERR_AGAIN_;
808 }
809 if ( (ENOTCONN == err) ||
810 (EPIPE == err) )
811 {
812 return MHD_ERR_CONNRESET_;
813 }
814 return MHD_ERR_BADF_; /* Fail hard */
815#endif /* HAVE_SOLARIS_SENDFILE */
816 }
817#ifdef EPOLL_SUPPORT
818 else if (send_size > (size_t)ret)
819 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
820#endif /* EPOLL_SUPPORT */
821#elif defined(HAVE_FREEBSD_SENDFILE)
822#ifdef SF_FLAGS
823 flags = used_thr_p_c ?
824 freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_;
825#endif /* SF_FLAGS */
826 if (0 != sendfile (file_fd,
827 connection->socket_fd,
828 (off_t) offsetu64,
829 send_size,
830 NULL,
831 &sent_bytes,
832 flags))
833 {
834 const int err = MHD_socket_get_error_();
835 if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
836 MHD_SCKT_ERR_IS_EINTR_(err) ||
837 EBUSY == err)
838 {
839 mhd_assert (SSIZE_MAX >= sent_bytes);
840 if (0 != sent_bytes)
841 return (ssize_t)sent_bytes;
842
843 return MHD_ERR_AGAIN_;
844 }
845 /* Some unrecoverable error. Possibly file FD is not suitable
846 * for sendfile(). Retry with standard send(). */
847 connection->resp_sender = MHD_resp_sender_std;
848 return MHD_ERR_AGAIN_;
849 }
850 mhd_assert (0 < sent_bytes);
851 mhd_assert (SSIZE_MAX >= sent_bytes);
852 ret = (ssize_t)sent_bytes;
853#elif defined(HAVE_DARWIN_SENDFILE)
854 len = (off_t)send_size; /* chunk always fit */
855 if (0 != sendfile (file_fd,
856 connection->socket_fd,
857 (off_t) offsetu64,
858 &len,
859 NULL,
860 0))
861 {
862 const int err = MHD_socket_get_error_();
863 if (MHD_SCKT_ERR_IS_EAGAIN_(err) ||
864 MHD_SCKT_ERR_IS_EINTR_(err))
865 {
866 mhd_assert (0 <= len);
867 mhd_assert (SSIZE_MAX >= len);
868 mhd_assert (send_size >= (size_t)len);
869 if (0 != len)
870 return (ssize_t)len;
871
872 return MHD_ERR_AGAIN_;
873 }
874 if (ENOTCONN == err ||
875 EPIPE == err)
876 return MHD_ERR_CONNRESET_;
877 if (ENOTSUP == err ||
878 EOPNOTSUPP == err)
879 { /* This file FD is not suitable for sendfile().
880 * Retry with standard send(). */
881 connection->resp_sender = MHD_resp_sender_std;
882 return MHD_ERR_AGAIN_;
883 }
884 return MHD_ERR_BADF_; /* Return hard error. */
885 }
886 mhd_assert (0 <= len);
887 mhd_assert (SSIZE_MAX >= len);
888 mhd_assert (send_size >= (size_t)len);
889 ret = (ssize_t)len;
890#endif /* HAVE_FREEBSD_SENDFILE */
891
892 ret = lo_ret;
893 if (0 > ret)
894 {
895 /* ! could be avoided by redefining the variable. */
896 have_cork = ! connection->sk_tcp_nodelay_on;
897
898#ifdef MSG_MORE
899 have_more = true;
900#else
901 have_more = false;
902#endif
903
904#if TCP_NODELAY
905 use_corknopush = false;
906#elif TCP_CORK
907 use_corknopush = true;
908#elif TCP_NOPUSH
909 use_corknopush = true;
910#endif
911
912#ifdef HTTPS_SUPPORT
913 using_tls = (0 != (connection->daemon->options & MHD_USE_TLS));
914#endif
915
916#if TCP_CORK
917 /* When we have CORK, we can have NODELAY on the same system,
918 * at least since Linux 2.2 and both can be combined since
919 * Linux 2.5.71. For more details refer to tcp(7) on Linux.
920 * No other system in 2019-06 has TCP_CORK. */
921 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
922 {
923 MHD_send_socket_state_cork_nodelay_ (connection,
924 false,
925 true,
926 true,
927 true);
928 }
929#elif TCP_NOPUSH
930 /* TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the
931 * exception that we know that TCP_NOPUSH will definitely
932 * exist and we can disregard TCP_NODELAY unless requested. */
933 if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork))
934 {
935 MHD_send_socket_state_nopush_ (connection, true, false);
936 }
937#endif
938 return lo_ret;
939 }
940 else
941 {
942 return ret;
943 }
944}
945#endif /* _MHD_HAVE_SENDFILE */