penguin/tinyproxy

An L4 proxy designed to act as a tiny transparent shim

commit 8e590e3ab669e8e687f123d5cfd564072c29e6db

author斟酌 鵬兄 <tgckpg@gmail.com>
date2026-06-06T11:46:36Z
subjectmanual test scripts
commit 8e590e3ab669e8e687f123d5cfd564072c29e6db
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2026-06-06T11:46:36Z

    manual test scripts
---
 devtools/close-wait/large_upstream.py | 34 ++++++++++++++++++++++++++++++
 devtools/close-wait/slow_client.py    | 39 +++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/devtools/close-wait/large_upstream.py b/devtools/close-wait/large_upstream.py
new file mode 100755
index 0000000..6e9a9a9
--- /dev/null
+++ b/devtools/close-wait/large_upstream.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+import socket
+import threading
+
+HOST = "127.0.0.1"
+PORT = 12700
+BODY_SIZE = 4 * 1024 * 1024
+
+BODY = b"x" * BODY_SIZE
+RESP = (
+	b"HTTP/1.1 200 OK\r\n"
+	+ f"Content-Length: {BODY_SIZE}\r\n".encode()
+	+ b"Connection: close\r\n"
+	+ b"\r\n"
+	+ BODY
+)
+
+def handle(c):
+	try:
+		c.recv(4096)
+		c.sendall(RESP)
+	finally:
+		c.close()
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+s.bind((HOST, PORT))
+s.listen(4096)
+
+print(f"serving {BODY_SIZE} bytes on {HOST}:{PORT}")
+
+while True:
+	c, _ = s.accept()
+	threading.Thread(target=handle, args=(c,), daemon=True).start()
diff --git a/devtools/close-wait/slow_client.py b/devtools/close-wait/slow_client.py
new file mode 100755
index 0000000..2c656fc
--- /dev/null
+++ b/devtools/close-wait/slow_client.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+import socket
+import time
+
+HOST = "127.0.0.1"
+PORT = 12800
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+# Make receive buffer reasonably large so the kernel can queue data
+# while the application is sleeping.
+s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8 * 1024 * 1024)
+
+s.connect((HOST, PORT))
+s.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")
+
+print("request sent; not reading for 5s")
+time.sleep(5)
+
+total = 0
+chunks = []
+
+try:
+	while True:
+		b = s.recv(65536)
+		if not b:
+			break
+		chunks.append(b)
+		total += len(b)
+except ConnectionResetError as e:
+	print(f"RESET after {total} bytes: {e}")
+	raise
+
+print(f"read total={total} bytes")
+data = b"".join(chunks)
+
+header, _, body = data.partition(b"\r\n\r\n")
+print(header.decode(errors="replace"))
+print(f"body bytes={len(body)}")