pax_global_header00006660000000000000000000000064136173424640014524gustar00rootroot0000000000000052 comment=8fdb1a5dd6a124951db39cb33f2438a89e0bb027 node-https-proxy-agent-5.0.0/000077500000000000000000000000001361734246400160665ustar00rootroot00000000000000node-https-proxy-agent-5.0.0/.editorconfig000066400000000000000000000013131361734246400205410ustar00rootroot00000000000000root = 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-5.0.0/.eslintrc.js000066400000000000000000000055671361734246400203420ustar00rootroot00000000000000module.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-5.0.0/.github/000077500000000000000000000000001361734246400174265ustar00rootroot00000000000000node-https-proxy-agent-5.0.0/.github/workflows/000077500000000000000000000000001361734246400214635ustar00rootroot00000000000000node-https-proxy-agent-5.0.0/.github/workflows/test.yml000066400000000000000000000015141361734246400231660ustar00rootroot00000000000000name: 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-5.0.0/.gitignore000066400000000000000000000000451361734246400200550ustar00rootroot00000000000000/node_modules /yarn.lock /?.?s /dist node-https-proxy-agent-5.0.0/.npmignore000066400000000000000000000000341361734246400200620ustar00rootroot00000000000000History.md test .travis.yml node-https-proxy-agent-5.0.0/README.md000066400000000000000000000112311361734246400173430ustar00rootroot00000000000000https-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-5.0.0/package.json000066400000000000000000000025751361734246400203650ustar00rootroot00000000000000{ "name": "https-proxy-agent", "version": "5.0.0", "description": "An HTTP(s) proxy `http.Agent` implementation for HTTPS", "main": "dist/index", "types": "dist/index", "files": [ "dist" ], "scripts": { "prebuild": "rimraf dist", "build": "tsc", "test": "mocha --reporter spec", "test-lint": "eslint src --ext .js,.ts", "prepublishOnly": "npm run build" }, "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": "6", "debug": "4" }, "devDependencies": { "@types/debug": "4", "@types/node": "^12.12.11", "@typescript-eslint/eslint-plugin": "1.6.0", "@typescript-eslint/parser": "1.1.0", "eslint": "5.16.0", "eslint-config-airbnb": "17.1.0", "eslint-config-prettier": "4.1.0", "eslint-import-resolver-typescript": "1.1.1", "eslint-plugin-import": "2.16.0", "eslint-plugin-jsx-a11y": "6.2.1", "eslint-plugin-react": "7.12.4", "mocha": "^6.2.2", "proxy": "1", "rimraf": "^3.0.0", "typescript": "^3.5.3" }, "engines": { "node": ">= 6" } } node-https-proxy-agent-5.0.0/src/000077500000000000000000000000001361734246400166555ustar00rootroot00000000000000node-https-proxy-agent-5.0.0/src/agent.ts000066400000000000000000000141351361734246400203270ustar00rootroot00000000000000import net from 'net'; import tls from 'tls'; import url from 'url'; import assert from 'assert'; import createDebug from 'debug'; import { OutgoingHttpHeaders } from 'http'; import { Agent, ClientRequest, RequestOptions } from 'agent-base'; import { HttpsProxyAgentOptions } from '.'; import parseProxyResponse from './parse-proxy-response'; const debug = createDebug('https-proxy-agent:agent'); /** * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to * the specified "HTTP(s) proxy server" in order to proxy HTTPS requests. * * Outgoing HTTP requests are first tunneled through the proxy server using the * `CONNECT` HTTP request method to establish a connection to the proxy server, * and then the proxy server connects to the destination target and issues the * HTTP request from the proxy server. * * `https:` requests have their socket connection upgraded to TLS once * the connection to the proxy server has been established. * * @api public */ export default class HttpsProxyAgent extends Agent { private secureProxy: boolean; private proxy: HttpsProxyAgentOptions; constructor(_opts: string | HttpsProxyAgentOptions) { let opts: HttpsProxyAgentOptions; if (typeof _opts === 'string') { opts = url.parse(_opts); } else { opts = _opts; } if (!opts) { throw new Error( 'an HTTP(S) proxy server `host` and `port` must be specified!' ); } debug('creating new HttpsProxyAgent instance: %o', opts); super(opts); const proxy: HttpsProxyAgentOptions = { ...opts }; // If `true`, then connect to the proxy server over TLS. // Defaults to `false`. this.secureProxy = opts.secureProxy || isHTTPS(proxy.protocol); // Prefer `hostname` over `host`, and set the `port` if needed. proxy.host = proxy.hostname || proxy.host; if (typeof proxy.port === 'string') { proxy.port = parseInt(proxy.port, 10); } if (!proxy.port && proxy.host) { 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; } /** * Called when the node-core HTTP client library is creating a * new HTTP request. * * @api protected */ async callback( req: ClientRequest, opts: RequestOptions ): Promise { const { proxy, secureProxy } = this; // Create a socket connection to the proxy server. let socket: net.Socket; if (secureProxy) { debug('Creating `tls.Socket`: %o', proxy); socket = tls.connect(proxy as tls.ConnectionOptions); } else { debug('Creating `net.Socket`: %o', proxy); socket = net.connect(proxy as net.NetConnectOpts); } const headers: OutgoingHttpHeaders = { ...proxy.headers }; const hostname = `${opts.host}:${opts.port}`; let payload = `CONNECT ${hostname} HTTP/1.1\r\n`; // Inject the `Proxy-Authorization` header if necessary. 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 not the default port. let { host, port, secureEndpoint } = opts; if (!isDefaultPort(port, secureEndpoint)) { host += `:${port}`; } headers.Host = host; headers.Connection = 'close'; for (const name of Object.keys(headers)) { payload += `${name}: ${headers[name]}\r\n`; } const proxyResponsePromise = parseProxyResponse(socket); socket.write(`${payload}\r\n`); const { statusCode, buffered } = await proxyResponsePromise; if (statusCode === 200) { req.once('socket', resume); if (opts.secureEndpoint) { const servername = opts.servername || opts.host; if (!servername) { throw new Error('Could not determine "servername"'); } // The proxy is connecting to a TLS server, so upgrade // this socket connection to a TLS connection. debug('Upgrading socket connection to TLS'); return tls.connect({ ...omit(opts, 'host', 'hostname', 'path', 'port'), socket, servername }); } return socket; } // 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. // Close the original socket, and a new "fake" 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(); const fakeSocket = new net.Socket(); fakeSocket.readable = true; // Need to wait for the "socket" event to re-play the "data" events. req.once('socket', (s: net.Socket) => { debug('replaying proxy buffer for failed request'); assert(s.listenerCount('data') > 0); // Replay the "buffered" Buffer onto the fake `socket`, since at // this point the HTTP module machinery has been hooked up for // the user. s.push(buffered); s.push(null); }); return fakeSocket; } } function resume(socket: net.Socket | tls.TLSSocket): void { socket.resume(); } function isDefaultPort(port: number, secure: boolean): boolean { return Boolean((!secure && port === 80) || (secure && port === 443)); } function isHTTPS(protocol?: string | null): boolean { return typeof protocol === 'string' ? /^https:?$/i.test(protocol) : false; } function omit( obj: T, ...keys: K ): { [K2 in Exclude]: T[K2]; } { const ret = {} as { [K in keyof typeof obj]: (typeof obj)[K]; }; let key: keyof typeof obj; for (key in obj) { if (!keys.includes(key)) { ret[key] = obj[key]; } } return ret; } node-https-proxy-agent-5.0.0/src/index.ts000066400000000000000000000017441361734246400203420ustar00rootroot00000000000000import net from 'net'; import tls from 'tls'; import { Url } from 'url'; import { AgentOptions } from 'agent-base'; import { OutgoingHttpHeaders } from 'http'; import _HttpsProxyAgent from './agent'; function createHttpsProxyAgent( opts: string | createHttpsProxyAgent.HttpsProxyAgentOptions ): _HttpsProxyAgent { return new _HttpsProxyAgent(opts); } namespace createHttpsProxyAgent { interface BaseHttpsProxyAgentOptions { headers?: OutgoingHttpHeaders; secureProxy?: boolean; host?: string | null; path?: string | null; port?: string | number | null; } export interface HttpsProxyAgentOptions extends AgentOptions, BaseHttpsProxyAgentOptions, Partial< Omit< Url & net.NetConnectOpts & tls.ConnectionOptions, keyof BaseHttpsProxyAgentOptions > > {} export type HttpsProxyAgent = _HttpsProxyAgent; export const HttpsProxyAgent = _HttpsProxyAgent; createHttpsProxyAgent.prototype = _HttpsProxyAgent.prototype; } export = createHttpsProxyAgent; node-https-proxy-agent-5.0.0/src/parse-proxy-response.ts000066400000000000000000000035711361734246400233600ustar00rootroot00000000000000import createDebug from 'debug'; import { Readable } from 'stream'; const debug = createDebug('https-proxy-agent:parse-proxy-response'); export interface ProxyResponse { statusCode: number; buffered: Buffer; } export default function parseProxyResponse( socket: Readable ): Promise { return new Promise((resolve, reject) => { // 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... let buffersLength = 0; const buffers: Buffer[] = []; function read() { const 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?: Error) { debug('onclose had error %o', err); } function onend() { debug('onend'); } function onerror(err: Error) { cleanup(); debug('onerror %o', err); reject(err); } function ondata(b: Buffer) { buffers.push(b); buffersLength += b.length; const buffered = Buffer.concat(buffers, buffersLength); const endOfHeaders = buffered.indexOf('\r\n\r\n'); if (endOfHeaders === -1) { // keep buffering debug('have not received end of HTTP headers yet...'); read(); return; } const firstLine = buffered.toString( 'ascii', 0, buffered.indexOf('\r\n') ); const statusCode = +firstLine.split(' ')[1]; debug('got proxy server response: %o', firstLine); resolve({ statusCode, buffered }); } socket.on('error', onerror); socket.on('close', onclose); socket.on('end', onend); read(); }); } node-https-proxy-agent-5.0.0/test/000077500000000000000000000000001361734246400170455ustar00rootroot00000000000000node-https-proxy-agent-5.0.0/test/ssl-cert-snakeoil.key000066400000000000000000000015671361734246400231270ustar00rootroot00000000000000-----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-5.0.0/test/ssl-cert-snakeoil.pem000066400000000000000000000012701361734246400231070ustar00rootroot00000000000000-----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-5.0.0/test/test.js000066400000000000000000000240461361734246400203700ustar00rootroot00000000000000/** * Module dependencies. */ let fs = require('fs'); let url = require('url'); let http = require('http'); let https = require('https'); let assert = require('assert'); let Proxy = require('proxy'); let HttpsProxyAgent = require('../'); describe('HttpsProxyAgent', function() { let server; let serverPort; let sslServer; let sslServerPort; let proxy; let proxyPort; let sslProxy; let 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 let 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 let 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() { let 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() { let opts = url.parse(`http://localhost:${proxyPort}`); let 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() { let agent = new HttpsProxyAgent({ port: proxyPort }); assert.equal(false, agent.secureProxy); }); it('should be `false` when "http:" protocol is used', function() { let agent = new HttpsProxyAgent({ port: proxyPort, protocol: 'http:' }); assert.equal(false, agent.secureProxy); }); it('should be `true` when "https:" protocol is used', function() { let agent = new HttpsProxyAgent({ port: proxyPort, protocol: 'https:' }); assert.equal(true, agent.secureProxy); }); it('should be `true` when "https" protocol is used', function() { let 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)); }); let proxy = process.env.HTTP_PROXY || process.env.http_proxy || `http://localhost:${proxyPort}`; let agent = new HttpsProxyAgent(proxy); let opts = url.parse(`http://localhost:${serverPort}`); opts.agent = agent; let req = http.get(opts, function(res) { let 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)); }); let proxy = process.env.HTTPS_PROXY || process.env.https_proxy || `https://localhost:${sslProxyPort}`; proxy = url.parse(proxy); proxy.rejectUnauthorized = false; let agent = new HttpsProxyAgent(proxy); let opts = url.parse(`http://localhost:${serverPort}`); opts.agent = agent; http.get(opts, function(res) { let 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); }; let proxyUri = process.env.HTTP_PROXY || process.env.http_proxy || `http://localhost:${proxyPort}`; let agent = new HttpsProxyAgent(proxyUri); let opts = {}; // `host` and `port` don't really matter since the proxy will reject anyways opts.host = 'localhost'; opts.port = 80; opts.agent = agent; let 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 "end" event on the `http.IncomingMessage` if the proxy responds with non-200 status code', 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); res.resume(); res.on('end', 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 let proxyUri = 'http://localhost:4'; let agent = new HttpsProxyAgent(proxyUri); let opts = url.parse('http://nodejs.org'); opts.agent = agent; let 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(); }); let uri = `http://localhost:${serverPort}`; let proxyOpts = url.parse(uri); proxyOpts.headers = { Foo: 'bar' }; let agent = new HttpsProxyAgent(proxyOpts); let 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)); }); let proxy = process.env.HTTP_PROXY || process.env.http_proxy || `http://localhost:${proxyPort}`; let agent = new HttpsProxyAgent(proxy); let opts = url.parse(`https://localhost:${sslServerPort}`); opts.rejectUnauthorized = false; opts.agent = agent; https.get(opts, function(res) { let 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)); }); let proxy = process.env.HTTPS_PROXY || process.env.https_proxy || `https://localhost:${sslProxyPort}`; proxy = url.parse(proxy); proxy.rejectUnauthorized = false; let agent = new HttpsProxyAgent(proxy); let opts = url.parse(`https://localhost:${sslServerPort}`); opts.agent = agent; opts.rejectUnauthorized = false; https.get(opts, function(res) { let 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)); }); let proxy = process.env.HTTPS_PROXY || process.env.https_proxy || `https://localhost:${sslProxyPort}`; proxy = url.parse(proxy); proxy.rejectUnauthorized = false; let agent = new HttpsProxyAgent(proxy); agent.defaultPort = sslServerPort; let opts = url.parse(`https://localhost:${sslServerPort}`); opts.agent = agent; opts.rejectUnauthorized = false; https.get(opts, function(res) { let 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(); }); }); }); }); }); node-https-proxy-agent-5.0.0/tsconfig.json000066400000000000000000000005421361734246400205760ustar00rootroot00000000000000{ "compilerOptions": { "strict": true, "module": "CommonJS", "target": "es2015", "esModuleInterop": true, "lib": ["esnext"], "outDir": "dist", "sourceMap": true, "declaration": true, "typeRoots": [ "./@types", "./node_modules/@types" ] }, "include": ["src/**/*"], "exclude": ["node_modules"] }