diff options
author | ng0 <ng0@n0.is> | 2019-07-25 17:33:14 +0000 |
---|---|---|
committer | ng0 <ng0@n0.is> | 2019-07-25 17:33:14 +0000 |
commit | 5bfd85fc577faa16598d2d6602057644d3d8e088 (patch) | |
tree | a5c637fdc3ff0617d009847193f0f7b654f05d8b /src/microhttpd/mhd_send.c | |
parent | c2915ac74083f5803d7d26de83b65eac4cb341a7 (diff) | |
download | libmicrohttpd-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.c | 288 |
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 | */ | ||
674 | static int freebsd_sendfile_flags_; | ||
675 | |||
676 | /** | ||
677 | * FreeBSD sendfile() flags for thread-per-connection | ||
678 | */ | ||
679 | static int freebsd_sendfile_flags_thd_p_c_; | ||
680 | #endif /* SF_FLAGS */ | ||
681 | /** | ||
682 | * Initialises static variables | ||
683 | */ | ||
684 | void | ||
685 | MHD_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 | */ | ||
715 | static ssize_t | ||
716 | sendfile_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 */ | ||