gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] 09/09: test_upgrade: implemented timeouts for all socket


From: gnunet
Subject: [libmicrohttpd] 09/09: test_upgrade: implemented timeouts for all socket operations
Date: Fri, 24 Nov 2023 19:54:04 +0100

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

commit 38c06b6aeb3aa3b4ab4cab6cce9e08c55c2a9b9c
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Fri Nov 24 17:43:30 2023 +0300

    test_upgrade: implemented timeouts for all socket operations
---
 src/microhttpd/test_upgrade.c | 358 +++++++++++++++++++++++++++++++-----------
 1 file changed, 263 insertions(+), 95 deletions(-)

diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c
index ddc47653..b30ad64b 100644
--- a/src/microhttpd/test_upgrade.c
+++ b/src/microhttpd/test_upgrade.c
@@ -62,6 +62,14 @@
 #endif /* HAVE_FORK && HAVE_WAITPID */
 #endif /* HTTPS_SUPPORT */
 
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef MHD_WINSOCK_SOCKETS
+#    error Both MHD_POSIX_SOCKETS and MHD_WINSOCK_SOCKETS are defined
+#  endif /* MHD_WINSOCK_SOCKETS */
+#elif ! defined(MHD_WINSOCK_SOCKETS)
+#  error Neither MHD_POSIX_SOCKETS nor MHD_WINSOCK_SOCKETS are defined
+#endif /* MHD_WINSOCK_SOCKETS */
+
 
 #ifndef MHD_STATICSTR_LEN_
 /**
@@ -262,6 +270,7 @@ gnutlscli_connect (int *sock,
 #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
 
 
+#if 0 /* Unused code */
 /**
  * Change socket to blocking.
  *
@@ -288,6 +297,35 @@ make_blocking (MHD_socket fd)
 }
 
 
+#endif /* Unused code */
+
+
+/**
+ * Change socket to non-blocking.
+ *
+ * @param fd the socket to manipulate
+ */
+static void
+make_nonblocking (MHD_socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+  int flags;
+
+  flags = fcntl (fd, F_GETFL);
+  if (-1 == flags)
+    externalErrorExitDesc ("fcntl() failed");
+  if (O_NONBLOCK != (flags & O_NONBLOCK))
+    if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK))
+      externalErrorExitDesc ("fcntl() failed");
+#elif defined(MHD_WINSOCK_SOCKETS)
+  unsigned long flags = 1;
+
+  if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+    externalErrorExitDesc ("ioctlsocket() failed");
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
 /**
  * Enable TCP_NODELAY on TCP/IP socket.
  *
@@ -337,6 +375,8 @@ struct wr_socket
     wr_plain = 1,
     wr_tls = 2
   } t;
+
+  bool is_nonblocking;
 #ifdef HTTPS_SUPPORT
   /**
    * TLS credentials
@@ -363,6 +403,28 @@ struct wr_socket
 #define wr_fd(s) ((s)->fd)
 
 
+#if 0 /* Unused code */
+static void
+wr_make_blocking  (struct wr_socket *s)
+{
+  if (s->is_nonblocking)
+    make_blocking (s->fd);
+  s->is_nonblocking = false;
+}
+
+
+#endif /* Unused code */
+
+
+static void
+wr_make_nonblocking  (struct wr_socket *s)
+{
+  if (! s->is_nonblocking)
+    make_nonblocking (s->fd);
+  s->is_nonblocking = true;
+}
+
+
 /**
  * Create wr_socket with plain TCP underlying socket
  * @return created socket on success, NULL otherwise
@@ -378,6 +440,7 @@ wr_create_plain_sckt (void)
   }
   s->t = wr_plain;
   s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  s->is_nonblocking = false;
   if (MHD_INVALID_SOCKET != s->fd)
   {
     make_nodelay (s->fd);
@@ -406,6 +469,7 @@ wr_create_tls_sckt (void)
   s->t = wr_tls;
   s->tls_connected = 0;
   s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  s->is_nonblocking = false;
   if (MHD_INVALID_SOCKET != s->fd)
   {
     make_nodelay (s->fd);
@@ -469,65 +533,28 @@ wr_create_from_plain_sckt (MHD_socket plain_sk)
   }
   s->t = wr_plain;
   s->fd = plain_sk;
+  s->is_nonblocking = false; /* The actual mode is unknown */
+  wr_make_nonblocking (s);   /* Force set mode to have correct status */
   make_nodelay (s->fd);
   return s;
 }
 
 
