Repository: node-modules/agentkeepalive Branch: master Commit: 422e9fc35aaa Files: 41 Total size: 176.7 KB Directory structure: gitextract_ylvm8n_x/ ├── .eslintignore ├── .eslintrc ├── .github/ │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ └── nodejs.yml ├── .gitignore ├── History.md ├── LICENSE ├── README.md ├── benchmark/ │ ├── echo.js │ ├── proxy.js │ ├── readme.md │ ├── siegerc │ ├── sleep_server.js │ └── start.sh ├── browser.js ├── example/ │ ├── http_agent.js │ ├── https_agent.js │ └── server.js ├── index.d.ts ├── index.js ├── lib/ │ ├── agent.js │ ├── constants.js │ └── https_agent.js ├── package.json └── test/ ├── fixtures/ │ ├── agenttest-cert.pem │ ├── agenttest-key.pem │ ├── ca.cnf │ ├── ca.key │ ├── ca.pem │ ├── genkey.sh │ ├── server.cnf │ └── ts/ │ ├── index.ts │ └── tsconfig.json ├── http_agent.test.js ├── https_agent.test.js ├── server_timeout.test.js ├── test-ECONNRESET.test.js ├── test-http-agent-maxsockets-regress-4050.test.js ├── test-https-agent-session-eviction.test.js ├── test-https-ipv6.test.js └── ts.test.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintignore ================================================ test/fixtures/ts ================================================ FILE: .eslintrc ================================================ { "extends": "eslint-config-egg" } ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ##### Checklist - [ ] `npm test` passes - [ ] tests and/or benchmarks are included - [ ] documentation is changed or added - [ ] commit message follows commit guidelines ##### Affected core subsystem(s) ##### Description of change ================================================ FILE: .github/workflows/nodejs.yml ================================================ # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions name: Node.js CI on: push: branches: - main - master pull_request: branches: - main - master schedule: - cron: '0 2 * * *' jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: node-version: [8, 10, 12, 14, 16] os: [ubuntu-latest] steps: - name: Checkout Git Source uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: Install Dependencies run: npm i - name: Continuous Integration run: npm run ci - name: Code Coverage uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} ================================================ FILE: .gitignore ================================================ lib-cov coverage.html *.seed *.log *.csv *.dat *.out *.pid *.gz pids logs results node_modules npm-debug.log coverage/ .nyc_output/ test/fixtures/ts/*.js package-lock.json ================================================ FILE: History.md ================================================ 4.6.0 / 2024-12-29 ================== **features** * [[`7d79ddd`](http://github.com/node-modules/agentkeepalive/commit/7d79ddd2e4a5685013b4a8fee810a823028d517a)] - feat: export `HttpAgent` as standalone class to avoid namespace clutter and support ESM style imports (#119) (Radoslav Kirilov <>) 4.5.0 / 2023-08-06 ================== **others** * [[`1e5e312`](http://github.com/node-modules/agentkeepalive/commit/1e5e312f36491243372dbfee0dd47607e7b3d94a)] - deps: remove debug and depd (#114) (fengmk2 <>) 4.4.0 / 2023-08-05 ================== **features** * [[`c7c1e93`](http://github.com/node-modules/agentkeepalive/commit/c7c1e93beba7310d7c2cc9647dd211a686d21cac)] - feat: return socket from createConnection (#113) (Nabeel Bukhari <>) 4.3.0 / 2023-03-06 ================== **others** * [[`6f9852b`](http://github.com/node-modules/agentkeepalive/commit/6f9852bf6f674846103e403fd9c84e92fc24f820)] - deps: depd@2.0.0 (#109) (Brian DeHamer <>) * [[`fd4bd9b`](http://github.com/node-modules/agentkeepalive/commit/fd4bd9b0e0f051de3cb49559d1b0d534a0ded18c)] - test: use npm install (#110) (fengmk2 <>) * [[`d52822c`](http://github.com/node-modules/agentkeepalive/commit/d52822c1243c689df1c8232a3bb14139cf87fae5)] - chore: update contributors (fengmk2 <>) 4.2.1 / 2022-02-21 ================== **fixes** * [[`8b13b5c`](http://github.com/node-modules/agentkeepalive/commit/8b13b5ca797f4779a0a8d393ad8ecb622cd27987)] - fix: explicitly set `| undefined` in type definitions (#99) (Benoit Lemoine <>) 4.2.0 / 2021-12-31 ================== **fixes** * [[`f418c67`](http://github.com/node-modules/agentkeepalive/commit/f418c67a63c061c7261592d4553bc455e0b0d306)] - fix: change `freeSocketTimeout` default value to 4000 (#102) (fengmk2 <>) **others** * [[`bc2a1ce`](http://github.com/node-modules/agentkeepalive/commit/bc2a1cea0884b4d18b0d244bf00006d9107963df)] - doc(readme): making `timeout`'s default clear (#100) (Aaron <>) 4.1.4 / 2021-02-05 ================== **fixes** * [[`4d04794`](http://github.com/node-modules/agentkeepalive/commit/4d047946b1547b4edff92ea40205aee4f0c8aa46)] - fix(types): correct `Https` constructor argument (#89) (Simen Bekkhus <>) 4.1.3 / 2020-06-15 ================== **fixes** * [[`4ba9f9c`](http://github.com/node-modules/agentkeepalive/commit/4ba9f9c844f2a6b8037ce56599d25c69ef054d91)] - fix: compatible with node v12.16.3 (#91) (killa <>) 4.1.2 / 2020-04-25 ================== **fixes** * [[`de66b02`](http://github.com/node-modules/agentkeepalive/commit/de66b0206d064a97129c2c31bcdabd4d64557b91)] - fix: detect http request timeout handler (#88) (fengmk2 <>) 4.1.1 / 2020-04-25 ================== **fixes** * [[`bbd20c0`](http://github.com/node-modules/agentkeepalive/commit/bbd20c03b8cf7dfb00b3aad1ada26d4ab90d2d6e)] - fix: definition error (#87) (吖猩 <>) **others** * [[`3b01699`](http://github.com/node-modules/agentkeepalive/commit/3b01699b8e90022d5f56898dd709e4fe7ee7cdaa)] - test: run test on node 12 (#84) (Igor Savin <>) 4.1.0 / 2019-10-12 ================== **features** * [[`fe33b80`](http://github.com/node-modules/agentkeepalive/commit/fe33b800acc09109388bfe65107550952b6fc7b0)] - feat: Add `reusedSocket` property on client request (#82) (Weijia Wang <>) **others** * [[`77ba744`](http://github.com/node-modules/agentkeepalive/commit/77ba744667bb6b9e5986a53e5222f62094db12b9)] - docs: fix grammar in readme (#81) (Herrington Darkholme <<2883231+HerringtonDarkholme@users.noreply.github.com>>) 4.0.2 / 2019-02-19 ================== **fixes** * [[`56d4a9b`](http://github.com/node-modules/agentkeepalive/commit/56d4a9b2a4499ea28943ddb590358d7831a02cb1)] - fix: HttpAgent export = internal (#74) (Andrew Leedham <>) 4.0.1 / 2019-02-19 ================== **fixes** * [[`bad1ac0`](http://github.com/node-modules/agentkeepalive/commit/bad1ac0e710fbc486717e14e68c59266d35df6a8)] - fix: HttpsAgent Type Definition (#71) (#72) (Andrew Leedham <>) * [[`f48a4a7`](http://github.com/node-modules/agentkeepalive/commit/f48a4a701ea6fbe43781c91e1c0aaad6e328ac7f)] - fix: export interface (#70) (Vinay <>) **others** * [[`9124343`](http://github.com/node-modules/agentkeepalive/commit/91243437cfdd324cb97f39dee76746d5e5f4cd72)] - chore: add agent.options.keepAlive instead agent.keepAlive (fengmk2 <>) * [[`d177d40`](http://github.com/node-modules/agentkeepalive/commit/d177d40422fe7296990b4e270cf498e3f33c18fa)] - test: add request timeout bigger than agent timeout cases (fengmk2 <>) 4.0.0 / 2018-10-23 ================== **features** * [[`5c9f3bb`](http://github.com/node-modules/agentkeepalive/commit/5c9f3bbd60555744edcf777105b148982a1a42b6)] - feat: impl the new Agent extend http.Agent (fengmk2 <>) **others** * [[`498c8f1`](http://github.com/node-modules/agentkeepalive/commit/498c8f13cf76600d3dd6e1c91cdf2d8292355dff)] - chore: move LICENSE from readme to file (fengmk2 <>) * [[`4f39894`](http://github.com/node-modules/agentkeepalive/commit/4f398942ba2f90cf4501239e56ac4e6344931a01)] - bugfix: support agent.options.timeout on https agent (fengmk2 <>) 3.5.2 / 2018-10-19 ================== **fixes** * [[`5751fc1`](http://github.com/node-modules/agentkeepalive/commit/5751fc1180ed6544602c681ffbd08ca66a0cb12c)] - fix: sockLen being miscalculated when removing sockets (#60) (Ehden Sinai <>) 3.5.1 / 2018-07-31 ================== **fixes** * [[`495f1ab`](http://github.com/node-modules/agentkeepalive/commit/495f1ab625d43945d72f68096b97db723d4f0657)] - fix: add the lost npm files (#66) (Henry Zhuang <>) 3.5.0 / 2018-07-31 ================== **features** * [[`16f5aea`](http://github.com/node-modules/agentkeepalive/commit/16f5aeadfda57f1c602652f1472a63cc83cd05bf)] - feat: add typing define. (#65) (Henry Zhuang <>) **others** * [[`28fa062`](http://github.com/node-modules/agentkeepalive/commit/28fa06246fb5103f88ebeeb8563757a9078b8157)] - docs: add "per host" to description of maxFreeSockets (tony-gutierrez <>) * [[`7df2577`](http://github.com/node-modules/agentkeepalive/commit/7df25774f00a1031ca4daad2878a17e0539072a2)] - test: run test on node 10 (#63) (fengmk2 <>) 3.4.1 / 2018-03-08 ================== **fixes** * [[`4d3a3b1`](http://github.com/node-modules/agentkeepalive/commit/4d3a3b1f7b16595febbbd39eeed72b2663549014)] - fix: Handle ipv6 addresses in host-header correctly with TLS (#53) (Mattias Holmlund <>) **others** * [[`55a7a5c`](http://github.com/node-modules/agentkeepalive/commit/55a7a5cd33e97f9a8370083dcb041c5552f10ac9)] - test: stop timer after test end (fengmk2 <>) 3.4.0 / 2018-02-27 ================== **features** * [[`bc7cadb`](http://github.com/node-modules/agentkeepalive/commit/bc7cadb30ecd2071e2b341ac53ae1a2b8155c43d)] - feat: use socket custom freeSocketKeepAliveTimeout first (#59) (fengmk2 <>) **others** * [[`138eda8`](http://github.com/node-modules/agentkeepalive/commit/138eda81e10b632aaa87bea0cb66d8667124c4e8)] - doc: fix `keepAliveMsecs` params description (#55) (Hongcai Deng <>) 3.3.0 / 2017-06-20 ================== * feat: add statusChanged getter (#51) * chore: format License 3.2.0 / 2017-06-10 ================== * feat: add expiring active sockets * test: add node 8 (#49) 3.1.0 / 2017-02-20 ================== * feat: timeout support humanize ms (#48) 3.0.0 / 2016-12-20 ================== * fix: emit agent socket close event * test: add remove excess calls to removeSocket * test: use egg-ci * test: refactor test with eslint rules * feat: merge _http_agent.js from 7.2.1 2.2.0 / 2016-06-26 ================== * feat: Add browser shim (noop) for isomorphic use. (#39) * chore: add security check badge 2.1.1 / 2016-04-06 ================== * https: fix ssl socket leak when keepalive is used * chore: remove circle ci image 2.1.0 / 2016-04-02 ================== * fix: opened sockets number overflow maxSockets 2.0.5 / 2016-03-16 ================== * fix: pick _evictSession to httpsAgent 2.0.4 / 2016-03-13 ================== * test: add Circle ci * test: add appveyor ci build * refactor: make sure only one error listener * chore: use codecov * fix: handle idle socket error * test: run on more node versions 2.0.3 / 2015-08-03 ================== * fix: add default error handler to avoid Unhandled error event throw 2.0.2 / 2015-04-25 ================== * fix: remove socket from freeSockets on 'timeout' (@pmalouin) 2.0.1 / 2015-04-19 ================== * fix: add timeoutSocketCount to getCurrentStatus() * feat(getCurrentStatus): add getCurrentStatus 2.0.0 / 2015-04-01 ================== * fix: socket.destroyed always be undefined on 0.10.x * Make it compatible with node v0.10.x (@lattmann) 1.2.1 / 2015-03-23 ================== * patch from iojs: don't overwrite servername option * patch commits from joyent/node * add max sockets test case * add nagle algorithm delayed link 1.2.0 / 2014-09-02 ================== * allow set keepAliveTimeout = 0 * support timeout on working socket. fixed #6 1.1.0 / 2014-08-28 ================== * add some socket counter for deep monitor 1.0.0 / 2014-08-13 ================== * update _http_agent, only support 0.11+, only support node 0.11.0+ 0.2.2 / 2013-11-19 ================== * support node 0.8 and node 0.10 0.2.1 / 2013-11-08 ================== * fix socket does not timeout bug, it will hang on life, must use 0.2.x on node 0.11 0.2.0 / 2013-11-06 ================== * use keepalive agent on node 0.11+ impl 0.1.5 / 2013-06-24 ================== * support coveralls * add node 0.10 test * add 0.8.22 original https.js * add original http.js module to diff * update jscover * mv pem to fixtures * add https agent usage ================================================ FILE: LICENSE ================================================ The MIT License Copyright(c) node-modules and other contributors. Copyright(c) 2012 - 2015 fengmk2 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. ================================================ FILE: README.md ================================================ # agentkeepalive [![NPM version][npm-image]][npm-url] [![Known Vulnerabilities][snyk-image]][snyk-url] [![Node.js CI](https://github.com/node-modules/agentkeepalive/actions/workflows/nodejs.yml/badge.svg)](https://github.com/node-modules/agentkeepalive/actions/workflows/nodejs.yml) [![npm download][download-image]][download-url] [npm-image]: https://img.shields.io/npm/v/agentkeepalive.svg?style=flat [npm-url]: https://npmjs.org/package/agentkeepalive [snyk-image]: https://snyk.io/test/npm/agentkeepalive/badge.svg?style=flat-square [snyk-url]: https://snyk.io/test/npm/agentkeepalive [download-image]: https://img.shields.io/npm/dm/agentkeepalive.svg?style=flat-square [download-url]: https://npmjs.org/package/agentkeepalive The enhancement features `keep alive` `http.Agent`. Support `http` and `https`. ## What's different from original `http.Agent`? - `keepAlive=true` by default - Disable Nagle's algorithm: `socket.setNoDelay(true)` - Add free socket timeout: avoid long time inactivity socket leak in the free-sockets queue. - Add active socket timeout: avoid long time inactivity socket leak in the active-sockets queue. - TTL for active socket. ## Node.js version required Support Node.js >= `8.0.0` ## Install ```bash $ npm install agentkeepalive --save ``` ## new Agent([options]) * `options` {Object} Set of configurable options to set on the agent. Can have the following fields: * `keepAlive` {Boolean} Keep sockets around in a pool to be used by other requests in the future. Default = `true`. * `keepAliveMsecs` {Number} When using the keepAlive option, specifies the initial delay for TCP Keep-Alive packets. Ignored when the keepAlive option is false or undefined. Defaults to 1000. Default = `1000`. Only relevant if `keepAlive` is set to `true`. * `freeSocketTimeout`: {Number} Sets the free socket to timeout after `freeSocketTimeout` milliseconds of inactivity on the free socket. The default [server-side timeout](https://nodejs.org/api/http.html#serverkeepalivetimeout) is 5000 milliseconds, to [avoid ECONNRESET exceptions](https://medium.com/ssense-tech/reduce-networking-errors-in-nodejs-23b4eb9f2d83), we set the default value to `4000` milliseconds. Only relevant if `keepAlive` is set to `true`. * `timeout`: {Number} Sets the working socket to timeout after `timeout` milliseconds of inactivity on the working socket. Default is `freeSocketTimeout * 2` so long as that value is greater than or equal to 8 seconds, otherwise the default is 8 seconds. * `maxSockets` {Number} Maximum number of sockets to allow per host. Default = `Infinity`. * `maxFreeSockets` {Number} Maximum number of sockets (per host) to leave open in a free state. Only relevant if `keepAlive` is set to `true`. Default = `256`. * `socketActiveTTL` {Number} Sets the socket active time to live, even if it's in use. If not set, the behaviour keeps the same (the socket will be released only when free) Default = `null`. ## Usage ```js const http = require('http'); const HttpAgent = require('agentkeepalive').HttpAgent; const keepaliveAgent = new HttpAgent({ maxSockets: 100, maxFreeSockets: 10, timeout: 60000, // active socket keepalive for 60 seconds freeSocketTimeout: 30000, // free socket keepalive for 30 seconds }); const options = { host: 'cnodejs.org', port: 80, path: '/', method: 'GET', agent: keepaliveAgent, }; const req = http.request(options, res => { console.log('STATUS: ' + res.statusCode); console.log('HEADERS: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', function (chunk) { console.log('BODY: ' + chunk); }); }); req.on('error', e => { console.log('problem with request: ' + e.message); }); req.end(); setTimeout(() => { if (keepaliveAgent.statusChanged) { console.log('[%s] agent status changed: %j', Date(), keepaliveAgent.getCurrentStatus()); } }, 2000); ``` ### `getter agent.statusChanged` counters have change or not after last checkpoint. ### `agent.getCurrentStatus()` `agent.getCurrentStatus()` will return a object to show the status of this agent: ```js { createSocketCount: 10, closeSocketCount: 5, timeoutSocketCount: 0, requestCount: 5, freeSockets: { 'localhost:57479:': 3 }, sockets: { 'localhost:57479:': 5 }, requests: {} } ``` ### Support `https` ```js const https = require('https'); const HttpsAgent = require('agentkeepalive').HttpsAgent; const keepaliveAgent = new HttpsAgent(); // https://www.google.com/search?q=nodejs&sugexp=chrome,mod=12&sourceid=chrome&ie=UTF-8 const options = { host: 'www.google.com', port: 443, path: '/search?q=nodejs&sugexp=chrome,mod=12&sourceid=chrome&ie=UTF-8', method: 'GET', agent: keepaliveAgent, }; const req = https.request(options, res => { console.log('STATUS: ' + res.statusCode); console.log('HEADERS: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', chunk => { console.log('BODY: ' + chunk); }); }); req.on('error', e => { console.log('problem with request: ' + e.message); }); req.end(); setTimeout(() => { console.log('agent status: %j', keepaliveAgent.getCurrentStatus()); }, 2000); ``` ### Support `req.reusedSocket` This agent implements the `req.reusedSocket` to determine whether a request is send through a reused socket. When server closes connection at unfortunate time ([keep-alive race](https://code-examples.net/en/q/28a8069)), the http client will throw a `ECONNRESET` error. Under this circumstance, `req.reusedSocket` is useful when we want to retry the request automatically. ```js const http = require('http'); const HttpAgent = require('agentkeepalive').HttpAgent; const agent = new HttpAgent(); const req = http .get('http://localhost:3000', { agent }, (res) => { // ... }) .on('error', (err) => { if (req.reusedSocket && err.code === 'ECONNRESET') { // retry the request or anything else... } }) ``` This behavior is consistent with Node.js core. But through `agentkeepalive`, you can use this feature in older Node.js version. ## [Benchmark](https://github.com/node-modules/agentkeepalive/tree/master/benchmark) run the benchmark: ```bash cd benchmark sh start.sh ``` Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz node@v0.8.9 50 maxSockets, 60 concurrent, 1000 requests per concurrent, 5ms delay Keep alive agent (30 seconds): ```js Transactions: 60000 hits Availability: 100.00 % Elapsed time: 29.70 secs Data transferred: 14.88 MB Response time: 0.03 secs Transaction rate: 2020.20 trans/sec Throughput: 0.50 MB/sec Concurrency: 59.84 Successful transactions: 60000 Failed transactions: 0 Longest transaction: 0.15 Shortest transaction: 0.01 ``` Normal agent: ```js Transactions: 60000 hits Availability: 100.00 % Elapsed time: 46.53 secs Data transferred: 14.88 MB Response time: 0.05 secs Transaction rate: 1289.49 trans/sec Throughput: 0.32 MB/sec Concurrency: 59.81 Successful transactions: 60000 Failed transactions: 0 Longest transaction: 0.45 Shortest transaction: 0.00 ``` Socket created: ```bash [proxy.js:120000] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:120000] normal , 53866 created, 84260 requestFinished, 1.56 req/socket, 0 requests, 0 sockets {" <10ms":75," <15ms":1112," <20ms":10947," <30ms":32130," <40ms":8228," <50ms":3002," <100ms":4274," <150ms":181," <200ms":18," >=200ms+":33} ``` ## License [MIT](LICENSE) ## Contributors |[
fengmk2](https://github.com/fengmk2)
|[
dead-horse](https://github.com/dead-horse)
|[
AndrewLeedham](https://github.com/AndrewLeedham)
|[
ngot](https://github.com/ngot)
|[
wrynearson](https://github.com/wrynearson)
|[
aaronArinder](https://github.com/aaronArinder)
| | :---: | :---: | :---: | :---: | :---: | :---: | |[
alexpenev-s](https://github.com/alexpenev-s)
|[
blemoine](https://github.com/blemoine)
|[
bdehamer](https://github.com/bdehamer)
|[
DylanPiercey](https://github.com/DylanPiercey)
|[
cixel](https://github.com/cixel)
|[
HerringtonDarkholme](https://github.com/HerringtonDarkholme)
| |[
denghongcai](https://github.com/denghongcai)
|[
kibertoad](https://github.com/kibertoad)
|[
pangorgo](https://github.com/pangorgo)
|[
mattiash](https://github.com/mattiash)
|[
nabeelbukhari](https://github.com/nabeelbukhari)
|[
pmalouin](https://github.com/pmalouin)
| [
SimenB](https://github.com/SimenB)
|[
vinaybedre](https://github.com/vinaybedre)
|[
starkwang](https://github.com/starkwang)
|[
killagu](https://github.com/killagu)
|[
tony-gutierrez](https://github.com/tony-gutierrez)
|[
whxaxes](https://github.com/whxaxes)
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Sat Aug 05 2023 02:36:31 GMT+0800`. ================================================ FILE: benchmark/echo.js ================================================ 'use strict'; const HttpAgent = require('../').HttpAgent; const agent = new HttpAgent({ keepAlive: true, maxSockets: 2, maxFreeSockets: 2, keepAliveMsecs: 30000, }); const options = { host: '10.125.196.152', port: 1984, path: '/1', method: 'GET', }; function get() { agent.get(options, res => { let size = 0; res.on('data', c => { size += c.length; }).on('end', () => { console.log('got %d bytes', size); }); }).on('error', err => { console.log('got error: %s', err); }); } setInterval(() => { get(); get(); }, 1000); get(); function showAgentDetail() { const peddingRequests = {}; for (const k in agent.requests) { const reqs = agent.requests[k]; peddingRequests[k] = reqs && reqs.length || 0; } const totalSockets = {}; for (const k in agent.sockets) { const socks = agent.sockets[k]; totalSockets[k] = socks && socks.length || 0; } const freeSockets = {}; for (const k in agent.freeSockets) { const socks = agent.freeSockets[k]; freeSockets[k] = socks && socks.length || 0; } const requestPerSocket = agent.createSocketCount && agent.requestFinishedCount / agent.createSocketCount; console.log('[%s] [worker:%d] HttpAgent(%s,%sms,%s,%s): requests: %d, created: %d, timeout: %d, reqs/socket: %s, pedding requests: %j, alive sockets: %j, free sockets: %j', Date(), process.pid, agent.keepAlive && agent.options.keepAlive, agent.keepAliveMsecs, agent.maxSockets, agent.maxFreeSockets, agent.requestFinishedCount, agent.createSocketCount, agent.timeoutSocketCount, requestPerSocket.toFixed(0), peddingRequests, totalSockets, freeSockets ); } setInterval(showAgentDetail, 3000); ================================================ FILE: benchmark/proxy.js ================================================ 'use strict'; var http = require('http'); var HttpAgent = require('../').HttpAgent; var maxSockets = parseInt(process.argv[2]) || 10; var maxFreeSockets = parseInt(process.argv[3]) || maxSockets; var SERVER = process.argv[4] || '127.0.0.1'; var agentKeepalive = new HttpAgent({ keepAlive: true, maxSockets: maxSockets, maxFreeSockets: maxFreeSockets, keepAliveTimeout: 30000, }); var agentHttp = new HttpAgent({ maxSockets: maxSockets, keepAlive: false, }); var count = 0; var rtKeepalives = { ' <10ms': 0, ' <15ms': 0, ' <20ms': 0, ' <30ms': 0, ' <40ms': 0, ' <50ms': 0, ' <100ms': 0, ' <150ms': 0, ' <200ms': 0, ' >=200ms+': 0 }; var rtNormals = { ' <10ms': 0, ' <15ms': 0, ' <20ms': 0, ' <30ms': 0, ' <40ms': 0, ' <50ms': 0, ' <100ms': 0, ' <150ms': 0, ' <200ms': 0, ' >=200ms+': 0 }; setInterval(function () { console.log('\n\n----------------------------------------------------------------'); console.log('[proxy.js:%d] keepalive, %d created, %d requests, %d req/socket, %d close, %d timeout\n%j', count, agentKeepalive.createSocketCount, agentKeepalive.requestCount, (agentKeepalive.requestCount / agentKeepalive.createSocketCount || 0).toFixed(2), agentKeepalive.closeSocketCount, agentKeepalive.timeoutSocketCount, rtKeepalives ); for (var name in agentKeepalive.sockets) { console.log('sockets %s: %d', name, agentKeepalive.sockets[name].length); } for (var name in agentKeepalive.freeSockets) { console.log('freeSockets %s: %d', name, agentKeepalive.freeSockets[name].length || 0); } for (var name in agentKeepalive.requests) { console.log('requests %s: %d', name, agentKeepalive.requests[name].length || 0); } console.log('----------------------------------------------------------------'); console.log('[proxy.js:%d] normal , %d created, %d requests, %d req/socket, %d close\n%j', count, agentHttp.createSocketCount, agentHttp.requestCount, (agentHttp.requestCount / agentHttp.createSocketCount || 0).toFixed(2), agentHttp.closeSocketCount, rtNormals ); for (var name in agentHttp.sockets) { console.log('sockets %s: %d', name, agentHttp.sockets[name].length); } for (var name in agentHttp.freeSockets) { console.log('freeSockets %s: %d', name, agentHttp.freeSockets[name].length || 0); } for (var name in agentHttp.requests) { console.log('requests %s: %d', name, agentHttp.requests[name].length || 0); } }, 2000); http.createServer(function (req, res) { var path = req.url; var agent = agentHttp; var method = 'GET'; var postData = null; var rts = rtNormals; if (path.indexOf('/post') === 0) { path = path.substring(5); method = 'POST'; postData = '{"sql":"select id from keepalive where age between 1 and 1000 and count between 1 and 100 order by id desc limit 2","data":null}'; } if (path.indexOf('/k') === 0) { path = path.substring(2); agent = agentKeepalive; rts = rtKeepalives; } var options = { host: SERVER, port: 1984, path: path, method: method, agent: agent }; req.on('data', function () {}); req.on('end', function () { var timer = null; var start = Date.now(); var client = http.request(options, function (response) { response.pipe(res); response.once('end', function () { var use = Date.now() - start; if (use < 10) { rts[' <10ms']++; } else if (use < 15) { rts[' <15ms']++; } else if (use < 20) { rts[' <20ms']++; } else if (use < 30) { rts[' <30ms']++; } else if (use < 40) { rts[' <40ms']++; } else if (use < 50) { rts[' <50ms']++; } else if (use < 100) { rts[' <100ms']++; } else if (use < 150) { rts[' <150ms']++; } else if (use < 200) { rts[' <200ms']++; } else { rts[' >=200ms+']++; } if (timer) { clearTimeout(timer); timer = null; } count++; }); }); client.on('error', function (err) { // console.log('error ' + req.url + ':' + err.message); res.statusCode = 500; res.end(err.message); }); timer = setTimeout(function () { // console.log('2000ms timeout ' + req.url); timer = null; client.abort(); }, 2000); client.end(postData); }); }).listen(1985); console.log('proxy start, listen on 1985'); ================================================ FILE: benchmark/readme.md ================================================ # Benchmark result Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz node@v0.8.9 50 maxSockets, 60 concurrent, 1000 requests per concurrent, 5ms delay Keep alive agent (30 seconds): ```js Transactions: 60000 hits Availability: 100.00 % Elapsed time: 29.70 secs Data transferred: 14.88 MB Response time: 0.03 secs Transaction rate: 2020.20 trans/sec Throughput: 0.50 MB/sec Concurrency: 59.84 Successful transactions: 60000 Failed transactions: 0 Longest transaction: 0.15 Shortest transaction: 0.01 ``` Normal agent: ```js Transactions: 60000 hits Availability: 100.00 % Elapsed time: 46.53 secs Data transferred: 14.88 MB Response time: 0.05 secs Transaction rate: 1289.49 trans/sec Throughput: 0.32 MB/sec Concurrency: 59.81 Successful transactions: 60000 Failed transactions: 0 Longest transaction: 0.45 Shortest transaction: 0.00 ``` Socket created: ``` [proxy.js:120000] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:120000] normal , 53866 created, 84260 requestFinished, 1.56 req/socket, 0 requests, 0 sockets {" <10ms":75," <15ms":1112," <20ms":10947," <30ms":32130," <40ms":8228," <50ms":3002," <100ms":4274," <150ms":181," <200ms":18," >=200ms+":33} ``` ```bash $ sh start.sh net.inet.ip.portrange.first: 12000 -> 12000 net.inet.tcp.msl: 1000 -> 1000 kern.maxfiles: 1000000 -> 1000000 kern.maxfilesperproc: 1000000 -> 1000000 Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz proxy start, listen on 1985 sleep server start, listen on 1984 v0.8.9 50 maxSockets, 60 concurrent, 1000 requests per concurrent, 5ms delay keep alive siege -c 60 -r 1000 -b http://localhost:1985/post/k/5 ** SIEGE 2.72 ** Preparing 60 concurrent users for battle. The server is now under siege...---------------------------------------------------------------- [proxy.js:1600] keepalive, 50 created, 1600 requestFinished, 32 req/socket, 0 requests, 50 sockets, 8 unusedSockets, 0 timeout {" <10ms":4," <15ms":334," <20ms":477," <30ms":502," <40ms":121," <50ms":35," <100ms":127," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:1600] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:5629] keepalive, 50 created, 5629 requestFinished, 112.58 req/socket, 0 requests, 50 sockets, 9 unusedSockets, 0 timeout {" <10ms":46," <15ms":1333," <20ms":1875," <30ms":1790," <40ms":365," <50ms":73," <100ms":147," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:5629] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:9926] keepalive, 50 created, 9926 requestFinished, 198.52 req/socket, 0 requests, 50 sockets, 11 unusedSockets, 0 timeout {" <10ms":98," <15ms":2718," <20ms":3311," <30ms":3046," <40ms":489," <50ms":96," <100ms":168," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:9926] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:14042] keepalive, 50 created, 14042 requestFinished, 280.84 req/socket, 0 requests, 50 sockets, 1 unusedSockets, 0 timeout {" <10ms":136," <15ms":3919," <20ms":4787," <30ms":4222," <40ms":652," <50ms":145," <100ms":181," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:14042] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:18233] keepalive, 50 created, 18233 requestFinished, 364.66 req/socket, 0 requests, 50 sockets, 17 unusedSockets, 0 timeout {" <10ms":173," <15ms":5239," <20ms":6318," <30ms":5401," <40ms":735," <50ms":171," <100ms":196," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:18233] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:22350] keepalive, 50 created, 22350 requestFinished, 447 req/socket, 0 requests, 50 sockets, 6 unusedSockets, 0 timeout {" <10ms":203," <15ms":6482," <20ms":7665," <30ms":6716," <40ms":859," <50ms":206," <100ms":219," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:22350] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:26373] keepalive, 50 created, 26373 requestFinished, 527.46 req/socket, 0 requests, 50 sockets, 11 unusedSockets, 0 timeout {" <10ms":221," <15ms":7790," <20ms":9185," <30ms":7631," <40ms":1042," <50ms":249," <100ms":255," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:26373] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:30501] keepalive, 50 created, 30501 requestFinished, 610.02 req/socket, 0 requests, 50 sockets, 15 unusedSockets, 0 timeout {" <10ms":248," <15ms":9095," <20ms":10715," <30ms":8715," <40ms":1204," <50ms":256," <100ms":268," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:30501] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:33819] keepalive, 50 created, 33819 requestFinished, 676.38 req/socket, 0 requests, 50 sockets, 13 unusedSockets, 0 timeout {" <10ms":303," <15ms":9768," <20ms":11677," <30ms":9749," <40ms":1571," <50ms":368," <100ms":327," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:33819] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:38097] keepalive, 50 created, 38097 requestFinished, 761.94 req/socket, 0 requests, 50 sockets, 5 unusedSockets, 0 timeout {" <10ms":355," <15ms":11127," <20ms":13253," <30ms":10940," <40ms":1653," <50ms":386," <100ms":327," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:38097] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:42325] keepalive, 50 created, 42325 requestFinished, 846.5 req/socket, 0 requests, 50 sockets, 10 unusedSockets, 0 timeout {" <10ms":399," <15ms":12507," <20ms":14682," <30ms":12236," <40ms":1720," <50ms":398," <100ms":327," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:42325] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:46568] keepalive, 50 created, 46568 requestFinished, 931.36 req/socket, 0 requests, 50 sockets, 3 unusedSockets, 0 timeout {" <10ms":443," <15ms":13947," <20ms":16180," <30ms":13421," <40ms":1780," <50ms":414," <100ms":327," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:46568] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:50666] keepalive, 50 created, 50666 requestFinished, 1013.32 req/socket, 0 requests, 50 sockets, 3 unusedSockets, 0 timeout {" <10ms":495," <15ms":15152," <20ms":17470," <30ms":14774," <40ms":1924," <50ms":463," <100ms":332," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:50666] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:54801] keepalive, 50 created, 54801 requestFinished, 1096.02 req/socket, 0 requests, 50 sockets, 14 unusedSockets, 0 timeout {" <10ms":540," <15ms":16484," <20ms":18745," <30ms":16066," <40ms":2059," <50ms":499," <100ms":352," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:54801] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:58861] keepalive, 50 created, 58861 requestFinished, 1177.22 req/socket, 0 requests, 50 sockets, 14 unusedSockets, 0 timeout {" <10ms":574," <15ms":17515," <20ms":20286," <30ms":17305," <40ms":2217," <50ms":550," <100ms":358," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:58861] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} done. Transactions: 60000 hits Availability: 100.00 % Elapsed time: 29.70 secs Data transferred: 14.88 MB Response time: 0.03 secs Transaction rate: 2020.20 trans/sec Throughput: 0.50 MB/sec Concurrency: 59.84 Successful transactions: 60000 Failed transactions: 0 Longest transaction: 0.15 Shortest transaction: 0.01 ---------------------------------------------------------------- [proxy.js:60000] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:60000] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:60000] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:60000] normal , 0 created, 0 requestFinished, 0 req/socket, 0 requests, 0 sockets {" <10ms":0," <15ms":0," <20ms":0," <30ms":0," <40ms":0," <50ms":0," <100ms":0," <150ms":0," <200ms":0," >=200ms+":0} normal siege -c 60 -r 1000 -b http://localhost:1985/post/5 ** SIEGE 2.72 ** Preparing 60 concurrent users for battle. The server is now under siege...---------------------------------------------------------------- [proxy.js:60237] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:60237] normal , 208 created, 286 requestFinished, 1.38 req/socket, 0 requests, 10 sockets {" <10ms":1," <15ms":0," <20ms":0," <30ms":23," <40ms":50," <50ms":64," <100ms":79," <150ms":20," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:62788] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:62788] normal , 2578 created, 3899 requestFinished, 1.51 req/socket, 0 requests, 45 sockets {" <10ms":1," <15ms":12," <20ms":472," <30ms":1349," <40ms":469," <50ms":239," <100ms":186," <150ms":60," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:65594] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:65594] normal , 5091 created, 7841 requestFinished, 1.54 req/socket, 0 requests, 33 sockets {" <10ms":1," <15ms":69," <20ms":1035," <30ms":2967," <40ms":765," <50ms":375," <100ms":322," <150ms":60," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:68371] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:68371] normal , 7669 created, 11825 requestFinished, 1.54 req/socket, 0 requests, 31 sockets {" <10ms":4," <15ms":89," <20ms":1538," <30ms":4702," <40ms":1041," <50ms":444," <100ms":493," <150ms":60," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:71027] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:71027] normal , 9974 created, 15498 requestFinished, 1.55 req/socket, 0 requests, 28 sockets {" <10ms":7," <15ms":164," <20ms":1955," <30ms":6209," <40ms":1414," <50ms":520," <100ms":686," <150ms":72," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:73665] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:73665] normal , 12283 created, 19152 requestFinished, 1.56 req/socket, 0 requests, 33 sockets {" <10ms":9," <15ms":213," <20ms":2383," <30ms":7659," <40ms":1798," <50ms":635," <100ms":883," <150ms":85," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:75652] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:75652] normal , 14101 created, 21974 requestFinished, 1.56 req/socket, 0 requests, 42 sockets {" <10ms":9," <15ms":221," <20ms":2527," <30ms":8311," <40ms":2354," <50ms":973," <100ms":1163," <150ms":94," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:78417] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:78417] normal , 16543 created, 25836 requestFinished, 1.56 req/socket, 0 requests, 36 sockets {" <10ms":14," <15ms":305," <20ms":2962," <30ms":9801," <40ms":2819," <50ms":1088," <100ms":1334," <150ms":94," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:80952] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:80952] normal , 18889 created, 29468 requestFinished, 1.56 req/socket, 0 requests, 43 sockets {" <10ms":14," <15ms":356," <20ms":3517," <30ms":11159," <40ms":3108," <50ms":1157," <100ms":1503," <150ms":120," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:83686] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:83686] normal , 21300 created, 33301 requestFinished, 1.56 req/socket, 0 requests, 38 sockets {" <10ms":16," <15ms":420," <20ms":4025," <30ms":12597," <40ms":3512," <50ms":1308," <100ms":1670," <150ms":120," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:86108] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:86108] normal , 23409 created, 36655 requestFinished, 1.57 req/socket, 0 requests, 22 sockets {" <10ms":18," <15ms":467," <20ms":4383," <30ms":13764," <40ms":3879," <50ms":1441," <100ms":2016," <150ms":122," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:88868] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:88868] normal , 25814 created, 40482 requestFinished, 1.57 req/socket, 0 requests, 46 sockets {" <10ms":19," <15ms":533," <20ms":4902," <30ms":15201," <40ms":4275," <50ms":1587," <100ms":2211," <150ms":122," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:91666] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 50 sockets, 50 unusedSockets, 0 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:91666] normal , 28381 created, 44473 requestFinished, 1.57 req/socket, 0 requests, 50 sockets {" <10ms":19," <15ms":554," <20ms":5545," <30ms":16824," <40ms":4529," <50ms":1706," <100ms":2349," <150ms":122," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:94360] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:94360] normal , 30806 created, 48274 requestFinished, 1.57 req/socket, 0 requests, 38 sockets {" <10ms":20," <15ms":602," <20ms":6022," <30ms":18432," <40ms":4797," <50ms":1819," <100ms":2528," <150ms":122," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:97025] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:97025] normal , 33198 created, 52025 requestFinished, 1.57 req/socket, 0 requests, 45 sockets {" <10ms":20," <15ms":656," <20ms":6538," <30ms":19831," <40ms":5199," <50ms":1936," <100ms":2700," <150ms":127," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:99476] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:99476] normal , 35422 created, 55496 requestFinished, 1.57 req/socket, 0 requests, 50 sockets {" <10ms":20," <15ms":691," <20ms":6957," <30ms":20987," <40ms":5591," <50ms":2119," <100ms":2947," <150ms":146," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:102244] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:102244] normal , 37970 created, 59456 requestFinished, 1.57 req/socket, 0 requests, 39 sockets {" <10ms":20," <15ms":724," <20ms":7531," <30ms":22621," <40ms":5963," <50ms":2201," <100ms":3008," <150ms":158," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:104983] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:104983] normal , 40458 created, 63337 requestFinished, 1.57 req/socket, 0 requests, 45 sockets {" <10ms":23," <15ms":765," <20ms":8094," <30ms":24200," <40ms":6253," <50ms":2323," <100ms":3149," <150ms":158," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:107668] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:107668] normal , 42842 created, 67108 requestFinished, 1.57 req/socket, 0 requests, 38 sockets {" <10ms":24," <15ms":819," <20ms":8633," <30ms":25651," <40ms":6577," <50ms":2419," <100ms":3369," <150ms":158," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:110199] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:110199] normal , 45166 created, 70731 requestFinished, 1.57 req/socket, 0 requests, 47 sockets {" <10ms":26," <15ms":865," <20ms":9193," <30ms":26962," <40ms":6891," <50ms":2525," <100ms":3546," <150ms":173," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:112975] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:112975] normal , 47579 created, 74565 requestFinished, 1.57 req/socket, 0 requests, 37 sockets {" <10ms":26," <15ms":935," <20ms":9745," <30ms":28452," <40ms":7222," <50ms":2639," <100ms":3765," <150ms":173," <200ms":18," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:114637] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:114637] normal , 48998 created, 76839 requestFinished, 1.57 req/socket, 0 requests, 23 sockets {" <10ms":26," <15ms":950," <20ms":9898," <30ms":29096," <40ms":7569," <50ms":2829," <100ms":4037," <150ms":181," <200ms":18," >=200ms+":33} ---------------------------------------------------------------- [proxy.js:117458] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:117458] normal , 51580 created, 80856 requestFinished, 1.57 req/socket, 0 requests, 26 sockets {" <10ms":27," <15ms":981," <20ms":10458," <30ms":30768," <40ms":7952," <50ms":2941," <100ms":4099," <150ms":181," <200ms":18," >=200ms+":33} done. Transactions: 60000 hits Availability: 100.00 % Elapsed time: 46.53 secs Data transferred: 14.88 MB Response time: 0.05 secs Transaction rate: 1289.49 trans/sec Throughput: 0.32 MB/sec Concurrency: 59.81 Successful transactions: 60000 Failed transactions: 0 Longest transaction: 0.45 Shortest transaction: 0.00 ---------------------------------------------------------------- [proxy.js:120000] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:120000] normal , 53866 created, 84260 requestFinished, 1.56 req/socket, 0 requests, 0 sockets {" <10ms":75," <15ms":1112," <20ms":10947," <30ms":32130," <40ms":8228," <50ms":3002," <100ms":4274," <150ms":181," <200ms":18," >=200ms+":33} ---------------------------------------------------------------- [proxy.js:120000] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0} ---------------------------------------------------------------- [proxy.js:120000] normal , 53866 created, 84260 requestFinished, 1.56 req/socket, 0 requests, 0 sockets {" <10ms":75," <15ms":1112," <20ms":10947," <30ms":32130," <40ms":8228," <50ms":3002," <100ms":4274," <150ms":181," <200ms":18," >=200ms+":33} ``` ================================================ FILE: benchmark/siegerc ================================================ # Updated by Siege 3.0.5, August-27-2014 # Copyright 2000-2013 by Jeffrey Fulmer, et al. # # Siege configuration file -- edit as necessary # For more information about configuring and running # this program, visit: http://www.joedog.org/ # # Variable declarations. You can set variables here # for use in the directives below. Example: # PROXY = proxy.joedog.org # Reference variables inside ${} or $(), example: # proxy-host = ${PROXY} # You can also reference ENVIRONMENT variables without # actually declaring them, example: # logfile = $(HOME)/var/siege.log # # Verbose mode # # Signify verbose mode, true turns on verbose output # ex: verbose = true|false # verbose = false # # Quiet mode # # When true, this turns off verbose and standard output. # You'll still see the opening announcement and the final # stats if you're running a siege but -g/--get will be # extremely quiet. This was added primarily for scripting # ex: quiet = true|false # quiet = true # # Get method - select an HTTP method to use when siege # is set to get mode, siege -g/--get URL. You may select # GET or HEAD. The default method is HEAD. As expected # HEAD prints just the headers and GET prints the entire # page. # # NOTE: This only applies when siege is invoked with # -g/--get. All other requests methods will be made # on the basis of the URL. # # example: gmethod = GET # gmethod = HEAD # # CSV Verbose format: with this option, you can choose # to format verbose output in traditional siege format # or comma separated format. The latter will allow you # to redirect output to a file for import into a spread # sheet, i.e., siege > file.csv # ex: csv = true|false (default false) # # csv = true # # Timestamp format: with this option, you can choose to # print a timestamp each line of output # example: timestamp = true|false (default false) # # sample: [Sat, 2010-11-20 10:39:13] HTTP/1.1 200 0.12 secs: 4003 bytes ==> / # # timestamp = true # # Full URL verbose format: By default siege displays # the URL path and not the full URL. With this option, # you # can instruct siege to show the complete URL. # ex: fullurl = true|false (default false) # # fullurl = true # # Display id: in verbose mode, display the siege user # id associated with the HTTP transaction information # ex: display-id = true|false # # display-id = # # Show logfile location. By default, siege displays the # logfile location at the end of every run when logging # You can turn this message off with this directive. # ex: show-logfile = false # show-logfile = true # # Default logging status, true turns logging on. # ex: logging = true|false # logging = false # # Logfile, the default siege logfile is $PREFIX/var/siege.log # This directive allows you to choose an alternative log file. # Environment variables may be used as shown in the examples: # ex: logfile = /home/jeff/var/log/siege.log # logfile = ${HOME}/var/log/siege.log # logfile = ${LOGFILE} # # logfile = # # HTTP protocol. Options HTTP/1.1 and HTTP/1.0. # Some webservers have broken implementation of the # 1.1 protocol which skews throughput evaluations. # If you notice some siege clients hanging for # extended periods of time, change this to HTTP/1.0 # ex: protocol = HTTP/1.1 # protocol = HTTP/1.0 # protocol = HTTP/1.1 # # Chunked encoding is required by HTTP/1.1 protocol # but siege allows you to turn it off as desired. # # ex: chunked = true # chunked = true # # Cache revalidation. # Siege supports cache revalidation for both ETag and # Last-modified headers. If a copy is still fresh, the # server responds with 304. # HTTP/1.1 200 0.00 secs: 2326 bytes ==> /apache_pb.gif # HTTP/1.1 304 0.00 secs: 0 bytes ==> /apache_pb.gif # HTTP/1.1 304 0.00 secs: 0 bytes ==> /apache_pb.gif # # ex: cache = true # cache = false # # Connection directive. Options "close" and "keep-alive" # Starting with release 2.57b3, siege implements persistent # connections in accordance to RFC 2068 using both chunked # encoding and content-length directives to determine the # page size. To run siege with persistent connections set # the connection directive to keep-alive. (Default close) # CAUTION: use the keep-alive directive with care. # DOUBLE CAUTION: this directive does not work well on HPUX # TRIPLE CAUTION: don't use keep-alives until further notice # ex: connection = close # connection = keep-alive # connection = keep-alive # # Default number of simulated concurrent users # ex: concurrent = 25 # concurrent = 15 # # Default duration of the siege. The right hand argument has # a modifier which specifies the time units, H=hours, M=minutes, # and S=seconds. If a modifier is not specified, then minutes # are assumed. # ex: time = 50M # # time = # # Repetitions. The length of siege may be specified in client # reps rather then a time duration. Instead of specifying a time # span, you can tell each siege instance to hit the server X number # of times. So if you chose 'reps = 20' and you've selected 10 # concurrent users, then siege will hit the server 200 times. # ex: reps = 20 # # reps = # # Default URLs file, set at configuration time, the default # file is PREFIX/etc/urls.txt. So if you configured siege # with --prefix=/usr/local then the urls.txt file is installed # int /usr/local/etc/urls.txt. Use the "file = " directive to # configure an alternative URLs file. You may use environment # variables as shown in the examples below: # ex: file = /export/home/jdfulmer/MYURLS.txt # file = $HOME/etc/urls.txt # file = $URLSFILE # # file = # # Default URL, this is a single URL that you want to test. This # is usually set at the command line with the -u option. When # used, this option overrides the urls.txt (-f FILE/--file=FILE) # option. You will HAVE to comment this out for in order to use # the urls.txt file option. # # NOTE: you may do the same thing by passing a URL to siege at # the command line: siege -c10 -r10 "www.joedog.org/" # Generally, it's a good idea to wrap a command line URL in quotes # # ex: url = https://shemp.whoohoo.com/docs/index.jsp # # url = # # Default delay value, see the siege(1) man page. # This value is used for load testing, it is not used # for benchmarking. # ex: delay = 3 # delay = 1 # # Connection timeout value. Set the value in seconds for # socket connection timeouts. The default value is 30 seconds. # ex: timeout = 30 # # timeout = # # Session expiration: This directive allows you to delete all # cookies after you pass through the URLs. This means siege will # grab a new session with each run through its URLs. The default # value is false. # ex: expire-session = true # # expire-session = # # Cookie support: by default siege accepts cookies. This directive # is available to disable that support. Set cookies to 'false' to # refuse cookies. Set it to 'true' to accept them. The default value # is true. # ex: cookies = false # # cookies = # # Failures: This is the number of total connection failures allowed # before siege aborts. Connection failures (timeouts, socket failures, # etc.) are combined with 400 and 500 level errors in the final stats, # but those errors do not count against the abort total. If you set # this total to 10, then siege will abort after ten socket timeouts, # but it will NOT abort after ten 404s. This is designed to prevent # a run-away mess on an unattended siege. The default value is 1024 # ex: failures = 50 # # failures = # # Internet simulation. If true, siege clients will hit # the URLs in the urls.txt file randomly, thereby simulating # internet usage. If false, siege will run through the # urls.txt file in order from first to last and back again. # ex: internet = true # internet = false # # Default benchmarking value, If true, there is NO delay # between server requests, siege runs as fast as the web # server and the network will let it. Set this to false # for load testing. # ex: benchmark = true # benchmark = false # # Set the siege User-Agent to identify yourself at the # host, the default is: JoeDog/1.00 [en] (X11; I; Siege #.##) # But that wreaks of corporate techno speak. Feel free # to make it more interesting :-) Since Limey is recovering # from minor surgery as I write this, I'll dedicate the # example to him... # # ex: user-agent = Limey The Bulldog # # user-agent = # # Accept-encoding. This option allows you to specify # acceptable encodings returned by the server. Use this # directive to turn on compression. By default we accept # gzip compression. # # ex: accept-encoding = * # accept-encoding = gzip # accept-encoding = compress;q=0.5;gzip;q=1 accept-encoding = gzip # # URL escaping was added in version 3.0.3. You may use this # directive to turn off this experimental feature. By default # this feature is active by default starting with v3.0.3 # # http://www.joedog.org/jukebox.php?band=the days of new # becomes: # http://www.joedog.org/jukebox.php?band=the%20days%20of%20the%20new # # ex: url-escaping = false # url-escaping = true # # TURN OFF THAT ANNOYING SPINNER! # Siege spawns a thread and runs a spinner to entertain you # as it collects and computes its stats. If you don't like # this feature, you may turn it off here. # ex: spinner = false # spinner = true # # WWW-Authenticate login. When siege hits a webpage # that requires basic authentication, it will search its # logins for authentication which matches the specific realm # requested by the server. If it finds a match, it will send # that login information. If it fails to match the realm, it # will send the default login information. (Default is "all"). # You may configure siege with several logins as long as no # two realms match. The format for logins is: # username:password[:realm] where "realm" is optional. # If you do not supply a realm, then it will default to "all" # ex: login = jdfulmer:topsecret:Admin # login = jeff:supersecret # # login = # # Login URL. This is the first URL to be hit by every siege # client. This feature was designed to allow you to login to # a server and establish a session. It will only be hit once # so if you need to hit this URL more then once, make sure it # also appears in your urls.txt file. # # ex: login-url = http://eos.haha.com/login.jsp POST name=jeff&pass=foo # # Siege versions after 2.69 support multi logins; you can configure # them with multiple login-url directives. Place each one on a separate # line. Siege loops through each login then starts again at the beginning # after it uses the last one. If you have more users than login-urls, then # siege starts reassigning ones that have already been used. # # ex: login-url = http://www.haha.com/login.php?name=homer&pass=whoohoo # login-url = http://www.haha.com/login.php?name=marge&pass=ohhomie # login-url = http://www.haha.com/login.php?name=bart&pass=eatMyShorts # # login-url = # # FTP login - This directive provides one of two ways # to login to an ftp server. You may also set credentials # in RFC-1738 format: ftp://user:pass@host.com/ink.jpg # # The format is USER:PASS:HOST separated by colon ':' # The host field is optional. If you don't set a host, # then siege will send the same user:pass to every FTP # server. You may use this directive MULTIPLE times. # Siege will store each instance in memory and send the # appropriate credentials at login time depending on the # hostname in the URL. # # ex: ftp-login: jdfulmer:whoohoo:ftp.joedog.org # ftp-login: jdfulmer:password # # ftp-login = # # FTP unique - This directive determines whether siege # will upload files with the same name (and therefore # overwrite whatever is on disk) or upload files each with a # unique name. If true, siege will rewrite the file name with # a timestamp in its name, i.e., p.jpg => p-3086060432.jpg # The default value is true. # # ex: unique = false # unique = true # # ssl-cert # This optional feature allows you to specify a path to a client # certificate. It is not neccessary to specify a certificate in # order to use https. If you don't know why you would want one, # then you probably don't need this feature. Use openssl to # generate a certificate and key with the following command: # $ openssl req -nodes -new -days 365 -newkey rsa:1024 \ # -keyout key.pem -out cert.pem # Specify a path to cert.pem as follows: # ex: ssl-cert = /home/jeff/.certs/cert.pem # # ssl-cert = # # ssl-key # Use this option to specify the key you generated with the command # above. ex: ssl-key = /home/jeff/.certs/key.pem # You may actually skip this option and combine both your cert and # your key in a single file: # $ cat key.pem > client.pem # $ cat cert.pem >> client.pem # Now set the path for ssl-cert: # ex: ssl-cert = /home/jeff/.certs/client.pem # (in this scenario, you comment out ssl-key) # # ssl-key = # # ssl-timeout # This option sets a connection timeout for the ssl library # ex: ssl-timeout = 30 # # ssl-timeout = # # ssl-ciphers # You can use this feature to select a specific ssl cipher # for HTTPs. To view the ones available with your library run # the following command: openssl ciphers # ex: ssl-ciphers = EXP-RC4-MD5 # # ssl-ciphers = # # Proxy-Authenticate. When scout hits a proxy server which # requires username and password authentication, it will this # username and password to the server. The format is username, # password and optional realm each separated by a colon. You # may enter more than one proxy-login as long as each one has # a different realm. If you do not enter a realm, then scout # will send that login information to all proxy challenges. If # you have more than one proxy-login, then scout will attempt # to match the login to the realm. # ex: proxy-login: jeff:secret:corporate # proxy-login: jeff:whoohoo # # proxy-login = # # Redirection support. This option allows to to control # whether a Location: hint will be followed. Most users # will want to follow redirection information, but sometimes # it's desired to just get the Location information. # # ex: follow-location = false # # follow-location = # Zero-length data. siege can be configured to disregard # results in which zero bytes are read after the headers. # Alternatively, such results can be counted in the final # tally of outcomes. # # ex: zero-data-ok = false # # zero-data-ok = # # end of siegerc ================================================ FILE: benchmark/sleep_server.js ================================================ 'use strict'; const http = require('http'); http.createServer((req, res) => { let size = 0; let data = ''; req.on('data', chunk => { size += chunk.length; data += chunk; }); req.on('end', () => { const timeout = parseInt(req.url.substring(1), 10) || 1; // default 1ms setTimeout(() => { const result = { timeout, headers: req.headers, size, data, }; res.socket.setNoDelay(true); res.end(JSON.stringify(result)); }, timeout); }); }).listen(1984); console.log('sleep server start, listen on 1984'); ================================================ FILE: benchmark/start.sh ================================================ #!/bin/bash # sudo sysctl -w net.inet.ip.portrange.first=12000 # sudo sysctl -w net.inet.tcp.msl=1000 # sudo sysctl -w kern.maxfiles=1000000 kern.maxfilesperproc=1000000 # sudo ulimit -n 100000 # sysctl -n machdep.cpu.brand_string SERVER=127.0.0.1 NUM=500 CONCURRENT=60 maxSockets=50 DELAY=5 POST=/post node sleep_server.js & sleep_server_pid=$! node proxy.js $maxSockets $SERVER & sleep 1 node -v echo "$maxSockets maxSockets, $CONCURRENT concurrent, $NUM requests per concurrent, ${DELAY}ms delay" echo "keep alive" echo "siege -R siegerc -c $CONCURRENT -r $NUM -b http://localhost:1985${POST}/k/$DELAY" siege -R siegerc -c $CONCURRENT -r $NUM -b http://localhost:1985${POST}/k/$DELAY sleep 5 echo "normal" echo "siege -R siegerc -c $CONCURRENT -r $NUM -b http://localhost:1985${POST}/$DELAY" siege -R siegerc -c $CONCURRENT -r $NUM -b http://localhost:1985${POST}/$DELAY sleep 3 kill $sleep_server_pid kill % ================================================ FILE: browser.js ================================================ module.exports = noop; module.exports.HttpsAgent = noop; // Noop function for browser since native api's don't use agents. function noop () {} ================================================ FILE: example/http_agent.js ================================================ 'use strict'; const http = require('http'); const HttpAgent = require('..').HttpAgent; const http_agent = new HttpAgent(); // https://www.google.com/search?q=nodejs&sugexp=chrome,mod=12&sourceid=chrome&ie=UTF-8 const options = { host: 'www.taobao.com', path: '/', method: 'GET', port: 80, agent: http_agent, }; function get() { const start = Date.now(); const req = http.get(options, res => { console.log('STATUS1: %d, %d ms', res.statusCode, Date.now() - start); console.log('HEADERS1: %j', res.headers); res.on('data', chunk => { console.log('BODY1: %d', chunk.length); }); res.on('end', () => { console.log('get end'); }); }); req.on('error', e => { console.log('problem with request: ' + e.message); }); } get(); setTimeout(() => { console.log('keep alive sockets:', http_agent); process.exit(); }, 300000); let count = 0; setInterval(() => { const name = http_agent.getName(options); const sockets = http_agent.sockets[name] || []; const freeSockets = http_agent.freeSockets[name] || []; console.log('%ss, %s, sockets: %d, destroyed: %s, free sockets: %d, destroyed: %s', ++count, name, sockets.length, sockets[0] && sockets[0].destroyed, freeSockets.length, freeSockets[0] && freeSockets[0].destroyed); }, 1000); setInterval(get, 120000); ================================================ FILE: example/https_agent.js ================================================ 'use strict'; const https = require('https'); const HttpsAgent = require('..').HttpsAgent; const agent = new HttpsAgent(); // https://www.google.com/search?q=nodejs&sugexp=chrome,mod=12&sourceid=chrome&ie=UTF-8 const options = { host: 'github.com', port: 443, path: '/', method: 'GET', agent, }; let start = Date.now(); const req = https.request(options, res => { console.log('STATUS1: %d, %d ms', res.statusCode, Date.now() - start); console.log('HEADERS1: %j', res.headers); res.setEncoding('utf8'); res.on('data', chunk => { console.log('BODY1: %d', chunk.length); }); res.on('end', () => { process.nextTick(() => { start = Date.now(); https.get(options, res => { console.log('STATUS2: %d, %d ms', res.statusCode, Date.now() - start); console.log('HEADERS2: %j', res.headers); res.setEncoding('utf8'); res.on('data', chunk => { console.log('BODY2: %d', chunk.length); }); }); }); }); }); req.on('error', e => { console.log('problem with request: ' + e.message); }); req.end(); setTimeout(() => { console.log('keep alive sockets:', agent); process.exit(); }, 5000); ================================================ FILE: example/server.js ================================================ 'use strict'; const http = require('http'); http.createServer((req, res) => { req.resume(); req.on('end', () => { res.statusCode = 200; res.end(req.method + ' ' + req.url); }); }).listen(8080); ================================================ FILE: index.d.ts ================================================ import * as http from 'http'; import * as https from 'https'; import * as net from 'net'; interface PlainObject { [key: string]: any; } declare class _HttpAgent extends http.Agent { constructor(opts?: AgentKeepAlive.HttpOptions); readonly statusChanged: boolean; createConnection(options: net.NetConnectOpts, cb?: Function): net.Socket; createSocket(req: http.IncomingMessage, options: http.RequestOptions, cb: Function): void; getCurrentStatus(): AgentKeepAlive.AgentStatus; } interface Constants { CURRENT_ID: Symbol; CREATE_ID: Symbol; INIT_SOCKET: Symbol; CREATE_HTTPS_CONNECTION: Symbol; SOCKET_CREATED_TIME: Symbol; SOCKET_NAME: Symbol; SOCKET_REQUEST_COUNT: Symbol; SOCKET_REQUEST_FINISHED_COUNT: Symbol; } /** * @deprecated instead use `import { HttpAgent } from 'agentkeepalive'; or `const HttpAgent = require('agentkeepalive').HttpAgent;` */ declare class AgentKeepAlive extends _HttpAgent {} declare namespace AgentKeepAlive { export interface AgentStatus { createSocketCount: number; createSocketErrorCount: number; closeSocketCount: number; errorSocketCount: number; timeoutSocketCount: number; requestCount: number; freeSockets: PlainObject; sockets: PlainObject; requests: PlainObject; } interface CommonHttpOption { keepAlive?: boolean | undefined; freeSocketTimeout?: number | undefined; freeSocketKeepAliveTimeout?: number | undefined; timeout?: number | undefined; socketActiveTTL?: number | undefined; } export interface HttpOptions extends http.AgentOptions, CommonHttpOption { } export interface HttpsOptions extends https.AgentOptions, CommonHttpOption { } export class HttpAgent extends _HttpAgent {} export class HttpsAgent extends https.Agent { constructor(opts?: HttpsOptions); readonly statusChanged: boolean; createConnection(options: net.NetConnectOpts, cb?: Function): net.Socket; createSocket(req: http.IncomingMessage, options: http.RequestOptions, cb: Function): void; getCurrentStatus(): AgentStatus; } export const constants: Constants; } export = AgentKeepAlive; ================================================ FILE: index.js ================================================ 'use strict'; const HttpAgent = require('./lib/agent'); module.exports = HttpAgent; module.exports.HttpAgent = HttpAgent; module.exports.HttpsAgent = require('./lib/https_agent'); module.exports.constants = require('./lib/constants'); ================================================ FILE: lib/agent.js ================================================ 'use strict'; const OriginalAgent = require('http').Agent; const ms = require('humanize-ms'); const debug = require('util').debuglog('agentkeepalive'); const { INIT_SOCKET, CURRENT_ID, CREATE_ID, SOCKET_CREATED_TIME, SOCKET_NAME, SOCKET_REQUEST_COUNT, SOCKET_REQUEST_FINISHED_COUNT, } = require('./constants'); // OriginalAgent come from // - https://github.com/nodejs/node/blob/v8.12.0/lib/_http_agent.js // - https://github.com/nodejs/node/blob/v10.12.0/lib/_http_agent.js // node <= 10 let defaultTimeoutListenerCount = 1; const majorVersion = parseInt(process.version.split('.', 1)[0].substring(1)); if (majorVersion >= 11 && majorVersion <= 12) { defaultTimeoutListenerCount = 2; } else if (majorVersion >= 13) { defaultTimeoutListenerCount = 3; } function deprecate(message) { console.log('[agentkeepalive:deprecated] %s', message); } class Agent extends OriginalAgent { constructor(options) { options = options || {}; options.keepAlive = options.keepAlive !== false; // default is keep-alive and 4s free socket timeout // see https://medium.com/ssense-tech/reduce-networking-errors-in-nodejs-23b4eb9f2d83 if (options.freeSocketTimeout === undefined) { options.freeSocketTimeout = 4000; } // Legacy API: keepAliveTimeout should be rename to `freeSocketTimeout` if (options.keepAliveTimeout) { deprecate('options.keepAliveTimeout is deprecated, please use options.freeSocketTimeout instead'); options.freeSocketTimeout = options.keepAliveTimeout; delete options.keepAliveTimeout; } // Legacy API: freeSocketKeepAliveTimeout should be rename to `freeSocketTimeout` if (options.freeSocketKeepAliveTimeout) { deprecate('options.freeSocketKeepAliveTimeout is deprecated, please use options.freeSocketTimeout instead'); options.freeSocketTimeout = options.freeSocketKeepAliveTimeout; delete options.freeSocketKeepAliveTimeout; } // Sets the socket to timeout after timeout milliseconds of inactivity on the socket. // By default is double free socket timeout. if (options.timeout === undefined) { // make sure socket default inactivity timeout >= 8s options.timeout = Math.max(options.freeSocketTimeout * 2, 8000); } // support humanize format options.timeout = ms(options.timeout); options.freeSocketTimeout = ms(options.freeSocketTimeout); options.socketActiveTTL = options.socketActiveTTL ? ms(options.socketActiveTTL) : 0; super(options); this[CURRENT_ID] = 0; // create socket success counter this.createSocketCount = 0; this.createSocketCountLastCheck = 0; this.createSocketErrorCount = 0; this.createSocketErrorCountLastCheck = 0; this.closeSocketCount = 0; this.closeSocketCountLastCheck = 0; // socket error event count this.errorSocketCount = 0; this.errorSocketCountLastCheck = 0; // request finished counter this.requestCount = 0; this.requestCountLastCheck = 0; // including free socket timeout counter this.timeoutSocketCount = 0; this.timeoutSocketCountLastCheck = 0; this.on('free', socket => { // https://github.com/nodejs/node/pull/32000 // Node.js native agent will check socket timeout eqs agent.options.timeout. // Use the ttl or freeSocketTimeout to overwrite. const timeout = this.calcSocketTimeout(socket); if (timeout > 0 && socket.timeout !== timeout) { socket.setTimeout(timeout); } }); } get freeSocketKeepAliveTimeout() { deprecate('agent.freeSocketKeepAliveTimeout is deprecated, please use agent.options.freeSocketTimeout instead'); return this.options.freeSocketTimeout; } get timeout() { deprecate('agent.timeout is deprecated, please use agent.options.timeout instead'); return this.options.timeout; } get socketActiveTTL() { deprecate('agent.socketActiveTTL is deprecated, please use agent.options.socketActiveTTL instead'); return this.options.socketActiveTTL; } calcSocketTimeout(socket) { /** * return <= 0: should free socket * return > 0: should update socket timeout * return undefined: not find custom timeout */ let freeSocketTimeout = this.options.freeSocketTimeout; const socketActiveTTL = this.options.socketActiveTTL; if (socketActiveTTL) { // check socketActiveTTL const aliveTime = Date.now() - socket[SOCKET_CREATED_TIME]; const diff = socketActiveTTL - aliveTime; if (diff <= 0) { return diff; } if (freeSocketTimeout && diff < freeSocketTimeout) { freeSocketTimeout = diff; } } // set freeSocketTimeout if (freeSocketTimeout) { // set free keepalive timer // try to use socket custom freeSocketTimeout first, support headers['keep-alive'] // https://github.com/node-modules/urllib/blob/b76053020923f4d99a1c93cf2e16e0c5ba10bacf/lib/urllib.js#L498 const customFreeSocketTimeout = socket.freeSocketTimeout || socket.freeSocketKeepAliveTimeout; return customFreeSocketTimeout || freeSocketTimeout; } } keepSocketAlive(socket) { const result = super.keepSocketAlive(socket); // should not keepAlive, do nothing if (!result) return result; const customTimeout = this.calcSocketTimeout(socket); if (typeof customTimeout === 'undefined') { return true; } if (customTimeout <= 0) { debug('%s(requests: %s, finished: %s) free but need to destroy by TTL, request count %s, diff is %s', socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], customTimeout); return false; } if (socket.timeout !== customTimeout) { socket.setTimeout(customTimeout); } return true; } // only call on addRequest reuseSocket(...args) { // reuseSocket(socket, req) super.reuseSocket(...args); const socket = args[0]; const req = args[1]; req.reusedSocket = true; const agentTimeout = this.options.timeout; if (getSocketTimeout(socket) !== agentTimeout) { // reset timeout before use socket.setTimeout(agentTimeout); debug('%s reset timeout to %sms', socket[SOCKET_NAME], agentTimeout); } socket[SOCKET_REQUEST_COUNT]++; debug('%s(requests: %s, finished: %s) reuse on addRequest, timeout %sms', socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], getSocketTimeout(socket)); } [CREATE_ID]() { const id = this[CURRENT_ID]++; if (this[CURRENT_ID] === Number.MAX_SAFE_INTEGER) this[CURRENT_ID] = 0; return id; } [INIT_SOCKET](socket, options) { // bugfix here. // https on node 8, 10 won't set agent.options.timeout by default // TODO: need to fix on node itself if (options.timeout) { const timeout = getSocketTimeout(socket); if (!timeout) { socket.setTimeout(options.timeout); } } if (this.options.keepAlive) { // Disable Nagle's algorithm: http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/ // https://fengmk2.com/benchmark/nagle-algorithm-delayed-ack-mock.html socket.setNoDelay(true); } this.createSocketCount++; if (this.options.socketActiveTTL) { socket[SOCKET_CREATED_TIME] = Date.now(); } // don't show the hole '-----BEGIN CERTIFICATE----' key string socket[SOCKET_NAME] = `sock[${this[CREATE_ID]()}#${options._agentKey}]`.split('-----BEGIN', 1)[0]; socket[SOCKET_REQUEST_COUNT] = 1; socket[SOCKET_REQUEST_FINISHED_COUNT] = 0; installListeners(this, socket, options); } createConnection(options, oncreate) { let called = false; const onNewCreate = (err, socket) => { if (called) return; called = true; if (err) { this.createSocketErrorCount++; return oncreate(err); } this[INIT_SOCKET](socket, options); oncreate(err, socket); }; const newSocket = super.createConnection(options, onNewCreate); if (newSocket) onNewCreate(null, newSocket); return newSocket; } get statusChanged() { const changed = this.createSocketCount !== this.createSocketCountLastCheck || this.createSocketErrorCount !== this.createSocketErrorCountLastCheck || this.closeSocketCount !== this.closeSocketCountLastCheck || this.errorSocketCount !== this.errorSocketCountLastCheck || this.timeoutSocketCount !== this.timeoutSocketCountLastCheck || this.requestCount !== this.requestCountLastCheck; if (changed) { this.createSocketCountLastCheck = this.createSocketCount; this.createSocketErrorCountLastCheck = this.createSocketErrorCount; this.closeSocketCountLastCheck = this.closeSocketCount; this.errorSocketCountLastCheck = this.errorSocketCount; this.timeoutSocketCountLastCheck = this.timeoutSocketCount; this.requestCountLastCheck = this.requestCount; } return changed; } getCurrentStatus() { return { createSocketCount: this.createSocketCount, createSocketErrorCount: this.createSocketErrorCount, closeSocketCount: this.closeSocketCount, errorSocketCount: this.errorSocketCount, timeoutSocketCount: this.timeoutSocketCount, requestCount: this.requestCount, freeSockets: inspect(this.freeSockets), sockets: inspect(this.sockets), requests: inspect(this.requests), }; } } // node 8 don't has timeout attribute on socket // https://github.com/nodejs/node/pull/21204/files#diff-e6ef024c3775d787c38487a6309e491dR408 function getSocketTimeout(socket) { return socket.timeout || socket._idleTimeout; } function installListeners(agent, socket, options) { debug('%s create, timeout %sms', socket[SOCKET_NAME], getSocketTimeout(socket)); // listener socket events: close, timeout, error, free function onFree() { // create and socket.emit('free') logic // https://github.com/nodejs/node/blob/master/lib/_http_agent.js#L311 // no req on the socket, it should be the new socket if (!socket._httpMessage && socket[SOCKET_REQUEST_COUNT] === 1) return; socket[SOCKET_REQUEST_FINISHED_COUNT]++; agent.requestCount++; debug('%s(requests: %s, finished: %s) free', socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT]); // should reuse on pedding requests? const name = agent.getName(options); if (socket.writable && agent.requests[name] && agent.requests[name].length) { // will be reuse on agent free listener socket[SOCKET_REQUEST_COUNT]++; debug('%s(requests: %s, finished: %s) will be reuse on agent free event', socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT]); } } socket.on('free', onFree); function onClose(isError) { debug('%s(requests: %s, finished: %s) close, isError: %s', socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], isError); agent.closeSocketCount++; } socket.on('close', onClose); // start socket timeout handler function onTimeout() { // onTimeout and emitRequestTimeout(_http_client.js) // https://github.com/nodejs/node/blob/v12.x/lib/_http_client.js#L711 const listenerCount = socket.listeners('timeout').length; // node <= 10, default listenerCount is 1, onTimeout // 11 < node <= 12, default listenerCount is 2, onTimeout and emitRequestTimeout // node >= 13, default listenerCount is 3, onTimeout, // onTimeout(https://github.com/nodejs/node/pull/32000/files#diff-5f7fb0850412c6be189faeddea6c5359R333) // and emitRequestTimeout const timeout = getSocketTimeout(socket); const req = socket._httpMessage; const reqTimeoutListenerCount = req && req.listeners('timeout').length || 0; debug('%s(requests: %s, finished: %s) timeout after %sms, listeners %s, defaultTimeoutListenerCount %s, hasHttpRequest %s, HttpRequest timeoutListenerCount %s', socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], timeout, listenerCount, defaultTimeoutListenerCount, !!req, reqTimeoutListenerCount); if (debug.enabled) { debug('timeout listeners: %s', socket.listeners('timeout').map(f => f.name).join(', ')); } agent.timeoutSocketCount++; const name = agent.getName(options); if (agent.freeSockets[name] && agent.freeSockets[name].indexOf(socket) !== -1) { // free socket timeout, destroy quietly socket.destroy(); // Remove it from freeSockets list immediately to prevent new requests // from being sent through this socket. agent.removeSocket(socket, options); debug('%s is free, destroy quietly', socket[SOCKET_NAME]); } else { // if there is no any request socket timeout handler, // agent need to handle socket timeout itself. // // custom request socket timeout handle logic must follow these rules: // 1. Destroy socket first // 2. Must emit socket 'agentRemove' event tell agent remove socket // from freeSockets list immediately. // Otherise you may be get 'socket hang up' error when reuse // free socket and timeout happen in the same time. if (reqTimeoutListenerCount === 0) { const error = new Error('Socket timeout'); error.code = 'ERR_SOCKET_TIMEOUT'; error.timeout = timeout; // must manually call socket.end() or socket.destroy() to end the connection. // https://nodejs.org/dist/latest-v10.x/docs/api/net.html#net_socket_settimeout_timeout_callback socket.destroy(error); agent.removeSocket(socket, options); debug('%s destroy with timeout error', socket[SOCKET_NAME]); } } } socket.on('timeout', onTimeout); function onError(err) { const listenerCount = socket.listeners('error').length; debug('%s(requests: %s, finished: %s) error: %s, listenerCount: %s', socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT], err, listenerCount); agent.errorSocketCount++; if (listenerCount === 1) { // if socket don't contain error event handler, don't catch it, emit it again debug('%s emit uncaught error event', socket[SOCKET_NAME]); socket.removeListener('error', onError); socket.emit('error', err); } } socket.on('error', onError); function onRemove() { debug('%s(requests: %s, finished: %s) agentRemove', socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], socket[SOCKET_REQUEST_FINISHED_COUNT]); // We need this function for cases like HTTP 'upgrade' // (defined by WebSockets) where we need to remove a socket from the // pool because it'll be locked up indefinitely socket.removeListener('close', onClose); socket.removeListener('error', onError); socket.removeListener('free', onFree); socket.removeListener('timeout', onTimeout); socket.removeListener('agentRemove', onRemove); } socket.on('agentRemove', onRemove); } module.exports = Agent; function inspect(obj) { const res = {}; for (const key in obj) { res[key] = obj[key].length; } return res; } ================================================ FILE: lib/constants.js ================================================ 'use strict'; module.exports = { // agent CURRENT_ID: Symbol('agentkeepalive#currentId'), CREATE_ID: Symbol('agentkeepalive#createId'), INIT_SOCKET: Symbol('agentkeepalive#initSocket'), CREATE_HTTPS_CONNECTION: Symbol('agentkeepalive#createHttpsConnection'), // socket SOCKET_CREATED_TIME: Symbol('agentkeepalive#socketCreatedTime'), SOCKET_NAME: Symbol('agentkeepalive#socketName'), SOCKET_REQUEST_COUNT: Symbol('agentkeepalive#socketRequestCount'), SOCKET_REQUEST_FINISHED_COUNT: Symbol('agentkeepalive#socketRequestFinishedCount'), }; ================================================ FILE: lib/https_agent.js ================================================ 'use strict'; const OriginalHttpsAgent = require('https').Agent; const HttpAgent = require('./agent'); const { INIT_SOCKET, CREATE_HTTPS_CONNECTION, } = require('./constants'); class HttpsAgent extends HttpAgent { constructor(options) { super(options); this.defaultPort = 443; this.protocol = 'https:'; this.maxCachedSessions = this.options.maxCachedSessions; /* istanbul ignore next */ if (this.maxCachedSessions === undefined) { this.maxCachedSessions = 100; } this._sessionCache = { map: {}, list: [], }; } createConnection(options, oncreate) { const socket = this[CREATE_HTTPS_CONNECTION](options, oncreate); this[INIT_SOCKET](socket, options); return socket; } } // https://github.com/nodejs/node/blob/master/lib/https.js#L89 HttpsAgent.prototype[CREATE_HTTPS_CONNECTION] = OriginalHttpsAgent.prototype.createConnection; [ 'getName', '_getSession', '_cacheSession', // https://github.com/nodejs/node/pull/4982 '_evictSession', ].forEach(function(method) { /* istanbul ignore next */ if (typeof OriginalHttpsAgent.prototype[method] === 'function') { HttpsAgent.prototype[method] = OriginalHttpsAgent.prototype[method]; } }); module.exports = HttpsAgent; ================================================ FILE: package.json ================================================ { "name": "agentkeepalive", "version": "4.6.0", "description": "Missing keepalive http.Agent", "main": "index.js", "browser": "browser.js", "files": [ "index.js", "index.d.ts", "browser.js", "lib" ], "scripts": { "contributor": "git-contributor", "test": "npm run lint && egg-bin test --full-trace", "test-local": "egg-bin test --full-trace", "cov": "cross-env NODE_DEBUG=agentkeepalive egg-bin cov --full-trace", "ci": "npm run lint && npm run cov", "lint": "eslint lib test index.js" }, "repository": { "type": "git", "url": "git://github.com/node-modules/agentkeepalive.git" }, "bugs": { "url": "https://github.com/node-modules/agentkeepalive/issues" }, "keywords": [ "http", "https", "agent", "keepalive", "agentkeepalive", "HttpAgent", "HttpsAgent" ], "dependencies": { "humanize-ms": "^1.2.1" }, "devDependencies": { "coffee": "^5.3.0", "cross-env": "^6.0.3", "egg-bin": "^4.9.0", "eslint": "^5.7.0", "eslint-config-egg": "^7.1.0", "git-contributor": "^2.0.0", "mm": "^2.4.1", "pedding": "^1.1.0", "typescript": "^3.8.3" }, "engines": { "node": ">= 8.0.0" }, "author": "fengmk2 (https://github.com/fengmk2)", "license": "MIT" } ================================================ FILE: test/fixtures/agenttest-cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIDyzCCArOgAwIBAgIUWHgqUZfGb/UAhDwy/TTdYkE3CiwwDQYJKoZIhvcNAQEL BQAwYTELMAkGA1UEBhMCU0UxEjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJ U3RvY2tob2xtMQswCQYDVQQDDAJjYTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs ZS5jb20wHhcNMjQxMjI4MTM0NzE1WhcNMjcwOTIzMTM0NzE1WjBSMQswCQYDVQQG EwJTRTESMBAGA1UECAwJU3RvY2tob2xtMRIwEAYDVQQHDAlTdG9ja2hvbG0xGzAZ BgNVBAMMEmFnZW50a2VlcGFsaXZlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP ADCCAQoCggEBAKyIpvObe/sNLw0k8KAQiKXhV233wPQDnocwP5xmoYi7Au84U4Dx esYk/aursc2S/jkiM1XCO37/FEe3IYffSH4D4HuBo+6jPzBSbbDFqPYNM+om52ad FjxWhN/v4Kwrc5a3nVilWULgsFqE5LiXp5Vuoi7HyEuGya23wM825QddgxizNRRr FT+Y7hDbGFdW/ZVTq3cpXm9E8sFkPMqiQUiZSt5XCPuh7AqN2Mt4Ghuebu/GhC7T CebAe+24ylIlY8bpg5m5bpyhZR7vOjVS6bcCR8MxONimWwlN50XYHgB5PhMEkCM3 jsB0xrxJSAHQraPOXeIwfxORxdW1qs0fKa0CAwEAAaOBiTCBhjAsBgNVHREEJTAj hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwCQYDVR0TBAIwADAL BgNVHQ8EBAMCBeAwHQYDVR0OBBYEFPp94MOUHtJpzS+9rDjBtyrxEPqgMB8GA1Ud IwQYMBaAFB7Dfbd+Q1uFOsGWENIzhFSA6e8TMA0GCSqGSIb3DQEBCwUAA4IBAQA4 DQxg2XCtwDkg4Blw2vl/E02PeY4vVXtkYpY3TNi4eE9YAqxXtyF98+Vpgbhyh5zv I+1h5h/UQbbMp4kSRXTx4BdE7ptSQdcTBOtU3RildOCdXQNFVE+Xc8LVIABL8tNK 0Fmip2QJEgjUFKgziLs5/XLRGL1JL5+sIrtK49XlwSazLDAdDDQdfC4f0YPnoQQk TrTBMoe0z98iMcYkAI6ffRxJTZvTmFtX0eYLSgIj/KmNCRlSGouLpjmV3yLu5hI1 LU4KOteqAiZyuxNDntFWD8tkJrX8qLUn9WHsZ0ud+9HvRQ8A5YMk6YPJSMf1KvF9 TjiKvg3asb2CwDe3y0fG -----END CERTIFICATE----- ================================================ FILE: test/fixtures/agenttest-key.pem ================================================ -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsiKbzm3v7DS8N JPCgEIil4Vdt98D0A56HMD+cZqGIuwLvOFOA8XrGJP2rq7HNkv45IjNVwjt+/xRH tyGH30h+A+B7gaPuoz8wUm2wxaj2DTPqJudmnRY8VoTf7+CsK3OWt51YpVlC4LBa hOS4l6eVbqIux8hLhsmtt8DPNuUHXYMYszUUaxU/mO4Q2xhXVv2VU6t3KV5vRPLB ZDzKokFImUreVwj7oewKjdjLeBobnm7vxoQu0wnmwHvtuMpSJWPG6YOZuW6coWUe 7zo1Uum3AkfDMTjYplsJTedF2B4AeT4TBJAjN47AdMa8SUgB0K2jzl3iMH8TkcXV tarNHymtAgMBAAECggEAHy3s2d5R5/socxwnXg3O3AdemPFqjc5voieufzGu5HpD W/7WXFmHYfKCYzk6fxee2K1dEJTy0o7/V7x5E0hfHeLOeRNjEDexLibfStBVqe2V 4a1ZKRqfT5UlGyEK/aI2l9ij10a+XE8ln54fhVpmvyMIrSKOiFOZ88pezjOx7QQF Y+LnKVb6b4yTPr7T9G7el7rzUtzf1rgvTib4+Dfe1ZwGHXdXSdGGqskOxOEre1zA RdCddjCYOBPHQ1fU/NN54/JidtbHRrYqnJPd7WjXLqXbIAifSCWZsgGTRoJt9HWm 2UG0dWA/+ab9dMEuS24YsqueZDpPZ6y7YBMV7Fo0wwKBgQDnOQx/MfFMPEzXC5QI 8M6w2MDpnSOCCE07ykny9F5who9ilPTr7Me5Cyx+uftC/7Ax5KmIPatWxOTtEB4q +RVW/PeRD8oq2+VHmC8XLlq7wlllbsi6HYo89pcUpHcGFI5wOFyBRBCSDVBjFXOO XaXtpK06NHU5Osv8WmHA3NHc/wKBgQC/BaI2pvSFdvXI5W/PtKHOH4xpeJO/8Ziu zFd9RCvHNxwfyLKrAHXqaCqDR3OPPN4Ao2JXaTy+f4b97tGHX4mIn8oaBmaUCDTy xCNk1C29daBMBRBqveaXPCfPcLgNYvMx6btgoSqGQBtMNyro48hw7uD2YF3DMZ56 iUrFR9R9UwKBgCVsP1CK1cH/9HPNpOz5hIps2nQ1AZ03GMD3kZZn5K0TqjtLXoSc swqI+2+bTEZgubSpjKLHUGbfwSl5NVjBLaoBkVQCGTdslaRLxjtbPkYrQ2q+TnCI /Wm2g2dM4xKx2wfgp2AokwIAc6VFwIknMQbQY1ULTnmvwXobarzbQIAjAoGBAJcC +yQ9hJ1mWBRD3crUP/5Vzokq+5MIie3WOWwcUoehN6ig3y50pMN1Kfayq9aXBeJ3 R61W0uC+rJdfD2H30yChQgKlAL8SZdlt2ZVCcA6RSPIQJtRb4Em7ErXZpIdgrGXP I4TWpGmRkIMGMfP+71zoXAwqTrWbKnAwzV181a1lAoGBAJd7BCyoK6+5BiUzkd0d asTVaV/BYDWdVrFFr78vjVoscqT66+yBmX3xo65wMHkw0yaRNWfKqE/1yseczQgi Q3IZCM9YIs+JIGP0mzNzK0apZTiEUwGEDnNvvK41hEMPfLypTBUyAM/mz0E9SyS8 2MTb4gwiIdfW8sgapxzGER7l -----END PRIVATE KEY----- ================================================ FILE: test/fixtures/ca.cnf ================================================ [ ca ] default_ca = CA_default [ CA_default ] serial = ca-serial crl = ca-crl.pem database = ca-database.txt name_opt = CA_default cert_opt = CA_default default_crl_days = 9999 default_md = md5 [ req ] default_bits = 2048 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no [ req_distinguished_name ] C = SE ST = Stockholm L = Stockholm CN = ca emailAddress = ca@example.com [ req_attributes ] challengePassword = test ================================================ FILE: test/fixtures/ca.key ================================================ -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBWx17nSe1zx8/ OTST6T5TtYn+lg9v4FCE5+qvukegqIezG79O6J4fjHJr6DagkMlYjGedJOuZd5NS jZOgb6Zz8ZRVnmxGFhfndu5PnFQTbSrEDZKQEB9/GDLvPhmrbyMtKvj+8m2s9rUH jCA+o76476o25LlA8GTP1HqZFj3oK0nPsCuuDrEy7aZYTdrDKI+FU7xsZHJZrrEQ PrBb04VtdlFnjOBJ1UeAWu9uypFJMaLzophgjIQZY6XFXWKYEV55MJgudmurC+sI 3lTpgnNNHuvjpKZ5xZ6pLP0O4pWs7GqWBK11fM0bTYxUcf7Xlnv0yuE7K3ffRYST 2GCbbxX1AgMBAAECggEAB/X8UHiBR/MhELif79t7FVw7APaxtQP+nLI/XE1zOuvY oA9RtAO2biSzGbrdjbfYXZZ0tg7s/YxJ59MfbTKCBaUNKLp7yVjwhZ2rPmJ4p07n 1o5oGeYoCp+qI5jvDdSaFXdLxpQ5rGG0XpaueMj27P6Lh6eo8ig6DMDtUuJypkPl Kc0gVX+eCZu7f3O+wI3WLykDO7csn/GQkS4Mcy+hRKVch4gGAU5EAzuLLU+vRPBS OLmj1X5O3epZchf+hzwB4/Sn1826Dw5Lni63NNOdsTCTLi2Rb2JUsutkzxweYtbw y8EXQuaruMWoERNt2qy2P85aTmwXqfHJe45BA8UiEwKBgQD5cy4pcOZLRMzr1AGI ybjrGR3DsDpFOez39LpUZNGFRVnGU7v2JUV7allx0HLtSJYJQR1BoGIKZbcgT2Ho CLurAfAsBAHzvRfHZUc32HLru/1XYe2hNllG8GkybMqYQiFIOuPEdKOIIlwFJYpY 9mp4VNrfwuAaxTBQcDcHsQO1ewKBgQDGbt33oStwsi3xqziYazXyB8bko06Q+lH6 k4DdckVPGDealKG4drdi+YAK/tB+YeShC7TiZx9GRKQDyED2eQucc+henLSsdCYJ RoZP9A4uORc4/sO0PE0mRx/ww06BVa5uaGv2xTeNt9ojRvmV/NwKyTx5NDZoPwM9 yKupukGvTwKBgQCP05xrIoB2J54r31pfH0gyrZGe0g5W+dYrBX0lydeQivL/SipW +xh7107pM7IAJFj1zwhqXWoJ6qnSxFKEMfza1cw/5LOncIC0ZC3TAkcIqqSg7ILR 7/87ysQs4dNSRNbhyaqoDER56q25/fIt5y5uYSat27PRW57G0ly4X5Tu3wKBgGZJ A9X/mBrRXely7wtySC5oX9e/bmJBBjz6B7UekCeDPjZKY0pot4MnRR9l1icvYuC4 3hbOPUrFWx4v/XyPTLLq9F7AvEkg3fJuDhHspdqhxxy0BkFDzCjtBMPgiPkWJ4H2 BaEAa/B7UtBYZ5Mu8mYE8U0w4tK9mHgiloo43l71AoGBAKJcF1MuHvddV7SzyK7j Hf7QdPzsLrPrqH6k9L1yVP1RsouHEgoHh63ot3cZBJEtDodjw9ISoiT4Z1SeGCva BSXYYjKlwo6ik6kFdy237VJu7XorEmpzhK+K2hoG7azyVJG3imsKCWHW7N75e+Id A+8fxkdCTHA8dB0pk4Rdth1l -----END PRIVATE KEY----- ================================================ FILE: test/fixtures/ca.pem ================================================ -----BEGIN CERTIFICATE----- MIIDczCCAlugAwIBAgIUYxxarpaqtKbdGhWDXUVEv9dGkvIwDQYJKoZIhvcNAQEL BQAwYTELMAkGA1UEBhMCU0UxEjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJ U3RvY2tob2xtMQswCQYDVQQDDAJjYTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs ZS5jb20wIBcNMjQxMjI4MTM0NzE1WhgPMjA1MjA1MTQxMzQ3MTVaMGExCzAJBgNV BAYTAlNFMRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAcMCVN0b2NraG9sbTEL MAkGA1UEAwwCY2ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMIIBIjAN BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVsde50ntc8fPzk0k+k+U7WJ/pYP b+BQhOfqr7pHoKiHsxu/TuieH4xya+g2oJDJWIxnnSTrmXeTUo2ToG+mc/GUVZ5s RhYX53buT5xUE20qxA2SkBAffxgy7z4Zq28jLSr4/vJtrPa1B4wgPqO+uO+qNuS5 QPBkz9R6mRY96CtJz7Arrg6xMu2mWE3awyiPhVO8bGRyWa6xED6wW9OFbXZRZ4zg SdVHgFrvbsqRSTGi86KYYIyEGWOlxV1imBFeeTCYLnZrqwvrCN5U6YJzTR7r46Sm ecWeqSz9DuKVrOxqlgStdXzNG02MVHH+15Z79MrhOyt330WEk9hgm28V9QIDAQAB oyEwHzAdBgNVHQ4EFgQUHsN9t35DW4U6wZYQ0jOEVIDp7xMwDQYJKoZIhvcNAQEL BQADggEBAA6peynGrNJfiD9yVvtZTfmnuAUTHlHJ9iUMmugo65J9D27ZgStgGTqE CwvhoYH+fbeJBB/WrWns48piWYXOUSoyPhCI/hGqGl4uu/d66OBBHLlOE6v2W5x0 EsOxAsTkobcG4y9CBgnoVdzcBuO+wfUFQDZYJkXFYYvK2GxJqJBTlJSqWITLute9 6e8BjytBzZnbI9AIopl+pt+ErMDQfkS1WIHDyg4ER0gpLDrQeJq0JxGuK/d6giYy hDtsGhz2ibk4DWVxYAlUIPwkvAnDMGW2AwbdAiX7HyqO2OqGwwberhiGZDXqLSoL LYor6j123IpnraOTz/NQjFMQ7ZyHVAs= -----END CERTIFICATE----- ================================================ FILE: test/fixtures/genkey.sh ================================================ #!/bin/bash # DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Generate ca key and pem openssl req -new -x509 -nodes -days 9999 -config ca.cnf -keyout ca.key -out ca.pem # Generate server key # openssl genrsa -out server.key 2048 openssl genrsa -out agenttest-key.pem 2048 # Generate a certificate signing request for server.key # openssl req -new -key server.key -out server.csr openssl req -new -key agenttest-key.pem -out agenttest.csr -config server.cnf # Sign the csr with the ca certificate, generating server.pem openssl x509 -req -extfile server.cnf -days 999 -passin "pass:password" -extensions v3_req -in agenttest.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out agenttest-cert.pem # openssl req -new -key agenttest-key.pem -out certrequest.csr rm agenttest.csr ca.srl # http://www.hacksparrow.com/node-js-https-ssl-certificate.html ================================================ FILE: test/fixtures/server.cnf ================================================ [ req ] default_bits = 2048 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no x509_extensions = v3_ca req_extensions = v3_req [ req_distinguished_name ] C = SE ST = Stockholm L = Stockholm CN = agentkeepalive.com [ req_attributes ] challengePassword = password [ v3_ca ] authorityInfoAccess = @issuer_info [ v3_req ] subjectAltName = @alt_names # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment [ issuer_info ] OCSP;URI.0 = http://ocsp.example.com/ caIssuers;URI.0 = http://example.com/ca.cert [ alt_names ] IP.1 = 127.0.0.1 IP.2 = ::1 DNS.1 = localhost ================================================ FILE: test/fixtures/ts/index.ts ================================================ import * as http from 'http'; import { constants, HttpAgent, HttpsAgent, HttpOptions, HttpsOptions, AgentStatus } from '../../..'; import * as assert from 'assert'; assert(constants.CREATE_ID); assert(constants.CREATE_HTTPS_CONNECTION); assert(constants.CURRENT_ID); const httpOpt: HttpOptions = { maxSockets: 100, maxFreeSockets: 10, timeout: 60000, // active socket keepalive for 60 seconds freeSocketTimeout: 30000, // free socket keepalive for 30 seconds }; const keepaliveAgent = new HttpAgent(httpOpt); const options = { host: 'cnodejs.org', port: 80, path: '/', method: 'GET', agent: keepaliveAgent, }; const req = http.request(options, res => { console.log('STATUS: ' + res.statusCode); console.log('HEADERS: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', function (chunk) { console.log('BODY: ' + chunk); }); }); req.on('error', e => { console.log('problem with request: ' + e.message); }); req.end(); setTimeout(() => { if (keepaliveAgent.statusChanged) { const httpAgentStatus: AgentStatus = keepaliveAgent.getCurrentStatus(); console.log('[%s] agent status changed: %j', Date(), httpAgentStatus); } }, 2000); // https const httpsOpt: HttpsOptions = { maxSockets: 100, maxFreeSockets: 10, timeout: 60000, // active socket keepalive for 60 seconds freeSocketTimeout: 30000, // free socket keepalive for 30 seconds }; const keepaliveHttpsAgent = new HttpsAgent(httpsOpt); const httpsAgentStatus: AgentStatus = keepaliveHttpsAgent.getCurrentStatus(); assert(httpsAgentStatus); ================================================ FILE: test/fixtures/ts/tsconfig.json ================================================ { "compileOnSave": true, "compilerOptions": { "target": "es2017", "module": "commonjs", "noImplicitAny": false, "experimentalDecorators": true, "emitDecoratorMetadata": true, "charset": "utf8", "allowJs": false, "pretty": false, "baseUrl": ".", "strict": true, "resolveJsonModule": true, "noEmitOnError": false, "noUnusedLocals": false, "noUnusedParameters": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, "skipLibCheck": false, "skipDefaultLibCheck": false, "importHelpers": false, "inlineSourceMap": true } } ================================================ FILE: test/http_agent.test.js ================================================ 'use strict'; const assert = require('assert'); const http = require('http'); const urlparse = require('url').parse; const pedding = require('pedding'); const mm = require('mm'); const HttpAgent = require('..').HttpAgent; const { CURRENT_ID, SOCKET_NAME, SOCKET_CREATED_TIME, SOCKET_REQUEST_COUNT, SOCKET_REQUEST_FINISHED_COUNT, } = require('..').constants; describe('test/agent.test.js', () => { const agentkeepalive = new HttpAgent({ keepAliveTimeout: 1000, maxSockets: 5, maxFreeSockets: 5, }); let port = null; const app = http.createServer((req, res) => { if (req.url === '/error') { res.destroy(); return; } else if (req.url === '/hang') { console.log('[new request] %s %s', req.method, req.url); // Wait forever. return; } else if (req.url === '/remote_close') { setTimeout(() => { req.connection.end(); }, 500); } const info = urlparse(req.url, true); if (info.query.timeout) { setTimeout(() => { res.end(info.query.timeout); }, parseInt(info.query.timeout)); return; } res.end(JSON.stringify({ info, url: req.url, headers: req.headers, socket: req.socket._getpeername(), })); }); before(done => { app.listen(0, () => { port = app.address().port; done(); }); }); afterEach(mm.restore); after(done => setTimeout(() => { agentkeepalive.destroy(); done(); }, 1500)); it('should default options set right', () => { const agent = agentkeepalive; assert(agent.keepAlive === true); assert(agent.keepAliveMsecs === 1000); assert(agent.maxSockets === 5); assert(agent.maxFreeSockets === 5); assert(agent.timeout === 8000); assert(agent.options.timeout === 8000); assert(agent.freeSocketKeepAliveTimeout === 1000); assert(agent.options.freeSocketTimeout === 1000); assert(!agent.socketActiveTTL); assert(agent.socketActiveTTL === 0); assert(agent.options.socketActiveTTL === 0); }); let remotePort = null; it('should request with connection: keep-alive with http.Agent(keepAlive=true)', done => { const agent = new http.Agent({ keepAlive: true, }); const req = http.request({ method: 'GET', port, path: '/', agent, }, res => { assert(res.statusCode === 200); const chunks = []; res.on('data', data => { chunks.push(data); }); res.on('end', () => { const data = JSON.parse(Buffer.concat(chunks)); assert(data.headers.connection === 'keep-alive'); done(); }); }); req.end(); }); // NOTE: The test is irrelevant since NodeJS >= v19 as behavior is changed and it has default `keep-alive` // see https://nodejs.org/en/blog/announcements/v19-release-announce#https11-keepalive-by-default it.skip('should request with connection: close with http.Agent()', done => { const req = http.request({ method: 'GET', port, path: '/', }, res => { assert(res.statusCode === 200); const chunks = []; res.on('data', data => { chunks.push(data); }); res.on('end', () => { const data = JSON.parse(Buffer.concat(chunks)); assert(data.headers.connection === 'close'); done(); }); }); req.end(); }); it('should destroy inactivity socket timeout by agent itself', done => { const name = 'localhost:' + port + ':'; const agentkeepalive = new HttpAgent({ freeSocketKeepAliveTimeout: '5s', timeout: '1s', }); assert(agentkeepalive.options.freeSocketTimeout === 5000); assert(agentkeepalive.options.timeout === 1000); assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); const chunks = []; res.resume(); res.on('data', data => { chunks.push(data); }); res.on('end', () => { const buf = Buffer.concat(chunks); console.log('end and got %d bytes', buf.length); const data = JSON.parse(buf); remotePort = data.socket.port; assert(data.headers.connection === 'keep-alive'); assert(agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); setTimeout(() => { assert(!agentkeepalive.sockets[name]); assert(agentkeepalive.freeSockets[name]); assert(agentkeepalive.freeSockets[name].length === 1); // request /hang timeout http.get({ agent: agentkeepalive, port, path: '/hang', }, () => { assert(false, 'should not run this'); }).on('error', err => { assert(err.message === 'Socket timeout'); assert(err.code === 'ERR_SOCKET_TIMEOUT'); done(); }); }, 20); }); }); }); it('should let request handle the socket timeout', done => { const name = 'localhost:' + port + ':'; const agentkeepalive = new HttpAgent({ freeSocketKeepAliveTimeout: '5s', timeout: '1s', }); assert(agentkeepalive.options.freeSocketTimeout === 5000); assert(agentkeepalive.options.timeout === 1000); assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); const chunks = []; res.resume(); res.on('data', data => { chunks.push(data); }); res.on('end', () => { const buf = Buffer.concat(chunks); console.log('end and got %d bytes', buf.length); const data = JSON.parse(buf); remotePort = data.socket.port; assert(data.headers.connection === 'keep-alive'); assert(agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); setTimeout(() => { assert(!agentkeepalive.sockets[name]); assert(agentkeepalive.freeSockets[name]); assert(agentkeepalive.freeSockets[name].length === 1); // request /hang timeout let handleTimeout = false; const req = http.get({ agent: agentkeepalive, port, path: '/hang', timeout: 2000, }, () => { assert(false, 'should not run this'); }).on('error', err => { assert(handleTimeout); // TODO: should be a better error message than "socket hang up" assert(err.message === 'socket hang up'); assert(err.code === 'ECONNRESET'); done(); }); req.on('timeout', () => { handleTimeout = true; req.abort(); }); }, 20); }); }); }); it('should request / 200 status', done => { const name = 'localhost:' + port + ':'; assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); const chunks = []; res.on('data', data => { chunks.push(data); }); res.on('end', () => { const data = JSON.parse(Buffer.concat(chunks)); remotePort = data.socket.port; assert(data.headers.connection === 'keep-alive'); assert(agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); setTimeout(() => { assert(!agentkeepalive.sockets[name]); assert(agentkeepalive.freeSockets[name]); assert(agentkeepalive.freeSockets[name].length === 1); done(); }, 20); }); }); const status = agentkeepalive.getCurrentStatus(); assert(status.createSocketCount === 1); assert(status.timeoutSocketCount === 0); assert(status.sockets[name] === 1); assert(!status.freeSockets[name]); }); it('should mock CURRENT_ID cross MAX_SAFE_INTEGER', _done => { const agent = new HttpAgent({ timeout: 1000, freeSocketTimeout: 1000, maxSockets: 10, maxFreeSockets: 5, }); agent[CURRENT_ID] = Number.MAX_SAFE_INTEGER - 1; const done = pedding(300, () => { // only allow 10 sockets assert(agent[CURRENT_ID] === 9); setImmediate(() => { const name = 'localhost:' + port + ':'; assert(agent.freeSockets[name].length === 5); agent.destroy(); _done(); }); }); function request(callback) { http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', callback); }); } for (let i = 0; i < 300; i++) { request(done); } }); it('should work on timeout same as freeSocketTimeout', done => { const agent = new HttpAgent({ timeout: 1000, freeSocketTimeout: 1000, }); http.get({ agent, port, path: '/', }, res => { const socket1 = res.socket; const timeout = socket1.timeout || socket1._idleTimeout; assert(timeout === 1000); assert(res.statusCode === 200); res.resume(); res.on('end', () => { setImmediate(() => { const timeout = socket1.timeout || socket1._idleTimeout; assert(timeout === 1000); http.get({ agent, port, path: '/', }, res => { const socket2 = res.socket; assert(socket2 === socket1); const timeout = socket2.timeout || socket2._idleTimeout; assert(timeout === 1000); assert(res.statusCode === 200); res.resume(); res.on('end', done); }); }); }); }); }); it('should work on freeSocketTimeout = 0', done => { const agent = new HttpAgent({ timeout: 100, freeSocketTimeout: 0, }); http.get({ agent, port, path: '/?timeout=80', }, res => { const socket1 = res.socket; const timeout = socket1.timeout || socket1._idleTimeout; assert(timeout === 100); assert(res.statusCode === 200); res.resume(); res.on('end', () => { setTimeout(() => { http.get({ agent, port, path: '/', }, res => { const socket2 = res.socket; assert(socket2 === socket1); const timeout = socket2.timeout || socket2._idleTimeout; assert(timeout === 100); assert(res.statusCode === 200); res.resume(); res.on('end', done); }); }, 80); }); }); }); it('should createConnection error', done => { const agent = new HttpAgent(); mm.error(require('http').Agent.prototype, 'createConnection', 'mock createConnection error'); http.get({ agent, port, path: '/', }).on('error', err => { assert(err); assert(err.message === 'mock createConnection error'); done(); }); }); it('should keepSocketAlive return false, no use any socket', done => { const agent = new HttpAgent(); mm(require('http').Agent.prototype, 'keepSocketAlive', () => { return false; }); http.get({ agent, port, path: '/', }, res => { const socket1 = res.socket; res.resume(); res.on('end', () => { setImmediate(() => { http.get({ agent, port, path: '/', }, res => { const socket2 = res.socket; assert(socket2 !== socket1); res.resume(); res.on('end', done); }); }); }); }); }); it('should agent emit socket error event', done => { const agent = new HttpAgent({ timeout: 100, }); const req = http.get({ agent, port, path: '/hang', }); // remove mocha default handler const originalException = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', originalException); process.once('uncaughtException', err => { // ignore future req error req.on('error', () => {}); process.on('uncaughtException', originalException); assert(err); assert(err.message === 'Socket timeout'); done(); }); }); it('should mock socket error', done => { done = pedding(2, done); const agent = new HttpAgent({ timeout: 100, }); const req = http.get({ agent, port, path: '/hang', }); req.on('socket', socket => { // remove req error listener const listener = socket.listeners('error').pop(); socket.removeListener('error', listener); // must destroy before emit error socket.destroy(); socket.emit('error', new Error('mock socket error')); }).on('error', err => { assert(err); assert(err.message === 'socket hang up'); done(); }); // remove mocha default handler const originalException = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', originalException); assert(process.listeners('uncaughtException').length === 0); process.once('uncaughtException', err => { process.on('uncaughtException', originalException); assert(err); assert(err.message === 'mock socket error'); done(); }); }); it('should request again and use the same socket', done => { const name = 'localhost:' + port + ':'; assert(!agentkeepalive.sockets[name]); assert(agentkeepalive.freeSockets[name]); assert(agentkeepalive.freeSockets[name].length === 1); http.get({ agent: agentkeepalive, port, path: '/foo', }, res => { assert(res.statusCode === 200); const chunks = []; res.on('data', data => { chunks.push(data); }); res.on('end', () => { const data = JSON.parse(Buffer.concat(chunks)); assert(data.socket.port === remotePort); assert(agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); setTimeout(() => { const status = agentkeepalive.getCurrentStatus(); assert(status.createSocketCount === 1); assert(status.closeSocketCount === 0); assert(status.timeoutSocketCount === 0); assert(status.requestCount === 2); assert(!status.sockets[name]); assert(status.freeSockets[name]); assert(status.freeSockets[name] === 1); done(); }, 10); }); }); assert(agentkeepalive.sockets[name]); assert(agentkeepalive.sockets[name].length === 1); assert(!agentkeepalive.freeSockets[name]); }); it('should remove keepalive socket when server side destroy()', done => { const agent = new HttpAgent({ keepAliveTimeout: 1000, maxSockets: 5, maxFreeSockets: 5, }); http.get({ agent, port, path: '/foo', }, res => { assert(res.statusCode === 200); const chunks = []; res.on('data', data => { chunks.push(data); }); res.on('end', () => { const data = JSON.parse(Buffer.concat(chunks)); assert(data.socket.port); setTimeout(next, 1); }); }); function next() { const name = 'localhost:' + port + ':'; assert(!agent.sockets[name]); assert(agent.freeSockets[name] && agent.freeSockets[name].length === 1); const req = http.get({ agent, port, path: '/error', }, () => { assert.fail('should not call this'); }); req.on('error', err => { assert(err.message === 'socket hang up'); assert(agent.sockets[name].length === 1); assert(!agent.freeSockets[name]); setTimeout(() => { assert(!agent.sockets[name]); assert(!agent.freeSockets[name]); done(); }, 10); }); assert(agent.sockets[name].length === 1); assert(!agent.freeSockets[name]); } }); it('should remove socket when socket.destroy()', done => { const agentkeepalive = new HttpAgent({ freeSocketTimeout: 1000, maxSockets: 5, maxFreeSockets: 5, }); const name = 'localhost:' + port + ':'; assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agentkeepalive.sockets[name].length === 1); assert(!agentkeepalive.freeSockets[name]); setTimeout(() => { assert(!agentkeepalive.sockets[name]); assert(agentkeepalive.freeSockets[name].length === 1); agentkeepalive.freeSockets[name][0].destroy(); setTimeout(() => { assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); done(); }, 10); }, 10); }); }).on('error', done); }); it('should use new socket when hit the max keepalive time: 1000ms', done => { const agentkeepalive = new HttpAgent({ freeSocketTimeout: 1000, maxSockets: 5, maxFreeSockets: 5, }); const name = 'localhost:' + port + ':'; assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); let lastPort = null; res.on('data', data => { data = JSON.parse(data); lastPort = data.socket.port; assert(lastPort > 0); }); res.on('end', () => { assert(agentkeepalive.sockets[name].length === 1); assert(!agentkeepalive.freeSockets[name]); // free keepAlive socket timeout and destroy setTimeout(() => { assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); assert(data.socket.port > 0); assert(data.socket.port !== lastPort); }); res.on('end', done); }); }, 2000); }); }); }); it('should disable keepalive when keepAlive=false', done => { const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ keepAlive: false, }); assert(agent.keepAlive === false); http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.on('data', data => { assert(JSON.parse(data).headers.connection === 'close'); }); res.on('end', () => { assert(agent.sockets[name].length === 1); assert(!agent.freeSockets[name]); setTimeout(() => { assert(!agent.sockets[name]); assert(!agent.freeSockets[name]); done(); }, 10); }); }); }); it('should not keepalive when client.abort()', done => { const agentkeepalive = new HttpAgent({ freeSocketTimeout: 1000, maxSockets: 5, maxFreeSockets: 5, }); const name = 'localhost:' + port + ':'; assert(!agentkeepalive.sockets[name]); const req = http.get({ agent: agentkeepalive, port, path: '/', }, () => { assert.fail('should not call this.'); }); req.on('error', err => { assert(err.message, 'socket hang up'); assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); done(); }); process.nextTick(() => { req.abort(); }); assert(agentkeepalive.sockets[name].length === 1); }); it('should keep 1 socket', done => { const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ maxSockets: 1, maxFreeSockets: 1, }); let lastPort = null; http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name].length === 1); assert(agent.requests[name].length === 1); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); lastPort = data.socket.port; assert(lastPort > 0); }); res.on('end', () => { // should be reuse process.nextTick(() => { assert(agent.sockets[name].length === 1); assert(!agent.freeSockets[name]); }); }); }); http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name].length === 1); assert(!agent.requests[name]); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); assert(data.socket.port === lastPort); }); res.on('end', () => { setTimeout(() => { // should keepalive 1 socket assert(!agent.sockets[name]); assert(agent.freeSockets[name].length === 1); done(); }, 10); }); }); // has 1 request pedding in the requests queue assert(agent.requests[name].length === 1); }); it('should keep 1 free socket', done => { const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ maxSockets: 2, maxFreeSockets: 1, }); let lastPort = null; http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name]); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); lastPort = data.socket.port; assert(lastPort > 0); }); res.on('end', () => { // should be reuse setTimeout(() => { assert(agent.freeSockets[name].length === 1); }, 100); }); }); http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name]); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); assert(data.socket.port !== lastPort); }); res.on('end', () => { setTimeout(() => { // should keepalive 1 socket assert(!agent.sockets[name]); assert(agent.freeSockets[name].length === 1); done(); }, 100); }); }); assert(!agent.requests[name]); }); it('should keep 2 free socket', done => { done = pedding(2, done); const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ maxSockets: 2, maxFreeSockets: 2, }); let lastPort = null; http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name].length); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); lastPort = data.socket.port; assert(lastPort > 0); }); res.on('end', () => { // should be reuse process.nextTick(() => { assert(agent.freeSockets[name]); done(); }); }); }); http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name].length); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); assert(data.socket.port !== lastPort); }); res.on('end', () => { setTimeout(() => { // should keepalive 2 free sockets assert(!agent.sockets[name]); assert(agent.freeSockets[name].length === 2); done(); }, 10); }); }); assert(!agent.requests[name]); }); it('should request /remote_close 200 status, after 500ms free socket close', done => { const name = 'localhost:' + port + ':'; assert(!agentkeepalive.sockets[name]); http.get({ agent: agentkeepalive, port, path: '/remote_close', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); setTimeout(() => { assert(!agentkeepalive.sockets[name]); assert(!agentkeepalive.freeSockets[name]); done(); }, 1000); }); }); }); it('should fire req timeout callback the first use socket', done => { done = pedding(2, done); const agent = new HttpAgent({ maxSockets: 2, maxFreeSockets: 2, }); http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { const lastStatus = agent.getCurrentStatus(); const req = http.get({ agent, port, path: '/hang', }, () => { assert.fail('should not call this'); }); req.setTimeout(100, () => { const status = agent.getCurrentStatus(); assert(status.timeoutSocketCount - lastStatus.timeoutSocketCount === 1); req.abort(); done(); }); req.on('error', err => { assert(err.message === 'socket hang up'); done(); }); }); }); }); it('should fire req timeout callback the second use socket', done => { done = pedding(2, done); const agent = new HttpAgent({ maxSockets: 2, maxFreeSockets: 2, }); http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { const lastStatus = agent.getCurrentStatus(); assert(lastStatus.createSocketCount === 1); // make sure reuse the same socket setImmediate(() => { const req = http.get({ agent, port, path: '/hang', }, () => { assert.fail('should not call this'); }); req.setTimeout(100, () => { const status = agent.getCurrentStatus(); assert(status.createSocketCount === 1); assert(status.timeoutSocketCount - lastStatus.timeoutSocketCount === 1); req.abort(); done(); }); req.on('error', err => { assert(err.message === 'socket hang up'); done(); }); }); }); }); }); it('should free socket timeout work', done => { const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ keepAliveTimeout: 100, }); let lastPort = null; http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name].length === 1); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); lastPort = data.socket.port; assert(lastPort > 0); }); res.on('end', () => { process.nextTick(() => { assert(!agent.sockets[name]); assert(agent.freeSockets[name].length === 1); // free socket timeout after 100ms setTimeout(() => { assert(!agent.freeSockets[name]); done(); }, 110); }); }); }); }); it('should first use working socket timeout', done => { const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ timeout: 100, }); http.get({ agent, port, path: '/hang', }, () => { throw new Error('should not run this'); }).on('error', err => { assert(err.message === 'Socket timeout'); assert(err.code === 'ERR_SOCKET_TIMEOUT'); assert(!agent.sockets[name]); done(); }); assert(agent.sockets[name].length === 1); }); it('should reuse working socket timeout', done => { const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ timeout: 100, }); http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { setImmediate(() => { http.get({ agent, port, path: '/hang', }, () => { throw new Error('should not run this'); }).on('error', err => { assert(err.message === 'Socket timeout'); assert(err.code === 'ERR_SOCKET_TIMEOUT'); assert(!agent.sockets[name]); done(); }); }); }); }); assert(agent.sockets[name].length === 1); }); it('should destroy free socket before timeout', done => { const name = 'localhost:' + port + ':'; const agent = new HttpAgent(); let lastPort = null; http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name].length === 1); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); lastPort = data.socket.port; assert(lastPort > 0); }); res.on('end', () => { process.nextTick(() => { assert(!agent.sockets[name]); assert(agent.freeSockets[name].length === 1); agent.freeSockets[name][0].destroy(); assert(agent.createSocketCount === 1); setTimeout(() => { assert(!agent.freeSockets[name]); // new request use the new socket http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name].length === 1); assert(res.statusCode === 200); assert(agent.createSocketCount === 2); res.resume(); res.on('end', done); }); }, 10); }); }); }); assert(agent.sockets[name].length === 1); }); it('should remove error socket and create new one handle pedding request', done => { done = pedding(2, done); const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ maxSockets: 1, maxFreeSockets: 1, }); let lastPort = null; http.get({ agent, port, path: '/error', }, () => { throw new Error('never run this'); }).on('error', err => { assert(err.message === 'socket hang up'); }).on('close', () => done()); http.get({ agent, port, path: '/', }, res => { assert(agent.sockets[name].length === 1); const socket = agent.sockets[name][0]; assert(socket[SOCKET_REQUEST_COUNT] === 1); // not finish assert(socket[SOCKET_REQUEST_FINISHED_COUNT] === 0); assert(res.statusCode === 200); res.on('data', data => { data = JSON.parse(data); lastPort = data.socket.port; assert(lastPort > 0); }); res.on('end', () => { process.nextTick(() => { assert(!agent.sockets[name]); assert(agent.freeSockets[name].length === 1); const socket = agent.freeSockets[name][0]; assert(socket[SOCKET_REQUEST_COUNT] === 1); // request finished assert(socket[SOCKET_REQUEST_FINISHED_COUNT] === 1); done(); }); }); }); assert(agent.requests[name].length === 1); }); it('should destroy all sockets when freeSockets is empty', done => { done = pedding(2, done); const name = 'localhost:' + port + ':'; const agent = new HttpAgent(); http.get({ agent, port, path: '/', }, res => { http.get({ agent, port, path: '/', }).on('error', err => { assert(err.message === 'socket hang up'); setTimeout(() => { assert(!agent.sockets[name]); assert(!agent.freeSockets[name]); done(); }, 10); }); assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agent.sockets[name].length === 2); agent.destroy(); done(); }); }); }); it('should destroy both sockets and freeSockets', done => { done = pedding(2, done); const name = 'localhost:' + port + ':'; const agent = new HttpAgent(); http.get({ agent, port, path: '/', }, res => { http.get({ agent, port, path: '/', }).on('error', err => { assert(err.message === 'socket hang up'); setTimeout(() => { assert(!agent.sockets[name]); assert(!agent.freeSockets[name]); done(); }, 10); }); assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agent.sockets[name].length === 2); assert(!agent.freeSockets[name]); setImmediate(() => { assert(agent.sockets[name].length === 1); assert(agent.freeSockets[name].length === 1); agent.destroy(); done(); }); }); }); }); it('should keep max sockets: bugfix for orginal keepalive agent', _done => { const name = 'localhost:' + port + ':'; const agentkeepalive = new HttpAgent({ maxSockets: 2, maxFreeSockets: 2, }); const done = pedding(2, err => { assert(!err); const pool = agentkeepalive.sockets[name]; assert(!pool); // all sockets on free list now const freepool = agentkeepalive.freeSockets[name]; assert(freepool.length === 2); _done(); }); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agentkeepalive.sockets[name]); setImmediate(done); }); }); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agentkeepalive.sockets[name]); setImmediate(done); }); }); }); it('should make sure max sockets limit work', _done => { const name = 'localhost:' + port + ':'; const agentkeepalive = new HttpAgent({ maxSockets: 2, maxFreeSockets: 2, }); const done = pedding(3, err => { assert(!err); const pool = agentkeepalive.sockets[name]; assert(!pool); // all sockets on free list now const freepool = agentkeepalive.freeSockets[name]; assert(freepool.length === 2); // make sure all free sockets SOCKET_REQUEST_FINISHED_COUNT equal to SOCKET_REQUEST_COUNT for (const s of freepool) { assert(s[SOCKET_REQUEST_FINISHED_COUNT] === s[SOCKET_REQUEST_COUNT]); } _done(); }); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agentkeepalive.sockets[name]); setImmediate(done); }); }); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agentkeepalive.sockets[name]); setImmediate(done); }); }); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agentkeepalive.sockets[name]); setImmediate(() => { // reuse free socket on addRequest assert(agentkeepalive.freeSockets[name]); http.get({ agent: agentkeepalive, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(agentkeepalive.sockets[name]); setImmediate(done); }); }); }); }); }); assert(agentkeepalive.sockets[name].length === 2); assert(!agentkeepalive.freeSockets[name]); }); it('should timeout and remove free socket', done => { done = pedding(2, done); const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ maxSockets: 1, maxFreeSockets: 1, freeSocketTimeout: 1000, }); const options = { hostname: 'registry.npmjs.org', port: 80, path: '/', method: 'GET', agent, }; let index = 0; const getRequest = () => { const currentIndex = index++; const req = http.request(options, res => { let size = 0; res.on('data', chunk => { size += chunk.length; }); res.on('end', () => { console.log('#%d req end, size: %d', currentIndex, size); done(); }); }); req.on('error', done); return req; }; const req = getRequest(); // Get a reference to the socket. req.on('socket', sock => { // Listen to timeout and send another request immediately. sock.on('timeout', () => { console.log('free socket:%s timeout', sock._host); assert(!sock.writable); // sock has been removed from freeSockets list assert(!agent.freeSockets[name]); console.log('new request send'); getRequest().end(); }); }); req.end(); }); it('should not open more sockets than maxSockets when request success', done => { done = pedding(3, done); const name = 'localhost:' + port + ':'; const agentkeepalive = new HttpAgent({ keepAlive: true, keepAliveTimeout: 1000, maxSockets: 1, maxFreeSockets: 1, }); http.get({ agent: agentkeepalive, port, path: '/hello1', }, res => { let info; assert(res.statusCode === 200); res.on('data', data => { info = JSON.parse(data); }); res.on('end', () => { assert(info.url === '/hello1'); assert(agentkeepalive.sockets[name].length === 1); done(); }); res.resume(); }); http.get({ agent: agentkeepalive, port, path: '/hello2', }, res => { let info; assert(res.statusCode === 200); res.on('data', data => { info = JSON.parse(data); }); res.on('end', () => { assert(info.url === '/hello2'); assert(agentkeepalive.sockets[name].length === 1); done(); }); res.resume(); }); http.get({ agent: agentkeepalive, port, path: '/hello3', }, res => { let info; assert(res.statusCode === 200); res.on('data', data => { info = JSON.parse(data); }); res.on('end', () => { assert(info.url === '/hello3'); assert(agentkeepalive.sockets[name].length === 1); done(); }); res.resume(); }); assert(Object.keys(agentkeepalive.sockets).length === 1); assert(agentkeepalive.sockets[name].length === 1); }); it('should not open more sockets than maxSockets when request timeout', done => { const name = 'localhost:' + port + ':'; const agentkeepalive = new HttpAgent({ keepAlive: true, timeout: 1000, maxSockets: 1, maxFreeSockets: 1, }); http.get({ agent: agentkeepalive, port, path: '/hang', }, () => { throw new Error('should not run this'); }) .on('error', () => { assert(agentkeepalive.sockets[name].length === 1); done(); }); http.get({ agent: agentkeepalive, port, path: '/hang', }, () => { throw new Error('should not run this'); }) .on('error', () => { // do noting }); http.get({ agent: agentkeepalive, port, path: '/hang', }, () => { throw new Error('should not run this'); }) .on('error', () => { // do noting }); assert(Object.keys(agentkeepalive.sockets).length === 1); }); it('should set req.reusedSocket to true when reuse socket', done => { const agent = new HttpAgent({ keepAlive: true, }); // First request const req1 = http.get({ port, path: '/', agent, }, res => { assert(res.statusCode === 200); res.on('data', () => {}); res.on('end', () => { setTimeout(() => { // Second request const req2 = http.get({ port, path: '/', agent, }, res => { assert(res.statusCode === 200); res.on('data', () => {}); res.on('end', () => { done(); }); }); // Second request reuses the socket assert(req2.reusedSocket); }, 10); }); }); // First request doesn't reuse the socket assert(!req1.reusedSocket); }); describe('request timeout > agent timeout', () => { it('should use request timeout', done => { const agent = new HttpAgent({ keepAlive: true, timeout: 1000, }); const req = http.get({ agent, port, path: '/?timeout=20000', timeout: 1500, }, res => { console.error(res.statusCode, res.headers); assert.fail('should not get res here'); }); let isTimeout = false; req.on('timeout', () => { isTimeout = true; req.abort(); }); req.on('error', err => { assert(isTimeout); assert(err); assert(err.message === 'socket hang up'); assert(err.code === 'ECONNRESET'); done(); }); }); }); describe('keepAlive = false', () => { it('should close socket after request', done => { const name = 'localhost:' + port + ':'; const agent = new HttpAgent({ keepAlive: false, }); http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { setTimeout(() => { assert(!agent.sockets[name]); assert(!agent.freeSockets[name]); done(); }, 10); }); }); }); }); describe('getCurrentStatus()', () => { it('should get current agent status', () => { const status = agentkeepalive.getCurrentStatus(); assert.deepEqual(Object.keys(status), [ 'createSocketCount', 'createSocketErrorCount', 'closeSocketCount', 'errorSocketCount', 'timeoutSocketCount', 'requestCount', 'freeSockets', 'sockets', 'requests', ]); }); }); describe('getter statusChanged', () => { it('should get statusChanged', () => { const agentkeepalive = new HttpAgent({ keepAliveTimeout: 1000, maxSockets: 5, maxFreeSockets: 5, }); assert(agentkeepalive.statusChanged === false); assert(agentkeepalive.statusChanged === false); agentkeepalive.createSocketCount++; assert(agentkeepalive.createSocketCount !== agentkeepalive.createSocketCountLastCheck); assert(agentkeepalive.statusChanged === true); assert(agentkeepalive.createSocketCount === agentkeepalive.createSocketCountLastCheck); assert(agentkeepalive.statusChanged === false); agentkeepalive.createSocketErrorCount++; assert(agentkeepalive.createSocketErrorCount !== agentkeepalive.createSocketErrorCountLastCheck); assert(agentkeepalive.statusChanged === true); assert(agentkeepalive.createSocketErrorCount === agentkeepalive.createSocketErrorCountLastCheck); assert(agentkeepalive.statusChanged === false); agentkeepalive.closeSocketCount++; assert(agentkeepalive.closeSocketCount !== agentkeepalive.closeSocketCountLastCheck); assert(agentkeepalive.statusChanged === true); assert(agentkeepalive.closeSocketCount === agentkeepalive.closeSocketCountLastCheck); assert(agentkeepalive.statusChanged === false); agentkeepalive.errorSocketCount++; assert(agentkeepalive.errorSocketCount !== agentkeepalive.errorSocketCountLastCheck); assert(agentkeepalive.statusChanged === true); assert(agentkeepalive.errorSocketCount === agentkeepalive.errorSocketCountLastCheck); assert(agentkeepalive.statusChanged === false); agentkeepalive.timeoutSocketCount++; assert(agentkeepalive.timeoutSocketCount !== agentkeepalive.timeoutSocketCountLastCheck); assert(agentkeepalive.statusChanged === true); assert(agentkeepalive.timeoutSocketCount === agentkeepalive.timeoutSocketCountLastCheck); assert(agentkeepalive.statusChanged === false); agentkeepalive.requestCount++; assert(agentkeepalive.requestCount !== agentkeepalive.requestCountLastCheck); assert(agentkeepalive.statusChanged === true); assert(agentkeepalive.requestCount === agentkeepalive.requestCountLastCheck); assert(agentkeepalive.statusChanged === false); }); }); describe('mock idle socket error', () => { it('should idle socket emit error event', done => { const agent = new HttpAgent(); const options = { host: 'r.cnpmjs.org', port: 80, path: '/', agent, }; const socketKey = agent.getName(options); const req = http.get(options, res => { let size = 0; assert(res.headers.connection === 'keep-alive'); res.on('data', chunk => { size += chunk.length; }); res.on('end', () => { assert(size > 0); assert(Object.keys(agent.sockets).length === 1); assert(Object.keys(agent.freeSockets).length === 0); process.nextTick(() => { assert(agent.freeSockets[socketKey].length === 1); setTimeout(() => { // agent should catch idle socket error event agent.freeSockets[socketKey][0].emit('error', new Error('mock read ECONNRESET')); setTimeout(() => { // error socket should be destroy and remove assert(Object.keys(agent.freeSockets).length === 0); done(); }, 10); }, 10); }); }); res.resume(); }); req.on('error', done); }); }); describe('options.socketActiveTTL', () => { it('should expire on free socket timeout when it is out of ttl', done => { const agent = new HttpAgent({ keepAlive: true, maxSockets: 5, maxFreeSockets: 5, timeout: 30000, freeSocketKeepAliveTimeout: 5000, socketActiveTTL: 100, }); const req1 = http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { const socket1 = req1.socket; const firstCreatedTime = socket1[SOCKET_CREATED_TIME]; assert(firstCreatedTime && typeof firstCreatedTime === 'number'); setTimeout(() => { const req2 = http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(req2.socket !== socket1); const currentCreatedTime = req2.socket[SOCKET_CREATED_TIME]; assert(currentCreatedTime && typeof currentCreatedTime === 'number'); assert(firstCreatedTime < currentCreatedTime); done(); }); }); }, 200); }); }); }); it('should expire on socket reuse detect when it is out of ttl', done => { const agent = new HttpAgent({ keepAlive: true, socketActiveTTL: 10, }); const req1 = http.get({ agent, port, path: '/?timeout=20', }, res => { const socket1 = req1.socket; const firstCreatedTime = socket1[SOCKET_CREATED_TIME]; assert(firstCreatedTime && typeof firstCreatedTime === 'number'); assert(res.statusCode === 200); res.resume(); res.on('end', () => { setImmediate(() => { const req2 = http.get({ agent, port, path: '/', }, res => { // not the same socket assert(req2.socket[SOCKET_NAME] !== socket1[SOCKET_NAME]); const currentCreatedTime = req2.socket[SOCKET_CREATED_TIME]; assert(currentCreatedTime && typeof currentCreatedTime === 'number'); assert(firstCreatedTime < currentCreatedTime); assert(res.statusCode === 200); res.resume(); res.on('end', done); }); }); }); }); }); it('should not expire active socket when it is in ttl', done => { const agent = new HttpAgent({ socketActiveTTL: 1000, }); const req1 = http.get({ agent, port, path: '/', }, res => { const socket1 = req1.socket; const firstCreatedTime = socket1[SOCKET_CREATED_TIME]; assert(firstCreatedTime && typeof firstCreatedTime === 'number'); assert(res.statusCode === 200); res.resume(); res.on('end', () => { setTimeout(function() { const timeout = socket1.timeout || socket1._idleTimeout; assert(timeout <= 1000); const req2 = http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(req2.socket[SOCKET_NAME] === socket1[SOCKET_NAME]); const currentCreatedTime = req2.socket[SOCKET_CREATED_TIME]; assert(currentCreatedTime && typeof currentCreatedTime === 'number'); assert(firstCreatedTime === currentCreatedTime); done(); }); }); }, 100); }); }); }); it('should TTL diff > freeSocketTimeout', done => { const agent = new HttpAgent({ freeSocketTimeout: 500, socketActiveTTL: 1000, }); const req1 = http.get({ agent, port, path: '/', }, res => { const socket1 = req1.socket; const firstCreatedTime = socket1[SOCKET_CREATED_TIME]; assert(firstCreatedTime && typeof firstCreatedTime === 'number'); assert(res.statusCode === 200); res.resume(); res.on('end', () => { setTimeout(function() { const timeout = socket1.timeout || socket1._idleTimeout; assert(timeout === 500); const req2 = http.get({ agent, port, path: '/', }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(req2.socket[SOCKET_NAME] === socket1[SOCKET_NAME]); const currentCreatedTime = req2.socket[SOCKET_CREATED_TIME]; assert(currentCreatedTime && typeof currentCreatedTime === 'number'); assert(firstCreatedTime === currentCreatedTime); done(); }); }); }, 100); }); }); }); }); }); ================================================ FILE: test/https_agent.test.js ================================================ 'use strict'; const https = require('https'); const urlparse = require('url').parse; const fs = require('fs'); const assert = require('assert'); const HttpsAgent = require('..').HttpsAgent; describe('test/https_agent.test.js', () => { let app = null; let port = null; const agentkeepalive = new HttpsAgent({ freeSocketTimeout: 1000, timeout: 2000, maxSockets: 5, maxFreeSockets: 5, }); before(done => { app = https.createServer({ key: fs.readFileSync(__dirname + '/fixtures/agenttest-key.pem'), cert: fs.readFileSync(__dirname + '/fixtures/agenttest-cert.pem'), }, (req, res) => { req.resume(); if (req.url === '/error') { res.destroy(); return; } else if (req.url === '/hang') { console.log('[new https request] %s %s', req.method, req.url); // Wait forever. return; } const info = urlparse(req.url, true); if (info.query.timeout) { console.log('[new https request] %s %s, query %j', req.method, req.url, info.query); setTimeout(() => { res.writeHeader(200, { 'Content-Length': `${info.query.timeout.length}`, }); res.end(info.query.timeout); }, parseInt(info.query.timeout)); return; } res.end(JSON.stringify({ info, url: req.url, headers: req.headers, remotePort: req.socket.remotePort, })); }); app.listen(0, () => { port = app.address().port; done(); }); }); after(done => { setTimeout(done, 1500); }); it('should GET / success with 200 status', done => { https.get({ agent: agentkeepalive, port, path: '/', ca: fs.readFileSync(__dirname + '/fixtures/ca.pem'), rejectUnauthorized: false, }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { assert(Object.keys(agentkeepalive.sockets).length === 1); assert(Object.keys(agentkeepalive.freeSockets).length === 0); setImmediate(() => { assert(Object.keys(agentkeepalive.sockets).length === 0); assert(Object.keys(agentkeepalive.freeSockets).length === 1); done(); }); }); }); assert(Object.keys(agentkeepalive.sockets).length === 1); assert(Object.keys(agentkeepalive.freeSockets).length === 0); }); it('should req handle custom timeout error', done => { const req = https.get({ agent: agentkeepalive, port, path: '/?timeout=100', ca: fs.readFileSync(__dirname + '/fixtures/ca.pem'), timeout: 50, rejectUnauthorized: false, }, res => { console.log(res.statusCode, res.headers); res.resume(); res.on('end', () => { done(new Error('should not run this')); }); }).on('error', err => { assert(err); assert(err.message === 'socket hang up'); done(); }); // node 8 don't support options.timeout on http.get if (process.version.startsWith('v8.')) { req.setTimeout(50); } req.on('timeout', () => { req.abort(); }); }); it('should agent handle default timeout error [bugfix for node 8, 10]', done => { const agent = new HttpsAgent({ freeSocketTimeout: 1000, timeout: 50, maxSockets: 5, maxFreeSockets: 5, rejectUnauthorized: false, }); https.get({ agent, port, path: '/?timeout=100', ca: fs.readFileSync(__dirname + '/fixtures/ca.pem'), }, res => { console.log(res.statusCode, res.headers); res.resume(); res.on('end', () => { done(new Error('should not run this')); }); }).on('error', err => { assert(err); assert(err.message === 'Socket timeout'); done(); }); }); it('should don\'t set timeout on options.timeout = 0', done => { const agent = new HttpsAgent({ freeSocketTimeout: 1000, timeout: 0, maxSockets: 5, maxFreeSockets: 5, rejectUnauthorized: false, }); https.get({ agent, port, path: '/', ca: fs.readFileSync(__dirname + '/fixtures/ca.pem'), }, res => { res.resume(); res.on('end', done); }); }); it('should free socket timeout', done => { https.get({ agent: agentkeepalive, port, path: '/', ca: fs.readFileSync(__dirname + '/fixtures/ca.pem'), rejectUnauthorized: false, }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { process.nextTick(() => { assert(Object.keys(agentkeepalive.sockets).length === 0); assert(Object.keys(agentkeepalive.freeSockets).length === 1); // wait for timeout setTimeout(() => { assert(Object.keys(agentkeepalive.sockets).length === 0); assert(Object.keys(agentkeepalive.freeSockets).length === 0); done(); }, 1500); }); }); }); assert(Object.keys(agentkeepalive.sockets).length === 1); }); it('should GET / and /foo use the same socket', done => { const options = { port, path: '/', agent: agentkeepalive, rejectUnauthorized: false, }; let remotePort = null; https.get(options, res => { assert(res.statusCode === 200); let data = null; res.on('data', chunk => { data = JSON.parse(chunk); }); res.on('end', () => { assert(data.remotePort > 0); assert(data.url === '/'); remotePort = data.remotePort; // request again options.path = '/foo'; process.nextTick(() => { https.get(options, res => { assert(res.statusCode === 200); let data = null; res.on('data', chunk => { data = JSON.parse(chunk); }); res.on('end', () => { assert(data.remotePort === remotePort); assert(data.url === '/foo'); process.nextTick(() => { assert(Object.keys(agentkeepalive.sockets).length === 0); assert(Object.keys(agentkeepalive.freeSockets).length === 1); done(); }); }); }); }); }); }); }); describe('request timeout > agent timeout', () => { it('should use request timeout', done => { const agent = new HttpsAgent({ keepAlive: true, timeout: 2000, }); const req = https.get({ agent, port, path: '/?timeout=20000', timeout: 2500, rejectUnauthorized: false, ca: fs.readFileSync(__dirname + '/fixtures/ca.pem'), }, res => { console.error(res.statusCode, res.headers); assert.fail('should not get res here'); }); let isTimeout = false; req.on('timeout', () => { isTimeout = true; req.abort(); }); req.on('error', err => { assert(isTimeout); assert(err); assert(err.message === 'socket hang up'); assert(err.code === 'ECONNRESET'); done(); }); }); }); }); ================================================ FILE: test/server_timeout.test.js ================================================ 'use strict'; const assert = require('assert'); const http = require('http'); const HttpAgent = require('..').HttpAgent; describe('test/server_timeout.test.js', () => { let port; let server; let timer; before(done => { server = http.createServer((req, res) => { if (server.keepAliveTimeout) { res.setHeader('Keep-Alive', `timeout=${parseInt(server.keepAliveTimeout / 1000)}`); } res.end('Hello World, ' + req.connection.remotePort); }); server.on('clientError', (err, socket) => { socket.end('HTTP/1.1 400 Bad Request\r\n\r\n'); }); server.keepAliveTimeout = 1000; server.listen(0, err => { port = server.address().port; done(err); }); }); after(() => { clearInterval(timer); }); it('should handle Keep-Alive header and not throw reset error', done => { const keepaliveAgent = new HttpAgent({ keepAlive: true, }); let count = 0; function request() { count++; const req = http.request({ method: 'GET', port, path: '/', agent: keepaliveAgent, }, res => { assert(res.statusCode === 200); const chunks = []; res.on('data', data => { chunks.push(data); }); res.on('end', () => { const text = Buffer.concat(chunks).toString(); console.log('[%s] status: %s, text: %s, headers: %j', count, text, res.statusCode, res.headers); assert(res.headers.connection === 'keep-alive'); assert(res.headers['keep-alive'] === 'timeout=1'); const m = /^timeout=(\d+?)/.exec(res.headers['keep-alive']); if (m) { const keepAliveTimeout = parseInt(m[1]) * 1000 - 500; if (keepAliveTimeout > 0) { req.socket.freeSocketKeepAliveTimeout = keepAliveTimeout; } } if (count > 5) { done(); } }); }); req.on('error', err => { console.error('[%s] error: %s', count, err); done(err); }); req.end(); } timer = setInterval(request, server.keepAliveTimeout); request(); }); }); ================================================ FILE: test/test-ECONNRESET.test.js ================================================ 'use strict'; const assert = require('assert'); const http = require('http'); const HttpAgent = require('..').HttpAgent; const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); // https://medium.com/ssense-tech/reduce-networking-errors-in-nodejs-23b4eb9f2d83 describe('test/test-ECONNRESET.test.js', () => { let port; let server; let timer; before(done => { server = http.createServer((req, res) => { res.end('Hello World'); }); server.keepAliveTimeout = 1000; server.listen(0, err => { port = server.address().port; done(err); }); }); after(() => { clearInterval(timer); }); it('should resolved socket hang up and ECONNRESET errors', done => { const keepaliveAgent = new HttpAgent({ keepAlive: true, freeSocketTimeout: 900, }); function request() { return new Promise((resolve, reject) => { const req = http.request({ method: 'GET', port, path: '/', agent: keepaliveAgent, }, res => { const chunks = []; res.on('data', data => { chunks.push(data); }); res.on('end', () => { const text = Buffer.concat(chunks).toString(); resolve(text); }); }); req.on('error', err => { reject(err); }); req.end(); }); } async function startSendingRequests() { let successes = 0; const failures = {}; for (let i = 0; i < 10; i++) { await wait(999); try { await request(); successes++; } catch (e) { failures[e.message] = (failures[e.message] || 0) + 1; } } return { successes, failures }; } startSendingRequests().then(({ successes, failures }) => { assert(Object.keys(failures).length === 0); assert(successes === 10); done(); }); }); }); ================================================ FILE: test/test-http-agent-maxsockets-regress-4050.test.js ================================================ // http: remove excess calls to removeSocket // https://github.com/nodejs/node/commit/6e11e220814e469cbbbe91b895362f6f11311c08 'use strict'; const assert = require('assert'); const http = require('http'); describe('test/test-http-agent-maxsockets-regress-4050.test.js', () => { it('should keep active sockets <= MAX_SOCKETS when all requests abort', done => { const MAX_SOCKETS = 2; const agent = new http.Agent({ keepAlive: true, keepAliveMsecs: 1000, maxSockets: MAX_SOCKETS, maxFreeSockets: 2, }); const server = http.createServer((req, res) => { res.end('hello world'); }); let port; const num_requests = 6; let finished = 0; function get(path) { const req = http.get({ host: 'localhost', port, agent, path, }, () => { req.abort(); const sockets = agent.sockets[Object.keys(agent.sockets)[0]]; assert(sockets.length <= MAX_SOCKETS); if (++finished === num_requests) { server.close(); done(); } }); } server.listen(0, () => { port = server.address().port; for (let i = 0; i < num_requests; i++) { get('/' + i); } }); }); }); ================================================ FILE: test/test-https-agent-session-eviction.test.js ================================================ // https://github.com/indutny/io.js/blob/b9b79fdbed704cb647700d799b94f15e88124763/test/parallel/test-https-agent-session-eviction.js 'use strict'; const https = require('https'); const assert = require('assert'); const fs = require('fs'); const constants = require('constants'); const HttpsAgent = require('..').HttpsAgent; describe('test/test-https-agent-session-eviction.test.js', () => { let port; let server; const httpsAgent = new HttpsAgent({ keepAlive: true, }); const options = { key: fs.readFileSync(__dirname + '/fixtures/agenttest-key.pem'), cert: fs.readFileSync(__dirname + '/fixtures/agenttest-cert.pem'), secureOptions: constants.SSL_OP_NO_TICKET, }; before(done => { // Create TLS1.2 server server = https.createServer(options, (req, res) => { res.end('ohai'); }); server.listen(0, () => { port = server.address().port; done(); }); }); it('should evict cached sessions on error', done => { get(); // Do request and let agent cache the session function get() { const req = https.request({ port, rejectUnauthorized: false, agent: httpsAgent, }, res => { console.log('first request done, %j', res.headers); res.resume(); res.on('end', () => { process.nextTick(() => { const name = Object.keys(httpsAgent.freeSockets); const socket = httpsAgent.freeSockets[name][0]; socket.on('close', err => { if (socket.destroyed) return; assert.equal(err.message, 'mock close error'); socket.destroy(); done(); }); socket.emit('close', new Error('mock close error')); }); }); }); req.end(); } }); }); ================================================ FILE: test/test-https-ipv6.test.js ================================================ 'use strict'; const https = require('https'); const assert = require('assert'); const fs = require('fs'); const constants = require('constants'); const HttpsAgent = require('..').HttpsAgent; const os = require('os'); function isIPv6Available() { const networkInterfaces = os.networkInterfaces(); return !!Object.keys(networkInterfaces).find(ifName => { const addresses = networkInterfaces[ifName]; return !!addresses.find(addr => addr.family === 'IPv6'); }); } describe('test/test-ipv6.test.js', () => { let port; let server; const httpsAgent = new HttpsAgent({ keepAlive: true, }); const options = { key: fs.readFileSync(__dirname + '/fixtures/agenttest-key.pem'), cert: fs.readFileSync(__dirname + '/fixtures/agenttest-cert.pem'), secureOptions: constants.SSL_OP_NO_TICKET, }; before(function(done) { if (!isIPv6Available()) { this.skip(); } // Create TLS1.2 server server = https.createServer(options, (req, res) => { res.end('ohai'); }); server.listen(0, () => { port = server.address().port; done(); }); }); it('should GET / success with 200 status from ::1', function(done) { const m = process.version.match(/^v(\d+)\.(\d+)/); const major = parseInt(m[1]); const minor = parseInt(m[2]); if (major < 8 || (major === 8 && minor < 10) || (major === 9 && minor < 1)) { // This only works in node-versions with the fix for // https://github.com/nodejs/node/issues/14736 included. this.skip(); } https.get({ agent: httpsAgent, hostname: '::1', port, path: '/', ca: fs.readFileSync(__dirname + '/fixtures/ca.pem'), rejectUnauthorized: false, }, res => { assert(res.statusCode === 200); res.resume(); res.on('end', () => { process.nextTick(() => { assert(Object.keys(httpsAgent.sockets).length === 0); assert(Object.keys(httpsAgent.freeSockets).length === 1); done(); }); }); }); assert(Object.keys(httpsAgent.sockets).length === 1); }); it('should not crash with invalid host-header', done => { https.get({ agent: httpsAgent, hostname: '::1', port, path: '/', headers: { host: '[::1:80', }, rejectUnauthorized: false, }, () => { done(); }); }); }); ================================================ FILE: test/ts.test.js ================================================ 'use strict'; const path = require('path'); const coffee = require('coffee'); describe('test/ts.test.js', () => { it('should compile ts and run without error', async () => { await coffee.fork( require.resolve('typescript/bin/tsc'), [ '-p', path.resolve(__dirname, './fixtures/ts/tsconfig.json') ] ) .debug() .expect('code', 0) .end(); await coffee.fork(path.resolve(__dirname, './fixtures/ts/index.js')) // .debug() .expect('code', 0) .end(); }); });