libmicrohttpd

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

commit a5fa8a08ef2cf017444cfa476cb3d932c1f6ba74
parent c0ac86b069fac3460fd2e4c7997245c5a1281536
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 17 Feb 2018 04:02:42 +0100

more work on connnection_call_handlers.c

Diffstat:
Asrc/gnutls/handshake.c | 15+++++++++++++++
Msrc/include/microhttpd2.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/microhttpd_tls.h | 5+++++
Msrc/lib/connection_add.c | 6+++---
Msrc/lib/connection_call_handlers.c | 1433++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/lib/connection_cleanup.c | 2+-
Msrc/lib/internal.h | 27++++++++++++++-------------
Msrc/lib/request.c | 2+-
8 files changed, 1520 insertions(+), 23 deletions(-)

diff --git a/src/gnutls/handshake.c b/src/gnutls/handshake.c @@ -0,0 +1,15 @@ + enum MHD_Bool + (*handshake)(void *cls, + struct MHD_TLS_ConnectionState *cs): + + + + if (MHD_TLS_CONN_NO_TLS != connection->tls_state) + { /* HTTPS connection. */ + if (MHD_TLS_CONN_CONNECTED > connection->tls_state) + { + if (! MHD_run_tls_handshake_ (connection)) + return MHD_FALSE; + } + } + return MHD_TRUE; diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h @@ -358,6 +358,23 @@ enum MHD_StatusCode */ MHD_SC_THREAD_TERMINATING = 10003, + /** + * Informational event, state machine status for a connection. + */ + MHD_SC_STATE_MACHINE_STATUS_REPORT = 10004, + + + /** + * MHD is closing a connection after the client closed it + * (perfectly normal end). + */ + MHD_SC_CONNECTION_CLOSED = 20000, + + /** + * MHD is closing a connection because the application + * logic to generate the response data completed. + */ + MHD_SC_APPLICATION_DATA_GENERATION_FINISHED = 20001, /** * Resource limit in terms of number of parallel connections @@ -428,6 +445,36 @@ enum MHD_StatusCode * being closed prematurely. (May be transient.) */ MHD_SC_UPGRADE_FORWARD_INCOMPLETE = 30011, + + /** + * MHD is closing a connection because it was reset. + */ + MHD_SC_CONNECTION_RESET_CLOSED = 30012, + + /** + * MHD is closing a connection because reading the + * request failed. + */ + MHD_SC_CONNECTION_READ_FAIL_CLOSED = 30013, + + /** + * MHD is closing a connection because writing the response failed. + */ + MHD_SC_CONNECTION_WRITE_FAIL_CLOSED = 30014, + + /** + * MHD is closing a connection because the application + * logic to generate the response data failed. + */ + MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED = 30015, + + /** + * We failed to allocate memory for generatig the response from our + * memory pool. Likely the request header was too large to leave + * enough room. + */ + MHD_SC_CONNECTION_POOL_MALLOC_FAILURE = 30016, + /** @@ -735,6 +782,12 @@ enum MHD_StatusCode * while having an upgrade connection still open. */ MHD_SC_SHUTDOWN_WITH_OPEN_UPGRADED_CONNECTION = 50053, + + /** + * Due to an unexpected internal error with the + * state machine, we closed the connection. + */ + MHD_SC_STATEMACHINE_FAILURE_CONNECTION_CLOSED = 50054, }; diff --git a/src/include/microhttpd_tls.h b/src/include/microhttpd_tls.h @@ -92,6 +92,11 @@ struct MHD_TLS_Plugin ...); + enum MHD_Bool + (*handshake)(void *cls, + struct MHD_TLS_ConnectionState *cs); + + ssize_t (*send)(void *cls, struct MHD_TLS_ConnectionState *cs, diff --git a/src/lib/connection_add.c b/src/lib/connection_add.c @@ -732,9 +732,9 @@ internal_add_connection (struct MHD_Daemon *daemon, errno = eno; return MHD_SC_CONNECTION_MALLOC_FAILURE; } - connection->request.pool + connection->pool = MHD_pool_create (daemon->connection_memory_limit_b); - if (NULL == connection->request.pool) + if (NULL == connection->pool) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, @@ -919,7 +919,7 @@ internal_add_connection (struct MHD_Daemon *daemon, daemon->connections_tail, connection); MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - MHD_pool_destroy (connection->request.pool); + MHD_pool_destroy (connection->pool); free (connection); if (0 != eno) errno = eno; diff --git a/src/lib/connection_call_handlers.c b/src/lib/connection_call_handlers.c @@ -23,8 +23,1431 @@ */ #include "internal.h" #include "connection_call_handlers.h" +#include "connection_update_last_activity.h" #include "connection_close.h" +#ifdef MHD_LINUX_SOLARIS_SENDFILE +#include <sys/sendfile.h> +#endif /* MHD_LINUX_SOLARIS_SENDFILE */ +#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE) +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */ + + +/** + * sendfile() chuck size + */ +#define MHD_SENFILE_CHUNK_ (0x20000) + +/** + * sendfile() chuck size for thread-per-connection + */ +#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000) + +#ifdef HAVE_FREEBSD_SENDFILE +#ifdef SF_FLAGS +/** + * FreeBSD sendfile() flags + */ +static int freebsd_sendfile_flags_; + +/** + * FreeBSD sendfile() flags for thread-per-connection + */ +static int freebsd_sendfile_flags_thd_p_c_; +#endif /* SF_FLAGS */ + + +/** + * Initialises static variables. + * + * FIXME: make sure its actually called! + */ +void +MHD_conn_init_static_ (void) +{ +/* FreeBSD 11 and later allow to specify read-ahead size + * and handles SF_NODISKIO differently. + * SF_FLAGS defined only on FreeBSD 11 and later. */ +#ifdef SF_FLAGS + long sys_page_size = sysconf (_SC_PAGESIZE); + if (0 > sys_page_size) + { /* Failed to get page size. */ + freebsd_sendfile_flags_ = SF_NODISKIO; + freebsd_sendfile_flags_thd_p_c_ = SF_NODISKIO; + } + else + { + freebsd_sendfile_flags_ = + SF_FLAGS((uint16_t)(MHD_SENFILE_CHUNK_ / sys_page_size), SF_NODISKIO); + freebsd_sendfile_flags_thd_p_c_ = + SF_FLAGS((uint16_t)(MHD_SENFILE_CHUNK_THR_P_C_ / sys_page_size), SF_NODISKIO); + } +#endif /* SF_FLAGS */ +} +#endif /* HAVE_FREEBSD_SENDFILE */ + + + +/** + * Message to transmit when http 1.1 request is received + */ +#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n" + + +/** + * A serious error occured, close the + * connection (and notify the application). + * + * @param connection connection to close with error + * @param sc the reason for closing the connection + * @param emsg error message (can be NULL) + */ +static void +connection_close_error (struct MHD_Connection *connection, + enum MHD_StatusCode sc, + const char *emsg) +{ +#ifdef HAVE_MESSAGES + if (NULL != emsg) + MHD_DLOG (connection->daemon, + sc, + emsg); +#else /* ! HAVE_MESSAGES */ + (void) emsg; /* Mute compiler warning. */ + (void) sc; +#endif /* ! HAVE_MESSAGES */ + MHD_connection_close_ (connection, + MHD_REQUEST_TERMINATED_WITH_ERROR); +} + + +/** + * Macro to only include error message in call to + * #connection_close_error() if we have HAVE_MESSAGES. + */ +#ifdef HAVE_MESSAGES +#define CONNECTION_CLOSE_ERROR(c, sc, emsg) connection_close_error (c, sc, emsg) +#else +#define CONNECTION_CLOSE_ERROR(c, sc, emsg) connection_close_error (c, sc, NULL) +#endif + + +/** + * Try growing the read buffer. We initially claim half the available + * buffer space for the read buffer (the other half being left for + * management data structures; the write buffer can in the end take + * virtually everything as the read buffer can be reduced to the + * minimum necessary at that point. + * + * @param request the request for which to grow the buffer + * @return true on success, false on failure + */ +static bool +try_grow_read_buffer (struct MHD_Request *request) +{ + struct MHD_Daemon *daemon = request->daemon; + void *buf; + size_t new_size; + + if (0 == request->read_buffer_size) + new_size = daemon->connection_memory_limit_b / 2; + else + new_size = request->read_buffer_size + + daemon->connection_memory_increment_b; + buf = MHD_pool_reallocate (request->connection->pool, + request->read_buffer, + request->read_buffer_size, + new_size); + if (NULL == buf) + return false; + /* we can actually grow the buffer, do it! */ + request->read_buffer = buf; + request->read_buffer_size = new_size; + return true; +} + + +/** + * This function handles a particular request when it has been + * determined that there is data to be read off a socket. + * + * @param request request to handle + */ +static void +MHD_request_handle_read_ (struct MHD_Request *request) +{ + struct MHD_Daemon *daemon = request->daemon; + struct MHD_Connection *connection = request->connection; + ssize_t bytes_read; + + if ( (MHD_REQUEST_CLOSED == request->state) || + (connection->suspended) ) + return; +#ifdef HTTPS_SUPPORT + { + struct MHD_TLS_Plugin *tls; + + if ( (NULL != (tls = daemon->tls_api)) && + (! tls->handshake (tls->cls, + connection->tls_cs)) ) + return; + } +#endif /* HTTPS_SUPPORT */ + + /* make sure "read" has a reasonable number of bytes + in buffer to use per system call (if possible) */ + if (request->read_buffer_offset + + daemon->connection_memory_increment_b > + request->read_buffer_size) + try_grow_read_buffer (request); + + if (request->read_buffer_size == request->read_buffer_offset) + return; /* No space for receiving data. */ + bytes_read = connection->recv_cls (connection, + &request->read_buffer + [request->read_buffer_offset], + request->read_buffer_size - + request->read_buffer_offset); + if (bytes_read < 0) + { + if (MHD_ERR_AGAIN_ == bytes_read) + return; /* No new data to process. */ + if (MHD_ERR_CONNRESET_ == bytes_read) + { + CONNECTION_CLOSE_ERROR (connection, + (MHD_REQUEST_INIT == request->state) + ? MHD_SC_CONNECTION_CLOSED + : MHD_SC_CONNECTION_RESET_CLOSED, + (MHD_REQUEST_INIT == request->state) + ? NULL + : _("Socket disconnected while reading request.\n")); + return; + } + CONNECTION_CLOSE_ERROR (connection, + (MHD_REQUEST_INIT == request->state) + ? MHD_SC_CONNECTION_CLOSED + : MHD_SC_CONNECTION_READ_FAIL_CLOSED, + (MHD_REQUEST_INIT == request->state) + ? NULL + : _("Connection socket is closed due to error when reading request.\n")); + return; + } + + if (0 == bytes_read) + { /* Remote side closed connection. */ + connection->read_closed = true; + MHD_connection_close_ (connection, + MHD_REQUEST_TERMINATED_CLIENT_ABORT); + return; + } + request->read_buffer_offset += bytes_read; + MHD_connection_update_last_activity_ (connection); +#if DEBUG_STATES + MHD_DLOG (daemon, + MHD_SC_STATE_MACHINE_STATUS_REPORT, + _("In function %s handling connection at state: %s\n"), + __FUNCTION__, + MHD_state_to_string (request->state)); +#endif + switch (request->state) + { + case MHD_REQUEST_INIT: + case MHD_REQUEST_URL_RECEIVED: + case MHD_REQUEST_HEADER_PART_RECEIVED: + case MHD_REQUEST_HEADERS_RECEIVED: + case MHD_REQUEST_HEADERS_PROCESSED: + case MHD_REQUEST_CONTINUE_SENDING: + case MHD_REQUEST_CONTINUE_SENT: + case MHD_REQUEST_BODY_RECEIVED: + case MHD_REQUEST_FOOTER_PART_RECEIVED: + /* nothing to do but default action */ + if (connection->read_closed) + { + MHD_connection_close_ (connection, + MHD_REQUEST_TERMINATED_READ_ERROR); + } + return; + case MHD_REQUEST_CLOSED: + return; +#ifdef UPGRADE_SUPPORT + case MHD_REQUEST_UPGRADE: + mhd_assert (0); + return; +#endif /* UPGRADE_SUPPORT */ + default: + /* shrink read buffer to how much is actually used */ + MHD_pool_reallocate (connection->pool, + request->read_buffer, + request->read_buffer_size + 1, + request->read_buffer_offset); + break; + } + return; +} + + +#if defined(_MHD_HAVE_SENDFILE) +/** + * Function for sending responses backed by file FD. + * + * @param connection the MHD connection structure + * @return actual number of bytes sent + */ +static ssize_t +sendfile_adapter (struct MHD_Connection *connection) +{ + struct MHD_Daemon *daemon = connection->daemon; + struct MHD_Request *request = &connection->request; + struct MHD_Response *response = request->response; + ssize_t ret; + const int file_fd = 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 = (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model); + 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 == request->resp_sender); + offsetu64 = request->response_write_position + response->fd_off; + left = response->total_size - request->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()'. */ + request->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 */ + request->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. */ + request->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(). */ + request->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(). */ + request->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 */ + return ret; +} +#endif /* _MHD_HAVE_SENDFILE */ + + +/** + * Check if we are done sending the write-buffer. If so, transition + * into "next_state". + * + * @param connection connection to check write status for + * @param next_state the next state to transition to + * @return false if we are not done, true if we are + */ +static bool +check_write_done (struct MHD_Request *request, + enum MHD_REQUEST_STATE next_state) +{ + if (request->write_buffer_append_offset != + request->write_buffer_send_offset) + return false; + request->write_buffer_append_offset = 0; + request->write_buffer_send_offset = 0; + request->state = next_state; + MHD_pool_reallocate (request->connection->pool, + request->write_buffer, + request->write_buffer_size, + 0); + request->write_buffer = NULL; + request->write_buffer_size = 0; + return true; +} + + +/** + * Prepare the response buffer of this request for sending. Assumes + * that the response mutex is already held. If the transmission is + * complete, this function may close the socket (and return false). + * + * @param request the request handle + * @return false if readying the response failed (the + * lock on the response will have been released already + * in this case). + */ +static bool +try_ready_normal_body (struct MHD_Request *request) +{ + struct MHD_Response *response = request->response; + struct MHD_Connection *connection = request->connection; + ssize_t ret; + + if (NULL == response->crc) + return true; + if ( (0 == response->total_size) || + (request->response_write_position == response->total_size) ) + return true; /* 0-byte response is always ready */ + if ( (response->data_start <= + request->response_write_position) && + (response->data_size + response->data_start > + request->response_write_position) ) + return true; /* response already ready */ +#if defined(_MHD_HAVE_SENDFILE) + if (MHD_resp_sender_sendfile == request->resp_sender) + { + /* will use sendfile, no need to bother response crc */ + return true; + } +#endif /* _MHD_HAVE_SENDFILE */ + + ret = response->crc (response->crc_cls, + request->response_write_position, + response->data, + (size_t) MHD_MIN ((uint64_t) response->data_buffer_size, + response->total_size - + request->response_write_position)); + if ( (((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) || + (((ssize_t) MHD_CONTENT_READER_END_WITH_ERROR) == ret) ) + { + /* either error or http 1.0 transfer, close socket! */ + response->total_size = request->response_write_position; + MHD_mutex_unlock_chk_ (&response->mutex); + if ( ((ssize_t)MHD_CONTENT_READER_END_OF_STREAM) == ret) + MHD_connection_close_ (connection, + MHD_REQUEST_TERMINATED_COMPLETED_OK); + else + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED, + _("Closing connection (application reported error generating data)\n")); + return false; + } + response->data_start = request->response_write_position; + response->data_size = ret; + if (0 == ret) + { + request->state = MHD_REQUEST_NORMAL_BODY_UNREADY; + MHD_mutex_unlock_chk_ (&response->mutex); + return false; + } + return true; +} + + +/** + * Prepare the response buffer of this request for sending. Assumes + * that the response mutex is already held. If the transmission is + * complete, this function may close the socket (and return false). + * + * @param connection the connection + * @return false if readying the response failed + */ +static bool +try_ready_chunked_body (struct MHD_Request *request) +{ + struct MHD_Connection *connection = request->connection; + struct MHD_Response *response = request->response; + struct MHD_Daemon *daemon = request->daemon; + ssize_t ret; + char *buf; + size_t size; + char cbuf[10]; /* 10: max strlen of "%x\r\n" */ + int cblen; + + if (NULL == response->crc) + return true; + if (0 == request->write_buffer_size) + { + size = MHD_MIN (daemon->connection_memory_limit_b, + 2 * (0xFFFFFF + sizeof(cbuf) + 2)); + do + { + size /= 2; + if (size < 128) + { + MHD_mutex_unlock_chk_ (&response->mutex); + /* not enough memory */ + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_CONNECTION_POOL_MALLOC_FAILURE, + _("Closing connection (out of memory)\n")); + return false; + } + buf = MHD_pool_allocate (connection->pool, + size, + MHD_NO); + } + while (NULL == buf); + request->write_buffer_size = size; + request->write_buffer = buf; + } + + if (0 == response->total_size) + ret = 0; /* response must be empty, don't bother calling crc */ + else if ( (response->data_start <= + request->response_write_position) && + (response->data_start + response->data_size > + request->response_write_position) ) + { + /* difference between response_write_position and data_start is less + than data_size which is size_t type, no need to check for overflow */ + const size_t data_write_offset + = (size_t)(request->response_write_position - response->data_start); + /* buffer already ready, use what is there for the chunk */ + ret = response->data_size - data_write_offset; + if ( ((size_t) ret) > request->write_buffer_size - sizeof (cbuf) - 2 ) + ret = request->write_buffer_size - sizeof (cbuf) - 2; + memcpy (&request->write_buffer[sizeof (cbuf)], + &response->data[data_write_offset], + ret); + } + else + { + /* buffer not in range, try to fill it */ + ret = response->crc (response->crc_cls, + request->response_write_position, + &request->write_buffer[sizeof (cbuf)], + request->write_buffer_size - sizeof (cbuf) - 2); + } + if ( ((ssize_t) MHD_CONTENT_READER_END_WITH_ERROR) == ret) + { + /* error, close socket! */ + response->total_size = request->response_write_position; + MHD_mutex_unlock_chk_ (&response->mutex); + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED, + _("Closing connection (application error generating response)\n")); + return false; + } + if ( (((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) || + (0 == response->total_size) ) + { + /* end of message, signal other side! */ + strcpy (request->write_buffer, + "0\r\n"); + request->write_buffer_append_offset = 3; + request->write_buffer_send_offset = 0; + response->total_size = request->response_write_position; + return true; + } + if (0 == ret) + { + request->state = MHD_REQUEST_CHUNKED_BODY_UNREADY; + MHD_mutex_unlock_chk_ (&response->mutex); + return false; + } + if (ret > 0xFFFFFF) + ret = 0xFFFFFF; + cblen = MHD_snprintf_(cbuf, + sizeof (cbuf), + "%X\r\n", + (unsigned int) ret); + mhd_assert(cblen > 0); + mhd_assert((size_t)cblen < sizeof(cbuf)); + memcpy (&request->write_buffer[sizeof (cbuf) - cblen], + cbuf, + cblen); + memcpy (&request->write_buffer[sizeof (cbuf) + ret], + "\r\n", + 2); + request->response_write_position += ret; + request->write_buffer_send_offset = sizeof (cbuf) - cblen; + request->write_buffer_append_offset = sizeof (cbuf) + ret + 2; + return true; +} + + +/** + * This function was created to handle writes to sockets when it has + * been determined that the socket can be written to. + * + * @param request the request to handle + */ +static void +MHD_request_handle_write_ (struct MHD_Request *request) +{ + struct MHD_Daemon *daemon = request->daemon; + struct MHD_Connection *connection = request->connection; + struct MHD_Response *response; + ssize_t ret; + + if (connection->suspended) + return; +#ifdef HTTPS_SUPPORT + { + struct MHD_TLS_Plugin *tls; + + if ( (NULL != (tls = daemon->tls_api)) && + (! tls->handshake (tls->cls, + connection->tls_cs)) ) + return; + } +#endif /* HTTPS_SUPPORT */ + +#if DEBUG_STATES + MHD_DLOG (daemon, + MHD_SC_STATE_MACHINE_STATUS_REPORT, + _("In function %s handling connection at state: %s\n"), + __FUNCTION__, + MHD_state_to_string (request->state)); +#endif + switch (request->state) + { + case MHD_REQUEST_INIT: + case MHD_REQUEST_URL_RECEIVED: + case MHD_REQUEST_HEADER_PART_RECEIVED: + case MHD_REQUEST_HEADERS_RECEIVED: + mhd_assert (0); + return; + case MHD_REQUEST_HEADERS_PROCESSED: + return; + case MHD_REQUEST_CONTINUE_SENDING: + ret = connection->send_cls (connection, + &HTTP_100_CONTINUE + [request->continue_message_write_offset], + MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE) - + request->continue_message_write_offset); + if (ret < 0) + { + if (MHD_ERR_AGAIN_ == ret) + return; +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, + _("Failed to send data in request for %s.\n"), + request->url); +#endif + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, + NULL); + return; + } + request->continue_message_write_offset += ret; + MHD_connection_update_last_activity_ (connection); + return; + case MHD_REQUEST_CONTINUE_SENT: + case MHD_REQUEST_BODY_RECEIVED: + case MHD_REQUEST_FOOTER_PART_RECEIVED: + case MHD_REQUEST_FOOTERS_RECEIVED: + mhd_assert (0); + return; + case MHD_REQUEST_HEADERS_SENDING: + ret = connection->send_cls (connection, + &request->write_buffer + [request->write_buffer_send_offset], + request->write_buffer_append_offset - + request->write_buffer_send_offset); + if (ret < 0) + { + if (MHD_ERR_AGAIN_ == ret) + return; + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, + _("Connection was closed while sending response headers.\n")); + return; + } + request->write_buffer_send_offset += ret; + MHD_connection_update_last_activity_ (connection); + if (MHD_REQUEST_HEADERS_SENDING != request->state) + return; + check_write_done (request, + MHD_REQUEST_HEADERS_SENT); + return; + case MHD_REQUEST_HEADERS_SENT: + return; + case MHD_REQUEST_NORMAL_BODY_READY: + response = request->response; + if (request->response_write_position < + request->response->total_size) + { + uint64_t data_write_offset; + + if (NULL != response->crc) + MHD_mutex_lock_chk_ (&response->mutex); + if (! try_ready_normal_body (request)) + { + /* mutex was already unlocked by try_ready_normal_body */ + return; + } +#if defined(_MHD_HAVE_SENDFILE) + if (MHD_resp_sender_sendfile == request->resp_sender) + { + ret = sendfile_adapter (connection); + } + else +#else /* ! _MHD_HAVE_SENDFILE */ + if (1) +#endif /* ! _MHD_HAVE_SENDFILE */ + { + data_write_offset = request->response_write_position + - response->data_start; + if (data_write_offset > (uint64_t)SIZE_MAX) + MHD_PANIC (_("Data offset exceeds limit")); + ret = connection->send_cls (connection, + &response->data + [(size_t)data_write_offset], + response->data_size - + (size_t)data_write_offset); +#if DEBUG_SEND_DATA + if (ret > 0) + fprintf (stderr, + _("Sent %d-byte DATA response: `%.*s'\n"), + (int) ret, + (int) ret, + &response->data[request->response_write_position - + response->data_start]); +#endif + } + if (NULL != response->crc) + MHD_mutex_unlock_chk_ (&response->mutex); + if (ret < 0) + { + if (MHD_ERR_AGAIN_ == ret) + return; +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, + _("Failed to send data in request for `%s'.\n"), + request->url); +#endif + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, + NULL); + return; + } + request->response_write_position += ret; + MHD_connection_update_last_activity_ (connection); + } + if (request->response_write_position == + request->response->total_size) + request->state = MHD_REQUEST_FOOTERS_SENT; /* have no footers */ + return; + case MHD_REQUEST_NORMAL_BODY_UNREADY: + mhd_assert (0); + return; + case MHD_REQUEST_CHUNKED_BODY_READY: + ret = connection->send_cls (connection, + &request->write_buffer + [request->write_buffer_send_offset], + request->write_buffer_append_offset - + request->write_buffer_send_offset); + if (ret < 0) + { + if (MHD_ERR_AGAIN_ == ret) + return; + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, + _("Connection was closed while sending response body.\n")); + return; + } + request->write_buffer_send_offset += ret; + MHD_connection_update_last_activity_ (connection); + if (MHD_REQUEST_CHUNKED_BODY_READY != request->state) + return; + check_write_done (request, + (request->response->total_size == + request->response_write_position) ? + MHD_REQUEST_BODY_SENT : + MHD_REQUEST_CHUNKED_BODY_UNREADY); + return; + case MHD_REQUEST_CHUNKED_BODY_UNREADY: + case MHD_REQUEST_BODY_SENT: + mhd_assert (0); + return; + case MHD_REQUEST_FOOTERS_SENDING: + ret = connection->send_cls (connection, + &request->write_buffer + [request->write_buffer_send_offset], + request->write_buffer_append_offset - + request->write_buffer_send_offset); + if (ret < 0) + { + if (MHD_ERR_AGAIN_ == ret) + return; + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, + _("Connection was closed while sending response body.\n")); + return; + } + request->write_buffer_send_offset += ret; + MHD_connection_update_last_activity_ (connection); + if (MHD_REQUEST_FOOTERS_SENDING != request->state) + return; + check_write_done (request, + MHD_REQUEST_FOOTERS_SENT); + return; + case MHD_REQUEST_FOOTERS_SENT: + mhd_assert (0); + return; + case MHD_REQUEST_CLOSED: + return; + case MHD_REQUEST_IN_CLEANUP: + mhd_assert (0); + return; +#ifdef UPGRADE_SUPPORT + case MHD_REQUEST_UPGRADE: + mhd_assert (0); + return; +#endif /* UPGRADE_SUPPORT */ + default: + mhd_assert (0); + CONNECTION_CLOSE_ERROR (connection, + MHD_SC_STATEMACHINE_FAILURE_CONNECTION_CLOSED, + _("Internal error\n")); + break; + } + return; +} + + +#if COMMENTED_OUT_FOR_REWRITE + + +/** + * This function was created to handle per-connection processing that + * has to happen even if the socket cannot be read or written to. + * @remark To be called only from thread that process connection's + * recv(), send() and response. + * + * @param connection connection to handle + * @return #MHD_YES if we should continue to process the + * connection (not dead yet), #MHD_NO if it died + */ +int +MHD_connection_handle_idle (struct MHD_Connection *connection) +{ + struct MHD_Daemon *daemon = connection->daemon; + char *line; + size_t line_len; + int ret; + + connection->in_idle = true; + while (! connection->suspended) + { +#ifdef HTTPS_SUPPORT + if (MHD_TLS_CONN_NO_TLS != connection->tls_state) + { /* HTTPS connection. */ + if ((MHD_TLS_CONN_INIT <= connection->tls_state) && + (MHD_TLS_CONN_CONNECTED > connection->tls_state)) + break; + } +#endif /* HTTPS_SUPPORT */ +#if DEBUG_STATES + MHD_DLOG (daemon, + _("In function %s handling connection at state: %s\n"), + __FUNCTION__, + MHD_state_to_string (connection->state)); +#endif + switch (connection->state) + { + case MHD_CONNECTION_INIT: + line = get_next_header_line (connection, + &line_len); + /* Check for empty string, as we might want + to tolerate 'spurious' empty lines; also + NULL means we didn't get a full line yet; + line is not 0-terminated here. */ + if ( (NULL == line) || + (0 == line[0]) ) + { + if (MHD_CONNECTION_INIT != connection->state) + continue; + if (connection->read_closed) + { + CONNECTION_CLOSE_ERROR (connection, + NULL); + continue; + } + break; + } + if (MHD_NO == parse_initial_message_line (connection, + line, + line_len)) + CONNECTION_CLOSE_ERROR (connection, + NULL); + else + connection->state = MHD_CONNECTION_URL_RECEIVED; + continue; + case MHD_CONNECTION_URL_RECEIVED: + line = get_next_header_line (connection, + NULL); + if (NULL == line) + { + if (MHD_CONNECTION_URL_RECEIVED != connection->state) + continue; + if (connection->read_closed) + { + CONNECTION_CLOSE_ERROR (connection, + NULL); + continue; + } + break; + } + if (0 == line[0]) + { + connection->state = MHD_CONNECTION_HEADERS_RECEIVED; + connection->header_size = (size_t) (line - connection->read_buffer); + continue; + } + if (MHD_NO == process_header_line (connection, + line)) + { + transmit_error_response (connection, + MHD_HTTP_BAD_REQUEST, + REQUEST_MALFORMED); + break; + } + connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED; + continue; + case MHD_CONNECTION_HEADER_PART_RECEIVED: + line = get_next_header_line (connection, + NULL); + if (NULL == line) + { + if (connection->state != MHD_CONNECTION_HEADER_PART_RECEIVED) + continue; + if (connection->read_closed) + { + CONNECTION_CLOSE_ERROR (connection, + NULL); + continue; + } + break; + } + if (MHD_NO == + process_broken_line (connection, + line, + MHD_HEADER_KIND)) + continue; + if (0 == line[0]) + { + connection->state = MHD_CONNECTION_HEADERS_RECEIVED; + connection->header_size = (size_t) (line - connection->read_buffer); + continue; + } + continue; + case MHD_CONNECTION_HEADERS_RECEIVED: + parse_connection_headers (connection); + if (MHD_CONNECTION_CLOSED == connection->state) + continue; + connection->state = MHD_CONNECTION_HEADERS_PROCESSED; + if (connection->suspended) + break; + continue; + case MHD_CONNECTION_HEADERS_PROCESSED: + call_connection_handler (connection); /* first call */ + if (MHD_CONNECTION_CLOSED == connection->state) + continue; + if (need_100_continue (connection)) + { + connection->state = MHD_CONNECTION_CONTINUE_SENDING; + if (MHD_NO != socket_flush_possible (connection)) + socket_start_extra_buffering (connection); + else + socket_start_no_buffering (connection); + + break; + } + if ( (NULL != connection->response) && + ( (MHD_str_equal_caseless_ (connection->method, + MHD_HTTP_METHOD_POST)) || + (MHD_str_equal_caseless_ (connection->method, + MHD_HTTP_METHOD_PUT))) ) + { + /* we refused (no upload allowed!) */ + connection->remaining_upload_size = 0; + /* force close, in case client still tries to upload... */ + connection->read_closed = true; + } + connection->state = (0 == connection->remaining_upload_size) + ? MHD_CONNECTION_FOOTERS_RECEIVED : MHD_CONNECTION_CONTINUE_SENT; + if (connection->suspended) + break; + continue; + case MHD_CONNECTION_CONTINUE_SENDING: + if (connection->continue_message_write_offset == + MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE)) + { + connection->state = MHD_CONNECTION_CONTINUE_SENT; + if (MHD_NO != socket_flush_possible (connection)) + socket_start_no_buffering_flush (connection); + else + socket_start_normal_buffering (connection); + + continue; + } + break; + case MHD_CONNECTION_CONTINUE_SENT: + if (0 != connection->read_buffer_offset) + { + process_request_body (connection); /* loop call */ + if (MHD_CONNECTION_CLOSED == connection->state) + continue; + } + if ( (0 == connection->remaining_upload_size) || + ( (MHD_SIZE_UNKNOWN == connection->remaining_upload_size) && + (0 == connection->read_buffer_offset) && + (connection->read_closed) ) ) + { + if ( (connection->have_chunked_upload) && + (! connection->read_closed) ) + connection->state = MHD_CONNECTION_BODY_RECEIVED; + else + connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; + if (connection->suspended) + break; + continue; + } + break; + case MHD_CONNECTION_BODY_RECEIVED: + line = get_next_header_line (connection, + NULL); + if (NULL == line) + { + if (connection->state != MHD_CONNECTION_BODY_RECEIVED) + continue; + if (connection->read_closed) + { + CONNECTION_CLOSE_ERROR (connection, + NULL); + continue; + } + break; + } + if (0 == line[0]) + { + connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; + if (connection->suspended) + break; + continue; + } + if (MHD_NO == process_header_line (connection, + line)) + { + transmit_error_response (connection, + MHD_HTTP_BAD_REQUEST, + REQUEST_MALFORMED); + break; + } + connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED; + continue; + case MHD_CONNECTION_FOOTER_PART_RECEIVED: + line = get_next_header_line (connection, + NULL); + if (NULL == line) + { + if (connection->state != MHD_CONNECTION_FOOTER_PART_RECEIVED) + continue; + if (connection->read_closed) + { + CONNECTION_CLOSE_ERROR (connection, + NULL); + continue; + } + break; + } + if (MHD_NO == + process_broken_line (connection, + line, + MHD_FOOTER_KIND)) + continue; + if (0 == line[0]) + { + connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; + if (connection->suspended) + break; + continue; + } + continue; + case MHD_CONNECTION_FOOTERS_RECEIVED: + call_connection_handler (connection); /* "final" call */ + if (connection->state == MHD_CONNECTION_CLOSED) + continue; + if (NULL == connection->response) + break; /* try again next time */ + if (MHD_NO == build_header_response (connection)) + { + /* oops - close! */ + CONNECTION_CLOSE_ERROR (connection, + _("Closing connection (failed to create response header)\n")); + continue; + } + connection->state = MHD_CONNECTION_HEADERS_SENDING; + if (MHD_NO != socket_flush_possible (connection)) + socket_start_extra_buffering (connection); + else + socket_start_no_buffering (connection); + + break; + case MHD_CONNECTION_HEADERS_SENDING: + /* no default action */ + break; + case MHD_CONNECTION_HEADERS_SENT: + /* Some clients may take some actions right after header receive */ + if (MHD_NO != socket_flush_possible (connection)) + socket_start_no_buffering_flush (connection); + +#ifdef UPGRADE_SUPPORT + if (NULL != connection->response->upgrade_handler) + { + socket_start_normal_buffering (connection); + connection->state = MHD_CONNECTION_UPGRADE; + /* This connection is "upgraded". Pass socket to application. */ + if (MHD_YES != + MHD_response_execute_upgrade_ (connection->response, + connection)) + { + /* upgrade failed, fail hard */ + CONNECTION_CLOSE_ERROR (connection, + NULL); + continue; + } + /* Response is not required anymore for this connection. */ + if (NULL != connection->response) + { + struct MHD_Response * const resp = connection->response; + connection->response = NULL; + MHD_destroy_response (resp); + } + continue; + } +#endif /* UPGRADE_SUPPORT */ + if (MHD_NO != socket_flush_possible (connection)) + socket_start_extra_buffering (connection); + else + socket_start_normal_buffering (connection); + + if (connection->have_chunked_upload) + connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY; + else + connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY; + continue; + case MHD_CONNECTION_NORMAL_BODY_READY: + /* nothing to do here */ + break; + case MHD_CONNECTION_NORMAL_BODY_UNREADY: + if (NULL != connection->response->crc) + MHD_mutex_lock_chk_ (&connection->response->mutex); + if (0 == connection->response->total_size) + { + if (NULL != connection->response->crc) + MHD_mutex_unlock_chk_ (&connection->response->mutex); + connection->state = MHD_CONNECTION_BODY_SENT; + continue; + } + if (try_ready_normal_body (connection)) + { + if (NULL != connection->response->crc) + MHD_mutex_unlock_chk_ (&connection->response->mutex); + connection->state = MHD_CONNECTION_NORMAL_BODY_READY; + /* Buffering for flushable socket was already enabled*/ + if (MHD_NO == socket_flush_possible (connection)) + socket_start_no_buffering (connection); + break; + } + /* mutex was already unlocked by "try_ready_normal_body */ + /* not ready, no socket action */ + break; + case MHD_CONNECTION_CHUNKED_BODY_READY: + /* nothing to do here */ + break; + case MHD_CONNECTION_CHUNKED_BODY_UNREADY: + if (NULL != connection->response->crc) + MHD_mutex_lock_chk_ (&connection->response->mutex); + if ( (0 == connection->response->total_size) || + (connection->response_write_position == + connection->response->total_size) ) + { + if (NULL != connection->response->crc) + MHD_mutex_unlock_chk_ (&connection->response->mutex); + connection->state = MHD_CONNECTION_BODY_SENT; + continue; + } + if (try_ready_chunked_body (connection)) + { + if (NULL != connection->response->crc) + MHD_mutex_unlock_chk_ (&connection->response->mutex); + connection->state = MHD_CONNECTION_CHUNKED_BODY_READY; + /* Buffering for flushable socket was already enabled */ + if (MHD_NO == socket_flush_possible (connection)) + socket_start_no_buffering (connection); + continue; + } + /* mutex was already unlocked by try_ready_chunked_body */ + break; + case MHD_CONNECTION_BODY_SENT: + if (MHD_NO == build_header_response (connection)) + { + /* oops - close! */ + CONNECTION_CLOSE_ERROR (connection, + _("Closing connection (failed to create response header)\n")); + continue; + } + if ( (! connection->have_chunked_upload) || + (connection->write_buffer_send_offset == + connection->write_buffer_append_offset) ) + connection->state = MHD_CONNECTION_FOOTERS_SENT; + else + connection->state = MHD_CONNECTION_FOOTERS_SENDING; + continue; + case MHD_CONNECTION_FOOTERS_SENDING: + /* no default action */ + break; + case MHD_CONNECTION_FOOTERS_SENT: + if (MHD_HTTP_PROCESSING == connection->responseCode) + { + /* After this type of response, we allow sending another! */ + connection->state = MHD_CONNECTION_HEADERS_PROCESSED; + MHD_destroy_response (connection->response); + connection->response = NULL; + /* FIXME: maybe partially reset memory pool? */ + continue; + } + if (MHD_NO != socket_flush_possible (connection)) + socket_start_no_buffering_flush (connection); + else + socket_start_normal_buffering (connection); + + MHD_destroy_response (connection->response); + connection->response = NULL; + if ( (NULL != daemon->notify_completed) && + (connection->client_aware) ) + { + connection->client_aware = false; + daemon->notify_completed (daemon->notify_completed_cls, + connection, + &connection->client_context, + MHD_REQUEST_TERMINATED_COMPLETED_OK); + } + if ( (MHD_CONN_USE_KEEPALIVE != connection->keepalive) || + (connection->read_closed) ) + { + /* have to close for some reason */ + MHD_connection_close_ (connection, + MHD_REQUEST_TERMINATED_COMPLETED_OK); + MHD_pool_destroy (connection->pool); + connection->pool = NULL; + connection->read_buffer = NULL; + connection->read_buffer_size = 0; + connection->read_buffer_offset = 0; + } + else + { + /* can try to keep-alive */ + if (MHD_NO != socket_flush_possible (connection)) + socket_start_normal_buffering (connection); + connection->version = NULL; + connection->state = MHD_CONNECTION_INIT; + connection->last = NULL; + connection->colon = NULL; + connection->header_size = 0; + connection->keepalive = MHD_CONN_KEEPALIVE_UNKOWN; + /* Reset the read buffer to the starting size, + preserving the bytes we have already read. */ + connection->read_buffer + = MHD_pool_reset (connection->pool, + connection->read_buffer, + connection->read_buffer_offset, + connection->daemon->pool_size / 2); + connection->read_buffer_size + = connection->daemon->pool_size / 2; + } + connection->client_aware = false; + connection->client_context = NULL; + connection->continue_message_write_offset = 0; + connection->responseCode = 0; + connection->headers_received = NULL; + connection->headers_received_tail = NULL; + connection->response_write_position = 0; + connection->have_chunked_upload = false; + connection->current_chunk_size = 0; + connection->current_chunk_offset = 0; + connection->method = NULL; + connection->url = NULL; + connection->write_buffer = NULL; + connection->write_buffer_size = 0; + connection->write_buffer_send_offset = 0; + connection->write_buffer_append_offset = 0; + continue; + case MHD_CONNECTION_CLOSED: + cleanup_connection (connection); + connection->in_idle = false; + return MHD_NO; +#ifdef UPGRADE_SUPPORT + case MHD_CONNECTION_UPGRADE: + connection->in_idle = false; + return MHD_YES; /* keep open */ +#endif /* UPGRADE_SUPPORT */ + default: + mhd_assert (0); + break; + } + break; + } + if (! connection->suspended) + { + time_t timeout; + timeout = connection->connection_timeout; + if ( (0 != timeout) && + (timeout < (MHD_monotonic_sec_counter() - connection->last_activity)) ) + { + MHD_connection_close_ (connection, + MHD_REQUEST_TERMINATED_TIMEOUT_REACHED); + connection->in_idle = false; + return MHD_YES; + } + } + MHD_connection_update_event_loop_info (connection); + ret = MHD_YES; +#ifdef EPOLL_SUPPORT + if ( (! connection->suspended) && + (0 != (daemon->options & MHD_USE_EPOLL)) ) + { + ret = MHD_connection_epoll_update_ (connection); + } +#endif /* EPOLL_SUPPORT */ + connection->in_idle = false; + return ret; +} + +// rewrite commented out +#endif + /** * Call the handlers for a connection in the appropriate order based @@ -56,13 +1479,13 @@ MHD_connection_call_handlers_ (struct MHD_Connection *con, if (con->tls_read_ready) read_ready = true; #endif /* HTTPS_SUPPORT */ - if (!force_close) + if (! force_close) { if ( (MHD_EVENT_LOOP_INFO_READ == con->request.event_loop_info) && read_ready) { - MHD_connection_handle_read (con); + MHD_request_handle_read_ (&con->request); ret = MHD_connection_handle_idle (con); states_info_processed = true; } @@ -72,7 +1495,7 @@ MHD_connection_call_handlers_ (struct MHD_Connection *con, con->request.event_loop_info) && write_ready) { - MHD_connection_handle_write (con); + MHD_request_handle_write_ (&con->request); ret = MHD_connection_handle_idle (con); states_info_processed = true; } @@ -104,7 +1527,7 @@ MHD_connection_call_handlers_ (struct MHD_Connection *con, { if (MHD_REQUEST_HEADERS_SENDING == con->request.state) { - MHD_connection_handle_write (con); + MHD_request_handle_write_ (&con->request); /* Always call 'MHD_connection_handle_idle()' after each read/write. */ ret = MHD_connection_handle_idle (con); } @@ -114,7 +1537,7 @@ MHD_connection_call_handlers_ (struct MHD_Connection *con, if ((MHD_REQUEST_NORMAL_BODY_READY == con->request.state) || (MHD_REQUEST_CHUNKED_BODY_READY == con->request.state)) { - MHD_connection_handle_write (con); + MHD_request_handle_write_ (&con->request); ret = MHD_connection_handle_idle (con); } } diff --git a/src/lib/connection_cleanup.c b/src/lib/connection_cleanup.c @@ -92,7 +92,7 @@ MHD_connection_cleanup_ (struct MHD_Daemon *daemon) #ifdef UPGRADE_SUPPORT cleanup_upgraded_connection (pos); #endif /* UPGRADE_SUPPORT */ - MHD_pool_destroy (pos->request.pool); + MHD_pool_destroy (pos->pool); #ifdef HTTPS_SUPPORT { struct MHD_TLS_Plugin *tls; diff --git a/src/lib/internal.h b/src/lib/internal.h @@ -209,8 +209,8 @@ typedef ssize_t * not have to be completed yet). A transition to * #MHD_REQUEST_CLOSED or #MHD_REQUEST_INIT requires the write * to be complete. - */ -enum MHD_REQUEST_STATE + */ +enum MHD_REQUEST_STATE // FIXME: fix capitalization! { /** * Request just started (no headers received). @@ -422,16 +422,6 @@ struct MHD_Request struct MHD_HTTP_Header *headers_received_tail; /** - * The memory pool is created whenever we first read from the TCP - * stream and destroyed at the end of each request (and re-created - * for the next request). In the meantime, this pointer is NULL. - * The pool is used for all request-related data except for the - * response (which maybe shared between requests) and the IP - * address (which persists across individual requests). - */ - struct MemoryPool *pool; // FIXME: keep with connnection! - - /** * We allow the main application to associate some pointer with the * HTTP request, which is passed to each #MHD_AccessHandlerCallback * and some other API calls. Here is where we store it. (MHD does @@ -567,7 +557,8 @@ struct MHD_Request */ uint64_t response_write_position; -#if defined(_MHD_HAVE_SENDFILE) + #if defined(_MHD_HAVE_SENDFILE) + // FIXME: document, fix capitalization! enum MHD_resp_sender_ { MHD_resp_sender_std = 0, @@ -713,6 +704,16 @@ struct MHD_Connection * Reference to the MHD_Daemon struct. */ struct MHD_Daemon *daemon; + + /** + * The memory pool is created whenever we first read from the TCP + * stream and destroyed at the end of each request (and re-created + * for the next request). In the meantime, this pointer is NULL. + * The pool is used for all request-related data except for the + * response (which maybe shared between requests) and the IP + * address (which persists across individual requests). + */ + struct MemoryPool *pool; /** * We allow the main application to associate some pointer with the diff --git a/src/lib/request.c b/src/lib/request.c @@ -100,7 +100,7 @@ MHD_request_set_value (struct MHD_Request *request, { struct MHD_HTTP_Header *pos; - pos = MHD_pool_allocate (request->pool, + pos = MHD_pool_allocate (request->connection->pool, sizeof (struct MHD_HTTP_Header), MHD_YES); if (NULL == pos)