-/**
- * Connect socket to specified address.
- * @param s socket to use
- * @param addr address to connect
- * @param length of structure pointed by @a addr
- * @return zero on success, -1 otherwise.
- */
-static int
-wr_connect (struct wr_socket *s,
-            const struct sockaddr *addr,
-            unsigned int length)
-{
-  if (0 != connect (s->fd, addr, (socklen_t) length))
-  {
-    testErrorLogDesc ("connect() failed");
-    return -1;
-  }
-  if (wr_plain == s->t)
-    return 0;
-#ifdef HTTPS_SUPPORT
-  if (wr_tls == s->t)
-  {
-    /* Do not try handshake here as
-     * it require processing on MHD side and
-     * when testing with "external" polling,
-     * test will call MHD processing only
-     * after return from wr_connect(). */
-    s->tls_connected = 0;
-    return 0;
-  }
-#endif /* HTTPS_SUPPORT */
-  testErrorLogDesc ("HTTPS socket connect called, but code does not support" \
-                    " HTTPS sockets");
-  return -1;
-}
-
-
 enum wr_wait_for_type
 {
   WR_WAIT_FOR_RECV = 0,
   WR_WAIT_FOR_SEND = 1
 };
 
-static void
-wr_wait_socket_ready_ (struct wr_socket *s,
-                       int timeout_ms,
-                       enum wr_wait_for_type wait_for)
+static bool
+wr_wait_socket_ready_noabort_ (struct wr_socket *s,
+                               int timeout_ms,
+                               enum wr_wait_for_type wait_for)
 {
   fd_set fds;
   int sel_res;
   struct timeval tmo;
-
-  if (0 > timeout_ms)
-    return;
+  struct timeval *tmo_ptr;
 
 #ifndef MHD_WINSOCK_SOCKETS
   if (FD_SETSIZE <= s->fd)
@@ -535,20 +562,26 @@ wr_wait_socket_ready_ (struct wr_socket *s,
 #endif /* ! MHD_WINSOCK_SOCKETS */
   FD_ZERO (&fds);
   FD_SET (s->fd, &fds);
+  if (0 <= timeout_ms)
+  {
 #if ! defined(_WIN32) || defined(__CYGWIN__)
-  tmo.tv_sec = (time_t) (timeout_ms / 1000);
+    tmo.tv_sec = (time_t) (timeout_ms / 1000);
 #else  /* Native W32 */
-  tmo.tv_sec = (long) (timeout_ms / 1000);
+    tmo.tv_sec = (long) (timeout_ms / 1000);
 #endif /* Native W32 */
-  tmo.tv_usec = ((long) (timeout_ms % 1000)) * 1000;
+    tmo.tv_usec = ((long) (timeout_ms % 1000)) * 1000;
+    tmo_ptr = &tmo;
+  }
+  else
+    tmo_ptr = NULL; /* No timeout */
 
   if (WR_WAIT_FOR_RECV == wait_for)
-    sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, &tmo);
+    sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr);
   else
-    sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, &tmo);
+    sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr);
 
   if (1 == sel_res)
-    return;
+    return true;
 
   if (0 == sel_res)
     fprintf (stderr, "Timeout");
@@ -563,6 +596,18 @@ wr_wait_socket_ready_ (struct wr_socket *s,
   }
   fprintf (stderr, " waiting for socket to be available for %s.\n",
            (WR_WAIT_FOR_RECV == wait_for) ? "receiving" : "sending");
