/*
     This file is part of libmicrohttpd
     Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
     Copyright (C) 2015-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 connection.c
 * @brief  Methods for managing connections
 * @author Daniel Pittman
 * @author Christian Grothoff
 * @author Karlson2k (Evgeny Grin)
 */
#include "internal.h"
#include "mhd_limits.h"
#include "connection.h"
#include "memorypool.h"
#include "response.h"
#include "mhd_mono_clock.h"
#include "mhd_str.h"
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
#include "mhd_locks.h"
#endif
#include "mhd_sockets.h"
#include "mhd_compat.h"
#include "mhd_itc.h"
#ifdef MHD_LINUX_SOLARIS_SENDFILE
#include <sys/sendfile.h>
#endif /* MHD_LINUX_SOLARIS_SENDFILE */
#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
#ifdef HTTPS_SUPPORT
#include "connection_https.h"
#endif /* HTTPS_SUPPORT */
#ifdef HAVE_SYS_PARAM_H
/* For FreeBSD version identification */
#include <sys/param.h>
#endif /* HAVE_SYS_PARAM_H */
#include "mhd_send.h"
#include "mhd_assert.h"

/**
 * Message to transmit when http 1.1 request is received
 */
#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"

/**
 * Response text used when the request (http header) is too big to
 * be processed.
 *
 * Intentionally empty here to keep our memory footprint
 * minimal.
 */
#ifdef HAVE_MESSAGES
#define REQUEST_TOO_BIG \
  "<html><head><title>Request too big</title></head><body>Your HTTP header was too big for the memory constraints of this webserver.</body></html>"
#else
#define REQUEST_TOO_BIG ""
#endif

/**
 * Response text used when the request (http header) does not
 * contain a "Host:" header and still claims to be HTTP 1.1.
 *
 * Intentionally empty here to keep our memory footprint
 * minimal.
 */
#ifdef HAVE_MESSAGES
#define REQUEST_LACKS_HOST \
  "<html><head><title>&quot;Host:&quot; header required</title></head><body>In HTTP 1.1, requests must include a &quot;Host:&quot; header, and your HTTP 1.1 request lacked such a header.</body></html>"
#else
#define REQUEST_LACKS_HOST ""
#endif

/**
 * Response text used when the request (http header) is
 * malformed.
 *
 * Intentionally empty here to keep our memory footprint
 * minimal.
 */
#ifdef HAVE_MESSAGES
#define REQUEST_MALFORMED \
  "<html><head><title>Request malformed</title></head><body>Your HTTP request was syntactically incorrect.</body></html>"
#else
#define REQUEST_MALFORMED ""
#endif

/**
 * Response text used when the request HTTP chunked encoding is
 * malformed.
 */
#ifdef HAVE_MESSAGES
#define REQUEST_CHUNKED_MALFORMED \
  "<html><head><title>Request malformed</title></head><body>Your HTTP chunked encoding was syntactically incorrect.</body></html>"
#else
#define REQUEST_CHUNKED_MALFORMED ""
#endif

/**
 * Response text used when the request HTTP chunk is too large.
 */
#ifdef HAVE_MESSAGES
#define REQUEST_CHUNK_TOO_LARGE \
  "<html><head><title>Request content too large</title></head><body>The chunk size used in your HTTP chunked encoded request is too large.</body></html>"
#else
#define REQUEST_CHUNK_TOO_LARGE ""
#endif

/**
 * Response text used when the request HTTP content is too large.
 */
#ifdef HAVE_MESSAGES
#define REQUEST_CONTENTLENGTH_TOOLARGE \
  "<html><head><title>Request content too large</title></head>" \
  "<body>Your HTTP request has too large value for <b>Content-Length</b> header.</body></html>"
#else
#define REQUEST_CONTENTLENGTH_TOOLARGE ""
#endif

/**
 * Response text used when the request HTTP chunked encoding is
 * malformed.
 */
#ifdef HAVE_MESSAGES
#define REQUEST_CONTENTLENGTH_MALFORMED \
  "<html><head><title>Request malformed</title></head>" \
  "<body>Your HTTP request has wrong value for <b>Content-Length</b> header.</body></html>"
#else
#define REQUEST_CONTENTLENGTH_MALFORMED ""
#endif

/**
 * Response text used when there is an internal server error.
 *
 * Intentionally empty here to keep our memory footprint
 * minimal.
 */
#ifdef HAVE_MESSAGES
#define INTERNAL_ERROR \
  "<html><head><title>Internal server error</title></head><body>Please ask the developer of this Web server to carefully read the GNU libmicrohttpd documentation about connection management and blocking.</body></html>"
#else
#define INTERNAL_ERROR ""
#endif

/**
 * Response text used when the request HTTP version is too old.
 */
#ifdef HAVE_MESSAGES
#define REQ_HTTP_VER_IS_TOO_OLD \
  "<html><head><title>Requested HTTP version is not supported</title></head><body>Requested HTTP version is too old and not supported.</body></html>"
#else
#define REQ_HTTP_VER_IS_TOO_OLD ""
#endif

/**
 * Response text used when the request HTTP version is not supported.
 */
#ifdef HAVE_MESSAGES
#define REQ_HTTP_VER_IS_NOT_SUPPORTED \
  "<html><head><title>Requested HTTP version is not supported</title></head><body>Requested HTTP version is not supported.</body></html>"
#else
#define REQ_HTTP_VER_IS_NOT_SUPPORTED ""
#endif


/**
 * sendfile() chuck size
 */
#define MHD_SENFILE_CHUNK_         (0x20000)

/**
 * sendfile() chuck size for thread-per-connection
 */
#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)

#ifdef HAVE_MESSAGES
/**
 * Return text description for MHD_ERR_*_ codes
 * @param mhd_err_code the error code
 * @return pointer to static string with error description
 */
static const char *
str_conn_error_ (ssize_t mhd_err_code)
{
  switch (mhd_err_code)
  {
  case MHD_ERR_AGAIN_:
    return _ ("The operation would block, retry later");
  case MHD_ERR_CONNRESET_:
    return _ ("The connection was forcibly closed by remote peer");
  case MHD_ERR_NOTCONN_:
    return _ ("The socket is not connected");
  case MHD_ERR_NOMEM_:
    return _ ("Not enough system resources to serve the request");
  case MHD_ERR_BADF_:
    return _ ("Bad FD value");
  case MHD_ERR_INVAL_:
    return _ ("Argument value is invalid");
  case MHD_ERR_OPNOTSUPP_:
    return _ ("Argument value is not supported");
  case MHD_ERR_PIPE_:
    return _ ("The socket is no longer available for sending");
  case MHD_ERR_TLS_:
    return _ ("TLS encryption or decryption error");
  default:
    break;   /* Mute compiler warning */
  }
  if (0 <= mhd_err_code)
    return _ ("Not an error code");

  mhd_assert (0); /* Should never be reachable */
  return _ ("Wrong error code value");
}


#endif /* HAVE_MESSAGES */

/**
 * Allocate memory from connection's memory pool.
 * If memory pool doesn't have enough free memory but read of write buffer
 * have some unused memory, the size of the buffer will be reduced as needed.
 * @param connection the connection to use
 * @param size the size of allocated memory area
 * @return pointer to allocated memory region in the pool or
 *         NULL if no memory is available
 */
static void *
connection_alloc_memory (struct MHD_Connection *connection,
                         size_t size)
{
  struct MHD_Connection *const c = connection; /* a short alias */
  struct MemoryPool *const pool = c->pool;     /* a short alias */
  size_t need_to_free; /**< The required amount of free memory */
  void *res;

  res = MHD_pool_try_alloc (pool, size, &need_to_free);
  if (NULL == res)
  {
    if (NULL != c->write_buffer)
    {
      /* The connection is in the sending phase */
      mhd_assert (MHD_CONNECTION_START_REPLY <= c->state);
      if (c->write_buffer_size - c->write_buffer_append_offset >= need_to_free)
      {
        char *buf;
        const size_t new_buf_size = c->write_buffer_size - need_to_free;
        buf = MHD_pool_reallocate (pool,
                                   c->write_buffer,
                                   c->write_buffer_size,
                                   new_buf_size);
        mhd_assert (c->write_buffer == buf);
        mhd_assert (c->write_buffer_append_offset <= new_buf_size);
        mhd_assert (c->write_buffer_send_offset <= new_buf_size);
        c->write_buffer_size = new_buf_size;
        c->write_buffer = buf;
      }
      else
        return NULL;
    }
    else if (NULL != c->read_buffer)
    {
      /* The connection is in the receiving phase */
      if (c->read_buffer_size - c->read_buffer_offset >= need_to_free)
      {
        char *buf;
        const size_t new_buf_size = c->read_buffer_size - need_to_free;
        buf = MHD_pool_reallocate (pool,
                                   c->read_buffer,
                                   c->read_buffer_size,
                                   new_buf_size);
        mhd_assert (c->read_buffer == buf);
        mhd_assert (c->read_buffer_offset <= new_buf_size);
        c->read_buffer_size = new_buf_size;
        c->read_buffer = buf;
      }
      else
        return NULL;
    }
    else
      return NULL;
    res = MHD_pool_allocate (pool, size, true);
    mhd_assert (NULL != res); /* It has been checked that pool has enough space */
  }
  return res;
}


/**
 * Callback for receiving data from the socket.
 *
 * @param connection the MHD connection structure
 * @param other where to write received data to
 * @param i maximum size of other (in bytes)
 * @return positive value for number of bytes actually received or
 *         negative value for error number MHD_ERR_xxx_
 */
static ssize_t
recv_param_adapter (struct MHD_Connection *connection,
                    void *other,
                    size_t i)
{
  ssize_t ret;

  if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
       (MHD_CONNECTION_CLOSED == connection->state) )
  {
    return MHD_ERR_NOTCONN_;
  }
  if (i > MHD_SCKT_SEND_MAX_SIZE_)
    i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */

  ret = MHD_recv_ (connection->socket_fd,
                   other,
                   i);
  if (0 > ret)
  {
    const int err = MHD_socket_get_error_ ();
    if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
    {
#ifdef EPOLL_SUPPORT
      /* Got EAGAIN --- no longer read-ready */
      connection->epoll_state &=
        ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
#endif /* EPOLL_SUPPORT */
      return MHD_ERR_AGAIN_;
    }
    if (MHD_SCKT_ERR_IS_EINTR_ (err))
      return MHD_ERR_AGAIN_;
    if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
      return MHD_ERR_CONNRESET_;
    if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EOPNOTSUPP_))
      return MHD_ERR_OPNOTSUPP_;
    if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_))
      return MHD_ERR_NOTCONN_;
    if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINVAL_))
      return MHD_ERR_INVAL_;
    if (MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err))
      return MHD_ERR_NOMEM_;
    if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EBADF_))
      return MHD_ERR_BADF_;
    /* Treat any other error as a hard error. */
    return MHD_ERR_NOTCONN_;
  }
#ifdef EPOLL_SUPPORT
  else if (i > (size_t) ret)
    connection->epoll_state &=
      ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
#endif /* EPOLL_SUPPORT */
  return ret;
}


/**
 * Get all of the headers from the request.
 *
 * @param connection connection to get values from
 * @param kind types of values to iterate over, can be a bitmask
 * @param iterator callback to call on each header;
 *        maybe NULL (then just count headers)
 * @param iterator_cls extra argument to @a iterator
 * @return number of entries iterated over
 *         -1 if connection is NULL.
 * @ingroup request
 */
int
MHD_get_connection_values (struct MHD_Connection *connection,
                           enum MHD_ValueKind kind,
                           MHD_KeyValueIterator iterator,
                           void *iterator_cls)
{
  int ret;
  struct MHD_HTTP_Header *pos;

  if (NULL == connection)
    return -1;
  ret = 0;
  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
    if (0 != (pos->kind & kind))
    {
      ret++;
      if ( (NULL != iterator) &&
           (MHD_NO == iterator (iterator_cls,
                                pos->kind,
                                pos->header,
                                pos->value)) )
        return ret;
    }
  return ret;
}


/**
 * Get all of the headers from the request.
 *
 * @param connection connection to get values from
 * @param kind types of values to iterate over, can be a bitmask
 * @param iterator callback to call on each header;
 *        maybe NULL (then just count headers)
 * @param iterator_cls extra argument to @a iterator
 * @return number of entries iterated over,
 *         -1 if connection is NULL.
 * @ingroup request
 */
int
MHD_get_connection_values_n (struct MHD_Connection *connection,
                             enum MHD_ValueKind kind,
                             MHD_KeyValueIteratorN iterator,
                             void *iterator_cls)
{
  int ret;
  struct MHD_HTTP_Header *pos;

  if (NULL == connection)
    return -1;
  ret = 0;

  if (NULL == iterator)
    for (pos = connection->headers_received; NULL != pos; pos = pos->next)
    {
      if (0 != (kind & pos->kind))
        ret++;
    }
  else
    for (pos = connection->headers_received; NULL != pos; pos = pos->next)
      if (0 != (kind & pos->kind))
      {
        ret++;
        if (MHD_NO == iterator (iterator_cls,
                                pos->kind,
                                pos->header,
                                pos->header_size,
                                pos->value,
                                pos->value_size))
          return ret;
      }
  return ret;
}


/**
 * This function can be used to add an arbitrary entry to connection.
 * Internal version of #MHD_set_connection_value_n() without checking
 * of arguments values.
 *
 * @param connection the connection for which a
 *                   value should be set
 * @param kind kind of the value
 * @param key key for the value, must be zero-terminated
 * @param key_size number of bytes in @a key (excluding 0-terminator)
 * @param value the value itself, must be zero-terminated
 * @param value_size number of bytes in @a value (excluding 0-terminator)
 * @return #MHD_NO if the operation could not be
 *         performed due to insufficient memory;
 *         #MHD_YES on success
 * @ingroup request
 */
static enum MHD_Result
MHD_set_connection_value_n_nocheck_ (struct MHD_Connection *connection,
                                     enum MHD_ValueKind kind,
                                     const char *key,
                                     size_t key_size,
                                     const char *value,
                                     size_t value_size)
{
  struct MHD_HTTP_Header *pos;

  pos = connection_alloc_memory (connection,
                                 sizeof (struct MHD_HTTP_Header));
  if (NULL == pos)
    return MHD_NO;
  pos->header = (char *) key;
  pos->header_size = key_size;
  pos->value = (char *) value;
  pos->value_size = value_size;
  pos->kind = kind;
  pos->next = NULL;
  /* append 'pos' to the linked list of headers */
  if (NULL == connection->headers_received_tail)
  {
    connection->headers_received = pos;
    connection->headers_received_tail = pos;
  }
  else
  {
    connection->headers_received_tail->next = pos;
    connection->headers_received_tail = pos;
  }
  return MHD_YES;
}


/**
 * This function can be used to add an arbitrary entry to connection.
 * This function could add entry with binary zero, which is allowed
 * for #MHD_GET_ARGUMENT_KIND. For other kind on entries it is
 * recommended to use #MHD_set_connection_value.
 *
 * This function MUST only be called from within the
 * #MHD_AccessHandlerCallback (otherwise, access maybe improperly
 * synchronized).  Furthermore, the client must guarantee that the key
 * and value arguments are 0-terminated strings that are NOT freed
 * until the connection is closed.  (The easiest way to do this is by
 * passing only arguments to permanently allocated strings.).
 *
 * @param connection the connection for which a
 *  value should be set
 * @param kind kind of the value
 * @param key key for the value, must be zero-terminated
 * @param key_size number of bytes in @a key (excluding 0-terminator)
 * @param value the value itself, must be zero-terminated
 * @param value_size number of bytes in @a value (excluding 0-terminator)
 * @return #MHD_NO if the operation could not be
 *         performed due to insufficient memory;
 *         #MHD_YES on success
 * @ingroup request
 */
enum MHD_Result
MHD_set_connection_value_n (struct MHD_Connection *connection,
                            enum MHD_ValueKind kind,
                            const char *key,
                            size_t key_size,
                            const char *value,
                            size_t value_size)
{
  if ( (MHD_GET_ARGUMENT_KIND != kind) &&
       ( ((key ? strlen (key) : 0) != key_size) ||
         ((value ? strlen (value) : 0) != value_size) ) )
    return MHD_NO; /* binary zero is allowed only in GET arguments */

  return MHD_set_connection_value_n_nocheck_ (connection,
                                              kind,
                                              key,
                                              key_size,
                                              value,
                                              value_size);
}


/**
 * This function can be used to add an entry to the HTTP headers of a
 * connection (so that the #MHD_get_connection_values function will
 * return them -- and the `struct MHD_PostProcessor` will also see
 * them).  This maybe required in certain situations (see Mantis
 * #1399) where (broken) HTTP implementations fail to supply values
 * needed by the post processor (or other parts of the application).
 *
 * This function MUST only be called from within the
 * #MHD_AccessHandlerCallback (otherwise, access maybe improperly
 * synchronized).  Furthermore, the client must guarantee that the key
 * and value arguments are 0-terminated strings that are NOT freed
 * until the connection is closed.  (The easiest way to do this is by
 * passing only arguments to permanently allocated strings.).
 *
 * @param connection the connection for which a
 *  value should be set
 * @param kind kind of the value
 * @param key key for the value
 * @param value the value itself
 * @return #MHD_NO if the operation could not be
 *         performed due to insufficient memory;
 *         #MHD_YES on success
 * @ingroup request
 */
enum MHD_Result
MHD_set_connection_value (struct MHD_Connection *connection,
                          enum MHD_ValueKind kind,
                          const char *key,
                          const char *value)
{
  return MHD_set_connection_value_n_nocheck_ (connection,
                                              kind,
                                              key,
                                              NULL != key
                                              ? strlen (key)
                                              : 0,
                                              value,
                                              NULL != value
                                              ? strlen (value)
                                              : 0);
}


/**
 * Get a particular header value.  If multiple
 * values match the kind, return any one of them.
 *
 * @param connection connection to get values from
 * @param kind what kind of value are we looking for
 * @param key the header to look for, NULL to lookup 'trailing' value without a key
 * @return NULL if no such item was found
 * @ingroup request
 */
const char *
MHD_lookup_connection_value (struct MHD_Connection *connection,
                             enum MHD_ValueKind kind,
                             const char *key)
{
  const char *value;

  value = NULL;
  (void) MHD_lookup_connection_value_n (connection,
                                        kind,
                                        key,
                                        (NULL == key) ? 0 : strlen (key),
                                        &value,
                                        NULL);
  return value;
}


/**
 * Get a particular header value.  If multiple
 * values match the kind, return any one of them.
 * @note Since MHD_VERSION 0x00096304
 *
 * @param connection connection to get values from
 * @param kind what kind of value are we looking for
 * @param key the header to look for, NULL to lookup 'trailing' value without a key
 * @param key_size the length of @a key in bytes
 * @param[out] value_ptr the pointer to variable, which will be set to found value,
 *                       will not be updated if key not found,
 *                       could be NULL to just check for presence of @a key
 * @param[out] value_size_ptr the pointer variable, which will set to found value,
 *                            will not be updated if key not found,
 *                            could be NULL
 * @return #MHD_YES if key is found,
 *         #MHD_NO otherwise.
 * @ingroup request
 */
