From e271874b1da3ec5c5f8c84636eb3a0a13aa404d4 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Mon, 21 Sep 2026 11:18:20 +0200 Subject: [PATCH] fix(downloads): replace the 5-minute total request deadline with a stall timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The download worker built its HTTP client with `Client::timeout(300s)`, which in reqwest is a total deadline that runs until the response body has finished. Every transfer longer than five minutes was cut off mid-body as "error decoding response body" and retried. A transcode ignores `Range`, so each retry restarted from byte zero, met the same deadline, and after three attempts the download failed — no feature film at transcode speed ever completed on a device whose audio must be re-encoded, and a large direct copy limped through in five-minute slices with a backoff between each. A connect timeout plus a read timeout that resets on every chunk catches a dead connection without capping how long a healthy transfer may run. Red first: a loopback server dribbling a body three times longer than the timeout failed with the old client (and burned the whole retry budget) and passes now; a second test hangs the socket and shows the stall is still detected. DR-289, UT-251. Co-Authored-By: Claude Opus 5 (1M context) --- docs/requirements.md | 4 +- src-tauri/Cargo.toml | 3 + src-tauri/src/download/worker.rs | 147 ++++++++++++++++++++++++++++++- 3 files changed, 151 insertions(+), 3 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index 2bee7565c..1088040db 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -490,6 +490,7 @@ Internal architecture, components, and application logic. | DR-198 | The webview runs under a real Content-Security-Policy, and the asset protocol is scoped to the one directory it still serves. `csp` was `null`, which disables CSP entirely: any script that reached the web layer — through a future `{@html}`, a dependency, or a devtools paste — would have inherited the whole IPC surface, and with it the user's session. `script-src 'self'` (Tauri injects a nonce for SvelteKit's inline bootstrap script at build time, so no `'unsafe-inline'` is needed) plus `object-src`/`frame-src 'none'` and `base-uri 'self'` is the part that is genuinely restrictive. `img-src`/`media-src`/`connect-src` cannot be: the Jellyfin origin is typed in by the user at run time and is commonly plain `http` on a LAN, so they allow `http:`/`https:` — a wide grant for *data*, but one that still bars `file:`, `filesystem:` and scripting schemes, and leaves `script-src` untouched. `style-src` keeps `'unsafe-inline'` because Svelte compiles `style="…"` attributes (including `app.html`'s `display: contents` wrapper) into markup; this is safe only while no `