@@ -20,17 +20,131 @@ var http = require('http');
2020var https = require ( 'https' ) ;
2121var util = require ( 'util' ) ;
2222
23- if ( typeof http . Agent . request === 'function' ) {
24- // node >= 0.11, default Agent is keepavlie
25- module . exports = http . Agent ;
26- module . exports . HttpsAgent = https . Agent ;
27- return ;
23+ var debug ;
24+ if ( process . env . NODE_DEBUG && / a g e n t k e e p a l i v e / . test ( process . env . NODE_DEBUG ) ) {
25+ debug = function ( x ) {
26+ console . error . apply ( console , arguments ) ;
27+ } ;
28+ } else {
29+ debug = function ( ) { } ;
2830}
2931
32+ function Agent ( options ) {
33+ options = options || { } ;
34+ options . keepAliveMsecs = options . keepAliveMsecs || options . maxKeepAliveTime ;
35+ http . Agent . call ( this , options ) ;
36+
37+ var self = this ;
38+ // max requests per keepalive socket, default is 0, no limit.
39+ self . maxKeepAliveRequests = parseInt ( options . maxKeepAliveRequests , 10 ) || 0 ;
40+ // max keep alive time, default 60 seconds.
41+ // if set `keepAliveMsecs = 0`, will disable keepalive feature.
42+ self . createSocketCount = 0 ;
43+ self . timeoutSocketCount = 0 ;
44+ self . requestFinishedCount = 0 ;
45+
46+ // override the `free` event listener
47+ self . removeAllListeners ( 'free' ) ;
48+ self . on ( 'free' , function ( socket , options ) {
49+ self . requestFinishedCount ++ ;
50+ socket . _requestCount ++ ;
51+
52+ var name = self . getName ( options ) ;
53+ debug ( 'agent.on(free)' , name ) ;
54+
55+ if ( ! socket . destroyed &&
56+ self . requests [ name ] && self . requests [ name ] . length ) {
57+ self . requests [ name ] . shift ( ) . onSocket ( socket ) ;
58+ if ( self . requests [ name ] . length === 0 ) {
59+ // don't leak
60+ delete self . requests [ name ] ;
61+ }
62+ } else {
63+ // If there are no pending requests, then put it in
64+ // the freeSockets pool, but only if we're allowed to do so.
65+ var req = socket . _httpMessage ;
66+ if ( req &&
67+ req . shouldKeepAlive &&
68+ ! socket . destroyed &&
69+ self . options . keepAlive ) {
70+ var freeSockets = self . freeSockets [ name ] ;
71+ var freeLen = freeSockets ? freeSockets . length : 0 ;
72+ var count = freeLen ;
73+ if ( self . sockets [ name ] )
74+ count += self . sockets [ name ] . length ;
75+
76+ if ( count >= self . maxSockets || freeLen >= self . maxFreeSockets ) {
77+ self . removeSocket ( socket , options ) ;
78+ socket . destroy ( ) ;
79+ } else {
80+ freeSockets = freeSockets || [ ] ;
81+ self . freeSockets [ name ] = freeSockets ;
82+ socket . setKeepAlive ( true , self . keepAliveMsecs ) ;
83+ socket . unref && socket . unref ( ) ;
84+ socket . _httpMessage = null ;
85+ self . removeSocket ( socket , options ) ;
86+ freeSockets . push ( socket ) ;
87+
88+ // Avoid duplicitive timeout events by removing timeout listeners set on
89+ // socket by previous requests. node does not do this normally because it
90+ // assumes sockets are too short-lived for it to matter. It becomes a
91+ // problem when sockets are being reused. Steps are being taken to fix
92+ // this issue upstream in node v0.10.0.
93+ //
94+ // See https://github.com/joyent/node/commit/451ff1540ab536237e8d751d241d7fc3391a4087
95+ if ( self . keepAliveMsecs && socket . _events && Array . isArray ( socket . _events . timeout ) ) {
96+ socket . removeAllListeners ( 'timeout' ) ;
97+ // Restore the socket's setTimeout() that was remove as collateral
98+ // damage.
99+ socket . setTimeout ( self . keepAliveMsecs , socket . _maxKeepAliveTimeout ) ;
100+ }
101+ }
102+ } else {
103+ self . removeSocket ( socket , options ) ;
104+ socket . destroy ( ) ;
105+ }
106+ }
107+ } ) ;
108+ }
30109
31- var Agent = require ( './_http_agent' ) . Agent ;
110+ util . inherits ( Agent , http . Agent ) ;
32111module . exports = Agent ;
33112
113+ Agent . prototype . createSocket = function ( req , options ) {
114+ var self = this ;
115+ var socket = http . Agent . prototype . createSocket . call ( this , req , options ) ;
116+ socket . _requestCount = 0 ;
117+ if ( self . keepAliveMsecs ) {
118+ socket . _maxKeepAliveTimeout = function ( ) {
119+ debug ( 'maxKeepAliveTimeout, socket destroy()' ) ;
120+ socket . destroy ( ) ;
121+ self . timeoutSocketCount ++ ;
122+ } ;
123+ socket . setTimeout ( self . keepAliveMsecs , socket . _maxKeepAliveTimeout ) ;
124+ // Disable Nagle's algorithm: http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/
125+ socket . setNoDelay ( true ) ;
126+ }
127+ this . createSocketCount ++ ;
128+ return socket ;
129+ } ;
130+
131+ Agent . prototype . removeSocket = function ( s , options ) {
132+ http . Agent . prototype . removeSocket . call ( this , s , options ) ;
133+ var name = this . getName ( options ) ;
134+ debug ( 'removeSocket' , name , 'destroyed:' , s . destroyed ) ;
135+
136+ if ( s . destroyed && this . freeSockets [ name ] ) {
137+ var index = this . freeSockets [ name ] . indexOf ( s ) ;
138+ if ( index !== - 1 ) {
139+ this . freeSockets [ name ] . splice ( index , 1 ) ;
140+ if ( this . freeSockets [ name ] . length === 0 ) {
141+ // don't leak
142+ delete this . freeSockets [ name ] ;
143+ }
144+ }
145+ }
146+ } ;
147+
34148function HttpsAgent ( options ) {
35149 Agent . call ( this , options ) ;
36150 this . defaultPort = 443 ;
0 commit comments