"""Fail-closed decoding for one Gitea API delivery attempt. Unexpected/malformed acknowledgements can follow a committed mutation: callers must not blindly retry. They may perform caller-initiated read-only reconciliation. """ import json import ssl from urllib.error import HTTPError, URLError MAX_RESPONSE_BYTES = 1024 * 1024 class DeliveryResponseError(Exception): """Sanitized response failure; message never includes server content.""" class InvalidExpectationError(DeliveryResponseError, ValueError): """The caller supplied an invalid endpoint-specific status expectation.""" class RejectedResponseError(DeliveryResponseError): """Known auth/redirect or other non-success rejection; do not retry blindly.""" class AcknowledgementUncertainError(DeliveryResponseError): """Mutation may have committed; do not retry; reconcile read-only if needed.""" def _validate_expected_status(expected_status): if (isinstance(expected_status, bool) or not isinstance(expected_status, int) or expected_status < 200 or expected_status > 299): raise InvalidExpectationError("expected_status must be an explicit success status integer") def _reject_constant(_value): raise ValueError("non-standard JSON constant") def decode_response(status, body, *, expected_status): """Require the exact expected status and a bounded JSON object body.""" _validate_expected_status(expected_status) if isinstance(status, bool) or not isinstance(status, int): raise AcknowledgementUncertainError("invalid response status; do not retry; reconcile read-only") if 300 <= status <= 399 or status in (401, 403): raise RejectedResponseError("request rejected or redirected; do not retry blindly") if status != expected_status: raise AcknowledgementUncertainError("unexpected response status; do not retry; reconcile read-only") if not isinstance(body, (bytes, bytearray)) or len(body) > MAX_RESPONSE_BYTES: raise AcknowledgementUncertainError("invalid or oversized response; do not retry; reconcile read-only") try: value = json.loads(bytes(body).decode("utf-8"), parse_constant=_reject_constant) except (UnicodeError, ValueError, RecursionError): raise AcknowledgementUncertainError("invalid JSON response; do not retry; reconcile read-only") from None if not isinstance(value, dict): raise AcknowledgementUncertainError("response JSON must be an object; do not retry; reconcile read-only") return value def deliver_once(send, *, expected_status): """Validate expectation, invoke injected sender exactly once, decode its (status, bytes).""" _validate_expected_status(expected_status) try: result = send() except (RejectedResponseError, ssl.SSLCertVerificationError): raise RejectedResponseError("transport security rejected; do not retry blindly") from None except HTTPError as error: if 300 <= error.code <= 399 or error.code in (401, 403): raise RejectedResponseError("request rejected or redirected; do not retry blindly") from None raise AcknowledgementUncertainError("HTTP acknowledgement uncertain; do not retry; reconcile read-only") from None except URLError as error: if isinstance(error.reason, ssl.SSLCertVerificationError): raise RejectedResponseError("TLS verification rejected; do not retry blindly") from None raise AcknowledgementUncertainError("transport failure; do not retry; reconcile read-only") from None except Exception: raise AcknowledgementUncertainError("transport failure; do not retry; reconcile read-only") from None try: status, body = result except Exception: raise AcknowledgementUncertainError("invalid transport response; do not retry; reconcile read-only") from None return decode_response(status, body, expected_status=expected_status)