Observed since 20 Oct, 19:57 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Advanced network connectivity diagnostics for Python applications
advanced-connection-test is a Python package for advanced network connectivity diagnostics. It allows you to accurately distinguish between various network states — no connection, LAN only, captive portal, mandatory proxy, SSL errors, and working connection — in a robust, safe, and transparent way.
Provide a reliable tool to:
pip install advanced-connection-test
Or, for local development with test dependencies:
git clone https://github.com/raffaellof/connection-test.git
cd connection-test
pip install -e ".[dev]"
Requirements: Python 3.7+, aiohttp >= 3.8.0
import asyncio
from connection_test import enhanced_connection_test, ConnectionStatus
result = asyncio.run(enhanced_connection_test())
if result.status == ConnectionStatus.CONNECTED_DIRECT:
print(f"Online in {result.test_duration_ms}ms")
elif result.status == ConnectionStatus.CAPTIVE_PORTAL:
print(f"Captive portal detected: {result.captive_portal_url}")
elif result.status == ConnectionStatus.PROXY_REQUIRED:
print(f"Proxy required: {result.detected_proxy_url}")
elif result.status == ConnectionStatus.PROXY_AUTH_FAILED:
print("Proxy credentials incorrect or missing")
elif result.status == ConnectionStatus.SSL_ERROR:
print("SSL error — check system date/time")
elif result.status == ConnectionStatus.LAN_ONLY:
print("Local network OK, but Internet unreachable")
elif result.status == ConnectionStatus.NO_CONNECTION:
print("No network detected")
Useful on networks where proxies block some sites but not others: pass the critical URLs for your application instead of relying on the default list.
from connection_test import enhanced_connection_test, ConnectionTestConfig
config = ConnectionTestConfig(
test_urls=["https://my-api.company.com", "https://www.google.com"],
timeout=10,
global_timeout=30,
)
result = asyncio.run(enhanced_connection_test(config=config))
print(result.status.value, result.message)
Tests all URLs in the list (instead of exiting on the first success) and returns details for each:
result = asyncio.run(enhanced_connection_test(test_all_urls=True))
for url_result in result.details.get("results_per_url", []):
print(f"{url_result['url']}: {'OK' if url_result['success'] else 'FAIL'}")
ConnectionStatus)| State | Description | requires_action |
|---|---|---|
CONNECTED_DIRECT | Working Internet connection (direct or transparent proxy) | No |
CONNECTED_PROXY | Working connection via configured proxy | No |
NO_CONNECTION | No active network interface | No |
LAN_ONLY | Local network OK, Internet unreachable (DNS fails) | No |
CAPTIVE_PORTAL | Access blocked by authentication portal | Yes |
CAPTIVE_PORTAL_PROXY | Captive portal detected with proxy environment configured | Yes |
PROXY_REQUIRED | Proxy required but not configured (detected on local port) | Yes |
PROXY_AUTH_FAILED | Proxy configured (or detected) but authentication failed (HTTP 407) | Yes |
PROXY_STALE | Outdated proxy configuration; direct connection now works | Yes |
SSL_ERROR | SSL errors on all URLs (system clock, root certificates) | Yes |
UNKNOWN_ERROR | State undetectable or global timeout exceeded | No |
Note:
detected_proxy_urlin the results always contains the proxy URL with masked credentials (http://***@host:port), never in clear text.
The test runs up to 6 sequential phases (Phase 0–5) with early-exit on the first conclusive result.
What it does:
Before starting any network test, it reads the environment variables
HTTP_PROXY, HTTPS_PROXY (and lowercase variants) to know if a proxy
is already configured.
How:
Reads os.getenv() and computes safe_proxy_url (masked URL) for logging.
Does not perform any network request. The collected information is used
in phases 3 and 4.
Why:
Separating env var reading from execution ensures safe_proxy_url is always
available for logs even in case of global timeout (the partial_state is updated immediately).
Outcome: None — preparatory phase, always proceeds.
What it does:
First checks if at least one network interface is active by attempting a TCP connection
to 8.8.8.8:53 (Google's public DNS server), then validates DNS resolution.
How:
SOCK_STREAM (TCP) socket with a 1-second timeout.www.google.com, github.com, cloudflare.com)
with a 2-second timeout per domain.Why:
This quickly separates transport-level failure from Internet reachability.
Socket failure indicates physical/link issues (unplugged cable, Wi-Fi off, broken driver).
DNS failure with socket success indicates LAN-only/split-horizon conditions.
UDP (SOCK_DGRAM) is not used because socket.connect() with UDP does not send
data nor verify reachability, always returning success even without a network.
Outcomes: NO_CONNECTION (socket fail) or LAN_ONLY (DNS fail).
What it does: Performs HTTPS requests to the configured URLs without a proxy, checking for 2xx status and domain match between requested URL and final response URL.
How:
Uses aiohttp.ClientSession without trust_env=True (default behavior), so proxy env vars
are ignored by aiohttp. unset_proxy_env_async() is kept as a defensive safeguard.
Supports two modes: performance (early-exit on first success) and
diagnostic (test_all_urls=True, tests all URLs).
Why:
Verifies real application connectivity. Domain match detects cross-domain redirects
typical of captive portals. Separate SSL error counting allows distinguishing
SSL_ERROR from other failures. If direct connectivity works while a proxy is configured
in env vars, that proxy configuration is considered stale.
Outcomes: CONNECTED_DIRECT (success without configured proxy),
PROXY_STALE (direct works with configured proxy), SSL_ERROR (all attempted URLs
returned SSL errors — in performance mode, only the actually attempted URLs are considered),
or proceeds to the next phase.
What it does:
If HTTP_PROXY/HTTPS_PROXY are configured, tests that proxy explicitly.
How:
Sends HTTPS requests through the configured proxy (proxy=... in aiohttp request).
PROXY_AUTH_FAILED.CONNECTED_PROXY.PROXY_STALE.CAPTIVE_PORTAL_PROXYPROXY_REQUIREDWhy: This phase distinguishes invalid credentials, stale proxy settings, proxy-dependent networks, and captive portal scenarios in proxy-configured environments.
Outcomes: CONNECTED_PROXY, PROXY_AUTH_FAILED, PROXY_STALE,
CAPTIVE_PORTAL_PROXY, PROXY_REQUIRED.
What it does: If no configured proxy solved the issue, scans local ports 8080, 3128, and 8888 looking for an undeclared proxy.
How:
Uses asyncio.open_connection() with a 0.5s timeout per port.
Each open port is validated with a real HTTP request through it.
PROXY_AUTH_FAILED.PROXY_REQUIRED.Outcome: PROXY_AUTH_FAILED, PROXY_REQUIRED, or proceeds.
What it does: Queries 3 dedicated HTTP endpoints to detect the presence of a captive portal using majority vote.
How: Sends HTTP requests (not HTTPS, deliberately interceptable) to:
connectivitycheck.gstatic.com/generate_204 → expected HTTP 204msftconnecttest.com/connecttest.txt → expected body "Microsoft Connect Test"detectportal.firefox.com/success.txt → expected body "success"If ≥50% of conclusive tests indicate interception, the captive portal is confirmed.
Why: A single endpoint may be temporarily unreachable (CDN down, corporate firewall) causing false positives. Three independent vendors with majority vote drastically reduce this possibility. Requests use HTTP because captive portals only intercept cleartext traffic — HTTPS cannot be altered without the certificate revealing the interception.
Outcome: CAPTIVE_PORTAL if confirmed, UNKNOWN_ERROR as final fallback.
_mask_proxy_credentials() before any logging output.os.environ — prevents race conditions in concurrent
contexts that simultaneously modify system proxy variables.enhanced_connection_test()async def enhanced_connection_test(
config: Optional[ConnectionTestConfig] = None,
test_urls: Optional[List[str]] = None,
timeout: int = 5,
test_all_urls: bool = False,
global_timeout: int = 60,
) -> ConnectionTestResult
| Parameter | Type | Default | Description |
|---|---|---|---|
config | ConnectionTestConfig | None | Configuration object (takes precedence over single parameters) |
test_urls | List[str] | None | URLs to test (default: GitHub, Google, PyPI, npm) |
timeout | int | 5 | Timeout for each HTTP request (seconds) |
test_all_urls | bool | False | If True, diagnostic mode (tests all URLs) |
global_timeout | int | 60 | Maximum timeout for the entire function (seconds) |
ConnectionTestConfigfrom connection_test import ConnectionTestConfig
config = ConnectionTestConfig(
test_urls=["https://example.com"],
timeout=10,
test_all_urls=False,
global_timeout=30,
)
ConnectionTestResultThe result contains:
status — value of ConnectionStatusmessage — description in English for the end userdetails — dictionary with technical information (tested URL, duration, error type, etc.)requires_action — True if user action is requiredsuggested_route — suggested path (e.g., '/proxy_login', '/auth/captive_portal')detected_proxy_url — detected proxy URL (masked credentials)captive_portal_url — intercepted captive portal URLtest_duration_ms — total duration in millisecondsMIT — see LICENSE
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.4 | 12 Jun 2026 | 5 |
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Observed since 20 Oct, 19:57 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:fbc90e4ab2e53e6dfb94451c286dc49051eca49d4b155e82782c75fbc61230b9