libmicrohttpd

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

commit be855dd3d8c0cf967355e230aa887539ba37a398
parent 126b496ed54c40d1163b1c30fb6e7fb6d5b7745d
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Sun,  4 Jun 2017 17:33:35 +0300

Refactoring: moved send_/recv_param_adapter() to connection.c and send_/recv_tls_adapter() to connection_https.c

Diffstat:
Msrc/microhttpd/connection.c | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/connection_https.c | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/daemon.c | 269-------------------------------------------------------------------------------
3 files changed, 269 insertions(+), 269 deletions(-)

diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -110,6 +110,168 @@ /** + * Callback for receiving data from the socket. + * + * @param connection the MHD connection structure + * @param other where to write received data to + * @param i maximum size of other (in bytes) + * @return number of bytes actually received + */ +static ssize_t +recv_param_adapter (struct MHD_Connection *connection, + void *other, + size_t i) +{ + ssize_t ret; + + if ( (MHD_INVALID_SOCKET == connection->socket_fd) || + (MHD_CONNECTION_CLOSED == connection->state) ) + { + MHD_socket_set_error_ (MHD_SCKT_ENOTCONN_); + return -1; + } + if (i > MHD_SCKT_SEND_MAX_SIZE_) + i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ + + ret = MHD_recv_ (connection->socket_fd, + other, + i); +#ifdef EPOLL_SUPPORT + if (0 > ret) + { + /* Got EAGAIN --- no longer read-ready */ + if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) + connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; + } + else if (i > (size_t)ret) + connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; +#endif + return ret; +} + + +/** + * Callback for writing data to the socket. + * + * @param connection the MHD connection structure + * @param other data to write + * @param i number of bytes to write + * @return actual number of bytes written + */ +static ssize_t +send_param_adapter (struct MHD_Connection *connection, + const void *other, + size_t i) +{ + ssize_t ret; + int err; + + if ( (MHD_INVALID_SOCKET == connection->socket_fd) || + (MHD_CONNECTION_CLOSED == connection->state) ) + { + MHD_socket_set_error_ (MHD_SCKT_ENOTCONN_); + return -1; + } + if (i > MHD_SCKT_SEND_MAX_SIZE_) + i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ + +#if LINUX + if ( (connection->write_buffer_append_offset == + connection->write_buffer_send_offset) && + (NULL != connection->response) && + (MHD_resp_sender_sendfile == connection->resp_sender) ) + { + /* can use sendfile */ + int file_fd = connection->response->fd; + uint64_t left; + uint64_t offsetu64; +#ifndef HAVE_SENDFILE64 + off_t offset; +#else /* HAVE_SENDFILE64 */ + off64_t offset; +#endif /* HAVE_SENDFILE64 */ + offsetu64 = connection->response_write_position + connection->response->fd_off; + left = connection->response->total_size - connection->response_write_position; + ret = 0; +#ifndef HAVE_SENDFILE64 + if ((uint64_t)OFF_T_MAX < offsetu64) + MHD_socket_set_error_to_ENOMEM (); + else + { + offset = (off_t) offsetu64; + ret = sendfile (connection->socket_fd, + file_fd, + &offset, + left); + } +#else /* HAVE_SENDFILE64 */ + if ((uint64_t)OFF64_T_MAX < offsetu64) + MHD_socket_set_error_to_ENOMEM (); + else + { + offset = (off64_t) offsetu64; + ret = sendfile64 (connection->socket_fd, + file_fd, + &offset, + left); + } +#endif /* HAVE_SENDFILE64 */ + if (0 < ret) + { + /* write successful */ +#ifdef EPOLL_SUPPORT + if (left > (uint64_t)ret) + connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; +#endif /* EPOLL_SUPPORT */ + return ret; + } + err = MHD_socket_get_error_(); +#ifdef EPOLL_SUPPORT + if ( (0 > ret) && (MHD_SCKT_ERR_IS_EAGAIN_(err)) ) + { + /* EAGAIN --- no longer write-ready */ + connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; + } +#endif + if (MHD_SCKT_ERR_IS_EINTR_ (err) || + MHD_SCKT_ERR_IS_EAGAIN_ (err)) + return 0; + if (MHD_SCKT_ERR_IS_(err, + MHD_SCKT_EBADF_)) + return -1; + /* 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; + } +#endif + ret = MHD_send_ (connection->socket_fd, + other, + i); + err = MHD_socket_get_error_(); +#ifdef EPOLL_SUPPORT + if (0 > ret) + { + /* EAGAIN --- no longer write-ready */ + if (MHD_SCKT_ERR_IS_EAGAIN_(err)) + connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; + } + else if (i > (size_t)ret) + connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; +#endif + /* Handle broken kernel / libc, returning -1 but not setting errno; + kill connection as that should be safe; reported on mailinglist here: + http://lists.gnu.org/archive/html/libmicrohttpd/2014-10/msg00023.html */ + if ( (0 > ret) && + (0 == err) ) + MHD_socket_set_error_ (MHD_SCKT_ECONNRESET_); + return ret; +} + + +/** * Check whether is possible to force push socket buffer content as * partial packet. * MHD use different buffering logic depending on whether flushing of @@ -3445,6 +3607,8 @@ MHD_set_http_callbacks_ (struct MHD_Connection *connection) { connection->read_handler = &MHD_connection_handle_read; connection->write_handler = &MHD_connection_handle_write; + connection->recv_cls = &recv_param_adapter; + connection->send_cls = &send_param_adapter; } diff --git a/src/microhttpd/connection_https.c b/src/microhttpd/connection_https.c @@ -36,6 +36,109 @@ /** + * Callback for receiving data from the socket. + * + * @param connection the MHD_Connection structure + * @param other where to write received data to + * @param i maximum size of other (in bytes) + * @return number of bytes actually received + */ +static ssize_t +recv_tls_adapter (struct MHD_Connection *connection, + void *other, + size_t i) +{ + ssize_t res; + + if (i > SSIZE_MAX) + i = SSIZE_MAX; + + res = gnutls_record_recv (connection->tls_session, + other, + i); + if ( (GNUTLS_E_AGAIN == res) || + (GNUTLS_E_INTERRUPTED == res) ) + { + MHD_socket_set_error_ (MHD_SCKT_EINTR_); +#ifdef EPOLL_SUPPORT + if (GNUTLS_E_AGAIN == res) + connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; +#endif + return -1; + } + if (res < 0) + { + /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication + disrupted); set errno to something caller will interpret + correctly as a hard error */ + MHD_socket_set_error_ (MHD_SCKT_ECONNRESET_); + connection->tls_read_ready = false; + return res; + } + +#ifdef EPOLL_SUPPORT + /* If data not available to fill whole buffer - socket is not read ready anymore. */ + if (i > (size_t)res) + connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; +#endif /* EPOLL_SUPPORT */ + + /* Check whether TLS buffers still have some unread data. */ + connection->tls_read_ready = ( ((size_t)res == i) && + (0 != gnutls_record_check_pending (connection->tls_session)) ); + return res; +} + + +/** + * Callback for writing data to the socket. + * + * @param connection the MHD connection structure + * @param other data to write + * @param i number of bytes to write + * @return actual number of bytes written + */ +static ssize_t +send_tls_adapter (struct MHD_Connection *connection, + const void *other, + size_t i) +{ + ssize_t res; + + if (i > SSIZE_MAX) + i = SSIZE_MAX; + + res = gnutls_record_send (connection->tls_session, + other, + i); + if ( (GNUTLS_E_AGAIN == res) || + (GNUTLS_E_INTERRUPTED == res) ) + { + MHD_socket_set_error_ (MHD_SCKT_EINTR_); +#ifdef EPOLL_SUPPORT + if (GNUTLS_E_AGAIN == res) + connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; +#endif + return -1; + } + if (res < 0) + { + /* some other GNUTLS error, should set 'errno'; as we do not + really understand the error (not listed in GnuTLS + documentation explicitly), we set 'errno' to something that + will cause the connection to fail. */ + MHD_socket_set_error_ (MHD_SCKT_ECONNRESET_); + return -1; + } +#ifdef EPOLL_SUPPORT + /* If NOT all available data was sent - socket is not write ready anymore. */ + if (i > (size_t)res) + connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; +#endif /* EPOLL_SUPPORT */ + return res; +} + + +/** * Give gnuTLS chance to work on the TLS handshake. * * @param connection connection to handshake on @@ -130,6 +233,8 @@ MHD_set_https_callbacks (struct MHD_Connection *connection) { connection->read_handler = &MHD_tls_connection_handle_read; connection->write_handler = &MHD_tls_connection_handle_write; + connection->recv_cls = &recv_tls_adapter; + connection->send_cls = &send_tls_adapter; } diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -431,109 +431,6 @@ MHD_ip_limit_del (struct MHD_Daemon *daemon, #ifdef HTTPS_SUPPORT /** - * Callback for receiving data from the socket. - * - * @param connection the MHD_Connection structure - * @param other where to write received data to - * @param i maximum size of other (in bytes) - * @return number of bytes actually received - */ -static ssize_t -recv_tls_adapter (struct MHD_Connection *connection, - void *other, - size_t i) -{ - ssize_t res; - - if (i > SSIZE_MAX) - i = SSIZE_MAX; - - res = gnutls_record_recv (connection->tls_session, - other, - i); - if ( (GNUTLS_E_AGAIN == res) || - (GNUTLS_E_INTERRUPTED == res) ) - { - MHD_socket_set_error_ (MHD_SCKT_EINTR_); -#ifdef EPOLL_SUPPORT - if (GNUTLS_E_AGAIN == res) - connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; -#endif - return -1; - } - if (res < 0) - { - /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication - disrupted); set errno to something caller will interpret - correctly as a hard error */ - MHD_socket_set_error_ (MHD_SCKT_ECONNRESET_); - connection->tls_read_ready = false; - return res; - } - -#ifdef EPOLL_SUPPORT - /* If data not available to fill whole buffer - socket is not read ready anymore. */ - if (i > (size_t)res) - connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; -#endif /* EPOLL_SUPPORT */ - - /* Check whether TLS buffers still have some unread data. */ - connection->tls_read_ready = ( ((size_t)res == i) && - (0 != gnutls_record_check_pending (connection->tls_session)) ); - return res; -} - - -/** - * Callback for writing data to the socket. - * - * @param connection the MHD connection structure - * @param other data to write - * @param i number of bytes to write - * @return actual number of bytes written - */ -static ssize_t -send_tls_adapter (struct MHD_Connection *connection, - const void *other, - size_t i) -{ - ssize_t res; - - if (i > SSIZE_MAX) - i = SSIZE_MAX; - - res = gnutls_record_send (connection->tls_session, - other, - i); - if ( (GNUTLS_E_AGAIN == res) || - (GNUTLS_E_INTERRUPTED == res) ) - { - MHD_socket_set_error_ (MHD_SCKT_EINTR_); -#ifdef EPOLL_SUPPORT - if (GNUTLS_E_AGAIN == res) - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif - return -1; - } - if (res < 0) - { - /* some other GNUTLS error, should set 'errno'; as we do not - really understand the error (not listed in GnuTLS - documentation explicitly), we set 'errno' to something that - will cause the connection to fail. */ - MHD_socket_set_error_ (MHD_SCKT_ECONNRESET_); - return -1; - } -#ifdef EPOLL_SUPPORT - /* If NOT all available data was sent - socket is not write ready anymore. */ - if (i > (size_t)res) - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif /* EPOLL_SUPPORT */ - return res; -} - - -/** * Read and setup our certificate and key. * * @param daemon handle to daemon to initialize @@ -2140,168 +2037,6 @@ exit: /** - * Callback for receiving data from the socket. - * - * @param connection the MHD connection structure - * @param other where to write received data to - * @param i maximum size of other (in bytes) - * @return number of bytes actually received - */ -static ssize_t -recv_param_adapter (struct MHD_Connection *connection, - void *other, - size_t i) -{ - ssize_t ret; - - if ( (MHD_INVALID_SOCKET == connection->socket_fd) || - (MHD_CONNECTION_CLOSED == connection->state) ) - { - MHD_socket_set_error_ (MHD_SCKT_ENOTCONN_); - return -1; - } - if (i > MHD_SCKT_SEND_MAX_SIZE_) - i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ - - ret = MHD_recv_ (connection->socket_fd, - other, - i); -#ifdef EPOLL_SUPPORT - if (0 > ret) - { - /* Got EAGAIN --- no longer read-ready */ - if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) - connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; - } - else if (i > (size_t)ret) - connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; -#endif - return ret; -} - - -/** - * Callback for writing data to the socket. - * - * @param connection the MHD connection structure - * @param other data to write - * @param i number of bytes to write - * @return actual number of bytes written - */ -static ssize_t -send_param_adapter (struct MHD_Connection *connection, - const void *other, - size_t i) -{ - ssize_t ret; - int err; - - if ( (MHD_INVALID_SOCKET == connection->socket_fd) || - (MHD_CONNECTION_CLOSED == connection->state) ) - { - MHD_socket_set_error_ (MHD_SCKT_ENOTCONN_); - return -1; - } - if (i > MHD_SCKT_SEND_MAX_SIZE_) - i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ - -#if LINUX - if ( (connection->write_buffer_append_offset == - connection->write_buffer_send_offset) && - (NULL != connection->response) && - (MHD_resp_sender_sendfile == connection->resp_sender) ) - { - /* can use sendfile */ - int file_fd = connection->response->fd; - uint64_t left; - uint64_t offsetu64; -#ifndef HAVE_SENDFILE64 - off_t offset; -#else /* HAVE_SENDFILE64 */ - off64_t offset; -#endif /* HAVE_SENDFILE64 */ - offsetu64 = connection->response_write_position + connection->response->fd_off; - left = connection->response->total_size - connection->response_write_position; - ret = 0; -#ifndef HAVE_SENDFILE64 - if ((uint64_t)OFF_T_MAX < offsetu64) - MHD_socket_set_error_to_ENOMEM (); - else - { - offset = (off_t) offsetu64; - ret = sendfile (connection->socket_fd, - file_fd, - &offset, - left); - } -#else /* HAVE_SENDFILE64 */ - if ((uint64_t)OFF64_T_MAX < offsetu64) - MHD_socket_set_error_to_ENOMEM (); - else - { - offset = (off64_t) offsetu64; - ret = sendfile64 (connection->socket_fd, - file_fd, - &offset, - left); - } -#endif /* HAVE_SENDFILE64 */ - if (0 < ret) - { - /* write successful */ -#ifdef EPOLL_SUPPORT - if (left > (uint64_t)ret) - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif /* EPOLL_SUPPORT */ - return ret; - } - err = MHD_socket_get_error_(); -#ifdef EPOLL_SUPPORT - if ( (0 > ret) && (MHD_SCKT_ERR_IS_EAGAIN_(err)) ) - { - /* EAGAIN --- no longer write-ready */ - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; - } -#endif - if (MHD_SCKT_ERR_IS_EINTR_ (err) || - MHD_SCKT_ERR_IS_EAGAIN_ (err)) - return 0; - if (MHD_SCKT_ERR_IS_(err, - MHD_SCKT_EBADF_)) - return -1; - /* 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; - } -#endif - ret = MHD_send_ (connection->socket_fd, - other, - i); - err = MHD_socket_get_error_(); -#ifdef EPOLL_SUPPORT - if (0 > ret) - { - /* EAGAIN --- no longer write-ready */ - if (MHD_SCKT_ERR_IS_EAGAIN_(err)) - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; - } - else if (i > (size_t)ret) - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif - /* Handle broken kernel / libc, returning -1 but not setting errno; - kill connection as that should be safe; reported on mailinglist here: - http://lists.gnu.org/archive/html/libmicrohttpd/2014-10/msg00023.html */ - if ( (0 > ret) && - (0 == err) ) - MHD_socket_set_error_ (MHD_SCKT_ECONNRESET_); - return ret; -} - - -/** * Free resources associated with all closed connections. * (destroy responses, free buffers, etc.). All closed * connections are kept in the "cleanup" doubly-linked list. @@ -2523,14 +2258,10 @@ internal_add_connection (struct MHD_Daemon *daemon, { /* set default connection handlers */ MHD_set_http_callbacks_ (connection); - connection->recv_cls = &recv_param_adapter; - connection->send_cls = &send_param_adapter; } else { #ifdef HTTPS_SUPPORT - connection->recv_cls = &recv_tls_adapter; - connection->send_cls = &send_tls_adapter; connection->state = MHD_TLS_CONNECTION_INIT; MHD_set_https_callbacks (connection); gnutls_init (&connection->tls_session,