diff mbox series

[3/8] gweb: Leverage TCP connect timeout.

Message ID 13557DEE-684A-4575-B186-47F3D8069C1B@nuovations.com (mailing list archive)
State Not Applicable, archived
Headers show
Series Add Configurable Online Check TCP Connect Timeout | expand

Commit Message

Grant Erickson Nov. 11, 2023, 6:59 p.m. UTC
This leverages the GWeb TCP connect timeout member and getter and adds
a new GLib TCP connect timeout identifier to the GWeb session object.

If the GWeb TCP connect timeout is greater than zero, a GLib connection
timeout timer and callback are added to the session such that, when they
expire, the in flight TCP connection is aborted and the session closure
function invoked with 'GWEB_HTTP_STATUS_CODE_REQUEST_TIMEOUT'.

A TCP connect timeout of zero ('0') indicates that no explicit connection
timeout will be used and no timer or callback added to the session, leaving
the timeout to the underlying operating system and its network stack.
---
 gweb/gweb.c | 99 +++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 77 insertions(+), 22 deletions(-)
diff mbox series

Patch

diff --git a/gweb/gweb.c b/gweb/gweb.c
index 5f09cb3a99cf..584f0106c5ee 100644
--- a/gweb/gweb.c
+++ b/gweb/gweb.c
@@ -75,6 +75,7 @@  struct web_session {
 	char *content_type;
 
 	GIOChannel *transport_channel;
+	guint connect_timeout;
 	guint transport_watch;
 	guint send_watch;
 
@@ -133,6 +134,8 @@  struct _GWeb {
 #define debug(web, format, arg...)				\
 	_debug(web, __FILE__, __func__, format, ## arg)
 
+static void close_session_transport(struct web_session *session);
+
 static void _debug(GWeb *web, const char *file, const char *caller,
 						const char *format, ...)
 {
@@ -154,6 +157,70 @@  static void _debug(GWeb *web, const char *file, const char *caller,
 	va_end(ap);
 }
 
+static inline void call_result_func(struct web_session *session, guint16 status)
+{
+
+	if (!session->result_func)
+		return;
+
+	if (status != GWEB_HTTP_STATUS_CODE_UNKNOWN)
+		session->result.status = status;
+
+	session->result_func(&session->result, session->user_data);
+
+}
+
+static inline void call_route_func(struct web_session *session)
+{
+	if (session->route_func)
+		session->route_func(session->address, session->addr->ai_family,
+				session->web->index, session->user_data);
+}
+
+static gboolean connect_timeout_cb(gpointer user_data)
+{
+	struct web_session *session = user_data;
+
+	debug(session->web, "session %p connect timeout after %ums",
+			session, g_web_get_connect_timeout(session->web));
+
+	session->connect_timeout = 0;
+
+	close_session_transport(session);
+
+	session->result.buffer = NULL;
+	session->result.length = 0;
+
+	call_result_func(session, GWEB_HTTP_STATUS_CODE_REQUEST_TIMEOUT);
+
+	return G_SOURCE_REMOVE;
+}
+
+static void add_connect_timeout(struct web_session *session,
+						guint timeout_ms)
+{
+	if (!session || !timeout_ms)
+		return;
+
+	debug(session->web, "add connect timeout %u ms", timeout_ms);
+
+	session->connect_timeout = g_timeout_add(timeout_ms,
+				connect_timeout_cb, session);
+}
+
+static void cancel_connect_timeout(struct web_session *session)
+{
+	if (!session)
+		return;
+
+	if (session->connect_timeout > 0) {
+		g_source_remove(session->connect_timeout);
+		session->connect_timeout = 0;
+
+		debug(session->web, "cancelled connect timeout");
+	}
+}
+
 static void close_session_transport(struct web_session *session)
 {
 	if (!session)
@@ -194,6 +261,8 @@  static void free_session(struct web_session *session)
 	if (session->resolv_action > 0)
 		g_resolv_cancel_lookup(web->resolv, session->resolv_action);
 
+	cancel_connect_timeout(session);
+
 	close_session_transport(session);
 
 	g_free(session->result.last_key);
@@ -485,26 +554,6 @@  done:
 	return timeout_ms;
 }
 
-static inline void call_result_func(struct web_session *session, guint16 status)
-{
-
-	if (!session->result_func)
-		return;
-
-	if (status != GWEB_HTTP_STATUS_CODE_UNKNOWN)
-		session->result.status = status;
-
-	session->result_func(&session->result, session->user_data);
-
-}
-
-static inline void call_route_func(struct web_session *session)
-{
-	if (session->route_func)
-		session->route_func(session->address, session->addr->ai_family,
-				session->web->index, session->user_data);
-}
-
 static bool process_send_buffer(struct web_session *session)
 {
 	GString *buf;
@@ -915,6 +964,8 @@  static gboolean received_data(GIOChannel *channel, GIOCondition cond,
 	gsize bytes_read;
 	GIOStatus status;
 
+	cancel_connect_timeout(session);
+
 	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
 		session->transport_watch = 0;
 		session->result.buffer = NULL;
@@ -1075,7 +1126,8 @@  static inline int bind_socket(int sk, int index, int family)
 	return err;
 }
 
-static int connect_session_transport(struct web_session *session)
+static int connect_session_transport(struct web_session *session,
+			guint connect_timeout_ms)
 {
 	GIOFlags flags;
 	int sk;
@@ -1133,6 +1185,8 @@  static int connect_session_transport(struct web_session *session)
 				G_IO_OUT | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
 						send_data, session);
 
+	add_connect_timeout(session, connect_timeout_ms);
+
 	return 0;
 }
 
@@ -1140,7 +1194,8 @@  static int create_transport(struct web_session *session)
 {
 	int err;
 
-	err = connect_session_transport(session);
+	err = connect_session_transport(session,
+			g_web_get_connect_timeout(session->web));
 	if (err < 0)
 		return err;