- Rename python/configs/usage.md → usage-simple.md so both scripts have a parallel runnable reference file in the same directory - Add python/configs/usage-phases.md: runnable sh commands + real expected output for phases.py against every error-path config, including a note on the key difference from simple.py (HTTP 4xx exits 0 in phases.py vs 1 in simple.py because phases.py completes at the network level) - Update doc cross-links in docs/usage/py-simple.md and py-phases.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
186 lines
5.0 KiB
Markdown
186 lines
5.0 KiB
Markdown
# `simple.py` — Example Config Files
|
|
|
|
Each `.txt` file in this directory targets a distinct error path so you can
|
|
observe `simple.py`'s behaviour for every failure class. Run them from the
|
|
`python/` directory.
|
|
|
|
---
|
|
|
|
## `all-ok.txt` — all sites respond successfully
|
|
|
|
```sh
|
|
python simple.py configs/all-ok.txt
|
|
echo "exit: $?"
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
OK xxx.xx ms https://example.com
|
|
OK xxx.xx ms https://www.google.com
|
|
OK xxx.xx ms https://www.iana.org
|
|
exit: 0
|
|
```
|
|
|
|
---
|
|
|
|
## `dns-failure.txt` — DNS resolution fails
|
|
|
|
Hostnames use the `.invalid` TLD (RFC 2606), which is guaranteed never to
|
|
resolve. Fails in milliseconds.
|
|
|
|
```sh
|
|
python simple.py configs/dns-failure.txt
|
|
echo "exit: $?"
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
FAIL ([Errno 8] nodename nor servname provided, or not known) https://this-host-does-not-exist.invalid
|
|
FAIL ([Errno 8] nodename nor servname provided, or not known) http://no.such.host.invalid
|
|
exit: 1
|
|
```
|
|
|
|
---
|
|
|
|
## `connection-refused.txt` — TCP connection refused
|
|
|
|
Loopback addresses with no server listening. The OS rejects the SYN immediately
|
|
(sub-millisecond failure).
|
|
|
|
```sh
|
|
python simple.py configs/connection-refused.txt
|
|
echo "exit: $?"
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
FAIL ([Errno 61] Connection refused) http://127.0.0.1:9999
|
|
FAIL ([Errno 61] Connection refused) http://127.0.0.1:19999
|
|
exit: 1
|
|
```
|
|
|
|
---
|
|
|
|
## `timeout.txt` — connect hangs until timeout
|
|
|
|
Non-routable IPs (private/documentation ranges) silently drop TCP SYNs.
|
|
`simple.py` waits the full **10-second** timeout per host.
|
|
|
|
> ⚠️ This run takes approximately **20 seconds** to complete.
|
|
|
|
```sh
|
|
python simple.py configs/timeout.txt
|
|
echo "exit: $?"
|
|
```
|
|
|
|
Expected output (after ~20 s):
|
|
```
|
|
FAIL (timed out) http://10.255.255.1/
|
|
FAIL (timed out) http://192.0.2.1/
|
|
exit: 1
|
|
```
|
|
|
|
---
|
|
|
|
## `tls-errors.txt` — TLS certificate errors
|
|
|
|
Uses [badssl.com](https://badssl.com) endpoints with intentionally broken
|
|
certificates. Requires internet access; badssl.com must be reachable.
|
|
|
|
```sh
|
|
python simple.py configs/tls-errors.txt
|
|
echo "exit: $?"
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
FAIL ([SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired …) https://expired.badssl.com/
|
|
FAIL ([SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate …) https://self-signed.badssl.com/
|
|
FAIL ([SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer …) https://incomplete-chain.badssl.com/
|
|
exit: 1
|
|
```
|
|
|
|
> **Note:** badssl.com occasionally resets connections mid-handshake. When that
|
|
> happens, the error reads `[Errno 54] Connection reset by peer` instead of
|
|
> `CERTIFICATE_VERIFY_FAILED`. The key behaviour is the same — every entry is
|
|
> reported as `FAIL` and the script exits 1. This demonstrates that `simple.py`
|
|
> handles both SSL-level and network-level TLS failures uniformly.
|
|
|
|
---
|
|
|
|
## `http-errors.txt` — HTTP 4xx / 5xx status codes
|
|
|
|
`urllib.request.urlopen()` raises `HTTPError` for 4xx/5xx, so `simple.py`
|
|
reports these as `FAIL` even though the server responded. The URLs are real
|
|
paths that reliably return 404 on stable public servers. Requires internet
|
|
access.
|
|
|
|
```sh
|
|
python simple.py configs/http-errors.txt
|
|
echo "exit: $?"
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
FAIL (HTTP Error 404: Not Found) https://www.google.com/this-page-does-not-exist-at-all-1234567890
|
|
FAIL (HTTP Error 404: Not Found) https://github.com/this-repo-does-not-exist-abcxyz123/no-way
|
|
FAIL (HTTP Error 404: Not Found) https://www.iana.org/this-page-does-not-exist-either
|
|
exit: 1
|
|
```
|
|
|
|
---
|
|
|
|
## `mixed.txt` — one of each (no timeout)
|
|
|
|
One successful site followed by one entry from each fast error class (DNS,
|
|
connection-refused, TLS, HTTP). Timeout is excluded so the run completes
|
|
quickly.
|
|
|
|
```sh
|
|
python simple.py configs/mixed.txt
|
|
echo "exit: $?"
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
OK xxx.xx ms https://example.com
|
|
FAIL ([Errno 8] nodename nor servname …) https://no.such.host.invalid
|
|
FAIL ([Errno 61] Connection refused) http://127.0.0.1:9999
|
|
FAIL ([SSL: CERTIFICATE_VERIFY_FAILED] …) https://self-signed.badssl.com/
|
|
FAIL (HTTP Error 404: Not Found) https://github.com/this-repo-does-not-exist-abcxyz123/no-way
|
|
exit: 1
|
|
```
|
|
|
|
> **Note:** the TLS entry may occasionally show `[Errno 54] Connection reset by peer`
|
|
> instead of `CERTIFICATE_VERIFY_FAILED` — badssl.com sometimes drops the connection
|
|
> before the handshake completes. Both are reported as `FAIL`; run `tls-errors.txt`
|
|
> in isolation for consistent cert-verification messages.
|
|
|
|
---
|
|
|
|
## Run all configs
|
|
|
|
Iterate over every config file (skip `timeout.txt` for a quick sweep, or omit
|
|
the exclusion to run all):
|
|
|
|
```sh
|
|
# Quick sweep — skips the slow timeout config
|
|
for f in configs/*.txt; do
|
|
[[ "$f" == *timeout* ]] && continue
|
|
echo
|
|
echo "=== $f ==="
|
|
python simple.py "$f"
|
|
echo "exit: $?"
|
|
done
|
|
```
|
|
|
|
```sh
|
|
# Full sweep — includes timeout (~20 s extra)
|
|
for f in configs/*.txt; do
|
|
echo
|
|
echo "=== $f ==="
|
|
python simple.py "$f"
|
|
echo "exit: $?"
|
|
done
|
|
```
|