ソースを参照

GnuTLS communication: the last part

Evgeny Grin (Karlson2k) 1 年間 前
コミット
cee65d14e2

+ 5 - 0
src/include/microhttpd2.h

@@ -1291,6 +1291,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_TLS_CONNECTION_INIT_FAILED = 51201
   ,
+  /**
+   * Failed to perform TLS handshake
+   */
+  MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED = 51220
+  ,
   /**
    * Something wrong in the internal MHD logic.
    * This error should be never returned if MHD works as expected.

+ 5 - 0
src/include/microhttpd2_preamble.h.in

@@ -1291,6 +1291,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_TLS_CONNECTION_INIT_FAILED = 51201
   ,
+  /**
+   * Failed to perform TLS handshake
+   */
+  MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED = 51220
+  ,
   /**
    * Something wrong in the internal MHD logic.
    * This error should be never returned if MHD works as expected.

+ 3 - 2
src/mhd2/Makefile.am

@@ -108,8 +108,9 @@ upgrade_files = \
 
 tls_common_files = \
   mhd_tls_choice.h \
-  mhd_tls_funcs.c           mhd_tls_funcs.h \
-  tls_dh_params.h
+  mhd_tls_funcs.c           mhd_tls_funcs.h           mhd_tls_enums.h \
+  tls_dh_params.h \
+  conn_tls_check.c          conn_tls_check.h
 
 tls_gnu_files = \
   tls_gnu_tls_lib.h         tls_gnu_daemon_data.h     tls_gnu_conn_data.h \

+ 31 - 82
src/mhd2/conn_data_process.c

@@ -46,26 +46,51 @@
 #include "conn_data_send.h"
 #include "stream_process_states.h"
 
+#ifdef MHD_ENABLE_HTTPS
+#  include "conn_tls_check.h"
+#endif /* MHD_ENABLE_HTTPS */
+
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
 mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
 {
+  bool send_ready_state_known;
+  bool has_sock_err;
+  bool data_processed;
+
+#ifdef MHD_ENABLE_HTTPS
+  if (mhd_C_HAS_TLS (c))
+  {
+    switch (mhd_conn_tls_check (c))
+    {
+    case mhd_CONN_TLS_CHECK_OK:
+      break;        /* Process HTTP data */
+    case mhd_CONN_TLS_CHECK_HANDSHAKING:
+      return true;  /* TLS is not yet ready */
+    case mhd_CONN_TLS_CHECK_BROKEN:
+      return false; /* Connection is broken */
+    default:
+      mhd_assert (0 && "Impossible value");
+      MHD_UNREACHABLE_;
+    }
+  }
+#endif /* MHD_ENABLE_HTTPS */
+
   /* The "send-ready" state is known if system polling call is edge-triggered
      (it always checks for both send- and recv-ready) or if connection needs
      sending (therefore "send-ready" was explicitly checked by sockets polling
      call). */
-  const bool send_ready_state_known =
+  send_ready_state_known =
     ((mhd_D_IS_USING_EDGE_TRIG (c->daemon)) ||
      (0 != (MHD_EVENT_LOOP_INFO_SEND & c->event_loop_info)));
-  const bool has_sock_err =
+  has_sock_err =
     (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk.ready));
-  bool data_processed;
-
   data_processed = false;
 
   if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
   {
     bool use_recv;
-    use_recv = (0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk.ready));
+    use_recv = (0 != (mhd_SOCKET_NET_STATE_RECV_READY
+                      & (c->sk.ready | mhd_C_HAS_TLS_DATA_IN (c))));
     use_recv = use_recv ||
                (has_sock_err && c->sk.props.is_nonblck);
 
@@ -85,7 +110,7 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
      * + connection is ready for sending or
      * + just formed send data, connection send ready status is not known and
      *   connection socket is non-blocking
-     * + detected network error on the connection, to check to the error */
+     * + detected network error on the connection, to check for the error */
     /* Assuming that after finishing receiving phase, connection send system
        buffers should have some space as sending was performed before receiving
        or has not been performed yet. */
@@ -107,80 +132,4 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
   if (! data_processed)
     return mhd_conn_process_data (c);
   return true;
-
-#if 0 // TODO: re-implement fasttrack as a single unified buffer sending
-  if (! force_close)
-  {
-    /* No need to check value of 'ret' here as closed connection
-     * cannot be in MHD_EVENT_LOOP_INFO_SEND state. */
-    if ( (MHD_EVENT_LOOP_INFO_SEND == c->event_loop_info) &&
-         write_ready)
-    {
-      MHD_connection_handle_write (c);
-      ret = MHD_connection_handle_idle (c);
-      states_info_processed = true;
-    }
-  }
-  else
-  {
-    MHD_connection_close_ (c,
-                           MHD_REQUEST_TERMINATED_WITH_ERROR);
-    return MHD_connection_handle_idle (c);
-  }
-
-  if (! states_info_processed)
-  {   /* Connection is not read or write ready, but external conditions
-       * may be changed and need to be processed. */
-    ret = MHD_connection_handle_idle (c);
-  }
-  /* Fast track for fast connections. */
-  /* If full request was read by single read_handler() invocation
-     and headers were completely prepared by single MHD_connection_handle_idle()
-     then try not to wait for next sockets polling and send response
-     immediately.
-     As writeability of socket was not checked and it may have
-     some data pending in system buffers, use this optimization
-     only for non-blocking sockets. */
-  /* No need to check 'ret' as connection is always in
-   * mhd_HTTP_STAGE_CLOSED state if 'ret' is equal 'MHD_NO'. */
-  else if (on_fasttrack && c->sk.props.is_nonblck)
-  {
-    if (mhd_HTTP_STAGE_HEADERS_SENDING == c->stage)
-    {
-      MHD_connection_handle_write (c);
-      /* Always call 'MHD_connection_handle_idle()' after each read/write. */
-      ret = MHD_connection_handle_idle (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_HTTP_STAGE_UNCHUNKED_BODY_READY == c->stage) ||
-        (mhd_HTTP_STAGE_CHUNKED_BODY_READY == c->stage))
-    {
-      MHD_connection_handle_write (c);
-      ret = MHD_connection_handle_idle (c);
-    }
-  }
-
-  /* All connection's data and states are processed for this turn.
-   * If connection already has more data to be processed - use
-   * zero timeout for next select()/poll(). */
-  /* Thread-per-connection do not need global zero timeout as
-   * connections are processed individually. */
-  /* Note: no need to check for read buffer availability for
-   * TLS read-ready connection in 'read info' state as connection
-   * without space in read buffer will be marked as 'info block'. */
-  if ( (! c->daemon->data_already_pending) &&
-       (! mhd_D_HAS_THR_PER_CONN (c->daemon)) )
-  {
-    if (0 != (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info))
-      c->daemon->data_already_pending = true;
-#ifdef HTTPS_SUPPORT
-    else if ( (c->tls_read_ready) &&
-              (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info)) )
-      c->daemon->data_already_pending = true;
-#endif /* HTTPS_SUPPORT */
-  }
-  return ret;
-#endif
 }

