Skip to content

Commit cd0525b

Browse files
ArchangelX360RasPhilCo
authored andcommitted
feat: adding no_proxy (and NO_PROXY) environment variable support (#28)
It is following the [convention of `curl`](curl/curl#1208 (comment)) regarding no_proxy
1 parent fa16392 commit cd0525b

File tree

3 files changed

+97
-4
lines changed

3 files changed

+97
-4
lines changed

src/http.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ export class HTTP<T> {
236236
this.options.protocol = u.protocol || this.options.protocol
237237
this.options.host = u.hostname || this.ctor.defaults.host || 'localhost'
238238
this.options.path = u.path || '/'
239-
this.options.agent = this.options.agent || deps.proxy.agent(this.secure)
239+
this.options.agent = this.options.agent || deps.proxy.agent(this.secure, this.options.host)
240240
this.options.port = u.port || this.options.port || (this.secure ? 443 : 80)
241241
}
242242
get headers(): http.IncomingMessage['headers'] {

src/proxy.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,81 @@ describe('with http proxy only', () => {
7373
})
7474
})
7575
})
76+
77+
describe('with no_proxy', () => {
78+
beforeEach(() => {
79+
Proxy.env.HTTP_PROXY = 'http://user:[email protected]'
80+
Proxy.env.NO_PROXY = 'some.com,test-domain.com'
81+
})
82+
83+
test('is an exact match of no_proxy', () => {
84+
expect(Proxy.agent(false, 'test-domain.com')).toBeUndefined()
85+
})
86+
87+
test('is a subdomain of no_proxy', () => {
88+
expect(Proxy.agent(false, 'something.prod.test-domain.com')).toBeUndefined()
89+
})
90+
91+
test('should be proxied', () => {
92+
expect(Proxy.agent(false, 'proxied-domain.com')).toMatchObject({
93+
options: {
94+
proxy: {
95+
host: 'foo.com',
96+
port: '8080',
97+
proxyAuth: 'user:pass',
98+
},
99+
},
100+
proxyOptions: {
101+
host: 'foo.com',
102+
port: '8080',
103+
proxyAuth: 'user:pass',
104+
},
105+
})
106+
})
107+
})
108+
109+
describe('proxy dodging', () => {
110+
test('not set should proxy', () => {
111+
Proxy.env.NO_PROXY = ''
112+
expect(Proxy.shouldDodgeProxy('test-domain.com')).toBe(false)
113+
expect(Proxy.shouldDodgeProxy('other-domain.com')).toBe(false)
114+
})
115+
116+
test('wildcard proxies any', () => {
117+
Proxy.env.NO_PROXY = '*'
118+
expect(Proxy.shouldDodgeProxy('test-domain.com')).toBe(true)
119+
expect(Proxy.shouldDodgeProxy('anything.other-domain.com')).toBe(true)
120+
})
121+
122+
test('exact domain should also match subdomains', () => {
123+
Proxy.env.NO_PROXY = 'test-domain.com'
124+
expect(Proxy.shouldDodgeProxy('test-domain.com')).toBe(true)
125+
expect(Proxy.shouldDodgeProxy('anything.test-domain.com')).toBe(true)
126+
expect(Proxy.shouldDodgeProxy('other-domain.com')).toBe(false)
127+
expect(Proxy.shouldDodgeProxy('anything.other-domain.com')).toBe(false)
128+
})
129+
130+
test('any sub domain should include the domain itself', () => {
131+
Proxy.env.NO_PROXY = '.test-domain.com'
132+
expect(Proxy.shouldDodgeProxy('test-domain.com')).toBe(true)
133+
expect(Proxy.shouldDodgeProxy('anything.test-domain.com')).toBe(true)
134+
expect(Proxy.shouldDodgeProxy('other-domain.com')).toBe(false)
135+
expect(Proxy.shouldDodgeProxy('anything.other-domain.com')).toBe(false)
136+
})
137+
138+
test('multiple domains', () => {
139+
Proxy.env.NO_PROXY = '.test-domain.com, .other-domain.com'
140+
expect(Proxy.shouldDodgeProxy('test-domain.com')).toBe(true)
141+
expect(Proxy.shouldDodgeProxy('anything.test-domain.com')).toBe(true)
142+
expect(Proxy.shouldDodgeProxy('other-domain.com')).toBe(true)
143+
expect(Proxy.shouldDodgeProxy('anything.other-domain.com')).toBe(true)
144+
})
145+
146+
test('match any subdomains', () => {
147+
Proxy.env.NO_PROXY = '.test-domain.com, other-domain.com'
148+
expect(Proxy.shouldDodgeProxy('test-domain.com')).toBe(true)
149+
expect(Proxy.shouldDodgeProxy('something.something-else.anything.test-domain.com')).toBe(true)
150+
expect(Proxy.shouldDodgeProxy('other-domain.com')).toBe(true)
151+
expect(Proxy.shouldDodgeProxy('something.anything.other-domain.com')).toBe(true)
152+
})
153+
})

src/proxy.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,22 @@ export default class ProxyUtil {
2020
return this.env.HTTPS_PROXY || this.env.https_proxy
2121
}
2222

23-
static get usingProxy(): boolean {
23+
static get noProxy() {
24+
return this.env.NO_PROXY || this.env.no_proxy
25+
}
26+
27+
static shouldDodgeProxy(host: string): boolean {
28+
if (!this.noProxy) return false
29+
if (this.noProxy === '*') return true
30+
31+
return this.noProxy
32+
.split(',')
33+
.map(p => p.trim())
34+
.some(p => (p[0] === '.' && host.endsWith(p.substr(1))) || host.endsWith(p))
35+
}
36+
37+
static usingProxy(host?: string): boolean {
38+
if (host && this.shouldDodgeProxy(host)) return false
2439
if (this.httpProxy || this.httpsProxy) return true
2540
return false
2641
}
@@ -45,8 +60,8 @@ export default class ProxyUtil {
4560
return filenames.map((filename): Buffer => fs.readFileSync(filename))
4661
}
4762

48-
static agent(https: boolean): any {
49-
if (!this.usingProxy) return
63+
static agent(https: boolean, host?: string): any {
64+
if (!this.usingProxy(host)) return
5065
const u = https ? this.httpsProxy || this.httpProxy : this.httpProxy
5166
if (u) {
5267
let proxyParsed = uri.parse(u)

0 commit comments

Comments
 (0)