Модуль 11 · Урок 42 із 58
HTTP client у Python: URL, timeout, errors і JSON
Надійний HTTP client не починається з одного urlopen. Він визначає дозволені endpoints, кодує URL, ставить timeout, відрізняє HTTP response від transport failure і перевіряє representation до того, як довірити дані application code.
Безпечна HTTP-практика модуля 11
Пакет містить synthetic fixtures, loopback-only HTTP server, bounded urllib client, Problem Details і contract tests. Тести виконують реальні request/response лише на 127.0.0.1 без Internet, персональних чи production-даних, credentials і production endpoints.
URL збирають компонентами
User input не вставляють у URL через concatenation. Path segment percent-encode окремо, query створюють через urlencode(), а результат перевіряють через urlsplit(). Важливо: parser розбирає структуру, але сам по собі не доводить, що URL безпечний.
from urllib.parse import urlencode, urlsplit
query = urlencode({"status": "open", "tag": "HTTP basics"})
url = f"http://127.0.0.1:8080/tasks?{query}"
parts = urlsplit(url)
if parts.hostname not in {"127.0.0.1", "localhost"}:
raise ValueError("endpoint_not_allowed")У реальному service allowlist може включати лише configured origin, scheme і port. Redirect target перевіряють повторно, інакше початково дозволений URL може привести до внутрішньої адреси.
Request фіксує intent і metadata
urllib.request.Request приймає URL, bytes content, headers і method. JSON серіалізують детерміновано, кодують UTF-8 та позначають Content-Type. Authentication secret надходить зі secret store й ніколи не потрапляє до lab, URL query, exception message або debug log.
import json
from urllib.request import Request
payload = json.dumps({"title": "Synthetic task"}).encode("utf-8")
request = Request(
url,
data=payload,
method="POST",
headers={"Content-Type": "application/json", "Accept": "application/json"},
)Timeout завжди явний
Без timeout worker може чекати довше за свій request budget. Один numeric timeout у urlopen не моделює всі production phases так гнучко, як спеціалізований client, але він кращий за необмежене очікування. Загальний budget має враховувати DNS, connect, TLS, response headers, body і retries.
Retry — policy, а не except: continue
Повторюйте лише визначені transient failures, у межах малого budget, із backoff та jitter. POST без idempotency contract не можна автоматично повторювати після невідомого результату.
HTTPError теж містить response
urllib.error.HTTPError виникає для HTTP non-success і водночас має code, headers та body. URLError описує transport-level failure. Їх не варто зливати в один except Exception: 404, timeout і malformed JSON потребують різних рішень та observability.
Response перевіряють до використання
Client обмежує body size, перевіряє Content-Type, декодує bytes і валідовує JSON structure. Навіть correct JSON є недовіреним input: потрібні expected object/list shape, required fields, numeric/string boundaries і policy для unknown fields.
- Закріпіть allowed origin і redirect policy.
- Складіть URL без string injection.
- Додайте explicit timeout та bounded body.
- Розділіть HTTP, transport, parse і domain failures.
- Логуйте operation, status, duration і request ID без secrets/content.
Методичні джерела
Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть на стандарти та авторитетну офіційну документацію.
Закріпіть матеріал уроку
Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.