aeat.adapters.outbound.aeat.browser._site_health_parsers module

Marker-based parsers for AEAT site-health classification.

Three pure functions inspect the HTTP status, headers, and body of a response and decide whether it looks like AEAT maintenance, a Web Application Firewall challenge, or a rate-limit response. evaluate_response() runs them in a deterministic order and returns the first non-None SiteHealthStatus.

The parsers are deliberately synchronous, side-effect free, and free of any Playwright dependency. adapters.outbound.aeat.browser._site_health_probe.probe_response() is the adapter boundary used by adapters.outbound.aeat.browser.BrowserSession.navigate(); it turns navigation responses into SiteHealthStatus records that can be carried by core.errors.SiteHealthError. The marker corpora reflect the observed mantenimiento, WAF, and rate-limit responses on AEAT Sede Electrónica.

See also

adapters.outbound.aeat.browser._site_health.SiteHealthStatus

Frozen record returned by every positive parser classification.

adapters.outbound.aeat.browser._site_health.SiteHealthState

Closed state catalogue emitted by this parser suite.

parse_mantenimiento_banner(url, http_status, headers, html, *, rate_limit_retry_after_default, _lowered=None)[source]

Detect an AEAT maintenance banner or interstitial.

Classifies the response as SiteHealthState.MANTENIMIENTO when two or more curated body markers match, or when exactly one body marker matches alongside a title containing mantenimiento or interrupcion. A title-only match with zero body markers never classifies: at least one body-marker hit is required as corroborating evidence to suppress false positives from unrelated pages whose <title> happens to mention maintenance.

Parameters:
  • url (str) – The probe URL.

  • http_status (int) – The observed HTTP status code.

  • headers (Mapping[str, str]) – Case-insensitive mapping of response headers.

  • html (str) – The response body.

  • rate_limit_retry_after_default (int) – Ignored here; accepted so the three parsers share a uniform signature wired from the browser session hook.

  • _lowered (str | None) – Optional pre-lowercased body forwarded from evaluate_response() to avoid redundant work. Public callers should leave it None.

Return type:

SiteHealthStatus | None

Returns:

A populated SiteHealthStatus or None when no maintenance markers fire.

parse_waf_challenge(url, http_status, headers, html, *, rate_limit_retry_after_default, _lowered=None)[source]

Detect a WAF block / challenge page.

Triggers either when the response is a 403 and any WAF marker is present, or when the body contains request blocked alongside a reference id / support id correlation token regardless of status code.

Parameters:
  • url (str) – The probe URL.

  • http_status (int) – The observed HTTP status code.

  • headers (Mapping[str, str]) – Case-insensitive mapping of response headers.

  • html (str) – The response body.

  • rate_limit_retry_after_default (int) – Ignored; reserved for interface parity.

  • _lowered (str | None) – Pre-computed lowercased html; computed from html when omitted.

Return type:

SiteHealthStatus | None

Returns:

A populated SiteHealthStatus or None when the response does not look WAF-blocked.

parse_rate_limit_response(url, http_status, headers, html, *, rate_limit_retry_after_default, now=None, _lowered=None)[source]

Detect a rate-limit response (HTTP 429 or 503).

Short-circuits unless http_status is 429 or 503. When the response is a 503 and the body also matches a maintenance marker the parser yields None so parse_mantenimiento_banner() can win; AEAT mantenimiento pages frequently answer with 503.

Retry-After is read case-insensitively from headers and supports both forms permitted by RFC 9110 §10.2.3: a non-negative integer delta-seconds, or an HTTP-date. For HTTP-date values the delta is computed against now (defaulting to datetime.now() in UTC), clamped to a non-negative integer. If both parses fail, or the computed delta would be zero, the parser falls back to rate_limit_retry_after_default.

Parameters:
  • url (str) – The probe URL.

  • http_status (int) – The observed HTTP status code.

  • headers (Mapping[str, str]) – Case-insensitive mapping of response headers.

  • html (str) – The response body.

  • rate_limit_retry_after_default (int) – Fallback value (seconds) when the Retry-After header is missing or unparseable.

  • now (datetime | None) – Injection seam for deterministic tests of the HTTP-date branch. Defaults to datetime.now() in UTC at call time.

  • _lowered (str | None) – Optional pre-lowercased body forwarded from evaluate_response().

Return type:

SiteHealthStatus | None

Returns:

A populated SiteHealthStatus or None when the response is not a rate-limit answer.

evaluate_response(url, http_status, headers, html, *, rate_limit_retry_after_default)[source]

Run the full parser suite and return the first non-OK hit.

This is the public parser entry point consumed by adapters.outbound.aeat.browser._site_health_probe.probe_response(). BrowserSession then raises core.errors.SiteHealthError for any returned status.

Parsers are evaluated in cost-then-specificity order:

  1. parse_rate_limit_response() (cheapest, header-first short-circuit; yields to mantenimiento when a 503 carries mantenimiento markers).

  2. parse_mantenimiento_banner().

  3. parse_waf_challenge().

Parameters:
  • url (str) – The probe URL.

  • http_status (int) – The observed HTTP status code.

  • headers (Mapping[str, str]) – Case-insensitive mapping of response headers.

  • html (str) – The response body.

  • rate_limit_retry_after_default (int) – Fallback Retry-After value in seconds, forwarded to the rate-limit parser.

Return type:

SiteHealthStatus | None

Returns:

A populated SiteHealthStatus describing the detected non-OK state, or None when no parser classified the response (i.e. the response looks healthy).