penguin/tinyproxy

An L4 proxy designed to act as a tiny transparent shim

commit d4bc9aa446e9fc562633c41cd91e9c242c4eed67

author斟酌 鵬兄 <tgckpg@gmail.com>
date2026-06-18T05:05:50Z
subjectFixed long upload session got timed out
commit d4bc9aa446e9fc562633c41cd91e9c242c4eed67
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2026-06-18T05:05:50Z

    Fixed long upload session got timed out
---
 src/stream_conn.c  | 77 ++++++++++++++++++++++++++++++++++++++++++++++++------
 src/stream_conn.h  |  5 ++++
 src/stream_pipe.c  |  2 ++
 src/stream_route.h |  4 +++
 4 files changed, 80 insertions(+), 8 deletions(-)

diff --git a/src/stream_conn.c b/src/stream_conn.c
index 5fe4b6f..ff87b2d 100644
--- a/src/stream_conn.c
+++ b/src/stream_conn.c
@@ -22,6 +22,11 @@ void free_conn(conn_t *conn) {
 		return;
 	}
 
+	if (conn->idle_ev) {
+        event_free(conn->idle_ev);
+        conn->idle_ev = NULL;
+    }
+
 	if (conn->client != NULL) {
 		bufferevent_free(conn->client);
 	}
@@ -43,15 +48,67 @@ static void set_connect_timeout(conn_t *conn, const struct route *r)
 	bufferevent_set_timeouts(conn->upstream, NULL, &connect_timeout);
 }
 
-static void set_idle_timeouts(conn_t *conn, const struct route *r)
+static void clear_idle_timeout(conn_t *conn)
 {
-	struct timeval idle_timeout = {
-		.tv_sec = r->opts.idle_timeout_sec,
+	bufferevent_set_timeouts(conn->client, NULL, NULL);
+	bufferevent_set_timeouts(conn->upstream, NULL, NULL);
+}
+
+static void schedule_conn_idle_timer(conn_t *conn)
+{
+	if (!conn->idle_ev)
+		return;
+
+	if (conn->route->opts.idle_timeout_sec <= 0)
+		return;
+
+	struct timeval tv = {
+		.tv_sec = conn->route->opts.idle_timeout_sec,
 		.tv_usec = 0,
 	};
 
-	bufferevent_set_timeouts(conn->client, &idle_timeout, &idle_timeout);
-	bufferevent_set_timeouts(conn->upstream, &idle_timeout, &idle_timeout);
+	evtimer_add(conn->idle_ev, &tv);
+}
+
+static void conn_idle_timeout_cb(evutil_socket_t fd, short events, void *arg)
+{
+	(void)events;
+	(void)fd;
+
+	conn_t *conn = arg;
+
+	if (!conn) {
+		return;
+	}
+
+	if (conn->activity_seq != conn->idle_check_seq) {
+		conn->idle_check_seq = conn->activity_seq;
+		schedule_conn_idle_timer(conn);
+		return;
+	}
+
+	LOG_WARN("stream connection idle timed out",
+		"listen", _LOGV_ENDPOINT(&conn->route->listen),
+		"upstream", _LOGV_ENDPOINT(&conn->route->upstream)
+	);
+
+	free_conn(conn);
+}
+
+static int start_conn_idle_timer(conn_t *conn)
+{
+	if (conn->route->opts.idle_timeout_sec <= 0)
+		return 0;
+
+	conn->activity_seq = 1;
+	conn->idle_check_seq = conn->activity_seq;
+
+	conn->idle_ev = evtimer_new(conn->owner->base, conn_idle_timeout_cb, conn);
+	if (!conn->idle_ev)
+		return -1;
+
+	schedule_conn_idle_timer(conn);
+	return 0;
 }
 
 void set_client_idle_timeout(conn_t *conn, const struct route *r)
@@ -229,7 +286,8 @@ void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg)
 
 	if (events & BEV_EVENT_CONNECTED) {
 		conn->upstream_connected = true;
-		set_idle_timeouts(conn, conn->route);
+		clear_idle_timeout(conn);
+		start_conn_idle_timer(conn);
 
 		bufferevent_enable(conn->client, EV_READ | EV_WRITE);
 		return;
@@ -409,6 +467,9 @@ void worker_adopt_client_fd(struct worker *w, struct worker_stream_client_msg *a
 
 	const struct route *r = ac->route;
 
+	conn->activity_seq = 1;
+	conn->idle_check_seq = 1;
+
 	conn->owner = w;
 	conn->route = r;
 
@@ -582,8 +643,8 @@ int stream_route_adopt_client_for_fuzz(
 	ac.route = r;
 
 	if (peer_addr != NULL &&
-	    peer_addr_len > 0 &&
-	    peer_addr_len <= sizeof(ac.peer_addr)) {
+		peer_addr_len > 0 &&
+		peer_addr_len <= sizeof(ac.peer_addr)) {
 		memcpy(&ac.peer_addr, peer_addr, peer_addr_len);
 		ac.peer_addr_len = peer_addr_len;
 	}
diff --git a/src/stream_conn.h b/src/stream_conn.h
index 320e2ef..6033344 100644
--- a/src/stream_conn.h
+++ b/src/stream_conn.h
@@ -20,6 +20,11 @@ void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg);
 
 void worker_adopt_client_fd(struct worker *w, struct worker_stream_client_msg *ac);
 
+static inline void conn_touch(conn_t *conn)
+{
+    conn->activity_seq++;
+}
+
 #ifdef FUZZ
 int stream_route_adopt_client_for_fuzz(
 	struct event_base *base,
diff --git a/src/stream_pipe.c b/src/stream_pipe.c
index b511ad1..eddaacd 100644
--- a/src/stream_pipe.c
+++ b/src/stream_pipe.c
@@ -35,6 +35,7 @@ void pipe_client_read_cb(struct bufferevent *client, void *arg)
 		stream_sniff_peek_client_input(&conn->sniff, src);
 	}
 
+	conn_touch(conn);
 	evbuffer_add_buffer(dst, src);
 
 	if (evbuffer_get_length(dst) >= STREAM_READ_HIGH_WATER) {
@@ -72,6 +73,7 @@ void pipe_upstream_read_cb(struct bufferevent *upstream, void *arg)
 	);
 #endif
 
+	conn_touch(conn);
 	evbuffer_add_buffer(dst, src);
 
 	if (evbuffer_get_length(dst) >= STREAM_READ_HIGH_WATER) {
diff --git a/src/stream_route.h b/src/stream_route.h
index adc08a8..a9e243a 100644
--- a/src/stream_route.h
+++ b/src/stream_route.h
@@ -35,6 +35,10 @@ typedef struct conn_s {
 	bool upstream_connected;
 	bool close_after_client_eof;
 	bool close_client_after_drain;
+
+	uint64_t activity_seq;
+	uint64_t idle_check_seq;
+	struct event *idle_ev;
 } conn_t;
 
 int start_stream_route(