+  return false;
+}
+
+
+static void
+wr_wait_socket_ready_ (struct wr_socket *s,
+                       int timeout_ms,
+                       enum wr_wait_for_type wait_for)
+{
+  if (wr_wait_socket_ready_noabort_ (s, timeout_ms, wait_for))
+    return;
+
   if (WR_WAIT_FOR_RECV == wait_for)
     mhdErrorExitDesc ("Client or application failed to receive the data");
   else
@@ -570,16 +615,105 @@ wr_wait_socket_ready_ (struct wr_socket *s,
 }
 
 
+/**
+ * Connect socket to specified address.
+ * @param s socket to use
+ * @param addr address to connect
+ * @param length of structure pointed by @a addr
+ * @param timeout_ms the maximum wait time in milliseconds to send the data,
+ *                   no limit if negative value is used
+ * @return zero on success, -1 otherwise.
+ */
+static int
+wr_connect_tmo (struct wr_socket *s,
+                const struct sockaddr *addr,
+                unsigned int length,
+                int timeout_ms)
+{
+  if (0 != connect (s->fd, addr, (socklen_t) length))
+  {
+    int err;
+    bool connect_completed = false;
+
+    err = MHD_socket_get_error_ ();
+#if defined(MHD_POSIX_SOCKETS)
+    while (! connect_completed && (EINTR == err))
+    {
+      connect_completed = (0 == connect (s->fd, addr, (socklen_t) length));
+      if (! connect_completed)
+      {
+        err = errno;
+        if (EALREADY == err)
+          err = EINPROGRESS;
+        else if (EISCONN == err)
+          connect_completed = true;
+      }
+    }
+#endif /* MHD_POSIX_SOCKETS */
+    if (! connect_completed &&
+        (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINPROGRESS_)
+         || MHD_SCKT_ERR_IS_EAGAIN_ (err))) /* No modern system uses EAGAIN, 
except W32 */
+      connect_completed =
+        wr_wait_socket_ready_noabort_ (s, timeout_ms, WR_WAIT_FOR_SEND);
+    if (! connect_completed)
+    {
+      testErrorLogDesc ("connect() failed");
+      return -1;
+    }
+  }
+  if (wr_plain == s->t)
+    return 0;
+#ifdef HTTPS_SUPPORT
+  if (wr_tls == s->t)
+  {
+    /* Do not try handshake here as
+     * it requires processing on MHD side and
+     * when testing with "external" polling,
+     * test will call MHD processing only
+     * after return from wr_connect(). */
+    s->tls_connected = 0;
+    return 0;
+  }
+#endif /* HTTPS_SUPPORT */
+  testErrorLogDesc ("HTTPS socket connect called, but code does not support" \
+                    " HTTPS sockets");
+  return -1;
+}
+
+
+/**
+ * Connect socket to specified address.
+ * @param s socket to use
+ * @param addr address to connect
+ * @param length of structure pointed by @a addr
+ * @return zero on success, -1 otherwise.
+ */
+static int
+wr_connect (struct wr_socket *s,
+            const struct sockaddr *addr,
+            unsigned int length)
+{
+  return wr_connect_tmo (s, addr, length, test_timeout * 1000);
+}
+
+
 #ifdef HTTPS_SUPPORT
 /* Only to be called from wr_send() and wr_recv() ! */
 static bool
