docs/04-engine-design.md §7's failure policy table has said "403 after
redirect: retry once with the original referrer; many CDNs require it"
since it was written, and Error::forbidden's own enum comment says the
same -- but grepping download_task.cpp and http_client.cpp for 403 turned
up nothing. It was never built.
Implemented at both points a 403 can surface:
- The probe (net::Prober, a separate request path from segment workers):
on_probe_result() now retries once via restart_probe(false), with
effective_referrer set to the download URL's own origin (origin_of(),
via net::split_url()), when the failure is Error::forbidden and this is
the first retry. A second 403 asks rather than fails outright --
auto_pause_locked(..., false, true), the same "ask, don't just fail"
path 416/etag-mismatch already use -- specifically so DownloadHandle::
refresh_url() stays usable afterward (its own contract requires a
non-terminal task); this is what makes the expiring-signed-url mode's
README-documented refresh_url() recovery actually reachable.
- Each segment worker (SegWorker::forbidden, set in seg_head() on a 403
HEAD): the same one-shot referrer retry via retry_worker(), landing on
auto_pause_locked() on a second 403 for the same reason.
Both paths route the retry's Referer through a new effective_referrer
field rather than spec.referrer directly, since the origin-retry must not
overwrite what the caller actually asked for -- start_worker_locked() and
restart_probe() were switched to send effective_referrer instead.
do_refresh_url() had two latent bugs surfaced by actually exercising the
expiring-signed-url recovery path end-to-end:
1. It unconditionally proceeded to resume even when the refresh probe
itself failed -- a bad refresh URL would silently un-pause a task with
nothing behind it. Now returns (stays paused) on !r.has_value().
2. It only handled "already probed once, just refreshing a few fields" --
for a task whose first-ever probe never succeeded (every hostile mode
this commit adds a test for that pauses at the initial probe, not
mid-download), s->registered was never true, so the existing
`if (s->registered) set_want()` never fired and nothing happened. Now
detects !s->have_probe and calls finish_probe_locked() directly, the
actual first-time registration/segmenter-construction path.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Q3QrF7rCt21bkAjt9BCDFQ