commit 7d7d9bc6b18e7aafeb4b01b13554bac4674221f4
parent 53e1e4864887512f39e964fc57c3b9f83d04a489
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date: Tue, 24 Oct 2023 17:47:23 +0300
test_upgrade: added timeout detection on send/recv operations
Diffstat:
1 file changed, 128 insertions(+), 10 deletions(-)
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c
@@ -162,7 +162,7 @@ _testErrorLog_func (const char *errDesc, const char *funcName, int lineNum)
/* Could be increased to facilitate debugging */
-static unsigned int test_timeout = 5U;
+static int test_timeout = 5;
static int verbose = 0;
@@ -511,10 +511,69 @@ wr_connect (struct wr_socket *s,
}
+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)
+{
+ fd_set fds;
+ int sel_res;
+ struct timeval tmo;
+
+ if (0 > timeout_ms)
+ return;
+
+#ifndef MHD_WINSOCK_SOCKETS
+ if (FD_SETSIZE <= s->fd)
+ externalErrorExitDesc ("Too large FD value");
+#endif /* ! MHD_WINSOCK_SOCKETS */
+ FD_ZERO (&fds);
+ FD_SET (s->fd, &fds);
+#if ! defined(_WIN32) || defined(__CYGWIN__)
+ tmo.tv_sec = (time_t) (timeout_ms / 1000);
+#else /* Native W32 */
+ tmo.tv_sec = (long) (timeout_ms / 1000);
+#endif /* Native W32 */
+ tmo.tv_usec = ((long) (timeout_ms % 1000)) * 1000;
+
+ if (WR_WAIT_FOR_RECV == wait_for)
+ sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, &tmo);
+ else
+ sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, &tmo);
+
+ if (1 == sel_res)
+ return;
+
+ if (0 == sel_res)
+ fprintf (stderr, "Timeout");
+ else
+ {
+#ifndef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "Error %d (%s)", (int) errno, strerror (errno));
+#else /* MHD_WINSOCK_SOCKETS */
+ fprintf (stderr, "Error (WSAGetLastError code: %d)",
+ (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ }
+ fprintf (stderr, " waiting for socket to be available for %s.\n",
+ (WR_WAIT_FOR_RECV == wait_for) ? "receiving" : "sending");
+ if (WR_WAIT_FOR_RECV == wait_for)
+ mhdErrorExitDesc ("Client or application failed to receive the data");
+ else
+ mhdErrorExitDesc ("Client or application failed to send the data");
+}
+
+
#ifdef HTTPS_SUPPORT
/* Only to be called from wr_send() and wr_recv() ! */
static bool
-wr_handshake (struct wr_socket *s)
+wr_handshake_ (struct wr_socket *s)
{
int res = gnutls_handshake (s->tls_s);
if (GNUTLS_E_SUCCESS == res)
@@ -545,24 +604,31 @@ wr_handshake (struct wr_socket *s)
* @param s the socket to use
* @param buf the buffer with data to send
* @param len the length of data in @a buf
+ * @param timeout_ms the maximum wait time in milliseconds to send the data,
+ * no limit if negative value is used
* @return number of bytes were sent if succeed,
* -1 if failed. Use #MHD_socket_get_error_()
* to get socket error.
*/
static ssize_t
-wr_send (struct wr_socket *s,
- const void *buf,
- size_t len)
+wr_send_tmo (struct wr_socket *s,
+ const void *buf,
+ size_t len,
+ int timeout_ms)
{
if (wr_plain == s->t)
+ {
+ 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)
{
ssize_t ret;
- if (! s->tls_connected && ! wr_handshake (s))
+ if (! s->tls_connected && ! wr_handshake_ (s))
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;
@@ -590,28 +656,53 @@ wr_send (struct wr_socket *s,
/**
+ * Send data to remote by socket.
+ * @param s the socket to use
+ * @param buf the buffer with data to send
+ * @param len the length of data in @a buf
+ * @return number of bytes were sent if succeed,
+ * -1 if failed. Use #MHD_socket_get_error_()
+ * to get socket error.
+ */
+static ssize_t
+wr_send (struct wr_socket *s,
+ const void *buf,
+ size_t len)
+{
+ return wr_send_tmo (s, buf, len, test_timeout * 1000);
+}
+
+
+/**
* Receive data from remote by socket.
* @param s the socket to use
* @param buf the buffer to store received data
* @param len the length of @a buf
+ * @param timeout_ms the maximum wait time in milliseconds to receive the data,
+ * no limit if negative value is used
* @return number of bytes were received if succeed,
* -1 if failed. Use #MHD_socket_get_error_()
* to get socket error.
*/
static ssize_t
-wr_recv (struct wr_socket *s,
- void *buf,
- size_t len)
+wr_recv_tmo (struct wr_socket *s,
+ void *buf,
+ size_t len,
+ int timeout_ms)
{
if (wr_plain == s->t)
+ {
+ wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV);
return MHD_recv_ (s->fd, buf, len);
+ }
#ifdef HTTPS_SUPPORT
if (wr_tls == s->t)
{
ssize_t ret;
- if (! s->tls_connected && ! wr_handshake (s))
+ if (! s->tls_connected && ! wr_handshake_ (s))
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;
@@ -637,6 +728,24 @@ wr_recv (struct wr_socket *s,
/**
+ * Receive data from remote by socket.
+ * @param s the socket to use
+ * @param buf the buffer to store received data
+ * @param len the length of @a buf
+ * @return number of bytes were received if succeed,
+ * -1 if failed. Use #MHD_socket_get_error_()
+ * to get socket error.
+ */
+static ssize_t
+wr_recv (struct wr_socket *s,
+ void *buf,
+ size_t len)
+{
+ return wr_recv_tmo (s, buf, len, test_timeout * 1000);
+}
+
+
+/**
* Close socket and release allocated resourced
* @param s the socket to close
* @return zero on succeed, -1 otherwise
@@ -1475,6 +1584,15 @@ main (int argc,
has_param (argc, argv, "-s") ||
has_param (argc, argv, "--silent"));
+ if ((((int) ((~((unsigned int) 0U)) >> 1)) / 1000) < test_timeout)
+ {
+ fprintf (stderr, "The test timeout value (%d) is too large.\n"
+ "The test cannot run.\n", test_timeout);
+ fprintf (stderr, "The maximum allowed timeout value is %d.\n",
+ (((int) ((~((unsigned int) 0U)) >> 1)) / 1000));
+ return 3;
+ }
+
if (test_tls)
{
use_tls_tool = TLS_LIB_GNUTLS; /* Should be always available as MHD uses it. */