_MHD_EXTERN enum MHD_Result
MHD_lookup_connection_value_n (struct MHD_Connection *connection,
                               enum MHD_ValueKind kind,
                               const char *key,
                               size_t key_size,
                               const char **value_ptr,
                               size_t *value_size_ptr)
{
  struct MHD_HTTP_Header *pos;

  if (NULL == connection)
    return MHD_NO;

  if (NULL == key)
  {
    for (pos = connection->headers_received; NULL != pos; pos = pos->next)
    {
      if ( (0 != (kind & pos->kind)) &&
           (NULL == pos->header) )
        break;
    }
  }
  else
  {
    for (pos = connection->headers_received; NULL != pos; pos = pos->next)
    {
      if ( (0 != (kind & pos->kind)) &&
           (key_size == pos->header_size) &&
           ( (key == pos->header) ||
             (MHD_str_equal_caseless_bin_n_ (key,
                                             pos->header,
                                             key_size) ) ) )
        break;
    }
  }

  if (NULL == pos)
    return MHD_NO;

  if (NULL != value_ptr)
    *value_ptr = pos->value;

  if (NULL != value_size_ptr)
    *value_size_ptr = pos->value_size;

  return MHD_YES;
}


/**
 * Check whether request 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 connection the connection to get values from
 * @param header     the header name
 * @param header_len the length of header, not including optional
 *                   terminating null-character
 * @param token      the token to find
 * @param token_len  the length of token, not including optional
 *                   terminating null-character.
 * @return true if token is found in specified header,
 *         false otherwise
 */
static bool
MHD_lookup_header_token_ci (const struct MHD_Connection *connection,
                            const char *header,
                            size_t header_len,
                            const char *token,
                            size_t token_len)
{
  struct MHD_HTTP_Header *pos;

  if ((NULL == connection) || (NULL == header) || (0 == header[0]) || (NULL ==
                                                                       token) ||
      (0 ==
       token
       [
         0]) )
    return false;

  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
  {
    if ((0 != (pos->kind & MHD_HEADER_KIND)) &&
        (header_len == pos->header_size) &&
        ( (header == pos->header) ||
          (MHD_str_equal_caseless_bin_n_ (header,
                                          pos->header,
                                          header_len)) ) &&
        (MHD_str_has_token_caseless_ (pos->value, token, token_len)))
      return true;
  }
  return false;
}


/**
 * Check whether request 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 c   the connection to get values from
 * @param h   the static string of header name
 * @param tkn the static string of token to find
 * @return true if token is found in specified header,
 *         false otherwise
 */
#define MHD_lookup_header_s_token_ci(c,h,tkn) \
  MHD_lookup_header_token_ci ((c),(h),MHD_STATICSTR_LEN_ (h), \
                              (tkn),MHD_STATICSTR_LEN_ (tkn))


/**
 * Do we (still) need to send a 100 continue
 * message for this connection?
 *
 * @param connection connection to test
 * @return false if we don't need 100 CONTINUE, true if we do
 */
static bool
need_100_continue (struct MHD_Connection *connection)
{
  const char *expect;

  return (MHD_IS_HTTP_VER_1_1_COMPAT (connection->http_ver) &&
          (MHD_NO != MHD_lookup_connection_value_n (connection,
                                                    MHD_HEADER_KIND,
                                                    MHD_HTTP_HEADER_EXPECT,
                                                    MHD_STATICSTR_LEN_ (
                                                      MHD_HTTP_HEADER_EXPECT),
                                                    &expect,
                                                    NULL)) &&
          (MHD_str_equal_caseless_ (expect,
                                    "100-continue")) );
}


/**
 * Mark connection as "closed".
 * @remark To be called from any thread.
 *
 * @param connection connection to close
 */
void
MHD_connection_mark_closed_ (struct MHD_Connection *connection)
{
  const struct MHD_Daemon *daemon = connection->daemon;

  if (0 == (daemon->options & MHD_USE_TURBO))
  {
#ifdef HTTPS_SUPPORT
    /* For TLS connection use shutdown of TLS layer
     * and do not shutdown TCP socket. This give more
     * chances to send TLS closure data to remote side.
     * Closure of TLS layer will be interpreted by
     * remote side as end of transmission. */
    if (0 != (daemon->options & MHD_USE_TLS))
    {
      if (! MHD_tls_connection_shutdown (connection))
        shutdown (connection->socket_fd,
                  SHUT_WR);
    }
    else   /* Combined with next 'shutdown()'. */
#endif /* HTTPS_SUPPORT */
    shutdown (connection->socket_fd,
              SHUT_WR);
  }
  connection->state = MHD_CONNECTION_CLOSED;
  connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
}


/**
 * Close the given connection and give the
 * specified termination code to the user.
 * @remark To be called only from thread that
 * process connection's recv(), send() and response.
 *
 * @param connection connection to close
 * @param termination_code termination reason to give
 */
void
MHD_connection_close_ (struct MHD_Connection *connection,
                       enum MHD_RequestTerminationCode termination_code)
{
  struct MHD_Daemon *daemon = connection->daemon;
  struct MHD_Response *resp = connection->response;

#ifdef MHD_USE_THREADS
  mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
               MHD_thread_ID_match_current_ (connection->pid) );
#endif /* MHD_USE_THREADS */
  if ( (NULL != daemon->notify_completed) &&
       (connection->client_aware) )
    daemon->notify_completed (daemon->notify_completed_cls,
                              connection,
                              &connection->client_context,
                              termination_code);
  connection->client_aware = false;
  if (NULL != resp)
  {
    connection->response = NULL;
    MHD_destroy_response (resp);
  }
  if (NULL != connection->pool)
  {
    MHD_pool_destroy (connection->pool);
    connection->pool = NULL;
  }

  MHD_connection_mark_closed_ (connection);
}


#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
/**
 * Stop TLS forwarding on upgraded connection and
 * reflect remote disconnect state to socketpair.
 * @remark In thread-per-connection mode this function
 * can be called from any thread, in other modes this
 * function must be called only from thread that process
 * daemon's select()/poll()/etc.
 *
 * @param connection the upgraded connection
 */
void
MHD_connection_finish_forward_ (struct MHD_Connection *connection)
{
  struct MHD_Daemon *daemon = connection->daemon;
  struct MHD_UpgradeResponseHandle *urh = connection->urh;

#ifdef MHD_USE_THREADS
  mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
               (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || \
               MHD_thread_ID_match_current_ (daemon->pid) );
#endif /* MHD_USE_THREADS */

  if (0 == (daemon->options & MHD_USE_TLS))
    return; /* Nothing to do with non-TLS connection. */

  if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
    DLL_remove (daemon->urh_head,
                daemon->urh_tail,
                urh);
#if EPOLL_SUPPORT
  if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
       (0 != epoll_ctl (daemon->epoll_upgrade_fd,
                        EPOLL_CTL_DEL,
                        connection->socket_fd,
                        NULL)) )
  {
    MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
  }
  if (urh->in_eready_list)
  {
    EDLL_remove (daemon->eready_urh_head,
                 daemon->eready_urh_tail,
                 urh);
    urh->in_eready_list = false;
  }
#endif /* EPOLL_SUPPORT */
  if (MHD_INVALID_SOCKET != urh->mhd.socket)
  {
#if EPOLL_SUPPORT
    if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
         (0 != epoll_ctl (daemon->epoll_upgrade_fd,
                          EPOLL_CTL_DEL,
                          urh->mhd.socket,
                          NULL)) )
    {
      MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
    }
#endif /* EPOLL_SUPPORT */
    /* Reflect remote disconnect to application by breaking
     * socketpair connection. */
    shutdown (urh->mhd.socket, SHUT_RDWR);
  }
  /* Socketpair sockets will remain open as they will be
   * used with MHD_UPGRADE_ACTION_CLOSE. They will be
   * closed by cleanup_upgraded_connection() during
   * connection's final cleanup.
   */
}


#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/


/**
 * A serious error occurred, close the
 * connection (and notify the application).
 *
 * @param connection connection to close with error
 * @param emsg error message (can be NULL)
 */
static void
connection_close_error (struct MHD_Connection *connection,
                        const char *emsg)
{
  connection->stop_with_error = true;
  connection->discard_request = true;
#ifdef HAVE_MESSAGES
  if (NULL != emsg)
    MHD_DLOG (connection->daemon,
              "%s\n",
              emsg);
#else  /* ! HAVE_MESSAGES */
  (void) emsg; /* Mute compiler warning. */
#endif /* ! HAVE_MESSAGES */
  MHD_connection_close_ (connection,
                         MHD_REQUEST_TERMINATED_WITH_ERROR);
}


/**
 * Macro to only include error message in call to
 * #connection_close_error() if we have HAVE_MESSAGES.
 */
#ifdef HAVE_MESSAGES
#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
#else
#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
#endif


/**
 * A serious error occurred, check whether error response is already
 * queued and close the connection if response wasn't queued.
 *
 * @param connection connection to close with error
 * @param emsg error message (can be NULL)
 */
static void
connection_close_error_check (struct MHD_Connection *connection,
                              const char *emsg)
{
  if ( (NULL != connection->response) &&
       (400 <= connection->responseCode) &&
       (NULL == connection->response->crc) && /* Static response only! */
       (connection->stop_with_error) &&
       (MHD_CONNECTION_HEADERS_SENDING == connection->state) )
    return; /* An error response was already queued */

  connection_close_error (connection, emsg);
}


/**
 * Macro to only include error message in call to
 * #connection_close_error_check() if we have HAVE_MESSAGES.
 */
#ifdef HAVE_MESSAGES
#define CONNECTION_CLOSE_ERROR_CHECK(c, emsg) \
  connection_close_error_check (c, emsg)
#else
#define CONNECTION_CLOSE_ERROR_CHECK(c, emsg) \
  connection_close_error_check (c, NULL)
#endif


/**
 * Prepare the response buffer of this connection for
 * sending.  Assumes that the response mutex is
 * already held.  If the transmission is complete,
 * this function may close the socket (and return
 * #MHD_NO).
 *
 * @param connection the connection
 * @return #MHD_NO if readying the response failed (the
 *  lock on the response will have been released already
 *  in this case).
 */
static enum MHD_Result
try_ready_normal_body (struct MHD_Connection *connection)
{
  ssize_t ret;
  struct MHD_Response *response;

  response = connection->response;
  mhd_assert (connection->rp_props.send_reply_body);

  if ( (0 == response->total_size) ||
                     /* TODO: replace the next check with assert */
       (connection->response_write_position == response->total_size) )
    return MHD_YES;  /* 0-byte response is always ready */
  if (NULL != response->data_iov)
  {
    size_t copy_size;

    if (NULL != connection->resp_iov.iov)
      return MHD_YES;
    copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
    connection->resp_iov.iov = connection_alloc_memory (connection,
                                                        copy_size);
    if (NULL == connection->resp_iov.iov)
    {
      MHD_mutex_unlock_chk_ (&response->mutex);
      /* not enough memory */
      CONNECTION_CLOSE_ERROR (connection,
                              _ ("Closing connection (out of memory)."));
      return MHD_NO;
    }
    memcpy (connection->resp_iov.iov,
            response->data_iov,
            copy_size);
    connection->resp_iov.cnt = response->data_iovcnt;
    connection->resp_iov.sent = 0;
    return MHD_YES;
  }
  if (NULL == response->crc)
    return MHD_YES;
  if ( (response->data_start <=
        connection->response_write_position) &&
       (response->data_size + response->data_start >
        connection->response_write_position) )
    return MHD_YES; /* response already ready */
#if defined(_MHD_HAVE_SENDFILE)
  if (MHD_resp_sender_sendfile == connection->resp_sender)
  {
    /* will use sendfile, no need to bother response crc */
    return MHD_YES;
  }
#endif /* _MHD_HAVE_SENDFILE */

  ret = response->crc (response->crc_cls,
                       connection->response_write_position,
                       response->data,
                       (size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
                                         response->total_size
                                         - connection->response_write_position));
  if ( (MHD_CONTENT_READER_END_OF_STREAM == ret) ||
       (MHD_CONTENT_READER_END_WITH_ERROR == ret) )
  {
    /* either error or http 1.0 transfer, close socket! */
    /* TODO: do not update total size, check whether response
     * was really with unknown size */
    response->total_size = connection->response_write_position;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
    MHD_mutex_unlock_chk_ (&response->mutex);
#endif
    if (MHD_CONTENT_READER_END_OF_STREAM == ret)
      MHD_connection_close_ (connection,
                             MHD_REQUEST_TERMINATED_COMPLETED_OK);
    else
      CONNECTION_CLOSE_ERROR (connection,
                              _ (
                                "Closing connection (application reported error generating data)."));
    return MHD_NO;
  }
  response->data_start = connection->response_write_position;
  response->data_size = ret;
  if (0 == ret)
  {
    connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
    MHD_mutex_unlock_chk_ (&response->mutex);
#endif
    return MHD_NO;
  }
  return MHD_YES;
}


/**
 * Prepare the response buffer of this connection for sending.
 * Assumes that the response mutex is already held.  If the
 * transmission is complete, this function may close the socket (and
 * return #MHD_NO).
 *
 * @param connection the connection
 * @param[out] p_finished the pointer to variable that will be set to "true"
 *                        when application returned indication of the end
 *                        of the stream
 * @return #MHD_NO if readying the response failed
 */
static enum MHD_Result
try_ready_chunked_body (struct MHD_Connection *connection,
                        bool *p_finished)
{
  ssize_t ret;
  struct MHD_Response *response;
  static const size_t max_chunk = 0xFFFFFF;
  char chunk_hdr[6];            /* 6: max strlen of "FFFFFF" */
  /* "FFFFFF" + "\r\n" */
  static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
  /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
  static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
  size_t chunk_hdr_len;
  uint64_t left_to_send;
  size_t size_to_fill;

  response = connection->response;
  mhd_assert (NULL != response->crc || NULL != response->data);

  mhd_assert (0 == connection->write_buffer_append_offset);

  /* The buffer must be reasonably large enough */
  if (128 > connection->write_buffer_size)
  {
    size_t size;

    size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
    if (128 > size)
    {
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
      MHD_mutex_unlock_chk_ (&response->mutex);
#endif
      /* not enough memory */
      CONNECTION_CLOSE_ERROR (connection,
                              _ ("Closing connection (out of memory)."));
      return MHD_NO;
    }
    /* Limit the buffer size to the largest usable size for chunks */
    if ( (max_chunk + max_chunk_overhead) < size)
      size = max_chunk + max_chunk_overhead;
    connection->write_buffer = MHD_pool_reallocate (connection->pool,
                                                    connection->write_buffer,
                                                    connection->
                                                    write_buffer_size, size);
    mhd_assert (NULL != connection->write_buffer);
    connection->write_buffer_size = size;
  }
  mhd_assert (max_chunk_overhead < connection->write_buffer_size);

  if (MHD_SIZE_UNKNOWN == response->total_size)
    left_to_send = MHD_SIZE_UNKNOWN;
  else
    left_to_send = response->total_size - connection->response_write_position;

  size_to_fill = connection->write_buffer_size - max_chunk_overhead;
  /* Limit size for the callback to the max usable size */
  if (max_chunk < size_to_fill)
    size_to_fill = max_chunk;
  if (left_to_send < size_to_fill)
    size_to_fill = (size_t) left_to_send;

  if (0 == left_to_send)
    /* nothing to send, don't bother calling crc */
    ret = MHD_CONTENT_READER_END_OF_STREAM;
  else if ( (response->data_start <=
             connection->response_write_position) &&
            (response->data_start + response->data_size >
             connection->response_write_position) )
  {
    /* difference between response_write_position and data_start is less
       than data_size which is size_t type, no need to check for overflow */
    const size_t data_write_offset
      = (size_t) (connection->response_write_position - response->data_start);
    /* buffer already ready, use what is there for the chunk */
    ret = response->data_size - data_write_offset;
    if ( ((size_t) ret) > size_to_fill)
      ret = (ssize_t) size_to_fill;
    memcpy (&connection->write_buffer[max_chunk_hdr_len],
            &response->data[data_write_offset],
            ret);
  }
  else
  {
    if (NULL == response->crc)
    { /* There is no way to reach this code */
#if defined(MHD_USE_THREADS)
      MHD_mutex_unlock_chk_ (&response->mutex);
#endif
      CONNECTION_CLOSE_ERROR (connection,
                              _ ("No callback for the chunked data."));
      return MHD_NO;
    }
    ret = response->crc (response->crc_cls,
                         connection->response_write_position,
                         &connection->write_buffer[max_chunk_hdr_len],
                         size_to_fill);
  }
  if (MHD_CONTENT_READER_END_WITH_ERROR == ret)
  {
    /* error, close socket! */
    /* TODO: remove update of the response size */
    response->total_size = connection->response_write_position;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
    MHD_mutex_unlock_chk_ (&response->mutex);
#endif
    CONNECTION_CLOSE_ERROR (connection,
                            _ (
                              "Closing connection (application error generating response)."));
    return MHD_NO;
  }
  if (MHD_CONTENT_READER_END_OF_STREAM == ret)
  {
    *p_finished = true;
    /* TODO: remove update of the response size */
    response->total_size = connection->response_write_position;
    return MHD_YES;
  }
  if (0 == ret)
  {
    connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
    MHD_mutex_unlock_chk_ (&response->mutex);
#endif
    return MHD_NO;
  }
  if (size_to_fill < (size_t) ret)
  {
#if defined(MHD_USE_THREADS)
    MHD_mutex_unlock_chk_ (&response->mutex);
#endif
    CONNECTION_CLOSE_ERROR (connection,
                            _ ("Closing connection (application returned " \
                               "more data than requested)."));
    return MHD_NO;
  }
  chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret, chunk_hdr,
                                      sizeof(chunk_hdr));
  mhd_assert (chunk_hdr_len != 0);
  mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
  *p_finished = false;
  connection->write_buffer_send_offset =
    (max_chunk_hdr_len - (chunk_hdr_len + 2));
  memcpy (connection->write_buffer + connection->write_buffer_send_offset,
          chunk_hdr,
          chunk_hdr_len);
  connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
  connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
  connection->write_buffer[max_chunk_hdr_len + ret] = '\r';
  connection->write_buffer[max_chunk_hdr_len + ret + 1] = '\n';
  connection->response_write_position += ret;
  connection->write_buffer_append_offset = max_chunk_hdr_len + ret + 2;
  return MHD_YES;
}


/**
 * Are we allowed to keep the given connection alive?
 * We can use the TCP stream for a second request if the connection
 * is HTTP 1.1 and the "Connection" header either does not exist or
 * is not set to "close", or if the connection is HTTP 1.0 and the
 * "Connection" header is explicitly set to "keep-alive".
 * If no HTTP version is specified (or if it is not 1.0 or 1.1), we
 * definitively close the connection.  If the "Connection" header is
 * not exactly "close" or "keep-alive", we proceed to use the default
 * for the respective HTTP version.
 * If response has HTTP/1.0 flag or has "Connection: close" header
 * then connection must be closed.
 * If full request has not been read then connection must be closed
 * as well.
 *
 * @param connection the connection to check for keepalive
 * @return MHD_CONN_USE_KEEPALIVE if (based on the request and the response),
 *         a keepalive is legal,
 *         MHD_CONN_MUST_CLOSE if connection must be closed after sending
 *         complete reply,
 *         MHD_CONN_MUST_UPGRADE if connection must be upgraded.
 */
static enum MHD_ConnKeepAlive
keepalive_possible (struct MHD_Connection *connection)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  struct MHD_Response *const r = c->response;  /**< a short alias */

  mhd_assert (NULL != r);
  if (MHD_CONN_MUST_CLOSE == c->keepalive)
    return MHD_CONN_MUST_CLOSE;

