[verified] Handle delivery acknowledgements and read-only recovery

This commit is contained in:
2026-10-13 06:26:49 +00:00
parent 3759fcbde3
commit dbd381a41b
5 changed files with 439 additions and 0 deletions
@@ -0,0 +1,80 @@
"""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)