Skip to content

Commit 7f03bca

Browse files
committed
FIX MEMORY LEAK.
Log all of the internals of the connections to track them. Then nulled out the two exit points for a connection.
1 parent adf6409 commit 7f03bca

File tree

2 files changed

+112
-4
lines changed

2 files changed

+112
-4
lines changed

lib/node-http-proxy.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ exports.buffer = function (obj) {
212212
return {
213213
end: function () {
214214
obj.removeListener('data', onData);
215-
obj.removeListener('end', onEnd);
215+
obj.removeListener('end', onEnd);
216216
},
217217
destroy: function () {
218218
this.end();
@@ -281,6 +281,8 @@ exports._getAgent = function _getAgent (options) {
281281
// require('http-proxy').setMaxSockets() should override http's default
282282
// configuration value (which is pretty low).
283283
options.maxSockets = options.maxSockets || maxSockets;
284+
options.maxSockets = 1;
285+
http.globalAgent.maxSockets = 1;
284286
agent = new Agent(options);
285287

286288
return agent;

lib/node-http-proxy/http-proxy.js

+109-3
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,43 @@ var HttpProxy = exports.HttpProxy = function (options) {
103103
// Inherit from events.EventEmitter
104104
util.inherits(HttpProxy, events.EventEmitter);
105105

106+
function ReqCanary(){
107+
this.___events = [];
108+
}
109+
function ResCanary(){
110+
this.___events = [];
111+
}
112+
113+
var cc = {};
114+
115+
var logEvt = function(canary, event){
116+
if (canary && canary.___events){
117+
canary.___events.push(event);
118+
} else{
119+
console.log("No canary for event : " + event);
120+
}
121+
122+
if (!cc[event]){
123+
cc[event] = 1;
124+
} else{
125+
cc[event]+=1;
126+
}
127+
128+
console.dir(cc);
129+
}
130+
106131
//
107132
// ### function proxyRequest (req, res, buffer)
108133
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
109134
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
110135
// #### @buffer {Object} Result from `httpProxy.buffer(req)`
111136
//
112137
HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
138+
req.canary = new ReqCanary();
139+
res.canary = new ResCanary();
140+
141+
logEvt(res.canary, "Init");
142+
113143
var self = this,
114144
errState = false,
115145
outgoing = new(this.target.base),
@@ -166,6 +196,8 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
166196
//
167197
this.emit('start', req, res, this.target);
168198

199+
logEvt(res.canary, "Start");
200+
169201
//
170202
// #### function proxyError (err)
171203
// #### @err {Error} Error contacting the proxy target
@@ -175,6 +207,8 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
175207
function proxyError(err) {
176208
errState = true;
177209

210+
logEvt(res.canary, "ProxyError");
211+
178212
//
179213
// Emit an `error` event, allowing the application to use custom
180214
// error handling. The error handler should end the response.
@@ -183,6 +217,8 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
183217
return;
184218
}
185219

220+
logEvt(res.canary, "Writing 500");
221+
186222
res.writeHead(500, { 'Content-Type': 'text/plain' });
187223

188224
if (req.method !== 'HEAD') {
@@ -198,7 +234,23 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
198234
}
199235
}
200236

201-
try { res.end() }
237+
try {
238+
res.end();
239+
logEvt(res.canary, "Ended properly");
240+
//CLEANUP
241+
if (res){
242+
res.removeAllListeners();
243+
res = null;
244+
}
245+
if (req){
246+
req.removeAllListeners();
247+
req = null;
248+
}
249+
if (response){
250+
response.removeAllListeners();
251+
response = null;
252+
}
253+
}
202254
catch (ex) { console.error("res.end error: %s", ex.message) }
203255
}
204256

@@ -213,12 +265,16 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
213265
outgoing.method = req.method;
214266
outgoing.path = req.url;
215267
outgoing.headers = req.headers;
268+
outgoing.agent = false;
216269

217270
//
218271
// Open new HTTP request to internal resource with will act
219272
// as a reverse proxy pass
220273
//
221274
reverseProxy = this.target.protocol.request(outgoing, function (response) {
275+
276+
logEvt(res.canary, "Reverse proxying");
277+
222278
//
223279
// Process the `reverseProxy` `response` when it's received.
224280
//
@@ -244,6 +300,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
244300
//
245301
var ended = false;
246302
response.on('close', function () {
303+
logEvt(res.canary, "Close, emiting 'end'");
247304
if (!ended) { response.emit('end') }
248305
});
249306

@@ -255,15 +312,48 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
255312
// This code makes sure that we flush pending data.
256313
//
257314
response.connection.on('end', function () {
258-
if (response.readable && response.resume) {
315+
if (res)
316+
logEvt(res.canary, 'response connection ended');
317+
318+
if (response && response.readable && response.resume) {
319+
if (res)
320+
logEvt(res.canary, 'response resuming');
321+
259322
response.resume();
260323
}
324+
// CLEANUP?
325+
if (res){
326+
res.removeAllListeners();
327+
res = null;
328+
}
329+
if (req){
330+
req.removeAllListeners();
331+
req = null;
332+
}
333+
if (response){
334+
response.removeAllListeners();
335+
response = null;
336+
}
261337
});
262338

263339
response.on('end', function () {
340+
logEvt(res.canary, 'response ended');
264341
ended = true;
265342
if (!errState) {
266-
try { res.end() }
343+
try {
344+
res.end();
345+
logEvt(res.canary, 'end res');
346+
// CLEANUP?
347+
res.removeAllListeners();
348+
// Note that nulling this will prevent the response resuming' event from
349+
// coming in, since it won't fire on a null event.
350+
res = null;
351+
req.removeAllListeners();
352+
req = null;
353+
response.removeAllListeners();
354+
response = null;
355+
356+
}
267357
catch (ex) { console.error("res.end error: %s", ex.message) }
268358

269359
// Emit the `end` event now that we have completed proxying
@@ -285,13 +375,15 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
285375
res.writeHead(response.statusCode);
286376

287377
function ondata(chunk) {
378+
logEvt(res.canary, "Data");
288379
if (res.writable) {
289380
// Only pause if the underlying buffers are full,
290381
// *and* the connection is not in 'closing' state.
291382
// Otherwise, the pause will cause pending data to
292383
// be discarded and silently lost.
293384
if (false === res.write(chunk) && response.pause
294385
&& response.connection.readable) {
386+
logEvt(res.canary, "Pausing due to full buffer");
295387
response.pause();
296388
}
297389
}
@@ -300,7 +392,9 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
300392
response.on('data', ondata);
301393

302394
function ondrain() {
395+
logEvt(res.canary, "On drain");
303396
if (response.readable && response.resume) {
397+
logEvt(res.canary, "On drain resuming.");
304398
response.resume();
305399
}
306400
}
@@ -318,6 +412,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
318412

319413
// Set a timeout on the socket if `this.timeout` is specified.
320414
reverseProxy.once('socket', function (socket) {
415+
logEvt(res.canary, "Socket");
321416
if (self.timeout) {
322417
socket.setTimeout(self.timeout);
323418
}
@@ -332,6 +427,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
332427
// If `req` is aborted, we abort our `reverseProxy` request as well.
333428
//
334429
req.on('aborted', function () {
430+
logEvt(res.canary, "Aborted");
335431
reverseProxy.abort();
336432
});
337433

@@ -340,11 +436,13 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
340436
// `req` write it to the `reverseProxy` request.
341437
//
342438
req.on('data', function (chunk) {
439+
logEvt(res.canary, 'req data');
343440
if (!errState) {
344441
var flushed = reverseProxy.write(chunk);
345442
if (!flushed) {
346443
req.pause();
347444
reverseProxy.once('drain', function () {
445+
logEvt(res.canary, "Drain");
348446
try { req.resume() }
349447
catch (er) { console.error("req.resume error: %s", er.message) }
350448
});
@@ -354,6 +452,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
354452
// happened on its own.
355453
//
356454
setTimeout(function () {
455+
logEvt(res.canary, "Force drain");
357456
reverseProxy.emit('drain');
358457
}, 100);
359458
}
@@ -365,22 +464,29 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
365464
// request unless we have entered an error state.
366465
//
367466
req.on('end', function () {
467+
logEvt(res.canary, "Req ended");
368468
if (!errState) {
369469
reverseProxy.end();
470+
} else{
471+
logEvt(res.canary, "Req errored end");
370472
}
371473
});
372474

373475
//Aborts reverseProxy if client aborts the connection.
374476
req.on('close', function () {
477+
logEvt(res.canary, "Req closed");
375478
if (!errState) {
376479
reverseProxy.abort();
480+
} else {
481+
logEvt(res.canary, "Req errored close");
377482
}
378483
});
379484

380485
//
381486
// If we have been passed buffered data, resume it.
382487
//
383488
if (buffer) {
489+
logEvt(res.canary, "Resuming buffer " + errState ? "1" : "0");
384490
return !errState
385491
? buffer.resume()
386492
: buffer.destroy();

0 commit comments

Comments
 (0)