|
#!/usr/bin/env -S uv run --script |
|
# /// script |
|
# requires-python = ">=3.10" |
|
# dependencies = [ |
|
# "aiohttp", |
|
# ] |
|
# /// |
|
|
|
""" |
|
AsyncIO AIOHTTP may be faster and/or use less resources than ThreadPool version |
|
""" |
|
|
|
import argparse |
|
import asyncio |
|
|
|
import aiohttp |
|
|
|
DEFAULT_PORTS = [22, 80, 443, 143, 8080, 3389] |
|
TIMEOUT = 2 # seconds |
|
MAX_CONCURRENT = 5 # limit simultaneous requests |
|
|
|
|
|
async def check_port( |
|
session: aiohttp.ClientSession, |
|
port: int, |
|
timeout: aiohttp.ClientTimeout, |
|
semaphore: asyncio.Semaphore, |
|
) -> int: |
|
"""Check if a port is accessible via portquiz.net""" |
|
async with semaphore: |
|
url = f"http://portquiz.net:{port}" |
|
async with session.get(url, timeout=timeout) as response: |
|
return response.status |
|
|
|
|
|
def _port_type(raw: str) -> int: |
|
port = int(raw) |
|
if not 1 <= port <= 65535: |
|
raise argparse.ArgumentTypeError("port must be 1-65535") |
|
return port |
|
|
|
|
|
async def main(ports: list[int], timeout: int, max_concurrent: int): |
|
|
|
to = aiohttp.ClientTimeout(total=timeout) |
|
semaphore = asyncio.Semaphore(max_concurrent) |
|
|
|
async with aiohttp.ClientSession() as session: |
|
tasks = [check_port(session, port, to, semaphore) for port in ports] |
|
results = await asyncio.gather(*tasks, return_exceptions=True) |
|
|
|
for port, result in zip(ports, results): |
|
if isinstance(result, aiohttp.ClientError): |
|
print(f"FAIL: {port} {result}") |
|
elif isinstance(result, asyncio.TimeoutError): |
|
print(f"FAIL: {port} timeout") |
|
elif isinstance(result, Exception): |
|
print(f"FAIL: {port} {result}") |
|
elif result != 200: |
|
print(f"FAIL: {port} returned HTTP {result}") |
|
else: |
|
print(f"OK: {port} is open") |
|
|
|
|
|
if __name__ == "__main__": |
|
parser = argparse.ArgumentParser(description="Port quiz tester") |
|
parser.add_argument( |
|
"-p", |
|
"--ports", |
|
nargs="+", |
|
type=_port_type, |
|
help="Ports to test", |
|
default=DEFAULT_PORTS, |
|
) |
|
parser.add_argument( |
|
"-t", |
|
"--timeout", |
|
type=int, |
|
help="Timeout for each port check in seconds", |
|
default=TIMEOUT, |
|
) |
|
parser.add_argument( |
|
"-c", |
|
"--concurrent", |
|
type=int, |
|
help="Maximum concurrent requests", |
|
default=MAX_CONCURRENT, |
|
) |
|
args = parser.parse_args() |
|
|
|
asyncio.run(main(args.ports, args.timeout, args.concurrent)) |