diff --git a/src/netsleuth/probes/captive_portal.py b/src/netsleuth/probes/captive_portal.py index ce51948..857aa47 100644 --- a/src/netsleuth/probes/captive_portal.py +++ b/src/netsleuth/probes/captive_portal.py @@ -1,5 +1,6 @@ from __future__ import annotations +import asyncio from urllib.parse import urlsplit import httpx @@ -28,7 +29,10 @@ def classify_portal_response( if redirect_host and redirect_host != requested_host: return "portal", f"redirected to a different host ({redirect_host})" if status == 200 and body: - return "portal", f"200 with a non-empty body instead of the expected {expected_status}" + return ( + "portal", + f"200 with a non-empty body instead of the expected {expected_status}", + ) return "suspect", f"unexpected status {status}" @@ -39,18 +43,24 @@ async def check_captive_portal( expected_status: int = 204, timeout: float, ) -> CaptivePortal: - checks: list[PortalCheck] = [] - for url in urls: + async def _check_url(url: str) -> PortalCheck: try: response = await client.get(url, follow_redirects=False, timeout=timeout) verdict, evidence = classify_portal_response( - url, response.status_code, response.text, response.headers.get("location"), expected_status + url, + response.status_code, + response.text, + response.headers.get("location"), + expected_status, + ) + return PortalCheck( + url=url, status=response.status_code, verdict=verdict, evidence=evidence ) except httpx.HTTPError as exc: verdict, evidence = "error", str(exc) - checks.append(PortalCheck(url=url, status=None, verdict=verdict, evidence=evidence)) - continue - checks.append(PortalCheck(url=url, status=response.status_code, verdict=verdict, evidence=evidence)) + return PortalCheck(url=url, status=None, verdict=verdict, evidence=evidence) + + checks = list(await asyncio.gather(*(_check_url(url) for url in urls))) portal_checks = [c for c in checks if c.verdict == "portal"] if portal_checks: