Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
73 lines
2.9 KiB
Python
73 lines
2.9 KiB
Python
"""Bounded JSON transport for fixed, deployment-owned HTTPS endpoints."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import ipaddress
|
|
from http.client import HTTPException
|
|
from urllib.error import HTTPError, URLError
|
|
from urllib.parse import urlsplit
|
|
from urllib.request import HTTPRedirectHandler, ProxyHandler, Request, build_opener
|
|
|
|
|
|
class TransportError(Exception):
|
|
"""Deliberately contains no response body, URL, token or request data."""
|
|
|
|
|
|
def fixed_origin(value: str, *, allow_internal_http=False) -> str:
|
|
parsed = urlsplit(value)
|
|
internal_http = False
|
|
if allow_internal_http and parsed.scheme == "http" and parsed.hostname:
|
|
try:
|
|
internal_http = ipaddress.ip_address(parsed.hostname).is_loopback
|
|
except ValueError:
|
|
internal_http = parsed.hostname.endswith((".svc", ".svc.cluster.local"))
|
|
if ((parsed.scheme != "https" and not internal_http) or not parsed.hostname or parsed.username
|
|
or parsed.password or parsed.path or parsed.query or parsed.fragment
|
|
or any(c.isspace() for c in value)):
|
|
raise ValueError("a fixed HTTPS origin without path or credentials is required")
|
|
return value
|
|
|
|
|
|
def https_origin(value: str) -> str:
|
|
return fixed_origin(value)
|
|
|
|
|
|
class _NoRedirect(HTTPRedirectHandler):
|
|
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
|
return None
|
|
|
|
|
|
class JSONTransport:
|
|
"""No redirects or ambient proxy credentials; five-second request timeout."""
|
|
|
|
def __init__(self, *, allow_internal_http=False) -> None:
|
|
self.allow_internal_http = allow_internal_http
|
|
self._opener = build_opener(ProxyHandler({}), _NoRedirect())
|
|
|
|
def request(self, method: str, url: str, *, headers=None, body=None) -> tuple[int, dict]:
|
|
parsed = urlsplit(url)
|
|
try:
|
|
fixed_origin(parsed.scheme + "://" + parsed.netloc,
|
|
allow_internal_http=self.allow_internal_http)
|
|
except ValueError:
|
|
raise TransportError("trusted transport required") from None
|
|
req = Request(url, method=method, data=body, headers=headers or {})
|
|
try:
|
|
try:
|
|
response = self._opener.open(req, timeout=5)
|
|
except HTTPError as exc:
|
|
response = exc
|
|
with response:
|
|
status = response.code
|
|
if 300 <= status < 400:
|
|
raise TransportError("upstream redirect refused")
|
|
raw = response.read(262145)
|
|
if len(raw) > 262144:
|
|
raise TransportError("upstream response too large")
|
|
result = json.loads(raw)
|
|
if not isinstance(result, dict):
|
|
raise TransportError("upstream object required")
|
|
return status, result
|
|
except (URLError, OSError, ValueError, HTTPException):
|
|
raise TransportError("upstream request failed") from None
|