mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-11-06 20:58:14 -04:00
chore(deps): update https-proxy-agent to 9.1.0 (#2383)
* chore(deps): update https-proxy-agent to 9.1.0 * test(proxy): isolate CONNECT test from leaked axios mocks * test(proxy): avoid global axios module registration * test(proxy): exercise createAxiosInstance in CONNECT test
This commit is contained in:
@@ -249,6 +249,34 @@ content: serialized headers may quote or escape filenames, and parsed patches
|
||||
can omit filenames. Check the diff and edit-snippet tests when changing these
|
||||
paths.
|
||||
|
||||
### Proxy agents
|
||||
|
||||
Use the shared helpers in `src/utils/proxy.ts`: `createAxiosInstance()` for
|
||||
Axios, `getWebSocketProxyAgent(url)` for Node WebSockets, and `getProxyFetchOptions()`
|
||||
for fetch. The Axios and WebSocket helpers apply `NO_PROXY` bypasses before
|
||||
selecting an agent. `getWebSocketProxyUrl(url)` supplies Bun's native WebSocket proxy
|
||||
option. Lowercase proxy environment variables take precedence over uppercase
|
||||
variants on the Axios and WebSocket paths.
|
||||
|
||||
`https-proxy-agent` is pinned exactly to `9.1.0` and remains an external runtime
|
||||
module in `scripts/externals.ts`. Use its public named ESM exports, such as
|
||||
`import { HttpsProxyAgent } from 'https-proxy-agent'`, and the constructor
|
||||
`new HttpsProxyAgent(proxyUrl, options)`. Both it and `agent-base` are ESM
|
||||
packages requiring Node.js 20 or later; use OpenClaude's supported Node.js
|
||||
`>=24.22.1` runtime. Keep dependency updates exact and generate `bun.lock` with
|
||||
`bun add --exact https-proxy-agent@<version>`.
|
||||
|
||||
Keep TLS verification enabled. Configure `NODE_EXTRA_CA_CERTS` before starting
|
||||
the CLI when a custom CA is needed. For runtime certificate configuration, the
|
||||
shared CA loader reads that file and the TLS helpers supply explicit options;
|
||||
clear the CA and mTLS caches after configuration changes. Proxy-agent
|
||||
constructor options configure the connection to the proxy. Supply target TLS
|
||||
options on the HTTPS request, using `getWebSocketTLSOptions()` where applicable;
|
||||
do not assume constructor `ca` options also trust the target inside a CONNECT
|
||||
tunnel. Run `bun test src/utils/proxy.connect.test.ts src/utils/proxy.test.ts`
|
||||
when changing these paths to verify HTTPS CONNECT routing and `NO_PROXY`
|
||||
bypass with certificate validation enabled.
|
||||
|
||||
## Provider Changes
|
||||
|
||||
OpenClaude supports multiple provider paths. Before contributing provider changes, review the relevant documentation to ensure your implementation follows the expected patterns:
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"google-auth-library": "10.6.2",
|
||||
"graphology": "0.26.0",
|
||||
"graphology-metrics": "2.4.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"https-proxy-agent": "9.1.0",
|
||||
"ignore": "7.0.13",
|
||||
"indent-string": "5.0.0",
|
||||
"js-tiktoken": "1.0.21",
|
||||
@@ -597,7 +597,7 @@
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
"agent-base": ["agent-base@9.0.0", "", {}, "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA=="],
|
||||
|
||||
"ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
|
||||
|
||||
@@ -877,7 +877,7 @@
|
||||
|
||||
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
||||
|
||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
"https-proxy-agent": ["https-proxy-agent@9.1.0", "", { "dependencies": { "agent-base": "9.0.0", "debug": "^4.3.4", "proxy-agent-negotiate": "1.1.0" } }, "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA=="],
|
||||
|
||||
"human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
|
||||
|
||||
@@ -1089,6 +1089,8 @@
|
||||
|
||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||
|
||||
"proxy-agent-negotiate": ["proxy-agent-negotiate@1.1.0", "", { "peerDependencies": { "kerberos": "^2.0.0" }, "optionalPeers": ["kerberos"] }, "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ=="],
|
||||
|
||||
"proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
@@ -1487,6 +1489,8 @@
|
||||
|
||||
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
||||
|
||||
"@typespec/ts-http-runtime/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"axios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
|
||||
|
||||
"bun-types/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
@@ -1513,8 +1517,12 @@
|
||||
|
||||
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"get-intrinsic/hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="],
|
||||
|
||||
"http-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"knip/zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="],
|
||||
|
||||
"needle/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
@@ -1561,6 +1569,8 @@
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="],
|
||||
|
||||
"@typespec/ts-http-runtime/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"axios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||
|
||||
"cli-highlight/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
@@ -1589,6 +1599,8 @@
|
||||
|
||||
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
||||
|
||||
"qrcode/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@
|
||||
"google-auth-library": "10.6.2",
|
||||
"graphology": "0.26.0",
|
||||
"graphology-metrics": "2.4.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"https-proxy-agent": "9.1.0",
|
||||
"ignore": "7.0.13",
|
||||
"indent-string": "5.0.0",
|
||||
"js-tiktoken": "1.0.21",
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
|
||||
import { once } from 'node:events'
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { createServer as createHttpServer, type IncomingMessage } from 'node:http'
|
||||
import {
|
||||
createServer as createHttpsServer,
|
||||
get,
|
||||
request,
|
||||
type RequestOptions,
|
||||
} from 'node:https'
|
||||
import { connect, type AddressInfo } from 'node:net'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import type { Duplex } from 'node:stream'
|
||||
import { snapshotEnv } from '../test/envSnapshot.js'
|
||||
import { clearCACertsCache } from './caCerts.js'
|
||||
import { clearMTLSCache, getMTLSAgent, getWebSocketTLSOptions } from './mtls.js'
|
||||
|
||||
// Cache-busting proxy.ts alone still imports any leaked global Axios mock.
|
||||
// Build a fresh copy of the production module with only its Axios import
|
||||
// redirected to the real CommonJS entry. Keep the other imports external so
|
||||
// CA/mTLS caches stay shared, without changing any global mock registration.
|
||||
const nonce = `${Date.now()}-${Math.random()}`
|
||||
const proxyBuild = await Bun.build({
|
||||
entrypoints: [join(import.meta.dir, 'proxy.ts')],
|
||||
target: 'bun',
|
||||
plugins: [{
|
||||
name: 'connect-test-real-axios',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /.*/ }, args => {
|
||||
if (!args.importer) return
|
||||
return {
|
||||
path: args.path === 'axios'
|
||||
? `${require.resolve('axios')}?real=${nonce}`
|
||||
: Bun.resolveSync(args.path, args.resolveDir),
|
||||
external: true,
|
||||
}
|
||||
})
|
||||
},
|
||||
}],
|
||||
})
|
||||
if (!proxyBuild.success) {
|
||||
throw new AggregateError(proxyBuild.logs, 'Could not build proxy.ts for CONNECT tests')
|
||||
}
|
||||
const { createAxiosInstance, getWebSocketProxyAgent } = await import(
|
||||
`data:text/javascript;base64,${Buffer.from(await proxyBuild.outputs[0].text()).toString('base64')}`
|
||||
) as typeof import('./proxy.js')
|
||||
|
||||
// Self-signed test certificate and key, valid only for the loopback target.
|
||||
const cert = `-----BEGIN CERTIFICATE-----
|
||||
MIIBmzCCAUGgAwIBAgIUAtOYg4xV7C9catkY/bwZc7XywMgwCgYIKoZIzj0EAwIw
|
||||
FDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTI2MTAyNDA4NTUxNloYDzIxMjYwOTMw
|
||||
MDg1NTE2WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjO
|
||||
PQMBBwNCAAS5HiqEES/b1Oe+NOKT+Cg3Gh89Rk0O7bSu0aDJpwdMzOxgOT2jTBmK
|
||||
2H5TUBnDWHjLktPtbmQQ4nMAb1kD4oKWo28wbTAdBgNVHQ4EFgQUu+xqnGfB/gu8
|
||||
DbNNHlVonV69Y64wHwYDVR0jBBgwFoAUu+xqnGfB/gu8DbNNHlVonV69Y64wDwYD
|
||||
VR0TAQH/BAUwAwEB/zAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwCgYIKoZI
|
||||
zj0EAwIDSAAwRQIgPy5jbT1ECH7LFy5r5JrV7xfsOkhwtbuwqVeidDBynFACIQCp
|
||||
sTyTz0llkGsTStOnpDBTzTUQzcDT8z6ZgqqxbY29nw==
|
||||
-----END CERTIFICATE-----`
|
||||
const key = `-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgXc/iw7bBMrZkWHsr
|
||||
tDvMDJ6qPLsspu6IH8lHZ3ccGvihRANCAAS5HiqEES/b1Oe+NOKT+Cg3Gh89Rk0O
|
||||
7bSu0aDJpwdMzOxgOT2jTBmK2H5TUBnDWHjLktPtbmQQ4nMAb1kD4oKW
|
||||
-----END PRIVATE KEY-----`
|
||||
|
||||
describe('HTTPS_PROXY CONNECT transport', () => {
|
||||
let restoreEnv: () => void
|
||||
let directory: string
|
||||
const sockets = new Set<Duplex>()
|
||||
let target: ReturnType<typeof createHttpsServer>
|
||||
let proxy: ReturnType<typeof createHttpServer>
|
||||
let targetUrl: string
|
||||
let connects: string[]
|
||||
|
||||
beforeEach(async () => {
|
||||
restoreEnv = snapshotEnv([
|
||||
'HTTPS_PROXY',
|
||||
'https_proxy',
|
||||
'HTTP_PROXY',
|
||||
'http_proxy',
|
||||
'NO_PROXY',
|
||||
'no_proxy',
|
||||
'NODE_EXTRA_CA_CERTS',
|
||||
'NODE_OPTIONS',
|
||||
'NODE_TLS_REJECT_UNAUTHORIZED',
|
||||
'CLAUDE_CODE_CLIENT_CERT',
|
||||
'CLAUDE_CODE_CLIENT_KEY',
|
||||
'CLAUDE_CODE_CLIENT_KEY_PASSPHRASE',
|
||||
'CLAUDE_CODE_PROXY_RESOLVES_HOSTS',
|
||||
], { clear: true })
|
||||
directory = mkdtempSync(join(tmpdir(), 'proxy-connect-'))
|
||||
const caPath = join(directory, 'ca.pem')
|
||||
writeFileSync(caPath, cert)
|
||||
process.env.NODE_EXTRA_CA_CERTS = caPath
|
||||
clearCACertsCache()
|
||||
clearMTLSCache()
|
||||
connects = []
|
||||
|
||||
target = createHttpsServer({ key, cert }, (_request, response) => {
|
||||
response.end('secure response')
|
||||
})
|
||||
target.listen(0, '127.0.0.1')
|
||||
await once(target, 'listening')
|
||||
const targetPort = (target.address() as AddressInfo).port
|
||||
targetUrl = `https://127.0.0.1:${targetPort}/test`
|
||||
|
||||
proxy = createHttpServer()
|
||||
proxy.on('connect', (request, client, head) => {
|
||||
connects.push(request.url!)
|
||||
// Forward only to this test's target; assert the CONNECT authority below.
|
||||
const upstream = connect(targetPort, '127.0.0.1', () => {
|
||||
client.write('HTTP/1.1 200 Connection Established\r\n\r\n')
|
||||
if (head.length) upstream.write(head)
|
||||
client.pipe(upstream)
|
||||
upstream.pipe(client)
|
||||
})
|
||||
sockets.add(client)
|
||||
sockets.add(upstream)
|
||||
client.on('error', () => upstream.destroy())
|
||||
upstream.on('error', () => client.destroy())
|
||||
})
|
||||
proxy.listen(0, '127.0.0.1')
|
||||
await once(proxy, 'listening')
|
||||
process.env.HTTPS_PROXY = `http://127.0.0.1:${(proxy.address() as AddressInfo).port}`
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
getMTLSAgent()?.destroy()
|
||||
for (const socket of sockets) socket.destroy()
|
||||
sockets.clear()
|
||||
target?.closeAllConnections()
|
||||
await Promise.all(
|
||||
[target, proxy].filter(Boolean).map(server =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
server.close(error => error ? reject(error) : resolve())
|
||||
}),
|
||||
),
|
||||
)
|
||||
restoreEnv()
|
||||
clearCACertsCache()
|
||||
clearMTLSCache()
|
||||
rmSync(directory, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test('Axios tunnels HTTPS through HTTPS_PROXY and bypasses it for NO_PROXY', async () => {
|
||||
const client = createAxiosInstance()
|
||||
// Native TLS reads NODE_EXTRA_CA_CERTS only at startup. Pass the runtime
|
||||
// CA loader's result to the target request; agent options configure the proxy.
|
||||
client.defaults.transport = {
|
||||
request: (
|
||||
options: RequestOptions,
|
||||
callback: (response: IncomingMessage) => void,
|
||||
) => request({ ...options, ...getWebSocketTLSOptions() }, callback),
|
||||
}
|
||||
const response = await client.get(targetUrl, { timeout: 3000 })
|
||||
expect(response.data).toBe('secure response')
|
||||
expect(connects).toEqual([new URL(targetUrl).host])
|
||||
|
||||
process.env.NO_PROXY = '127.0.0.1'
|
||||
const direct = await client.get(targetUrl, { timeout: 3000 })
|
||||
expect(direct.data).toBe('secure response')
|
||||
expect(connects).toHaveLength(1)
|
||||
})
|
||||
|
||||
test('WebSocket agent tunnels HTTPS and is omitted for NO_PROXY', async () => {
|
||||
const agent = getWebSocketProxyAgent(targetUrl.replace('https:', 'wss:'))
|
||||
expect(agent).toBeDefined()
|
||||
try {
|
||||
const body = await new Promise<string>((resolve, reject) => {
|
||||
const request = get(
|
||||
targetUrl,
|
||||
{ agent, ...getWebSocketTLSOptions() },
|
||||
response => {
|
||||
response.setEncoding('utf8')
|
||||
let data = ''
|
||||
response.on('data', chunk => { data += chunk })
|
||||
response.on('end', () => resolve(data))
|
||||
response.on('error', reject)
|
||||
},
|
||||
)
|
||||
request.on('error', reject)
|
||||
request.setTimeout(3000, () =>
|
||||
request.destroy(new Error('HTTPS request timed out')),
|
||||
)
|
||||
})
|
||||
expect(body).toBe('secure response')
|
||||
expect(connects).toEqual([new URL(targetUrl).host])
|
||||
process.env.NO_PROXY = '127.0.0.1'
|
||||
expect(getWebSocketProxyAgent(targetUrl.replace('https:', 'wss:'))).toBeUndefined()
|
||||
} finally {
|
||||
agent?.destroy()
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user