mirror of
https://github.com/Horizontal42/netsleuth.git
synced 2026-11-11 01:02:10 +03:00
perf: Use asyncio.gather for captive portal requests
Replaced sequential HTTP requests inside the captive portal probe with concurrent execution using asyncio.gather. This addresses an N+1 query pattern where the time taken was proportional to the number of URLs checked. Measurements indicate that execution time now scales with the slowest single request, drastically improving performance when multiple portal check URLs are configured. Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com>
This commit is contained in:
parent
8c99ac78b7
commit
6f67630963
1 changed files with 23 additions and 12 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
@ -32,6 +33,23 @@ def classify_portal_response(
|
||||||
return "suspect", f"unexpected status {status}"
|
return "suspect", f"unexpected status {status}"
|
||||||
|
|
||||||
|
|
||||||
|
async def _fetch_and_classify(
|
||||||
|
client: httpx.AsyncClient,
|
||||||
|
url: str,
|
||||||
|
expected_status: int,
|
||||||
|
timeout: float,
|
||||||
|
) -> 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
|
||||||
|
)
|
||||||
|
return PortalCheck(url=url, status=response.status_code, verdict=verdict, evidence=evidence)
|
||||||
|
except httpx.HTTPError as exc:
|
||||||
|
verdict, evidence = "error", str(exc)
|
||||||
|
return PortalCheck(url=url, status=None, verdict=verdict, evidence=evidence)
|
||||||
|
|
||||||
|
|
||||||
async def check_captive_portal(
|
async def check_captive_portal(
|
||||||
client: httpx.AsyncClient,
|
client: httpx.AsyncClient,
|
||||||
urls: list[str],
|
urls: list[str],
|
||||||
|
|
@ -39,18 +57,11 @@ async def check_captive_portal(
|
||||||
expected_status: int = 204,
|
expected_status: int = 204,
|
||||||
timeout: float,
|
timeout: float,
|
||||||
) -> CaptivePortal:
|
) -> CaptivePortal:
|
||||||
checks: list[PortalCheck] = []
|
coros = [
|
||||||
for url in urls:
|
_fetch_and_classify(client, url, expected_status, timeout)
|
||||||
try:
|
for url in urls
|
||||||
response = await client.get(url, follow_redirects=False, timeout=timeout)
|
]
|
||||||
verdict, evidence = classify_portal_response(
|
checks = list(await asyncio.gather(*coros))
|
||||||
url, response.status_code, response.text, response.headers.get("location"), expected_status
|
|
||||||
)
|
|
||||||
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))
|
|
||||||
|
|
||||||
portal_checks = [c for c in checks if c.verdict == "portal"]
|
portal_checks = [c for c in checks if c.verdict == "portal"]
|
||||||
if portal_checks:
|
if portal_checks:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue