fix(http): Node http client must not follow redirects#6487
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughReqwest redirect following is disabled in Perry’s Node HTTP request paths. A regression test verifies that a 307 response is returned directly, including its location header, without requesting the redirect target. ChangesHTTP redirect handling
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/perry-stdlib/src/http.rs (1)
928-939: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider deduplicating this extensive comment.
This large explanatory comment is duplicated verbatim from
crates/perry-ext-http/src/lib.rs. Consider keeping the detailed explanation in one place and adding a brief reference to it here to reduce duplication.♻️ Proposed refactor
- // Node's `http.request`/`http.get`/`https.request` NEVER follow - // redirects — a 3xx response is delivered to the caller verbatim - // (only `fetch` follows, per its WHATWG redirect mode). reqwest's - // default policy silently follows up to 10 hops, which is observably - // wrong for the Node client and, worse, turned Next.js's - // `proxyRequest` (its bundled `http-proxy` runs over this very client) - // into an infinite loop: a proxied sub-request that 307-redirects back - // to the entry path was auto-followed HERE instead of relayed to the - // proxy for a transparent response, so the router re-resolved the same - // middleware rewrite forever (`/` rewrites to `/en`, `/en` 307s to `/`). - // Disable following so the 3xx flows back to the caller as Node does. + // Disable redirect following so the 3xx flows back to the caller as Node does. + // See `apply_node_proxy_policy` in `crates/perry-ext-http/src/lib.rs` for detailed reasoning.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/perry-stdlib/src/http.rs` around lines 928 - 939, Deduplicate the redirect-policy explanation near the reqwest builder in the HTTP implementation: retain the detailed rationale in one canonical location, and replace the duplicate block in the surrounding request setup with a brief reference to that location while preserving builder.redirect(reqwest::redirect::Policy::none()).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/perry-stdlib/src/http.rs`:
- Around line 928-939: Deduplicate the redirect-policy explanation near the
reqwest builder in the HTTP implementation: retain the detailed rationale in one
canonical location, and replace the duplicate block in the surrounding request
setup with a brief reference to that location while preserving
builder.redirect(reqwest::redirect::Policy::none()).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 27f499d4-3ff9-4ddb-9bc0-5c0fa427515a
📒 Files selected for processing (3)
crates/perry-ext-http/src/lib.rscrates/perry-stdlib/src/http.rstest-files/test_gap_http_client_no_redirect_follow.ts
7c4a1e9 to
466673e
Compare
Node's `http.request`/`http.get`/`https.request` never follow HTTP
redirects — a 3xx is delivered to the caller verbatim (only `fetch`
follows, per its WHATWG redirect mode). Perry's Node http client is built
on reqwest, whose default `redirect::Policy` silently follows up to 10
hops. That auto-follow is observably wrong (the caller sees the final 200
instead of the 307 + `location`).
Worse, it breaks Next.js's `proxyRequest`: its bundled `http-proxy` runs
over this very client, so a proxied sub-request that 307-redirects back
to the entry path was auto-followed here instead of relayed as a
transparent response. With a locale middleware where `/` rewrites to
`/en` and `/en` 307s back to `/`, the router re-resolved the same rewrite
forever — the request hung (no response, connection leak) instead of
returning the 307.
Disable redirect-following (`Policy::none()`) on both Node-http client
backends:
- perry-ext-http `apply_node_proxy_policy` (the `external-http-client-
pump` path — every real client build funnels through it: HTTP_CLIENT,
the per-Agent client, the TLS-customized client, default_client)
- perry-stdlib `js_http_client_request_end`'s reqwest builder (the
non-`external-http-client-pump` path)
so a 3xx flows back to the caller exactly as Node delivers it. `fetch`
has its own client and redirect handling and is unaffected.
Claude-Session: https://claude.ai/code/session_01KD4GTmiwZjRrxShzQydJpF
466673e to
dcf64f1
Compare
Problem
Node's
http.request/http.get/https.requestnever follow HTTP redirects — a 3xx response is delivered to the caller verbatim (onlyfetchfollows, per its WHATWG redirect mode). Perry's Node http client is built onreqwest, whose defaultredirect::Policysilently follows up to 10 hops.That auto-follow is observably wrong (the caller sees the final
200instead of the307+location), and it breaks Next.js'sproxyRequest:proxyRequestruns Next's bundledhttp-proxyover this very client./internally rewrites to/enand/en307s back to/, the router re-resolved the same rewrite forever — the request hung (no response, connection leak) instead of returning the 307.Reproduction
Node:
307, server hit once. Perry (before):200, server hit twice (followed).Fix
Disable redirect-following (
reqwest::redirect::Policy::none()) on both Node-http client backends so a 3xx flows back to the caller exactly as Node delivers it:perry-ext-http—apply_node_proxy_policy(theexternal-http-client-pumppath). Every real client build funnels through it: the sharedHTTP_CLIENT, the per-Agentclient, the TLS-customized client, anddefault_client.perry-stdlib—js_http_client_request_end's reqwest builder (the non-external-http-client-pumppath).fetchhas its own client and redirect handling and is unaffected.Test
test-files/test_gap_http_client_no_redirect_follow.ts— starts a server that 307-redirects, doeshttp.get, and asserts the client reports the 307 +locationand the server is hit exactly once. Byte-for-byte identical tonode --experimental-strip-types.https://claude.ai/code/session_01KD4GTmiwZjRrxShzQydJpF
Summary by CodeRabbit
Bug Fixes
Locationheaders are now preserved and exposed as received, for Node-compatible behavior.Tests