#ifdef UPGRADE_SUPPORT
  /* TODO: Move below the next check when MHD stops closing connections
   * when response is queued in first callback */
  if (NULL != r->upgrade_handler)
  {
    /* No "close" token is enforced by 'add_response_header_connection()' */
    mhd_assert (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE));
    /* Valid HTTP version is enforced by 'MHD_queue_response()' */
    mhd_assert (MHD_IS_HTTP_VER_SUPPORTED (c->http_ver));
    mhd_assert (! c->stop_with_error);
    return MHD_CONN_MUST_UPGRADE;
  }
#endif /* UPGRADE_SUPPORT */

  mhd_assert ( (! c->stop_with_error) || (c->discard_request));
  if ((c->read_closed) || (c->discard_request))
    return MHD_CONN_MUST_CLOSE;

  if (0 != (r->flags & MHD_RF_HTTP_1_0_COMPATIBLE_STRICT))
    return MHD_CONN_MUST_CLOSE;
  if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
    return MHD_CONN_MUST_CLOSE;

  if (! MHD_IS_HTTP_VER_SUPPORTED (c->http_ver))
    return MHD_CONN_MUST_CLOSE;

  if (MHD_lookup_header_s_token_ci (c,
                                    MHD_HTTP_HEADER_CONNECTION,
                                    "close"))
    return MHD_CONN_MUST_CLOSE;

  if ((MHD_HTTP_VER_1_0 == connection->http_ver) ||
      (0 != (connection->response->flags & MHD_RF_HTTP_1_0_SERVER)))
  {
    if (MHD_lookup_header_s_token_ci (connection,
                                      MHD_HTTP_HEADER_CONNECTION,
                                      "Keep-Alive"))
      return MHD_CONN_USE_KEEPALIVE;

    return MHD_CONN_MUST_CLOSE;
  }

  if (MHD_IS_HTTP_VER_1_1_COMPAT (c->http_ver))
    return MHD_CONN_USE_KEEPALIVE;

  return MHD_CONN_MUST_CLOSE;
}


/**
 * Produce time stamp.
 *
 * Result is NOT null-terminated.
 * Result is always 29 bytes long.
 *
 * @param[out] date where to write the time stamp, with
 *             at least 29 bytes available space.
 */
static bool
get_date_str (char *date)
{
  static const char *const days[] = {
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  };
  static const char *const mons[] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  };
  static const size_t buf_len = 29;
  struct tm now;
  time_t t;
  const char *src;
#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
  ! defined(HAVE_GMTIME_R)
  struct tm *pNow;
#endif

  if ((time_t) -1 == time (&t))
    return false;
#if defined(HAVE_C11_GMTIME_S)
  if (NULL == gmtime_s (&t,
                        &now))
    return false;
#elif defined(HAVE_W32_GMTIME_S)
  if (0 != gmtime_s (&now,
                     &t))
    return false;
#elif defined(HAVE_GMTIME_R)
  if (NULL == gmtime_r (&t,
                        &now))
    return false;
#else
  pNow = gmtime (&t);
  if (NULL == pNow)
    return false;
  now = *pNow;
#endif

  /* Day of the week */
  src = days[now.tm_wday % 7];
  date[0] = src[0];
  date[1] = src[1];
  date[2] = src[2];
  date[3] = ',';
  date[4] = ' ';
  /* Day of the month */
  if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
                                 date + 5, buf_len - 5))
    return false;
  date[7] = ' ';
  /* Month */
  src = mons[now.tm_mon % 12];
  date[8] = src[0];
  date[9] = src[1];
  date[10] = src[2];
  date[11] = ' ';
  /* Year */
  if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
                              buf_len - 12))
    return false;
  date[16] = ' ';
  /* Time */
  MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
  date[19] = ':';
  MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
  date[22] = ':';
  MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
  date[25] = ' ';
  date[26] = 'G';
  date[27] = 'M';
  date[28] = 'T';

  return true;
}


/**
 * Produce HTTP DATE header.
 * Result is always 37 bytes long (plus one terminating null).
 *
 * @param[out] header where to write the header, with
 *             at least 38 bytes available space.
 */
static bool
get_date_header (char *header)
{
  if (! get_date_str (header + 6))
  {
    header[0] = 0;
    return false;
  }
  header[0] = 'D';
  header[1] = 'a';
  header[2] = 't';
  header[3] = 'e';
  header[4] = ':';
  header[5] = ' ';
  header[35] = '\r';
  header[36] = '\n';
  header[37] = 0;
  return true;
}


/**
 * Try growing the read buffer.  We initially claim half the available
 * buffer space for the read buffer (the other half being left for
 * management data structures; the write buffer can in the end take
 * virtually everything as the read buffer can be reduced to the
 * minimum necessary at that point.
 *
 * @param connection the connection
 * @param required set to 'true' if grow is required, i.e. connection
 *                 will fail if no additional space is granted
 * @return 'true' on success, 'false' on failure
 */
static bool
try_grow_read_buffer (struct MHD_Connection *connection,
                      bool required)
{
  size_t new_size;
  size_t avail_size;
  void *rb;

  avail_size = MHD_pool_get_free (connection->pool);
  if (0 == avail_size)
    return false;               /* No more space available */
  if (0 == connection->read_buffer_size)
    new_size = avail_size / 2;  /* Use half of available buffer for reading */
  else
  {
    size_t grow_size;

    grow_size = avail_size / 8;
    if (MHD_BUF_INC_SIZE > grow_size)
    {                  /* Shortage of space */
      if (! required)
        return false;  /* Grow is not mandatory, leave some space in pool */
      else
      {
        /* Shortage of space, but grow is mandatory */
        static const size_t small_inc = MHD_BUF_INC_SIZE / 8;
        if (small_inc < avail_size)
          grow_size = small_inc;
        else
          grow_size = avail_size;
      }
    }
    new_size = connection->read_buffer_size + grow_size;
  }
  /* we can actually grow the buffer, do it! */
  rb = MHD_pool_reallocate (connection->pool,
                            connection->read_buffer,
                            connection->read_buffer_size,
                            new_size);
  if (NULL == rb)
  {
    /* This should NOT be possible: we just computed 'new_size' so that
       it should fit. If it happens, somehow our read buffer is not in
       the right position in the pool, say because someone called
       MHD_pool_allocate() without 'from_end' set to 'true'? Anyway,
       should be investigated! (Ideally provide all data from
       *pool and connection->read_buffer and new_size for debugging). */
    mhd_assert (0);
    return false;
  }
  connection->read_buffer = rb;
  mhd_assert (NULL != connection->read_buffer);
  connection->read_buffer_size = new_size;
  return true;
}


/**
 * Shrink connection read buffer to the zero size of free space in the buffer
 * @param connection the connection whose read buffer is being manipulated
 */
static void
connection_shrink_read_buffer (struct MHD_Connection *connection)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  void *new_buf;

  if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
  {
    mhd_assert (0 == c->read_buffer_size);
    mhd_assert (0 == c->read_buffer_offset);
    return;
  }

  mhd_assert (c->read_buffer_offset <= c->read_buffer_size);
  new_buf = MHD_pool_reallocate (c->pool, c->read_buffer, c->read_buffer_size,
                                 c->read_buffer_offset);
  mhd_assert (c->read_buffer == new_buf);
  c->read_buffer = new_buf;
  c->read_buffer_size = c->read_buffer_offset;
}


/**
 * Allocate the maximum available amount of memory from MemoryPool
 * for write buffer.
 * @param connection the connection whose write buffer is being manipulated
 * @return the size of free space in write buffer, may be smaller
 *         than requested size.
 */
static size_t
connection_maximize_write_buffer (struct MHD_Connection *connection)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  struct MemoryPool *const pool = connection->pool;
  void *new_buf;
  size_t new_size;
  size_t free_size;

  mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
  mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset);
  mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset);

  free_size = MHD_pool_get_free (pool);
  if (0 != free_size)
  {
    new_size = c->write_buffer_size + free_size;
    /* This function must not move the buffer position.
     * MHD_pool_reallocate () may return the new position only if buffer was
     * allocated 'from_end' or is not the last allocation,
     * which should not happen. */
    new_buf = MHD_pool_reallocate (pool,
                                   c->write_buffer,
                                   c->write_buffer_size,
                                   new_size);
    mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
    c->write_buffer = new_buf;
    c->write_buffer_size = new_size;
    if (c->write_buffer_send_offset == c->write_buffer_append_offset)
    {
      /* All data have been sent, reset offsets to zero. */
      c->write_buffer_send_offset = 0;
      c->write_buffer_append_offset = 0;
    }
  }

  return c->write_buffer_size - c->write_buffer_append_offset;
}


#if 0 /* disable unused function */
/**
 * Shrink connection write buffer to the size of unsent data.
 *
 * @note: The number of calls of this function should be limited to avoid extra
 * zeroing of the memory.
 * @param connection the connection whose write buffer is being manipulated
 * @param connection the connection to manipulate write buffer
 */
static void
connection_shrink_write_buffer (struct MHD_Connection *connection)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  struct MemoryPool *const pool = connection->pool;
  void *new_buf;

  mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
  mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset);
  mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset);

  if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
  {
    mhd_assert (0 == c->write_buffer_append_offset);
    mhd_assert (0 == c->write_buffer_send_offset);
    c->write_buffer = NULL;
    return;
  }
  if (c->write_buffer_append_offset == c->write_buffer_size)
    return;

  new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
                                 c->write_buffer_append_offset);
  mhd_assert ((c->write_buffer == new_buf) || \
              (0 == c->write_buffer_append_offset));
  c->write_buffer_size = c->write_buffer_append_offset;
  if (0 == c->write_buffer_size)
    c->write_buffer = NULL;
  else
    c->write_buffer = new_buf;
}


#endif /* unused function */


/**
 * Switch connection from recv mode to send mode.
 *
 * Current request header or body will not be read anymore,
 * response must be assigned to connection.
 * @param connection the connection to prepare for sending.
 */
static void
connection_switch_from_recv_to_send (struct MHD_Connection *connection)
{
  /* Read buffer is not needed for this request, shrink it.*/
  connection_shrink_read_buffer (connection);
}


/**
 * Check whether reply body-specific headers (namely Content-Length,
 * Transfer-Encoding) are needed.
 *
 * If reply body-specific headers are not needed then body itself
 * is not allowed as well.
 * When reply body-specific headers are needed, the body itself
 * can be present or not, depending on other conditions.
 *
 * @param connection the connection to check
 * @return true if reply body-specific headers are needed,
 *         false otherwise.
 * @sa is_reply_body_needed()
 */
static bool
is_reply_body_headers_needed (struct MHD_Connection *connection)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  unsigned rcode;  /**< the response code */

  mhd_assert (100 <= (c->responseCode & (~MHD_ICY_FLAG)) && \
              999 >= (c->responseCode & (~MHD_ICY_FLAG)));

  rcode = (unsigned) (c->responseCode & (~MHD_ICY_FLAG));

  if (199 >= rcode)
    return false;

  if (MHD_HTTP_NO_CONTENT == rcode)
    return false;

#ifdef UPGRADE_SUPPORT
  if (NULL != c->response->upgrade_handler)
    return false;
#endif /* UPGRADE_SUPPORT */

  if ( (MHD_HTTP_MTHD_CONNECT == c->http_mthd) &&
       (2 == rcode / 100) )
    return false; /* Actually pass-through CONNECT is not supported by MHD */

  return true;
}


/**
 * Check whether reply body must be used.
 *
 * If reply body is needed, it could be zero-sized.
 *
 * @param connection the connection to check
 * @return true if reply body must be used,
 *         false otherwise
 * @sa is_reply_body_header_needed()
 */
static bool
is_reply_body_needed (struct MHD_Connection *connection)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  unsigned rcode;  /**< the response code */

  mhd_assert (100 <= (c->responseCode & (~MHD_ICY_FLAG)) && \
              999 >= (c->responseCode & (~MHD_ICY_FLAG)));

  if (! is_reply_body_headers_needed (c))
    return false;

  if (MHD_HTTP_MTHD_HEAD == c->http_mthd)
    return false;

  rcode = (unsigned) (c->responseCode & (~MHD_ICY_FLAG));
  if (MHD_HTTP_NOT_MODIFIED == rcode)
    return false;

  return true;
}


/**
 * Setup connection reply properties.
 *
 * Reply properties include presence of reply body, transfer-encoding
 * type and other.
 *
 * @param connection to connection to process
 * @param reply_body_allowed
 */
static void
setup_reply_properties (struct MHD_Connection *connection)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  struct MHD_Response *const r = c->response;  /**< a short alias */
  bool use_chunked;

  mhd_assert (NULL != r);

  /* ** Adjust reply properties ** */

  c->keepalive = keepalive_possible (c);
  c->rp_props.use_reply_body_headers = is_reply_body_headers_needed (c);
  if (c->rp_props.use_reply_body_headers)
    c->rp_props.send_reply_body = is_reply_body_needed (c);
  else
    c->rp_props.send_reply_body = false;

  if (c->rp_props.use_reply_body_headers)
  {
    if ((MHD_SIZE_UNKNOWN == r->total_size) ||
        (0 != (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED)))
    { /* Use chunked reply encoding if possible */

      /* Check whether chunked encoding is supported by the client */
      if (! MHD_IS_HTTP_VER_1_1_COMPAT (c->http_ver))
        use_chunked = false;
      /* Check whether chunked encoding is allowed for the reply */
      else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
                                 | MHD_RF_HTTP_1_0_SERVER)))
        use_chunked = false;
      else
        /* If chunked encoding is supported and allowed, and response size
         * is unknown, use chunked even for non-Keep-Alive connections.
         * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
         * Also use chunked if it is enforced by application and supported by
         * the client. */
        use_chunked = true;
    }
    else
      use_chunked = false;

    if ( (MHD_SIZE_UNKNOWN == r->total_size) && ! use_chunked)
    {
      /* End of the stream is indicated by closure */
      c->keepalive = MHD_CONN_MUST_CLOSE;
    }
  }
  else
    use_chunked = false; /* chunked encoding cannot be used without body */

  c->rp_props.chunked = use_chunked;
  c->rp_props.set = true;
}


/**
 * Append data to the buffer if enough space is available,
 * update position.
 * @param[out] buf the buffer to append data to
 * @param[in,out] ppos the pointer to position in the @a buffer
 * @param buf_size the size of the @a buffer
 * @param append the data to append
 * @param append_size the size of the @a append
 * @return true if data has been added and position has been updated,
 *         false if not enough space is available
 */
static bool
buffer_append (char *buf,
               size_t *ppos,
               size_t buf_size,
               const char *append,
               size_t append_size)
{
  mhd_assert (NULL != buf); /* Mute static analyzer */
  if (buf_size < *ppos + append_size)
    return false;
  memcpy (buf + *ppos, append, append_size);
  *ppos += append_size;
  return true;
}


/**
 * Append static string to the buffer if enough space is available,
 * update position.
 * @param[out] buf the buffer to append data to
 * @param[in,out] ppos the pointer to position in the @a buffer
 * @param buf_size the size of the @a buffer
 * @param str the static string to append
 * @return true if data has been added and position has been updated,
 *         false if not enough space is available
 */
#define buffer_append_s(buf,ppos,buf_size,str) \
  buffer_append(buf,ppos,buf_size,str, MHD_STATICSTR_LEN_(str))


/**
 * Add user-defined headers from response object to
 * the text buffer.
 *
 * @param buf the buffer to add headers to
 * @param ppos the pointer to the position in the @a buf
 * @param buf_size the size of the @a buf
 * @param response the response
 * @param kind the kind of objects (headers or footers)
 * @param filter_transf_enc skip "Transfer-Encoding" header if any
 * @param add_close add "close" token to the
 *                  "Connection:" header (if any), ignored if no "Connection:"
 *                  header was added by user or if "close" token is already
 *                  present in "Connection:" header
 * @param add_keep_alive add "Keep-Alive" token to the
 *                       "Connection:" header (if any)
 * @return true if succeed,
 *         false if buffer is too small
 */
static bool
add_user_headers (char *buf,
                  size_t *ppos,
                  size_t buf_size,
                  struct MHD_Response *response,
                  enum MHD_ValueKind kind,
                  bool filter_transf_enc,
                  bool add_close,
                  bool add_keep_alive)
{
  struct MHD_Response *const r = response; /**< a short alias */
  struct MHD_HTTP_Header *hdr; /**< Iterates through User-specified headers */
  size_t el_size; /**< the size of current element to be added to the @a buf */

  mhd_assert ((! filter_transf_enc) || MHD_HEADER_KIND == kind);
  mhd_assert ((! add_close) || MHD_HEADER_KIND == kind);
  mhd_assert ((! add_keep_alive) || MHD_HEADER_KIND == kind);
  mhd_assert (! add_close || ! add_keep_alive);

  if (0 == (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED))
    filter_transf_enc = false;  /* No such header */
  if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
  {
    add_close = false;          /* No such header */
    add_keep_alive = false;     /* No such header */
  }
  else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
    add_close = false;          /* "close" token was already set */

  for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
  {
    size_t initial_pos = *ppos;
    if (kind != hdr->kind)
      continue;
    if (filter_transf_enc)
    { /* Need to filter-out "Transfer-Encoding" */
      if ((MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING) ==
           hdr->header_size) &&
          (MHD_str_equal_caseless_bin_n_ (MHD_HTTP_HEADER_TRANSFER_ENCODING,
                                          hdr->header, hdr->header_size)) )
      {
        filter_transf_enc = false; /* There is the only one such header */
        continue; /* Skip "Transfer-Encoding" header */
      }
    }

    /* Add user header */
    el_size = hdr->header_size + 2 + hdr->value_size + 2;
    if (buf_size < *ppos + el_size)
      return false;
    memcpy (buf + *ppos, hdr->header, hdr->header_size);
    (*ppos) += hdr->header_size;
    buf[(*ppos)++] = ':';
    buf[(*ppos)++] = ' ';
    if (add_close || add_keep_alive)
    {
      /* "Connection:" header must be always the first one */
      mhd_assert (MHD_str_equal_caseless_n_ (hdr->header, \
                                             MHD_HTTP_HEADER_CONNECTION, \
                                             hdr->header_size));

      if (add_close)
      {
        el_size += MHD_STATICSTR_LEN_ ("close, ");
        if (buf_size < initial_pos + el_size)
          return false;
        memcpy (buf + *ppos, "close, ",
                MHD_STATICSTR_LEN_ ("close, "));
        *ppos += MHD_STATICSTR_LEN_ ("close, ");
      }
      else
      {
        el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
        if (buf_size < initial_pos + el_size)
          return false;
        memcpy (buf + *ppos, "Keep-Alive, ",
                MHD_STATICSTR_LEN_ ("Keep-Alive, "));
        *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
      }
      add_close = false;
      add_keep_alive = false;
    }
    if (0 != hdr->value_size)
      memcpy (buf + *ppos, hdr->value, hdr->value_size);
    *ppos += hdr->value_size;
    buf[(*ppos)++] = '\r';
    buf[(*ppos)++] = '\n';
    mhd_assert (initial_pos + el_size == (*ppos));
  }
  return true;
}


/**
 * Allocate the connection's write buffer and fill it with all of the
 * headers from the response.
 * Required headers are added here.
 *
 * @param connection the connection
 * @return #MHD_YES on success, #MHD_NO on failure (out of memory)
 */