-wr_handshake_ (struct wr_socket *s)
+wr_handshake_tmo_ (struct wr_socket *s,
+                   int timeout_ms)
 {
   int res = gnutls_handshake (s->tls_s);
+
+  while ((GNUTLS_E_AGAIN == res) || (GNUTLS_E_INTERRUPTED  == res))
+  {
+    wr_wait_socket_ready_ (s, timeout_ms,
+                           gnutls_record_get_direction (s->tls_s) ?
+                           WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
+    res = gnutls_handshake (s->tls_s);
+  }
   if (GNUTLS_E_SUCCESS == res)
     s->tls_connected = true;
-  else if (GNUTLS_E_AGAIN == res)
-    MHD_socket_set_error_ (MHD_SCKT_EAGAIN_);
   else
   {
     fprintf (stderr, "The error returned by gnutls_handshake() is "
@@ -596,6 +730,17 @@ wr_handshake_ (struct wr_socket *s)
 }
 
 
+#if 0 /* Unused function */
+/* Only to be called from wr_send() and wr_recv() ! */
+static bool
+wr_handshake_ (struct wr_socket *s)
+{
+  return wr_handshake_tmo_ (s, test_timeout * 1000);
+}
+
+
+#endif /* Unused function */
+
 #endif /* HTTPS_SUPPORT */
 
 
@@ -618,35 +763,44 @@ wr_send_tmo (struct wr_socket *s,
 {
   if (wr_plain == s->t)
   {
+    int err;
+    ssize_t res = MHD_send_ (s->fd, buf, len);
+    if (0 <= res)
+      return res;
+    err = MHD_socket_get_error_ ();
+    if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err))
+      return res;
     wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND);
     return MHD_send_ (s->fd, buf, len);
   }
 #ifdef HTTPS_SUPPORT
-  if (wr_tls == s->t)
+  else if (wr_tls == s->t)
   {
     ssize_t ret;
-    if (! s->tls_connected && ! wr_handshake_ (s))
+    if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms))
       return -1;
 
-    wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND);
-    ret = gnutls_record_send (s->tls_s, buf, len);
-    if (ret > 0)
-      return ret;
-    if (GNUTLS_E_AGAIN == ret)
-      MHD_socket_set_error_ (MHD_SCKT_EAGAIN_);
-    else
+    while (1)
     {
-      fprintf (stderr, "The error returned by gnutls_record_send() is "
-               "'%s' ", gnutls_strerror ((int) ret));
+      ret = gnutls_record_send (s->tls_s, buf, len);
+      if (ret >= 0)
+        return ret;
+      if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED  != ret))
+        break;
+      wr_wait_socket_ready_ (s, timeout_ms,
+                             gnutls_record_get_direction (s->tls_s) ?
+                             WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
+    }
+    fprintf (stderr, "The error returned by gnutls_record_send() is "
+             "'%s' ", gnutls_strerror ((int) ret));
 #if GNUTLS_VERSION_NUMBER >= 0x020600
-      fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
+    fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
 #else  /* GNUTLS_VERSION_NUMBER < 0x020600 */
-      fprintf (stderr, "(%d)\n", (int) ret);
+    fprintf (stderr, "(%d)\n", (int) ret);
 #endif /* GNUTLS_VERSION_NUMBER < 0x020600 */
-      testErrorLogDesc ("gnutls_record_send() failed with hard error");
-      MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_);   /* hard error */
-      return -1;
-    }
+    testErrorLogDesc ("gnutls_record_send() failed with hard error");
+    MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_);   /* hard error */
+    return -1;
   }
 #endif /* HTTPS_SUPPORT */
   testErrorLogDesc ("HTTPS socket send called, but code does not support" \
@@ -692,6 +846,13 @@ wr_recv_tmo (struct wr_socket *s,
 {
   if (wr_plain == s->t)
   {
+    int err;
+    ssize_t res = MHD_recv_ (s->fd, buf, len);
+    if (0 <= res)
+      return res;
+    err = MHD_socket_get_error_ ();
+    if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err))
+      return res;
     wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV);
     return MHD_recv_ (s->fd, buf, len);
   }
@@ -699,28 +860,31 @@ wr_recv_tmo (struct wr_socket *s,
   if (wr_tls == s->t)
   {
     ssize_t ret;
-    if (! s->tls_connected && ! wr_handshake_ (s))
+    if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms))
       return -1;
 
-    wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV);
-    ret = gnutls_record_recv (s->tls_s, buf, len);
-    if (ret >= 0)
-      return ret;
-    if (GNUTLS_E_AGAIN == ret)
-      MHD_socket_set_error_ (MHD_SCKT_EAGAIN_);
-    else
+    while (1)
     {
-      fprintf (stderr, "The error returned by gnutls_record_recv() is "
-               "'%s' ", gnutls_strerror ((int) ret));
+      ret = gnutls_record_recv (s->tls_s, buf, len);
+      if (ret >= 0)
+        return ret;
+      if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED  != ret))
+        break;
+      wr_wait_socket_ready_ (s, timeout_ms,
+                             gnutls_record_get_direction (s->tls_s) ?
+                             WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
+    }
+
+    fprintf (stderr, "The error returned by gnutls_record_recv() is "
+             "'%s' ", gnutls_strerror ((int) ret));
 #if GNUTLS_VERSION_NUMBER >= 0x020600
-      fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
+    fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
 #else  /* GNUTLS_VERSION_NUMBER < 0x020600 */
-      fprintf (stderr, "(%d)\n", (int) ret);
+    fprintf (stderr, "(%d)\n", (int) ret);
 #endif /* GNUTLS_VERSION_NUMBER < 0x020600 */
