penguin/tinyproxy

An L4 proxy designed to act as a tiny transparent shim

tests/test_tcp_stress.py

raw ยท 1832 bytes

import asyncio
import time
import os

from .support import (
	LISTEN_HOST,
	PROXY_PORT,
	SkipTest,
	run_tinyproxy_with_conf,
)

PROXY_PORT = PROXY_PORT + 1

async def close_writer(writer: asyncio.StreamWriter) -> None:
	try:
		writer.close()
		await writer.wait_closed()
	except ConnectionResetError:
		pass


async def run_one_concurrent(
	i: int,
	sem: asyncio.Semaphore,
) -> None:
	async with sem:
		reader, writer = await asyncio.open_connection(LISTEN_HOST, PROXY_PORT)

		try:
			got = await asyncio.wait_for(reader.read(4096), timeout=10.0)

			if not got:
				raise AssertionError(f"worker {i} got empty response")

			# Keep this weak on purpose. Other tests cover exact builtin output.
			if b":" not in got:
				raise AssertionError(f"worker {i} got invalid client_addr response: {got!r}")
		finally:
			await close_writer(writer)


async def test_concurrent_connections() -> None:
	proxy_bin = os.environ.get("TINYPROXY_BIN")
	if not proxy_bin:
		raise SkipTest("TINYPROXY_BIN is not set")

	total = int(os.environ.get("TOTAL", "10000"))
	concurrency = int(os.environ.get("CONCURRENCY", "10000"))

	conf_text = (
		f"listen"
		f" tcp {LISTEN_HOST}:{PROXY_PORT}"
		f" builtin client_addr\n"
	)

	started_at = time.monotonic()
	async with run_tinyproxy_with_conf(
		proxy_bin=proxy_bin,
		conf_text=conf_text,
		args=["-w", "0"],
		listen_host=LISTEN_HOST,
		listen_port=PROXY_PORT,
		proto="tcp",
	):
		sem = asyncio.Semaphore(concurrency)

		tasks = [
			asyncio.create_task(run_one_concurrent(i, sem))
			for i in range(total)
		]

		done = 0

		for task in asyncio.as_completed(tasks):
			await task
			done += 1

			if done % 1000 == 0:
				elapsed = time.monotonic() - started_at
				print(f"  {elapsed:.1f}s completed ({done}/{total})")


TESTS = [
	("test_concurrent_connections", test_concurrent_connections),
]