commit 30b68c153270619119ec36615bb54ee7a2816ecc Author: indexzero Date: Sat Jul 24 14:36:31 2010 -0400 [api] First commit of http-proxy diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/lib/node-proxy.js b/lib/node-proxy.js new file mode 100644 index 0000000..5d1a2e7 --- /dev/null +++ b/lib/node-proxy.js @@ -0,0 +1,120 @@ +/* + * node-proxy.js: Reverse proxy for node.js + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ + +var sys = require('sys'), + http = require('http'), + events = require('events'); + +// +// Creates a new instance of NodeProxy +// +//exports.create = function (req, res) { +// return new exports.nodeProxy(req, res); +//}; + +// +// The NodeProxy factory +// +exports.NodeProxy = function () { + var self = this; + this.emitter = new(events.EventEmitter); + + // If we were passed more than two arguments, + // assume they are request and response. + if(arguments.length >= 2) { + this.init(arguments[0], arguments[1]); + } +}; + +exports.NodeProxy.prototype = { + toArray: function (obj){ + var len = obj.length, + arr = new Array(len); + for (var i = 0; i < len; ++i) { + arr[i] = obj[i]; + } + return arr; + }, + + init: function (req, res) { + this.events = []; + + this.onData = function () { + self.events.push(["data"].concat(self.toArray(arguments))); + }; + this.onEnd = function () { + self.events.push(["end"].concat(self.toArray(arguments))); + }; + + req.addListener("data", this.onData); + req.addListener("end", this.onEnd); + }, + + proxyRequest: function (server, port, req, res) { + // Remark: nodeProxy.body exists solely for testability + this.body = ''; + var self = this; + + // Open new HTTP request to internal resource with will act as a reverse proxy pass + var c = http.createClient(port, server); + + // Make request to internal server, passing along the method and headers + var reverse_proxy = c.request(req.method, req.url, req.headers); + + // Add a listener for the connection timeout event + reverse_proxy.connection.addListener('error', function(err) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + + if(req.method !== 'HEAD') { + res.write('Not a HEAD request'); + } + + res.end(); + }); + + // Add a listener for the reverse_proxy response event + reverse_proxy.addListener("response", function (response) { + // Set the response headers of the client response + res.writeHead(response.statusCode, response.headers); + + // Add event handler for the proxied response in chunks + response.addListener("data", function (chunk) { + if(req.method !== 'HEAD') { + res.write(chunk, 'binary'); + this.body += chunk; + } + }); + + // Add event listener for end of proxied response + response.addListener("end", function () { + res.end(); + }); + }); + + // Chunk the client request body as chunks from the proxied request come in + req.addListener("data", function (chunk) { + reverse_proxy.write(chunk, 'binary'); + }) + + // At the end of the client request, we are going to stop the proxied request + req.addListener("end", function () { + // Remark: Emit the end event for testability + self.emitter.emit('something', self.body); + + reverse_proxy.end(); + }); + + req.removeListener('data', this.onData); + req.removeListener('end', this.onEnd); + + // Rebroadcast any events that have been buffered + for (var i = 0, len = this.events.length; i < len; ++i) { + req.emit.apply(req, this.events[i]); + } + } +}; diff --git a/test/node-proxy-test.js b/test/node-proxy-test.js new file mode 100644 index 0000000..123667d --- /dev/null +++ b/test/node-proxy-test.js @@ -0,0 +1,75 @@ +/* + * node-proxy-test.js: Tests for node-proxy. Reverse proxy for node.js + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ + +var vows = require('vows'), + sys = require('sys'), + assert = require('assert'), + http = require('http'); + +require.paths.unshift(require('path').join(__dirname, '../lib/')); + +var NodeProxy = require('node-proxy').NodeProxy; + +// +// Simple 'hello world' response for test purposes +// +var helloWorld = function(req, res) { + res.sendHeader(200, {'Content-Type': 'text/plain'}); + res.write('hello world') + res.end(); +}; + +// +// Creates the reverse proxy server +// +var startProxy = function (server, port, proxy) { + http.createServer(function (req, res){ + // Initialize the nodeProxy and start proxying the request + proxy.init(req, res); + proxy.proxyRequest(server, port, req, res); + }).listen(8080); +}; + +// +// Creates the 'hellonode' server +// +var startProxyTarget = function () { + http.createServer(function (req, res) { + helloWorld(req, res); + }).listen(8081); +}; + +// +// The default test bootstrapper +// +var startProxyTest = function () { + var proxy = new (NodeProxy); + startProxy('127.0.0.1', 8081, proxy); + startProxyTarget(); + return proxy; +}; + +var proxy = startProxyTest(); +proxy.emitter.addListener('something', function (body) { + sys.puts(body); +}) + +/*vows.describe('node-proxy').addBatch({ + "When an incoming request is proxied to the helloNode server" : { + topic: function () { + var proxy = startProxyTest(); + proxy.emitter.addListener('end', this.callback); + var client = http.createClient(8080, '127.0.0.1'); + client.request('GET', '/'); + }, + "it should received 'hello world'": function (num) { + sys.puts('got callback'); + //assert.equal(body, 'hello world'); + } + } +}).export(module);*/ \ No newline at end of file