+ 0 - 2
src/mhd2/conn_data_recv.c

@@ -57,8 +57,6 @@ mhd_conn_data_recv (struct MHD_Connection *restrict c,
   mhd_assert ((0 == (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \
               has_err);
 
-  // TODO: TLS support: handshake/transport layer
-
   buf = c->read_buffer + c->read_buffer_offset;
   buf_size = c->read_buffer_size - c->read_buffer_offset;
 

+ 0 - 4
src/mhd2/conn_data_send.c

@@ -83,10 +83,6 @@ mhd_conn_data_send (struct MHD_Connection *restrict c)
 
   // TODO: assert check suspended
 
-#ifdef HTTPS_SUPPORT
-  // TODO: TLS support, handshake
-#endif /* HTTPS_SUPPORT */
-
   // TODO: MOVE out STATES PROCESSING
 
   res = mhd_SOCKET_ERR_INTERNAL;

+ 2 - 1
src/mhd2/conn_mark_ready.h

@@ -108,7 +108,8 @@ mhd_conn_mark_ready_update3 (struct MHD_Connection *restrict c,
 {
   if (force_ready ||
       (0 !=
-       (((unsigned int) c->sk.ready) & ((unsigned int) c->event_loop_info)
+       ((((unsigned int) c->sk.ready) | mhd_C_HAS_TLS_DATA_IN (c))
+        & ((unsigned int) c->event_loop_info)
         & (MHD_EVENT_LOOP_INFO_RECV | MHD_EVENT_LOOP_INFO_SEND))))
     mhd_conn_mark_ready (c, d);
   else

+ 130 - 0
src/mhd2/conn_tls_check.c

@@ -0,0 +1,130 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  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/conn_tls_handshake.c
+ * @brief  The implementation of connection TLS handling functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "conn_tls_check.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+
+#include "mhd_socket_error_funcs.h"
+#include "daemon_logger.h"
+
+#include "mhd_tls_funcs.h"
+
+#include "conn_mark_ready.h"
+#include "stream_funcs.h"
+#include "stream_process_states.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnTlsCheckResult
+mhd_conn_tls_check (struct MHD_Connection *restrict c)
+{
+  mhd_assert (mhd_C_HAS_TLS (c));
+  mhd_assert (mhd_D_HAS_TLS (c->daemon));
+  mhd_assert ((mhd_CONN_STATE_TLS_HANDSHAKE_RECV == c->conn_state) || \
+              (mhd_CONN_STATE_TLS_HANDSHAKE_SEND == c->conn_state) || \
+              (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state));
+
+  if (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state)
+    return mhd_CONN_TLS_CHECK_OK; /* TLS is already connected */
+
+  if (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk.ready))
+  {
+    /* Some socket error has been detected. Do not try to handshake. */
+    if (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err)
+      c->sk.state.discnt_err = mhd_socket_error_get_from_socket (c->sk.fd);
+    mhd_conn_start_closing_skt_err (c);
+    return mhd_CONN_TLS_CHECK_BROKEN;
+  }
+  /* Check whether the socket is ready for the required send/recv operation */
+  if (0 == (((mhd_CONN_FLAG_RECV | mhd_CONN_FLAG_SEND)
+             & ((unsigned int) c->conn_state)
+             & ((unsigned int) c->sk.ready))))
+    return mhd_CONN_TLS_CHECK_HANDSHAKING;
+
+  switch (mhd_tls_conn_handshake (c->tls))
+  {
+  case mhd_TLS_PROCED_SUCCESS:
+    c->conn_state = mhd_CONN_STATE_TLS_CONNECTED;
+    if (! c->sk.props.is_nonblck)
+    {
+      /* The socket is blocking,
+         probably all available data has been processed already. */
+      c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' and 'send-ready' */
+                    (((unsigned int) c->sk.ready)
+                     & (~(enum mhd_SocketNetState)
+                        mhd_SOCKET_NET_STATE_SEND_READY)
+                     & (~(enum mhd_SocketNetState)
+                        mhd_SOCKET_NET_STATE_RECV_READY));
+    }
+    /* TLS is connected now, set event loop state based on HTTP protocol.
+       Some early application-level data could be processing in this round. */
+    mhd_conn_event_loop_state_update (c);
+
+    return mhd_CONN_TLS_CHECK_OK;
+    break;
+  case mhd_TLS_PROCED_RECV_MORE_NEEDED:
+    c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */
+                  (((unsigned int) c->sk.ready)
+                   & (~(enum mhd_SocketNetState)
+                      mhd_SOCKET_NET_STATE_RECV_READY));
+  /* Intentional fall-through */
+  case mhd_TLS_PROCED_RECV_INTERRUPTED:
+    c->conn_state = mhd_CONN_STATE_TLS_HANDSHAKE_RECV;
+    c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
+    break;
+  case mhd_TLS_PROCED_SEND_MORE_NEEDED:
+    c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+                  (((unsigned int) c->sk.ready)
+                   & (~(enum mhd_SocketNetState)
+                      mhd_SOCKET_NET_STATE_SEND_READY));
+  /* Intentional fall-through */
+  case mhd_TLS_PROCED_SEND_INTERRUPTED:
+    c->conn_state = mhd_CONN_STATE_TLS_HANDSHAKE_SEND;
+    c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
+    break;
+  case mhd_TLS_PROCED_FAILED:
+    c->conn_state = mhd_CONN_STATE_TLS_FAILED;
+    mhd_LOG_MSG (c->daemon, \
+                 MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED, \
+                 "Failed to perform TLS handshake on the new connection");
+    c->sk.state.discnt_err = mhd_SOCKET_ERR_TLS;
+    mhd_conn_start_closing_skt_err (c);
+    return mhd_CONN_TLS_CHECK_BROKEN;
+    break;
+  default:
+    mhd_assert (0 && "Should be unreachable");
+    MHD_UNREACHABLE_;
+    return mhd_CONN_TLS_CHECK_BROKEN;
+  }
+
+  mhd_conn_mark_ready_update (c);
+  return mhd_CONN_TLS_CHECK_HANDSHAKING;
+}

+ 71 - 0
src/mhd2/conn_tls_check.h

@@ -0,0 +1,71 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  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/conn_tls_check.h
+ * @brief  The declarations of connection TLS handling functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_CONN_TLS_CHECK_H
+#define MHD_CONN_TLS_CHECK_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * The results of connection TLS checking
+ */
+enum mhd_ConnTlsCheckResult
+{
+  /**
+   * The TLS layer is connected, the communication over TLS can be performed
+   */
+  mhd_CONN_TLS_CHECK_OK = 0
+  ,
+  /**
+   * The TLS layer connection is in progress.
+   * Communication over TLS is not possible yet.
+   */
+  mhd_CONN_TLS_CHECK_HANDSHAKING
+  ,
+  /**
+   * The connection is broken and must be closed
+   */
+  mhd_CONN_TLS_CHECK_BROKEN
+};
+
+
+/**
+ * Check connection TLS status, perform TLS (re-)handshake if necessary,
+ * update connection's recv()/send() event loop state and connection active
+ * state if network operation has been performed.
+ * @param c the connection to process
+ * @return #mhd_CONN_TLS_CHECK_OK if the connection can be used,
+ *         other enum mhd_ConnTlsCheckResult values otherwise
+ */
+MHD_INTERNAL enum mhd_ConnTlsCheckResult
+mhd_conn_tls_check (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_CONN_TLS_CHECK_H */

+ 4 - 1
src/mhd2/daemon_add_conn.c

@@ -249,10 +249,13 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon,
                        "the new connection");
           ret = MHD_SC_TLS_CONNECTION_INIT_FAILED;
         }
-#ifndef NDEBUG
         else
+        {
+          c->conn_state = mhd_CONN_STATE_TLS_HANDSHAKE_RECV;
+#ifndef NDEBUG
           c->dbg.tls_inited = true;
 #endif
+        }
       }
 
       if (MHD_SC_OK == ret)

+ 129 - 3
src/mhd2/mhd_connection.h

@@ -79,6 +79,106 @@
 
 struct MHD_Connection; /* forward declaration */
 
+#define mhd_CONN_FLAG_RECV      (1u << 0)
+#define mhd_CONN_FLAG_SEND      (1u << 1)
+#define mhd_CONN_FLAG_TLS       (1u << 2)
+#define mhd_CONN_FLAG_HANDSHAKE (1u << 3)
+#define mhd_CONN_FLAG_CLOSING   (1u << 4)
+#define mhd_CONN_FLAG_ERROR     (1u << 6)
+#define mhd_CONN_FLAG_CLOSED    (1u << 7)
+
+/**
+ * The states of connection TLS layer
+ * The bits (1 << 0) | (1 << 1) in enum values match the same bits in
+ * enum MHD_ConnectionEventLoopInfo and in enum mhd_SocketNetState values
+ */
+enum MHD_FIXED_ENUM_ mhd_ConnState
+{
+  /**
+   * TLS not started / plain TCP communication
+   */
+  mhd_CONN_STATE_TCP_CONNECTED = 0
+  ,
+  /**
+   * TLS handshake in progress, need to receive the data
+   */
+  mhd_CONN_STATE_TLS_HANDSHAKE_RECV =
+    mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_HANDSHAKE | mhd_CONN_FLAG_RECV
+  ,
+  /**
+   * TLS handshake in progress, need to send the data
+   */
+  mhd_CONN_STATE_TLS_HANDSHAKE_SEND =
+    mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_HANDSHAKE | mhd_CONN_FLAG_SEND
+  ,
+  /**
+   * TLS connection established, HTTP communication is performing
+   */
+  mhd_CONN_STATE_TLS_CONNECTED = mhd_CONN_FLAG_TLS
+  ,
+  /**
+   * Sending TLS message for shutting down TLS communication on the MHD side
+   */
+  mhd_CONN_STATE_TLS_SHUT_WR_SENDING =
+    mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_CLOSING | mhd_CONN_FLAG_SEND
+  ,
+  /**
+   * Waiting to receive message from remote for shutting down TLS communication
+   */
+  mhd_CONN_STATE_TLS_LINGERING =
+    mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_CLOSING | mhd_CONN_FLAG_RECV
+  ,
+  /**
+   * TLS communication gracefully closed.
+   * This state should be avoided. Use #mhd_CONN_STATE_TCP_CONNECTED or
+   * #mhd_CONN_STATE_CLOSED.
+   */
+  mhd_CONN_STATE_TLS_CLOSED = mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_CLOSED
+  ,
+  /**
+   * TLS communication broken
+   */
+  mhd_CONN_STATE_TLS_FAILED = mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_ERROR
+#if 0 // TODO: Extend to TCP states
+  ,
+  /**
+   * Setting TCP shutdown WR
+   */
+  mhd_CONN_STATE_TCP_SHUT_WR_SENDING =
+    mhd_CONN_FLAG_CLOSING | mhd_CONN_FLAG_SEND
+  ,
+  /**
+   * Waiting for EOF from the remote side
+   */
+  mhd_CONN_STATE_TCP_LINGERING =
+    mhd_CONN_FLAG_CLOSING | mhd_CONN_FLAG_RECV
+#endif
+  ,
+  /**
+   * TCP communication closed
+   */
+  mhd_CONN_STATE_CLOSED = mhd_CONN_FLAG_CLOSED
+};
+
+#ifdef MHD_ENABLE_HTTPS
+/**
+ * The status of TLS buffer for incoming (receive) data
+ */
+enum mhd_TlsBufDataIn
+{
+  /**
+   * No data in pending in the TLS buffer
+   */
+  mhd_TLS_BUF_NO_DATA = 0
+  ,
+  /**
+   * The incoming data in already pending in the TLS buffer
+   */
+  mhd_TLS_BUF_HAS_DATA_IN = mhd_SOCKET_NET_STATE_RECV_READY
+};
+#endif /* MHD_ENABLE_HTTPS */
+
+
 /**
  * What is this connection waiting for?
  */
@@ -413,7 +513,17 @@ struct MHD_Connection
    * deallocated separately.
    */
   struct mhd_TlsConnData *tls;
-#endif
+
+  /**
+   * The state of the communication layer
+   */
+  enum mhd_ConnState conn_state;
+
+  /**
+   * Status of TLS buffer for the incoming data
+   */
+  enum mhd_TlsBufDataIn tls_has_data_in;
+#endif /* MHD_ENABLE_HTTPS */
 
   /**
    * 'true' if connection is in 'process ready' list,
@@ -616,11 +726,27 @@ struct MHD_Connection
  * Returns non-zero if connection has TLS enabled or zero otherwise
  */
 #  define mhd_C_HAS_TLS(c) (((c)->tls) ? (! 0) : (0))
-#else
+#else  /* ! MHD_ENABLE_HTTPS */
 /**
  * Returns non-zero if connection has TLS enabled or zero otherwise
  */
 #  define mhd_C_HAS_TLS(c) (0)
-#endif
+#endif /* ! MHD_ENABLE_HTTPS */
+
+
+#ifdef MHD_ENABLE_HTTPS
+/**
+ * Returns #mhd_SOCKET_NET_STATE_RECV_READY if connection has incoming data
+ * pending in TLS buffers
+ */
+#  define mhd_C_HAS_TLS_DATA_IN(c) \
+        (((c)->tls) ? ((unsigned int) ((c)->tls_has_data_in)) : (0u))
+#else  /* ! MHD_ENABLE_HTTPS */
+/**
+ * Returns #mhd_SOCKET_NET_STATE_RECV_READY if connection has incoming data
+ * pending in TLS buffers
+ */
+#  define mhd_C_HAS_TLS_DATA_IN(c) (0)
+#endif /* ! MHD_ENABLE_HTTPS */
 
 #endif /* ! MHD_CONNECTION_H */

+ 52 - 4
src/mhd2/mhd_recv.c

@@ -33,14 +33,19 @@
 #include "mhd_socket_type.h"
 #include "sys_sockets_headers.h"
 #include "mhd_sockets_macros.h"
-#include "mhd_socket_error_funcs.h"
 
 #include "mhd_limits.h"
 
+#include "mhd_assert.h"
+#include "mhd_socket_error_funcs.h"
+
+#ifdef MHD_ENABLE_HTTPS
+#  include "mhd_tls_funcs.h"
+#endif
 
 static MHD_FN_PAR_NONNULL_ALL_
 MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
-mhd_plain_recv (struct MHD_Connection *restrict c,
+mhd_recv_plain (struct MHD_Connection *restrict c,
                 size_t buf_size,
                 char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
                 size_t *restrict received)
@@ -76,6 +81,43 @@ mhd_plain_recv (struct MHD_Connection *restrict c,
 }
 
 
+#ifdef MHD_ENABLE_HTTPS
+
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+mhd_recv_tls (struct MHD_Connection *restrict c,
+              size_t buf_size,
+              char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+              size_t *restrict received)
+{
+  /* TLS connection */
+  enum mhd_SocketError res;
+
+  mhd_assert (mhd_C_HAS_TLS (c));
+  mhd_assert (0 != buf_size);
+
+  res = mhd_tls_conn_recv (c->tls,
+                           buf_size,
+                           buf,
+                           received);
+  c->tls_has_data_in = mhd_TLS_BUF_NO_DATA; /* Updated with the actual value below */
+
+  if (mhd_SOCKET_ERR_AGAIN == res)
+    c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */
+                  (((unsigned int) c->sk.ready)
+                   & (~(enum mhd_SocketNetState)
+                      mhd_SOCKET_NET_STATE_RECV_READY));
+  else if ((mhd_SOCKET_ERR_NO_ERROR == res) &&
+           (*received == buf_size) &&
+           mhd_tls_conn_has_data_in (c->tls))
+    c->tls_has_data_in = mhd_TLS_BUF_HAS_DATA_IN;
+
+  return res;
+}
+
+
+#endif /* MHD_ENABLE_HTTPS */
+
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
 MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
 mhd_recv (struct MHD_Connection *restrict c,
@@ -86,7 +128,13 @@ mhd_recv (struct MHD_Connection *restrict c,
   mhd_assert (MHD_INVALID_SOCKET != c->sk.fd);
   mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage);
 
-  // TODO: implement TLS support
+#ifdef MHD_ENABLE_HTTPS
+  if (mhd_C_HAS_TLS (c))
+    return mhd_recv_tls (c,
+                         buf_size,
+                         buf,
+                         received);
+#endif /* MHD_ENABLE_HTTPS */
 
-  return mhd_plain_recv (c, buf_size, buf, received);
+  return mhd_recv_plain (c, buf_size, buf, received);
 }

+ 78 - 36
src/mhd2/mhd_send.c

@@ -71,6 +71,10 @@
 
 #include "mhd_limits.h"
 
+#ifdef MHD_ENABLE_HTTPS
+#  include "mhd_tls_funcs.h"
+#endif
+
 #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \
   defined(MHD_SOCKETS_KIND_WINSOCK)
 #  define mhd_USE_VECT_SEND 1
@@ -751,7 +755,7 @@ post_send_setopt (struct MHD_Connection *connection,
 static MHD_FN_PAR_NONNULL_ALL_
 MHD_FN_PAR_IN_SIZE_ (3,2)
 MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
-mhd_plain_send (struct MHD_Connection *restrict c,
+mhd_send_plain (struct MHD_Connection *restrict c,
                 size_t buf_size,
                 const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
                 bool push_data,
@@ -761,6 +765,9 @@ mhd_plain_send (struct MHD_Connection *restrict c,
   ssize_t res;
   bool full_buf_sent;
 
+  mhd_assert (! mhd_C_HAS_TLS (c));
+  mhd_assert (! mhd_D_HAS_TLS (c->daemon));
+
   if (buf_size > MHD_SCKT_SEND_MAX_SIZE_)
   {
     buf_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */
@@ -816,6 +823,52 @@ mhd_plain_send (struct MHD_Connection *restrict c,
 }
 
 
+#ifdef MHD_ENABLE_HTTPS
+
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2)
+MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
+mhd_send_tls (struct MHD_Connection *restrict c,
+              size_t buf_size,
+              const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+              bool push_data,
+              size_t *restrict sent)
+{
+  /* TLS connection */
+  enum mhd_SocketError res;
+
+  mhd_assert (mhd_C_HAS_TLS (c));
+  mhd_assert (mhd_D_HAS_TLS (c->daemon));
+  mhd_assert (0 != buf_size);
+
+  pre_send_setopt (c, false, push_data);
+
+  res = mhd_tls_conn_send (c->tls,
+                           buf_size,
+                           buf,
+                           sent);
+
+  if (mhd_SOCKET_ERR_NO_ERROR != res)
+  {
+    if (mhd_SOCKET_ERR_AGAIN == res)
+      c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
+                    (((unsigned int) c->sk.ready)
+                     & (~(enum mhd_SocketNetState)
+                        mhd_SOCKET_NET_STATE_SEND_READY));
+    return res;
+  }
+
+  /* If there is a need to push the data from network buffers
+   * call post_send_setopt(). */
+  if (push_data && (buf_size == *sent))
+    post_send_setopt (c, false, true);
+
+  return mhd_SOCKET_ERR_NO_ERROR;
+}
+
+
+#endif /* MHD_ENABLE_HTTPS */
+
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
 MHD_FN_PAR_IN_SIZE_ (3,2)
 MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
@@ -825,28 +878,19 @@ mhd_send_data (struct MHD_Connection *restrict connection,
                bool push_data,
                size_t *restrict sent)
 {
-  const bool tls_conn = false; // TODO: TLS support
-
   mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd);
   mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage);
 
-  if (tls_conn)
-  {
-    enum mhd_SocketError ret;
-
-#ifdef HTTPS_SUPPORT
-    pre_send_setopt (connection,
-                     (! tls_conn),
-                     push_data);
-    ret = mhd_SOCKET_ERR_OTHER;
-    mhd_assert (0 && "Not implemented yet");
-#else  /* ! HTTPS_SUPPORT  */
-    ret = mhd_SOCKET_ERR_NOTCONN;
-#endif /* ! HTTPS_SUPPORT  */
-    return ret;
-  }
+#ifdef MHD_ENABLE_HTTPS
+  if (mhd_C_HAS_TLS (connection))
+    return mhd_send_tls (connection,
+                         buf_size,
+                         buf,
+                         push_data,
+                         sent);
+#endif /* MHD_ENABLE_HTTPS */
 
-  return mhd_plain_send (connection,
+  return mhd_send_plain (connection,
                          buf_size,
                          buf,
                          push_data,
@@ -885,9 +929,7 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
   bool no_vec; /* Is vector-send() disallowed? */
 
   no_vec = false;
-#ifdef HTTPS_SUPPORT
-  no_vec = no_vec || (false); // TODO: TLS support
-#endif /* HTTPS_SUPPORT */
+  no_vec = no_vec || (mhd_C_HAS_TLS (connection));
 #if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL) ) && \
   defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
   defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED)
@@ -947,7 +989,6 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
                          push_hdr,
                          sent);
 
-    // TODO: check 'send-ready'
     if ((mhd_SOCKET_ERR_NO_ERROR == ret) &&
         (header_size == *sent) &&
         (0 != body_size) &&
@@ -976,6 +1017,8 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
   }
 #ifdef mhd_USE_VECT_SEND
 
+  mhd_assert (! mhd_C_HAS_TLS (connection));
+
   if (header_size > (header_size + body_size))
   {
     /* Return value limit */
@@ -1133,7 +1176,7 @@ mhd_send_sendfile (struct MHD_Connection *restrict c,
   mhd_assert (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc);
   mhd_assert (MHD_SIZE_UNKNOWN != c->rp.response->cntn_size);
   mhd_assert (chunk_size <= (size_t) SSIZE_MAX);
-  // mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS)); // TODO: TLS support
+  mhd_assert (! mhd_C_HAS_TLS (c));
 
   send_size = 0;
   push_data = true;
@@ -1373,7 +1416,8 @@ send_iov_nontls (struct MHD_Connection *restrict connection,
   DWORD cnt_w;
 #endif /* MHD_SOCKETS_KIND_WINSOCK */
 
-  // TODO: assert for non-TLS
+  mhd_assert (! mhd_C_HAS_TLS (connection));
+  mhd_assert (! mhd_D_HAS_TLS (connection->daemon));
 
   mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd);
   mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage);
@@ -1492,7 +1536,7 @@ send_iov_nontls (struct MHD_Connection *restrict connection,
 
 #endif /* mhd_USE_VECT_SEND */
 
-#if ! defined(mhd_USE_VECT_SEND) || defined(HTTPS_SUPPORT) || \
+#if ! defined(mhd_USE_VECT_SEND) || defined(MHD_ENABLE_HTTPS) || \
   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
 
 
@@ -1574,7 +1618,7 @@ send_iov_emu (struct MHD_Connection *restrict connection,
 }
 
 
-#endif /* !mhd_USE_VECT_SEND || HTTPS_SUPPORT
+#endif /* !mhd_USE_VECT_SEND || MHD_ENABLE_HTTPS
           || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
 
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
@@ -1585,10 +1629,10 @@ mhd_send_iovec (struct MHD_Connection *restrict connection,
                 size_t *restrict sent)
 {
 #ifdef mhd_USE_VECT_SEND
-#if defined(HTTPS_SUPPORT) || \
+#if defined(MHD_ENABLE_HTTPS) || \
   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   bool use_iov_send = true;
-#endif /* HTTPS_SUPPORT || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+#endif /* MHD_ENABLE_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
 #endif /* mhd_USE_VECT_SEND */
 
   mhd_assert (NULL != connection->rp.resp_iov.iov);
@@ -1596,24 +1640,22 @@ mhd_send_iovec (struct MHD_Connection *restrict connection,
               connection->rp.response->cntn_dtype);
   mhd_assert (connection->rp.resp_iov.cnt > connection->rp.resp_iov.sent);
 #ifdef mhd_USE_VECT_SEND
-#if defined(HTTPS_SUPPORT) || \
+#if defined(MHD_ENABLE_HTTPS) || \
   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
-#ifdef HTTPS_SUPPORT
   use_iov_send = use_iov_send &&
-                 (true); // TODO: TLS support
-#endif /* HTTPS_SUPPORT */
+                 (! mhd_C_HAS_TLS (connection));
 #ifdef mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
   use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked ||
                                   connection->sk.props.has_spipe_supp);
 #endif /* mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   if (use_iov_send)
-#endif /* HTTPS_SUPPORT || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
+#endif /* MHD_ENABLE_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
   return send_iov_nontls (connection, r_iov, push_data, sent);
 #endif /* mhd_USE_VECT_SEND */
 
-#if ! defined(mhd_USE_VECT_SEND) || defined(HTTPS_SUPPORT) || \
+#if ! defined(mhd_USE_VECT_SEND) || defined(MHD_ENABLE_HTTPS) || \
   defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
   return send_iov_emu (connection, r_iov, push_data, sent);
-#endif /* !mhd_USE_VECT_SEND || HTTPS_SUPPORT
+#endif /* !mhd_USE_VECT_SEND || MHD_ENABLE_HTTPS
           || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
 }

+ 72 - 0
src/mhd2/mhd_tls_enums.h

@@ -0,0 +1,72 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  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/mhd_tls_enums.h
+ * @brief  The definition of internal enums used for TLS communication
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_TLS_ENUMS_H
+#define MHD_TLS_ENUMS_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_ENABLE_HTTPS
+#error This header should be used only if HTTPS is enabled
+#endif
+
+/**
+ * Result of performing TLS procedure
+ */
+enum MHD_FIXED_ENUM_ mhd_TlsProcedureResult
+{
+  /**
+   * Completed successfully
+   */
+  mhd_TLS_PROCED_SUCCESS = 0
+  ,
+  /**
+   * In progress, receive operation interrupted
+   */
+  mhd_TLS_PROCED_RECV_INTERRUPTED
+  ,
+  /**
+   * In progress, send operation interrupted
+   */
+  mhd_TLS_PROCED_SEND_INTERRUPTED
+  ,
+  /**
+   * In progress, need to receive more data
+   */
+  mhd_TLS_PROCED_RECV_MORE_NEEDED
+  ,
+  /**
+   * In progress, need to send more data
+   */
+  mhd_TLS_PROCED_SEND_MORE_NEEDED
+  ,
+  /**
+   * Procedure failed
+   */
+  mhd_TLS_PROCED_FAILED
+};
+
+#endif /* ! MHD_TLS_ENUMS_H */

+ 61 - 0
src/mhd2/mhd_tls_funcs.h

@@ -116,6 +116,67 @@
         mhd_TLS_FUNC (_conn_deinit)((c_tls))
 
 
+/* ** TLS connection establishing ** */
+
+/**
+ * Perform TLS handshake
+ * @param c_tls the connection TLS handle
+ * @return #mhd_TLS_PROCED_SUCCESS if completed successfully
+ *         or other enum mhd_TlsProcedureResult values
+ */
+#define mhd_tls_conn_handshake(c_tls)       \
+        mhd_TLS_FUNC (_conn_handshake)((c_tls))
+
+/**
+ * Perform shutdown of TLS layer
+ * @param c_tls the connection TLS handle
+ * @return #mhd_TLS_PROCED_SUCCESS if completed successfully
+ *         or other enum mhd_TlsProcedureResult values
+ */
+#define mhd_tls_conn_shutdown(c_tls)       \
+        mhd_TLS_FUNC (_conn_shutdown)((c_tls))
+
+/* ** Data sending and receiving over TLS connection ** */
+
+/**
+ * Receive the data from the remote side over TLS connection
+ *
+ * @param c_tls the connection TLS handle
+ * @param buf_size the size of the @a buf buffer
+ * @param[out] buf the buffer to fill with the received data
+ * @param[out] received the pointer to variable to get the size of the data
+ *                      actually put to the @a buffer
+ * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets
+ *         the received size) or socket error
+ */
+#define mhd_tls_conn_recv(c_tls,buf_size,buf,received)  \
+        mhd_TLS_FUNC (_conn_recv)((c_tls),(buf_size),(buf),(received))
+
+/**
+ * Check whether any incoming data is pending in the TLS buffers
+ *
+ * @param c_tls the connection TLS handle
+ * @return 'true' if any incoming remote data is already pending (the TLS recv()
+ *          call can be performed),
+ *         'false' otherwise
+ */
+#define mhd_tls_conn_has_data_in(c_tls)       \
+        mhd_TLS_FUNC (_conn_has_data_in)((c_tls))
+
+/**
+ * Send data to the remote side over TLS connection
+ *
+ * @param c_tls the connection TLS handle
+ * @param buffer_size the size of the @a buffer (in bytes)
+ * @param buffer content of the buffer to send
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ *         the sent size) or socket error
+ */
+#define mhd_tls_conn_send(c_tls,buf_size,buf,sent)      \
+        mhd_TLS_FUNC (_conn_send)((c_tls),(buf_size),(buf),(sent))
+
+
 /* ** General information function ** */
 
 /**

+ 26 - 1
src/mhd2/stream_funcs.c

@@ -52,6 +52,10 @@
 #include "conn_mark_ready.h"
 #include "stream_process_reply.h"
 
+#ifdef MHD_ENABLE_HTTPS
+#  include "mhd_tls_funcs.h"
+#endif
+
 #include "mhd_public_api.h"
 
 
@@ -822,7 +826,28 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
   }
   else
   {
-    if (mhd_socket_shut_wr (c->sk.fd) && (! c->sk.state.rmt_shut_wr))
+    bool use_graceful_closing;
+
+    mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err));
+
+    use_graceful_closing = true;
+#ifdef MHD_ENABLE_HTTPS
+    if (mhd_C_HAS_TLS (c))
+    {
+      if ((0 != (((unsigned int) c->sk.ready)
+                 & mhd_SOCKET_NET_STATE_SEND_READY))
+          || c->sk.props.is_nonblck)
+        use_graceful_closing =
+          (mhd_TLS_PROCED_FAILED != mhd_tls_conn_shutdown (c->tls));
+    }
+#endif /* MHD_ENABLE_HTTPS */
+    else if (1)
+    {
+      use_graceful_closing = mhd_socket_shut_wr (c->sk.fd);
+      if (use_graceful_closing)
+        use_graceful_closing = (! c->sk.state.rmt_shut_wr); /* Skip as already closed */
+    }
+    if (use_graceful_closing)
     {
       (void) 0; // TODO: start local lingering phase
       c->stage = mhd_HTTP_STAGE_PRE_CLOSING; // TODO: start local lingering phase

+ 1 - 3
src/mhd2/stream_process_reply.c

@@ -302,13 +302,11 @@ setup_reply_properties (struct MHD_Connection *restrict c)
       c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_IOV;
       break;
     case mhd_RESPONSE_CONTENT_DATA_FILE:
-#if 0 // TODO: TLS support
-      if (use_tls)
+      if (mhd_C_HAS_TLS (c))
       {
         c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
         break;
       }
-#endif
 #ifdef MHD_USE_SENDFILE
       if (r->cntn.file.use_sf)
       {

+ 12 - 20
src/mhd2/stream_process_states.c

@@ -55,24 +55,13 @@
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
 mhd_conn_event_loop_state_update (struct MHD_Connection *restrict c)
 {
-  /* Do not update states of suspended connection */
-  mhd_assert (! c->suspended);
-
-  if (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY))
-  {
-    mhd_assert (0 && "Should be handled earlier");
-    mhd_conn_start_closing_skt_err (c);
-    return false;
-  }
+#ifdef MHD_ENABLE_HTTPS
+  mhd_assert (! mhd_C_HAS_TLS (c) || \
+              (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state));
+  mhd_assert (mhd_C_HAS_TLS (c) || \
+              (mhd_CONN_STATE_TCP_CONNECTED == c->conn_state));
+#endif /* MHD_ENABLE_HTTPS */
 
-#if 0 // def HTTPS_SUPPORT // TODO: implement TLS support
-  if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
-  {   /* HTTPS connection. */
-    switch (connection->tls_state)
-    {
-    }
-  }
-#endif /* HTTPS_SUPPORT */
   switch (c->stage)
   {
   case mhd_HTTP_STAGE_INIT:
@@ -288,9 +277,12 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
 
   while (! c->suspended)
   {
-#ifdef HTTPS_SUPPORT
-    // TODO: support TLS, handshake
-#endif /* HTTPS_SUPPORT */
+#ifdef MHD_ENABLE_HTTPS
+    mhd_assert (! mhd_C_HAS_TLS (c) || \
+                (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state));
+    mhd_assert (mhd_C_HAS_TLS (c) || \
+                (mhd_CONN_STATE_TCP_CONNECTED == c->conn_state));
+#endif /* MHD_ENABLE_HTTPS */
     switch (c->stage)
     {
     case mhd_HTTP_STAGE_INIT:

+ 21 - 0
src/mhd2/tls_gnu_conn_data.h

@@ -35,6 +35,16 @@
 
 #include "tls_gnu_tls_lib.h"
 
+#ifndef NDEBUG
+struct mhd_TlsGnuConnDebug
+{
+  unsigned int is_inited;
+  unsigned int is_tls_handshake_completed;
+  unsigned int is_finished;
+  unsigned int is_failed;
+};
+#endif /* ! NDEBUG */
+
 /**
  * The structure with connection-specific GnuTLS data
  */
@@ -44,6 +54,17 @@ struct mhd_TlsGnuConnData
    * GnuTLS session data
    */
   gnutls_session_t sess;
+
+  /**
+   * 'true' if received EOF (the remote side initiated shutting down)
+   */
+  bool rmt_shut_tls_wr;
+#ifndef NDEBUG
+  /**
+   * Debugging data
+   */
+  struct mhd_TlsGnuConnDebug dbg;
+#endif /* ! NDEBUG */
 };
 
 #endif /* ! MHD_TLS_GNU_CONN_DATA_H */

+ 207 - 1
src/mhd2/tls_gnu_funcs.c

@@ -312,7 +312,7 @@ static const struct MHD_StringNullable tlsgnulib_base_priorities[] = {
   {0, NULL} /* Replaced with app-defined name */
   ,
 #ifdef mhd_TLS_GNU_SUPPORTS_MULTI_KEYWORDS_PRIORITY
-  mhd_MSTR_INIT ("@LIBMICROHTTPD,@SYSTEM")
+  mhd_MSTR_INIT ("@LIBMICROHTTPD,SYSTEM")
 #else
   mhd_MSTR_INIT ("@LIBMICROHTTPD")
   ,
@@ -539,6 +539,9 @@ mhd_tls_gnu_conn_init (const struct mhd_TlsGnuDaemonData *restrict d_tls,
         (void) alpn_res; /* Ignore any possible ALPN set errors */
       }
 #endif /* mhd_TLS_GNU_HAS_ALPN */
+#ifndef NDEBUG
+      c_tls->dbg.is_inited = true;
+#endif /* ! NDEBUG */
 
       return true; /* Success exit point */
     }
@@ -559,5 +562,208 @@ MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
 mhd_tls_gnu_conn_deinit (struct mhd_TlsGnuConnData *restrict c_tls)
 {
   mhd_assert (NULL != c_tls->sess);
+  mhd_assert (c_tls->dbg.is_inited);
   gnutls_deinit (c_tls->sess);
 }
+
+
+/* ** TLS connection establishing ** */
+
+MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+enum mhd_TlsProcedureResult
+mhd_tls_gnu_conn_handshake (struct mhd_TlsGnuConnData *restrict c_tls)
+{
+  int res;
+
+  mhd_assert (c_tls->dbg.is_inited);
+  mhd_assert (! c_tls->dbg.is_tls_handshake_completed);
+  mhd_assert (! c_tls->dbg.is_failed);
+
+  res = gnutls_handshake (c_tls->sess);
+  switch (res)
+  {
+  case GNUTLS_E_SUCCESS:
+#ifndef NDEBUG
+    c_tls->dbg.is_tls_handshake_completed = true;
+#endif /* ! NDEBUG */
+    return mhd_TLS_PROCED_SUCCESS;
+  case GNUTLS_E_INTERRUPTED:
+  case GNUTLS_E_AGAIN:
+  case GNUTLS_E_WARNING_ALERT_RECEIVED: /* Ignore any warning for now */
+    if (1)
+    {
+      int is_sending;
+
+      is_sending = gnutls_record_get_direction (c_tls->sess);
+      if (GNUTLS_E_INTERRUPTED == res)
+        return is_sending ?
+               mhd_TLS_PROCED_SEND_INTERRUPTED :
+               mhd_TLS_PROCED_RECV_INTERRUPTED;
+      return is_sending ?
+             mhd_TLS_PROCED_SEND_MORE_NEEDED :
+             mhd_TLS_PROCED_RECV_MORE_NEEDED;
+    }
+    break;
+  default:
+    break;
+  }
+#ifndef NDEBUG
+  c_tls->dbg.is_failed = true;
+#endif /* ! NDEBUG */
+  return mhd_TLS_PROCED_FAILED;
+}
+
+
+MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+enum mhd_TlsProcedureResult
+mhd_tls_gnu_conn_shutdown (struct mhd_TlsGnuConnData *restrict c_tls)
+{
+  int res;
+
+  mhd_assert (c_tls->dbg.is_inited);
+  mhd_assert (c_tls->dbg.is_tls_handshake_completed);
+  mhd_assert (! c_tls->dbg.is_failed);
+
+  res = gnutls_bye (c_tls->sess,
+                    c_tls->rmt_shut_tls_wr ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);
+  switch (res)
+  {
+  case GNUTLS_E_SUCCESS:
+#ifndef NDEBUG
+    c_tls->dbg.is_finished = true;
+#endif /* ! NDEBUG */
+    return mhd_TLS_PROCED_SUCCESS;
+  case GNUTLS_E_INTERRUPTED:
+  case GNUTLS_E_AGAIN:
+  case GNUTLS_E_WARNING_ALERT_RECEIVED: /* Ignore any warning for now */
+    if (1)
+    {
+      int is_sending;
+
+      is_sending = gnutls_record_get_direction (c_tls->sess);
+      if (GNUTLS_E_INTERRUPTED == res)
+        return is_sending ?
+               mhd_TLS_PROCED_SEND_INTERRUPTED :
+               mhd_TLS_PROCED_RECV_INTERRUPTED;
+      return is_sending ?
+             mhd_TLS_PROCED_SEND_MORE_NEEDED :
+             mhd_TLS_PROCED_RECV_MORE_NEEDED;
+    }
+    break;
+  default:
+    break;
+  }
+#ifndef NDEBUG
+  c_tls->dbg.is_failed = true;
+#endif /* ! NDEBUG */
+  return mhd_TLS_PROCED_FAILED;
+}
+
+
+/* ** Data receiving and sending ** */
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_SIZE_ (3,2)
+MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+mhd_tls_gnu_conn_recv (struct mhd_TlsGnuConnData *restrict c_tls,
+                       size_t buf_size,
+                       char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+                       size_t *restrict received)
+{
+  ssize_t res;
+
+  mhd_assert (c_tls->dbg.is_inited);
+  mhd_assert (c_tls->dbg.is_tls_handshake_completed);
+  mhd_assert (! c_tls->dbg.is_failed);
+
+  /* Check for GnuTLS return value limitation */
+  if (0 > (ssize_t) buf_size)
+    buf_size = (ssize_t) ((~((size_t) 0u)) >> 1); /* SSIZE_MAX */
+
+  res = gnutls_record_recv (c_tls->sess,
+                            buf,
+                            buf_size);
+  if (0 >= res)
+  {
+    *received = 0;
+    switch (res)
+    {
+    case 0: /* Not an error */
+      c_tls->rmt_shut_tls_wr = true;
+      return mhd_SOCKET_ERR_NO_ERROR;
+    case GNUTLS_E_AGAIN:
+      return mhd_SOCKET_ERR_AGAIN;
+    case GNUTLS_E_INTERRUPTED:
+      return mhd_SOCKET_ERR_INTR;
+    case GNUTLS_E_PREMATURE_TERMINATION:
+      return mhd_SOCKET_ERR_CONNRESET;
+    default:
+      break;
+    }
+    /* Treat all other kinds of errors as hard errors */
+#ifndef NDEBUG
+    c_tls->dbg.is_failed = true;
+#endif /* ! NDEBUG */
+    return mhd_SOCKET_ERR_TLS;
+  }
+
+  *received = (size_t) res;
+  return mhd_SOCKET_ERR_NO_ERROR;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_tls_gnu_conn_has_data_in (struct mhd_TlsGnuConnData *restrict c_tls)
+{
+  return 0 != gnutls_record_check_pending (c_tls->sess);
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2)
+MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+mhd_tls_gnu_conn_send (struct mhd_TlsGnuConnData *restrict c_tls,
+                       size_t buf_size,
+                       const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+                       size_t *restrict sent)
+{
+  ssize_t res;
+
+  mhd_assert (c_tls->dbg.is_inited);
+  mhd_assert (c_tls->dbg.is_tls_handshake_completed);
+  mhd_assert (! c_tls->dbg.is_failed);
+
+  /* Check for GnuTLS return value limitation */
+  if (0 > (ssize_t) buf_size)
+    buf_size = (ssize_t) ((~((size_t) 0u)) >> 1); /* SSIZE_MAX */
+
+  res = gnutls_record_send (c_tls->sess,
+                            buf,
+                            buf_size);
+
+  mhd_assert (0 != res);
+
+  if (0 > res)
+  {
+    *sent = 0;
+    switch (res)
+    {
+    case GNUTLS_E_AGAIN:
+      return mhd_SOCKET_ERR_AGAIN;
+    case GNUTLS_E_INTERRUPTED:
+      return mhd_SOCKET_ERR_INTR;
+    case GNUTLS_E_PREMATURE_TERMINATION:
+      return mhd_SOCKET_ERR_CONNRESET;
+    default:
+      break;
+    }
+    /* Treat all other kinds of errors as hard errors */
+#ifndef NDEBUG
+    c_tls->dbg.is_failed = true;
+#endif /* ! NDEBUG */
+    return mhd_SOCKET_ERR_TLS;
+  }
+
+  *sent = (size_t) res;
+  return mhd_SOCKET_ERR_NO_ERROR;
+}

+ 76 - 0
src/mhd2/tls_gnu_funcs.h

@@ -38,6 +38,9 @@
 
 #include "mhd_status_code_int.h"
 
+#include "mhd_tls_enums.h"
+#include "mhd_socket_error.h"
+
 /**
  * The structure with daemon-specific GnuTLS data
  */
@@ -140,4 +143,77 @@ MHD_INTERNAL void
 mhd_tls_gnu_conn_deinit (struct mhd_TlsGnuConnData *restrict c_tls)
 MHD_FN_PAR_NONNULL_ALL_;
 
+
+/* ** TLS connection establishing ** */
+
+/**
+ * Perform TLS handshake
+ * @param c_tls the connection TLS handle
+ * @return #mhd_TLS_PROCED_SUCCESS if completed successfully
+ *         or other enum mhd_TlsProcedureResult values
+ */
+MHD_INTERNAL enum mhd_TlsProcedureResult
+mhd_tls_gnu_conn_handshake (struct mhd_TlsGnuConnData *restrict c_tls)
+MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Perform shutdown of TLS layer
+ * @param c_tls the connection TLS handle
+ * @return #mhd_TLS_PROCED_SUCCESS if completed successfully
+ *         or other enum mhd_TlsProcedureResult values
+ */
+MHD_INTERNAL enum mhd_TlsProcedureResult
+mhd_tls_gnu_conn_shutdown (struct mhd_TlsGnuConnData *restrict c_tls)
+MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_;
+
+
+/* ** Data sending and receiving over TLS connection ** */
+
+/**
+ * Receive the data from the remote side over TLS connection
+ *
+ * @param c_tls the connection TLS handle
+ * @param buf_size the size of the @a buf buffer
+ * @param[out] buf the buffer to fill with the received data
+ * @param[out] received the pointer to variable to get the size of the data
+ *                      actually put to the @a buffer
+ * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets
+ *         the received size) or socket error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_tls_gnu_conn_recv (struct mhd_TlsGnuConnData *restrict c_tls,
+                       size_t buf_size,
+                       char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+                       size_t *restrict received)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4);
+
+/**
+ * Check whether any incoming data is pending in the TLS buffers
+ *
+ * @param c_tls the connection TLS handle
+ * @return 'true' if any incoming remote data is already pending (the TLS recv()
+ *          call can be performed),
+ *         'false' otherwise
+ */
+MHD_INTERNAL bool
+mhd_tls_gnu_conn_has_data_in (struct mhd_TlsGnuConnData *restrict c_tls)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Send data to the remote side over TLS connection
+ *
+ * @param c_tls the connection TLS handle
+ * @param buffer_size the size of the @a buffer (in bytes)
+ * @param buffer content of the buffer to send
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ *         the sent size) or socket error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_tls_gnu_conn_send (struct mhd_TlsGnuConnData *restrict c_tls,
+                       size_t buf_size,
+                       const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
+                       size_t *restrict sent)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4);
+
 #endif /* ! MHD_TLS_GNU_FUNCS_H */