diff --git a/web3.js/src/agent-manager.js b/web3.js/src/agent-manager.js new file mode 100644 index 0000000000..2b1f91fc06 --- /dev/null +++ b/web3.js/src/agent-manager.js @@ -0,0 +1,38 @@ +// @flow + +import {Agent} from 'http'; + +export const DESTROY_TIMEOUT_MS = 5000; + +export class AgentManager { + _agent: Agent = AgentManager._newAgent(); + _activeRequests = 0; + _destroyTimeout: TimeoutID | null = null; + + static _newAgent(): Agent { + return new Agent({keepAlive: true, maxSockets: 25}); + } + + requestStart(): Agent { + // $FlowExpectedError - Don't manage agents in the browser + if (process.browser) return; + + this._activeRequests++; + clearTimeout(this._destroyTimeout); + this._destroyTimeout = null; + return this._agent; + } + + requestEnd() { + // $FlowExpectedError - Don't manage agents in the browser + if (process.browser) return; + + this._activeRequests--; + if (this._activeRequests === 0 && this._destroyTimeout === null) { + this._destroyTimeout = setTimeout(() => { + this._agent.destroy(); + this._agent = AgentManager._newAgent(); + }, DESTROY_TIMEOUT_MS); + } + } +} diff --git a/web3.js/src/connection.js b/web3.js/src/connection.js index ba7d878b80..8224fb4221 100644 --- a/web3.js/src/connection.js +++ b/web3.js/src/connection.js @@ -20,6 +20,7 @@ import type {Blockhash} from './blockhash'; import type {FeeCalculator} from './fee-calculator'; import type {Account} from './account'; import type {TransactionSignature} from './transaction'; +import {AgentManager} from './agent-manager'; export const BLOCKHASH_CACHE_TIMEOUT_MS = 30 * 1000; @@ -502,10 +503,13 @@ type ConfirmedBlock = { }; function createRpcRequest(url): RpcRequest { + const agentManager = new AgentManager(); const server = jayson(async (request, callback) => { + const agent = agentManager.requestStart(); const options = { method: 'POST', body: request, + agent, headers: { 'Content-Type': 'application/json', }, @@ -539,6 +543,8 @@ function createRpcRequest(url): RpcRequest { } } catch (err) { callback(err); + } finally { + agentManager.requestEnd(); } }); diff --git a/web3.js/test/agent-manager.test.js b/web3.js/test/agent-manager.test.js new file mode 100644 index 0000000000..244a27eb81 --- /dev/null +++ b/web3.js/test/agent-manager.test.js @@ -0,0 +1,40 @@ +// @flow + +import {AgentManager, DESTROY_TIMEOUT_MS} from '../src/agent-manager'; +import {sleep} from '../src/util/sleep'; + +jest.setTimeout(10 * 1000); + +test('agent manager', async () => { + const manager = new AgentManager(); + const agent = manager._agent; + expect(manager._activeRequests).toBe(0); + expect(manager._destroyTimeout).toBeNull(); + + manager.requestStart(); + + expect(manager._activeRequests).toBe(1); + expect(manager._destroyTimeout).toBeNull(); + + manager.requestEnd(); + + expect(manager._activeRequests).toBe(0); + expect(manager._destroyTimeout).not.toBeNull(); + + manager.requestStart(); + manager.requestStart(); + + expect(manager._activeRequests).toBe(2); + expect(manager._destroyTimeout).toBeNull(); + + manager.requestEnd(); + manager.requestEnd(); + + expect(manager._activeRequests).toBe(0); + expect(manager._destroyTimeout).not.toBeNull(); + expect(manager._agent).toBe(agent); + + await sleep(DESTROY_TIMEOUT_MS); + + expect(manager._agent).not.toBe(agent); +});