Skip to content

feat(cli): make download timeout configurable and more forgiving - #2386

Open
tarikermis wants to merge 5 commits into
voidzero-dev:mainfrom
tarikermis:feat/download-timeout
Open

feat(cli): make download timeout configurable and more forgiving#2386
tarikermis wants to merge 5 commits into
voidzero-dev:mainfrom
tarikermis:feat/download-timeout

Conversation

@tarikermis

Copy link
Copy Markdown

What & why

Closes #2370.

Node.js runtime tarballs (~25–50 MB) and package-manager tarballs are downloaded through the process-wide shared HTTP client, whose per-request total timeout is a hardcoded 2 minutes (crates/vp_shared/src/http.rs). reqwest's timeout covers the entire body stream, so on connections slower than ~250 KB/s a healthy Node.js download is aborted mid-transfer, every retry starts from scratch, and vp env install / vp migrate effectively cannot complete. There was also no way to override the timeout.

This change gives large file downloads their own per-request timeout instead of raising the shared default:

  • New vp_shared::download_timeout() — 10-minute default, overridable with the VP_DOWNLOAD_TIMEOUT_SECS environment variable (positive integer seconds; a set-but-invalid value warns and falls back to the default).
  • Applied per request in the two download_file implementations: crates/vp_js_runtime/src/download.rs (Node.js runtimes — all download_runtime* callers funnel through it) and crates/vp_pm_cli/src/request.rs (npm/yarn/pnpm/bun tarballs — all go through HttpClient::download_file).
  • The shared client's 2-minute default is unchanged, so small metadata fetches (version indexes, checksums, registry JSON) still fail fast when stuck.
  • Documented in docs/guide/installer-env-vars.md alongside the other runtime variables.

Verification

  • Root cause confirmed on current main: both download paths issue client.get(url).send() against the shared client with the hardcoded 2-minute total timeout; a 30–50 MB archive needs a sustained ≥ ~300 KB/s to finish inside that window.
  • cargo check -p vp_shared -p vp_js_runtime -p vp_pm_cli --all-targets --locked — clean.
  • cargo clippy -p vp_shared -p vp_js_runtime -p vp_pm_cli --all-targets --all-features -- --deny warnings (same -A set as just lint) — clean.
  • cargo fmt --all — applied, no drift.
  • cargo test -p vp_shared -p vp_js_runtime -p vp_pm_cli — all suites pass (962 passed, 0 failed; new unit tests cover the env parsing: default, valid override, whitespace trimming, and invalid/blank/zero/negative fallback).
  • Existing httpmock-based download tests in vp_pm_cli still pass, exercising the patched request path end to end.

Notes / limitations

  • I could not reproduce a real slow-network Node.js download locally; behavior was verified by code inspection plus the unit/integration tests above. No snapshot tests were run (JS build not involved in this change).
  • Scope: vp upgrade and the standalone installer download the vp tarball itself via get_bytes, which still uses the shared 2-minute default; extending the knob there would touch a different API and is left for a follow-up if desired.
  • Trade-off worth knowing: the timeout is per attempt, so worst-case wall time with the existing retries grows from ~8 to ~40 minutes. Each attempt shows a progress bar, and a fresh retry on a flaky link often lands a faster connection, so this was kept consistent with the existing retry design.
  • An alternative design considered (raised during review): an idle/read timeout (abort only when no bytes arrive for N seconds) instead of a total budget. reqwest's per-request API only offers a total timeout, and reworking the stream loops for idle semantics is a larger behavioral change; the total timeout + env override implements the issue's suggested solution with a contained diff. Happy to explore the idle-timeout variant as a follow-up.

AI disclosure

This PR was prepared with AI assistance (code authored and tested by an AI agent, reviewed by a second independent AI reviewer pass whose findings were addressed or noted above).

Node.js runtime and package-manager tarball downloads shared the HTTP
client's hardcoded 2-minute per-request timeout with small metadata
fetches. On slow or flaky connections that budget aborts an otherwise
healthy transfer, and there was no way to override it.

Give large file downloads their own per-request timeout: a 10-minute
default, overridable via the VP_DOWNLOAD_TIMEOUT_SECS environment
variable. The shared client's 2-minute default stays as-is so a stuck
metadata fetch still fails fast.

Closes voidzero-dev#2370
@netlify

netlify Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deploy Preview for viteplus-preview ready!

Name Link
🔨 Latest commit 6d3b34c
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a7a109abb7ddd00088bf95b
😎 Deploy Preview https://deploy-preview-2386--viteplus-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

### `VP_DOWNLOAD_TIMEOUT_SECS`

- **Purpose**: Per-request timeout, in seconds, for large downloads such as Node.js runtimes and package-manager tarballs
- **Default**: `600` (10 minutes)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does the default ten-minute reference value come from? Could you refer to what the default values for this parameter are in other Node.js Version Managers?

@fengmk2 fengmk2 self-assigned this Aug 9, 2026
@fengmk2

