Skip to content

Commit 6f3795a

Browse files
smckee-r7pimterry
authored andcommitted
fix pac-proxy-agent use + add tests
1 parent 4f79ee5 commit 6f3795a

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

src/rules/http-agents.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as https from 'https';
66
import * as LRU from 'lru-cache';
77

88
import getHttpsProxyAgent = require('https-proxy-agent');
9-
import getPacProxyAgent = require('pac-proxy-agent');
9+
import { PacProxyAgent } from 'pac-proxy-agent';
1010
import { SocksProxyAgent } from 'socks-proxy-agent';
1111
const getSocksProxyAgent = (opts: any) => new SocksProxyAgent(opts);
1212

@@ -28,8 +28,8 @@ const ProxyAgentFactoryMap = {
2828
'http:': getHttpsProxyAgent, // HTTPS here really means 'CONNECT-tunnelled' - it can do either
2929
'https:': getHttpsProxyAgent,
3030

31-
'pac+http:': getPacProxyAgent,
32-
'pac+https:': getPacProxyAgent,
31+
'pac+http:': (...args: any) => new PacProxyAgent(...args),
32+
'pac+https:': (...args: any) => new PacProxyAgent(...args),
3333

3434
'socks:': getSocksProxyAgent,
3535
'socks4:': getSocksProxyAgent,
@@ -76,7 +76,7 @@ export async function getAgent({
7676
});
7777

7878
if (!proxyAgentCache.has(cacheKey)) {
79-
const { protocol, auth, hostname, port } = url.parse(proxySetting.proxyUrl);
79+
const { href, protocol, auth, hostname, port } = url.parse(proxySetting.proxyUrl);
8080
const buildProxyAgent = ProxyAgentFactoryMap[protocol as keyof typeof ProxyAgentFactoryMap];
8181

8282
// If you specify trusted CAs, we override the CAs used for this connection, i.e. the trusted
@@ -88,6 +88,7 @@ export async function getAgent({
8888
);
8989

9090
proxyAgentCache.set(cacheKey, buildProxyAgent({
91+
href,
9192
protocol,
9293
auth,
9394
hostname,

test/integration/proxying/upstream-proxying.spec.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import _ = require("lodash");
22
import * as fs from 'fs/promises';
33
import request = require("request-promise-native");
4+
import url = require('url');
45

56
import { getLocal, Mockttp, MockedEndpoint, getAdminServer, getRemote } from "../../..";
67
import {
@@ -453,6 +454,97 @@ nodeOnly(() => {
453454

454455
});
455456

457+
describe("with a PAC file", () => {
458+
const https = {
459+
keyPath: './test/fixtures/test-ca.key',
460+
certPath: './test/fixtures/test-ca.pem'
461+
};
462+
463+
const intermediateProxy = getLocal({ https });
464+
465+
beforeEach(async () => {
466+
server = getLocal({ https });
467+
await server.start();
468+
469+
await intermediateProxy.start();
470+
471+
process.env = _.merge({}, process.env, server.proxyEnv);
472+
});
473+
474+
afterEach(async () => {
475+
await intermediateProxy.stop();
476+
});
477+
478+
it("should forward traffic to intermediateProxy using PAC file", async () => {
479+
const pacFile = `function FindProxyForURL(url, host) { return "PROXY ${url.parse(intermediateProxy.url).host}"; }`;
480+
await remoteServer.forGet('/proxy-all').thenReply(200, pacFile);
481+
482+
await server.forAnyRequest().thenPassThrough({
483+
ignoreHostHttpsErrors: true,
484+
proxyConfig: {
485+
proxyUrl: `pac+${remoteServer.url}/proxy-all`
486+
}
487+
});
488+
489+
await intermediateProxy.forAnyRequest().thenPassThrough({
490+
ignoreHostHttpsErrors: true,
491+
beforeRequest: (req) => {
492+
expect(req.url).to.equal('https://example.com/');
493+
}
494+
});
495+
496+
// make request
497+
await request.get('https://example.com/');
498+
});
499+
500+
it("should bypass intermediateProxy using PAC file", async () => {
501+
const pacFile = `function FindProxyForURL(url, host) { if (host.endsWith(".org")) return "DIRECT"; return "PROXY ${url.parse(intermediateProxy.url).host}"; }`;
502+
await remoteServer.forGet('/proxy-bypass').thenReply(200, pacFile);
503+
504+
await server.forAnyRequest().thenPassThrough({
505+
ignoreHostHttpsErrors: true,
506+
proxyConfig: {
507+
proxyUrl: `pac+${remoteServer.url}/proxy-bypass`
508+
}
509+
});
510+
511+
await intermediateProxy.forAnyRequest().thenPassThrough({
512+
ignoreHostHttpsErrors: true,
513+
beforeRequest: (req) => {
514+
expect(req.url).to.not.equal('https://example.org/');
515+
}
516+
});
517+
518+
// make a request that hits the proxy based on PAC file
519+
await request.get('https://example.com/');
520+
521+
// make a request that bypasses proxy based on PAC file
522+
await request.get('https://example.org/');
523+
});
524+
525+
it("should fallback to intermediateProxy using PAC file", async () => {
526+
const pacFile = `function FindProxyForURL(url, host) { return "PROXY invalid-proxy:8080; PROXY ${url.parse(intermediateProxy.url).host};"; }`;
527+
await remoteServer.forGet('/proxy-fallback').thenReply(200, pacFile);
528+
529+
await server.forAnyRequest().thenPassThrough({
530+
ignoreHostHttpsErrors: true,
531+
proxyConfig: {
532+
proxyUrl: `pac+${remoteServer.url}/proxy-fallback`
533+
}
534+
});
535+
536+
await intermediateProxy.forAnyRequest().thenPassThrough({
537+
ignoreHostHttpsErrors: true,
538+
beforeRequest: (req) => {
539+
expect(req.url).to.equal('https://example.com/');
540+
}
541+
});
542+
543+
// make a request
544+
await request.get('https://example.com/');
545+
});
546+
});
547+
456548
});
457549

458550
});

0 commit comments

Comments
 (0)