commit e77fce2749a06b448e496140e8ad7e51891972de
parent f6e1ee426f9caebe49476ec88720d87032a433a6
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 9 Feb 2018 05:44:20 +0100
more work on mhd2 api implementation
Diffstat:
25 files changed, 2777 insertions(+), 976 deletions(-)
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2006-2017 Christian Grothoff, Karlson2k (Evgeny Grin)
+ Copyright (C) 2006-2018 Christian Grothoff, Karlson2k (Evgeny Grin)
(and other contributing authors)
This library is free software; you can redistribute it and/or
@@ -23,8 +23,9 @@
* Note that we do not indicate which of the OLD APIs
* simply need to be kept vs. deprecated.
*
+ *
* The goal is to provide a basis for discussion!
- * None of this is implemented yet.
+ * Little of this is implemented yet.
*
* Main goals:
* - simplify application callbacks by splitting header/upload/post
@@ -64,7 +65,13 @@
* supported (include the descriptive string) by using an enum;
* - simplify API for common-case of one-shot responses by
* eliminating need for destroy response in most cases;
+ *
+ * TODO:
+ * - varargs in upgrade is still there and ugly (and not even used!)
+ * - migrate event loop apis (get fdset, timeout, MHD_run(), etc.)
*/
+#ifndef MICROHTTPD2_H
+#define MICROHTTPD2_H
/**
@@ -1018,8 +1025,8 @@ MHD_daemon_tls_key_and_cert_from_memory (struct MHD_Daemon *daemon,
* @return #MHD_SC_OK upon success; TODO: define failure modes
*/
_MHD_EXTERN enum MHD_StatusCode
- MHD_daemon_tls_mem_dhparams (struct MHD_Daemon *daemon,
- const char *dh);
+MHD_daemon_tls_mem_dhparams (struct MHD_Daemon *daemon,
+ const char *dh);
/**
@@ -1356,17 +1363,16 @@ MHD_daemon_digest_auth_nc_length (struct MHD_Daemon *daemon,
/**
- * Generate option to set a custom timeout for the given connection.
- * Specified as the number of seconds. Use zero for no timeout. If
- * timeout was set to zero (or unset) before, setting of a new value
- * by MHD_connection_set_option() will reset timeout timer.
+ * Set custom timeout for the given connection.
+ * Specified as the number of seconds. Use zero for no timeout.
+ * Calling this function will reset timeout timer.
*
* @param connection connection to configure timeout for
* @param timeout_s new timeout in seconds
*/
-_MHD_EXTERN struct MHD_ConnectionOption
-MHD_connection_timeout (struct MHD_Connection *connection,
- unsigned int timeout_s);
+_MHD_EXTERN void
+MHD_connection_set_timeout (struct MHD_Connection *connection,
+ unsigned int timeout_s);
/* **************** Request handling functions ***************** */
@@ -1792,6 +1798,9 @@ struct MHD_UpgradeResponseHandle;
* It allows applications to perform 'special' actions on
* the underlying socket from the upgrade.
*
+ * FIXME: this API still uses the untyped, ugly varargs.
+ * Should we not modernize this one as well?
+ *
* @param urh the handle identifying the connection to perform
* the upgrade @a action on.
* @param operation which operation should be performed
@@ -1987,6 +1996,7 @@ MHD_response_get_header (struct MHD_Response *response,
/* ************Upload and PostProcessor functions ********************** */
+
/**
* Action telling MHD to continue processing the upload.
*
@@ -2493,3 +2503,6 @@ MHD_daemon_get_information_sz (struct MHD_Daemon *daemon,
info_type, \
return_value) \
MHD_daemon_get_information_sz((daemon), (info_type), (return_value), sizeof(union MHD_DaemonInformation));
+
+
+#endif
diff --git a/src/lib/action_continue.c b/src/lib/action_continue.c
@@ -0,0 +1,60 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/action_continue.c
+ * @brief implementation of MHD_action_continue()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * The continue action is being run. Continue
+ * handling the upload.
+ *
+ * @param cls NULL
+ * @param request the request to apply the action to
+ */
+static void
+cont_action (void *cls,
+ struct MHD_Request *request)
+{
+ /* not sure yet, but this function body may
+ just legitimately stay empty... */
+}
+
+
+/**
+ * Action telling MHD to continue processing the upload.
+ *
+ * @return action operation, never NULL
+ */
+struct MHD_Action *
+MHD_action_continue (void)
+{
+ static MHD_Action acont = {
+ .action = &cont_action,
+ .action_cls = NULL
+ };
+
+ return &acont;
+}
+
+/* end of action_continue.c */
diff --git a/src/lib/action_from_response.c b/src/lib/action_from_response.c
@@ -0,0 +1,130 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/action_from_response.c
+ * @brief implementation of #MHD_action_from_response()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * A response was given as the desired action for a @a request.
+ * Queue the response for the request.
+ *
+ * @param cls the `struct MHD_Response`
+ * @param request the request we are processing
+ */
+static void
+response_action (void *cls,
+ struct MHD_Request *request)
+{
+ struct MHD_Response *response = cls;
+ struct MHD_Daemon *daemon = response->daemon;
+
+ if (daemon->shutdown)
+ return MHD_YES; /* If daemon was shut down in parallel,
+ * response will be aborted now or on later stage. */
+
+#ifdef UPGRADE_SUPPORT
+ if ( (NULL != response->upgrade_handler) &&
+ (0 == (daemon->options & MHD_ALLOW_UPGRADE)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Attempted 'upgrade' connection on daemon without MHD_ALLOW_UPGRADE option!\n"));
+#endif
+ return MHD_NO;
+ }
+#endif /* UPGRADE_SUPPORT */
+ request->response = response;
+#if defined(_MHD_HAVE_SENDFILE)
+ if ( (-1 == response->fd)
+#if HTTPS_SUPPORT
+ || (NULL != daemon->tls_api)
+#endif
+ )
+ request->resp_sender = MHD_resp_sender_std;
+ else
+ request->resp_sender = MHD_resp_sender_sendfile;
+#endif /* _MHD_HAVE_SENDFILE */
+
+ if ( ( (NULL != request->method) &&
+ (MHD_str_equal_caseless_ (request->method,
+ MHD_HTTP_METHOD_HEAD)) ) ||
+ (MHD_HTTP_OK > response->status_code) ||
+ (MHD_HTTP_NO_CONTENT == response->status_code) ||
+ (MHD_HTTP_NOT_MODIFIED == response->status_code) )
+ {
+ /* if this is a "HEAD" request, or a status code for
+ which a body is not allowed, pretend that we
+ have already sent the full message body. */
+ request->response_write_position = response->total_size;
+ }
+ if ( (MHD_REQUEST_HEADERS_PROCESSED == request->state) &&
+ (NULL != connection->method) &&
+ ( (MHD_str_equal_caseless_ (request->method,
+ MHD_HTTP_METHOD_POST)) ||
+ (MHD_str_equal_caseless_ (request->method,
+ MHD_HTTP_METHOD_PUT))) )
+ {
+ /* response was queued "early", refuse to read body / footers or
+ further requests! */
+ connection->read_closed = true;
+ request->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ }
+ if (! request->in_idle)
+ (void) MHD_connection_handle_idle (connection);
+}
+
+
+/**
+ * Converts a @a response to an action. If @a consume
+ * is set, the reference to the @a response is consumed
+ * by the conversion. If @a consume is #MHD_NO, then
+ * the response can be converted to actions in the future.
+ * However, the @a response is frozen by this step and
+ * must no longer be modified (i.e. by setting headers).
+ *
+ * @param response response to convert, not NULL
+ * @param destroy_after_use should the response object be consumed?
+ * @return corresponding action, never returns NULL
+ *
+ * Implementation note: internally, this is largely just
+ * a cast (and possibly an RC increment operation),
+ * as a response *is* an action. As no memory is
+ * allocated, this operation cannot fail.
+ */
+_MHD_EXTERN struct MHD_Action *
+MHD_action_from_response (struct MHD_Response *response,
+ enum MHD_bool destroy_after_use)
+{
+ response->action.action = &response_action;
+ response->action.action_cls = response;
+ if (! destroy_after_use)
+ {
+ MHD_mutex_lock_chk_ (&response->mutex);
+ response->reference_count++;
+ MHD_mutex_unlock_chk_ (&response->mutex);
+ }
+ return &response->action;
+}
+
+/* end of action_from_response */
diff --git a/src/lib/action_parse_post.c b/src/lib/action_parse_post.c
@@ -0,0 +1,61 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/action_parse_post.c
+ * @brief implementation of MHD_action_parse_post()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+
+/**
+ * Create an action that parses a POST request.
+ *
+ * This action can be used to (incrementally) parse the data portion
+ * of a POST request. Note that some buggy browsers fail to set the
+ * encoding type. If you want to support those, you may have to call
+ * #MHD_set_connection_value with the proper encoding type before
+ * returning this action (if no supported encoding type is detected,
+ * returning this action will cause a bad request to be returned to
+ * the client).
+ *
+ * @param buffer_size maximum number of bytes to use for
+ * internal buffering (used only for the parsing,
+ * specifically the parsing of the keys). A
+ * tiny value (256-1024) should be sufficient.
+ * Do NOT use a value smaller than 256. For good
+ * performance, use 32 or 64k (i.e. 65536).
+ * @param iter iterator to be called with the parsed data,
+ * Must NOT be NULL.
+ * @param iter_cls first argument to @a iter
+ * @return NULL on error (out of memory, unsupported encoding),
+ * otherwise a PP handle
+ * @ingroup request
+ */
+struct MHD_Action *
+MHD_action_parse_post (size_t buffer_size,
+ MHD_PostDataIterator iter,
+ void *iter_cls)
+{
+ return NULL; /* not yet implemented */
+}
+
+/* end of action_parse_post.c */
diff --git a/src/lib/action_process_upload.c b/src/lib/action_process_upload.c
@@ -0,0 +1,92 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/action_process_upload.c
+ * @brief implementation of MHD_action_process_upload()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Internal details about an upload action.
+ */
+struct UploadAction
+{
+ /**
+ * An upload action is an action. This field
+ * must come first!
+ */
+ struct MHD_Action action;
+
+ MHD_UploadCallback uc;
+
+ void *uc_cls;
+
+};
+
+
+/**
+ * The application wants to process uploaded data for
+ * the given request. Do it!
+ *
+ * @param cls the `struct UploadAction` with the
+ * function we are to call for upload data
+ * @param request the request for which we are to process
+ * upload data
+ */
+static void
+upload_action (void *cls,
+ struct MHD_Request *request)
+{
+ struct UploadAction *ua = cls;
+
+ (void) ua;
+ // FIXME: implement!
+}
+
+
+/**
+ * Create an action that handles an upload.
+ *
+ * @param uc function to call with uploaded data
+ * @param uc_cls closure for @a uc
+ * @return NULL on error (out of memory)
+ * @ingroup action
+ */
+struct MHD_Action *
+MHD_action_process_upload (MHD_UploadCallback uc,
+ void *uc_cls)
+{
+ struct UploadAction *ua;
+
+ if (NULL == (ua = malloc (sizeof (struct UploadAction))))
+ return NULL;
+ ua->action = &upload_action;
+ ua->action_cls = ua;
+ ua->uc = uc;
+ ua->uc_cls = uc_cls;
+ return ua;
+}
+
+
+/* end of action_process_upload.c */
+
+
diff --git a/src/lib/action_suspend.c b/src/lib/action_suspend.c
@@ -0,0 +1,132 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/action_suspend.c
+ * @brief implementation of MHD_action_suspend()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * The suspend action is being run. Suspend handling
+ * of the given request.
+ *
+ * @param cls NULL
+ * @param request the request to apply the action to
+ */
+static void
+suspend_action (void *cls,
+ struct MHD_Request *request)
+{
+ struct MHD_Daemon *daemon = connection->daemon;
+
+ MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
+ if (connection->resuming)
+ {
+ /* suspending again while we didn't even complete resuming yet */
+ connection->resuming = false;
+ MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
+ return;
+ }
+ if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ {
+ if (connection->connection_timeout == daemon->connection_timeout)
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ else
+ XDLL_remove (daemon->manual_timeout_head,
+ daemon->manual_timeout_tail,
+ connection);
+ }
+ DLL_remove (daemon->connections_head,
+ daemon->connections_tail,
+ connection);
+ mhd_assert (! connection->suspended);
+ DLL_insert (daemon->suspended_connections_head,
+ daemon->suspended_connections_tail,
+ connection);
+ connection->suspended = true;
+#ifdef EPOLL_SUPPORT
+ if (MHD_ELS_EPOLL == daemon->event_loop_syscall)
+ {
+ if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
+ {
+ EDLL_remove (daemon->eready_head,
+ daemon->eready_tail,
+ connection);
+ connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
+ }
+ if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET))
+ {
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_DEL,
+ connection->socket_fd,
+ NULL))
+ MHD_PANIC (_("Failed to remove FD from epoll set\n"));
+ connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
+ }
+ connection->epoll_state |= MHD_EPOLL_STATE_SUSPENDED;
+ }
+#endif
+ MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
+}
+
+
+/**
+ * Suspend handling of network data for a given request. This can
+ * be used to dequeue a request from MHD's event loop for a while.
+ *
+ * If you use this API in conjunction with a internal select or a
+ * thread pool, you must set the option #MHD_USE_ITC to
+ * ensure that a resumed request is immediately processed by MHD.
+ *
+ * Suspended requests continue to count against the total number of
+ * requests allowed (per daemon, as well as per IP, if such limits
+ * are set). Suspended requests will NOT time out; timeouts will
+ * restart when the request handling is resumed. While a
+ * request is suspended, MHD will not detect disconnects by the
+ * client.
+ *
+ * The only safe time to suspend a request is from either a
+ * #MHD_RequestHeaderCallback, #MHD_UploadCallback, or a
+ * #MHD_RequestfetchResponseCallback. Suspending a request
+ * at any other time will cause an assertion failure.
+ *
+ * Finally, it is an API violation to call #MHD_daemon_stop() while
+ * having suspended requests (this will at least create memory and
+ * socket leaks or lead to undefined behavior). You must explicitly
+ * resume all requests before stopping the daemon.
+ *
+ * @return action to cause a request to be suspended.
+ */
+struct MHD_Action *
+MHD_action_suspend (void)
+{
+ static MHD_Action suspend = {
+ .action = &suspend_action,
+ .action_cls = NULL
+ };
+
+ return &suspend;
+}
+
+/* end of action_suspend.c */
diff --git a/src/lib/connection_info.c b/src/lib/connection_info.c
@@ -0,0 +1,52 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/connection_info.c
+ * @brief implementation of MHD_connection_get_information_sz()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Obtain information about the given connection.
+ * Use wrapper macro #MHD_connection_get_information() instead of direct use
+ * of this function.
+ *
+ * @param connection what connection to get information about
+ * @param info_type what information is desired?
+ * @param[out] return_value pointer to union where requested information will
+ * be stored
+ * @param return_value_size size of union MHD_ConnectionInformation at compile
+ * time
+ * @return #MHD_YES on success, #MHD_NO on error
+ * (@a info_type is unknown, NULL pointer etc.)
+ * @ingroup specialized
+ */
+enum MHD_Bool
+MHD_connection_get_information_sz (struct MHD_Connection *connection,
+ enum MHD_ConnectionInformationType info_type,
+ union MHD_ConnectionInformation *return_value,
+ size_t return_value_size)
+{
+ return MHD_NO; /* FIXME: not yet implemented */
+}
+
+/* end of connection_info.c */
diff --git a/src/lib/connection_options.c b/src/lib/connection_options.c
@@ -1,16 +1,113 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/connection_options.c
+ * @brief functions to set per-connection options
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
/**
- * Generate option to set a custom timeout for the given connection.
- * Specified as the number of seconds. Use zero for no timeout. If
- * timeout was set to zero (or unset) before, setting of a new value
- * by MHD_connection_set_option() will reset timeout timer.
+ * Set custom timeout for the given connection. Specified as the
+ * number of seconds. Use zero for no timeout. Calling this function
+ * will reset timeout timer.
*
* @param connection connection to configure timeout for
* @param timeout_s new timeout in seconds
*/
-struct MHD_ConnectionOption
-MHD_connection_timeout (struct MHD_Connection *connection,
- unsigned int timeout_s);
+void
+MHD_connection_set_timeout (struct MHD_Connection *connection,
+ unsigned int timeout_s)
+{
+ struct MHD_Daemon *daemon = connection->daemon;
+
+ connection->last_activity = MHD_monotonic_sec_counter();
+ if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
+ {
+ /* Simple case, no need to lock to update DLLs */
+ connection->connection_timeout = (time_t) timeout_s;
+ return;
+ }
+ MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
+ if (! connection->suspended)
+ {
+ if (connection->connection_timeout == daemon->connection_timeout)
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ else
+ XDLL_remove (daemon->manual_timeout_head,
+ daemon->manual_timeout_tail,
+ connection);
+ }
+ connection->connection_timeout = (time_t) timeout_s;
+ if (! connection->suspended)
+ {
+ if (connection->connection_timeout == daemon->connection_timeout)
+ XDLL_insert (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ else
+ XDLL_insert (daemon->manual_timeout_head,
+ daemon->manual_timeout_tail,
+ connection);
+ }
+ MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
+}
+
+
+/**
+ * Update the 'last_activity' field of the connection to the current
+ * time and move the connection to the head of the 'normal_timeout'
+ * list if the timeout for the connection uses the default value.
+ *
+ * @param connection the connection that saw some activity
+ */
+void
+MHD_update_last_activity_ (struct MHD_Connection *connection)
+{
+ struct MHD_Daemon *daemon = connection->daemon;
+
+ if (0 == connection->connection_timeout)
+ return; /* Skip update of activity for connections
+ without timeout timer. */
+ if (connection->suspended)
+ return; /* no activity on suspended connections */
+
+ connection->last_activity = MHD_monotonic_sec_counter();
+ if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
+ return; /* each connection has personal timeout */
+ if (connection->connection_timeout != daemon->connection_timeout)
+ return; /* custom timeout, no need to move it in "normal" DLL */
+ MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
+ /* move connection to head of timeout list (by remove + add operation) */
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ XDLL_insert (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
+}
+/* end of connection_options.c */
diff --git a/src/lib/daemon.c b/src/lib/daemon.c
@@ -1,928 +0,0 @@
-/*
- This file is part of libmicrohttpd
- Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-/**
- * @file lib/daemon.c
- * @brief main functions to start a daemon
- * @author Christian Grothoff
- */
-#include "internal.h"
-
-/* ************************* event loops ********************** */
-
-
-
-/* TODO: migrate! */
-
-
-/* ************* Functions for MHD_daemon_start() ************ */
-
-
-/**
- * Set listen socket options to allow port rebinding (or not)
- * depending on how MHD was configured.
- *
- * @param daemon[in,out] the daemon with the listen socket to configure
- * @return #MHD_SC_OK on success (or non-fatal errors)
- */
-static enum MHD_StatusCode
-configure_listen_reuse (struct MHD_Daemon *daemon)
-{
- const MHD_SCKT_OPT_BOOL_ on = 1;
-
- /* Apply the socket options according to
- listening_address_reuse. */
- /* FIXME: used to be -1/0/1, now defined as a bool!
- MISMATCH! */
- if (0 == daemon->listening_address_reuse)
- {
-#ifndef MHD_WINSOCK_SOCKETS
- /* No user requirement, use "traditional" default SO_REUSEADDR
- * on non-W32 platforms, and do not fail if it doesn't work.
- * Don't use it on W32, because on W32 it will allow multiple
- * bind to the same address:port, like SO_REUSEPORT on others. */
- if (0 > setsockopt (listen_fd,
- SOL_SOCKET,
- SO_REUSEADDR,
- (void*) &on, sizeof (on)))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
- _("setsockopt failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- }
-#endif /* ! MHD_WINSOCK_SOCKETS */
- return MHD_SC_OK;
- }
- if (daemon->listening_address_reuse > 0)
- {
- /* User requested to allow reusing listening address:port. */
-#ifndef MHD_WINSOCK_SOCKETS
- /* Use SO_REUSEADDR on non-W32 platforms, and do not fail if
- * it doesn't work. */
- if (0 > setsockopt (listen_fd,
- SOL_SOCKET,
- SO_REUSEADDR,
- (void*)&on, sizeof (on)))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
- _("setsockopt failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
- }
-#endif /* ! MHD_WINSOCK_SOCKETS */
- /* Use SO_REUSEADDR on Windows and SO_REUSEPORT on most platforms.
- * Fail if SO_REUSEPORT is not defined or setsockopt fails.
- */
- /* SO_REUSEADDR on W32 has the same semantics
- as SO_REUSEPORT on BSD/Linux */
-#if defined(MHD_WINSOCK_SOCKETS) || defined(SO_REUSEPORT)
- if (0 > setsockopt (listen_fd,
- SOL_SOCKET,
-#ifndef MHD_WINSOCK_SOCKETS
- SO_REUSEPORT,
-#else /* MHD_WINSOCK_SOCKETS */
- SO_REUSEADDR,
-#endif /* MHD_WINSOCK_SOCKETS */
- (void *) &on,
- sizeof (on)))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
- _("setsockopt failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
- }
-#else /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */
- /* we're supposed to allow address:port re-use, but
- on this platform we cannot; fail hard */
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED,
- _("Cannot allow listening address reuse: SO_REUSEPORT not defined\n"));
-#endif
- return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED;
-#endif /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */
- }
-
- /* if (daemon->listening_address_reuse < 0) */
- /* User requested to disallow reusing listening address:port.
- * Do nothing except for Windows where SO_EXCLUSIVEADDRUSE
- * is used and Solaris with SO_EXCLBIND.
- * Fail if MHD was compiled for W32 without SO_EXCLUSIVEADDRUSE
- * or setsockopt fails.
- */
-#if (defined(MHD_WINSOCK_SOCKETS) && defined(SO_EXCLUSIVEADDRUSE)) || \
- (defined(__sun) && defined(SO_EXCLBIND))
- if (0 > setsockopt (listen_fd,
- SOL_SOCKET,
-#ifdef SO_EXCLUSIVEADDRUSE
- SO_EXCLUSIVEADDRUSE,
-#else /* SO_EXCLBIND */
- SO_EXCLBIND,
-#endif /* SO_EXCLBIND */
- (void *) &on,
- sizeof (on)))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED,
- _("setsockopt failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED;
- }
-#elif defined(MHD_WINSOCK_SOCKETS) /* SO_EXCLUSIVEADDRUSE not defined on W32? */
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED,
- _("Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined\n"));
-#endif
- return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED;
-#endif /* MHD_WINSOCK_SOCKETS */
- return MHD_SC_OK;
-}
-
-
-/**
- * Open, configure and bind the listen socket (if required).
- *
- * @param daemon[in,out] daemon to open the socket for
- * @return #MHD_SC_OK on success
- */
-static enum MHD_StatusCode
-open_listen_socket (struct MHD_Daemon *daemon)
-{
- enum MHD_StatusCode sc;
- bool usev6;
- socklen_t addrlen;
- struct sockaddr_storage ss;
- const struct sockaddr *sa;
-
- if (MHD_INVALID_SOCKET != daemon->listen_fd)
- return MHD_SC_OK; /* application opened it for us! */
-
- /* Determine address family */
- if (MHD_AF_NONE != daemon->address_family)
- {
- switch (daemon->address_family)
- {
- case MHD_AF_NONE:
- abort ();
- case MHD_AF_AUTO:
-#if HAVE_INET6
- use_v6 = true;
-#else
- use_v6 = false;
-#endif
- break;
- case MHD_AF_INET:
- use_v6 = false;
- break;
- case MHD_AF_INET6:
- case MHD_AF_DUAL:
-#if HAVE_INET6
- use_v6 = true;
- break;
-#else
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD,
- _("IPv6 not supported by this build\n"));
-#endif
- return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
-#endif
- }
- }
- else if (0 != daemon->listen_sa_len)
- {
- /* we have a listen address, get AF from there! */
- switch (daemon->listen_sa.ss_family)
- {
- case AF_INET:
- use_v6 = false;
- break;
-#ifdef AF_INET6
- case AF_INET6:
- use_v6 = true;
- break;
-#endif
-#ifdef AF_UNIX
- case AF_UNIX:
- // FIXME: not implemented
- // (need to change MHD_socket_create_listen_() API!)
-#endif
- default:
- return MHD_SC_AF_NOT_SUPPORTED_BY_BUILD;
- }
- }
- else
- {
- /* no listening desired, that's OK */
- return MHD_SC_OK;
- }
-
- /* try to open listen socket */
- try_open_listen_socket:
- daemon->listen_socket = MHD_socket_create_listen_(use_v6);
- if ( (MHD_INVALID_SOCKET == daemon->listen_socket) &&
- (MHD_AF_AUTO == daemon->address_family) &&
- (use_v6) )
- {
- use_v6 = false;
- goto try_open_listen_socket;
- }
- if (MHD_INVALID_SOCKET == daemon->listen_socket)
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET,
- _("Failed to create socket for listening: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET;
- }
-
- if (MHD_SC_OK !=
- (sc = configure_listen_reuse (daemon)))
- return sc;
-
- /* configure for dual stack (or not) */
- if (use_v6)
- {
-#if defined IPPROTO_IPV6 && defined IPV6_V6ONLY
- /* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see "IPPROTO_IPV6 Socket Options"
- (http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx);
- and may also be missing on older POSIX systems; good luck if you have any of those,
- your IPv6 socket may then also bind against IPv4 anyway... */
- const MHD_SCKT_OPT_BOOL_ v6_only =
- (MHD_AF_INET6 == daemon->address_family);
- if (0 > setsockopt (listen_fd,
- IPPROTO_IPV6,
- IPV6_V6ONLY,
- (const void *) &v6_only,
- sizeof (v6_only)))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_FAILED,
- _("setsockopt failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- }
-#else
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED,
- _("Cannot explicitly setup dual stack behavior on this platform\n"));
-#endif
-#endif
- }
-
- /* Determine address to bind to */
- if (0 != daemon->listen_sa_len)
- {
- /* Bind address explicitly given */
- sa = daemon->listen_sa;
- addrlen = daemon->listen_sa_len;
- }
- else
- {
- /* Compute bind address based on port and AF */
-#if HAVE_INET6
- if (use_v6)
- {
-#ifdef IN6ADDR_ANY_INIT
- static const struct in6_addr static_in6any = IN6ADDR_ANY_INIT;
-#endif
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
-
- addrlen = sizeof (struct sockaddr_in6);
- memset (sin6,
- 0,
- sizeof (struct sockaddr_in6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = htons (daemon->listen_port);
-#ifdef IN6ADDR_ANY_INIT
- sin6->sin6_addr = static_in6any;
-#endif
-#if HAVE_SOCKADDR_IN_SIN_LEN
- sin6->sin6_len = sizeof (struct sockaddr_in6);
-#endif
- }
- else
-#endif
- {
- struct sockaddr_in *sin4 = (struct sockaddr_in *) &ss;
-
- addrlen = sizeof (struct sockaddr_in);
- memset (sin4,
- 0,
- sizeof (struct sockaddr_in));
- sin4->sin_family = AF_INET;
- sin4->sin_port = htons (daemon->listen_port);
- if (0 != INADDR_ANY)
- sin4->sin_addr.s_addr = htonl (INADDR_ANY);
-#if HAVE_SOCKADDR_IN_SIN_LEN
- sin4->sin_len = sizeof (struct sockaddr_in);
-#endif
- }
- sa = (const struct sockaddr *) ss;
- }
-
- /* actually do the bind() */
- if (-1 == bind (daemon->listen_socket,
- sa,
- addrlen))
- {
-#ifdef HAVE_MESSAGES
- unsigned int port = 0;
-
- switch (sa->sa_family)
- {
- case AF_INET:
- if (addrlen == sizeof (struct sockaddr_in))
- port = ntohs (((const struct sockaddr_in*)sa)->sin_port);
- else
- port = UINT16_MAX + 1; /* indicate size error */
- break;
- case AF_INET6:
- if (addrlen == sizeof (struct sockaddr_in6))
- port = ntohs (((const struct sockaddr_in6*)sa)->sin_port);
- else
- port = UINT16_MAX + 1; /* indicate size error */
- break;
- default:
- port = UINT_MAX; /* AF_UNIX? */
- break;
- }
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_SOCKET_BIND_FAILED,
- _("Failed to bind to port %u: %s\n"),
- port,
- MHD_socket_last_strerr_ ());
-#endif
- return MHD_SC_LISTEN_SOCKET_BIND_FAILED;
- }
-
- /* setup TCP_FASTOPEN */
-#ifdef TCP_FASTOPEN
- if (MHD_FOM_DISABLE != daemon->fast_open_method)
- {
- if (0 != setsockopt (daemon->listen_socket,
- IPPROTO_TCP,
- TCP_FASTOPEN,
- &daemon->fastopen_queue_size,
- sizeof (daemon->fastopen_queue_size)))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_FAST_OPEN_FAILURE,
- _("setsockopt failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- if (MHD_FOM_REQUIRE == daemon->fast_open_method)
- return MHD_SC_FAST_OPEN_FAILURE;
- }
- }
-#endif
-
- /* setup listening */
- if (0 > listen (daemon->listen_socket,
- daemon->listen_backlog_size))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_FAILURE,
- _("Failed to listen for connections: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- return MHD_SC_LISTEN_FAILURE;
- }
- return MHD_SC_OK;
-}
-
-
-/**
- * Obtain the listen port number from the socket (if it
- * was not explicitly set by us, i.e. if we were given
- * a listen socket or if the port was 0 and the OS picked
- * a free one).
- *
- * @param daemon[in,out] daemon to obtain the port number for
- */
-static void
-get_listen_port_number (struct MHD_Daemon *daemon)
-{
- struct sockaddr_storage servaddr;
- socklen_t addrlen;
-
- if ( (0 != daemon->port) ||
- (MHD_INVALID_SOCKET == daemon->listen_socket) )
- return; /* nothing to be done */
-
- memset (&servaddr,
- 0,
- sizeof (struct sockaddr_storage));
- addrlen = sizeof (servaddr);
- if (0 != getsockname (daemon->listen_socket,
- (struct sockaddr *) &servaddr,
- &addrlen))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE,
- _("Failed to get listen port number: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif /* HAVE_MESSAGES */
- return;
- }
-#ifdef MHD_POSIX_SOCKETS
- if (sizeof (servaddr) < addrlen)
- {
- /* should be impossible with `struct sockaddr_storage` */
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE,
- _("Failed to get listen port number (`struct sockaddr_storage` too small!?)\n"));
-#endif /* HAVE_MESSAGES */
- return;
- }
-#endif /* MHD_POSIX_SOCKETS */
- switch (servaddr.ss_family)
- {
- case AF_INET:
- {
- struct sockaddr_in *s4 = (struct sockaddr_in *) &servaddr;
-
- daemon->port = ntohs (s4->sin_port);
- break;
- }
-#ifdef HAVE_INET6
- case AF_INET6:
- {
- struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &servaddr;
-
- daemon->port = ntohs(s6->sin6_port);
- break;
- }
-#endif /* HAVE_INET6 */
-#ifdef AF_UNIX
- case AF_UNIX:
- daemon->port = 0; /* special value for UNIX domain sockets */
- break;
-#endif
- default:
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_PORT_INTROSPECTION_UNKNOWN_AF,
- _("Unknown address family!\n"));
-#endif
- daemon->port = 0; /* ugh */
- break;
- }
-}
-
-
-/**
- * Setup epoll() FD for the daemon and initialize it to listen
- * on the listen FD.
- * @remark To be called only from thread that process
- * daemon's select()/poll()/etc.
- *
- * @param daemon daemon to initialize for epoll()
- * @return #MHD_SC_OK on success
- */
-static enum MHD_StatusCode
-setup_epoll_to_listen (struct MHD_Daemon *daemon)
-{
- struct epoll_event event;
- MHD_socket ls;
-
- /* FIXME: update function! */
- daemon->epoll_fd = setup_epoll_fd (daemon);
- if (-1 == daemon->epoll_fd)
- return MHD_NO;
-#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- if (0 != (MHD_ALLOW_UPGRADE & daemon->options))
- {
- daemon->epoll_upgrade_fd = setup_epoll_fd (daemon);
- if (MHD_INVALID_SOCKET == daemon->epoll_upgrade_fd)
- return MHD_NO;
- }
-#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
- if ( (MHD_INVALID_SOCKET == (ls = daemon->listen_fd)) ||
- (daemon->was_quiesced) )
- return MHD_YES; /* non-listening daemon */
- event.events = EPOLLIN;
- event.data.ptr = daemon;
- if (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_ADD,
- ls,
- &event))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _("Call to epoll_ctl failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- return MHD_NO;
- }
- daemon->listen_socket_in_epoll = true;
- if (MHD_ITC_IS_VALID_(daemon->itc))
- {
- event.events = EPOLLIN;
- event.data.ptr = (void *) epoll_itc_marker;
- if (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_ADD,
- MHD_itc_r_fd_ (daemon->itc),
- &event))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _("Call to epoll_ctl failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- return MHD_NO;
- }
- }
- return MHD_SC_OK;
-}
-#endif
-
-
-/**
- * Thread that runs the polling loop until the daemon
- * is explicitly shut down.
- *
- * @param cls `struct MHD_Deamon` to run select loop in a thread for
- * @return always 0 (on shutdown)
- */
-static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
-MHD_polling_thread (void *cls)
-{
- struct MHD_Daemon *daemon = cls;
-
- MHD_thread_init_ (&daemon->pid);
- while (! daemon->shutdown)
- {
- switch (daemon->event_loop_syscall)
- {
- case MHD_ELS_AUTO:
- MHD_PANIC ("MHD_ELS_AUTO should have been mapped to preferred style");
- break;
- case MHD_ELS_SELECT:
- MHD_select (daemon,
- MHD_YES);
- break;
- case MHD_ELS_POLL:
- MHD_poll (daemon,
- MHD_YES);
- break;
- case MHD_ELS_EPOLL:
-#ifdef EPOLL_SUPPORT
- MHD_epoll (daemon,
- MHD_YES);
-#else
- MHD_PANIC ("MHD_ELS_EPOLL not supported, should have failed earlier");
-#endif
- break;
- }
- MHD_cleanup_connections (daemon);
- }
- /* Resume any pending for resume connections, join
- * all connection's threads (if any) and finally cleanup
- * everything. */
- close_all_connections (daemon);
-
- return (MHD_THRD_RTRN_TYPE_)0;
-}
-
-
-/**
- * Setup the thread pool (if needed).
- *
- * @param daemon[in,out] daemon to setup thread pool for
- * @return #MHD_SC_OK on success
- */
-static enum MHD_StatusCode
-setup_thread_pool (struct MHD_Daemon *daemon)
-{
- /* Coarse-grained count of connections per thread (note error
- * due to integer division). Also keep track of how many
- * connections are leftover after an equal split. */
- unsigned int conns_per_thread = daemon->connection_limit
- / daemon->threading_model;
- unsigned int leftover_conns = daemon->connection_limit
- % daemon->threading_model;
- unsigned int i;
- enum MHD_StatusCode sc;
-
- /* Allocate memory for pooled objects */
- daemon->worker_pool = calloc (daemon->threading_model,
- sizeof (struct MHD_Daemon));
- if (NULL == daemon->worker_pool)
- return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
-
- /* Start the workers in the pool */
- for (i = 0; i < daemon->threading_model; i++)
- {
- /* Create copy of the Daemon object for each worker */
- struct MHD_Daemon *d = &daemon->worker_pool[i];
-
- memcpy (d,
- daemon,
- sizeof (struct MHD_Daemon));
- /* Adjust pooling params for worker daemons; note that memcpy()
- has already copied MHD_USE_INTERNAL_POLLING_THREAD thread model into
- the worker threads. */
- d->master = daemon;
- d->worker_pool_size = 0;
- d->worker_pool = NULL;
- /* Divide available connections evenly amongst the threads.
- * Thread indexes in [0, leftover_conns) each get one of the
- * leftover connections. */
- d->connection_limit = conns_per_thread;
- if (i < leftover_conns)
- ++d->connection_limit;
-
- if (! daemon->disable_itc)
- {
- if (! MHD_itc_init_ (d->itc))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_ITC_INITIALIZATION_FAILED,
- _("Failed to create worker inter-thread communication channel: %s\n"),
- MHD_itc_last_strerror_() );
-#endif
- sc = MHD_SC_ITC_INITIALIZATION_FAILED;
- goto thread_failed;
- }
- if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
- (! MHD_SCKT_FD_FITS_FDSET_(MHD_itc_r_fd_ (d->itc),
- NULL)) )
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_ITC_DESCRIPTOR_TOO_LARGE,
- _("File descriptor for inter-thread communication channel exceeds maximum value\n"));
-#endif
- MHD_itc_destroy_chk_ (d->itc);
- sc = MHD_SC_ITC_DESCRIPTOR_TOO_LARGE;
- goto thread_failed;
- }
- }
- else
- {
- MHD_itc_set_invalid_ (d->itc);
- }
-
-#ifdef EPOLL_SUPPORT
- if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
- (MHD_SC_OK != (sc = setup_epoll_to_listen (d))) )
- goto thread_failed;
-#endif
-
- /* Must init cleanup connection mutex for each worker */
- if (! MHD_mutex_init_ (&d->cleanup_connection_mutex))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE,
- _("MHD failed to initialize cleanup connection mutex\n"));
-#endif
- if (! daemon->disable_itc)
- MHD_itc_destroy_chk_ (d->itc);
- sc = MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE;
- goto thread_failed;
- }
-
- /* Spawn the worker thread */
- if (! MHD_create_named_thread_ (&d->pid,
- "MHD-worker",
- daemon->thread_stack_size,
- &MHD_polling_thread,
- d))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_THREAD_POOL_LAUNCH_FAILURE,
- _("Failed to create pool thread: %s\n"),
- MHD_strerror_ (errno));
-#endif
- sc = MHD_SC_THREAD_POOL_LAUNCH_FAILURE;
- /* Free memory for this worker; cleanup below handles
- * all previously-created workers. */
- if (! daemon->disable_itc)
- MHD_itc_destroy_chk_ (d->itc);
- MHD_mutex_destroy_chk_ (&d->cleanup_connection_mutex);
- goto thread_failed;
- }
- } /* end for() */
- return MHD_SC_OK;
-
-thread_failed:
- /* If no worker threads created, then shut down normally. Calling
- MHD_stop_daemon (as we do below) doesn't work here since it
- assumes a 0-sized thread pool means we had been in the default
- MHD_USE_INTERNAL_POLLING_THREAD mode. */
- if (0 == i)
- {
- if (NULL != daemon->worker_pool)
- {
- free (daemon->worker_pool);
- daemon->worker_pool = NULL;
- }
- return MHD_SC_THREAD_LAUNCH_FAILURE;
- }
- /* Shutdown worker threads we've already created. Pretend
- as though we had fully initialized our daemon, but
- with a smaller number of threads than had been
- requested. */
- daemon->worker_pool_size = i;
- daemon->listen_socket = MHD_daemon_quiesce (daemon);
- return MHD_SC_THREAD_LAUNCH_FAILURE;
-}
-
-
-/**
- * Start a webserver.
- *
- * @param daemon daemon to start; you can no longer set
- * options on this daemon after this call!
- * @return #MHD_SC_OK on success
- * @ingroup event
- */
-enum MHD_StatusCode
-MHD_daemon_start (struct MHD_Daemon *daemon)
-{
- enum MHD_StatusCode sc;
-
- if (MHD_ELS_AUTO == daemon->event_loop_syscall)
- {
-#if EPOLL_SUPPORT
- /* We do not support thread-per-connection in combination
- with epoll, so use poll in this case, otherwise prefer
- epoll. */
- if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
- daemon->event_loop_syscall = MHD_ELS_POLL;
- else
- daemon->event_loop_syscall = MHD_ELS_EPOLL;
-#elif HAVE_POLL
- daemon->event_loop_syscall = MHD_ELS_POLL;
-#else
- daemon->event_loop_syscall = MHD_ELS_SELECT;
-#endif
- }
-
-#ifdef EPOLL_SUPPORT
- if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
- (0 == daemon->worker_pool_size) &&
- (MHD_INVALID_SOCKET != daemon->listen_socket) &&
- (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) )
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID,
- _("Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n"));
-#endif
- return MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID;
- }
-#endif
-
- /* Setup ITC */
- if ( (! daemon->disable_itc) &&
- (0 == daemon->worker_pool_size) )
- {
- if (! MHD_itc_init_ (daemon->itc))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_ITC_INITIALIZATION_FAILED,
- _("Failed to create inter-thread communication channel: %s\n"),
- MHD_itc_last_strerror_ ());
-#endif
- return MHD_SC_ITC_INITIALIZATION_FAILED;
- }
- if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
- (! MHD_SCKT_FD_FITS_FDSET_(MHD_itc_r_fd_ (daemon->itc),
- NULL)) )
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_ITC_DESCRIPTOR_TOO_LARGE,
- _("File descriptor for inter-thread communication channel exceeds maximum value\n"));
-#endif
- return MHD_SC_ITC_DESCRIPTOR_TOO_LARGE;
- }
- }
-
- if (MHD_SC_OK != (sc = open_listen_socket (daemon)))
- return sc;
-
- /* Check listen socket is in range (if we are limited) */
- if ( (MHD_INVALID_SOCKET != daemon->listen_socket) &&
- (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
- (! MHD_SCKT_FD_FITS_FDSET_(daemon->listen_socket,
- NULL)) )
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_SOCKET_TOO_LARGE,
- _("Socket descriptor larger than FD_SETSIZE: %d > %d\n"),
- daemon->listen_socket,
- FD_SETSIZE);
-#endif
- return MHD_SC_LISTEN_SOCKET_TOO_LARGE;
- }
-
- /* set listen socket to non-blocking */
- if ( (MHD_INVALID_SOCKET != daemon->listen_socket) &&
- (! MHD_socket_nonblocking_ (daemon->listen_socket)) )
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE,
- _("Failed to set nonblocking mode on listening socket: %s\n"),
- MHD_socket_last_strerr_());
-#endif
- if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) ||
- (daemon->worker_pool_size > 0) )
- {
- /* Accept must be non-blocking. Multiple children may wake
- * up to handle a new connection, but only one will win the
- * race. The others must immediately return. As this is
- * not possible, we must fail hard here. */
- return MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE;
- }
- }
-
-#ifdef EPOLL_SUPPORT
- /* Setup epoll */
- if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
- (0 == daemon->worker_pool_size) &&
- (MHD_INVALID_SOCKET != daemon->listen_socket) &&
- (MHD_SC_OK != (sc = setup_epoll_to_listen (daemon))) )
- return sc;
-#endif
-
- /* Setup main listen thread (only if we have no thread pool or
- external event loop and do have a listen socket) */
- /* FIXME: why no worker thread if we have no listen socket? */
- if ( ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) ||
- (1 == daemon->threading_model) ) &&
- (MHD_INVALID_SOCKET != daemon->listen_socket) &&
- (! MHD_create_named_thread_ (&daemon->pid,
- (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
- ? "MHD-listen"
- : "MHD-single",
- daemon->thread_stack_size,
- &MHD_polling_thread,
- daemon) )
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- MHD_SC_THREAD_MAIN_LAUNCH_FAILURE,
- _("Failed to create listen thread: %s\n"),
- MHD_strerror_ (errno));
-#endif
- return MHD_SC_THREAD_MAIN_LAUNCH_FAILURE;
- }
-
- /* Setup worker threads */
- /* FIXME: why no thread pool if we have no listen socket? */
- if ( (1 < daemon->threading_model) &&
- (MHD_INVALID_SOCKET != daemon->listen_socket) &&
- (MHD_SC_OK != (sc = setup_thread_pool (daemon))) )
- return sc;
-
- return MHD_SC_OK;
-}
-
-
-
-/* end of daemon.c */
diff --git a/src/lib/daemon_info.c b/src/lib/daemon_info.c
@@ -1,20 +1,52 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
/**
- * Obtain information about the given daemon
- * (not fully implemented!).
+ * @file lib/daemon_info.c
+ * @brief implementation of MHD_daemon_get_information_sz()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Obtain information about the given daemon.
+ * Use wrapper macro #MHD_daemon_get_information() instead of direct use
+ * of this function.
*
* @param daemon what daemon to get information about
* @param info_type what information is desired?
- * @param ... depends on @a info_type
- * @return NULL if this information is not available
- * (or if the @a info_type is unknown)
+ * @param[out] return_value pointer to union where requested information will
+ * be stored
+ * @param return_value_size size of union MHD_DaemonInformation at compile
+ * time
+ * @return #MHD_YES on success, #MHD_NO on error
+ * (@a info_type is unknown, NULL pointer etc.)
* @ingroup specialized
*/
-const union MHD_DaemonInfo *
-MHD_get_daemon_info (struct MHD_Daemon *daemon,
- enum MHD_DaemonInfoType info_type,
- ...)
+enum MHD_Bool
+MHD_daemon_get_information_sz (struct MHD_Daemon *daemon,
+ enum MHD_DaemonInformationType info_type,
+ union MHD_DaemonInformation *return_value,
+ size_t return_value_size)
{
+#if OLD
if (NULL == daemon)
return NULL;
switch (info_type)
@@ -55,6 +87,9 @@ MHD_get_daemon_info (struct MHD_Daemon *daemon,
default:
return NULL;
}
+#else
+ return MHD_NO;
+#endif
}
-
+/* end of daemon_info.c */
diff --git a/src/lib/daemon_start.c b/src/lib/daemon_start.c
@@ -0,0 +1,928 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/daemon.c
+ * @brief functions to start a daemon
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+/* ************************* event loops ********************** */
+
+
+
+/* TODO: migrate! */
+
+
+/* ************* Functions for MHD_daemon_start() ************ */
+
+
+/**
+ * Set listen socket options to allow port rebinding (or not)
+ * depending on how MHD was configured.
+ *
+ * @param daemon[in,out] the daemon with the listen socket to configure
+ * @return #MHD_SC_OK on success (or non-fatal errors)
+ */
+static enum MHD_StatusCode
+configure_listen_reuse (struct MHD_Daemon *daemon)
+{
+ const MHD_SCKT_OPT_BOOL_ on = 1;
+
+ /* Apply the socket options according to
+ listening_address_reuse. */
+ /* FIXME: used to be -1/0/1, now defined as a bool!
+ MISMATCH! */
+ if (0 == daemon->listening_address_reuse)
+ {
+#ifndef MHD_WINSOCK_SOCKETS
+ /* No user requirement, use "traditional" default SO_REUSEADDR
+ * on non-W32 platforms, and do not fail if it doesn't work.
+ * Don't use it on W32, because on W32 it will allow multiple
+ * bind to the same address:port, like SO_REUSEPORT on others. */
+ if (0 > setsockopt (listen_fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ (void*) &on, sizeof (on)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ }
+#endif /* ! MHD_WINSOCK_SOCKETS */
+ return MHD_SC_OK;
+ }
+ if (daemon->listening_address_reuse > 0)
+ {
+ /* User requested to allow reusing listening address:port. */
+#ifndef MHD_WINSOCK_SOCKETS
+ /* Use SO_REUSEADDR on non-W32 platforms, and do not fail if
+ * it doesn't work. */
+ if (0 > setsockopt (listen_fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ (void*)&on, sizeof (on)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
+ }
+#endif /* ! MHD_WINSOCK_SOCKETS */
+ /* Use SO_REUSEADDR on Windows and SO_REUSEPORT on most platforms.
+ * Fail if SO_REUSEPORT is not defined or setsockopt fails.
+ */
+ /* SO_REUSEADDR on W32 has the same semantics
+ as SO_REUSEPORT on BSD/Linux */
+#if defined(MHD_WINSOCK_SOCKETS) || defined(SO_REUSEPORT)
+ if (0 > setsockopt (listen_fd,
+ SOL_SOCKET,
+#ifndef MHD_WINSOCK_SOCKETS
+ SO_REUSEPORT,
+#else /* MHD_WINSOCK_SOCKETS */
+ SO_REUSEADDR,
+#endif /* MHD_WINSOCK_SOCKETS */
+ (void *) &on,
+ sizeof (on)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
+ }
+#else /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */
+ /* we're supposed to allow address:port re-use, but
+ on this platform we cannot; fail hard */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED,
+ _("Cannot allow listening address reuse: SO_REUSEPORT not defined\n"));
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED;
+#endif /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */
+ }
+
+ /* if (daemon->listening_address_reuse < 0) */
+ /* User requested to disallow reusing listening address:port.
+ * Do nothing except for Windows where SO_EXCLUSIVEADDRUSE
+ * is used and Solaris with SO_EXCLBIND.
+ * Fail if MHD was compiled for W32 without SO_EXCLUSIVEADDRUSE
+ * or setsockopt fails.
+ */
+#if (defined(MHD_WINSOCK_SOCKETS) && defined(SO_EXCLUSIVEADDRUSE)) || \
+ (defined(__sun) && defined(SO_EXCLBIND))
+ if (0 > setsockopt (listen_fd,
+ SOL_SOCKET,
+#ifdef SO_EXCLUSIVEADDRUSE
+ SO_EXCLUSIVEADDRUSE,
+#else /* SO_EXCLBIND */
+ SO_EXCLBIND,
+#endif /* SO_EXCLBIND */
+ (void *) &on,
+ sizeof (on)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED;
+ }
+#elif defined(MHD_WINSOCK_SOCKETS) /* SO_EXCLUSIVEADDRUSE not defined on W32? */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED,
+ _("Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined\n"));
+#endif
+ return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED;
+#endif /* MHD_WINSOCK_SOCKETS */
+ return MHD_SC_OK;
+}
+
+
+/**
+ * Open, configure and bind the listen socket (if required).
+ *
+ * @param daemon[in,out] daemon to open the socket for
+ * @return #MHD_SC_OK on success
+ */
+static enum MHD_StatusCode
+open_listen_socket (struct MHD_Daemon *daemon)
+{
+ enum MHD_StatusCode sc;
+ bool usev6;
+ socklen_t addrlen;
+ struct sockaddr_storage ss;
+ const struct sockaddr *sa;
+
+ if (MHD_INVALID_SOCKET != daemon->listen_fd)
+ return MHD_SC_OK; /* application opened it for us! */
+
+ /* Determine address family */
+ if (MHD_AF_NONE != daemon->address_family)
+ {
+ switch (daemon->address_family)
+ {
+ case MHD_AF_NONE:
+ abort ();
+ case MHD_AF_AUTO:
+#if HAVE_INET6
+ use_v6 = true;
+#else
+ use_v6 = false;
+#endif
+ break;
+ case MHD_AF_INET:
+ use_v6 = false;
+ break;
+ case MHD_AF_INET6:
+ case MHD_AF_DUAL:
+#if HAVE_INET6
+ use_v6 = true;
+ break;
+#else
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD,
+ _("IPv6 not supported by this build\n"));
+#endif
+ return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
+#endif
+ }
+ }
+ else if (0 != daemon->listen_sa_len)
+ {
+ /* we have a listen address, get AF from there! */
+ switch (daemon->listen_sa.ss_family)
+ {
+ case AF_INET:
+ use_v6 = false;
+ break;
+#ifdef AF_INET6
+ case AF_INET6:
+ use_v6 = true;
+ break;
+#endif
+#ifdef AF_UNIX
+ case AF_UNIX:
+ // FIXME: not implemented
+ // (need to change MHD_socket_create_listen_() API!)
+#endif
+ default:
+ return MHD_SC_AF_NOT_SUPPORTED_BY_BUILD;
+ }
+ }
+ else
+ {
+ /* no listening desired, that's OK */
+ return MHD_SC_OK;
+ }
+
+ /* try to open listen socket */
+ try_open_listen_socket:
+ daemon->listen_socket = MHD_socket_create_listen_(use_v6);
+ if ( (MHD_INVALID_SOCKET == daemon->listen_socket) &&
+ (MHD_AF_AUTO == daemon->address_family) &&
+ (use_v6) )
+ {
+ use_v6 = false;
+ goto try_open_listen_socket;
+ }
+ if (MHD_INVALID_SOCKET == daemon->listen_socket)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET,
+ _("Failed to create socket for listening: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET;
+ }
+
+ if (MHD_SC_OK !=
+ (sc = configure_listen_reuse (daemon)))
+ return sc;
+
+ /* configure for dual stack (or not) */
+ if (use_v6)
+ {
+#if defined IPPROTO_IPV6 && defined IPV6_V6ONLY
+ /* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see "IPPROTO_IPV6 Socket Options"
+ (http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx);
+ and may also be missing on older POSIX systems; good luck if you have any of those,
+ your IPv6 socket may then also bind against IPv4 anyway... */
+ const MHD_SCKT_OPT_BOOL_ v6_only =
+ (MHD_AF_INET6 == daemon->address_family);
+ if (0 > setsockopt (listen_fd,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ (const void *) &v6_only,
+ sizeof (v6_only)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_FAILED,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ }
+#else
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED,
+ _("Cannot explicitly setup dual stack behavior on this platform\n"));
+#endif
+#endif
+ }
+
+ /* Determine address to bind to */
+ if (0 != daemon->listen_sa_len)
+ {
+ /* Bind address explicitly given */
+ sa = daemon->listen_sa;
+ addrlen = daemon->listen_sa_len;
+ }
+ else
+ {
+ /* Compute bind address based on port and AF */
+#if HAVE_INET6
+ if (use_v6)
+ {
+#ifdef IN6ADDR_ANY_INIT
+ static const struct in6_addr static_in6any = IN6ADDR_ANY_INIT;
+#endif
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
+
+ addrlen = sizeof (struct sockaddr_in6);
+ memset (sin6,
+ 0,
+ sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons (daemon->listen_port);
+#ifdef IN6ADDR_ANY_INIT
+ sin6->sin6_addr = static_in6any;
+#endif
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sin6->sin6_len = sizeof (struct sockaddr_in6);
+#endif
+ }
+ else
+#endif
+ {
+ struct sockaddr_in *sin4 = (struct sockaddr_in *) &ss;
+
+ addrlen = sizeof (struct sockaddr_in);
+ memset (sin4,
+ 0,
+ sizeof (struct sockaddr_in));
+ sin4->sin_family = AF_INET;
+ sin4->sin_port = htons (daemon->listen_port);
+ if (0 != INADDR_ANY)
+ sin4->sin_addr.s_addr = htonl (INADDR_ANY);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sin4->sin_len = sizeof (struct sockaddr_in);
+#endif
+ }
+ sa = (const struct sockaddr *) ss;
+ }
+
+ /* actually do the bind() */
+ if (-1 == bind (daemon->listen_socket,
+ sa,
+ addrlen))
+ {
+#ifdef HAVE_MESSAGES
+ unsigned int port = 0;
+
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ if (addrlen == sizeof (struct sockaddr_in))
+ port = ntohs (((const struct sockaddr_in*)sa)->sin_port);
+ else
+ port = UINT16_MAX + 1; /* indicate size error */
+ break;
+ case AF_INET6:
+ if (addrlen == sizeof (struct sockaddr_in6))
+ port = ntohs (((const struct sockaddr_in6*)sa)->sin_port);
+ else
+ port = UINT16_MAX + 1; /* indicate size error */
+ break;
+ default:
+ port = UINT_MAX; /* AF_UNIX? */
+ break;
+ }
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_SOCKET_BIND_FAILED,
+ _("Failed to bind to port %u: %s\n"),
+ port,
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_SOCKET_BIND_FAILED;
+ }
+
+ /* setup TCP_FASTOPEN */
+#ifdef TCP_FASTOPEN
+ if (MHD_FOM_DISABLE != daemon->fast_open_method)
+ {
+ if (0 != setsockopt (daemon->listen_socket,
+ IPPROTO_TCP,
+ TCP_FASTOPEN,
+ &daemon->fastopen_queue_size,
+ sizeof (daemon->fastopen_queue_size)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_FAST_OPEN_FAILURE,
+ _("setsockopt failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ if (MHD_FOM_REQUIRE == daemon->fast_open_method)
+ return MHD_SC_FAST_OPEN_FAILURE;
+ }
+ }
+#endif
+
+ /* setup listening */
+ if (0 > listen (daemon->listen_socket,
+ daemon->listen_backlog_size))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_FAILURE,
+ _("Failed to listen for connections: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_SC_LISTEN_FAILURE;
+ }
+ return MHD_SC_OK;
+}
+
+
+/**
+ * Obtain the listen port number from the socket (if it
+ * was not explicitly set by us, i.e. if we were given
+ * a listen socket or if the port was 0 and the OS picked
+ * a free one).
+ *
+ * @param daemon[in,out] daemon to obtain the port number for
+ */
+static void
+get_listen_port_number (struct MHD_Daemon *daemon)
+{
+ struct sockaddr_storage servaddr;
+ socklen_t addrlen;
+
+ if ( (0 != daemon->port) ||
+ (MHD_INVALID_SOCKET == daemon->listen_socket) )
+ return; /* nothing to be done */
+
+ memset (&servaddr,
+ 0,
+ sizeof (struct sockaddr_storage));
+ addrlen = sizeof (servaddr);
+ if (0 != getsockname (daemon->listen_socket,
+ (struct sockaddr *) &servaddr,
+ &addrlen))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE,
+ _("Failed to get listen port number: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif /* HAVE_MESSAGES */
+ return;
+ }
+#ifdef MHD_POSIX_SOCKETS
+ if (sizeof (servaddr) < addrlen)
+ {
+ /* should be impossible with `struct sockaddr_storage` */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE,
+ _("Failed to get listen port number (`struct sockaddr_storage` too small!?)\n"));
+#endif /* HAVE_MESSAGES */
+ return;
+ }
+#endif /* MHD_POSIX_SOCKETS */
+ switch (servaddr.ss_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *s4 = (struct sockaddr_in *) &servaddr;
+
+ daemon->port = ntohs (s4->sin_port);
+ break;
+ }
+#ifdef HAVE_INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &servaddr;
+
+ daemon->port = ntohs(s6->sin6_port);
+ break;
+ }
+#endif /* HAVE_INET6 */
+#ifdef AF_UNIX
+ case AF_UNIX:
+ daemon->port = 0; /* special value for UNIX domain sockets */
+ break;
+#endif
+ default:
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_PORT_INTROSPECTION_UNKNOWN_AF,
+ _("Unknown address family!\n"));
+#endif
+ daemon->port = 0; /* ugh */
+ break;
+ }
+}
+
+
+/**
+ * Setup epoll() FD for the daemon and initialize it to listen
+ * on the listen FD.
+ * @remark To be called only from thread that process
+ * daemon's select()/poll()/etc.
+ *
+ * @param daemon daemon to initialize for epoll()
+ * @return #MHD_SC_OK on success
+ */
+static enum MHD_StatusCode
+setup_epoll_to_listen (struct MHD_Daemon *daemon)
+{
+ struct epoll_event event;
+ MHD_socket ls;
+
+ /* FIXME: update function! */
+ daemon->epoll_fd = setup_epoll_fd (daemon);
+ if (-1 == daemon->epoll_fd)
+ return MHD_NO;
+#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
+ if (0 != (MHD_ALLOW_UPGRADE & daemon->options))
+ {
+ daemon->epoll_upgrade_fd = setup_epoll_fd (daemon);
+ if (MHD_INVALID_SOCKET == daemon->epoll_upgrade_fd)
+ return MHD_NO;
+ }
+#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
+ if ( (MHD_INVALID_SOCKET == (ls = daemon->listen_fd)) ||
+ (daemon->was_quiesced) )
+ return MHD_YES; /* non-listening daemon */
+ event.events = EPOLLIN;
+ event.data.ptr = daemon;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ ls,
+ &event))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Call to epoll_ctl failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_NO;
+ }
+ daemon->listen_socket_in_epoll = true;
+ if (MHD_ITC_IS_VALID_(daemon->itc))
+ {
+ event.events = EPOLLIN;
+ event.data.ptr = (void *) epoll_itc_marker;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ MHD_itc_r_fd_ (daemon->itc),
+ &event))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _("Call to epoll_ctl failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ return MHD_NO;
+ }
+ }
+ return MHD_SC_OK;
+}
+#endif
+
+
+/**
+ * Thread that runs the polling loop until the daemon
+ * is explicitly shut down.
+ *
+ * @param cls `struct MHD_Deamon` to run select loop in a thread for
+ * @return always 0 (on shutdown)
+ */
+static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
+MHD_polling_thread (void *cls)
+{
+ struct MHD_Daemon *daemon = cls;
+
+ MHD_thread_init_ (&daemon->pid);
+ while (! daemon->shutdown)
+ {
+ switch (daemon->event_loop_syscall)
+ {
+ case MHD_ELS_AUTO:
+ MHD_PANIC ("MHD_ELS_AUTO should have been mapped to preferred style");
+ break;
+ case MHD_ELS_SELECT:
+ MHD_select (daemon,
+ MHD_YES);
+ break;
+ case MHD_ELS_POLL:
+ MHD_poll (daemon,
+ MHD_YES);
+ break;
+ case MHD_ELS_EPOLL:
+#ifdef EPOLL_SUPPORT
+ MHD_epoll (daemon,
+ MHD_YES);
+#else
+ MHD_PANIC ("MHD_ELS_EPOLL not supported, should have failed earlier");
+#endif
+ break;
+ }
+ MHD_cleanup_connections (daemon);
+ }
+ /* Resume any pending for resume connections, join
+ * all connection's threads (if any) and finally cleanup
+ * everything. */
+ close_all_connections (daemon);
+
+ return (MHD_THRD_RTRN_TYPE_)0;
+}
+
+
+/**
+ * Setup the thread pool (if needed).
+ *
+ * @param daemon[in,out] daemon to setup thread pool for
+ * @return #MHD_SC_OK on success
+ */
+static enum MHD_StatusCode
+setup_thread_pool (struct MHD_Daemon *daemon)
+{
+ /* Coarse-grained count of connections per thread (note error
+ * due to integer division). Also keep track of how many
+ * connections are leftover after an equal split. */
+ unsigned int conns_per_thread = daemon->connection_limit
+ / daemon->threading_model;
+ unsigned int leftover_conns = daemon->connection_limit
+ % daemon->threading_model;
+ unsigned int i;
+ enum MHD_StatusCode sc;
+
+ /* Allocate memory for pooled objects */
+ daemon->worker_pool = MHD_calloc_ (daemon->threading_model,
+ sizeof (struct MHD_Daemon));
+ if (NULL == daemon->worker_pool)
+ return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
+
+ /* Start the workers in the pool */
+ for (i = 0; i < daemon->threading_model; i++)
+ {
+ /* Create copy of the Daemon object for each worker */
+ struct MHD_Daemon *d = &daemon->worker_pool[i];
+
+ memcpy (d,
+ daemon,
+ sizeof (struct MHD_Daemon));
+ /* Adjust pooling params for worker daemons; note that memcpy()
+ has already copied MHD_USE_INTERNAL_POLLING_THREAD thread model into
+ the worker threads. */
+ d->master = daemon;
+ d->worker_pool_size = 0;
+ d->worker_pool = NULL;
+ /* Divide available connections evenly amongst the threads.
+ * Thread indexes in [0, leftover_conns) each get one of the
+ * leftover connections. */
+ d->connection_limit = conns_per_thread;
+ if (i < leftover_conns)
+ ++d->connection_limit;
+
+ if (! daemon->disable_itc)
+ {
+ if (! MHD_itc_init_ (d->itc))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_ITC_INITIALIZATION_FAILED,
+ _("Failed to create worker inter-thread communication channel: %s\n"),
+ MHD_itc_last_strerror_() );
+#endif
+ sc = MHD_SC_ITC_INITIALIZATION_FAILED;
+ goto thread_failed;
+ }
+ if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
+ (! MHD_SCKT_FD_FITS_FDSET_(MHD_itc_r_fd_ (d->itc),
+ NULL)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_ITC_DESCRIPTOR_TOO_LARGE,
+ _("File descriptor for inter-thread communication channel exceeds maximum value\n"));
+#endif
+ MHD_itc_destroy_chk_ (d->itc);
+ sc = MHD_SC_ITC_DESCRIPTOR_TOO_LARGE;
+ goto thread_failed;
+ }
+ }
+ else
+ {
+ MHD_itc_set_invalid_ (d->itc);
+ }
+
+#ifdef EPOLL_SUPPORT
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
+ (MHD_SC_OK != (sc = setup_epoll_to_listen (d))) )
+ goto thread_failed;
+#endif
+
+ /* Must init cleanup connection mutex for each worker */
+ if (! MHD_mutex_init_ (&d->cleanup_connection_mutex))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE,
+ _("MHD failed to initialize cleanup connection mutex\n"));
+#endif
+ if (! daemon->disable_itc)
+ MHD_itc_destroy_chk_ (d->itc);
+ sc = MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE;
+ goto thread_failed;
+ }
+
+ /* Spawn the worker thread */
+ if (! MHD_create_named_thread_ (&d->pid,
+ "MHD-worker",
+ daemon->thread_stack_size,
+ &MHD_polling_thread,
+ d))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_THREAD_POOL_LAUNCH_FAILURE,
+ _("Failed to create pool thread: %s\n"),
+ MHD_strerror_ (errno));
+#endif
+ sc = MHD_SC_THREAD_POOL_LAUNCH_FAILURE;
+ /* Free memory for this worker; cleanup below handles
+ * all previously-created workers. */
+ if (! daemon->disable_itc)
+ MHD_itc_destroy_chk_ (d->itc);
+ MHD_mutex_destroy_chk_ (&d->cleanup_connection_mutex);
+ goto thread_failed;
+ }
+ } /* end for() */
+ return MHD_SC_OK;
+
+thread_failed:
+ /* If no worker threads created, then shut down normally. Calling
+ MHD_stop_daemon (as we do below) doesn't work here since it
+ assumes a 0-sized thread pool means we had been in the default
+ MHD_USE_INTERNAL_POLLING_THREAD mode. */
+ if (0 == i)
+ {
+ if (NULL != daemon->worker_pool)
+ {
+ free (daemon->worker_pool);
+ daemon->worker_pool = NULL;
+ }
+ return MHD_SC_THREAD_LAUNCH_FAILURE;
+ }
+ /* Shutdown worker threads we've already created. Pretend
+ as though we had fully initialized our daemon, but
+ with a smaller number of threads than had been
+ requested. */
+ daemon->worker_pool_size = i;
+ daemon->listen_socket = MHD_daemon_quiesce (daemon);
+ return MHD_SC_THREAD_LAUNCH_FAILURE;
+}
+
+
+/**
+ * Start a webserver.
+ *
+ * @param daemon daemon to start; you can no longer set
+ * options on this daemon after this call!
+ * @return #MHD_SC_OK on success
+ * @ingroup event
+ */
+enum MHD_StatusCode
+MHD_daemon_start (struct MHD_Daemon *daemon)
+{
+ enum MHD_StatusCode sc;
+
+ if (MHD_ELS_AUTO == daemon->event_loop_syscall)
+ {
+#if EPOLL_SUPPORT
+ /* We do not support thread-per-connection in combination
+ with epoll, so use poll in this case, otherwise prefer
+ epoll. */
+ if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
+ daemon->event_loop_syscall = MHD_ELS_POLL;
+ else
+ daemon->event_loop_syscall = MHD_ELS_EPOLL;
+#elif HAVE_POLL
+ daemon->event_loop_syscall = MHD_ELS_POLL;
+#else
+ daemon->event_loop_syscall = MHD_ELS_SELECT;
+#endif
+ }
+
+#ifdef EPOLL_SUPPORT
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
+ (0 == daemon->worker_pool_size) &&
+ (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID,
+ _("Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n"));
+#endif
+ return MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID;
+ }
+#endif
+
+ /* Setup ITC */
+ if ( (! daemon->disable_itc) &&
+ (0 == daemon->worker_pool_size) )
+ {
+ if (! MHD_itc_init_ (daemon->itc))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_ITC_INITIALIZATION_FAILED,
+ _("Failed to create inter-thread communication channel: %s\n"),
+ MHD_itc_last_strerror_ ());
+#endif
+ return MHD_SC_ITC_INITIALIZATION_FAILED;
+ }
+ if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
+ (! MHD_SCKT_FD_FITS_FDSET_(MHD_itc_r_fd_ (daemon->itc),
+ NULL)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_ITC_DESCRIPTOR_TOO_LARGE,
+ _("File descriptor for inter-thread communication channel exceeds maximum value\n"));
+#endif
+ return MHD_SC_ITC_DESCRIPTOR_TOO_LARGE;
+ }
+ }
+
+ if (MHD_SC_OK != (sc = open_listen_socket (daemon)))
+ return sc;
+
+ /* Check listen socket is in range (if we are limited) */
+ if ( (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (MHD_ELS_SELECT == daemon->event_loop_syscall) &&
+ (! MHD_SCKT_FD_FITS_FDSET_(daemon->listen_socket,
+ NULL)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_SOCKET_TOO_LARGE,
+ _("Socket descriptor larger than FD_SETSIZE: %d > %d\n"),
+ daemon->listen_socket,
+ FD_SETSIZE);
+#endif
+ return MHD_SC_LISTEN_SOCKET_TOO_LARGE;
+ }
+
+ /* set listen socket to non-blocking */
+ if ( (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (! MHD_socket_nonblocking_ (daemon->listen_socket)) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE,
+ _("Failed to set nonblocking mode on listening socket: %s\n"),
+ MHD_socket_last_strerr_());
+#endif
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) ||
+ (daemon->worker_pool_size > 0) )
+ {
+ /* Accept must be non-blocking. Multiple children may wake
+ * up to handle a new connection, but only one will win the
+ * race. The others must immediately return. As this is
+ * not possible, we must fail hard here. */
+ return MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE;
+ }
+ }
+
+#ifdef EPOLL_SUPPORT
+ /* Setup epoll */
+ if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) &&
+ (0 == daemon->worker_pool_size) &&
+ (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (MHD_SC_OK != (sc = setup_epoll_to_listen (daemon))) )
+ return sc;
+#endif
+
+ /* Setup main listen thread (only if we have no thread pool or
+ external event loop and do have a listen socket) */
+ /* FIXME: why no worker thread if we have no listen socket? */
+ if ( ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) ||
+ (1 == daemon->threading_model) ) &&
+ (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (! MHD_create_named_thread_ (&daemon->pid,
+ (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model)
+ ? "MHD-listen"
+ : "MHD-single",
+ daemon->thread_stack_size,
+ &MHD_polling_thread,
+ daemon) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ MHD_SC_THREAD_MAIN_LAUNCH_FAILURE,
+ _("Failed to create listen thread: %s\n"),
+ MHD_strerror_ (errno));
+#endif
+ return MHD_SC_THREAD_MAIN_LAUNCH_FAILURE;
+ }
+
+ /* Setup worker threads */
+ /* FIXME: why no thread pool if we have no listen socket? */
+ if ( (1 < daemon->threading_model) &&
+ (MHD_INVALID_SOCKET != daemon->listen_socket) &&
+ (MHD_SC_OK != (sc = setup_thread_pool (daemon))) )
+ return sc;
+
+ return MHD_SC_OK;
+}
+
+
+
+/* end of daemon.c */
diff --git a/src/lib/init.c b/src/lib/init.c
@@ -1,3 +1,28 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/init.c
+ * @brief initialization routines
+ * @author Christian Grothoff
+ */
+#include "internal.h"
#include "init.h"
diff --git a/src/lib/init.h b/src/lib/init.h
@@ -1,3 +1,28 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/init.h
+ * @brief functions to initialize library
+ * @author Christian Grothoff
+ */
+#include "internal.h"
#ifndef INIT_H
diff --git a/src/lib/internal.h b/src/lib/internal.h
@@ -562,12 +562,6 @@ struct MHD_Request
enum MHD_RequestEventLoopInfo event_loop_info;
/**
- * HTTP response code. Only valid if response object
- * is already set.
- */
- unsigned int responseCode;
-
- /**
* Did we ever call the "default_handler" on this request? (this
* flag will determine if we call the #MHD_OPTION_NOTIFY_COMPLETED
* handler when the request closes down).
@@ -594,16 +588,6 @@ struct MHD_Request
* be set to #MHD_NO again (before the final call to the handler).
*/
bool have_chunked_upload;
-
- /**
- * Is the request suspended?
- */
- bool suspended;
-
- /**
- * Is the request wanting to resume?
- */
- bool resuming;
};
@@ -660,6 +644,15 @@ struct MHD_Connection
*/
struct MHD_Request request;
+ /**
+ * Is the connection suspended?
+ */
+ bool suspended;
+
+ /**
+ * Is the connection wanting to resume?
+ */
+ bool resuming;
/**
* Set to `true` if the thread has been joined.
@@ -679,8 +672,7 @@ struct MHD_Connection
*/
bool read_closed;
-
- /**
+ /**
* Length of the foreign address.
*/
socklen_t addr_len;
@@ -998,12 +990,166 @@ struct MHD_Daemon
*/
bool allow_address_reuse;
-
+
+};
+
+
+/**
+ * Action function implementing some action to be
+ * performed on a request.
+ *
+ * @param cls action-specfic closure
+ * @param request the request on which the action is to be performed
+ */
+typedef void
+(*ActionCallback) (void *cls,
+ const struct MHD_Request *request);
+
+
+/**
+ * Actions are returned by the application to drive the request
+ * handling of MHD.
+ */
+struct MHD_Action
+{
+
+ /**
+ * Function to call for the action.
+ */
+ ActionCallback action;
+
+ /**
+ * Closure for @a action
+ */
+ void *action_cls;
};
+/**
+ * Representation of an HTTP response.
+ */
+struct MHD_Response
+{
+
+ /**
+ * A response *is* an action. See also
+ * #MHD_action_from_response(). Hence this field
+ * must be the first field in a response!
+ */
+ struct MHD_Action action;
+
+ /**
+ * Headers to send for the response. Initially
+ * the linked list is created in inverse order;
+ * the order should be inverted before sending!
+ */
+ struct MHD_HTTP_Header *first_header;
+
+ /**
+ * Buffer pointing to data that we are supposed
+ * to send as a response.
+ */
+ char *data;
+
+ /**
+ * Closure to give to the content reader @e crc
+ * and content reader free callback @e crfc.
+ */
+ void *crc_cls;
+
+ /**
+ * How do we get more data? NULL if we are
+ * given all of the data up front.
+ */
+ MHD_ContentReaderCallback crc;
+
+ /**
+ * NULL if data must not be freed, otherwise
+ * either user-specified callback or "&free".
+ */
+ MHD_ContentReaderFreeCallback crfc;
+
+ /**
+ * Function to call once MHD is finished with
+ * the request, may be NULL.
+ */
+ MHD_RequestTerminationCallback termination_cb;
+
+ /**
+ * Closure for @e termination_cb.
+ */
+ void *termination_cb_cls;
+
+#ifdef UPGRADE_SUPPORT
+ /**
+ * Application function to call once we are done sending the headers
+ * of the response; NULL unless this is a response created with
+ * #MHD_create_response_for_upgrade().
+ */
+ MHD_UpgradeHandler upgrade_handler;
+
+ /**
+ * Closure for @e uh.
+ */
+ void *upgrade_handler_cls;
+#endif /* UPGRADE_SUPPORT */
+
+ /**
+ * Mutex to synchronize access to @e data, @e size and
+ * @e reference_count.
+ */
+ MHD_mutex_ mutex;
+
+ /**
+ * Set to #MHD_SIZE_UNKNOWN if size is not known.
+ */
+ uint64_t total_size;
+
+ /**
+ * At what offset in the stream is the
+ * beginning of @e data located?
+ */
+ uint64_t data_start;
+
+ /**
+ * Offset to start reading from when using @e fd.
+ */
+ uint64_t fd_off;
+
+ /**
+ * Number of bytes ready in @e data (buffer may be larger
+ * than what is filled with payload).
+ */
+ size_t data_size;
+
+ /**
+ * Size of the data buffer @e data.
+ */
+ size_t data_buffer_size;
+
+ /**
+ * HTTP status code of the response.
+ */
+ enum MHD_HTTP_StatusCode status_code;
+
+ /**
+ * Reference count for this response. Free once the counter hits
+ * zero.
+ */
+ unsigned int reference_count;
+ /**
+ * File-descriptor if this response is FD-backed.
+ */
+ int fd;
+
+ /**
+ * Only respond in HTTP 1.0 mode.
+ */
+ bool v10_only;
+
+};
diff --git a/src/lib/panic.c b/src/lib/panic.c
@@ -1,3 +1,28 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/panic.c
+ * @brief functions to panic (abort)
+ * @author Christian Grothoff
+ */
+#include "internal.h"
/**
diff --git a/src/lib/request_info.c b/src/lib/request_info.c
@@ -0,0 +1,53 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/request_info.c
+ * @brief implementation of MHD_request_get_information_sz()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Obtain information about the given request.
+ * Use wrapper macro #MHD_request_get_information() instead of direct use
+ * of this function.
+ *
+ * @param request what request to get information about
+ * @param info_type what information is desired?
+ * @param[out] return_value pointer to union where requested information will
+ * be stored
+ * @param return_value_size size of union MHD_RequestInformation at compile
+ * time
+ * @return #MHD_YES on success, #MHD_NO on error
+ * (@a info_type is unknown, NULL pointer etc.)
+ * @ingroup specialized
+ */
+enum MHD_Bool
+MHD_request_get_information_sz (struct MHD_Request *request,
+ enum MHD_RequestInformationType info_type,
+ union MHD_RequestInformation *return_value,
+ size_t return_value_size)
+{
+ return MHD_NO; /* not implemented */
+}
+
+
+/* end of request_info.c */
diff --git a/src/lib/request_resume.c b/src/lib/request_resume.c
@@ -0,0 +1,48 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/request_resume.c
+ * @brief implementation of MHD_request_resume()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Resume handling of network data for suspended request. It is
+ * safe to resume a suspended request at any time. Calling this
+ * function on a request that was not previously suspended will
+ * result in undefined behavior.
+ *
+ * If you are using this function in ``external'' select mode, you must
+ * make sure to run #MHD_run() afterwards (before again calling
+ * #MHD_get_fdset(), as otherwise the change may not be reflected in
+ * the set returned by #MHD_get_fdset() and you may end up with a
+ * request that is stuck until the next network activity.
+ *
+ * @param request the request to resume
+ */
+void
+MHD_request_resume (struct MHD_Request *request)
+{
+ abort (); // not implemented...
+}
+
+/* end of request_resume.c */
diff --git a/src/lib/response.c b/src/lib/response.c
@@ -0,0 +1,264 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/response.c
+ * @brief implementation of general response functions
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
+ */
+#include "internal.h"
+
+
+/**
+ * Add a header or footer line to the response.
+ *
+ * @param response response to add a header to
+ * @param kind header or footer
+ * @param header the header to add
+ * @param content value to add
+ * @return false on error (i.e. invalid header or content format).
+ */
+static bool
+add_response_entry (struct MHD_Response *response,
+ enum MHD_ValueKind kind,
+ const char *header,
+ const char *content)
+{
+ struct MHD_HTTP_Header *hdr;
+
+ if ( (NULL == header) ||
+ (NULL == content) ||
+ (0 == header[0]) ||
+ (0 == content[0]) ||
+ (NULL != strchr (header, '\t')) ||
+ (NULL != strchr (header, '\r')) ||
+ (NULL != strchr (header, '\n')) ||
+ (NULL != strchr (content, '\t')) ||
+ (NULL != strchr (content, '\r')) ||
+ (NULL != strchr (content, '\n')) )
+ return false;
+ if (NULL == (hdr = malloc (sizeof (struct MHD_HTTP_Header))))
+ return false;
+ if (NULL == (hdr->header = strdup (header)))
+ {
+ free (hdr);
+ return false;
+ }
+ if (NULL == (hdr->value = strdup (content)))
+ {
+ free (hdr->header);
+ free (hdr);
+ return false;
+ }
+ hdr->kind = kind;
+ hdr->next = response->first_header;
+ response->first_header = hdr;
+ return true;
+}
+
+
+/**
+ * Explicitly decrease reference counter of a response object. If the
+ * counter hits zero, destroys a response object and associated
+ * resources. Usually, this is implicitly done by converting a
+ * response to an action and returning the action to MHD.
+ *
+ * @param response response to decrement RC of
+ * @ingroup response
+ */
+void
+MHD_response_queue_for_destroy (struct MHD_Response *response)
+{
+ struct MHD_HTTP_Header *pos;
+
+ MHD_mutex_lock_chk_ (&response->mutex);
+ if (0 != --(response->reference_count))
+ {
+ MHD_mutex_unlock_chk_ (&response->mutex);
+ return;
+ }
+ MHD_mutex_unlock_chk_ (&response->mutex);
+ MHD_mutex_destroy_chk_ (&response->mutex);
+ if (NULL != response->crfc)
+ response->crfc (response->crc_cls);
+ while (NULL != response->first_header)
+ {
+ pos = response->first_header;
+ response->first_header = pos->next;
+ free (pos->header);
+ free (pos->value);
+ free (pos);
+ }
+ free (response);
+}
+
+
+/**
+ * Add a header line to the response.
+ *
+ * @param response response to add a header to
+ * @param header the header to add
+ * @param content value to add
+ * @return #MHD_NO on error (i.e. invalid header or content format),
+ * or out of memory
+ * @ingroup response
+ */
+enum MHD_Bool
+MHD_response_add_header (struct MHD_Response *response,
+ const char *header,
+ const char *content)
+{
+ return add_response_entry (response,
+ MHD_HEADER_KIND,
+ header,
+ content) ? MHD_TRUE : MHD_FALSE;
+}
+
+
+/**
+ * Add a tailer line to the response.
+ *
+ * @param response response to add a footer to
+ * @param footer the footer to add
+ * @param content value to add
+ * @return #MHD_NO on error (i.e. invalid footer or content format),
+ * or out of memory
+ * @ingroup response
+ */
+enum MHD_Bool
+MHD_response_add_trailer (struct MHD_Response *response,
+ const char *footer,
+ const char *content)
+{
+ return add_response_entry (response,
+ MHD_FOOTER_KIND,
+ footer,
+ content) ? MHD_TRUE : MHD_FALSE;
+}
+
+
+/**
+ * Delete a header (or footer) line from the response.
+ *
+ * @param response response to remove a header from
+ * @param header the header to delete
+ * @param content value to delete
+ * @return #MHD_NO on error (no such header known)
+ * @ingroup response
+ */
+enum MHD_Bool
+MHD_response_del_header (struct MHD_Response *response,
+ const char *header,
+ const char *content)
+{
+ struct MHD_HTTP_Header *pos;
+ struct MHD_HTTP_Header *prev;
+
+ if ( (NULL == header) ||
+ (NULL == content) )
+ return MHD_NO;
+ prev = NULL;
+ pos = response->first_header;
+ while (NULL != pos)
+ {
+ if ((0 == strcmp (header,
+ pos->header)) &&
+ (0 == strcmp (content,
+ pos->value)))
+ {
+ free (pos->header);
+ free (pos->value);
+ if (NULL == prev)
+ response->first_header = pos->next;
+ else
+ prev->next = pos->next;
+ free (pos);
+ return MHD_YES;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+ return MHD_NO;
+}
+
+
+/**
+ * Get all of the headers (and footers) added to a response.
+ *
+ * @param response response to query
+ * @param iterator callback to call on each header;
+ * maybe NULL (then just count headers)
+ * @param iterator_cls extra argument to @a iterator
+ * @return number of entries iterated over
+ * @ingroup response
+ */
+unsigned int
+MHD_response_get_headers (struct MHD_Response *response,
+ MHD_KeyValueIterator iterator,
+ void *iterator_cls)
+{
+ unsigned int numHeaders = 0;
+ struct MHD_HTTP_Header *pos;
+
+ for (pos = response->first_header;
+ NULL != pos;
+ pos = pos->next)
+ {
+ numHeaders++;
+ if ( (NULL != iterator) &&
+ (MHD_YES != iterator (iterator_cls,
+ pos->kind,
+ pos->header,
+ pos->value)) )
+ break;
+ }
+ return numHeaders;
+}
+
+
+/**
+ * Get a particular header (or footer) from the response.
+ *
+ * @param response response to query
+ * @param key which header to get
+ * @return NULL if header does not exist
+ * @ingroup response
+ */
+const char *
+MHD_response_get_header (struct MHD_Response *response,
+ const char *key)
+{
+ struct MHD_HTTP_Header *pos;
+
+ if (NULL == key)
+ return NULL;
+ for (pos = response->first_header;
+ NULL != pos;
+ pos = pos->next)
+ {
+ if (MHD_str_equal_caseless_ (pos->header,
+ key))
+ return pos->value;
+ }
+ return NULL;
+}
+
+/* end of response.c */
diff --git a/src/lib/response_for_upgrade.c b/src/lib/response_for_upgrade.c
@@ -0,0 +1,90 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/response_for_upgrade.c
+ * @brief implementation of MHD_response_for_upgrade()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Create a response object that can be used for 101 UPGRADE
+ * responses, for example to implement WebSockets. After sending the
+ * response, control over the data stream is given to the callback (which
+ * can then, for example, start some bi-directional communication).
+ * If the response is queued for multiple connections, the callback
+ * will be called for each connection. The callback
+ * will ONLY be called after the response header was successfully passed
+ * to the OS; if there are communication errors before, the usual MHD
+ * connection error handling code will be performed.
+ *
+ * MHD will automatically set the correct HTTP status
+ * code (#MHD_HTTP_SWITCHING_PROTOCOLS).
+ * Setting correct HTTP headers for the upgrade must be done
+ * manually (this way, it is possible to implement most existing
+ * WebSocket versions using this API; in fact, this API might be useful
+ * for any protocol switch, not just WebSockets). Note that
+ * draft-ietf-hybi-thewebsocketprotocol-00 cannot be implemented this
+ * way as the header "HTTP/1.1 101 WebSocket Protocol Handshake"
+ * cannot be generated; instead, MHD will always produce "HTTP/1.1 101
+ * Switching Protocols" (if the response code 101 is used).
+ *
+ * As usual, the response object can be extended with header
+ * information and then be used any number of times (as long as the
+ * header information is not connection-specific).
+ *
+ * @param upgrade_handler function to call with the "upgraded" socket
+ * @param upgrade_handler_cls closure for @a upgrade_handler
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+struct MHD_Response *
+MHD_response_for_upgrade (MHD_UpgradeHandler upgrade_handler,
+ void *upgrade_handler_cls)
+{
+ struct MHD_Response *response;
+
+ mhd_assert (NULL != upgrade_handler);
+ response = MHD_calloc_ (1,
+ sizeof (struct MHD_Response));
+ if (NULL == response)
+ return NULL;
+ if (! MHD_mutex_init_ (&response->mutex))
+ {
+ free (response);
+ return NULL;
+ }
+ response->upgrade_handler = upgrade_handler;
+ response->upgrade_handler_cls = upgrade_handler_cls;
+ response->status_code = MHD_HTTP_SWITCHING_PROTOCOLS;
+ response->total_size = MHD_SIZE_UNKNOWN;
+ response->reference_count = 1;
+ if (MHD_NO ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONNECTION,
+ "Upgrade"))
+ {
+ MHD_destroy_response (response);
+ return NULL;
+ }
+ return response;
+}
+
+/* end of response_for_upgrade.c */
diff --git a/src/lib/response_from_buffer.c b/src/lib/response_from_buffer.c
@@ -0,0 +1,88 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/response_from_buffer.c
+ * @brief implementation of MHD_response_from_buffer()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Create a response object. The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param sc status code to use for the response;
+ * #MHD_HTTP_NO_CONTENT is only valid if @a size is 0;
+ * @param size size of the data portion of the response
+ * @param buffer size bytes containing the response's data portion
+ * @param mode flags for buffer management
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+struct MHD_Response *
+MHD_response_from_buffer (enum MHD_HTTP_StatusCode sc,
+ size_t size,
+ void *buffer,
+ enum MHD_ResponseMemoryMode mode)
+{
+ struct MHD_Response *response;
+ void *tmp;
+
+ mhd_assert ( (NULL != data) ||
+ (0 == size) );
+ if (NULL ==
+ (response = MHD_calloc_ (1,
+ sizeof (struct MHD_Response))))
+ return NULL;
+ response->fd = -1;
+ if (! MHD_mutex_init_ (&response->mutex))
+ {
+ free (response);
+ return NULL;
+ }
+ if ( (MHD_RESPMEM_MUST_COPY == mode) &&
+ (size > 0) )
+ {
+ if (NULL == (tmp = malloc (size)))
+ {
+ MHD_mutex_destroy_chk_ (&response->mutex);
+ free (response);
+ return NULL;
+ }
+ memcpy (tmp,
+ data,
+ size);
+ data = tmp;
+ }
+ if (MHD_RESPMEM_PERSISTENT != mode)
+ {
+ response->crfc = &free;
+ response->crc_cls = data;
+ }
+ response->status_code = sc;
+ response->reference_count = 1;
+ response->total_size = size;
+ response->data = data;
+ response->data_size = size;
+ return response;
+}
+
+/* end of response_from_buffer.c */
diff --git a/src/lib/response_from_callback.c b/src/lib/response_from_callback.c
@@ -0,0 +1,80 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/response_from_callback.c
+ * @brief implementation of MHD_response_from_callback()
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Create a response action. The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param sc status code to return
+ * @param size size of the data portion of the response, #MHD_SIZE_UNKNOWN for unknown
+ * @param block_size preferred block size for querying crc (advisory only,
+ * MHD may still call @a crc using smaller chunks); this
+ * is essentially the buffer size used for IO, clients
+ * should pick a value that is appropriate for IO and
+ * memory performance requirements
+ * @param crc callback to use to obtain response data
+ * @param crc_cls extra argument to @a crc
+ * @param crfc callback to call to free @a crc_cls resources
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+struct MHD_Response *
+MHD_response_from_callback (enum MHD_HTTP_StatusCode sc,
+ uint64_t size,
+ size_t block_size,
+ MHD_ContentReaderCallback crc,
+ void *crc_cls,
+ MHD_ContentReaderFreeCallback crfc)
+{
+ struct MHD_Response *response;
+
+ mhd_assert (NULL != crc);
+ mhd_assert (0 != block_size);
+ if (NULL ==
+ (response = MHD_calloc_ (1,
+ sizeof (struct MHD_Response) +
+ block_size)))
+ return NULL;
+ response->fd = -1;
+ response->status_code = sc;
+ response->data = (void *) &response[1];
+ response->data_buffer_size = block_size;
+ if (! MHD_mutex_init_ (&response->mutex))
+ {
+ free (response);
+ return NULL;
+ }
+ response->crc = crc;
+ response->crfc = crfc;
+ response->crc_cls = crc_cls;
+ response->reference_count = 1;
+ response->total_size = size;
+ return response;
+}
+
+
+/* end of response_from_callback.c */
diff --git a/src/lib/response_from_fd.c b/src/lib/response_from_fd.c
@@ -0,0 +1,198 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/response_from_fd.c
+ * @brief implementation of MHD_response_from_fd()
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
+ */
+#include "internal.h"
+
+
+/**
+ * Given a file descriptor, read data from the file
+ * to generate the response.
+ *
+ * @param cls pointer to the response
+ * @param pos offset in the file to access
+ * @param buf where to write the data
+ * @param max number of bytes to write at most
+ * @return number of bytes written
+ */
+static ssize_t
+file_reader (void *cls,
+ uint64_t pos,
+ char *buf,
+ size_t max)
+{
+ struct MHD_Response *response = cls;
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ ssize_t n;
+#else /* _WIN32 && !__CYGWIN__ */
+ const HANDLE fh = (HANDLE) _get_osfhandle (response->fd);
+#endif /* _WIN32 && !__CYGWIN__ */
+ const int64_t offset64 = (int64_t)(pos + response->fd_off);
+
+ if (offset64 < 0)
+ return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ if (max > SSIZE_MAX)
+ max = SSIZE_MAX; /* Clamp to maximum return value. */
+
+#if defined(HAVE_PREAD64)
+ n = pread64 (response->fd,
+ buf,
+ max,
+ offset64);
+#elif defined(HAVE_PREAD)
+ if ( (sizeof(off_t) < sizeof (uint64_t)) &&
+ (offset64 > (uint64_t)INT32_MAX) )
+ return MHD_CONTENT_READER_END_WITH_ERROR; /* Read at required position is not possible. */
+
+ n = pread (response->fd,
+ buf,
+ max,
+ (off_t) offset64);
+#else /* ! HAVE_PREAD */
+#if defined(HAVE_LSEEK64)
+ if (lseek64 (response->fd,
+ offset64,
+ SEEK_SET) != offset64)
+ return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */
+#else /* ! HAVE_LSEEK64 */
+ if ( (sizeof(off_t) < sizeof (uint64_t)) &&
+ (offset64 > (uint64_t)INT32_MAX) )
+ return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */
+
+ if (lseek (response->fd,
+ (off_t) offset64,
+ SEEK_SET) != (off_t) offset64)
+ return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */
+#endif /* ! HAVE_LSEEK64 */
+ n = read (response->fd,
+ buf,
+ max);
+
+#endif /* ! HAVE_PREAD */
+ if (0 == n)
+ return MHD_CONTENT_READER_END_OF_STREAM;
+ if (n < 0)
+ return MHD_CONTENT_READER_END_WITH_ERROR;
+ return n;
+#else /* _WIN32 && !__CYGWIN__ */
+ if (INVALID_HANDLE_VALUE == fh)
+ return MHD_CONTENT_READER_END_WITH_ERROR; /* Value of 'response->fd' is not valid. */
+ else
+ {
+ OVERLAPPED f_ol = {0, 0, {{0, 0}}, 0}; /* Initialize to zero. */
+ ULARGE_INTEGER pos_uli;
+ DWORD toRead = (max > INT32_MAX) ? INT32_MAX : (DWORD) max;
+ DWORD resRead;
+
+ pos_uli.QuadPart = (uint64_t) offset64; /* Simple transformation 64bit -> 2x32bit. */
+ f_ol.Offset = pos_uli.LowPart;
+ f_ol.OffsetHigh = pos_uli.HighPart;
+ if (! ReadFile (fh,
+ (void*)buf,
+ toRead,
+ &resRead,
+ &f_ol))
+ return MHD_CONTENT_READER_END_WITH_ERROR; /* Read error. */
+ if (0 == resRead)
+ return MHD_CONTENT_READER_END_OF_STREAM;
+ return (ssize_t) resRead;
+ }
+#endif /* _WIN32 && !__CYGWIN__ */
+}
+
+
+/**
+ * Destroy file reader context. Closes the file
+ * descriptor.
+ *
+ * @param cls pointer to file descriptor
+ */
+static void
+free_callback (void *cls)
+{
+ struct MHD_Response *response = cls;
+
+ (void) close (response->fd);
+ response->fd = -1;
+}
+
+
+/**
+ * Create a response object based on an @a fd from which
+ * data is read. The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param sc status code to return
+ * @param fd file descriptor referring to a file on disk with the
+ * data; will be closed when response is destroyed;
+ * fd should be in 'blocking' mode
+ * @param offset offset to start reading from in the file;
+ * reading file beyond 2 GiB may be not supported by OS or
+ * MHD build; see ::MHD_FEATURE_LARGE_FILE
+ * @param size size of the data portion of the response;
+ * sizes larger than 2 GiB may be not supported by OS or
+ * MHD build; see ::MHD_FEATURE_LARGE_FILE
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+struct MHD_Response *
+MHD_response_from_fd (enum MHD_HTTP_StatusCode sc,
+ int fd,
+ uint64_t offset,
+ uint64_t size)
+{
+ struct MHD_Response *response;
+
+ mhd_assert (-1 != fd);
+#if !defined(HAVE___LSEEKI64) && !defined(HAVE_LSEEK64)
+ if ( (sizeof (uint64_t) > sizeof (off_t)) &&
+ ( (size > (uint64_t)INT32_MAX) ||
+ (offset > (uint64_t)INT32_MAX) ||
+ ((size + offset) >= (uint64_t)INT32_MAX) ) )
+ return NULL;
+#endif
+ if ( ((int64_t) size < 0) ||
+ ((int64_t) offset < 0) ||
+ ((int64_t) (size + offset) < 0) )
+ return NULL;
+
+ response = MHD_response_from_callback (size,
+ 4 * 1024,
+ &file_reader,
+ NULL,
+ &free_callback);
+ if (NULL == response)
+ return NULL;
+ response->fd = fd;
+ response->status_code = sc;
+ response->fd_off = offset;
+ response->crc_cls = response;
+ return response;
+}
+
+/* end of response_from_fd.c */
+
diff --git a/src/lib/response_options.c b/src/lib/response_options.c
@@ -0,0 +1,61 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/response_option.c
+ * @brief implementation of response options
+ * @author Christian Grothoff
+ */
+#include "internal.h"
+
+
+/**
+ * Only respond in conservative HTTP 1.0-mode. In
+ * particular, do not (automatically) sent "Connection" headers and
+ * always close the connection after generating the response.
+ *
+ * @param request the request for which we force HTTP 1.0 to be used
+ */
+void
+MHD_response_option_v10_only (struct MHD_Response *response)
+{
+ response->v10_only = true;
+}
+
+
+/**
+ * Set a function to be called once MHD is finished with the
+ * request.
+ *
+ * @param response which response to set the callback for
+ * @param termination_cb function to call
+ * @param termination_cb_cls closure for @e termination_cb
+ */
+void
+MHD_response_option_termination_callback (struct MHD_Response *response,
+ MHD_RequestTerminationCallback termination_cb,
+ void *termination_cb_cls)
+{
+ /* Q: should we assert termination_cb non-NULL? */
+ response->termination_cb = termination_cb;
+ response->termination_cb_cls = termination_cb_cls;
+}
+
+
+/* end of response_option.c */
diff --git a/src/lib/version.c b/src/lib/version.c
@@ -1,3 +1,28 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file lib/version.c
+ * @brief versioning and optional feature tests
+ * @author Christian Grothoff
+ */
+#include "internal.h"
/**
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
@@ -246,12 +246,13 @@ MHD_get_response_header (struct MHD_Response *response,
NULL != pos;
pos = pos->next)
{
- if ( MHD_str_equal_caseless_ (pos->header, key) )
+ if (MHD_str_equal_caseless_ (pos->header, key))
return pos->value;
}
return NULL;
}
+
/**
* Check whether response header contains particular token.
*