@@ -20,17 +20,131 @@ var http = require('http');
20
20
var https = require ( 'https' ) ;
21
21
var util = require ( 'util' ) ;
22
22
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 ( ) { } ;
28
30
}
29
31
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
+ }
30
109
31
- var Agent = require ( './_http_agent' ) . Agent ;
110
+ util . inherits ( Agent , http . Agent ) ;
32
111
module . exports = Agent ;
33
112
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
+
34
148
function HttpsAgent ( options ) {
35
149
Agent . call ( this , options ) ;
36
150
this . defaultPort = 443 ;
0 commit comments