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:
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)