diff --git a/src/agent.ts b/src/agent.ts index 5c6c799..754775c 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -262,4 +262,5 @@ type PacProxyAgentOptions = SocksProxyAgentOptions & { fallbackToDirect?: boolean; originalAgent?: false | http.Agent; + _vscodeTestReplaceCaCerts?: boolean; } diff --git a/src/index.ts b/src/index.ts index 39e09bc..29fc5e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -395,6 +395,7 @@ export function createHttpPatch(params: ProxyAgentParams, originals: typeof http originalAgent: (!useProxySettings || isLocalhost || config === 'fallback') ? originalAgent : undefined, lookupProxyAuthorization: params.lookupProxyAuthorization, // keepAlive: ((originalAgent || originals.globalAgent) as { keepAlive?: boolean }).keepAlive, // Skipping due to https://github.com/microsoft/vscode/issues/228872. + _vscodeTestReplaceCaCerts: (options as SecureContextOptionsPatch)._vscodeTestReplaceCaCerts, }, opts => new Promise(resolve => addCertificatesToOptionsV1(params, params.addCertificatesV1(), opts, resolve))); agent.protocol = isHttps ? 'https:' : 'http:'; options.agent = agent diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 3c6aac4..65459d5 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -19,12 +19,16 @@ services: build: test-proxy-client volumes: - ..:/repo + - ./test-https-proxy/mitmproxy-config:/root/.mitmproxy networks: - test-proxies working_dir: /repo/tests/test-client environment: - MOCHA_TESTS=src/proxy.test.ts command: /bin/sh -c ' + while [ ! -f /root/.mitmproxy/mitmproxy-ca-cert.pem ]; do sleep 1; done && + cp /root/.mitmproxy/mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy.crt && + update-ca-certificates && /usr/local/bin/configure-kerberos-client.sh && rm -rf /root/.npm && npm run test:watch' @@ -35,6 +39,8 @@ services: condition: service_started test-http-kerberos-proxy: condition: service_started + test-https-proxy: + condition: service_started test-http-proxy: image: ubuntu/squid:latest networks: @@ -68,6 +74,21 @@ services: depends_on: test-https-server: condition: service_healthy + test-https-proxy: + image: mitmproxy/mitmproxy:latest + # https://stackoverflow.com/q/61453754 + command: /bin/sh -c 'update-ca-certificates && mitmdump --set ssl_insecure=true' + volumes: + - ./test-https-proxy/mitmproxy-config:/root/.mitmproxy + - ./test-https-server/ssl_cert.pem:/usr/local/share/ca-certificates/test-https-server.crt + networks: + - test-proxies + - test-proxies-and-servers + ports: + - 8080 + depends_on: + test-https-server: + condition: service_healthy test-https-server: image: test-https-server:latest build: test-https-server diff --git a/tests/test-client/package-lock.json b/tests/test-client/package-lock.json index 631a7f8..f8bc64c 100644 --- a/tests/test-client/package-lock.json +++ b/tests/test-client/package-lock.json @@ -11,11 +11,12 @@ "devDependencies": { "@types/kerberos": "^1.1.2", "@types/mocha": "5.2.5", - "@types/node": "^16.17.1", + "@types/node": "^20.8.4", "kerberos": "^2.0.1", "mocha": "10.2.0", "ts-node": "9.1.1", - "typescript": "^4.2.2" + "typescript": "^5.2.2", + "undici": "^6.20.1" } }, "node_modules/@types/kerberos": { @@ -31,10 +32,14 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.18.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.36.tgz", - "integrity": "sha512-8egDX8dE50XyXWH6C6PRCNkTP106DuUrvdrednFouDSmCi7IOvrqr0frznfZaHifHH/3aq/7a7v9N4wdXMqhBQ==", - "dev": true + "version": "20.17.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz", + "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } }, "node_modules/ansi-colors": { "version": "4.1.1", @@ -1349,18 +1354,36 @@ } }, "node_modules/typescript": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", - "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, + "node_modules/undici": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", + "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1489,10 +1512,13 @@ "dev": true }, "@types/node": { - "version": "16.18.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.36.tgz", - "integrity": "sha512-8egDX8dE50XyXWH6C6PRCNkTP106DuUrvdrednFouDSmCi7IOvrqr0frznfZaHifHH/3aq/7a7v9N4wdXMqhBQ==", - "dev": true + "version": "20.17.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz", + "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==", + "dev": true, + "requires": { + "undici-types": "~6.19.2" + } }, "ansi-colors": { "version": "4.1.1", @@ -2427,9 +2453,21 @@ } }, "typescript": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", - "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true + }, + "undici": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", + "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "dev": true + }, + "undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, "util-deprecate": { diff --git a/tests/test-client/package.json b/tests/test-client/package.json index 93e86c2..83aa345 100644 --- a/tests/test-client/package.json +++ b/tests/test-client/package.json @@ -13,10 +13,11 @@ "devDependencies": { "@types/kerberos": "^1.1.2", "@types/mocha": "5.2.5", - "@types/node": "^16.17.1", + "@types/node": "^20.8.4", "kerberos": "^2.0.1", "mocha": "10.2.0", "ts-node": "9.1.1", - "typescript": "^4.2.2" + "typescript": "^5.2.2", + "undici": "^6.20.1" } } diff --git a/tests/test-client/src/proxy.test.ts b/tests/test-client/src/proxy.test.ts index 042d873..a928b19 100644 --- a/tests/test-client/src/proxy.test.ts +++ b/tests/test-client/src/proxy.test.ts @@ -1,8 +1,11 @@ import * as https from 'https'; +import * as tls from 'tls'; import * as assert from 'assert'; +import * as fs from 'fs'; +import * as path from 'path'; import * as vpa from '../../..'; import { createPacProxyAgent } from '../../../src/agent'; -import { testRequest, ca, unusedCa, proxiedProxyAgentParamsV1 } from './utils'; +import { testRequest, ca, unusedCa, proxiedProxyAgentParamsV1, tlsProxiedProxyAgentParamsV1 } from './utils'; describe('Proxied client', function () { it('should use HTTP proxy for HTTPS connection', function () { @@ -14,6 +17,27 @@ describe('Proxied client', function () { }); }); + it('should use HTTPS proxy for HTTPS connection', function () { + const { resolveProxyWithRequest: resolveProxy } = vpa.createProxyResolver(tlsProxiedProxyAgentParamsV1); + const patchedHttps: typeof https = { + ...https, + ...vpa.createHttpPatch(tlsProxiedProxyAgentParamsV1, https, resolveProxy), + } as any; + return testRequest(patchedHttps, { + hostname: 'test-https-server', + path: '/test-path', + _vscodeTestReplaceCaCerts: true, + }); + }); + + it('should use HTTPS proxy for HTTPS connection (fetch)', async function () { + const { resolveProxyURL } = vpa.createProxyResolver(tlsProxiedProxyAgentParamsV1); + const patchedFetch = vpa.createFetchPatch(tlsProxiedProxyAgentParamsV1, globalThis.fetch, resolveProxyURL); + const res = await patchedFetch('https://test-https-server/test-path'); + assert.strictEqual(res.status, 200); + assert.strictEqual((await res.json()).status, 'OK!'); + }); + it('should support basic auth', function () { return testRequest(https, { hostname: 'test-https-server', diff --git a/tests/test-client/src/utils.ts b/tests/test-client/src/utils.ts index 3138232..bd4036e 100644 --- a/tests/test-client/src/utils.ts +++ b/tests/test-client/src/utils.ts @@ -18,6 +18,7 @@ export const directProxyAgentParams: vpa.ProxyAgentParams = { resolveProxy: async () => 'DIRECT', getProxyURL: () => undefined, getProxySupport: () => 'override', + isAdditionalFetchSupportEnabled: () => true, addCertificatesV1: () => false, addCertificatesV2: () => true, log: console, @@ -42,6 +43,11 @@ export const proxiedProxyAgentParamsV1: vpa.ProxyAgentParams = { resolveProxy: async () => 'PROXY test-http-proxy:3128', }; +export const tlsProxiedProxyAgentParamsV1: vpa.ProxyAgentParams = { + ...directProxyAgentParamsV1, + resolveProxy: async () => 'HTTPS test-https-proxy:8080', +}; + export async function testRequest(client: C, options: C extends typeof https ? (https.RequestOptions & vpa.SecureContextOptionsPatch) : http.RequestOptions, testOptions: { assertResult?: (result: any, req: http.ClientRequest, res: http.IncomingMessage) => void; } = {}) { return new Promise((resolve, reject) => { const req = client.request(options, res => { diff --git a/tests/test-client/tsconfig.json b/tests/test-client/tsconfig.json index 1ed0e7f..d518893 100644 --- a/tests/test-client/tsconfig.json +++ b/tests/test-client/tsconfig.json @@ -4,10 +4,7 @@ "target": "es2015", "esModuleInterop": true, "strict": true, - "resolveJsonModule": true, - "lib": [ - "esnext" - ] + "resolveJsonModule": true }, "exclude": [ "node_modules" diff --git a/tests/test-https-proxy/.gitignore b/tests/test-https-proxy/.gitignore new file mode 100644 index 0000000..ab12bb6 --- /dev/null +++ b/tests/test-https-proxy/.gitignore @@ -0,0 +1 @@ +mitmproxy-config \ No newline at end of file