libmicrohttpd

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

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:
Msrc/include/microhttpd2.h | 35++++++++++++++++++++++++-----------
Asrc/lib/action_continue.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/action_from_response.c | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/action_parse_post.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/action_process_upload.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/action_suspend.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/connection_info.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/connection_options.c | 111++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Dsrc/lib/daemon.c | 928-------------------------------------------------------------------------------
Msrc/lib/daemon_info.c | 55+++++++++++++++++++++++++++++++++++++++++++++----------
Asrc/lib/daemon_start.c | 928+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/init.c | 25+++++++++++++++++++++++++
Msrc/lib/init.h | 25+++++++++++++++++++++++++
Msrc/lib/internal.h | 184++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/lib/panic.c | 25+++++++++++++++++++++++++
Asrc/lib/request_info.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/request_resume.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/response.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/response_for_upgrade.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/response_from_buffer.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/response_from_callback.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/response_from_fd.c | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/response_options.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/version.c | 25+++++++++++++++++++++++++
Msrc/microhttpd/response.c | 3++-
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. *