pax_global_header00006660000000000000000000000064135740061410014513gustar00rootroot0000000000000052 comment=176d4b4fb20e229cf6cd1008f06bf97833fd725f node-https-proxy-agent-4.0.0/000077500000000000000000000000001357400614100160545ustar00rootroot00000000000000node-https-proxy-agent-4.0.0/.editorconfig000066400000000000000000000013131357400614100205270ustar00rootroot00000000000000root = true [*] indent_style = tab indent_size = 4 tab_width = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [{*.json,*.json.example,*.gyp,*.yml,*.yaml,*.workflow}] indent_style = space indent_size = 2 [{*.py,*.asm}] indent_style = space [*.py] indent_size = 4 [*.asm] indent_size = 8 [*.md] trim_trailing_whitespace = false # Ideal settings - some plugins might support these. [*.js] quote_type = single [{*.c,*.cc,*.h,*.hh,*.cpp,*.hpp,*.m,*.mm,*.mpp,*.js,*.java,*.go,*.rs,*.php,*.ng,*.jsx,*.ts,*.d,*.cs,*.swift}] curly_bracket_next_line = false spaces_around_operators = true spaces_around_brackets = outside # close enough to 1TB indent_brace_style = K&R node-https-proxy-agent-4.0.0/.eslintrc.js000066400000000000000000000055671357400614100203300ustar00rootroot00000000000000module.exports = { 'extends': [ 'airbnb', 'prettier' ], 'parser': '@typescript-eslint/parser', 'parserOptions': { 'ecmaVersion': 2018, 'sourceType': 'module', 'modules': true }, 'plugins': [ '@typescript-eslint' ], 'settings': { 'import/resolver': { 'typescript': { } } }, 'rules': { 'quotes': [ 2, 'single', { 'allowTemplateLiterals': true } ], 'class-methods-use-this': 0, 'consistent-return': 0, 'func-names': 0, 'global-require': 0, 'guard-for-in': 0, 'import/no-duplicates': 0, 'import/no-dynamic-require': 0, 'import/no-extraneous-dependencies': 0, 'import/prefer-default-export': 0, 'lines-between-class-members': 0, 'no-await-in-loop': 0, 'no-bitwise': 0, 'no-console': 0, 'no-continue': 0, 'no-control-regex': 0, 'no-empty': 0, 'no-loop-func': 0, 'no-nested-ternary': 0, 'no-param-reassign': 0, 'no-plusplus': 0, 'no-restricted-globals': 0, 'no-restricted-syntax': 0, 'no-shadow': 0, 'no-underscore-dangle': 0, 'no-use-before-define': 0, 'prefer-const': 0, 'prefer-destructuring': 0, 'camelcase': 0, 'no-unused-vars': 0, // in favor of '@typescript-eslint/no-unused-vars' // 'indent': 0 // in favor of '@typescript-eslint/indent' '@typescript-eslint/no-unused-vars': 'warn', // '@typescript-eslint/indent': ['error', 2] // this might conflict with a lot ongoing changes '@typescript-eslint/no-array-constructor': 'error', '@typescript-eslint/adjacent-overload-signatures': 'error', '@typescript-eslint/class-name-casing': 'error', '@typescript-eslint/interface-name-prefix': 'error', '@typescript-eslint/no-empty-interface': 'error', '@typescript-eslint/no-inferrable-types': 'error', '@typescript-eslint/no-misused-new': 'error', '@typescript-eslint/no-namespace': 'error', '@typescript-eslint/no-non-null-assertion': 'error', '@typescript-eslint/no-parameter-properties': 'error', '@typescript-eslint/no-triple-slash-reference': 'error', '@typescript-eslint/prefer-namespace-keyword': 'error', '@typescript-eslint/type-annotation-spacing': 'error', // '@typescript-eslint/array-type': 'error', // '@typescript-eslint/ban-types': 'error', // '@typescript-eslint/explicit-function-return-type': 'warn', // '@typescript-eslint/explicit-member-accessibility': 'error', // '@typescript-eslint/member-delimiter-style': 'error', // '@typescript-eslint/no-angle-bracket-type-assertion': 'error', // '@typescript-eslint/no-explicit-any': 'warn', // '@typescript-eslint/no-object-literal-type-assertion': 'error', // '@typescript-eslint/no-use-before-define': 'error', // '@typescript-eslint/no-var-requires': 'error', // '@typescript-eslint/prefer-interface': 'error' } } node-https-proxy-agent-4.0.0/.github/000077500000000000000000000000001357400614100174145ustar00rootroot00000000000000node-https-proxy-agent-4.0.0/.github/workflows/000077500000000000000000000000001357400614100214515ustar00rootroot00000000000000node-https-proxy-agent-4.0.0/.github/workflows/test.yml000066400000000000000000000015141357400614100231540ustar00rootroot00000000000000name: Node CI on: push: branches: - master tags: - '!*' pull_request: jobs: build: name: Test Node.js ${{ matrix.node-version }} on ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] node-version: [6.x, 8.x, 10.x, 12.x] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v1 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: Print Node.js Version run: node --version - name: Install Dependencies run: npm install env: CI: true - name: Run "build" step run: npm run build --if-present env: CI: true - name: Run tests run: npm test env: CI: true node-https-proxy-agent-4.0.0/.gitignore000066400000000000000000000000371357400614100200440ustar00rootroot00000000000000/node_modules /yarn.lock /?.js node-https-proxy-agent-4.0.0/.npmignore000066400000000000000000000000341357400614100200500ustar00rootroot00000000000000History.md test .travis.yml node-https-proxy-agent-4.0.0/README.md000066400000000000000000000112311357400614100173310ustar00rootroot00000000000000https-proxy-agent ================ ### An HTTP(s) proxy `http.Agent` implementation for HTTPS [![Build Status](https://github.com/TooTallNate/node-https-proxy-agent/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-https-proxy-agent/actions?workflow=Node+CI) This module provides an `http.Agent` implementation that connects to a specified HTTP or HTTPS proxy server, and can be used with the built-in `https` module. Specifically, this `Agent` implementation connects to an intermediary "proxy" server and issues the [CONNECT HTTP method][CONNECT], which tells the proxy to open a direct TCP connection to the destination server. Since this agent implements the CONNECT HTTP method, it also works with other protocols that use this method when connecting over proxies (i.e. WebSockets). See the "Examples" section below for more. Installation ------------ Install with `npm`: ``` bash $ npm install https-proxy-agent ``` Examples -------- #### `https` module example ``` js var url = require('url'); var https = require('https'); var HttpsProxyAgent = require('https-proxy-agent'); // HTTP/HTTPS proxy to connect to var proxy = process.env.http_proxy || 'http://168.63.76.32:3128'; console.log('using proxy server %j', proxy); // HTTPS endpoint for the proxy to connect to var endpoint = process.argv[2] || 'https://graph.facebook.com/tootallnate'; console.log('attempting to GET %j', endpoint); var options = url.parse(endpoint); // create an instance of the `HttpsProxyAgent` class with the proxy server information var agent = new HttpsProxyAgent(proxy); options.agent = agent; https.get(options, function (res) { console.log('"response" event!', res.headers); res.pipe(process.stdout); }); ``` #### `ws` WebSocket connection example ``` js var url = require('url'); var WebSocket = require('ws'); var HttpsProxyAgent = require('https-proxy-agent'); // HTTP/HTTPS proxy to connect to var proxy = process.env.http_proxy || 'http://168.63.76.32:3128'; console.log('using proxy server %j', proxy); // WebSocket endpoint for the proxy to connect to var endpoint = process.argv[2] || 'ws://echo.websocket.org'; var parsed = url.parse(endpoint); console.log('attempting to connect to WebSocket %j', endpoint); // create an instance of the `HttpsProxyAgent` class with the proxy server information var options = url.parse(proxy); var agent = new HttpsProxyAgent(options); // finally, initiate the WebSocket connection var socket = new WebSocket(endpoint, { agent: agent }); socket.on('open', function () { console.log('"open" event!'); socket.send('hello world'); }); socket.on('message', function (data, flags) { console.log('"message" event! %j %j', data, flags); socket.close(); }); ``` API --- ### new HttpsProxyAgent(Object options) The `HttpsProxyAgent` class implements an `http.Agent` subclass that connects to the specified "HTTP(s) proxy server" in order to proxy HTTPS and/or WebSocket requests. This is achieved by using the [HTTP `CONNECT` method][CONNECT]. The `options` argument may either be a string URI of the proxy server to use, or an "options" object with more specific properties: * `host` - String - Proxy host to connect to (may use `hostname` as well). Required. * `port` - Number - Proxy port to connect to. Required. * `protocol` - String - If `https:`, then use TLS to connect to the proxy. * `headers` - Object - Additional HTTP headers to be sent on the HTTP CONNECT method. * Any other options given are passed to the `net.connect()`/`tls.connect()` functions. License ------- (The MIT License) Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. [CONNECT]: http://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_Tunneling node-https-proxy-agent-4.0.0/index.d.ts000066400000000000000000000010411357400614100177510ustar00rootroot00000000000000declare module 'https-proxy-agent' { import * as https from 'https'; namespace HttpsProxyAgent { interface HttpsProxyAgentOptions { host: string; port: number | string; secureProxy?: boolean; headers?: { [key: string]: string; }; [key: string]: any; } } // HttpsProxyAgent doesnt *actually* extend https.Agent, but for my purposes I want it to pretend that it does class HttpsProxyAgent extends https.Agent { constructor(opts: HttpsProxyAgent.HttpsProxyAgentOptions | string); } export = HttpsProxyAgent; } node-https-proxy-agent-4.0.0/index.js000066400000000000000000000142651357400614100175310ustar00rootroot00000000000000/** * Module dependencies. */ var net = require('net'); var tls = require('tls'); var url = require('url'); var assert = require('assert'); var Agent = require('agent-base'); var inherits = require('util').inherits; var debug = require('debug')('https-proxy-agent'); /** * Module exports. */ module.exports = HttpsProxyAgent; /** * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to the * specified "HTTP(s) proxy server" in order to proxy HTTPS requests. * * @api public */ function HttpsProxyAgent(opts) { if (!(this instanceof HttpsProxyAgent)) return new HttpsProxyAgent(opts); if ('string' == typeof opts) opts = url.parse(opts); if (!opts) throw new Error( 'an HTTP(S) proxy server `host` and `port` must be specified!' ); debug('creating new HttpsProxyAgent instance: %o', opts); Agent.call(this, opts); var proxy = Object.assign({}, opts); // if `true`, then connect to the proxy server over TLS. defaults to `false`. this.secureProxy = proxy.protocol ? /^https:?$/i.test(proxy.protocol) : false; // prefer `hostname` over `host`, and set the `port` if needed proxy.host = proxy.hostname || proxy.host; proxy.port = +proxy.port || (this.secureProxy ? 443 : 80); // ALPN is supported by Node.js >= v5. // attempt to negotiate http/1.1 for proxy servers that support http/2 if (this.secureProxy && !('ALPNProtocols' in proxy)) { proxy.ALPNProtocols = ['http 1.1']; } if (proxy.host && proxy.path) { // if both a `host` and `path` are specified then it's most likely the // result of a `url.parse()` call... we need to remove the `path` portion so // that `net.connect()` doesn't attempt to open that as a unix socket file. delete proxy.path; delete proxy.pathname; } this.proxy = proxy; } inherits(HttpsProxyAgent, Agent); /** * Called when the node-core HTTP client library is creating a new HTTP request. * * @api public */ HttpsProxyAgent.prototype.callback = function connect(req, opts, fn) { var proxy = this.proxy; // create a socket connection to the proxy server var socket; if (this.secureProxy) { socket = tls.connect(proxy); } else { socket = net.connect(proxy); } // we need to buffer any HTTP traffic that happens with the proxy before we get // the CONNECT response, so that if the response is anything other than an "200" // response code, then we can re-play the "data" events on the socket once the // HTTP parser is hooked up... var buffers = []; var buffersLength = 0; function read() { var b = socket.read(); if (b) ondata(b); else socket.once('readable', read); } function cleanup() { socket.removeListener('end', onend); socket.removeListener('error', onerror); socket.removeListener('close', onclose); socket.removeListener('readable', read); } function onclose(err) { debug('onclose had error %o', err); } function onend() { debug('onend'); } function onerror(err) { cleanup(); fn(err); } function ondata(b) { buffers.push(b); buffersLength += b.length; var buffered = Buffer.concat(buffers, buffersLength); var str = buffered.toString('ascii'); if (!~str.indexOf('\r\n\r\n')) { // keep buffering debug('have not received end of HTTP headers yet...'); read(); return; } var firstLine = str.substring(0, str.indexOf('\r\n')); var statusCode = +firstLine.split(' ')[1]; debug('got proxy server response: %o', firstLine); if (200 == statusCode) { // 200 Connected status code! var sock = socket; // nullify the buffered data since we won't be needing it buffers = buffered = null; if (opts.secureEndpoint) { // since the proxy is connecting to an SSL server, we have // to upgrade this socket connection to an SSL connection debug( 'upgrading proxy-connected socket to TLS connection: %o', opts.host ); opts.socket = socket; opts.servername = opts.servername || opts.host; opts.host = null; opts.hostname = null; opts.port = null; sock = tls.connect(opts); } cleanup(); req.once('socket', resume); fn(null, sock); } else { // some other status code that's not 200... need to re-play the HTTP header // "data" events onto the socket once the HTTP machinery is attached so // that the node core `http` can parse and handle the error status code cleanup(); // the original socket is closed, and a new closed socket is // returned instead, so that the proxy doesn't get the HTTP request // written to it (which may contain `Authorization` headers or other // sensitive data). // // See: https://hackerone.com/reports/541502 socket.destroy(); socket = new net.Socket(); socket.readable = true; // save a reference to the concat'd Buffer for the `onsocket` callback buffers = buffered; // need to wait for the "socket" event to re-play the "data" events req.once('socket', onsocket); fn(null, socket); } } function onsocket(socket) { debug('replaying proxy buffer for failed request'); assert(socket.listenerCount('data') > 0); // replay the "buffers" Buffer onto the `socket`, since at this point // the HTTP module machinery has been hooked up for the user socket.push(buffers); // nullify the cached Buffer instance buffers = null; } socket.on('error', onerror); socket.on('close', onclose); socket.on('end', onend); read(); var hostname = opts.host + ':' + opts.port; var msg = 'CONNECT ' + hostname + ' HTTP/1.1\r\n'; var headers = Object.assign({}, proxy.headers); if (proxy.auth) { headers['Proxy-Authorization'] = 'Basic ' + Buffer.from(proxy.auth).toString('base64'); } // the Host header should only include the port // number when it is a non-standard port var host = opts.host; if (!isDefaultPort(opts.port, opts.secureEndpoint)) { host += ':' + opts.port; } headers['Host'] = host; headers['Connection'] = 'close'; Object.keys(headers).forEach(function(name) { msg += name + ': ' + headers[name] + '\r\n'; }); socket.write(msg + '\r\n'); }; /** * Resumes a socket. * * @param {(net.Socket|tls.Socket)} socket The socket to resume * @api public */ function resume(socket) { socket.resume(); } function isDefaultPort(port, secure) { return Boolean((!secure && port === 80) || (secure && port === 443)); } node-https-proxy-agent-4.0.0/package.json000066400000000000000000000014111357400614100203370ustar00rootroot00000000000000{ "name": "https-proxy-agent", "version": "4.0.0", "description": "An HTTP(s) proxy `http.Agent` implementation for HTTPS", "main": "./index.js", "types": "./index.d.ts", "scripts": { "test": "mocha --reporter spec" }, "repository": { "type": "git", "url": "git://github.com/TooTallNate/node-https-proxy-agent.git" }, "keywords": [ "https", "proxy", "endpoint", "agent" ], "author": "Nathan Rajlich (http://n8.io/)", "license": "MIT", "bugs": { "url": "https://github.com/TooTallNate/node-https-proxy-agent/issues" }, "dependencies": { "agent-base": "5", "debug": "4" }, "devDependencies": { "mocha": "6", "proxy": "1" }, "engines": { "node": ">= 6.0.0" } } node-https-proxy-agent-4.0.0/test/000077500000000000000000000000001357400614100170335ustar00rootroot00000000000000node-https-proxy-agent-4.0.0/test/ssl-cert-snakeoil.key000066400000000000000000000015671357400614100231150ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQCzURxIqzer0ACAbX/lHdsn4Gd9PLKrf7EeDYfIdV0HZKPD8WDr bBx2/fBu0OW2sjnzv/SVZbJ0DAuPE/p0+eT0qb2qC10iz9iTD7ribd7gxhirVb8y b3fBjXsxc8V8p4Ny1LcvNSqCjwUbJqdRogfoJeTiqPM58z5sNzuv5iq7iwIDAQAB AoGAPMQy4olrP0UotlzlJ36bowLP70ffgHCwU+/f4NWs5fF78c3du0oSx1w820Dd Z7E0JF8bgnlJJTxjumPZz0RUCugrEHBKJmzEz3cxF5E3+7NvteZcjKn9D67RrM5x 1/uSZ9cqKE9cYvY4fSuHx18diyZ4axR/wB1Pea2utjjDM+ECQQDb9ZbmmaWMiRpQ 5Up+loxP7BZNPsEVsm+DVJmEFbaFgGfncWBqSIqnPNjMwTwj0OigTwCAEGPkfRVW T0pbYWCxAkEA0LK7SCTwzyDmhASUalk0x+3uCAA6ryFdwJf/wd8TRAvVOmkTEldX uJ7ldLvfrONYO3v56uKTU/SoNdZYzKtO+wJAX2KM4ctXYy5BXztPpr2acz4qHa1N Bh+vBAC34fOYhyQ76r3b1btHhWZ5jbFuZwm9F2erC94Ps5IaoqcX07DSwQJAPKGw h2U0EPkd/3zVIZCJJQya+vgWFIs9EZcXVtvYXQyTBkVApTN66MhBIYjzkub5205J bVQmOV37AKklY1DhwQJAA1wos0cYxro02edzatxd0DIR2r4qqOqLkw6BhYHhq6HJ ZvIcQkHqdSXzdETFc01I1znDGGIrJHcnvKWgBPoEUg== -----END RSA PRIVATE KEY----- node-https-proxy-agent-4.0.0/test/ssl-cert-snakeoil.pem000066400000000000000000000012701357400614100230750ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIB1TCCAT4CCQDV5mPlzm9+izANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQ3 NTI3YmQ3Ny1hYjNlLTQ3NGItYWNlNy1lZWQ2MDUzOTMxZTcwHhcNMTUwNzA2MjI0 NTA3WhcNMjUwNzAzMjI0NTA3WjAvMS0wKwYDVQQDEyQ3NTI3YmQ3Ny1hYjNlLTQ3 NGItYWNlNy1lZWQ2MDUzOTMxZTcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB ALNRHEirN6vQAIBtf+Ud2yfgZ308sqt/sR4Nh8h1XQdko8PxYOtsHHb98G7Q5bay OfO/9JVlsnQMC48T+nT55PSpvaoLXSLP2JMPuuJt3uDGGKtVvzJvd8GNezFzxXyn g3LUty81KoKPBRsmp1GiB+gl5OKo8znzPmw3O6/mKruLAgMBAAEwDQYJKoZIhvcN AQEFBQADgYEACzoHUF8UV2Z6541Q2wKEA0UFUzmUjf/E1XwBO+1P15ZZ64uw34B4 1RwMPtAo9RY/PmICTWtNxWGxkzwb2JtDWtnxVER/lF8k2XcXPE76fxTHJF/BKk9J QU8OTD1dd9gHCBviQB9TqntRZ5X7axjtuWjb2umY+owBYzAHZkp1HKI= -----END CERTIFICATE----- node-https-proxy-agent-4.0.0/test/test.js000066400000000000000000000230331357400614100203510ustar00rootroot00000000000000/** * Module dependencies. */ var fs = require('fs'); var url = require('url'); var http = require('http'); var https = require('https'); var assert = require('assert'); var Proxy = require('proxy'); var HttpsProxyAgent = require('../'); describe('HttpsProxyAgent', function() { var server; var serverPort; var sslServer; var sslServerPort; var proxy; var proxyPort; var sslProxy; var sslProxyPort; before(function(done) { // setup target HTTP server server = http.createServer(); server.listen(function() { serverPort = server.address().port; done(); }); }); before(function(done) { // setup HTTP proxy server proxy = Proxy(); proxy.listen(function() { proxyPort = proxy.address().port; done(); }); }); before(function(done) { // setup target HTTPS server var options = { key: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.key'), cert: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.pem') }; sslServer = https.createServer(options); sslServer.listen(function() { sslServerPort = sslServer.address().port; done(); }); }); before(function(done) { // setup SSL HTTP proxy server var options = { key: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.key'), cert: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.pem') }; sslProxy = Proxy(https.createServer(options)); sslProxy.listen(function() { sslProxyPort = sslProxy.address().port; done(); }); }); // shut down test HTTP server after(function(done) { server.once('close', function() { done(); }); server.close(); }); after(function(done) { proxy.once('close', function() { done(); }); proxy.close(); }); after(function(done) { sslServer.once('close', function() { done(); }); sslServer.close(); }); after(function(done) { sslProxy.once('close', function() { done(); }); sslProxy.close(); }); describe('constructor', function() { it('should throw an Error if no "proxy" argument is given', function() { assert.throws(function() { new HttpsProxyAgent(); }); }); it('should accept a "string" proxy argument', function() { var agent = new HttpsProxyAgent('http://localhost:' + proxyPort); assert.equal('localhost', agent.proxy.host); assert.equal(proxyPort, agent.proxy.port); }); it('should accept a `url.parse()` result object argument', function() { var opts = url.parse('http://localhost:' + proxyPort); var agent = new HttpsProxyAgent(opts); assert.equal('localhost', agent.proxy.host); assert.equal(proxyPort, agent.proxy.port); }); describe('secureProxy', function() { it('should default to `false`', function() { var agent = new HttpsProxyAgent({ port: proxyPort }); assert.equal(false, agent.secureProxy); }); it('should be `false` when "http:" protocol is used', function() { var agent = new HttpsProxyAgent({ port: proxyPort, protocol: 'http:' }); assert.equal(false, agent.secureProxy); }); it('should be `true` when "https:" protocol is used', function() { var agent = new HttpsProxyAgent({ port: proxyPort, protocol: 'https:' }); assert.equal(true, agent.secureProxy); }); it('should be `true` when "https" protocol is used', function() { var agent = new HttpsProxyAgent({ port: proxyPort, protocol: 'https' }); assert.equal(true, agent.secureProxy); }); }); }); describe('"http" module', function() { beforeEach(function() { delete proxy.authenticate; }); it('should work over an HTTP proxy', function(done) { server.once('request', function(req, res) { res.end(JSON.stringify(req.headers)); }); var proxy = process.env.HTTP_PROXY || process.env.http_proxy || 'http://localhost:' + proxyPort; var agent = new HttpsProxyAgent(proxy); var opts = url.parse('http://localhost:' + serverPort); opts.agent = agent; var req = http.get(opts, function(res) { var data = ''; res.setEncoding('utf8'); res.on('data', function(b) { data += b; }); res.on('end', function() { data = JSON.parse(data); assert.equal('localhost:' + serverPort, data.host); done(); }); }); req.once('error', done); }); it('should work over an HTTPS proxy', function(done) { server.once('request', function(req, res) { res.end(JSON.stringify(req.headers)); }); var proxy = process.env.HTTPS_PROXY || process.env.https_proxy || 'https://localhost:' + sslProxyPort; proxy = url.parse(proxy); proxy.rejectUnauthorized = false; var agent = new HttpsProxyAgent(proxy); var opts = url.parse('http://localhost:' + serverPort); opts.agent = agent; http.get(opts, function(res) { var data = ''; res.setEncoding('utf8'); res.on('data', function(b) { data += b; }); res.on('end', function() { data = JSON.parse(data); assert.equal('localhost:' + serverPort, data.host); done(); }); }); }); it('should receive the 407 authorization code on the `http.ClientResponse`', function(done) { // set a proxy authentication function for this test proxy.authenticate = function(req, fn) { // reject all requests fn(null, false); }; var proxyUri = process.env.HTTP_PROXY || process.env.http_proxy || 'http://localhost:' + proxyPort; var agent = new HttpsProxyAgent(proxyUri); var opts = {}; // `host` and `port` don't really matter since the proxy will reject anyways opts.host = 'localhost'; opts.port = 80; opts.agent = agent; var req = http.get(opts, function(res) { assert.equal(407, res.statusCode); assert('proxy-authenticate' in res.headers); done(); }); }); it('should not error if the proxy responds with 407 and the request is aborted', function(done) { proxy.authenticate = function(req, fn) { fn(null, false); }; const proxyUri = process.env.HTTP_PROXY || process.env.http_proxy || 'http://localhost:' + proxyPort; const req = http.get( { agent: new HttpsProxyAgent(proxyUri) }, function(res) { assert.equal(407, res.statusCode); req.abort(); } ); req.on('abort', done); }); it('should emit an "error" event on the `http.ClientRequest` if the proxy does not exist', function(done) { // port 4 is a reserved, but "unassigned" port var proxyUri = 'http://localhost:4'; var agent = new HttpsProxyAgent(proxyUri); var opts = url.parse('http://nodejs.org'); opts.agent = agent; var req = http.get(opts); req.once('error', function(err) { assert.equal('ECONNREFUSED', err.code); req.abort(); done(); }); }); it('should allow custom proxy "headers"', function(done) { server.once('connect', function(req, socket, head) { assert.equal('CONNECT', req.method); assert.equal('bar', req.headers.foo); socket.destroy(); done(); }); var uri = 'http://localhost:' + serverPort; var proxyOpts = url.parse(uri); proxyOpts.headers = { Foo: 'bar' }; var agent = new HttpsProxyAgent(proxyOpts); var opts = {}; // `host` and `port` don't really matter since the proxy will reject anyways opts.host = 'localhost'; opts.port = 80; opts.agent = agent; http.get(opts); }); }); describe('"https" module', function() { it('should work over an HTTP proxy', function(done) { sslServer.once('request', function(req, res) { res.end(JSON.stringify(req.headers)); }); var proxy = process.env.HTTP_PROXY || process.env.http_proxy || 'http://localhost:' + proxyPort; var agent = new HttpsProxyAgent(proxy); var opts = url.parse('https://localhost:' + sslServerPort); opts.rejectUnauthorized = false; opts.agent = agent; https.get(opts, function(res) { var data = ''; res.setEncoding('utf8'); res.on('data', function(b) { data += b; }); res.on('end', function() { data = JSON.parse(data); assert.equal('localhost:' + sslServerPort, data.host); done(); }); }); }); it('should work over an HTTPS proxy', function(done) { sslServer.once('request', function(req, res) { res.end(JSON.stringify(req.headers)); }); var proxy = process.env.HTTPS_PROXY || process.env.https_proxy || 'https://localhost:' + sslProxyPort; proxy = url.parse(proxy); proxy.rejectUnauthorized = false; var agent = new HttpsProxyAgent(proxy); var opts = url.parse('https://localhost:' + sslServerPort); opts.agent = agent; opts.rejectUnauthorized = false; https.get(opts, function(res) { var data = ''; res.setEncoding('utf8'); res.on('data', function(b) { data += b; }); res.on('end', function() { data = JSON.parse(data); assert.equal('localhost:' + sslServerPort, data.host); done(); }); }); }); it('should not send a port number for the default port', function(done) { sslServer.once('request', function(req, res) { res.end(JSON.stringify(req.headers)); }); var proxy = process.env.HTTPS_PROXY || process.env.https_proxy || 'https://localhost:' + sslProxyPort; proxy = url.parse(proxy); proxy.rejectUnauthorized = false; var agent = new HttpsProxyAgent(proxy); agent.defaultPort = sslServerPort; var opts = url.parse('https://localhost:' + sslServerPort); opts.agent = agent; opts.rejectUnauthorized = false; https.get(opts, function(res) { var data = ''; res.setEncoding('utf8'); res.on('data', function(b) { data += b; }); res.on('end', function() { data = JSON.parse(data); assert.equal('localhost', data.host); done(); }); }); }); }); });