Skip to content

fix(desktop): make the updater download resilient — retry, resume, IPv4 fallback#4141

Merged
esengine merged 2 commits into
main-v2from
fix/desktop-updater-retry
Jun 12, 2026
Merged

fix(desktop): make the updater download resilient — retry, resume, IPv4 fallback#4141
esengine merged 2 commits into
main-v2from
fix/desktop-updater-retry

Conversation

@esengine

@esengine esengine commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Makes the desktop self-updater survive flaky links — specifically the CN IPv6 → Cloudflare routes that reset mid-transfer and surfaced as:

更新失败:Get "https://dl.reasonix.io/desktop-v1.6.0/Reasonix-windows-amd64-installer.exe": ... wsarecv: An existing connection was forcibly closed by the remote host.

The updater did a single GET for the manifest, the installer asset, and the signature, and gave up on the first reset.

Changes

  1. Retry transient transport failuresretryTransient wraps the manifest, asset, and signature fetches and retries up to 3 times with a short backoff (0.5s, 1s), bailing immediately on context cancellation. Signature + sha256 verification stay downstream and terminal, so a corrupt body is never retried into a "success".

  2. Resume instead of restart — on a retry, download sends a Range: bytes=<n>- request and continues from the bytes already received rather than re-pulling the whole tens-of-MB installer. A 206 carries the remainder (total taken from Content-Range); a 200 means the server ignored Range, so we reset and re-download in full.

  3. IPv4 fallback — a first-try reset usually means the IPv6 route is the problem, so retries switch to an IPv4-pinned client. netclient.TransportOptions gains a ForceIPv4 flag that pins the dialer to tcp4 (preserving the default 30s dial/keep-alive). Off by default — no behavior change for any existing caller.

Tests

  • TestDownloadRecoversFromMidStreamReset — server truncates the body mid-stream for the first N-1 attempts, then serves the full body; download recovers.
  • TestDownloadResumesWithRange — first attempt sends a prefix then drops the socket; the retry sends Range: bytes=200-, the server replies 206, and the assembled bytes equal the original file.
  • TestDownloadFallsBackToSecondClient — primary client always errors; download completes via the fallback client.
  • TestDownloadGivesUpAfterCap / TestRetryTransientStopsWhenCancelled — persistent failure stops after the cap; a cancelled context short-circuits after one attempt.
  • TestForceIPv4Dials (netclient) — the forced-IPv4 transport dials a v4 listener and rejects an IPv6 literal.

All updater + netclient tests pass locally (go vet clean). The race detector runs in CI (local box has no cgo/gcc).

A single mid-stream connection reset — common on CN IPv6 routes to Cloudflare — failed the whole self-update with a raw "forcibly closed" error. Retry the manifest, asset, and signature fetches up to 3 times with a short backoff so a transient RST self-recovers instead of surfacing.
@esengine esengine requested a review from SivanCola as a code owner June 12, 2026 07:00
@github-actions github-actions Bot added desktop Wails desktop app (desktop/**) updater Auto-update / installer / release packaging v2 Go rewrite (1.x) — main-v2 branch, active development labels Jun 12, 2026
Build on the download retry: a retry now resumes from the bytes already received via an HTTP Range request instead of restarting the tens-of-MB installer from zero, and switches to an IPv4-pinned client — a first-try reset usually means the IPv6 route (CN to Cloudflare) is the problem. netclient gains a ForceIPv4 transport option for the fallback.
@esengine esengine changed the title fix(desktop): retry transient failures in the updater download fix(desktop): make the updater download resilient — retry, resume, IPv4 fallback Jun 12, 2026
@esengine esengine merged commit 5c026df into main-v2 Jun 12, 2026
14 checks passed
@esengine esengine deleted the fix/desktop-updater-retry branch June 12, 2026 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop Wails desktop app (desktop/**) updater Auto-update / installer / release packaging v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant