Skip to content

Cancelled HTTP fetch returns ParseResponse, degrading every in-flight indexer #429

Description

@forkwright

Finding

The CancellationToken branch in fetch_xml returns SearchIndexerError::ParseResponse with the message "request cancelled" in both TorznabClient and NewznabClient. In handle_search_error, the ParseResponse variant maps to the "degraded" indexer status, because it is treated as evidence the indexer returned malformed XML. A request cancelled by the caller (handler timeout, aborted query) is therefore indistinguishable from a parse failure and permanently marks every in-flight indexer as degraded even though all of them are healthy.

Evidence

crates/zetesis/src/client/torznab.rs:53-59:

() = ct.cancelled() => {
    return Err(SearchIndexerError::ParseResponse {
        url: url.to_string(),
        error: "request cancelled".to_string(),});
}

Identical cancellation branch at crates/zetesis/src/client/newznab.rs:53-59.

crates/zetesis/src/search.rs:230 maps the variant to a status change:

SearchIndexerError::ParseResponse { .. } => Some("degraded"),

Why this matters

Under the counter-surveillance threat model, the search subsystem is the operator's primary tooling and its availability matters. A single cancelled search silently degrades every indexer that was mid-request. An adversary able to induce cancellations (network-level disruption that trips the handler timeout) can disable the operator's entire indexer set without compromising any indexer, and the operator sees only mysteriously failing searches with no actionable error. Repeated frontend timeouts produce the same self-inflicted outage.

Desired correction

Add a dedicated SearchIndexerError::Cancelled variant (or reuse an existing variant that carries no status side-effect) and return it from the cancellation branch in both TorznabClient::fetch_xml and NewznabClient::fetch_xml. Add a matching arm in handle_search_error that maps Cancelled to None (no status update), and log the cancellation distinctly from a parse failure.

Done when: cancelling a request triggers no indexer status update, and the log line for a cancelled search is clearly distinguished from a parse failure.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions