├── index.mjs ├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── index.d.ts ├── test ├── index.test-d.ts ├── ssl.cert ├── utils.js ├── ssl.key ├── node-fetch.test.js ├── needle.test.js ├── got.test.js ├── simple-get.test.js ├── https-http.test.js ├── http-https.test.js ├── https-https.test.js └── http-http.test.js ├── LICENSE ├── package.json ├── .gitignore ├── index.js └── README.md /index.mjs: -------------------------------------------------------------------------------- 1 | import mod from './index.js' 2 | 3 | export default mod 4 | export const HttpProxyAgent = mod.HttpProxyAgent 5 | export const HttpsProxyAgent = mod.HttpsProxyAgent 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test 8 | runs-on: ${{ matrix.os }} 9 | 10 | strategy: 11 | matrix: 12 | node-version: [10.x, 12.x, 14.x] 13 | os: [ubuntu-latest, windows-latest, macOS-latest] 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | 23 | - name: Install 24 | run: | 25 | npm install 26 | 27 | - name: Test 28 | run: | 29 | npm test 30 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import * as http from 'http' 2 | import * as https from 'https' 3 | import { URL } from 'url' 4 | 5 | declare class HttpProxyAgent extends http.Agent { 6 | constructor(options: HttpProxyAgentOptions) 7 | } 8 | 9 | interface HttpProxyAgentOptions extends http.AgentOptions { 10 | proxy: string | URL 11 | } 12 | 13 | declare class HttpsProxyAgent extends https.Agent { 14 | constructor(options: HttpsProxyAgentOptions) 15 | } 16 | 17 | interface HttpsProxyAgentOptions extends https.AgentOptions { 18 | proxy: string | URL 19 | } 20 | 21 | export { 22 | HttpProxyAgent, 23 | HttpProxyAgentOptions, 24 | HttpsProxyAgent, 25 | HttpsProxyAgentOptions 26 | } 27 | -------------------------------------------------------------------------------- /test/index.test-d.ts: -------------------------------------------------------------------------------- 1 | import * as http from 'http' 2 | import * as https from 'https' 3 | import { expectType } from 'tsd' 4 | import { 5 | HttpProxyAgent, 6 | HttpProxyAgentOptions, 7 | HttpsProxyAgent, 8 | HttpsProxyAgentOptions 9 | } from '../' 10 | 11 | { 12 | const agent = new HttpProxyAgent({ 13 | keepAlive: true, 14 | keepAliveMsecs: 1000, 15 | maxSockets: 256, 16 | maxFreeSockets: 256, 17 | proxy: 'http://localhost:8080' 18 | }) 19 | 20 | expectType(agent) 21 | http.request({ 22 | method: 'GET', 23 | hostname: 'localhost', 24 | port: 9200, 25 | agent 26 | }) 27 | } 28 | 29 | { 30 | const agent = new HttpsProxyAgent({ 31 | keepAlive: true, 32 | keepAliveMsecs: 1000, 33 | maxSockets: 256, 34 | maxFreeSockets: 256, 35 | proxy: 'http://localhost:8080' 36 | }) 37 | 38 | expectType(agent) 39 | https.request({ 40 | method: 'GET', 41 | hostname: 'localhost', 42 | port: 9200, 43 | agent 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tomas Della Vedova 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/ssl.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDBzCCAe+gAwIBAgIJALbQMeb7k/WqMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV 3 | BAMMD3d3dy5mYXN0aWZ5Lm9yZzAeFw0xNzAyMDcyMDE5NDJaFw0yNzAyMDUyMDE5 4 | NDJaMBoxGDAWBgNVBAMMD3d3dy5mYXN0aWZ5Lm9yZzCCASIwDQYJKoZIhvcNAQEB 5 | BQADggEPADCCAQoCggEBAKtfXzDMmU+n3A7oVVOiqp6Z5cgu1t+qgj7TadwXONvO 6 | RZvuOcE8BZpM9tQEDE5XEIdcszDx0tWKHHSobgZAxDaEuK1PMhh/RTNvw1KzYJFm 7 | 2G38mqgm11JUni87xmIFqpgJfeCApHnWUv+3/npuQniOoVSL13jdXEifeFM8onQn 8 | R73TVDyvMOjljTulMo0n9V8pYhVSzPnm2uxTu03p5+HosQE2bU0QKj7k8/8dwRVX 9 | EqnTtbLoW+Wf7V2W3cr/UnfPH8JSaBWTqct0pgXqYIqOSTiWQkO7pE69mGPHrRlm 10 | 7+whp4WRriTacB3Ul+Cbx28wHU+D83ver4A8LKGVDSECAwEAAaNQME4wHQYDVR0O 11 | BBYEFHVzTr/tNziIUrR75UHXXA84yqmgMB8GA1UdIwQYMBaAFHVzTr/tNziIUrR7 12 | 5UHXXA84yqmgMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKVSdGeF 13 | vYcZOi0TG2WX7O3tSmu4G4nGxTldFiEVF89G0AU+HhNy9iwKXQLjDB7zMe/ZKbtJ 14 | cQgc6s8eZWxBk/OoPD1WNFGstx2EO2kRkSUBKhwnOct7CIS5X+NPXyHx2Yi03JHX 15 | unMA4WaHyo0dK4vAuali4OYdQqajNwL74avkRIxXFnZQeHzaq6tc6gX+ryB4dDSr 16 | tYn46Lo14D5jH6PtZ8DlGK+jIzM4IE7TEp2iv0CgaTU4ryt/SHPnLxfwZUpl7gSO 17 | EqkMAy3TlRMpv0oXM2Vh/CsyJzq2P/nY/O3bolsashSPWo9WsQTH4giYVA51ZVDK 18 | lGksQD+oWpfa3X0= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hpagent", 3 | "version": "0.1.2", 4 | "description": "A ready to use http and https agent for working with proxies that keeps connections alive!", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "exports": { 8 | ".": { 9 | "require": "./index.js", 10 | "import": "./index.mjs" 11 | }, 12 | "./": "./" 13 | }, 14 | "scripts": { 15 | "test": "standard && ava -v test/*.test.js && tsd" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/delvedor/hpagent.git" 20 | }, 21 | "keywords": [ 22 | "agent", 23 | "http", 24 | "https", 25 | "secure", 26 | "proxy", 27 | "alive", 28 | "keep-alive" 29 | ], 30 | "author": "Tomas Della Vedova", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/delvedor/hpagent/issues" 34 | }, 35 | "homepage": "https://github.com/delvedor/hpagent#readme", 36 | "tsd": { 37 | "directory": "test" 38 | }, 39 | "devDependencies": { 40 | "ava": "^3.10.1", 41 | "got": "^11.5.1", 42 | "needle": "^2.5.0", 43 | "node-fetch": "^2.6.0", 44 | "proxy": "^1.0.2", 45 | "simple-get": "^4.0.0", 46 | "standard": "^16.0.1", 47 | "tsd": "^0.13.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // We are using self-signed certificates 4 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 5 | 6 | const proxy = require('proxy') 7 | const { readFileSync } = require('fs') 8 | const { join } = require('path') 9 | const http = require('http') 10 | const https = require('https') 11 | 12 | const ssl = { 13 | key: readFileSync(join(__dirname, 'ssl.key')), 14 | cert: readFileSync(join(__dirname, 'ssl.cert')) 15 | } 16 | 17 | function createProxy () { 18 | return new Promise((resolve, reject) => { 19 | const server = proxy(http.createServer()) 20 | server.listen(0, '127.0.0.1', () => { 21 | resolve(server) 22 | }) 23 | }) 24 | } 25 | 26 | function createSecureProxy () { 27 | return new Promise((resolve, reject) => { 28 | const server = proxy(https.createServer(ssl)) 29 | server.listen(0, '127.0.0.1', () => { 30 | resolve(server) 31 | }) 32 | }) 33 | } 34 | 35 | function createServer (handler, callback) { 36 | return new Promise((resolve, reject) => { 37 | const server = http.createServer() 38 | server.listen(0, '127.0.0.1', () => { 39 | resolve(server) 40 | }) 41 | }) 42 | } 43 | 44 | function createSecureServer (handler, callback) { 45 | return new Promise((resolve, reject) => { 46 | const server = https.createServer(ssl) 47 | server.listen(0, '127.0.0.1', () => { 48 | resolve(server) 49 | }) 50 | }) 51 | } 52 | 53 | module.exports = { 54 | ssl, 55 | createProxy, 56 | createSecureProxy, 57 | createServer, 58 | createSecureServer 59 | } 60 | -------------------------------------------------------------------------------- /test/ssl.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEAq19fMMyZT6fcDuhVU6KqnpnlyC7W36qCPtNp3Bc4285Fm+45 3 | wTwFmkz21AQMTlcQh1yzMPHS1YocdKhuBkDENoS4rU8yGH9FM2/DUrNgkWbYbfya 4 | qCbXUlSeLzvGYgWqmAl94ICkedZS/7f+em5CeI6hVIvXeN1cSJ94UzyidCdHvdNU 5 | PK8w6OWNO6UyjSf1XyliFVLM+eba7FO7Tenn4eixATZtTRAqPuTz/x3BFVcSqdO1 6 | suhb5Z/tXZbdyv9Sd88fwlJoFZOpy3SmBepgio5JOJZCQ7ukTr2YY8etGWbv7CGn 7 | hZGuJNpwHdSX4JvHbzAdT4Pze96vgDwsoZUNIQIDAQABAoIBAG278ys/R8he1yVg 8 | lgqo9ZH7P8zwWTz9ZMsv+vAomor9SUtwvuDCO2AzejYGpY6gZ4AV1tQ3dOaxukjk 9 | 9Rbh8AJs+AhZ1t0i2b/3B95z6BkS/vFmt+2GeYhJkMT0BLMNp9AU+9p+5VLy71C5 10 | k6T3525k/l8x8HZ/YDFMk/LQt8GhvM6A3J3BNElKraiDVO6ZIWgQQ5wiefJkApo1 11 | BsptHNTx83FbnkEbAahmOR8PfKcRdKY/mZDM2WrlfoU2uwVzPV0/KdYucpsfg2et 12 | jb5bdJzcvZDuDF4GsPi1asCSC1c403R0XGuPFW9TiBuOPxbfhYK2o60yTggX6H2X 13 | 39WBc/ECgYEA3KNGgXEWzDSLpGciUisP+MzulOdQPawBTUHNykpQklEppnZbNWCX 14 | 07dv6uasnp0pFHG4WlhZJ4+IQBpZH6xAVy9y68PvN7IDYdgMiEiYPSyqQu0rvJGa 15 | 2ZR79SHDokZ8K5oofocC839RzleNRqWqxIwhHt29sxVs73kvml6OQm0CgYEAxtbA 16 | zbQwf6DXtFwutSgfOLgdXQK72beBdyeTcpUGbkonl5xHSbtz0CFmRpKiPnXfgg4W 17 | GXlTrqlYF/o048B7dU9+jCKY5DXx1Yzg/EFisEIClad3WXMhNOz1vBYVH6xU3Zq1 18 | YuYr5dcqiCWDv89e6Y6WJOhwIDZi6RqikD2EJQUCgYEAnWSAJFCnIa8OOo4z5oe/ 19 | kg2m2GQWUphEKXeatQbEaUwquQvPTsmEJUzDMr+xPkkAiAwDpbdGijkSyh/Bmh2H 20 | nGpFwbf5CzMaxI6ZihK3P1SAdNO5koAQBcytjJW0eCtt4rDK2E+5pDgcBGVia5Y8 21 | to78BYfLDlhnaIF7mtR/CRUCgYEAvGCuzvOcUv4F/eirk5NMaQb9QqYZZD2XWVTU 22 | O2T2b7yvX9J+M1t1cESESe4X6cbwlp1T0JSCdGIZhLXWL8Om80/52zfX07VLxP6w 23 | FCy6G7SeEDxVNRh+6E5qzOO65YP17vDoUacxBZJgyBWKiUkkaW9dzd+sgsgj0yYZ 24 | xz+QlyUCgYEAxdNWQnz0pR5Rt2dbIedPs7wmiZ7eAe0VjCdhMa52IyJpejdeB6Bn 25 | Es+3lkHr0Xzty8XlQZcpbswhM8UZRgPVoBvvwQdQbv5yV+LdUu69pLM7InsdZy8u 26 | opPY/+q9lRdJt4Pbep3pOWYeLP7k5l4vei2vOEMHRjHnoqM5etSb6RU= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .DS_Store 107 | -------------------------------------------------------------------------------- /test/node-fetch.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fetch = require('node-fetch') 4 | const test = require('ava') 5 | const { 6 | createServer, 7 | createSecureServer, 8 | createProxy, 9 | createSecureProxy 10 | } = require('./utils') 11 | const { HttpProxyAgent, HttpsProxyAgent } = require('../') 12 | 13 | test('http to http', async t => { 14 | const server = await createServer() 15 | const proxy = await createProxy() 16 | server.on('request', (req, res) => res.end('ok')) 17 | 18 | const response = await fetch(`http://${server.address().address}:${server.address().port}`, { 19 | agent: new HttpProxyAgent({ 20 | keepAlive: true, 21 | keepAliveMsecs: 1000, 22 | maxSockets: 256, 23 | maxFreeSockets: 256, 24 | scheduling: 'lifo', 25 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 26 | }) 27 | }) 28 | 29 | t.is(await response.text(), 'ok') 30 | t.is(response.status, 200) 31 | 32 | server.close() 33 | proxy.close() 34 | }) 35 | 36 | test('https to http', async t => { 37 | const server = await createServer() 38 | const proxy = await createSecureProxy() 39 | server.on('request', (req, res) => res.end('ok')) 40 | 41 | const response = await fetch(`http://${server.address().address}:${server.address().port}`, { 42 | agent: new HttpProxyAgent({ 43 | keepAlive: true, 44 | keepAliveMsecs: 1000, 45 | maxSockets: 256, 46 | maxFreeSockets: 256, 47 | scheduling: 'lifo', 48 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 49 | }) 50 | }) 51 | 52 | t.is(await response.text(), 'ok') 53 | t.is(response.status, 200) 54 | 55 | server.close() 56 | proxy.close() 57 | }) 58 | 59 | test('http to https', async t => { 60 | const server = await createSecureServer() 61 | const proxy = await createProxy() 62 | server.on('request', (req, res) => res.end('ok')) 63 | 64 | const response = await fetch(`https://${server.address().address}:${server.address().port}`, { 65 | agent: new HttpsProxyAgent({ 66 | keepAlive: true, 67 | keepAliveMsecs: 1000, 68 | maxSockets: 256, 69 | maxFreeSockets: 256, 70 | scheduling: 'lifo', 71 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 72 | }) 73 | }) 74 | 75 | t.is(await response.text(), 'ok') 76 | t.is(response.status, 200) 77 | 78 | server.close() 79 | proxy.close() 80 | }) 81 | 82 | test('https to https', async t => { 83 | const server = await createSecureServer() 84 | const proxy = await createSecureProxy() 85 | server.on('request', (req, res) => res.end('ok')) 86 | 87 | const response = await fetch(`https://${server.address().address}:${server.address().port}`, { 88 | agent: new HttpsProxyAgent({ 89 | keepAlive: true, 90 | keepAliveMsecs: 1000, 91 | maxSockets: 256, 92 | maxFreeSockets: 256, 93 | scheduling: 'lifo', 94 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 95 | }) 96 | }) 97 | 98 | t.is(await response.text(), 'ok') 99 | t.is(response.status, 200) 100 | 101 | server.close() 102 | proxy.close() 103 | }) 104 | -------------------------------------------------------------------------------- /test/needle.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const needle = require('needle') 4 | const test = require('ava') 5 | const { 6 | createServer, 7 | createSecureServer, 8 | createProxy, 9 | createSecureProxy 10 | } = require('./utils') 11 | const { HttpProxyAgent, HttpsProxyAgent } = require('../') 12 | 13 | test('http to http', async t => { 14 | const server = await createServer() 15 | const proxy = await createProxy() 16 | server.on('request', (req, res) => res.end('ok')) 17 | 18 | const response = await needle('get', `http://${server.address().address}:${server.address().port}`, { 19 | agent: new HttpProxyAgent({ 20 | keepAlive: true, 21 | keepAliveMsecs: 1000, 22 | maxSockets: 256, 23 | maxFreeSockets: 256, 24 | scheduling: 'lifo', 25 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 26 | }) 27 | }) 28 | 29 | t.is(response.body.toString(), 'ok') 30 | t.is(response.statusCode, 200) 31 | 32 | server.close() 33 | proxy.close() 34 | }) 35 | 36 | test('https to http', async t => { 37 | const server = await createServer() 38 | const proxy = await createSecureProxy() 39 | server.on('request', (req, res) => res.end('ok')) 40 | 41 | const response = await needle('get', `http://${server.address().address}:${server.address().port}`, { 42 | agent: new HttpProxyAgent({ 43 | keepAlive: true, 44 | keepAliveMsecs: 1000, 45 | maxSockets: 256, 46 | maxFreeSockets: 256, 47 | scheduling: 'lifo', 48 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 49 | }) 50 | }) 51 | 52 | t.is(response.body.toString(), 'ok') 53 | t.is(response.statusCode, 200) 54 | 55 | server.close() 56 | proxy.close() 57 | }) 58 | 59 | test('http to https', async t => { 60 | const server = await createSecureServer() 61 | const proxy = await createProxy() 62 | server.on('request', (req, res) => res.end('ok')) 63 | 64 | const response = await needle('get', `https://${server.address().address}:${server.address().port}`, { 65 | agent: new HttpsProxyAgent({ 66 | keepAlive: true, 67 | keepAliveMsecs: 1000, 68 | maxSockets: 256, 69 | maxFreeSockets: 256, 70 | scheduling: 'lifo', 71 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 72 | }) 73 | }) 74 | 75 | t.is(response.body.toString(), 'ok') 76 | t.is(response.statusCode, 200) 77 | 78 | server.close() 79 | proxy.close() 80 | }) 81 | 82 | test('https to https', async t => { 83 | const server = await createSecureServer() 84 | const proxy = await createSecureProxy() 85 | server.on('request', (req, res) => res.end('ok')) 86 | 87 | const response = await needle('get', `https://${server.address().address}:${server.address().port}`, { 88 | agent: new HttpsProxyAgent({ 89 | keepAlive: true, 90 | keepAliveMsecs: 1000, 91 | maxSockets: 256, 92 | maxFreeSockets: 256, 93 | scheduling: 'lifo', 94 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 95 | }) 96 | }) 97 | 98 | t.is(response.body.toString(), 'ok') 99 | t.is(response.statusCode, 200) 100 | 101 | server.close() 102 | proxy.close() 103 | }) 104 | -------------------------------------------------------------------------------- /test/got.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const got = require('got') 4 | const test = require('ava') 5 | const { 6 | createServer, 7 | createSecureServer, 8 | createProxy, 9 | createSecureProxy 10 | } = require('./utils') 11 | const { HttpProxyAgent, HttpsProxyAgent } = require('../') 12 | 13 | test('http to http', async t => { 14 | const server = await createServer() 15 | const proxy = await createProxy() 16 | server.on('request', (req, res) => res.end('ok')) 17 | 18 | const response = await got(`http://${server.address().address}:${server.address().port}`, { 19 | agent: { 20 | http: new HttpProxyAgent({ 21 | keepAlive: true, 22 | keepAliveMsecs: 1000, 23 | maxSockets: 256, 24 | maxFreeSockets: 256, 25 | scheduling: 'lifo', 26 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 27 | }) 28 | } 29 | }) 30 | 31 | t.is(response.body, 'ok') 32 | t.is(response.statusCode, 200) 33 | 34 | server.close() 35 | proxy.close() 36 | }) 37 | 38 | test('https to http', async t => { 39 | const server = await createServer() 40 | const proxy = await createSecureProxy() 41 | server.on('request', (req, res) => res.end('ok')) 42 | 43 | const response = await got(`http://${server.address().address}:${server.address().port}`, { 44 | agent: { 45 | http: new HttpProxyAgent({ 46 | keepAlive: true, 47 | keepAliveMsecs: 1000, 48 | maxSockets: 256, 49 | maxFreeSockets: 256, 50 | scheduling: 'lifo', 51 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 52 | }) 53 | } 54 | }) 55 | 56 | t.is(response.body, 'ok') 57 | t.is(response.statusCode, 200) 58 | 59 | server.close() 60 | proxy.close() 61 | }) 62 | 63 | test('http to https', async t => { 64 | const server = await createSecureServer() 65 | const proxy = await createProxy() 66 | server.on('request', (req, res) => res.end('ok')) 67 | 68 | const response = await got(`https://${server.address().address}:${server.address().port}`, { 69 | agent: { 70 | http: new HttpsProxyAgent({ 71 | keepAlive: true, 72 | keepAliveMsecs: 1000, 73 | maxSockets: 256, 74 | maxFreeSockets: 256, 75 | scheduling: 'lifo', 76 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 77 | }) 78 | } 79 | }) 80 | 81 | t.is(response.body, 'ok') 82 | t.is(response.statusCode, 200) 83 | 84 | server.close() 85 | proxy.close() 86 | }) 87 | 88 | test('https to https', async t => { 89 | const server = await createSecureServer() 90 | const proxy = await createSecureProxy() 91 | server.on('request', (req, res) => res.end('ok')) 92 | 93 | const response = await got(`https://${server.address().address}:${server.address().port}`, { 94 | agent: { 95 | http: new HttpsProxyAgent({ 96 | keepAlive: true, 97 | keepAliveMsecs: 1000, 98 | maxSockets: 256, 99 | maxFreeSockets: 256, 100 | scheduling: 'lifo', 101 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 102 | }) 103 | } 104 | }) 105 | 106 | t.is(response.body, 'ok') 107 | t.is(response.statusCode, 200) 108 | 109 | server.close() 110 | proxy.close() 111 | }) 112 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const https = require('https') 4 | const http = require('http') 5 | const { URL } = require('url') 6 | 7 | class HttpProxyAgent extends http.Agent { 8 | constructor (options) { 9 | const { proxy, ...opts } = options 10 | super(opts) 11 | this.proxy = typeof proxy === 'string' 12 | ? new URL(proxy) 13 | : proxy 14 | } 15 | 16 | createConnection (options, callback) { 17 | const requestOptions = { 18 | method: 'CONNECT', 19 | host: this.proxy.hostname, 20 | port: this.proxy.port, 21 | path: `${options.host}:${options.port}`, 22 | setHost: false, 23 | headers: { connection: this.keepAlive ? 'keep-alive' : 'close', host: `${options.host}:${options.port}` }, 24 | agent: false 25 | } 26 | 27 | if (this.proxy.username || this.proxy.password) { 28 | const base64 = Buffer.from(`${this.proxy.username || ''}:${this.proxy.password || ''}`).toString('base64') 29 | requestOptions.headers['proxy-authorization'] = `Basic ${base64}` 30 | } 31 | 32 | const request = (this.proxy.protocol === 'http:' ? http : https).request(requestOptions) 33 | request.once('connect', (response, socket, head) => { 34 | request.removeAllListeners() 35 | socket.removeAllListeners() 36 | if (response.statusCode === 200) { 37 | callback(null, socket) 38 | } else { 39 | callback(new Error(`Bad response: ${response.statusCode}`), null) 40 | } 41 | }) 42 | 43 | request.once('error', err => { 44 | request.removeAllListeners() 45 | callback(err, null) 46 | }) 47 | 48 | request.end() 49 | } 50 | } 51 | 52 | class HttpsProxyAgent extends https.Agent { 53 | constructor (options) { 54 | const { proxy, ...opts } = options 55 | super(opts) 56 | this.proxy = typeof proxy === 'string' 57 | ? new URL(proxy) 58 | : proxy 59 | } 60 | 61 | createConnection (options, callback) { 62 | const requestOptions = { 63 | method: 'CONNECT', 64 | host: this.proxy.hostname, 65 | port: this.proxy.port, 66 | path: `${options.host}:${options.port}`, 67 | setHost: false, 68 | headers: { connection: this.keepAlive ? 'keep-alive' : 'close', host: `${options.host}:${options.port}` }, 69 | agent: false 70 | } 71 | 72 | if (this.proxy.username || this.proxy.password) { 73 | const base64 = Buffer.from(`${this.proxy.username || ''}:${this.proxy.password || ''}`).toString('base64') 74 | requestOptions.headers['proxy-authorization'] = `Basic ${base64}` 75 | } 76 | 77 | const request = (this.proxy.protocol === 'http:' ? http : https).request(requestOptions) 78 | request.once('connect', (response, socket, head) => { 79 | request.removeAllListeners() 80 | socket.removeAllListeners() 81 | if (response.statusCode === 200) { 82 | const secureSocket = super.createConnection({ ...options, socket }) 83 | callback(null, secureSocket) 84 | } else { 85 | callback(new Error(`Bad response: ${response.statusCode}`), null) 86 | } 87 | }) 88 | 89 | request.once('error', err => { 90 | request.removeAllListeners() 91 | callback(err, null) 92 | }) 93 | 94 | request.end() 95 | } 96 | } 97 | 98 | module.exports = { 99 | HttpProxyAgent, 100 | HttpsProxyAgent 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hpagent 2 | 3 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) ![build](https://github.com/delvedor/hpagent/workflows/build/badge.svg) 4 | 5 | A ready to use http and https agent for working with proxies that keeps connections alive! 6 | 7 | ## Install 8 | 9 | ``` 10 | npm install hpagent 11 | ``` 12 | 13 | ## Usage 14 | 15 | Based on your infrastructure, you should use the http agent or the https agent. 16 | The following table will help you picking the right one. 17 | 18 | | Type | Proxy | Server | 19 | |-------------------|--------|--------| 20 | | `HttpProxyAgent` | HTTP | HTTP | 21 | | `HttpProxyAgent` | HTTPS | HTTP | 22 | | `HttpsProxyAgent` | HTTP | HTTPS | 23 | | `HttpsProxyAgent` | HTTPS | HTTPS | 24 | 25 | ```js 26 | const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent') 27 | ``` 28 | 29 | Once you have understood the right agent for your use case, you can instance it. It takes the same parameter of the Node.js core's http(s) agent and an additional `proxy` option, which is the url of your proxy. 30 | 31 | ```js 32 | const http = require('http') 33 | const { HttpProxyAgent } = require('hpagent') 34 | 35 | const agent = new HttpProxyAgent({ 36 | keepAlive: true, 37 | keepAliveMsecs: 1000, 38 | maxSockets: 256, 39 | maxFreeSockets: 256, 40 | proxy: 'http://localhost:8080' 41 | }) 42 | 43 | http.get('http://localhost:9200', { agent }) 44 | .on('response', console.log) 45 | .end() 46 | ``` 47 | 48 | If your proxy requires basic authentication, you can configure it in the proxy url: 49 | 50 | ```js 51 | const http = require('http') 52 | const { HttpProxyAgent } = require('hpagent') 53 | 54 | const agent = new HttpProxyAgent({ 55 | keepAlive: true, 56 | keepAliveMsecs: 1000, 57 | maxSockets: 256, 58 | maxFreeSockets: 256, 59 | proxy: 'http://user:pwd@localhost:8080' 60 | }) 61 | 62 | http.get('http://localhost:9200', { agent }) 63 | .on('response', console.log) 64 | .end() 65 | ``` 66 | 67 | ## Integrations 68 | 69 | Following you can find the list of userland http libraries that are tested with this agent. 70 | 71 | ### [got](https://github.com/sindresorhus/got) 72 | 73 | ```js 74 | got('http://localhost:9200', { 75 | agent: { 76 | http: new HttpProxyAgent({ 77 | keepAlive: true, 78 | keepAliveMsecs: 1000, 79 | maxSockets: 256, 80 | maxFreeSockets: 256, 81 | scheduling: 'lifo', 82 | proxy: 'http://localhost:8080' 83 | }) 84 | } 85 | }) 86 | ``` 87 | 88 | ### [needle](https://github.com/tomas/needle) 89 | 90 | ```js 91 | needle('get', 'http://localhost:9200', { 92 | agent: new HttpProxyAgent({ 93 | keepAlive: true, 94 | keepAliveMsecs: 1000, 95 | maxSockets: 256, 96 | maxFreeSockets: 256, 97 | scheduling: 'lifo', 98 | proxy: 'http://localhost:8080' 99 | }) 100 | }) 101 | ``` 102 | 103 | ### [node-fetch](https://github.com/node-fetch/node-fetch) 104 | 105 | ```js 106 | fetch('http://localhost:9200', { 107 | agent: new HttpProxyAgent({ 108 | keepAlive: true, 109 | keepAliveMsecs: 1000, 110 | maxSockets: 256, 111 | maxFreeSockets: 256, 112 | scheduling: 'lifo', 113 | proxy: 'http://localhost:8080' 114 | }) 115 | }) 116 | ``` 117 | 118 | ### [simple-get](https://github.com/feross/simple-get) 119 | 120 | ```js 121 | sget.concat({ 122 | url: `http://${server.address().address}:${server.address().port}`, 123 | agent: new HttpProxyAgent({ 124 | keepAlive: true, 125 | keepAliveMsecs: 1000, 126 | maxSockets: 256, 127 | maxFreeSockets: 256, 128 | scheduling: 'lifo', 129 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 130 | }) 131 | }, function (err, response, data) { 132 | // handle the response 133 | }) 134 | ``` 135 | 136 | ## License 137 | 138 | This software is licensed under the [MIT](./LICENSE). 139 | -------------------------------------------------------------------------------- /test/simple-get.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sget = require('simple-get') 4 | const test = require('ava') 5 | const { 6 | createServer, 7 | createSecureServer, 8 | createProxy, 9 | createSecureProxy 10 | } = require('./utils') 11 | const { HttpProxyAgent, HttpsProxyAgent } = require('..') 12 | 13 | test('http to http', async t => { 14 | const server = await createServer() 15 | const proxy = await createProxy() 16 | server.on('request', (req, res) => res.end('ok')) 17 | 18 | const response = await new Promise((resolve, reject) => { 19 | sget.concat({ 20 | url: `http://${server.address().address}:${server.address().port}`, 21 | agent: new HttpProxyAgent({ 22 | keepAlive: true, 23 | keepAliveMsecs: 1000, 24 | maxSockets: 256, 25 | maxFreeSockets: 256, 26 | scheduling: 'lifo', 27 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 28 | }) 29 | }, function (err, response, data) { 30 | if (err) { 31 | return reject(err) 32 | } 33 | t.is(data.toString(), 'ok') 34 | 35 | return resolve(response) 36 | }) 37 | }) 38 | 39 | t.is(response.statusCode, 200) 40 | 41 | server.close() 42 | proxy.close() 43 | }) 44 | 45 | test('https to http', async t => { 46 | const server = await createServer() 47 | const proxy = await createSecureProxy() 48 | server.on('request', (req, res) => res.end('ok')) 49 | 50 | const response = await new Promise((resolve, reject) => { 51 | sget.concat({ 52 | url: `http://${server.address().address}:${server.address().port}`, 53 | agent: new HttpProxyAgent({ 54 | keepAlive: true, 55 | keepAliveMsecs: 1000, 56 | maxSockets: 256, 57 | maxFreeSockets: 256, 58 | scheduling: 'lifo', 59 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 60 | }) 61 | }, function (err, response, data) { 62 | if (err) { 63 | return reject(err) 64 | } 65 | t.is(data.toString(), 'ok') 66 | 67 | return resolve(response) 68 | }) 69 | }) 70 | 71 | t.is(response.statusCode, 200) 72 | 73 | server.close() 74 | proxy.close() 75 | }) 76 | 77 | test('http to https', async t => { 78 | const server = await createSecureServer() 79 | const proxy = await createProxy() 80 | server.on('request', (req, res) => res.end('ok')) 81 | 82 | const response = await new Promise((resolve, reject) => { 83 | sget.concat({ 84 | url: `https://${server.address().address}:${server.address().port}`, 85 | agent: new HttpsProxyAgent({ 86 | keepAlive: true, 87 | keepAliveMsecs: 1000, 88 | maxSockets: 256, 89 | maxFreeSockets: 256, 90 | scheduling: 'lifo', 91 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 92 | }) 93 | }, function (err, response, data) { 94 | if (err) { 95 | return reject(err) 96 | } 97 | t.is(data.toString(), 'ok') 98 | 99 | return resolve(response) 100 | }) 101 | }) 102 | 103 | t.is(response.statusCode, 200) 104 | 105 | server.close() 106 | proxy.close() 107 | }) 108 | 109 | test('https to https', async t => { 110 | const server = await createSecureServer() 111 | const proxy = await createSecureProxy() 112 | server.on('request', (req, res) => res.end('ok')) 113 | 114 | const response = await new Promise((resolve, reject) => { 115 | sget.concat({ 116 | url: `https://${server.address().address}:${server.address().port}`, 117 | agent: new HttpsProxyAgent({ 118 | keepAlive: true, 119 | keepAliveMsecs: 1000, 120 | maxSockets: 256, 121 | maxFreeSockets: 256, 122 | scheduling: 'lifo', 123 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 124 | }) 125 | }, function (err, response, data) { 126 | if (err) { 127 | return reject(err) 128 | } 129 | t.is(data.toString(), 'ok') 130 | 131 | return resolve(response) 132 | }) 133 | }) 134 | 135 | t.is(response.statusCode, 200) 136 | 137 | server.close() 138 | proxy.close() 139 | }) 140 | -------------------------------------------------------------------------------- /test/https-http.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const http = require('http') 4 | const test = require('ava') 5 | const { createServer, createSecureProxy } = require('./utils') 6 | const { HttpProxyAgent } = require('../') 7 | 8 | function request (opts) { 9 | return new Promise((resolve, reject) => { 10 | const req = http.request(opts, resolve) 11 | req.on('error', reject) 12 | req.end(opts.body) 13 | }) 14 | } 15 | 16 | test('Basic', async t => { 17 | const server = await createServer() 18 | const proxy = await createSecureProxy() 19 | server.on('request', (req, res) => res.end('ok')) 20 | 21 | const response = await request({ 22 | method: 'GET', 23 | hostname: server.address().address, 24 | port: server.address().port, 25 | path: '/', 26 | agent: new HttpProxyAgent({ 27 | keepAlive: true, 28 | keepAliveMsecs: 1000, 29 | maxSockets: 256, 30 | maxFreeSockets: 256, 31 | scheduling: 'lifo', 32 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 33 | }) 34 | }) 35 | 36 | let body = '' 37 | response.setEncoding('utf8') 38 | for await (const chunk of response) { 39 | body += chunk 40 | } 41 | 42 | t.is(body, 'ok') 43 | t.is(response.statusCode, 200) 44 | 45 | server.close() 46 | proxy.close() 47 | }) 48 | 49 | test('Connection header (keep-alive)', async t => { 50 | const server = await createServer() 51 | const proxy = await createSecureProxy() 52 | server.on('request', (req, res) => res.end('ok')) 53 | 54 | proxy.authenticate = function (req, fn) { 55 | t.is(req.headers.connection, 'keep-alive') 56 | fn(null, true) 57 | } 58 | 59 | const response = await request({ 60 | method: 'GET', 61 | hostname: server.address().address, 62 | port: server.address().port, 63 | path: '/', 64 | agent: new HttpProxyAgent({ 65 | keepAlive: true, 66 | keepAliveMsecs: 1000, 67 | maxSockets: 256, 68 | maxFreeSockets: 256, 69 | scheduling: 'lifo', 70 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 71 | }) 72 | }) 73 | 74 | let body = '' 75 | response.setEncoding('utf8') 76 | for await (const chunk of response) { 77 | body += chunk 78 | } 79 | 80 | t.is(body, 'ok') 81 | t.is(response.statusCode, 200) 82 | 83 | server.close() 84 | proxy.close() 85 | }) 86 | 87 | test('Connection header (close)', async t => { 88 | const server = await createServer() 89 | const proxy = await createSecureProxy() 90 | server.on('request', (req, res) => res.end('ok')) 91 | 92 | proxy.authenticate = function (req, fn) { 93 | t.is(req.headers.connection, 'close') 94 | fn(null, true) 95 | } 96 | 97 | const response = await request({ 98 | method: 'GET', 99 | hostname: server.address().address, 100 | port: server.address().port, 101 | path: '/', 102 | agent: new HttpProxyAgent({ 103 | keepAlive: false, 104 | keepAliveMsecs: 1000, 105 | maxSockets: Infinity, 106 | maxFreeSockets: 256, 107 | scheduling: 'lifo', 108 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 109 | }) 110 | }) 111 | 112 | let body = '' 113 | response.setEncoding('utf8') 114 | for await (const chunk of response) { 115 | body += chunk 116 | } 117 | 118 | t.is(body, 'ok') 119 | t.is(response.statusCode, 200) 120 | 121 | server.close() 122 | proxy.close() 123 | }) 124 | 125 | test('Proxy authentication (empty)', async t => { 126 | const server = await createServer() 127 | const proxy = await createSecureProxy() 128 | server.on('request', (req, res) => res.end('ok')) 129 | 130 | proxy.authenticate = function (req, fn) { 131 | fn(null, req.headers['proxy-authorization'] === undefined) 132 | } 133 | 134 | const response = await request({ 135 | method: 'GET', 136 | hostname: server.address().address, 137 | port: server.address().port, 138 | path: '/', 139 | agent: new HttpProxyAgent({ 140 | keepAlive: true, 141 | keepAliveMsecs: 1000, 142 | maxSockets: 256, 143 | maxFreeSockets: 256, 144 | scheduling: 'lifo', 145 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 146 | }) 147 | }) 148 | 149 | let body = '' 150 | response.setEncoding('utf8') 151 | for await (const chunk of response) { 152 | body += chunk 153 | } 154 | 155 | t.is(body, 'ok') 156 | t.is(response.statusCode, 200) 157 | 158 | server.close() 159 | proxy.close() 160 | }) 161 | 162 | test('Proxy authentication', async t => { 163 | const server = await createServer() 164 | const proxy = await createSecureProxy() 165 | server.on('request', (req, res) => res.end('ok')) 166 | 167 | proxy.authenticate = function (req, fn) { 168 | fn(null, req.headers['proxy-authorization'] === `Basic ${Buffer.from('hello:world').toString('base64')}`) 169 | } 170 | 171 | const response = await request({ 172 | method: 'GET', 173 | hostname: server.address().address, 174 | port: server.address().port, 175 | path: '/', 176 | agent: new HttpProxyAgent({ 177 | keepAlive: true, 178 | keepAliveMsecs: 1000, 179 | maxSockets: 256, 180 | maxFreeSockets: 256, 181 | scheduling: 'lifo', 182 | proxy: `https://hello:world@${proxy.address().address}:${proxy.address().port}` 183 | }) 184 | }) 185 | 186 | let body = '' 187 | response.setEncoding('utf8') 188 | for await (const chunk of response) { 189 | body += chunk 190 | } 191 | 192 | t.is(body, 'ok') 193 | t.is(response.statusCode, 200) 194 | 195 | server.close() 196 | proxy.close() 197 | }) 198 | 199 | test('Configure the agent to reuse sockets', async t => { 200 | const server = await createServer() 201 | const proxy = await createSecureProxy() 202 | server.on('request', (req, res) => res.end('ok')) 203 | 204 | let count = 0 205 | proxy.on('connection', () => { 206 | count += 1 207 | t.is(count, 1) 208 | }) 209 | 210 | const agent = new HttpProxyAgent({ 211 | keepAlive: true, 212 | keepAliveMsecs: 1000, 213 | maxSockets: 256, 214 | maxFreeSockets: 256, 215 | scheduling: 'lifo', 216 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 217 | }) 218 | 219 | let response = await request({ 220 | method: 'GET', 221 | hostname: server.address().address, 222 | port: server.address().port, 223 | path: '/', 224 | agent 225 | }) 226 | 227 | let body = '' 228 | response.setEncoding('utf8') 229 | for await (const chunk of response) { 230 | body += chunk 231 | } 232 | 233 | t.is(body, 'ok') 234 | t.is(response.statusCode, 200) 235 | 236 | response = await request({ 237 | method: 'GET', 238 | hostname: server.address().address, 239 | port: server.address().port, 240 | path: '/', 241 | agent 242 | }) 243 | 244 | body = '' 245 | response.setEncoding('utf8') 246 | for await (const chunk of response) { 247 | body += chunk 248 | } 249 | 250 | t.is(body, 'ok') 251 | t.is(response.statusCode, 200) 252 | 253 | server.close() 254 | proxy.close() 255 | }) 256 | 257 | test('Configure the agent to NOT reuse sockets', async t => { 258 | const server = await createServer() 259 | const proxy = await createSecureProxy() 260 | server.on('request', (req, res) => res.end('ok')) 261 | 262 | const ports = [] 263 | proxy.on('connection', socket => { 264 | t.false(ports.includes(socket.remotePort)) 265 | ports.push(socket.remotePort) 266 | }) 267 | 268 | const agent = new HttpProxyAgent({ 269 | keepAlive: false, 270 | keepAliveMsecs: 1000, 271 | maxSockets: Infinity, 272 | maxFreeSockets: 256, 273 | scheduling: 'lifo', 274 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 275 | }) 276 | 277 | let response = await request({ 278 | method: 'GET', 279 | hostname: server.address().address, 280 | port: server.address().port, 281 | path: '/', 282 | agent 283 | }) 284 | 285 | let body = '' 286 | response.setEncoding('utf8') 287 | for await (const chunk of response) { 288 | body += chunk 289 | } 290 | 291 | t.is(body, 'ok') 292 | t.is(response.statusCode, 200) 293 | 294 | response = await request({ 295 | method: 'GET', 296 | hostname: server.address().address, 297 | port: server.address().port, 298 | path: '/', 299 | agent 300 | }) 301 | 302 | body = '' 303 | response.setEncoding('utf8') 304 | for await (const chunk of response) { 305 | body += chunk 306 | } 307 | 308 | t.is(body, 'ok') 309 | t.is(response.statusCode, 200) 310 | 311 | server.close() 312 | proxy.close() 313 | }) 314 | -------------------------------------------------------------------------------- /test/http-https.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const https = require('https') 4 | const test = require('ava') 5 | const { createSecureServer, createProxy } = require('./utils') 6 | const { HttpsProxyAgent } = require('../') 7 | 8 | function request (opts) { 9 | return new Promise((resolve, reject) => { 10 | const req = https.request(opts, resolve) 11 | req.on('error', reject) 12 | req.end(opts.body) 13 | }) 14 | } 15 | 16 | test('Basic', async t => { 17 | const server = await createSecureServer() 18 | const proxy = await createProxy() 19 | server.on('request', (req, res) => res.end('ok')) 20 | 21 | const response = await request({ 22 | method: 'GET', 23 | hostname: server.address().address, 24 | port: server.address().port, 25 | path: '/', 26 | agent: new HttpsProxyAgent({ 27 | keepAlive: true, 28 | keepAliveMsecs: 1000, 29 | maxSockets: 256, 30 | maxFreeSockets: 256, 31 | scheduling: 'lifo', 32 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 33 | }) 34 | }) 35 | 36 | let body = '' 37 | response.setEncoding('utf8') 38 | for await (const chunk of response) { 39 | body += chunk 40 | } 41 | 42 | t.is(body, 'ok') 43 | t.is(response.statusCode, 200) 44 | 45 | server.close() 46 | proxy.close() 47 | }) 48 | 49 | test('Connection header (keep-alive)', async t => { 50 | const server = await createSecureServer() 51 | const proxy = await createProxy() 52 | server.on('request', (req, res) => res.end('ok')) 53 | 54 | proxy.authenticate = function (req, fn) { 55 | t.is(req.headers.connection, 'keep-alive') 56 | fn(null, true) 57 | } 58 | 59 | const response = await request({ 60 | method: 'GET', 61 | hostname: server.address().address, 62 | port: server.address().port, 63 | path: '/', 64 | agent: new HttpsProxyAgent({ 65 | keepAlive: true, 66 | keepAliveMsecs: 1000, 67 | maxSockets: 256, 68 | maxFreeSockets: 256, 69 | scheduling: 'lifo', 70 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 71 | }) 72 | }) 73 | 74 | let body = '' 75 | response.setEncoding('utf8') 76 | for await (const chunk of response) { 77 | body += chunk 78 | } 79 | 80 | t.is(body, 'ok') 81 | t.is(response.statusCode, 200) 82 | 83 | server.close() 84 | proxy.close() 85 | }) 86 | 87 | test('Connection header (close)', async t => { 88 | const server = await createSecureServer() 89 | const proxy = await createProxy() 90 | server.on('request', (req, res) => res.end('ok')) 91 | 92 | proxy.authenticate = function (req, fn) { 93 | t.is(req.headers.connection, 'close') 94 | fn(null, true) 95 | } 96 | 97 | const response = await request({ 98 | method: 'GET', 99 | hostname: server.address().address, 100 | port: server.address().port, 101 | path: '/', 102 | agent: new HttpsProxyAgent({ 103 | keepAlive: false, 104 | keepAliveMsecs: 1000, 105 | maxSockets: Infinity, 106 | maxFreeSockets: 256, 107 | scheduling: 'lifo', 108 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 109 | }) 110 | }) 111 | 112 | let body = '' 113 | response.setEncoding('utf8') 114 | for await (const chunk of response) { 115 | body += chunk 116 | } 117 | 118 | t.is(body, 'ok') 119 | t.is(response.statusCode, 200) 120 | 121 | server.close() 122 | proxy.close() 123 | }) 124 | 125 | test('Proxy authentication (empty)', async t => { 126 | const server = await createSecureServer() 127 | const proxy = await createProxy() 128 | server.on('request', (req, res) => res.end('ok')) 129 | 130 | proxy.authenticate = function (req, fn) { 131 | fn(null, req.headers['proxy-authorization'] === undefined) 132 | } 133 | 134 | const response = await request({ 135 | method: 'GET', 136 | hostname: server.address().address, 137 | port: server.address().port, 138 | path: '/', 139 | agent: new HttpsProxyAgent({ 140 | keepAlive: true, 141 | keepAliveMsecs: 1000, 142 | maxSockets: 256, 143 | maxFreeSockets: 256, 144 | scheduling: 'lifo', 145 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 146 | }) 147 | }) 148 | 149 | let body = '' 150 | response.setEncoding('utf8') 151 | for await (const chunk of response) { 152 | body += chunk 153 | } 154 | 155 | t.is(body, 'ok') 156 | t.is(response.statusCode, 200) 157 | 158 | server.close() 159 | proxy.close() 160 | }) 161 | 162 | test('Proxy authentication', async t => { 163 | const server = await createSecureServer() 164 | const proxy = await createProxy() 165 | server.on('request', (req, res) => res.end('ok')) 166 | 167 | proxy.authenticate = function (req, fn) { 168 | fn(null, req.headers['proxy-authorization'] === `Basic ${Buffer.from('hello:world').toString('base64')}`) 169 | } 170 | 171 | const response = await request({ 172 | method: 'GET', 173 | hostname: server.address().address, 174 | port: server.address().port, 175 | path: '/', 176 | agent: new HttpsProxyAgent({ 177 | keepAlive: true, 178 | keepAliveMsecs: 1000, 179 | maxSockets: 256, 180 | maxFreeSockets: 256, 181 | scheduling: 'lifo', 182 | proxy: `http://hello:world@${proxy.address().address}:${proxy.address().port}` 183 | }) 184 | }) 185 | 186 | let body = '' 187 | response.setEncoding('utf8') 188 | for await (const chunk of response) { 189 | body += chunk 190 | } 191 | 192 | t.is(body, 'ok') 193 | t.is(response.statusCode, 200) 194 | 195 | server.close() 196 | proxy.close() 197 | }) 198 | 199 | test('Configure the agent to reuse sockets', async t => { 200 | const server = await createSecureServer() 201 | const proxy = await createProxy() 202 | server.on('request', (req, res) => res.end('ok')) 203 | 204 | let count = 0 205 | proxy.on('connection', () => { 206 | count += 1 207 | t.is(count, 1) 208 | }) 209 | 210 | const agent = new HttpsProxyAgent({ 211 | keepAlive: true, 212 | keepAliveMsecs: 1000, 213 | maxSockets: 256, 214 | maxFreeSockets: 256, 215 | scheduling: 'lifo', 216 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 217 | }) 218 | 219 | let response = await request({ 220 | method: 'GET', 221 | hostname: server.address().address, 222 | port: server.address().port, 223 | path: '/', 224 | agent 225 | }) 226 | 227 | let body = '' 228 | response.setEncoding('utf8') 229 | for await (const chunk of response) { 230 | body += chunk 231 | } 232 | 233 | t.is(body, 'ok') 234 | t.is(response.statusCode, 200) 235 | 236 | response = await request({ 237 | method: 'GET', 238 | hostname: server.address().address, 239 | port: server.address().port, 240 | path: '/', 241 | agent 242 | }) 243 | 244 | body = '' 245 | response.setEncoding('utf8') 246 | for await (const chunk of response) { 247 | body += chunk 248 | } 249 | 250 | t.is(body, 'ok') 251 | t.is(response.statusCode, 200) 252 | 253 | server.close() 254 | proxy.close() 255 | }) 256 | 257 | test('Configure the agent to NOT reuse sockets', async t => { 258 | const server = await createSecureServer() 259 | const proxy = await createProxy() 260 | server.on('request', (req, res) => res.end('ok')) 261 | 262 | const ports = [] 263 | proxy.on('connection', socket => { 264 | t.false(ports.includes(socket.remotePort)) 265 | ports.push(socket.remotePort) 266 | }) 267 | 268 | const agent = new HttpsProxyAgent({ 269 | keepAlive: false, 270 | keepAliveMsecs: 1000, 271 | maxSockets: Infinity, 272 | maxFreeSockets: 256, 273 | scheduling: 'lifo', 274 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 275 | }) 276 | 277 | let response = await request({ 278 | method: 'GET', 279 | hostname: server.address().address, 280 | port: server.address().port, 281 | path: '/', 282 | agent 283 | }) 284 | 285 | let body = '' 286 | response.setEncoding('utf8') 287 | for await (const chunk of response) { 288 | body += chunk 289 | } 290 | 291 | t.is(body, 'ok') 292 | t.is(response.statusCode, 200) 293 | 294 | response = await request({ 295 | method: 'GET', 296 | hostname: server.address().address, 297 | port: server.address().port, 298 | path: '/', 299 | agent 300 | }) 301 | 302 | body = '' 303 | response.setEncoding('utf8') 304 | for await (const chunk of response) { 305 | body += chunk 306 | } 307 | 308 | t.is(body, 'ok') 309 | t.is(response.statusCode, 200) 310 | 311 | server.close() 312 | proxy.close() 313 | }) 314 | -------------------------------------------------------------------------------- /test/https-https.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const https = require('https') 4 | const test = require('ava') 5 | const { createSecureServer, createSecureProxy } = require('./utils') 6 | const { HttpsProxyAgent } = require('../') 7 | 8 | function request (opts) { 9 | return new Promise((resolve, reject) => { 10 | const req = https.request(opts, resolve) 11 | req.on('error', reject) 12 | req.end(opts.body) 13 | }) 14 | } 15 | 16 | test('Basic', async t => { 17 | const server = await createSecureServer() 18 | const proxy = await createSecureProxy() 19 | server.on('request', (req, res) => res.end('ok')) 20 | 21 | const response = await request({ 22 | method: 'GET', 23 | hostname: server.address().address, 24 | port: server.address().port, 25 | path: '/', 26 | agent: new HttpsProxyAgent({ 27 | keepAlive: true, 28 | keepAliveMsecs: 1000, 29 | maxSockets: 256, 30 | maxFreeSockets: 256, 31 | scheduling: 'lifo', 32 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 33 | }) 34 | }) 35 | 36 | let body = '' 37 | response.setEncoding('utf8') 38 | for await (const chunk of response) { 39 | body += chunk 40 | } 41 | 42 | t.is(body, 'ok') 43 | t.is(response.statusCode, 200) 44 | 45 | server.close() 46 | proxy.close() 47 | }) 48 | 49 | test('Connection header (keep-alive)', async t => { 50 | const server = await createSecureServer() 51 | const proxy = await createSecureProxy() 52 | server.on('request', (req, res) => res.end('ok')) 53 | 54 | proxy.authenticate = function (req, fn) { 55 | t.is(req.headers.connection, 'keep-alive') 56 | fn(null, true) 57 | } 58 | 59 | const response = await request({ 60 | method: 'GET', 61 | hostname: server.address().address, 62 | port: server.address().port, 63 | path: '/', 64 | agent: new HttpsProxyAgent({ 65 | keepAlive: true, 66 | keepAliveMsecs: 1000, 67 | maxSockets: 256, 68 | maxFreeSockets: 256, 69 | scheduling: 'lifo', 70 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 71 | }) 72 | }) 73 | 74 | let body = '' 75 | response.setEncoding('utf8') 76 | for await (const chunk of response) { 77 | body += chunk 78 | } 79 | 80 | t.is(body, 'ok') 81 | t.is(response.statusCode, 200) 82 | 83 | server.close() 84 | proxy.close() 85 | }) 86 | 87 | test('Connection header (close)', async t => { 88 | const server = await createSecureServer() 89 | const proxy = await createSecureProxy() 90 | server.on('request', (req, res) => res.end('ok')) 91 | 92 | proxy.authenticate = function (req, fn) { 93 | t.is(req.headers.connection, 'close') 94 | fn(null, true) 95 | } 96 | 97 | const response = await request({ 98 | method: 'GET', 99 | hostname: server.address().address, 100 | port: server.address().port, 101 | path: '/', 102 | agent: new HttpsProxyAgent({ 103 | keepAlive: false, 104 | keepAliveMsecs: 1000, 105 | maxSockets: Infinity, 106 | maxFreeSockets: 256, 107 | scheduling: 'lifo', 108 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 109 | }) 110 | }) 111 | 112 | let body = '' 113 | response.setEncoding('utf8') 114 | for await (const chunk of response) { 115 | body += chunk 116 | } 117 | 118 | t.is(body, 'ok') 119 | t.is(response.statusCode, 200) 120 | 121 | server.close() 122 | proxy.close() 123 | }) 124 | 125 | test('Proxy authentication (empty)', async t => { 126 | const server = await createSecureServer() 127 | const proxy = await createSecureProxy() 128 | server.on('request', (req, res) => res.end('ok')) 129 | 130 | proxy.authenticate = function (req, fn) { 131 | fn(null, req.headers['proxy-authorization'] === undefined) 132 | } 133 | 134 | const response = await request({ 135 | method: 'GET', 136 | hostname: server.address().address, 137 | port: server.address().port, 138 | path: '/', 139 | agent: new HttpsProxyAgent({ 140 | keepAlive: true, 141 | keepAliveMsecs: 1000, 142 | maxSockets: 256, 143 | maxFreeSockets: 256, 144 | scheduling: 'lifo', 145 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 146 | }) 147 | }) 148 | 149 | let body = '' 150 | response.setEncoding('utf8') 151 | for await (const chunk of response) { 152 | body += chunk 153 | } 154 | 155 | t.is(body, 'ok') 156 | t.is(response.statusCode, 200) 157 | 158 | server.close() 159 | proxy.close() 160 | }) 161 | 162 | test('Proxy authentication', async t => { 163 | const server = await createSecureServer() 164 | const proxy = await createSecureProxy() 165 | server.on('request', (req, res) => res.end('ok')) 166 | 167 | proxy.authenticate = function (req, fn) { 168 | fn(null, req.headers['proxy-authorization'] === `Basic ${Buffer.from('hello:world').toString('base64')}`) 169 | } 170 | 171 | const response = await request({ 172 | method: 'GET', 173 | hostname: server.address().address, 174 | port: server.address().port, 175 | path: '/', 176 | agent: new HttpsProxyAgent({ 177 | keepAlive: true, 178 | keepAliveMsecs: 1000, 179 | maxSockets: 256, 180 | maxFreeSockets: 256, 181 | scheduling: 'lifo', 182 | proxy: `https://hello:world@${proxy.address().address}:${proxy.address().port}` 183 | }) 184 | }) 185 | 186 | let body = '' 187 | response.setEncoding('utf8') 188 | for await (const chunk of response) { 189 | body += chunk 190 | } 191 | 192 | t.is(body, 'ok') 193 | t.is(response.statusCode, 200) 194 | 195 | server.close() 196 | proxy.close() 197 | }) 198 | 199 | test('Configure the agent to reuse sockets', async t => { 200 | const server = await createSecureServer() 201 | const proxy = await createSecureProxy() 202 | server.on('request', (req, res) => res.end('ok')) 203 | 204 | let count = 0 205 | proxy.on('connection', () => { 206 | count += 1 207 | t.is(count, 1) 208 | }) 209 | 210 | const agent = new HttpsProxyAgent({ 211 | keepAlive: true, 212 | keepAliveMsecs: 1000, 213 | maxSockets: 256, 214 | maxFreeSockets: 256, 215 | scheduling: 'lifo', 216 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 217 | }) 218 | 219 | let response = await request({ 220 | method: 'GET', 221 | hostname: server.address().address, 222 | port: server.address().port, 223 | path: '/', 224 | agent 225 | }) 226 | 227 | let body = '' 228 | response.setEncoding('utf8') 229 | for await (const chunk of response) { 230 | body += chunk 231 | } 232 | 233 | t.is(body, 'ok') 234 | t.is(response.statusCode, 200) 235 | 236 | response = await request({ 237 | method: 'GET', 238 | hostname: server.address().address, 239 | port: server.address().port, 240 | path: '/', 241 | agent 242 | }) 243 | 244 | body = '' 245 | response.setEncoding('utf8') 246 | for await (const chunk of response) { 247 | body += chunk 248 | } 249 | 250 | t.is(body, 'ok') 251 | t.is(response.statusCode, 200) 252 | 253 | server.close() 254 | proxy.close() 255 | }) 256 | 257 | test('Configure the agent to NOT reuse sockets', async t => { 258 | const server = await createSecureServer() 259 | const proxy = await createSecureProxy() 260 | server.on('request', (req, res) => res.end('ok')) 261 | 262 | const ports = [] 263 | proxy.on('connection', socket => { 264 | t.false(ports.includes(socket.remotePort)) 265 | ports.push(socket.remotePort) 266 | }) 267 | 268 | const agent = new HttpsProxyAgent({ 269 | keepAlive: false, 270 | keepAliveMsecs: 1000, 271 | maxSockets: Infinity, 272 | maxFreeSockets: 256, 273 | scheduling: 'lifo', 274 | proxy: `https://${proxy.address().address}:${proxy.address().port}` 275 | }) 276 | 277 | let response = await request({ 278 | method: 'GET', 279 | hostname: server.address().address, 280 | port: server.address().port, 281 | path: '/', 282 | agent 283 | }) 284 | 285 | let body = '' 286 | response.setEncoding('utf8') 287 | for await (const chunk of response) { 288 | body += chunk 289 | } 290 | 291 | t.is(body, 'ok') 292 | t.is(response.statusCode, 200) 293 | 294 | response = await request({ 295 | method: 'GET', 296 | hostname: server.address().address, 297 | port: server.address().port, 298 | path: '/', 299 | agent 300 | }) 301 | 302 | body = '' 303 | response.setEncoding('utf8') 304 | for await (const chunk of response) { 305 | body += chunk 306 | } 307 | 308 | t.is(body, 'ok') 309 | t.is(response.statusCode, 200) 310 | 311 | server.close() 312 | proxy.close() 313 | }) 314 | -------------------------------------------------------------------------------- /test/http-http.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const http = require('http') 4 | const test = require('ava') 5 | const { createServer, createProxy } = require('./utils') 6 | const { HttpProxyAgent } = require('../') 7 | 8 | function request (opts) { 9 | return new Promise((resolve, reject) => { 10 | const req = http.request(opts, resolve) 11 | req.on('error', reject) 12 | req.end(opts.body) 13 | }) 14 | } 15 | 16 | test('Basic', async t => { 17 | const server = await createServer() 18 | const proxy = await createProxy() 19 | server.on('request', (req, res) => res.end('ok')) 20 | 21 | const response = await request({ 22 | method: 'GET', 23 | hostname: server.address().address, 24 | port: server.address().port, 25 | path: '/', 26 | agent: new HttpProxyAgent({ 27 | keepAlive: true, 28 | keepAliveMsecs: 1000, 29 | maxSockets: 256, 30 | maxFreeSockets: 256, 31 | scheduling: 'lifo', 32 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 33 | }) 34 | }) 35 | 36 | let body = '' 37 | response.setEncoding('utf8') 38 | for await (const chunk of response) { 39 | body += chunk 40 | } 41 | 42 | t.is(body, 'ok') 43 | t.is(response.statusCode, 200) 44 | 45 | server.close() 46 | proxy.close() 47 | }) 48 | 49 | test('Connection header (keep-alive)', async t => { 50 | const server = await createServer() 51 | const proxy = await createProxy() 52 | server.on('request', (req, res) => res.end('ok')) 53 | 54 | proxy.authenticate = function (req, fn) { 55 | t.is(req.headers.connection, 'keep-alive') 56 | fn(null, true) 57 | } 58 | 59 | const response = await request({ 60 | method: 'GET', 61 | hostname: server.address().address, 62 | port: server.address().port, 63 | path: '/', 64 | agent: new HttpProxyAgent({ 65 | keepAlive: true, 66 | keepAliveMsecs: 1000, 67 | maxSockets: 256, 68 | maxFreeSockets: 256, 69 | scheduling: 'lifo', 70 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 71 | }) 72 | }) 73 | 74 | let body = '' 75 | response.setEncoding('utf8') 76 | for await (const chunk of response) { 77 | body += chunk 78 | } 79 | 80 | t.is(body, 'ok') 81 | t.is(response.statusCode, 200) 82 | 83 | server.close() 84 | proxy.close() 85 | }) 86 | 87 | test('Connection header (close)', async t => { 88 | const server = await createServer() 89 | const proxy = await createProxy() 90 | server.on('request', (req, res) => res.end('ok')) 91 | 92 | proxy.authenticate = function (req, fn) { 93 | t.is(req.headers.connection, 'close') 94 | fn(null, true) 95 | } 96 | 97 | const response = await request({ 98 | method: 'GET', 99 | hostname: server.address().address, 100 | port: server.address().port, 101 | path: '/', 102 | agent: new HttpProxyAgent({ 103 | keepAlive: false, 104 | keepAliveMsecs: 1000, 105 | maxSockets: Infinity, 106 | maxFreeSockets: 256, 107 | scheduling: 'lifo', 108 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 109 | }) 110 | }) 111 | 112 | let body = '' 113 | response.setEncoding('utf8') 114 | for await (const chunk of response) { 115 | body += chunk 116 | } 117 | 118 | t.is(body, 'ok') 119 | t.is(response.statusCode, 200) 120 | 121 | server.close() 122 | proxy.close() 123 | }) 124 | 125 | test('Proxy authentication (empty)', async t => { 126 | const server = await createServer() 127 | const proxy = await createProxy() 128 | server.on('request', (req, res) => res.end('ok')) 129 | 130 | proxy.authenticate = function (req, fn) { 131 | fn(null, req.headers['proxy-authorization'] === undefined) 132 | } 133 | 134 | const response = await request({ 135 | method: 'GET', 136 | hostname: server.address().address, 137 | port: server.address().port, 138 | path: '/', 139 | agent: new HttpProxyAgent({ 140 | keepAlive: true, 141 | keepAliveMsecs: 1000, 142 | maxSockets: 256, 143 | maxFreeSockets: 256, 144 | scheduling: 'lifo', 145 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 146 | }) 147 | }) 148 | 149 | let body = '' 150 | response.setEncoding('utf8') 151 | for await (const chunk of response) { 152 | body += chunk 153 | } 154 | 155 | t.is(body, 'ok') 156 | t.is(response.statusCode, 200) 157 | 158 | server.close() 159 | proxy.close() 160 | }) 161 | 162 | test('Proxy authentication', async t => { 163 | const server = await createServer() 164 | const proxy = await createProxy() 165 | server.on('request', (req, res) => res.end('ok')) 166 | 167 | proxy.authenticate = function (req, fn) { 168 | fn(null, req.headers['proxy-authorization'] === `Basic ${Buffer.from('hello:world').toString('base64')}`) 169 | } 170 | 171 | const response = await request({ 172 | method: 'GET', 173 | hostname: server.address().address, 174 | port: server.address().port, 175 | path: '/', 176 | agent: new HttpProxyAgent({ 177 | keepAlive: true, 178 | keepAliveMsecs: 1000, 179 | maxSockets: 256, 180 | maxFreeSockets: 256, 181 | scheduling: 'lifo', 182 | proxy: `http://hello:world@${proxy.address().address}:${proxy.address().port}` 183 | }) 184 | }) 185 | 186 | let body = '' 187 | response.setEncoding('utf8') 188 | for await (const chunk of response) { 189 | body += chunk 190 | } 191 | 192 | t.is(body, 'ok') 193 | t.is(response.statusCode, 200) 194 | 195 | server.close() 196 | proxy.close() 197 | }) 198 | 199 | test('Configure the agent to reuse sockets', async t => { 200 | const server = await createServer() 201 | const proxy = await createProxy() 202 | server.on('request', (req, res) => res.end('ok')) 203 | 204 | let count = 0 205 | proxy.on('connection', () => { 206 | count += 1 207 | t.is(count, 1) 208 | }) 209 | 210 | const agent = new HttpProxyAgent({ 211 | keepAlive: true, 212 | keepAliveMsecs: 1000, 213 | maxSockets: 256, 214 | maxFreeSockets: 256, 215 | scheduling: 'lifo', 216 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 217 | }) 218 | 219 | let response = await request({ 220 | method: 'GET', 221 | hostname: server.address().address, 222 | port: server.address().port, 223 | path: '/', 224 | agent 225 | }) 226 | 227 | let body = '' 228 | response.setEncoding('utf8') 229 | for await (const chunk of response) { 230 | body += chunk 231 | } 232 | 233 | t.is(body, 'ok') 234 | t.is(response.statusCode, 200) 235 | 236 | response = await request({ 237 | method: 'GET', 238 | hostname: server.address().address, 239 | port: server.address().port, 240 | path: '/', 241 | agent 242 | }) 243 | 244 | body = '' 245 | response.setEncoding('utf8') 246 | for await (const chunk of response) { 247 | body += chunk 248 | } 249 | 250 | t.is(body, 'ok') 251 | t.is(response.statusCode, 200) 252 | 253 | server.close() 254 | proxy.close() 255 | }) 256 | 257 | test('Configure the agent to NOT reuse sockets', async t => { 258 | const server = await createServer() 259 | const proxy = await createProxy() 260 | server.on('request', (req, res) => res.end('ok')) 261 | 262 | const ports = [] 263 | proxy.on('connection', socket => { 264 | t.false(ports.includes(socket.remotePort)) 265 | ports.push(socket.remotePort) 266 | }) 267 | 268 | const agent = new HttpProxyAgent({ 269 | keepAlive: false, 270 | keepAliveMsecs: 1000, 271 | maxSockets: Infinity, 272 | maxFreeSockets: 256, 273 | scheduling: 'lifo', 274 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 275 | }) 276 | 277 | let response = await request({ 278 | method: 'GET', 279 | hostname: server.address().address, 280 | port: server.address().port, 281 | path: '/', 282 | agent 283 | }) 284 | 285 | let body = '' 286 | response.setEncoding('utf8') 287 | for await (const chunk of response) { 288 | body += chunk 289 | } 290 | 291 | t.is(body, 'ok') 292 | t.is(response.statusCode, 200) 293 | 294 | response = await request({ 295 | method: 'GET', 296 | hostname: server.address().address, 297 | port: server.address().port, 298 | path: '/', 299 | agent 300 | }) 301 | 302 | body = '' 303 | response.setEncoding('utf8') 304 | for await (const chunk of response) { 305 | body += chunk 306 | } 307 | 308 | t.is(body, 'ok') 309 | t.is(response.statusCode, 200) 310 | 311 | server.close() 312 | proxy.close() 313 | }) 314 | 315 | test('Test Host Header', async t => { 316 | const server = await createServer() 317 | const proxy = await createProxy() 318 | server.on('request', (req, res) => res.end('ok')) 319 | 320 | proxy.authenticate = function (req, fn) { 321 | t.is(req.headers.host, `${server.address().address}:${server.address().port}`) 322 | fn(null, true) 323 | } 324 | 325 | const response = await request({ 326 | method: 'GET', 327 | hostname: server.address().address, 328 | port: server.address().port, 329 | path: '/', 330 | agent: new HttpProxyAgent({ 331 | keepAlive: true, 332 | keepAliveMsecs: 1000, 333 | maxSockets: 256, 334 | maxFreeSockets: 256, 335 | scheduling: 'lifo', 336 | proxy: `http://${proxy.address().address}:${proxy.address().port}` 337 | }) 338 | }) 339 | 340 | let body = '' 341 | response.setEncoding('utf8') 342 | for await (const chunk of response) { 343 | body += chunk 344 | } 345 | 346 | t.is(body, 'ok') 347 | t.is(response.statusCode, 200) 348 | 349 | server.close() 350 | proxy.close() 351 | }) 352 | --------------------------------------------------------------------------------