static enum MHD_Result
build_header_response (struct MHD_Connection *connection)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  struct MHD_Response *const r = c->response;  /**< a short alias */
  char *buf;                                   /**< the output buffer */
  size_t pos;                                  /**< append offset in the @a buf */
  size_t buf_size;                             /**< the size of the @a buf */
  size_t el_size;                              /**< the size of current element to be added to the @a buf */
  unsigned rcode;                              /**< the response code */
  bool use_conn_close;                         /**< Use "Connection: close" header */
  bool use_conn_k_alive;                       /**< Use "Connection: Keep-Alive" header */

  mhd_assert (NULL != r);

  /* ** Adjust response properties ** */

  setup_reply_properties (c);

  mhd_assert (c->rp_props.set);
  mhd_assert ((MHD_CONN_MUST_CLOSE == c->keepalive) || \
              (MHD_CONN_USE_KEEPALIVE == c->keepalive) || \
              (MHD_CONN_MUST_UPGRADE == c->keepalive));
#ifdef UPGRADE_SUPPORT
  mhd_assert ((NULL == r->upgrade_handler) || \
              (MHD_CONN_MUST_UPGRADE == c->keepalive));
#else  /* ! UPGRADE_SUPPORT */
  mhd_assert (MHD_CONN_MUST_UPGRADE != c->keepalive);
#endif /* ! UPGRADE_SUPPORT */
  mhd_assert ((! c->rp_props.chunked) || c->rp_props.use_reply_body_headers);
  mhd_assert ((! c->rp_props.send_reply_body) || \
              c->rp_props.use_reply_body_headers);
#ifdef UPGRADE_SUPPORT
  mhd_assert (NULL == r->upgrade_handler || \
              ! c->rp_props.use_reply_body_headers);
#endif /* UPGRADE_SUPPORT */

  rcode = (unsigned) (c->responseCode & (~MHD_ICY_FLAG));
  if (MHD_CONN_MUST_CLOSE == c->keepalive)
  {
    /* The closure of connection must be always indicated by header
     * to avoid hung connections */
    use_conn_close = true;
    use_conn_k_alive = false;
  }
  else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
  {
    use_conn_close = false;
    /* Add "Connection: keep-alive" if request is HTTP/1.0 or
     * if reply is HTTP/1.0
     * For HTTP/1.1 add header only if explicitly requested by app
     * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
    if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
        (MHD_HTTP_VER_1_0 == c->http_ver) ||
        (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
      use_conn_k_alive = true;
    else
      use_conn_k_alive = false;
  }
  else
  {
    use_conn_close = false;
    use_conn_k_alive = false;
  }

  /* ** Actually build the response header ** */

  /* Get all space available */
  connection_maximize_write_buffer (c);
  buf = c->write_buffer;
  pos = c->write_buffer_append_offset;
  buf_size = c->write_buffer_size;
  if (0 == buf_size)
    return MHD_NO;
  mhd_assert (NULL != buf);

  /* * The status line * */

  /* The HTTP version */
  if (0 == (c->responseCode & MHD_ICY_FLAG))
  { /* HTTP reply */
    if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
    { /* HTTP/1.1 reply */
      /* Use HTTP/1.1 responses for HTTP/1.0 clients.
       * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
      if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
        return MHD_NO;
    }
    else
    { /* HTTP/1.0 reply */
      if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
        return MHD_NO;
    }
  }
  else
  { /* ICY reply */
    if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
      return MHD_NO;
  }

  /* The response code */
  if (buf_size < pos + 5) /* space + code + space */
    return MHD_NO;
  buf[pos++] = ' ';
  pos += MHD_uint16_to_str (rcode, buf + pos,
                            buf_size - pos);
  buf[pos++] = ' ';

  /* The reason phrase */
  el_size = MHD_get_reason_phrase_len_for (rcode);
  if (0 == el_size)
  {
    if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
      return MHD_NO;
  }
  else if (! buffer_append (buf, &pos, buf_size,
                            MHD_get_reason_phrase_for (rcode),
                            el_size))
    return MHD_NO;

  /* The linefeed */
  if (buf_size < pos + 2)
    return MHD_NO;
  buf[pos++] = '\r';
  buf[pos++] = '\n';

  /* * The headers * */

  /* Main automatic headers */

  /* The "Date:" header */
  if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
       (0 == (c->daemon->options & MHD_USE_SUPPRESS_DATE_NO_CLOCK)) )
  {
    /* Additional byte for unused zero-termination */
    if (buf_size < pos + 38)
      return MHD_NO;
    if (get_date_header (buf + pos))
      pos += 37;
  }
  /* The "Connection:" header */
  mhd_assert (! use_conn_close || ! use_conn_k_alive);
  mhd_assert (! use_conn_k_alive || ! use_conn_close);
  if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
  {
    if (use_conn_close)
    {
      if (! buffer_append_s (buf, &pos, buf_size,
                             MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
        return MHD_NO;
    }
    else if (use_conn_k_alive)
    {
      if (! buffer_append_s (buf, &pos, buf_size,
                             MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
        return MHD_NO;
    }
  }

  /* User-defined headers */

  if (! add_user_headers (buf, &pos, buf_size, r, MHD_HEADER_KIND,
                          ! c->rp_props.chunked,
                          use_conn_close,
                          use_conn_k_alive))
    return MHD_NO;

  /* Other automatic headers */

  if (c->rp_props.use_reply_body_headers)
  {
    /* Body-specific headers */
    if (c->rp_props.chunked)
    { /* Chunked encoding is used */
      if (0 == (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED))
      { /* No chunked encoding header set by user */
        if (! buffer_append_s (buf, &pos, buf_size,
                               MHD_HTTP_HEADER_TRANSFER_ENCODING ": " \
                               "chunked\r\n"))
          return MHD_NO;
      }
    }
    else
    { /* Chunked encoding is not used */
      if (MHD_SIZE_UNKNOWN != r->total_size)
      {
        if (! buffer_append_s (buf, &pos, buf_size,
                               MHD_HTTP_HEADER_CONTENT_LENGTH ": "))
          return MHD_NO;
        el_size = MHD_uint64_to_str (r->total_size, buf + pos,
                                     buf_size - pos);
        if (0 == el_size)
          return MHD_NO;
        pos += el_size;

        if (buf_size < pos + 2)
          return MHD_NO;
        buf[pos++] = '\r';
        buf[pos++] = '\n';
      }
    }
  }

  /* * Header termination * */
  if (buf_size < pos + 2)
    return MHD_NO;
  buf[pos++] = '\r';
  buf[pos++] = '\n';

  c->write_buffer_append_offset = pos;
  return MHD_YES;
}


/**
 * Allocate the connection's write buffer (if necessary) and fill it
 * with response footers.
 * Works only for chunked responses as other responses do not need
 * and do not support any kind of footers.
 *
 * @param connection the connection
 * @return #MHD_YES on success, #MHD_NO on failure (out of memory)
 */
static enum MHD_Result
build_connection_chunked_response_footer (struct MHD_Connection *connection)
{
  char *buf;           /**< the buffer to write footers to */
  size_t buf_size;     /**< the size of the @a buf */
  size_t used_size;    /**< the used size of the @a buf */
  struct MHD_Connection *const c = connection; /**< a short alias */
  struct MHD_HTTP_Header *pos;

  mhd_assert (connection->rp_props.chunked);
  /* TODO: allow combining of the final footer with the last chunk,
   * modify the next assert. */
  mhd_assert (MHD_CONNECTION_BODY_SENT == connection->state);
  mhd_assert (NULL != c->response);

  buf_size = connection_maximize_write_buffer (c);
  /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
  if (buf_size < 5)
    return MHD_NO;
  mhd_assert (NULL != c->write_buffer);
  buf = c->write_buffer + c->write_buffer_append_offset;
  mhd_assert (NULL != buf);
  used_size = 0;
  buf[used_size++] = '0';
  buf[used_size++] = '\r';
  buf[used_size++] = '\n';

  for (pos = c->response->first_header; NULL != pos; pos = pos->next)
  {
    if (MHD_FOOTER_KIND == pos->kind)
    {
      size_t new_used_size; /* resulting size with this header */
      /* '4' is colon, space, linefeeds */
      new_used_size = used_size + pos->header_size + pos->value_size + 4;
      if (new_used_size > buf_size)
        return MHD_NO;
      memcpy (buf + used_size, pos->header, pos->header_size);
      used_size += pos->header_size;
      buf[used_size++] = ':';
      buf[used_size++] = ' ';
      memcpy (buf + used_size, pos->value, pos->value_size);
      used_size += pos->value_size;
      buf[used_size++] = '\r';
      buf[used_size++] = '\n';
      mhd_assert (used_size == new_used_size);
    }
  }
  if (used_size + 2 > buf_size)
    return MHD_NO;
  buf[used_size++] = '\r';
  buf[used_size++] = '\n';

  c->write_buffer_append_offset += used_size;
  mhd_assert (c->write_buffer_append_offset <= c->write_buffer_size);

  return MHD_YES;
}


/**
 * We encountered an error processing the request.
 * Handle it properly by stopping to read data
 * and sending the indicated response code and message.
 *
 * @param connection the connection
 * @param status_code the response code to send (400, 413 or 414)
 * @param message the error message to send
 * @param message_len the length of the @a message
 */
static void
transmit_error_response_len (struct MHD_Connection *connection,
                             unsigned int status_code,
                             const char *message,
                             size_t message_len)
{
  struct MHD_Response *response;
  enum MHD_Result iret;

  mhd_assert (! connection->stop_with_error); /* Do not send error twice */
  if (connection->stop_with_error)
  { /* Should not happen */
    if (MHD_CONNECTION_CLOSED > connection->state)
      connection->state = MHD_CONNECTION_CLOSED;

    return;
  }
  connection->stop_with_error = true;
  connection->discard_request = true;
#ifdef HAVE_MESSAGES
  MHD_DLOG (connection->daemon,
            _ ("Error processing request (HTTP response code is %u ('%s')). " \
               "Closing connection.\n"),
            status_code,
            message);
#endif
  if (MHD_CONNECTION_START_REPLY < connection->state)
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (connection->daemon,
              _ ("Too late to send an error response, " \
                 "response is being sent already.\n"),
              status_code,
              message);
#endif
    CONNECTION_CLOSE_ERROR (connection,
                            _ ("Too late for error response."));
    return;
  }
  /* TODO: remove when special error queue function is implemented */
  connection->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
  if (0 != connection->read_buffer_size)
  {
    /* Read buffer is not needed anymore, discard it
     * to free some space for error response. */
    connection->read_buffer = MHD_pool_reallocate (connection->pool,
                                                   connection->read_buffer,
                                                   connection->read_buffer_size,
                                                   0);
    connection->read_buffer_size = 0;
    connection->read_buffer_offset = 0;
  }
  if (NULL != connection->response)
  {
    MHD_destroy_response (connection->response);
    connection->response = NULL;
  }
  response = MHD_create_response_from_buffer (message_len,
                                              (void *) message,
                                              MHD_RESPMEM_PERSISTENT);
  if (NULL == response)
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (connection->daemon,
              _ ("Failed to create error response.\n"),
              status_code,
              message);
#endif
    /* can't even send a reply, at least close the connection */
    connection->state = MHD_CONNECTION_CLOSED;
    return;
  }
  iret = MHD_queue_response (connection,
                             status_code,
                             response);
  MHD_destroy_response (response);
  if (MHD_NO == iret)
  {
    /* can't even send a reply, at least close the connection */
    CONNECTION_CLOSE_ERROR (connection,
                            _ ("Closing connection " \
                               "(failed to queue error response)."));
    return;
  }
  mhd_assert (NULL != connection->response);
  /* Do not reuse this connection. */
  connection->keepalive = MHD_CONN_MUST_CLOSE;
  if (MHD_NO == build_header_response (connection))
  {
    /* No memory. Release everything. */
    connection->version = NULL;
    connection->method = NULL;
    connection->url = NULL;
    connection->last = NULL;
    connection->colon = NULL;
    connection->headers_received = NULL;
    connection->headers_received_tail = NULL;
    connection->write_buffer = NULL;
    connection->write_buffer_size = 0;
    connection->write_buffer_send_offset = 0;
    connection->write_buffer_append_offset = 0;
    connection->read_buffer
      = MHD_pool_reset (connection->pool,
                        NULL,
                        0,
                        0);
    connection->read_buffer_size = 0;

    /* Retry with empty buffer */
    if (MHD_NO == build_header_response (connection))
    {
      CONNECTION_CLOSE_ERROR (connection,
                              _ ("Closing connection " \
                                 "(failed to create error response header)."));
      return;
    }
  }
  connection->state = MHD_CONNECTION_HEADERS_SENDING;
}


/**
 * Transmit static string as error response
 */
#define transmit_error_response_static(c, code, msg) \
  transmit_error_response_len (c, code, msg, MHD_STATICSTR_LEN_ (msg))

/**
 * Update the 'event_loop_info' field of this connection based on the state
 * that the connection is now in.  May also close the connection or
 * perform other updates to the connection if needed to prepare for
 * the next round of the event loop.
 *
 * @param connection connection to get poll set for
 */
static void
MHD_connection_update_event_loop_info (struct MHD_Connection *connection)
{
  /* Do not update states of suspended connection */
  if (connection->suspended)
    return; /* States will be updated after resume. */
#ifdef HTTPS_SUPPORT
  if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
  {   /* HTTPS connection. */
    switch (connection->tls_state)
    {
    case MHD_TLS_CONN_INIT:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
      return;
    case MHD_TLS_CONN_HANDSHAKING:
      if (0 == gnutls_record_get_direction (connection->tls_session))
        connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
      else
        connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
      return;
    default:
      break;
    }
  }
#endif /* HTTPS_SUPPORT */
  while (1)
  {
#if DEBUG_STATES
    MHD_DLOG (connection->daemon,
              _ ("In function %s handling connection at state: %s\n"),
              __FUNCTION__,
              MHD_state_to_string (connection->state));
#endif
    switch (connection->state)
    {
    case MHD_CONNECTION_INIT:
    case MHD_CONNECTION_REQ_LINE_RECEIVING:
    case MHD_CONNECTION_URL_RECEIVED:
    case MHD_CONNECTION_HEADER_PART_RECEIVED:
      /* while reading headers, we always grow the
         read buffer if needed, no size-check required */
      if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
           (! try_grow_read_buffer (connection, true)) )
      {
        if (connection->url != NULL)
          transmit_error_response_static (connection,
                                          MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
                                          REQUEST_TOO_BIG);
        else
          transmit_error_response_static (connection,
                                          MHD_HTTP_URI_TOO_LONG,
                                          REQUEST_TOO_BIG);
        continue;
      }
      if (! connection->discard_request)
        connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
      else
        connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
      break;
    case MHD_CONNECTION_HEADERS_RECEIVED:
      mhd_assert (0);
      break;
    case MHD_CONNECTION_HEADERS_PROCESSED:
      mhd_assert (0);
      break;
    case MHD_CONNECTION_CONTINUE_SENDING:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
      break;
    case MHD_CONNECTION_CONTINUE_SENT:
      if (connection->read_buffer_offset == connection->read_buffer_size)
      {
        const bool internal_poll = (0 != (connection->daemon->options
                                          & MHD_USE_INTERNAL_POLLING_THREAD));
        if ( (! try_grow_read_buffer (connection, true)) &&
             internal_poll)
        {
          /* failed to grow the read buffer, and the
             client which is supposed to handle the
             received data in a *blocking* fashion
             (in this mode) did not handle the data as
             it was supposed to!
             => we would either have to do busy-waiting
             (on the client, which would likely fail),
             or if we do nothing, we would just timeout
             on the connection (if a timeout is even
             set!).
             Solution: we kill the connection with an error */
          transmit_error_response_static (connection,
                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
                                          INTERNAL_ERROR);
          continue;
        }
      }
      if ( (connection->read_buffer_offset < connection->read_buffer_size) &&
           (! connection->discard_request) )
        connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
      else
        connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
      break;
    case MHD_CONNECTION_BODY_RECEIVED:
    case MHD_CONNECTION_FOOTER_PART_RECEIVED:
      /* while reading footers, we always grow the
         read buffer if needed, no size-check required */
      if (connection->read_closed)
      {
        CONNECTION_CLOSE_ERROR (connection,
                                NULL);
        continue;
      }
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
      /* transition to FOOTERS_RECEIVED
         happens in read handler */
      break;
    case MHD_CONNECTION_FOOTERS_RECEIVED:
      mhd_assert (0);
      break;
    case MHD_CONNECTION_FULL_REQ_RECEIVED:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
      break;
    case MHD_CONNECTION_START_REPLY:
      mhd_assert (0);
      break;
    case MHD_CONNECTION_HEADERS_SENDING:
      /* headers in buffer, keep writing */
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
      break;
    case MHD_CONNECTION_HEADERS_SENT:
      mhd_assert (0);
      break;
    case MHD_CONNECTION_NORMAL_BODY_READY:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
      break;
    case MHD_CONNECTION_NORMAL_BODY_UNREADY:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
      break;
    case MHD_CONNECTION_CHUNKED_BODY_READY:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
      break;
    case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
      break;
    case MHD_CONNECTION_BODY_SENT:
      mhd_assert (0);
      break;
    case MHD_CONNECTION_FOOTERS_SENDING:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
      break;
    case MHD_CONNECTION_FOOTERS_SENT:
      mhd_assert (0);
      break;
    case MHD_CONNECTION_CLOSED:
      connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
      return;           /* do nothing, not even reading */
#ifdef UPGRADE_SUPPORT
    case MHD_CONNECTION_UPGRADE:
      mhd_assert (0);
      break;
#endif /* UPGRADE_SUPPORT */
    default:
      mhd_assert (0);
    }
    break;
  }
}


/**
 * Parse a single line of the HTTP header.  Advance read_buffer (!)
 * appropriately.  If the current line does not fit, consider growing
 * the buffer.  If the line is far too long, close the connection.  If
 * no line is found (incomplete, buffer too small, line too long),
 * return NULL.  Otherwise return a pointer to the line.
 *
 * @param connection connection we're processing
 * @param[out] line_len pointer to variable that receive
 *             length of line or NULL
 * @return NULL if no full line is available; note that the returned
 *         string will not be 0-termianted
 */
static char *
get_next_header_line (struct MHD_Connection *connection,
                      size_t *line_len)
{
  char *rbuf;
  size_t pos;

  if (0 == connection->read_buffer_offset)
    return NULL;
  pos = 0;
  rbuf = connection->read_buffer;
  mhd_assert (NULL != rbuf);

  do
  {
    const char c = rbuf[pos];
    bool found;
    found = false;
    if ( ('\r' == c) && (pos < connection->read_buffer_offset - 1) &&
         ('\n' == rbuf[pos + 1]) )
    { /* Found CRLF */
      found = true;
      if (line_len)
        *line_len = pos;
      rbuf[pos++] = 0; /* Replace CR with zero */
      rbuf[pos++] = 0; /* Replace LF with zero */
    }
    else if ('\n' == c) /* TODO: Add MHD option to disallow */
    { /* Found bare LF */
      found = true;
      if (line_len)
        *line_len = pos;
      rbuf[pos++] = 0; /* Replace LF with zero */
    }
    if (found)
    {
      connection->read_buffer += pos;
      connection->read_buffer_size -= pos;
      connection->read_buffer_offset -= pos;
      return rbuf;
    }
  } while (++pos < connection->read_buffer_offset);

  /* not found, consider growing... */
  if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
       (! try_grow_read_buffer (connection, true)) )
  {
    if (NULL != connection->url)
      transmit_error_response_static (connection,
                                      MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
                                      REQUEST_TOO_BIG);
    else
      transmit_error_response_static (connection,
                                      MHD_HTTP_URI_TOO_LONG,
                                      REQUEST_TOO_BIG);
  }
  if (line_len)
    *line_len = 0;
  return NULL;
}


