penguin/tinyproxy

An L4 proxy designed to act as a tiny transparent shim

commit f0780f01e405364779720f38b2aec12c6b63caf2

author斟酌 鵬兄 <tgckpg@gmail.com>
date2026-06-05T18:20:25Z
subjectunify close behavior
commit f0780f01e405364779720f38b2aec12c6b63caf2
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2026-06-05T18:20:25Z

    unify close behavior
---
 devtools/ab_test.sh  | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/stream_builtin.c | 21 +++++++++++++---
 src/stream_conn.c    | 10 +++-----
 3 files changed, 92 insertions(+), 9 deletions(-)

diff --git a/devtools/ab_test.sh b/devtools/ab_test.sh
new file mode 100755
index 0000000..9e98c1f
--- /dev/null
+++ b/devtools/ab_test.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Run from repo root:
+#   sudo ./test_tinyproxy.sh
+#
+# Routes:
+#   :12700 -> builtin http_ok
+#   :12800 -> TCP proxy to 127.0.0.1:12700
+
+NOFILE="${NOFILE:-1048576}"
+WORKERS="${WORKERS:-0}"
+TINYPROXY="${TINYPROXY:-/usr/local/bin/tinyproxy}"
+
+LISTEN_BACKLOG="${LISTEN_BACKLOG:-65535}"
+SYN_BACKLOG="${SYN_BACKLOG:-65535}"
+EPHEMERAL_RANGE="${EPHEMERAL_RANGE:-10000 65535}"
+
+if [ "$(id -u)" -ne 0 ]; then
+	echo "error: run as root: sudo $0" >&2
+	exit 1
+fi
+
+if [ ! -x "$TINYPROXY" ]; then
+	echo "error: tinyproxy binary not found/executable: $TINYPROXY" >&2
+	exit 1
+fi
+
+echo "== tinyproxy stress-test launcher =="
+echo "tinyproxy:        $TINYPROXY"
+echo "workers:          $WORKERS"
+echo "nofile:           $NOFILE"
+echo "somaxconn:        $LISTEN_BACKLOG"
+echo "tcp_max_syn_backlog: $SYN_BACKLOG"
+echo "ephemeral ports:  $EPHEMERAL_RANGE"
+echo
+
+echo "== raising process fd limit =="
+ulimit -n "$NOFILE"
+echo "soft nofile: $(ulimit -Sn)"
+echo "hard nofile: $(ulimit -Hn)"
+echo
+
+echo "== applying temporary sysctl tuning =="
+sysctl -w "net.core.somaxconn=$LISTEN_BACKLOG"
+sysctl -w "net.ipv4.tcp_max_syn_backlog=$SYN_BACKLOG"
+sysctl -w "net.ipv4.ip_local_port_range=$EPHEMERAL_RANGE"
+echo
+
+echo "== current relevant sysctls =="
+sysctl net.core.somaxconn
+sysctl net.ipv4.tcp_max_syn_backlog
+sysctl net.ipv4.ip_local_port_range
+echo
+
+echo "== starting tinyproxy =="
+echo "test direct builtin:"
+echo "  ab -c 10000 -n 100000 http://127.0.0.1:12700/"
+echo
+echo "test chained tcp->tcp->builtin:"
+echo "  ab -c 10000 -n 100000 http://127.0.0.1:12800/"
+echo
+echo "watch sockets:"
+echo "  ./watch_tinyproxy_sockets.sh"
+echo
+
+exec "$TINYPROXY" \
+	-w"$WORKERS" \
+	-L "tcp :12700 builtin http_ok" \
+	-L "tcp :12800 tcp 127.0.0.1:12700"
diff --git a/src/stream_builtin.c b/src/stream_builtin.c
index 60a1617..f0f406c 100644
--- a/src/stream_builtin.c
+++ b/src/stream_builtin.c
@@ -108,11 +108,26 @@ int start_stream_builtin(conn_t *conn)
 	switch (res.action) {
 	case X_BUILTIN_ACTION_CLOSE:
 		if (res.data_len > 0) {
+			bufferevent_setcb(
+				conn->client,
+				builtin_client_read_cb,
+				builtin_close_after_write_cb,
+				stream_client_event_cb,
+				conn
+			);
 			bufferevent_write(conn->client, res.data, res.data_len);
-			bufferevent_setcb(conn->client, NULL, builtin_close_after_write_cb, stream_client_event_cb, conn);
-			bufferevent_enable(conn->client, EV_WRITE);
+			bufferevent_enable(conn->client, EV_READ | EV_WRITE);
 		} else {
-			free_conn(conn);
+			finish_client_write(conn);
+			conn->close_after_client_eof = true;
+			bufferevent_setcb(
+				conn->client,
+				builtin_client_read_cb,
+				NULL,
+				stream_client_event_cb,
+				conn
+			);
+			bufferevent_enable(conn->client, EV_READ);
 		}
 		return 0;
 
diff --git a/src/stream_conn.c b/src/stream_conn.c
index 86466ae..43bdd20 100644
--- a/src/stream_conn.c
+++ b/src/stream_conn.c
@@ -94,7 +94,9 @@ static void drain_client_then_close(conn_t *conn)
 	}
 
 	if (!client_has_pending_output(conn)) {
-		free_conn(conn);
+		finish_client_write(conn);
+		conn->close_client_after_drain = false;
+		conn->close_after_client_eof = true;
 	}
 }
 
@@ -237,11 +239,7 @@ void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg)
 	}
 
 	if (events & BEV_EVENT_EOF) {
-		if (client_has_pending_output(conn)) {
-			goto out_drain_client;
-		}
-
-		goto out_free;
+		goto out_drain_client;
 	}
 
 	LOG_DEBUG("unhandled upstream event",