fetch: respect HTTP Cache-Control headers with TTL-based invalidation (#91729) (#93228)
## Summary
This is a re-application of the HTTP fetch part of #91729. Stacked on
#93227.
### Fix fetch to respect HTTP `Cache-Control` headers
Previously, `fetch` results were cached indefinitely, meaning results
would never be refreshed (unless the cache was invalidated). Now they
are `session_dependent` with a TTL to ensure we respect HTTP cache
settings (e.g. Google Fonts with `max-age=86400`).
New two-task pattern:
- **`fetch_inner`** (NOT `session_dependent`): Performs the HTTP
request, grabs an `Invalidator` for itself, and returns the response +
invalidator + absolute deadline. Cached across sessions.
- **`fetch`** (`network`, `session_dependent`): Reads the cached
`fetch_inner` result and spawns a timer to invalidate when the TTL
expires.
On warm cache restore, `fetch` re-executes (session-dependent), reads
the persisted deadline from `fetch_inner`'s cached result, computes
remaining TTL, and spawns a timer — no HTTP request unless the TTL has
already expired. Mid-session, the timer fires and triggers a re-fetch.
Error handling: On fetch failure, `fetch_inner` takes a dependency on
`Completion::session_dependent()` so transient errors (network down, DNS
failure) are retried on the next session without busy-looping.
## Test Plan
3 new integration tests in `turbo-tasks-fetch/tests/fetch.rs`:
- `ttl_invalidates_within_session` — mock server returns `max-age=1`,
body changes, verifies re-fetch after TTL
- `ttl_invalidates_on_session_restore` — fetches with TTL, stops TT,
waits for expiry, warm restores with new TT, verifies re-fetch
- `errors_retried_on_session_restore` — server returns 500, stops TT,
fixes server, warm restores, verifies success
- Existing 6 fetch tests continue to pass
<!-- NEXT_JS_LLM_PR -->