Skip to content

Commit 9f0aeac

Browse files
committed
[api] Object creation is cheap for HttpProxy, so lets take advantage
1 parent 66afb2a commit 9f0aeac

File tree

2 files changed

+115
-99
lines changed

2 files changed

+115
-99
lines changed

demo.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -45,28 +45,28 @@ httpProxy.createServer(9000, 'localhost').listen(8000);
4545
sys.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
4646

4747
/****** http proxy server with latency******/
48-
httpProxy.createServer(function (req, res, proxy){
48+
/*httpProxy.createServer(function (req, res, proxy){
4949
setTimeout(function(){
5050
proxy.proxyRequest(9000, 'localhost', req, res);
5151
}, 200)
5252
}).listen(8001);
53-
sys.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8001 '.yellow + 'with latency'.magenta.underline );
53+
sys.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8001 '.yellow + 'with latency'.magenta.underline );*/
5454

5555
/****** http server with proxyRequest handler and latency******/
56-
http.createServer(function (req, res){
56+
/*http.createServer(function (req, res){
5757
var proxy = new httpProxy.HttpProxy;
5858
proxy.watch(req, res);
5959
6060
setTimeout(function(){
6161
proxy.proxyRequest(9000, 'localhost', req, res);
6262
}, 200);
6363
}).listen(8002);
64-
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta);
64+
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta);*/
6565

6666
/****** regular http server ******/
67-
/*http.createServer(function (req, res){
67+
http.createServer(function (req, res){
6868
res.writeHead(200, {'Content-Type': 'text/plain'});
6969
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
7070
res.end();
7171
}).listen(9000);
72-
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);*/
72+
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);

lib/node-http-proxy.js

+109-93
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
var sys = require('sys'),
2828
http = require('http'),
2929
eyes = require('eyes'),
30+
pool = require('pool'),
3031
events = require('events'),
3132
pool = require('pool'),
3233
min = 0,
@@ -38,9 +39,26 @@ manager.setMinClients(min);
3839
manager.setMaxClients(max);
3940

4041
exports.createServer = function () {
41-
// Initialize the nodeProxy to start proxying requests
42-
var proxy = new (exports.HttpProxy);
43-
return proxy.createServer.apply(proxy, arguments);
42+
var args, callback, port, host;
43+
args = Array.prototype.slice.call(arguments);
44+
callback = typeof args[args.length - 1] === 'function' && args.pop();
45+
if (args[0]) port = args[0];
46+
if (args[1]) host = args[1];
47+
48+
var server = http.createServer(function (req, res){
49+
var proxy = new HttpProxy(req, res);
50+
51+
// If we were passed a callback to process the request
52+
// or response in some way, then call it.
53+
if(callback) {
54+
callback(req, res, proxy);
55+
}
56+
else {
57+
proxy.proxyRequest(port, server);
58+
}
59+
});
60+
61+
return server;
4462
};
4563

4664
exports.setMin = function (value) {
@@ -53,14 +71,15 @@ exports.setMax = function (value) {
5371
manager.setMaxClients(max);
5472
};
5573

56-
exports.HttpProxy = function () {
74+
var HttpProxy = function (req, res) {
5775
this.emitter = new(events.EventEmitter);
5876
this.events = {};
59-
this.listeners = {};
60-
this.collisions = {};
77+
this.req = req;
78+
this.res = res;
79+
this.watch(req);
6180
};
6281

63-
exports.HttpProxy.prototype = {
82+
HttpProxy.prototype = {
6483
toArray: function (obj){
6584
var len = obj.length,
6685
arr = new Array(len);
@@ -70,119 +89,60 @@ exports.HttpProxy.prototype = {
7089
return arr;
7190
},
7291

73-
createServer: function () {
74-
var self = this,
75-
server,
76-
port,
77-
callback;
78-
79-
if (typeof(arguments[0]) === "function") {
80-
callback = arguments[0];
81-
}
82-
else {
83-
port = arguments[0];
84-
server = arguments[1];
85-
}
86-
87-
var proxyServer = http.createServer(function (req, res){
88-
self.watch(req, res);
89-
90-
// If we were passed a callback to process the request
91-
// or response in some way, then call it.
92-
if(callback) {
93-
callback(req, res, self);
94-
}
95-
else {
96-
self.proxyRequest(port, server, req, res);
97-
}
98-
});
99-
100-
return proxyServer;
101-
},
102-
103-
watch: function (req, res) {
92+
watch: function (req) {
93+
this.events = [];
10494
var self = this;
105-
106-
// Create a unique id for this request so
107-
// we can reference it later.
108-
var id = new Date().getTime().toString();
109-
110-
// If we get a request in the same tick, we need to
111-
// append to the id so it stays unique.
112-
if(typeof this.collisions[id] === 'undefined') {
113-
this.collisions[id] = 0;
114-
}
115-
else {
116-
this.collisions[id]++;
117-
id += this.collisions[id];
118-
}
119-
120-
req.id = id;
121-
this.events[req.id] = [];
122-
123-
this.listeners[req.id] = {
124-
onData: function () {
125-
self.events[req.id].push(['data'].concat(self.toArray(arguments)));
126-
},
127-
onEnd: function () {
128-
self.events[req.id].push(['end'].concat(self.toArray(arguments)));
129-
}
95+
96+
this.onData = function () {
97+
self.events.push(['data'].concat(self.toArray(arguments)));
98+
};
99+
this.onEnd = function () {
100+
self.events.push(['end'].concat(self.toArray(arguments)));
130101
};
131102

132-
req.addListener('data', this.listeners[req.id].onData);
133-
req.addListener('end', this.listeners[req.id].onEnd);
134-
103+
req.addListener('data', this.onData);
104+
req.addListener('end', this.onEnd);
135105
},
136106

137-
unwatch: function (req, res) {
138-
req.removeListener('data', this.listeners[req.id].onData);
139-
req.removeListener('end', this.listeners[req.id].onEnd);
107+
unwatch: function (req) {
108+
req.removeListener('data', this.onData);
109+
req.removeListener('end', this.onEnd);
140110

141111
// Rebroadcast any events that have been buffered
142-
while(this.events[req.id].length > 0) {
143-
var args = this.events[req.id].shift();
144-
req.emit.apply(req, args);
145-
}
146-
147-
// Remove the data from the event and listeners hashes
148-
delete this.listeners[req.id];
149-
delete this.events[req.id];
150-
151-
// If this request id is a base time, delete it
152-
if (typeof this.collisions[req.id] !== 'undefined') {
153-
delete this.collisions[req.id];
112+
for (var i = 0, len = this.events.length; i < len; ++i) {
113+
req.emit.apply(req, this.events[i]);
154114
}
155115
},
156116

157-
proxyRequest: function (port, server, req, res) {
117+
proxyRequest: function (port, server) {
158118
// Remark: nodeProxy.body exists solely for testability
159-
this.body = '';
160-
var self = this;
119+
var self = this, req = this.req, res = this.res;
120+
self.body = '';
161121

162122
// Open new HTTP request to internal resource with will act as a reverse proxy pass
163-
var p = manager.getPool(port, server);
164-
eyes.inspect(req.headers);
165-
// Make request to internal server, passing along the method and headers
123+
var p = manager.getPool(port, server);
124+
166125
p.request(req.method, req.url, req.headers, function (reverse_proxy) {
167-
// Add a listener for the connection timeout event
168-
reverse_proxy.connection.addListener('error', function (err) {
126+
// Create an error handler so we can use it temporarily
127+
var error = function (err) {
169128
res.writeHead(200, {'Content-Type': 'text/plain'});
170129

171130
if(req.method !== 'HEAD') {
172131
res.write('An error has occurred: ' + sys.puts(JSON.stringify(err)));
173132
}
174133

175134
res.end();
176-
});
177-
135+
};
136+
// Add a listener for the connection timeout event
137+
reverse_proxy.connection.addListener('error', error);
178138

179139
// Add a listener for the reverse_proxy response event
180140
reverse_proxy.addListener('response', function (response) {
181141
if (response.headers.connection) {
182142
if (req.headers.connection) response.headers.connection = req.headers.connection;
183143
else response.headers.connection = 'close';
184144
}
185-
145+
186146
// Set the response headers of the client response
187147
res.writeHead(response.statusCode, response.headers);
188148

@@ -211,9 +171,65 @@ exports.HttpProxy.prototype = {
211171
// At the end of the client request, we are going to stop the proxied request
212172
req.addListener('end', function () {
213173
reverse_proxy.end();
174+
reverse_proxy.connection.removeListener('error', error);
214175
});
215176

216-
self.unwatch(req, res);
177+
self.unwatch(req);
217178
});
218179
}
219180
};
181+
182+
exports.HttpProxy = HttpProxy;
183+
184+
/*// Create an error handler so we can use it temporarily
185+
var error = function (err) {
186+
res.writeHead(200, {'Content-Type': 'text/plain'});
187+
188+
if(req.method !== 'HEAD') {
189+
res.write('An error has occurred: ' + sys.puts(JSON.stringify(err)));
190+
}
191+
192+
res.end();
193+
};
194+
// Add a listener for the connection timeout event
195+
reverse_proxy.connection.addListener('error', error);
196+
197+
// Add a listener for the reverse_proxy response event
198+
reverse_proxy.addListener('response', function (response) {
199+
if (response.headers.connection) {
200+
if (req.headers.connection) response.headers.connection = req.headers.connection;
201+
else response.headers.connection = 'close';
202+
}
203+
204+
// Set the response headers of the client response
205+
res.writeHead(response.statusCode, response.headers);
206+
207+
// Add event handler for the proxied response in chunks
208+
response.addListener('data', function (chunk) {
209+
if(req.method !== 'HEAD') {
210+
res.write(chunk, 'binary');
211+
self.body += chunk;
212+
}
213+
});
214+
215+
// Add event listener for end of proxied response
216+
response.addListener('end', function () {
217+
// Remark: Emit the end event for testability
218+
self.emitter.emit('end', null, self.body);
219+
220+
res.end();
221+
});
222+
});
223+
224+
// Chunk the client request body as chunks from the proxied request come in
225+
req.addListener('data', function (chunk) {
226+
reverse_proxy.write(chunk, 'binary');
227+
})
228+
229+
// At the end of the client request, we are going to stop the proxied request
230+
req.addListener('end', function () {
231+
reverse_proxy.end();
232+
//reverse_proxy.connection.removeListener('error', error);
233+
});
234+
235+
self.unwatch(req);*/

0 commit comments

Comments
 (0)