/**
 * Add an entry to the HTTP headers of a connection.  If this fails,
 * transmit an error response (request too big).
 *
 * @param connection the connection for which a
 *  value should be set
 * @param kind kind of the value
 * @param key key for the value
 * @param key_size number of bytes in @a key
 * @param value the value itself
 * @param value_size number of bytes in @a value
 * @return #MHD_NO on failure (out of memory), #MHD_YES for success
 */
static enum MHD_Result
connection_add_header (struct MHD_Connection *connection,
                       const char *key,
                       size_t key_size,
                       const char *value,
                       size_t value_size,
                       enum MHD_ValueKind kind)
{
  if (MHD_NO ==
      MHD_set_connection_value_n (connection,
                                  kind,
                                  key,
                                  key_size,
                                  value,
                                  value_size))
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (connection->daemon,
              _ ("Not enough memory in pool to allocate header record!\n"));
#endif
    transmit_error_response_static (connection,
                                    MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
                                    REQUEST_TOO_BIG);
    return MHD_NO;
  }
  return MHD_YES;
}


/**
 * Parse the cookie header (see RFC 2109).
 *
 * @param connection connection to parse header of
 * @return #MHD_YES for success, #MHD_NO for failure (malformed, out of memory)
 */
static enum MHD_Result
parse_cookie_header (struct MHD_Connection *connection)
{
  const char *hdr;
  size_t hdr_len;
  char *cpy;
  char *pos;
  char *sce;
  char *semicolon;
  char *equals;
  char *ekill;
  char *end;
  char old;
  int quotes;

  if (MHD_NO == MHD_lookup_connection_value_n (connection,
                                               MHD_HEADER_KIND,
                                               MHD_HTTP_HEADER_COOKIE,
                                               MHD_STATICSTR_LEN_ (
                                                 MHD_HTTP_HEADER_COOKIE),
                                               &hdr,
                                               &hdr_len))
    return MHD_YES;
  cpy = connection_alloc_memory (connection,
                                 hdr_len + 1);
  if (NULL == cpy)
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (connection->daemon,
              _ ("Not enough memory in pool to parse cookies!\n"));
#endif
    transmit_error_response_static (connection,
                                    MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
                                    REQUEST_TOO_BIG);
    return MHD_NO;
  }
  memcpy (cpy,
          hdr,
          hdr_len);
  cpy[hdr_len] = '\0';
  pos = cpy;
  while (NULL != pos)
  {
    while (' ' == *pos)
      pos++;                    /* skip spaces */

    sce = pos;
    while ( ((*sce) != '\0') &&
            ((*sce) != ',') &&
            ((*sce) != ';') &&
            ((*sce) != '=') )
      sce++;
    /* remove tailing whitespace (if any) from key */
    ekill = sce - 1;
    while ( (*ekill == ' ') &&
            (ekill >= pos) )
      *(ekill--) = '\0';
    old = *sce;
    *sce = '\0';
    if (old != '=')
    {
      /* value part omitted, use empty string... */
      if (MHD_NO ==
          connection_add_header (connection,
                                 pos,
                                 ekill - pos + 1,
                                 "",
                                 0,
                                 MHD_COOKIE_KIND))
        return MHD_NO;
      if (old == '\0')
        break;
      pos = sce + 1;
      continue;
    }
    equals = sce + 1;
    quotes = 0;
    semicolon = equals;
    while ( ('\0' != semicolon[0]) &&
            ( (0 != quotes) ||
              ( (';' != semicolon[0]) &&
                (',' != semicolon[0]) ) ) )
    {
      if ('"' == semicolon[0])
        quotes = (quotes + 1) & 1;
      semicolon++;
    }
    end = semicolon;
    if ('\0' == semicolon[0])
      semicolon = NULL;
    if (NULL != semicolon)
    {
      semicolon[0] = '\0';
      semicolon++;
    }
    /* remove quotes */
    if ( ('"' == equals[0]) &&
         ('"' == end[-1]) )
    {
      equals++;
      end--;
      *end = '\0';
    }
    if (MHD_NO ==
        connection_add_header (connection,
                               pos,
                               ekill - pos + 1,
                               equals,
                               end - equals,
                               MHD_COOKIE_KIND))
      return MHD_NO;
    pos = semicolon;
  }
  return MHD_YES;
}


/**
 * Detect HTTP version
 *
 * @param connection the connection
 * @param http_string the pointer to HTTP version string
 * @param len the length of @a http_string in bytes
 * @return #MHD_YES if HTTP version is correct and supported,
 *         #MHD_NO if HTTP version is not correct or unsupported.
 */
static enum MHD_Result
parse_http_version (struct MHD_Connection *connection,
                    const char *http_string,
                    size_t len)
{
  const char *const h = http_string; /**< short alias */
  mhd_assert (NULL != http_string);

  /* String must starts with 'HTTP/d.d', case-sensetive match.
   * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
  if ((len != 8) ||
      (h[0] != 'H') || (h[1] != 'T') || (h[2] != 'T') || (h[3] != 'P') ||
      (h[4] != '/')
      || (h[6] != '.') ||
      ((h[5] < '0') || (h[5] > '9')) ||
      ((h[7] < '0') || (h[7] > '9')))
  {
    connection->http_ver = MHD_HTTP_VER_INVALID;
    transmit_error_response_static (connection,
                                    MHD_HTTP_BAD_REQUEST,
                                    REQUEST_MALFORMED);
    return MHD_NO;
  }
  if (1 == h[5] - '0')
  {
    /* HTTP/1.x */
    if (1 == h[7] - '0')
      connection->http_ver = MHD_HTTP_VER_1_1;
    else if (0 == h[7] - '0')
      connection->http_ver = MHD_HTTP_VER_1_0;
    else
      connection->http_ver = MHD_HTTP_VER_1_2__1_9;

    return MHD_YES;
  }

  if (0 == h[5] - '0')
  {
    /* Too old major version */
    connection->http_ver = MHD_HTTP_VER_TOO_OLD;
    transmit_error_response_static (connection,
                                    MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED,
                                    REQ_HTTP_VER_IS_TOO_OLD);
    return MHD_NO;
  }

  connection->http_ver = MHD_HTTP_VER_FUTURE;
  transmit_error_response_static (connection,
                                  MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED,
                                  REQ_HTTP_VER_IS_NOT_SUPPORTED);
  return MHD_NO;
}


/**
 * Detect standard HTTP request method
 *
 * @param connection the connection
 * @param method the pointer to HTTP request method string
 * @param len the length of @a method in bytes
 * @return #MHD_YES if HTTP method is valid string,
 *         #MHD_NO if HTTP method string is not valid.
 */
