mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
2929 lines
74 KiB
C
2929 lines
74 KiB
C
/*
|
|
This file is part of libmicrohttpd
|
|
Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
|
|
Copyright (C) 2014-2023 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).
|
|
*
|
|
* Currently set to reasonable maximum MSS size.
|
|
*/
|
|
#define MHD_BUF_INC_SIZE 1500
|
|
|
|
#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.
|
|
*
|
|
* A milestone state. No received data is processed in this state.
|
|
*/
|
|
MHD_CONNECTION_REQ_LINE_RECEIVED = MHD_CONNECTION_REQ_LINE_RECEIVING + 1,
|
|
|
|
/**
|
|
* Receiving request headers. Wait for the rest of the headers.
|
|
*/
|
|
MHD_CONNECTION_REQ_HEADERS_RECEIVING = MHD_CONNECTION_REQ_LINE_RECEIVED + 1,
|
|
|
|
/**
|
|
* We got the request headers. Process them.
|
|
*/
|
|
MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_REQ_HEADERS_RECEIVING + 1,
|
|
|
|
/**
|
|
* We have processed the request headers. Call application callback.
|
|
*/
|
|
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.
|
|
*
|
|
* A milestone state. No received data is processed in this state.
|
|
*/
|
|
MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_BODY_RECEIVING + 1,
|
|
|
|
/**
|
|
* We are reading the request footers.
|
|
*/
|
|
MHD_CONNECTION_FOOTERS_RECEIVING = MHD_CONNECTION_BODY_RECEIVED + 1,
|
|
|
|
/**
|
|
* We received the entire footer.
|
|
*
|
|
* A milestone state. No received data is processed in this state.
|
|
*/
|
|
MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTERS_RECEIVING + 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 RFC9110) 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;
|
|
|
|
|
|
/**
|
|
* The request line processing data
|
|
*/
|
|
struct MHD_RequestLineProcessing
|
|
{
|
|
/**
|
|
* The position of the next character to be processed
|
|
*/
|
|
size_t proc_pos;
|
|
/**
|
|
* The number of empty lines skipped
|
|
*/
|
|
unsigned int skipped_empty_lines;
|
|
/**
|
|
* The position of the start of the current/last found whitespace block,
|
|
* zero if not found yet.
|
|
*/
|
|
size_t last_ws_start;
|
|
/**
|
|
* The position of the next character after the last known whitespace
|
|
* character in the current/last found whitespace block,
|
|
* zero if not found yet.
|
|
*/
|
|
size_t last_ws_end;
|
|
/**
|
|
* The pointer to the request target.
|
|
* The request URI will be formed based on it.
|
|
*/
|
|
char *rq_tgt;
|
|
/**
|
|
* The pointer to the first question mark in the @a rq_tgt.
|
|
*/
|
|
char *rq_tgt_qmark;
|
|
/**
|
|
* The number of whitespace characters in the request URI
|
|
*/
|
|
size_t num_ws_in_uri;
|
|
};
|
|
|
|
/**
|
|
* The request header processing data
|
|
*/
|
|
struct MHD_HeaderProcessing
|
|
{
|
|
/**
|
|
* The position of the last processed character
|
|
*/
|
|
size_t proc_pos;
|
|
|
|
/**
|
|
* The position of the first whitespace character in current contiguous
|
|
* whitespace block.
|
|
* Zero when no whitespace found or found non-whitespace character after
|
|
* whitespace.
|
|
* Must be zero, if the current character is not whitespace.
|
|
*/
|
|
size_t ws_start;
|
|
|
|
/**
|
|
* Indicates that end of the header (field) name found.
|
|
* Must be false until the first colon in line is found.
|
|
*/
|
|
bool name_end_found;
|
|
|
|
/**
|
|
* The length of the header name.
|
|
* Must be zero until the first colon in line is found.
|
|
* Name always starts at zero position.
|
|
*/
|
|
size_t name_len;
|
|
|
|
/**
|
|
* The position of the first character of the header value.
|
|
* Zero when the first character has not been found yet.
|
|
*/
|
|
size_t value_start;
|
|
|
|
/**
|
|
* Line starts with whitespace.
|
|
* It's meaningful only for the first line, as other lines should be handled
|
|
* as "folded".
|
|
*/
|
|
bool starts_with_ws;
|
|
};
|
|
|
|
/**
|
|
* The union of request line and header processing data
|
|
*/
|
|
union MHD_HeadersProcessing
|
|
{
|
|
/**
|
|
* The request line processing data
|
|
*/
|
|
struct MHD_RequestLineProcessing rq_line;
|
|
|
|
/**
|
|
* The request header processing data
|
|
*/
|
|
struct MHD_HeaderProcessing hdr;
|
|
};
|
|
|
|
|
|
/**
|
|
* The union of text staring point and the size of the text
|
|
*/
|
|
union MHD_StartOrSize
|
|
{
|
|
/**
|
|
* The starting point of the text.
|
|
* Valid when the text is being processed and the end of the text
|
|
* is not yet determined.
|
|
*/
|
|
const char *start;
|
|
/**
|
|
* The size of the text.
|
|
* Valid when the text has been processed and the end of the text
|
|
* is known.
|
|
*/
|
|
size_t size;
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* The original length of the request target.
|
|
*/
|
|
size_t req_target_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.
|
|
* This includes the request line, all request headers, the header section
|
|
* terminating empty line, with all CRLF (or LF) characters.
|
|
*/
|
|
size_t header_size;
|
|
|
|
/**
|
|
* The union of the size of all request field lines (headers) and
|
|
* the starting point of the first request field line (the first header).
|
|
* Until #MHD_CONNECTION_HEADERS_RECEIVED the @a start member is valid,
|
|
* staring with #MHD_CONNECTION_HEADERS_RECEIVED the @a size member is valid.
|
|
* The size includes CRLF (or LR) characters, but does not include
|
|
* the terminating empty line.
|
|
*/
|
|
union MHD_StartOrSize field_lines;
|
|
|
|
/**
|
|
* 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 (from the currently
|
|
* processed chunk for chunked uploads) 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. This allow the application handler
|
|
* process the data by the fixed portions or other way suitable for
|
|
* application developer.
|
|
* If no data have been processed, than MHD will wait for more data
|
|
* to come (as it makes no sense to call the same connection handler
|
|
* under the same conditions). However this is dangerous as if buffer
|
|
* is completely used then connection is aborted. Connection
|
|
* suspension should be used in such case.
|
|
*/
|
|
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 */
|
|
/**
|
|
* Number of bare CR characters that were replaced with space characters
|
|
* in the request line or in the headers (field lines).
|
|
*/
|
|
size_t num_cr_sp_replaced;
|
|
|
|
/**
|
|
* The number of header lines skipped because they have no colon
|
|
*/
|
|
size_t skipped_broken_lines;
|
|
|
|
/**
|
|
* The data of the request line / request headers processing
|
|
*/
|
|
union MHD_HeadersProcessing hdrs;
|
|
};
|
|
|
|
|
|
/**
|
|
* 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_ tid;
|
|
#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;
|
|
|
|
/**
|
|
* Are we currently in the #MHD_AccessHandlerCallback
|
|
* for this connection (and thus eligible to receive
|
|
* calls to #MHD_queue_response()?).
|
|
*/
|
|
bool in_access_handler;
|
|
|
|
/**
|
|
* 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_ tid;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
#ifdef HAS_FD_SETSIZE_OVERRIDABLE
|
|
/**
|
|
* The value of FD_SETSIZE used by the daemon.
|
|
* For external sockets polling this is the value provided by the application
|
|
* via MHD_OPTION_APP_FD_SETSIZE or current FD_SETSIZE value.
|
|
* For internal threads modes this is always current FD_SETSIZE value.
|
|
*/
|
|
int fdset_size;
|
|
|
|
/**
|
|
* Indicates whether @a fdset_size value was set by application.
|
|
* 'false' if default value is used.
|
|
*/
|
|
bool fdset_size_set_by_app;
|
|
#endif /* HAS_FD_SETSIZE_OVERRIDABLE */
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* Size of @a digest_auth_random.
|
|
*/
|
|
size_t digest_auth_rand_size;
|
|
|
|
/**
|
|
* 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 the nonce-nc array.
|
|
*/
|
|
unsigned int nonce_nc_size;
|
|
|
|
/**
|
|
* Nonce bind type.
|
|
*/
|
|
unsigned int dauth_bind_type;
|
|
|
|
/**
|
|
* Default nonce validity length.
|
|
*/
|
|
unsigned int dauth_def_nonce_timeout;
|
|
|
|
/**
|
|
* Default maximum nc (nonce count) value.
|
|
*/
|
|
uint32_t dauth_def_max_nc;
|
|
#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;
|
|
|
|
/* 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;
|
|
|
|
#if defined(_DEBUG) && defined(HAVE_ACCEPT4)
|
|
/**
|
|
* If set to 'true', accept() function will be used instead of accept4() even
|
|
* if accept4() is available.
|
|
* This is a workaround for zzuf, which does not support sockets created
|
|
* by accept4() function.
|
|
* There is no API to change the value of this member, it can be flipped
|
|
* only by direct access to the struct member.
|
|
*/
|
|
bool avoid_accept4;
|
|
#endif /* _DEBUG */
|
|
};
|
|
|
|
|
|
#if defined(HAVE_POLL) && defined(EPOLL_SUPPORT)
|
|
/**
|
|
* Checks whether the @a d daemon is using select()
|
|
*/
|
|
#define MHD_D_IS_USING_SELECT_(d) \
|
|
(0 == (d->options & (MHD_USE_POLL | MHD_USE_EPOLL)))
|
|
/**
|
|
* Checks whether the @a d daemon is using poll()
|
|
*/
|
|
#define MHD_D_IS_USING_POLL_(d) (0 != ((d)->options & MHD_USE_POLL))
|
|
/**
|
|
* Checks whether the @a d daemon is using epoll
|
|
*/
|
|
#define MHD_D_IS_USING_EPOLL_(d) (0 != ((d)->options & MHD_USE_EPOLL))
|
|
#elif defined(HAVE_POLL)
|
|
/**
|
|
* Checks whether the @a d daemon is using select()
|
|
*/
|
|
#define MHD_D_IS_USING_SELECT_(d) (0 == ((d)->options & MHD_USE_POLL))
|
|
/**
|
|
* Checks whether the @a d daemon is using poll()
|
|
*/
|
|
#define MHD_D_IS_USING_POLL_(d) (0 != ((d)->options & MHD_USE_POLL))
|
|
/**
|
|
* Checks whether the @a d daemon is using epoll
|
|
*/
|
|
#define MHD_D_IS_USING_EPOLL_(d) ((void) (d), 0)
|
|
#elif defined(EPOLL_SUPPORT)
|
|
/**
|
|
* Checks whether the @a d daemon is using select()
|
|
*/
|
|
#define MHD_D_IS_USING_SELECT_(d) (0 == ((d)->options & MHD_USE_EPOLL))
|
|
/**
|
|
* Checks whether the @a d daemon is using poll()
|
|
*/
|
|
#define MHD_D_IS_USING_POLL_(d) ((void) (d), 0)
|
|
/**
|
|
* Checks whether the @a d daemon is using epoll
|
|
*/
|
|
#define MHD_D_IS_USING_EPOLL_(d) (0 != ((d)->options & MHD_USE_EPOLL))
|
|
#else /* select() only */
|
|
/**
|
|
* Checks whether the @a d daemon is using select()
|
|
*/
|
|
#define MHD_D_IS_USING_SELECT_(d) ((void) (d), ! 0)
|
|
/**
|
|
* Checks whether the @a d daemon is using poll()
|
|
*/
|
|
#define MHD_D_IS_USING_POLL_(d) ((void) (d), 0)
|
|
/**
|
|
* Checks whether the @a d daemon is using epoll
|
|
*/
|
|
#define MHD_D_IS_USING_EPOLL_(d) ((void) (d), 0)
|
|
#endif /* select() only */
|
|
|
|
#if defined(MHD_USE_THREADS)
|
|
/**
|
|
* Checks whether the @a d daemon is using internal polling thread
|
|
*/
|
|
#define MHD_D_IS_USING_THREADS_(d) \
|
|
(0 != (d->options & (MHD_USE_INTERNAL_POLLING_THREAD)))
|
|
/**
|
|
* Checks whether the @a d daemon is using thread-per-connection mode
|
|
*/
|
|
#define MHD_D_IS_USING_THREAD_PER_CONN_(d) \
|
|
(0 != ((d)->options & MHD_USE_THREAD_PER_CONNECTION))
|
|
|
|
/**
|
|
* Check whether the @a d daemon has thread-safety enabled.
|
|
*/
|
|
#define MHD_D_IS_THREAD_SAFE_(d) \
|
|
(0 == ((d)->options & MHD_USE_NO_THREAD_SAFETY))
|
|
#else /* ! MHD_USE_THREADS */
|
|
/**
|
|
* Checks whether the @a d daemon is using internal polling thread
|
|
*/
|
|
#define MHD_D_IS_USING_THREADS_(d) ((void) d, 0)
|
|
/**
|
|
* Checks whether the @a d daemon is using thread-per-connection mode
|
|
*/
|
|
#define MHD_D_IS_USING_THREAD_PER_CONN_(d) ((void) d, 0)
|
|
|
|
/**
|
|
* Check whether the @a d daemon has thread-safety enabled.
|
|
*/
|
|
#define MHD_D_IS_THREAD_SAFE_(d) ((void) d, 0)
|
|
#endif /* ! MHD_USE_THREADS */
|
|
|
|
#ifdef HAS_FD_SETSIZE_OVERRIDABLE
|
|
/**
|
|
* Get FD_SETSIZE used by the daemon @a d
|
|
*/
|
|
#define MHD_D_GET_FD_SETSIZE_(d) ((d)->fdset_size)
|
|
#else /* ! HAS_FD_SETSIZE_OVERRIDABLE */
|
|
/**
|
|
* Get FD_SETSIZE used by the daemon @a d
|
|
*/
|
|
#define MHD_D_GET_FD_SETSIZE_(d) (FD_SETSIZE)
|
|
#endif /* ! HAS_FD_SETSIZE_OVERRIDABLE */
|
|
|
|
/**
|
|
* Check whether socket @a sckt fits fd_sets used by the daemon @a d
|
|
*/
|
|
#define MHD_D_DOES_SCKT_FIT_FDSET_(sckt,d) \
|
|
MHD_SCKT_FD_FITS_FDSET_SETSIZE_(sckt,NULL,MHD_D_GET_FD_SETSIZE_(d))
|
|
|
|
|
|
#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
|