-      testErrorLogDesc ("gnutls_record_recv() failed with hard error");
-      MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_);   /* hard error */
-      return -1;
-    }
+    testErrorLogDesc ("gnutls_record_recv() failed with hard error");
+    MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_);   /* hard error */
+    return -1;
   }
 #endif /* HTTPS_SUPPORT */
   return -1;
@@ -945,7 +1109,7 @@ send_all (struct wr_socket *sock,
   size_t sent;
   const uint8_t *const buf = (const uint8_t *) data;
 
-  make_blocking (wr_fd (sock));
+  wr_make_nonblocking (sock);
   for (sent = 0; sent < data_size; sent += (size_t) ret)
   {
     ret = wr_send (sock,
@@ -980,7 +1144,7 @@ recv_hdr (struct wr_socket *sock)
   char c;
   ssize_t ret;
 
-  make_blocking (wr_fd (sock));
+  wr_make_nonblocking (sock);
   next = '\r';
   i = 0;
   while (i < 4)
@@ -1032,7 +1196,7 @@ recv_all (struct wr_socket *sock,
   if (NULL == buf)
     externalErrorExitDesc ("malloc() failed");
 
-  make_blocking (wr_fd (sock));
+  wr_make_nonblocking (sock);
   for (rcvd = 0; rcvd < data_size; rcvd += (size_t) ret)
   {
     ret = wr_recv (sock,
@@ -1193,6 +1357,7 @@ upgrade_cb (void *cls,
   (void) extra_in; /* Unused. Silent compiler warning. */
 
   usock = wr_create_from_plain_sckt (sock);
+  wr_make_nonblocking (usock);
   if (0 != extra_in_size)
     mhdErrorExitDesc ("'extra_in_size' is not zero");
   if (0 != pthread_create (&pt,
@@ -1297,20 +1462,20 @@ run_mhd_select_loop (struct MHD_Daemon *daemon)
   const union MHD_DaemonInfo *pdinfo;
   bool connection_was_accepted;
   bool connection_has_finished;
-  bool use_epoll = false;
 #ifdef EPOLL_SUPPORT
+  bool use_epoll = false;
   int ep = -1;
 
-  pdinfo = MHD_get_daemeon_info (daemon,
-                                 MHD_DAEMON_INFO_FLAGS);
+  pdinfo = MHD_get_daemon_info (daemon,
+                                MHD_DAEMON_INFO_FLAGS);
   if (NULL == pdinfo)
     mhdErrorExitDesc ("MHD_get_daemon_info() failed");
   else
     use_epoll = (0 != (pdinfo->flags & MHD_USE_EPOLL));
   if (use_epoll)
   {
-    pdinfo = MHD_get_daemeon_info (daemon,
-                                   MHD_DAEMON_INFO_EPOLL_FD);
+    pdinfo = MHD_get_daemon_info (daemon,
+                                  MHD_DAEMON_INFO_EPOLL_FD);
     if (NULL == pdinfo)
       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
     ep = pdinfo->listen_fd;
@@ -1367,7 +1532,7 @@ run_mhd_select_loop (struct MHD_Daemon *daemon)
     {
       if (ep != max_fd)
         mhdErrorExitDesc ("Wrong 'max_fd' value");
-      if (! FD_ISSET (&rs, ep))
+      if (! FD_ISSET (ep, &rs))
         mhdErrorExitDesc ("Epoll FD is NOT set in read fd_set");
     }
 #endif /* EPOLL_SUPPORT */
@@ -1543,6 +1708,7 @@ test_upgrade (unsigned int flags,
     sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt ();
     if (NULL == sock)
       externalErrorExitDesc ("Create socket failed");
+    wr_make_nonblocking (sock);
     sa.sin_family = AF_INET;
     sa.sin_port = htons (dinfo->port);
     sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
@@ -1565,6 +1731,8 @@ test_upgrade (unsigned int flags,
     sock =  wr_create_from_plain_sckt (tls_fork_sock);
     if (NULL == sock)
       externalErrorExitDesc ("wr_create_from_plain_sckt() failed");
+
+    wr_make_nonblocking (sock);
 #else  /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */
     externalErrorExitDesc ("Unsupported 'use_tls_tool' value");
 #endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]