static enum MHD_Result
parse_http_std_method (struct MHD_Connection *connection,
                       const char *method,
                       size_t len)
{
  const char *const m = method; /**< short alias */
  mhd_assert (NULL != m);

  if (0 == len)
    return MHD_NO;

  if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
      (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
    connection->http_mthd = MHD_HTTP_MTHD_GET;
  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
           (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
    connection->http_mthd = MHD_HTTP_MTHD_HEAD;
  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
           (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
    connection->http_mthd = MHD_HTTP_MTHD_POST;
  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
           (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
    connection->http_mthd = MHD_HTTP_MTHD_PUT;
  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
           (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
    connection->http_mthd = MHD_HTTP_MTHD_DELETE;
  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
           (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
    connection->http_mthd = MHD_HTTP_MTHD_CONNECT;
  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
           (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
    connection->http_mthd = MHD_HTTP_MTHD_OPTIONS;
  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
           (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
    connection->http_mthd = MHD_HTTP_MTHD_TRACE;
  else
    connection->http_mthd = MHD_HTTP_MTHD_OTHER;

  /* Any method string with non-zero length is valid */
  return MHD_YES;
}


/**
 * Parse the first line of the HTTP HEADER.
 *
 * @param connection the connection (updated)
 * @param line the first line, not 0-terminated
 * @param line_len length of the first @a line
 * @return #MHD_YES if the line is ok, #MHD_NO if it is malformed
 */
static enum MHD_Result
parse_initial_message_line (struct MHD_Connection *connection,
                            char *line,
                            size_t line_len)
{
  struct MHD_Daemon *daemon = connection->daemon;
  const char *curi;
  char *uri;
  char *http_version;
  char *args;
  unsigned int unused_num_headers;

  if (NULL == (uri = memchr (line,
                             ' ',
                             line_len)))
    return MHD_NO;              /* serious error */
  uri[0] = '\0';
  connection->method = line;
  if (MHD_NO == parse_http_std_method (connection, connection->method,
                                       (size_t) (uri - line)))
    return MHD_NO;
  uri++;
  /* Skip any spaces. Not required by standard but allow
     to be more tolerant. */
  /* TODO: do not skip them in standard mode */
  while ( (' ' == uri[0]) &&
          ( (size_t) (uri - line) < line_len) )
    uri++;
  if ((size_t) (uri - line) == line_len)
  {
    /* No URI and no http version given */
    curi = "";
    uri = NULL;
    connection->version = "";
    args = NULL;
    if (MHD_NO == parse_http_version (connection, connection->version, 0))
      return MHD_NO;
  }
  else
  {
    size_t uri_len;
    curi = uri;
    /* Search from back to accept malformed URI with space */
    http_version = line + line_len - 1;
    /* Skip any trailing spaces */
    /* TODO: do not skip them in standard mode */
    while ( (' ' == http_version[0]) &&
            (http_version > uri) )
      http_version--;
    /* Find first space in reverse direction */
    while ( (' ' != http_version[0]) &&
            (http_version > uri) )
      http_version--;
    if (http_version > uri)
    {
      /* http_version points to character before HTTP version string */
      http_version[0] = '\0';
      connection->version = http_version + 1;
      if (MHD_NO == parse_http_version (connection, connection->version,
                                        line_len
                                        - (connection->version - line)))
        return MHD_NO;
      uri_len = http_version - uri;
    }
    else
    {
      connection->version = "";
      if (MHD_NO == parse_http_version (connection, connection->version, 0))
        return MHD_NO;
      uri_len = line_len - (uri - line);
    }
    /* check for spaces in URI if we are "strict" */
    if ( (1 <= daemon->strict_for_client) &&
         (NULL != memchr (uri,
                          ' ',
                          uri_len)) )
    {
      /* space exists in URI and we are supposed to be strict, reject */
      return MHD_NO;
    }

    args = memchr (uri,
                   '?',
                   uri_len);
  }

  /* log callback before we modify URI *or* args */
  if (NULL != daemon->uri_log_callback)
  {
    connection->client_aware = true;
    connection->client_context
      = daemon->uri_log_callback (daemon->uri_log_callback_cls,
                                  uri,
                                  connection);
  }

  if (NULL != args)
  {
    args[0] = '\0';
    args++;
    /* note that this call clobbers 'args' */
    MHD_parse_arguments_ (connection,
                          MHD_GET_ARGUMENT_KIND,
                          args,
                          &connection_add_header,
                          &unused_num_headers);
  }

  /* unescape URI *after* searching for arguments and log callback */
  if (NULL != uri)
    daemon->unescape_callback (daemon->unescape_callback_cls,
                               connection,
                               uri);
  connection->url = curi;
  return MHD_YES;
}


/**
 * Call the handler of the application for this
 * connection.  Handles chunking of the upload
 * as well as normal uploads.
 *
 * @param connection connection we're processing
 */
static void
call_connection_handler (struct MHD_Connection *connection)
{
  struct MHD_Daemon *daemon = connection->daemon;
  size_t processed;

  if (NULL != connection->response)
    return;                     /* already queued a response */
  processed = 0;
  connection->client_aware = true;
  if (MHD_NO ==
      daemon->default_handler (daemon->default_handler_cls,
                               connection,
                               connection->url,
                               connection->method,
                               connection->version,
                               NULL,
                               &processed,
                               &connection->client_context))
  {
    /* serious internal error, close connection */
    CONNECTION_CLOSE_ERROR (connection,
                            _ (
                              "Application reported internal error, closing connection."));
    return;
  }
}


/**
 * Call the handler of the application for this
 * connection.  Handles chunking of the upload
 * as well as normal uploads.
 *
 * @param connection connection we're processing
 */
static void
process_request_body (struct MHD_Connection *connection)
{
  struct MHD_Daemon *daemon = connection->daemon;
  size_t available;
  bool instant_retry;
  char *buffer_head;

  if (NULL != connection->response)
  {
    /* TODO: discard all read buffer as early response
     * means that connection have to be closed. */
    /* already queued a response, discard remaining upload
       (but not more, there might be another request after it) */
    size_t purge;

    purge = (size_t) MHD_MIN (connection->remaining_upload_size,
                              (uint64_t) connection->read_buffer_offset);
    connection->remaining_upload_size -= purge;
    if (connection->read_buffer_offset > purge)
      memmove (connection->read_buffer,
               &connection->read_buffer[purge],
               connection->read_buffer_offset - purge);
    connection->read_buffer_offset -= purge;
    return;
  }

  buffer_head = connection->read_buffer;
  available = connection->read_buffer_offset;
  do
  {
    size_t to_be_processed;
    size_t left_unprocessed;
    size_t processed_size;

    instant_retry = false;
    if (connection->have_chunked_upload)
    {
      mhd_assert (MHD_SIZE_UNKNOWN == connection->remaining_upload_size);
      if ( (connection->current_chunk_offset ==
            connection->current_chunk_size) &&
           (0 != connection->current_chunk_size) )
      {
        size_t i;
        mhd_assert (0 != available);
        /* skip new line at the *end* of a chunk */
        i = 0;
        if ( (2 <= available) &&
             ('\r' == buffer_head[0]) &&
             ('\n' == buffer_head[1]) )
          i += 2;                        /* skip CRLF */
        else if ('\n' == buffer_head[0]) /* TODO: Add MHD option to disallow */
          i++;                           /* skip bare LF */
        else if (2 > available)
          break;                         /* need more upload data */
        if (0 == i)
        {
          /* malformed encoding */
          transmit_error_response_static (connection,
                                          MHD_HTTP_BAD_REQUEST,
                                          REQUEST_CHUNKED_MALFORMED);
          return;
        }
        available -= i;
        buffer_head += i;
        connection->current_chunk_offset = 0;
        connection->current_chunk_size = 0;
        if (0 == available)
          break;
      }
      if (0 != connection->current_chunk_size)
      {
        uint64_t cur_chunk_left;
        mhd_assert (connection->current_chunk_offset < \
                    connection->current_chunk_size);
        /* we are in the middle of a chunk, give
           as much as possible to the client (without
           crossing chunk boundaries) */
        cur_chunk_left
          = connection->current_chunk_size - connection->current_chunk_offset;
        if (cur_chunk_left > available)
          to_be_processed = available;
        else
        {         /* cur_chunk_left <= (size_t)available */
          to_be_processed = (size_t) cur_chunk_left;
          if (available > to_be_processed)
            instant_retry = true;
        }
      }
      else
      {
        size_t i;
        /** The length of the string with the number of the chunk size */
        size_t chunk_size_len;
        bool found_chunk_size_str;
        bool malformed;

        /* we need to read chunk boundaries */
        i = 0;
        found_chunk_size_str = false;
        chunk_size_len = 0;
        mhd_assert (0 != available);
        do
        {
          if ('\n' == buffer_head[i])
          {
            if ((0 < i) && ('\r' == buffer_head[i - 1]))
            { /* CRLF */
              if (! found_chunk_size_str)
                chunk_size_len = i - 1;
            }
            else
            { /* bare LF */
              /* TODO: Add an option to disallow bare LF */
              if (! found_chunk_size_str)
                chunk_size_len = i;
            }
            found_chunk_size_str = true;
            break; /* Found the end of the string */
          }
          else if (! found_chunk_size_str && (';' == buffer_head[i]))
          { /* Found chunk extension */
            chunk_size_len = i;
            found_chunk_size_str = true;
          }
        } while (available > ++i);
        mhd_assert ((i == available) || found_chunk_size_str);
        mhd_assert ((0 == chunk_size_len) || found_chunk_size_str);
        malformed = ((0 == chunk_size_len) && found_chunk_size_str);
        if (! malformed)
        {
          /* Check whether size is valid hexadecimal number
           * even if end of the string is not found yet. */
          size_t num_dig;
          uint64_t chunk_size;
          mhd_assert (0 < i);
          if (! found_chunk_size_str)
          {
            mhd_assert (i == available);
            /* Check already available part of the size string for valid
             * hexadecimal digits. */
            chunk_size_len = i;
            if ('\r' == buffer_head[i - 1])
            {
              chunk_size_len--;
              malformed = (0 == chunk_size_len);
            }
          }
          num_dig = MHD_strx_to_uint64_n_ (buffer_head,
                                           chunk_size_len,
                                           &chunk_size);
          malformed = malformed || (chunk_size_len != num_dig);

          if ((available != i) && ! malformed)
          {
            /* Found end of the string and the size of the chunk is valid */

            mhd_assert (found_chunk_size_str);
            /* Start reading payload data of the chunk */
            connection->current_chunk_offset = 0;
            connection->current_chunk_size = chunk_size;
            i++; /* Consume the last checked char */
            available -= i;
            buffer_head += i;

            if (0 == connection->current_chunk_size)
            { /* The final (termination) chunk */
              connection->remaining_upload_size = 0;
              break;
            }
            if (available > 0)
              instant_retry = true;
            continue;
          }

          if ((0 == num_dig) && (0 != chunk_size_len))
          { /* Check whether result is invalid due to uint64_t overflow */
            /* At least one byte is always available
             * in the input buffer here. */
            const char d = buffer_head[0]; /**< first digit */
            if ((('0' <= d) && ('9' >= d)) ||
                (('A' <= d) && ('F' >= d)) ||
                (('a' <= d) && ('f' >= d)))
            { /* The first char is a valid hexadecimal digit */
              transmit_error_response_static (connection,
                                              MHD_HTTP_CONTENT_TOO_LARGE,
                                              REQUEST_CHUNK_TOO_LARGE);
              return;
            }
          }
        }
        if (malformed)
        {
          transmit_error_response_static (connection,
                                          MHD_HTTP_BAD_REQUEST,
                                          REQUEST_CHUNKED_MALFORMED);
          return;
        }
        mhd_assert (available == i);
        break; /* The end of the string not found, need more upload data */
      }
    }
    else
    {
      /* no chunked encoding, give all to the client */
      mhd_assert (MHD_SIZE_UNKNOWN != connection->remaining_upload_size);
      mhd_assert (0 != connection->remaining_upload_size);
      if (connection->remaining_upload_size < available)
        to_be_processed = (size_t) connection->remaining_upload_size;
      else
        to_be_processed = available;
    }
    left_unprocessed = to_be_processed;
    connection->client_aware = true;
    if (MHD_NO ==
        daemon->default_handler (daemon->default_handler_cls,
                                 connection,
                                 connection->url,
                                 connection->method,
                                 connection->version,
                                 buffer_head,
                                 &left_unprocessed,
                                 &connection->client_context))
    {
      /* serious internal error, close connection */
      CONNECTION_CLOSE_ERROR (connection,
                              _ ("Application reported internal error, " \
                                 "closing connection."));
      return;
    }
    if (left_unprocessed > to_be_processed)
      mhd_panic (mhd_panic_cls,
                 __FILE__,
                 __LINE__
#ifdef HAVE_MESSAGES
                 , _ ("libmicrohttpd API violation.\n")
#else
                 , NULL
#endif
                 );
    if (0 != left_unprocessed)
    {
      instant_retry = false; /* client did not process everything */
#ifdef HAVE_MESSAGES
      /* client did not process all upload data, complain if
         the setup was incorrect, which may prevent us from
         handling the rest of the request */
      if ( (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) &&
           (! connection->suspended) )
        MHD_DLOG (daemon,
                  _ ("WARNING: incomplete upload processing and connection " \
                     "not suspended may result in hung connection.\n"));
#endif
    }
    processed_size = to_be_processed - left_unprocessed;
    if (connection->have_chunked_upload)
      connection->current_chunk_offset += processed_size;
    /* dh left "processed" bytes in buffer for next time... */
    buffer_head += processed_size;
    available -= processed_size;
    if (! connection->have_chunked_upload)
    {
      mhd_assert (MHD_SIZE_UNKNOWN != connection->remaining_upload_size);
      connection->remaining_upload_size -= processed_size;
    }
    else
      mhd_assert (MHD_SIZE_UNKNOWN == connection->remaining_upload_size);
  } while (instant_retry);
  /* TODO: zero out reused memory region */
  if ( (available > 0) &&
       (buffer_head != connection->read_buffer) )
    memmove (connection->read_buffer,
             buffer_head,
             available);
  else
    mhd_assert ((0 == available) || \
                (connection->read_buffer_offset == available));
  connection->read_buffer_offset = available;
}


/**
 * Check if we are done sending the write-buffer.
 * If so, transition into "next_state".
 *
 * @param connection connection to check write status for
 * @param next_state the next state to transition to
 * @return #MHD_NO if we are not done, #MHD_YES if we are
 */
static enum MHD_Result
check_write_done (struct MHD_Connection *connection,
                  enum MHD_CONNECTION_STATE next_state)
{
  if ( (connection->write_buffer_append_offset !=
        connection->write_buffer_send_offset)
       /* || data_in_tls_buffers == true  */
       )
    return MHD_NO;
  connection->write_buffer_append_offset = 0;
  connection->write_buffer_send_offset = 0;
  connection->state = next_state;
  return MHD_YES;
}


/**
 * We have received (possibly the beginning of) a line in the
 * header (or footer).  Validate (check for ":") and prepare
 * to process.
 *
 * @param connection connection we're processing
 * @param line line from the header to process
 * @return #MHD_YES on success, #MHD_NO on error (malformed @a line)
 */
static enum MHD_Result
process_header_line (struct MHD_Connection *connection,
                     char *line)
{
  char *colon;

  /* line should be normal header line, find colon */
  colon = strchr (line, ':');
  if (NULL == colon)
  {
    /* error in header line, die hard */
    return MHD_NO;
  }
  if (-1 >= connection->daemon->strict_for_client)
  {
    /* check for whitespace before colon, which is not allowed
 by RFC 7230 section 3.2.4; we count space ' ' and
 tab '\t', but not '\r\n' as those would have ended the line. */
    const char *white;

    white = strchr (line, ' ');
    if ( (NULL != white) &&
         (white < colon) )
      return MHD_NO;
    white = strchr (line, '\t');
    if ( (NULL != white) &&
         (white < colon) )
      return MHD_NO;
  }
  /* zero-terminate header */
  colon[0] = '\0';
  colon++;                      /* advance to value */
  while ( ('\0' != colon[0]) &&
          ( (' ' == colon[0]) ||
            ('\t' == colon[0]) ) )
    colon++;
  /* we do the actual adding of the connection
     header at the beginning of the while
     loop since we need to be able to inspect
     the *next* header line (in case it starts
     with a space...) */
  connection->last = line;
  connection->colon = colon;
  return MHD_YES;
}


/**
 * Process a header value that spans multiple lines.
 * The previous line(s) are in connection->last.
 *
 * @param connection connection we're processing
 * @param line the current input line
 * @param kind if the line is complete, add a header
 *        of the given kind
 * @return #MHD_YES if the line was processed successfully
 */
static enum MHD_Result
process_broken_line (struct MHD_Connection *connection,
                     char *line,
                     enum MHD_ValueKind kind)
{
  char *last;
  char *tmp;
  size_t last_len;
  size_t tmp_len;

  last = connection->last;
  if ( (' ' == line[0]) ||
       ('\t' == line[0]) )
  {
    /* value was continued on the next line, see
       http://www.jmarshall.com/easy/http/ */
    last_len = strlen (last);
    /* skip whitespace at start of 2nd line */
    tmp = line;
    while ( (' ' == tmp[0]) ||
            ('\t' == tmp[0]) )
      tmp++;
    tmp_len = strlen (tmp);
    /* FIXME: we might be able to do this better (faster!), as most
       likely 'last' and 'line' should already be adjacent in
       memory; however, doing this right gets tricky if we have a
       value continued over multiple lines (in which case we need to
       record how often we have done this so we can check for
       adjacency); also, in the case where these are not adjacent
       (not sure how it can happen!), we would want to allocate from
       the end of the pool, so as to not destroy the read-buffer's
       ability to grow nicely. */
    last = MHD_pool_reallocate (connection->pool,
                                last,
                                last_len + 1,
                                last_len + tmp_len + 1);
    if (NULL == last)
    {
      transmit_error_response_static (connection,
                                      MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
                                      REQUEST_TOO_BIG);
      return MHD_NO;
    }
    memcpy (&last[last_len],
            tmp,
            tmp_len + 1);
    connection->last = last;
    return MHD_YES;             /* possibly more than 2 lines... */
  }
  mhd_assert ( (NULL != last) &&
               (NULL != connection->colon) );
  if (MHD_NO ==
      connection_add_header (connection,
                             last,
                             strlen (last),
                             connection->colon,
                             strlen (connection->colon),
                             kind))
  {
    /* Error has been queued by connection_add_header() */
    return MHD_NO;
  }
  /* we still have the current line to deal with... */
  if (0 != line[0])
  {
    if (MHD_NO == process_header_line (connection,
                                       line))
    {
      transmit_error_response_static (connection,
                                      MHD_HTTP_BAD_REQUEST,
                                      REQUEST_MALFORMED);
      return MHD_NO;
    }
  }
  return MHD_YES;
}


/**
 * Parse the various headers; figure out the size
 * of the upload and make sure the headers follow
 * the protocol.  Advance to the appropriate state.
 *
 * @param connection connection we're processing
 */
static void
parse_connection_headers (struct MHD_Connection *connection)
{
  const char *clen;
  const char *enc;
  size_t val_len;

  parse_cookie_header (connection);
  if ( (1 <= connection->daemon->strict_for_client) &&
       (MHD_IS_HTTP_VER_1_1_COMPAT (connection->http_ver)) &&
       (MHD_NO ==
        MHD_lookup_connection_value_n (connection,
                                       MHD_HEADER_KIND,
                                       MHD_HTTP_HEADER_HOST,
                                       MHD_STATICSTR_LEN_ (
                                         MHD_HTTP_HEADER_HOST),
                                       NULL,
                                       NULL)) )
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (connection->daemon,
              _ ("Received HTTP/1.1 request without `Host' header.\n"));
#endif
    transmit_error_response_static (connection,
                                    MHD_HTTP_BAD_REQUEST,
                                    REQUEST_LACKS_HOST);
    return;
  }

  connection->remaining_upload_size = 0;
  if (MHD_NO != MHD_lookup_connection_value_n (connection,
                                               MHD_HEADER_KIND,
                                               MHD_HTTP_HEADER_TRANSFER_ENCODING,
                                               MHD_STATICSTR_LEN_ (
                                                 MHD_HTTP_HEADER_TRANSFER_ENCODING),
                                               &enc,
                                               NULL))
  {
    connection->remaining_upload_size = MHD_SIZE_UNKNOWN;
    if (MHD_str_equal_caseless_ (enc,
                                 "chunked"))
      connection->have_chunked_upload = true;
  }
  else
  {
    if (MHD_NO != MHD_lookup_connection_value_n (connection,
                                                 MHD_HEADER_KIND,
                                                 MHD_HTTP_HEADER_CONTENT_LENGTH,
                                                 MHD_STATICSTR_LEN_ (
                                                   MHD_HTTP_HEADER_CONTENT_LENGTH),
                                                 &clen,
                                                 &val_len))
    {
      size_t num_digits;

      num_digits = MHD_str_to_uint64_n_ (clen,
                                         val_len,
                                         &connection->remaining_upload_size);
      if ( (val_len != num_digits) ||
           (0 == num_digits) )
      {
        connection->remaining_upload_size = 0;
        if ((0 == num_digits) &&
            (0 != val_len) &&
            ('0' <= clen[0]) && ('9' >= clen[0]))
        {
#ifdef HAVE_MESSAGES
          MHD_DLOG (connection->daemon,
                    _ ("Too large value of 'Content-Length' header. " \
                       "Closing connection.\n"));
#endif
          transmit_error_response_static (connection,
                                          MHD_HTTP_CONTENT_TOO_LARGE,
                                          REQUEST_CONTENTLENGTH_TOOLARGE);
        }
        else
        {
#ifdef HAVE_MESSAGES
          MHD_DLOG (connection->daemon,
                    _ ("Failed to parse `Content-Length' header. " \
                       "Closing connection.\n"));
#endif
          transmit_error_response_static (connection,
                                          MHD_HTTP_BAD_REQUEST,
                                          REQUEST_CONTENTLENGTH_MALFORMED);
        }
      }
    }
  }
}


/**
 * Update the 'last_activity' field of the connection to the current time
 * and move the connection to the head of the 'normal_timeout' list if
 * the timeout for the connection uses the default value.
 *
 * @param connection the connection that saw some activity
 */
void
MHD_update_last_activity_ (struct MHD_Connection *connection)
{
  struct MHD_Daemon *daemon = connection->daemon;

  if (0 == connection->connection_timeout_ms)
    return;  /* Skip update of activity for connections
               without timeout timer. */
  if (connection->suspended)
    return;  /* no activity on suspended connections */

  connection->last_activity = MHD_monotonic_msec_counter ();
  if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
    return; /* each connection has personal timeout */

  if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
    return; /* custom timeout, no need to move it in "normal" DLL */
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
  MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
#endif
  /* move connection to head of timeout list (by remove + add operation) */
  XDLL_remove (daemon->normal_timeout_head,
               daemon->normal_timeout_tail,
               connection);
  XDLL_insert (daemon->normal_timeout_head,
               daemon->normal_timeout_tail,
               connection);
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
  MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
#endif
}


/**
 * This function handles a particular connection when it has been
 * determined that there is data to be read off a socket. All
 * implementations (multithreaded, external polling, internal polling)
 * call this function to handle reads.
 *
 * @param connection connection to handle
 * @param socket_error set to true if socket error was detected
 */
void
MHD_connection_handle_read (struct MHD_Connection *connection,
                            bool socket_error)
{
  ssize_t bytes_read;

  if ( (MHD_CONNECTION_CLOSED == connection->state) ||
       (connection->suspended) )
    return;
#ifdef HTTPS_SUPPORT
  if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
  {   /* HTTPS connection. */
    if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
    {
      if (! MHD_run_tls_handshake_ (connection))
        return;
    }
  }
#endif /* HTTPS_SUPPORT */

  /* make sure "read" has a reasonable number of bytes
     in buffer to use per system call (if possible) */
  if (connection->read_buffer_offset + connection->daemon->pool_increment >
      connection->read_buffer_size)
    try_grow_read_buffer (connection,
                          (connection->read_buffer_size ==
                           connection->read_buffer_offset));

  if (connection->read_buffer_size == connection->read_buffer_offset)
    return; /* No space for receiving data. */
  bytes_read = connection->recv_cls (connection,
                                     &connection->read_buffer
                                     [connection->read_buffer_offset],
                                     connection->read_buffer_size
                                     - connection->read_buffer_offset);
  if ((bytes_read < 0) || socket_error)
  {
    if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
      return;     /* No new data to process. */
    if ((bytes_read > 0) && connection->sk_nonblck)
    { /* Try to detect the socket error */
      int dummy;
      bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
    }
    if (MHD_ERR_CONNRESET_ == bytes_read)
    {
      if ( (MHD_CONNECTION_INIT < connection->state) &&
           (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
      {
#ifdef HAVE_MESSAGES
        MHD_DLOG (connection->daemon,
                  _ ("Socket has been disconnected when reading request.\n"));
#endif
        connection->discard_request = true;
      }
      MHD_connection_close_ (connection,
                             MHD_REQUEST_TERMINATED_READ_ERROR);
      return;
    }

#ifdef HAVE_MESSAGES
    if (MHD_CONNECTION_INIT != connection->state)
      MHD_DLOG (connection->daemon,
                _ ("Connection socket is closed when reading " \
                   "request due to the error: %s\n"),
                (bytes_read < 0) ? str_conn_error_ (bytes_read) :
                "detected connection closure");
#endif
    CONNECTION_CLOSE_ERROR (connection,
                            NULL);
    return;
  }

  if (0 == bytes_read)
  {   /* Remote side closed connection. */
    connection->read_closed = true;
    if ( (MHD_CONNECTION_INIT < connection->state) &&
         (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (connection->daemon,
                _ ("Connection was closed by remote side with incomplete "
                   "request.\n"));
#endif
      connection->discard_request = true;
      MHD_connection_close_ (connection,
                             MHD_REQUEST_TERMINATED_CLIENT_ABORT);
    }
    else if (MHD_CONNECTION_INIT == connection->state)
      /* This termination code cannot be reported to the application
       * because application has not been informed yet about this request */
      MHD_connection_close_ (connection,
                             MHD_REQUEST_TERMINATED_COMPLETED_OK);
    else
      MHD_connection_close_ (connection,
                             MHD_REQUEST_TERMINATED_WITH_ERROR);
    return;
  }
  connection->read_buffer_offset += bytes_read;
  MHD_update_last_activity_ (connection);
#if DEBUG_STATES
  MHD_DLOG (connection->daemon,
            _ ("In function %s handling connection at state: %s\n"),
            __FUNCTION__,
            MHD_state_to_string (connection->state));
#endif
  switch (connection->state)
  {
  case MHD_CONNECTION_INIT:
  case MHD_CONNECTION_REQ_LINE_RECEIVING:
  case MHD_CONNECTION_URL_RECEIVED:
  case MHD_CONNECTION_HEADER_PART_RECEIVED:
  case MHD_CONNECTION_HEADERS_RECEIVED:
  case MHD_CONNECTION_HEADERS_PROCESSED:
  case MHD_CONNECTION_CONTINUE_SENDING:
  case MHD_CONNECTION_CONTINUE_SENT:
  case MHD_CONNECTION_BODY_RECEIVED:
  case MHD_CONNECTION_FOOTER_PART_RECEIVED:
    /* nothing to do but default action */
    if (connection->read_closed)
    {
      /* TODO: check whether this really needed */
      MHD_connection_close_ (connection,
                             MHD_REQUEST_TERMINATED_READ_ERROR);
    }
    return;
  case MHD_CONNECTION_CLOSED:
    return;
#ifdef UPGRADE_SUPPORT
  case MHD_CONNECTION_UPGRADE:
    mhd_assert (0);
    return;
#endif /* UPGRADE_SUPPORT */
  default:
    /* shrink read buffer to how much is actually used */
    if ((0 != connection->read_buffer_size) &&
        (connection->read_buffer_size != connection->read_buffer_offset))
    {
      mhd_assert (NULL != connection->read_buffer);
      connection->read_buffer =
        MHD_pool_reallocate (connection->pool,
                             connection->read_buffer,
                             connection->read_buffer_size,
                             connection->read_buffer_offset);
      connection->read_buffer_size = connection->read_buffer_offset;
    }
    break;
  }
  return;
}


/**
 * This function was created to handle writes to sockets when it has
 * been determined that the socket can be written to.
 *
 * @param connection connection to handle
 */
void
MHD_connection_handle_write (struct MHD_Connection *connection)
{
  struct MHD_Response *response;
  ssize_t ret;
  if (connection->suspended)
    return;

#ifdef HTTPS_SUPPORT
  if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
  {   /* HTTPS connection. */
    if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
    {
      if (! MHD_run_tls_handshake_ (connection))
        return;
    }
  }
#endif /* HTTPS_SUPPORT */

#if DEBUG_STATES
  MHD_DLOG (connection->daemon,
            _ ("In function %s handling connection at state: %s\n"),
            __FUNCTION__,
            MHD_state_to_string (connection->state));
#endif
  switch (connection->state)
  {
  case MHD_CONNECTION_INIT:
  case MHD_CONNECTION_REQ_LINE_RECEIVING:
  case MHD_CONNECTION_URL_RECEIVED:
  case MHD_CONNECTION_HEADER_PART_RECEIVED:
  case MHD_CONNECTION_HEADERS_RECEIVED:
    mhd_assert (0);
    return;
  case MHD_CONNECTION_HEADERS_PROCESSED:
    return;
  case MHD_CONNECTION_CONTINUE_SENDING:
    ret = MHD_send_data_ (connection,
                          &HTTP_100_CONTINUE
                          [connection->continue_message_write_offset],
                          MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE)
                          - connection->continue_message_write_offset,
                          true);
    if (ret < 0)
    {
      if (MHD_ERR_AGAIN_ == ret)
        return;
#ifdef HAVE_MESSAGES
      MHD_DLOG (connection->daemon,
                _ ("Failed to send data in request for %s.\n"),
                connection->url);
#endif
      CONNECTION_CLOSE_ERROR (connection,
                              NULL);
      return;
    }
#if _MHD_DEBUG_SEND_DATA
    fprintf (stderr,
             _ ("Sent 100 continue response: `%.*s'\n"),
             (int) ret,
             &HTTP_100_CONTINUE[connection->continue_message_write_offset]);
#endif
    connection->continue_message_write_offset += ret;
    MHD_update_last_activity_ (connection);
    return;
  case MHD_CONNECTION_CONTINUE_SENT:
  case MHD_CONNECTION_BODY_RECEIVED:
  case MHD_CONNECTION_FOOTER_PART_RECEIVED:
  case MHD_CONNECTION_FOOTERS_RECEIVED:
  case MHD_CONNECTION_FULL_REQ_RECEIVED:
  case MHD_CONNECTION_START_REPLY:
    mhd_assert (0);
    return;
  case MHD_CONNECTION_HEADERS_SENDING:
    {
      struct MHD_Response *const resp = connection->response;
      const size_t wb_ready = connection->write_buffer_append_offset
                              - connection->write_buffer_send_offset;
      mhd_assert (connection->write_buffer_append_offset >= \
                  connection->write_buffer_send_offset);
      mhd_assert (NULL != resp);
      mhd_assert ( (0 == resp->data_size) || \
                   (0 == resp->data_start) || \
                   (NULL != resp->crc) );
      mhd_assert ( (0 == connection->response_write_position) || \
                   (resp->total_size ==
                    connection->response_write_position) || \
                   (MHD_SIZE_UNKNOWN ==
                    connection->response_write_position) );
      mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
                  (! connection->rp_props.send_reply_body));

      if ( (connection->rp_props.send_reply_body) &&
           (NULL == resp->crc) &&
           (NULL == resp->data_iov) &&
           /* TODO: remove the next check as 'send_reply_body' is used */
           (0 == connection->response_write_position) &&
           (! connection->rp_props.chunked) )
      {
        mhd_assert (resp->total_size >= resp->data_size);
        mhd_assert (0 == resp->data_start);
        /* Send response headers alongside the response body, if the body
         * data is available. */
        ret = MHD_send_hdr_and_body_ (connection,
                                      &connection->write_buffer
                                      [connection->write_buffer_send_offset],
                                      wb_ready,
                                      false,
                                      resp->data,
                                      resp->data_size,
                                      (resp->total_size == resp->data_size));
      }
      else
      {
        /* This is response for HEAD request or reply body is not allowed
         * for any other reason or reply body is dynamically generated. */
        /* Do not send the body data even if it's available. */
        ret = MHD_send_hdr_and_body_ (connection,
                                      &connection->write_buffer
                                      [connection->write_buffer_send_offset],
                                      wb_ready,
                                      false,
                                      NULL,
                                      0,
                                      ((0 == resp->total_size) ||
                                       (! connection->rp_props.send_reply_body)
                                      ));
      }

      if (ret < 0)
      {
        if (MHD_ERR_AGAIN_ == ret)
          return;
#ifdef HAVE_MESSAGES
        MHD_DLOG (connection->daemon,
                  _ ("Failed to send the response headers for the " \
                     "request for `%s'. Error: %s\n"),
                  connection->url,
                  str_conn_error_ (ret));
#endif
        CONNECTION_CLOSE_ERROR (connection,
                                NULL);
        return;
      }
      /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
      if (((size_t) ret) > wb_ready)
      {
        /* The complete header and some response data have been sent,
         * update both offsets. */
        mhd_assert (0 == connection->response_write_position);
        mhd_assert (! connection->rp_props.chunked);
        mhd_assert (connection->rp_props.send_reply_body);
        connection->write_buffer_send_offset += wb_ready;
        connection->response_write_position = ret - wb_ready;
      }
      else
        connection->write_buffer_send_offset += ret;
      MHD_update_last_activity_ (connection);
      if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
        return;
      check_write_done (connection,
                        MHD_CONNECTION_HEADERS_SENT);
      return;
    }
  case MHD_CONNECTION_HEADERS_SENT:
    return;
  case MHD_CONNECTION_NORMAL_BODY_READY:
    response = connection->response;
    if (connection->response_write_position <
        connection->response->total_size)
    {
      uint64_t data_write_offset;

#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
      if (NULL != response->crc)
        MHD_mutex_lock_chk_ (&response->mutex);
#endif
      if (MHD_NO == try_ready_normal_body (connection))
      {
        /* mutex was already unlocked by try_ready_normal_body */
        return;
      }
#if defined(_MHD_HAVE_SENDFILE)
      if (MHD_resp_sender_sendfile == connection->resp_sender)
      {
        mhd_assert (NULL == response->data_iov);
        ret = MHD_send_sendfile_ (connection);
      }
      else /* combined with the next 'if' */
#endif /* _MHD_HAVE_SENDFILE */
      if (NULL != response->data_iov)
      {
        ret = MHD_send_iovec_ (connection,
                               &connection->resp_iov,
                               true);
      }
      else
      {
        data_write_offset = connection->response_write_position
                            - response->data_start;
        if (data_write_offset > (uint64_t) SIZE_MAX)
          MHD_PANIC (_ ("Data offset exceeds limit.\n"));
        ret = MHD_send_data_ (connection,
                              &response->data
                              [(size_t) data_write_offset],
                              response->data_size
                              - (size_t) data_write_offset,
                              true);
#if _MHD_DEBUG_SEND_DATA
        if (ret > 0)
          fprintf (stderr,
                   _ ("Sent %d-byte DATA response: `%.*s'\n"),
                   (int) ret,
                   (int) ret,
                   &response->data[connection->response_write_position
                                   - response->data_start]);
#endif
      }
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
      if (NULL != response->crc)
        MHD_mutex_unlock_chk_ (&response->mutex);
#endif
      if (ret < 0)
      {
        if (MHD_ERR_AGAIN_ == ret)
          return;
#ifdef HAVE_MESSAGES
        MHD_DLOG (connection->daemon,
                  _ ("Failed to send the response body for the " \
                     "request for `%s'. Error: %s\n"),
                  connection->url,
                  str_conn_error_ (ret));
#endif
        CONNECTION_CLOSE_ERROR (connection,
                                NULL);
        return;
      }
      connection->response_write_position += ret;
      MHD_update_last_activity_ (connection);
    }
    if (connection->response_write_position ==
        connection->response->total_size)
      connection->state = MHD_CONNECTION_FOOTERS_SENT;   /* have no footers */
    return;
  case MHD_CONNECTION_NORMAL_BODY_UNREADY:
    mhd_assert (0);
    return;
  case MHD_CONNECTION_CHUNKED_BODY_READY:
    ret = MHD_send_data_ (connection,
                          &connection->write_buffer
                          [connection->write_buffer_send_offset],
                          connection->write_buffer_append_offset
                          - connection->write_buffer_send_offset,
                          true);
    if (ret < 0)
    {
      if (MHD_ERR_AGAIN_ == ret)
        return;
#ifdef HAVE_MESSAGES
      MHD_DLOG (connection->daemon,
                _ ("Failed to send the chunked response body for the " \
                   "request for `%s'. Error: %s\n"),
                connection->url,
                str_conn_error_ (ret));
#endif
      CONNECTION_CLOSE_ERROR (connection,
                              NULL);
      return;
    }
    connection->write_buffer_send_offset += ret;
    MHD_update_last_activity_ (connection);
    if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
      return;
    check_write_done (connection,
                      (connection->response->total_size ==
                       connection->response_write_position) ?
                      MHD_CONNECTION_BODY_SENT :
                      MHD_CONNECTION_CHUNKED_BODY_UNREADY);
    return;
  case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
  case MHD_CONNECTION_BODY_SENT:
    mhd_assert (0);
    return;
  case MHD_CONNECTION_FOOTERS_SENDING:
    ret = MHD_send_data_ (connection,
                          &connection->write_buffer
                          [connection->write_buffer_send_offset],
                          connection->write_buffer_append_offset
                          - connection->write_buffer_send_offset,
                          true);
    if (ret < 0)
    {
      if (MHD_ERR_AGAIN_ == ret)
        return;
#ifdef HAVE_MESSAGES
      MHD_DLOG (connection->daemon,
                _ ("Failed to send the footers for the " \
                   "request for `%s'. Error: %s\n"),
                connection->url,
                str_conn_error_ (ret));
#endif
      CONNECTION_CLOSE_ERROR (connection,
                              NULL);
      return;
    }
    connection->write_buffer_send_offset += ret;
    MHD_update_last_activity_ (connection);
    if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
      return;
    check_write_done (connection,
                      MHD_CONNECTION_FOOTERS_SENT);
    return;
  case MHD_CONNECTION_FOOTERS_SENT:
    mhd_assert (0);
    return;
  case MHD_CONNECTION_CLOSED:
    return;
#ifdef UPGRADE_SUPPORT
  case MHD_CONNECTION_UPGRADE:
    mhd_assert (0);
    return;
#endif /* UPGRADE_SUPPORT */
  default:
    mhd_assert (0);
    CONNECTION_CLOSE_ERROR (connection,
                            _ ("Internal error.\n"));
    break;
  }
  return;
}


/**
 * Check whether connection has timed out.
 * @param c the connection to check
 * @return true if connection has timeout and needs to be closed,
 *         false otherwise.
 */
static bool
connection_check_timedout (struct MHD_Connection *c)
{
  const uint64_t timeout = c->connection_timeout_ms;
  uint64_t now;
  uint64_t since_actv;

  if (c->suspended)
    return false;
  if (0 == timeout)
    return false;
  now = MHD_monotonic_msec_counter ();
  since_actv = now - c->last_activity;
  /* Keep the next lines in sync with #connection_get_wait() to avoid
   * undesired side-effects like busy-waiting. */
  if (timeout < since_actv)
  {
    if (UINT64_MAX / 2 < since_actv)
    {
      const uint64_t jump_back = c->last_activity - now;
      /* Very unlikely that it is more than quarter-million years pause.
       * More likely that system clock jumps back. */
      if (5000 >= jump_back)
      {
#ifdef HAVE_MESSAGES
        MHD_DLOG (c->daemon,
                  _ ("Detected system clock %u milliseconds jump back.\n"),
                  (unsigned int) jump_back);
#endif
        return false;
      }
#ifdef HAVE_MESSAGES
      MHD_DLOG (c->daemon,
                _ ("Detected too large system clock %" PRIu64 " milliseconds "
                   "jump back.\n"),
                jump_back);
#endif
    }
    return true;
  }
  return false;
}


/**
 * Clean up the state of the given connection and move it into the
 * clean up queue for final disposal.
 * @remark To be called only from thread that process connection's
 * recv(), send() and response.
 *
 * @param connection handle for the connection to clean up
 */
static void
cleanup_connection (struct MHD_Connection *connection)
{
  struct MHD_Daemon *daemon = connection->daemon;
#ifdef MHD_USE_THREADS
  mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
               MHD_thread_ID_match_current_ (connection->pid) );
#endif /* MHD_USE_THREADS */

  if (connection->in_cleanup)
    return; /* Prevent double cleanup. */
  connection->in_cleanup = true;
  if (NULL != connection->response)
  {
    MHD_destroy_response (connection->response);
    connection->response = NULL;
  }
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
  MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
#endif
  if (connection->suspended)
  {
    DLL_remove (daemon->suspended_connections_head,
                daemon->suspended_connections_tail,
                connection);
    connection->suspended = false;
  }
  else
  {
    if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
    {
      if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
        XDLL_remove (daemon->normal_timeout_head,
                     daemon->normal_timeout_tail,
                     connection);
      else
        XDLL_remove (daemon->manual_timeout_head,
                     daemon->manual_timeout_tail,
                     connection);
    }
    DLL_remove (daemon->connections_head,
                daemon->connections_tail,
                connection);
  }
  DLL_insert (daemon->cleanup_head,
              daemon->cleanup_tail,
              connection);
  connection->resuming = false;
  connection->in_idle = false;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
  MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
#endif
  if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
  {
    /* if we were at the connection limit before and are in
       thread-per-connection mode, signal the main thread
       to resume accepting connections */
    if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
         (! MHD_itc_activate_ (daemon->itc, "c")) )
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                _ (
                  "Failed to signal end of connection via inter-thread communication channel.\n"));
#endif
    }
  }
}


/**
 * Reset connection after request-reply cycle.
 * @param connection the connection to process
 * @param reuse the flag to choose whether to close connection or
 *              prepare connection for the next request processing
 */
static void
connection_reset (struct MHD_Connection *connection,
                  bool reuse)
{
  struct MHD_Connection *const c = connection; /**< a short alias */
  struct MHD_Daemon *const d = connection->daemon;

  if (! reuse)
  {
    /* Next function will destroy response, notify client,
     * destroy memory pool, and set connection state to "CLOSED" */
    MHD_connection_close_ (connection,
                           c->stop_with_error ?
                           MHD_REQUEST_TERMINATED_WITH_ERROR :
                           MHD_REQUEST_TERMINATED_COMPLETED_OK);
    c->read_buffer = NULL;
    c->read_buffer_size = 0;
    c->read_buffer_offset = 0;
    c->write_buffer = NULL;
    c->write_buffer_size = 0;
    c->write_buffer_send_offset = 0;
    c->write_buffer_append_offset = 0;
  }
  else
  {
    /* Reset connection to process the next request */
    size_t new_read_buf_size;
    mhd_assert (! c->stop_with_error);
    mhd_assert (! c->discard_request);

    if ( (NULL != d->notify_completed) &&
         (c->client_aware) )
      d->notify_completed (d->notify_completed_cls,
                           c,
                           &c->client_context,
                           MHD_REQUEST_TERMINATED_COMPLETED_OK);
    c->client_aware = false;

    if (NULL != c->response)
      MHD_destroy_response (c->response);
    c->response = NULL;
    c->version = NULL;
    c->http_ver = MHD_HTTP_VER_UNKNOWN;
    c->last = NULL;
    c->colon = NULL;
    c->header_size = 0;
    c->keepalive = MHD_CONN_KEEPALIVE_UNKOWN;
    /* Reset the read buffer to the starting size,
       preserving the bytes we have already read. */
    new_read_buf_size = c->daemon->pool_size / 2;
    if (c->read_buffer_offset > new_read_buf_size)
      new_read_buf_size = c->read_buffer_offset;

    connection->read_buffer
      = MHD_pool_reset (c->pool,
                        c->read_buffer,
                        c->read_buffer_offset,
                        new_read_buf_size);
    c->read_buffer_size = new_read_buf_size;
    c->continue_message_write_offset = 0;
    c->headers_received = NULL;
    c->headers_received_tail = NULL;
    c->have_chunked_upload = false;
    c->current_chunk_size = 0;
    c->current_chunk_offset = 0;
    c->responseCode = 0;
    c->response_write_position = 0;
    c->method = NULL;
    c->http_mthd = MHD_HTTP_MTHD_NO_METHOD;
    c->url = NULL;
    memset (&c->rp_props, 0, sizeof(c->rp_props));
    c->write_buffer = NULL;
    c->write_buffer_size = 0;
    c->write_buffer_send_offset = 0;
    c->write_buffer_append_offset = 0;
    /* iov (if any) was deallocated by MHD_pool_reset */
    memset (&connection->resp_iov, 0, sizeof(connection->resp_iov));
    c->state = MHD_CONNECTION_INIT;
  }
  connection->client_context = NULL;
}


/**
 * This function was created to handle per-connection processing that
 * has to happen even if the socket cannot be read or written to.
 * @remark To be called only from thread that process connection's
 * recv(), send() and response.
 *
 * @param connection connection to handle
 * @return #MHD_YES if we should continue to process the
 *         connection (not dead yet), #MHD_NO if it died
 */
enum MHD_Result
MHD_connection_handle_idle (struct MHD_Connection *connection)
{
  struct MHD_Daemon *daemon = connection->daemon;
  char *line;
  size_t line_len;
  enum MHD_Result ret;
#ifdef MHD_USE_THREADS
  mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
               MHD_thread_ID_match_current_ (connection->pid) );
#endif /* MHD_USE_THREADS */
  /* 'daemon' is not used if epoll is not available and asserts are disabled */
  (void) daemon; /* Mute compiler warning */

  connection->in_idle = true;
  while (! connection->suspended)
  {
#ifdef HTTPS_SUPPORT
    if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
    {     /* HTTPS connection. */
      if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
          (MHD_TLS_CONN_CONNECTED > connection->tls_state))
        break;
    }
#endif /* HTTPS_SUPPORT */
#if DEBUG_STATES
    MHD_DLOG (daemon,
              _ ("In function %s handling connection at state: %s\n"),
              __FUNCTION__,
              MHD_state_to_string (connection->state));
#endif
    switch (connection->state)
    {
    case MHD_CONNECTION_INIT:
    case MHD_CONNECTION_REQ_LINE_RECEIVING:
      line = get_next_header_line (connection,
                                   &line_len);
      if (NULL != line)
      {
        /* Check for empty string, as we might want
           to tolerate 'spurious' empty lines */
        if (0 == line[0])
        {
          /* TODO: Add MHD option to not tolerate it */
          connection->state = MHD_CONNECTION_INIT;
          continue; /* Process the next line */
        }
        if (MHD_NO == parse_initial_message_line (connection,
                                                  line,
                                                  line_len))
          CONNECTION_CLOSE_ERROR_CHECK (connection,
                                        NULL);
        else
        {
          mhd_assert (MHD_IS_HTTP_VER_SUPPORTED (connection->http_ver));
          connection->state = MHD_CONNECTION_URL_RECEIVED;
        }
        continue;
      }
      /* NULL means we didn't get a full line yet */
      if (connection->discard_request)
      {
        mhd_assert (MHD_CONNECTION_INIT != connection->state);
        continue;
      }
      if (0 < connection->read_buffer_offset)
        connection->state = MHD_CONNECTION_REQ_LINE_RECEIVING;
      break;
    case MHD_CONNECTION_URL_RECEIVED:
      line = get_next_header_line (connection,
                                   NULL);
      if (NULL == line)
      {
        if (MHD_CONNECTION_URL_RECEIVED != connection->state)
          continue;
        if (connection->read_closed)
        {
          CONNECTION_CLOSE_ERROR (connection,
                                  NULL);
          continue;
        }
        break;
      }
      if (0 == line[0])
      {
        connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
        connection->header_size = (size_t) (connection->read_buffer
                                            - connection->method);
        continue;
      }
      if (MHD_NO == process_header_line (connection,
                                         line))
      {
        transmit_error_response_static (connection,
                                        MHD_HTTP_BAD_REQUEST,
                                        REQUEST_MALFORMED);
        break;
      }
      connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED;
      continue;
    case MHD_CONNECTION_HEADER_PART_RECEIVED:
      line = get_next_header_line (connection,
                                   NULL);
      if (NULL == line)
      {
        if (connection->state != MHD_CONNECTION_HEADER_PART_RECEIVED)
          continue;
        if (connection->read_closed)
        {
          CONNECTION_CLOSE_ERROR (connection,
                                  NULL);
          continue;
        }
        break;
      }
      if (MHD_NO ==
          process_broken_line (connection,
                               line,
                               MHD_HEADER_KIND))
        continue;
      if (0 == line[0])
      {
        connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
        connection->header_size = (size_t) (connection->read_buffer
                                            - connection->method);
        continue;
      }
      continue;
    case MHD_CONNECTION_HEADERS_RECEIVED:
      parse_connection_headers (connection);
      if (MHD_CONNECTION_CLOSED == connection->state)
        continue;
      connection->state = MHD_CONNECTION_HEADERS_PROCESSED;
      if (connection->suspended)
        break;
      continue;
    case MHD_CONNECTION_HEADERS_PROCESSED:
      call_connection_handler (connection);     /* first call */
      if (MHD_CONNECTION_CLOSED == connection->state)
        continue;
      if (connection->suspended)
        continue;
      if ( (NULL == connection->response) &&
           (need_100_continue (connection)) )
      {
        connection->state = MHD_CONNECTION_CONTINUE_SENDING;
        break;
      }
      if ( (NULL != connection->response) &&
           (0 != connection->remaining_upload_size) )
      {
        /* we refused (no upload allowed!) */
        connection->remaining_upload_size = 0;
        /* force close, in case client still tries to upload... */
        connection->discard_request = true;
      }
      connection->state = (0 == connection->remaining_upload_size)
                          ? MHD_CONNECTION_FULL_REQ_RECEIVED
                          : MHD_CONNECTION_CONTINUE_SENT;
      if (connection->suspended)
        break;
      continue;
    case MHD_CONNECTION_CONTINUE_SENDING:
      if (connection->continue_message_write_offset ==
          MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE))
      {
        connection->state = MHD_CONNECTION_CONTINUE_SENT;
        continue;
      }
      break;
    case MHD_CONNECTION_CONTINUE_SENT:
      if (0 != connection->read_buffer_offset)
      {
        process_request_body (connection);           /* loop call */
        if (connection->discard_request)
        {
          mhd_assert (MHD_CONNECTION_CONTINUE_SENT != connection->state);
          continue;
        }
      }
      if ( (0 == connection->remaining_upload_size) ||
           ( (MHD_SIZE_UNKNOWN == connection->remaining_upload_size) &&
             (0 == connection->read_buffer_offset) &&
             (connection->discard_request) ) )
      {
        if ( (connection->have_chunked_upload) &&
             (! connection->discard_request) )
          connection->state = MHD_CONNECTION_BODY_RECEIVED;
        else
          connection->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
        if (connection->suspended)
          break;
        continue;
      }
      break;
    case MHD_CONNECTION_BODY_RECEIVED:
      line = get_next_header_line (connection,
                                   NULL);
      if (NULL == line)
      {
        if (connection->state != MHD_CONNECTION_BODY_RECEIVED)
          continue;
        if (connection->read_closed)
        {
          CONNECTION_CLOSE_ERROR (connection,
                                  NULL);
          continue;
        }
        if (0 < connection->read_buffer_offset)
          connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
        break;
      }
      if (0 == line[0])
      {
        connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
        if (connection->suspended)
          break;
        continue;
      }
      if (MHD_NO == process_header_line (connection,
                                         line))
      {
        transmit_error_response_static (connection,
                                        MHD_HTTP_BAD_REQUEST,
                                        REQUEST_MALFORMED);
        break;
      }
      connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
      continue;
    case MHD_CONNECTION_FOOTER_PART_RECEIVED:
      line = get_next_header_line (connection,
                                   NULL);
      if (NULL == line)
      {
        if (connection->state != MHD_CONNECTION_FOOTER_PART_RECEIVED)
          continue;
        if (connection->read_closed)
        {
          CONNECTION_CLOSE_ERROR (connection,
                                  NULL);
          continue;
        }
        break;
      }
      if (MHD_NO ==
          process_broken_line (connection,
                               line,
                               MHD_FOOTER_KIND))
        continue;
      if (0 == line[0])
      {
        connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
        if (connection->suspended)
          break;
        continue;
      }
      continue;
    case MHD_CONNECTION_FOOTERS_RECEIVED:
      /* The header, the body, and the footers of the request has been received,
       * switch to the final processing of the request. */
      connection->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
      continue;
    case MHD_CONNECTION_FULL_REQ_RECEIVED:
      call_connection_handler (connection);     /* "final" call */
      if (connection->state == MHD_CONNECTION_CLOSED)
        continue;
      if (NULL == connection->response)
        break;                  /* try again next time */
      /* Response is ready, start reply */
      connection->state = MHD_CONNECTION_START_REPLY;
      continue;
    case MHD_CONNECTION_START_REPLY:
      mhd_assert (NULL != connection->response);
      connection_switch_from_recv_to_send (connection);
      if (MHD_NO == build_header_response (connection))
      {
        /* oops - close! */
        CONNECTION_CLOSE_ERROR (connection,
                                _ ("Closing connection (failed to create "
                                   "response header).\n"));
        continue;
      }
      connection->state = MHD_CONNECTION_HEADERS_SENDING;
      break;

    case MHD_CONNECTION_HEADERS_SENDING:
      /* no default action */
      break;
    case MHD_CONNECTION_HEADERS_SENT:
      /* Some clients may take some actions right after header receive */
#ifdef UPGRADE_SUPPORT
      if (NULL != connection->response->upgrade_handler)
      {
        connection->state = MHD_CONNECTION_UPGRADE;
        /* This connection is "upgraded".  Pass socket to application. */
        if (MHD_NO ==
            MHD_response_execute_upgrade_ (connection->response,
                                           connection))
        {
          /* upgrade failed, fail hard */
          CONNECTION_CLOSE_ERROR (connection,
                                  NULL);
          continue;
        }
        /* Response is not required anymore for this connection. */
        {
          struct MHD_Response *const resp = connection->response;

          connection->response = NULL;
          MHD_destroy_response (resp);
        }
        continue;
      }
#endif /* UPGRADE_SUPPORT */

      if (connection->rp_props.chunked)
        connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
      else
        connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
      continue;
    case MHD_CONNECTION_NORMAL_BODY_READY:
      /* nothing to do here */
      break;
    case MHD_CONNECTION_NORMAL_BODY_UNREADY:
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
      if (NULL != connection->response->crc)
        MHD_mutex_lock_chk_ (&connection->response->mutex);
#endif
      if (0 == connection->response->total_size)
      {
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
        if (NULL != connection->response->crc)
          MHD_mutex_unlock_chk_ (&connection->response->mutex);
#endif
        if (connection->rp_props.chunked)
          connection->state = MHD_CONNECTION_BODY_SENT;
        else
          connection->state = MHD_CONNECTION_FOOTERS_SENT;
        continue;
      }
      if (MHD_NO != try_ready_normal_body (connection))
      {
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
        if (NULL != connection->response->crc)
          MHD_mutex_unlock_chk_ (&connection->response->mutex);
#endif
        connection->state = MHD_CONNECTION_NORMAL_BODY_READY;
        /* Buffering for flushable socket was already enabled*/

        break;
      }
      /* mutex was already unlocked by "try_ready_normal_body */
      /* not ready, no socket action */
      break;
    case MHD_CONNECTION_CHUNKED_BODY_READY:
      /* nothing to do here */
      break;
    case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
      if (NULL != connection->response->crc)
        MHD_mutex_lock_chk_ (&connection->response->mutex);
#endif
      if ( (0 == connection->response->total_size) ||
           (connection->response_write_position ==
            connection->response->total_size) )
      {
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
        if (NULL != connection->response->crc)
          MHD_mutex_unlock_chk_ (&connection->response->mutex);
#endif
        connection->state = MHD_CONNECTION_BODY_SENT;
        continue;
      }
      if (1)
      { /* pseudo-branch for local variables scope */
        bool finished;
        if (MHD_NO != try_ready_chunked_body (connection, &finished))
        {
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
          if (NULL != connection->response->crc)
            MHD_mutex_unlock_chk_ (&connection->response->mutex);
#endif
          connection->state = finished ? MHD_CONNECTION_BODY_SENT :
                              MHD_CONNECTION_CHUNKED_BODY_READY;
          continue;
        }
        /* mutex was already unlocked by try_ready_chunked_body */
      }
      break;
    case MHD_CONNECTION_BODY_SENT:
      mhd_assert (connection->rp_props.chunked);

      if (MHD_NO == build_connection_chunked_response_footer (connection))
      {
        /* oops - close! */
        CONNECTION_CLOSE_ERROR (connection,
                                _ (
                                  "Closing connection (failed to create response footer)."));
        continue;
      }
      /* TODO: remove next 'if' */
      if ( (! connection->rp_props.chunked) ||
           (connection->write_buffer_send_offset ==
            connection->write_buffer_append_offset) )
        connection->state = MHD_CONNECTION_FOOTERS_SENT;
      else
        connection->state = MHD_CONNECTION_FOOTERS_SENDING;
      continue;
    case MHD_CONNECTION_FOOTERS_SENDING:
      /* no default action */
      break;
    case MHD_CONNECTION_FOOTERS_SENT:
      if (MHD_HTTP_PROCESSING == connection->responseCode)
      {
        /* After this type of response, we allow sending another! */
        connection->state = MHD_CONNECTION_HEADERS_PROCESSED;
        MHD_destroy_response (connection->response);
        connection->response = NULL;
        /* FIXME: maybe partially reset memory pool? */
        continue;
      }
      /* Reset connection after complete reply */
      connection_reset (connection,
                        MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
                        ! connection->read_closed &&
                        ! connection->discard_request);
      continue;
    case MHD_CONNECTION_CLOSED:
      cleanup_connection (connection);
      connection->in_idle = false;
      return MHD_NO;
#ifdef UPGRADE_SUPPORT
    case MHD_CONNECTION_UPGRADE:
      connection->in_idle = false;
      return MHD_YES;     /* keep open */
#endif /* UPGRADE_SUPPORT */
    default:
      mhd_assert (0);
      break;
    }
    break;
  }
  if (connection_check_timedout (connection))
  {
    MHD_connection_close_ (connection,
                           MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
    connection->in_idle = false;
    return MHD_YES;
  }
  MHD_connection_update_event_loop_info (connection);
  ret = MHD_YES;
#ifdef EPOLL_SUPPORT
  if ( (! connection->suspended) &&
       (0 != (daemon->options & MHD_USE_EPOLL)) )
  {
    ret = MHD_connection_epoll_update_ (connection);
  }
#endif /* EPOLL_SUPPORT */
  connection->in_idle = false;
  return ret;
}


#ifdef EPOLL_SUPPORT
/**
 * Perform epoll() processing, possibly moving the connection back into
 * the epoll() set if needed.
 *
 * @param connection connection to process
 * @return #MHD_YES if we should continue to process the
 *         connection (not dead yet), #MHD_NO if it died
 */
enum MHD_Result
MHD_connection_epoll_update_ (struct MHD_Connection *connection)
{
  struct MHD_Daemon *daemon = connection->daemon;

  if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
       (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
       (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
       ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
           (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
         ( (MHD_EVENT_LOOP_INFO_READ == connection->event_loop_info) &&
           (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
  {
    /* add to epoll set */
    struct epoll_event event;

    event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
    event.data.ptr = connection;
    if (0 != epoll_ctl (daemon->epoll_fd,
                        EPOLL_CTL_ADD,
                        connection->socket_fd,
                        &event))
    {
#ifdef HAVE_MESSAGES
      if (0 != (daemon->options & MHD_USE_ERROR_LOG))
        MHD_DLOG (daemon,
                  _ ("Call to epoll_ctl failed: %s\n"),
                  MHD_socket_last_strerr_ ());
#endif
      connection->state = MHD_CONNECTION_CLOSED;
      cleanup_connection (connection);
      return MHD_NO;
    }
    connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
  }
  return MHD_YES;
}


#endif


/**
 * Set callbacks for this connection to those for HTTP.
 *
 * @param connection connection to initialize
 */
void
MHD_set_http_callbacks_ (struct MHD_Connection *connection)
{
  connection->recv_cls = &recv_param_adapter;
}


/**
 * Obtain information about the given connection.
 *
 * @param connection what connection to get information about
 * @param info_type what information is desired?
 * @param ... depends on @a info_type
 * @return NULL if this information is not available
 *         (or if the @a info_type is unknown)
 * @ingroup specialized
 */
const union MHD_ConnectionInfo *
MHD_get_connection_info (struct MHD_Connection *connection,
                         enum MHD_ConnectionInfoType info_type,
                         ...)
{
  switch (info_type)
  {
#ifdef HTTPS_SUPPORT
  case MHD_CONNECTION_INFO_CIPHER_ALGO:
    if (NULL == connection->tls_session)
      return NULL;
    connection->cipher = gnutls_cipher_get (connection->tls_session);
    return (const union MHD_ConnectionInfo *) &connection->cipher;
  case MHD_CONNECTION_INFO_PROTOCOL:
    if (NULL == connection->tls_session)
      return NULL;
    connection->protocol = gnutls_protocol_get_version (
      connection->tls_session);
    return (const union MHD_ConnectionInfo *) &connection->protocol;
  case MHD_CONNECTION_INFO_GNUTLS_SESSION:
    if (NULL == connection->tls_session)
      return NULL;
    return (const union MHD_ConnectionInfo *) &connection->tls_session;
#endif /* HTTPS_SUPPORT */
  case MHD_CONNECTION_INFO_CLIENT_ADDRESS:
    return (const union MHD_ConnectionInfo *) &connection->addr;
  case MHD_CONNECTION_INFO_DAEMON:
    return (const union MHD_ConnectionInfo *) &connection->daemon;
  case MHD_CONNECTION_INFO_CONNECTION_FD:
    return (const union MHD_ConnectionInfo *) &connection->socket_fd;
  case MHD_CONNECTION_INFO_SOCKET_CONTEXT:
    return (const union MHD_ConnectionInfo *) &connection->socket_context;
  case MHD_CONNECTION_INFO_CONNECTION_SUSPENDED:
    connection->suspended_dummy = connection->suspended ? MHD_YES : MHD_NO;
    return (const union MHD_ConnectionInfo *) &connection->suspended_dummy;
  case MHD_CONNECTION_INFO_CONNECTION_TIMEOUT:
    connection->connection_timeout_dummy =
      (unsigned int) connection->connection_timeout_ms / 1000;
    return (const union MHD_ConnectionInfo *) &connection->
           connection_timeout_dummy;
  case MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE:
    if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
         (MHD_CONNECTION_CLOSED == connection->state) )
      return NULL;   /* invalid, too early! */
    return (const union MHD_ConnectionInfo *) &connection->header_size;
  case MHD_CONNECTION_INFO_HTTP_STATUS:
    if (NULL == connection->response)
      return NULL;
    return (const union MHD_ConnectionInfo *) &connection->responseCode;
  default:
    return NULL;
  }
}


/**
 * Set a custom option for the given connection, overriding defaults.
 *
 * @param connection connection to modify
 * @param option option to set
 * @param ... arguments to the option, depending on the option type
 * @return #MHD_YES on success, #MHD_NO if setting the option failed
 * @ingroup specialized
 */
enum MHD_Result
MHD_set_connection_option (struct MHD_Connection *connection,
                           enum MHD_CONNECTION_OPTION option,
                           ...)
{
  va_list ap;
  struct MHD_Daemon *daemon;
  unsigned int ui_val;

  daemon = connection->daemon;
  switch (option)
  {
  case MHD_CONNECTION_OPTION_TIMEOUT:
    if (0 == connection->connection_timeout_ms)
      connection->last_activity = MHD_monotonic_msec_counter ();
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
    MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
#endif
    if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
         (! connection->suspended) )
    {
      if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
        XDLL_remove (daemon->normal_timeout_head,
                     daemon->normal_timeout_tail,
                     connection);
      else
        XDLL_remove (daemon->manual_timeout_head,
                     daemon->manual_timeout_tail,
                     connection);
    }
    va_start (ap, option);
    ui_val = va_arg (ap, unsigned int);
    va_end (ap);
#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
    if ((UINT64_MAX / 4000 - 1) < ui_val)
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (connection->daemon,
                _ ("The specified connection timeout (%u) is too " \
                   "large. Maximum allowed value (%" PRIu64 ") will be used " \
                   "instead.\n"),
                ui_val,
                (UINT64_MAX / 4000 - 1));
#endif
      ui_val = UINT64_MAX / 4000 - 1;
    }
    else
#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
    connection->connection_timeout_ms = ui_val * 1000;
    if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
         (! connection->suspended) )
    {
      if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
        XDLL_insert (daemon->normal_timeout_head,
                     daemon->normal_timeout_tail,
                     connection);
      else
        XDLL_insert (daemon->manual_timeout_head,
                     daemon->manual_timeout_tail,
                     connection);
    }
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
    MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
#endif
    return MHD_YES;
  default:
    return MHD_NO;
  }
}


/**
 * Queue a response to be transmitted to the client (as soon as
 * possible but after #MHD_AccessHandlerCallback returns).
 *
 * For any active connection this function must be called
 * only by #MHD_AccessHandlerCallback callback.
 * For suspended connection this function can be called at any moment. Response
 * will be sent as soon as connection is resumed.
 *
 * @param connection the connection identifying the client
 * @param status_code HTTP status code (i.e. #MHD_HTTP_OK)
 * @param response response to transmit
 * @return #MHD_NO on error (i.e. reply already sent),
 *         #MHD_YES on success or if message has been queued
 * @ingroup response
 * @sa #MHD_AccessHandlerCallback
 */
enum MHD_Result
MHD_queue_response (struct MHD_Connection *connection,
                    unsigned int status_code,
                    struct MHD_Response *response)
{
  struct MHD_Daemon *daemon;

  if ((NULL == connection) || (NULL == response))
    return MHD_NO;

  daemon = connection->daemon;

#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
  if ( (! connection->suspended) &&
       (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) &&
       (! MHD_thread_ID_match_current_ (connection->pid)) )
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              _ ("Attempted to queue response on wrong thread!\n"));
#endif
    return MHD_NO;
  }
#endif

  if (daemon->shutdown)
    return MHD_YES; /* If daemon was shut down in parallel,
                     * response will be aborted now or on later stage. */

  if ( (NULL != connection->response) ||
       ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
         (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) ) )
    return MHD_NO;

#ifdef UPGRADE_SUPPORT
  if (NULL != response->upgrade_handler)
  {
    struct MHD_HTTP_Header *conn_header;
    if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                _ ("Attempted 'upgrade' connection on daemon without" \
                   " MHD_ALLOW_UPGRADE option!\n"));
#endif
      return MHD_NO;
    }
    if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                _ ("Application used invalid status code for" \
                   " 'upgrade' response!\n"));
