feat(remote): keep slow remote session requests alive with a heartbeat

Titles, tracks and licence stream a newline every 30s until the body is ready, so a slow service outlasts the client timeout and proxy idle limits; add a per-server timeout.
This commit is contained in:
imSp4rky
2026-09-24 10:18:03 -06:00
parent c06b00ec63
commit 91db1fa0b1
21 changed files with 377 additions and 42 deletions
+14 -1
View File
@@ -203,6 +203,18 @@ body that uses an integer `status` and no `error_code` or `timestamp`.
(integer `status`, no `error_code`). The `GET /api/health` endpoint is exempt
from authentication, so it never returns the key-error shape.
## Late failures with status `200`
The remote session `titles`, `tracks` and `license` routes send status `200` and the headers
when the work takes longer than 30 seconds. They then send a newline every 30 seconds to keep
the connection open. See [Slow responses](endpoints.md#slow-responses). A failure after that
time arrives as the standard error body, but with status `200`. The body has no field for the
HTTP status that the error would otherwise have. `error_code` and `retryable` stay in the body.
!!! warning "Check the body on these routes"
On these routes a `200` does not always mean success. Parse the body and treat
`"status": "error"` with an `error_code` as a failure.
## Responses with no body
Not every failure or completion has a JSON body. Some endpoints answer with
@@ -257,7 +269,8 @@ For anyone writing a consumer of the API, a durable strategy is:
!!! note "Reference consumer"
unshackle's own remote-download client treats any status of `400` or above as
fatal: it logs `Server error [<error_code>]: <message>` and exits. It also wraps
fatal, and a `200` body with `"status": "error"` and an `error_code` too: it logs
`Server error [<error_code>]: <message>` and exits. It also wraps
requests in a retry adapter (five attempts, backing off on `429`, `500`, `502`,
`503`, and `504`). This is a reasonable template for your own client's error
handling.