Evgeny Grin (Karlson2k) il y a 1 an
Parent
commit
a701a3ef6f

+ 36 - 5
src/include/microhttpd2.h

@@ -529,20 +529,41 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_HOST_HEADER_MISSING = 40060
   ,
+  /**
+   * Request has more than one "Host:" header.
+   */
+  MHD_SC_HOST_HEADER_SEVERAL = 40061
+  ,
   /**
    * The given content length was not a number.
    */
-  MHD_SC_CONTENT_LENGTH_MALFORMED = 40061
+  MHD_SC_CONTENT_LENGTH_MALFORMED = 40062
+  ,
+  /**
+   * Request has more than one "Content-Length:" header with the same value.
+   */
+  MHD_SC_CONTENT_LENGTH_SEVERAL_SAME = 40063
+  ,
+  /**
+   * Request has more than one "Content-Length:" header with the different
+   * values.
+   */
+  MHD_SC_CONTENT_LENGTH_SEVERAL_DIFFERENT = 40064
   ,
   /**
    * The BOTH Content-Length and Transfer-Encoding headers are used.
    */
-  MHD_SC_CONTENT_LENGTH_AND_TR_ENC = 40062
+  MHD_SC_CONTENT_LENGTH_AND_TR_ENC = 40065
   ,
   /**
    * The Content-Length is too large to be handled.
    */
-  MHD_SC_CONTENT_LENGTH_TOO_LARGE = 40062
+  MHD_SC_CONTENT_LENGTH_TOO_LARGE = 40066
+  ,
+  /**
+   * Transfer encoding in request is unsupported or invalid.
+   */
+  MHD_SC_CHUNKED_ENCODING_UNSUPPORTED = 40067
   ,
   /**
    * The given uploaded, chunked-encoded body was malformed.
@@ -1218,14 +1239,24 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
   MHD_SC_RESP_HEADER_VALUE_INVALID = 60051
   ,
   /**
-   * The the provided MHD_Action is invalid
+   * The provided MHD_Action is invalid
    */
   MHD_SC_ACTION_INVALID = 60080
   ,
   /**
-   * The the provided MHD_UploadAction is invalid
+   * The provided MHD_UploadAction is invalid
    */
   MHD_SC_UPLOAD_ACTION_INVALID = 60081
+  ,
+  /**
+   * The response must be empty
+   */
+  MHD_SC_REPLY_NOT_EMPTY_RESPONSE = 60101
+  ,
+  /**
+   * The "Content-Length" header is not allowed in the reply
+   */
+  MHD_SC_REPLY_CONTENT_LENGTH_NOT_ALLOWED = 60102
 
 };
 

+ 8 - 0
src/include/microhttpd2_generated_response_options.h

@@ -46,6 +46,10 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_ResponseOption
    * Response still use HTTP/1.1 version in header, but always close the connection after sending the response and do not use chunked encoding for the response.
    * You can also set the #MHD_R_O_HTTP_1_0_SERVER flag to force HTTP/1.0 version in the response.
    * Responses are still compatible with HTTP/1.1.
+   * Summary:
+   * + declared reply version: HTTP/1.1
+   * + keep-alive: no
+   * + chunked: no
    *
    * This option can be used to communicate with some broken client, which does not implement HTTP/1.1 features, but advertises HTTP/1.1 support.
    */
@@ -59,6 +63,10 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_ResponseOption
    * Chunked encoding will not be used for the response.
    * Due to backward compatibility, responses still can be used with HTTP/1.1 clients.
    * This option can be used to emulate HTTP/1.0 server (for response part only as chunked encoding in requests (if any) is processed by MHD).
+   * Summary:
+   * + declared reply version: HTTP/1.0
+   * + keep-alive: possible
+   * + chunked: no
    *
    * With this option HTTP/1.0 server is emulated (with support for 'keep-alive' connections).
    */

+ 2 - 1
src/mhd2/Makefile.am

@@ -68,6 +68,7 @@ libmicrohttpd2_la_SOURCES = \
   daemon_funcs.c            daemon_funcs.h \
   conn_data_process.c       conn_data_process.h \
   conn_data_recv.c          conn_data_recv.h \
+  conn_data_send.c          conn_data_send.h \
   request_funcs.c           request_funcs.h \
   request_get_value.c       request_get_value.h \
   respond_with_error.h \
@@ -79,7 +80,7 @@ libmicrohttpd2_la_SOURCES = \
   stream_funcs.c            stream_funcs.h \
   stream_process_states.c   stream_process_states.h \
   stream_process_request.c  stream_process_request.h \
-  stream_process_reply.h
+  stream_process_reply.c    stream_process_reply.h
 
 if ! HAVE_SYS_CALLOC
 libmicrohttpd2_la_SOURCES += \

+ 2 - 2
src/mhd2/conn_data_process.c