#endif
      return MHD_NO;
    }
    if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                _ ("Application used invalid response" \
                   " without \"Connection\" header!\n"));
#endif
      return MHD_NO;
    }
    conn_header = response->first_header;
    mhd_assert (NULL != conn_header);
    mhd_assert (MHD_str_equal_caseless_ (conn_header->header,
                                         MHD_HTTP_HEADER_CONNECTION));
    if (! MHD_str_has_s_token_caseless_ (conn_header->value,
                                         "upgrade"))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                _ ("Application used invalid response" \
                   " without \"upgrade\" token in" \
                   " \"Connection\" header!\n"));
#endif
      return MHD_NO;
    }
    if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->http_ver))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                _ ("Connection \"Upgrade\" can be used " \
                   "with HTTP/1.1 connections!\n"));
#endif
      return MHD_NO;
    }
  }
#endif /* UPGRADE_SUPPORT */
  if ( (100 > (status_code & (~MHD_ICY_FLAG))) ||
       (999 < (status_code & (~MHD_ICY_FLAG))) )
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG (daemon,
              _ ("Refused wrong status code (%u). " \
                 "HTTP requires three digits status code!\n"),
              (status_code & (~MHD_ICY_FLAG)));
#endif
    return MHD_NO;
  }
  if (200 > (status_code & (~MHD_ICY_FLAG)))
  {
    if (MHD_HTTP_VER_1_0 == connection->http_ver)
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                _ ("Wrong status code (%u) refused. " \
                   "HTTP/1.0 clients do not support 1xx status codes!\n"),
                (status_code & (~MHD_ICY_FLAG)));
