diff --git a/package-lock.json b/package-lock.json index c0c9210b2..d03a1f6c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -661,6 +661,14 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -4111,6 +4119,19 @@ "is-symbol": "^1.0.2" } }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -6615,6 +6636,25 @@ "toidentifier": "1.0.0" } }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -6625,6 +6665,30 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", + "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "humanize-plus": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/humanize-plus/-/humanize-plus-1.8.2.tgz", diff --git a/package.json b/package.json index 47deb2a8f..b4d9c57e4 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "helmet": "3.19.0", "html": "1.0.0", "html2plaintext": "2.1.2", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.2", "image-type": "4.1.0", "imagemin": "7.0.0", "imagemin-giflossy": "5.1.10", diff --git a/src/services/request.js b/src/services/request.js index 69d8c1f0a..e079af185 100644 --- a/src/services/request.js +++ b/src/services/request.js @@ -8,7 +8,13 @@ const url = require('url'); // this allows to support system proxy function exec(opts) { + // hack for cases where electron.net does not work but we don't want to set proxy + if (opts.proxy === 'noproxy') { + opts.proxy = null; + } + const client = getClient(opts); + const proxyAgent = getProxyAgent(opts); const parsedTargetUrl = url.parse(opts.url); return new Promise(async (resolve, reject) => { @@ -24,34 +30,18 @@ function exec(opts) { headers['Authorization'] = `Basic ${token}`; } - let host = parsedTargetUrl.hostname; - let protocol = parsedTargetUrl.protocol; - let port = parsedTargetUrl.port; - let path = parsedTargetUrl.path; - - if (opts.proxy) { - // see https://stackoverflow.com/questions/3862813/how-can-i-use-an-http-proxy-with-node-js-http-client - const parsedProxyUrl = url.parse(opts.proxy); - - protocol = parsedProxyUrl.protocol; - host = parsedProxyUrl.hostname; - port = parsedProxyUrl.port; - path = opts.url; - - headers['Host'] = parsedTargetUrl.host; // host also includes port - } - const request = client.request({ method: opts.method, // url is used by electron net module url: opts.url, // 4 fields below are used by http and https node modules - protocol, - host, - port, - path, + protocol: parsedTargetUrl.protocol, + host: parsedTargetUrl.hostname, + port: parsedTargetUrl.port, + path: parsedTargetUrl.path, timeout: opts.timeout, - headers + headers, + agent: proxyAgent }); request.on('error', err => reject(generateError(opts, err))); @@ -91,6 +81,24 @@ function exec(opts) { }) } +function getProxyAgent(opts) { + if (!opts.proxy) { + return; + } + + const {protocol} = url.parse(opts.url); + + if (protocol === 'http:' || protocol === 'https:') { + const protoNoColon = protocol.substr(0, protocol.length - 1); + const AgentClass = require(protoNoColon + '-proxy-agent'); + + return new AgentClass(opts.proxy); + } + else { + return null; + } +} + function getClient(opts) { // it's not clear how to explicitly configure proxy (as opposed to system proxy) // so in that case we always use node's modules @@ -98,14 +106,7 @@ function getClient(opts) { return require('electron').net; } else { - // hack for cases where electron.net does not work but we don't want to set proxy - if (opts.proxy === 'noproxy') { - opts.proxy = null; - } - - // in case there's explicit proxy then we need to use protocol of the proxy since we're actually - // connecting to the proxy server and not to the end-target server - const {protocol} = url.parse(opts.proxy || opts.url); + const {protocol} = url.parse(opts.url); if (protocol === 'http:' || protocol === 'https:') { return require(protocol.substr(0, protocol.length - 1));