fengmk2 commented Aug 9, 2026

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc82508b2d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/vp_shared/src/http.rs Outdated
An extreme but parseable value (e.g. 18446744073709551615) was accepted
and the resulting Duration overflowed reqwest's request-deadline
computation (Instant + Duration panics; the release profile aborts), so
a malformed timeout crashed every download instead of producing the
documented warn-and-fallback.

Reject values above 86400 (24 hours) with the same warn-and-fallback as
other invalid values, and cover the boundary and overflow cases in the
unit test.

Refs voidzero-dev#2370
wan9chi pushed a commit that referenced this pull request Aug 10, 2026
… Node version (#2390)

Since 2026-08-08 the Windows PTY snapshot leg fails intermittently on
`dev_engines_runtime_pnpm11`: the `vp dlx -s print-current-version` step
times out after 60s with empty output (13+ runs across branches, main
included, e.g. [run
31302523708](https://github.com/voidzero-dev/vite-plus/actions/runs/31302523708)).
The fixture pinned devEngines node `22.22.2`, the only pin in the suite
that the CI runtime seed does not carry, so the step had to download
Node.js from nodejs.org inside its 60s budget. Connections from the
Windows runner to nodejs.org stall intermittently, and the shared HTTP
client's 2-minute request timeout (see #2386) outlives the step budget,
so a stalled attempt can neither fail nor retry in time. The sibling
`dev_engines_runtime_pnpm10` pins the seeded default `22.18.0` and
passed in ~2s in the same failed runs, which clears the npm registry
path and isolates the stall to nodejs.org.

Pin the pnpm11 fixture to `22.18.0` as well. The `22.22.2` pin carried
no assertion value: #1289 picked it as the then-latest 22.x above vp's
minimum, and the snapshot redacts the printed version to `<version>`.
The case still verifies that vp honors `devEngines.runtime` under pnpm
11. Verified with `cargo test -p vp_cli_snapshots --test cli_snapshots
-- dev_engines_runtime_pnpm11` (passes in 6s, no snapshot drift).
@fengmk2

fengmk2 commented Aug 10, 2026

Copy link
Copy Markdown
Member

@codex review

@fengmk2 fengmk2 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tarikermis Thanks!

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: f4632e6fff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@fengmk2 fengmk2 added the preview-build Publish this PR's commits to the registry bridge as preview builds label Aug 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Registry bridge build (4c9367f)

Warning

This build is from the fork tarikermis/vite-plus and has not been reviewed.
Installing it runs that code on your machine. Build log

This commit build is published to the registry bridge, which serves these as ordinary npm versions (every other package proxies to npmjs):

Package Version
vite-plus 0.0.0-commit.4c9367f409111d88ac9338427886270029bd6b8e
@voidzero-dev/vite-plus-core 0.0.0-commit.4c9367f409111d88ac9338427886270029bd6b8e

Install the Vite+ CLI built from this commit, then migrate a project:

# macOS / Linux
curl -fsSL https://vite.plus | VP_PR_VERSION=2386 bash
# Windows (PowerShell)
$env:VP_PR_VERSION="2386"; irm https://vite.plus/ps1 | iex

After installing, upgrade the current project's vite-plus to this test build with:

vp migrate

Or point your package manager at the bridge registry https://registry-bridge.viteplus.dev/:

Package manager Registry config
npm / pnpm / Bun .npmrc: registry=https://registry-bridge.viteplus.dev/
Yarn (v2+) .yarnrc.yml: npmRegistryServer: "https://registry-bridge.viteplus.dev/"

Then pin the build (vite aliases to vite-plus-core; pnpm can use a catalog, npm an overrides entry):

{
  "devDependencies": {
    "vite-plus": "0.0.0-commit.4c9367f409111d88ac9338427886270029bd6b8e",
    "vite": "npm:@voidzero-dev/vite-plus-core@0.0.0-commit.4c9367f409111d88ac9338427886270029bd6b8e"
  }
}

Comment thread docs/guide/installer-env-vars.md Outdated
- **Default**: None (verification enabled)
- **Details**: [Node.js Signature Verification](/guide/env#node-js-signature-verification)

### `VP_DOWNLOAD_TIMEOUT_SECS`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d prefer VP_DOWNLOAD_TIMEOUT over VP_DOWNLOAD_TIMEOUT_SECS, so the value can evolve to support duration formats like 10m without baking the unit into the variable name. Plain integer values could default to seconds.

@tarikermis

Copy link
Copy Markdown
Author

Good point - renaming to VP_DOWNLOAD_TIMEOUT and keeping plain ints as seconds. Best Regards, Tarik

Keep the unit out of the name so the value can later accept duration
formats like 10m; plain integers remain seconds. Bounds (1..=86400),
warning text, and warn-fallback behavior are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview-build Publish this PR's commits to the registry bridge as preview builds

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Download Node Timeout

2 participants