#endif
      return MHD_NO;
    }
    if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
                                 | MHD_RF_HTTP_1_0_SERVER)))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
                _ ("Wrong status code (%u) refused. " \
                   "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
                (status_code & (~MHD_ICY_FLAG)));
#endif
      return MHD_NO;
    }
  }

  MHD_increment_response_rc (response);
  connection->response = response;
  connection->responseCode = status_code;
#if defined(_MHD_HAVE_SENDFILE)
  if ( (response->fd == -1) ||
       (response->is_pipe) ||
       (0 != (connection->daemon->options & MHD_USE_TLS))
#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
       defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
       || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
          MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
       )
    connection->resp_sender = MHD_resp_sender_std;
  else
    connection->resp_sender = MHD_resp_sender_sendfile;
#endif /* _MHD_HAVE_SENDFILE */
  /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
     to avoid two user-space copies... */

  if ( (MHD_HTTP_MTHD_HEAD == connection->http_mthd) ||
       (MHD_HTTP_OK > status_code) ||
       (MHD_HTTP_NO_CONTENT == status_code) ||
       (MHD_HTTP_NOT_MODIFIED == status_code) )
  {
    /* if this is a "HEAD" request, or a status code for
       which a body is not allowed, pretend that we
       have already sent the full message body. */
    /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
     * checks */
    connection->response_write_position = response->total_size;
  }
  if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
  {
    /* response was queued "early", refuse to read body / footers or
       further requests! */
    connection->discard_request = true;
    connection->state = MHD_CONNECTION_START_REPLY;
    connection->remaining_upload_size = 0;
  }
  if (! connection->in_idle)
    (void) MHD_connection_handle_idle (connection);
  MHD_update_last_activity_ (connection);
  return MHD_YES;
}


/* end of connection.c */
