libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 0c40dc481b3ed4149c9bb82118d5c923fb34dcde
parent 0b9d7d3bf3a26a512efe404c7ac0044bdd486932
Author: ng0 <ng0@n0.is>
Date:   Wed, 24 Jul 2019 10:17:45 +0000

move sendfile function work to connection.c

Diffstat:
Msrc/microhttpd/connection.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/mhd_send.c | 262+------------------------------------------------------------------------------
Msrc/microhttpd/mhd_send.h | 9+++++++++
3 files changed, 82 insertions(+), 261 deletions(-)

diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -288,6 +288,12 @@ send_param_adapter (struct MHD_Connection *connection, static ssize_t sendfile_adapter (struct MHD_Connection *connection) { + bool want_cork = false; + bool have_cork; + bool have_more; + bool use_corknopush; + bool using_tls = false; + ssize_t ret; const int file_fd = connection->response->fd; uint64_t left; @@ -454,6 +460,72 @@ sendfile_adapter (struct MHD_Connection *connection) mhd_assert (send_size >= (size_t)len); ret = (ssize_t)len; #endif /* HAVE_FREEBSD_SENDFILE */ + + /* ! could be avoided by redefining the variable. */ + have_cork = ! connection->sk_tcp_nodelay_on; + +#ifdef MSG_MORE + have_more = true; +#else + have_more = false; +#endif + +#if TCP_NODELAY + use_corknopush = false; +#elif TCP_CORK + use_corknopush = true; +#elif TCP_NOPUSH + use_corknopush = true; +#endif + +#ifdef HTTPS_SUPPORT + using_tls = (0 != (connection->daemon->options & MHD_USE_TLS)); +#endif + +#if TCP_CORK + /* When we have CORK, we can have NODELAY on the same system, + * at least since Linux 2.2 and both can be combined since + * Linux 2.5.71. For more details refer to tcp(7) on Linux. + * No other system in 2019-06 has TCP_CORK. */ + if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) + { + if (0 == setsockopt (connection->socket_fd, + IPPROTO_TCP, + TCP_CORK, + (const void *) &off_val, + sizeof (off_val))) + { + connection->sk_tcp_nodelay_on = true; + } + else if (0 == setsockopt (connection->socket_fd, + IPPROTO_TCP, + TCP_NODELAY, + (const void *) &on_val, + sizeof (on_val))) + { + connection->sk_tcp_nodelay_on = true; + } + } +#elif TCP_NOPUSH + /* TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the + * exception that we know that TCP_NOPUSH will definitely + * exist and we can disregard TCP_NODELAY unless requested. */ + if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) + { + MHD_send_socket_state_nopush_ (connection, true, false); + /* + if (0 == setsockopt (connection->socket_fd, + IPPROTO_TCP, + TCP_NOPUSH, + (const void *) &on_val, + sizeof (on_val))) + { + connection->sk_tcp_nodelay_on = false; + } + */ + } +#endif + return ret; } #endif /* _MHD_HAVE_SENDFILE */ diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c @@ -24,14 +24,13 @@ * @author ng0 <ng0@n0.is> */ -/* TODO: sendfile() wrappers. */ +/* TODO: sendfile() wrapper, in connection.c */ /* Functions to be used in: send_param_adapter, MHD_send_ * and every place where sendfile(), sendfile64(), setsockopt() * are used. */ #include "mhd_send.h" -#include "connection.h" /** * Set socket to nodelay, on or off. @@ -496,262 +495,3 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection, * with this seems to be to mmap the file and write(2) as * large a chunk as possible to the socket. Alternatively, * use madvise(..., MADV_SEQUENTIAL). */ - -#if defined(_MHD_HAVE_SENDFILE) -/** - * Function for sending responses backed by file FD. - * A sendfile() wrapper which also performs cork/uncork - * operations. - * - * @param connection the #MHD_Connection structure - * @return actual number of bytes sent - */ -ssize_t -MHD_sendfile_on_connection_ (struct MHD_Connection *connection) -{ - // I'm looking for a version of sendfile_adapter() that *also* performs - // cork/uncork operations. Specifically, we want to make sure that the - // buffer is flushed after sendfile() is done (so setsockopt() behavior - // equivalent to MHD_SSO_NO_CORK), and _also_ of course update the - // setsockopt state, i.e. connection->sk_tcp_nodelay_on = true; - - bool want_cork = false; - bool have_cork; - bool have_more; - bool use_corknopush; - bool using_tls = false; - ssize_t ret; - const int file_fd = connection->response->fd; - uint64_t left; - uint64_t offsetu64; -#ifndef HAVE_SENDFILE64 - const uint64_t max_off_t = (uint64_t)OFF_T_MAX; -#else /* HAVE_SENDFILE64 */ - const uint64_t max_off_t = (uint64_t)OFF64_T_MAX; -#endif /* HAVE_SENDFILE64 */ -#ifdef MHD_LINUX_SOLARIS_SENDFILE -#ifndef HAVE_SENDFILE64 - off_t offset; -#else /* HAVE_SENDFILE64 */ - off64_t offset; -#endif /* HAVE_SENDFILE64 */ -#endif /* MHD_LINUX_SOLARIS_SENDFILE */ -#ifdef HAVE_FREEBSD_SENDFILE - off_t sent_bytes; - int flags = 0; -#endif -#ifdef HAVE_DARWIN_SENDFILE - off_t len; -#endif /* HAVE_DARWIN_SENDFILE */ - const bool used_thr_p_c = (0 != (connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION)); - const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : MHD_SENFILE_CHUNK_; - size_t send_size = 0; - mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender); - - offsetu64 = connection->response_write_position + connection->response->fd_off; - left = connection->response->total_size - connection->response_write_position; - /* Do not allow system to stick sending on single fast connection: - * use 128KiB chunks (2MiB for thread-per-connection). */ - send_size = (left > chunk_size) ? chunk_size : (size_t) left; - if (max_off_t < offsetu64) - { /* Retry to send with standard 'send()'. */ - connection->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; - } -#ifdef MHD_LINUX_SOLARIS_SENDFILE -#ifndef HAVE_SENDFILE64 - offset = (off_t) offsetu64; - ret = sendfile (connection->socket_fd, - file_fd, - &offset, - send_size); -#else /* HAVE_SENDFILE64 */ - offset = (off64_t) offsetu64; - ret = sendfile64 (connection->socket_fd, - file_fd, - &offset, - send_size); -#endif /* HAVE_SENDFILE64 */ - if (0 > ret) - { - const int err = MHD_socket_get_error_(); - if (MHD_SCKT_ERR_IS_EAGAIN_(err)) - { -#ifdef EPOLL_SUPPORT - /* EAGAIN --- no longer write-ready */ - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif /* EPOLL_SUPPORT */ - return MHD_ERR_AGAIN_; - } - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return MHD_ERR_AGAIN_; -#ifdef HAVE_LINUX_SENDFILE - if (MHD_SCKT_ERR_IS_(err, - MHD_SCKT_EBADF_)) - return MHD_ERR_BADF_; - /* sendfile() failed with EINVAL if mmap()-like operations are not - supported for FD or other 'unusual' errors occurred, so we should try - to fall back to 'SEND'; see also this thread for info on - odd libc/Linux behavior with sendfile: - http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */ - connection->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; -#else /* HAVE_SOLARIS_SENDFILE */ - if ( (EAFNOSUPPORT == err) || - (EINVAL == err) || - (EOPNOTSUPP == err) ) - { /* Retry with standard file reader. */ - connection->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; - } - if ( (ENOTCONN == err) || - (EPIPE == err) ) - { - return MHD_ERR_CONNRESET_; - } - return MHD_ERR_BADF_; /* Fail hard */ -#endif /* HAVE_SOLARIS_SENDFILE */ - } -#ifdef EPOLL_SUPPORT - else if (send_size > (size_t)ret) - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif /* EPOLL_SUPPORT */ -#elif defined(HAVE_FREEBSD_SENDFILE) -#ifdef SF_FLAGS - flags = used_thr_p_c ? - freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_; -#endif /* SF_FLAGS */ - if (0 != sendfile (file_fd, - connection->socket_fd, - (off_t) offsetu64, - send_size, - NULL, - &sent_bytes, - flags)) - { - const int err = MHD_socket_get_error_(); - if (MHD_SCKT_ERR_IS_EAGAIN_(err) || - MHD_SCKT_ERR_IS_EINTR_(err) || - EBUSY == err) - { - mhd_assert (SSIZE_MAX >= sent_bytes); - if (0 != sent_bytes) - return (ssize_t)sent_bytes; - - return MHD_ERR_AGAIN_; - } - /* Some unrecoverable error. Possibly file FD is not suitable - * for sendfile(). Retry with standard send(). */ - connection->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; - } - mhd_assert (0 < sent_bytes); - mhd_assert (SSIZE_MAX >= sent_bytes); - ret = (ssize_t)sent_bytes; -#elif defined(HAVE_DARWIN_SENDFILE) - len = (off_t)send_size; /* chunk always fit */ - if (0 != sendfile (file_fd, - connection->socket_fd, - (off_t) offsetu64, - &len, - NULL, - 0)) - { - const int err = MHD_socket_get_error_(); - if (MHD_SCKT_ERR_IS_EAGAIN_(err) || - MHD_SCKT_ERR_IS_EINTR_(err)) - { - mhd_assert (0 <= len); - mhd_assert (SSIZE_MAX >= len); - mhd_assert (send_size >= (size_t)len); - if (0 != len) - return (ssize_t)len; - - return MHD_ERR_AGAIN_; - } - if (ENOTCONN == err || - EPIPE == err) - return MHD_ERR_CONNRESET_; - if (ENOTSUP == err || - EOPNOTSUPP == err) - { /* This file FD is not suitable for sendfile(). - * Retry with standard send(). */ - connection->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; - } - return MHD_ERR_BADF_; /* Return hard error. */ - } - mhd_assert (0 <= len); - mhd_assert (SSIZE_MAX >= len); - mhd_assert (send_size >= (size_t)len); - ret = (ssize_t)len; -#endif /* HAVE_FREEBSD_SENDFILE */ - - /* ! could be avoided by redefining the variable. */ - have_cork = ! connection->sk_tcp_nodelay_on; - -#ifdef MSG_MORE - have_more = true; -#else - have_more = false; -#endif - -#if TCP_NODELAY - use_corknopush = false; -#elif TCP_CORK - use_corknopush = true; -#elif TCP_NOPUSH - use_corknopush = true; -#endif - -#ifdef HTTPS_SUPPORT - using_tls = (0 != (connection->daemon->options & MHD_USE_TLS)); -#endif - -#if TCP_CORK - /* When we have CORK, we can have NODELAY on the same system, - * at least since Linux 2.2 and both can be combined since - * Linux 2.5.71. For more details refer to tcp(7) on Linux. - * No other system in 2019-06 has TCP_CORK. */ - if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) - { - if (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_CORK, - (const void *) &off_val, - sizeof (off_val))) - { - connection->sk_tcp_nodelay_on = true; - } - else if (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NODELAY, - (const void *) &on_val, - sizeof (on_val))) - { - connection->sk_tcp_nodelay_on = true; - } - } -#elif TCP_NOPUSH - /* TCP_NOPUSH on FreeBSD is equal to cork on Linux, with the - * exception that we know that TCP_NOPUSH will definitely - * exist and we can disregard TCP_NODELAY unless requested. */ - if ((! using_tls) && (use_corknopush) && (have_cork && ! want_cork)) - { - MHD_send_socket_state_nopush_ (connection, true, false); - /* - if (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NOPUSH, - (const void *) &on_val, - sizeof (on_val))) - { - connection->sk_tcp_nodelay_on = false; - } - */ - } -#endif - - return ret; -} -#endif /* _MHD_HAVE_SENDFILE */ diff --git a/src/microhttpd/mhd_send.h b/src/microhttpd/mhd_send.h @@ -77,4 +77,13 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection, ssize_t MHD_sendfile_on_connection_ (struct MHD_Connection *connection); + +void +MHD_send_socket_state_nopush_ (struct MHD_Connection *connection, + bool value, + bool state_store); + +void +MHD_send_socket_state_nodelay_ (struct MHD_Connection *connection, + bool value); #endif /* MHD_SEND_H */