Repository navigation
fix(cli): 联网命令遵循 HTTP(S)_PROXY / NO_PROXY(0.9.1) #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| const PROXY_ENV = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"] as const; | ||
|
|
||
| /** | ||
| * Node's built-in fetch ignores the proxy environment variables that curl, npm and pnpm honour (unless the process was | ||
| * started with NODE_USE_ENV_PROXY), so behind a proxy every command that talks to the updates server fails with | ||
| * "fetch failed". When one is set, route the global fetch through undici's EnvHttpProxyAgent, which also reads NO_PROXY. | ||
| * undici is imported only then: environments without a proxy, such as reproducible builds, never load it. | ||
| */ | ||
| export async function useEnvProxy(env: NodeJS.ProcessEnv = process.env): Promise<boolean> { | ||
| if (!PROXY_ENV.some((name) => env[name])) return false; | ||
| const { EnvHttpProxyAgent, setGlobalDispatcher } = await import("undici"); | ||
| const httpProxy = env.HTTP_PROXY ?? env.http_proxy; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium · Configured proxy is ignored When an uppercase proxy or Treat empty uppercase values as unset when falling back to lowercase environment variables. Prompt for AI agentsIn `packages/cli/src/proxy.ts` at line 12:
**Configured proxy is ignored**
When an uppercase proxy or `NO_PROXY` variable is empty while its lowercase counterpart is configured, `??` selects the empty uppercase value instead of the configured lowercase value, so `EnvHttpProxyAgent` receives an empty proxy URL or `noProxy` list and requests use the wrong routing behavior.
Treat empty uppercase values as unset when falling back to lowercase environment variables. |
||
| const httpsProxy = env.HTTPS_PROXY ?? env.https_proxy; | ||
| const noProxy = env.NO_PROXY ?? env.no_proxy; | ||
| setGlobalDispatcher(new EnvHttpProxyAgent({ | ||
| ...(httpProxy !== undefined && { httpProxy }), | ||
| ...(httpsProxy !== undefined && { httpsProxy }), | ||
| ...(noProxy !== undefined && { noProxy }), | ||
|
Comment on lines
+15
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium · Loopback requests leak tokens When a proxy is configured and an authenticated command targets an allowed loopback HTTP URL that Ensure localhost and loopback addresses are excluded from proxying even when Prompt for AI agentsIn `packages/cli/src/proxy.ts` at lines 15-18:
**Loopback requests leak tokens**
When a proxy is configured and an authenticated command targets an allowed loopback HTTP URL that `NO_PROXY` does not exclude, `EnvHttpProxyAgent` sends the request through the proxy, so local Wrangler commands fail to reach the server and the proxy can read bearer tokens. Add loopback hosts to the default `NO_PROXY` exclusions.
Ensure localhost and loopback addresses are excluded from proxying even when `NO_PROXY` is unset or does not list them. |
||
| })); | ||
| return true; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import assert from "node:assert/strict"; | ||
| import { createServer, type Server } from "node:http"; | ||
| import type { AddressInfo } from "node:net"; | ||
| import { after, before, describe, it } from "node:test"; | ||
| import { Agent, setGlobalDispatcher } from "undici"; | ||
| import { useEnvProxy } from "../src/proxy.ts"; | ||
|
|
||
| describe("useEnvProxy", () => { | ||
| let proxy: Server; | ||
| let origin: Server; | ||
| let proxyUrl: string; | ||
| const seen: string[] = []; | ||
|
|
||
| before(async () => { | ||
| // undici tunnels through the proxy with CONNECT even for http targets; answer it, then serve the tunnelled | ||
| // request from an in-process "origin" so no real network is touched | ||
| origin = createServer((req, res) => { | ||
| seen.push(`GET ${req.headers.host}${req.url}`); | ||
| res.end("via proxy"); | ||
| }); | ||
| proxy = createServer(); | ||
| proxy.on("connect", (req, socket) => { | ||
| seen.push(`CONNECT ${req.url}`); | ||
| socket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); | ||
| origin.emit("connection", socket); | ||
| }); | ||
| await new Promise<void>((resolve) => proxy.listen(0, "127.0.0.1", resolve)); | ||
| proxyUrl = `http://127.0.0.1:${(proxy.address() as AddressInfo).port}`; | ||
| }); | ||
|
|
||
| after(async () => { | ||
| setGlobalDispatcher(new Agent()); | ||
| // the proxy agent keeps its connection alive; close() alone would wait for it forever | ||
| proxy.closeAllConnections(); | ||
| origin.closeAllConnections(); | ||
| await new Promise((resolve) => proxy.close(resolve)); | ||
| }); | ||
|
|
||
| it("leaves fetch alone without proxy variables", async () => { | ||
| assert.equal(await useEnvProxy({}), false); | ||
| }); | ||
|
|
||
| it("routes the global fetch through HTTP_PROXY", async () => { | ||
| assert.equal(await useEnvProxy({ HTTP_PROXY: proxyUrl }), true); | ||
| const response = await fetch("http://updates.example.invalid/demo/ping"); | ||
| assert.equal(await response.text(), "via proxy"); | ||
| assert.deepEqual(seen, ["CONNECT updates.example.invalid:80", "GET updates.example.invalid/demo/ping"]); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,2 @@ | ||
| /** The tinyui-core version; a package records it as the runtime it needs (docs/updates.md §1.1). */ | ||
| export const VERSION = "0.9.0"; | ||
| export const VERSION = "0.9.1"; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Medium · Offline commands fail
When a proxy variable contains a malformed URL and the user invokes an offline command,
useEnvProxy()constructsEnvHttpProxyAgentbeforecommand.run, so the malformed URL throws and commands such asbuild,bundle, andschemado not run.Bypass proxy initialization for commands that do not make network requests.
Prompt for AI agents