Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit fdd155a

Browse files
committed
feat(http): support HTTPS_PROXY and NO_PROXY environment variables
By default, use the system-defined HTTPS proxy settings when connecting to the Cloudflare API. This module will use the proxy defined by HTTPS_PROXY unless the Cloudflare API host is matched by the pattern specified in NO_PROXY. Bug: #36
1 parent 6b8177a commit fdd155a

6 files changed

Lines changed: 189 additions & 5 deletions

File tree

index.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const prototypal = require('es-class');
1111
const auto = require('autocreate');
1212

1313
const Client = require('./lib/Client');
14+
const proxy = require('./lib/proxy');
1415

1516
/* eslint-disable global-require */
1617
const resources = {
@@ -23,6 +24,31 @@ const resources = {
2324
};
2425
/* eslint-enable global-require */
2526

27+
/**
28+
* withEnvProxy configures an HTTPS proxy if required to reach the Cloudflare API.
29+
*
30+
* @private
31+
* @param {Object} opts - The current Cloudflare options
32+
*/
33+
const withEnvProxy = function withEnvProxy(opts) {
34+
/* eslint-disable no-process-env */
35+
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
36+
const noProxy = process.env.NO_PROXY || process.env.no_proxy;
37+
/* eslint-enable no-process-env */
38+
39+
if (httpsProxy) {
40+
const agent = proxy.proxyAgent(
41+
httpsProxy,
42+
noProxy,
43+
'https://api.cloudflare.com'
44+
);
45+
46+
if (agent) {
47+
opts.agent = agent;
48+
}
49+
}
50+
};
51+
2652
/**
2753
* Constructs and returns a new Cloudflare API client with the specified authentication.
2854
*
@@ -41,10 +67,14 @@ const resources = {
4167
const Cloudflare = auto(
4268
prototypal({
4369
constructor: function constructor(auth) {
44-
const client = new Client({
70+
const opts = {
4571
email: auth && auth.email,
4672
key: auth && auth.key,
47-
});
73+
};
74+
75+
withEnvProxy(opts);
76+
77+
const client = new Client(opts);
4878

4979
Object.defineProperty(this, '_client', {
5080
value: client,

lib/Getter.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ const assign = require('object-assign');
1212
const got = require('got');
1313

1414
module.exports = prototypal({
15+
constructor: function constructor(options) {
16+
this._agent = options.agent;
17+
},
1518
got(uri, options) {
1619
options = assign({}, options);
20+
options.agent = this._agent;
1721

1822
return got(uri, options);
1923
},

lib/proxy.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (C) 2014-present Cloudflare, Inc.
3+
4+
* This software may be modified and distributed under the terms
5+
* of the MIT license. See the LICENSE file for details.
6+
*/
7+
8+
'use strict';
9+
10+
const shouldProxy = require('should-proxy');
11+
const HttpsProxyAgent = require('https-proxy-agent');
12+
13+
/**
14+
* proxyAgent returns an HTTPS agent to use to access the base URL.
15+
*
16+
* @private
17+
* @param {string} httpsProxy - HTTPS Proxy URL
18+
* @param {string} noProxy - URLs that should be excluded from proxying
19+
* @param {string} base - The client base URL
20+
* @returns {https.Agent|null} - The HTTPS agent, if required to access the base URL.
21+
*/
22+
const proxyAgent = function proxyAgent(httpsProxy, noProxy, base) {
23+
if (!httpsProxy) {
24+
return null;
25+
}
26+
noProxy = noProxy || '';
27+
28+
const ok = shouldProxy(base, {
29+
no_proxy: noProxy, // eslint-disable-line camelcase
30+
});
31+
32+
if (!ok) {
33+
return null;
34+
}
35+
36+
return new HttpsProxyAgent(httpsProxy);
37+
};
38+
39+
module.exports.proxyAgent = proxyAgent;

package-lock.json

Lines changed: 36 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
"autocreate": "^1.1.0",
1111
"es-class": "^2.1.1",
1212
"got": "^6.3.0",
13+
"https-proxy-agent": "^2.1.1",
1314
"object-assign": "^4.1.0",
15+
"should-proxy": "^1.0.4",
1416
"url-pattern": "^1.0.3"
1517
},
1618
"devDependencies": {

test/proxy.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (C) 2014-present Cloudflare, Inc.
3+
4+
* This software may be modified and distributed under the terms
5+
* of the MIT license. See the LICENSE file for details.
6+
*/
7+
8+
'use strict';
9+
10+
const assert = require('power-assert');
11+
const mocha = require('mocha');
12+
13+
const proxy = require('../lib/proxy');
14+
15+
const describe = mocha.describe;
16+
const it = mocha.it;
17+
18+
describe('proxy agents', () => {
19+
it('should not return an agent when parameters are not set', done => {
20+
const tests = [
21+
['', '', 'example.com'],
22+
[null, null, 'http://example.com/'],
23+
];
24+
25+
tests.forEach(test => {
26+
const agent = proxy.proxyAgent(test[0], test[1], test[2]);
27+
28+
assert.ok(!agent, 'agent was unexpected');
29+
});
30+
31+
done();
32+
});
33+
34+
it('should not return an agent when noProxy matches base', done => {
35+
const tests = [
36+
['http://10.0.0.1:1234', 'example.com', 'http://example.com'],
37+
['http://10.0.0.1:1234', '.example.com', 'http://example.com'],
38+
['http://10.0.0.1:1234', 'foobar.com,.example.com', 'http://example.com'],
39+
];
40+
41+
tests.forEach(test => {
42+
const agent = proxy.proxyAgent(test[0], test[1], test[2]);
43+
44+
assert.ok(!agent, 'agent was unexpected');
45+
});
46+
47+
done();
48+
});
49+
50+
it('should return an agent when noProxy is not set', done => {
51+
const tests = [
52+
['http://10.0.0.1:1234', null, 'http://example.com'],
53+
['http://10.0.0.1:1234', '', 'http://example.com'],
54+
];
55+
56+
tests.forEach(test => {
57+
const agent = proxy.proxyAgent(test[0], test[1], test[2]);
58+
59+
assert.ok(agent, 'expected an agent');
60+
});
61+
62+
done();
63+
});
64+
65+
it("should return an agent when noProxy doesn't match", done => {
66+
const agent = proxy.proxyAgent(
67+
'http://10.0.0.1:1234',
68+
'.example.com',
69+
'https://example.org'
70+
);
71+
72+
assert.ok(agent, 'expected an agent');
73+
74+
done();
75+
});
76+
});

0 commit comments

Comments
 (0)