Full Code of node-modules/agentkeepalive for AI

master 422e9fc35aaa cached
41 files
176.7 KB
52.3k tokens
42 symbols
1 requests
Download .txt
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
================================================
<!--
Thank you for your pull request. Please review below requirements.
Bug fixes and new features should include tests and possibly benchmarks.
Contributors guide: https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md

感谢您贡献代码。请确认下列 checklist 的完成情况。
Bug 修复和新功能必须包含测试,必要时请附上性能测试。
Contributors guide: https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md
-->

##### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [ ] `npm test` passes
- [ ] tests and/or benchmarks are included
- [ ] documentation is changed or added
- [ ] commit message follows commit guidelines

##### Affected core subsystem(s)
<!-- Provide affected core subsystem(s). -->


##### Description of change
<!-- Provide a description of the change below this comment. -->


================================================
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 <<smoke@users.noreply.github.com>>)

4.5.0 / 2023-08-06
==================

**others**
  * [[`1e5e312`](http://github.com/node-modules/agentkeepalive/commit/1e5e312f36491243372dbfee0dd47607e7b3d94a)] - deps: remove debug and depd (#114) (fengmk2 <<fengmk2@gmail.com>>)

4.4.0 / 2023-08-05
==================

**features**
  * [[`c7c1e93`](http://github.com/node-modules/agentkeepalive/commit/c7c1e93beba7310d7c2cc9647dd211a686d21cac)] - feat: return socket from createConnection (#113) (Nabeel Bukhari <<nabeel.bukhari@hotmail.com>>)

4.3.0 / 2023-03-06
==================

**others**
  * [[`6f9852b`](http://github.com/node-modules/agentkeepalive/commit/6f9852bf6f674846103e403fd9c84e92fc24f820)] - deps: depd@2.0.0 (#109) (Brian DeHamer <<bdehamer@github.com>>)
  * [[`fd4bd9b`](http://github.com/node-modules/agentkeepalive/commit/fd4bd9b0e0f051de3cb49559d1b0d534a0ded18c)] - test: use npm install (#110) (fengmk2 <<fengmk2@gmail.com>>)
  * [[`d52822c`](http://github.com/node-modules/agentkeepalive/commit/d52822c1243c689df1c8232a3bb14139cf87fae5)] - chore: update contributors (fengmk2 <<fengmk2@gmail.com>>)

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 <<lemoine.benoit@gmail.com>>)

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 <<fengmk2@gmail.com>>)

**others**
  * [[`bc2a1ce`](http://github.com/node-modules/agentkeepalive/commit/bc2a1cea0884b4d18b0d244bf00006d9107963df)] - doc(readme): making `timeout`'s default clear (#100) (Aaron <<aaronarinder@gmail.com>>)

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 <<sbekkhus91@gmail.com>>)

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 <<killa123@126.com>>)

4.1.2 / 2020-04-25
==================

**fixes**
  * [[`de66b02`](http://github.com/node-modules/agentkeepalive/commit/de66b0206d064a97129c2c31bcdabd4d64557b91)] - fix: detect http request timeout handler (#88) (fengmk2 <<fengmk2@gmail.com>>)

4.1.1 / 2020-04-25
==================

**fixes**
  * [[`bbd20c0`](http://github.com/node-modules/agentkeepalive/commit/bbd20c03b8cf7dfb00b3aad1ada26d4ab90d2d6e)] - fix: definition error (#87) (吖猩 <<whxaxes@qq.com>>)

**others**
  * [[`3b01699`](http://github.com/node-modules/agentkeepalive/commit/3b01699b8e90022d5f56898dd709e4fe7ee7cdaa)] - test: run test on node 12 (#84) (Igor Savin <<iselwin@gmail.com>>)

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 <<starkwang@126.com>>)

**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 <<AndrewLeedham@outlook.com>>)

4.0.1 / 2019-02-19
==================

**fixes**
  * [[`bad1ac0`](http://github.com/node-modules/agentkeepalive/commit/bad1ac0e710fbc486717e14e68c59266d35df6a8)] - fix: HttpsAgent Type Definition (#71) (#72) (Andrew Leedham <<AndrewLeedham@outlook.com>>)
  * [[`f48a4a7`](http://github.com/node-modules/agentkeepalive/commit/f48a4a701ea6fbe43781c91e1c0aaad6e328ac7f)] - fix: export interface (#70) (Vinay <<vinaybedre@gmail.com>>)

**others**
  * [[`9124343`](http://github.com/node-modules/agentkeepalive/commit/91243437cfdd324cb97f39dee76746d5e5f4cd72)] - chore: add agent.options.keepAlive instead agent.keepAlive (fengmk2 <<fengmk2@gmail.com>>)
  * [[`d177d40`](http://github.com/node-modules/agentkeepalive/commit/d177d40422fe7296990b4e270cf498e3f33c18fa)] - test: add request timeout bigger than agent timeout cases (fengmk2 <<fengmk2@gmail.com>>)

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 <<fengmk2@gmail.com>>)

**others**
  * [[`498c8f1`](http://github.com/node-modules/agentkeepalive/commit/498c8f13cf76600d3dd6e1c91cdf2d8292355dff)] - chore: move LICENSE from readme to file (fengmk2 <<fengmk2@gmail.com>>)
  * [[`4f39894`](http://github.com/node-modules/agentkeepalive/commit/4f398942ba2f90cf4501239e56ac4e6344931a01)] - bugfix: support agent.options.timeout on https agent (fengmk2 <<fengmk2@gmail.com>>)

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 <<cixel@users.noreply.github.com>>)

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 <<zhuanghengfei@gmail.com>>)

3.5.0 / 2018-07-31
==================

**features**
  * [[`16f5aea`](http://github.com/node-modules/agentkeepalive/commit/16f5aeadfda57f1c602652f1472a63cc83cd05bf)] - feat: add typing define. (#65) (Henry Zhuang <<zhuanghengfei@gmail.com>>)

**others**
  * [[`28fa062`](http://github.com/node-modules/agentkeepalive/commit/28fa06246fb5103f88ebeeb8563757a9078b8157)] - docs: add "per host" to description of maxFreeSockets (tony-gutierrez <<tony.gutierrez@bluefletch.com>>)
  * [[`7df2577`](http://github.com/node-modules/agentkeepalive/commit/7df25774f00a1031ca4daad2878a17e0539072a2)] - test: run test on node 10 (#63) (fengmk2 <<fengmk2@gmail.com>>)

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 <<u376@m1.holmlund.se>>)

**others**
  * [[`55a7a5c`](http://github.com/node-modules/agentkeepalive/commit/55a7a5cd33e97f9a8370083dcb041c5552f10ac9)] - test: stop timer after test end (fengmk2 <<fengmk2@gmail.com>>)

3.4.0 / 2018-02-27
==================

**features**
  * [[`bc7cadb`](http://github.com/node-modules/agentkeepalive/commit/bc7cadb30ecd2071e2b341ac53ae1a2b8155c43d)] - feat: use socket custom freeSocketKeepAliveTimeout first (#59) (fengmk2 <<fengmk2@gmail.com>>)

**others**
  * [[`138eda8`](http://github.com/node-modules/agentkeepalive/commit/138eda81e10b632aaa87bea0cb66d8667124c4e8)] - doc: fix `keepAliveMsecs` params description (#55) (Hongcai Deng <<admin@dhchouse.com>>)

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 <fengmk2@gmail.com>

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)

<!-- GITCONTRIBUTOR_START -->

## Contributors

|[<img src="https://avatars.githubusercontent.com/u/156269?v=4" width="100px;"/><br/><sub><b>fengmk2</b></sub>](https://github.com/fengmk2)<br/>|[<img src="https://avatars.githubusercontent.com/u/985607?v=4" width="100px;"/><br/><sub><b>dead-horse</b></sub>](https://github.com/dead-horse)<br/>|[<img src="https://avatars.githubusercontent.com/u/5557458?v=4" width="100px;"/><br/><sub><b>AndrewLeedham</b></sub>](https://github.com/AndrewLeedham)<br/>|[<img src="https://avatars.githubusercontent.com/u/5243774?v=4" width="100px;"/><br/><sub><b>ngot</b></sub>](https://github.com/ngot)<br/>|[<img src="https://avatars.githubusercontent.com/u/25919630?v=4" width="100px;"/><br/><sub><b>wrynearson</b></sub>](https://github.com/wrynearson)<br/>|[<img src="https://avatars.githubusercontent.com/u/26738844?v=4" width="100px;"/><br/><sub><b>aaronArinder</b></sub>](https://github.com/aaronArinder)<br/>|
| :---: | :---: | :---: | :---: | :---: | :---: |
|[<img src="https://avatars.githubusercontent.com/u/10976983?v=4" width="100px;"/><br/><sub><b>alexpenev-s</b></sub>](https://github.com/alexpenev-s)<br/>|[<img src="https://avatars.githubusercontent.com/u/959726?v=4" width="100px;"/><br/><sub><b>blemoine</b></sub>](https://github.com/blemoine)<br/>|[<img src="https://avatars.githubusercontent.com/u/398027?v=4" width="100px;"/><br/><sub><b>bdehamer</b></sub>](https://github.com/bdehamer)<br/>|[<img src="https://avatars.githubusercontent.com/u/4985201?v=4" width="100px;"/><br/><sub><b>DylanPiercey</b></sub>](https://github.com/DylanPiercey)<br/>|[<img src="https://avatars.githubusercontent.com/u/3770250?v=4" width="100px;"/><br/><sub><b>cixel</b></sub>](https://github.com/cixel)<br/>|[<img src="https://avatars.githubusercontent.com/u/2883231?v=4" width="100px;"/><br/><sub><b>HerringtonDarkholme</b></sub>](https://github.com/HerringtonDarkholme)<br/>|
|[<img src="https://avatars.githubusercontent.com/u/1433247?v=4" width="100px;"/><br/><sub><b>denghongcai</b></sub>](https://github.com/denghongcai)<br/>|[<img src="https://avatars.githubusercontent.com/u/1847934?v=4" width="100px;"/><br/><sub><b>kibertoad</b></sub>](https://github.com/kibertoad)<br/>|[<img src="https://avatars.githubusercontent.com/u/5236150?v=4" width="100px;"/><br/><sub><b>pangorgo</b></sub>](https://github.com/pangorgo)<br/>|[<img src="https://avatars.githubusercontent.com/u/588898?v=4" width="100px;"/><br/><sub><b>mattiash</b></sub>](https://github.com/mattiash)<br/>|[<img src="https://avatars.githubusercontent.com/u/182440?v=4" width="100px;"/><br/><sub><b>nabeelbukhari</b></sub>](https://github.com/nabeelbukhari)<br/>|[<img src="https://avatars.githubusercontent.com/u/1411117?v=4" width="100px;"/><br/><sub><b>pmalouin</b></sub>](https://github.com/pmalouin)<br/>|
[<img src="https://avatars.githubusercontent.com/u/1404810?v=4" width="100px;"/><br/><sub><b>SimenB</b></sub>](https://github.com/SimenB)<br/>|[<img src="https://avatars.githubusercontent.com/u/2630384?v=4" width="100px;"/><br/><sub><b>vinaybedre</b></sub>](https://github.com/vinaybedre)<br/>|[<img src="https://avatars.githubusercontent.com/u/10933333?v=4" width="100px;"/><br/><sub><b>starkwang</b></sub>](https://github.com/starkwang)<br/>|[<img src="https://avatars.githubusercontent.com/u/6897780?v=4" width="100px;"/><br/><sub><b>killagu</b></sub>](https://github.com/killagu)<br/>|[<img src="https://avatars.githubusercontent.com/u/15345331?v=4" width="100px;"/><br/><sub><b>tony-gutierrez</b></sub>](https://github.com/tony-gutierrez)<br/>|[<img src="https://avatars.githubusercontent.com/u/5856440?v=4" width="100px;"/><br/><sub><b>whxaxes</b></sub>](https://github.com/whxaxes)<br/>

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`.

<!-- GITCONTRIBUTOR_END -->


================================================
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 <fengmk2@gmail.com> (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();
  });
});
Download .txt
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
Download .txt
SYMBOL INDEX (42 symbols across 12 files)

FILE: benchmark/echo.js
  function get (line 19) | function get() {
  function showAgentDetail (line 39) | function showAgentDetail() {

FILE: browser.js
  function noop (line 5) | function noop () {}

FILE: example/http_agent.js
  function get (line 17) | function get() {

FILE: index.d.ts
  type PlainObject (line 5) | interface PlainObject {
  class _HttpAgent (line 9) | class _HttpAgent extends http.Agent {
  type Constants (line 17) | interface Constants {
  class AgentKeepAlive (line 31) | class AgentKeepAlive extends _HttpAgent {}
  type AgentStatus (line 34) | interface AgentStatus {
  type CommonHttpOption (line 46) | interface CommonHttpOption {
  type HttpOptions (line 54) | interface HttpOptions extends http.AgentOptions, CommonHttpOption { }
  type HttpsOptions (line 55) | interface HttpsOptions extends https.AgentOptions, CommonHttpOption { }
  class HttpAgent (line 57) | class HttpAgent extends _HttpAgent {}
  class HttpsAgent (line 58) | class HttpsAgent extends https.Agent {

FILE: lib/agent.js
  function deprecate (line 29) | function deprecate(message) {
  class Agent (line 33) | class Agent extends OriginalAgent {
    method constructor (line 34) | constructor(options) {
    method freeSocketKeepAliveTimeout (line 104) | get freeSocketKeepAliveTimeout() {
    method timeout (line 109) | get timeout() {
    method socketActiveTTL (line 114) | get socketActiveTTL() {
    method calcSocketTimeout (line 119) | calcSocketTimeout(socket) {
    method keepSocketAlive (line 148) | keepSocketAlive(socket) {
    method reuseSocket (line 169) | reuseSocket(...args) {
    method [CREATE_ID] (line 187) | [CREATE_ID]() {
    method [INIT_SOCKET] (line 193) | [INIT_SOCKET](socket, options) {
    method createConnection (line 220) | createConnection(options, oncreate) {
    method statusChanged (line 239) | get statusChanged() {
    method getCurrentStatus (line 257) | getCurrentStatus() {
  function getSocketTimeout (line 274) | function getSocketTimeout(socket) {
  function installListeners (line 278) | function installListeners(agent, socket, options) {
  function inspect (line 396) | function inspect(obj) {

FILE: lib/https_agent.js
  class HttpsAgent (line 10) | class HttpsAgent extends HttpAgent {
    method constructor (line 11) | constructor(options) {
    method createConnection (line 28) | createConnection(options, oncreate) {

FILE: test/http_agent.test.js
  function request (line 294) | function request(callback) {
  function next (line 547) | function next() {

FILE: test/server_timeout.test.js
  function request (line 38) | function request() {

FILE: test/test-ECONNRESET.test.js
  function request (line 35) | function request() {
  function startSendingRequests (line 59) | async function startSendingRequests() {

FILE: test/test-http-agent-maxsockets-regress-4050.test.js
  function get (line 28) | function get(path) {

FILE: test/test-https-agent-session-eviction.test.js
  function get (line 37) | function get() {

FILE: test/test-https-ipv6.test.js
  function isIPv6Available (line 10) | function isIPv6Available() {
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (190K chars).
[
  {
    "path": ".eslintignore",
    "chars": 16,
    "preview": "test/fixtures/ts"
  },
  {
    "path": ".eslintrc",
    "chars": 37,
    "preview": "{\n  \"extends\": \"eslint-config-egg\"\n}\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 791,
    "preview": "<!--\nThank you for your pull request. Please review below requirements.\nBug fixes and new features should include tests "
  },
  {
    "path": ".github/workflows/nodejs.yml",
    "chars": 1043,
    "preview": "# This workflow will do a clean install of node dependencies, build the source code and run tests across different versi"
  },
  {
    "path": ".gitignore",
    "chars": 174,
    "preview": "lib-cov\ncoverage.html\n*.seed\n*.log\n*.csv\n*.dat\n*.out\n*.pid\n*.gz\n\npids\nlogs\nresults\n\nnode_modules\nnpm-debug.log\ncoverage/"
  },
  {
    "path": "History.md",
    "chars": 10399,
    "preview": "\n4.6.0 / 2024-12-29\n==================\n\n**features**\n  * [[`7d79ddd`](http://github.com/node-modules/agentkeepalive/comm"
  },
  {
    "path": "LICENSE",
    "chars": 1144,
    "preview": "The MIT License\n\nCopyright(c) node-modules and other contributors.\nCopyright(c) 2012 - 2015 fengmk2 <fengmk2@gmail.com>\n"
  },
  {
    "path": "README.md",
    "chars": 11813,
    "preview": "# agentkeepalive\n\n[![NPM version][npm-image]][npm-url]\n[![Known Vulnerabilities][snyk-image]][snyk-url]\n[![Node.js CI](h"
  },
  {
    "path": "benchmark/echo.js",
    "chars": 1716,
    "preview": "'use strict';\n\nconst HttpAgent = require('../').HttpAgent;\n\nconst agent = new HttpAgent({\n  keepAlive: true,\n  maxSocket"
  },
  {
    "path": "benchmark/proxy.js",
    "chars": 4534,
    "preview": "'use strict';\n\nvar http = require('http');\nvar HttpAgent = require('../').HttpAgent;\n\nvar maxSockets = parseInt(process."
  },
  {
    "path": "benchmark/readme.md",
    "chars": 29735,
    "preview": "# Benchmark result\n\nIntel(R) Core(TM)2 Duo CPU     P8600  @ 2.40GHz\n\nnode@v0.8.9\n\n50 maxSockets, 60 concurrent, 1000 req"
  },
  {
    "path": "benchmark/siegerc",
    "chars": 14347,
    "preview": "# Updated by Siege 3.0.5, August-27-2014\n# Copyright 2000-2013 by Jeffrey Fulmer, et al.\n#\n# Siege configuration file --"
  },
  {
    "path": "benchmark/sleep_server.js",
    "chars": 588,
    "preview": "'use strict';\n\nconst http = require('http');\n\nhttp.createServer((req, res) => {\n  let size = 0;\n  let data = '';\n  req.o"
  },
  {
    "path": "benchmark/start.sh",
    "chars": 925,
    "preview": "#!/bin/bash\n\n# sudo sysctl -w net.inet.ip.portrange.first=12000\n# sudo sysctl -w net.inet.tcp.msl=1000\n# sudo sysctl -w "
  },
  {
    "path": "browser.js",
    "chars": 144,
    "preview": "module.exports = noop;\nmodule.exports.HttpsAgent = noop;\n\n// Noop function for browser since native api's don't use agen"
  },
  {
    "path": "example/http_agent.js",
    "chars": 1333,
    "preview": "'use strict';\n\nconst http = require('http');\nconst HttpAgent = require('..').HttpAgent;\n\nconst http_agent = new HttpAgen"
  },
  {
    "path": "example/https_agent.js",
    "chars": 1183,
    "preview": "'use strict';\n\nconst https = require('https');\nconst HttpsAgent = require('..').HttpsAgent;\n\nconst agent = new HttpsAgen"
  },
  {
    "path": "example/server.js",
    "chars": 210,
    "preview": "'use strict';\n\nconst http = require('http');\n\nhttp.createServer((req, res) => {\n  req.resume();\n  req.on('end', () => {\n"
  },
  {
    "path": "index.d.ts",
    "chars": 2136,
    "preview": "import * as http from 'http';\nimport * as https from 'https';\nimport * as net from 'net';\n\ninterface PlainObject {\n  [ke"
  },
  {
    "path": "index.js",
    "chars": 236,
    "preview": "'use strict';\n\nconst HttpAgent = require('./lib/agent');\nmodule.exports = HttpAgent;\nmodule.exports.HttpAgent = HttpAgen"
  },
  {
    "path": "lib/agent.js",
    "chars": 15296,
    "preview": "'use strict';\n\nconst OriginalAgent = require('http').Agent;\nconst ms = require('humanize-ms');\nconst debug = require('ut"
  },
  {
    "path": "lib/constants.js",
    "chars": 559,
    "preview": "'use strict';\n\nmodule.exports = {\n  // agent\n  CURRENT_ID: Symbol('agentkeepalive#currentId'),\n  CREATE_ID: Symbol('agen"
  },
  {
    "path": "lib/https_agent.js",
    "chars": 1264,
    "preview": "'use strict';\n\nconst OriginalHttpsAgent = require('https').Agent;\nconst HttpAgent = require('./agent');\nconst {\n  INIT_S"
  },
  {
    "path": "package.json",
    "chars": 1324,
    "preview": "{\n  \"name\": \"agentkeepalive\",\n  \"version\": \"4.6.0\",\n  \"description\": \"Missing keepalive http.Agent\",\n  \"main\": \"index.js"
  },
  {
    "path": "test/fixtures/agenttest-cert.pem",
    "chars": 1375,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDyzCCArOgAwIBAgIUWHgqUZfGb/UAhDwy/TTdYkE3CiwwDQYJKoZIhvcNAQEL\nBQAwYTELMAkGA1UEBhMCU0UxEjA"
  },
  {
    "path": "test/fixtures/agenttest-key.pem",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsiKbzm3v7DS8N\nJPCgEIil4Vdt98D0A56HMD+cZqG"
  },
  {
    "path": "test/fixtures/ca.cnf",
    "chars": 628,
    "preview": "[ ca ]\ndefault_ca      = CA_default\n\n[ CA_default ]\nserial = ca-serial\ncrl = ca-crl.pem\ndatabase = ca-database.txt\nname_"
  },
  {
    "path": "test/fixtures/ca.key",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBWx17nSe1zx8/\nOTST6T5TtYn+lg9v4FCE5+qvuke"
  },
  {
    "path": "test/fixtures/ca.pem",
    "chars": 1257,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDczCCAlugAwIBAgIUYxxarpaqtKbdGhWDXUVEv9dGkvIwDQYJKoZIhvcNAQEL\nBQAwYTELMAkGA1UEBhMCU0UxEjA"
  },
  {
    "path": "test/fixtures/genkey.sh",
    "chars": 862,
    "preview": "#!/bin/bash\n# DIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\n\n# Generate ca key and pem\nopenssl req -new -x509 "
  },
  {
    "path": "test/fixtures/server.cnf",
    "chars": 844,
    "preview": "[ req ]\ndefault_bits           = 2048\ndays                   = 9999\ndistinguished_name     = req_distinguished_name\nattr"
  },
  {
    "path": "test/fixtures/ts/index.ts",
    "chars": 1574,
    "preview": "import * as http from 'http';\nimport { constants, HttpAgent, HttpsAgent, HttpOptions, HttpsOptions, AgentStatus } from '"
  },
  {
    "path": "test/fixtures/ts/tsconfig.json",
    "chars": 655,
    "preview": "{\n  \"compileOnSave\": true,\n  \"compilerOptions\": {\n    \"target\": \"es2017\",\n    \"module\": \"commonjs\",\n    \"noImplicitAny\":"
  },
  {
    "path": "test/http_agent.test.js",
    "chars": 52055,
    "preview": "'use strict';\n\nconst assert = require('assert');\nconst http = require('http');\nconst urlparse = require('url').parse;\nco"
  },
  {
    "path": "test/https_agent.test.js",
    "chars": 7203,
    "preview": "'use strict';\n\nconst https = require('https');\nconst urlparse = require('url').parse;\nconst fs = require('fs');\nconst as"
  },
  {
    "path": "test/server_timeout.test.js",
    "chars": 2173,
    "preview": "'use strict';\n\nconst assert = require('assert');\nconst http = require('http');\nconst HttpAgent = require('..').HttpAgent"
  },
  {
    "path": "test/test-ECONNRESET.test.js",
    "chars": 1954,
    "preview": "'use strict';\n\nconst assert = require('assert');\nconst http = require('http');\nconst HttpAgent = require('..').HttpAgent"
  },
  {
    "path": "test/test-http-agent-maxsockets-regress-4050.test.js",
    "chars": 1254,
    "preview": "// http: remove excess calls to removeSocket\n// https://github.com/nodejs/node/commit/6e11e220814e469cbbbe91b895362f6f11"
  },
  {
    "path": "test/test-https-agent-session-eviction.test.js",
    "chars": 1811,
    "preview": "// https://github.com/indutny/io.js/blob/b9b79fdbed704cb647700d799b94f15e88124763/test/parallel/test-https-agent-session"
  },
  {
    "path": "test/test-https-ipv6.test.js",
    "chars": 2391,
    "preview": "'use strict';\n\nconst https = require('https');\nconst assert = require('assert');\nconst fs = require('fs');\nconst constan"
  },
  {
    "path": "test/ts.test.js",
    "chars": 518,
    "preview": "'use strict';\n\nconst path = require('path');\nconst coffee = require('coffee');\n\ndescribe('test/ts.test.js', () => {\n  it"
  }
]

About this extraction

This page contains the full source code of the node-modules/agentkeepalive GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (176.7 KB), approximately 52.3k tokens, and a symbol index with 42 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!