@@ -93,7 +93,7 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
 
     if (use_send)
     {
-      mhd_conn_data_send (c, has_sock_err);
+      mhd_conn_data_send (c);
       if (! mhd_conn_process_data (c))
         return false;
       data_processed = true;
@@ -149,7 +149,7 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
     /* If all headers were sent by single write_handler() and
      * response body is prepared by single MHD_connection_handle_idle()
      * call - continue. */
-    if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) ||
+    if ((MHD_CONNECTION_UNCHUNKED_BODY_READY == c->state) ||
         (MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
     {
       MHD_connection_handle_write (c);

+ 19 - 17
src/mhd2/conn_data_send.c

@@ -34,13 +34,16 @@
 #include "sys_bool_type.h"
 #include "sys_base_types.h"
 #include "mhd_str_macros.h"
-#include "mhd_socket_error.h"
 
 #include "mhd_assert.h"
 
 #include "mhd_connection.h"
+#include "mhd_response.h"
+
+#include "mhd_socket_error.h"
 
 #include "mhd_send.h"
+#include "stream_funcs.h"
 
 
 /**
@@ -69,8 +72,7 @@ check_write_done (struct MHD_Connection *restrict connection,
 
 
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
-mhd_conn_data_send (struct MHD_Connection *restrict c,
-                    bool has_err)
+mhd_conn_data_send (struct MHD_Connection *restrict c)
 {
   static const char http_100_continue_msg[] =
     mdh_HTTP_1_1_100_CONTINUE_REPLY;
@@ -90,10 +92,10 @@ mhd_conn_data_send (struct MHD_Connection *restrict c,
   if (MHD_CONNECTION_CONTINUE_SENDING == c->state)
   {
     res = mhd_send_data (c,
-                         http_100_continue_msg
-                         [c->continue_message_write_offset],
                          http_100_continue_msg_len
                          - c->continue_message_write_offset,
+                         http_100_continue_msg
+                         + c->continue_message_write_offset,
                          true,
                          &sent);
     if (mhd_SOCKET_ERR_NO_ERROR != res)
@@ -107,26 +109,26 @@ mhd_conn_data_send (struct MHD_Connection *restrict c,
     mhd_assert (c->write_buffer_append_offset >= \
                 c->write_buffer_send_offset);
     mhd_assert (NULL != resp);
-    mhd_assert ((MHD_CONN_MUST_UPGRADE != c->keepalive) || \
+    mhd_assert ((mhd_CONN_MUST_UPGRADE != c->conn_reuse) || \
                 (! c->rp.props.send_reply_body));
 
     // TODO: support body generating alongside with header sending
 
     if ((c->rp.props.send_reply_body) &&
-        (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) &&
-        (! c->rp.props.chunked) )
+        (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc))
     {
       /* Send response headers alongside the response body, if the body
        * data is available. */
       mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
+      mhd_assert (! c->rp.props.chunked);
 
       res = mhd_send_hdr_and_body (c,
+                                   wb_ready,
                                    c->write_buffer
                                    + c->write_buffer_send_offset,
-                                   wb_ready,
                                    false,
-                                   (const char *) resp->cntn.buf,
                                    resp->cntn_size,
+                                   (const char *) resp->cntn.buf,
                                    true,
                                    &sent);
     }
@@ -136,12 +138,12 @@ mhd_conn_data_send (struct MHD_Connection *restrict c,
        * for any other reason or reply body is dynamically generated. */
       /* Do not send the body data even if it's available. */
       res = mhd_send_hdr_and_body (c,
+                                   wb_ready,
                                    c->write_buffer
                                    + c->write_buffer_send_offset,
-                                   wb_ready,
                                    false,
-                                   NULL,
                                    0,
+                                   NULL,
                                    ((0 == resp->cntn_size) ||
                                     (! c->rp.props.send_reply_body)),
                                    &sent);
@@ -170,7 +172,7 @@ mhd_conn_data_send (struct MHD_Connection *restrict c,
     }
 
   }
-  else if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) ||
+  else if ((MHD_CONNECTION_UNCHUNKED_BODY_READY == c->state) ||
            (MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
   {
     struct MHD_Response *const restrict resp = c->rp.response;
@@ -183,9 +185,9 @@ mhd_conn_data_send (struct MHD_Connection *restrict c,
       mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
 
       res = mhd_send_data (c,
+                           c->rp.rsp_cntn_read_pos - resp->cntn_size,
                            (const char *) resp->cntn.buf
                            + c->rp.rsp_cntn_read_pos,
-                           c->rp.rsp_cntn_read_pos - resp->cntn_size,
                            true,
                            &sent);
     }
@@ -195,9 +197,9 @@ mhd_conn_data_send (struct MHD_Connection *restrict c,
                   c->write_buffer_send_offset);
 
       res = mhd_send_data (c,
-                           c->write_buffer + c->write_buffer_send_offset,
                            c->write_buffer_append_offset
                            - c->write_buffer_send_offset,
+                           c->write_buffer + c->write_buffer_send_offset,
                            true,
                            &sent);
     }
@@ -245,10 +247,10 @@ mhd_conn_data_send (struct MHD_Connection *restrict c,
   else if (MHD_CONNECTION_FOOTERS_SENDING == c->state)
   {
     res = mhd_send_data (c,
-                         c->write_buffer
-                         + c->write_buffer_send_offset,
                          c->write_buffer_append_offset
                          - c->write_buffer_send_offset,
+                         c->write_buffer
+                         + c->write_buffer_send_offset,
                          true,
                          &sent);
     if (mhd_SOCKET_ERR_NO_ERROR != res)

+ 1 - 4
src/mhd2/conn_data_send.h

@@ -38,12 +38,9 @@ struct MHD_Connection; /* forward declarations */
  * type.
  *
  * @param c the connection to use
- * @param has_err if 'true' then just check for the network error
- *        type is performed
  */
 MHD_INTERNAL void
-mhd_conn_data_send (struct MHD_Connection *restrict c,
-                    bool has_err)
+mhd_conn_data_send (struct MHD_Connection *restrict c)
 MHD_FN_PAR_NONNULL_ALL_;
 
 

+ 40 - 27
src/mhd2/daemon_add_conn.c

@@ -79,7 +79,7 @@ connection_set_initial_state (struct MHD_Connection *restrict c)
 
   mhd_assert (MHD_CONNECTION_INIT == c->state);
 
-  c->keepalive = MHD_CONN_KEEPALIVE_UNKOWN;
+  c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE;
   c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
 
   memset (&c->rq, 0, sizeof(c->rq));
@@ -241,7 +241,7 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
 
   if (NULL == (connection = mhd_calloc (1, sizeof (struct MHD_Connection))))
   {
-    MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE,
+    mhd_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE,
                  "Failed to allocate memory for the new connection");
     mhd_socket_close (client_socket);
     return MHD_SC_CONNECTION_MALLOC_FAILURE;
@@ -262,7 +262,7 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
   {
     if (NULL == (connection->addr = malloc (addrlen)))
     {
-      MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE,
+      mhd_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE,
                    "Failed to allocate memory for the new connection");
       mhd_socket_close (client_socket);
       free (connection);
@@ -320,7 +320,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
   connection->pool = mdh_pool_create (daemon->conns.cfg.mem_pool_size);
   if (NULL == connection->pool)
   { /* 'pool' creation failed */
-    MHD_LOG_MSG (daemon, MHD_SC_POOL_MALLOC_FAILURE, \
+    mhd_LOG_MSG (daemon, MHD_SC_POOL_MALLOC_FAILURE, \
                  "Failed to allocate memory for the connection memory pool.");
     res = MHD_SC_POOL_MALLOC_FAILURE;
   }
@@ -329,7 +329,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
 
     if (daemon->conns.block_new)
     { /* Connections limit */
-      MHD_LOG_MSG (daemon, MHD_SC_LIMIT_CONNECTIONS_REACHED, \
+      mhd_LOG_MSG (daemon, MHD_SC_LIMIT_CONNECTIONS_REACHED, \
                    "Server reached connection limit. " \
                    "Closing inbound connection.");
       res = MHD_SC_LIMIT_CONNECTIONS_REACHED;
@@ -363,7 +363,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
 #ifdef EAGAIN
           if (EAGAIN == errno)
           {
-            MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_THREAD_SYS_LIMITS_REACHED,
+            mhd_LOG_MSG (daemon, MHD_SC_CONNECTION_THREAD_SYS_LIMITS_REACHED,
                          "Failed to create a new thread because it would "
                          "have exceeded the system limit on the number of "
                          "threads or no system resources available.");
@@ -373,7 +373,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
 #endif /* EAGAIN */
           if (1)
           {
-            MHD_LOG_MSG (daemon, MHD_SC_CONNECTION_THREAD_LAUNCH_FAILURE,
+            mhd_LOG_MSG (daemon, MHD_SC_CONNECTION_THREAD_LAUNCH_FAILURE,
                          "Failed to create a thread.");
             res = MHD_SC_CONNECTION_THREAD_LAUNCH_FAILURE;
           }
@@ -401,7 +401,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
                               connection->socket_fd,
                               &event))
           {
-            MHD_LOG_MSG (daemon, MHD_SC_EPOLL_CTL_ADD_FAILED,
+            mhd_LOG_MSG (daemon, MHD_SC_EPOLL_CTL_ADD_FAILED,
                          "Failed to add connection socket .");
             res = MHD_SC_EPOLL_CTL_ADD_FAILED;
           }
@@ -489,7 +489,7 @@ internal_add_connection (struct MHD_Daemon *daemon,
 
   if (! mhd_FD_FITS_DAEMON (daemon, client_socket))
   {
-    MHD_LOG_MSG (daemon, MHD_SC_SOCKET_OUTSIDE_OF_SET_RANGE, \
+    mhd_LOG_MSG (daemon, MHD_SC_SOCKET_OUTSIDE_OF_SET_RANGE, \
                  "New connection socket descriptor value is too large for " \
                  "the daemon configuration.");
     (void) mhd_socket_close (client_socket);
@@ -500,7 +500,7 @@ internal_add_connection (struct MHD_Daemon *daemon,
       ((mhd_POLL_TYPE_EPOLL == daemon->events.poll_type) ||
        (mhd_WM_INT_EXTERNAL_EVENTS_EDGE == daemon->wmode_int)))
   {
-    MHD_LOG_MSG (daemon, MHD_SC_NONBLOCKING_REQUIRED, \
+    mhd_LOG_MSG (daemon, MHD_SC_NONBLOCKING_REQUIRED, \
                  "The daemon configuration requires non-blocking sockets, "
                  "the new socket has not been added.");
     (void) mhd_socket_close (client_socket);
@@ -622,7 +622,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
     {
       if (sizeof(struct sockaddr_in) > addrlen)
       {
-        MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+        mhd_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
                      "MHD_add_connection() has been called with " \
                      "incorrect 'addrlen' value.");
         return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
@@ -631,7 +631,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
       if ((0 != addr->sa_len) &&
           (sizeof(struct sockaddr_in) > (size_t) addr->sa_len) )
       {
-        MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+        mhd_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
                      "MHD_add_connection() has been called with " \
                      "non-zero value of 'sa_len' member of " \
                      "'struct sockaddr' which does not match 'sa_family'.");
@@ -644,7 +644,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
     {
       if (sizeof(struct sockaddr_in6) > addrlen)
       {
-        MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+        mhd_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
                      "MHD_add_connection() has been called with " \
                      "incorrect 'addrlen' value.");
         return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
@@ -653,7 +653,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
       if ((0 != addr->sa_len) &&
           (sizeof(struct sockaddr_in6) > (size_t) addr->sa_len) )
       {
-        MHD_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+        mhd_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
                      "MHD_add_connection() has been called with " \
                      "non-zero value of 'sa_len' member of " \
                      "'struct sockaddr' which does not match 'sa_family'.");
@@ -671,7 +671,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
 
   if (! mhd_socket_nonblocking (client_socket))
   {
-    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \
+    mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \
                  "Failed to set nonblocking mode on the new client socket.");
     sk_nonbl = false;
   }
@@ -688,7 +688,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
     sk_spipe_supprs = MHD_socket_nosignal_ (client_socket);
   if (! sk_spipe_supprs)
   {
-    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED, \
+    mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED, \
                  "Failed to suppress SIGPIPE on the new client socket.");
 #ifndef MSG_NOSIGNAL
     /* Application expects that SIGPIPE will be suppressed,
@@ -707,7 +707,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
   if (1) // TODO: implement turbo
   {
     if (! mhd_socket_noninheritable (client_socket))
-      MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \
+      mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \
                    "Failed to set noninheritable mode on new client socket.");
   }
 
@@ -874,7 +874,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
         /* Not setting 'block_new' flag, as there is no way it
            would ever be cleared.  Instead trying to produce
            bit fat ugly warning. */
-        MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY, \
+        mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY, \
                      "Hit process or system resource limit at FIRST " \
                      "connection. This is really bad as there is no sane " \
                      "way to proceed. Will try busy waiting for system " \
@@ -883,8 +883,8 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
       else
       {
         daemon->conns.block_new = true;
-        MHD_LOG_PRINT (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED, \
-                       MHD_LOG_FMT ("Hit process or system resource limit " \
+        mhd_LOG_PRINT (daemon, MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED, \
+                       mhd_LOG_FMT ("Hit process or system resource limit " \
                                     "at %u connections, temporarily " \
                                     "suspending accept(). Consider setting " \
                                     "a lower MHD_OPTION_CONNECTION_LIMIT."), \
@@ -892,14 +892,14 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
       }
       return mhd_DAEMON_ACCEPT_FAILED;
     }
-    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY,
+    mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY,
                  "Error accepting connection.");
     return mhd_DAEMON_ACCEPT_FAILED;
   }
 
   if (mhd_FD_FITS_DAEMON (daemon, s))
   {
-    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_OUTSIDE_OF_SET_RANGE, \
+    mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_OUTSIDE_OF_SET_RANGE, \
                  "The accepted socket has value outside of allowed range.");
     (void) mhd_socket_close (s);
     return mhd_DAEMON_ACCEPT_FAILED;
@@ -913,7 +913,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
   if (0 >= addrlen)
   {
     if (mhd_SOCKET_TYPE_IP == daemon->net.listen.type)
-      MHD_LOG_MSG (daemon, MHD_SC_ACCEPTED_UNKNOWN_TYPE, \
+      mhd_LOG_MSG (daemon, MHD_SC_ACCEPTED_UNKNOWN_TYPE, \
                    "Accepted socket has non-positive length of the address. " \
                    "Processing the new socket as a socket with " \
                    "unknown type.");
@@ -924,7 +924,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
   {
     /* Should not happen as 'sockaddr_storage' must be large enough to
      * store any address supported by the system. */
-    MHD_LOG_MSG (daemon, MHD_SC_ACCEPTED_SOCKADDR_TOO_LARGE, \
+    mhd_LOG_MSG (daemon, MHD_SC_ACCEPTED_SOCKADDR_TOO_LARGE, \
                  "Accepted socket address is larger than expected by " \
                  "system headers. Processing the new socket as a socket with " \
                  "unknown type.");
@@ -943,7 +943,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
 
   if (! sk_nonbl && ! mhd_socket_nonblocking (s))
   {
-    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \
+    mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, \
                  "Failed to set nonblocking mode on incoming connection " \
                  "socket.");
   }
@@ -952,7 +952,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
 
   if (! sk_cloexec && ! mhd_socket_noninheritable (s))
   {
-    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \
+    mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, \
                  "Failed to set non-inheritable mode on incoming connection " \
                  "socket.");
   }
@@ -960,7 +960,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
 #if defined(MHD_socket_nosignal_)
   if (! sk_spipe_supprs && ! MHD_socket_nosignal_ (s))
   {
-    MHD_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED,
+    mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED,
                  "Failed to suppress SIGPIPE on incoming connection " \
                  "socket.");
 #ifndef MSG_NOSIGNAL
@@ -986,3 +986,16 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
                                                 sk_non_ip)) ?
          mhd_DAEMON_ACCEPT_FAILED : mhd_DAEMON_ACCEPT_SUCCESS;
 }
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_close_final (struct MHD_Connection *restrict c)
+{
+  if ((NULL != mhd_DLINKEDL_GET_NEXT (c, proc_ready)) ||
+      (NULL != mhd_DLINKEDL_GET_PREV (c, proc_ready)) ||
+      (c == mhd_DLINKEDL_GET_FIRST (&(c->daemon->events), proc_ready)))
+    mhd_DLINKEDL_DEL (&(c->daemon->events), c, proc_ready);
+
+  mhd_assert (0 && "Not finished yet");
+  // TODO: finish
+}

+ 1 - 1
src/mhd2/daemon_funcs.c

@@ -57,7 +57,7 @@ mhd_daemon_trigger_itc (struct MHD_Daemon *restrict d)
   mhd_assert (mhd_ITC_IS_VALID (d->threading.itc));
   if (! mhd_itc_activate (d->threading.itc))
   {
-    MHD_LOG_MSG (d, MHD_SC_ITC_USE_FAILED, \
+    mhd_LOG_MSG (d, MHD_SC_ITC_USE_FAILED, \
                  "Failed to communicate by ITC with the daemon thread.");
     return false;
   }

+ 13 - 13
src/mhd2/daemon_logger.h

@@ -56,20 +56,20 @@ mhd_logger (struct MHD_Daemon *daemon,
  * Any '%' symbols should be doubled ('%%') to avoid interpretation as a format
  * specifier symbol.
  */
-#define MHD_LOG_MSG(daemon,sc,msg) mhd_logger (daemon,sc,msg)
+#define mhd_LOG_MSG(daemon,sc,msg) mhd_logger (daemon,sc,msg)
 
 /**
  * Format message and log it
  *
- * Always use with #MHD_LOG_FMT() for the format string.
+ * Always use with #mhd_LOG_FMT() for the format string.
  */
-#define MHD_LOG_PRINT mhd_logger
+#define mhd_LOG_PRINT mhd_logger
 
 /**
  * The wrapper macro for the format string to be used for format parameter for
- * the #MHD_LOG_FMT() macro
+ * the #mhd_LOG_FMT() macro
  */
-#define MHD_LOG_FMT(format_string) format_string
+#define mhd_LOG_FMT(format_string) format_string
 
 #else  /* ! HAVE_LOG_FUNCTIONALITY */
 
@@ -83,14 +83,14 @@ mhd_logger (struct MHD_Daemon *daemon,
  * Any '%' symbols should be doubled ('%%') to avoid interpretation as a format
  * specifier symbol.
  */
-#define MHD_LOG_MSG(daemon,sc,msg)  do { (void) daemon; } while (0)
+#define mhd_LOG_MSG(daemon,sc,msg)  do { (void) daemon; } while (0)
 
 /**
  * Format message and log it
  *
- * Always use with #MHD_LOG_FMT() for the format string.
+ * Always use with #mhd_LOG_FMT() for the format string.
  */
-#define MHD_LOG_PRINT(daemon,sc,fm,...)  do { (void) daemon; } while (0)
+#define mhd_LOG_PRINT(daemon,sc,fm,...)  do { (void) daemon; } while (0)
 
 #else  /* ! HAVE_MACRO_VARIADIC */
 
@@ -99,10 +99,10 @@ mhd_logger (struct MHD_Daemon *daemon,
 /**
  * Format message and log it
  *
- * Always use with #MHD_LOG_FMT() for the format string.
+ * Always use with #mhd_LOG_FMT() for the format string.
  */
 MHD_static_inline_ void
-MHD_LOG_PRINT (struct MHD_Daemon *daemon,
+mhd_LOG_PRINT (struct MHD_Daemon *daemon,
                enum MHD_StatusCode sc,
                const char *fm,
                ...)
@@ -118,15 +118,15 @@ MHD_LOG_PRINT (struct MHD_Daemon *daemon,
  * Any '%' symbols should be doubled ('%%') to avoid interpretation as a format
  * specifier symbol.
  */
-#define MHD_LOG_MSG(daemon,sc,msg) MHD_LOG_PRINT (daemon,sc,NULL)
+#define mhd_LOG_MSG(daemon,sc,msg) mhd_LOG_PRINT (daemon,sc,NULL)
 
 #endif /* ! HAVE_MACRO_VARIADIC */
 
 /**
  * The wrapper macro for the format string to be used for format parameter for
- * the #MHD_LOG_FMT() macro
+ * the #mhd_LOG_FMT() macro
  */
-#define MHD_LOG_FMT(format_string) NULL
+#define mhd_LOG_FMT(format_string) NULL
 
 #endif /* ! HAVE_LOG_FUNCTIONALITY */
 

+ 66 - 66
src/mhd2/daemon_start.c

@@ -122,7 +122,7 @@ daemon_set_work_mode (struct MHD_Daemon *restrict d,
   case MHD_WM_EXTERNAL_EVENT_LOOP_CB_EDGE:
     if (MHD_SPS_AUTO != s->poll_syscall)
     {
-      MHD_LOG_MSG ( \
+      mhd_LOG_MSG ( \
         d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \
         "The requested work mode is not compatible with setting " \
         "socket polling syscall.");
@@ -137,14 +137,14 @@ daemon_set_work_mode (struct MHD_Daemon *restrict d,
     if ((MHD_SPS_AUTO != s->poll_syscall) &&
         (MHD_SPS_EPOLL != s->poll_syscall))
     {
-      MHD_LOG_MSG ( \
+      mhd_LOG_MSG ( \
         d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \
         "The requested work mode MHD_WM_EXTERNAL_SINGLE_FD_WATCH " \
         "is not compatible with requested socket polling syscall.");
       return MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID;
     }
 #ifndef MHD_USE_EPOLL
-    MHD_LOG_MSG ( \
+    mhd_LOG_MSG ( \
       d, MHD_SC_FEATURE_DISABLED, \
       "The epoll is required for the requested work mode " \
       "MHD_WM_EXTERNAL_SINGLE_FD_WATCH, but not available on this " \
@@ -157,7 +157,7 @@ daemon_set_work_mode (struct MHD_Daemon *restrict d,
   case MHD_WM_THREAD_PER_CONNECTION:
     if (MHD_SPS_EPOLL == s->poll_syscall)
     {
-      MHD_LOG_MSG ( \
+      mhd_LOG_MSG ( \
         d, MHD_SC_SYSCALL_WORK_MODE_COMBINATION_INVALID, \
         "The requested work mode MHD_WM_THREAD_PER_CONNECTION " \
         "is not compatible with 'epoll' sockets polling.");
@@ -166,7 +166,7 @@ daemon_set_work_mode (struct MHD_Daemon *restrict d,
   /* Intentional fallthrough */
   case MHD_WM_WORKER_THREADS:
 #ifndef MHD_USE_THREADS
-    MHD_LOG_MSG (d, MHD_SC_FEATURE_DISABLED, \
+    mhd_LOG_MSG (d, MHD_SC_FEATURE_DISABLED, \
                  "The internal threads modes are not supported by this " \
                  "build of MHD.");
     return MHD_SC_FEATURE_DISABLED;
@@ -180,7 +180,7 @@ daemon_set_work_mode (struct MHD_Daemon *restrict d,
 #endif /* MHD_USE_THREADS */
     break;
   default:
-    MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_UNEXPECTED_WM, \
+    mhd_LOG_MSG (d, MHD_SC_CONFIGURATION_UNEXPECTED_WM, \
                  "Wrong requested work mode.");
     return MHD_SC_CONFIGURATION_UNEXPECTED_WM;
   }
@@ -304,14 +304,14 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
     /* Check for options conflicts */
     if (0 != s->bind_sa.v_sa_len)
     {
-      MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
+      mhd_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
                    "MHD_D_O_BIND_SA cannot be used together " \
                    "with MHD_D_O_LISTEN_SOCKET");
       return MHD_SC_OPTIONS_CONFLICT;
     }
     else if (MHD_AF_NONE != s->bind_port.v_af)
     {
-      MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
+      mhd_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
                    "MHD_D_O_BIND_PORT cannot be used together " \
                    "with MHD_D_O_LISTEN_SOCKET");
       return MHD_SC_OPTIONS_CONFLICT;
@@ -333,7 +333,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
       /* Check for options conflicts */
       if (MHD_AF_NONE != s->bind_port.v_af)
       {
-        MHD_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
+        mhd_LOG_MSG (d, MHD_SC_OPTIONS_CONFLICT, \
                      "MHD_D_O_BIND_SA cannot be used together " \
                      "with MHD_D_O_BIND_PORT");
         return MHD_SC_OPTIONS_CONFLICT;
@@ -346,7 +346,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
         sk_type = mhd_SKT_IP_V4_ONLY;
         if (sizeof(sa_all.sa_i4) > s->bind_sa.v_sa_len)
         {
-          MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+          mhd_LOG_MSG (d, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
                        "The size of the provided sockaddr does not match "
                        "used address family");
           return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
@@ -364,7 +364,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
         sk_type = mhd_SKT_IP_V6_ONLY;
         if (sizeof(sa_all.sa_i6) > s->bind_sa.v_sa_len)
         {
-          MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
+          mhd_LOG_MSG (d, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \
                        "The size of the provided sockaddr does not match "
                        "used address family");
           return MHD_SC_CONFIGURATION_WRONG_SA_SIZE;
@@ -393,7 +393,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
       {
         if (mhd_SKT_IP_V6_ONLY != sk_type)
         {
-          MHD_LOG_MSG (d, MHD_SC_LISTEN_DUAL_STACK_NOT_SUITABLE, \
+          mhd_LOG_MSG (d, MHD_SC_LISTEN_DUAL_STACK_NOT_SUITABLE, \
                        "IP dual stack is not possible for provided sockaddr");
         }
 #ifdef HAVE_INET6
@@ -402,7 +402,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 #ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure
           sk_type = mhd_SKT_IP_DUAL_REQUIRED;
 #else  /* ! IPV6_V6ONLY */
-          MHD_LOG_MSG (d, \
+          mhd_LOG_MSG (d, \
                        MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED, \
                        "IP dual stack is not supported by this platform or " \
                        "by this MHD build");
@@ -469,7 +469,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 #ifdef HAVE_INET6
         sk_type = mhd_SKT_IP_V6_ONLY;
 #else  /* ! HAVE_INET6 */
-        MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
+        mhd_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
                      "IPv6 is not supported by this MHD build or " \
                      "by this platform");
         return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
@@ -482,14 +482,14 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 #ifdef IPV6_V6ONLY // TODO: detect constants declarations in configure
         sk_type = mhd_SKT_IP_DUAL_REQUIRED;
 #else  /* ! IPV6_V6ONLY */
-        MHD_LOG_MSG (d,
+        mhd_LOG_MSG (d,
                      MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED, \
                      "IP dual stack is not supported by this platform or " \
                      "by this MHD build");
         sk_type = mhd_SKT_IP_V6_ONLY;
 #endif /* ! IPV6_V6ONLY */
 #else  /* ! HAVE_INET6 */
-        MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
+        mhd_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
                      "IPv6 is not supported by this MHD build or " \
                      "by this platform");
         return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
@@ -505,7 +505,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
         sk_type = mhd_SKT_IP_V6_ONLY;
 #endif /* ! IPV6_V6ONLY */
 #else  /* ! HAVE_INET6 */
-        MHD_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
+        mhd_LOG_MSG (d, MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, \
                      "IPv6 is not supported by this MHD build or " \
                      "by this platform");
         return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD;
@@ -527,7 +527,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 #endif /* ! HAVE_INET6 */
         break;
       default:
-        MHD_LOG_MSG (d, MHD_SC_AF_NOT_SUPPORTED_BY_BUILD, \
+        mhd_LOG_MSG (d, MHD_SC_AF_NOT_SUPPORTED_BY_BUILD, \
                      "Unknown address family specified");
         return MHD_SC_AF_NOT_SUPPORTED_BY_BUILD;
       }
@@ -625,7 +625,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
       bool is_af_err = mhd_SCKT_LERR_IS_AF ();
 
       if (is_af_err)
-        MHD_LOG_MSG (d, MHD_SC_AF_NOT_AVAILABLE, \
+        mhd_LOG_MSG (d, MHD_SC_AF_NOT_AVAILABLE, \
                      "The requested socket address family is rejected " \
                      "by the OS");
 
@@ -637,7 +637,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 #endif /* HAVE_INET6 */
 
       if (! is_af_err)
-        MHD_LOG_MSG (d, MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET, \
+        mhd_LOG_MSG (d, MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET, \
                      "Failed to open listen socket");
 
       return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET;
@@ -660,7 +660,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
   { /* The scope for automatic socket close for error returns */
     if (! mhd_FD_FITS_DAEMON (d,sk))
     {
-      MHD_LOG_MSG (d, MHD_SC_LISTEN_FD_OUTSIDE_OF_SET_RANGE, \
+      mhd_LOG_MSG (d, MHD_SC_LISTEN_FD_OUTSIDE_OF_SET_RANGE, \
                    "The listen FD value is higher than allowed");
       ret = MHD_SC_LISTEN_FD_OUTSIDE_OF_SET_RANGE;
       break;
@@ -669,7 +669,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
     if (! is_non_inhr)
     {
       if (! mhd_socket_noninheritable (sk))
-        MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NOINHERIT_FAILED, \
+        mhd_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NOINHERIT_FAILED, \
                      "OS refused to make the listen socket non-inheritable");
     }
 
@@ -729,7 +729,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
               /* The dual-stack state is definitely wrong */
               if (mhd_SKT_IP_V6_ONLY == sk_type)
               {
-                MHD_LOG_MSG ( \
+                mhd_LOG_MSG ( \
                   d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED, \
                   "Failed to disable IP dual-stack configuration " \
                   "for the listen socket");
@@ -738,7 +738,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
               }
               else if (mhd_SKT_UNKNOWN != sk_type)
               {
-                MHD_LOG_MSG ( \
+                mhd_LOG_MSG ( \
                   d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_REJECTED, \
                   "Cannot enable IP dual-stack configuration " \
                   "for the listen socket");
@@ -753,7 +753,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
             {
               /* The dual-stack state is unknown */
               if (mhd_SKT_UNKNOWN != sk_type)
-                MHD_LOG_MSG (
+                mhd_LOG_MSG (
                   d, MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_UNKNOWN, \
                   "Failed to set dual-stack (IPV6_ONLY) configuration " \
                   "for the listen socket, using system defaults");
@@ -784,7 +784,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
                              (const void *) &fo_param,
                              sizeof (fo_param)))
         {
-          MHD_LOG_MSG (d, MHD_SC_LISTEN_FAST_OPEN_FAILURE, \
+          mhd_LOG_MSG (d, MHD_SC_LISTEN_FAST_OPEN_FAILURE, \
                        "OS refused to enable TCP Fast Open on " \
                        "the listen socket");
           if (MHD_FOM_AUTO < d->settings->tcp_fastopen.v_option)
@@ -796,7 +796,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 #else  /* ! TCP_FASTOPEN */
         if (MHD_FOM_AUTO < d->settings->tcp_fastopen.v_option)
         {
-          MHD_LOG_MSG (d, MHD_SC_LISTEN_FAST_OPEN_FAILURE, \
+          mhd_LOG_MSG (d, MHD_SC_LISTEN_FAST_OPEN_FAILURE, \
                        "The OS does not support TCP Fast Open");
           ret = MHD_SC_LISTEN_FAST_OPEN_FAILURE;
           break;
@@ -812,12 +812,12 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
         if (0 != setsockopt (sk, IPPROTO_TCP, SO_REUSEADDR,
                              (const void *) &on_val1, sizeof (on_val1)))
         {
-          MHD_LOG_MSG (d, MHD_SC_LISTEN_PORT_REUSE_ENABLE_FAILED, \
+          mhd_LOG_MSG (d, MHD_SC_LISTEN_PORT_REUSE_ENABLE_FAILED, \
                        "OS refused to enable address reuse on " \
                        "the listen socket");
         }
 #else  /* ! SO_REUSEADDR */
-        MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \
+        mhd_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \
                      "The OS does not support address reuse for sockets");
 #endif /* ! SO_REUSEADDR */
 #endif /* ! MHD_WINSOCK_SOCKETS */
@@ -833,14 +833,14 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 #endif /* ! MHD_WINSOCK_SOCKETS */
                                (const void *) &on_val2, sizeof (on_val2)))
           {
-            MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED, \
+            mhd_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED, \
                          "OS refused to enable address sharing " \
                          "on the listen socket");
             ret = MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED;
             break;
           }
 #else  /* ! SO_REUSEADDR && ! MHD_WINSOCK_SOCKETS */
-          MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \
+          mhd_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \
                        "The OS does not support address sharing for sockets");
           ret = MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED;
           break;
@@ -860,7 +860,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 #endif
                              (const void *) &on_val, sizeof (on_val)))
         {
-          MHD_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_EXCLUSIVE_ENABLE_FAILED, \
+          mhd_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_EXCLUSIVE_ENABLE_FAILED, \
                        "OS refused to enable exclusive address use " \
                        "on the listen socket");
           ret = MHD_SC_LISTEN_ADDRESS_EXCLUSIVE_ENABLE_FAILED;
@@ -885,7 +885,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
           return create_bind_listen_stream_socket (d, s, true, false);
         }
 #endif /* HAVE_INET6 */
-        MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_BIND_FAILED, \
+        mhd_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_BIND_FAILED, \
                      "Failed to bind the listen socket");
         ret = MHD_SC_LISTEN_SOCKET_BIND_FAILED;
         break;
@@ -919,7 +919,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
             return create_bind_listen_stream_socket (d, s, true, false);
           }
 #endif /* HAVE_INET6 */
-          MHD_LOG_MSG (d, MHD_SC_LISTEN_FAILURE, \
+          mhd_LOG_MSG (d, MHD_SC_LISTEN_FAILURE, \
                        "Failed to start listening on the listen socket");
           ret = MHD_SC_LISTEN_FAILURE;
           break;
@@ -932,7 +932,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
     {
       is_non_block = mhd_socket_nonblocking (sk);
       if (! is_non_block)
-        MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \
+        mhd_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \
                      "OS refused to make the listen socket non-blocking");
     }
 
@@ -998,7 +998,7 @@ detect_listen_type_and_port (struct MHD_Daemon *restrict d)
   if (0 != getsockname (d->net.listen.fd, &(sa_all.sa), &sa_size))
   {
     if (mhd_SOCKET_TYPE_IP == d->net.listen.type)
-      MHD_LOG_MSG (d, MHD_SC_LISTEN_PORT_DETECT_FAILURE, \
+      mhd_LOG_MSG (d, MHD_SC_LISTEN_PORT_DETECT_FAILURE, \
                    "Failed to detect the port number on the listening socket");
     return;
   }
@@ -1043,7 +1043,7 @@ detect_listen_type_and_port (struct MHD_Daemon *restrict d)
 
   if ((declared_type != d->net.listen.type)
       && (mhd_SOCKET_TYPE_IP == declared_type))
-    MHD_LOG_MSG (d, MHD_SC_UNEXPECTED_SOCKET_ERROR, \
+    mhd_LOG_MSG (d, MHD_SC_UNEXPECTED_SOCKET_ERROR, \
                  "The type of listen socket is detected as non-IP, while " \
                  "the socket has been created as an IP socket");
 }
@@ -1075,20 +1075,20 @@ init_epoll (struct MHD_Daemon *restrict d)
   if (0 <= e_fd)
   {
     if (! mhd_socket_noninheritable (e_fd))
-      MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_CONFIGURE_NOINHERIT_FAILED, \
+      mhd_LOG_MSG (d, MHD_SC_EPOLL_CTL_CONFIGURE_NOINHERIT_FAILED, \
                    "Failed to make epoll control FD non-inheritable");
   }
 #endif /* ! HAVE_EPOLL_CREATE1 */
   if (0 > e_fd)
   {
-    MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_CREATE_FAILED, \
+    mhd_LOG_MSG (d, MHD_SC_EPOLL_CTL_CREATE_FAILED, \
                  "Failed to create epoll control FD");
     return MHD_SC_EPOLL_CTL_CREATE_FAILED; /* Failure exit point */
   }
 
   if (! mhd_FD_FITS_DAEMON (d, e_fd))
   {
-    MHD_LOG_MSG (d, MHD_SC_EPOLL_CTL_OUTSIDE_OF_SET_RANGE, \
+    mhd_LOG_MSG (d, MHD_SC_EPOLL_CTL_OUTSIDE_OF_SET_RANGE, \
                  "The epoll control FD value is higher than allowed");
     (void) close (e_fd);
     return MHD_SC_EPOLL_CTL_OUTSIDE_OF_SET_RANGE; /* Failure exit point */
@@ -1156,7 +1156,7 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
   case MHD_SPS_SELECT:
     mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
 #ifndef MHD_USE_SELECT
-    MHD_LOG_MSG (d, MHD_SC_SELECT_SYSCALL_NOT_AVAILABLE, \
+    mhd_LOG_MSG (d, MHD_SC_SELECT_SYSCALL_NOT_AVAILABLE, \
                  "'select()' is not supported by the platform or " \
                  "this MHD build");
     return MHD_SC_SELECT_SYSCALL_NOT_AVAILABLE;
@@ -1167,7 +1167,7 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
   case MHD_SPS_POLL:
     mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
 #ifndef MHD_USE_POLL
-    MHD_LOG_MSG (d, MHD_SC_POLL_SYSCALL_NOT_AVAILABLE, \
+    mhd_LOG_MSG (d, MHD_SC_POLL_SYSCALL_NOT_AVAILABLE, \
                  "'poll()' is not supported by the platform or " \
                  "this MHD build");
     return MHD_SC_POLL_SYSCALL_NOT_AVAILABLE;
@@ -1178,7 +1178,7 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
   case MHD_SPS_EPOLL:
     mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int));
 #ifndef MHD_USE_EPOLL
-    MHD_LOG_MSG (d, MHD_SC_EPOLL_SYSCALL_NOT_AVAILABLE, \
+    mhd_LOG_MSG (d, MHD_SC_EPOLL_SYSCALL_NOT_AVAILABLE, \
                  "'epoll' is not supported by the platform or " \
                  "this MHD build");
     return MHD_SC_EPOLL_SYSCALL_NOT_AVAILABLE;
@@ -1187,7 +1187,7 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
 #endif /* MHD_USE_EPOLL */
     break;
   default:
-    MHD_LOG_MSG (d, MHD_SC_CONFIGURATION_UNEXPECTED_SPS,
+    mhd_LOG_MSG (d, MHD_SC_CONFIGURATION_UNEXPECTED_SPS,
                  "Wrong socket polling syscall specified");
     return MHD_SC_CONFIGURATION_UNEXPECTED_SPS;
   }
@@ -1243,7 +1243,7 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d,
 #elif defined(MHD_USE_SELECT)
     chosen_type = mhd_POLL_TYPE_SELECT;
 #else
-    MHD_LOG_MSG (d, MHD_SC_FEATURE_DISABLED, \
+    mhd_LOG_MSG (d, MHD_SC_FEATURE_DISABLED, \
                  "All suitable internal sockets polling technologies are " \
                  "disabled in this MHD build");
     return MHD_SC_FEATURE_DISABLED;
@@ -1361,7 +1361,7 @@ daemon_init_net (struct MHD_Daemon *restrict d,
           && ((mhd_WM_INT_EXTERNAL_EVENTS_EDGE == d->wmode_int) ||
               (mhd_WM_INT_INTERNAL_EVENTS_THREAD_POOL == d->wmode_int)))
       {
-        MHD_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \
+        mhd_LOG_MSG (d, MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, \
                      "The selected daemon work mode requires listening socket "
                      "in non-blocking mode");
         ret = MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE;
@@ -1449,7 +1449,7 @@ daemon_init_large_buf (struct MHD_Daemon *restrict d,
   d->req_cfg.large_buf.space_left = s->global_large_buffer_size;
   if (! mhd_mutex_init_short (&(d->req_cfg.large_buf.lock)))
   {
-    MHD_LOG_MSG (d, MHD_SC_MUTEX_INIT_FAILURE, \
+    mhd_LOG_MSG (d, MHD_SC_MUTEX_INIT_FAILURE, \
                  "Failed to initialise mutex for the global large buffer.");
     return MHD_SC_MUTEX_INIT_FAILURE;
   }
@@ -1520,7 +1520,7 @@ allocate_events (struct MHD_Daemon *restrict d)
       }
       free (d->events.data.select.rfds);
     }
-    MHD_LOG_MSG (d, MHD_SC_FD_SET_MEMORY_ALLOCATE_FAILURE, \
+    mhd_LOG_MSG (d, MHD_SC_FD_SET_MEMORY_ALLOCATE_FAILURE, \
                  "Failed to allocate memory for fd_sets for the daemon");
     return MHD_SC_FD_SET_MEMORY_ALLOCATE_FAILURE;
     break;
@@ -1553,7 +1553,7 @@ allocate_events (struct MHD_Daemon *restrict d)
         free (d->events.data.poll.fds);
       }
     }
-    MHD_LOG_MSG (d, MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE, \
+    mhd_LOG_MSG (d, MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE, \
                  "Failed to allocate memory for fd_sets for the daemon");
     return MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE;
     break;
@@ -1594,7 +1594,7 @@ allocate_events (struct MHD_Daemon *restrict d)
         return MHD_SC_OK; /* Success exit point */
       }
     }
-    MHD_LOG_MSG (d, MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE, \
+    mhd_LOG_MSG (d, MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE, \
                  "Failed to allocate memory for fd_sets for the daemon");
     return MHD_SC_POLL_FDS_MEMORY_ALLOCATE_FAILURE;
     break;
@@ -1686,20 +1686,20 @@ init_itc (struct MHD_Daemon *restrict d)
   if (! mhd_itc_init (&(d->threading.itc)))
   {
 #if defined(MHD_ITC_EVENTFD_)
-    MHD_LOG_MSG ( \
+    mhd_LOG_MSG ( \
       d, MHD_SC_ITC_INITIALIZATION_FAILED, \
       "Failed to initialise eventFD for inter-thread communication");
 #elif defined(MHD_ITC_PIPE_)
-    MHD_LOG_MSG ( \
+    mhd_LOG_MSG ( \
       d, MHD_SC_ITC_INITIALIZATION_FAILED, \
       "Failed to create a pipe for inter-thread communication");
 #elif defined(MHD_ITC_SOCKETPAIR_)
-    MHD_LOG_MSG ( \
+    mhd_LOG_MSG ( \
       d, MHD_SC_ITC_INITIALIZATION_FAILED, \
       "Failed to create a socketpair for inter-thread communication");
 #else
 #warning Missing expicit handling of the ITC type
-    MHD_LOG_MSG ( \
+    mhd_LOG_MSG ( \
       d, MHD_SC_ITC_INITIALIZATION_FAILED, \
       "Failed to initialise inter-thread communication");
 #endif
@@ -1804,7 +1804,7 @@ add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
       if (0 != epoll_ctl (d->events.data.epoll.e_fd, EPOLL_CTL_ADD,
                           mhd_itc_r_fd (d->threading.itc), &reg_event))
       {
-        MHD_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \
+        mhd_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \
                      "Failed to add ITC fd to the epoll monitoring.");
         return MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE;
       }
@@ -1816,7 +1816,7 @@ add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d)
         if (0 != epoll_ctl (d->events.data.epoll.e_fd, EPOLL_CTL_ADD,
                             d->net.listen.fd, &reg_event))
         {
-          MHD_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \
+          mhd_LOG_MSG (d, MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE, \
                        "Failed to add listening fd to the epoll monitoring.");
           return MHD_SC_EPOLL_ADD_DAEMON_FDS_FAILURE;
         }
@@ -1983,7 +1983,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d,
         (0 != s->work_mode.params.num_worker_threads) &&
         (s->global_connection_limit < s->work_mode.params.num_worker_threads))
     {
-      MHD_LOG_MSG ( \
+      mhd_LOG_MSG ( \
         d, MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL, \
         "The limit specified by MHD_D_O_GLOBAL_CONNECTION_LIMIT is smaller " \
         "then the number of worker threads.");
@@ -2021,7 +2021,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d,
       {
         if (d->net.cfg.max_fd_num == s->fd_number_limit)
         {
-          MHD_LOG_MSG ( \
+          mhd_LOG_MSG ( \
             d, MHD_SC_MAX_FD_NUMBER_LIMIT_TOO_STRICT, \
             "The limit specified by MHD_D_O_FD_NUMBER_LIMIT is too strict " \
             "for this daemon settings.");
@@ -2068,7 +2068,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d,
 #endif /* MHD_POSIX_SOCKETS */
   if (error_by_fd_setsize)
   {
-    MHD_LOG_MSG ( \
+    mhd_LOG_MSG ( \
       d, MHD_SC_SYS_FD_SETSIZE_TOO_STRICT, \
       "The FD_SETSIZE is too strict to run daemon with the polling " \
       "by select() and with the specified number of workers.");
@@ -2272,7 +2272,7 @@ init_workers_pool (struct MHD_Daemon *restrict d,
     (sizeof(struct MHD_Daemon) * num_workers);
   if (workers_pool_size / num_workers != sizeof(struct MHD_Daemon))
   { /* Overflow */
-    MHD_LOG_MSG ( \
+    mhd_LOG_MSG ( \
       d, MHD_SC_THREAD_POOL_MALLOC_FAILURE, \
       "The size of the thread pool is too large.");
     return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
@@ -2286,7 +2286,7 @@ init_workers_pool (struct MHD_Daemon *restrict d,
   d->threading.hier.pool.workers = malloc (workers_pool_size);
   if (NULL == d->threading.hier.pool.workers)
   {
-    MHD_LOG_MSG ( \
+    mhd_LOG_MSG ( \
       d, MHD_SC_THREAD_POOL_MALLOC_FAILURE, \
       "Failed to allocate memory for the thread pool.");
     return MHD_SC_THREAD_POOL_MALLOC_FAILURE;
@@ -2486,7 +2486,7 @@ start_individual_daemon_thread (struct MHD_Daemon *restrict d)
           &mhd_worker_all_events, \
           (void*) d))
     {
-      MHD_LOG_MSG (d, MHD_SC_THREAD_MAIN_LAUNCH_FAILURE, \
+      mhd_LOG_MSG (d, MHD_SC_THREAD_MAIN_LAUNCH_FAILURE, \
                    "Failed to start daemon main thread.");
       return MHD_SC_THREAD_MAIN_LAUNCH_FAILURE;
     }
@@ -2499,7 +2499,7 @@ start_individual_daemon_thread (struct MHD_Daemon *restrict d)
           &mhd_worker_all_events, \
           (void*) d))
     {
-      MHD_LOG_MSG (d, MHD_SC_THREAD_WORKER_LAUNCH_FAILURE, \
+      mhd_LOG_MSG (d, MHD_SC_THREAD_WORKER_LAUNCH_FAILURE, \
                    "Failed to start daemon worker thread.");
       return MHD_SC_THREAD_WORKER_LAUNCH_FAILURE;
     }
@@ -2512,7 +2512,7 @@ start_individual_daemon_thread (struct MHD_Daemon *restrict d)
           &mhd_worker_listening_only, \
           (void*) d))
     {
-      MHD_LOG_MSG (d, MHD_SC_THREAD_LISTENING_LAUNCH_FAILURE, \
+      mhd_LOG_MSG (d, MHD_SC_THREAD_LISTENING_LAUNCH_FAILURE, \
                    "Failed to start daemon listening thread.");
       return MHD_SC_THREAD_LISTENING_LAUNCH_FAILURE;
     }
@@ -2551,7 +2551,7 @@ stop_individual_daemon_thread (struct MHD_Daemon *restrict d)
   mhd_daemon_trigger_itc (d);
   if (! mhd_thread_handle_ID_join_thread (d->threading.tid))
   {
-    MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_ERROR, \
+    mhd_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_ERROR, \
                  "Failed to stop daemon main thread.");
   }
 }
@@ -2596,7 +2596,7 @@ stop_worker_pool_threads (struct MHD_Daemon *restrict d,
     mhd_assert (mhd_thread_handle_ID_is_valid_handle (worker->threading.tid));
     if (! mhd_thread_handle_ID_join_thread (worker->threading.tid))
     {
-      MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_ERROR, \
+      mhd_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_ERROR, \
                    "Failed to stop a worker thread.");
     }
   }

+ 8 - 9
src/mhd2/events_process.c

@@ -44,7 +44,6 @@
 #include "conn_data_process.h"
 
 
-
 MHD_FN_PAR_NONNULL_ (1) static void
 update_conn_net_status (struct MHD_Daemon *restrict d,
                         struct MHD_Connection *restrict c,
@@ -220,8 +219,8 @@ daemon_process_all_act_coons (struct MHD_Daemon *restrict d)
   {
     struct MHD_Connection *next;
     next = mhd_DLINKEDL_GET_NEXT (c, proc_ready); /* The current connection can be closed */
-    if (! mhd_conn_process_recv_send_data(c))
-      mhd_conn_close_final(c);
+    if (! mhd_conn_process_recv_send_data (c))
+      mhd_conn_close_final (c);
 
     c = next;
   }
@@ -303,7 +302,7 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d,
               d->events.data.poll.rel[i_s].fd_id);
   if (0 != (d->events.data.poll.fds[i_s].revents & (POLLERR | POLLNVAL)))
   {
-    MHD_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
+    mhd_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
                  "System reported that ITC has an error status.");
     /* ITC is broken, need to stop the daemon thread now as otherwise
        application will not be able to stop the thread. */
@@ -334,7 +333,7 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d,
     if (0 != (revents & (POLLERR | POLLNVAL | POLLHUP)))
     {
       --num_events;
-      MHD_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
+      mhd_LOG_MSG (d, MHD_SC_ITC_STATUS_ERROR, \
                    "System reported that the listening socket has an error " \
                    "status. The daemon will not listen any more.");
       /* Close the listening socket unless the master daemon should close it */
@@ -442,11 +441,11 @@ get_all_net_updates_by_poll (struct MHD_Daemon *restrict d,
     {
       if (is_hard_error)
       {
-        MHD_LOG_MSG (d, MHD_SC_POLL_HARD_ERROR, \
+        mhd_LOG_MSG (d, MHD_SC_POLL_HARD_ERROR, \
                      "The poll() encountered unrecoverable error.");
         return false;
       }
-      MHD_LOG_MSG (d, MHD_SC_POLL_SOFT_ERROR, \
+      mhd_LOG_MSG (d, MHD_SC_POLL_SOFT_ERROR, \
                    "The poll() encountered error.");
     }
   }
@@ -540,7 +539,7 @@ mhd_worker_all_events (void *cls)
   }
   if (! d->threading.stop_requested)
   {
-    MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \
+    mhd_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \
                  "The daemon thread is stopping, but termination has not " \
                  "been requested by the daemon.");
   }
@@ -599,7 +598,7 @@ mhd_worker_listening_only (void *cls)
   }
   if (! d->threading.stop_requested)
   {
-    MHD_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \
+    mhd_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \
                  "The daemon thread is stopping, but termination has not been " \
                  "requested by the daemon.");
   }

+ 1 - 1
src/mhd2/http_status_str.c

@@ -195,7 +195,7 @@ MHD_HTTP_status_code_to_string (enum MHD_HTTP_StatusCode code)
 
 
 MHD_INTERNAL MHD_FN_CONST_ const struct MHD_String *
-mhd_HTTP_status_code_to_string_int (unsigned int code)
+mhd_HTTP_status_code_to_string_int (uint_fast16_t code)
 {
   static const struct MHD_String no_status =
     mhd_MSTR_INIT ("Nonstandard Status");

+ 2 - 1
src/mhd2/http_status_str.h

@@ -29,6 +29,7 @@
 
 #include "mhd_sys_options.h"
 
+#include "sys_base_types.h"
 #include "mhd_str_types.h"
 
 /**
@@ -39,7 +40,7 @@
  * @return pointer to MHD_String, never NULL.
  */
 MHD_INTERNAL const struct MHD_String *
-mhd_HTTP_status_code_to_string_int (unsigned int code)
+mhd_HTTP_status_code_to_string_int (uint_fast16_t code)
 MHD_FN_CONST_;
 
 

+ 11 - 24
src/mhd2/mhd_connection.h

@@ -295,12 +295,12 @@ enum MHD_FIXED_ENUM_ MHD_CONNECTION_STATE
    * We are waiting for the client to provide more
    * data of a non-chunked body.
    */
-  MHD_CONNECTION_NORMAL_BODY_UNREADY,
+  MHD_CONNECTION_UNCHUNKED_BODY_UNREADY,
 
   /**
    * We are ready to send a part of a non-chunked body.  Send it.
    */
-  MHD_CONNECTION_NORMAL_BODY_READY,
+  MHD_CONNECTION_UNCHUNKED_BODY_READY,
 
   /**
    * We are waiting for the client to provide a chunk of the body.
@@ -339,27 +339,22 @@ enum MHD_FIXED_ENUM_ MHD_CONNECTION_STATE
 /**
  * Ability to use same connection for next request
  */
-enum MHD_FIXED_ENUM_ MHD_ConnKeepAlive
+enum MHD_FIXED_ENUM_ mhd_ConnReuse
 {
   /**
    * Connection must be closed after sending response.
    */
-  MHD_CONN_MUST_CLOSE = -1,
-
-  /**
-   * KeelAlive state is not yet determined
-   */
-  MHD_CONN_KEEPALIVE_UNKOWN = 0,
-
+  mhd_CONN_MUST_CLOSE = -1
+  ,
   /**
-   * Connection can be used for serving next request
+   * KeepAlive state is possible
    */
-  MHD_CONN_USE_KEEPALIVE = 1,
-
+  mhd_CONN_KEEPALIVE_POSSIBLE = 0
+  ,
   /**
    * Connection will be upgraded
    */
-  MHD_CONN_MUST_UPGRADE = 2
+  mhd_CONN_MUST_UPGRADE = 1
 };
 
 /**
@@ -450,10 +445,10 @@ struct MHD_Connection
 
   /**
    * Close connection after sending response?
-   * Functions may change value from "Unknown" or "KeepAlive" to "Must close",
+   * Functions may change value from "KeepAlive" to "Must close",
    * but no functions reset value "Must Close" to any other value.
    */
-  enum MHD_ConnKeepAlive keepalive;
+  enum mhd_ConnReuse conn_reuse;
 
   /**
    * Buffer for reading requests.  Allocated in pool.  Actually one
@@ -569,14 +564,6 @@ struct MHD_Connection
    */
   enum mhd_Tristate sk_nodelay;
 
-  /**
-   * Has this socket been closed for reading (i.e.  other side closed
-   * the connection)?  If so, we must completely close the connection
-   * once we are done sending our response (and stop trying to read
-   * from this socket).
-   */
-  bool read_closed;
-
   /**
    * Some error happens during processing the connection therefore this
    * connection must be closed.

+ 13 - 0
src/mhd2/mhd_daemon.h

@@ -833,6 +833,11 @@ struct mhd_DaemonRequestProcessingSettings
    * Shared large buffer data
    */
   struct mhd_DeamonLargeBuffer large_buf; // TODO: set from settings
+
+  /**
+   * Suppress "Date:" header in responses
+   */
+  bool suppress_date; // TODO: set from settings
 };
 
 
@@ -953,6 +958,14 @@ struct MHD_Daemon
 #  define mhd_D_HAS_THREADS(d) (0)
 #endif
 
+#ifdef MHD_USE_THREADS
+#  define mhd_D_HAS_THR_PER_CONN(d) \
+        (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION == \
+         ((d)->wmode_int))
+#else
+#  define mhd_D_HAS_THR_PER_CONN(d) (0)
+#endif
+
 #define mhd_D_HAS_WORKERS(d) mhd_D_TYPE_HAS_WORKERS ((d)->threading.d_type)
 
 #define mhd_D_HAS_MASTER(d) mhd_D_TYPE_HAS_MASTER_DAEMON ((d)->threading.d_type)

+ 1 - 0
src/mhd2/mhd_reply.h

@@ -53,6 +53,7 @@ struct MHD_Reply_Properties
   bool use_reply_body_headers; /**< Use reply body-specific headers */
   bool send_reply_body; /**< Send reply body (can be zero-sized) */
   bool chunked; /**< Use chunked encoding for reply */
+  bool end_by_closing; /**< Signal end of content (only) by closing connection */
 };
 
 /**

+ 14 - 2
src/mhd2/mhd_response.h

@@ -200,7 +200,7 @@ struct mhd_ResponseReuseData
   /**
    * Indicate that response could be used more than one time
    */
-  bool reusable;
+  volatile bool reusable;
 
   /**
    * The number of active uses of the response.
@@ -236,10 +236,12 @@ struct mhd_ResponseConfiguration
   /**
    * If 'true', "Connection: close" header must be always used
    */
-  bool conn_close;
+  bool close_forced;
 
   /**
    * Use "HTTP/1.0" in the reply header
+   * @a chunked is 'false' if this flag set.
+   * @a close_forced is 'true' is this flag set.
    */
   bool mode_1_0;
 
@@ -247,6 +249,16 @@ struct mhd_ResponseConfiguration
    * The (possible incorrect) content length is provided by application
    */
   bool cnt_len_by_app;
+
+  /**
+   * Response has "Date:" header
+   */
+  bool has_hdr_date; // TODO: set the member
+
+  /**
+   * Response has "Connection:" header
+   */
+  bool has_hdr_conn; // TODO: set the member
 };
 
 #ifndef NDEBUG

+ 5 - 5
src/mhd2/mhd_send.c

@@ -223,7 +223,7 @@ mhd_connection_set_nodelay_state (struct MHD_Connection *connection,
   }
   else
   {
-    MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_NODELAY_FAILED, \
+    mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_NODELAY_FAILED, \
                  "Failed to set required TCP_NODELAY option for the socket.");
   }
 #else  /* ! TCP_NODELAY */
@@ -267,10 +267,10 @@ mhd_connection_set_cork_state (struct MHD_Connection *connection,
   else
   {
 #  ifdef TCP_CORK
-    MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
+    mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
                  "Failed to set required TCP_CORK option for the socket.");
 #  else
-    MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
+    mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
                  "Failed to set required TCP_NOPUSH option for the socket.");
 #  endif
   }
@@ -556,7 +556,7 @@ zero_send (struct MHD_Connection *connection)
   mhd_assert (mhd_T_IS_YES (connection->sk_nodelay));
   if (0 == mhd_sys_send (connection->socket_fd, &dummy, 0))
     return true;
-  MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_ZERO_SEND_FAILED, \
+  mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_ZERO_SEND_FAILED, \
                "Failed to push the data by zero-sized send.");
   return false;
 }
@@ -740,7 +740,7 @@ post_send_setopt (struct MHD_Connection *connection,
 
   /* Failed to push the data. */
 #endif /* ! mhd_TCP_CORK_NOPUSH */
-  MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_FLUSH_LAST_PART_FAILED, \
+  mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_FLUSH_LAST_PART_FAILED, \
                "Failed to force flush the last part of the response header " \
                "or the response content that might have been buffered by " \
                "the kernel. The client may experience some delay (usually " \

+ 3 - 3
src/mhd2/mhd_str.c

@@ -1422,13 +1422,13 @@ mhd_uint32_to_strx (uint_fast32_t val,
 
 #ifndef MHD_FAVOR_SMALL_CODE
 MHD_INTERNAL size_t
-mhd_uint16_to_str (uint_least16_t val,
+mhd_uint16_to_str (uint_fast16_t val,
                    char *buf,
                    size_t buf_size)
 {
   char *chr;  /**< pointer to the current printed digit */
   /* The biggest printable number is 65535 */
-  uint_least16_t divisor = UINT16_C (10000);
+  uint_fast16_t divisor = UINT16_C (10000);
   int digit;
 
   chr = buf;
@@ -1450,7 +1450,7 @@ mhd_uint16_to_str (uint_least16_t val,
     buf_size--;
     if (1 == divisor)
       return (size_t) (chr - buf);
-    val = (uint_least16_t) (val % divisor);
+    val = (uint_fast16_t) (val % divisor);
     divisor /= 10;
     digit = (int) (val / divisor);
     mhd_assert (digit < 10);

+ 37 - 2
src/mhd2/request_get_value.c

@@ -31,16 +31,19 @@
 
 #include "mhd_request.h"
 
-#include "mhd_public_api.h"
+#include "mhd_connection.h"
 
 #include "mhd_dlinked_list.h"
 #include "mhd_assert.h"
+#include "mhd_str.h"
+
+#include "mhd_public_api.h"
 
 
 MHD_INTERNAL
 MHD_FN_PAR_NONNULL_ (1)
 MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4) const struct MHD_StringNullable *
-mhd_request_get_value_n (struct MHD_Request *MHD_RESTRICT request,
+mhd_request_get_value_n (struct MHD_Request *restrict request,
                          enum MHD_ValueKind kind,
                          size_t key_len,
                          const char *restrict key)
@@ -72,3 +75,35 @@ MHD_request_get_value (struct MHD_Request *MHD_RESTRICT request,
   len = strlen (key);
   return mhd_request_get_value_n (request, kind, len, key);
 }
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (3)
+MHD_FN_PAR_CSTR_ (5) bool
+mhd_stream_has_header_token (const struct MHD_Connection *restrict c,
+                             size_t header_len,
+                             const char *restrict header,
+                             size_t token_len,
+                             const char *restrict token)
+{
+  struct mhd_RequestField *f;
+
+  mhd_assert (MHD_CONNECTION_START_REPLY >= c->state);
+
+  for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields);
+       NULL != f;
+       f = mhd_DLINKEDL_GET_NEXT (f, fields))
+  {
+    if ((MHD_VK_HEADER == f->field.kind) &&
+        (header_len == f->field.nv.name.len) &&
+        (mhd_str_equal_caseless_bin_n (header,
+                                       f->field.nv.name.cstr,
+                                       header_len)) &&
+        (mhd_str_has_token_caseless (f->field.nv.value.cstr,
+                                     token,
+                                     token_len)))
+      return true;
+  }
+
+  return false;
+}

+ 42 - 2
src/mhd2/request_get_value.h

@@ -29,10 +29,11 @@
 
 #include "mhd_sys_options.h"
 #include "sys_base_types.h"
-#include "mhd_public_api.h"
-
+#include "sys_bool_type.h"
 #include "mhd_str_macros.h"
 
+#include "mhd_public_api.h"
+
 /**
  * Get specified field value from request
  * If multiple values match the kind, return any one of them.
@@ -74,3 +75,42 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
         mhd_request_get_value_n ((r),(k),mhd_SSTR_LEN (str),(str))
 
 #endif /* ! MHD_REQUEST_GET_VALUE_H */
+
+
+/**
+ * Check whether the 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 c          the connection to check values
+ * @param header_len the length of header, not including optional
+ *                   terminating null-character
+ * @param header     the header name
+ * @param token_len  the length of token, not including optional
+ *                   terminating null-character.
+ * @param token      the token to find
+ * @return true if the token is found in the specified header,
+ *         false otherwise
+ */
+MHD_INTERNAL bool
+mhd_stream_has_header_token (const struct MHD_Connection *restrict c,
+                             size_t header_len,
+                             const char *restrict header,
+                             size_t token_len,
+                             const char *restrict token)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5);
+
+/**
+ * Check whether the 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 c          the connection to check values
+ * @param hdr        the statically allocated header name string
+ * @param token      the statically allocated string of token to find
+ * @return true if the token is found in the specified header,
+ *         false otherwise
+ */
+#define mhd_stream_has_header_token_st(c,hdr,tkn) \
+        mhd_stream_has_header_token ((c), mhd_SSTR_LEN (hdr), (hdr), \
+                                     mhd_SSTR_LEN (tkn), (tkn))

+ 1 - 1
src/mhd2/response_add_header.c

@@ -140,7 +140,7 @@ MHD_response_add_header (struct MHD_Response *response,
   else
     need_unlock = false;
 
-  // TODO: add special processing for "Date", "Connection", "Content-Length"
+  // TODO: add special processing for "Date", "Connection", "Content-Length", "Transfer-Encoding"
 
   res = response_add_header_int (response, name, value);
 

+ 9 - 0
src/mhd2/response_from.c

@@ -57,6 +57,9 @@ response_create_basic (enum MHD_HTTP_StatusCode sc,
   struct MHD_Response *restrict r;
   struct ResponseOptions *restrict s;
 
+  if ((100 > sc) || (999 < sc))
+    return NULL;
+
   r = mhd_calloc (1, sizeof(struct MHD_Response));
   if (NULL != r)
   {
@@ -321,6 +324,9 @@ MHD_response_from_fd (enum MHD_HTTP_StatusCode sc,
     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
     res->cntn.file.fd = fd;
     res->cntn.file.offset = offset;
+#ifdef MHD_USE_SENDFILE
+    res->cntn.file.use_sf = true;
+#endif
     res->cntn.file.is_pipe = false; /* Not necessary */
   }
   return res;
@@ -339,6 +345,9 @@ MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc,
     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
     res->cntn.file.fd = fd;
     res->cntn.file.offset = 0; /* Not necessary */
+#ifdef MHD_USE_SENDFILE
+    res->cntn.file.use_sf = false; /* Not necessary */
+#endif
     res->cntn.file.is_pipe = true;
   }
   return res;

+ 13 - 3
src/mhd2/response_funcs.c

@@ -81,17 +81,27 @@ response_set_properties (struct MHD_Response *restrict r)
   r->cfg.head_only = s->head_only_response;
   if (s->http_1_0_compatible_strict)
   {
-    r->cfg.conn_close = true;
+    r->cfg.close_forced = true;
     r->cfg.chunked = false;
+    r->cfg.mode_1_0 = s->http_1_0_server;
+  }
+  else if (s->http_1_0_server)
+  {
+    r->cfg.close_forced = s->conn_close || (MHD_SIZE_UNKNOWN == r->cntn_size);
+    r->cfg.chunked = false;
+    r->cfg.mode_1_0 = true;
   }
   else
   {
-    r->cfg.conn_close = s->conn_close;
+    r->cfg.close_forced = s->conn_close;
     r->cfg.chunked = s->chunked_enc || (MHD_SIZE_UNKNOWN == r->cntn_size);
+    r->cfg.mode_1_0 = false;
   }
-  r->cfg.mode_1_0 = s->http_1_0_server;
+
   r->cfg.cnt_len_by_app = s->insanity_header_content_length; // TODO: set only if "content-lengh" header is used
 
+  // TODO: calculate size of the headers and the "Connection:" header
+
   r->settings = NULL;
   free (s);
 }

+ 130 - 2
src/mhd2/stream_funcs.c

@@ -25,15 +25,23 @@
  */
 
 #include "mhd_sys_options.h"
+
 #include "stream_funcs.h"
-#include "mhd_connection.h"
+
 #include <string.h>
-#include "mhd_public_api.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
 #include "mhd_assert.h"
 #include "mhd_mempool.h"
 #include "mhd_str.h"
 #include "mhd_str_macros.h"
+
 #include "request_get_value.h"
+#include "response_destroy.h"
+#include "mhd_mono_clock.h"
+
+#include "mhd_public_api.h"
 
 
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void *
@@ -457,3 +465,123 @@ mhd_stream_switch_from_recv_to_send (struct MHD_Connection *c)
   /* Read buffer is not needed for this request, shrink it.*/
   mhd_stream_shrink_read_buffer (c);
 }
+
+
+/**
+ * Finish request serving.
+ * The stream will be re-used or closed.
+ *
+ * @param c the connection to use.
+ */
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
+                               bool reuse)
+{
+  struct MHD_Daemon *const restrict d = c->daemon;
+
+  if (! reuse)
+  {
+    /* Next function will destroy response, notify client,
+     * destroy memory pool, and set connection state to "CLOSED" */
+    mhd_conn_pre_close_req_finished (c);
+    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 0 // TODO: notification callback
+    if ( (NULL != d->notify_completed) &&
+         (c->rq.client_aware) )
+      d->notify_completed (d->notify_completed_cls,
+                           c,
+                           &c->rq.client_context,
+                           MHD_REQUEST_TERMINATED_COMPLETED_OK);
+#endif
+    c->rq.client_aware = false;
+
+    if (NULL != c->rp.response)
+      mhd_response_dec_use_count (c->rp.response);
+    c->rp.response = NULL;
+
+    c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE;
+    c->state = MHD_CONNECTION_INIT;
+    c->event_loop_info =
+      (0 == c->read_buffer_offset) ?
+      MHD_EVENT_LOOP_INFO_READ : MHD_EVENT_LOOP_INFO_PROCESS;
+
+    memset (&c->rq, 0, sizeof(c->rq));
+
+    /* iov (if any) will be deallocated by mhd_pool_reset */
+    memset (&c->rp, 0, sizeof(c->rp));
+
+    c->write_buffer = NULL;
+    c->write_buffer_size = 0;
+    c->write_buffer_send_offset = 0;
+    c->write_buffer_append_offset = 0;
+    c->continue_message_write_offset = 0;
+
+    /* Reset the read buffer to the starting size,
+       preserving the bytes we have already read. */
+    new_read_buf_size = d->conns.cfg.mem_pool_size / 2;
+    if (c->read_buffer_offset > new_read_buf_size)
+      new_read_buf_size = c->read_buffer_offset;
+
+    c->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->rq.client_context = NULL;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_check_timedout (struct MHD_Connection *restrict c)
+{
+  (void) c;
+  // TODO: implement
+  return false;
+}
+
+
+/**
+ * Update last activity mark to the current time..
+ * @param c the connection to update
+ */
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
+{
+  struct MHD_Daemon *const restrict d = c->daemon;
+#if defined(MHD_USE_THREADS)
+  mhd_assert (! mhd_D_HAS_WORKERS (d));
+#endif /* MHD_USE_THREADS */
+
+  mhd_assert (! c->suspended);
+
+  if (0 == c->connection_timeout_ms)
+    return;  /* Skip update of activity for connections
+               without timeout timer. */
+
+  c->last_activity = MHD_monotonic_msec_counter ();
+  if (mhd_D_HAS_THR_PER_CONN (d))
+    return; /* each connection has personal timeout */
+
+  if (c->connection_timeout_ms != d->conns.cfg.timeout)
+    return; /* custom timeout, no need to move it in "normal" DLL */
+
+  /* move connection to head of timeout list (by remove + add operation) */
+  mhd_DLINKEDL_DEL_D (&(d->conns.def_timeout), c, by_timeout);
+  mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout);
+}

+ 46 - 2
src/mhd2/stream_funcs.h

@@ -29,6 +29,7 @@
 
 #include "mhd_sys_options.h"
 #include "sys_base_types.h"
+#include "sys_bool_type.h"
 
 
 struct MHD_Connection; /* forward declaration */
@@ -114,10 +115,33 @@ MHD_INTERNAL void
 mhd_stream_switch_from_recv_to_send (struct MHD_Connection *c)
 MHD_FN_PAR_NONNULL_ALL_;
 
+/**
+ * Finish request serving.
+ * The stream will be re-used or closed.
+ *
+ * @param c the connection to use.
+ */
+MHD_INTERNAL void
+mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
+                               bool reuse)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Update last activity mark to the current time..
+ * @param c the connection to update
+ */
+MHD_INTERNAL void
+mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
 
 enum mhd_StreamAbortReason
 {
   mhd_STREAM_ABORT_CLIENT_HTTP_ERR
+  ,
+  mhd_STREAM_ABORT_NO_POOL_MEM_FOR_REPLY
+  ,
+  mhd_STREAM_ABORT_APP_ERROR
 };
 
 /**
@@ -140,9 +164,11 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
 /**
  * Update last activity mark to the current time..
  * @param c the connection to update
+ * @return 'true' if connection has not been timed out,
+ *         'false' otherwise
  */
-MHD_INTERNAL void
-mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
+MHD_INTERNAL bool
+mhd_stream_check_timedout (struct MHD_Connection *restrict c)
 MHD_FN_PAR_NONNULL_ALL_;
 
 
@@ -165,5 +191,23 @@ MHD_INTERNAL void
 mhd_conn_pre_close_skt_err (struct MHD_Connection *restrict c)
 MHD_FN_PAR_NONNULL_ALL_;
 
+/**
+ * Perform initial clean-up and mark for closing.
+ * Set the reason to "request finished"
+ * @param c to make
+ */
+MHD_INTERNAL void
+mhd_conn_pre_close_req_finished (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Perform initial clean-up and mark for closing.
+ * Set the reason to "timed out".
+ * @param c to make
+ */
+MHD_INTERNAL void
+mhd_conn_pre_close_timedout (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
 
 #endif /* ! MHD_STREAM_FUNCS_H */

+ 1140 - 0
src/mhd2/stream_process_reply.c

@@ -0,0 +1,1140 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2014-2024 Evgeny Grin (Karlson2k)
+  Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
+
+  GNU libmicrohttpd 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.
+
+  GNU libmicrohttpd 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 src/mhd2/stream_process_reply.h
+ * @brief  The implementation of internal functions for forming and sending
+ *         replies for requests
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other
+ * contributors.
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#ifdef HAVE_TIME_H
+#  include <time.h>
+#endif
+
+#include "mhd_daemon.h"
+#include "mhd_response.h"
+#include "mhd_reply.h"
+#include "mhd_connection.h"
+
+#include "daemon_logger.h"
+#include "mhd_assert.h"
+
+#include "mhd_str.h"
+#include "http_status_str.h"
+#include "stream_process_reply.h"
+#include "stream_funcs.h"
+#include "request_get_value.h"
+
+#include "mhd_public_api.h"
+
+
+/**
+ * This enum type describes requirements for reply body and reply bode-specific
+ * headers (namely Content-Length, Transfer-Encoding).
+ */
+enum replyBodyUse
+{
+  /**
+   * No reply body allowed.
+   * Reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked' are
+   * not allowed as well.
+   */
+  RP_BODY_NONE = 0,
+
+  /**
+   * Do not send reply body.
+   * Reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked' are
+   * allowed, but optional.
+   */
+  RP_BODY_HEADERS_ONLY = 1,
+
+  /**
+   * Send reply body and
+   * reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked'.
+   * Reply body headers are required.
+   */
+  RP_BODY_SEND = 2
+};
+
+
+/**
+ * Is it allowed to reuse the connection?
+ * The TCP stream can be reused for the next requests 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), the connection
+ * is definitively closed.  If the "Connection" header is not exactly "close"
+ * or "keep-alive", connection is reused if is it HTTP/1.1.
+ * 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 as more client data may be sent.
+ *
+ * @param c the connection to check for re-use
+ * @return mhd_CONN_KEEPALIVE_POSSIBLE if (based on the request and
+ *         the response) a connection could be reused,
+ *         MHD_CONN_MUST_CLOSE if connection must be closed after sending
+ *         complete reply,
+ *         mhd_CONN_MUST_UPGRADE if connection must be upgraded.
+ */
+static MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnReuse
+get_conn_reuse (struct MHD_Connection *c)
+{
+  const struct MHD_Response *const restrict rp = c->rp.response;
+
+  mhd_assert (NULL != rp);
+  if (mhd_CONN_MUST_CLOSE == c->conn_reuse)
+    return mhd_CONN_MUST_CLOSE;
+
+  mhd_assert ( (! c->stop_with_error) || (c->discard_request));
+  if ((c->sk_rmt_shut_wr) || (c->discard_request))
+    return mhd_CONN_MUST_CLOSE;
+
+  if (rp->cfg.close_forced)
+    return mhd_CONN_MUST_CLOSE;
+
+  mhd_assert ((MHD_SIZE_UNKNOWN != rp->cntn_size) || \
+              (! rp->cfg.mode_1_0));
+
+  if (! MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver))
+    return mhd_CONN_MUST_CLOSE;
+
+  if (rp->cfg.mode_1_0 &&
+      ! mhd_stream_has_header_token_st (c,
+                                        MHD_HTTP_HEADER_CONNECTION,
+                                        "keep-alive"))
+    return mhd_CONN_MUST_CLOSE;
+
+#if 0 // def UPGRADE_SUPPORT // TODO: Implement 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->rq.http_ver));
+    mhd_assert (! c->stop_with_error);
+    return mhd_CONN_MUST_UPGRADE;
+  }
+#endif /* UPGRADE_SUPPORT */
+
+  return mhd_CONN_KEEPALIVE_POSSIBLE;
+}
+
+
+/**
+ * Check whether reply body must be used.
+ *
+ * If reply body is needed, it could be zero-sized.
+ *
+ * @param c the connection to check
+ * @param rcode the response code
+ * @return enum value indicating whether response body can be used and
+ *         whether response body length headers are allowed or required.
+ * @sa is_reply_body_header_needed()
+ */
+static enum replyBodyUse
+is_reply_body_needed (struct MHD_Connection *restrict c,
+                      uint_fast16_t rcode)
+{
+  mhd_assert (100 <= rcode);
+  mhd_assert (999 >= rcode);
+
+  if (199 >= rcode)
+    return RP_BODY_NONE;
+
+  if (MHD_HTTP_STATUS_NO_CONTENT == rcode)
+    return RP_BODY_NONE;
+
+#if 0
+  /* This check is not needed as upgrade handler is used only with code 101 */
+#ifdef UPGRADE_SUPPORT
+  if (NULL != rp.response->upgrade_handler)
+    return RP_BODY_NONE;
+#endif /* UPGRADE_SUPPORT */
+#endif
+
+#if 0
+  /* CONNECT is not supported by MHD */
+  /* Successful responses for connect requests are filtered by
+   * MHD_queue_response() */
+  if ( (mhd_HTTP_METHOD_CONNECT == c->rq.http_mthd) &&
+       (2 == rcode / 100) )
+    return false; /* Actually pass-through CONNECT is not supported by MHD */
+#endif
+
+  /* Reply body headers could be used.
+   * Check whether reply body itself must be used. */
+
+  if (mhd_HTTP_METHOD_HEAD == c->rq.http_mthd)
+    return RP_BODY_HEADERS_ONLY;
+
+  if (MHD_HTTP_STATUS_NOT_MODIFIED == rcode)
+    return RP_BODY_HEADERS_ONLY;
+
+  /* Reply body must be sent.
+   * The body may have zero length, but body size must be indicated by
+   * headers ('Content-Length:' or 'Transfer-Encoding: chunked'). */
+  return RP_BODY_SEND;
+}
+
+
+/**
+ * Setup connection reply properties.
+ *
+ * Reply properties include presence of reply body, transfer-encoding
+ * type and other.
+ *
+ * @param connection to connection to process
+ */
+static MHD_FN_PAR_NONNULL_ALL_ void
+setup_reply_properties (struct MHD_Connection *restrict c)
+{
+  struct MHD_Response *const restrict r = c->rp.response;  /**< a short alias */
+  enum replyBodyUse use_rp_body;
+  bool use_chunked;
+  bool end_by_closing;
+
+  mhd_assert (NULL != r);
+
+  /* ** Adjust reply properties ** */
+
+  c->conn_reuse = get_conn_reuse (c);
+  use_rp_body = is_reply_body_needed (c, r->sc);
+  c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
+  c->rp.props.use_reply_body_headers = (use_rp_body >= RP_BODY_HEADERS_ONLY);
+
+#if 0 // def UPGRADE_SUPPORT // TODO: upgrade support
+  mhd_assert ( (NULL == r->upgrade_handler) ||
+               (RP_BODY_NONE == use_rp_body) );
+#endif /* UPGRADE_SUPPORT */
+
+  use_chunked = false;
+  end_by_closing = false;
+  if (c->rp.props.use_reply_body_headers)
+  {
+    if (r->cfg.chunked)
+    {
+      mhd_assert (! r->cfg.mode_1_0);
+      use_chunked = (MHD_HTTP_VERSION_1_1 == c->rq.http_ver);
+    }
+    if ((MHD_SIZE_UNKNOWN == r->cntn_size) &&
+        (! use_chunked) &&
+        (c->rp.props.send_reply_body))
+    {
+      /* End of the stream is indicated by closure */
+      end_by_closing = true;
+    }
+  }
+
+  if (end_by_closing)
+  {
+    mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
+    /* End of the stream is indicated by closure */
+    c->conn_reuse = mhd_CONN_MUST_CLOSE;
+  }
+
+  c->rp.props.chunked = use_chunked;
+  c->rp.props.end_by_closing = end_by_closing;
+
+  if ((! c->rp.props.send_reply_body) || (0 == r->cntn_size))
+    c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+  else if (c->rp.props.chunked)
+    c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+  else
+  {
+    switch (r->cntn_dtype)
+    {
+    case mhd_RESPONSE_CONTENT_DATA_BUFFER:
+      c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_RESP_BUF;
+      break;
+    case mhd_RESPONSE_CONTENT_DATA_IOVEC:
+      c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_IOV;
+      break;
+    case mhd_RESPONSE_CONTENT_DATA_FILE:
+#if 0 // TODO: TLS support
+      if (use_tls)
+      {
+        c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+        break;
+      }
+#endif
+#ifdef MHD_USE_SENDFILE
+      if (r->cntn.file.use_sf)
+      {
+        c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_FILE;
+        break;
+      }
+#endif
+      c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+      break;
+    case mhd_RESPONSE_CONTENT_DATA_CALLBACK:
+      c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
+      break;
+    case mhd_RESPONSE_CONTENT_DATA_INVALID:
+    default:
+      mhd_assert (0 && "Impossible value");
+      MHD_UNREACHABLE_;
+      c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+      break;
+    }
+  }
+
+#ifdef _DEBUG
+  c->rp.props.set = true;
+#endif /* _DEBUG */
+}
+
+
+/**
+ * Check whether queued response is suitable for @a connection.
+ * @param connection to connection to check
+ */
+static void
+check_connection_reply (struct MHD_Connection *restrict c)
+{
+  struct MHD_Response *const restrict r = c->rp.response;  /**< a short alias */
+
+  mhd_assert (c->rp.props.set);
+
+  if ( (! c->rp.props.use_reply_body_headers) &&
+       (0 != r->cntn_size) )
+  {
+    mhd_LOG_PRINT (c->daemon, MHD_SC_REPLY_NOT_EMPTY_RESPONSE,
+                   mhd_LOG_FMT ("This reply with response code %u " \
+                                "cannot use reply content. Non-empty " \
+                                "response content is ignored and not used."),
+                   (unsigned) (c->rp.response->sc));
+  }
+  if ( (! c->rp.props.use_reply_body_headers) &&
+       (r->cfg.cnt_len_by_app) )
+  {
+    mhd_LOG_PRINT (c->daemon, MHD_SC_REPLY_CONTENT_LENGTH_NOT_ALLOWED,
+                   mhd_LOG_FMT ("This reply with response code %u " \
+                                "cannot use reply content. Application " \
+                                "defined \"Content-Length\" header " \
+                                "violates HTTP specification."),
+                   (unsigned) (c->rp.response->sc));
+  }
+}
+
+
+/**
+ * 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 of savailable space.
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (1) 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_GMTIME_R)
+  if (NULL == gmtime_r (&t,
+                        &now))
+    return false;
+#elif 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;
+#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 ((uint_least16_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 MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (1) bool
+get_date_header (char *header)
+{
+  header[0] = 'D';
+  header[1] = 'a';
+  header[2] = 't';
+  header[3] = 'e';
+  header[4] = ':';
+  header[5] = ' ';
+  if (! get_date_str (header + 6))
+  {
+    header[0] = 0;
+    return false;
+  }
+  header[35] = '\r';
+  header[36] = '\n';
+  header[37] = 0;
+  return 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 MHD_FN_PAR_NONNULL_ALL_ 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;
+}
+
+
+/**
+ * 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 filter_content_len skip "Content-Length" 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 MHD_FN_PAR_NONNULL_ALL_ bool
+add_user_headers (char *restrict buf,
+                  size_t *restrict ppos,
+                  size_t buf_size,
+                  struct MHD_Response *restrict r,
+                  bool filter_content_len,
+                  bool add_close,
+                  bool add_keep_alive)
+{
+  struct mhd_ResponseHeader *hdr; /**< Iterates through User-specified headers */
+  size_t el_size; /**< the size of current element to be added to the @a buf */
+
+  mhd_assert (! add_close || ! add_keep_alive);
+  mhd_assert (! add_keep_alive || ! add_close);
+
+  if (r->cfg.has_hdr_conn)
+  {
+    add_close = false;          /* No such header */
+    add_keep_alive = false;     /* No such header */
+  }
+
+  for (hdr = mhd_DLINKEDL_GET_FIRST (r, headers);
+       NULL != hdr;
+       hdr = mhd_DLINKEDL_GET_NEXT (hdr, headers))
+  {
+    size_t initial_pos = *ppos;
+
+    if (filter_content_len)
+    { /* Need to filter-out "Content-Length" */
+      if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH, \
+                                       hdr->name.cstr,
+                                       hdr->name.len))
+      {
+        /* Reset filter flag  */
+        filter_content_len = false;
+        continue; /* Skip "Content-Length" header */
+      }
+    }
+
+    /* Add user header */
+    el_size = hdr->name.len + 2 + hdr->value.len + 2;
+    if (buf_size < *ppos + el_size)
+      return false;
+    memcpy (buf + *ppos, hdr->name.cstr, hdr->name.len);
+    (*ppos) += hdr->name.len;
+    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->name.cstr, \
+                                            MHD_HTTP_HEADER_CONNECTION, \
+                                            hdr->name.len));
+
+      if (add_close)
+      {
+        el_size += mhd_SSTR_LEN ("close, ");
+        if (buf_size < initial_pos + el_size)
+          return false;
+        memcpy (buf + *ppos, "close, ",
+                mhd_SSTR_LEN ("close, "));
+        *ppos += mhd_SSTR_LEN ("close, ");
+      }
+      else
+      {
+        el_size += mhd_SSTR_LEN ("Keep-Alive, ");
+        if (buf_size < initial_pos + el_size)
+          return false;
+        memcpy (buf + *ppos, "Keep-Alive, ",
+                mhd_SSTR_LEN ("Keep-Alive, "));
+        *ppos += mhd_SSTR_LEN ("Keep-Alive, ");
+      }
+      add_close = false;
+      add_keep_alive = false;
+    }
+    if (0 != hdr->value.len)
+      memcpy (buf + *ppos, hdr->value.cstr, hdr->value.len);
+    *ppos += hdr->value.len;
+    buf[(*ppos)++] = '\r';
+    buf[(*ppos)++] = '\n';
+    mhd_assert (initial_pos + el_size == (*ppos));
+  }
+  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_SSTR_LEN (str))
+
+/**
+ * Allocate the connection's write buffer and fill it with all of the
+ * headers from the response.
+ * Inner version of the function.
+ *
+ * @param c the connection to process
+ * @return 'true' if state has been update,
+ *         'false' if connection is going to be aborted
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+build_header_response_inn (struct MHD_Connection *restrict c)
+{
+  struct MHD_Response *const restrict r = c->rp.response;
+  char *restrict 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 */
+  uint_fast16_t 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->conn_reuse) || \
+              (mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse) || \
+              (mhd_CONN_MUST_UPGRADE == c->conn_reuse));
+#if 0 // def UPGRADE_SUPPORT // TODO: upgrade support
+  mhd_assert ((NULL == r->upgrade_handler) || \
+              (mhd_CONN_MUST_UPGRADE == c->keepalive));
+#else  /* ! UPGRADE_SUPPORT */
+  mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
+#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);
+  mhd_assert ((! c->rp.props.end_by_closing) || \
+              (mhd_CONN_MUST_CLOSE == c->conn_reuse));
+#if 0 // def UPGRADE_SUPPORT  // TODO: upgrade support
+  mhd_assert (NULL == r->upgrade_handler || \
+              ! c->rp.props.use_reply_body_headers);
+#endif /* UPGRADE_SUPPORT */
+
+  check_connection_reply (c);
+
+  rcode = (uint_fast16_t) c->rp.response->sc;
+  if (mhd_CONN_MUST_CLOSE == c->conn_reuse)
+  {
+    /* 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_KEEPALIVE_POSSIBLE == c->conn_reuse)
+  {
+    mhd_assert (! r->cfg.mode_1_0);
+    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 (r->cfg.mode_1_0 ||
+        (MHD_HTTP_VERSION_1_0 == c->rq.http_ver))
+      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 */
+  mhd_stream_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 false;
+  mhd_assert (NULL != buf);
+
+  // TODO: use pre-calculated header size
+  /* * The status line * */
+
+  /* The HTTP version */
+  if (! c->rp.responseIcy)
+  { /* HTTP reply */
+    if (! r->cfg.mode_1_0)
+    { /* 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_STR))
+        return false;
+    }
+    else
+    { /* HTTP/1.0 reply */
+      if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0_STR))
+        return false;
+    }
+  }
+  else
+  { /* ICY reply */
+    if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
+      return false;
+  }
+
+  /* The response code */
+  if (buf_size < pos + 5) /* space + code + space */
+    return false;
+  buf[pos++] = ' ';
+  pos += mhd_uint16_to_str ((uint16_t) rcode, buf + pos,
+                            buf_size - pos);
+  buf[pos++] = ' ';
+
+  /* The reason phrase */
+  if (1)
+  {
+    const struct MHD_String *stat_str;
+    stat_str = mhd_HTTP_status_code_to_string_int (rcode);
+    mhd_assert (0 != stat_str->len);
+    if (! buffer_append (buf, &pos, buf_size,
+                         stat_str->cstr,
+                         stat_str->len))
+      return false;
+  }
+  /* The linefeed */
+  if (buf_size < pos + 2)
+    return false;
+  buf[pos++] = '\r';
+  buf[pos++] = '\n';
+
+  /* * The headers * */
+
+  /* Main automatic headers */
+
+  /* The "Date:" header */
+  if ( (! r->cfg.has_hdr_date) &&
+       (! c->daemon->req_cfg.suppress_date) )
+  {
+    /* Additional byte for unused zero-termination */
+    if (buf_size < pos + 38)
+      return false;
+    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 (! r->cfg.has_hdr_conn)
+  {
+    if (use_conn_close)
+    {
+      if (! buffer_append_s (buf, &pos, buf_size,
+                             MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
+        return false;
+    }
+    else if (use_conn_k_alive)
+    {
+      if (! buffer_append_s (buf, &pos, buf_size,
+                             MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
+        return false;
+    }
+  }
+
+  /* User-defined headers */
+
+  if (! add_user_headers (buf, &pos, buf_size, r,
+                          ! c->rp.props.use_reply_body_headers,
+                          use_conn_close,
+                          use_conn_k_alive))
+    return false;
+
+  /* Other automatic headers */
+
+  if (c->rp.props.use_reply_body_headers)
+  {
+    /* Body-specific headers */
+
+    if (c->rp.props.chunked)
+    { /* Chunked encoding is used */
+      mhd_assert (! c->rp.props.end_by_closing);
+      if (! buffer_append_s (buf, &pos, buf_size,
+                             MHD_HTTP_HEADER_TRANSFER_ENCODING ": " \
+                             "chunked\r\n"))
+        return false;
+    }
+    else /* Chunked encoding is not used */
+    {
+      if ((MHD_SIZE_UNKNOWN != r->cntn_size) &&
+          (! c->rp.props.end_by_closing) &&
+          (! r->cfg.chunked) &&
+          (! r->cfg.head_only))
+      { /* The size is known and can be indicated by the header */
+        if (r->cfg.cnt_len_by_app)
+        { /* The response does not have "Content-Length" header */
+          if (! buffer_append_s (buf, &pos, buf_size,
+                                 MHD_HTTP_HEADER_CONTENT_LENGTH ": "))
+            return false;
+          el_size = mhd_uint64_to_str (r->cntn_size,
+                                       buf + pos,
+                                       buf_size - pos);
+          if (0 == el_size)
+            return false;
+          pos += el_size;
+
+          if (buf_size < pos + 2)
+            return false;
+          buf[pos++] = '\r';
+          buf[pos++] = '\n';
+        }
+      }
+      else
+      {
+        mhd_assert ((! c->rp.props.send_reply_body) || \
+                    (mhd_CONN_MUST_CLOSE = c->conn_reuse));
+        (void) 0;
+      }
+    }
+  }
+
+  /* * Header termination * */
+  if (buf_size < pos + 2)
+    return false;
+  buf[pos++] = '\r';
+  buf[pos++] = '\n';
+
+  c->write_buffer_append_offset = pos;
+  return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_build_header_response (struct MHD_Connection *restrict c)
+{
+  if (! build_header_response_inn (c))
+  {
+    mhd_STREAM_ABORT (c,
+                      mhd_STREAM_ABORT_NO_POOL_MEM_FOR_REPLY,
+                      "No memory in the pool for the reply headers.");
+    return false;
+  }
+  c->state = MHD_CONNECTION_HEADERS_SENDING;
+  return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
+{
+  struct MHD_Response *const restrict r = c->rp.response;
+
+  mhd_assert (c->rp.props.send_reply_body);
+  mhd_assert (c->rp.rsp_cntn_read_pos != r->cntn_size);
+
+  if (0 == r->cntn_size)
+    return;  /* 0-byte response is always ready */
+
+  mhd_assert (mhd_REPLY_CNTN_LOC_NOWHERE != c->rp.cntn_loc);
+  if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)
+  {
+    (void) 0; /* Nothing to do, buffers are ready */
+  }
+  else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
+  {
+    // TODO: implement
+    mhd_assert (0 && "Not implemented yet");
+    c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+    c->rp.rsp_cntn_read_pos = r->cntn_size;
+#if 0
+    ret = response->crc (response->crc_cls,
+                         connection->rp.rsp_cntn_read_pos,
+                         (char *) response->data,
+                         (size_t) MHD_MIN ((uint64_t)
+                                           response->data_buffer_size,
+                                           response->total_size
+                                           - connection->rp.rsp_cntn_read_pos));
+    if (0 > 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->rp.rsp_cntn_read_pos;
+  #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->rp.rsp_cntn_read_pos;
+    response->data_size = (size_t) ret;
+#endif
+  }
+  else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
+  {
+    size_t copy_size;
+
+    mhd_assert (NULL != c->rp.resp_iov.iov);
+    mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype);
+
+    copy_size = r->cntn.iovec.cnt * sizeof(mhd_iovec);
+    c->rp.resp_iov.iov = mhd_stream_alloc_memory (c,
+                                                  copy_size);
+    if (NULL == c->rp.resp_iov.iov)
+    {
+      /* not enough memory */
+      mhd_STREAM_ABORT (c,
+                        mhd_STREAM_ABORT_NO_POOL_MEM_FOR_REPLY,
+                        "No memory in the pool for the response data.");
+      return;
+    }
+    memcpy (c->rp.resp_iov.iov,
+            &(r->cntn.iovec.iov),
+            copy_size);
+    c->rp.resp_iov.cnt = r->cntn.iovec.cnt;
+    c->rp.resp_iov.sent = 0;
+  }
+#if defined(MHD_USE_SENDFILE)
+  else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc)
+  {
+    (void) 0; /* Nothing to do, file should be ready directly */
+  }
+#endif /* MHD_USE_SENDFILE */
+  else
+  {
+    mhd_assert (0 && "Impossible value");
+    MHD_UNREACHABLE_;
+    c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
+    c->rp.rsp_cntn_read_pos = r->cntn_size;
+  }
+
+  c->state = MHD_CONNECTION_UNCHUNKED_BODY_READY;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
+{
+  size_t filled;
+  struct MHD_Response *const restrict r = c->rp.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;
+
+  mhd_assert (0 == c->write_buffer_append_offset);
+  mhd_assert (0 == c->write_buffer_send_offset);
+
+  /* The buffer must be reasonably large enough */
+  if (32 > c->write_buffer_size)
+  {
+    mhd_STREAM_ABORT (c,
+                      mhd_STREAM_ABORT_NO_POOL_MEM_FOR_REPLY,
+                      "No memory in the pool for the reply chunked content.");
+  }
+  mhd_assert (max_chunk_overhead < \
+              (c->write_buffer_size));
+
+  if (MHD_SIZE_UNKNOWN == r->cntn_size)
+    left_to_send = MHD_SIZE_UNKNOWN;
+  else
+    left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos;
+
+  mhd_assert (0 != left_to_send);
+  if (0 != left_to_send)
+  {
+    size_to_fill =
+      c->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;
+  }
+  else
+    size_to_fill = 0;
+
+  if ((0 == left_to_send) &&
+      (mhd_RESPONSE_CONTENT_DATA_CALLBACK != r->cntn_dtype))
+  {
+    filled = 0;
+  }
+  else if (mhd_RESPONSE_CONTENT_DATA_BUFFER == r->cntn_dtype)
+  {
+    mhd_assert (size_to_fill <= \
+                r->cntn_size - (size_t) c->rp.rsp_cntn_read_pos);
+    memcpy (c->write_buffer + max_chunk_hdr_len,
+            r->cntn.buf + (size_t) c->rp.rsp_cntn_read_pos,
+            size_to_fill);
+    filled = size_to_fill;
+  }
+  else
+  {
+    mhd_assert (0 && "Not implemented yet");
+    filled = 0;
+  }
+  if (size_to_fill < filled)
+  {
+    mhd_STREAM_ABORT (c,
+                      mhd_STREAM_ABORT_APP_ERROR,
+                      "Closing connection (application returned more data "
+                      "than requested).");
+    return;
+  }
+
+  if (0 == filled)
+  {
+    c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
+    return;
+  }
+  chunk_hdr_len = mhd_uint32_to_strx ((uint_fast32_t) filled,
+                                      chunk_hdr,
+                                      sizeof(chunk_hdr));
+  mhd_assert (chunk_hdr_len != 0);
+  mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
+  c->write_buffer_send_offset = max_chunk_hdr_len - (chunk_hdr_len + 2);
+  memcpy (c->write_buffer + c->write_buffer_send_offset,
+          chunk_hdr,
+          chunk_hdr_len);
+  c->write_buffer[max_chunk_hdr_len - 2] = '\r';
+  c->write_buffer[max_chunk_hdr_len - 1] = '\n';
+  c->write_buffer[max_chunk_hdr_len + filled] = '\r';
+  c->write_buffer[max_chunk_hdr_len + filled + 1] = '\n';
+  c->write_buffer_append_offset = max_chunk_hdr_len + filled + 2;
+  if (0 != filled)
+    c->rp.rsp_cntn_read_pos += filled;
+  else
+    c->rp.rsp_cntn_read_pos = r->cntn_size;
+
+  c->state = MHD_CONNECTION_CHUNKED_BODY_READY;
+}
+
+
+/**
+ * Allocate the connection's write buffer (if necessary) and fill it
+ * with response footers.
+ * Inner version.
+ *
+ * @param c the connection
+ * @return 'true' if footers formed successfully,
+ *         'false' if not enough buffer
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+prep_chunked_footer_inn (struct MHD_Connection *restrict c)
+{
+  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_HTTP_Res_Header *pos;
+
+  mhd_assert (c->rp.props.chunked);
+  mhd_assert (MHD_CONNECTION_CHUNKED_BODY_SENT == c->state);
+  mhd_assert (NULL != c->rp.response);
+
+  buf_size = mhd_stream_maximize_write_buffer (c);
+  /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
+  if (buf_size < 5)
+    return false;
+  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';
+
+#if 0 // TODO: use dynamic/connection's footers
+  for (pos = c->rp.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);
+    }
+  }
+#endif
+
+  if (used_size + 2 > buf_size)
+    return false;
+  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 true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
+{
+  if (! prep_chunked_footer_inn (c))
+  {
+    mhd_STREAM_ABORT (c,
+                      mhd_STREAM_ABORT_NO_POOL_MEM_FOR_REPLY,
+                      "No memory in the pool for the reply chunked footer.");
+    return;
+  }
+  c->state = MHD_CONNECTION_FOOTERS_SENDING;
+}

+ 44 - 0
src/mhd2/stream_process_reply.h

@@ -35,4 +35,48 @@
 struct MHD_Connection; /* forward declaration */
 
 
+/**
+ * Allocate the connection's write buffer and fill it with all of the
+ * headers from the response.
+ * Required headers are added here.
+ *
+ * @param c the connection to process
+ * @return 'true' if state has been update,
+ *         'false' if connection is going to be aborted
+ */
+MHD_INTERNAL bool
+mhd_stream_build_header_response (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Prepare the unchunked response content of this connection for sending.
+ *
+ * @param c the connection
+ */
+MHD_INTERNAL void
+mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Prepare the chunked response content of this connection for sending.
+ *
+ * @param c the connection
+ */
+MHD_INTERNAL void
+mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Allocate the connection's write buffer (if necessary) and fill it
+ * with response footers.
+ *
+ * @param c the connection
+ */
+MHD_INTERNAL void
+mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
 #endif /* ! MHD_STREAM_PROCESS_REPLY_H */

+ 251 - 116
src/mhd2/stream_process_request.c

@@ -305,6 +305,17 @@
         "<body>HTTP/1.1 request without <b>&quot;Host:&quot;</b>.</body>" \
         "</html>"
 
+/**
+ * Response text used when the request has more than one "Host:" header.
+ */
+#define ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS \
+        "<html>" \
+        "<head>" \
+        "<title>Several &quot;Host:&quot; headers used</title></head>" \
+        "<body>" \
+        "Request with more than one <b>&quot;Host:&quot;</b> header.</body>" \
+        "</html>"
+
 /**
  * Response text used when the request has unsupported "Transfer-Encoding:".
  */
@@ -342,6 +353,14 @@
         "<body>HTTP request has wrong value for " \
         "<b>Content-Length</b> header.</body></html>"
 
+/**
+ * Response text used when the request has more than one "Content-Length:"
+ * header.
+ */
+#define ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL \
+        "<html><head><title>Request malformed</title></head>" \
+        "<body>HTTP request has several " \
+        "<b>Content-Length</b> headers.</body></html>"
 
 /**
  * Response text used when the request HTTP chunked encoding is
@@ -1200,7 +1219,7 @@ process_request_target (struct MHD_Connection *c)
                               &request_add_get_arg,
                               c))
     {
-      MHD_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_GET_PARAM,
+      mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_GET_PARAM,
                    "Not enough memory in the pool to store GET parameter");
 
       mhd_RESPOND_WITH_ERROR_STATIC (
@@ -1708,7 +1727,7 @@ get_req_header (struct MHD_Connection *restrict c,
              See RFC 9112, Section 2.2-8 */
           mhd_assert (allow_wsp_at_start);
 
-          MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED,
+          mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED,
                        "Whitespace-prefixed first header line " \
                        "has been skipped.");
           skip_line = true;
@@ -2012,11 +2031,11 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c,
         mhd_assert (hdr_name.cstr < hdr_value.cstr);
 
         if (! process_footers)
-          MHD_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
+          mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
                        "Failed to allocate memory in the connection memory " \
                        "pool to store header.");
         else
-          MHD_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
+          mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
                        "Failed to allocate memory in the connection memory " \
                        "pool to store footer.");
 
@@ -2062,25 +2081,25 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c,
   if (1 == c->rq.num_cr_sp_replaced)
   {
     if (! process_footers)
-      MHD_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+      mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
                    "One bare CR character has been replaced with space " \
                    "in the request line or in the request headers.");
     else
-      MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \
+      mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \
                    "One bare CR character has been replaced with space " \
                    "in the request footers.");
   }
   else if (0 != c->rq.num_cr_sp_replaced)
   {
     if (! process_footers)
-      MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
-                     MHD_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \
+      mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                     mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \
                                   "been replaced with spaces in the request " \
                                   "line and/or in the request headers."), \
                      (uint_fast64_t) c->rq.num_cr_sp_replaced);
     else
-      MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
-                     MHD_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \
+      mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                     mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \
                                   "been replaced with spaces in the request " \
                                   "footers."), \
                      (uint_fast64_t) c->rq.num_cr_sp_replaced);
@@ -2090,22 +2109,22 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c,
   if (1 == c->rq.skipped_broken_lines)
   {
     if (! process_footers)
-      MHD_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \
+      mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \
                    "One header line without colon has been skipped.");
     else
-      MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \
+      mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \
                    "One footer line without colon has been skipped.");
   }
   else if (0 != c->rq.skipped_broken_lines)
   {
     if (! process_footers)
-      MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
-                     MHD_LOG_FMT ("%" PRIu64 " header lines without colons "
+      mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                     mhd_LOG_FMT ("%" PRIu64 " header lines without colons "
                                   "have been skipped."),
                      (uint_fast64_t) c->rq.skipped_broken_lines);
     else
-      MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
-                     MHD_LOG_FMT ("%" PRIu64 " footer lines without colons "
+      mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                     mhd_LOG_FMT ("%" PRIu64 " footer lines without colons "
                                   "have been skipped."),
                      (uint_fast64_t) c->rq.skipped_broken_lines);
   }
@@ -2390,12 +2409,13 @@ parse_cookies_string (const size_t str_len,
  * Parse the cookie header (see RFC 6265).
  *
  * @param connection connection to parse header of
+ * @param cookie_val the value of the "Cookie:" header
  * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
  */
 static enum _MHD_ParseCookie
-parse_cookie_header (struct MHD_Connection *restrict connection)
+parse_cookie_header (struct MHD_Connection *restrict connection,
+                     struct MHD_StringNullable *restrict cookie_val)
 {
-  const struct MHD_StringNullable *hvalue;
   char *cpy;
   size_t i;
   enum _MHD_ParseCookie parse_res;
@@ -2404,31 +2424,28 @@ parse_cookie_header (struct MHD_Connection *restrict connection)
   const bool allow_partially_correct_cookie =
     (1 >= connection->daemon->req_cfg.strictnees);
 
-  hvalue = mhd_request_get_value_st (&(connection->rq),
-                                     MHD_VK_HEADER,
-                                     MHD_HTTP_HEADER_COOKIE);
-  if (NULL == hvalue)
+  if (NULL == cookie_val)
     return MHD_PARSE_COOKIE_OK;
-  if (0 == hvalue->len)
+  if (0 == cookie_val->len)
     return MHD_PARSE_COOKIE_OK;
 
   cpy = mhd_stream_alloc_memory (connection,
-                                 hvalue->len + 1);
+                                 cookie_val->len + 1);
   if (NULL == cpy)
     parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
   else
   {
     memcpy (cpy,
-            hvalue->cstr,
-            hvalue->len + 1);
-    mhd_assert (0 == cpy[hvalue->len]);
+            cookie_val->cstr,
+            cookie_val->len + 1);
+    mhd_assert (0 == cpy[cookie_val->len]);
 
     /* Must not have initial whitespaces */
     mhd_assert (' ' != cpy[0]);
     mhd_assert ('\t' != cpy[0]);
 
     i = 0;
-    parse_res = parse_cookies_string (hvalue->len - i, cpy + i, connection);
+    parse_res = parse_cookies_string (cookie_val->len - i, cpy + i, connection);
   }
 
   switch (parse_res)
@@ -2437,7 +2454,7 @@ parse_cookie_header (struct MHD_Connection *restrict connection)
     break;
   case MHD_PARSE_COOKIE_OK_LAX:
     if (saved_tail != connection->rq.fields.last)
-      MHD_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_NOT_COMPLIANT, \
+      mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_NOT_COMPLIANT, \
                    "The Cookie header has been parsed, but it is not "
                    "fully compliant with specifications.");
     break;
@@ -2450,21 +2467,21 @@ parse_cookie_header (struct MHD_Connection *restrict connection)
         /* Memory remains allocated until the end of the request processing */
         connection->rq.fields.last = saved_tail;  // FIXME: a better way?
         saved_tail->fields.next = NULL;  // FIXME: a better way?
-        MHD_LOG_MSG ( \
+        mhd_LOG_MSG ( \
           connection->daemon, MHD_SC_REQ_COOKIE_IGNORED_NOT_COMPLIANT, \
           "The Cookie header is ignored as it contains malformed data.");
       }
       else
-        MHD_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_PARTIALLY, \
+        mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_PARTIALLY, \
                      "The Cookie header has been only partially parsed " \
                      "as it contains malformed data.");
     }
     else
-      MHD_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_INVALID,
+      mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_INVALID,
                    "The Cookie header has malformed data.");
     break;
   case MHD_PARSE_COOKIE_NO_MEMORY:
-    MHD_LOG_MSG (connection->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE,
+    mhd_LOG_MSG (connection->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE,
                  "Not enough memory in the connection pool to "
                  "parse client cookies!\n");
     break;
@@ -2509,116 +2526,234 @@ handle_req_cookie_no_space (struct MHD_Connection *restrict c)
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
 mhd_stream_parse_connection_headers (struct MHD_Connection *restrict c)
 {
-  const struct MHD_StringNullable *hcntnlen;
-  const struct MHD_StringNullable *htrenc;
-
-#ifdef COOKIE_SUPPORT
-  if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (c))
-  {
-    handle_req_cookie_no_space (c);
-    return;
-  }
-#endif /* COOKIE_SUPPORT */
-
-  if ( (-3 < c->daemon->req_cfg.strictnees) &&
-       (MHD_HTTP_VERSION_1_1 == c->rq.http_ver) &&
-       (NULL == mhd_request_get_value_st (&(c->rq),
-                                          MHD_VK_HEADER,
-                                          MHD_HTTP_HEADER_HOST)))
-  {
-    MHD_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MISSING, \
-                 "Received HTTP/1.1 request without 'Host' header.");
-    mhd_RESPOND_WITH_ERROR_STATIC (c,
-                                   MHD_HTTP_STATUS_BAD_REQUEST,
-                                   ERR_RSP_REQUEST_LACKS_HOST);
-    return;
-  }
+  bool has_host;
+  bool has_trenc;
+  bool has_cntnlen;
+  bool has_keepalive;
+  struct mhd_RequestField *f;
 
   /* The presence of the request body is indicated by "Content-Length:" or
      "Transfer-Encoding:" request headers.
      Unless one of these two headers is used, the request has no request body.
      See RFC9112, Section 6, paragraph 4. */
+  c->rq.have_chunked_upload = false;
   c->rq.cntn.cntn_size = 0;
-  htrenc = mhd_request_get_value_st (&(c->rq),
-                                     MHD_VK_HEADER,
-                                     MHD_HTTP_HEADER_TRANSFER_ENCODING);
 
-  if (NULL != htrenc)
+  has_host = false;
+  has_trenc = false;
+  has_cntnlen = false;
+  has_keepalive = true;
+
+  for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields);
+       NULL != f;
+       f = mhd_DLINKEDL_GET_NEXT (f, fields))
   {
-    if (! mhd_str_equal_caseless_n_st ("chunked", htrenc->cstr, htrenc->len))
+    if (MHD_VK_HEADER != f->field.kind)
+      continue;
+
+    /* "Host:" */
+    if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_HOST,
+                                     f->field.nv.name.cstr,
+                                     f->field.nv.name.len))
     {
-      mhd_RESPOND_WITH_ERROR_STATIC (c,
-                                     MHD_HTTP_STATUS_BAD_REQUEST,
-                                     ERR_RSP_UNSUPPORTED_TR_ENCODING);
-      return;
+      if ((has_host)
+          && (-3 < c->daemon->req_cfg.strictnees))
+      {
+        mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_SEVERAL, \
+                     "Received request with more than one 'Host' header.");
+        mhd_RESPOND_WITH_ERROR_STATIC (c,
+                                       MHD_HTTP_STATUS_BAD_REQUEST,
+                                       ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS);
+        return;
+      }
+      has_host = true;
+      continue;
     }
 
-    c->rq.have_chunked_upload = true;
-    c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN;
+#ifdef COOKIE_SUPPORT
+    /* "Cookie:" */
+    if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_COOKIE,
+                                     f->field.nv.name.cstr,
+                                     f->field.nv.name.len))
+    {
+      if (MHD_PARSE_COOKIE_NO_MEMORY ==
+          parse_cookie_header (c,
+                               &(f->field.nv.value)))
+      {
+        handle_req_cookie_no_space (c);
+        return;
+      }
+      continue;
+    }
+#endif /* COOKIE_SUPPORT */
 
-    if (MHD_HTTP_VERSION_1_1 != c->rq.http_ver)
-      c->keepalive = MHD_CONN_MUST_CLOSE; /* Framing could in incorrect */
-  }
+    /* "Content-Length:" */
+    if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH,
+                                     f->field.nv.name.cstr,
+                                     f->field.nv.name.len))
+    {
+      size_t num_digits;
+      uint_fast64_t cntn_size;
+
+      num_digits = mhd_str_to_uint64_n (f->field.nv.name.cstr,
+                                        f->field.nv.name.len,
+                                        &cntn_size);
+      if (((0 == num_digits) &&
+           (0 != f->field.nv.name.len) &&
+           ('9' >= f->field.nv.name.cstr[0])
+           && ('0' <= f->field.nv.name.cstr[0]))
+          || (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size))
+      {
+        mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_TOO_LARGE, \
+                     "Too large value of 'Content-Length' header. " \
+                     "Closing connection.");
+        mhd_RESPOND_WITH_ERROR_STATIC (c, \
+                                       MHD_HTTP_STATUS_CONTENT_TOO_LARGE, \
+                                       ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE);
+        return;
+      }
+      else if ((f->field.nv.name.len != num_digits) ||
+               (0 == num_digits))
+      {
+        mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_MALFORMED, \
+                     "Failed to parse 'Content-Length' header. " \
+                     "Closing connection.");
+        mhd_RESPOND_WITH_ERROR_STATIC (c, \
+                                       MHD_HTTP_STATUS_BAD_REQUEST, \
+                                       ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED);
+        return;
+      }
 
-  hcntnlen = mhd_request_get_value_st (&(c->rq),
-                                       MHD_VK_HEADER,
-                                       MHD_HTTP_HEADER_CONTENT_LENGTH);
+      if (has_cntnlen)
+      {
+        bool send_err;
+        send_err = false;
+        if (c->rq.cntn.cntn_size == cntn_size)
+        {
+          if (0 < c->daemon->req_cfg.strictnees)
+          {
+            mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_SAME, \
+                         "Received request with more than one " \
+                         "'Content-Length' header with the same value.");
+            send_err = true;
+          }
+        }
+        else
+        {
+          mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_DIFFERENT, \
+                       "Received request with more than one " \
+                       "'Content-Length' header with conflicting values.");
+          send_err = true;
+        }
 
-  if ((NULL != hcntnlen) && (NULL != htrenc))
-  {
-    /* TODO: add individual settings */
-    if (1 <= c->daemon->req_cfg.strictnees)
+        if (send_err)
+        {
+          mhd_RESPOND_WITH_ERROR_STATIC ( \
+            c, \
+            MHD_HTTP_STATUS_BAD_REQUEST, \
+            ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL);
+          return;
+        }
+      }
+      mhd_assert ((0 == c->rq.cntn.cntn_size) || \
+                  (c->rq.cntn.cntn_size == cntn_size));
+      c->rq.cntn.cntn_size = cntn_size;
+      has_cntnlen = true;
+      continue;
+    }
+
+    /* "Connection:" */
+    if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONNECTION,
+                                     f->field.nv.name.cstr,
+                                     f->field.nv.name.len))
     {
-      mhd_RESPOND_WITH_ERROR_STATIC (c,
-                                     MHD_HTTP_STATUS_BAD_REQUEST,
-                                     ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING);
-      return;
+      if (mhd_str_has_token_caseless (f->field.nv.name.cstr,
+                                      "close",
+                                      mhd_SSTR_LEN ("close")))
+      {
+        mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
+        c->conn_reuse = mhd_CONN_MUST_CLOSE;
+      }
+      else if ((MHD_HTTP_VERSION_1_0 == c->rq.http_ver)
+               && (mhd_CONN_MUST_CLOSE != c->conn_reuse))
+      {
+        if (mhd_str_has_token_caseless (f->field.nv.name.cstr,
+                                        "keep-alive",
+                                        mhd_SSTR_LEN ("keep-alive")))
+          has_keepalive = true;
+      }
+
+      continue;
     }
-    else
+
+    /* "Transfer-Encoding:" */
+    if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_TRANSFER_ENCODING,
+                                     f->field.nv.name.cstr,
+                                     f->field.nv.name.len))
     {
-      /* Must close connection after reply to prevent potential attack */
-      c->keepalive = MHD_CONN_MUST_CLOSE;
-      MHD_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_AND_TR_ENC, \
-                   "The 'Content-Length' request header is ignored " \
-                   "as chunked Transfer-Encoding is used " \
-                   "for this request.");
+      if (mhd_str_equal_caseless_n_st ("chunked",
+                                       f->field.nv.value.cstr,
+                                       f->field.nv.value.len))
+      {
+        c->rq.have_chunked_upload = true;
+        c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN;
+      }
+      else
+      {
+        mhd_LOG_MSG (c->daemon, MHD_SC_CHUNKED_ENCODING_UNSUPPORTED, \
+                     "The 'Transfer-Encoding' used in request is " \
+                     "unsupported or invalid.");
+        mhd_RESPOND_WITH_ERROR_STATIC (c,
+                                       MHD_HTTP_STATUS_BAD_REQUEST,
+                                       ERR_RSP_UNSUPPORTED_TR_ENCODING);
+        return;
+      }
+      has_trenc = true;
+      continue;
     }
   }
-  else if (NULL != hcntnlen)
-  {
-    size_t num_digits;
 
-    num_digits = mhd_str_to_uint64_n (hcntnlen->cstr,
-                                      hcntnlen->len,
-                                      &c->rq.cntn.cntn_size);
-
-    if (((0 == num_digits) &&
-         (0 != hcntnlen->len) &&
-         ('0' <= hcntnlen->cstr[0]) && ('9' >= hcntnlen->cstr[0]))
-        || (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size))
+  if (has_trenc && has_cntnlen)
+  {
+    if (0 < c->daemon->req_cfg.strictnees)
     {
-      c->rq.cntn.cntn_size = 0;
-      MHD_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_TOO_LARGE, \
-                   "Too large value of 'Content-Length' header. " \
-                   "Closing connection.");
-      mhd_RESPOND_WITH_ERROR_STATIC (c,
-                                     MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
-                                     ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE);
+      mhd_RESPOND_WITH_ERROR_STATIC ( \
+        c, \
+        MHD_HTTP_STATUS_BAD_REQUEST, \
+        ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING);
       return;
     }
-    else if ((hcntnlen->len != num_digits) ||
-             (0 == num_digits))
+    /* Must close connection after reply to prevent potential attack */
+    c->conn_reuse = mhd_CONN_MUST_CLOSE;
+    c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN;
+    mhd_assert (c->rq.have_chunked_upload);
+    mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_AND_TR_ENC, \
+                 "The 'Content-Length' request header is ignored " \
+                 "as chunked 'Transfer-Encoding' is used " \
+                 "for this request.");
+  }
+
+  if (MHD_HTTP_VERSION_1_1 <= c->rq.http_ver)
+  {
+    if ((! has_host) &&
+        (-3 < c->daemon->req_cfg.strictnees))
     {
-      c->rq.cntn.cntn_size = 0;
-      MHD_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_MALFORMED, \
-                   "Failed to parse 'Content-Length' header. " \
-                   "Closing connection.");
+      mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MISSING, \
+                   "Received HTTP/1.1 request without 'Host' header.");
       mhd_RESPOND_WITH_ERROR_STATIC (c,
                                      MHD_HTTP_STATUS_BAD_REQUEST,
-                                     ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED);
+                                     ERR_RSP_REQUEST_LACKS_HOST);
       return;
     }
   }
+  else
+  {
+    if (! has_keepalive)
+      c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Do not re-use HTTP/1.0 connection by default */
+    if (has_trenc)
+      c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Framing could be incorrect */
+  }
+
   c->state = MHD_CONNECTION_HEADERS_PROCESSED;
   return;
 }
@@ -2741,7 +2876,7 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
   if (((NULL != a) && (&(c->rq.app_act.head_act) != a)) ||
       ! mhd_ACTION_IS_VALID (c->rq.app_act.head_act.act))
   {
-    MHD_LOG_MSG (d, MHD_SC_ACTION_INVALID, \
+    mhd_LOG_MSG (d, MHD_SC_ACTION_INVALID, \
                  "Provided action is not a correct action generated " \
                  "for the current request.");
     a = NULL;
@@ -2807,7 +2942,7 @@ process_upload_action (struct MHD_Connection *restrict c,
       (final &&
        (mhd_UPLOAD_ACTION_CONTINUE == c->rq.app_act.upl_act.act)))
   {
-    MHD_LOG_MSG (c->daemon, MHD_SC_UPLOAD_ACTION_INVALID, \
+    mhd_LOG_MSG (c->daemon, MHD_SC_UPLOAD_ACTION_INVALID, \
                  "Provided action is not a correct action generated " \
                  "for the current request.");
     act = NULL;

+ 40 - 65
src/mhd2/stream_process_states.c

@@ -35,9 +35,18 @@
 #include "mhd_str_macros.h"
 
 #include "mhd_connection.h"
+#include "mhd_response.h"
 #include "stream_process_states.h"
 #include "stream_funcs.h"
 #include "stream_process_request.h"
+#include "stream_process_reply.h"
+
+static MHD_FN_PAR_NONNULL_ALL_ bool
+update_active_state (struct MHD_Connection *restrict c)
+{
+  mhd_assert (0); (void) c;
+  return true;
+}
 
 
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
@@ -142,6 +151,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
       c->state = MHD_CONNECTION_FOOTERS_RECEIVING;
       continue;
     case MHD_CONNECTION_FOOTERS_RECEIVING:
+      mhd_assert (c->rq.have_chunked_upload);
       if (mhd_stream_get_request_headers (c, true))
       {
         mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING < c->state);
@@ -152,6 +162,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
       mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING == c->state);
       break;
     case MHD_CONNECTION_FOOTERS_RECEIVED:
+      mhd_assert (c->rq.have_chunked_upload);
       c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
       continue;
     case MHD_CONNECTION_FULL_REQ_RECEIVED:
@@ -165,28 +176,22 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
       if (mhd_stream_process_req_recv_finished (c))
         continue;
       break;
+    // TODO: add stage for setup and full request buffers cleanup
     case MHD_CONNECTION_START_REPLY:
       mhd_assert (NULL != c->rp.response);
       mhd_stream_switch_from_recv_to_send (c);
-      if (MHD_NO == build_header_response (c))
-      {
-        /* oops - close! */
-        CONNECTION_CLOSE_ERROR (c,
-                                _ ("Closing connection (failed to create "
-                                   "response header).\n"));
-        continue;
-      }
-      c->state = MHD_CONNECTION_HEADERS_SENDING;
+      if (! mhd_stream_build_header_response (c))
+        break;
+      mhd_assert (MHD_CONNECTION_START_REPLY != c->state);
       break;
-
     case MHD_CONNECTION_HEADERS_SENDING:
-      /* no default action */
+      /* no default action, wait for sending all the headers */
       break;
     case MHD_CONNECTION_HEADERS_SENT:
-      mhd_assert (0 && "Not implemented yet");
 #if 0 // def UPGRADE_SUPPORT // TODO: upgrade support
       if (NULL != c->rp.response->upgrade_handler)
       {
+        mhd_assert (0 && "Not implemented yet");
         c->state = MHD_CONNECTION_UPGRADE;
         /* This connection is "upgraded".  Pass socket to application. */
         if (MHD_NO ==
@@ -215,36 +220,25 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
         if (c->rp.props.chunked)
           c->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
         else
-          c->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+          c->state = MHD_CONNECTION_UNCHUNKED_BODY_UNREADY;
       }
       else
         c->state = MHD_CONNECTION_FULL_REPLY_SENT;
       continue;
-    case MHD_CONNECTION_NORMAL_BODY_READY:
+    case MHD_CONNECTION_UNCHUNKED_BODY_READY:
       mhd_assert (c->rp.props.send_reply_body);
       mhd_assert (! c->rp.props.chunked);
-      /* nothing to do here */
+      /* nothing to do here, send the data */
       break;
-    case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+    case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY:
       mhd_assert (c->rp.props.send_reply_body);
       mhd_assert (! c->rp.props.chunked);
       if (0 == c->rp.response->cntn_size)
-      {
-        if (c->rp.props.chunked)
-          c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
-        else
-          c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+      { /* a shortcut */
+        c->state = MHD_CONNECTION_FULL_REPLY_SENT;
         continue;
       }
-      if (MHD_NO != try_ready_normal_body (c))
-      {
-        c->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 */
+      mhd_stream_prep_unchunked_body (c);
       break;
     case MHD_CONNECTION_CHUNKED_BODY_READY:
       mhd_assert (c->rp.props.send_reply_body);
@@ -261,17 +255,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
         c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
         continue;
       }
-      if (1)
-      { /* pseudo-branch for local variables scope */
-        bool finished;
-        if (MHD_NO != try_ready_chunked_body (c, &finished))
-        {
-          c->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
-                     MHD_CONNECTION_CHUNKED_BODY_READY;
-          continue;
-        }
-        /* mutex was already unlocked by try_ready_chunked_body */
-      }
+      mhd_stream_prep_chunked_body (c);
       break;
     case MHD_CONNECTION_CHUNKED_BODY_SENT:
       mhd_assert (c->rp.props.send_reply_body);
@@ -279,18 +263,8 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
       mhd_assert (c->write_buffer_send_offset <= \
                   c->write_buffer_append_offset);
 
-      if (MHD_NO == build_connection_chunked_response_footer (c))
-      {
-        /* oops - close! */
-        CONNECTION_CLOSE_ERROR (c,
-                                _ ("Closing connection (failed to create " \
-                                   "response footer)."));
-        continue;
-      }
-      mhd_assert (c->write_buffer_send_offset < \
-                  c->write_buffer_append_offset);
-      c->state = MHD_CONNECTION_FOOTERS_SENDING;
-      continue;
+      mhd_stream_prep_chunked_footer (c);
+      break;
     case MHD_CONNECTION_FOOTERS_SENDING:
       mhd_assert (c->rp.props.send_reply_body);
       mhd_assert (c->rp.props.chunked);
@@ -299,20 +273,22 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
     case MHD_CONNECTION_FULL_REPLY_SENT:
       // FIXME: support MHD_HTTP_STATUS_PROCESSING ?
       /* Reset connection after complete reply */
-      connection_reset (c,
-                        MHD_CONN_USE_KEEPALIVE == c->keepalive &&
-                        ! c->read_closed &&
-                        ! c->discard_request);
+      mhd_stream_finish_req_serving ( \
+        c,
+        mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse
+        && ! c->discard_request
+        && ! c->sk_rmt_shut_wr);
       continue;
     case MHD_CONNECTION_CLOSED:
-      cleanup_connection (c);
-      return MHD_NO;
+      mhd_assert (0 && "Should be unreachable");
+      MHD_UNREACHABLE_;
+      return false;
 #if 0 // def UPGRADE_SUPPORT
     case MHD_CONNECTION_UPGRADE:
       return MHD_YES;     /* keep open */
 #endif /* UPGRADE_SUPPORT */
     default:
-      mhd_assert (0);
+      mhd_assert (0 && "Impossible value");
       break;
     }
     break;
@@ -323,13 +299,12 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
     // TODO: process
   }
 
-  if (connection_check_timedout (c)) // TODO: centralise timeout checks
+  if (mhd_stream_check_timedout (c)) // TODO: centralise timeout checks
   {
-    MHD_connection_close_ (c,
-                           MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
-    return MHD_YES;
+    mhd_conn_pre_close_timedout (c);
+    return false;
   }
-  mhd_conn_update_active_state (c);
+  update_active_state (c);
   /* MHD_connection_update_event_loop_info (c);*/
 
   return true;