| /* |
| This file is part of libmicrohttpd |
| Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff |
| Copyright (C) 2014-2021 Evgeny Grin (Karlson2k) |
| |
| 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 microhttpd/internal.h |
| * @brief MHD internal shared structures |
| * @author Daniel Pittman |
| * @author Christian Grothoff |
| * @author Karlson2k (Evgeny Grin) |
| */ |
| |
| #ifndef INTERNAL_H |
| #define INTERNAL_H |
| |
| #include "mhd_options.h" |
| #include "platform.h" |
| #include "microhttpd.h" |
| #include "mhd_assert.h" |
| |
| #ifdef HTTPS_SUPPORT |
| #include <gnutls/gnutls.h> |
| #if GNUTLS_VERSION_MAJOR >= 3 |
| #include <gnutls/abstract.h> |
| #endif |
| #endif /* HTTPS_SUPPORT */ |
| |
| #ifdef HAVE_STDBOOL_H |
| #include <stdbool.h> |
| #endif |
| |
| #ifdef HAVE_INTTYPES_H |
| #include <inttypes.h> |
| #endif /* HAVE_INTTYPES_H */ |
| |
| #ifndef PRIu64 |
| #define PRIu64 "llu" |
| #endif /* ! PRIu64 */ |
| |
| /* Must be included before other internal headers! */ |
| #include "mhd_panic.h" |
| |
| #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) |
| #include "mhd_threads.h" |
| #endif |
| #include "mhd_locks.h" |
| #include "mhd_sockets.h" |
| #include "mhd_itc_types.h" |
| #include "mhd_str_types.h" |
| #if defined(BAUTH_SUPPORT) || defined(DAUTH_SUPPORT) |
| #include "gen_auth.h" |
| #endif /* BAUTH_SUPPORT || DAUTH_SUPPORT*/ |
| |
| |
| /** |
| * Macro to drop 'const' qualifier from pointer without compiler warning. |
| * To be used *only* to deal with broken external APIs, which require non-const |
| * pointer to unmodifiable data. |
| * Must not be used to transform pointers for MHD needs. |
| */ |
| #define _MHD_DROP_CONST(ptr) ((void *) ((uintptr_t) ((const void *) (ptr)))) |
| |
| /** |
| * @def _MHD_MACRO_NO |
| * "Negative answer"/"false" for use in macros, meaningful for precompiler |
| */ |
| #define _MHD_MACRO_NO 0 |
| |
| /** |
| * @def _MHD_MACRO_YES |
| * "Positive answer"/"true" for use in macros, meaningful for precompiler |
| */ |
| #define _MHD_MACRO_YES 1 |
| |
| /** |
| * Close FD and abort execution if error is detected. |
| * @param fd the FD to close |
| */ |
| #define MHD_fd_close_chk_(fd) do { \ |
| if ( (0 != close ((fd)) && (EBADF == errno)) ) { \ |
| MHD_PANIC (_ ("Failed to close FD.\n")); \ |
| } \ |
| } while (0) |
| |
| /* |
| #define EXTRA_CHECKS _MHD_MACRO_NO |
| * Not used. Behaviour is controlled by _DEBUG/NDEBUG macros. |
| */ |
| |
| #ifndef _MHD_DEBUG_CONNECT |
| /** |
| * Print extra messages when establishing |
| * connections? (only adds non-error messages). |
| */ |
| #define _MHD_DEBUG_CONNECT _MHD_MACRO_NO |
| #endif /* ! _MHD_DEBUG_CONNECT */ |
| |
| #ifndef _MHD_DEBUG_SEND_DATA |
| /** |
| * Should all data send be printed to stderr? |
| */ |
| #define _MHD_DEBUG_SEND_DATA _MHD_MACRO_NO |
| #endif /* ! _MHD_DEBUG_SEND_DATA */ |
| |
| #ifndef _MHD_DEBUG_CLOSE |
| /** |
| * Add extra debug messages with reasons for closing connections |
| * (non-error reasons). |
| */ |
| #define _MHD_DEBUG_CLOSE _MHD_MACRO_NO |
| #endif /* ! _MHD_DEBUG_CLOSE */ |
| |
| #define MHD_MAX(a,b) (((a)<(b)) ? (b) : (a)) |
| #define MHD_MIN(a,b) (((a)<(b)) ? (a) : (b)) |
| |
| |
| /** |
| * Minimum reasonable size by which MHD tries to increment read/write buffers. |
| * We usually begin with half the available pool space for the |
| * IO-buffer, but if absolutely needed we additively grow by the |
| * number of bytes given here (up to -- theoretically -- the full pool |
| * space). |
| */ |
| #define MHD_BUF_INC_SIZE 1024 |
| |
| #ifndef MHD_STATICSTR_LEN_ |
| /** |
| * Determine length of static string / macro strings at compile time. |
| */ |
| #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) |
| #endif /* ! MHD_STATICSTR_LEN_ */ |
| |
| |
| /** |
| * Tri-state on/off/unknown |
| */ |
| enum MHD_tristate |
| { |
| _MHD_UNKNOWN = -1, /**< State is not yet checked nor set */ |
| _MHD_OFF = false, /**< State is "off" / "disabled" */ |
| _MHD_NO = false, /**< State is "off" / "disabled" */ |
| _MHD_ON = true, /**< State is "on" / "enabled" */ |
| _MHD_YES = true /**< State is "on" / "enabled" */ |
| } _MHD_FIXED_ENUM; |
| |
| |
| /** |
| * State of the socket with respect to epoll (bitmask). |
| */ |
| enum MHD_EpollState |
| { |
| |
| /** |
| * The socket is not involved with a defined state in epoll() right |
| * now. |
| */ |
| MHD_EPOLL_STATE_UNREADY = 0, |
| |
| /** |
| * epoll() told us that data was ready for reading, and we did |
| * not consume all of it yet. |
| */ |
| MHD_EPOLL_STATE_READ_READY = 1, |
| |
| /** |
| * epoll() told us that space was available for writing, and we did |
| * not consume all of it yet. |
| */ |
| MHD_EPOLL_STATE_WRITE_READY = 2, |
| |
| /** |
| * Is this connection currently in the 'eready' EDLL? |
| */ |
| MHD_EPOLL_STATE_IN_EREADY_EDLL = 4, |
| |
| /** |
| * Is this connection currently in the epoll() set? |
| */ |
| MHD_EPOLL_STATE_IN_EPOLL_SET = 8, |
| |
| /** |
| * Is this connection currently suspended? |
| */ |
| MHD_EPOLL_STATE_SUSPENDED = 16, |
| |
| /** |
| * Is this connection in some error state? |
| */ |
| MHD_EPOLL_STATE_ERROR = 128 |
| } _MHD_FIXED_FLAGS_ENUM; |
| |
| |
| /** |
| * What is this connection waiting for? |
| */ |
| enum MHD_ConnectionEventLoopInfo |
| { |
| /** |
| * We are waiting to be able to read. |
| */ |
| MHD_EVENT_LOOP_INFO_READ = 1 << 0, |
| |
| /** |
| * We are waiting to be able to write. |
| */ |
| MHD_EVENT_LOOP_INFO_WRITE = 1 << 1, |
| |
| /** |
| * We are waiting for the application to provide data. |
| */ |
| MHD_EVENT_LOOP_INFO_PROCESS = 1 << 2, |
| |
| /** |
| * Some data is ready to be processed, but more data could |
| * be read. |
| */ |
| MHD_EVENT_LOOP_INFO_PROCESS_READ = |
| MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_PROCESS, |
| |
| /** |
| * We are finished and are awaiting cleanup. |
| */ |
| MHD_EVENT_LOOP_INFO_CLEANUP = 1 << 3 |
| } _MHD_FIXED_ENUM; |
| |
| |
| /** |
| * Additional test value for enum MHD_FLAG to check only for MHD_ALLOW_SUSPEND_RESUME and |
| * NOT for MHD_USE_ITC. |
| */ |
| #define MHD_TEST_ALLOW_SUSPEND_RESUME 8192 |
| |
| /** |
| * Maximum length of a nonce in digest authentication. 64(SHA-256 Hex) + |
| * 12(Timestamp Hex) + 1(NULL); hence 77 should suffice, but Opera |
| * (already) takes more (see Mantis #1633), so we've increased the |
| * value to support something longer... |
| */ |
| #define MAX_CLIENT_NONCE_LENGTH 129 |
| |
| /** |
| * The maximum size of MHD-generated nonce when printed with hexadecimal chars. |
| * |
| * This is equal to "(32 bytes for SHA-256 (or SHA-512/256) nonce plus 6 bytes |
| * for timestamp) multiplied by two hex chars per byte". |
| * Please keep it in sync with digestauth.c |
| */ |
| #if defined(MHD_SHA256_SUPPORT) || defined(MHD_SHA512_256_SUPPORT) |
| #define MAX_DIGEST_NONCE_LENGTH ((32 + 6) * 2) |
| #else /* !MHD_SHA256_SUPPORT && !MHD_SHA512_256_SUPPORT */ |
| #define MAX_DIGEST_NONCE_LENGTH ((16 + 6) * 2) |
| #endif /* !MHD_SHA256_SUPPORT && !MHD_SHA512_256_SUPPORT */ |
| |
| /** |
| * A structure representing the internal holder of the |
| * nonce-nc map. |
| */ |
| struct MHD_NonceNc |
| { |
| |
| /** |
| * Nonce counter, a value that increases for each subsequent |
| * request for the same nonce. Matches the largest last received |
| * 'nc' value. |
| * This 'nc' value was already used by the client. |
| */ |
| uint32_t nc; |
| |
| /** |
| * Bitmask over the previous 64 nonce counter values (down to to nc-64). |
| * Used to allow out-of-order 'nc'. |
| * If bit in the bitmask is set to one, then this 'nc' value was already used |
| * by the client. |
| */ |
| uint64_t nmask; |
| |
| /** |
| * Nonce value |
| */ |
| char nonce[MAX_DIGEST_NONCE_LENGTH + 1]; |
| |
| }; |
| |
| #ifdef HAVE_MESSAGES |
| /** |
| * fprintf()-like helper function for logging debug |
| * messages. |
| */ |
| void |
| MHD_DLOG (const struct MHD_Daemon *daemon, |
| const char *format, |
| ...); |
| |
| #endif |
| |
| |
| /** |
| * Header or footer for HTTP response. |
| */ |
| struct MHD_HTTP_Res_Header |
| { |
| /** |
| * Headers are kept in a double-linked list. |
| */ |
| struct MHD_HTTP_Res_Header *next; |
| |
| /** |
| * Headers are kept in a double-linked list. |
| */ |
| struct MHD_HTTP_Res_Header *prev; |
| |
| /** |
| * The name of the header (key), without the colon. |
| */ |
| char *header; |
| |
| /** |
| * The length of the @a header, not including the final zero termination. |
| */ |
| size_t header_size; |
| |
| /** |
| * The value of the header. |
| */ |
| char *value; |
| |
| /** |
| * The length of the @a value, not including the final zero termination. |
| */ |
| size_t value_size; |
| |
| /** |
| * Type of the value. |
| */ |
| enum MHD_ValueKind kind; |
| |
| }; |
| |
| |
| /** |
| * Header, footer, or cookie for HTTP request. |
| */ |
| struct MHD_HTTP_Req_Header |
| { |
| /** |
| * Headers are kept in a double-linked list. |
| */ |
| struct MHD_HTTP_Req_Header *next; |
| |
| /** |
| * Headers are kept in a double-linked list. |
| */ |
| struct MHD_HTTP_Req_Header *prev; |
| |
| /** |
| * The name of the header (key), without the colon. |
| */ |
| const char *header; |
| |
| /** |
| * The length of the @a header, not including the final zero termination. |
| */ |
| size_t header_size; |
| |
| /** |
| * The value of the header. |
| */ |
| const char *value; |
| |
| /** |
| * The length of the @a value, not including the final zero termination. |
| */ |
| size_t value_size; |
| |
| /** |
| * Type of the value. |
| */ |
| enum MHD_ValueKind kind; |
| |
| }; |
| |
| |
| /** |
| * Automatically assigned flags |
| */ |
| enum MHD_ResponseAutoFlags |
| { |
| MHD_RAF_NO_FLAGS = 0, /**< No auto flags */ |
| MHD_RAF_HAS_CONNECTION_HDR = 1 << 0, /**< Has "Connection" header */ |
| MHD_RAF_HAS_CONNECTION_CLOSE = 1 << 1, /**< Has "Connection: close" */ |
| MHD_RAF_HAS_TRANS_ENC_CHUNKED = 1 << 2, /**< Has "Transfer-Encoding: chunked" */ |
| MHD_RAF_HAS_CONTENT_LENGTH = 1 << 3, /**< Has "Content-Length" header */ |
| MHD_RAF_HAS_DATE_HDR = 1 << 4 /**< Has "Date" header */ |
| } _MHD_FIXED_FLAGS_ENUM; |
| |
| |
| #if defined(MHD_WINSOCK_SOCKETS) |
| /** |
| * Internally used I/O vector type for use with winsock. |
| * Binary matches system "WSABUF". |
| */ |
| typedef struct _MHD_W32_iovec |
| { |
| unsigned long iov_len; |
| char *iov_base; |
| } MHD_iovec_; |
| #define MHD_IOV_ELMN_MAX_SIZE ULONG_MAX |
| typedef unsigned long MHD_iov_size_; |
| #elif defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) |
| /** |
| * Internally used I/O vector type for use when writev or sendmsg |
| * is available. Matches system "struct iovec". |
| */ |
| typedef struct iovec MHD_iovec_; |
| #define MHD_IOV_ELMN_MAX_SIZE SIZE_MAX |
| typedef size_t MHD_iov_size_; |
| #else |
| /** |
| * Internally used I/O vector type for use when writev or sendmsg |
| * is not available. |
| */ |
| typedef struct MHD_IoVec MHD_iovec_; |
| #define MHD_IOV_ELMN_MAX_SIZE SIZE_MAX |
| typedef size_t MHD_iov_size_; |
| #endif |
| |
| |
| struct MHD_iovec_track_ |
| { |
| /** |
| * The copy of array of iovec elements. |
| * The copy of elements are updated during sending. |
| * The number of elements is not changed during lifetime. |
| */ |
| MHD_iovec_ *iov; |
| |
| /** |
| * The number of elements in @a iov. |
| * This value is not changed during lifetime. |
| */ |
| size_t cnt; |
| |
| /** |
| * The number of sent elements. |
| * At the same time, it is the index of the next (or current) element |
| * to send. |
| */ |
| size_t sent; |
| }; |
| |
| /** |
| * Representation of a response. |
| */ |
| struct MHD_Response |
| { |
| |
| /** |
| * Head of double-linked list of headers to send for the response. |
| */ |
| struct MHD_HTTP_Res_Header *first_header; |
| |
| /** |
| * Tail of double-linked list of headers to send for the response. |
| */ |
| struct MHD_HTTP_Res_Header *last_header; |
| |
| /** |
| * Buffer pointing to data that we are supposed |
| * to send as a response. |
| */ |
| const 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; |
| |
| #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 */ |
| |
| #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) |
| /** |
| * Mutex to synchronize access to @e data, @e size and |
| * @e reference_count. |
| */ |
| MHD_mutex_ mutex; |
| #endif |
| |
| /** |
| * The size of the response body. |
| * 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 writable data buffer @e data. |
| */ |
| size_t data_buffer_size; |
| |
| /** |
| * 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; |
| |
| /** |
| * Flags set for the MHD response. |
| */ |
| enum MHD_ResponseFlags flags; |
| |
| /** |
| * Automatic flags set for the MHD response. |
| */ |
| enum MHD_ResponseAutoFlags flags_auto; |
| |
| /** |
| * If the @e fd is a pipe (no sendfile()). |
| */ |
| bool is_pipe; |
| |
| /** |
| * I/O vector used with MHD_create_response_from_iovec. |
| */ |
| MHD_iovec_ *data_iov; |
| |
| /** |
| * Number of elements in data_iov. |
| */ |
| unsigned int data_iovcnt; |
| }; |
| |
| |
| /** |
| * States in a state machine for a connection. |
| * |
| * The main transitions are any-state to #MHD_CONNECTION_CLOSED, any |
| * state to state+1, #MHD_CONNECTION_FOOTERS_SENT to |
| * #MHD_CONNECTION_INIT. #MHD_CONNECTION_CLOSED is the terminal state |
| * and #MHD_CONNECTION_INIT the initial state. |
| * |
| * Note that transitions for *reading* happen only after the input has |
| * been processed; transitions for *writing* happen after the |
| * respective data has been put into the write buffer (the write does |
| * not have to be completed yet). A transition to |
| * #MHD_CONNECTION_CLOSED or #MHD_CONNECTION_INIT requires the write |
| * to be complete. |
| */ |
| enum MHD_CONNECTION_STATE |
| { |
| /** |
| * Connection just started (no headers received). |
| * Waiting for the line with the request type, URL and version. |
| */ |
| MHD_CONNECTION_INIT = 0, |
| |
| /** |
| * Part of the request line was received. |
| * Wait for complete line. |
| */ |
| MHD_CONNECTION_REQ_LINE_RECEIVING = MHD_CONNECTION_INIT + 1, |
| |
| /** |
| * We got the URL (and request type and version). Wait for a header line. |
| */ |
| MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_REQ_LINE_RECEIVING + 1, |
| |
| /** |
| * We got part of a multi-line request header. Wait for the rest. |
| */ |
| MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1, |
| |
| /** |
| * We got the request headers. Process them. |
| */ |
| MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1, |
| |
| /** |
| * We have processed the request headers. Send 100 continue. |
| */ |
| MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1, |
| |
| /** |
| * We have processed the headers and need to send 100 CONTINUE. |
| */ |
| MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1, |
| |
| /** |
| * We have sent 100 CONTINUE (or do not need to). Read the message body. |
| */ |
| MHD_CONNECTION_BODY_RECEIVING = MHD_CONNECTION_CONTINUE_SENDING + 1, |
| |
| /** |
| * We got the request body. Wait for a line of the footer. |
| */ |
| MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_BODY_RECEIVING + 1, |
| |
| /** |
| * We got part of a line of the footer. Wait for the |
| * rest. |
| */ |
| MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1, |
| |
| /** |
| * We received the entire footer. |
| */ |
| MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1, |
| |
| /** |
| * We received the entire request. |
| * Wait for a response to be queued. |
| */ |
| MHD_CONNECTION_FULL_REQ_RECEIVED = MHD_CONNECTION_FOOTERS_RECEIVED + 1, |
| |
| /** |
| * Finished reading of the request and the response is ready. |
| * Switch internal logic from receiving to sending, prepare connection |
| * sending the reply and build the reply header. |
| */ |
| MHD_CONNECTION_START_REPLY = MHD_CONNECTION_FULL_REQ_RECEIVED + 1, |
| |
| /** |
| * We have prepared the response headers in the write buffer. |
| * Send the response headers. |
| */ |
| MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_START_REPLY + 1, |
| |
| /** |
| * We have sent the response headers. Get ready to send the body. |
| */ |
| MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1, |
| |
| /** |
| * We are waiting for the client to provide more |
| * data of a non-chunked body. |
| */ |
| MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_HEADERS_SENT + 1, |
| |
| /** |
| * We are ready to send a part of a non-chunked body. Send it. |
| */ |
| MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1, |
| |
| /** |
| * We are waiting for the client to provide a chunk of the body. |
| */ |
| MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1, |
| |
| /** |
| * We are ready to send a chunk. |
| */ |
| MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1, |
| |
| /** |
| * We have sent the chunked response body. Prepare the footers. |
| */ |
| MHD_CONNECTION_CHUNKED_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_READY + 1, |
| |
| /** |
| * We have prepared the response footer. Send it. |
| */ |
| MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_CHUNKED_BODY_SENT + 1, |
| |
| /** |
| * We have sent the entire reply. |
| * Shutdown connection or restart processing to get a new request. |
| */ |
| MHD_CONNECTION_FULL_REPLY_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1, |
| |
| /** |
| * This connection is to be closed. |
| */ |
| MHD_CONNECTION_CLOSED = MHD_CONNECTION_FULL_REPLY_SENT + 1 |
| |
| #ifdef UPGRADE_SUPPORT |
| , |
| /** |
| * Connection was "upgraded" and socket is now under the |
| * control of the application. |
| */ |
| MHD_CONNECTION_UPGRADE = MHD_CONNECTION_CLOSED + 1 |
| #endif /* UPGRADE_SUPPORT */ |
| |
| } _MHD_FIXED_ENUM; |
| |
| |
| /** |
| * States of TLS transport layer. |
| */ |
| enum MHD_TLS_CONN_STATE |
| { |
| MHD_TLS_CONN_NO_TLS = 0, /**< Not a TLS connection (plain socket). */ |
| MHD_TLS_CONN_INIT, /**< TLS connection is not established yet. */ |
| MHD_TLS_CONN_HANDSHAKING, /**< TLS is in handshake process. */ |
| MHD_TLS_CONN_CONNECTED, /**< TLS is established. */ |
| MHD_TLS_CONN_WR_CLOSING, /**< Closing WR side of TLS layer. */ |
| MHD_TLS_CONN_WR_CLOSED, /**< WR side of TLS layer is closed. */ |
| MHD_TLS_CONN_TLS_CLOSING, /**< TLS session is terminating. */ |
| MHD_TLS_CONN_TLS_CLOSED, /**< TLS session is terminated. */ |
| MHD_TLS_CONN_TLS_FAILED, /**< TLS session failed. */ |
| MHD_TLS_CONN_INVALID_STATE/**< Sentinel. Not a valid value. */ |
| } _MHD_FIXED_ENUM; |
| |
| /** |
| * Should all state transitions be printed to stderr? |
| */ |
| #define DEBUG_STATES _MHD_MACRO_NO |
| |
| |
| #ifdef HAVE_MESSAGES |
| #if DEBUG_STATES |
| const char * |
| MHD_state_to_string (enum MHD_CONNECTION_STATE state); |
| |
| #endif |
| #endif |
| |
| /** |
| * Function to receive plaintext data. |
| * |
| * @param conn the connection struct |
| * @param write_to where to write received data |
| * @param max_bytes maximum number of bytes to receive |
| * @return number of bytes written to @a write_to |
| */ |
| typedef ssize_t |
| (*ReceiveCallback) (struct MHD_Connection *conn, |
| void *write_to, |
| size_t max_bytes); |
| |
| |
| /** |
| * Function to transmit plaintext data. |
| * |
| * @param conn the connection struct |
| * @param read_from where to read data to transmit |
| * @param max_bytes maximum number of bytes to transmit |
| * @return number of bytes transmitted |
| */ |
| typedef ssize_t |
| (*TransmitCallback) (struct MHD_Connection *conn, |
| const void *read_from, |
| size_t max_bytes); |
| |
| |
| /** |
| * Ability to use same connection for next request |
| */ |
| enum MHD_ConnKeepAlive |
| { |
| /** |
| * Connection must be closed after sending response. |
| */ |
| MHD_CONN_MUST_CLOSE = -1, |
| |
| /** |
| * KeelAlive state is not yet determined |
| */ |
| MHD_CONN_KEEPALIVE_UNKOWN = 0, |
| |
| /** |
| * Connection can be used for serving next request |
| */ |
| MHD_CONN_USE_KEEPALIVE = 1, |
| |
| /** |
| * Connection will be upgraded |
| */ |
| MHD_CONN_MUST_UPGRADE = 2 |
| } _MHD_FIXED_ENUM; |
| |
| enum MHD_HTTP_Version |
| { |
| /** |
| * Not a HTTP protocol or HTTP version is invalid. |
| */ |
| MHD_HTTP_VER_INVALID = -1, |
| |
| /** |
| * HTTP version is not yet received from the client. |
| */ |
| MHD_HTTP_VER_UNKNOWN = 0, |
| |
| /** |
| * HTTP version before 1.0, unsupported. |
| */ |
| MHD_HTTP_VER_TOO_OLD = 1, |
| |
| /** |
| * HTTP version 1.0 |
| */ |
| MHD_HTTP_VER_1_0 = 2, |
| |
| /** |
| * HTTP version 1.1 |
| */ |
| MHD_HTTP_VER_1_1 = 3, |
| |
| /** |
| * HTTP version 1.2-1.9, must be used as 1.1 |
| */ |
| MHD_HTTP_VER_1_2__1_9 = 4, |
| |
| /** |
| * HTTP future version. Unsupported. |
| */ |
| MHD_HTTP_VER_FUTURE = 100 |
| } _MHD_FIXED_ENUM; |
| |
| /** |
| * Returns boolean 'true' if HTTP version is supported by MHD |
| */ |
| #define MHD_IS_HTTP_VER_SUPPORTED(ver) (MHD_HTTP_VER_1_0 <= (ver) && \ |
| MHD_HTTP_VER_1_2__1_9 >= (ver)) |
| |
| /** |
| * Protocol should be used as HTTP/1.1 protocol. |
| * |
| * See the last paragraph of |
| * https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 |
| */ |
| #define MHD_IS_HTTP_VER_1_1_COMPAT(ver) (MHD_HTTP_VER_1_1 == (ver) || \ |
| MHD_HTTP_VER_1_2__1_9 == (ver)) |
| |
| /** |
| * The HTTP method. |
| * |
| * Only primary methods (specified in RFC7231) are defined here. |
| */ |
| enum MHD_HTTP_Method |
| { |
| /** |
| * No request string has been received yet |
| */ |
| MHD_HTTP_MTHD_NO_METHOD = 0, |
| /** |
| * HTTP method GET |
| */ |
| MHD_HTTP_MTHD_GET = 1, |
| /** |
| * HTTP method HEAD |
| */ |
| MHD_HTTP_MTHD_HEAD = 2, |
| /** |
| * HTTP method POST |
| */ |
| MHD_HTTP_MTHD_POST = 3, |
| /** |
| * HTTP method PUT |
| */ |
| MHD_HTTP_MTHD_PUT = 4, |
| /** |
| * HTTP method DELETE |
| */ |
| MHD_HTTP_MTHD_DELETE = 5, |
| /** |
| * HTTP method CONNECT |
| */ |
| MHD_HTTP_MTHD_CONNECT = 6, |
| /** |
| * HTTP method OPTIONS |
| */ |
| MHD_HTTP_MTHD_OPTIONS = 7, |
| /** |
| * HTTP method TRACE |
| */ |
| MHD_HTTP_MTHD_TRACE = 8, |
| /** |
| * Other HTTP method. Check the string value. |
| */ |
| MHD_HTTP_MTHD_OTHER = 1000 |
| } _MHD_FIXED_ENUM; |
| |
| |
| /** |
| * Request-specific values. |
| * |
| * Meaningful for the current request only. |
| */ |
| struct MHD_Request |
| { |
| /** |
| * HTTP version string (i.e. http/1.1). Allocated |
| * in pool. |
| */ |
| const char *version; |
| |
| /** |
| * HTTP protocol version as enum. |
| */ |
| enum MHD_HTTP_Version http_ver; |
| |
| /** |
| * Request method. Should be GET/POST/etc. Allocated in pool. |
| */ |
| const char *method; |
| |
| /** |
| * The request method as enum. |
| */ |
| enum MHD_HTTP_Method http_mthd; |
| |
| /** |
| * Requested URL (everything after "GET" only). Allocated |
| * in pool. |
| */ |
| const char *url; |
| |
| /** |
| * The length of the @a url in characters, not including the terminating zero. |
| */ |
| size_t url_len; |
| |
| /** |
| * Linked list of parsed headers. |
| */ |
| struct MHD_HTTP_Req_Header *headers_received; |
| |
| /** |
| * Tail of linked list of parsed headers. |
| */ |
| struct MHD_HTTP_Req_Header *headers_received_tail; |
| |
| /** |
| * Number of bytes we had in the HTTP header, set once we |
| * pass #MHD_CONNECTION_HEADERS_RECEIVED. |
| */ |
| size_t header_size; |
| |
| /** |
| * How many more bytes of the body do we expect |
| * to read? #MHD_SIZE_UNKNOWN for unknown. |
| */ |
| uint64_t remaining_upload_size; |
| |
| /** |
| * Are we receiving with chunked encoding? |
| * This will be set to #MHD_YES after we parse the headers and |
| * are processing the body with chunks. |
| * After we are done with the body and we are processing the footers; |
| * once the footers are also done, this will be set to #MHD_NO again |
| * (before the final call to the handler). |
| * It is used only for requests, chunked encoding for response is |
| * indicated by @a rp_props. |
| */ |
| bool have_chunked_upload; |
| |
| /** |
| * If we are receiving with chunked encoding, where are we right |
| * now? |
| * Set to 0 if we are waiting to receive the chunk size; |
| * otherwise, this is the size of the current chunk. |
| * A value of zero is also used when we're at the end of the chunks. |
| */ |
| uint64_t current_chunk_size; |
| |
| /** |
| * If we are receiving with chunked encoding, where are we currently |
| * with respect to the current chunk (at what offset / position)? |
| */ |
| uint64_t current_chunk_offset; |
| |
| /** |
| * Indicate that some of the upload payload data have been processed |
| * by the last call of the connection handler. |
| * If any data have been processed, but some data left in the buffer |
| * for further processing, then MHD will use zero timeout before the |
| * next data processing round. |
| * If no data have been processed, than MHD will wait for more data |
| * to come (as it makes no sense to call the connection handler with |
| * the same conditions). |
| */ |
| bool some_payload_processed; |
| |
| /** |
| * We allow the main application to associate some pointer with the |
| * HTTP request, which is passed to each #MHD_AccessHandlerCallback |
| * and some other API calls. Here is where we store it. (MHD does |
| * not know or care what it is). |
| */ |
| void *client_context; |
| |
| /** |
| * Did we ever call the "default_handler" on this request? |
| * This flag determines if we have called the #MHD_OPTION_NOTIFY_COMPLETED |
| * handler when the request finishes. |
| */ |
| bool client_aware; |
| |
| #ifdef BAUTH_SUPPORT |
| /** |
| * Basic Authorization parameters. |
| * The result of Basic Authorization header parsing. |
| * Allocated in the connection's pool. |
| */ |
| const struct MHD_RqBAuth *bauth; |
| |
| /** |
| * Set to true if current request headers are checked for Basic Authorization |
| */ |
| bool bauth_tried; |
| #endif /* BAUTH_SUPPORT */ |
| #ifdef DAUTH_SUPPORT |
| /** |
| * Digest Authorization parameters. |
| * The result of Digest Authorization header parsing. |
| * Allocated in the connection's pool. |
| */ |
| const struct MHD_RqDAuth *dauth; |
| |
| /** |
| * Set to true if current request headers are checked for Digest Authorization |
| */ |
| bool dauth_tried; |
| #endif /* DAUTH_SUPPORT */ |
| |
| /** |
| * Last incomplete header line during parsing of headers. |
| * Allocated in pool. Only valid if state is |
| * either #MHD_CONNECTION_HEADER_PART_RECEIVED or |
| * #MHD_CONNECTION_FOOTER_PART_RECEIVED. |
| */ |
| char *last; |
| |
| /** |
| * Position after the colon on the last incomplete header |
| * line during parsing of headers. |
| * Allocated in pool. Only valid if state is |
| * either #MHD_CONNECTION_HEADER_PART_RECEIVED or |
| * #MHD_CONNECTION_FOOTER_PART_RECEIVED. |
| */ |
| char *colon; |
| }; |
| |
| |
| /** |
| * Reply-specific properties. |
| */ |
| struct MHD_Reply_Properties |
| { |
| #ifdef _DEBUG |
| bool set; /**< Indicates that other members are set and valid */ |
| #endif /* _DEBUG */ |
| bool use_reply_body_headers; /**< Use reply body-specific headers */ |
| bool send_reply_body; /**< Send reply body (can be zero-sized) */ |
| bool chunked; /**< Use chunked encoding for reply */ |
| }; |
| |
| #if defined(_MHD_HAVE_SENDFILE) |
| enum MHD_resp_sender_ |
| { |
| MHD_resp_sender_std = 0, |
| MHD_resp_sender_sendfile |
| }; |
| #endif /* _MHD_HAVE_SENDFILE */ |
| |
| /** |
| * Reply-specific values. |
| * |
| * Meaningful for the current reply only. |
| */ |
| struct MHD_Reply |
| { |
| /** |
| * Response to transmit (initially NULL). |
| */ |
| struct MHD_Response *response; |
| |
| /** |
| * HTTP response code. Only valid if response object |
| * is already set. |
| */ |
| unsigned int responseCode; |
| |
| /** |
| * The "ICY" response. |
| * Reply begins with the SHOUTcast "ICY" line instead of "HTTP". |
| */ |
| bool responseIcy; |
| |
| /** |
| * Current write position in the actual response |
| * (excluding headers, content only; should be 0 |
| * while sending headers). |
| */ |
| uint64_t rsp_write_position; |
| |
| /** |
| * The copy of iov response. |
| * Valid if iovec response is used. |
| * Updated during send. |
| * Members are allocated in the pool. |
| */ |
| struct MHD_iovec_track_ resp_iov; |
| |
| #if defined(_MHD_HAVE_SENDFILE) |
| enum MHD_resp_sender_ resp_sender; |
| #endif /* _MHD_HAVE_SENDFILE */ |
| |
| /** |
| * Reply-specific properties |
| */ |
| struct MHD_Reply_Properties props; |
| }; |
| |
| /** |
| * State kept for each HTTP request. |
| */ |
| struct MHD_Connection |
| { |
| |
| #ifdef EPOLL_SUPPORT |
| /** |
| * Next pointer for the EDLL listing connections that are epoll-ready. |
| */ |
| struct MHD_Connection *nextE; |
| |
| /** |
| * Previous pointer for the EDLL listing connections that are epoll-ready. |
| */ |
| struct MHD_Connection *prevE; |
| #endif |
| |
| /** |
| * Next pointer for the DLL describing our IO state. |
| */ |
| struct MHD_Connection *next; |
| |
| /** |
| * Previous pointer for the DLL describing our IO state. |
| */ |
| struct MHD_Connection *prev; |
| |
| /** |
| * Next pointer for the XDLL organizing connections by timeout. |
| * This DLL can be either the |
| * 'manual_timeout_head/manual_timeout_tail' or the |
| * 'normal_timeout_head/normal_timeout_tail', depending on whether a |
| * custom timeout is set for the connection. |
| */ |
| struct MHD_Connection *nextX; |
| |
| /** |
| * Previous pointer for the XDLL organizing connections by timeout. |
| */ |
| struct MHD_Connection *prevX; |
| |
| /** |
| * Reference to the MHD_Daemon struct. |
| */ |
| struct MHD_Daemon *daemon; |
| |
| /** |
| * Request-specific data |
| */ |
| struct MHD_Request rq; |
| |
| /** |
| * Reply-specific data |
| */ |
| struct MHD_Reply rp; |
| |
| /** |
| * The memory pool is created whenever we first read from the TCP |
| * stream and destroyed at the end of each request (and re-created |
| * for the next request). In the meantime, this pointer is NULL. |
| * The pool is used for all connection-related data except for the |
| * response (which maybe shared between connections) and the IP |
| * address (which persists across individual requests). |
| */ |
| struct MemoryPool *pool; |
| |
| /** |
| * We allow the main application to associate some pointer with the |
| * TCP connection (which may span multiple HTTP requests). Here is |
| * where we store it. (MHD does not know or care what it is). |
| * The location is given to the #MHD_NotifyConnectionCallback and |
| * also accessible via #MHD_CONNECTION_INFO_SOCKET_CONTEXT. |
| */ |
| void *socket_context; |
| |
| /** |
| * Close connection after sending response? |
| * Functions may change value from "Unknown" or "KeepAlive" to "Must close", |
| * but no functions reset value "Must Close" to any other value. |
| */ |
| enum MHD_ConnKeepAlive keepalive; |
| |
| /** |
| * Buffer for reading requests. Allocated in pool. Actually one |
| * byte larger than @e read_buffer_size (if non-NULL) to allow for |
| * 0-termination. |
| */ |
| char *read_buffer; |
| |
| /** |
| * Buffer for writing response (headers only). Allocated |
| * in pool. |
| */ |
| char *write_buffer; |
| |
| /** |
| * Foreign address (of length @e addr_len). MALLOCED (not |
| * in pool!). |
| */ |
| struct sockaddr_storage *addr; |
| |
| #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) |
| /** |
| * Thread handle for this connection (if we are using |
| * one thread per connection). |
| */ |
| MHD_thread_handle_ID_ pid; |
| #endif |
| |
| /** |
| * Size of @e read_buffer (in bytes). |
| * This value indicates how many bytes we're willing to read |
| * into the buffer. |
| */ |
| size_t read_buffer_size; |
| |
| /** |
| * Position where we currently append data in @e read_buffer (the |
| * next char after the last valid position). |
| */ |
| size_t read_buffer_offset; |
| |
| /** |
| * Size of @e write_buffer (in bytes). |
| */ |
| size_t write_buffer_size; |
| |
| /** |
| * Offset where we are with sending from @e write_buffer. |
| */ |
| size_t write_buffer_send_offset; |
| |
| /** |
| * Last valid location in write_buffer (where do we |
| * append and up to where is it safe to send?) |
| */ |
| size_t write_buffer_append_offset; |
| |
| /** |
| * Position in the 100 CONTINUE message that |
| * we need to send when receiving http 1.1 requests. |
| */ |
| size_t continue_message_write_offset; |
| |
| /** |
| * Length of the foreign address. |
| */ |
| socklen_t addr_len; |
| |
| /** |
| * Last time this connection had any activity |
| * (reading or writing). |
| */ |
| uint64_t last_activity; |
| |
| /** |
| * After how many milliseconds of inactivity should |
| * this connection time out? |
| * Zero for no timeout. |
| */ |
| uint64_t connection_timeout_ms; |
| |
| /** |
| * Socket for this connection. Set to #MHD_INVALID_SOCKET if |
| * this connection has died (daemon should clean |
| * up in that case). |
| */ |
| MHD_socket socket_fd; |
| |
| /** |
| * true if @e socket_fd is not TCP/IP (a UNIX domain socket, a pipe), |
| * false (TCP/IP) otherwise. |
| */ |
| enum MHD_tristate is_nonip; |
| |
| /** |
| * true if #socket_fd is non-blocking, false otherwise. |
| */ |
| bool sk_nonblck; |
| |
| /** |
| * true if connection socket has set SIGPIPE suppression |
| */ |
| bool sk_spipe_suppress; |
| |
| /** |
| * Tracks TCP_CORK / TCP_NOPUSH of the connection socket. |
| */ |
| enum MHD_tristate sk_corked; |
| |
| /** |
| * Tracks TCP_NODELAY state of the connection socket. |
| */ |
| enum MHD_tristate sk_nodelay; |
| |
| /** |
| * Has this socket been closed for reading (i.e. other side closed |
| * the connection)? If so, we must completely close the connection |
| * once we are done sending our response (and stop trying to read |
| * from this socket). |
| */ |
| bool read_closed; |
| |
| /** |
| * Some error happens during processing the connection therefore this |
| * connection must be closed. |
| * The error may come from the client side (like wrong request format), |
| * from the application side (like data callback returned error), or from |
| * the OS side (like out-of-memory). |
| */ |
| bool stop_with_error; |
| |
| /** |
| * Response queued early, before the request is fully processed, |
| * the client upload is rejected. |
| * The connection cannot be reused for additional requests as the current |
| * request is incompletely read and it is unclear where is the initial |
| * byte of the next request. |
| */ |
| bool discard_request; |
| |
| #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) |
| /** |
| * Set to `true` if the thread has been joined. |
| */ |
| bool thread_joined; |
| #endif |
| |
| /** |
| * Are we currently inside the "idle" handler (to avoid recursively |
| * invoking it). |
| */ |
| bool in_idle; |
| |
| /** |
| * Connection is in the cleanup DL-linked list. |
| */ |
| bool in_cleanup; |
| |
| #ifdef EPOLL_SUPPORT |
| /** |
| * What is the state of this socket in relation to epoll? |
| */ |
| enum MHD_EpollState epoll_state; |
| #endif |
| |
| /** |
| * State in the FSM for this connection. |
| */ |
| enum MHD_CONNECTION_STATE state; |
| |
| /** |
| * What is this connection waiting for? |
| */ |
| enum MHD_ConnectionEventLoopInfo event_loop_info; |
| |
| /** |
| * Function used for reading HTTP request stream. |
| */ |
| ReceiveCallback recv_cls; |
| |
| #ifdef UPGRADE_SUPPORT |
| /** |
| * If this connection was upgraded, this points to |
| * the upgrade response details such that the |
| * #thread_main_connection_upgrade()-logic can perform the |
| * bi-directional forwarding. |
| */ |
| struct MHD_UpgradeResponseHandle *urh; |
| #endif /* UPGRADE_SUPPORT */ |
| |
| #ifdef HTTPS_SUPPORT |
| |
| /** |
| * State required for HTTPS/SSL/TLS support. |
| */ |
| gnutls_session_t tls_session; |
| |
| /** |
| * State of connection's TLS layer |
| */ |
| enum MHD_TLS_CONN_STATE tls_state; |
| |
| /** |
| * Could it be that we are ready to read due to TLS buffers |
| * even though the socket is not? |
| */ |
| bool tls_read_ready; |
| #endif /* HTTPS_SUPPORT */ |
| |
| /** |
| * Is the connection suspended? |
| */ |
| bool suspended; |
| |
| /** |
| * Is the connection wanting to resume? |
| */ |
| volatile bool resuming; |
| |
| /** |
| * Special member to be returned by #MHD_get_connection_info() |
| */ |
| union MHD_ConnectionInfo connection_info_dummy; |
| }; |
| |
| |
| #ifdef UPGRADE_SUPPORT |
| /** |
| * Buffer we use for upgrade response handling in the unlikely |
| * case where the memory pool was so small it had no buffer |
| * capacity left. Note that we don't expect to _ever_ use this |
| * buffer, so it's mostly wasted memory (except that it allows |
| * us to handle a tricky error condition nicely). So no need to |
| * make this one big. Applications that want to perform well |
| * should just pick an adequate size for the memory pools. |
| */ |
| #define RESERVE_EBUF_SIZE 8 |
| |
| /** |
| * Context we pass to epoll() for each of the two sockets |
| * of a `struct MHD_UpgradeResponseHandle`. We need to do |
| * this so we can distinguish the two sockets when epoll() |
| * gives us event notifications. |
| */ |
| struct UpgradeEpollHandle |
| { |
| /** |
| * Reference to the overall response handle this struct is |
| * included within. |
| */ |
| struct MHD_UpgradeResponseHandle *urh; |
| |
| /** |
| * The socket this event is kind-of about. Note that this is NOT |
| * necessarily the socket we are polling on, as for when we read |
| * from TLS, we epoll() on the connection's socket |
| * (`urh->connection->socket_fd`), while this then the application's |
| * socket (where the application will read from). Nevertheless, for |
| * the application to read, we need to first read from TLS, hence |
| * the two are related. |
| * |
| * Similarly, for writing to TLS, this epoll() will be on the |
| * connection's `socket_fd`, and this will merely be the FD which |
| * the application would write to. Hence this struct must always be |
| * interpreted based on which field in `struct |
| * MHD_UpgradeResponseHandle` it is (`app` or `mhd`). |
| */ |
| MHD_socket socket; |
| |
| /** |
| * IO-state of the @e socket (or the connection's `socket_fd`). |
| */ |
| enum MHD_EpollState celi; |
| |
| }; |
| |
| |
| /** |
| * Handle given to the application to manage special |
| * actions relating to MHD responses that "upgrade" |
| * the HTTP protocol (i.e. to WebSockets). |
| */ |
| struct MHD_UpgradeResponseHandle |
| { |
| /** |
| * The connection for which this is an upgrade handle. Note that |
| * because a response may be shared over many connections, this may |
| * not be the only upgrade handle for the response of this connection. |
| */ |
| struct MHD_Connection *connection; |
| |
| #ifdef HTTPS_SUPPORT |
| /** |
| * Kept in a DLL per daemon. |
| */ |
| struct MHD_UpgradeResponseHandle *next; |
| |
| /** |
| * Kept in a DLL per daemon. |
| */ |
| struct MHD_UpgradeResponseHandle *prev; |
| |
| #ifdef EPOLL_SUPPORT |
| /** |
| * Next pointer for the EDLL listing urhs that are epoll-ready. |
| */ |
| struct MHD_UpgradeResponseHandle *nextE; |
| |
| /** |
| * Previous pointer for the EDLL listing urhs that are epoll-ready. |
| */ |
| struct MHD_UpgradeResponseHandle *prevE; |
| |
| /** |
| * Specifies whether urh already in EDLL list of ready connections. |
| */ |
| bool in_eready_list; |
| #endif |
| |
| /** |
| * The buffer for receiving data from TLS to |
| * be passed to the application. Contains @e in_buffer_size |
| * bytes (unless @e in_buffer_size is zero). Do not free! |
| */ |
| char *in_buffer; |
| |
| /** |
| * The buffer for receiving data from the application to |
| * be passed to TLS. Contains @e out_buffer_size |
| * bytes (unless @e out_buffer_size is zero). Do not free! |
| */ |
| char *out_buffer; |
| |
| /** |
| * Size of the @e in_buffer. |
| * Set to 0 if the TLS connection went down for reading or socketpair |
| * went down for writing. |
| */ |
| size_t in_buffer_size; |
| |
| /** |
| * Size of the @e out_buffer. |
| * Set to 0 if the TLS connection went down for writing or socketpair |
| * went down for reading. |
| */ |
| size_t out_buffer_size; |
| |
| /** |
| * Number of bytes actually in use in the @e in_buffer. Can be larger |
| * than @e in_buffer_size if and only if @a in_buffer_size is zero and |
| * we still have bytes that can be forwarded. |
| * Reset to zero if all data was forwarded to socketpair or |
| * if socketpair went down for writing. |
| */ |
| size_t in_buffer_used; |
| |
| /** |
| * Number of bytes actually in use in the @e out_buffer. Can be larger |
| * than @e out_buffer_size if and only if @a out_buffer_size is zero and |
| * we still have bytes that can be forwarded. |
| * Reset to zero if all data was forwarded to TLS connection or |
| * if TLS connection went down for writing. |
| */ |
| size_t out_buffer_used; |
| |
| /** |
| * The socket we gave to the application (r/w). |
| */ |
| struct UpgradeEpollHandle app; |
| |
| /** |
| * If @a app_sock was a socketpair, our end of it, otherwise |
| * #MHD_INVALID_SOCKET; (r/w). |
| */ |
| struct UpgradeEpollHandle mhd; |
| |
| /** |
| * Emergency IO buffer we use in case the memory pool has literally |
| * nothing left. |
| */ |
| char e_buf[RESERVE_EBUF_SIZE]; |
| |
| #endif /* HTTPS_SUPPORT */ |
| |
| /** |
| * Set to true after the application finished with the socket |
| * by #MHD_UPGRADE_ACTION_CLOSE. |
| * |
| * When BOTH @e was_closed (changed by command from application) |
| * AND @e clean_ready (changed internally by MHD) are set to |
| * #MHD_YES, function #MHD_resume_connection() will move this |
| * connection to cleanup list. |
| * @remark This flag could be changed from any thread. |
| */ |
| volatile bool was_closed; |
| |
| /** |
| * Set to true if connection is ready for cleanup. |
| * |
| * In TLS mode functions #MHD_connection_finish_forward_() must |
| * be called before setting this flag to true. |
| * |
| * In thread-per-connection mode, true in this flag means |
| * that connection's thread exited or about to exit and will |
| * not use MHD_Connection::urh data anymore. |
| * |
| * In any mode true in this flag also means that |
| * MHD_Connection::urh data will not be used for socketpair |
| * forwarding and forwarding itself is finished. |
| * |
| * When BOTH @e was_closed (changed by command from application) |
| * AND @e clean_ready (changed internally by MHD) are set to |
| * true, function #MHD_resume_connection() will move this |
| * connection to cleanup list. |
| * @remark This flag could be changed from thread that process |
| * connection's recv(), send() and response. |
| */ |
| volatile bool clean_ready; |
| }; |
| #endif /* UPGRADE_SUPPORT */ |
| |
| |
| /** |
| * Signature of function called to log URI accesses. |
| * |
| * @param cls closure |
| * @param uri uri being accessed |
| * @param con connection handle |
| * @return new closure |
| */ |
| typedef void * |
| (*LogCallback)(void *cls, |
| const char *uri, |
| struct MHD_Connection *con); |
| |
| /** |
| * Signature of function called to unescape URIs. See also |
| * #MHD_http_unescape(). |
| * |
| * @param cls closure |
| * @param conn connection handle |
| * @param uri 0-terminated string to unescape (should be updated) |
| * @return length of the resulting string |
| */ |
| typedef size_t |
| (*UnescapeCallback)(void *cls, |
| struct MHD_Connection *conn, |
| char *uri); |
| |
| |
| /** |
| * State kept for each MHD daemon. All connections are kept in two |
| * doubly-linked lists. The first one reflects the state of the |
| * connection in terms of what operations we are waiting for (read, |
| * write, locally blocked, cleanup) whereas the second is about its |
| * timeout state (default or custom). |
| */ |
| struct MHD_Daemon |
| { |
| |
| /** |
| * Callback function for all requests. |
| */ |
| MHD_AccessHandlerCallback default_handler; |
| |
| /** |
| * Closure argument to default_handler. |
| */ |
| void *default_handler_cls; |
| |
| /** |
| * Daemon's flags (bitfield). |
| * |
| * @remark Keep this member after pointer value to keep it |
| * properly aligned as it will be used as member of union MHD_DaemonInfo. |
| */ |
| enum MHD_FLAG options; |
| |
| /** |
| * Head of doubly-linked list of new, externally added connections. |
| */ |
| struct MHD_Connection *new_connections_head; |
| |
| /** |
| * Tail of doubly-linked list of new, externally added connections. |
| */ |
| struct MHD_Connection *new_connections_tail; |
| |
| /** |
| * Head of doubly-linked list of our current, active connections. |
| */ |
| struct MHD_Connection *connections_head; |
| |
| /** |
| * Tail of doubly-linked list of our current, active connections. |
| */ |
| struct MHD_Connection *connections_tail; |
| |
| /** |
| * Head of doubly-linked list of our current but suspended connections. |
| */ |
| struct MHD_Connection *suspended_connections_head; |
| |
| /** |
| * Tail of doubly-linked list of our current but suspended connections. |
| */ |
| struct MHD_Connection *suspended_connections_tail; |
| |
| /** |
| * Head of doubly-linked list of connections to clean up. |
| */ |
| struct MHD_Connection *cleanup_head; |
| |
| /** |
| * Tail of doubly-linked list of connections to clean up. |
| */ |
| struct MHD_Connection *cleanup_tail; |
| |
| /** |
| * _MHD_YES if the @e listen_fd socket is a UNIX domain socket. |
| */ |
| enum MHD_tristate listen_is_unix; |
| |
| #ifdef EPOLL_SUPPORT |
| /** |
| * Head of EDLL of connections ready for processing (in epoll mode). |
| */ |
| struct MHD_Connection *eready_head; |
| |
| /** |
| * Tail of EDLL of connections ready for processing (in epoll mode) |
| */ |
| struct MHD_Connection *eready_tail; |
| |
| /** |
| * File descriptor associated with our epoll loop. |
| * |
| * @remark Keep this member after pointer value to keep it |
| * properly aligned as it will be used as member of union MHD_DaemonInfo. |
| */ |
| int epoll_fd; |
| |
| /** |
| * true if the @e listen_fd socket is in the 'epoll' set, |
| * false if not. |
| */ |
| bool listen_socket_in_epoll; |
| |
| #ifdef UPGRADE_SUPPORT |
| #ifdef HTTPS_SUPPORT |
| /** |
| * File descriptor associated with the #run_epoll_for_upgrade() loop. |
| * Only available if #MHD_USE_HTTPS_EPOLL_UPGRADE is set. |
| */ |
| int epoll_upgrade_fd; |
| |
| /** |
| * true if @e epoll_upgrade_fd is in the 'epoll' set, |
| * false if not. |
| */ |
| bool upgrade_fd_in_epoll; |
| #endif /* HTTPS_SUPPORT */ |
| |
| /** |
| * Head of EDLL of upgraded connections ready for processing (in epoll mode). |
| */ |
| struct MHD_UpgradeResponseHandle *eready_urh_head; |
| |
| /** |
| * Tail of EDLL of upgraded connections ready for processing (in epoll mode) |
| */ |
| struct MHD_UpgradeResponseHandle *eready_urh_tail; |
| #endif /* UPGRADE_SUPPORT */ |
| #endif /* EPOLL_SUPPORT */ |
| |
| /** |
| * Head of the XDLL of ALL connections with a default ('normal') |
| * timeout, sorted by timeout (earliest at the tail, most recently |
| * used connection at the head). MHD can just look at the tail of |
| * this list to determine the timeout for all of its elements; |
| * whenever there is an event of a connection, the connection is |
| * moved back to the tail of the list. |
| * |
| * All connections by default start in this list; if a custom |
| * timeout that does not match @e connection_timeout_ms is set, they |
| * are moved to the @e manual_timeout_head-XDLL. |
| * Not used in MHD_USE_THREAD_PER_CONNECTION mode as each thread |
| * needs only one connection-specific timeout. |
| */ |
| struct MHD_Connection *normal_timeout_head; |
| |
| /** |
| * Tail of the XDLL of ALL connections with a default timeout, |
| * sorted by timeout (earliest timeout at the tail). |
| * Not used in MHD_USE_THREAD_PER_CONNECTION mode. |
| */ |
| struct MHD_Connection *normal_timeout_tail; |
| |
| /** |
| * Head of the XDLL of ALL connections with a non-default/custom |
| * timeout, unsorted. MHD will do a O(n) scan over this list to |
| * determine the current timeout. |
| * Not used in MHD_USE_THREAD_PER_CONNECTION mode. |
| */ |
| struct MHD_Connection *manual_timeout_head; |
| |
| /** |
| * Tail of the XDLL of ALL connections with a non-default/custom |
| * timeout, unsorted. |
| * Not used in MHD_USE_THREAD_PER_CONNECTION mode. |
| */ |
| struct MHD_Connection *manual_timeout_tail; |
| |
| /** |
| * Function to call to check if we should accept or reject an |
| * incoming request. May be NULL. |
| */ |
| MHD_AcceptPolicyCallback apc; |
| |
| /** |
| * Closure argument to apc. |
| */ |
| void *apc_cls; |
| |
| /** |
| * Function to call when we are done processing |
| * a particular request. May be NULL. |
| */ |
| MHD_RequestCompletedCallback notify_completed; |
| |
| /** |
| * Closure argument to @e notify_completed. |
| */ |
| void *notify_completed_cls; |
| |
| /** |
| * Function to call when we are starting/stopping |
| * a connection. May be NULL. |
| */ |
| MHD_NotifyConnectionCallback notify_connection; |
| |
| /** |
| * Closure argument to @e notify_connection. |
| */ |
| void *notify_connection_cls; |
| |
| /** |
| * Function to call with the full URI at the |
| * beginning of request processing. May be NULL. |
| * <p> |
| * Returns the initial pointer to internal state |
| * kept by the client for the request. |
| */ |
| LogCallback uri_log_callback; |
| |
| /** |
| * Closure argument to @e uri_log_callback. |
| */ |
| void *uri_log_callback_cls; |
| |
| /** |
| * Function to call when we unescape escape sequences. |
| */ |
| UnescapeCallback unescape_callback; |
| |
| /** |
| * Closure for @e unescape_callback. |
| */ |
| void *unescape_callback_cls; |
| |
| /** |
| * Listen port. |
| * |
| * @remark Keep this member after pointer value to keep it |
| * properly aligned as it will be used as member of union MHD_DaemonInfo. |
| */ |
| uint16_t port; |
| |
| #ifdef HAVE_MESSAGES |
| /** |
| * Function for logging error messages (if we |
| * support error reporting). |
| */ |
| MHD_LogCallback custom_error_log; |
| |
| /** |
| * Closure argument to @e custom_error_log. |
| */ |
| void *custom_error_log_cls; |
| #endif |
| |
| /** |
| * Pointer to master daemon (NULL if this is the master) |
| */ |
| struct MHD_Daemon *master; |
| |
| /** |
| * Listen socket. |
| * |
| * @remark Keep this member after pointer value to keep it |
| * properly aligned as it will be used as member of union MHD_DaemonInfo. |
| */ |
| MHD_socket listen_fd; |
| |
| /** |
| * Listen socket is non-blocking. |
| */ |
| bool listen_nonblk; |
| |
| #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) |
| /** |
| * Worker daemons (one per thread) |
| */ |
| struct MHD_Daemon *worker_pool; |
| #endif |
| |
| /** |
| * Table storing number of connections per IP |
| */ |
| void *per_ip_connection_count; |
| |
| /** |
| * Number of active parallel connections. |
| * |
| * @remark Keep this member after pointer value to keep it |
| * properly aligned as it will be used as member of union MHD_DaemonInfo. |
| */ |
| unsigned int connections; |
| |
| /** |
| * Size of the per-connection memory pools. |
| */ |
| size_t pool_size; |
| |
| /** |
| * Increment for growth of the per-connection memory pools. |
| */ |
| size_t pool_increment; |
| |
| #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) |
| /** |
| * Size of threads created by MHD. |
| */ |
| size_t thread_stack_size; |
| |
| /** |
| * Number of worker daemons |
| */ |
| unsigned int worker_pool_size; |
| |
| /** |
| * The select thread handle (if we have internal select) |
| */ |
| MHD_thread_handle_ID_ pid; |
| |
| /** |
| * Mutex for per-IP connection counts. |
| */ |
| MHD_mutex_ per_ip_connection_mutex; |
| |
| /** |
| * Mutex for (modifying) access to the "cleanup", "normal_timeout" and |
| * "manual_timeout" DLLs. |
| */ |
| MHD_mutex_ cleanup_connection_mutex; |
| |
| /** |
| * Mutex for any access to the "new connections" DL-list. |
| */ |
| MHD_mutex_ new_connections_mutex; |
| #endif |
| |
| /** |
| * Our #MHD_OPTION_SERVER_INSANITY level, bits indicating |
| * which sanity checks are off. |
| */ |
| enum MHD_DisableSanityCheck insanity_level; |
| |
| /** |
| * Whether to allow/disallow/ignore reuse of listening address. |
| * The semantics is the following: |
| * 0: ignore (user did not ask for neither allow/disallow, use SO_REUSEADDR |
| * except W32) |
| * >0: allow (use SO_REUSEPORT on most platforms, SO_REUSEADDR on Windows) |
| * <0: disallow (mostly no action, SO_EXCLUSIVEADDRUSE on Windows or SO_EXCLBIND |
| * on Solaris) |
| */ |
| int listening_address_reuse; |
| |
| |
| /** |
| * Inter-thread communication channel (also used to unblock |
| * select() in non-threaded code). |
| */ |
| struct MHD_itc_ itc; |
| |
| /** |
| * Are we shutting down? |
| */ |
| volatile bool shutdown; |
| |
| /** |
| * Has this daemon been quiesced via #MHD_quiesce_daemon()? |
| * If so, we should no longer use the @e listen_fd (including |
| * removing it from the @e epoll_fd when possible). |
| */ |
| volatile bool was_quiesced; |
| |
| /** |
| * Did we hit some system or process-wide resource limit while |
| * trying to accept() the last time? If so, we don't accept new |
| * connections until we close an existing one. This effectively |
| * temporarily lowers the "connection_limit" to the current |
| * number of connections. |
| */ |
| bool at_limit; |
| |
| /* |
| * Do we need to process resuming connections? |
| */ |
| volatile bool resuming; |
| |
| /** |
| * Indicate that new connections in @e new_connections_head list |
| * need to be processed. |
| */ |
| volatile bool have_new; |
| |
| /** |
| * 'True' if some data is already waiting to be processed. |
| * If set to 'true' - zero timeout for select()/poll*() |
| * is used. |
| * Should be reset each time before processing connections |
| * and raised by any connection which require additional |
| * immediately processing (application does not provide |
| * data for response, data waiting in TLS buffers etc.) |
| */ |
| bool data_already_pending; |
| |
| /** |
| * Limit on the number of parallel connections. |
| */ |
| unsigned int connection_limit; |
| |
| /** |
| * After how many milliseconds of inactivity should |
| * this connection time out? |
| * Zero for no timeout. |
| */ |
| uint64_t connection_timeout_ms; |
| |
| /** |
| * Maximum number of connections per IP, or 0 for |
| * unlimited. |
| */ |
| unsigned int per_ip_connection_limit; |
| |
| /** |
| * The strictness level for parsing of incoming data. |
| * @see #MHD_OPTION_CLIENT_DISCIPLINE_LVL |
| */ |
| int client_discipline; |
| |
| /** |
| * True if SIGPIPE is blocked |
| */ |
| bool sigpipe_blocked; |
| |
| #ifdef HTTPS_SUPPORT |
| #ifdef UPGRADE_SUPPORT |
| /** |
| * Head of DLL of upgrade response handles we are processing. |
| * Used for upgraded TLS connections when thread-per-connection |
| * is not used. |
| */ |
| struct MHD_UpgradeResponseHandle *urh_head; |
| |
| /** |
| * Tail of DLL of upgrade response handles we are processing. |
| * Used for upgraded TLS connections when thread-per-connection |
| * is not used. |
| */ |
| struct MHD_UpgradeResponseHandle *urh_tail; |
| #endif /* UPGRADE_SUPPORT */ |
| |
| /** |
| * Desired cipher algorithms. |
| */ |
| gnutls_priority_t priority_cache; |
| |
| /** |
| * What kind of credentials are we offering |
| * for SSL/TLS? |
| */ |
| gnutls_credentials_type_t cred_type; |
| |
| /** |
| * Server x509 credentials |
| */ |
| gnutls_certificate_credentials_t x509_cred; |
| |
| /** |
| * Diffie-Hellman parameters |
| */ |
| gnutls_dh_params_t dh_params; |
| |
| /** |
| * Server PSK credentials |
| */ |
| gnutls_psk_server_credentials_t psk_cred; |
| |
| #if GNUTLS_VERSION_MAJOR >= 3 |
| /** |
| * Function that can be used to obtain the certificate. Needed |
| * for SNI support. See #MHD_OPTION_HTTPS_CERT_CALLBACK. |
| */ |
| gnutls_certificate_retrieve_function2 *cert_callback; |
| |
| /** |
| * Function that can be used to obtain the shared key. |
| */ |
| MHD_PskServerCredentialsCallback cred_callback; |
| |
| /** |
| * Closure for @e cred_callback. |
| */ |
| void *cred_callback_cls; |
| #endif |
| |
| #if GNUTLS_VERSION_NUMBER >= 0x030603 |
| /** |
| * Function that can be used to obtain the certificate. Needed |
| * for OCSP stapling support. See #MHD_OPTION_HTTPS_CERT_CALLBACK2. |
| */ |
| gnutls_certificate_retrieve_function3 *cert_callback2; |
| #endif |
| |
| /** |
| * Pointer to our SSL/TLS key (in ASCII) in memory. |
| */ |
| const char *https_mem_key; |
| |
| /** |
| * Pointer to our SSL/TLS certificate (in ASCII) in memory. |
| */ |
| const char *https_mem_cert; |
| |
| /** |
| * Pointer to 0-terminated HTTPS passphrase in memory. |
| */ |
| const char *https_key_password; |
| |
| /** |
| * Pointer to our SSL/TLS certificate authority (in ASCII) in memory. |
| */ |
| const char *https_mem_trust; |
| |
| /** |
| * Our Diffie-Hellman parameters in memory. |
| */ |
| gnutls_dh_params_t https_mem_dhparams; |
| |
| /** |
| * true if we have initialized @e https_mem_dhparams. |
| */ |
| bool have_dhparams; |
| |
| /** |
| * true if ALPN is disabled. |
| */ |
| bool disable_alpn; |
| |
| #endif /* HTTPS_SUPPORT */ |
| |
| #ifdef DAUTH_SUPPORT |
| |
| /** |
| * Character array of random values. |
| */ |
| const char *digest_auth_random; |
| |
| /** |
| * The malloc'ed copy of the @a digest_auth_random. |
| */ |
| void *digest_auth_random_copy; |
| |
| /** |
| * An array that contains the map nonce-nc. |
| */ |
| struct MHD_NonceNc *nnc; |
| |
| #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) |
| /** |
| * A rw-lock for synchronizing access to @e nnc. |
| */ |
| MHD_mutex_ nnc_lock; |
| #endif |
| |
| /** |
| * Size of `digest_auth_random. |
| */ |
| size_t digest_auth_rand_size; |
| |
| /** |
| * Size of the nonce-nc array. |
| */ |
| unsigned int nonce_nc_size; |
| |
| /** |
| * Nonce bind type. |
| */ |
| unsigned int dauth_bind_type; |
| #endif |
| |
| #ifdef TCP_FASTOPEN |
| /** |
| * The queue size for incoming SYN + DATA packets. |
| */ |
| unsigned int fastopen_queue_size; |
| #endif |
| |
| /** |
| * The size of queue for listen socket. |
| */ |
| unsigned int listen_backlog_size; |
| |
| /** |
| * The number of user options used. |
| * |
| * Contains number of only meaningful options, i.e. #MHD_OPTION_END |
| * and #MHD_OPTION_ARRAY are not counted, while options inside |
| * #MHD_OPTION_ARRAY are counted. |
| */ |
| size_t num_opts; |
| |
| /* TODO: replace with a single member */ |
| /** |
| * The value to be returned by #MHD_get_daemon_info() |
| */ |
| union MHD_DaemonInfo daemon_info_dummy_listen_fd; |
| |
| #ifdef EPOLL_SUPPORT |
| /** |
| * The value to be returned by #MHD_get_daemon_info() |
| */ |
| union MHD_DaemonInfo daemon_info_dummy_epoll_fd; |
| #endif /* EPOLL_SUPPORT */ |
| |
| /** |
| * The value to be returned by #MHD_get_daemon_info() |
| */ |
| union MHD_DaemonInfo daemon_info_dummy_num_connections; |
| |
| /** |
| * The value to be returned by #MHD_get_daemon_info() |
| */ |
| union MHD_DaemonInfo daemon_info_dummy_flags; |
| |
| /** |
| * The value to be returned by #MHD_get_daemon_info() |
| */ |
| union MHD_DaemonInfo daemon_info_dummy_port; |
| }; |
| |
| |
| #ifdef DAUTH_SUPPORT |
| |
| /** |
| * Parameter of request's Digest Authorization header |
| */ |
| struct MHD_RqDAuthParam |
| { |
| /** |
| * The string with length, NOT zero-terminated |
| */ |
| struct _MHD_str_w_len value; |
| /** |
| * True if string must be "unquoted" before processing. |
| * This member is false if the string is used in DQUOTE marks, but no |
| * backslash-escape is used in the string. |
| */ |
| bool quoted; |
| }; |
| |
| /** |
| * Request client's Digest Authorization header parameters |
| */ |
| struct MHD_RqDAuth |
| { |
| struct MHD_RqDAuthParam nonce; |
| struct MHD_RqDAuthParam opaque; |
| struct MHD_RqDAuthParam response; |
| struct MHD_RqDAuthParam username; |
| struct MHD_RqDAuthParam username_ext; |
| struct MHD_RqDAuthParam realm; |
| struct MHD_RqDAuthParam uri; |
| /* The raw QOP value, used in the 'response' calculation */ |
| struct MHD_RqDAuthParam qop_raw; |
| struct MHD_RqDAuthParam cnonce; |
| struct MHD_RqDAuthParam nc; |
| |
| /* Decoded values are below */ |
| bool userhash; /* True if 'userhash' parameter has value 'true'. */ |
| enum MHD_DigestAuthAlgo3 algo3; |
| enum MHD_DigestAuthQOP qop; |
| }; |
| |
| |
| #endif /* DAUTH_SUPPORT */ |
| |
| /** |
| * Insert an element at the head of a DLL. Assumes that head, tail and |
| * element are structs with prev and next fields. |
| * |
| * @param head pointer to the head of the DLL |
| * @param tail pointer to the tail of the DLL |
| * @param element element to insert |
| */ |
| #define DLL_insert(head,tail,element) do { \ |
| mhd_assert (NULL == (element)->next); \ |
| mhd_assert (NULL == (element)->prev); \ |
| (element)->next = (head); \ |
| (element)->prev = NULL; \ |
| if ((tail) == NULL) { \ |
| (tail) = element; \ |
| } else { \ |
| (head)->prev = element; \ |
| } \ |
| (head) = (element); } while (0) |
| |
| |
| /** |
| * Remove an element from a DLL. Assumes |
| * that head, tail and element are structs |
| * with prev and next fields. |
| * |
| * @param head pointer to the head of the DLL |
| * @param tail pointer to the tail of the DLL |
| * @param element element to remove |
| */ |
| #define DLL_remove(head,tail,element) do { \ |
| mhd_assert ( (NULL != (element)->next) || ((element) == (tail))); \ |
| mhd_assert ( (NULL != (element)->prev) || ((element) == (head))); \ |
| if ((element)->prev == NULL) { \ |
| (head) = (element)->next; \ |
| } else { \ |
| (element)->prev->next = (element)->next; \ |
| } \ |
| if ((element)->next == NULL) { \ |
| (tail) = (element)->prev; \ |
| } else { \ |
| (element)->next->prev = (element)->prev; \ |
| } \ |
| (element)->next = NULL; \ |
| (element)->prev = NULL; } while (0) |
| |
| |
| /** |
| * Insert an element at the head of a XDLL. Assumes that head, tail and |
| * element are structs with prevX and nextX fields. |
| * |
| * @param head pointer to the head of the XDLL |
| * @param tail pointer to the tail of the XDLL |
| * @param element element to insert |
| */ |
| #define XDLL_insert(head,tail,element) do { \ |
| mhd_assert (NULL == (element)->nextX); \ |
| mhd_assert (NULL == (element)->prevX); \ |
| (element)->nextX = (head); \ |
| (element)->prevX = NULL; \ |
| if (NULL == (tail)) { \ |
| (tail) = element; \ |
| } else { \ |
| (head)->prevX = element; \ |
| } \ |
| (head) = (element); } while (0) |
| |
| |
| /** |
| * Remove an element from a XDLL. Assumes |
| * that head, tail and element are structs |
| * with prevX and nextX fields. |
| * |
| * @param head pointer to the head of the XDLL |
| * @param tail pointer to the tail of the XDLL |
| * @param element element to remove |
| */ |
| #define XDLL_remove(head,tail,element) do { \ |
| mhd_assert ( (NULL != (element)->nextX) || ((element) == (tail))); \ |
| mhd_assert ( (NULL != (element)->prevX) || ((element) == (head))); \ |
| if (NULL == (element)->prevX) { \ |
| (head) = (element)->nextX; \ |
| } else { \ |
| (element)->prevX->nextX = (element)->nextX; \ |
| } \ |
| if (NULL == (element)->nextX) { \ |
| (tail) = (element)->prevX; \ |
| } else { \ |
| (element)->nextX->prevX = (element)->prevX; \ |
| } \ |
| (element)->nextX = NULL; \ |
| (element)->prevX = NULL; } while (0) |
| |
| |
| /** |
| * Insert an element at the head of a EDLL. Assumes that head, tail and |
| * element are structs with prevE and nextE fields. |
| * |
| * @param head pointer to the head of the EDLL |
| * @param tail pointer to the tail of the EDLL |
| * @param element element to insert |
| */ |
| #define EDLL_insert(head,tail,element) do { \ |
| (element)->nextE = (head); \ |
| (element)->prevE = NULL; \ |
| if ((tail) == NULL) { \ |
| (tail) = element; \ |
| } else { \ |
| (head)->prevE = element; \ |
| } \ |
| (head) = (element); } while (0) |
| |
| |
| /** |
| * Remove an element from a EDLL. Assumes |
| * that head, tail and element are structs |
| * with prevE and nextE fields. |
| * |
| * @param head pointer to the head of the EDLL |
| * @param tail pointer to the tail of the EDLL |
| * @param element element to remove |
| */ |
| #define EDLL_remove(head,tail,element) do { \ |
| if ((element)->prevE == NULL) { \ |
| (head) = (element)->nextE; \ |
| } else { \ |
| (element)->prevE->nextE = (element)->nextE; \ |
| } \ |
| if ((element)->nextE == NULL) { \ |
| (tail) = (element)->prevE; \ |
| } else { \ |
| (element)->nextE->prevE = (element)->prevE; \ |
| } \ |
| (element)->nextE = NULL; \ |
| (element)->prevE = NULL; } while (0) |
| |
| |
| /** |
| * Convert all occurrences of '+' to ' '. |
| * |
| * @param arg string that is modified (in place), must be 0-terminated |
| */ |
| void |
| MHD_unescape_plus (char *arg); |
| |
| |
| /** |
| * Callback invoked when iterating over @a key / @a value |
| * argument pairs during parsing. |
| * |
| * @param cls context of the iteration |
| * @param key 0-terminated key string, never NULL |
| * @param key_size number of bytes in key |
| * @param value 0-terminated binary data, may include binary zeros, may be NULL |
| * @param value_size number of bytes in value |
| * @param kind origin of the key-value pair |
| * @return #MHD_YES on success (continue to iterate) |
| * #MHD_NO to signal failure (and abort iteration) |
| */ |
| typedef enum MHD_Result |
| (*MHD_ArgumentIterator_)(void *cls, |
| const char *key, |
| size_t key_size, |
| const char *value, |
| size_t value_size, |
| enum MHD_ValueKind kind); |
| |
| |
| /** |
| * Parse and unescape the arguments given by the client |
| * as part of the HTTP request URI. |
| * |
| * @param kind header kind to pass to @a cb |
| * @param connection connection to add headers to |
| * @param[in,out] args argument URI string (after "?" in URI), |
| * clobbered in the process! |
| * @param cb function to call on each key-value pair found |
| * @param cls the iterator context |
| * @return #MHD_NO on failure (@a cb returned #MHD_NO), |
| * #MHD_YES for success (parsing succeeded, @a cb always |
| * returned #MHD_YES) |
| */ |
| enum MHD_Result |
| MHD_parse_arguments_ (struct MHD_Connection *connection, |
| enum MHD_ValueKind kind, |
| char *args, |
| MHD_ArgumentIterator_ cb, |
| void *cls); |
| |
| |
| /** |
| * Check whether response header contains particular token. |
| * |
| * Token could be surrounded by spaces and tabs and delimited by comma. |
| * Case-insensitive match used for header names and tokens. |
| * |
| * @param response the response to query |
| * @param key header name |
| * @param key_len the length of @a key, not including optional |
| * terminating null-character. |
| * @param token the token to find |
| * @param token_len the length of @a token, not including optional |
| * terminating null-character. |
| * @return true if token is found in specified header, |
| * false otherwise |
| */ |
| bool |
| MHD_check_response_header_token_ci (const struct MHD_Response *response, |
| const char *key, |
| size_t key_len, |
| const char *token, |
| size_t token_len); |
| |
| /** |
| * Check whether response header contains particular static @a tkn. |
| * |
| * Token could be surrounded by spaces and tabs and delimited by comma. |
| * Case-insensitive match used for header names and tokens. |
| * @param r the response to query |
| * @param k header name |
| * @param tkn the static string of token to find |
| * @return true if token is found in specified header, |
| * false otherwise |
| */ |
| #define MHD_check_response_header_s_token_ci(r,k,tkn) \ |
| MHD_check_response_header_token_ci ((r),(k),MHD_STATICSTR_LEN_ (k), \ |
| (tkn),MHD_STATICSTR_LEN_ (tkn)) |
| |
| |
| /** |
| * Internal version of #MHD_suspend_connection(). |
| * |
| * @remark In thread-per-connection mode: can be called from any thread, |
| * in any other mode: to be called only from thread that process |
| * daemon's select()/poll()/etc. |
| * |
| * @param connection the connection to suspend |
| */ |
| void |
| internal_suspend_connection_ (struct MHD_Connection *connection); |
| |
| |
| /** |
| * Trace up to and return master daemon. If the supplied daemon |
| * is a master, then return the daemon itself. |
| * |
| * @param daemon handle to a daemon |
| * @return master daemon handle |
| */ |
| _MHD_static_inline struct MHD_Daemon * |
| MHD_get_master (struct MHD_Daemon *const daemon) |
| { |
| struct MHD_Daemon *ret; |
| |
| if (NULL != daemon->master) |
| ret = daemon->master; |
| else |
| ret = daemon; |
| mhd_assert (NULL == ret->master); |
| |
| return ret; |
| } |
| |
| |
| #ifdef UPGRADE_SUPPORT |
| /** |
| * Mark upgraded connection as closed by application. |
| * |
| * The @a connection pointer must not be used after call of this function |
| * as it may be freed in other thread immediately. |
| * @param connection the upgraded connection to mark as closed by application |
| */ |
| void |
| MHD_upgraded_connection_mark_app_closed_ (struct MHD_Connection *connection); |
| #endif /* UPGRADE_SUPPORT */ |
| |
| |
| #endif |