[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.
- [libmicrohttpd] branch master updated (a19df909 -> 38c06b6a), gnunet, 2023/11/24
- [libmicrohttpd] 01/09: mhd_sockets: fixed error code for W32, gnunet, 2023/11/24
- [libmicrohttpd] 03/09: test_upgrade: used sized send and receive, removed VLA, other improvements, gnunet, 2023/11/24
- [libmicrohttpd] 02/09: test_upgrade{,_large}: minor improvements, gnunet, 2023/11/24
- [libmicrohttpd] 09/09: test_upgrade: implemented timeouts for all socket operations,
gnunet <=
- [libmicrohttpd] 05/09: test_upgrade: set TCP_NODELAY unconditionally on used sockets, gnunet, 2023/11/24
- [libmicrohttpd] 06/09: test_upgrade: added timeout detection on send/recv operations, gnunet, 2023/11/24
- [libmicrohttpd] 04/09: test_upgrade: added initial support for timeout detection, gnunet, 2023/11/24
- [libmicrohttpd] 07/09: test_upgrade: fixed timeout value for external select, gnunet, 2023/11/24
- [libmicrohttpd] 08/09: test_upgrade: merged similar code paths, gnunet, 2023/11/24