From 4d131567211bcefc6ef0b0592d374fef7bd5abd8 Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 1 Aug 2013 10:50:39 +0200 Subject: [PATCH 001/210] [dist] first --- .gitignore | 2 ++ index.js | 1 + lib/caronte.js | 61 ++++++++++++++++++++++++++++++++++ lib/caronte/index.js | 4 +++ lib/caronte/streams/forward.js | 3 ++ lib/caronte/streams/proxy.js | 3 ++ lib/caronte/web.js | 25 ++++++++++++++ lib/caronte/web/index.js | 56 +++++++++++++++++++++++++++++++ package.json | 22 ++++++++++++ 9 files changed, 177 insertions(+) create mode 100644 .gitignore create mode 100644 index.js create mode 100644 lib/caronte.js create mode 100644 lib/caronte/index.js create mode 100644 lib/caronte/streams/forward.js create mode 100644 lib/caronte/streams/proxy.js create mode 100644 lib/caronte/web.js create mode 100644 lib/caronte/web/index.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..1bd722694 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +*.swp diff --git a/index.js b/index.js new file mode 100644 index 000000000..7007beda1 --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/caronte'); \ No newline at end of file diff --git a/lib/caronte.js b/lib/caronte.js new file mode 100644 index 000000000..97e2411b6 --- /dev/null +++ b/lib/caronte.js @@ -0,0 +1,61 @@ +var http = require('http'), + https = require('https'), + url = require('url'), + caronte = require('./caronte'), + events = require('eventemitter2'), + proxy = exports; + +/** + * Creates the proxy server. + * + * Examples: + * + * caronte.createProxyServer({ .. }, 8000) + * // => '{ web: [Function], ws: [Function] ... }' + * + * @param {Object} Options Config object passed to the proxy + * + * @return {Object} Proxy Proxy object with handlers for `ws` and `web` requests + * + * @api public + */ + +proxy.createProxyServer = function createProxyServer(options) { + if(!options) { + throw new Error([ + "`options` is needed and it must have the following layout:", + " ", + " { ", + " target : ", + " forward: ", + " ssl : ", + " ws : ", + " xfwd : ", + " } ", + " ", + "NOTE: `options.ws` and `options.ssl` are optional ", + " either one or both `options.target` and ", + " `options.forward` must exist " + ].join("\n")); + } + + ['target', 'forward'].forEach(function(key) { + options[key] = url.parse(options[key]); + }); + + return { + __proto__: new events.EventEmitter2({ wildcard: true, delimiter: ':' }), + web : caronte.createWebProxy(options), + ws : caronte.createWsProxy(options), + listen : function listen(port) { + var server = options.ssl ? http.createServer(this.web) : https.createServer(options.ssl, this.web); + + if(options.ws) { + server.on('upgrade', this.ws); + } + + return server; + } + }; +}; + diff --git a/lib/caronte/index.js b/lib/caronte/index.js new file mode 100644 index 000000000..25b4984c8 --- /dev/null +++ b/lib/caronte/index.js @@ -0,0 +1,4 @@ +var caronte = exports; + +caronte.createWebProxy = require('./web'); +caronte.createWsProxy = require('./ws'); \ No newline at end of file diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js new file mode 100644 index 000000000..d813e1cc6 --- /dev/null +++ b/lib/caronte/streams/forward.js @@ -0,0 +1,3 @@ +function ForwardStream() { + +} \ No newline at end of file diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js new file mode 100644 index 000000000..8d1f0672f --- /dev/null +++ b/lib/caronte/streams/proxy.js @@ -0,0 +1,3 @@ +function ProxyStream() { + +} \ No newline at end of file diff --git a/lib/caronte/web.js b/lib/caronte/web.js new file mode 100644 index 000000000..3c76b3ab1 --- /dev/null +++ b/lib/caronte/web.js @@ -0,0 +1,25 @@ +var passes = require('./web/'); + +module.exports = createWebProxy; + +function createWebProxy(options) { + passes = Object.keys(passes).map(function(pass) { + return passes[pass]; + }); + + return function(req, res) { + var self = this; + + self.emit('caronte:web:begin', req, res); + + passes.forEach(function(pass) { + var event = 'caronte:web:' + pass.name.toLowerCase(); + + self.emit(event + ':begin', req, res); + pass(req, res, options); + self.emit(event + ':end'); + }); + + self.emit('caronte:web:end'); + }; +}; \ No newline at end of file diff --git a/lib/caronte/web/index.js b/lib/caronte/web/index.js new file mode 100644 index 000000000..f0d9426ea --- /dev/null +++ b/lib/caronte/web/index.js @@ -0,0 +1,56 @@ +var ForwardStream = require('../streams/forward'), + ProxyStream = require('../streams/proxy'), + passes = exports; + +/*! + * List of passes. + * + * A `pass` is just a function that is executed on `req, res, options` + * so that you can easily add new checks while still keeping the base + * flexible. + * + */ + +passes.XHeaders = XHeaders; +passes.deleteLength = deleteLength; +passes.timeout = timeout; +passes.stream = stream; + +function deleteLength(req, res, options) { + if(req.method === 'DELETE' && !req.headers['content-length']) { + req.headers['content-length'] = '0'; + } +} + +function timeout(req, res, options) { + if(options.timeout) { + req.socket.setTimeout(options.timeout); + } +} + +function XHeaders(req, res, options) { + var values = { + for : req.connection.remoteAddress || req.socket.remoteAddress, + port : req.connection.remotePort || req.socket.remotePort, + proto: req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http') + }; + + ['for', 'port', 'proto'].forEach(function(header) { + req.headers['x-forwarded-' + header] = + (req.headers['x-forwarded-' + header] || '') + + (req.headers['x-forwarded-' + header] ? ',' : '') + + values[header] + }); +} + +function stream(req, res, options) { + if(options.forward) { + req.pipe(new ForwardStream(options.forward)); + } + + if(options.target) { + return req.pipe(new ProxyStream(res, options)).pipe(res); + } + + res.end(); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 000000000..67ab1fd14 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name" : "caronte", + "version" : "0.0.0", + "description" : "HTTP proxying for the masses", + "author" : "yawnt ", + + "main" : "index.js", + + "dependencies" : { + "eventemitter2": "*" + }, + "devDependencies": { + "mocha" : "*", + "expect.js": "*", + "dox" : "*" + }, + "scripts" : { + "test" : "mocha -t 20000 -R spec -r expect test/*-test.js" + }, + + "license" : "MIT" +} From 8273cb6461e4d33f36e583b0354d1bea038d0a56 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 3 Aug 2013 08:47:07 +0200 Subject: [PATCH 002/210] [refactor] move to leaner architecture --- index.js | 9 ++++++ lib/caronte.js | 6 ++-- lib/caronte/index.js | 36 +++++++++++++++++++-- lib/caronte/{web/index.js => passes/web.js} | 15 +++++---- lib/caronte/web.js | 25 -------------- 5 files changed, 53 insertions(+), 38 deletions(-) rename lib/caronte/{web/index.js => passes/web.js} (89%) delete mode 100644 lib/caronte/web.js diff --git a/index.js b/index.js index 7007beda1..5c73968f3 100644 --- a/index.js +++ b/index.js @@ -1 +1,10 @@ +/*! + * + * Charon the demon, with the eyes of glede, + * Beckoning to them, collects them all together, + * Beats with his oar whoever lags behind + * Dante - The Divine Comedy (Canto III) + * + */ + module.exports = require('./lib/caronte'); \ No newline at end of file diff --git a/lib/caronte.js b/lib/caronte.js index 97e2411b6..a06306008 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -33,9 +33,9 @@ proxy.createProxyServer = function createProxyServer(options) { " xfwd : ", " } ", " ", - "NOTE: `options.ws` and `options.ssl` are optional ", - " either one or both `options.target` and ", - " `options.forward` must exist " + "NOTE: `options.ws` and `options.ssl` are optional. ", + " `options.target and `options.forward` cannot be ", + " both missing " ].join("\n")); } diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 25b4984c8..7304850d1 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -1,4 +1,34 @@ -var caronte = exports; +var caronte = exports, + web = require('./passes/web'); + ws = require('./passes/ws'); + +caronte.createWebProxy = createRightProxy('web'); +caronte.createWsProxy = createRightProxy('ws'); + +function createRightProxy(type) { + passes = type === 'ws' ? ws : web; + return function(options) { + + passes = Object.keys(passes).map(function(pass) { + return passes[pass]; + }); + + return function(req, res) { + var self = this, + ev = 'caronte:' + type + ':'; + + self.emit(ev + 'begin', req, res); + + passes.forEach(function(pass) { + var event = ev + pass.name.toLowerCase(); + + self.emit(event + 'begin', req, res); + pass(req, res, options); + self.emit(event + 'end'); + }); + + self.emit(ev + 'end'); + }; + }; +} -caronte.createWebProxy = require('./web'); -caronte.createWsProxy = require('./ws'); \ No newline at end of file diff --git a/lib/caronte/web/index.js b/lib/caronte/passes/web.js similarity index 89% rename from lib/caronte/web/index.js rename to lib/caronte/passes/web.js index f0d9426ea..be6753ccc 100644 --- a/lib/caronte/web/index.js +++ b/lib/caronte/passes/web.js @@ -3,18 +3,14 @@ var ForwardStream = require('../streams/forward'), passes = exports; /*! - * List of passes. + * Array of passes. * * A `pass` is just a function that is executed on `req, res, options` * so that you can easily add new checks while still keeping the base * flexible. - * */ -passes.XHeaders = XHeaders; -passes.deleteLength = deleteLength; -passes.timeout = timeout; -passes.stream = stream; +[ // <-- function deleteLength(req, res, options) { if(req.method === 'DELETE' && !req.headers['content-length']) { @@ -53,4 +49,9 @@ function stream(req, res, options) { } res.end(); -} \ No newline at end of file +} + +] // <-- + .forEach(function(func) { + passes[func.name] = func; + }); \ No newline at end of file diff --git a/lib/caronte/web.js b/lib/caronte/web.js deleted file mode 100644 index 3c76b3ab1..000000000 --- a/lib/caronte/web.js +++ /dev/null @@ -1,25 +0,0 @@ -var passes = require('./web/'); - -module.exports = createWebProxy; - -function createWebProxy(options) { - passes = Object.keys(passes).map(function(pass) { - return passes[pass]; - }); - - return function(req, res) { - var self = this; - - self.emit('caronte:web:begin', req, res); - - passes.forEach(function(pass) { - var event = 'caronte:web:' + pass.name.toLowerCase(); - - self.emit(event + ':begin', req, res); - pass(req, res, options); - self.emit(event + ':end'); - }); - - self.emit('caronte:web:end'); - }; -}; \ No newline at end of file From d05af4af60a5f3d308aa68bf09ab0cf9e5528c52 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 3 Aug 2013 15:45:07 +0200 Subject: [PATCH 003/210] [refactor docs] add descriptions --- lib/caronte/common.js | 32 ++++++++++++++ lib/caronte/index.js | 16 +++++++ lib/caronte/passes/web.js | 44 +++++++++++++++++++ lib/caronte/streams/forward.js | 80 +++++++++++++++++++++++++++++++++- 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 lib/caronte/common.js diff --git a/lib/caronte/common.js b/lib/caronte/common.js new file mode 100644 index 000000000..7028bb171 --- /dev/null +++ b/lib/caronte/common.js @@ -0,0 +1,32 @@ +var common = exports; + +/** + * Copies the right headers from `options` and `req` to + * `outgoing` which is then used to fire the proxied + * request. + * + * Examples: + * + * common.setupOutgoing(outgoing, options, req) + * // => { host: ..., hostname: ...} + * + * @param {Object} Outgoing Base object to be filled with required properties + * @param {Object} Options Config object passed to the proxy + * @param {ClientRequest} Req Request Object + *  + * @return {Object} Outgoing Object with all required properties set + * + * @api private + */ + +common.setupOutgoing = function(outgoing, options, req) { + ['host', 'hostname', 'port', 'socketPath', 'agent'].forEach( + function(e) { outgoing[e] = options[e]; } + ); + + ['method', 'path', 'headers'].forEach( + function(e) { outgoing[e] = req[e]; } + ); + + return outgoing; +}; \ No newline at end of file diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 7304850d1..0b9f544c0 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -5,6 +5,22 @@ var caronte = exports, caronte.createWebProxy = createRightProxy('web'); caronte.createWsProxy = createRightProxy('ws'); +/** + * Returns a function that creates the loader for + * either `ws` or `web`'s passes. + * + * Examples: + * + * caronte.createRightProxy('ws') + * // => [Function] + * + * @param {String} Type Either 'ws' or 'web' + *  + * @return {Function} Loader Function that when called returns an iterator for the right passes + * + * @api private + */ + function createRightProxy(type) { passes = type === 'ws' ? ws : web; return function(options) { diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index be6753ccc..c15f4e192 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -12,19 +12,51 @@ var ForwardStream = require('../streams/forward'), [ // <-- +/** + * Sets `content-length` to '0' if request is of DELETE type. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + function deleteLength(req, res, options) { if(req.method === 'DELETE' && !req.headers['content-length']) { req.headers['content-length'] = '0'; } } +/** + * Sets timeout in request socket if it was specified in options. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + function timeout(req, res, options) { if(options.timeout) { req.socket.setTimeout(options.timeout); } } +/** + * Sets `x-forwarded-*` headers if specified in config. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + function XHeaders(req, res, options) { + if(!options.xfwd) return; + var values = { for : req.connection.remoteAddress || req.socket.remoteAddress, port : req.connection.remotePort || req.socket.remotePort, @@ -39,6 +71,18 @@ function XHeaders(req, res, options) { }); } +/** + * Does the actual proxying. If `forward` is enabled fires up + * a ForwardStream, same happens for ProxyStream. The request + * just dies otherwise. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + function stream(req, res, options) { if(options.forward) { req.pipe(new ForwardStream(options.forward)); diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js index d813e1cc6..640c75d01 100644 --- a/lib/caronte/streams/forward.js +++ b/lib/caronte/streams/forward.js @@ -1,3 +1,79 @@ +var Writable = require('stream').Writable, + common = require('../common'), + http = require('http'), + https = require('https'); + +module.exports = ForwardStream; + +/** + * Forwards the request to the external target specified in options + * + * Examples: + * + * new ForwardStream(options) + * // => { ... } + * + * @param {Object} Options Config object passed to the proxy + *  + * @return {ForwardStream} Stream A clone of ForwardStream + * + * @api private + */ + function ForwardStream() { - -} \ No newline at end of file + Writable.call(this); + + this.once('pipe', this.onPipe); + this.once('finish', this.onFinish); +} + +/** + * Fires up the request to the external target + * + * Examples: + * + * (new ForwardStream(options)).onPipe(req) + * // => undefined + * + * @param {HttpRequest} Req Request object + * + * @api private + */ + +ForwardStream.prototype.onPipe = function(request) { + this.forwardReq = (options.ssl ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, request); + ); +}; + +/** + * Closes forwarded request when `pipe` is finished + * + * Examples: + * + * (new ForwardStream(options)).onFinish() + * // => undefined + * + * @api private + */ + +ForwardStream.prototype.onFinish = function() { + this.forwardReq.end(); +}; + +/** + * Implements `stream.Writable`, writes to the forwarded request + * + * Examples: + * + * (new ForwardStream(options))._write(chunk, encoding, clb) + * // => undefined + * + * @api private + */ + +ForwardStream.prototype._write = function(chunk, encoding, clb) { + this.forwardReq.write(chunk, encoding, clb); +}; + +require('util').inherits(ForwardStream, Writable); \ No newline at end of file From 34f16e74647095199f84ab61e10c8dafd60b505a Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 3 Aug 2013 15:56:05 +0200 Subject: [PATCH 004/210] [fix] making @stoke a happy camper --- index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 5c73968f3..68de922bd 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,13 @@ /*! + * Caron dimonio, con occhi di bragia + * loro accennando, tutte le raccoglie; + * batte col remo qualunque s’adagia * * Charon the demon, with the eyes of glede, * Beckoning to them, collects them all together, * Beats with his oar whoever lags behind + * * Dante - The Divine Comedy (Canto III) - * */ module.exports = require('./lib/caronte'); \ No newline at end of file From 004a46c09df2f0f7b15d8e8f7119bc6039e0c01c Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 3 Aug 2013 16:44:23 +0200 Subject: [PATCH 005/210] [test] COVERAGE --- package.json | 15 ++++++++++----- test/truth-test.js | 8 ++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 test/truth-test.js diff --git a/package.json b/package.json index 67ab1fd14..8cef91a7f 100644 --- a/package.json +++ b/package.json @@ -7,15 +7,20 @@ "main" : "index.js", "dependencies" : { - "eventemitter2": "*" + "eventemitter2" : "*" }, "devDependencies": { - "mocha" : "*", - "expect.js": "*", - "dox" : "*" + "mocha" : "*", + "expect.js" : "*", + "dox" : "*", + "coveralls" : "*", + "mocha-lcov-reporter": "*", + "blanket" : "*" }, "scripts" : { - "test" : "mocha -t 20000 -R spec -r expect test/*-test.js" + "blanket" : { "pattern": "caronte/lib" }, + "test" : "mocha -R spec test/*-test.js && npm run-script test-cov", + "test-cov" : "mocha --require blanket -R html-cov > cov/coverage.html" }, "license" : "MIT" diff --git a/test/truth-test.js b/test/truth-test.js new file mode 100644 index 000000000..437df3432 --- /dev/null +++ b/test/truth-test.js @@ -0,0 +1,8 @@ +var caronte = require('../'), + expect = require('expect.js'); + +describe('the truthness', function() { + it('should be true', function() { + expect(true).to.be(true); + }) +}); From 16eacfa961d2a2d80534e95eba83010ed6ab01b4 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 3 Aug 2013 16:54:48 +0200 Subject: [PATCH 006/210] [test] started writing tests --- test/lib-caronte-test.js | 27 +++++++++++++++++++++++++++ test/truth-test.js | 8 -------- 2 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 test/lib-caronte-test.js delete mode 100644 test/truth-test.js diff --git a/test/lib-caronte-test.js b/test/lib-caronte-test.js new file mode 100644 index 000000000..8358c5f99 --- /dev/null +++ b/test/lib-caronte-test.js @@ -0,0 +1,27 @@ +var caronte = require('../lib/caronte'), + expect = require('expect.js'); + +describe('lib/caronte.js', function() { + describe('#createProxyServer', function() { + it('should throw without options', function() { + var error; + try { + caronte.createProxyServer(); + } catch(e) { + error = e; + } + + expect(error).to.be.an(Error); + }) + + it('should return an object otherwise', function() { + var obj = caronte.createProxyServer({ + target: 'http://www.google.com:80' + }); + + expect(obj.web).to.be.a(Function); + expect(obj.ws).to.be.a(Function); + expect(obj.listen).to.be.a(Function); + }); + }); +}); diff --git a/test/truth-test.js b/test/truth-test.js deleted file mode 100644 index 437df3432..000000000 --- a/test/truth-test.js +++ /dev/null @@ -1,8 +0,0 @@ -var caronte = require('../'), - expect = require('expect.js'); - -describe('the truthness', function() { - it('should be true', function() { - expect(true).to.be(true); - }) -}); From a255f984fecf24c9290f3ad58d1b68e54a7509eb Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 9 Aug 2013 18:58:56 +0200 Subject: [PATCH 007/210] [fix] tests --- cov/coverage.html | 341 +++++++++++++++++++++++++++++++++ lib/caronte.js | 3 +- lib/caronte/passes/web.js | 6 +- lib/caronte/passes/ws.js | 1 + lib/caronte/streams/forward.js | 2 +- 5 files changed, 348 insertions(+), 5 deletions(-) create mode 100644 cov/coverage.html create mode 100644 lib/caronte/passes/ws.js diff --git a/cov/coverage.html b/cov/coverage.html new file mode 100644 index 000000000..edbc970e0 --- /dev/null +++ b/cov/coverage.html @@ -0,0 +1,341 @@ +Coverage +

Coverage

44%
67
30
37

/Users/yawnt/Codes/caronte/lib/caronte.js

66%
12
8
4
LineHitsSource
11var http = require('http'),
2 https = require('https'),
3 url = require('url'),
4 caronte = require('./caronte/'),
5 events = require('eventemitter2'),
6 proxy = exports;
7
8/**
9 * Creates the proxy server.
10 *
11 * Examples:
12 *
13 * caronte.createProxyServer({ .. }, 8000)
14 * // => '{ web: [Function], ws: [Function] ... }'
15 *
16 * @param {Object} Options Config object passed to the proxy
17 *
18 * @return {Object} Proxy Proxy object with handlers for `ws` and `web` requests
19 *
20 * @api public
21 */
22
231proxy.createProxyServer = function createProxyServer(options) {
242 if(!options) {
251 throw new Error([
26 "`options` is needed and it must have the following layout:",
27 " ",
28 " { ",
29 " target : <url string to be parsed with the url module> ",
30 " forward: <url string to be parsed with the url module> ",
31 " ssl : <object to be passed to https.createServer()> ",
32 " ws : <true/false, if you want to proxy websockets> ",
33 " xfwd : <true/false, adds x-forward headers> ",
34 " } ",
35 " ",
36 "NOTE: `options.ws` and `options.ssl` are optional. ",
37 " `options.target and `options.forward` cannot be ",
38 " both missing "
39 ].join("\n"));
40 }
41
421 ['target', 'forward'].forEach(function(key) {
433 if(!options[key]) return;
441 options[key] = url.parse(options[key]);
45 });
46
471 return {
48 __proto__: new events.EventEmitter2({ wildcard: true, delimiter: ':' }),
49 web : caronte.createWebProxy(options),
50 ws : caronte.createWsProxy(options),
51 listen : function listen(port) {
520 var server = options.ssl ? http.createServer(this.web) : https.createServer(options.ssl, this.web);
53
540 if(options.ws) {
550 server.on('upgrade', this.ws);
56 }
57
580 return server;
59 }
60 };
61};
62
63

/Users/yawnt/Codes/caronte/lib/caronte/common.js

28%
7
2
5
LineHitsSource
11var common = exports;
2
3/**
4 * Copies the right headers from `options` and `req` to
5 * `outgoing` which is then used to fire the proxied
6 * request.
7 *
8 * Examples:
9 *
10 * common.setupOutgoing(outgoing, options, req)
11 * // => { host: ..., hostname: ...}
12 *
13 * @param {Object} Outgoing Base object to be filled with required properties
14 * @param {Object} Options Config object passed to the proxy
15 * @param {ClientRequest} Req Request Object
16
17 * @return {Object} Outgoing Object with all required properties set
18 *
19 * @api private
20 */
21
221common.setupOutgoing = function(outgoing, options, req) {
230 ['host', 'hostname', 'port', 'socketPath', 'agent'].forEach(
240 function(e) { outgoing[e] = options[e]; }
25 );
26
270 ['method', 'path', 'headers'].forEach(
280 function(e) { outgoing[e] = req[e]; }
29 );
30
310 return outgoing;
32};

/Users/yawnt/Codes/caronte/lib/caronte/index.js

50%
18
9
9
LineHitsSource
11var caronte = exports,
2 web = require('./passes/web');
31 ws = require('./passes/ws');
4
51caronte.createWebProxy = createRightProxy('web');
61caronte.createWsProxy = createRightProxy('ws');
7
8/**
9 * Returns a function that creates the loader for
10 * either `ws` or `web`'s passes.
11 *
12 * Examples:
13 *
14 * caronte.createRightProxy('ws')
15 * // => [Function]
16 *
17 * @param {String} Type Either 'ws' or 'web'
18
19 * @return {Function} Loader Function that when called returns an iterator for the right passes
20 *
21 * @api private
22 */
23
241function createRightProxy(type) {
252 passes = type === 'ws' ? ws : web;
262 return function(options) {
27
282 passes = Object.keys(passes).map(function(pass) {
290 return passes[pass];
30 });
31
322 return function(req, res) {
330 var self = this,
34 ev = 'caronte:' + type + ':';
35
360 self.emit(ev + 'begin', req, res);
37
380 passes.forEach(function(pass) {
390 var event = ev + pass.name.toLowerCase();
40
410 self.emit(event + 'begin', req, res);
420 pass(req, res, options);
430 self.emit(event + 'end');
44 });
45
460 self.emit(ev + 'end');
47 };
48 };
49}
50
51

/Users/yawnt/Codes/caronte/lib/caronte/passes/web.js

18%
16
3
13
LineHitsSource
11var ForwardStream = require('../streams/forward'),
2 ProxyStream = require('../streams/proxy'),
3 passes = exports;
4
5/*!
6 * Array of passes.
7 *
8 * A `pass` is just a function that is executed on `req, res, options`
9 * so that you can easily add new checks while still keeping the base
10 * flexible.
11 */
12
131[ // <--
14
15/**
16 * Sets `content-length` to '0' if request is of DELETE type.
17 *
18 * @param {ClientRequest} Req Request object
19 * @param {IncomingMessage} Res Response object
20 * @param {Object} Options Config object passed to the proxy
21 *
22 * @api private
23 */
24
25function deleteLength(req, res, options) {
260 if(req.method === 'DELETE' && !req.headers['content-length']) {
270 req.headers['content-length'] = '0';
28 }
29},
30
31/**
32 * Sets timeout in request socket if it was specified in options.
33 *
34 * @param {ClientRequest} Req Request object
35 * @param {IncomingMessage} Res Response object
36 * @param {Object} Options Config object passed to the proxy
37 *
38 * @api private
39 */
40
41function timeout(req, res, options) {
420 if(options.timeout) {
430 req.socket.setTimeout(options.timeout);
44 }
45},
46
47/**
48 * Sets `x-forwarded-*` headers if specified in config.
49 *
50 * @param {ClientRequest} Req Request object
51 * @param {IncomingMessage} Res Response object
52 * @param {Object} Options Config object passed to the proxy
53 *
54 * @api private
55 */
56
57function XHeaders(req, res, options) {
580 if(!options.xfwd) return;
59
600 var values = {
61 for : req.connection.remoteAddress || req.socket.remoteAddress,
62 port : req.connection.remotePort || req.socket.remotePort,
63 proto: req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http')
64 };
65
660 ['for', 'port', 'proto'].forEach(function(header) {
670 req.headers['x-forwarded-' + header] =
68 (req.headers['x-forwarded-' + header] || '') +
69 (req.headers['x-forwarded-' + header] ? ',' : '') +
70 values[header]
71 });
72},
73
74/**
75 * Does the actual proxying. If `forward` is enabled fires up
76 * a ForwardStream, same happens for ProxyStream. The request
77 * just dies otherwise.
78 *
79 * @param {ClientRequest} Req Request object
80 * @param {IncomingMessage} Res Response object
81 * @param {Object} Options Config object passed to the proxy
82 *
83 * @api private
84 */
85
86function stream(req, res, options) {
870 if(options.forward) {
880 req.pipe(new ForwardStream(options.forward));
89 }
90
910 if(options.target) {
920 return req.pipe(new ProxyStream(res, options)).pipe(res);
93 }
94
950 res.end();
96}
97
98] // <--
99 .forEach(function(func) {
1004 passes[func.name] = func;
101 });

/Users/yawnt/Codes/caronte/lib/caronte/passes/ws.js

0%
0
0
0
LineHitsSource
1// ws

/Users/yawnt/Codes/caronte/lib/caronte/streams/forward.js

53%
13
7
6
LineHitsSource
11var Writable = require('stream').Writable,
2 common = require('../common'),
3 http = require('http'),
4 https = require('https');
5
61module.exports = ForwardStream;
7
8/**
9 * Forwards the request to the external target specified in options
10 *
11 * Examples:
12 *
13 * new ForwardStream(options)
14 * // => { ... }
15 *
16 * @param {Object} Options Config object passed to the proxy
17
18 * @return {ForwardStream} Stream A clone of ForwardStream
19 *
20 * @api private
21 */
22
231function ForwardStream() {
240 Writable.call(this);
25
260 this.once('pipe', this.onPipe);
270 this.once('finish', this.onFinish);
28}
29
30/**
31 * Fires up the request to the external target
32 *
33 * Examples:
34 *
35 * (new ForwardStream(options)).onPipe(req)
36 * // => undefined
37 *
38 * @param {HttpRequest} Req Request object
39 *
40 * @api private
41 */
42
431ForwardStream.prototype.onPipe = function(request) {
440 this.forwardReq = (options.ssl ? https : http).request(
45 common.setupOutgoing(options.ssl || {}, options, request)
46 );
47};
48
49/**
50 * Closes forwarded request when `pipe` is finished
51 *
52 * Examples:
53 *
54 * (new ForwardStream(options)).onFinish()
55 * // => undefined
56 *
57 * @api private
58 */
59
601ForwardStream.prototype.onFinish = function() {
610 this.forwardReq.end();
62};
63
64/**
65 * Implements `stream.Writable`, writes to the forwarded request
66 *
67 * Examples:
68 *
69 * (new ForwardStream(options))._write(chunk, encoding, clb)
70 * // => undefined
71 *
72 * @api private
73 */
74
751ForwardStream.prototype._write = function(chunk, encoding, clb) {
760 this.forwardReq.write(chunk, encoding, clb);
77};
78
791require('util').inherits(ForwardStream, Writable);

/Users/yawnt/Codes/caronte/lib/caronte/streams/proxy.js

100%
1
1
0
LineHitsSource
11function ProxyStream() {
2
3}
\ No newline at end of file diff --git a/lib/caronte.js b/lib/caronte.js index a06306008..c05701c91 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -1,7 +1,7 @@ var http = require('http'), https = require('https'), url = require('url'), - caronte = require('./caronte'), + caronte = require('./caronte/'), events = require('eventemitter2'), proxy = exports; @@ -40,6 +40,7 @@ proxy.createProxyServer = function createProxyServer(options) { } ['target', 'forward'].forEach(function(key) { + if(!options[key]) return; options[key] = url.parse(options[key]); }); diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index c15f4e192..ea9e2d91b 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -26,7 +26,7 @@ function deleteLength(req, res, options) { if(req.method === 'DELETE' && !req.headers['content-length']) { req.headers['content-length'] = '0'; } -} +}, /** * Sets timeout in request socket if it was specified in options. @@ -42,7 +42,7 @@ function timeout(req, res, options) { if(options.timeout) { req.socket.setTimeout(options.timeout); } -} +}, /** * Sets `x-forwarded-*` headers if specified in config. @@ -69,7 +69,7 @@ function XHeaders(req, res, options) { (req.headers['x-forwarded-' + header] ? ',' : '') + values[header] }); -} +}, /** * Does the actual proxying. If `forward` is enabled fires up diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js new file mode 100644 index 000000000..54e117ba5 --- /dev/null +++ b/lib/caronte/passes/ws.js @@ -0,0 +1 @@ +// ws \ No newline at end of file diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js index 640c75d01..7ab802864 100644 --- a/lib/caronte/streams/forward.js +++ b/lib/caronte/streams/forward.js @@ -42,7 +42,7 @@ function ForwardStream() { ForwardStream.prototype.onPipe = function(request) { this.forwardReq = (options.ssl ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, request); + common.setupOutgoing(options.ssl || {}, options, request) ); }; From 335af81d0244e62ecb501690bd15bc5a04ec51a3 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 9 Aug 2013 18:59:20 +0200 Subject: [PATCH 008/210] [minor] remove coverage --- .gitignore | 1 + cov/coverage.html | 341 ---------------------------------------------- 2 files changed, 1 insertion(+), 341 deletions(-) delete mode 100644 cov/coverage.html diff --git a/.gitignore b/.gitignore index 1bd722694..1a07e33df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules *.swp +cov diff --git a/cov/coverage.html b/cov/coverage.html deleted file mode 100644 index edbc970e0..000000000 --- a/cov/coverage.html +++ /dev/null @@ -1,341 +0,0 @@ -Coverage -

Coverage

44%
67
30
37

/Users/yawnt/Codes/caronte/lib/caronte.js

66%
12
8
4
LineHitsSource
11var http = require('http'),
2 https = require('https'),
3 url = require('url'),
4 caronte = require('./caronte/'),
5 events = require('eventemitter2'),
6 proxy = exports;
7
8/**
9 * Creates the proxy server.
10 *
11 * Examples:
12 *
13 * caronte.createProxyServer({ .. }, 8000)
14 * // => '{ web: [Function], ws: [Function] ... }'
15 *
16 * @param {Object} Options Config object passed to the proxy
17 *
18 * @return {Object} Proxy Proxy object with handlers for `ws` and `web` requests
19 *
20 * @api public
21 */
22
231proxy.createProxyServer = function createProxyServer(options) {
242 if(!options) {
251 throw new Error([
26 "`options` is needed and it must have the following layout:",
27 " ",
28 " { ",
29 " target : <url string to be parsed with the url module> ",
30 " forward: <url string to be parsed with the url module> ",
31 " ssl : <object to be passed to https.createServer()> ",
32 " ws : <true/false, if you want to proxy websockets> ",
33 " xfwd : <true/false, adds x-forward headers> ",
34 " } ",
35 " ",
36 "NOTE: `options.ws` and `options.ssl` are optional. ",
37 " `options.target and `options.forward` cannot be ",
38 " both missing "
39 ].join("\n"));
40 }
41
421 ['target', 'forward'].forEach(function(key) {
433 if(!options[key]) return;
441 options[key] = url.parse(options[key]);
45 });
46
471 return {
48 __proto__: new events.EventEmitter2({ wildcard: true, delimiter: ':' }),
49 web : caronte.createWebProxy(options),
50 ws : caronte.createWsProxy(options),
51 listen : function listen(port) {
520 var server = options.ssl ? http.createServer(this.web) : https.createServer(options.ssl, this.web);
53
540 if(options.ws) {
550 server.on('upgrade', this.ws);
56 }
57
580 return server;
59 }
60 };
61};
62
63

/Users/yawnt/Codes/caronte/lib/caronte/common.js

28%
7
2
5
LineHitsSource
11var common = exports;
2
3/**
4 * Copies the right headers from `options` and `req` to
5 * `outgoing` which is then used to fire the proxied
6 * request.
7 *
8 * Examples:
9 *
10 * common.setupOutgoing(outgoing, options, req)
11 * // => { host: ..., hostname: ...}
12 *
13 * @param {Object} Outgoing Base object to be filled with required properties
14 * @param {Object} Options Config object passed to the proxy
15 * @param {ClientRequest} Req Request Object
16
17 * @return {Object} Outgoing Object with all required properties set
18 *
19 * @api private
20 */
21
221common.setupOutgoing = function(outgoing, options, req) {
230 ['host', 'hostname', 'port', 'socketPath', 'agent'].forEach(
240 function(e) { outgoing[e] = options[e]; }
25 );
26
270 ['method', 'path', 'headers'].forEach(
280 function(e) { outgoing[e] = req[e]; }
29 );
30
310 return outgoing;
32};

/Users/yawnt/Codes/caronte/lib/caronte/index.js

50%
18
9
9
LineHitsSource
11var caronte = exports,
2 web = require('./passes/web');
31 ws = require('./passes/ws');
4
51caronte.createWebProxy = createRightProxy('web');
61caronte.createWsProxy = createRightProxy('ws');
7
8/**
9 * Returns a function that creates the loader for
10 * either `ws` or `web`'s passes.
11 *
12 * Examples:
13 *
14 * caronte.createRightProxy('ws')
15 * // => [Function]
16 *
17 * @param {String} Type Either 'ws' or 'web'
18
19 * @return {Function} Loader Function that when called returns an iterator for the right passes
20 *
21 * @api private
22 */
23
241function createRightProxy(type) {
252 passes = type === 'ws' ? ws : web;
262 return function(options) {
27
282 passes = Object.keys(passes).map(function(pass) {
290 return passes[pass];
30 });
31
322 return function(req, res) {
330 var self = this,
34 ev = 'caronte:' + type + ':';
35
360 self.emit(ev + 'begin', req, res);
37
380 passes.forEach(function(pass) {
390 var event = ev + pass.name.toLowerCase();
40
410 self.emit(event + 'begin', req, res);
420 pass(req, res, options);
430 self.emit(event + 'end');
44 });
45
460 self.emit(ev + 'end');
47 };
48 };
49}
50
51

/Users/yawnt/Codes/caronte/lib/caronte/passes/web.js

18%
16
3
13
LineHitsSource
11var ForwardStream = require('../streams/forward'),
2 ProxyStream = require('../streams/proxy'),
3 passes = exports;
4
5/*!
6 * Array of passes.
7 *
8 * A `pass` is just a function that is executed on `req, res, options`
9 * so that you can easily add new checks while still keeping the base
10 * flexible.
11 */
12
131[ // <--
14
15/**
16 * Sets `content-length` to '0' if request is of DELETE type.
17 *
18 * @param {ClientRequest} Req Request object
19 * @param {IncomingMessage} Res Response object
20 * @param {Object} Options Config object passed to the proxy
21 *
22 * @api private
23 */
24
25function deleteLength(req, res, options) {
260 if(req.method === 'DELETE' && !req.headers['content-length']) {
270 req.headers['content-length'] = '0';
28 }
29},
30
31/**
32 * Sets timeout in request socket if it was specified in options.
33 *
34 * @param {ClientRequest} Req Request object
35 * @param {IncomingMessage} Res Response object
36 * @param {Object} Options Config object passed to the proxy
37 *
38 * @api private
39 */
40
41function timeout(req, res, options) {
420 if(options.timeout) {
430 req.socket.setTimeout(options.timeout);
44 }
45},
46
47/**
48 * Sets `x-forwarded-*` headers if specified in config.
49 *
50 * @param {ClientRequest} Req Request object
51 * @param {IncomingMessage} Res Response object
52 * @param {Object} Options Config object passed to the proxy
53 *
54 * @api private
55 */
56
57function XHeaders(req, res, options) {
580 if(!options.xfwd) return;
59
600 var values = {
61 for : req.connection.remoteAddress || req.socket.remoteAddress,
62 port : req.connection.remotePort || req.socket.remotePort,
63 proto: req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http')
64 };
65
660 ['for', 'port', 'proto'].forEach(function(header) {
670 req.headers['x-forwarded-' + header] =
68 (req.headers['x-forwarded-' + header] || '') +
69 (req.headers['x-forwarded-' + header] ? ',' : '') +
70 values[header]
71 });
72},
73
74/**
75 * Does the actual proxying. If `forward` is enabled fires up
76 * a ForwardStream, same happens for ProxyStream. The request
77 * just dies otherwise.
78 *
79 * @param {ClientRequest} Req Request object
80 * @param {IncomingMessage} Res Response object
81 * @param {Object} Options Config object passed to the proxy
82 *
83 * @api private
84 */
85
86function stream(req, res, options) {
870 if(options.forward) {
880 req.pipe(new ForwardStream(options.forward));
89 }
90
910 if(options.target) {
920 return req.pipe(new ProxyStream(res, options)).pipe(res);
93 }
94
950 res.end();
96}
97
98] // <--
99 .forEach(function(func) {
1004 passes[func.name] = func;
101 });

/Users/yawnt/Codes/caronte/lib/caronte/passes/ws.js

0%
0
0
0
LineHitsSource
1// ws

/Users/yawnt/Codes/caronte/lib/caronte/streams/forward.js

53%
13
7
6
LineHitsSource
11var Writable = require('stream').Writable,
2 common = require('../common'),
3 http = require('http'),
4 https = require('https');
5
61module.exports = ForwardStream;
7
8/**
9 * Forwards the request to the external target specified in options
10 *
11 * Examples:
12 *
13 * new ForwardStream(options)
14 * // => { ... }
15 *
16 * @param {Object} Options Config object passed to the proxy
17
18 * @return {ForwardStream} Stream A clone of ForwardStream
19 *
20 * @api private
21 */
22
231function ForwardStream() {
240 Writable.call(this);
25
260 this.once('pipe', this.onPipe);
270 this.once('finish', this.onFinish);
28}
29
30/**
31 * Fires up the request to the external target
32 *
33 * Examples:
34 *
35 * (new ForwardStream(options)).onPipe(req)
36 * // => undefined
37 *
38 * @param {HttpRequest} Req Request object
39 *
40 * @api private
41 */
42
431ForwardStream.prototype.onPipe = function(request) {
440 this.forwardReq = (options.ssl ? https : http).request(
45 common.setupOutgoing(options.ssl || {}, options, request)
46 );
47};
48
49/**
50 * Closes forwarded request when `pipe` is finished
51 *
52 * Examples:
53 *
54 * (new ForwardStream(options)).onFinish()
55 * // => undefined
56 *
57 * @api private
58 */
59
601ForwardStream.prototype.onFinish = function() {
610 this.forwardReq.end();
62};
63
64/**
65 * Implements `stream.Writable`, writes to the forwarded request
66 *
67 * Examples:
68 *
69 * (new ForwardStream(options))._write(chunk, encoding, clb)
70 * // => undefined
71 *
72 * @api private
73 */
74
751ForwardStream.prototype._write = function(chunk, encoding, clb) {
760 this.forwardReq.write(chunk, encoding, clb);
77};
78
791require('util').inherits(ForwardStream, Writable);

/Users/yawnt/Codes/caronte/lib/caronte/streams/proxy.js

100%
1
1
0
LineHitsSource
11function ProxyStream() {
2
3}
\ No newline at end of file From cedc5c4bd2059585e1222ec4f03f09e8bcc808fc Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 9 Aug 2013 19:13:44 +0200 Subject: [PATCH 009/210] [tests] add more tests --- lib/caronte/streams/forward.js | 12 +++++++----- package.json | 2 +- test/lib-caronte-common-test.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 test/lib-caronte-common-test.js diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js index 7ab802864..b7e8ccd03 100644 --- a/lib/caronte/streams/forward.js +++ b/lib/caronte/streams/forward.js @@ -21,12 +21,16 @@ module.exports = ForwardStream; */ function ForwardStream() { + var self = this; + Writable.call(this); - this.once('pipe', this.onPipe); - this.once('finish', this.onFinish); + this.once('pipe', function() { self.onPipe() }); + this.once('finish', function() { self.onFinish() }); } +require('util').inherits(ForwardStream, Writable); + /** * Fires up the request to the external target * @@ -74,6 +78,4 @@ ForwardStream.prototype.onFinish = function() { ForwardStream.prototype._write = function(chunk, encoding, clb) { this.forwardReq.write(chunk, encoding, clb); -}; - -require('util').inherits(ForwardStream, Writable); \ No newline at end of file +}; \ No newline at end of file diff --git a/package.json b/package.json index 8cef91a7f..e2b3f25d1 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "scripts" : { "blanket" : { "pattern": "caronte/lib" }, - "test" : "mocha -R spec test/*-test.js && npm run-script test-cov", + "test" : "mocha -R landing test/*-test.js", "test-cov" : "mocha --require blanket -R html-cov > cov/coverage.html" }, diff --git a/test/lib-caronte-common-test.js b/test/lib-caronte-common-test.js new file mode 100644 index 000000000..b489c9ff7 --- /dev/null +++ b/test/lib-caronte-common-test.js @@ -0,0 +1,33 @@ +var common = require('../lib/caronte/common'), + expect = require('expect.js'); + +describe('lib/caronte/common.js', function() { + describe('#setupOutgoing', function() { + it('should setup the right headers', function() { + var outgoing = {}; + common.setupOutgoing(outgoing, + { + host : 'hey', + hostname : 'how', + socketPath: 'are', + port : 'you', + agent : '?' + }, + { + method : 'i', + path : 'am', + headers : 'proxy' + }); + + expect(outgoing.host).to.eql('hey'); + expect(outgoing.hostname).to.eql('how'); + expect(outgoing.socketPath).to.eql('are'); + expect(outgoing.port).to.eql('you'); + expect(outgoing.agent).to.eql('?'); + + expect(outgoing.method).to.eql('i'); + expect(outgoing.path).to.eql('am'); + expect(outgoing.headers).to.eql('proxy') + }); + }); +}); \ No newline at end of file From 4f24664e8a50aa9b9a3ea155d067b85f94a8c81b Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 9 Aug 2013 19:37:58 +0200 Subject: [PATCH 010/210] [api] add draft for proxystream --- lib/caronte/streams/forward.js | 2 +- lib/caronte/streams/proxy.js | 39 +++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js index b7e8ccd03..f1f85c7e5 100644 --- a/lib/caronte/streams/forward.js +++ b/lib/caronte/streams/forward.js @@ -25,7 +25,7 @@ function ForwardStream() { Writable.call(this); - this.once('pipe', function() { self.onPipe() }); + this.once('pipe', function(pipe) { self.onPipe(pipe) }); this.once('finish', function() { self.onFinish() }); } diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index 8d1f0672f..713550c84 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -1,3 +1,40 @@ +var Duplex = require('stream').Duplex, + common = require('../common'), + http = require('http'), + https = require('https'); + function ProxyStream() { + var self = this; + + Duplex.call(this); + + this.once('pipe', function(pipe) { self.onPipe(pipe); }); + this.once('finish', function() { self.onFinish(); }); +} + +ProxyStream.prototype.onPipe = function(request) { + var self = this; + + this.proxyReq = (options.ssl ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, request) + ); + + this.proxyReq.once('response', function(response) { + self.onResponse(response); + }) + this.proxyReq.on('error', function() {}); // XXX TODO: add error handling +} + +ProxyStream.prototype.onFinish = function() { -} \ No newline at end of file +} + +ProxyStream.prototype.onResponse = function() { + +} + +ProxyStream.prototype._read = function() {} + +ProxyStream.prototype._write = function() {} + +require('util').inherits(ForwardStream, Duplex); \ No newline at end of file From 6a4294cbdfe85fa162969b1393032adc9d418441 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 9 Aug 2013 19:40:40 +0200 Subject: [PATCH 011/210] [feature] implement _write and _read --- lib/caronte/streams/proxy.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index 713550c84..c6b75c498 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -29,12 +29,18 @@ ProxyStream.prototype.onFinish = function() { } -ProxyStream.prototype.onResponse = function() { - +ProxyStream.prototype.onResponse = function(proxyRes) { + this.proxyRes = proxyRes; } -ProxyStream.prototype._read = function() {} +ProxyStream.prototype._write = function(chunk, encoding, callback) { + this.proxyReq.write(chunk, encoding, callback); +}; + +ProxyStream.prototype._read = function(size) { + var chunk = (this.proxyRes ? this.proxyRes.read(size) : '') || ''; -ProxyStream.prototype._write = function() {} + this.push(chunk); +}; require('util').inherits(ForwardStream, Duplex); \ No newline at end of file From 2e7343d728a3187d48821b88ec2e2d4699bb2afe Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 9 Aug 2013 20:52:21 +0200 Subject: [PATCH 012/210] [fix] making @jcrugzz a happy camper --- lib/caronte/streams/proxy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index c6b75c498..10afdaec1 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -21,7 +21,7 @@ ProxyStream.prototype.onPipe = function(request) { this.proxyReq.once('response', function(response) { self.onResponse(response); - }) + }); this.proxyReq.on('error', function() {}); // XXX TODO: add error handling } From bd3df45010f282997cae3a699c7ecb885c01bdf8 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 9 Aug 2013 20:52:55 +0200 Subject: [PATCH 013/210] [fix] woops --- lib/caronte/streams/proxy.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index 10afdaec1..5112b436e 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -12,6 +12,8 @@ function ProxyStream() { this.once('finish', function() { self.onFinish(); }); } +require('util').inherits(ProxyStream, Duplex); + ProxyStream.prototype.onPipe = function(request) { var self = this; @@ -43,4 +45,3 @@ ProxyStream.prototype._read = function(size) { this.push(chunk); }; -require('util').inherits(ForwardStream, Duplex); \ No newline at end of file From 9ab8749a9bec33b49c495975e8364336ad7be1a3 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 10 Aug 2013 20:41:25 +0200 Subject: [PATCH 014/210] [feature] started working on error propagation, kinda sucks, gotta think it over --- lib/caronte/index.js | 2 +- lib/caronte/passes/web.js | 7 ++++--- lib/caronte/streams/forward.js | 9 +++++++-- lib/caronte/streams/proxy.js | 33 ++++++++++++++++++++++++--------- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 0b9f544c0..88f7368a7 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -39,7 +39,7 @@ function createRightProxy(type) { var event = ev + pass.name.toLowerCase(); self.emit(event + 'begin', req, res); - pass(req, res, options); + pass(req, res, options, self); self.emit(event + 'end'); }); diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index ea9e2d91b..97c69afb2 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -79,17 +79,18 @@ function XHeaders(req, res, options) { * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object * @param {Object} Options Config object passed to the proxy + * @param {Object} Instance Proxy object that emits events * * @api private */ -function stream(req, res, options) { +function stream(req, res, options, instance) { if(options.forward) { - req.pipe(new ForwardStream(options.forward)); + req.pipe(new ForwardStream(options, instance)); } if(options.target) { - return req.pipe(new ProxyStream(res, options)).pipe(res); + return req.pipe(new ProxyStream(options, res, instance)).pipe(res); } res.end(); diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js index f1f85c7e5..a3fe955b4 100644 --- a/lib/caronte/streams/forward.js +++ b/lib/caronte/streams/forward.js @@ -20,9 +20,12 @@ module.exports = ForwardStream; * @api private */ -function ForwardStream() { +function ForwardStream(options) { var self = this; + self.options = options; + self.res = res; + Writable.call(this); this.once('pipe', function(pipe) { self.onPipe(pipe) }); @@ -48,10 +51,12 @@ ForwardStream.prototype.onPipe = function(request) { this.forwardReq = (options.ssl ? https : http).request( common.setupOutgoing(options.ssl || {}, options, request) ); + + this.forwardReq.on('error', function() {}); /** Fire and forget */ }; /** - * Closes forwarded request when `pipe` is finished + * Closes forwarded request when `pipe` is finished. * * Examples: * diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index 5112b436e..dad221639 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -3,7 +3,11 @@ var Duplex = require('stream').Duplex, http = require('http'), https = require('https'); -function ProxyStream() { +function ProxyStream(options, res, instance) { + this.options = options; + this.res = res; + this.instance = instance; + var self = this; Duplex.call(this); @@ -14,26 +18,37 @@ function ProxyStream() { require('util').inherits(ProxyStream, Duplex); -ProxyStream.prototype.onPipe = function(request) { +ProxyStream.prototype.onPipe = function(req) { + this.req = req; + var self = this; this.proxyReq = (options.ssl ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, request) + common.setupOutgoing(options.ssl || {}, options, req) ); - this.proxyReq.once('response', function(response) { - self.onResponse(response); + this.proxyReq.once('response', function(proxyRes) { + self.onResponse(proxyRes); }); - this.proxyReq.on('error', function() {}); // XXX TODO: add error handling -} + this.proxyReq.on('error', function(e) { + self.onError(e); + }); +}; ProxyStream.prototype.onFinish = function() { -} +}; ProxyStream.prototype.onResponse = function(proxyRes) { this.proxyRes = proxyRes; -} +}; + +ProxyStream.prototype.onError = function(e) { + if(this.instance.emit('error', this.req, this.res, e)) return; + + this.res.writeHead(500, { 'Content-Type': 'text/plain' }); + this.res.end('Internal Server Error'); +}; ProxyStream.prototype._write = function(chunk, encoding, callback) { this.proxyReq.write(chunk, encoding, callback); From d4f0da898e5e8a2d6740e50a7fc34576435e1132 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 20 Aug 2013 16:09:37 +0200 Subject: [PATCH 015/210] [fix] some stuff start debugging proxystream --- .gitignore | 1 + lib/caronte.js | 8 ++++- lib/caronte/common.js | 4 +-- lib/caronte/index.js | 13 ++++---- lib/caronte/streams/proxy.js | 59 +++++++++++++++++++++++++++++++----- 5 files changed, 68 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 1a07e33df..9ace78b32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules *.swp cov +ttest.js diff --git a/lib/caronte.js b/lib/caronte.js index c05701c91..81a25e469 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -31,6 +31,7 @@ proxy.createProxyServer = function createProxyServer(options) { " ssl : ", " ws : ", " xfwd : ", + " maxSock: ", " } ", " ", "NOTE: `options.ws` and `options.ssl` are optional. ", @@ -42,6 +43,9 @@ proxy.createProxyServer = function createProxyServer(options) { ['target', 'forward'].forEach(function(key) { if(!options[key]) return; options[key] = url.parse(options[key]); + + options[key].maxSockets = options.maxSock; + options[key].agent = new (options.ssl ? https.Agent : http.Agent)(options[key]); }); return { @@ -49,12 +53,14 @@ proxy.createProxyServer = function createProxyServer(options) { web : caronte.createWebProxy(options), ws : caronte.createWsProxy(options), listen : function listen(port) { - var server = options.ssl ? http.createServer(this.web) : https.createServer(options.ssl, this.web); + var server = options.ssl ? https.createServer(options.ssl, this.web) : http.createServer(this.web); if(options.ws) { server.on('upgrade', this.ws); } + server.listen(port); + return server; } }; diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 7028bb171..59a1cf94b 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -20,8 +20,8 @@ var common = exports; */ common.setupOutgoing = function(outgoing, options, req) { - ['host', 'hostname', 'port', 'socketPath', 'agent'].forEach( - function(e) { outgoing[e] = options[e]; } + ['host', 'hostname', 'port', 'socketPath'/*, 'agent'*/].forEach( + function(e) { outgoing[e] = options.target[e]; } ); ['method', 'path', 'headers'].forEach( diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 88f7368a7..bf6160976 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -22,7 +22,8 @@ caronte.createWsProxy = createRightProxy('ws'); */ function createRightProxy(type) { - passes = type === 'ws' ? ws : web; + var passes = (type === 'ws') ? ws : web; + return function(options) { passes = Object.keys(passes).map(function(pass) { @@ -33,17 +34,17 @@ function createRightProxy(type) { var self = this, ev = 'caronte:' + type + ':'; - self.emit(ev + 'begin', req, res); + //self.emit(ev + 'begin', req, res); passes.forEach(function(pass) { - var event = ev + pass.name.toLowerCase(); + var evnt = ev + pass.name.toLowerCase(); - self.emit(event + 'begin', req, res); + //self.emit(evnt + 'begin', req, res); pass(req, res, options, self); - self.emit(event + 'end'); + //self.emit(evnt + 'end'); }); - self.emit(ev + 'end'); + //self.emit(ev + 'end'); }; }; } diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index dad221639..6d67218f0 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -4,14 +4,14 @@ var Duplex = require('stream').Duplex, https = require('https'); function ProxyStream(options, res, instance) { + Duplex.call(this); + this.options = options; this.res = res; this.instance = instance; var self = this; - Duplex.call(this); - this.once('pipe', function(pipe) { self.onPipe(pipe); }); this.once('finish', function() { self.onFinish(); }); } @@ -23,11 +23,12 @@ ProxyStream.prototype.onPipe = function(req) { var self = this; - this.proxyReq = (options.ssl ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req) + this.proxyReq = (self.options.ssl ? https : http).request( + common.setupOutgoing(self.options.ssl || {}, self.options, req) ); - + //console.log(common.setupOutgoing(self.options.ssl || {}, self.options, req)); this.proxyReq.once('response', function(proxyRes) { + console.log(proxyRes); self.onResponse(proxyRes); }); this.proxyReq.on('error', function(e) { @@ -36,16 +37,57 @@ ProxyStream.prototype.onPipe = function(req) { }; ProxyStream.prototype.onFinish = function() { - + this.proxyReq.end(); }; ProxyStream.prototype.onResponse = function(proxyRes) { this.proxyRes = proxyRes; + + // rewrite + if(req.httpVersion === '1.0') { + res.headers.connection = req.headers.connection || 'close'; + } + else if(!res.headers.connection) { + res.headers.connection = req.headers.connection || 'keep-alive'; + } + + if(req.httpVersion === '1.0' || (req.method === 'DELETE' && !req.headers['content-length'])) { + delete res.headers['transfer-encoding']; + } + + if(~[301,302].indexOf(res.statusCode) && typeof res.headers.location !== 'undefined') { + var location = url.parse(res.headers.location); + if ( + location.host === req.headers.host && + ( + source.https && !target.https || + target.https && !source.https + ) + ) { + res.headers.location = res.headers.location.replace(/^https\:/, 'http:'); + } + } + + self.emit('proxyResponse', req, response, res); + + Object.keys(res.headers).forEach(function (key) { + response.setHeader(key, res.headers[key]); + }); + response.writeHead(response.statusCode); + + res.on('readable', function() { + self.read(0); + }); + + res.on('end', function() { + self.push(null); + }); + self.emit('readable'); }; ProxyStream.prototype.onError = function(e) { - if(this.instance.emit('error', this.req, this.res, e)) return; - + if(this.instance.emit('proxyError', this.req, this.res, e)) return; + this.res.writeHead(500, { 'Content-Type': 'text/plain' }); this.res.end('Internal Server Error'); }; @@ -60,3 +102,4 @@ ProxyStream.prototype._read = function(size) { this.push(chunk); }; +module.exports = ProxyStream; \ No newline at end of file From 356f43d719998d135e0fc404ac8508e330cf1e5b Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 20 Aug 2013 17:32:29 +0200 Subject: [PATCH 016/210] [fix] ProxyStraem now works --- lib/caronte/streams/proxy.js | 82 ++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index 6d67218f0..2903c07c6 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -28,7 +28,6 @@ ProxyStream.prototype.onPipe = function(req) { ); //console.log(common.setupOutgoing(self.options.ssl || {}, self.options, req)); this.proxyReq.once('response', function(proxyRes) { - console.log(proxyRes); self.onResponse(proxyRes); }); this.proxyReq.on('error', function(e) { @@ -43,46 +42,47 @@ ProxyStream.prototype.onFinish = function() { ProxyStream.prototype.onResponse = function(proxyRes) { this.proxyRes = proxyRes; - // rewrite - if(req.httpVersion === '1.0') { - res.headers.connection = req.headers.connection || 'close'; - } - else if(!res.headers.connection) { - res.headers.connection = req.headers.connection || 'keep-alive'; - } - - if(req.httpVersion === '1.0' || (req.method === 'DELETE' && !req.headers['content-length'])) { - delete res.headers['transfer-encoding']; - } - - if(~[301,302].indexOf(res.statusCode) && typeof res.headers.location !== 'undefined') { - var location = url.parse(res.headers.location); - if ( - location.host === req.headers.host && - ( - source.https && !target.https || - target.https && !source.https - ) - ) { - res.headers.location = res.headers.location.replace(/^https\:/, 'http:'); - } - } - - self.emit('proxyResponse', req, response, res); - - Object.keys(res.headers).forEach(function (key) { - response.setHeader(key, res.headers[key]); - }); - response.writeHead(response.statusCode); - - res.on('readable', function() { - self.read(0); - }); - - res.on('end', function() { - self.push(null); - }); - self.emit('readable'); + var self = this; + + if(this.req.httpVersion === '1.0') { + proxyRes.headers.connection = this.req.headers.connection || 'close'; + } + else if(!proxyRes.headers.connection) { + proxyRes.headers.connection = this.req.headers.connection || 'keep-alive'; + } + + if(this.req.httpVersion === '1.0' || (this.req.method === 'DELETE' && !this.req.headers['content-length'])) { + delete proxyRes.headers['transfer-encoding']; + } + + /*if(~[301,302].indexOf(this.res.statusCode) && typeof this.res.headers.location !== 'undefined') { + var location = url.parse(this.res.headers.location); + if ( + location.host === this.req.headers.host && + ( + source.https && !target.https || + target.https && !source.https + ) + ) { + this.res.headers.location = this.res.headers.location.replace(/^https\:/, 'http:'); + } + }*/ + + Object.keys(proxyRes.headers).forEach(function (key) { + self.res.setHeader(key, proxyRes.headers[key]); + }); + + this.res.writeHead(proxyRes.statusCode); + + proxyRes.on('readable', function() { + self.read(0); + }); + + proxyRes.on('end', function() { + self.push(null); + }); + + self.emit('readable'); }; ProxyStream.prototype.onError = function(e) { From d40e4beb62381b962b6cf3254451de0a39f182b1 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 21 Aug 2013 17:37:38 +0200 Subject: [PATCH 017/210] [test] passes/web.js (first 2 funcs) --- .gitignore | 1 + test/lib-caronte-common-test.js | 14 ++++++++------ test/lib-caronte-passes-web-test.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 test/lib-caronte-passes-web-test.js diff --git a/.gitignore b/.gitignore index 9ace78b32..52241f829 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules *.swp cov ttest.js +notes diff --git a/test/lib-caronte-common-test.js b/test/lib-caronte-common-test.js index b489c9ff7..3c2ccd109 100644 --- a/test/lib-caronte-common-test.js +++ b/test/lib-caronte-common-test.js @@ -7,11 +7,13 @@ describe('lib/caronte/common.js', function() { var outgoing = {}; common.setupOutgoing(outgoing, { - host : 'hey', - hostname : 'how', - socketPath: 'are', - port : 'you', - agent : '?' + target: { + host : 'hey', + hostname : 'how', + socketPath: 'are', + port : 'you', + agent : '?' + } }, { method : 'i', @@ -23,7 +25,7 @@ describe('lib/caronte/common.js', function() { expect(outgoing.hostname).to.eql('how'); expect(outgoing.socketPath).to.eql('are'); expect(outgoing.port).to.eql('you'); - expect(outgoing.agent).to.eql('?'); + //expect(outgoing.agent).to.eql('?'); expect(outgoing.method).to.eql('i'); expect(outgoing.path).to.eql('am'); diff --git a/test/lib-caronte-passes-web-test.js b/test/lib-caronte-passes-web-test.js new file mode 100644 index 000000000..b814612ed --- /dev/null +++ b/test/lib-caronte-passes-web-test.js @@ -0,0 +1,28 @@ +var caronte = require('../lib/caronte/passes/web'), + expect = require('expect.js'); + +describe('lib/caronte/passes/web.js', function() { + describe('#deleteLength', function() { + it('should change `content-length`', function() { + var stubRequest = { + method: 'DELETE', + headers: {} + }; + caronte.deleteLength(stubRequest, {}, {}); + expect(stubRequest.headers['content-length']).to.eql('0'); + }) + }); + + describe('#timeout', function() { + it('should set timeout on the socket', function() { + var done = false, stubRequest = { + socket: { + setTimeout: function(value) { done = value; } + } + } + + caronte.timeout(stubRequest, {}, { timeout: 5000}); + expect(done).to.eql(5000); + }); + }); +}); From c02b721321c455bc287c3fed6b9b21392ce2fc70 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 26 Aug 2013 00:21:30 -0500 Subject: [PATCH 018/210] [test] passes/web.js XHeaders func --- test/lib-caronte-passes-web-test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/lib-caronte-passes-web-test.js b/test/lib-caronte-passes-web-test.js index b814612ed..4ec5ca1a2 100644 --- a/test/lib-caronte-passes-web-test.js +++ b/test/lib-caronte-passes-web-test.js @@ -25,4 +25,21 @@ describe('lib/caronte/passes/web.js', function() { expect(done).to.eql(5000); }); }); + + describe('#XHeaders', function () { + var stubRequest = { + connection: { + remoteAddress: '192.168.1.2', + remotePort: '8080' + }, + headers: {} + } + + it('set the correct x-forwarded-* headers', function () { + caronte.XHeaders(stubRequest, {}, { xfwd: true }); + expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); + expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); + expect(stubRequest.headers['x-forwarded-proto']).to.be('http'); + }); + }); }); From 2fac7b9b009b12a940efb22de3af6db55ee686a9 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 26 Aug 2013 16:34:32 -0500 Subject: [PATCH 019/210] [test] added the lib/caronte/streams/forward.js initial test, one test pending --- lib/caronte/streams/forward.js | 4 ++- test/lib-caronte-streams-forward-test.js | 37 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 test/lib-caronte-streams-forward-test.js diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js index a3fe955b4..665a91927 100644 --- a/lib/caronte/streams/forward.js +++ b/lib/caronte/streams/forward.js @@ -24,7 +24,9 @@ function ForwardStream(options) { var self = this; self.options = options; - self.res = res; + // To uncomment the line below, please see + // https://github.com/yawnt/caronte/commit/9ab8749a9bec33b49c495975e8364336ad7be1a3#commitcomment-3947117 + //self.res = res; Writable.call(this); diff --git a/test/lib-caronte-streams-forward-test.js b/test/lib-caronte-streams-forward-test.js new file mode 100644 index 000000000..5e74a1efd --- /dev/null +++ b/test/lib-caronte-streams-forward-test.js @@ -0,0 +1,37 @@ +var ForwardStream = require('../lib/caronte/streams/forward'), + expect = require('expect.js'), + Writable = require('stream').Writable, + http = require('http'); + + +describe('lib/caronte/passes/web.js', function () { + describe('forward stream constructor', function () { + it('should be an instance of Writable stream and get the correct options and methods', function () { + var stubOptions = { + key: 'value' + }; + var forwardProxy = new ForwardStream(stubOptions); + + expect(forwardProxy).to.be.a(Writable); + expect(forwardProxy.options).to.eql({ key: 'value' }); + expect(forwardProxy.onPipe).to.be.a('function'); + expect(forwardProxy.onFinish).to.be.a('function'); + expect(forwardProxy._events).to.have.property('pipe'); + expect(forwardProxy._events).to.have.property('finish'); + }); + }); + + describe('should pipe the request and finish it', function () { + it('should make the request on pipe and finish it'); + var stubOptions = { + target: { + hostname : 'www.google.com', + port : '80', + path : '/' + } + }; + + var forwardProxy = new ForwardStream({}); + + }); +}); \ No newline at end of file From 8fc33893672d26013c2b2ff396b777bcf1751527 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 28 Aug 2013 14:49:27 +0200 Subject: [PATCH 020/210] [test] add test for forwardstream --- lib/caronte/common.js | 5 ++-- lib/caronte/index.js | 1 - lib/caronte/streams/forward.js | 6 ++--- test/lib-caronte-streams-forward-test.js | 30 +++++++++++++++--------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 59a1cf94b..78b45864e 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -13,15 +13,16 @@ var common = exports; * @param {Object} Outgoing Base object to be filled with required properties * @param {Object} Options Config object passed to the proxy * @param {ClientRequest} Req Request Object + * @param {String} Forward String to select forward or target *  * @return {Object} Outgoing Object with all required properties set * * @api private */ -common.setupOutgoing = function(outgoing, options, req) { +common.setupOutgoing = function(outgoing, options, req, forward) { ['host', 'hostname', 'port', 'socketPath'/*, 'agent'*/].forEach( - function(e) { outgoing[e] = options.target[e]; } + function(e) { outgoing[e] = options[forward || 'target'][e]; } ); ['method', 'path', 'headers'].forEach( diff --git a/lib/caronte/index.js b/lib/caronte/index.js index bf6160976..6700a4396 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -33,7 +33,6 @@ function createRightProxy(type) { return function(req, res) { var self = this, ev = 'caronte:' + type + ':'; - //self.emit(ev + 'begin', req, res); passes.forEach(function(pass) { diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js index 665a91927..b1bb58f3b 100644 --- a/lib/caronte/streams/forward.js +++ b/lib/caronte/streams/forward.js @@ -50,8 +50,8 @@ require('util').inherits(ForwardStream, Writable); */ ForwardStream.prototype.onPipe = function(request) { - this.forwardReq = (options.ssl ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, request) + this.forwardReq = (this.options.ssl ? https : http).request( + common.setupOutgoing(this.options.ssl || {}, this.options, request, 'forward') ); this.forwardReq.on('error', function() {}); /** Fire and forget */ @@ -85,4 +85,4 @@ ForwardStream.prototype.onFinish = function() { ForwardStream.prototype._write = function(chunk, encoding, clb) { this.forwardReq.write(chunk, encoding, clb); -}; \ No newline at end of file +}; diff --git a/test/lib-caronte-streams-forward-test.js b/test/lib-caronte-streams-forward-test.js index 5e74a1efd..8de2afe6d 100644 --- a/test/lib-caronte-streams-forward-test.js +++ b/test/lib-caronte-streams-forward-test.js @@ -1,4 +1,5 @@ -var ForwardStream = require('../lib/caronte/streams/forward'), +var caronte = require('../'), + ForwardStream = require('../lib/caronte/streams/forward'); expect = require('expect.js'), Writable = require('stream').Writable, http = require('http'); @@ -22,16 +23,23 @@ describe('lib/caronte/passes/web.js', function () { }); describe('should pipe the request and finish it', function () { - it('should make the request on pipe and finish it'); - var stubOptions = { - target: { - hostname : 'www.google.com', - port : '80', - path : '/' - } - }; + it('should make the request on pipe and finish it', function(done) { + var result; + + var p = caronte.createProxyServer({ + forward: 'http://127.0.0.1:8080' + }).listen('8081') - var forwardProxy = new ForwardStream({}); + var s = http.createServer(function(req, res) { + expect(req.method).to.eql('GET'); + s.close(); + p.close(); + done(); + }); + s.listen('8080'); + + http.request('http://127.0.0.1:8081', function() {}).end(); + }); }); -}); \ No newline at end of file +}); From c9612798f1207a4c40b616608bf6274d79ad0e4d Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 28 Aug 2013 14:58:57 +0200 Subject: [PATCH 021/210] [test] proxystream test --- test/lib-caronte-streams-forward-test.js | 2 +- test/lib-caronte-streams-proxy-test.js | 52 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test/lib-caronte-streams-proxy-test.js diff --git a/test/lib-caronte-streams-forward-test.js b/test/lib-caronte-streams-forward-test.js index 8de2afe6d..5289856cc 100644 --- a/test/lib-caronte-streams-forward-test.js +++ b/test/lib-caronte-streams-forward-test.js @@ -5,7 +5,7 @@ var caronte = require('../'), http = require('http'); -describe('lib/caronte/passes/web.js', function () { +describe('lib/caronte/streams/forward.js', function () { describe('forward stream constructor', function () { it('should be an instance of Writable stream and get the correct options and methods', function () { var stubOptions = { diff --git a/test/lib-caronte-streams-proxy-test.js b/test/lib-caronte-streams-proxy-test.js new file mode 100644 index 000000000..0c0debb8a --- /dev/null +++ b/test/lib-caronte-streams-proxy-test.js @@ -0,0 +1,52 @@ +var caronte = require('../'), + ProxyStream = require('../lib/caronte/streams/proxy'); + expect = require('expect.js'), + Duplex = require('stream').Duplex, + http = require('http'); + + +describe('lib/caronte/streams/proxy.js', function () { + describe('proxy stream constructor', function () { + it('should be an instance of Duplex stream and get the correct options and methods', function () { + var stubOptions = { + key: 'value' + }; + var proxyStream = new ProxyStream(stubOptions); + + expect(proxyStream).to.be.a(Duplex); + expect(proxyStream.options).to.eql({ key: 'value' }); + expect(proxyStream.onPipe).to.be.a('function'); + expect(proxyStream.onFinish).to.be.a('function'); + expect(proxyStream._events).to.have.property('pipe'); + expect(proxyStream._events).to.have.property('finish'); + }); + }); + + describe('should pipe the request and finish it', function () { + it('should make the request on pipe and finish it', function(done) { + var result; + + var p = caronte.createProxyServer({ + target: 'http://127.0.0.1:8080' + }).listen('8081'); + + var s = http.createServer(function(req, res) { + expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1'); + s.close(); + p.close(); + done(); + }); + + s.listen('8080'); + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'POST', + headers: { + 'x-forwarded-for': '127.0.0.1' + } + }, function() {}).end(); + }); + }); +}); From abf1d90fdf05a17ebe05a3e90d464a592e0aee69 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 28 Aug 2013 15:22:04 +0200 Subject: [PATCH 022/210] [fix] use agent pool --- lib/caronte.js | 2 +- lib/caronte/common.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/caronte.js b/lib/caronte.js index 81a25e469..3ccd2517a 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -45,7 +45,7 @@ proxy.createProxyServer = function createProxyServer(options) { options[key] = url.parse(options[key]); options[key].maxSockets = options.maxSock; - options[key].agent = new (options.ssl ? https.Agent : http.Agent)(options[key]); + options[key].agent = new (options.ssl ? https.Agent : http.Agent)(options[key].maxSockets || 100); }); return { diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 78b45864e..68f4a9292 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -21,7 +21,7 @@ var common = exports; */ common.setupOutgoing = function(outgoing, options, req, forward) { - ['host', 'hostname', 'port', 'socketPath'/*, 'agent'*/].forEach( + ['host', 'hostname', 'port', 'socketPath', 'agent'].forEach( function(e) { outgoing[e] = options[forward || 'target'][e]; } ); From 27df8d72ad86d02cfce00a6e5c183d93dd50f97e Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 28 Aug 2013 13:45:09 -0500 Subject: [PATCH 023/210] [test] testing the onResponse proxy method --- test/lib-caronte-streams-proxy-test.js | 48 +++++++++++++++++++++----- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/test/lib-caronte-streams-proxy-test.js b/test/lib-caronte-streams-proxy-test.js index 0c0debb8a..a68f99141 100644 --- a/test/lib-caronte-streams-proxy-test.js +++ b/test/lib-caronte-streams-proxy-test.js @@ -22,22 +22,20 @@ describe('lib/caronte/streams/proxy.js', function () { }); }); - describe('should pipe the request and finish it', function () { + describe('caronte createProxyServer() method', function () { it('should make the request on pipe and finish it', function(done) { - var result; - - var p = caronte.createProxyServer({ + var proxy = caronte.createProxyServer({ target: 'http://127.0.0.1:8080' }).listen('8081'); - var s = http.createServer(function(req, res) { + var source = http.createServer(function(req, res) { expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1'); - s.close(); - p.close(); + source.close(); + proxy.close(); done(); }); - s.listen('8080'); + source.listen('8080'); http.request({ hostname: '127.0.0.1', @@ -49,4 +47,38 @@ describe('lib/caronte/streams/proxy.js', function () { }, function() {}).end(); }); }); + + describe('caronte createProxyServer() method with response', function () { + it('should make the request, handle response and finish it', function(done) { + var proxy = caronte.createProxyServer({ + target: 'http://127.0.0.1:8080' + }).listen('8081'); + + var source = http.createServer(function(req, res) { + expect(req.method).to.eql('GET'); + res.writeHead(200, {'Content-Type': 'text/plain'}) + res.end('Hello from ' + source.address().port); + }); + + source.listen('8080'); + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'GET', + }, function(res) { + expect(res.statusCode).to.eql(200); + + res.on('data', function (data) { + expect(data.toString()).to.eql('Hello from 8080'); + }); + + res.on('end', function () { + source.close(); + proxy.close(); + done(); + }); + }).end(); + }); + }); }); From b85aa16e75401a223a947cde444d42cf7eeafb67 Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 28 Aug 2013 14:01:55 -0500 Subject: [PATCH 024/210] [test] test onError part, proxying to no where --- test/lib-caronte-streams-proxy-test.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/lib-caronte-streams-proxy-test.js b/test/lib-caronte-streams-proxy-test.js index a68f99141..671f80cc0 100644 --- a/test/lib-caronte-streams-proxy-test.js +++ b/test/lib-caronte-streams-proxy-test.js @@ -81,4 +81,29 @@ describe('lib/caronte/streams/proxy.js', function () { }).end(); }); }); + + describe('caronte createProxyServer() method with error response', function () { + it('should make the request and response with error', function(done) { + var proxy = caronte.createProxyServer({ + target: 'http://127.0.0.1:8080' + }).listen('8081'); + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'GET', + }, function(res) { + expect(res.statusCode).to.eql(500); + + res.on('data', function (data) { + expect(data.toString()).to.eql('Internal Server Error'); + }); + + res.on('end', function () { + proxy.close(); + done(); + }); + }).end(); + }); + }); }); From a6256cac1df1739e3da78fe5f0cf122ef7ce6b14 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 3 Sep 2013 20:56:18 +0200 Subject: [PATCH 025/210] [fix] short circuit --- lib/caronte/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 6700a4396..eaa55872d 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -35,12 +35,14 @@ function createRightProxy(type) { ev = 'caronte:' + type + ':'; //self.emit(ev + 'begin', req, res); - passes.forEach(function(pass) { + + passes.every(function(pass) { var evnt = ev + pass.name.toLowerCase(); //self.emit(evnt + 'begin', req, res); - pass(req, res, options, self); + var val = pass(req, res, options, self); //self.emit(evnt + 'end'); + return val; }); //self.emit(ev + 'end'); From 4480699d3a2a5080c051e7b8a100689fd1f58657 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 3 Sep 2013 20:57:22 +0200 Subject: [PATCH 026/210] [fix] use some --- lib/caronte/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index eaa55872d..21da74481 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -36,7 +36,7 @@ function createRightProxy(type) { //self.emit(ev + 'begin', req, res); - passes.every(function(pass) { + passes.some(function(pass) { var evnt = ev + pass.name.toLowerCase(); //self.emit(evnt + 'begin', req, res); From 79f7f99528661162ae4153856888f078f666e017 Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 3 Sep 2013 14:09:35 -0500 Subject: [PATCH 027/210] [lib] initial draft to websockets passes --- lib/caronte/passes/ws.js | 53 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 54e117ba5..e91002e46 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -1 +1,52 @@ -// ws \ No newline at end of file +/*! + * Array of passes. + * + * A `pass` is just a function that is executed on `req, res, options` + * so that you can easily add new checks while still keeping the base + * flexible. + */ + +/* + * Websockets Passes + * + */ + +var passes = exports; + +[ + /* + * WebSocket requests must have the `GET` method and + * the `upgrade:websocket` header + */ + function checkMethodAndHeader (req, res, options) { + if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') { + req.end(); + // Return true to prevent the next passes to be executed + return true; + } + }, + + /** + * Sets `x-forwarded-*` headers if specified in config. + * + */ + + function XHeaders(req, res, options) { + if(!options.xfwd) return; + + var values = { + for : req.connection.remoteAddress || req.socket.remoteAddress, + port : req.connection.remotePort || req.socket.remotePort, + proto: req.connection.pair ? 'wss' : 'ws' + }; + + ['for', 'port', 'proto'].forEach(function(header) { + req.headers['x-forwarded-' + header] = + (req.headers['x-forwarded-' + header] || '') + + (req.headers['x-forwarded-' + header] ? ',' : '') + + values[header] + }); + } +].forEach(function(func) { + passes[func.name] = func; +}); \ No newline at end of file From 07551c63e428551e5d6e52362efd9620a14c71b4 Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 5 Sep 2013 17:28:25 +0200 Subject: [PATCH 028/210] websocket draft --- lib/caronte/passes/ws.js | 81 ++++++++++++++++++-------------- lib/caronte/streams/websocket.js | 60 +++++++++++++++++++++++ 2 files changed, 106 insertions(+), 35 deletions(-) create mode 100644 lib/caronte/streams/websocket.js diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index e91002e46..d89f752a1 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -14,39 +14,50 @@ var passes = exports; [ - /* - * WebSocket requests must have the `GET` method and - * the `upgrade:websocket` header - */ - function checkMethodAndHeader (req, res, options) { - if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') { - req.end(); - // Return true to prevent the next passes to be executed - return true; - } - }, - - /** - * Sets `x-forwarded-*` headers if specified in config. - * - */ - - function XHeaders(req, res, options) { - if(!options.xfwd) return; - - var values = { - for : req.connection.remoteAddress || req.socket.remoteAddress, - port : req.connection.remotePort || req.socket.remotePort, - proto: req.connection.pair ? 'wss' : 'ws' - }; - - ['for', 'port', 'proto'].forEach(function(header) { - req.headers['x-forwarded-' + header] = - (req.headers['x-forwarded-' + header] || '') + - (req.headers['x-forwarded-' + header] ? ',' : '') + - values[header] - }); +/** + * WebSocket requests must have the `GET` method and + * the `upgrade:websocket` header + */ + +function checkMethodAndHeader (req, res, options) { + if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') { + req.end(); + + return true; } -].forEach(function(func) { - passes[func.name] = func; -}); \ No newline at end of file +}, + +/** + * Sets `x-forwarded-*` headers if specified in config. + * + */ + +function XHeaders(req, res, options) { + if(!options.xfwd) return; + + var values = { + for : req.connection.remoteAddress || req.socket.remoteAddress, + port : req.connection.remotePort || req.socket.remotePort, + proto: req.connection.pair ? 'wss' : 'ws' + }; + + ['for', 'port', 'proto'].forEach(function(header) { + req.headers['x-forwarded-' + header] = + (req.headers['x-forwarded-' + header] || '') + + (req.headers['x-forwarded-' + header] ? ',' : '') + + values[header] + }); +}, + +/** + * + * + */ +function stream(req, res, options, instance) { + req.pipe(new WebsocketStream(options, instance)).pipe(res); +} + +] // <-- + .forEach(function(func) { + passes[func.name] = func; + }); diff --git a/lib/caronte/streams/websocket.js b/lib/caronte/streams/websocket.js new file mode 100644 index 000000000..aa9a8cfd8 --- /dev/null +++ b/lib/caronte/streams/websocket.js @@ -0,0 +1,60 @@ +var Duplex = require('stream').Duplex, + common = require('common'), + http = require('http'), + https = require('https'); + +function WebsocketStream(options, res, instance) { + Duplex.call(this); + + this.options = options; + this.res = res; + this.instance = intance; + + var self = this; + + this.once('pipe', function(pipe) { self.onPipe(pipe); }); + this.once('finish', function() { self.onFinish(); }); +} + +require('util').inherits(WebsocketStream, Duplex); + +WebsocketStream.prototype.onPipe = function(req) { + this.req = req; + + var self = this; + + this.proxyReq = (self.options.ssl ? https : http).request( + common.setupOutgoing(self.options.ssl || {}, self.options, req) + ); + + this.proxyReq.once('response', function(proxyRes) { + self.onResponse(proxyRes); + }); + this.proxyReq.on('error', function(e) { + self.onError(e); + }); +}; + +WebsocketStream.prototye.onFinish = function() { + +}; + +WebsocketStream.prototype.onResponse = function(proxyRes) { + +}; + +WebsocketStream.prototype.onError = function(e) { + +}; + + +WebsocketStream.prototype._write = function(chunk, encoding, callback) { + +}; + +WebsocketStream.prototype._read = function(size) { + +}; + + +WebsocketStream.prototype From 3a39e444ff68a74f6b586f0736bbd3f8a2511ca5 Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 5 Sep 2013 17:44:23 +0200 Subject: [PATCH 029/210] new error propagation --- lib/caronte.js | 6 +++++- lib/caronte/index.js | 11 ++++++----- lib/caronte/passes/web.js | 9 ++++----- lib/caronte/streams/proxy.js | 7 +++---- lib/caronte/streams/websocket.js | 4 +++- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/caronte.js b/lib/caronte.js index 3ccd2517a..ed5e4253a 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -48,8 +48,9 @@ proxy.createProxyServer = function createProxyServer(options) { options[key].agent = new (options.ssl ? https.Agent : http.Agent)(options[key].maxSockets || 100); }); + options.ee = new events.EventEmitter2({ wildcard: true, delimiter: ':' }); + return { - __proto__: new events.EventEmitter2({ wildcard: true, delimiter: ':' }), web : caronte.createWebProxy(options), ws : caronte.createWsProxy(options), listen : function listen(port) { @@ -62,6 +63,9 @@ proxy.createProxyServer = function createProxyServer(options) { server.listen(port); return server; + }, + emitter : function() { + return options.ee; } }; }; diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 21da74481..747a64bab 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -33,19 +33,20 @@ function createRightProxy(type) { return function(req, res) { var self = this, ev = 'caronte:' + type + ':'; - //self.emit(ev + 'begin', req, res); + options.ee.emit(ev + 'begin', req, res); passes.some(function(pass) { var evnt = ev + pass.name.toLowerCase(); - //self.emit(evnt + 'begin', req, res); - var val = pass(req, res, options, self); - //self.emit(evnt + 'end'); + options.ee.emit(evnt + 'begin', req, res); + var val = pass(req, res, options); + options.ee.emit(evnt + 'end'); + return val; }); - //self.emit(ev + 'end'); + options.ee.emit(ev + 'end'); }; }; } diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index 97c69afb2..dc815abd0 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -79,18 +79,17 @@ function XHeaders(req, res, options) { * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object * @param {Object} Options Config object passed to the proxy - * @param {Object} Instance Proxy object that emits events * * @api private */ -function stream(req, res, options, instance) { +function stream(req, res, options) { if(options.forward) { - req.pipe(new ForwardStream(options, instance)); + req.pipe(new ForwardStream(options)); } if(options.target) { - return req.pipe(new ProxyStream(options, res, instance)).pipe(res); + return req.pipe(new ProxyStream(options, res)).pipe(res); } res.end(); @@ -99,4 +98,4 @@ function stream(req, res, options, instance) { ] // <-- .forEach(function(func) { passes[func.name] = func; - }); \ No newline at end of file + }); diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index 2903c07c6..2f939876e 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -3,12 +3,11 @@ var Duplex = require('stream').Duplex, http = require('http'), https = require('https'); -function ProxyStream(options, res, instance) { +function ProxyStream(options, res) { Duplex.call(this); this.options = options; this.res = res; - this.instance = instance; var self = this; @@ -86,7 +85,7 @@ ProxyStream.prototype.onResponse = function(proxyRes) { }; ProxyStream.prototype.onError = function(e) { - if(this.instance.emit('proxyError', this.req, this.res, e)) return; + if(this.options.ee.emit('proxyError', this.req, this.res, e)) return; this.res.writeHead(500, { 'Content-Type': 'text/plain' }); this.res.end('Internal Server Error'); @@ -102,4 +101,4 @@ ProxyStream.prototype._read = function(size) { this.push(chunk); }; -module.exports = ProxyStream; \ No newline at end of file +module.exports = ProxyStream; diff --git a/lib/caronte/streams/websocket.js b/lib/caronte/streams/websocket.js index aa9a8cfd8..2d2dc69d6 100644 --- a/lib/caronte/streams/websocket.js +++ b/lib/caronte/streams/websocket.js @@ -36,10 +36,12 @@ WebsocketStream.prototype.onPipe = function(req) { }; WebsocketStream.prototye.onFinish = function() { - + this.proxyReq.end(); }; WebsocketStream.prototype.onResponse = function(proxyRes) { + this.proxyRes = proxyRes; + }; From 1993faf8a4227acda3423d46cf2cf13b4d9861e7 Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 5 Sep 2013 17:45:03 +0200 Subject: [PATCH 030/210] new error propagation - follows --- lib/caronte/streams/websocket.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/caronte/streams/websocket.js b/lib/caronte/streams/websocket.js index 2d2dc69d6..3f073ab1f 100644 --- a/lib/caronte/streams/websocket.js +++ b/lib/caronte/streams/websocket.js @@ -3,12 +3,11 @@ var Duplex = require('stream').Duplex, http = require('http'), https = require('https'); -function WebsocketStream(options, res, instance) { +function WebsocketStream(options, res) { Duplex.call(this); this.options = options; this.res = res; - this.instance = intance; var self = this; From e0faaaf81152203b96f0313c68706468e7ee7357 Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 10 Sep 2013 19:31:47 -0500 Subject: [PATCH 031/210] [fix] minor and short fixes --- lib/caronte/passes/ws.js | 10 ++++++---- lib/caronte/streams/proxy.js | 4 ++-- lib/caronte/streams/websocket.js | 11 +++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index d89f752a1..a99277f65 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -20,10 +20,12 @@ var passes = exports; */ function checkMethodAndHeader (req, res, options) { - if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') { - req.end(); - - return true; + if (req.method !== 'GET' || !req.headers.upgrade) { + req.end(); return true; + } + + if (req.headers.upgrade.toLowerCase() !== 'websocket') { + req.end(); return true; } }, diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js index 2f939876e..29c2fa78f 100644 --- a/lib/caronte/streams/proxy.js +++ b/lib/caronte/streams/proxy.js @@ -3,6 +3,8 @@ var Duplex = require('stream').Duplex, http = require('http'), https = require('https'); +module.exports = ProxyStream; + function ProxyStream(options, res) { Duplex.call(this); @@ -100,5 +102,3 @@ ProxyStream.prototype._read = function(size) { this.push(chunk); }; - -module.exports = ProxyStream; diff --git a/lib/caronte/streams/websocket.js b/lib/caronte/streams/websocket.js index 3f073ab1f..dc43daf04 100644 --- a/lib/caronte/streams/websocket.js +++ b/lib/caronte/streams/websocket.js @@ -1,8 +1,10 @@ var Duplex = require('stream').Duplex, - common = require('common'), + common = require('../common'), http = require('http'), https = require('https'); +module.exports = WebsocketStream; + function WebsocketStream(options, res) { Duplex.call(this); @@ -34,7 +36,7 @@ WebsocketStream.prototype.onPipe = function(req) { }); }; -WebsocketStream.prototye.onFinish = function() { +WebsocketStream.prototype.onFinish = function() { this.proxyReq.end(); }; @@ -55,7 +57,4 @@ WebsocketStream.prototype._write = function(chunk, encoding, callback) { WebsocketStream.prototype._read = function(size) { -}; - - -WebsocketStream.prototype +}; \ No newline at end of file From 8b3fe32f6ae60ae067bc5e40cdc43015e689467f Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 10 Sep 2013 19:36:19 -0500 Subject: [PATCH 032/210] [tests] added the ws passes test and the streams webscokets test --- test/lib-caronte-passes-ws-test.js | 87 ++++++++++++++++ test/lib-caronte-streams-websockets-test.js | 109 ++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 test/lib-caronte-passes-ws-test.js create mode 100644 test/lib-caronte-streams-websockets-test.js diff --git a/test/lib-caronte-passes-ws-test.js b/test/lib-caronte-passes-ws-test.js new file mode 100644 index 000000000..f794cca3e --- /dev/null +++ b/test/lib-caronte-passes-ws-test.js @@ -0,0 +1,87 @@ +var caronte = require('../lib/caronte/passes/ws'), + expect = require('expect.js'); + +describe('lib/caronte/passes/ws.js', function () { + describe('#checkMethodAndHeader', function () { + it('should drop non-GET connections', function () { + var endCalled = false, + stubRequest = { + method: 'DELETE', + headers: {}, + end: function () { + // Simulate Stream.end() method when call + endCalled = true; + } + }, + returnValue = caronte.checkMethodAndHeader(stubRequest, {}, {}); + expect(returnValue).to.be(true); + expect(endCalled).to.be(true); + }) + + it('should drop connections when no upgrade header', function () { + var endCalled = false, + stubRequest = { + method: 'GET', + headers: {}, + end: function () { + // Simulate Stream.end() method when call + endCalled = true; + } + }, + returnValue = caronte.checkMethodAndHeader(stubRequest, {}, {}); + expect(returnValue).to.be(true); + expect(endCalled).to.be(true); + }) + + it('should drop connections when upgrade header is different of `websocket`', function () { + var endCalled = false, + stubRequest = { + method: 'GET', + headers: { + upgrade: 'anotherprotocol' + }, + end: function () { + // Simulate Stream.end() method when call + endCalled = true; + } + }, + returnValue = caronte.checkMethodAndHeader(stubRequest, {}, {}); + expect(returnValue).to.be(true); + expect(endCalled).to.be(true); + }) + + it('should return nothing when all is ok', function () { + var endCalled = false, + stubRequest = { + method: 'GET', + headers: { + upgrade: 'websocket' + }, + end: function () { + // Simulate Stream.end() method when call + endCalled = true; + } + }, + returnValue = caronte.checkMethodAndHeader(stubRequest, {}, {}); + expect(returnValue).to.be(undefined); + expect(endCalled).to.be(false); + }) + }); + + describe('#XHeaders', function () { + // var stubRequest = { + // connection: { + // remoteAddress: '192.168.1.2', + // remotePort: '8080' + // }, + // headers: {} + // } + + // it('set the correct x-forwarded-* headers', function () { + // caronte.XHeaders(stubRequest, {}, { xfwd: true }); + // expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); + // expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); + // expect(stubRequest.headers['x-forwarded-proto']).to.be('http'); + // }); + }); +}); diff --git a/test/lib-caronte-streams-websockets-test.js b/test/lib-caronte-streams-websockets-test.js new file mode 100644 index 000000000..74248e7b3 --- /dev/null +++ b/test/lib-caronte-streams-websockets-test.js @@ -0,0 +1,109 @@ +var caronte = require('../'), + WebSocket = require('../lib/caronte/streams/websocket'); + expect = require('expect.js'), + Duplex = require('stream').Duplex, + http = require('http'); + + +describe('lib/caronte/streams/websocket.js', function () { + describe('WebSocket stream constructor', function () { + it('should be an instance of Duplex stream and get the correct options and methods', function () { + var stubOptions = { + key: 'value' + }; + var WebSocketStream = new WebSocket(stubOptions); + + expect(WebSocketStream).to.be.a(Duplex); + expect(WebSocketStream.options).to.eql({ key: 'value' }); + expect(WebSocketStream.onPipe).to.be.a('function'); + expect(WebSocketStream.onFinish).to.be.a('function'); + expect(WebSocketStream._events).to.have.property('pipe'); + expect(WebSocketStream._events).to.have.property('finish'); + }); + }); + + describe('caronte createWebSocketServer() method', function () { + // it('should make the request on pipe and finish it', function(done) { + // var proxy = caronte.createProxyServer({ + // target: 'http://127.0.0.1:8080' + // }).listen('8081'); + + // var source = http.createServer(function(req, res) { + // expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1'); + // source.close(); + // proxy.close(); + // done(); + // }); + + // source.listen('8080'); + + // http.request({ + // hostname: '127.0.0.1', + // port: '8081', + // method: 'POST', + // headers: { + // 'x-forwarded-for': '127.0.0.1' + // } + // }, function() {}).end(); + // }); + }); + + describe('caronte createProxyServer() method with response', function () { + // it('should make the request, handle response and finish it', function(done) { + // var proxy = caronte.createProxyServer({ + // target: 'http://127.0.0.1:8080' + // }).listen('8081'); + + // var source = http.createServer(function(req, res) { + // expect(req.method).to.eql('GET'); + // res.writeHead(200, {'Content-Type': 'text/plain'}) + // res.end('Hello from ' + source.address().port); + // }); + + // source.listen('8080'); + + // http.request({ + // hostname: '127.0.0.1', + // port: '8081', + // method: 'GET', + // }, function(res) { + // expect(res.statusCode).to.eql(200); + + // res.on('data', function (data) { + // expect(data.toString()).to.eql('Hello from 8080'); + // }); + + // res.on('end', function () { + // source.close(); + // proxy.close(); + // done(); + // }); + // }).end(); + // }); + }); + + describe('caronte createProxyServer() method with error response', function () { + // it('should make the request and response with error', function(done) { + // var proxy = caronte.createProxyServer({ + // target: 'http://127.0.0.1:8080' + // }).listen('8081'); + + // http.request({ + // hostname: '127.0.0.1', + // port: '8081', + // method: 'GET', + // }, function(res) { + // expect(res.statusCode).to.eql(500); + + // res.on('data', function (data) { + // expect(data.toString()).to.eql('Internal Server Error'); + // }); + + // res.on('end', function () { + // proxy.close(); + // done(); + // }); + // }).end(); + // }); + }); +}); From c9cd6d2ad324e0e6222932c8f29f27621071e045 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 13 Sep 2013 19:07:34 +0200 Subject: [PATCH 033/210] [fix] make @mmalecki a happy camper --- lib/caronte.js | 4 +--- lib/caronte/index.js | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/caronte.js b/lib/caronte.js index ed5e4253a..444e8fd25 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -51,6 +51,7 @@ proxy.createProxyServer = function createProxyServer(options) { options.ee = new events.EventEmitter2({ wildcard: true, delimiter: ':' }); return { + ee : options.ee, web : caronte.createWebProxy(options), ws : caronte.createWsProxy(options), listen : function listen(port) { @@ -63,9 +64,6 @@ proxy.createProxyServer = function createProxyServer(options) { server.listen(port); return server; - }, - emitter : function() { - return options.ee; } }; }; diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 747a64bab..194676e8b 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -30,9 +30,12 @@ function createRightProxy(type) { return passes[pass]; }); - return function(req, res) { + return function(req, res, opts) { var self = this, ev = 'caronte:' + type + ':'; + + if(typeof opts !== 'undefined') { options = opts; } + options.ee.emit(ev + 'begin', req, res); From 4a4607d075a912746386d1751fd6b0fc98cf6b20 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 13 Sep 2013 20:06:51 +0200 Subject: [PATCH 034/210] support websockets --- lib/caronte/index.js | 19 ++++++++++++++++--- lib/caronte/passes/ws.js | 3 +++ package.json | 3 ++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 194676e8b..8339faee6 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -30,11 +30,24 @@ function createRightProxy(type) { return passes[pass]; }); - return function(req, res, opts) { + return function(req, res /*, [head], [opts] */) { var self = this, - ev = 'caronte:' + type + ':'; + args = [].slice.call(arguments), + cntr = args.length - 1, + ev = 'caronte:' + type + ':', + head; - if(typeof opts !== 'undefined') { options = opts; } + if( + !(args[cntr] instanceof Buffer) && + args[cntr] !== res + ) { + options = opts; + cntr--; + } + + if(args[cntr] instanceof Buffer) { + head = args[cntr]; + } options.ee.emit(ev + 'begin', req, res); diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index a99277f65..827617b47 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -1,3 +1,6 @@ +var WebsocketStream = require('../streams/websocket'), + passes = exports; + /*! * Array of passes. * diff --git a/package.json b/package.json index e2b3f25d1..9968b65b0 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "dox" : "*", "coveralls" : "*", "mocha-lcov-reporter": "*", - "blanket" : "*" + "blanket" : "*", + "ws" : "*" }, "scripts" : { "blanket" : { "pattern": "caronte/lib" }, From a74cd85c8a5aae2851acf7139648fefd6a02a57b Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 13 Sep 2013 20:49:52 +0200 Subject: [PATCH 035/210] socket.io stuff --- lib/caronte/index.js | 2 +- lib/caronte/passes/ws.js | 18 ++++++-- lib/caronte/streams/websocket.js | 73 ++++++++++++++++++++++++++++---- 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 8339faee6..586ed3f60 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -56,7 +56,7 @@ function createRightProxy(type) { var evnt = ev + pass.name.toLowerCase(); options.ee.emit(evnt + 'begin', req, res); - var val = pass(req, res, options); + var val = pass(req, res, options, head); options.ee.emit(evnt + 'end'); return val; diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 827617b47..8a3515537 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -28,10 +28,22 @@ function checkMethodAndHeader (req, res, options) { } if (req.headers.upgrade.toLowerCase() !== 'websocket') { - req.end(); return true; + res.destroy(); return true; } }, +/** + * Setup socket + * + */ + +function setupSocket(req, res) { + res.setTimeout(0); + res.setNoDelay(true); + + res.setKeepAlive(true, 0); +}, + /** * Sets `x-forwarded-*` headers if specified in config. * @@ -58,8 +70,8 @@ function XHeaders(req, res, options) { * * */ -function stream(req, res, options, instance) { - req.pipe(new WebsocketStream(options, instance)).pipe(res); +function stream(req, res, options, head) { + req.pipe(new WebsocketStream(options, head)).pipe(res); } ] // <-- diff --git a/lib/caronte/streams/websocket.js b/lib/caronte/streams/websocket.js index dc43daf04..e7c71b31c 100644 --- a/lib/caronte/streams/websocket.js +++ b/lib/caronte/streams/websocket.js @@ -8,8 +8,9 @@ module.exports = WebsocketStream; function WebsocketStream(options, res) { Duplex.call(this); - this.options = options; - this.res = res; + this.options = options; + this.res = res; + this.handshakeDone = false; var self = this; @@ -28,9 +29,13 @@ WebsocketStream.prototype.onPipe = function(req) { common.setupOutgoing(self.options.ssl || {}, self.options, req) ); - this.proxyReq.once('response', function(proxyRes) { - self.onResponse(proxyRes); + this.proxyReq.once('socket', function(proxySocket) { + self.onSocket(proxySocket); }); + this.proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { + self.onUpgrade(proxyRes, proxySocket, proxyHead); + }); + this.proxyReq.on('error', function(e) { self.onError(e); }); @@ -40,8 +45,25 @@ WebsocketStream.prototype.onFinish = function() { this.proxyReq.end(); }; -WebsocketStream.prototype.onResponse = function(proxyRes) { - this.proxyRes = proxyRes; +WebsocketStream.prototype.onSocket = function(proxySocket) { + + +}; + +WebsocketStream.prototype.onUpgrade = function(proxyRes, proxySocket, proxyHead) { + this.handshake = { + headers : proxyRes.headers, + statusCode : proxyRes.statusCode + }; + + this.proxyRes = proxyRes; + this.proxySocket = proxySocket; + this.proxyHead = proxyHead; + + proxySocket.setTimeout(0); + proxySocket.setNoDelay(true); + + proxySocket.setKeepAlive(true, 0); }; @@ -52,9 +74,42 @@ WebsocketStream.prototype.onError = function(e) { WebsocketStream.prototype._write = function(chunk, encoding, callback) { - + this.proxySocket.write(chunk, encoding, callback); }; WebsocketStream.prototype._read = function(size) { - -}; \ No newline at end of file + var chunk = (this.proxySocket ? this.proxySocket.read(size) : '') || ''; + + if(chunk && !this.handshakeDone) { + var headers = ''; + + if (this.handshake.statusCode && this.handshake.statusCode == 101) { + headers = [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade', + 'Sec-WebSocket-Accept: ' + this.handshake.headers['sec-websocket-accept'] + ]; + + headers = headers.concat('', '').join('\r\n'); + } + + /* + * Socket.IO specific code + */ + + var sdata = chunk.toString(); + sdata = sdata.substr(0, sdata.search(CRLF + CRLF)); + chunk = data.slice(Buffer.byteLength(sdata), data.length); + + if (self.source.https && !self.target.https) { sdata = sdata.replace('ws:', 'wss:'); } + + this.push(headers + sdata); + this.push(data); + + this.handshakeDone = true; + return; + } + + this.push(chunk); +}; From e45bfd66a21a2470c5a4a4cc1d6095494bbc0f6b Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 13 Sep 2013 23:38:12 +0200 Subject: [PATCH 036/210] stuff --- .gitignore | 2 +- lib/caronte/passes/ws.js | 24 ++++++------- lib/caronte/streams/websocket.js | 22 +++++++++--- ttest.js | 58 ++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 ttest.js diff --git a/.gitignore b/.gitignore index 52241f829..517f9cee0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules *.swp cov -ttest.js +!ttest.js notes diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 8a3515537..a4303304d 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -4,7 +4,7 @@ var WebsocketStream = require('../streams/websocket'), /*! * Array of passes. * - * A `pass` is just a function that is executed on `req, res, options` + * A `pass` is just a function that is executed on `req, socket, options` * so that you can easily add new checks while still keeping the base * flexible. */ @@ -22,13 +22,13 @@ var passes = exports; * the `upgrade:websocket` header */ -function checkMethodAndHeader (req, res, options) { +function checkMethodAndHeader (req, socket) { if (req.method !== 'GET' || !req.headers.upgrade) { - req.end(); return true; + socket.destroy(); return true; } if (req.headers.upgrade.toLowerCase() !== 'websocket') { - res.destroy(); return true; + socket.destroy(); return true; } }, @@ -37,11 +37,11 @@ function checkMethodAndHeader (req, res, options) { * */ -function setupSocket(req, res) { - res.setTimeout(0); - res.setNoDelay(true); +function setupSocket(req, socket) { + socket.setTimeout(0); + socket.setNoDelay(true); - res.setKeepAlive(true, 0); + socket.setKeepAlive(true, 0); }, /** @@ -49,11 +49,11 @@ function setupSocket(req, res) { * */ -function XHeaders(req, res, options) { +function XHeaders(req, socket, options) { if(!options.xfwd) return; var values = { - for : req.connection.remoteAddress || req.socket.remoteAddress, + for : req.connection.remoteAddsockets || req.socket.remoteAddsockets, port : req.connection.remotePort || req.socket.remotePort, proto: req.connection.pair ? 'wss' : 'ws' }; @@ -70,8 +70,8 @@ function XHeaders(req, res, options) { * * */ -function stream(req, res, options, head) { - req.pipe(new WebsocketStream(options, head)).pipe(res); +function stream(req, socket, options, head) { + req.pipe(new WebsocketStream(options, head)).pipe(socket); } ] // <-- diff --git a/lib/caronte/streams/websocket.js b/lib/caronte/streams/websocket.js index e7c71b31c..4d24f5bf5 100644 --- a/lib/caronte/streams/websocket.js +++ b/lib/caronte/streams/websocket.js @@ -47,10 +47,11 @@ WebsocketStream.prototype.onFinish = function() { WebsocketStream.prototype.onSocket = function(proxySocket) { - }; WebsocketStream.prototype.onUpgrade = function(proxyRes, proxySocket, proxyHead) { + var self = this; + this.handshake = { headers : proxyRes.headers, statusCode : proxyRes.statusCode @@ -63,9 +64,17 @@ WebsocketStream.prototype.onUpgrade = function(proxyRes, proxySocket, proxyHead) proxySocket.setTimeout(0); proxySocket.setNoDelay(true); - proxySocket.setKeepAlive(true, 0); + proxySocket.setKeepAlive(true, 0); + + proxySocket.on('readable', function() { + self.read(0); + }); + proxySocket.on('end', function() { + self.push(null); + }); + self.emit('readable'); }; WebsocketStream.prototype.onError = function(e) { @@ -98,8 +107,8 @@ WebsocketStream.prototype._read = function(size) { * Socket.IO specific code */ - var sdata = chunk.toString(); - sdata = sdata.substr(0, sdata.search(CRLF + CRLF)); + /*var sdata = chunk.toString(); + sdata = sdata.substr(0, sdata.search('\r\n\r\n')); chunk = data.slice(Buffer.byteLength(sdata), data.length); if (self.source.https && !self.target.https) { sdata = sdata.replace('ws:', 'wss:'); } @@ -108,7 +117,10 @@ WebsocketStream.prototype._read = function(size) { this.push(data); this.handshakeDone = true; - return; + return; + */ + this.push(headers); + this.push(chunk); } this.push(chunk); diff --git a/ttest.js b/ttest.js new file mode 100644 index 000000000..d06df539c --- /dev/null +++ b/ttest.js @@ -0,0 +1,58 @@ +var caronte = require('./'), + http = require('http'), + ws = require('ws'); + +var proxyTo = new ws.Server({ port: 9090 }); + +proxyTo.on('connection', function(ws) { + console.log('connection!'); + ws.on('message', function(msg) { + console.log('received: ' + msg); + }); + ws.send('derpity?'); +}); + +/*caronte.createProxyServer({ + ws : true, + target: 'http://127.0.0.1:9090' +}).listen(8000);*/ + + +var client = new ws('ws://127.0.0.1:8000'); +client.on('open', function() { + client.send('baaaka'); + console.log('sent: baaaaka'); +}); + + +var srv = http.createServer(function(req, res) { + res.end('1'); +}).listen(8000); + +srv.on('upgrade', function(req, socket, head) { + var options = { + port: 9090, + hostname: '127.0.0.1', + headers: req.headers + } + var req = http.request(options); + req.end(); + socket.on('data', function(d) { + console.log('yoo'); + console.log(d); + }); + var s; + req.on('socket', function(ss) { + s = ss; + }); + req.on('upgrade', function(res, sock, hd) { + /*console.log(hd.toString('utf-8')); + var str = Object.keys(res.headers).map(function(i) { + return i + ": " + res.headers[i]; + }).join('\r\n'); + socket.write("HTTP/1.1 101 Switching Protocols\r\n" + str); + + socket.write(hd); + socket.pipe(sock).pipe(socket);*/ + }); +}); From 1a7bef0cda58243416a263075dc6eb51f22b6dec Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 10:06:03 +0200 Subject: [PATCH 037/210] mm test file --- ttest.js | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/ttest.js b/ttest.js index d06df539c..33da443e3 100644 --- a/ttest.js +++ b/ttest.js @@ -29,30 +29,25 @@ var srv = http.createServer(function(req, res) { res.end('1'); }).listen(8000); +srv.on('connection', function(s) { + s.pipe(process.stdout); +}); + srv.on('upgrade', function(req, socket, head) { + var options = { port: 9090, hostname: '127.0.0.1', headers: req.headers } - var req = http.request(options); - req.end(); - socket.on('data', function(d) { - console.log('yoo'); - console.log(d); - }); - var s; - req.on('socket', function(ss) { - s = ss; - }); - req.on('upgrade', function(res, sock, hd) { - /*console.log(hd.toString('utf-8')); - var str = Object.keys(res.headers).map(function(i) { - return i + ": " + res.headers[i]; - }).join('\r\n'); - socket.write("HTTP/1.1 101 Switching Protocols\r\n" + str); - - socket.write(hd); - socket.pipe(sock).pipe(socket);*/ + var r = http.request(options); + + r.on('upgrade', function(res, sock, hd) { + if (hd && hd.length) sock.unshift(hd); + + + socket.pipe(sock).pipe(socket); }); + + r.end(); }); From f97c0c6167371c5ff92e6361b1df02e3fd5506d7 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 10:22:31 +0200 Subject: [PATCH 038/210] write --- ttest.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ttest.js b/ttest.js index 33da443e3..ca9334434 100644 --- a/ttest.js +++ b/ttest.js @@ -47,6 +47,15 @@ srv.on('upgrade', function(req, socket, head) { socket.pipe(sock).pipe(socket); + //req.pipe(r).pipe(socket); + /*console.log(hd.toString('utf-8')); + var str = Object.keys(res.headers).map(function(i) { + return i + ": " + res.headers[i]; + }).join('\r\n'); + socket.write("HTTP/1.1 101 Switching Protocols\r\n" + str); + + socket.write(hd); + socket.pipe(sock).pipe(socket);*/ }); r.end(); From 79a14acfd2b2bf03f5ae2b334e7a37e619da6bb9 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 12:48:53 +0200 Subject: [PATCH 039/210] [feature] websocket support --- lib/caronte/passes/ws.js | 21 ++++++++++++++++++- ttest.js | 44 +++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index a4303304d..aa2ce1aaf 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -1,4 +1,6 @@ var WebsocketStream = require('../streams/websocket'), + http = require('http'), + common = require('../common'), passes = exports; /*! @@ -71,7 +73,24 @@ function XHeaders(req, socket, options) { * */ function stream(req, socket, options, head) { - req.pipe(new WebsocketStream(options, head)).pipe(socket); + var r = http.request( + common.setupOutgoing(options.ssl || {}, options, req) + ); + + r.on('upgrade', function(res, proxySock, hd) { + if (hd && hd.length) proxySock.unshift(hd); + + socket.write('HTTP/1.1 101 Switching Protocols\r\n'); + socket.write(Object.keys(res.headers).map(function(i) { + return i + ": " + res.headers[i]; + }).join('\r\n') + '\r\n\r\n'); + proxySock.pipe(socket).pipe(proxySock); + }); + + r.end(); + + + //req.pipe(new WebsocketStream(options, head)).pipe(socket); } ] // <-- diff --git a/ttest.js b/ttest.js index ca9334434..ee34ed61e 100644 --- a/ttest.js +++ b/ttest.js @@ -1,3 +1,4 @@ +'use strict'; /* jshint node:true */ var caronte = require('./'), http = require('http'), ws = require('ws'); @@ -7,33 +8,38 @@ var proxyTo = new ws.Server({ port: 9090 }); proxyTo.on('connection', function(ws) { console.log('connection!'); ws.on('message', function(msg) { - console.log('received: ' + msg); + ws.send('ohai: ' + msg); + setTimeout(function() { + ws.send('HAHAHHA'); + }, 10000); }); ws.send('derpity?'); }); -/*caronte.createProxyServer({ +caronte.createProxyServer({ ws : true, target: 'http://127.0.0.1:9090' -}).listen(8000);*/ +}).listen(8000); var client = new ws('ws://127.0.0.1:8000'); client.on('open', function() { client.send('baaaka'); console.log('sent: baaaaka'); + setTimeout(function() { + client.send('cacca'); + }, 5000); + client.on('message', function(msg) { + console.log('server said: ' + msg); + }); }); -var srv = http.createServer(function(req, res) { +/*var srv = http.createServer(function(req, res) { res.end('1'); }).listen(8000); -srv.on('connection', function(s) { - s.pipe(process.stdout); -}); - -srv.on('upgrade', function(req, socket, head) { +srv.on('upgrade', function(req, sock, head) { var options = { port: 9090, @@ -42,21 +48,17 @@ srv.on('upgrade', function(req, socket, head) { } var r = http.request(options); - r.on('upgrade', function(res, sock, hd) { - if (hd && hd.length) sock.unshift(hd); + r.on('upgrade', function(res, proxySock, hd) { + if (hd && hd.length) proxySock.unshift(hd); - - socket.pipe(sock).pipe(socket); - //req.pipe(r).pipe(socket); - /*console.log(hd.toString('utf-8')); - var str = Object.keys(res.headers).map(function(i) { + sock.write('HTTP/1.1 101 Switching Protocols\r\n'); + sock.write(Object.keys(res.headers).map(function(i) { return i + ": " + res.headers[i]; - }).join('\r\n'); - socket.write("HTTP/1.1 101 Switching Protocols\r\n" + str); - - socket.write(hd); - socket.pipe(sock).pipe(socket);*/ + }).join('\r\n') + '\r\n\r\n'); + proxySock.pipe(sock).pipe(proxySock); }); r.end(); }); + +*/ From 893100972c22febbf133134394bc0bcef47d9e12 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 13:01:53 +0200 Subject: [PATCH 040/210] [fix] naming --- lib/caronte/passes/ws.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index aa2ce1aaf..90ab609f3 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -77,14 +77,14 @@ function stream(req, socket, options, head) { common.setupOutgoing(options.ssl || {}, options, req) ); - r.on('upgrade', function(res, proxySock, hd) { - if (hd && hd.length) proxySock.unshift(hd); + r.on('upgrade', function(proxyRes, proxySocket, proxyHead) { + if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); socket.write('HTTP/1.1 101 Switching Protocols\r\n'); - socket.write(Object.keys(res.headers).map(function(i) { - return i + ": " + res.headers[i]; + socket.write(Object.keys(proxyRes.headers).map(function(i) { + return i + ": " + proxyRes.headers[i]; }).join('\r\n') + '\r\n\r\n'); - proxySock.pipe(socket).pipe(proxySock); + proxySocket.pipe(socket).pipe(proxySocket); }); r.end(); From 2a593664a5768c90d9b2edf4c298460416b38926 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 13:02:28 +0200 Subject: [PATCH 041/210] [fix] naming --- lib/caronte/passes/ws.js | 7 +- lib/caronte/streams/websocket.js | 127 ------------------------------- 2 files changed, 3 insertions(+), 131 deletions(-) delete mode 100644 lib/caronte/streams/websocket.js diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 90ab609f3..11e85c67e 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -1,7 +1,6 @@ -var WebsocketStream = require('../streams/websocket'), - http = require('http'), - common = require('../common'), - passes = exports; +var http = require('http'), + common = require('../common'), + passes = exports; /*! * Array of passes. diff --git a/lib/caronte/streams/websocket.js b/lib/caronte/streams/websocket.js deleted file mode 100644 index 4d24f5bf5..000000000 --- a/lib/caronte/streams/websocket.js +++ /dev/null @@ -1,127 +0,0 @@ -var Duplex = require('stream').Duplex, - common = require('../common'), - http = require('http'), - https = require('https'); - -module.exports = WebsocketStream; - -function WebsocketStream(options, res) { - Duplex.call(this); - - this.options = options; - this.res = res; - this.handshakeDone = false; - - var self = this; - - this.once('pipe', function(pipe) { self.onPipe(pipe); }); - this.once('finish', function() { self.onFinish(); }); -} - -require('util').inherits(WebsocketStream, Duplex); - -WebsocketStream.prototype.onPipe = function(req) { - this.req = req; - - var self = this; - - this.proxyReq = (self.options.ssl ? https : http).request( - common.setupOutgoing(self.options.ssl || {}, self.options, req) - ); - - this.proxyReq.once('socket', function(proxySocket) { - self.onSocket(proxySocket); - }); - this.proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { - self.onUpgrade(proxyRes, proxySocket, proxyHead); - }); - - this.proxyReq.on('error', function(e) { - self.onError(e); - }); -}; - -WebsocketStream.prototype.onFinish = function() { - this.proxyReq.end(); -}; - -WebsocketStream.prototype.onSocket = function(proxySocket) { - -}; - -WebsocketStream.prototype.onUpgrade = function(proxyRes, proxySocket, proxyHead) { - var self = this; - - this.handshake = { - headers : proxyRes.headers, - statusCode : proxyRes.statusCode - }; - - this.proxyRes = proxyRes; - this.proxySocket = proxySocket; - this.proxyHead = proxyHead; - - proxySocket.setTimeout(0); - proxySocket.setNoDelay(true); - - proxySocket.setKeepAlive(true, 0); - - proxySocket.on('readable', function() { - self.read(0); - }); - - proxySocket.on('end', function() { - self.push(null); - }); - - self.emit('readable'); -}; - -WebsocketStream.prototype.onError = function(e) { - -}; - - -WebsocketStream.prototype._write = function(chunk, encoding, callback) { - this.proxySocket.write(chunk, encoding, callback); -}; - -WebsocketStream.prototype._read = function(size) { - var chunk = (this.proxySocket ? this.proxySocket.read(size) : '') || ''; - - if(chunk && !this.handshakeDone) { - var headers = ''; - - if (this.handshake.statusCode && this.handshake.statusCode == 101) { - headers = [ - 'HTTP/1.1 101 Switching Protocols', - 'Upgrade: websocket', - 'Connection: Upgrade', - 'Sec-WebSocket-Accept: ' + this.handshake.headers['sec-websocket-accept'] - ]; - - headers = headers.concat('', '').join('\r\n'); - } - - /* - * Socket.IO specific code - */ - - /*var sdata = chunk.toString(); - sdata = sdata.substr(0, sdata.search('\r\n\r\n')); - chunk = data.slice(Buffer.byteLength(sdata), data.length); - - if (self.source.https && !self.target.https) { sdata = sdata.replace('ws:', 'wss:'); } - - this.push(headers + sdata); - this.push(data); - - this.handshakeDone = true; - return; - */ - this.push(headers); - this.push(chunk); - } - - this.push(chunk); -}; From 6a03e5f7cf356416ea13584e279f5bfa3791c058 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 13:04:59 +0200 Subject: [PATCH 042/210] [fix] remove stuff --- lib/caronte/passes/ws.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 11e85c67e..3f44e5daa 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -87,9 +87,6 @@ function stream(req, socket, options, head) { }); r.end(); - - - //req.pipe(new WebsocketStream(options, head)).pipe(socket); } ] // <-- From 7d71a867a8bdc375f7577cec3905cca89bbf415c Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 13:06:00 +0200 Subject: [PATCH 043/210] [fix] naming convention --- lib/caronte/passes/ws.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 3f44e5daa..821cb2eb0 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -72,11 +72,11 @@ function XHeaders(req, socket, options) { * */ function stream(req, socket, options, head) { - var r = http.request( + var proxyReq = http.request( common.setupOutgoing(options.ssl || {}, options, req) ); - r.on('upgrade', function(proxyRes, proxySocket, proxyHead) { + proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); socket.write('HTTP/1.1 101 Switching Protocols\r\n'); @@ -86,7 +86,7 @@ function stream(req, socket, options, head) { proxySocket.pipe(socket).pipe(proxySocket); }); - r.end(); + proxyReq.end(); } ] // <-- From 07cfa6b981ff54d8d96eea6c9aa4b560ee3867ec Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 13:17:45 +0200 Subject: [PATCH 044/210] [experiment] new api for proxying --- lib/caronte/passes/web.js | 17 +++++++++++++++-- ttest.js | 22 ++++++++++++---------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index dc815abd0..363d8ff5e 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -1,5 +1,8 @@ var ForwardStream = require('../streams/forward'), ProxyStream = require('../streams/proxy'), + http = require('http'), + https = require('https'), + common = require('../common'), passes = exports; /*! @@ -84,7 +87,17 @@ function XHeaders(req, res, options) { */ function stream(req, res, options) { - if(options.forward) { + var proxyReq = (options.ssl ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req) + ); + + proxyReq.on('response', function(proxyRes) { + req.pipe(proxyRes).pipe(res); + }); + + proxyReq.end(); + + /*if(options.forward) { req.pipe(new ForwardStream(options)); } @@ -92,7 +105,7 @@ function stream(req, res, options) { return req.pipe(new ProxyStream(options, res)).pipe(res); } - res.end(); + res.end();*/ } ] // <-- diff --git a/ttest.js b/ttest.js index ee34ed61e..2101db6d4 100644 --- a/ttest.js +++ b/ttest.js @@ -3,7 +3,7 @@ var caronte = require('./'), http = require('http'), ws = require('ws'); -var proxyTo = new ws.Server({ port: 9090 }); +/*var proxyTo = new ws.Server({ port: 9090 }); proxyTo.on('connection', function(ws) { console.log('connection!'); @@ -16,12 +16,6 @@ proxyTo.on('connection', function(ws) { ws.send('derpity?'); }); -caronte.createProxyServer({ - ws : true, - target: 'http://127.0.0.1:9090' -}).listen(8000); - - var client = new ws('ws://127.0.0.1:8000'); client.on('open', function() { client.send('baaaka'); @@ -33,12 +27,20 @@ client.on('open', function() { console.log('server said: ' + msg); }); }); +*/ + + +caronte.createProxyServer({ + ws : true, + target: 'http://127.0.0.1:9090' +}).listen(8080); -/*var srv = http.createServer(function(req, res) { - res.end('1'); -}).listen(8000); +var srv = http.createServer(function(req, res) { + res.end('ciao proxy'); +}).listen(9090); +/* srv.on('upgrade', function(req, sock, head) { var options = { From 031aa0fbf30bd377696c4efa508f6fc769bf1070 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 13:33:38 +0200 Subject: [PATCH 045/210] [fix] slimmer proxying --- lib/caronte/passes/web.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index 363d8ff5e..33da58575 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -91,8 +91,10 @@ function stream(req, res, options) { common.setupOutgoing(options.ssl || {}, options, req) ); + req.pipe(proxyReq); + proxyReq.on('response', function(proxyRes) { - req.pipe(proxyRes).pipe(res); + proxyRes.pipe(res); }); proxyReq.end(); From 8c8c455541f21ad9a9ac7ca19d1f37368206a2e2 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 14:00:31 +0200 Subject: [PATCH 046/210] support forward --- lib/caronte/passes/web.js | 18 ++++++++++++------ ttest.js | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index 33da58575..6f82da22e 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -1,9 +1,7 @@ -var ForwardStream = require('../streams/forward'), - ProxyStream = require('../streams/proxy'), - http = require('http'), - https = require('https'), - common = require('../common'), - passes = exports; +var http = require('http'), + https = require('https'), + common = require('../common'), + passes = exports; /*! * Array of passes. @@ -87,6 +85,14 @@ function XHeaders(req, res, options) { */ function stream(req, res, options) { + if(options.forward) { + var forwardReq = (options.ssl ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req, 'forward') + ); + req.pipe(forwardReq); + return res.end(); + } + var proxyReq = (options.ssl ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); diff --git a/ttest.js b/ttest.js index 2101db6d4..3f945e4aa 100644 --- a/ttest.js +++ b/ttest.js @@ -32,13 +32,14 @@ client.on('open', function() { caronte.createProxyServer({ ws : true, - target: 'http://127.0.0.1:9090' + forward: 'http://127.0.0.1:9090' }).listen(8080); var srv = http.createServer(function(req, res) { res.end('ciao proxy'); + console.log('suca'); }).listen(9090); /* srv.on('upgrade', function(req, sock, head) { From 886a8707078f59d0467b34686455bb5bdfadbc0c Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 18:44:39 +0200 Subject: [PATCH 047/210] [docs] readme --- .gitignore | 2 +- README.md | 46 +++++++++++++++++++++++++++++++++++++ ttest.js | 67 ------------------------------------------------------ 3 files changed, 47 insertions(+), 68 deletions(-) create mode 100644 README.md delete mode 100644 ttest.js diff --git a/.gitignore b/.gitignore index 517f9cee0..d1ae71570 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules *.swp cov -!ttest.js +atest.js notes diff --git a/README.md b/README.md new file mode 100644 index 000000000..cf7e59a72 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +Caronte +======= + +Caronte is an HTTP programmable proxying library that supports +websockets. It is suitable for implementing components such as +proxies and load balancers. + +### Contributing and Issues + +* Search on Google/Github +* If you can't find anything, open an issue +* If you feel comfortable about fixing the issue, fork the repo +* Commit to your local branch (which must be different from `master`) +* Submit your Pull Request (be sure to include tests and update documentation) + +### Test + +``` +$ npm test +``` + +### License + +``` +The MIT License (MIT) + +Copyright (c) 2013 Nodejitsu Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` diff --git a/ttest.js b/ttest.js deleted file mode 100644 index 3f945e4aa..000000000 --- a/ttest.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict'; /* jshint node:true */ -var caronte = require('./'), - http = require('http'), - ws = require('ws'); - -/*var proxyTo = new ws.Server({ port: 9090 }); - -proxyTo.on('connection', function(ws) { - console.log('connection!'); - ws.on('message', function(msg) { - ws.send('ohai: ' + msg); - setTimeout(function() { - ws.send('HAHAHHA'); - }, 10000); - }); - ws.send('derpity?'); -}); - -var client = new ws('ws://127.0.0.1:8000'); -client.on('open', function() { - client.send('baaaka'); - console.log('sent: baaaaka'); - setTimeout(function() { - client.send('cacca'); - }, 5000); - client.on('message', function(msg) { - console.log('server said: ' + msg); - }); -}); -*/ - - -caronte.createProxyServer({ - ws : true, - forward: 'http://127.0.0.1:9090' -}).listen(8080); - - - -var srv = http.createServer(function(req, res) { - res.end('ciao proxy'); - console.log('suca'); -}).listen(9090); -/* -srv.on('upgrade', function(req, sock, head) { - - var options = { - port: 9090, - hostname: '127.0.0.1', - headers: req.headers - } - var r = http.request(options); - - r.on('upgrade', function(res, proxySock, hd) { - if (hd && hd.length) proxySock.unshift(hd); - - sock.write('HTTP/1.1 101 Switching Protocols\r\n'); - sock.write(Object.keys(res.headers).map(function(i) { - return i + ": " + res.headers[i]; - }).join('\r\n') + '\r\n\r\n'); - proxySock.pipe(sock).pipe(proxySock); - }); - - r.end(); -}); - -*/ From c4ddc4edd324d9910a11eea14561a0e3b953f29c Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 18:46:13 +0200 Subject: [PATCH 048/210] [fix] quote --- README.md | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index cf7e59a72..932822d72 100644 --- a/README.md +++ b/README.md @@ -21,26 +21,25 @@ $ npm test ### License -``` -The MIT License (MIT) - -Copyright (c) 2013 Nodejitsu Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -``` +>The MIT License (MIT) +> +>Copyright (c) 2013 Nodejitsu Inc. +> +>Permission is hereby granted, free of charge, to any person obtaining a copy +>of this software and associated documentation files (the "Software"), to deal +>in the Software without restriction, including without limitation the rights +>to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +>copies of the Software, and to permit persons to whom the Software is +>furnished to do so, subject to the following conditions: +> +>The above copyright notice and this permission notice shall be included in +>all copies or substantial portions of the Software. +> +>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +>AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +>LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +>OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +>THE SOFTWARE. + From ec981c5b74bf43dd36c8ca89833b751f59f01d38 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 19:10:33 +0200 Subject: [PATCH 049/210] [fix] docs --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.md b/README.md index 932822d72..9a2db2273 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,42 @@ Caronte is an HTTP programmable proxying library that supports websockets. It is suitable for implementing components such as proxies and load balancers. +### Core Concept + +A new proxy is created by calling `createProxyServer` and passing +an `options` object as argument ([valid properties are available here](https://github.com/yawnt/caronte/blob/master/lib/caronte.js#L26-L39)) + +```javascript +var caronte = require('caronte'); + +var proxy = caronte.createProxyServer(options); +``` + +An object will be returned with four values: + +* web `req, res, [options]` (used for proxying regular HTTP(S) requests) +* ws `req, socket, head, [options]` (used for proxying WS(S) requests) +* ee (an EventEmitter2 that emits events, you can hook into them to customize behaviour) +* listen `port` (a function that wraps the object in a webserver, for your convenience) + +Is it then possible to proxy requests by calling these functions + +```javascript +require('http').createServer(function(req, res) { + proxy.web(req, res, { target: 'http://mytarget.com:8080' }); +}); +``` + +When a request is proxied it follows two different pipelines ([available here](https://github.com/yawnt/caronte/tree/master/lib/caronte/passes)) +which apply trasformations to both the `req` and `res` object. +The first pipeline (ingoing) is responsible for the creation and manipulation of the stream that connects your client to the target. +The second pipeline (outgoing) is responsible for the creation and manipulation of the stream that, from your target, returns datas +to the client. + +You can easily add a `pass` (stages) into both the pipelines (XXX: ADD API). + +In addition, every stage emits a corresponding event so introspection during the process is always available. + ### Contributing and Issues * Search on Google/Github From 63b016c8ef7ba4520a4b4ab171b145585e5d30eb Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 22:03:00 +0200 Subject: [PATCH 050/210] [fix] yawnt baaaka .. fixes #8 --- lib/caronte/common.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 68f4a9292..52b776716 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -25,9 +25,11 @@ common.setupOutgoing = function(outgoing, options, req, forward) { function(e) { outgoing[e] = options[forward || 'target'][e]; } ); - ['method', 'path', 'headers'].forEach( + ['method', 'headers'].forEach( function(e) { outgoing[e] = req[e]; } ); + outgoing.path = req.url; + return outgoing; -}; \ No newline at end of file +}; From dad211e71c9ac3b32eba1ea3755edb688053b9d3 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 22:52:22 +0200 Subject: [PATCH 051/210] keepalive sockets --- .gitignore | 1 + lib/caronte/common.js | 9 ++++++++ lib/caronte/passes/ws.js | 4 ++++ primus-proxy.js | 50 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 primus-proxy.js diff --git a/.gitignore b/.gitignore index d1ae71570..0e8f9c134 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules cov atest.js notes +prismus-proxy.js diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 52b776716..2a2954bb5 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -33,3 +33,12 @@ common.setupOutgoing = function(outgoing, options, req, forward) { return outgoing; }; + +common.setupSocket = function(socket) { + socket.setTimeout(0); + socket.setNoDelay(true); + + socket.setKeepAlive(true, 0); + + return socket; +}; diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 821cb2eb0..1c9215b84 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -72,11 +72,15 @@ function XHeaders(req, socket, options) { * */ function stream(req, socket, options, head) { + common.setupSocket(socket); + var proxyReq = http.request( common.setupOutgoing(options.ssl || {}, options, req) ); proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { + common.setupSocket(proxySocket); + if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); socket.write('HTTP/1.1 101 Switching Protocols\r\n'); diff --git a/primus-proxy.js b/primus-proxy.js new file mode 100644 index 000000000..9a23276a0 --- /dev/null +++ b/primus-proxy.js @@ -0,0 +1,50 @@ +var http = require('http'); +var caronte = require('./'); +var Primus = require('primus'); + +var server = http.createServer(function (req, res) { + res.writeHead(500); + res.end('Not Implemented\n'); +}); + +var primus = new Primus(server, { transformer: 'engine.io' }); +var Socket = primus.Socket; + +primus.on('error', function (err) { + console.log('Primus ' + err); +}); + +primus.on('connection', function (spark) { + spark.write({ from: 'server', to: 'client' }); + + spark.on('data', function (data) { + console.dir(data); + }); +}); + +primus.on('disconnection', function (spark) { + console.log('disconnected'); +}); + +server.listen(9000); + +var proxy = caronte.createProxyServer({ + ws: true, + target: 'http://localhost:9000' +}); + +var srv = proxy.listen(3000); + +var socket = new Socket('http://localhost:3000'); + +socket.on('reconnecting', function () { + console.log('reconnecting'); +}); + +socket.on('open', function () { + socket.write({ from: 'client', to: 'server' }) +}); + +socket.on('data', function (data) { + console.dir(data); +}); From 7599cee3fd03a5ce645e313f35557a41c9ac1aee Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 14 Sep 2013 22:52:45 +0200 Subject: [PATCH 052/210] [fix] minor --- .gitignore | 2 +- primus-proxy.js | 50 ------------------------------------------------- 2 files changed, 1 insertion(+), 51 deletions(-) delete mode 100644 primus-proxy.js diff --git a/.gitignore b/.gitignore index 0e8f9c134..a39b4e147 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ node_modules cov atest.js notes -prismus-proxy.js +primus-proxy.js diff --git a/primus-proxy.js b/primus-proxy.js deleted file mode 100644 index 9a23276a0..000000000 --- a/primus-proxy.js +++ /dev/null @@ -1,50 +0,0 @@ -var http = require('http'); -var caronte = require('./'); -var Primus = require('primus'); - -var server = http.createServer(function (req, res) { - res.writeHead(500); - res.end('Not Implemented\n'); -}); - -var primus = new Primus(server, { transformer: 'engine.io' }); -var Socket = primus.Socket; - -primus.on('error', function (err) { - console.log('Primus ' + err); -}); - -primus.on('connection', function (spark) { - spark.write({ from: 'server', to: 'client' }); - - spark.on('data', function (data) { - console.dir(data); - }); -}); - -primus.on('disconnection', function (spark) { - console.log('disconnected'); -}); - -server.listen(9000); - -var proxy = caronte.createProxyServer({ - ws: true, - target: 'http://localhost:9000' -}); - -var srv = proxy.listen(3000); - -var socket = new Socket('http://localhost:3000'); - -socket.on('reconnecting', function () { - console.log('reconnecting'); -}); - -socket.on('open', function () { - socket.write({ from: 'client', to: 'server' }) -}); - -socket.on('data', function (data) { - console.dir(data); -}); From 6e77cd390929842088ae9f6deb922a6627ddfecd Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 00:43:01 +0200 Subject: [PATCH 053/210] [fix] do not call .end --- lib/caronte/passes/web.js | 2 +- lib/caronte/passes/ws.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index 6f82da22e..64ee2875c 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -103,7 +103,7 @@ function stream(req, res, options) { proxyRes.pipe(res); }); - proxyReq.end(); + //proxyReq.end(); /*if(options.forward) { req.pipe(new ForwardStream(options)); diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 1c9215b84..63a4deeaf 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -90,7 +90,7 @@ function stream(req, socket, options, head) { proxySocket.pipe(socket).pipe(proxySocket); }); - proxyReq.end(); + proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT } ] // <-- From 92de6a6f951a6a82e61cc34b5de9dc378d65e788 Mon Sep 17 00:00:00 2001 From: Jarrett Cruger Date: Sat, 14 Sep 2013 20:46:05 -0400 Subject: [PATCH 054/210] [fix] add ability to proxy websockets over HTTPS --- lib/caronte/passes/ws.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 63a4deeaf..baf13a723 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -1,10 +1,11 @@ var http = require('http'), + https = require('https'), common = require('../common'), passes = exports; /*! * Array of passes. - * + * * A `pass` is just a function that is executed on `req, socket, options` * so that you can easily add new checks while still keeping the base * flexible. @@ -60,7 +61,7 @@ function XHeaders(req, socket, options) { }; ['for', 'port', 'proto'].forEach(function(header) { - req.headers['x-forwarded-' + header] = + req.headers['x-forwarded-' + header] = (req.headers['x-forwarded-' + header] || '') + (req.headers['x-forwarded-' + header] ? ',' : '') + values[header] @@ -74,10 +75,10 @@ function XHeaders(req, socket, options) { function stream(req, socket, options, head) { common.setupSocket(socket); - var proxyReq = http.request( + var proxyReq = (options.ssl ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); - + proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { common.setupSocket(proxySocket); @@ -95,5 +96,5 @@ function stream(req, socket, options, head) { ] // <-- .forEach(function(func) { - passes[func.name] = func; + passes[func.name] = func; }); From 3c91ed3d26d9af640d0c7a09fb9cdaf80ad673ca Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 13:03:51 +0200 Subject: [PATCH 055/210] [fix] proxy to http(s) --- lib/caronte/passes/ws.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index baf13a723..1fd3fd780 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -75,7 +75,7 @@ function XHeaders(req, socket, options) { function stream(req, socket, options, head) { common.setupSocket(socket); - var proxyReq = (options.ssl ? https : http).request( + var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); From 26c4c43a06263ec6721bc0e8a90644297d0cf217 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 13:06:39 +0200 Subject: [PATCH 056/210] [fix] proxying to https --- lib/caronte/passes/web.js | 4 ++-- lib/caronte/passes/ws.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index 64ee2875c..8c70a6e1a 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -86,14 +86,14 @@ function XHeaders(req, res, options) { function stream(req, res, options) { if(options.forward) { - var forwardReq = (options.ssl ? https : http).request( + var forwardReq = (options.target.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req, 'forward') ); req.pipe(forwardReq); return res.end(); } - var proxyReq = (options.ssl ? https : http).request( + var proxyReq = (options.target.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index 1fd3fd780..a14386666 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -74,7 +74,7 @@ function XHeaders(req, socket, options) { */ function stream(req, socket, options, head) { common.setupSocket(socket); - + console.log(options.target.protocol); var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); From 18341d559717e0a86f5ee4da024109e4b5a595a7 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 13:07:24 +0200 Subject: [PATCH 057/210] [fix] console --- lib/caronte/passes/ws.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws.js index a14386666..1fd3fd780 100644 --- a/lib/caronte/passes/ws.js +++ b/lib/caronte/passes/ws.js @@ -74,7 +74,7 @@ function XHeaders(req, socket, options) { */ function stream(req, socket, options, head) { common.setupSocket(socket); - console.log(options.target.protocol); + var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); From d1663549ec070e7ae8bc45ffb148f40ee903192f Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 13:18:21 +0200 Subject: [PATCH 058/210] [fix] default port --- lib/caronte/common.js | 5 +- lib/caronte/streams/forward.js | 88 ---------------------------- lib/caronte/streams/proxy.js | 104 --------------------------------- 3 files changed, 4 insertions(+), 193 deletions(-) delete mode 100644 lib/caronte/streams/forward.js delete mode 100644 lib/caronte/streams/proxy.js diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 2a2954bb5..6b75c6f47 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -21,7 +21,10 @@ var common = exports; */ common.setupOutgoing = function(outgoing, options, req, forward) { - ['host', 'hostname', 'port', 'socketPath', 'agent'].forEach( + outgoing.port = options[forward || 'target'].port || + (~['https:', 'wss:'].indexOf(options[forward || 'target'].protocol) ? 443 : 80); + + ['host', 'hostname', 'socketPath', 'agent'].forEach( function(e) { outgoing[e] = options[forward || 'target'][e]; } ); diff --git a/lib/caronte/streams/forward.js b/lib/caronte/streams/forward.js deleted file mode 100644 index b1bb58f3b..000000000 --- a/lib/caronte/streams/forward.js +++ /dev/null @@ -1,88 +0,0 @@ -var Writable = require('stream').Writable, - common = require('../common'), - http = require('http'), - https = require('https'); - -module.exports = ForwardStream; - -/** - * Forwards the request to the external target specified in options - * - * Examples: - * - * new ForwardStream(options) - * // => { ... } - * - * @param {Object} Options Config object passed to the proxy - *  - * @return {ForwardStream} Stream A clone of ForwardStream - * - * @api private - */ - -function ForwardStream(options) { - var self = this; - - self.options = options; - // To uncomment the line below, please see - // https://github.com/yawnt/caronte/commit/9ab8749a9bec33b49c495975e8364336ad7be1a3#commitcomment-3947117 - //self.res = res; - - Writable.call(this); - - this.once('pipe', function(pipe) { self.onPipe(pipe) }); - this.once('finish', function() { self.onFinish() }); -} - -require('util').inherits(ForwardStream, Writable); - -/** - * Fires up the request to the external target - * - * Examples: - * - * (new ForwardStream(options)).onPipe(req) - * // => undefined - * - * @param {HttpRequest} Req Request object - * - * @api private - */ - -ForwardStream.prototype.onPipe = function(request) { - this.forwardReq = (this.options.ssl ? https : http).request( - common.setupOutgoing(this.options.ssl || {}, this.options, request, 'forward') - ); - - this.forwardReq.on('error', function() {}); /** Fire and forget */ -}; - -/** - * Closes forwarded request when `pipe` is finished. - * - * Examples: - * - * (new ForwardStream(options)).onFinish() - * // => undefined - * - * @api private - */ - -ForwardStream.prototype.onFinish = function() { - this.forwardReq.end(); -}; - -/** - * Implements `stream.Writable`, writes to the forwarded request - * - * Examples: - * - * (new ForwardStream(options))._write(chunk, encoding, clb) - * // => undefined - * - * @api private - */ - -ForwardStream.prototype._write = function(chunk, encoding, clb) { - this.forwardReq.write(chunk, encoding, clb); -}; diff --git a/lib/caronte/streams/proxy.js b/lib/caronte/streams/proxy.js deleted file mode 100644 index 29c2fa78f..000000000 --- a/lib/caronte/streams/proxy.js +++ /dev/null @@ -1,104 +0,0 @@ -var Duplex = require('stream').Duplex, - common = require('../common'), - http = require('http'), - https = require('https'); - -module.exports = ProxyStream; - -function ProxyStream(options, res) { - Duplex.call(this); - - this.options = options; - this.res = res; - - var self = this; - - this.once('pipe', function(pipe) { self.onPipe(pipe); }); - this.once('finish', function() { self.onFinish(); }); -} - -require('util').inherits(ProxyStream, Duplex); - -ProxyStream.prototype.onPipe = function(req) { - this.req = req; - - var self = this; - - this.proxyReq = (self.options.ssl ? https : http).request( - common.setupOutgoing(self.options.ssl || {}, self.options, req) - ); - //console.log(common.setupOutgoing(self.options.ssl || {}, self.options, req)); - this.proxyReq.once('response', function(proxyRes) { - self.onResponse(proxyRes); - }); - this.proxyReq.on('error', function(e) { - self.onError(e); - }); -}; - -ProxyStream.prototype.onFinish = function() { - this.proxyReq.end(); -}; - -ProxyStream.prototype.onResponse = function(proxyRes) { - this.proxyRes = proxyRes; - - var self = this; - - if(this.req.httpVersion === '1.0') { - proxyRes.headers.connection = this.req.headers.connection || 'close'; - } - else if(!proxyRes.headers.connection) { - proxyRes.headers.connection = this.req.headers.connection || 'keep-alive'; - } - - if(this.req.httpVersion === '1.0' || (this.req.method === 'DELETE' && !this.req.headers['content-length'])) { - delete proxyRes.headers['transfer-encoding']; - } - - /*if(~[301,302].indexOf(this.res.statusCode) && typeof this.res.headers.location !== 'undefined') { - var location = url.parse(this.res.headers.location); - if ( - location.host === this.req.headers.host && - ( - source.https && !target.https || - target.https && !source.https - ) - ) { - this.res.headers.location = this.res.headers.location.replace(/^https\:/, 'http:'); - } - }*/ - - Object.keys(proxyRes.headers).forEach(function (key) { - self.res.setHeader(key, proxyRes.headers[key]); - }); - - this.res.writeHead(proxyRes.statusCode); - - proxyRes.on('readable', function() { - self.read(0); - }); - - proxyRes.on('end', function() { - self.push(null); - }); - - self.emit('readable'); -}; - -ProxyStream.prototype.onError = function(e) { - if(this.options.ee.emit('proxyError', this.req, this.res, e)) return; - - this.res.writeHead(500, { 'Content-Type': 'text/plain' }); - this.res.end('Internal Server Error'); -}; - -ProxyStream.prototype._write = function(chunk, encoding, callback) { - this.proxyReq.write(chunk, encoding, callback); -}; - -ProxyStream.prototype._read = function(size) { - var chunk = (this.proxyRes ? this.proxyRes.read(size) : '') || ''; - - this.push(chunk); -}; From 58238421945bcc4236e280ebca7799b831ae29a4 Mon Sep 17 00:00:00 2001 From: sateffen Date: Sun, 15 Sep 2013 13:39:38 +0200 Subject: [PATCH 059/210] [Fix] 2 spelling mistakes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a2db2273..6b4637c56 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ require('http').createServer(function(req, res) { ``` When a request is proxied it follows two different pipelines ([available here](https://github.com/yawnt/caronte/tree/master/lib/caronte/passes)) -which apply trasformations to both the `req` and `res` object. +which apply transformations to both the `req` and `res` object. The first pipeline (ingoing) is responsible for the creation and manipulation of the stream that connects your client to the target. -The second pipeline (outgoing) is responsible for the creation and manipulation of the stream that, from your target, returns datas +The second pipeline (outgoing) is responsible for the creation and manipulation of the stream that, from your target, returns data to the client. You can easily add a `pass` (stages) into both the pipelines (XXX: ADD API). From 4c2f2f3b9a5ba65f97403e778a670f14301d52c1 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 14:57:19 +0200 Subject: [PATCH 060/210] [logo] --- doc/logo.png | Bin 0 -> 48744 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/logo.png diff --git a/doc/logo.png b/doc/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..412de186b2ea95b4d34c5ec4a1dc178c8f7bd252 GIT binary patch literal 48744 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

?lwdFnp@9#ccGq@gLZS| zEsw;SO}>NNUB5SUz#TTfRs~P*&BHFx!Z%PEbb9eA(-JZHWI9b&#qIH|;%`5(@^+s2j$6PD3 zGE5T>iu{?)~f1%-*xxlU$lf`H;(hv1|s z%OqI>dTO%boU*Kb|9axF3gE{v_B%04)fZHVOyNy?+0aRl>2pTbkW{*I6TZs z)&T=Xfp5p9=*6Vx)tQjVg}|6{S?Z%BDi=H7x2}xiIR!0!(T`3GM~}Ag>UJ)a32S+| zYo6zSw<_8s0j@$v9^@+ny8-CQT`$@e7-@)ASd$=(K{-?PH;>8lODzPPnwO=B8al-X z)eXr^ifZqxGhffxs;{D>2LH6wN2hxBOfla;P?&P-zRKg#j!B|7j`W6YY>J|F#?Ye4 z7)am&{{BoG&?A}3#eD?T=zepehaN9b?;)wiR4r^pd##dRBRmj!%7Az9{i;PZyBMQZ zO-hlWLP=oYR}~?&GkPNQnooHIcn+jsHE^7B zm6zkjZel8vGUNnLgiKpMGA`n}{ z=o+!_oxFBU#^*a23C-AGh80j>XNimA4@k9LVEiD14j9{$DuBHNrPscS(x&!j_#fOsG^hIW*fst7q>Fss zrWT5*jZfUlRd2X0s2-e!9Q$4Dwgk_tn*-4`ioV*MYV(JI(GWlZX76L4v|c?*WBqvl z-q{W8Ch{heYLw^0+fBiITfD}>kibCT0tmQv>cL6XY#*V119sI3efUwcoCxZ8?ARpV zXFsW(#y9(Gk<3@S^A?S_6e6d~QyCeq)Z!#lR@^O3RWTDW`qFOQ$?sG7Ca~|m;wp(OkQ*B6`h(j z50ncH<@^!Lwh{bBsuS|Is0$F3$gX^XCT0Ehlqk$FsUe&I0IO1$ zNCU>gPmP>b@^)f7@8~$}QA0jz2i=!XsuVKpum=e! z0e*Hb;1;19FkTN9-KD(nd6lSBGY-C(c#U~|P%kN})A!w@@{4_5pXO(>H+3wOFeKTU zHZG=F(W9Q$^H6GeQGZ#qFWl&cWNee*sK$T;BXHE?7bB7U9nZ03m|7XL27v{-_}>5E z!AvL2(d*fA<(aho?RyY?Z**C1G*p^eIGjGp@E|`eg@{xVPo`>}d+u^d>{}(oy$DVo zPljEYyHuQ#j3!Nf0d*8NHhg0L$^!2|0PxDKLxRDAGTg!g-KK0&8x+{+K#(SfK6~mh zpb&w(RJN@*U=r~g8P6VPw^C!r#zn|P$h5WR;NeMWT#D#R&y~_|C8tGvuX~FQS0wLY+a-{>^`kHs8QJru80?D~1Q&hh-r{Xu@6#7zU6mn?URR&97LO5%>}@EC zV<~%hK{7>!mjkdBAd(^vjdMVGak@PpgT9Irtu&814!B-)I34Rmt|@RWLa5ELp^@~H zkW?G)c4(Ov;7-W01cL%a*hCb0Gboe$ba_^I5z=z7NJ!1kH(t^VCYh}*xi#STuqvn& ztwp{Dy}9V*O*IfIO_B*V?MLHyyW4I?)^y2>C7uLEy}5NaYFj?h*F!0yR!L5?pFMFL z5Z-l_R$rRPHrkr2f4Net0x^=plO{=>BHGwp`Ru#1v~qgIK}AJnd0co>hR;{$$=d4l z_SEYm+9RBTS7n(OZe|1_&H4>N7fyhAt0uYov`f1doUz%EHj6zu#~_No;;1D8oiS2#`imL77yc zo#9akmfFpY^oox}KBgtw4}8&qfn=7qwcqCfE4MNV$gZ{z^X)oR1!vL7*(6my{fffd z>j#dPsg|&dCo2f7em)Y`FB-77;yP!P0K$6_(zV+Z4LGi+XLyxsB(WmGWhY(N)9N=^ zc9#HOQbbO7Qs1nk$>|f1mNPtsqA90+>Zgp@t3Td5=GZUmlhyC4076MkZUu-0n;tjx z0~gd1{zYc(av?;(MxNf5suWT}v{c&;z#IKzjW=cd34Ka470VfjM z8_oNANy8r`wbq%rTAgS!zAO(^L6&`9k$taOBfcaviCZOW&8mHs_W{D(#;(*Udl9X3 z4btylIx4lX34XD2&9Hm3XF}QII!W_=bZS38k7@jBtGzPBg6mz^=%qq&l6o#)k^k%T zr&tx^at%lkt46Ndbd1EU$H)C!0p|i3TeGqj8U{V+f@8;pg&36C9IqZvDNDHtl|JtC z2Eq%=d8=epSm2c8rIfKli&Q)mZ~bIuiz{w62l-fXkAS{Urb6g!HM0Pm z7o6zOh|o@z2K!vIgZi&M3>vAjv>DW7s9c;AvDu{Db?(^cFXNTk)f3JO(v)hSI@lUS z^=%itT_)s8ifCJ)OpG{KyB3py@c^cWLCfKCtF_l##4IuX(kFaK+`M(wPOFDo-nLA~ z=>3*%bJFVb2b+%Gd~ST`>HW1CEMoQ&ED`M_R4ZziL{={A#mD!`*_=IxN89__9c~mTFbA6HomGmr7f^1WW~VijbjSa_f-0E^>jsDL7afEWt}O zHNt(P6s5@-4o+*Gp+ExmpTwl%m1*-BsTYS)YeN|}82ki>WUtZe`KoBd3Ua5Y592Ong3HZ+e8+!ia%=_pj z%el+*=7Bua*}I9y*Irtkk_>fm|6!q%BNI!B7dPnmV^j?OE*-nlBFKVorZ+9>sT80> zm&~g}&so3pVp-w~V72u7JqAwKZG0i~jC3L!fW!LmTXj0WPhkEyO~jWaXD$0=OjWdM z&^qz=iy+kzz?v-}yPyfVo}uqZ*w-&_p^s>{Fay-Z$74*f5b8d_G`jjMyY#R=C6$uk zR^zo~wzwcM;JkUEID6#5o8gI&nq&>!Q#l2{Kdoqa!ySphV$(KMNhs80Jwm*R$BM;2 z!mUs?Iih3Wgo7OG)|2M=qoMBhiPZHB;2x?qd@gc&vo)QPZ@e`{k5l6G?(|IJWczz( zJ)yA#{Ug?b13}i-l@&#@rq!-k<#-}lV$Aiguo%w$c;GTWA(w?7F-iSY2Kn36xc7TK z54t+Q5NC7reGGlI&v0;(2v=O0Q3Z>FqMWFRY_ze=>$`z%b&*-jP8Y=rDN}P>bvOF8l z{diI^Y}8JTDXN)&eBtPElwHB<*}cy2%V87&i1O-p)VcO=_?vR=IeGZB(_kd%*JuOs zwjuZT*>en}VLeq^o8{TAtmA$$5VItAVJGpGXEntbIL6QWUozp1RW6JgjZk2ftnS?y zJrb;lZU~o^hEMr+j{_UE+HTv5HzUB?)^u5Xki+NI`Y^{6I-aDQCH)eJ-|HV=r6hz)5p>DQvn*bCRN7y@W)@pi z>U@fL*0F6uLqY~5P9%K0kbwtyhW|qV;S0>IstdS;O41C+!QSeDdS`ij@d&q)X1l2e zS_o0P%WT@Oja{SQCo9&2(-@mG%>qBEVZvHr?7gAt$Do zRJO8ANM`YNmx#5_)0lA9N<)s;96~-5-Q4-H=N+Gd_@@Pr*9;zDlfn+uRI5sd!oUwm z@w+*gWk_`{`b=}mBh*;nAA2p2uQs0EwhcO6q6nFI-EU)$Qm%?-HIb0k+9vV|G7`#c zE1P0!S)C*_d`?r1sb1WftEHUeq?O51ub|^7P-9;v{~>sgu&I~1+EOsxvGeH*ephF{ zS#cvhEg7xHKuEgQ$v^Z#V_hYn1<+0&1JRDAw~wggt2jgjLyeUV(gtss6*&<~E9e`m z$KENkC4NtOs~2H|1K;QFK!Qr>;wC_3=m^~>8WiysYY`VQ4j`OVN{y_1^4(bM28gn2 zNTsBF_~5Chj8H44%$oMu(UaN6YzmA5 zc+J-}bJlp?8?_Qj+YWnJspBcS5H+^dn6A;L$-L8u{81#0lqZXMzbeQ1s@oXl{C*B~67KU7y;g5jx7Y{edH6%YlI*X%x4V3 zT1A!~!}h3*;w$X%rMdrQ(x7Yu>QJlFJ4Mdw(}<}{{L!O}`&a?+3CfsF79WRpzSu|D z^xhN_mDaFF?2-OJ+iVC00T-_9{e_%qkMzAWq#j+od+cZqv(Zno`ph*F5E|7Hm@&15 zNvbv)W{eNczXwwu7SBjf642t1NV)TyV2hU=EKoef+_D;cWnI~W5=;#kt^`TtQ{t2a z3>iX=Kv(QMK%{M{z!i1yiDjm!3|G-8aemULjic+&T0>v;{Z?6J94W zOI3PGO7<-#!o3UrLPIF}{irSnFw}G5suJeT@n|58k51Obx9Q`IDCgu+uwN&}i|APk z#7G%Q_zhCyN>KyAKjtj>Inn}Aw|T)3@Y20n&H9+N*N((mSH9$hC4)VgKNRo-sX@E& zV^4{G-Fb-xq9^)5wnBdbvuvWNrvI}gz4ku82Vuc5LgB`AeISz$`DHA-@K8wO#FFWT zS)Gu40aVy)aQlO23E;P9Guu@(i0sPYuJh2`ZstS_(WD%x;qwo4V{1qS=FCS1@)7A? zZCmg54fb=@+WF7MjX!azu$u1loB*8S7);$9O3bGtuD@nhfrT1O|z_ z9DQe^mI-!OhLF#;ZO@&x9%b=MvZi&AmvrqoCpEC@cQ(8j&VK#{*J6}s^tD`Ou_%nM zwhV!+P_nt0M@^Tt%Gj;A2Q2=TQC(Ucx`nZN;W}PAvi@W5CK*@`;J)V&v4?T1KUQ&l z!#ybSTrA4Y8g^13SMVdxJ8+D+nG%o{8BF69e#f#tvE+;hS7=6bWXJ{?=&O^`j#WE9 zLm)-XV}9G1dbF<P(YujNOz2gy;>obCArl*k3#O)atv87|Fm8G6SK#v(D`FJci($K zohukAKtrn|669yoYEX=>nxh0^o zwiI7|`M_{fW1U$C0&*!D-tXyZY2b2Br1(QOr)T3@G8Zun^Gd)=#&UeR!$B z)n4$NjY(um|5K$6k~$s`w~4cJ-bM))r_Fd*mK|pKo;NNctYH0PFg;XB+J!$|JG$S` z0YN5*UT>Crq<#CCjX%RseLDs%^Aqi~jZxJjdFVbOX~`&V37k2w0m7n^qFLFMmdN`y zDRksb(I83%!3>HFrMN*oUIJPmZ)V3k0gib*+cxRaV>z|j#&OepThD-qbYl_7k9g?l z1SgTQ)m_NRIjTyV_J;SyXJfb$GS++DXw1)n2xpES_ceiXlu?3!1QT0$_S1$2uO$#D za|Tjw%)nr*f@%LKNMv~D$D@rj-R6DaS3ACMhT7Ss{I$ai@yprY-=|KDskXbGJiC9a z6oZlZ=)ML#H4aJFBKliFLl9RDaXYUJ4L2`*pxA_|d=tV1Ih1Z5z%H9?zP3$w#Kb5dZEUmUnMzBNI@wU3uTgja z|1*yZP)<-48v0SbA14n>h;)XlS0IhVkBKR`MT6VcX~`7Rt0u03I$2lBbn#OHu5#`0 z?Q8X%Bw6@6cGl=^R+Kbw8fWC^_8rX^u-wu6N479WhOlMg^T$>6^@Whfc_^cB>H-!~ z0q*g_%VWe$G9!wpQi4U*Puj|vfeZ!`~N<`goZiv7ZAia#Gkmt0Z@>hyMUvRE}-*7g5Ney_)^6N{Zf#?D|%~z^OoHvkE zY2n_D<5lf+OAgqV$Z$I6dJ+0!1;+Y783rKh?a4s%nH`Wl0+`c9?#Rc=4p&FMCV*kS zxDR`RwRLHaa_pV1#m|j>Deni7LiRbamK}5+k6w)}C`K zrzO=>L{T=Bad7Z{iQ?kTsQQ97(!LIZeep2|s>b)bQj)RPu7IZ0{C7BJI zrn2n2yc8B-vofNGrPmq|Jp@<|JK9tOPk$=;n?HoDNE)r6y2j2#i8ak?d5UZQA{fxk z1@2Nhk1+tp2?tuptFlDUvmSxGE+rrfmlAOO0rAGZo2DeHtUgl>&4JkiT50unYL8xc zm<~n*djez+4{z)qqr8%g1HCtBp^s|1T@2`pf6(~x)Os!_yQn$8gd~@$8?b+e0r zX?uA7I60_j>n+j?5$wkfK0V?JPqE=gKQ2T}Ev; zd!5Odu^s2(Ie((MuBZMQOxgSKoD$;ML|>tCAYz$HAS0*8as+Vf&qI9WrQ4WInkyBM zxkXes>fL&)f|YAz(U3F*KT@{5Y4FL~nIHHQF=SLt@SSB)C^)*Emn9A_cC51NoC~2zxX3%p+KH8FZ&--f} zR~_CB@|i|VZswcwzyc^UT7RMO0^-U5r@7i-n4HO}SyHLKs=o!(LL_#2*~TJcLs|CJ zc7%DL60Nc@fh>&2uSF1CtiRz`RLB_V6{GpZF3?Qi2lI%jt5Rs!Cgvy+Qb``vSw*c_ znWso0re1jVL?PTjm{Wz;65aX@WK(5E&tjN8-z-nX*G~e>n`g%lBw#1^%^xD}Uu?2AJn6*gMx>%jUmnAuqb!H{B_w?y-haU zx2^{_jP~hZ$$)t+Ps)zC^q(YZ?B**B${TcWqoWyhr3iL+u`7tF(&2K~dG2S%`e77I zE86&_tkmX#eOKzFzgQG{SocdjHG-*WA{w{iBLa$WUC1Q`B&-V+E)!uu+O`NyZkW;*LC;$Y0 za_FA`2{+VweCo2$rEgc5fk0jlp`xt260YkTv?9%zCAoAEvl2HBC0Paq5Zq9qCE5~^ z1>}6X`|L7s)cf|OeD`2HIaAwo);IVf;FY6{+#_8W54<|H+FXo9VKVqN)9!lP&EBSbiAhlwoQj)mz72w^&7z0Bx!7u0YZ<8U7SE*SV!3BHUl__=GBvS zNj8;x@X3b$T!gLb4}seO;6?L%ei{wzNp_*f`$K;hqjxwQhO>gMVUJ#0n;&cl8D%yw zTDVk~_A9JNHAa0>PLv@*z>;dS!_6UW2=2UHL9Mg1?&*XcO zdZ^hU8M>hR5?CscFyLi{Cs{NZ3fgD{Q()ew=*9S>vwAMtiHc>E{QT4QBcg^-@a&|p z(n`!%8i@*zr*&PbtZ5QJCeYda2&kNBvXuIk7HPQYkNtBFw8I4WFVfs!FO&^F&VSnk zvLM>HqoSWlB_-x$%`q1}dR;>WR|PH-;r*H2@4Xe26(937%9yB+lxn)IQ5iBI#U)WK0axN4R9#6=!}F}fU8R9rK(t-=>f z9~aZ5Tk9&aYV3#3J89)!UB}Q})o=IF8ii(hP>?NrY%izKT)9~LXDC=0Imc{m0?f$b zU<5Ff>d%)lh9d*f=O~8pDNAm+nR|TSeMpC#yQayL6T7z&fh3Wk_O2t3ys)4r( z2;|ijL&EEgx$Kegv^SAW1u3AbqwoRQASzSWC_ZVXrAi?q?plwyL96|X5IonUUJb=& zfKfW`rHoHzZEZYJz=4j0(JSO(tWi)zQdWVxw*OaQXB`ye7rl8=QIr&E5D)|uYivorh8I^#HFfV|xI-g}<&d7g9fQiRd> z196cw-#GGmjaM=D*LJcN-RH2DakfMYEnetY$rh1!PEg_+TLpU+u`f<$=VrCO6~k;t z-)Y-IJ`6>_iD!8_m8~O(wY*qt@zxe!1|#R+{OV6%vq=oU;tX8#Cq-Jl%Bhpx6G{4p zsc)RHTP6AB2*YMPUQ6*f;sP^>?ET=kMHQC{JJ1Uy8^mP9-m+<_ zw+A{jZuyEO4`nioOA$*NLtW{D6g#5&4Le(z^vaD7FNiMG{9G*#dBo^qTNzL;YtW&` zDK0KwTfkhN0fZKa31C~uu(?oGESe4mW7E!Qy0dQ8_qdUrpjVjWoqo)%$>ul|G1#}I zI5oz2>oo=<%>1uoMWR?jHtdD7eBFoC75_teBX2inD?ci!u1kYHd!?J=v}}=}8Vikl z#3tbi@r;m-T1SGJ{F#7BobB>HrrmALrkvOp&Hcv zom}Rji~wyL`?p1%6lPia>Pv$<4c>I}Se&}T32Cg_Xe4DKTIxVqtEx(z5*c|Z+N<~U zp~l@&Y~?vZwWbrJrt0fQUGa?YCA^0(p|Spo^$ySNElL(HtD~YbP#+K1BlM~f+Db?q z9&Z|{_-^Xk)y(zqeg1#T(%nntGw03RT--fnxx`sPBR{gYrW#dXnDDN0ha=zo){jTe38 z@FZp;SwbJvL?xqQf@D-+$n+2s$@{y!^K<_3u0WXr`REre=)ccGSHB^!|`!ko(X3isS)7G(|vs-YK^V^^}?|d zN3fN%$e$Dm&I|%o&R^#eY%(2d`{&n0+&_Xc9y=`9I^{c)B=-6l6U@a<3=e>&#sT;AbQ#Ms=S20EJ6dxrB1twrd=GjqOF!a*8p zS*D2OB04+qYoCe@1O+iE4lm{bLz~p+v$GfM-5k=HBt3e9DXDSnT0J`(=-Uw$@xz$6 zUoX!p}wj4vvG;~eCUBNMR0l;vF1iRr7wo&9{8 zocD%)$dnhh44c?a_q|qDK2R2(PmpVg4Nw{U98Qhm7eOAcMkl7H^G$$OoC~LEAjG%= ztzvz&*Ss4k4E-o*x}m2$Y_2&9eXe!9@jNyd?Gz}F=#_>fl&*M6iML|XSdV!x#~KfJ z=>NT~DIT0E#d;{}S49>0^R3EzM&gCU+XJQR&CrsilgD|MW{HatWx;c&Sp^*Kj%>F_ zmZ|zc2f@EV2^$Yf+sc|hI`;-@a%J-4-h#)?3H>@xSy*WCC{=zvRsn;!k6$(`phu0@ zgX8C`9@iwYop?LyG=&0f>6=tY1MrE9!q+@*w=91xP2Yy*a~heb$XcUr)%XoBy_6pB zQRlt1yu-*eJTN}_I&DV1jiPLOWGF$NG%Gv~wysTrPlbGWY+tEF))U;j(5M4-S`|!U za;gZhO=ZvmrfMnslJJ!h3|ux8#^+7EU&qpDm3QU_dS`EaE&JecrhO?%)V9IN!PuLy z{f!L6Yezzs)$D0)C9f}OV3Jc`>x^6bZ8$xaa=ir; zh$gkOUcAcv3vTo_)8n`C(1io8#pbrZF@#>a9}sq#9W*ry63MFAwUl3}sjg>@?1tS6 z0 z;HKVajRbp=1(75=tkYOcM{g3qbqgmQiM^@z2e^6D^D3gUXvX0za=qci8tp@Gyt$pI zI&!jb+ItOU72Iz9yLSX5mm|CUNf}s&!g!_oK~e8IUniZkEz2JK9QDr+yEU|V<>fQv z3_=1hXaVWbV&M?8DFg;6b(*oE>TN))X!G*{6q{5dR*4K3czLn?9G%O1bg2FB^6#Y1 zM4qLKk$I+Wt`gfsd--=Oin->vY>e`8@oAhy`XQ!p|Dt7Vf|d{%RRRIcql<0E%`KQr ziAPCjO@F>z&O!Iz?4CbzspM@2q7)h&YrZ)o%b-tJFH5i;jD5O6 znlxA%yqf;Cby{+;SdMOW5X~k{h2?k|q4|t}WVOw~BPpn)#!HW@Yu3R%YR~mzZ_=@k z!i?wkDKyfYE*CW6K0^Y`87QSVeVl8U?$JEb})c z|H?hm>^iMEp*MzGg5- zJ^Lmc>Fje7xu;WwD&Flo`jBG+mW0(8;!XWR(wPTo=lG8U=WrNxQRh%kCnsczsLbs$8N z#{1HF33ifcs&`j;Gfgj4LDl;YB^lnO zV3gCtzq7o{=MnW_6vQX&TbfFo^XHxDfI=5~9#CvQJ~LZOb8&B(_{^I>+vJ3+*^O=K z{`lS!pp5qw73PAZcsrudX9c+k+6Z%(0=FzGa4cv)a526IN+sL)5IjqLr-meyvE6Wp zcCxV5g)xn#Hn&V*RIB*`yhoNM11y$3z&2jS3ACRoysBod-xGX_W#AkMBJX5ocKE!w z&I5nLALW#ozijf($*Jb{&qZ23x^%%Ee7|sJJhpHAU714yQXEnVTgNJLvDQ&cLE#AL zFx>aUHUFJA0vO0rP)|`?X!PKvr<(lM!}s5%JDC3C>6y0phq1+`Db9)Cx@HyCy%x|G zl^cjE*Ztz+5clSA{+@YP5yMJM>OmdDl0!xMx4~>-LN}ZMx!}=+)$|94xVQUR^!fH@ zttW|lExl}M-SEco-vdOj4ZeyJ1%2_p`gmZcZOnniIw!H_z0uvDII*;Mw7)Imd&Xz{ zkzm@l7&SLSIeyW;RgzJh)@tGq)k`Xh>8C9r_jHU6xE(yg-5ijp!Mf1VVw8u`IdCE? zy95tBK>15$-wKkqFf+3vSHU$IZx+zb+n0J|>Tq+`hi_xY-%kc*|#E zb(CO3XrP)O*|hq(#-Mj^keSAcL3unleF~n^fL+n0D64pZm$fd9Zu39#r~}#L)@8(Q zgJHe$H{f}JO;=ejeFQ0Oxgwk#NI}rAf%_I8*3+@gJtrROT(2UY7>Kh82L7mHzH91){T*3mHwhpPNgb)zP<*C4fGPbDq9o6cL6 z#ZoD$F3vJZBBwox?)FUtn*_~Y>dL&qr-#J{jcZ^8?X5k=95?vUJ zkY#479&OLM7D=Z-X~IBK4c!d;tOborZ4XDVAZ43@%HLJjv5k#KdaJQC8y208w0abe zCC}~~A=2QjonChIhsQN)z8 zph!|~5<%F$msXOrl@@=4<$_fuIqN+9n^U)9-}E4BMuR$%$`L6e-C zTeb+loM~sb4o3^6f5}6*_*@)R-GXlGmep0z6Tb8_AQz$D>SRPI=<31E&1uMcA%oSKiOF)J zTQ;36z@HI}dXk(p{I5->+>H#Z97Zqfri)A$=8=%oHAJSj zNf;;>WT-|hg8^_Izwd{+IDHA(ErKF!+EO884@s+--pohcamk<@fW8fgKnISGwA`m7 zK*m%e1N?-bO+!8Et(17EaoBa46y!_%wSSEktkZWj+bLXL4^AbD{K2AEADq4g_EO;8Yh}T(>AiS zjHYb4+A2dvW7SWejLpoJCKlWrxrS$(zz!i;dPl=DSJ;)oFGLe3_t9sMbTNcvz_U>sQjWvA?ugoJm$4$yf-$)3Fghgh21 zyNUXL_~2v^VY6fG3>@8p_L70C*)f(>;uC>pT{jyemYl*yg7MvSr_!!xYGWN7?#I7x ztW6?VMp{K|rZJ!Ts>$?nI>~}E3ck;xD)%o(bTyCsn+4&RzcW?6mtfh|@Q)HUO1jHj zae0tM=Wa=)@XHYOL0EskvhV}y*tiw_WXdsT$ zJy7cEDzg9A_r|J)K;5W3U&+%L&o4scHG0fZbN28XPEIB1@c{wKs00IlWo#RjM^~Z4 zLu0YVNGP0Rer&#_W%_43^=cxmGXkcl$kYwp#~?>)h_)nqXMJQ|Sj6 z+_{K(i7mfVI+Rc$DROrNNtHVPn?$zIJ-X6Ks^xO8!I#fnIUdm@q9BD6_+hS9k12883L`kP8^rVJzSJRkHe(ZQ9r)zH)Wk@r4IgX!mW}; zZ?*lm7`OW-O`EinFvlo+}Em=JAYgCdNpJwyVXr2lLpP`Nn zx?JD;h_(yHcdimIjz+0U|9;K(4n8(0Jxo_bN0l?^Iu$A5D#?JBlNeJ;_XEXTFVS3; z-k>NvM|Xp|K*G#7WD78kQw8I1{i_W{l)~%<0?!J_y9!q-flmA9>uT{mDKb$cXI-rl zC$?Q_lMy1TlqZv)hV<@0v6mL04V@`8|AGm}qL_f6WP!7tM^e{*YIL8HE-!`DGPkQS z1-H@I09htvj3+-OP_OkS4VP?tGQw7RkR%2>4&1E2$@){49AvtTFC7e5xW1jV-TcPg z-$EWG8FoT6%Uc%CqWnOB#ievL5<~vg?w_<$T*KnE&gYXOm0wONXyCS>??7$J#YC%F z;gvXd^t$fql}A82Ri@m*rr}e9M?4??t9;YvX-Yq#-ULJ2w+afp#c?;eAD7SZ- z-;2|#vs3h<##wNllxiRO)?GmiL($$X)}2Ea3?_*ZvE+@$1mug)rZ|-bMy5padrp`o zq6Q}ZHaO8;tD>CdYsJ$lUbup+Wi!&oy@!Q9W{LcJe%tT zH~Vt80&9mX4>vz4SKBk?2^3Pk(Mg~L2~ERsvkGj;i|DLl%;AR)bGr;EG|il;OP!BB zKpZrYd|Rr@)@r<`+Ipx!xUt0}03iwg96m%HP|=K~2>vMUOvvUG1ZoUHq;hcH z=8Ge<04OlfcL{&(3UP9cZ!aA<=ZRiX%Nbd(21NX5Y zo$B_*FWcU>-u}*0ja#_(yKn8H)8RHskIXN9ViGilWgRA-KpVUzuv$V*8xw;j+j+Qo zxUaR>ozpoM7n?vKbwQ!swv=~vR8#+RC+~5I)3l4lo1FTUG8HJk$u#Cp$uHD!{AX1N z^2tRoSq1gyi*Y+v%I~xlpv&?sjow$hGd&fCD=EJ&VL#8Q>D0T}a-AImp-ZWxAQwOw@6yk@9bcTyiJPASdAsv{qP|tYYekvt~!@>X! zTk*wyr6r$YVJE4xg)85uwQ*Ek zNe+Y2aegdF&?_qC;QG0CSTz-LLDn=ht?MMHSL+?=!X69o^nT!<6@G zF1BN>?0k#Bqh#@wpEgTB^a-yo`z#qo=K1;Mrt8=Ojf}npl{5sHTDtOX=p~g7-f>+0 znjU4HJ^`ctzX#a&(hCZA18eLzb3e=c?eI#L>Mw0DQFZCz)nYF|WmNJUC_GGMxVMz% zvgvdXzTr?Tx0znrPyP3H}hisRJ%~$E7Nhwft z%AgY2IGJ>d;#d(gNF!2Z(I<(v9c48CJByAYg9SRetJke>DMYgzs8ZX;9Ht09odxTo zxv+mEF(q>y3lHe;0L{MIWlo*J&%H}X%n});6+USVOCY|}L$maTuX*ohjsAK9%i;Hi z_$&9*7xr9dRPbMqi>>`;-jyqwg<^IiMTvat>DIRvpQ$9k)=o7hO3Y+;?V67>PMFG~7Lf)2=6V*E+C~_ZT%wP%I(rN0V<}&v<(c<~E#Yh5NxcG7YQt5YQ!-2yv zuccNjE7OT<2RPi=V4SPkpuH z;Dy~#bAPnOX8w}vKiLMiJmUm-W4GE35VTpZGNu}8kDlTwCYszI#b4ET8++x$Jvh+P z@k*cd6|BZcGhKMVY0^nbJlu3-+79ea*Hyf)6D6M&S}v}E^7eS|=B0+_cgO#}B+&oR zHUbk7BiCnd689x!IA0)jOH6&ER|?w9rSrANN60gqF~BgzhF-T>@&e|YcT z>&A=LGYpK0h7)agM!$lNmL-NW!}}o?2713$%09A3E7Op-bJ_rFMWQENw0A77X6n%> z3;tS^zrczY`KUNn@?@EOoRy|C_cLUNRHxIJL_|P1`ZKm3*()?|tG3uHyg!d-x>yT6<#sWoubroqvy2JBr-RY%A|yzIwdf87Jnb^M(6eN?KZ$ zkAA^egh-CTi}0hVDz?H118`MA6wKX@>jSr5W0M`)njuqv-7;=DW-g($@b;ynjHzT$ zYwzequ!FX3oW3s=t1~8m0}*cZICIBi4*&76ewi&M3%pIn$8IQ~s;)2|xV1y(2!M?E#OrAgtpR2f(;q!`tI9^;H@df9zUL8; zLsFfn#n066UB9kzE)zoYU^ymC*RVtG74P+BBL)FUrHNV3h1f@kbMkGkUhg7=E0dqs zoNiq9djQy~%^rz^hf`F7FtU@KwY9B+tu6?zNg8h0THnvk$C?^;JBac})JznMc4zau ztqP2%wE7+dmfiMPy4zi$_cAKMJMm%ZRiSdd7fnfhgppBQ+#v(Y@VJT5&cUOi;=j85 zcRhi+JzbAx7E2fOc=#B7Ie%ctQAvPB*mWHjHcLX_&xyGuS!>s)tQNH}!-?YwL0ttc@nEBG>xtX%jE^moZ z*OUt9zUQKyan@7m=&Mks(udP4qd^#IpZ=LxKdxYb<{?t+>^Ucs%F#dg8yO{ z^t*V#$3jWwP_dn-6f-PD%7Uk#Yk>rDWzXYJKDn-{owaJnl&7O~I36>lkAs`VJMeWJ z_wSwBo}>I(ael;ac2^niuQwkTflhDR(XW#SsRm#Wzfg;TY!_cg=9a;K^h=*>0tu5> z#$J%%XO>nc^Io5?fpIuf_KkpyfI@U+VAd8VBnl9_A54C9P?C<$ua183F}0O0?=mi5 zM}d$$X=9Qxe_t;35dC`non{UH@Xr=;7BMUVux#7c230j~y&6OLZF@Pw8NP^jPkng6 zE?IoaWU)@CSLN4?#$okE|EG8}lktfeuios#ffCELP)m=9j4KCF5-FAE)#P6uk9)TK z*nSJ${OlM>zySYu-yn#@Jz9@U$n>gg5y{W@wZT*Guf^EV8fQ-?R-=PVr5(stTeHFr zIqVFO0b}q7p7xU}R}BAVm7Z!&yZHE;l*`fN`5F(jH!=msaZbFk8F|#N+_JqidZ}hb z{hX`UhRK7AO$~k&|0h2`;T(}aku8jYs-Q4iD-Lh|7!LgnA)zPEP(# zeg~`PTTs`Zx1)V-uewbJU1WNxp!(OQyflg#D|YO7J3ysZn(m>c5$;5E^b$ocesS~s z{LnCBz7lOhqlaJ?qr*lQ76yNs0zd;}Y}1rwxd2U8NpfZsK03pbt_14$V1GS+ z!QGB_+#gQNMDay&p_IqJ)IvT_LqWd6fUrrC&Z<;(NiC^aneEK z_hVj1H}}N@h9CsADs=J3XZAJVl%T>MT3H+vK7)eUxt)u=-IZ*kyxOVv zLO9%d=nDaPfOH6XfOzos+smMR>?24^d~VM{f8Rf+y3whM z>+BElYyS_`C+y!O@2diPh`p1{dC|-u89~Z>wcv5<5ui-Y!f%3yP43Go^(ixkPg{^M zlYs9F!3cfn6uSsBW5mw7YT76Zb80+&(nClU!}Aj{(n*4F&4G!DmeV!j_UwE}lGFS7 zNlt|VBDBhSg?M6tj@VyKZFH<5Z3%F%(zJZ6D`v%Byo3$QZ{o6bU< z5;!7_{yuzcZ?1ba=0ADU!%xKXBg%|2;EsShpd-MG`p6U8v|$U0aJOGV`xM~h!YH%^ zQ#vW6%9XwgWZ4qhPz9r_o5G_F9(TOj+0gtcNrdI!DKE|}TviwA74Vi- zqf2__OAJK6CHVM6%NI_{7l|nXsVR&RLKtfSc1uI)S{HuTU%0G`TFd*sN?wUW`N^smGxn-IE*ToC{ zEC2Q_&2PTUDk1CEE}vcM)@%A&&H>hYEgDIwJpP4u;w!zhe>?8)jpOfAZ-vQKgs=p} z5oi>I#vy;#H_b%j79y75%Au*Hf!_DT7yZ0K-y5obs=#+RO`4RMwFjEDEm=zH{Rg`K;R4lW z-9vh0V-DkCjTh3`n^77I4FqfPxvFk2 zYM#3_L-9BePEYG@mz%gWFAd;)<6};B;J`>|dT!nRh-Ez9 ziPW?n5sJEQ0jrG-Z0t*;E~@*eWq%8~^!fYprwo zdPnPI7$bI@e@ddrg#--OrpW2b<#i;SXDH8_2RbsD*wri`D)eqEg)%C!N-dxm`_~@s zyzZIUrvpTS-$}%Y1TQxATk#Wr-Hf4w`S5F6tV4KQw(<4$>Fvclb)BL!xGh%t)W69; zr_NQWXc35O&QP#OD8BbKV&9QfdY*k3omE|nCSN(BII4Rk!W@(A5#@cuBqtuw=~_Up zvnW9?WskN$l-@xDz+v^gi95;RfCa94%g~3$&Y2E@CU4)3Semh~4)XV5xmdbDObFV} z$kmv9L1z}|EuCaws$aDV7mjsjgyx@Z_a@}uq2)Yxk!h9P2_3SX7`B6#0vO6DNr<5D z$i!q`_{tx7b+6+M3d?IN9)YOC(WMaGmn{CJ&xTCBc2$oay^MZ z(eTi^VCK_%;y)`%9nBZ1z?x)-05=gSBONStY7P+q+c%Vpqpg5P99y3DO%z#I*-9(bRCdG4wC@x1D zjkILTo}*qX!sjeS#naB;ViYR<$FB9T-YY4+GD7NsWnA~N~gjkDXM ztx56xoS{}Ck{5qg4i4fQFUTZwq9W36BGGshmLG2sLW8kGUl#hb=@A$p*()`yB>%K4 z{+sM^yawOWigne7Z2RmWqQSqT+e9ys?&s;Dc>M5!c?7(lA;B6$Jc@1p@I5%R;P-)3 z$>+RF7#xwEWq?zveZ}@@&EqT;;yoa+Hk_JySeSCjhqrNu#z9f}vCZ3S6Z0^W%@3=u z^-YmdMr7STZRqvbTL){-9`t&Qp>oTODVI$`N?Ar}Rx%yUw_ie3yIFOBT!g|W(q&xcesyVEa1fCOVtQPTYi3qwB}aH^n63Q9cxWrpJ$Er% z;#%+hu8Kg5x+ql@C)l-nBRNG7uAD1@yT5NR^9()QL&phI-donh7dyBQg8CvPQBt*}m6GIgODc z@ulohX(-AG1m(CcGu_697Ow<{9cLs;QyEcpplR z%CIW#Q1S?)%d?5gGnZO0t8q-g*m`K6*3&{Z(ko-Eg^cWI<1M_eC zuq0Hx8m(-Legpdu%hX>R47H>n7KF_Y%-i6_vi+8xw{rg%WeLIwU6rM1Qif6ZnFBnJ zgVM%xT&0+fCb9(wU%L7wl>$=M7~&%YJ=Qm}Xqiqv z5Tdtx14_ULb^hUaCWO*JGd;>Sp#wz1>iwYt4tqy>NDW|DQ#{?17sf!WYq%E?@a(jq zE;^!#7`o_@RCjpJADX!?3Z=&6)8godgb7c=AZ)lTBCuIk!!mzdzTFqw5aci`$nJCX;*_sV&VVHA^(J+QpMwnxMg1d&pg8n(s2HGaVA?KwbG>)- z+<&;6@Aior9WGqP5cTGFard0Ay;8ZmXAq?-?#XtVvM6bfKyt?DX>0x%P99}gWBzmd z`Tj;X2ao>2RHYew{rHFl9>``&y}DLXE?*!L@^>z3;%dc0N}CAi8?d`>YLngmlT8IJ zEi=wr>pP4qblW4qm9E=YsSZNhfDDG@L5Pfs7;ya0gudal2}2Md9mt)4xD@h9xb8GB zp$^SeCoOmpxA|XHFC>%bj$W1r!EUzQs>oN?>!%`q%xvc)PT2~8Ou?A>D*f#N4f`yy zkl&@jj)l|l!0tTH_0}{=UK3SGw#kne=VOzscJC`41!PGI=^hm>WrgIQ;Epim-P+@@ zmujWT(bF|+fgwtV@%=VU`j#)5S)rpYV`8Y9FUo9-HR@h=PZZ;68NIB{c2l3R7loX^D%Dznps0jGdh)Q>_+Ln&u83@>q2T8e&UNc`_l= ziH=G`V!gG~9Fj$!?ZcvQ zAYVQeTc;TX6FvemoogV-n4oUUCv_px)Pe$&K(IR}f}HTeHEl#nD?gBB#Nl`_)%BL< zY^3JN*u8EuU+D@~lUz<|s-6cJakWm(BZF1#1T;9;w}tg|2&AxjaC z>|H%@QfDW!#|*&A6OUP;8OI>rTJxX3lzQYG25!6H*^Lh^ht?A2-`)n2bvk~7Y!EOB zOqx9sEQL4?(8m_b;3`mloS-IV@q^ zQ6IoKF^Tommk1j5zI6)n6xikAQV7|OycKwb(j8HfZJn8NP%~hSYRIQC{+i+GoA^v; znBwwAsd1+slC0LZc!B{t{p`U8Z%}Blf^ej`n;bIipeRY~-pwnO+f*WdSU6JWxKzkY z*B|L-_yFon_iOuY-EBr{Mp~Lz!<_E&k?Mcc77xD2k$21`)6clvf(ZK67ni#io0hqN zp$E73G_E^HAqXqJJ5k|D;H1u*6;CX9*)~Ap!86@i=d`3G_4PK$>M3}7q4pHwq;;2Y zIBOIc87F!Nem@Zqsa#be{bm-`Nt%|Pn}sgFZT~^H8Rf(dgm^in`5w8j>$`xX^jxf` z9F=M}ICn!XBJ~Y_Sof-IPhSv$7gHR9<_pzaVk&+K=8Z_`VMfX`O43C^Kopkgr|qn5 zwM2;~kPwS>@psejrR8s`FKCG27IkuCyKT2&xtC|*LW^5Ytc?Z}!00(BQ(RXNH zgL?l_N4*1dDMDs9QX+TByo}8;4gS+7j}~R%;pDT~!h`@j-nBZhxeuK9uc88ValC$P zc1j^Gx2_S_|6^;c`4>-M9&8<%u38c;l3jm?TvUEsqK zq}gWG9r<|lO{_y+^dCzPJ3`1hNokcbBuz+#!A!%ppcKZFewmhhh1Ie1&`{7vo3})mLZ* zea(6we>Y+);gO$qWWOQE(;vzve;wx&{#qD2^P#diA3#u(e8*GbZ$>8tGiYKc>WB?R zT19h)J8@Ip|JJ_KbiJJ5I?Oka%Q5I~WYl;vYLfj;GZ+Csn(Uhper_;6>4pv7zJ6E{ zxOKFQzVGeP)$XG6EFIM-ej8tc5np;RNnmVD5qdqTKdfgtxK8#JzxRwc_i(tuW<4EkB8B7b|V{7{ec^O9VPzP{u81&+^fNq z4G=^{aVhgrKVW6p9`xsR6=n88Bx4erRy~lYO=6VFrWG>9uMvZ-0jkJXdj+u z=nXxAl`Gai_yAN`z&_1b)i>4RMBBg`t|I#f@?y)W-lE#hZblOlkbs3urOFr?SL1+n z%zQ@t<#R*V!p1lX^+~x{@s>NB+q??(ge3KQjq{=P=MmMHMvRT?7vAXAkC47F_8Za^ zQm=>B!?O=Rc11m1ktiS!? zvF@4m_J9Uftqz7~6wKyxz1^ zks$arU3hJX34(HAOhRI>5Nj0mTTYQks^vr2N1mGWDauYT>FhD?gL zrCW1-fp%*c0RmvD44vMgLc_|R!OkvOTOXTlIN0OWjmebD@9r3ltLOa8^Xw>|@HG9G z3$C0u61I0>8(b--^YOj8!;~;P`cH1Fj}fvWLADnJd>2EYkMoS|?q?;> z1w%jYKT<$_Feu_7-7GL)GLNNHId#*Q(Y9XRg@w-mp~@%-V#BRwkxy4dmr?=6Dl?*6~qkR&A-s}?f6l6f^DgV{;W zQ(~x?r;X?A4iSWNC%^s?~5H_LBTb)p!_rXR9LD{j%P` z6)kTIqpZrLbn=LPeHh8p8mS1$qao8vsblz7j9#Bj);gnP`vur+WnuF&LYmC};k=`g zi+C$jJ_@h0saj3y@Ov#tHrUJ zzixNm-JUJH36f2v&=*c@ttj~KRwuW5lUHl^5o!q@X}L&5wGIK(yF}q~PqOiQ-97C? zkn=y{w4uOOH;xs35{*#$Oe(Weuk2S6HBTz8id{sfiC)w*=g?i<`IH@yaw>IRrlVH1 z2Q?cbQX|Sx)dczC8z>zN(l#~adw=z2u;JMkA!P_Em$3ykxziCVW1{v~hAG8XKWEtJ z7OSp}s-$^(3Cd)Gy1K8- zcXr4ZpP-RU-SrLpu0n_A9lVwG>T=OK*^@1x07%z!atE{E?}sfDKkBEP~@T>Fplv9dsVBQ36rmbsYO48GKI$HkkG{9B{@x_4ULQjEL z5d)QLDc8MY?Yomax>lR=sbv~(F;t`5F4TOiab_8KUwG1DVdmYZIwZ;;FZ8?zq37lE zplHvI3|4IR2I#4#poZIWig88!+4!+!&p~CxiOBzZ4f(Hq9*Bxo#{&qLE@97}pfd4r z3eaLwvv^_%;43H;WeST_Qr43x4`rM6UWgce*H)m=)={nZw+n$OF|3LFv||s+s4n7xr$Bf6sLr>yF-` z=g0-njz)_qMHTZT7RjKYxIJ5NR6vLi342gAMvLbkO z%)ZPiP-1B$iPx)ijPsZ+Y zY-@ic+|Dz`6x}bBXME2UAuwzk>>0-Qo!Fm7xc%_tyv{;iP*2CzJ0jwl_}j@h~8Bg zEc`1ew2Y!1TMB>|Xi;mrE~dE<^V%ErGeny@9V6anj4dj~`}||#Gp#*QW`d^Ry%se-nf^Ue;QMw}TJfHP)J~Ou6*US&w4h_>GDE-Y8)09#j1)WE?0<7C{-^ z_g(+FV?8ms$RFI?_O%mCl*`VQrvkcG;0gl^BspeKRPXAOhjr{P>=zHn4+Nf8b$j)1 zkwy10va)(ny(c4no!b8_=yg%lYH8+tW^6%>{M82@+`T=H(?5OYQe4yXHq1RU{ETZ) zk?1nbDpl!6jsN?j`G&7Y1$%1)GgFP!3NtuwC^Lceq~iB~sNtPgyBUv#YQu=ntEZP5 z_kg^f0hTD_lUcs0odoAK>6b#TBJlPM>%ZI8%bA#vo|n{!Hz3A;OJT{}`d$6GJ*^tjq*%GGh6 z3RAq(t*qo3wHM(sw{PDF&QwAcndCFa7tRgm)MDg*P(5|?W4N45Iky03U8k;}qxxGtpp&0i$BA*K9VLaQc0<#=0@!p5}@K-V1VTtD}p z@^mS=^nI*IuzWUR_JGaQYNbKvGi`PtODs2|rm%r2tJ=9nhWm*vM-ggk9zHc2ad?`G~hAX{l)x> z#3@z_P7iZ_n`u#*O8VylZ}wI1rHA(mwtvict+Q~j%l)qE1(&tf+UNKq9R`<1&uW-z z>Wp{vFoS^2f`VP6G)4T-_`c4|@SfG#1gAflD%&BGf8(BrWL;t-V1yA(Ogb$8?nC(d zOi*SetAO=qkw&eRlboDsB5A?fY*H4lW)Dt!d7jiD{o&3*Wk*MHoD@8spB@fp2d`ZG zkuQ=Iv*e3fcjKTS6pI|0;=%bG0r^ELdGBLO4pe)OV(%Q_y24&;UNlE zbgOsw85wP@7drfM1HkdIo4W@C#{Rqa)&|y2ITDq5JS88oC}c!4hh$%tELgv@t<_fkrk*Xi zLw&hzzE=rey8qqx%^{4e9+ETh$|7LTWYqeKeJ_t$BuIrg_8q-VYlK00V}1Z4e$QNB zr`~Cdc*SoW+Vl~}QHpjQMJ&4P8huF>&nW6xXqEal-NLMQGcQEehtqsn_sr`Og5MY{+4`0;wHL9z5aS#3bHSO^9bZ_$EDV6PGsl zBiqTBD$$xR=v_wu$83FqSZn?x@5x2{(J8_b6?Tm$d^p{;EON7$U7p%82>i6quQ0lE zl`8U;OTJ-os1G$4zW0{DVzPOLaxjiXRDwlX0rR10l;d7nb-kB;f@g(BWrC>pvJ$B$ z*`rdq^5a|25oa1h-4T#!)3hl;s1iZ=Bx#x`OAyC)lyx|g!f&z>Dh`uq(je5159D7}T6Ic< z-12b(eJ2P@sEJA*k-`TXrTfoNC79{uGlSnUXjs}_Ceg|dh(`HjLtGpxOP?PpRP_+- zJk<^DH$ zY5hh}@}%=XHJmg@EP-bd2PiEtLN9_StE``BHeg&utI}M$&mkH`AQee;=rRBOP^3HF>?o7>1}G z^u>_^9xbT~5=yxUvQ6mTZH?zr1?Bs$Ns>|F=V^h@HMiJJY*H9{9#J28>J^)a=1W`Q zJP5Cz^Pscaw0q5>Va~RvgTF8T;P{X~&^9v_8};7F45QwyBpY zYCGE^2p-RfXqdp%uUxCh)eoZJ_(FiAR22CIk?+6-wQWniRTXI;*(*yCPqOM^9%8~U zV(T6jUgMT0?Za=?ILEG}7&!a?R&{^&kK4ytMnqn`t8sT Date: Sun, 15 Sep 2013 14:59:55 +0200 Subject: [PATCH 061/210] [docs] add logo --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 6b4637c56..c067d83a8 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,11 @@ Caronte is an HTTP programmable proxying library that supports websockets. It is suitable for implementing components such as proxies and load balancers. +

+ logo +

+ + ### Core Concept A new proxy is created by calling `createProxyServer` and passing From 57abb7f26c14e281c3be07a8b84e3c79e066f59f Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 15:00:53 +0200 Subject: [PATCH 062/210] [fix] move logo --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c067d83a8..c18fea1fe 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ Caronte ======= -Caronte is an HTTP programmable proxying library that supports -websockets. It is suitable for implementing components such as -proxies and load balancers. -

logo

+Caronte is an HTTP programmable proxying library that supports +websockets. It is suitable for implementing components such as +proxies and load balancers. ### Core Concept From aaff1966e4e2eb42c9890e57737f57a64e8d964a Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 15:01:30 +0200 Subject: [PATCH 063/210] [fix] move logo --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c18fea1fe..9c30f9c69 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -Caronte -======= -

logo

+Caronte +======= + Caronte is an HTTP programmable proxying library that supports websockets. It is suitable for implementing components such as proxies and load balancers. From ee3cc380665a31ec6af28ddb73dfc543f430d3f8 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 15:05:22 +0200 Subject: [PATCH 064/210] [fix] new logo --- README.md | 6 +++++- doc/logo.png | Bin 48744 -> 47464 bytes 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c30f9c69..842d59cee 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- logo + Logo by DiegoPQ

Caronte @@ -59,6 +59,10 @@ In addition, every stage emits a corresponding event so introspection during the $ npm test ``` +### Logo + +Logo created by [Diego Pasquali](http://dribbble.com/diegopq) + ### License >The MIT License (MIT) diff --git a/doc/logo.png b/doc/logo.png index 412de186b2ea95b4d34c5ec4a1dc178c8f7bd252..a36bdcf2aa5a926166b3d3797025a420ab155132 100644 GIT binary patch delta 32813 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 Vq%`yL0x0mGqO6KcvDEW`{{{a$t(^b> delta 34118 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+!Hmn+6lnWVGC??|X_PWaxFk#vxsMWhux1D_Fg{ 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
vcMUfF%YLH4i>v8){W>}uLfl-DVzU7@o-pR- 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 Date: Sun, 15 Sep 2013 15:07:09 +0200 Subject: [PATCH 065/210] [fix] layout --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 842d59cee..f66f59abd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Logo by DiegoPQ +

Caronte From dd0f7b8876ae5b57fffab8857735b25b159f2bdb Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 15:07:39 +0200 Subject: [PATCH 066/210] [docs] logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f66f59abd..eb1c779a7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

Caronte From 4d3a4e1ee7370347898d1863ab73aa68ed345d8d Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 15:30:05 +0200 Subject: [PATCH 067/210] [fix] readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index eb1c779a7..b8c4196fa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

Caronte @@ -12,7 +12,7 @@ proxies and load balancers. ### Core Concept A new proxy is created by calling `createProxyServer` and passing -an `options` object as argument ([valid properties are available here](https://github.com/yawnt/caronte/blob/master/lib/caronte.js#L26-L39)) +an `options` object as argument ([valid properties are available here](tree/master/lib/caronte.js#L26-L39)) ```javascript var caronte = require('caronte'); @@ -35,7 +35,7 @@ require('http').createServer(function(req, res) { }); ``` -When a request is proxied it follows two different pipelines ([available here](https://github.com/yawnt/caronte/tree/master/lib/caronte/passes)) +When a request is proxied it follows two different pipelines ([available here](tree/master/lib/caronte/passes)) which apply transformations to both the `req` and `res` object. The first pipeline (ingoing) is responsible for the creation and manipulation of the stream that connects your client to the target. The second pipeline (outgoing) is responsible for the creation and manipulation of the stream that, from your target, returns data From 0637322d96e54bbcf5a14bf009dd73314cada4ce Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 15:33:48 +0200 Subject: [PATCH 068/210] [fix] url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8c4196fa..86e7ff746 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

Caronte From adc5be020c7fff09a1c05ac771d5c5ab61002c23 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 17:19:07 +0200 Subject: [PATCH 069/210] [fix] opts --- lib/caronte/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 586ed3f60..ca3e64395 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -41,7 +41,7 @@ function createRightProxy(type) { !(args[cntr] instanceof Buffer) && args[cntr] !== res ) { - options = opts; + options = args[cntr]; cntr--; } From 60de543d04eaddec018a09634edc427a87cb6b68 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 17:55:41 +0200 Subject: [PATCH 070/210] [fix] headers, fixes #467 --- lib/caronte/index.js | 4 ++-- lib/caronte/passes/web-outgoing.js | 20 +++++++++++++++++++ lib/caronte/passes/web.js | 32 ++++++++++++++++++++---------- 3 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 lib/caronte/passes/web-outgoing.js diff --git a/lib/caronte/index.js b/lib/caronte/index.js index ca3e64395..4f4139d61 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -34,7 +34,7 @@ function createRightProxy(type) { var self = this, args = [].slice.call(arguments), cntr = args.length - 1, - ev = 'caronte:' + type + ':', + ev = 'caronte:' + type + ':incoming:', head; if( @@ -53,7 +53,7 @@ function createRightProxy(type) { passes.some(function(pass) { - var evnt = ev + pass.name.toLowerCase(); + var evnt = ev + pass.name.toLowerCase() + ':'; options.ee.emit(evnt + 'begin', req, res); var val = pass(req, res, options, head); diff --git a/lib/caronte/passes/web-outgoing.js b/lib/caronte/passes/web-outgoing.js new file mode 100644 index 000000000..b0f86ce4d --- /dev/null +++ b/lib/caronte/passes/web-outgoing.js @@ -0,0 +1,20 @@ +var passes = exports; + +/*! + * Array of passes. + * + * A `pass` is just a function that is executed on `req, res, options` + * so that you can easily add new checks while still keeping the base + * flexible. + */ + +[ // <-- + function writeHeaders(res, proxyRes) { + Object.keys(proxyRes.headers).forEach(function(key) { + res.setHeader(key, proxyRes.headers[key]); + }); + } +] // <-- + .forEach(function(func) { + passes[func.name] = func; + }); diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web.js index 8c70a6e1a..c7a925d2e 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web.js @@ -1,8 +1,13 @@ var http = require('http'), https = require('https'), + web_o = require('./web-outgoing'), common = require('../common'), passes = exports; +web_o = Object.keys(web_o).map(function(pass) { + return web_o[pass]; +}); + /*! * Array of passes. * @@ -100,20 +105,27 @@ function stream(req, res, options) { req.pipe(proxyReq); proxyReq.on('response', function(proxyRes) { + var ev = 'caronte:outgoing:web:'; + + options.ee.emit(ev + 'begin', req, res); + + web_o.some(function(pass) { + var evnt = ev + pass.name.toLowerCase() + ':'; + + options.ee.emit(evnt + 'begin', req, res); + var val = pass(res, proxyRes); + options.ee.emit(evnt + 'end'); + + return val; + }); + + options.ee.emit(ev + 'end'); + + proxyRes.pipe(res); }); //proxyReq.end(); - - /*if(options.forward) { - req.pipe(new ForwardStream(options)); - } - - if(options.target) { - return req.pipe(new ProxyStream(options, res)).pipe(res); - } - - res.end();*/ } ] // <-- From e08d4edad339d0f7f55900b3e6e6a0e770960215 Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 18:02:00 +0200 Subject: [PATCH 071/210] [fix] write status --- lib/caronte/index.js | 4 ++-- lib/caronte/passes/{web.js => web-incoming.js} | 2 +- lib/caronte/passes/web-outgoing.js | 6 ++++++ lib/caronte/passes/{ws.js => ws-incoming.js} | 0 4 files changed, 9 insertions(+), 3 deletions(-) rename lib/caronte/passes/{web.js => web-incoming.js} (97%) rename lib/caronte/passes/{ws.js => ws-incoming.js} (100%) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 4f4139d61..30db143f5 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -1,6 +1,6 @@ var caronte = exports, - web = require('./passes/web'); - ws = require('./passes/ws'); + web = require('./passes/web-incoming'); + ws = require('./passes/ws-incoming'); caronte.createWebProxy = createRightProxy('web'); caronte.createWsProxy = createRightProxy('ws'); diff --git a/lib/caronte/passes/web.js b/lib/caronte/passes/web-incoming.js similarity index 97% rename from lib/caronte/passes/web.js rename to lib/caronte/passes/web-incoming.js index c7a925d2e..6906ae873 100644 --- a/lib/caronte/passes/web.js +++ b/lib/caronte/passes/web-incoming.js @@ -91,7 +91,7 @@ function XHeaders(req, res, options) { function stream(req, res, options) { if(options.forward) { - var forwardReq = (options.target.protocol === 'https:' ? https : http).request( + var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req, 'forward') ); req.pipe(forwardReq); diff --git a/lib/caronte/passes/web-outgoing.js b/lib/caronte/passes/web-outgoing.js index b0f86ce4d..4e981518b 100644 --- a/lib/caronte/passes/web-outgoing.js +++ b/lib/caronte/passes/web-outgoing.js @@ -9,11 +9,17 @@ var passes = exports; */ [ // <-- + + function writeStatusCode(res, proxyRes) { + res.writeHead(proxyRes.statusCode); + }, + function writeHeaders(res, proxyRes) { Object.keys(proxyRes.headers).forEach(function(key) { res.setHeader(key, proxyRes.headers[key]); }); } + ] // <-- .forEach(function(func) { passes[func.name] = func; diff --git a/lib/caronte/passes/ws.js b/lib/caronte/passes/ws-incoming.js similarity index 100% rename from lib/caronte/passes/ws.js rename to lib/caronte/passes/ws-incoming.js From 29afab4488e098b1554441797f6a4e3dc450c21f Mon Sep 17 00:00:00 2001 From: yawnt Date: Sun, 15 Sep 2013 22:07:19 +0200 Subject: [PATCH 072/210] [fix] headers, closes #469 --- lib/caronte/passes/web-outgoing.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/caronte/passes/web-outgoing.js b/lib/caronte/passes/web-outgoing.js index 4e981518b..941bc6001 100644 --- a/lib/caronte/passes/web-outgoing.js +++ b/lib/caronte/passes/web-outgoing.js @@ -9,15 +9,15 @@ var passes = exports; */ [ // <-- - - function writeStatusCode(res, proxyRes) { - res.writeHead(proxyRes.statusCode); - }, - + function writeHeaders(res, proxyRes) { Object.keys(proxyRes.headers).forEach(function(key) { res.setHeader(key, proxyRes.headers[key]); }); + }, + + function writeStatusCode(res, proxyRes) { + res.writeHead(proxyRes.statusCode); } ] // <-- From 1b867a7f594f7dfe49fc17ff53451a353ec509d9 Mon Sep 17 00:00:00 2001 From: srossross Date: Sun, 15 Sep 2013 15:20:51 -0700 Subject: [PATCH 073/210] ENH: added error events --- lib/caronte/passes/web-incoming.js | 8 ++++++++ lib/caronte/passes/ws-incoming.js | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/lib/caronte/passes/web-incoming.js b/lib/caronte/passes/web-incoming.js index 6906ae873..66beb011c 100644 --- a/lib/caronte/passes/web-incoming.js +++ b/lib/caronte/passes/web-incoming.js @@ -102,6 +102,14 @@ function stream(req, res, options) { common.setupOutgoing(options.ssl || {}, options, req) ); + proxyReq.on('error', function(err){ + var ev = 'caronte:outgoing:web:'; + if (options.ee.listeners(ev + 'error').length == 0){ + throw err; + } + options.ee.emit(ev + 'error', err, req, res); + }); + req.pipe(proxyReq); proxyReq.on('response', function(proxyRes) { diff --git a/lib/caronte/passes/ws-incoming.js b/lib/caronte/passes/ws-incoming.js index 1fd3fd780..3b5e1f525 100644 --- a/lib/caronte/passes/ws-incoming.js +++ b/lib/caronte/passes/ws-incoming.js @@ -78,6 +78,13 @@ function stream(req, socket, options, head) { var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); + proxyReq.on('error', function(err){ + var ev = 'caronte:outgoing:ws:'; + if (options.ee.listeners(ev + 'error').length == 0){ + throw err; + } + options.ee.emit(ev + 'error', err, req, res); + }); proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { common.setupSocket(proxySocket); From 268afe34bb51448d511c9cd73c03e97d1c1baee0 Mon Sep 17 00:00:00 2001 From: srossross Date: Mon, 16 Sep 2013 10:37:11 -0700 Subject: [PATCH 074/210] ENH: updated `ws` and `web` functions to use the global options object as a base even if request options is specified. request options will over write any global options in a conflict --- lib/caronte/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 30db143f5..8a7e83742 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -1,5 +1,6 @@ var caronte = exports, - web = require('./passes/web-incoming'); + extend = require('util')._extend, + web = require('./passes/web-incoming'), ws = require('./passes/ws-incoming'); caronte.createWebProxy = createRightProxy('web'); @@ -41,7 +42,11 @@ function createRightProxy(type) { !(args[cntr] instanceof Buffer) && args[cntr] !== res ) { - options = args[cntr]; + //Copy global options + options = extend({}, options); + //Overwrite with request options + extend(options, args[cntr]); + cntr--; } From ef946a7697b38b13178881b3d1ebde63681dd4a1 Mon Sep 17 00:00:00 2001 From: srossross Date: Mon, 16 Sep 2013 10:49:53 -0700 Subject: [PATCH 075/210] ENH: updated target and forward options so that a string may be specified --- lib/caronte/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 8a7e83742..ef0187fa9 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -1,5 +1,6 @@ var caronte = exports, extend = require('util')._extend, + parse_url = require('url').parse, web = require('./passes/web-incoming'), ws = require('./passes/ws-incoming'); @@ -56,7 +57,12 @@ function createRightProxy(type) { options.ee.emit(ev + 'begin', req, res); - + ['target', 'forward'].forEach( + function(e) { + if (typeof options[e] === 'string') + options[e] = parse_url(options[e]); + }); + passes.some(function(pass) { var evnt = ev + pass.name.toLowerCase() + ':'; From edd8e2f04e4b39391b062fa6437d61b4ebde8748 Mon Sep 17 00:00:00 2001 From: srossross Date: Mon, 16 Sep 2013 11:01:34 -0700 Subject: [PATCH 076/210] ENH: updated readme with an example --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 86e7ff746..ea381d2e5 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,25 @@ You can easily add a `pass` (stages) into both the pipelines (XXX: ADD API). In addition, every stage emits a corresponding event so introspection during the process is always available. +#### Setup a stand-alone proxy server with custom server logic + +``` js +var http = require('http'), + caronte = require('caronte'); + +// +// Create a proxy server with custom application logic +// +var proxy = caronte.createProxyServer({}); + +var server = require('http').createServer(function(req, res) { + proxy.web(req, res, { target: 'http://127.0.0.1:5060' }); +}); + +console.log("listening on port 5050") +server.listen(5050); +``` + ### Contributing and Issues * Search on Google/Github From 07091b5077a40dfee29f6fd33ecb38d3fa25b801 Mon Sep 17 00:00:00 2001 From: srossross Date: Mon, 16 Sep 2013 11:21:20 -0700 Subject: [PATCH 077/210] ENH: updated README and added examples file. --- README.md | 29 +++++++++++++++++++++++++++++ examples/stand-alone.js | 15 +++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 examples/stand-alone.js diff --git a/README.md b/README.md index ea381d2e5..dcede4515 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,24 @@ You can easily add a `pass` (stages) into both the pipelines (XXX: ADD API). In addition, every stage emits a corresponding event so introspection during the process is always available. +#### Setup a basic stand-alone proxy server + +var http = require('http'), + caronte = require('caronte'); +// +// Create your proxy server +// +caronte.createProxyServer({target:'http://localhost:9000'}).listen(8000); + +// +// Create your target server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); + #### Setup a stand-alone proxy server with custom server logic ``` js @@ -72,6 +90,17 @@ server.listen(5050); * Commit to your local branch (which must be different from `master`) * Submit your Pull Request (be sure to include tests and update documentation) +### Options + +`caronte.createProxyServer` supports the following options: + + * **target**: + * **forward**: + * **ssl**: object to be passed to https.createServer() + * **ws**: true/false, if you want to proxy websockets + * **xfwd**: true/false, adds x-forward headers + * **maxSock**: maximum number of sockets + ### Test ``` diff --git a/examples/stand-alone.js b/examples/stand-alone.js new file mode 100644 index 000000000..d3848abf4 --- /dev/null +++ b/examples/stand-alone.js @@ -0,0 +1,15 @@ +var http = require('http'), + caronte = require('caronte'); +// +// Create your proxy server +// +caronte.createProxyServer({target:'http://localhost:9000'}).listen(8000); + +// +// Create your target server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); \ No newline at end of file From bd106d69f074a1c7018e685a4e144e23a17beb8c Mon Sep 17 00:00:00 2001 From: srossross Date: Mon, 16 Sep 2013 11:24:00 -0700 Subject: [PATCH 078/210] Updated readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dcede4515..e5b38566b 100644 --- a/README.md +++ b/README.md @@ -94,8 +94,8 @@ server.listen(5050); `caronte.createProxyServer` supports the following options: - * **target**: - * **forward**: + * **target**: url string to be parsed with the url module + * **forward**: url string to be parsed with the url module * **ssl**: object to be passed to https.createServer() * **ws**: true/false, if you want to proxy websockets * **xfwd**: true/false, adds x-forward headers From dfdedf23b71dc4c5cbeb034422c9214bd18401ae Mon Sep 17 00:00:00 2001 From: David Glasser Date: Mon, 16 Sep 2013 11:33:57 -0700 Subject: [PATCH 079/210] Fix accidental write to global variable. --- lib/caronte/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 30db143f5..5defa0e9b 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -1,5 +1,5 @@ var caronte = exports, - web = require('./passes/web-incoming'); + web = require('./passes/web-incoming'), ws = require('./passes/ws-incoming'); caronte.createWebProxy = createRightProxy('web'); From 469b0d4e9f065b94acf1f9e4a05a73b5efe48368 Mon Sep 17 00:00:00 2001 From: yawnt Date: Mon, 16 Sep 2013 21:36:09 +0200 Subject: [PATCH 080/210] [fix] add 0.10 compatibily.. closes #474 --- .gitignore | 1 + package.json | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index a39b4e147..099c4d9df 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ cov atest.js notes primus-proxy.js +tes.js diff --git a/package.json b/package.json index 9968b65b0..0f556b14e 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,9 @@ "test-cov" : "mocha --require blanket -R html-cov > cov/coverage.html" }, + "engines" : { + "node" : ">=0.10.0" + }, + "license" : "MIT" } From da9de7034a452d1281217a349bc9403fddcc2b7f Mon Sep 17 00:00:00 2001 From: yawnt Date: Mon, 16 Sep 2013 22:12:52 +0200 Subject: [PATCH 081/210] [docs] fix syntax highlighting --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e5b38566b..c710504cc 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ In addition, every stage emits a corresponding event so introspection during the #### Setup a basic stand-alone proxy server +```js var http = require('http'), caronte = require('caronte'); // @@ -62,6 +63,7 @@ http.createServer(function (req, res) { res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2)); res.end(); }).listen(9000); +``` #### Setup a stand-alone proxy server with custom server logic From f1aeb0500cde39b63e570323e0e478530d1222ab Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 15:26:44 -0500 Subject: [PATCH 082/210] [misc] use the local mocha instead the global --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0f556b14e..f11557c6d 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ }, "scripts" : { "blanket" : { "pattern": "caronte/lib" }, - "test" : "mocha -R landing test/*-test.js", - "test-cov" : "mocha --require blanket -R html-cov > cov/coverage.html" + "test" : "./node_modules/.bin/mocha -R landing test/*-test.js", + "test-cov" : "./node_modules/.bin/mocha --require blanket -R html-cov > cov/coverage.html" }, "engines" : { From 275a5192fa257f78287a954b347e65023795487d Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 16:49:51 -0500 Subject: [PATCH 083/210] [fix] typo --- lib/caronte/passes/ws-incoming.js | 2 +- ...sses-web-test.js => lib-caronte-passes-web-incoming-test.js} | 0 ...passes-ws-test.js => lib-caronte-passes-ws-incoming-test.js} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename test/{lib-caronte-passes-web-test.js => lib-caronte-passes-web-incoming-test.js} (100%) rename test/{lib-caronte-passes-ws-test.js => lib-caronte-passes-ws-incoming-test.js} (100%) diff --git a/lib/caronte/passes/ws-incoming.js b/lib/caronte/passes/ws-incoming.js index 3b5e1f525..0adab3db8 100644 --- a/lib/caronte/passes/ws-incoming.js +++ b/lib/caronte/passes/ws-incoming.js @@ -55,7 +55,7 @@ function XHeaders(req, socket, options) { if(!options.xfwd) return; var values = { - for : req.connection.remoteAddsockets || req.socket.remoteAddsockets, + for : req.connection.remoteAddress || req.socket.remoteAddress, port : req.connection.remotePort || req.socket.remotePort, proto: req.connection.pair ? 'wss' : 'ws' }; diff --git a/test/lib-caronte-passes-web-test.js b/test/lib-caronte-passes-web-incoming-test.js similarity index 100% rename from test/lib-caronte-passes-web-test.js rename to test/lib-caronte-passes-web-incoming-test.js diff --git a/test/lib-caronte-passes-ws-test.js b/test/lib-caronte-passes-ws-incoming-test.js similarity index 100% rename from test/lib-caronte-passes-ws-test.js rename to test/lib-caronte-passes-ws-incoming-test.js From 5bb83b967edb514402698eecfe3db7ab5fe60b06 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 16:50:30 -0500 Subject: [PATCH 084/210] [tests] make the tests run with the last refactor --- test/lib-caronte-passes-web-incoming-test.js | 2 +- test/lib-caronte-passes-ws-incoming-test.js | 148 +++++++++++++------ 2 files changed, 104 insertions(+), 46 deletions(-) diff --git a/test/lib-caronte-passes-web-incoming-test.js b/test/lib-caronte-passes-web-incoming-test.js index 4ec5ca1a2..cc0a760a7 100644 --- a/test/lib-caronte-passes-web-incoming-test.js +++ b/test/lib-caronte-passes-web-incoming-test.js @@ -1,4 +1,4 @@ -var caronte = require('../lib/caronte/passes/web'), +var caronte = require('../lib/caronte/passes/web-incoming'), expect = require('expect.js'); describe('lib/caronte/passes/web.js', function() { diff --git a/test/lib-caronte-passes-ws-incoming-test.js b/test/lib-caronte-passes-ws-incoming-test.js index f794cca3e..2c077d4df 100644 --- a/test/lib-caronte-passes-ws-incoming-test.js +++ b/test/lib-caronte-passes-ws-incoming-test.js @@ -1,87 +1,145 @@ -var caronte = require('../lib/caronte/passes/ws'), +var caronte = require('../lib/caronte/passes/ws-incoming'), expect = require('expect.js'); -describe('lib/caronte/passes/ws.js', function () { +describe('lib/caronte/passes/ws-incoming.js', function () { describe('#checkMethodAndHeader', function () { it('should drop non-GET connections', function () { - var endCalled = false, + var destroyCalled = false, stubRequest = { method: 'DELETE', - headers: {}, - end: function () { - // Simulate Stream.end() method when call - endCalled = true; - } + headers: {} }, - returnValue = caronte.checkMethodAndHeader(stubRequest, {}, {}); + stubSocket = { + destroy: function () { + // Simulate Socket.destroy() method when call + destroyCalled = true; + } + } + returnValue = caronte.checkMethodAndHeader(stubRequest, stubSocket); expect(returnValue).to.be(true); - expect(endCalled).to.be(true); + expect(destroyCalled).to.be(true); }) it('should drop connections when no upgrade header', function () { - var endCalled = false, + var destroyCalled = false, stubRequest = { method: 'GET', - headers: {}, - end: function () { - // Simulate Stream.end() method when call - endCalled = true; - } + headers: {} }, - returnValue = caronte.checkMethodAndHeader(stubRequest, {}, {}); + stubSocket = { + destroy: function () { + // Simulate Socket.destroy() method when call + destroyCalled = true; + } + } + returnValue = caronte.checkMethodAndHeader(stubRequest, stubSocket); expect(returnValue).to.be(true); - expect(endCalled).to.be(true); + expect(destroyCalled).to.be(true); }) it('should drop connections when upgrade header is different of `websocket`', function () { - var endCalled = false, + var destroyCalled = false, stubRequest = { method: 'GET', headers: { upgrade: 'anotherprotocol' - }, - end: function () { - // Simulate Stream.end() method when call - endCalled = true; } }, - returnValue = caronte.checkMethodAndHeader(stubRequest, {}, {}); + stubSocket = { + destroy: function () { + // Simulate Socket.destroy() method when call + destroyCalled = true; + } + } + returnValue = caronte.checkMethodAndHeader(stubRequest, stubSocket); expect(returnValue).to.be(true); - expect(endCalled).to.be(true); + expect(destroyCalled).to.be(true); }) it('should return nothing when all is ok', function () { - var endCalled = false, + var destroyCalled = false, stubRequest = { method: 'GET', headers: { upgrade: 'websocket' - }, - end: function () { - // Simulate Stream.end() method when call - endCalled = true; } }, - returnValue = caronte.checkMethodAndHeader(stubRequest, {}, {}); + stubSocket = { + destroy: function () { + // Simulate Socket.destroy() method when call + destroyCalled = true; + } + } + returnValue = caronte.checkMethodAndHeader(stubRequest, stubSocket); expect(returnValue).to.be(undefined); - expect(endCalled).to.be(false); + expect(destroyCalled).to.be(false); }) }); + describe('#setupSocket', function () { + it('Set the correct config to the socket', function () { + var stubSocket = { + setTimeout: function (num) { + // Simulate Socket.setTimeout() + socketConfig.timeout = num; + }, + setNoDelay: function (bol) { + // Simulate Socket.setNoDelay() + socketConfig.nodelay = bol; + }, + setKeepAlive: function (bol) { + // Simulate Socket.setKeepAlive() + socketConfig.keepalive = bol; + } + }, + socketConfig = { + timeout: null, + nodelay: false, + keepalive: false + }, + returnValue = caronte.setupSocket({}, stubSocket); + expect(returnValue).to.be(undefined); + expect(socketConfig.timeout).to.eql(0); + expect(socketConfig.nodelay).to.eql(true); + expect(socketConfig.keepalive).to.eql(true); + }); + }); + describe('#XHeaders', function () { - // var stubRequest = { - // connection: { - // remoteAddress: '192.168.1.2', - // remotePort: '8080' - // }, - // headers: {} - // } + it('return if no forward request', function () { + var returnValue = caronte.XHeaders({}, {}, {}); + expect(returnValue).to.be(undefined); + }); + + it('set the correct x-forwarded-* headers from req.connection', function () { + var stubRequest = { + connection: { + remoteAddress: '192.168.1.2', + remotePort: '8080' + }, + headers: {} + } + caronte.XHeaders(stubRequest, {}, { xfwd: true }); + expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); + expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); + expect(stubRequest.headers['x-forwarded-proto']).to.be('ws'); + }); - // it('set the correct x-forwarded-* headers', function () { - // caronte.XHeaders(stubRequest, {}, { xfwd: true }); - // expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); - // expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); - // expect(stubRequest.headers['x-forwarded-proto']).to.be('http'); - // }); + it('set the correct x-forwarded-* headers from req.socket', function () { + var stubRequest = { + socket: { + remoteAddress: '192.168.1.3', + remotePort: '8181' + }, + connection: { + pair: true + }, + headers: {} + }; + caronte.XHeaders(stubRequest, {}, { xfwd: true }); + expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.3'); + expect(stubRequest.headers['x-forwarded-port']).to.be('8181'); + expect(stubRequest.headers['x-forwarded-proto']).to.be('wss'); + }); }); }); From 1cb967b90aaa5b9da57727b8acbd95108437797a Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 17:27:06 -0500 Subject: [PATCH 085/210] [tests] fixed according new refactor and added test to common.setupSocket() --- test/lib-caronte-common-test.js | 66 ++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/test/lib-caronte-common-test.js b/test/lib-caronte-common-test.js index 3c2ccd109..1523cffd8 100644 --- a/test/lib-caronte-common-test.js +++ b/test/lib-caronte-common-test.js @@ -1,9 +1,9 @@ var common = require('../lib/caronte/common'), expect = require('expect.js'); -describe('lib/caronte/common.js', function() { - describe('#setupOutgoing', function() { - it('should setup the right headers', function() { +describe('lib/caronte/common.js', function () { + describe('#setupOutgoing', function () { + it('should setup the correct headers', function () { var outgoing = {}; common.setupOutgoing(outgoing, { @@ -17,7 +17,7 @@ describe('lib/caronte/common.js', function() { }, { method : 'i', - path : 'am', + url : 'am', headers : 'proxy' }); @@ -25,11 +25,67 @@ describe('lib/caronte/common.js', function() { expect(outgoing.hostname).to.eql('how'); expect(outgoing.socketPath).to.eql('are'); expect(outgoing.port).to.eql('you'); - //expect(outgoing.agent).to.eql('?'); + expect(outgoing.agent).to.eql('?'); expect(outgoing.method).to.eql('i'); expect(outgoing.path).to.eql('am'); expect(outgoing.headers).to.eql('proxy') }); + + it('set the port according to the protocol', function () { + var outgoing = {}; + common.setupOutgoing(outgoing, + { + target: { + host : 'how', + hostname : 'are', + socketPath: 'you', + agent : '?', + protocol: 'https:' + } + }, + { + method : 'i', + url : 'am', + headers : 'proxy' + }); + + expect(outgoing.host).to.eql('how'); + expect(outgoing.hostname).to.eql('are'); + expect(outgoing.socketPath).to.eql('you'); + expect(outgoing.agent).to.eql('?'); + + expect(outgoing.method).to.eql('i'); + expect(outgoing.path).to.eql('am'); + expect(outgoing.headers).to.eql('proxy') + + expect(outgoing.port).to.eql(443); + }); + }); + + describe('#setupSocket', function () { + it('should setup a socket', function () { + var socketConfig = { + timeout: null, + nodelay: false, + keepalive: false + }, + stubSocket = { + setTimeout: function (num) { + socketConfig.timeout = num; + }, + setNoDelay: function (bol) { + socketConfig.nodelay = bol; + }, + setKeepAlive: function (bol) { + socketConfig.keepalive = bol; + } + } + returnValue = common.setupSocket(stubSocket); + + expect(socketConfig.timeout).to.eql(0); + expect(socketConfig.nodelay).to.eql(true); + expect(socketConfig.keepalive).to.eql(true); + }); }); }); \ No newline at end of file From 7b9169c8c58962b5f7c7a37d0da6847beac3b121 Mon Sep 17 00:00:00 2001 From: srossross Date: Mon, 16 Sep 2013 15:59:19 -0700 Subject: [PATCH 086/210] FIX: ws error event --- lib/caronte/passes/ws-incoming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/caronte/passes/ws-incoming.js b/lib/caronte/passes/ws-incoming.js index 3b5e1f525..5fb99b609 100644 --- a/lib/caronte/passes/ws-incoming.js +++ b/lib/caronte/passes/ws-incoming.js @@ -83,7 +83,7 @@ function stream(req, socket, options, head) { if (options.ee.listeners(ev + 'error').length == 0){ throw err; } - options.ee.emit(ev + 'error', err, req, res); + options.ee.emit(ev + 'error', err, req, socket, head); }); proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { From dc9d7e5452c7d39ae1d242cb8021ca75e4f736d4 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 18:59:48 -0500 Subject: [PATCH 087/210] [tests] drop the test of own streams, moved the usable tests --- test/lib-caronte-streams-forward-test.js | 45 ------- test/lib-caronte-test.js | 154 ++++++++++++++++++++++- 2 files changed, 153 insertions(+), 46 deletions(-) delete mode 100644 test/lib-caronte-streams-forward-test.js diff --git a/test/lib-caronte-streams-forward-test.js b/test/lib-caronte-streams-forward-test.js deleted file mode 100644 index 5289856cc..000000000 --- a/test/lib-caronte-streams-forward-test.js +++ /dev/null @@ -1,45 +0,0 @@ -var caronte = require('../'), - ForwardStream = require('../lib/caronte/streams/forward'); - expect = require('expect.js'), - Writable = require('stream').Writable, - http = require('http'); - - -describe('lib/caronte/streams/forward.js', function () { - describe('forward stream constructor', function () { - it('should be an instance of Writable stream and get the correct options and methods', function () { - var stubOptions = { - key: 'value' - }; - var forwardProxy = new ForwardStream(stubOptions); - - expect(forwardProxy).to.be.a(Writable); - expect(forwardProxy.options).to.eql({ key: 'value' }); - expect(forwardProxy.onPipe).to.be.a('function'); - expect(forwardProxy.onFinish).to.be.a('function'); - expect(forwardProxy._events).to.have.property('pipe'); - expect(forwardProxy._events).to.have.property('finish'); - }); - }); - - describe('should pipe the request and finish it', function () { - it('should make the request on pipe and finish it', function(done) { - var result; - - var p = caronte.createProxyServer({ - forward: 'http://127.0.0.1:8080' - }).listen('8081') - - var s = http.createServer(function(req, res) { - expect(req.method).to.eql('GET'); - s.close(); - p.close(); - done(); - }); - - s.listen('8080'); - - http.request('http://127.0.0.1:8081', function() {}).end(); - }); - }); -}); diff --git a/test/lib-caronte-test.js b/test/lib-caronte-test.js index 8358c5f99..dbb278f71 100644 --- a/test/lib-caronte-test.js +++ b/test/lib-caronte-test.js @@ -1,5 +1,6 @@ var caronte = require('../lib/caronte'), - expect = require('expect.js'); + expect = require('expect.js'), + http = require('http'); describe('lib/caronte.js', function() { describe('#createProxyServer', function() { @@ -24,4 +25,155 @@ describe('lib/caronte.js', function() { expect(obj.listen).to.be.a(Function); }); }); + + describe('#createProxyServer with forward options and using web-incoming passes', function () { + it('should pipe the request using web-incoming#stream method', function (done) { + var proxy = caronte.createProxyServer({ + forward: 'http://127.0.0.1:8080' + }).listen('8081') + + var source = http.createServer(function(req, res) { + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql('8081'); + source.close(); + proxy.close(); + done(); + }); + + source.listen('8080'); + + http.request('http://127.0.0.1:8081', function() {}).end(); + }) + }); + + describe('#createProxyServer using the web-incoming passes', function () { + it('should make the request on pipe and finish it', function(done) { + var proxy = caronte.createProxyServer({ + target: 'http://127.0.0.1:8080' + }).listen('8081'); + + var source = http.createServer(function(req, res) { + expect(req.method).to.eql('POST'); + expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1'); + expect(req.headers.host.split(':')[1]).to.eql('8081'); + source.close(); + proxy.close(); + done(); + }); + + source.listen('8080'); + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'POST', + headers: { + 'x-forwarded-for': '127.0.0.1' + } + }, function() {}).end(); + }); + }); + + describe('#createProxyServer using the web-incoming passes', function () { + it('should make the request, handle response and finish it', function(done) { + var proxy = caronte.createProxyServer({ + target: 'http://127.0.0.1:8080' + }).listen('8081'); + + var source = http.createServer(function(req, res) { + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql('8081'); + res.writeHead(200, {'Content-Type': 'text/plain'}) + res.end('Hello from ' + source.address().port); + }); + + source.listen('8080'); + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'GET', + }, function(res) { + expect(res.statusCode).to.eql(200); + + res.on('data', function (data) { + expect(data.toString()).to.eql('Hello from 8080'); + }); + + res.on('end', function () { + source.close(); + proxy.close(); + done(); + }); + }).end(); + }); + }); + + describe('#createProxyServer() method with error response', function () { + it('should make the request and emit the error event', function(done) { + var proxy = caronte.createProxyServer({ + target: 'http://127.0.0.1:8080' + }); + + proxy.ee.on('caronte:outgoing:web:error', function (err) { + expect(err).to.be.an(Error); + expect(err.code).to.be('ECONNREFUSED'); + proxyServer.close(); + done(); + }) + + var proxyServer = proxy.listen('8081'); + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'GET', + }, function() {}).end(); + }); + }); + + describe('#createProxyServer using the web-incoming passes', function () { + it('should emit events correclty', function(done) { + var proxy = caronte.createProxyServer({ + target: 'http://127.0.0.1:8080' + }), + + proxyServer = proxy.listen('8081'), + + source = http.createServer(function(req, res) { + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql('8081'); + res.writeHead(200, {'Content-Type': 'text/plain'}) + res.end('Hello from ' + source.address().port); + }), + + events = []; + + source.listen('8080'); + + proxy.ee.on('caronte:**', function (uno, dos, tres) { + events.push(this.event); + }) + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'GET', + }, function(res) { + expect(res.statusCode).to.eql(200); + + res.on('data', function (data) { + expect(data.toString()).to.eql('Hello from 8080'); + }); + + res.on('end', function () { + expect(events).to.contain('caronte:outgoing:web:begin'); + expect(events).to.contain('caronte:outgoing:web:end'); + source.close(); + proxyServer.close(); + done(); + }); + }).end(); + }); + }); }); From 7e25bded27effc1b3d47121ce21465a4e2ec7c0b Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 19:04:49 -0500 Subject: [PATCH 088/210] [tests] removed unused tests --- test/lib-caronte-streams-proxy-test.js | 109 -------------------- test/lib-caronte-streams-websockets-test.js | 109 -------------------- 2 files changed, 218 deletions(-) delete mode 100644 test/lib-caronte-streams-proxy-test.js delete mode 100644 test/lib-caronte-streams-websockets-test.js diff --git a/test/lib-caronte-streams-proxy-test.js b/test/lib-caronte-streams-proxy-test.js deleted file mode 100644 index 671f80cc0..000000000 --- a/test/lib-caronte-streams-proxy-test.js +++ /dev/null @@ -1,109 +0,0 @@ -var caronte = require('../'), - ProxyStream = require('../lib/caronte/streams/proxy'); - expect = require('expect.js'), - Duplex = require('stream').Duplex, - http = require('http'); - - -describe('lib/caronte/streams/proxy.js', function () { - describe('proxy stream constructor', function () { - it('should be an instance of Duplex stream and get the correct options and methods', function () { - var stubOptions = { - key: 'value' - }; - var proxyStream = new ProxyStream(stubOptions); - - expect(proxyStream).to.be.a(Duplex); - expect(proxyStream.options).to.eql({ key: 'value' }); - expect(proxyStream.onPipe).to.be.a('function'); - expect(proxyStream.onFinish).to.be.a('function'); - expect(proxyStream._events).to.have.property('pipe'); - expect(proxyStream._events).to.have.property('finish'); - }); - }); - - describe('caronte createProxyServer() method', function () { - it('should make the request on pipe and finish it', function(done) { - var proxy = caronte.createProxyServer({ - target: 'http://127.0.0.1:8080' - }).listen('8081'); - - var source = http.createServer(function(req, res) { - expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1'); - source.close(); - proxy.close(); - done(); - }); - - source.listen('8080'); - - http.request({ - hostname: '127.0.0.1', - port: '8081', - method: 'POST', - headers: { - 'x-forwarded-for': '127.0.0.1' - } - }, function() {}).end(); - }); - }); - - describe('caronte createProxyServer() method with response', function () { - it('should make the request, handle response and finish it', function(done) { - var proxy = caronte.createProxyServer({ - target: 'http://127.0.0.1:8080' - }).listen('8081'); - - var source = http.createServer(function(req, res) { - expect(req.method).to.eql('GET'); - res.writeHead(200, {'Content-Type': 'text/plain'}) - res.end('Hello from ' + source.address().port); - }); - - source.listen('8080'); - - http.request({ - hostname: '127.0.0.1', - port: '8081', - method: 'GET', - }, function(res) { - expect(res.statusCode).to.eql(200); - - res.on('data', function (data) { - expect(data.toString()).to.eql('Hello from 8080'); - }); - - res.on('end', function () { - source.close(); - proxy.close(); - done(); - }); - }).end(); - }); - }); - - describe('caronte createProxyServer() method with error response', function () { - it('should make the request and response with error', function(done) { - var proxy = caronte.createProxyServer({ - target: 'http://127.0.0.1:8080' - }).listen('8081'); - - http.request({ - hostname: '127.0.0.1', - port: '8081', - method: 'GET', - }, function(res) { - expect(res.statusCode).to.eql(500); - - res.on('data', function (data) { - expect(data.toString()).to.eql('Internal Server Error'); - }); - - res.on('end', function () { - proxy.close(); - done(); - }); - }).end(); - }); - }); -}); diff --git a/test/lib-caronte-streams-websockets-test.js b/test/lib-caronte-streams-websockets-test.js deleted file mode 100644 index 74248e7b3..000000000 --- a/test/lib-caronte-streams-websockets-test.js +++ /dev/null @@ -1,109 +0,0 @@ -var caronte = require('../'), - WebSocket = require('../lib/caronte/streams/websocket'); - expect = require('expect.js'), - Duplex = require('stream').Duplex, - http = require('http'); - - -describe('lib/caronte/streams/websocket.js', function () { - describe('WebSocket stream constructor', function () { - it('should be an instance of Duplex stream and get the correct options and methods', function () { - var stubOptions = { - key: 'value' - }; - var WebSocketStream = new WebSocket(stubOptions); - - expect(WebSocketStream).to.be.a(Duplex); - expect(WebSocketStream.options).to.eql({ key: 'value' }); - expect(WebSocketStream.onPipe).to.be.a('function'); - expect(WebSocketStream.onFinish).to.be.a('function'); - expect(WebSocketStream._events).to.have.property('pipe'); - expect(WebSocketStream._events).to.have.property('finish'); - }); - }); - - describe('caronte createWebSocketServer() method', function () { - // it('should make the request on pipe and finish it', function(done) { - // var proxy = caronte.createProxyServer({ - // target: 'http://127.0.0.1:8080' - // }).listen('8081'); - - // var source = http.createServer(function(req, res) { - // expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1'); - // source.close(); - // proxy.close(); - // done(); - // }); - - // source.listen('8080'); - - // http.request({ - // hostname: '127.0.0.1', - // port: '8081', - // method: 'POST', - // headers: { - // 'x-forwarded-for': '127.0.0.1' - // } - // }, function() {}).end(); - // }); - }); - - describe('caronte createProxyServer() method with response', function () { - // it('should make the request, handle response and finish it', function(done) { - // var proxy = caronte.createProxyServer({ - // target: 'http://127.0.0.1:8080' - // }).listen('8081'); - - // var source = http.createServer(function(req, res) { - // expect(req.method).to.eql('GET'); - // res.writeHead(200, {'Content-Type': 'text/plain'}) - // res.end('Hello from ' + source.address().port); - // }); - - // source.listen('8080'); - - // http.request({ - // hostname: '127.0.0.1', - // port: '8081', - // method: 'GET', - // }, function(res) { - // expect(res.statusCode).to.eql(200); - - // res.on('data', function (data) { - // expect(data.toString()).to.eql('Hello from 8080'); - // }); - - // res.on('end', function () { - // source.close(); - // proxy.close(); - // done(); - // }); - // }).end(); - // }); - }); - - describe('caronte createProxyServer() method with error response', function () { - // it('should make the request and response with error', function(done) { - // var proxy = caronte.createProxyServer({ - // target: 'http://127.0.0.1:8080' - // }).listen('8081'); - - // http.request({ - // hostname: '127.0.0.1', - // port: '8081', - // method: 'GET', - // }, function(res) { - // expect(res.statusCode).to.eql(500); - - // res.on('data', function (data) { - // expect(data.toString()).to.eql('Internal Server Error'); - // }); - - // res.on('end', function () { - // proxy.close(); - // done(); - // }); - // }).end(); - // }); - }); -}); From 40902506af3361b642b8798350b48404fe0a4e78 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 19:37:28 -0500 Subject: [PATCH 089/210] [tests] fix code coverage, changed pattern on blanket options --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f11557c6d..bc8c4c0ad 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "ws" : "*" }, "scripts" : { - "blanket" : { "pattern": "caronte/lib" }, + "blanket" : { "pattern": "lib/caronte" }, "test" : "./node_modules/.bin/mocha -R landing test/*-test.js", "test-cov" : "./node_modules/.bin/mocha --require blanket -R html-cov > cov/coverage.html" }, From 02007ed0fb38f798436ae5669bb18d4f27496667 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 20:59:10 -0500 Subject: [PATCH 090/210] [tests] added tests for websockets --- test/lib-caronte-test.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/lib-caronte-test.js b/test/lib-caronte-test.js index dbb278f71..06ab11e1f 100644 --- a/test/lib-caronte-test.js +++ b/test/lib-caronte-test.js @@ -1,6 +1,7 @@ var caronte = require('../lib/caronte'), expect = require('expect.js'), - http = require('http'); + http = require('http'), + ws = require('ws'); describe('lib/caronte.js', function() { describe('#createProxyServer', function() { @@ -176,4 +177,35 @@ describe('lib/caronte.js', function() { }).end(); }); }); + + describe('#createProxyServer using the ws-incoming passes', function () { + it('should proxy the websockets stream', function (done) { + var proxy = caronte.createProxyServer({ + target: 'ws://127.0.0.1:8080', + ws: true + }), + proxyServer = proxy.listen('8081'), + destiny = new ws.Server({ port: 8080 }, function () { + var client = new ws('ws://127.0.0.1:8081'); + + client.on('open', function () { + client.send('hello there'); + }); + + client.on('message', function (msg) { + expect(msg).to.be('Hello over websockets'); + proxyServer.close(); + destiny.close(); + done(); + }); + }); + + destiny.on('connection', function (socket) { + socket.on('message', function (msg) { + expect(msg).to.be('hello there'); + socket.send('Hello over websockets'); + }); + }); + }); + }); }); From 06025002303f351f71d9e5f78a93895257f0d283 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 21:00:34 -0500 Subject: [PATCH 091/210] [tests] added .travis.yml file --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..87fb96432 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: node_js +node_js: + - 0.8 + - "0.10" + - "0.11" + +notifications: + email: + - travis@nodejitsu.com + irc: "irc.freenode.org#nodejitsu" \ No newline at end of file From 10a0db4f0dd4594839f9098b9d67130085a067bc Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 21:37:49 -0500 Subject: [PATCH 092/210] [tests] added test for socket.io proxying --- package.json | 4 +++- test/lib-caronte-test.js | 43 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index bc8c4c0ad..c0b741774 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "coveralls" : "*", "mocha-lcov-reporter": "*", "blanket" : "*", - "ws" : "*" + "ws" : "*", + "socket.io" : "*", + "socket.io-client" : "*" }, "scripts" : { "blanket" : { "pattern": "lib/caronte" }, diff --git a/test/lib-caronte-test.js b/test/lib-caronte-test.js index 06ab11e1f..0558ef934 100644 --- a/test/lib-caronte-test.js +++ b/test/lib-caronte-test.js @@ -1,7 +1,10 @@ -var caronte = require('../lib/caronte'), - expect = require('expect.js'), - http = require('http'), - ws = require('ws'); +var caronte = require('../lib/caronte'), + expect = require('expect.js'), + http = require('http'), + ws = require('ws') + io = require('socket.io'), + ioClient = require('socket.io-client'); + describe('lib/caronte.js', function() { describe('#createProxyServer', function() { @@ -194,6 +197,7 @@ describe('lib/caronte.js', function() { client.on('message', function (msg) { expect(msg).to.be('Hello over websockets'); + client.close(); proxyServer.close(); destiny.close(); done(); @@ -208,4 +212,35 @@ describe('lib/caronte.js', function() { }); }); }); + + describe('#createProxyServer using the ws-incoming passes', function () { + it('should proxy a socket.io stream', function (done) { + var proxy = caronte.createProxyServer({ + target: 'ws://127.0.0.1:8080', + ws: true + }), + proxyServer = proxy.listen('8081'), + destiny = io.listen(8080, function () { + var client = ioClient.connect('ws://127.0.0.1:8081'); + + client.on('connect', function () { + client.emit('incoming', 'hello there'); + }); + + client.on('outgoing', function (data) { + expect(data).to.be('Hello over websockets'); + proxyServer.close(); + destiny.server.close(); + done(); + }); + }); + + destiny.sockets.on('connection', function (socket) { + socket.on('incoming', function (msg) { + expect(msg).to.be('hello there'); + socket.emit('outgoing', 'Hello over websockets'); + }); + }) + }); + }) }); From 8eff1a1f26bb739dfc5a1ad90b140ff2a18921d5 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 16 Sep 2013 21:40:42 -0500 Subject: [PATCH 093/210] [test][misc] remove node@0.8 to test on travis --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 87fb96432..835fed5ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,9 @@ language: node_js node_js: - - 0.8 - "0.10" - "0.11" notifications: email: - travis@nodejitsu.com - irc: "irc.freenode.org#nodejitsu" \ No newline at end of file + irc: "irc.freenode.org#nodejitsu" From 031452e4007f1084a760e78be6908319c12c2c1c Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 10:31:52 +0200 Subject: [PATCH 094/210] [fix] closes #473 --- lib/caronte/passes/ws-incoming.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/caronte/passes/ws-incoming.js b/lib/caronte/passes/ws-incoming.js index ef88de35a..e5bdcf60e 100644 --- a/lib/caronte/passes/ws-incoming.js +++ b/lib/caronte/passes/ws-incoming.js @@ -75,6 +75,9 @@ function XHeaders(req, socket, options) { function stream(req, socket, options, head) { common.setupSocket(socket); + if (head && head.length) socket.unshift(head); + + var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); From 2c10f256b658bc0e906c20f29d94ab7eaf653055 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 10:59:45 +0200 Subject: [PATCH 095/210] [fix] write connection header --- lib/caronte/passes/web-incoming.js | 2 +- lib/caronte/passes/web-outgoing.js | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/caronte/passes/web-incoming.js b/lib/caronte/passes/web-incoming.js index 66beb011c..ce494feb5 100644 --- a/lib/caronte/passes/web-incoming.js +++ b/lib/caronte/passes/web-incoming.js @@ -121,7 +121,7 @@ function stream(req, res, options) { var evnt = ev + pass.name.toLowerCase() + ':'; options.ee.emit(evnt + 'begin', req, res); - var val = pass(res, proxyRes); + var val = pass(req, res, proxyRes); options.ee.emit(evnt + 'end'); return val; diff --git a/lib/caronte/passes/web-outgoing.js b/lib/caronte/passes/web-outgoing.js index 941bc6001..7d4ff9405 100644 --- a/lib/caronte/passes/web-outgoing.js +++ b/lib/caronte/passes/web-outgoing.js @@ -9,14 +9,29 @@ var passes = exports; */ [ // <-- - - function writeHeaders(res, proxyRes) { + + function setConnection(req, res, proxyRes) { + if (req.httpVersion === '1.0') { + if (req.headers.connection) { + proxyRes.headers.connection = req.headers.connection + } else { + proxyRes.headers.connection = 'close' + } + } else if (!proxyRes.headers.connection) { + if (req.headers.connection) { proxyRes.headers.connection = req.headers.connection } + else { + proxyRes.headers.connection = 'keep-alive' + } + } + }, + + function writeHeaders(req, res, proxyRes) { Object.keys(proxyRes.headers).forEach(function(key) { res.setHeader(key, proxyRes.headers[key]); }); }, - function writeStatusCode(res, proxyRes) { + function writeStatusCode(req, res, proxyRes) { res.writeHead(proxyRes.statusCode); } From 16a4d9da1136b79f40ad80482d3fd17dc74274b1 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 11:20:18 +0200 Subject: [PATCH 096/210] [test] added tests for web-outgoing.js --- test/lib-caronte-passes-web-outgoing-test.js | 90 ++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 test/lib-caronte-passes-web-outgoing-test.js diff --git a/test/lib-caronte-passes-web-outgoing-test.js b/test/lib-caronte-passes-web-outgoing-test.js new file mode 100644 index 000000000..b51e17b74 --- /dev/null +++ b/test/lib-caronte-passes-web-outgoing-test.js @@ -0,0 +1,90 @@ +var caronte = require('../lib/caronte/passes/web-outgoing'), + expect = require('expect.js'); + +describe('lib/caronte/passes/web-outgoing.js', function () { + describe('#setConnection', function () { + it('set the right connection with 1.0 - `close`', function() { + var proxyRes = { headers: {} }; + caronte.setConnection({ + httpVersion: '1.0', + headers: { + connection: null + } + }, {}, proxyRes); + + expect(proxyRes.headers.connection).to.eql('close'); + }); + + it('set the right connection with 1.0 - req.connection', function() { + var proxyRes = { headers: {} }; + caronte.setConnection({ + httpVersion: '1.0', + headers: { + connection: 'hey' + } + }, {}, proxyRes); + + expect(proxyRes.headers.connection).to.eql('hey'); + }); + + it('set the right connection - req.connection', function() { + var proxyRes = { headers: {} }; + caronte.setConnection({ + httpVersion: null, + headers: { + connection: 'hola' + } + }, {}, proxyRes); + + expect(proxyRes.headers.connection).to.eql('hola'); + }); + + it('set the right connection - `keep-alive`', function() { + var proxyRes = { headers: {} }; + caronte.setConnection({ + httpVersion: null, + headers: { + connection: null + } + }, {}, proxyRes); + + expect(proxyRes.headers.connection).to.eql('keep-alive'); + }); + + }); + + describe('#writeStatusCode', function () { + it('should write status code', function() { + var res = { + writeHead: function(n) { + expect(n).to.eql(200); + } + } + + caronte.writeStatusCode({}, res, { statusCode: 200 }); + }); + }); + + describe('#writeHeaders', function() { + var proxyRes = { + headers: { + hey: 'hello', + how: 'are you?' + } + }; + + var res = { + setHeader: function(k, v) { + this.headers[k] = v; + }, + headers: {} + }; + + caronte.writeHeaders({}, res, proxyRes); + + expect(res.headers.hey).to.eql('hello'); + expect(res.headers.how).to.eql('are you?'); + }); + +}); + From 6b618787598a2a37850898dbdb3b4fe8f3c3414d Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 11:24:35 +0200 Subject: [PATCH 097/210] [docs] add travis build status --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index c710504cc..3a4bc0244 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ Caronte is an HTTP programmable proxying library that supports websockets. It is suitable for implementing components such as proxies and load balancers. +### Build Status + +

+ +

+ ### Core Concept A new proxy is created by calling `createProxyServer` and passing From afc4d0931fa1bc73ef797e2b59570e2f8145d447 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 12:14:52 +0200 Subject: [PATCH 098/210] [fix] pooled connections, closes #478 --- lib/caronte.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/caronte.js b/lib/caronte.js index 444e8fd25..78d7bd4f3 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -32,6 +32,7 @@ proxy.createProxyServer = function createProxyServer(options) { " ws : ", " xfwd : ", " maxSock: ", + " agent : ", " } ", " ", "NOTE: `options.ws` and `options.ssl` are optional. ", @@ -45,7 +46,7 @@ proxy.createProxyServer = function createProxyServer(options) { options[key] = url.parse(options[key]); options[key].maxSockets = options.maxSock; - options[key].agent = new (options.ssl ? https.Agent : http.Agent)(options[key].maxSockets || 100); + options[key].agent = options.agent || false // new (options.ssl ? https.Agent : http.Agent)(options[key].maxSockets || 100); }); options.ee = new events.EventEmitter2({ wildcard: true, delimiter: ':' }); From 8663ac1c43505f0081d906c3cd8e702d4b5ddeb0 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 16:28:54 +0200 Subject: [PATCH 099/210] [fix] do not send chunked responses to http1.0 clients --- lib/caronte/passes/web-outgoing.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/caronte/passes/web-outgoing.js b/lib/caronte/passes/web-outgoing.js index 7d4ff9405..48ff12dd6 100644 --- a/lib/caronte/passes/web-outgoing.js +++ b/lib/caronte/passes/web-outgoing.js @@ -10,6 +10,12 @@ var passes = exports; [ // <-- + function removeChunked(req, res, proxyRes) { + if(req.httpVersion === '1.0') { + delete proxyRes.headers['transfer-encoding']; + } + }, + function setConnection(req, res, proxyRes) { if (req.httpVersion === '1.0') { if (req.headers.connection) { From ca092635e7ac4d967b554e3b94a16a931946d464 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 16:32:55 +0200 Subject: [PATCH 100/210] [test] remove chunked on http1.0 --- test/lib-caronte-passes-web-outgoing-test.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/lib-caronte-passes-web-outgoing-test.js b/test/lib-caronte-passes-web-outgoing-test.js index b51e17b74..64fc7ead9 100644 --- a/test/lib-caronte-passes-web-outgoing-test.js +++ b/test/lib-caronte-passes-web-outgoing-test.js @@ -86,5 +86,19 @@ describe('lib/caronte/passes/web-outgoing.js', function () { expect(res.headers.how).to.eql('are you?'); }); + + describe('#removeChunked', function() { + var proxyRes = { + headers: { + 'transfer-encoding': 'hello' + } + }; + + + caronte.removeChunked({ httpVersion: '1.0' }, {}, proxyRes); + + expect(proxyRes.headers['transfer-encoding']).to.eql(undefined); + }); + }); From f36cb4d5a110fc86272e878278f103f313c86f56 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 19:30:59 +0200 Subject: [PATCH 101/210] [fix] coveralls.. will it work? --- .travis.yml | 3 +++ package.json | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 835fed5ff..d7eeacc3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,3 +7,6 @@ notifications: email: - travis@nodejitsu.com irc: "irc.freenode.org#nodejitsu" + +script: + npm run-script coveralls diff --git a/package.json b/package.json index c0b741774..22406ba23 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,12 @@ "blanket" : "*", "ws" : "*", "socket.io" : "*", - "socket.io-client" : "*" + "socket.io-client" : "*", + "coveralls" : "*", + "mocha-lcov-reporter": "*" }, "scripts" : { + "coveralls" : "mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js", "blanket" : { "pattern": "lib/caronte" }, "test" : "./node_modules/.bin/mocha -R landing test/*-test.js", "test-cov" : "./node_modules/.bin/mocha --require blanket -R html-cov > cov/coverage.html" From 1ceea3e5f9b6232d60d673946bbccb7d8ccb4beb Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 20:11:03 +0200 Subject: [PATCH 102/210] [docs] test badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3a4bc0244..2df118fb5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ proxies and load balancers.

+

### Core Concept From 72a89eab8bafef3742d78e8de8631094f961f427 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 20:14:16 +0200 Subject: [PATCH 103/210] [fix] link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2df118fb5..bb5eacaf4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ proxies and load balancers.

- +

### Core Concept From 69f126b34cbd190be8541a854d21f13bfb5a61bf Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 17 Sep 2013 20:15:34 +0200 Subject: [PATCH 104/210] [fix] space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb5eacaf4..f58bbbab8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ proxies and load balancers. ### Build Status

- +   

From 13741a823f1c1c884d4a37e597e4b188598b0e25 Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 11:03:23 -0700 Subject: [PATCH 105/210] ENH: updated https and agent option Removed logic from createProxyServer and put it into setupOutgoing. Conflicts: lib/caronte.js --- lib/caronte.js | 9 +-------- lib/caronte/common.js | 14 +++++++++++++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/caronte.js b/lib/caronte.js index 78d7bd4f3..ed0280cfa 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -28,6 +28,7 @@ proxy.createProxyServer = function createProxyServer(options) { " { ", " target : ", " forward: ", + " agent : ", " ssl : ", " ws : ", " xfwd : ", @@ -41,14 +42,6 @@ proxy.createProxyServer = function createProxyServer(options) { ].join("\n")); } - ['target', 'forward'].forEach(function(key) { - if(!options[key]) return; - options[key] = url.parse(options[key]); - - options[key].maxSockets = options.maxSock; - options[key].agent = options.agent || false // new (options.ssl ? https.Agent : http.Agent)(options[key].maxSockets || 100); - }); - options.ee = new events.EventEmitter2({ wildcard: true, delimiter: ':' }); return { diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 6b75c6f47..a2728580b 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -1,4 +1,7 @@ -var common = exports; +var common = exports + , http = require('http') + , https = require('https') + ; /** * Copies the right headers from `options` and `req` to @@ -32,6 +35,15 @@ common.setupOutgoing = function(outgoing, options, req, forward) { function(e) { outgoing[e] = req[e]; } ); + if (options.agent){ + outgoing.agent = options.agent; + } + + if (!outgoing.agent){ + var Agent = (~['https:', 'wss:'].indexOf(options[forward || 'target'].protocol) ? https.Agent : http.Agent); + outgoing.agent = new Agent(options.maxSock || 100); + } + outgoing.path = req.url; return outgoing; From 427d8d85369b0cd1d38afa0dd0f28ac98fa16001 Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 11:44:09 -0700 Subject: [PATCH 106/210] ENH: added new https example, needs to be simplified before merge updated existing example with log output --- examples/https.js | 54 +++++++++++++++++++++++++++++++++++++++++ examples/stand-alone.js | 2 ++ 2 files changed, 56 insertions(+) create mode 100644 examples/https.js diff --git a/examples/https.js b/examples/https.js new file mode 100644 index 000000000..cab220653 --- /dev/null +++ b/examples/https.js @@ -0,0 +1,54 @@ +var http = require('http') + , https = require('https') + , caronte = require('caronte') + ; +// +// Create your proxy server +// +var options = {target:'https://google.com', + agent: new https.Agent({rejectUnauthorized:false}), + }; + +var proxyServer = caronte.createProxyServer(options); + +proxyServer.ee.on('*:error', function(err, req, res){ + res.end('There was an error proxying your request'); +}); + +console.log("Proxy server listening on port 8000"); +proxyServer.listen(8000); + + +// +// Create your proxy server +// +var options2 = {target:'https://google.com', + headers: {'host':'google.com'}, + }; + +var proxyServer2 = caronte.createProxyServer(options2); + +proxyServer2.ee.on('*:error', function(err, req, res){ + res.end('There was an error proxying your request'); +}); + +console.log("Proxy server 2 listening on port 8001"); +proxyServer2.listen(8001); + +// +// Create your proxy server +// +var options3 = {target:'https://google.com'}; + +var proxyServer3 = caronte.createProxyServer(options3); + +proxyServer3.ee.on('*:error', function(err, req, res){ + res.end('There was an error proxying your request'); +}); + +console.log("Proxy server 3 listening on port 8002"); +proxyServer3.listen(8002); + + + + diff --git a/examples/stand-alone.js b/examples/stand-alone.js index d3848abf4..081134e6b 100644 --- a/examples/stand-alone.js +++ b/examples/stand-alone.js @@ -3,11 +3,13 @@ var http = require('http'), // // Create your proxy server // +console.log("Proxy server listening on port 8000"); caronte.createProxyServer({target:'http://localhost:9000'}).listen(8000); // // Create your target server // +console.log("Web server listening on port 9000"); http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2)); From 7d840d35151be1aac612798754af47368594781d Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 11:45:41 -0700 Subject: [PATCH 107/210] ENH: added 'headers' to available options, to add or overwrite existing headers --- lib/caronte/common.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/caronte/common.js b/lib/caronte/common.js index a2728580b..47ada1b6a 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -1,6 +1,7 @@ -var common = exports +var common = exports , http = require('http') , https = require('https') + , extend = require('util')._extend ; /** @@ -35,6 +36,10 @@ common.setupOutgoing = function(outgoing, options, req, forward) { function(e) { outgoing[e] = req[e]; } ); + if (options.headers){ + extend(outgoing.headers, options.headers); + } + if (options.agent){ outgoing.agent = options.agent; } From 1c7ace26c5a36fb63497f1ab67793c5b75495063 Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 11:50:04 -0700 Subject: [PATCH 108/210] ENH: updated example --- examples/https.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/https.js b/examples/https.js index cab220653..8e3410dc2 100644 --- a/examples/https.js +++ b/examples/https.js @@ -38,7 +38,8 @@ proxyServer2.listen(8001); // // Create your proxy server // -var options3 = {target:'https://google.com'}; +var options3 = {target:'https://google.com', + xfwd:true}; var proxyServer3 = caronte.createProxyServer(options3); From f566a42e511f4a6a8f3620f64e05df209e61b64f Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 14:51:56 -0700 Subject: [PATCH 109/210] ENH: updated examples --- examples/https-secure.js | 15 +++++++++++ examples/https.js | 55 ++++------------------------------------ 2 files changed, 20 insertions(+), 50 deletions(-) create mode 100644 examples/https-secure.js diff --git a/examples/https-secure.js b/examples/https-secure.js new file mode 100644 index 000000000..b6d7bb759 --- /dev/null +++ b/examples/https-secure.js @@ -0,0 +1,15 @@ +var caronte = require('caronte'), + https = require('https'); +/* + * Create your proxy server pointing to a secure domain + * Enable ssl validation + */ +var options = {target : 'https://google.com', + agent : https.globalAgent, + headers: {host: 'google.com'} + }; + +var proxyServer = caronte.createProxyServer(options); +console.log("Proxy server listening on port 8000"); +proxyServer.listen(8000); + diff --git a/examples/https.js b/examples/https.js index 8e3410dc2..b64e3cf2f 100644 --- a/examples/https.js +++ b/examples/https.js @@ -1,55 +1,10 @@ -var http = require('http') - , https = require('https') - , caronte = require('caronte') - ; -// -// Create your proxy server -// -var options = {target:'https://google.com', - agent: new https.Agent({rejectUnauthorized:false}), - }; +var caronte = require('caronte'); +/* + * Create your proxy server pointing to a secure domain + */ +var options = {target:'https://google.com'}; var proxyServer = caronte.createProxyServer(options); - -proxyServer.ee.on('*:error', function(err, req, res){ - res.end('There was an error proxying your request'); -}); - console.log("Proxy server listening on port 8000"); proxyServer.listen(8000); - -// -// Create your proxy server -// -var options2 = {target:'https://google.com', - headers: {'host':'google.com'}, - }; - -var proxyServer2 = caronte.createProxyServer(options2); - -proxyServer2.ee.on('*:error', function(err, req, res){ - res.end('There was an error proxying your request'); -}); - -console.log("Proxy server 2 listening on port 8001"); -proxyServer2.listen(8001); - -// -// Create your proxy server -// -var options3 = {target:'https://google.com', - xfwd:true}; - -var proxyServer3 = caronte.createProxyServer(options3); - -proxyServer3.ee.on('*:error', function(err, req, res){ - res.end('There was an error proxying your request'); -}); - -console.log("Proxy server 3 listening on port 8002"); -proxyServer3.listen(8002); - - - - From 12cda561afe534427a5f84da9d7e0beb64a8ecbc Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 14:52:53 -0700 Subject: [PATCH 110/210] ENH: updated agent options in `common.setupOutgoing` --- lib/caronte.js | 4 +--- lib/caronte/common.js | 21 +++++---------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/lib/caronte.js b/lib/caronte.js index ed0280cfa..337fac94d 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -28,12 +28,10 @@ proxy.createProxyServer = function createProxyServer(options) { " { ", " target : ", " forward: ", - " agent : ", + " agent : ", " ssl : ", " ws : ", " xfwd : ", - " maxSock: ", - " agent : ", " } ", " ", "NOTE: `options.ws` and `options.ssl` are optional. ", diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 47ada1b6a..e84f36ca5 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -1,8 +1,5 @@ -var common = exports - , http = require('http') - , https = require('https') - , extend = require('util')._extend - ; +var common = exports, + extend = require('util')._extend; /** * Copies the right headers from `options` and `req` to @@ -28,7 +25,7 @@ common.setupOutgoing = function(outgoing, options, req, forward) { outgoing.port = options[forward || 'target'].port || (~['https:', 'wss:'].indexOf(options[forward || 'target'].protocol) ? 443 : 80); - ['host', 'hostname', 'socketPath', 'agent'].forEach( + ['host', 'hostname', 'socketPath'].forEach( function(e) { outgoing[e] = options[forward || 'target'][e]; } ); @@ -39,16 +36,8 @@ common.setupOutgoing = function(outgoing, options, req, forward) { if (options.headers){ extend(outgoing.headers, options.headers); } - - if (options.agent){ - outgoing.agent = options.agent; - } - - if (!outgoing.agent){ - var Agent = (~['https:', 'wss:'].indexOf(options[forward || 'target'].protocol) ? https.Agent : http.Agent); - outgoing.agent = new Agent(options.maxSock || 100); - } - + + outgoing.agent = options.agent || false; outgoing.path = req.url; return outgoing; From 1b5fb1d8fc21421b8383919d93e4149b586b211b Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 15:00:28 -0700 Subject: [PATCH 111/210] DOC: updated readme with options --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f58bbbab8..c4296136a 100644 --- a/README.md +++ b/README.md @@ -105,10 +105,14 @@ server.listen(5050); * **target**: url string to be parsed with the url module * **forward**: url string to be parsed with the url module + * **agent**: object to be passed to http(s).request (see Node's [https agent][0] and [http agent][1] agent objects) + +If you are using the `proxyServer.listen` method, the following options are also applicable: + * **ssl**: object to be passed to https.createServer() * **ws**: true/false, if you want to proxy websockets * **xfwd**: true/false, adds x-forward headers - * **maxSock**: maximum number of sockets + ### Test @@ -144,3 +148,5 @@ Logo created by [Diego Pasquali](http://dribbble.com/diegopq) >OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN >THE SOFTWARE. +[0]: http://nodejs.org/api/https.html#https_class_https_agent +[1]: http://nodejs.org/api/http.html#http_class_http_agent From a350fadea6bace293131581487f8c66948009449 Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 15:06:22 -0700 Subject: [PATCH 112/210] FIX: tests. still need to add more tests tho --- test/lib-caronte-common-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/lib-caronte-common-test.js b/test/lib-caronte-common-test.js index 1523cffd8..88536e2c1 100644 --- a/test/lib-caronte-common-test.js +++ b/test/lib-caronte-common-test.js @@ -7,12 +7,12 @@ describe('lib/caronte/common.js', function () { var outgoing = {}; common.setupOutgoing(outgoing, { + agent : '?', target: { host : 'hey', hostname : 'how', socketPath: 'are', port : 'you', - agent : '?' } }, { @@ -35,12 +35,12 @@ describe('lib/caronte/common.js', function () { it('set the port according to the protocol', function () { var outgoing = {}; common.setupOutgoing(outgoing, - { + { + agent : '?', target: { host : 'how', hostname : 'are', socketPath: 'you', - agent : '?', protocol: 'https:' } }, From 39b0c46a6967fda5329760ad93a8ec01bc4a6f14 Mon Sep 17 00:00:00 2001 From: srossross Date: Tue, 17 Sep 2013 15:29:48 -0700 Subject: [PATCH 113/210] TEST: added agent and header tests --- test/lib-caronte-common-test.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/lib-caronte-common-test.js b/test/lib-caronte-common-test.js index 88536e2c1..ac79867c5 100644 --- a/test/lib-caronte-common-test.js +++ b/test/lib-caronte-common-test.js @@ -13,12 +13,13 @@ describe('lib/caronte/common.js', function () { hostname : 'how', socketPath: 'are', port : 'you', - } + }, + headers: {'fizz': 'bang', 'overwritten':true}, }, { method : 'i', url : 'am', - headers : 'proxy' + headers : {'pro':'xy','overwritten':false} }); expect(outgoing.host).to.eql('hey'); @@ -29,7 +30,16 @@ describe('lib/caronte/common.js', function () { expect(outgoing.method).to.eql('i'); expect(outgoing.path).to.eql('am'); - expect(outgoing.headers).to.eql('proxy') + + expect(outgoing.headers.pro).to.eql('xy'); + expect(outgoing.headers.fizz).to.eql('bang'); + expect(outgoing.headers.overwritten).to.eql(true); + }); + + it('should set the agent to false if none is given', function () { + var outgoing = {}; + common.setupOutgoing(outgoing, {target: {},}, {}); + expect(outgoing.agent).to.eql(false); }); it('set the port according to the protocol', function () { From 7ad5c0f993294c9e2e7650e15fbc62d11a2cb062 Mon Sep 17 00:00:00 2001 From: srossross Date: Wed, 18 Sep 2013 09:07:56 -0700 Subject: [PATCH 114/210] DOC: updated readme @yawnt I think it is good to go. If you have any other tests in mind let me know. --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c4296136a..b53985aa6 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ server.listen(5050); * **target**: url string to be parsed with the url module * **forward**: url string to be parsed with the url module - * **agent**: object to be passed to http(s).request (see Node's [https agent][0] and [http agent][1] agent objects) + * **agent**: object to be passed to http(s).request (see Node's [https agent](http://nodejs.org/api/https.html#https_class_https_agent) and [http agent](http://nodejs.org/api/http.html#http_class_http_agent) objects) If you are using the `proxyServer.listen` method, the following options are also applicable: @@ -148,5 +148,4 @@ Logo created by [Diego Pasquali](http://dribbble.com/diegopq) >OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN >THE SOFTWARE. -[0]: http://nodejs.org/api/https.html#https_class_https_agent -[1]: http://nodejs.org/api/http.html#http_class_http_agent + From 5dcdf2b36c24a9584f044b7529265b9ac861d8c7 Mon Sep 17 00:00:00 2001 From: cronopio Date: Fri, 20 Sep 2013 19:28:53 -0500 Subject: [PATCH 115/210] [doc] added some documentation to functions and comments to understand better the code --- lib/caronte/common.js | 17 +++++++++++++ lib/caronte/index.js | 9 +++++++ lib/caronte/passes/web-incoming.js | 11 ++++++++- lib/caronte/passes/web-outgoing.js | 38 ++++++++++++++++++++++++++++++ lib/caronte/passes/ws-incoming.js | 27 ++++++++++++++++++++- 5 files changed, 100 insertions(+), 2 deletions(-) diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 6b75c6f47..2b9d43675 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -37,6 +37,23 @@ common.setupOutgoing = function(outgoing, options, req, forward) { return outgoing; }; +/** + * Set the proper configuration for sockets, + * set no delay and set keep alive, also set + * the timeout to 0. + * + * Examples: + * + * common.setupSocket(socket) + * // => Socket + * + * @param {Socket} Socket instance to setup + *  + * @return {Socket} Return the configured socket. + * + * @api private + */ + common.setupSocket = function(socket) { socket.setTimeout(0); socket.setNoDelay(true); diff --git a/lib/caronte/index.js b/lib/caronte/index.js index ef0187fa9..c3bb69fb7 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -65,6 +65,15 @@ function createRightProxy(type) { passes.some(function(pass) { var evnt = ev + pass.name.toLowerCase() + ':'; + + /** + * Call of passes functions + * pass(req, res, options, head) + * + * In WebSockets case the `res` variable + * refer to the connection socket + * pass(req, socket, options, head) + */ options.ee.emit(evnt + 'begin', req, res); var val = pass(req, res, options, head); diff --git a/lib/caronte/passes/web-incoming.js b/lib/caronte/passes/web-incoming.js index ce494feb5..1c1a6b9eb 100644 --- a/lib/caronte/passes/web-incoming.js +++ b/lib/caronte/passes/web-incoming.js @@ -91,6 +91,7 @@ function XHeaders(req, res, options) { function stream(req, res, options) { if(options.forward) { + // If forward enable, so just pipe the request var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req, 'forward') ); @@ -98,15 +99,19 @@ function stream(req, res, options) { return res.end(); } + // Request initalization var proxyReq = (options.target.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); + // Error Handler proxyReq.on('error', function(err){ var ev = 'caronte:outgoing:web:'; + // If no error listeners, so throw the error. if (options.ee.listeners(ev + 'error').length == 0){ throw err; } + // Also emit the error events options.ee.emit(ev + 'error', err, req, res); }); @@ -117,10 +122,14 @@ function stream(req, res, options) { options.ee.emit(ev + 'begin', req, res); + // When the previous request respond, we apply the + // outgoing passes to the response web_o.some(function(pass) { var evnt = ev + pass.name.toLowerCase() + ':'; - options.ee.emit(evnt + 'begin', req, res); + options.ee.emit(evnt + 'begin', req, res); + // Call the pass with the proxy response + // pass(ClientRequest, IncomingMessage, proxyResponse) var val = pass(req, res, proxyRes); options.ee.emit(evnt + 'end'); diff --git a/lib/caronte/passes/web-outgoing.js b/lib/caronte/passes/web-outgoing.js index 48ff12dd6..abdaec925 100644 --- a/lib/caronte/passes/web-outgoing.js +++ b/lib/caronte/passes/web-outgoing.js @@ -10,12 +10,31 @@ var passes = exports; [ // <-- + /** + * If is a HTTP 1.0 request, remove chunk headers + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {proxyResponse} Res Response object from the proxy request + * + * @api private + */ function removeChunked(req, res, proxyRes) { if(req.httpVersion === '1.0') { delete proxyRes.headers['transfer-encoding']; } }, + /** + * If is a HTTP 1.0 request, set the correct connection header + * or if connection header not present, then use `keep-alive` + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {proxyResponse} Res Response object from the proxy request + * + * @api private + */ function setConnection(req, res, proxyRes) { if (req.httpVersion === '1.0') { if (req.headers.connection) { @@ -31,12 +50,31 @@ var passes = exports; } }, + /** + * Copy headers from proxyResponse to response + * set each header in response object. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {proxyResponse} Res Response object from the proxy request + * + * @api private + */ function writeHeaders(req, res, proxyRes) { Object.keys(proxyRes.headers).forEach(function(key) { res.setHeader(key, proxyRes.headers[key]); }); }, + /** + * Set the statusCode from the proxyResponse + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {proxyResponse} Res Response object from the proxy request + * + * @api private + */ function writeStatusCode(req, res, proxyRes) { res.writeHead(proxyRes.statusCode); } diff --git a/lib/caronte/passes/ws-incoming.js b/lib/caronte/passes/ws-incoming.js index e5bdcf60e..ca2b668c8 100644 --- a/lib/caronte/passes/ws-incoming.js +++ b/lib/caronte/passes/ws-incoming.js @@ -22,6 +22,11 @@ var passes = exports; /** * WebSocket requests must have the `GET` method and * the `upgrade:websocket` header + * + * @param {ClientRequest} Req Request object + * @param {Socket} Websocket + * + * @api private */ function checkMethodAndHeader (req, socket) { @@ -35,8 +40,14 @@ function checkMethodAndHeader (req, socket) { }, /** - * Setup socket + * Set the proper configuration for sockets, + * set no delay and set keep alive, also set + * the timeout to 0. * + * @param {ClientRequest} Req Request object + * @param {Socket} Websocket + * + * @api private */ function setupSocket(req, socket) { @@ -49,6 +60,11 @@ function setupSocket(req, socket) { /** * Sets `x-forwarded-*` headers if specified in config. * + * @param {ClientRequest} Req Request object + * @param {Socket} Websocket + * @param {Object} Options Config object passed to the proxy + * + * @api private */ function XHeaders(req, socket, options) { @@ -69,8 +85,14 @@ function XHeaders(req, socket, options) { }, /** + * Does the actual proxying. Make the request and upgrade it + * send the Switching Protocols request and pipe the sockets. * + * @param {ClientRequest} Req Request object + * @param {Socket} Websocket + * @param {Object} Options Config object passed to the proxy * + * @api private */ function stream(req, socket, options, head) { common.setupSocket(socket); @@ -81,11 +103,14 @@ function stream(req, socket, options, head) { var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); + // Error Handler proxyReq.on('error', function(err){ var ev = 'caronte:outgoing:ws:'; + // If no error listeners, so throw the error. if (options.ee.listeners(ev + 'error').length == 0){ throw err; } + // Also emit the error events options.ee.emit(ev + 'error', err, req, socket, head); }); From 4a517fbe6e621d77e7be35c07d19e60f61faaae4 Mon Sep 17 00:00:00 2001 From: cronopio Date: Fri, 20 Sep 2013 20:47:02 -0500 Subject: [PATCH 116/210] [readme] add links to badges on readme, fix #483 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f58bbbab8..5ef7f3887 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,10 @@ proxies and load balancers. ### Build Status

-    - + +    + +

### Core Concept From 32a40889cedfd6b0d92224aa921700a7b7271c68 Mon Sep 17 00:00:00 2001 From: srossross Date: Sat, 21 Sep 2013 13:15:34 -0700 Subject: [PATCH 117/210] DOC: Added error handling example --- examples/error-handling.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 examples/error-handling.js diff --git a/examples/error-handling.js b/examples/error-handling.js new file mode 100644 index 000000000..06b126eb3 --- /dev/null +++ b/examples/error-handling.js @@ -0,0 +1,26 @@ +var caronte = require('../index'); +/* + * Create your proxy server + */ +var proxyServer = caronte.createProxyServer({target:'http://localhost:30404', ws:true}); + +// Register an error handler for web requests +proxyServer.ee.on("caronte:outgoing:web:error", function(err, req, res){ + res.writeHead(502); + res.end("There was an error proxying your request"); +}); + +// Register an error handler for web-socket requests +proxyServer.ee.on("caronte:outgoing:ws:error", function(err, req, socket, head){ + socket.close(); +}); + +// You may also use a wild card +proxyServer.ee.on("*:*:*:error", function(err, req){ + console.log("The error event '" + this.event + "' happened errno: " + err.errno); +}); + + +console.log("Proxy server is listening on port 8000"); +proxyServer.listen(8000); + From 0aeaba7fe6c51f150d0322eb90a77c1701ed88f5 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 25 Sep 2013 14:58:59 +0200 Subject: [PATCH 118/210] [fix] remove trailing whitespaces --- lib/caronte.js | 10 +++++----- lib/caronte/common.js | 10 +++++----- lib/caronte/index.js | 28 ++++++++++++++-------------- lib/caronte/passes/web-incoming.js | 30 +++++++++++++++--------------- lib/caronte/passes/web-outgoing.js | 12 ++++++------ 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/lib/caronte.js b/lib/caronte.js index 337fac94d..0bda8cd18 100644 --- a/lib/caronte.js +++ b/lib/caronte.js @@ -7,15 +7,15 @@ var http = require('http'), /** * Creates the proxy server. - * + * * Examples: - * + * * caronte.createProxyServer({ .. }, 8000) * // => '{ web: [Function], ws: [Function] ... }' * - * @param {Object} Options Config object passed to the proxy + * @param {Object} Options Config object passed to the proxy * - * @return {Object} Proxy Proxy object with handlers for `ws` and `web` requests + * @return {Object} Proxy Proxy object with handlers for `ws` and `web` requests * * @api public */ @@ -40,7 +40,7 @@ proxy.createProxyServer = function createProxyServer(options) { ].join("\n")); } - options.ee = new events.EventEmitter2({ wildcard: true, delimiter: ':' }); + options.ee = new events.EventEmitter2({ wildcard: true, delimiter: ':' }); return { ee : options.ee, diff --git a/lib/caronte/common.js b/lib/caronte/common.js index 88a20c185..ed18aa418 100644 --- a/lib/caronte/common.js +++ b/lib/caronte/common.js @@ -2,8 +2,8 @@ var common = exports, extend = require('util')._extend; /** - * Copies the right headers from `options` and `req` to - * `outgoing` which is then used to fire the proxied + * Copies the right headers from `options` and `req` to + * `outgoing` which is then used to fire the proxied * request. * * Examples: @@ -22,9 +22,9 @@ var common = exports, */ common.setupOutgoing = function(outgoing, options, req, forward) { - outgoing.port = options[forward || 'target'].port || + outgoing.port = options[forward || 'target'].port || (~['https:', 'wss:'].indexOf(options[forward || 'target'].protocol) ? 443 : 80); - + ['host', 'hostname', 'socketPath'].forEach( function(e) { outgoing[e] = options[forward || 'target'][e]; } ); @@ -36,7 +36,7 @@ common.setupOutgoing = function(outgoing, options, req, forward) { if (options.headers){ extend(outgoing.headers, options.headers); } - + outgoing.agent = options.agent || false; outgoing.path = req.url; diff --git a/lib/caronte/index.js b/lib/caronte/index.js index c3bb69fb7..214de9780 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -8,7 +8,7 @@ caronte.createWebProxy = createRightProxy('web'); caronte.createWsProxy = createRightProxy('ws'); /** - * Returns a function that creates the loader for + * Returns a function that creates the loader for * either `ws` or `web`'s passes. * * Examples: @@ -26,7 +26,7 @@ caronte.createWsProxy = createRightProxy('ws'); function createRightProxy(type) { var passes = (type === 'ws') ? ws : web; - return function(options) { + return function(options) { passes = Object.keys(passes).map(function(pass) { return passes[pass]; @@ -42,25 +42,25 @@ function createRightProxy(type) { if( !(args[cntr] instanceof Buffer) && args[cntr] !== res - ) { + ) { //Copy global options options = extend({}, options); //Overwrite with request options extend(options, args[cntr]); - + cntr--; - } - + } + if(args[cntr] instanceof Buffer) { head = args[cntr]; } - options.ee.emit(ev + 'begin', req, res); + options.ee.emit(ev + 'begin', req, res); ['target', 'forward'].forEach( - function(e) { + function(e) { if (typeof options[e] === 'string') - options[e] = parse_url(options[e]); + options[e] = parse_url(options[e]); }); passes.some(function(pass) { @@ -74,16 +74,16 @@ function createRightProxy(type) { * refer to the connection socket * pass(req, socket, options, head) */ - - options.ee.emit(evnt + 'begin', req, res); - var val = pass(req, res, options, head); + + options.ee.emit(evnt + 'begin', req, res); + var val = pass(req, res, options, head); options.ee.emit(evnt + 'end'); - + return val; }); options.ee.emit(ev + 'end'); - }; + }; }; } diff --git a/lib/caronte/passes/web-incoming.js b/lib/caronte/passes/web-incoming.js index 1c1a6b9eb..eb6bbd8d8 100644 --- a/lib/caronte/passes/web-incoming.js +++ b/lib/caronte/passes/web-incoming.js @@ -10,7 +10,7 @@ web_o = Object.keys(web_o).map(function(pass) { /*! * Array of passes. - * + * * A `pass` is just a function that is executed on `req, res, options` * so that you can easily add new checks while still keeping the base * flexible. @@ -22,7 +22,7 @@ web_o = Object.keys(web_o).map(function(pass) { * Sets `content-length` to '0' if request is of DELETE type. * * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object + * @param {IncomingMessage} Res Response object * @param {Object} Options Config object passed to the proxy * * @api private @@ -38,7 +38,7 @@ function deleteLength(req, res, options) { * Sets timeout in request socket if it was specified in options. * * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object + * @param {IncomingMessage} Res Response object * @param {Object} Options Config object passed to the proxy * * @api private @@ -54,7 +54,7 @@ function timeout(req, res, options) { * Sets `x-forwarded-*` headers if specified in config. * * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object + * @param {IncomingMessage} Res Response object * @param {Object} Options Config object passed to the proxy * * @api private @@ -70,7 +70,7 @@ function XHeaders(req, res, options) { }; ['for', 'port', 'proto'].forEach(function(header) { - req.headers['x-forwarded-' + header] = + req.headers['x-forwarded-' + header] = (req.headers['x-forwarded-' + header] || '') + (req.headers['x-forwarded-' + header] ? ',' : '') + values[header] @@ -83,7 +83,7 @@ function XHeaders(req, res, options) { * just dies otherwise. * * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object + * @param {IncomingMessage} Res Response object * @param {Object} Options Config object passed to the proxy * * @api private @@ -112,34 +112,34 @@ function stream(req, res, options) { throw err; } // Also emit the error events - options.ee.emit(ev + 'error', err, req, res); + options.ee.emit(ev + 'error', err, req, res); }); - + req.pipe(proxyReq); proxyReq.on('response', function(proxyRes) { var ev = 'caronte:outgoing:web:'; - options.ee.emit(ev + 'begin', req, res); - + options.ee.emit(ev + 'begin', req, res); + // When the previous request respond, we apply the // outgoing passes to the response web_o.some(function(pass) { var evnt = ev + pass.name.toLowerCase() + ':'; - + options.ee.emit(evnt + 'begin', req, res); // Call the pass with the proxy response // pass(ClientRequest, IncomingMessage, proxyResponse) - var val = pass(req, res, proxyRes); + var val = pass(req, res, proxyRes); options.ee.emit(evnt + 'end'); - + return val; }); options.ee.emit(ev + 'end'); - proxyRes.pipe(res); + proxyRes.pipe(res); }); //proxyReq.end(); @@ -147,5 +147,5 @@ function stream(req, res, options) { ] // <-- .forEach(function(func) { - passes[func.name] = func; + passes[func.name] = func; }); diff --git a/lib/caronte/passes/web-outgoing.js b/lib/caronte/passes/web-outgoing.js index abdaec925..20f94fdae 100644 --- a/lib/caronte/passes/web-outgoing.js +++ b/lib/caronte/passes/web-outgoing.js @@ -2,7 +2,7 @@ var passes = exports; /*! * Array of passes. - * + * * A `pass` is just a function that is executed on `req, res, options` * so that you can easily add new checks while still keeping the base * flexible. @@ -14,7 +14,7 @@ var passes = exports; * If is a HTTP 1.0 request, remove chunk headers * * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object + * @param {IncomingMessage} Res Response object * @param {proxyResponse} Res Response object from the proxy request * * @api private @@ -30,7 +30,7 @@ var passes = exports; * or if connection header not present, then use `keep-alive` * * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object + * @param {IncomingMessage} Res Response object * @param {proxyResponse} Res Response object from the proxy request * * @api private @@ -55,7 +55,7 @@ var passes = exports; * set each header in response object. * * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object + * @param {IncomingMessage} Res Response object * @param {proxyResponse} Res Response object from the proxy request * * @api private @@ -70,7 +70,7 @@ var passes = exports; * Set the statusCode from the proxyResponse * * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object + * @param {IncomingMessage} Res Response object * @param {proxyResponse} Res Response object from the proxy request * * @api private @@ -81,5 +81,5 @@ var passes = exports; ] // <-- .forEach(function(func) { - passes[func.name] = func; + passes[func.name] = func; }); From 17399e7c3ef9addf9dd8f7c628b273e693f128a1 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 25 Sep 2013 15:08:27 +0200 Subject: [PATCH 119/210] [fix] more jshint intendation --- lib/caronte/passes/web-incoming.js | 224 ++++++++++++++--------------- lib/caronte/passes/web-outgoing.js | 11 +- lib/caronte/passes/ws-incoming.js | 208 ++++++++++++++------------- 3 files changed, 219 insertions(+), 224 deletions(-) diff --git a/lib/caronte/passes/web-incoming.js b/lib/caronte/passes/web-incoming.js index eb6bbd8d8..40eb345c7 100644 --- a/lib/caronte/passes/web-incoming.js +++ b/lib/caronte/passes/web-incoming.js @@ -18,132 +18,132 @@ web_o = Object.keys(web_o).map(function(pass) { [ // <-- -/** - * Sets `content-length` to '0' if request is of DELETE type. - * - * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object - * @param {Object} Options Config object passed to the proxy - * - * @api private - */ - -function deleteLength(req, res, options) { - if(req.method === 'DELETE' && !req.headers['content-length']) { - req.headers['content-length'] = '0'; - } -}, - -/** - * Sets timeout in request socket if it was specified in options. - * - * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object - * @param {Object} Options Config object passed to the proxy - * - * @api private - */ - -function timeout(req, res, options) { - if(options.timeout) { - req.socket.setTimeout(options.timeout); - } -}, - -/** - * Sets `x-forwarded-*` headers if specified in config. - * - * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object - * @param {Object} Options Config object passed to the proxy - * - * @api private - */ - -function XHeaders(req, res, options) { - if(!options.xfwd) return; - - var values = { - for : req.connection.remoteAddress || req.socket.remoteAddress, - port : req.connection.remotePort || req.socket.remotePort, - proto: req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http') - }; - - ['for', 'port', 'proto'].forEach(function(header) { - req.headers['x-forwarded-' + header] = - (req.headers['x-forwarded-' + header] || '') + - (req.headers['x-forwarded-' + header] ? ',' : '') + - values[header] - }); -}, - -/** - * Does the actual proxying. If `forward` is enabled fires up - * a ForwardStream, same happens for ProxyStream. The request - * just dies otherwise. - * - * @param {ClientRequest} Req Request object - * @param {IncomingMessage} Res Response object - * @param {Object} Options Config object passed to the proxy - * - * @api private - */ + /** + * Sets `content-length` to '0' if request is of DELETE type. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + + function deleteLength(req, res, options) { + if(req.method === 'DELETE' && !req.headers['content-length']) { + req.headers['content-length'] = '0'; + } + }, + + /** + * Sets timeout in request socket if it was specified in options. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + + function timeout(req, res, options) { + if(options.timeout) { + req.socket.setTimeout(options.timeout); + } + }, + + /** + * Sets `x-forwarded-*` headers if specified in config. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + + function XHeaders(req, res, options) { + if(!options.xfwd) return; + + var values = { + for : req.connection.remoteAddress || req.socket.remoteAddress, + port : req.connection.remotePort || req.socket.remotePort, + proto: req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http') + }; + + ['for', 'port', 'proto'].forEach(function(header) { + req.headers['x-forwarded-' + header] = + (req.headers['x-forwarded-' + header] || '') + + (req.headers['x-forwarded-' + header] ? ',' : '') + + values[header]; + }); + }, + + /** + * Does the actual proxying. If `forward` is enabled fires up + * a ForwardStream, same happens for ProxyStream. The request + * just dies otherwise. + * + * @param {ClientRequest} Req Request object + * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + + function stream(req, res, options) { + if(options.forward) { + // If forward enable, so just pipe the request + var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req, 'forward') + ); + req.pipe(forwardReq); + return res.end(); + } -function stream(req, res, options) { - if(options.forward) { - // If forward enable, so just pipe the request - var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req, 'forward') + // Request initalization + var proxyReq = (options.target.protocol === 'https:' ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req) ); - req.pipe(forwardReq); - return res.end(); - } - - // Request initalization - var proxyReq = (options.target.protocol === 'https:' ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req) - ); - // Error Handler - proxyReq.on('error', function(err){ - var ev = 'caronte:outgoing:web:'; - // If no error listeners, so throw the error. - if (options.ee.listeners(ev + 'error').length == 0){ + // Error Handler + proxyReq.on('error', function(err){ + var ev = 'caronte:outgoing:web:'; + // If no error listeners, so throw the error. + if (!options.ee.listeners(ev + 'error').length){ throw err; - } - // Also emit the error events - options.ee.emit(ev + 'error', err, req, res); - }); + } + // Also emit the error events + options.ee.emit(ev + 'error', err, req, res); + }); - req.pipe(proxyReq); + req.pipe(proxyReq); - proxyReq.on('response', function(proxyRes) { - var ev = 'caronte:outgoing:web:'; + proxyReq.on('response', function(proxyRes) { + var ev = 'caronte:outgoing:web:'; - options.ee.emit(ev + 'begin', req, res); + options.ee.emit(ev + 'begin', req, res); - // When the previous request respond, we apply the - // outgoing passes to the response - web_o.some(function(pass) { - var evnt = ev + pass.name.toLowerCase() + ':'; + // When the previous request respond, we apply the + // outgoing passes to the response + web_o.some(function(pass) { + var evnt = ev + pass.name.toLowerCase() + ':', val; - options.ee.emit(evnt + 'begin', req, res); - // Call the pass with the proxy response - // pass(ClientRequest, IncomingMessage, proxyResponse) - var val = pass(req, res, proxyRes); - options.ee.emit(evnt + 'end'); + options.ee.emit(evnt + 'begin', req, res); + // Call the pass with the proxy response + // pass(ClientRequest, IncomingMessage, proxyResponse) + val = pass(req, res, proxyRes); + options.ee.emit(evnt + 'end'); - return val; - }); + return val; + }); - options.ee.emit(ev + 'end'); + options.ee.emit(ev + 'end'); - proxyRes.pipe(res); - }); + proxyRes.pipe(res); + }); - //proxyReq.end(); -} + //proxyReq.end(); + } ] // <-- .forEach(function(func) { diff --git a/lib/caronte/passes/web-outgoing.js b/lib/caronte/passes/web-outgoing.js index 20f94fdae..9281c4a45 100644 --- a/lib/caronte/passes/web-outgoing.js +++ b/lib/caronte/passes/web-outgoing.js @@ -37,16 +37,9 @@ var passes = exports; */ function setConnection(req, res, proxyRes) { if (req.httpVersion === '1.0') { - if (req.headers.connection) { - proxyRes.headers.connection = req.headers.connection - } else { - proxyRes.headers.connection = 'close' - } + proxyRes.headers.connection = req.headers.connection || 'close'; } else if (!proxyRes.headers.connection) { - if (req.headers.connection) { proxyRes.headers.connection = req.headers.connection } - else { - proxyRes.headers.connection = 'keep-alive' - } + proxyRes.headers.connection = req.headers.connection || 'keep-alive'; } }, diff --git a/lib/caronte/passes/ws-incoming.js b/lib/caronte/passes/ws-incoming.js index ca2b668c8..2afec8f55 100644 --- a/lib/caronte/passes/ws-incoming.js +++ b/lib/caronte/passes/ws-incoming.js @@ -19,115 +19,117 @@ var http = require('http'), var passes = exports; [ -/** - * WebSocket requests must have the `GET` method and - * the `upgrade:websocket` header - * - * @param {ClientRequest} Req Request object - * @param {Socket} Websocket - * - * @api private - */ - -function checkMethodAndHeader (req, socket) { - if (req.method !== 'GET' || !req.headers.upgrade) { - socket.destroy(); return true; - } - - if (req.headers.upgrade.toLowerCase() !== 'websocket') { - socket.destroy(); return true; - } -}, - -/** - * Set the proper configuration for sockets, - * set no delay and set keep alive, also set - * the timeout to 0. - * - * @param {ClientRequest} Req Request object - * @param {Socket} Websocket - * - * @api private - */ - -function setupSocket(req, socket) { - socket.setTimeout(0); - socket.setNoDelay(true); - - socket.setKeepAlive(true, 0); -}, - -/** - * Sets `x-forwarded-*` headers if specified in config. - * - * @param {ClientRequest} Req Request object - * @param {Socket} Websocket - * @param {Object} Options Config object passed to the proxy - * - * @api private - */ - -function XHeaders(req, socket, options) { - if(!options.xfwd) return; - - var values = { - for : req.connection.remoteAddress || req.socket.remoteAddress, - port : req.connection.remotePort || req.socket.remotePort, - proto: req.connection.pair ? 'wss' : 'ws' - }; - - ['for', 'port', 'proto'].forEach(function(header) { - req.headers['x-forwarded-' + header] = - (req.headers['x-forwarded-' + header] || '') + - (req.headers['x-forwarded-' + header] ? ',' : '') + - values[header] - }); -}, - -/** - * Does the actual proxying. Make the request and upgrade it - * send the Switching Protocols request and pipe the sockets. - * - * @param {ClientRequest} Req Request object - * @param {Socket} Websocket - * @param {Object} Options Config object passed to the proxy - * - * @api private - */ -function stream(req, socket, options, head) { - common.setupSocket(socket); - - if (head && head.length) socket.unshift(head); - + /** + * WebSocket requests must have the `GET` method and + * the `upgrade:websocket` header + * + * @param {ClientRequest} Req Request object + * @param {Socket} Websocket + * + * @api private + */ + + function checkMethodAndHeader (req, socket) { + if (req.method !== 'GET' || !req.headers.upgrade) { + socket.destroy(); + return true; + } - var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req) - ); - // Error Handler - proxyReq.on('error', function(err){ - var ev = 'caronte:outgoing:ws:'; - // If no error listeners, so throw the error. - if (options.ee.listeners(ev + 'error').length == 0){ - throw err; + if (req.headers.upgrade.toLowerCase() !== 'websocket') { + socket.destroy(); + return true; } - // Also emit the error events - options.ee.emit(ev + 'error', err, req, socket, head); - }); + }, + + /** + * Set the proper configuration for sockets, + * set no delay and set keep alive, also set + * the timeout to 0. + * + * @param {ClientRequest} Req Request object + * @param {Socket} Websocket + * + * @api private + */ + + function setupSocket(req, socket) { + socket.setTimeout(0); + socket.setNoDelay(true); + + socket.setKeepAlive(true, 0); + }, + + /** + * Sets `x-forwarded-*` headers if specified in config. + * + * @param {ClientRequest} Req Request object + * @param {Socket} Websocket + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + + function XHeaders(req, socket, options) { + if(!options.xfwd) return; + + var values = { + for : req.connection.remoteAddress || req.socket.remoteAddress, + port : req.connection.remotePort || req.socket.remotePort, + proto: req.connection.pair ? 'wss' : 'ws' + }; + + ['for', 'port', 'proto'].forEach(function(header) { + req.headers['x-forwarded-' + header] = + (req.headers['x-forwarded-' + header] || '') + + (req.headers['x-forwarded-' + header] ? ',' : '') + + values[header]; + }); + }, + + /** + * Does the actual proxying. Make the request and upgrade it + * send the Switching Protocols request and pipe the sockets. + * + * @param {ClientRequest} Req Request object + * @param {Socket} Websocket + * @param {Object} Options Config object passed to the proxy + * + * @api private + */ + function stream(req, socket, options, head) { + common.setupSocket(socket); + + if (head && head.length) socket.unshift(head); + + + var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req) + ); + // Error Handler + proxyReq.on('error', function(err){ + var ev = 'caronte:outgoing:ws:'; + // If no error listeners, so throw the error. + if (!options.ee.listeners(ev + 'error').length){ + throw err; + } + // Also emit the error events + options.ee.emit(ev + 'error', err, req, socket, head); + }); - proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { - common.setupSocket(proxySocket); + proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { + common.setupSocket(proxySocket); - if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); + if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); - socket.write('HTTP/1.1 101 Switching Protocols\r\n'); - socket.write(Object.keys(proxyRes.headers).map(function(i) { - return i + ": " + proxyRes.headers[i]; - }).join('\r\n') + '\r\n\r\n'); - proxySocket.pipe(socket).pipe(proxySocket); - }); + socket.write('HTTP/1.1 101 Switching Protocols\r\n'); + socket.write(Object.keys(proxyRes.headers).map(function(i) { + return i + ": " + proxyRes.headers[i]; + }).join('\r\n') + '\r\n\r\n'); + proxySocket.pipe(socket).pipe(proxySocket); + }); - proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT -} + proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT + } ] // <-- .forEach(function(func) { From 455f97e14cb4929e0a3a5c746471e9c5e76436fc Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 25 Sep 2013 15:11:04 +0200 Subject: [PATCH 120/210] [fix] finished jshint fixes --- lib/caronte/index.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/caronte/index.js b/lib/caronte/index.js index 214de9780..86b79a45f 100644 --- a/lib/caronte/index.js +++ b/lib/caronte/index.js @@ -57,14 +57,13 @@ function createRightProxy(type) { options.ee.emit(ev + 'begin', req, res); - ['target', 'forward'].forEach( - function(e) { - if (typeof options[e] === 'string') - options[e] = parse_url(options[e]); + ['target', 'forward'].forEach(function(e) { + if (typeof options[e] === 'string') + options[e] = parse_url(options[e]); }); passes.some(function(pass) { - var evnt = ev + pass.name.toLowerCase() + ':'; + var evnt = ev + pass.name.toLowerCase() + ':', val; /** * Call of passes functions @@ -76,7 +75,7 @@ function createRightProxy(type) { */ options.ee.emit(evnt + 'begin', req, res); - var val = pass(req, res, options, head); + val = pass(req, res, options, head); options.ee.emit(evnt + 'end'); return val; From f7f5fa727e8f1d3f4946e61ad03830dab1da01a5 Mon Sep 17 00:00:00 2001 From: indexzero Date: Thu, 26 Sep 2013 03:27:55 -0400 Subject: [PATCH 121/210] [dist doc] Added documentation for consistent benchmarking of node-http-proxy --- benchmark/README.md | 33 ++++++++ benchmark/scripts/hello.js | 3 + benchmark/scripts/proxy.js | 6 ++ benchmark/scripts/websockets-throughput.js | 88 ++++++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 benchmark/README.md create mode 100644 benchmark/scripts/hello.js create mode 100644 benchmark/scripts/proxy.js create mode 100644 benchmark/scripts/websockets-throughput.js diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 000000000..2a852d14e --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,33 @@ +# Benchmarking `node-http-proxy` + +The long-term goal of these scripts and documentation is to provide a consistent and well understood benchmarking process for `node-http-proxy` so that performance does not degrade over time. They were initially created to compare the performance of `v0.10.3` and `v1.0.0` (which was a significant rewrite). + +## Pre-requisites + +All benchmarking shall be done with [wrk](https://github.com/wg/wrk) which _is the same tool used for performance testing by the node.js core team._ **Make sure you have `wrk` installed before continuing**. + +``` +$ wrk +Usage: wrk + Options: + -c, --connections Connections to keep open + -r, --requests Total requests to make + -t, --threads Number of threads to use + + -H, --header Add header to request + -v, --version Print version details + + Numeric arguments may include a SI unit (2k, 2M, 2G) +``` + +## Benchmarks + +1. [Simple HTTP benchmark](#simple-http) + +### Simple HTTP + +_This benchmark requires three terminals running:_ + +1. **A proxy server:** `node benchmark/scripts/proxy.js` +2. **A target server:** `node benchmark/scripts/hello.js` +3. **A wrk process:** `wrk -c 20 -r 10000 -t 2 http://127.0.0.1:8000` \ No newline at end of file diff --git a/benchmark/scripts/hello.js b/benchmark/scripts/hello.js new file mode 100644 index 000000000..be09b6388 --- /dev/null +++ b/benchmark/scripts/hello.js @@ -0,0 +1,3 @@ +require('http').createServer(function(req, res) { + res.end('Hello world!'); +}).listen(9000); \ No newline at end of file diff --git a/benchmark/scripts/proxy.js b/benchmark/scripts/proxy.js new file mode 100644 index 000000000..a70c55817 --- /dev/null +++ b/benchmark/scripts/proxy.js @@ -0,0 +1,6 @@ +var http = require('http'), + httpProxy = require('../../'); +// +// Create your proxy server +// +httpProxy.createProxyServer({ target: 'http://localhost:9000' }).listen(8000); \ No newline at end of file diff --git a/benchmark/scripts/websockets-throughput.js b/benchmark/scripts/websockets-throughput.js new file mode 100644 index 000000000..866e42461 --- /dev/null +++ b/benchmark/scripts/websockets-throughput.js @@ -0,0 +1,88 @@ +var crypto = require('crypto'), + WebSocket = require('ws'), + async = require('async'), + httpProxy = require('../../'); + +var SERVER_PORT = 8415, + PROXY_PORT = 8514; + +var testSets = [ + { + size: 1024 * 1024, // 1 MB + count: 128 // 128 MB + }, + { + size: 1024, // 1 KB, + count: 1024 // 1 MB + }, + { + size: 128, // 128 B + count: 1024 * 8 // 1 MB + } +]; + +testSets.forEach(function (set) { + set.buffer = new Buffer(crypto.randomBytes(set.size)); + + set.buffers = []; + for (var i = 0; i < set.count; i++) { + set.buffers.push(set.buffer); + } +}); + +function runSet(set, callback) { + function runAgainst(port, callback) { + function send(sock) { + sock.send(set.buffers[got++]); + if (got === set.count) { + t = new Date() - t; + + server.close(); + proxy.close(); + + callback(null, t); + } + } + + var server = new WebSocket.Server({ port: SERVER_PORT }), + proxy = httpProxy.createServer(SERVER_PORT, 'localhost').listen(PROXY_PORT), + client = new WebSocket('ws://localhost:' + port), + got = 0, + t = new Date(); + + server.on('connection', function (ws) { + send(ws); + + ws.on('message', function (msg) { + send(ws); + }); + }); + + client.on('message', function () { + send(client); + }); + } + + async.series({ + server: async.apply(runAgainst, SERVER_PORT), + proxy: async.apply(runAgainst, PROXY_PORT) + }, function (err, results) { + if (err) { + throw err; + } + + var mb = (set.size * set.count) / (1024 * 1024); + console.log(set.size / (1024) + ' KB * ' + set.count + ' (' + mb + ' MB)'); + + Object.keys(results).forEach(function (key) { + var t = results[key], + throughput = mb / (t / 1000); + + console.log(' ' + key + ' took ' + t + ' ms (' + throughput + ' MB/s)'); + }); + + callback(); + }); +} + +async.forEachLimit(testSets, 1, runSet); From bb0d28c58729e2cc70e8446f7fbf1113a6fa9310 Mon Sep 17 00:00:00 2001 From: indexzero Date: Thu, 26 Sep 2013 03:37:08 -0400 Subject: [PATCH 122/210] [refactor minor] s/caronte/http-proxy/ or s/caronte/httpProxy/ where appropriate. --- README.md | 24 +++++------ examples/error-handling.js | 8 ++-- examples/https-secure.js | 4 +- examples/https.js | 4 +- examples/stand-alone.js | 4 +- index.js | 2 +- lib/{caronte.js => http-proxy.js} | 20 +++++----- lib/{caronte => http-proxy}/common.js | 0 lib/{caronte => http-proxy}/index.js | 16 ++++---- .../passes/web-incoming.js | 4 +- .../passes/web-outgoing.js | 0 .../passes/ws-incoming.js | 2 +- package.json | 14 ++++--- test/lib-caronte-common-test.js | 4 +- test/lib-caronte-passes-web-incoming-test.js | 10 ++--- test/lib-caronte-passes-web-outgoing-test.js | 18 ++++----- test/lib-caronte-passes-ws-incoming-test.js | 20 +++++----- test/lib-caronte-test.js | 40 +++++++++---------- 18 files changed, 99 insertions(+), 95 deletions(-) rename lib/{caronte.js => http-proxy.js} (79%) rename lib/{caronte => http-proxy}/common.js (100%) rename lib/{caronte => http-proxy}/index.js (83%) rename lib/{caronte => http-proxy}/passes/web-incoming.js (97%) rename lib/{caronte => http-proxy}/passes/web-outgoing.js (100%) rename lib/{caronte => http-proxy}/passes/ws-incoming.js (98%) diff --git a/README.md b/README.md index bc64e4226..c44fcd6d7 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@

-Caronte +node-http-proxy ======= -Caronte is an HTTP programmable proxying library that supports +`node-http-proxy` is an HTTP programmable proxying library that supports websockets. It is suitable for implementing components such as proxies and load balancers. @@ -21,12 +21,12 @@ proxies and load balancers. ### Core Concept A new proxy is created by calling `createProxyServer` and passing -an `options` object as argument ([valid properties are available here](tree/master/lib/caronte.js#L26-L39)) +an `options` object as argument ([valid properties are available here](tree/master/lib/http-proxy.js#L26-L39)) ```javascript -var caronte = require('caronte'); +var httpProxy = require('http-proxy'); -var proxy = caronte.createProxyServer(options); +var proxy = httpProxy.createProxyServer(options); ``` An object will be returned with four values: @@ -44,7 +44,7 @@ require('http').createServer(function(req, res) { }); ``` -When a request is proxied it follows two different pipelines ([available here](tree/master/lib/caronte/passes)) +When a request is proxied it follows two different pipelines ([available here](tree/master/lib/http-proxy/passes)) which apply transformations to both the `req` and `res` object. The first pipeline (ingoing) is responsible for the creation and manipulation of the stream that connects your client to the target. The second pipeline (outgoing) is responsible for the creation and manipulation of the stream that, from your target, returns data @@ -58,11 +58,11 @@ In addition, every stage emits a corresponding event so introspection during the ```js var http = require('http'), - caronte = require('caronte'); + httpProxy = require('http-proxy'); // // Create your proxy server // -caronte.createProxyServer({target:'http://localhost:9000'}).listen(8000); +httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(8000); // // Create your target server @@ -78,12 +78,12 @@ http.createServer(function (req, res) { ``` js var http = require('http'), - caronte = require('caronte'); + httpProxy = require('http-proxy'); // // Create a proxy server with custom application logic // -var proxy = caronte.createProxyServer({}); +var proxy = httpProxy.createProxyServer({}); var server = require('http').createServer(function(req, res) { proxy.web(req, res, { target: 'http://127.0.0.1:5060' }); @@ -103,7 +103,7 @@ server.listen(5050); ### Options -`caronte.createProxyServer` supports the following options: +`httpProxy.createProxyServer` supports the following options: * **target**: url string to be parsed with the url module * **forward**: url string to be parsed with the url module @@ -130,7 +130,7 @@ Logo created by [Diego Pasquali](http://dribbble.com/diegopq) >The MIT License (MIT) > ->Copyright (c) 2013 Nodejitsu Inc. +>Copyright (c) 2010 - 2013 Nodejitsu Inc. > >Permission is hereby granted, free of charge, to any person obtaining a copy >of this software and associated documentation files (the "Software"), to deal diff --git a/examples/error-handling.js b/examples/error-handling.js index 06b126eb3..f646a8de4 100644 --- a/examples/error-handling.js +++ b/examples/error-handling.js @@ -1,17 +1,17 @@ -var caronte = require('../index'); +var httpProxy = require('../index'); /* * Create your proxy server */ -var proxyServer = caronte.createProxyServer({target:'http://localhost:30404', ws:true}); +var proxyServer = httpProxy.createProxyServer({target:'http://localhost:30404', ws:true}); // Register an error handler for web requests -proxyServer.ee.on("caronte:outgoing:web:error", function(err, req, res){ +proxyServer.ee.on("http-proxy:outgoing:web:error", function(err, req, res){ res.writeHead(502); res.end("There was an error proxying your request"); }); // Register an error handler for web-socket requests -proxyServer.ee.on("caronte:outgoing:ws:error", function(err, req, socket, head){ +proxyServer.ee.on("http-proxy:outgoing:ws:error", function(err, req, socket, head){ socket.close(); }); diff --git a/examples/https-secure.js b/examples/https-secure.js index b6d7bb759..4ddbe8d23 100644 --- a/examples/https-secure.js +++ b/examples/https-secure.js @@ -1,4 +1,4 @@ -var caronte = require('caronte'), +var httpProxy = require('http-proxy'), https = require('https'); /* * Create your proxy server pointing to a secure domain @@ -9,7 +9,7 @@ var options = {target : 'https://google.com', headers: {host: 'google.com'} }; -var proxyServer = caronte.createProxyServer(options); +var proxyServer = httpProxy.createProxyServer(options); console.log("Proxy server listening on port 8000"); proxyServer.listen(8000); diff --git a/examples/https.js b/examples/https.js index b64e3cf2f..7f0aed3e2 100644 --- a/examples/https.js +++ b/examples/https.js @@ -1,10 +1,10 @@ -var caronte = require('caronte'); +var httpProxy = require('http-proxy'); /* * Create your proxy server pointing to a secure domain */ var options = {target:'https://google.com'}; -var proxyServer = caronte.createProxyServer(options); +var proxyServer = httpProxy.createProxyServer(options); console.log("Proxy server listening on port 8000"); proxyServer.listen(8000); diff --git a/examples/stand-alone.js b/examples/stand-alone.js index 081134e6b..3bf6ddccf 100644 --- a/examples/stand-alone.js +++ b/examples/stand-alone.js @@ -1,10 +1,10 @@ var http = require('http'), - caronte = require('caronte'); + httpProxy = require('http-proxy'); // // Create your proxy server // console.log("Proxy server listening on port 8000"); -caronte.createProxyServer({target:'http://localhost:9000'}).listen(8000); +httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(8000); // // Create your target server diff --git a/index.js b/index.js index 68de922bd..e6fac8584 100644 --- a/index.js +++ b/index.js @@ -10,4 +10,4 @@ * Dante - The Divine Comedy (Canto III) */ -module.exports = require('./lib/caronte'); \ No newline at end of file +module.exports = require('./lib/http-proxy'); \ No newline at end of file diff --git a/lib/caronte.js b/lib/http-proxy.js similarity index 79% rename from lib/caronte.js rename to lib/http-proxy.js index 0bda8cd18..8cabc6fa1 100644 --- a/lib/caronte.js +++ b/lib/http-proxy.js @@ -1,16 +1,16 @@ -var http = require('http'), - https = require('https'), - url = require('url'), - caronte = require('./caronte/'), - events = require('eventemitter2'), - proxy = exports; +var http = require('http'), + https = require('https'), + url = require('url'), + httpProxy = require('./http-proxy'), + events = require('eventemitter2'), + proxy = exports; /** * Creates the proxy server. * * Examples: * - * caronte.createProxyServer({ .. }, 8000) + * httpProxy.createProxyServer({ .. }, 8000) * // => '{ web: [Function], ws: [Function] ... }' * * @param {Object} Options Config object passed to the proxy @@ -20,7 +20,7 @@ var http = require('http'), * @api public */ -proxy.createProxyServer = function createProxyServer(options) { +proxy.createProxyServer = proxy.createServer = function createProxyServer(options) { if(!options) { throw new Error([ "`options` is needed and it must have the following layout:", @@ -44,8 +44,8 @@ proxy.createProxyServer = function createProxyServer(options) { return { ee : options.ee, - web : caronte.createWebProxy(options), - ws : caronte.createWsProxy(options), + web : httpProxy.createWebProxy(options), + ws : httpProxy.createWsProxy(options), listen : function listen(port) { var server = options.ssl ? https.createServer(options.ssl, this.web) : http.createServer(this.web); diff --git a/lib/caronte/common.js b/lib/http-proxy/common.js similarity index 100% rename from lib/caronte/common.js rename to lib/http-proxy/common.js diff --git a/lib/caronte/index.js b/lib/http-proxy/index.js similarity index 83% rename from lib/caronte/index.js rename to lib/http-proxy/index.js index 86b79a45f..22d2d5c9b 100644 --- a/lib/caronte/index.js +++ b/lib/http-proxy/index.js @@ -1,11 +1,11 @@ -var caronte = exports, - extend = require('util')._extend, +var httpProxy = exports, + extend = require('util')._extend, parse_url = require('url').parse, - web = require('./passes/web-incoming'), - ws = require('./passes/ws-incoming'); + web = require('./passes/web-incoming'), + ws = require('./passes/ws-incoming'); -caronte.createWebProxy = createRightProxy('web'); -caronte.createWsProxy = createRightProxy('ws'); +httpProxy.createWebProxy = createRightProxy('web'); +httpProxy.createWsProxy = createRightProxy('ws'); /** * Returns a function that creates the loader for @@ -13,7 +13,7 @@ caronte.createWsProxy = createRightProxy('ws'); * * Examples: * - * caronte.createRightProxy('ws') + * httpProxy.createRightProxy('ws') * // => [Function] * * @param {String} Type Either 'ws' or 'web' @@ -36,7 +36,7 @@ function createRightProxy(type) { var self = this, args = [].slice.call(arguments), cntr = args.length - 1, - ev = 'caronte:' + type + ':incoming:', + ev = 'http-proxy:' + type + ':incoming:', head; if( diff --git a/lib/caronte/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js similarity index 97% rename from lib/caronte/passes/web-incoming.js rename to lib/http-proxy/passes/web-incoming.js index 40eb345c7..8f5afae9d 100644 --- a/lib/caronte/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -106,7 +106,7 @@ web_o = Object.keys(web_o).map(function(pass) { // Error Handler proxyReq.on('error', function(err){ - var ev = 'caronte:outgoing:web:'; + var ev = 'http-proxy:outgoing:web:'; // If no error listeners, so throw the error. if (!options.ee.listeners(ev + 'error').length){ throw err; @@ -118,7 +118,7 @@ web_o = Object.keys(web_o).map(function(pass) { req.pipe(proxyReq); proxyReq.on('response', function(proxyRes) { - var ev = 'caronte:outgoing:web:'; + var ev = 'http-proxy:outgoing:web:'; options.ee.emit(ev + 'begin', req, res); diff --git a/lib/caronte/passes/web-outgoing.js b/lib/http-proxy/passes/web-outgoing.js similarity index 100% rename from lib/caronte/passes/web-outgoing.js rename to lib/http-proxy/passes/web-outgoing.js diff --git a/lib/caronte/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js similarity index 98% rename from lib/caronte/passes/ws-incoming.js rename to lib/http-proxy/passes/ws-incoming.js index 2afec8f55..070cefbb9 100644 --- a/lib/caronte/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -107,7 +107,7 @@ var passes = exports; ); // Error Handler proxyReq.on('error', function(err){ - var ev = 'caronte:outgoing:ws:'; + var ev = 'http-proxy:outgoing:ws:'; // If no error listeners, so throw the error. if (!options.ee.listeners(ev + 'error').length){ throw err; diff --git a/package.json b/package.json index 22406ba23..a4f653b5d 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,13 @@ { - "name" : "caronte", - "version" : "0.0.0", + "name" : "http-proxy", + "version" : "1.0.0", "description" : "HTTP proxying for the masses", - "author" : "yawnt ", - + "author": "Nodejitsu Inc. ", + "maintainers" : [ + "yawnt ", + "indexzero " + ], + "main" : "index.js", "dependencies" : { @@ -24,7 +28,7 @@ }, "scripts" : { "coveralls" : "mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js", - "blanket" : { "pattern": "lib/caronte" }, + "blanket" : { "pattern": "lib/http-proxy" }, "test" : "./node_modules/.bin/mocha -R landing test/*-test.js", "test-cov" : "./node_modules/.bin/mocha --require blanket -R html-cov > cov/coverage.html" }, diff --git a/test/lib-caronte-common-test.js b/test/lib-caronte-common-test.js index ac79867c5..2f9fa1ed8 100644 --- a/test/lib-caronte-common-test.js +++ b/test/lib-caronte-common-test.js @@ -1,7 +1,7 @@ -var common = require('../lib/caronte/common'), +var common = require('../lib/http-proxy/common'), expect = require('expect.js'); -describe('lib/caronte/common.js', function () { +describe('lib/http-proxy/common.js', function () { describe('#setupOutgoing', function () { it('should setup the correct headers', function () { var outgoing = {}; diff --git a/test/lib-caronte-passes-web-incoming-test.js b/test/lib-caronte-passes-web-incoming-test.js index cc0a760a7..4269d5f8b 100644 --- a/test/lib-caronte-passes-web-incoming-test.js +++ b/test/lib-caronte-passes-web-incoming-test.js @@ -1,14 +1,14 @@ -var caronte = require('../lib/caronte/passes/web-incoming'), +var httpProxy = require('../lib/http-proxy/passes/web-incoming'), expect = require('expect.js'); -describe('lib/caronte/passes/web.js', function() { +describe('lib/http-proxy/passes/web.js', function() { describe('#deleteLength', function() { it('should change `content-length`', function() { var stubRequest = { method: 'DELETE', headers: {} }; - caronte.deleteLength(stubRequest, {}, {}); + httpProxy.deleteLength(stubRequest, {}, {}); expect(stubRequest.headers['content-length']).to.eql('0'); }) }); @@ -21,7 +21,7 @@ describe('lib/caronte/passes/web.js', function() { } } - caronte.timeout(stubRequest, {}, { timeout: 5000}); + httpProxy.timeout(stubRequest, {}, { timeout: 5000}); expect(done).to.eql(5000); }); }); @@ -36,7 +36,7 @@ describe('lib/caronte/passes/web.js', function() { } it('set the correct x-forwarded-* headers', function () { - caronte.XHeaders(stubRequest, {}, { xfwd: true }); + httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); expect(stubRequest.headers['x-forwarded-proto']).to.be('http'); diff --git a/test/lib-caronte-passes-web-outgoing-test.js b/test/lib-caronte-passes-web-outgoing-test.js index 64fc7ead9..0ae0bda6f 100644 --- a/test/lib-caronte-passes-web-outgoing-test.js +++ b/test/lib-caronte-passes-web-outgoing-test.js @@ -1,11 +1,11 @@ -var caronte = require('../lib/caronte/passes/web-outgoing'), +var httpProxy = require('../lib/http-proxy/passes/web-outgoing'), expect = require('expect.js'); -describe('lib/caronte/passes/web-outgoing.js', function () { +describe('lib/http-proxy/passes/web-outgoing.js', function () { describe('#setConnection', function () { it('set the right connection with 1.0 - `close`', function() { var proxyRes = { headers: {} }; - caronte.setConnection({ + httpProxy.setConnection({ httpVersion: '1.0', headers: { connection: null @@ -17,7 +17,7 @@ describe('lib/caronte/passes/web-outgoing.js', function () { it('set the right connection with 1.0 - req.connection', function() { var proxyRes = { headers: {} }; - caronte.setConnection({ + httpProxy.setConnection({ httpVersion: '1.0', headers: { connection: 'hey' @@ -29,7 +29,7 @@ describe('lib/caronte/passes/web-outgoing.js', function () { it('set the right connection - req.connection', function() { var proxyRes = { headers: {} }; - caronte.setConnection({ + httpProxy.setConnection({ httpVersion: null, headers: { connection: 'hola' @@ -41,7 +41,7 @@ describe('lib/caronte/passes/web-outgoing.js', function () { it('set the right connection - `keep-alive`', function() { var proxyRes = { headers: {} }; - caronte.setConnection({ + httpProxy.setConnection({ httpVersion: null, headers: { connection: null @@ -61,7 +61,7 @@ describe('lib/caronte/passes/web-outgoing.js', function () { } } - caronte.writeStatusCode({}, res, { statusCode: 200 }); + httpProxy.writeStatusCode({}, res, { statusCode: 200 }); }); }); @@ -80,7 +80,7 @@ describe('lib/caronte/passes/web-outgoing.js', function () { headers: {} }; - caronte.writeHeaders({}, res, proxyRes); + httpProxy.writeHeaders({}, res, proxyRes); expect(res.headers.hey).to.eql('hello'); expect(res.headers.how).to.eql('are you?'); @@ -95,7 +95,7 @@ describe('lib/caronte/passes/web-outgoing.js', function () { }; - caronte.removeChunked({ httpVersion: '1.0' }, {}, proxyRes); + httpProxy.removeChunked({ httpVersion: '1.0' }, {}, proxyRes); expect(proxyRes.headers['transfer-encoding']).to.eql(undefined); }); diff --git a/test/lib-caronte-passes-ws-incoming-test.js b/test/lib-caronte-passes-ws-incoming-test.js index 2c077d4df..87dfcef94 100644 --- a/test/lib-caronte-passes-ws-incoming-test.js +++ b/test/lib-caronte-passes-ws-incoming-test.js @@ -1,7 +1,7 @@ -var caronte = require('../lib/caronte/passes/ws-incoming'), +var httpProxy = require('../lib/http-proxy/passes/ws-incoming'), expect = require('expect.js'); -describe('lib/caronte/passes/ws-incoming.js', function () { +describe('lib/http-proxy/passes/ws-incoming.js', function () { describe('#checkMethodAndHeader', function () { it('should drop non-GET connections', function () { var destroyCalled = false, @@ -15,7 +15,7 @@ describe('lib/caronte/passes/ws-incoming.js', function () { destroyCalled = true; } } - returnValue = caronte.checkMethodAndHeader(stubRequest, stubSocket); + returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket); expect(returnValue).to.be(true); expect(destroyCalled).to.be(true); }) @@ -32,7 +32,7 @@ describe('lib/caronte/passes/ws-incoming.js', function () { destroyCalled = true; } } - returnValue = caronte.checkMethodAndHeader(stubRequest, stubSocket); + returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket); expect(returnValue).to.be(true); expect(destroyCalled).to.be(true); }) @@ -51,7 +51,7 @@ describe('lib/caronte/passes/ws-incoming.js', function () { destroyCalled = true; } } - returnValue = caronte.checkMethodAndHeader(stubRequest, stubSocket); + returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket); expect(returnValue).to.be(true); expect(destroyCalled).to.be(true); }) @@ -70,7 +70,7 @@ describe('lib/caronte/passes/ws-incoming.js', function () { destroyCalled = true; } } - returnValue = caronte.checkMethodAndHeader(stubRequest, stubSocket); + returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket); expect(returnValue).to.be(undefined); expect(destroyCalled).to.be(false); }) @@ -97,7 +97,7 @@ describe('lib/caronte/passes/ws-incoming.js', function () { nodelay: false, keepalive: false }, - returnValue = caronte.setupSocket({}, stubSocket); + returnValue = httpProxy.setupSocket({}, stubSocket); expect(returnValue).to.be(undefined); expect(socketConfig.timeout).to.eql(0); expect(socketConfig.nodelay).to.eql(true); @@ -107,7 +107,7 @@ describe('lib/caronte/passes/ws-incoming.js', function () { describe('#XHeaders', function () { it('return if no forward request', function () { - var returnValue = caronte.XHeaders({}, {}, {}); + var returnValue = httpProxy.XHeaders({}, {}, {}); expect(returnValue).to.be(undefined); }); @@ -119,7 +119,7 @@ describe('lib/caronte/passes/ws-incoming.js', function () { }, headers: {} } - caronte.XHeaders(stubRequest, {}, { xfwd: true }); + httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); expect(stubRequest.headers['x-forwarded-proto']).to.be('ws'); @@ -136,7 +136,7 @@ describe('lib/caronte/passes/ws-incoming.js', function () { }, headers: {} }; - caronte.XHeaders(stubRequest, {}, { xfwd: true }); + httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.3'); expect(stubRequest.headers['x-forwarded-port']).to.be('8181'); expect(stubRequest.headers['x-forwarded-proto']).to.be('wss'); diff --git a/test/lib-caronte-test.js b/test/lib-caronte-test.js index 0558ef934..b9aa8af7f 100644 --- a/test/lib-caronte-test.js +++ b/test/lib-caronte-test.js @@ -1,17 +1,17 @@ -var caronte = require('../lib/caronte'), - expect = require('expect.js'), - http = require('http'), - ws = require('ws') - io = require('socket.io'), - ioClient = require('socket.io-client'); +var httpProxy = require('../lib/http-proxy'), + expect = require('expect.js'), + http = require('http'), + ws = require('ws') + io = require('socket.io'), + ioClient = require('socket.io-client'); -describe('lib/caronte.js', function() { +describe('lib/http-proxy.js', function() { describe('#createProxyServer', function() { it('should throw without options', function() { var error; try { - caronte.createProxyServer(); + httpProxy.createProxyServer(); } catch(e) { error = e; } @@ -20,7 +20,7 @@ describe('lib/caronte.js', function() { }) it('should return an object otherwise', function() { - var obj = caronte.createProxyServer({ + var obj = httpProxy.createProxyServer({ target: 'http://www.google.com:80' }); @@ -32,7 +32,7 @@ describe('lib/caronte.js', function() { describe('#createProxyServer with forward options and using web-incoming passes', function () { it('should pipe the request using web-incoming#stream method', function (done) { - var proxy = caronte.createProxyServer({ + var proxy = httpProxy.createProxyServer({ forward: 'http://127.0.0.1:8080' }).listen('8081') @@ -52,7 +52,7 @@ describe('lib/caronte.js', function() { describe('#createProxyServer using the web-incoming passes', function () { it('should make the request on pipe and finish it', function(done) { - var proxy = caronte.createProxyServer({ + var proxy = httpProxy.createProxyServer({ target: 'http://127.0.0.1:8080' }).listen('8081'); @@ -80,7 +80,7 @@ describe('lib/caronte.js', function() { describe('#createProxyServer using the web-incoming passes', function () { it('should make the request, handle response and finish it', function(done) { - var proxy = caronte.createProxyServer({ + var proxy = httpProxy.createProxyServer({ target: 'http://127.0.0.1:8080' }).listen('8081'); @@ -115,11 +115,11 @@ describe('lib/caronte.js', function() { describe('#createProxyServer() method with error response', function () { it('should make the request and emit the error event', function(done) { - var proxy = caronte.createProxyServer({ + var proxy = httpProxy.createProxyServer({ target: 'http://127.0.0.1:8080' }); - proxy.ee.on('caronte:outgoing:web:error', function (err) { + proxy.ee.on('http-proxy:outgoing:web:error', function (err) { expect(err).to.be.an(Error); expect(err.code).to.be('ECONNREFUSED'); proxyServer.close(); @@ -138,7 +138,7 @@ describe('lib/caronte.js', function() { describe('#createProxyServer using the web-incoming passes', function () { it('should emit events correclty', function(done) { - var proxy = caronte.createProxyServer({ + var proxy = httpProxy.createProxyServer({ target: 'http://127.0.0.1:8080' }), @@ -155,7 +155,7 @@ describe('lib/caronte.js', function() { source.listen('8080'); - proxy.ee.on('caronte:**', function (uno, dos, tres) { + proxy.ee.on('http-proxy:**', function (uno, dos, tres) { events.push(this.event); }) @@ -171,8 +171,8 @@ describe('lib/caronte.js', function() { }); res.on('end', function () { - expect(events).to.contain('caronte:outgoing:web:begin'); - expect(events).to.contain('caronte:outgoing:web:end'); + expect(events).to.contain('http-proxy:outgoing:web:begin'); + expect(events).to.contain('http-proxy:outgoing:web:end'); source.close(); proxyServer.close(); done(); @@ -183,7 +183,7 @@ describe('lib/caronte.js', function() { describe('#createProxyServer using the ws-incoming passes', function () { it('should proxy the websockets stream', function (done) { - var proxy = caronte.createProxyServer({ + var proxy = httpProxy.createProxyServer({ target: 'ws://127.0.0.1:8080', ws: true }), @@ -215,7 +215,7 @@ describe('lib/caronte.js', function() { describe('#createProxyServer using the ws-incoming passes', function () { it('should proxy a socket.io stream', function (done) { - var proxy = caronte.createProxyServer({ + var proxy = httpProxy.createProxyServer({ target: 'ws://127.0.0.1:8080', ws: true }), From 8269eca2bb34d08336b8889e06e53d3522fa79fe Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 26 Sep 2013 11:13:16 +0200 Subject: [PATCH 123/210] [fix] tests --- lib/http-proxy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index 8cabc6fa1..2fe565e86 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -1,7 +1,7 @@ var http = require('http'), https = require('https'), url = require('url'), - httpProxy = require('./http-proxy'), + httpProxy = require('./http-proxy/'), events = require('eventemitter2'), proxy = exports; From a51b0622780f48160001f9e74340f7d720cbfce6 Mon Sep 17 00:00:00 2001 From: 3rd-Eden Date: Tue, 1 Oct 2013 09:14:37 +0200 Subject: [PATCH 124/210] [minor] Remove duplicate dependencies and cleanup of the scripts --- package.json | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index a4f653b5d..770b97801 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "yawnt ", "indexzero " ], - - "main" : "index.js", + + "main" : "index.js", "dependencies" : { "eventemitter2" : "*" @@ -22,19 +22,17 @@ "blanket" : "*", "ws" : "*", "socket.io" : "*", - "socket.io-client" : "*", - "coveralls" : "*", - "mocha-lcov-reporter": "*" + "socket.io-client" : "*" }, "scripts" : { "coveralls" : "mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js", "blanket" : { "pattern": "lib/http-proxy" }, - "test" : "./node_modules/.bin/mocha -R landing test/*-test.js", - "test-cov" : "./node_modules/.bin/mocha --require blanket -R html-cov > cov/coverage.html" + "test" : "mocha -R landing test/*-test.js", + "test-cov" : "mocha --require blanket -R html-cov > cov/coverage.html" }, - "engines" : { - "node" : ">=0.10.0" + "engines" : { + "node" : ">=0.10.0" }, "license" : "MIT" From b79bd29d5e984f34b9c07fbdc803aed83b3fd0bb Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 8 Oct 2013 22:24:16 +0200 Subject: [PATCH 125/210] [feature] start working on the new server --- lib/http-proxy.js | 24 +++------------------ lib/http-proxy/index.js | 48 +++++++++++++++++++++++++++++------------ package.json | 2 +- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index 2fe565e86..d84772a10 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -21,7 +21,7 @@ var http = require('http'), */ proxy.createProxyServer = proxy.createServer = function createProxyServer(options) { - if(!options) { + /* if(!options) { throw new Error([ "`options` is needed and it must have the following layout:", " ", @@ -38,25 +38,7 @@ proxy.createProxyServer = proxy.createServer = function createProxyServer(option " `options.target and `options.forward` cannot be ", " both missing " ].join("\n")); - } + } */ - options.ee = new events.EventEmitter2({ wildcard: true, delimiter: ':' }); - - return { - ee : options.ee, - web : httpProxy.createWebProxy(options), - ws : httpProxy.createWsProxy(options), - listen : function listen(port) { - var server = options.ssl ? https.createServer(options.ssl, this.web) : http.createServer(this.web); - - if(options.ws) { - server.on('upgrade', this.ws); - } - - server.listen(port); - - return server; - } - }; + return new ProxyServer(options, httpProxy.createWebProxy(options), httpProxy.createWsProxy(options)); }; - diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 22d2d5c9b..9a2798fd6 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -1,11 +1,13 @@ var httpProxy = exports, extend = require('util')._extend, parse_url = require('url').parse, + EE3 = require('eventemitter3').EventEmitter, web = require('./passes/web-incoming'), ws = require('./passes/ws-incoming'); httpProxy.createWebProxy = createRightProxy('web'); httpProxy.createWsProxy = createRightProxy('ws'); +httpProxy.Server = ProxyServer; /** * Returns a function that creates the loader for @@ -36,7 +38,6 @@ function createRightProxy(type) { var self = this, args = [].slice.call(arguments), cntr = args.length - 1, - ev = 'http-proxy:' + type + ':incoming:', head; if( @@ -55,16 +56,13 @@ function createRightProxy(type) { head = args[cntr]; } - options.ee.emit(ev + 'begin', req, res); - ['target', 'forward'].forEach(function(e) { if (typeof options[e] === 'string') options[e] = parse_url(options[e]); }); - passes.some(function(pass) { - var evnt = ev + pass.name.toLowerCase() + ':', val; + for(var i=0; i < passes.length; i++) { /** * Call of passes functions * pass(req, res, options, head) @@ -73,16 +71,38 @@ function createRightProxy(type) { * refer to the connection socket * pass(req, socket, options, head) */ - - options.ee.emit(evnt + 'begin', req, res); - val = pass(req, res, options, head); - options.ee.emit(evnt + 'end'); - - return val; - }); - - options.ee.emit(ev + 'end'); + if(passes[i](req, res, this, head)) { // passes can return a truthy value to halt the loop + break; + } + } }; }; } + +function ProxyServer(options, web, ws) { + this.web = web; + this.ws = ws; + this.options = options; +} + +ProxyServer.prototype.listen = function(port) { + var self = this, + closure = function(req, res) { self.web(req, res); }, + server = options.ssl ? + https.createServer(this.options.ssl, closure) : + http.createServer(closure); + + if(options.ws) { + server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); }); + } + + server.listen(port); + + return server; +}; + +ProxyServer.prototype.before = function() {}; +ProxyServer.prototype.after = function() {}; + +require('util').inherits(ProxyServer, EE); diff --git a/package.json b/package.json index 770b97801..98cd9b5d4 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "main" : "index.js", "dependencies" : { - "eventemitter2" : "*" + "eventemitter3" : "*" }, "devDependencies": { "mocha" : "*", From 601dbcbfe929af31995568b4f36b877245809058 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 9 Oct 2013 16:59:03 +0200 Subject: [PATCH 126/210] [fix] refactor error handling --- lib/http-proxy.js | 2 +- lib/http-proxy/index.js | 10 +++---- lib/http-proxy/passes/web-incoming.js | 41 ++++++--------------------- lib/http-proxy/passes/ws-incoming.js | 14 +++------ 4 files changed, 19 insertions(+), 48 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index d84772a10..945644372 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -2,7 +2,6 @@ var http = require('http'), https = require('https'), url = require('url'), httpProxy = require('./http-proxy/'), - events = require('eventemitter2'), proxy = exports; /** @@ -42,3 +41,4 @@ proxy.createProxyServer = proxy.createServer = function createProxyServer(option return new ProxyServer(options, httpProxy.createWebProxy(options), httpProxy.createWsProxy(options)); }; + diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 9a2798fd6..5c6e7e045 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -89,20 +89,20 @@ function ProxyServer(options, web, ws) { ProxyServer.prototype.listen = function(port) { var self = this, closure = function(req, res) { self.web(req, res); }, - server = options.ssl ? + this._server = options.ssl ? https.createServer(this.options.ssl, closure) : http.createServer(closure); if(options.ws) { - server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); }); + this._server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); }); } - server.listen(port); + this._server.listen(port); - return server; + return this; }; ProxyServer.prototype.before = function() {}; ProxyServer.prototype.after = function() {}; -require('util').inherits(ProxyServer, EE); +require('util').inherits(ProxyServer, EE3); diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index 8f5afae9d..13d1c0164 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -89,55 +89,32 @@ web_o = Object.keys(web_o).map(function(pass) { * @api private */ - function stream(req, res, options) { + function stream(req, res, server) { if(options.forward) { // If forward enable, so just pipe the request - var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req, 'forward') + var forwardReq = (server.options.forward.protocol === 'https:' ? https : http).request( + common.setupOutgoing(server.options.ssl || {}, server.options, req, 'forward') ); req.pipe(forwardReq); return res.end(); } // Request initalization - var proxyReq = (options.target.protocol === 'https:' ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req) + var proxyReq = (server.options.target.protocol === 'https:' ? https : http).request( + common.setupOutgoing(server.options.ssl || {}, server.options, req) ); // Error Handler proxyReq.on('error', function(err){ - var ev = 'http-proxy:outgoing:web:'; - // If no error listeners, so throw the error. - if (!options.ee.listeners(ev + 'error').length){ - throw err; - } - // Also emit the error events - options.ee.emit(ev + 'error', err, req, res); + server.emit('error', err); }); req.pipe(proxyReq); proxyReq.on('response', function(proxyRes) { - var ev = 'http-proxy:outgoing:web:'; - - options.ee.emit(ev + 'begin', req, res); - - // When the previous request respond, we apply the - // outgoing passes to the response - web_o.some(function(pass) { - var evnt = ev + pass.name.toLowerCase() + ':', val; - - options.ee.emit(evnt + 'begin', req, res); - // Call the pass with the proxy response - // pass(ClientRequest, IncomingMessage, proxyResponse) - val = pass(req, res, proxyRes); - options.ee.emit(evnt + 'end'); - - return val; - }); - - options.ee.emit(ev + 'end'); - + for(var i=0; i < web_o.length; i++) { + if(web_o[i](req, res, proxyRes)) { break; } + } proxyRes.pipe(res); }); diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index 070cefbb9..47ed638fc 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -96,24 +96,18 @@ var passes = exports; * * @api private */ - function stream(req, socket, options, head) { + function stream(req, socket, server, head) { common.setupSocket(socket); if (head && head.length) socket.unshift(head); - var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req) + var proxyReq = (~['https:', 'wss:'].indexOf(server.options.target.protocol) ? https : http).request( + common.setupOutgoing(server.options.ssl || {}, server.options, req) ); // Error Handler proxyReq.on('error', function(err){ - var ev = 'http-proxy:outgoing:ws:'; - // If no error listeners, so throw the error. - if (!options.ee.listeners(ev + 'error').length){ - throw err; - } - // Also emit the error events - options.ee.emit(ev + 'error', err, req, socket, head); + server.emit('error', err); }); proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { From c7924e01f92aeec07333273f0882c1dd5e9521ae Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 9 Oct 2013 17:23:44 +0200 Subject: [PATCH 127/210] [fix] callback as optional error handler --- lib/http-proxy/index.js | 41 +++++++++++++++++++++++---- lib/http-proxy/passes/web-incoming.js | 11 +++++-- lib/http-proxy/passes/ws-incoming.js | 9 ++++-- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 5c6e7e045..83e1993de 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -35,10 +35,17 @@ function createRightProxy(type) { }); return function(req, res /*, [head], [opts] */) { - var self = this, + var passes = this.passes || passes, args = [].slice.call(arguments), cntr = args.length - 1, - head; + head, cbl; + + /* optional args parse begin */ + if(typeof args[cntr] === 'function') { + cbl = args[cntr]; + + cntr--; + } if( !(args[cntr] instanceof Buffer) && @@ -56,6 +63,8 @@ function createRightProxy(type) { head = args[cntr]; } + /* optional args parse end */ + ['target', 'forward'].forEach(function(e) { if (typeof options[e] === 'string') options[e] = parse_url(options[e]); @@ -71,7 +80,7 @@ function createRightProxy(type) { * refer to the connection socket * pass(req, socket, options, head) */ - if(passes[i](req, res, this, head)) { // passes can return a truthy value to halt the loop + if(passes[i](req, res, cbl ? this : false, head, cbl)) { // passes can return a truthy value to halt the loop break; } } @@ -84,6 +93,10 @@ function ProxyServer(options, web, ws) { this.web = web; this.ws = ws; this.options = options; + + this.passes = Object.keys(passes).map(function(pass) { + return passes[pass]; + }); } ProxyServer.prototype.listen = function(port) { @@ -102,7 +115,25 @@ ProxyServer.prototype.listen = function(port) { return this; }; -ProxyServer.prototype.before = function() {}; -ProxyServer.prototype.after = function() {}; +ProxyServer.prototype.before = function(passName, callback) { + var i = false; + this.passes.forEach(function(v, idx) { + if(v.name === passName) i = idx; + }) + + if(!i) throw new Error('No such pass'); + + this.passes.splice(i, 0, callback); +}; +ProxyServer.prototype.after = function(passName, callback) { + var i = false; + this.passes.forEach(function(v, idx) { + if(v.name === passName) i = idx; + }) + + if(!i) throw new Error('No such pass'); + + this.passes.splice(i++, 0, callback); +}; require('util').inherits(ProxyServer, EE3); diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index 13d1c0164..b8f936d51 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -89,8 +89,8 @@ web_o = Object.keys(web_o).map(function(pass) { * @api private */ - function stream(req, res, server) { - if(options.forward) { + function stream(req, res, server, _, clb) { + if(server.options.forward) { // If forward enable, so just pipe the request var forwardReq = (server.options.forward.protocol === 'https:' ? https : http).request( common.setupOutgoing(server.options.ssl || {}, server.options, req, 'forward') @@ -106,7 +106,12 @@ web_o = Object.keys(web_o).map(function(pass) { // Error Handler proxyReq.on('error', function(err){ - server.emit('error', err); + if(server) { + server.emit('error', err); + } + else {  + clb(err); + } }); req.pipe(proxyReq); diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index 47ed638fc..66c75d506 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -96,7 +96,7 @@ var passes = exports; * * @api private */ - function stream(req, socket, server, head) { + function stream(req, socket, server, head, clb) { common.setupSocket(socket); if (head && head.length) socket.unshift(head); @@ -107,7 +107,12 @@ var passes = exports; ); // Error Handler proxyReq.on('error', function(err){ - server.emit('error', err); + if(server) { + server.emit('error', err); + } + else { + clb(err); + } }); proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { From 5a1504f0764b7747b53cc0d92a69ff3093e85ade Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 9 Oct 2013 17:28:39 +0200 Subject: [PATCH 128/210] [fix] minor typo --- lib/http-proxy/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 83e1993de..c4b043cac 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -90,6 +90,8 @@ function createRightProxy(type) { function ProxyServer(options, web, ws) { + EE3.call(this); + this.web = web; this.ws = ws; this.options = options; From 3d8e5383cd9d527825f95d9071a87865fcebca05 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 9 Oct 2013 17:31:02 +0200 Subject: [PATCH 129/210] [fix] better code --- lib/http-proxy.js | 2 +- lib/http-proxy/index.js | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index 945644372..f17217864 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -39,6 +39,6 @@ proxy.createProxyServer = proxy.createServer = function createProxyServer(option ].join("\n")); } */ - return new ProxyServer(options, httpProxy.createWebProxy(options), httpProxy.createWsProxy(options)); + return new ProxyServer(options); }; diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index c4b043cac..4faa3956b 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -4,10 +4,8 @@ var httpProxy = exports, EE3 = require('eventemitter3').EventEmitter, web = require('./passes/web-incoming'), ws = require('./passes/ws-incoming'); - -httpProxy.createWebProxy = createRightProxy('web'); -httpProxy.createWsProxy = createRightProxy('ws'); -httpProxy.Server = ProxyServer; + +httpProxy.Server = ProxyServer; /** * Returns a function that creates the loader for @@ -89,11 +87,11 @@ function createRightProxy(type) { } -function ProxyServer(options, web, ws) { +function ProxyServer(options) { EE3.call(this); - this.web = web; - this.ws = ws; + this.web = createRightProxy('web')(options); + this.ws = reateRightProxy('ws')(options); this.options = options; this.passes = Object.keys(passes).map(function(pass) { From a9f9e21eda2f8e912523e6b62abb0101c0353505 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 9 Oct 2013 17:37:20 +0200 Subject: [PATCH 130/210] [fix] --- lib/http-proxy/index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 4faa3956b..7528f46e6 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -101,10 +101,11 @@ function ProxyServer(options) { ProxyServer.prototype.listen = function(port) { var self = this, - closure = function(req, res) { self.web(req, res); }, - this._server = options.ssl ? - https.createServer(this.options.ssl, closure) : - http.createServer(closure); + closure = function(req, res) { self.web(req, res); }; + + this._server = options.ssl ? + https.createServer(this.options.ssl, closure) : + http.createServer(closure); if(options.ws) { this._server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); }); From c9f5772fc18226aca31471bc96c44a6dbff5cbea Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 9 Oct 2013 10:49:13 -0500 Subject: [PATCH 131/210] [tests] remove caronte and use http-proxy for file names --- .../{lib-caronte-common-test.js => lib-http-proxy-common-test.js} | 0 ...ncoming-test.js => lib-http-proxy-passes-web-incoming-test.js} | 0 ...utgoing-test.js => lib-http-proxy-passes-web-outgoing-test.js} | 0 ...incoming-test.js => lib-http-proxy-passes-ws-incoming-test.js} | 0 test/{lib-caronte-test.js => lib-http-proxy-test.js} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename test/{lib-caronte-common-test.js => lib-http-proxy-common-test.js} (100%) rename test/{lib-caronte-passes-web-incoming-test.js => lib-http-proxy-passes-web-incoming-test.js} (100%) rename test/{lib-caronte-passes-web-outgoing-test.js => lib-http-proxy-passes-web-outgoing-test.js} (100%) rename test/{lib-caronte-passes-ws-incoming-test.js => lib-http-proxy-passes-ws-incoming-test.js} (100%) rename test/{lib-caronte-test.js => lib-http-proxy-test.js} (100%) diff --git a/test/lib-caronte-common-test.js b/test/lib-http-proxy-common-test.js similarity index 100% rename from test/lib-caronte-common-test.js rename to test/lib-http-proxy-common-test.js diff --git a/test/lib-caronte-passes-web-incoming-test.js b/test/lib-http-proxy-passes-web-incoming-test.js similarity index 100% rename from test/lib-caronte-passes-web-incoming-test.js rename to test/lib-http-proxy-passes-web-incoming-test.js diff --git a/test/lib-caronte-passes-web-outgoing-test.js b/test/lib-http-proxy-passes-web-outgoing-test.js similarity index 100% rename from test/lib-caronte-passes-web-outgoing-test.js rename to test/lib-http-proxy-passes-web-outgoing-test.js diff --git a/test/lib-caronte-passes-ws-incoming-test.js b/test/lib-http-proxy-passes-ws-incoming-test.js similarity index 100% rename from test/lib-caronte-passes-ws-incoming-test.js rename to test/lib-http-proxy-passes-ws-incoming-test.js diff --git a/test/lib-caronte-test.js b/test/lib-http-proxy-test.js similarity index 100% rename from test/lib-caronte-test.js rename to test/lib-http-proxy-test.js From b333e63648aa67ea1b1aaf17ba684e5fc6f751a6 Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 9 Oct 2013 11:04:41 -0500 Subject: [PATCH 132/210] [tests] fixing minor typos --- lib/http-proxy.js | 2 +- lib/http-proxy/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index f17217864..bcaa36e9e 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -39,6 +39,6 @@ proxy.createProxyServer = proxy.createServer = function createProxyServer(option ].join("\n")); } */ - return new ProxyServer(options); + return new httpProxy.Server(options); }; diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 7528f46e6..1678d1959 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -91,7 +91,7 @@ function ProxyServer(options) { EE3.call(this); this.web = createRightProxy('web')(options); - this.ws = reateRightProxy('ws')(options); + this.ws = createRightProxy('ws')(options); this.options = options; this.passes = Object.keys(passes).map(function(pass) { From a7042132c881656dd32f915d9b0b962f0ef92efb Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 9 Oct 2013 12:00:42 -0500 Subject: [PATCH 133/210] [tests] fixing tests, fixed some typos and changed how passes are stored --- lib/http-proxy/index.js | 27 +++++++++++++-------------- test/lib-http-proxy-test.js | 14 +++++++------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 1678d1959..9776c65d2 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -2,6 +2,8 @@ var httpProxy = exports, extend = require('util')._extend, parse_url = require('url').parse, EE3 = require('eventemitter3').EventEmitter, + http = require('http'), + https = require('https'), web = require('./passes/web-incoming'), ws = require('./passes/ws-incoming'); @@ -24,16 +26,9 @@ httpProxy.Server = ProxyServer; */ function createRightProxy(type) { - var passes = (type === 'ws') ? ws : web; - return function(options) { - - passes = Object.keys(passes).map(function(pass) { - return passes[pass]; - }); - return function(req, res /*, [head], [opts] */) { - var passes = this.passes || passes, + var passes = (type === 'ws') ? this.wsPasses : this.webPasses, args = [].slice.call(arguments), cntr = args.length - 1, head, cbl; @@ -78,7 +73,7 @@ function createRightProxy(type) { * refer to the connection socket * pass(req, socket, options, head) */ - if(passes[i](req, res, cbl ? this : false, head, cbl)) { // passes can return a truthy value to halt the loop + if(passes[i](req, res, cbl ? false : this, head, cbl)) { // passes can return a truthy value to halt the loop break; } } @@ -94,8 +89,12 @@ function ProxyServer(options) { this.ws = createRightProxy('ws')(options); this.options = options; - this.passes = Object.keys(passes).map(function(pass) { - return passes[pass]; + this.webPasses = Object.keys(web).map(function(pass) { + return web[pass]; + }); + + this.wsPasses = Object.keys(ws).map(function(pass) { + return ws[pass]; }); } @@ -103,11 +102,11 @@ ProxyServer.prototype.listen = function(port) { var self = this, closure = function(req, res) { self.web(req, res); }; - this._server = options.ssl ? + this._server = this.options.ssl ? https.createServer(this.options.ssl, closure) : http.createServer(closure); - if(options.ws) { + if(this.options.ws) { this._server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); }); } @@ -137,4 +136,4 @@ ProxyServer.prototype.after = function(passName, callback) { this.passes.splice(i++, 0, callback); }; -require('util').inherits(ProxyServer, EE3); +//require('util').inherits(ProxyServer, EE3); diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index b9aa8af7f..dd3136e5e 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -40,7 +40,7 @@ describe('lib/http-proxy.js', function() { expect(req.method).to.eql('GET'); expect(req.headers.host.split(':')[1]).to.eql('8081'); source.close(); - proxy.close(); + proxy._server.close(); done(); }); @@ -61,7 +61,7 @@ describe('lib/http-proxy.js', function() { expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1'); expect(req.headers.host.split(':')[1]).to.eql('8081'); source.close(); - proxy.close(); + proxy._server.close(); done(); }); @@ -106,7 +106,7 @@ describe('lib/http-proxy.js', function() { res.on('end', function () { source.close(); - proxy.close(); + proxy._server.close(); done(); }); }).end(); @@ -122,7 +122,7 @@ describe('lib/http-proxy.js', function() { proxy.ee.on('http-proxy:outgoing:web:error', function (err) { expect(err).to.be.an(Error); expect(err.code).to.be('ECONNREFUSED'); - proxyServer.close(); + proxyServer._server.close(); done(); }) @@ -174,7 +174,7 @@ describe('lib/http-proxy.js', function() { expect(events).to.contain('http-proxy:outgoing:web:begin'); expect(events).to.contain('http-proxy:outgoing:web:end'); source.close(); - proxyServer.close(); + proxyServer._server.close(); done(); }); }).end(); @@ -198,7 +198,7 @@ describe('lib/http-proxy.js', function() { client.on('message', function (msg) { expect(msg).to.be('Hello over websockets'); client.close(); - proxyServer.close(); + proxyServer._server.close(); destiny.close(); done(); }); @@ -229,7 +229,7 @@ describe('lib/http-proxy.js', function() { client.on('outgoing', function (data) { expect(data).to.be('Hello over websockets'); - proxyServer.close(); + proxyServer._server.close(); destiny.server.close(); done(); }); From c65ffbb976467dc1768983dcffe111d18e8f2db1 Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 9 Oct 2013 12:40:05 -0500 Subject: [PATCH 134/210] [tests] fixed inherits problem and listen for the correct event --- lib/http-proxy/index.js | 4 +- test/lib-http-proxy-test.js | 78 ++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 9776c65d2..17c96cf07 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -98,6 +98,8 @@ function ProxyServer(options) { }); } +require('util').inherits(ProxyServer, EE3); + ProxyServer.prototype.listen = function(port) { var self = this, closure = function(req, res) { self.web(req, res); }; @@ -135,5 +137,3 @@ ProxyServer.prototype.after = function(passName, callback) { this.passes.splice(i++, 0, callback); }; - -//require('util').inherits(ProxyServer, EE3); diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index dd3136e5e..9bb882d9b 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -119,7 +119,7 @@ describe('lib/http-proxy.js', function() { target: 'http://127.0.0.1:8080' }); - proxy.ee.on('http-proxy:outgoing:web:error', function (err) { + proxy.on('error', function (err) { expect(err).to.be.an(Error); expect(err.code).to.be('ECONNREFUSED'); proxyServer._server.close(); @@ -136,50 +136,50 @@ describe('lib/http-proxy.js', function() { }); }); - describe('#createProxyServer using the web-incoming passes', function () { - it('should emit events correclty', function(done) { - var proxy = httpProxy.createProxyServer({ - target: 'http://127.0.0.1:8080' - }), + // describe('#createProxyServer using the web-incoming passes', function () { + // it('should emit events correclty', function(done) { + // var proxy = httpProxy.createProxyServer({ + // target: 'http://127.0.0.1:8080' + // }), - proxyServer = proxy.listen('8081'), + // proxyServer = proxy.listen('8081'), - source = http.createServer(function(req, res) { - expect(req.method).to.eql('GET'); - expect(req.headers.host.split(':')[1]).to.eql('8081'); - res.writeHead(200, {'Content-Type': 'text/plain'}) - res.end('Hello from ' + source.address().port); - }), + // source = http.createServer(function(req, res) { + // expect(req.method).to.eql('GET'); + // expect(req.headers.host.split(':')[1]).to.eql('8081'); + // res.writeHead(200, {'Content-Type': 'text/plain'}) + // res.end('Hello from ' + source.address().port); + // }), - events = []; + // events = []; - source.listen('8080'); + // source.listen('8080'); - proxy.ee.on('http-proxy:**', function (uno, dos, tres) { - events.push(this.event); - }) + // proxy.ee.on('http-proxy:**', function (uno, dos, tres) { + // events.push(this.event); + // }) - http.request({ - hostname: '127.0.0.1', - port: '8081', - method: 'GET', - }, function(res) { - expect(res.statusCode).to.eql(200); - - res.on('data', function (data) { - expect(data.toString()).to.eql('Hello from 8080'); - }); - - res.on('end', function () { - expect(events).to.contain('http-proxy:outgoing:web:begin'); - expect(events).to.contain('http-proxy:outgoing:web:end'); - source.close(); - proxyServer._server.close(); - done(); - }); - }).end(); - }); - }); + // http.request({ + // hostname: '127.0.0.1', + // port: '8081', + // method: 'GET', + // }, function(res) { + // expect(res.statusCode).to.eql(200); + + // res.on('data', function (data) { + // expect(data.toString()).to.eql('Hello from 8080'); + // }); + + // res.on('end', function () { + // expect(events).to.contain('http-proxy:outgoing:web:begin'); + // expect(events).to.contain('http-proxy:outgoing:web:end'); + // source.close(); + // proxyServer._server.close(); + // done(); + // }); + // }).end(); + // }); + // }); describe('#createProxyServer using the ws-incoming passes', function () { it('should proxy the websockets stream', function (done) { From 86750c7e594c419dfae957aaf7e44e61e1d480e8 Mon Sep 17 00:00:00 2001 From: cronopio Date: Thu, 10 Oct 2013 11:04:17 -0500 Subject: [PATCH 135/210] [tests] throw error when no options, ALL TESTS PASSING! YAY --- lib/http-proxy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index bcaa36e9e..24efb4687 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -20,7 +20,7 @@ var http = require('http'), */ proxy.createProxyServer = proxy.createServer = function createProxyServer(options) { - /* if(!options) { + if(!options) { throw new Error([ "`options` is needed and it must have the following layout:", " ", @@ -37,7 +37,7 @@ proxy.createProxyServer = proxy.createServer = function createProxyServer(option " `options.target and `options.forward` cannot be ", " both missing " ].join("\n")); - } */ + } return new httpProxy.Server(options); }; From bbe2b2788a7ee3c74fd44fe88b6dcf213264436f Mon Sep 17 00:00:00 2001 From: James Manning Date: Sat, 19 Oct 2013 23:43:22 -0400 Subject: [PATCH 136/210] attempting to fix link to valid options properties Current link for 'valid properties are available here' goes to url: https://github.com/nodejitsu/node-http-proxy/blob/caronte/tree/master/lib/http-proxy.js#L26-L39 The url works fine if 'tree/master/' is removed, so this is trying to remove that part of the relative path. The same removal of 'tree/master/' is being made for the 'available here' link that is preceded by "When a request is proxied it follows two different pipelines" since it suffers the same issue. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c44fcd6d7..344d6c32c 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ proxies and load balancers. ### Core Concept A new proxy is created by calling `createProxyServer` and passing -an `options` object as argument ([valid properties are available here](tree/master/lib/http-proxy.js#L26-L39)) +an `options` object as argument ([valid properties are available here](lib/http-proxy.js#L26-L39)) ```javascript var httpProxy = require('http-proxy'); @@ -44,7 +44,7 @@ require('http').createServer(function(req, res) { }); ``` -When a request is proxied it follows two different pipelines ([available here](tree/master/lib/http-proxy/passes)) +When a request is proxied it follows two different pipelines ([available here](lib/http-proxy/passes)) which apply transformations to both the `req` and `res` object. The first pipeline (ingoing) is responsible for the creation and manipulation of the stream that connects your client to the target. The second pipeline (outgoing) is responsible for the creation and manipulation of the stream that, from your target, returns data From 1d1ee8858283d7c8984f1c1d6c5185b6822f9235 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 03:25:47 -0500 Subject: [PATCH 137/210] [tests] the options got a problem and this test probe that timeout is not being set --- test/lib-http-proxy-test.js | 38 ++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index 9bb882d9b..73ee11345 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -126,7 +126,7 @@ describe('lib/http-proxy.js', function() { done(); }) - var proxyServer = proxy.listen('8081'); + proxy.listen('8081'); http.request({ hostname: '127.0.0.1', @@ -136,6 +136,38 @@ describe('lib/http-proxy.js', function() { }); }); + describe('#createProxyServer setting the correct timeout value', function () { + it('should hang up the socket at the timeout', function (done) { + this.timeout(30); + var proxy = httpProxy.createProxyServer({ + target: 'http://127.0.0.1:8080', + timeout: 3 + }).listen('8081'); + + var source = http.createServer(function(req, res) { + setTimeout(function () { + res.end('At this point the socket should be closed'); + }, 5) + }); + + source.listen('8080'); + + var testReq = http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'GET', + }, function() {}); + + testReq.on('error', function (e) { + expect(e).to.be.an(Error); + expect(e.code).to.be.eql('ECONNRESET'); + done(); + }); + + testReq.end(); + }) + }) + // describe('#createProxyServer using the web-incoming passes', function () { // it('should emit events correclty', function(done) { // var proxy = httpProxy.createProxyServer({ @@ -244,3 +276,7 @@ describe('lib/http-proxy.js', function() { }); }) }); + +describe('#createProxyServer using the ws-incoming passes', function () { + it('should call the callback with the error'); +}) \ No newline at end of file From 90fb01d38ac5af7ef395547b24e985b6f63b4abc Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 03:26:54 -0500 Subject: [PATCH 138/210] [fix] fixed options and server reference to can access them from passes functions --- lib/http-proxy/index.js | 2 +- lib/http-proxy/passes/web-incoming.js | 31 ++++++++++++++++----------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 17c96cf07..ba3425eca 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -73,7 +73,7 @@ function createRightProxy(type) { * refer to the connection socket * pass(req, socket, options, head) */ - if(passes[i](req, res, cbl ? false : this, head, cbl)) { // passes can return a truthy value to halt the loop + if(passes[i].call(this, req, res, head, cbl)) { // passes can return a truthy value to halt the loop break; } } diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index b8f936d51..03725710f 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -23,12 +23,13 @@ web_o = Object.keys(web_o).map(function(pass) { * * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object - * @param {Object} Options Config object passed to the proxy * * @api private */ - function deleteLength(req, res, options) { + function deleteLength(req, res) { + // Now the options are stored on this + var options = this.options; if(req.method === 'DELETE' && !req.headers['content-length']) { req.headers['content-length'] = '0'; } @@ -39,12 +40,13 @@ web_o = Object.keys(web_o).map(function(pass) { * * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object - * @param {Object} Options Config object passed to the proxy * * @api private */ - function timeout(req, res, options) { + function timeout(req, res) { + // Now the options are stored on this + var options = this.options; if(options.timeout) { req.socket.setTimeout(options.timeout); } @@ -55,12 +57,13 @@ web_o = Object.keys(web_o).map(function(pass) { * * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object - * @param {Object} Options Config object passed to the proxy * * @api private */ - function XHeaders(req, res, options) { + function XHeaders(req, res) { + // Now the options are stored on this + var options = this.options; if(!options.xfwd) return; var values = { @@ -84,24 +87,26 @@ web_o = Object.keys(web_o).map(function(pass) { * * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object - * @param {Object} Options Config object passed to the proxy * * @api private */ - function stream(req, res, server, _, clb) { - if(server.options.forward) { + function stream(req, res, head, clb) { + var server = this; + // Now the options are stored on this + var options = this.options; + if(options.forward) { // If forward enable, so just pipe the request - var forwardReq = (server.options.forward.protocol === 'https:' ? https : http).request( - common.setupOutgoing(server.options.ssl || {}, server.options, req, 'forward') + var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req, 'forward') ); req.pipe(forwardReq); return res.end(); } // Request initalization - var proxyReq = (server.options.target.protocol === 'https:' ? https : http).request( - common.setupOutgoing(server.options.ssl || {}, server.options, req) + var proxyReq = (options.target.protocol === 'https:' ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req) ); // Error Handler From 9b3e1eb247df29d18ea299ff4ebb2f10eeb71269 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 03:46:30 -0500 Subject: [PATCH 139/210] [fix] fixed passes functions, now 'this' can be used and options are stored on 'this.options' --- lib/http-proxy/passes/ws-incoming.js | 9 +++++---- test/lib-http-proxy-test.js | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index 66c75d506..3e715c902 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -64,12 +64,13 @@ var passes = exports; * * @param {ClientRequest} Req Request object * @param {Socket} Websocket - * @param {Object} Options Config object passed to the proxy * * @api private */ - function XHeaders(req, socket, options) { + function XHeaders(req, socket) { + // Now the options are stored on this + var options = this.options; if(!options.xfwd) return; var values = { @@ -92,11 +93,11 @@ var passes = exports; * * @param {ClientRequest} Req Request object * @param {Socket} Websocket - * @param {Object} Options Config object passed to the proxy * * @api private */ - function stream(req, socket, server, head, clb) { + function stream(req, socket, head, clb) { + var server = this; common.setupSocket(socket); if (head && head.length) socket.unshift(head); diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index 73ee11345..a74276e82 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -122,7 +122,7 @@ describe('lib/http-proxy.js', function() { proxy.on('error', function (err) { expect(err).to.be.an(Error); expect(err.code).to.be('ECONNREFUSED'); - proxyServer._server.close(); + proxy._server.close(); done(); }) @@ -148,9 +148,7 @@ describe('lib/http-proxy.js', function() { setTimeout(function () { res.end('At this point the socket should be closed'); }, 5) - }); - - source.listen('8080'); + }).listen('8080'); var testReq = http.request({ hostname: '127.0.0.1', @@ -161,6 +159,8 @@ describe('lib/http-proxy.js', function() { testReq.on('error', function (e) { expect(e).to.be.an(Error); expect(e.code).to.be.eql('ECONNRESET'); + proxy._server.close(); + source.close(); done(); }); From 52ecd52ee5aa78603e44ba8d5ff9187410351622 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 03:59:14 -0500 Subject: [PATCH 140/210] [tests] fix test to use the new way to pass options --- lib/http-proxy/passes/web-incoming.js | 2 -- test/lib-http-proxy-passes-web-incoming-test.js | 6 +++--- test/lib-http-proxy-passes-ws-incoming-test.js | 6 +++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index 03725710f..ca74bfff1 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -28,8 +28,6 @@ web_o = Object.keys(web_o).map(function(pass) { */ function deleteLength(req, res) { - // Now the options are stored on this - var options = this.options; if(req.method === 'DELETE' && !req.headers['content-length']) { req.headers['content-length'] = '0'; } diff --git a/test/lib-http-proxy-passes-web-incoming-test.js b/test/lib-http-proxy-passes-web-incoming-test.js index 4269d5f8b..cd2bacd85 100644 --- a/test/lib-http-proxy-passes-web-incoming-test.js +++ b/test/lib-http-proxy-passes-web-incoming-test.js @@ -8,7 +8,7 @@ describe('lib/http-proxy/passes/web.js', function() { method: 'DELETE', headers: {} }; - httpProxy.deleteLength(stubRequest, {}, {}); + httpProxy.deleteLength(stubRequest, {}); expect(stubRequest.headers['content-length']).to.eql('0'); }) }); @@ -21,7 +21,7 @@ describe('lib/http-proxy/passes/web.js', function() { } } - httpProxy.timeout(stubRequest, {}, { timeout: 5000}); + httpProxy.timeout.call({ options: { timeout: 5000 }}, stubRequest, {}); expect(done).to.eql(5000); }); }); @@ -36,7 +36,7 @@ describe('lib/http-proxy/passes/web.js', function() { } it('set the correct x-forwarded-* headers', function () { - httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); + httpProxy.XHeaders.call({ options: { xfwd: true }}, stubRequest, {}); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); expect(stubRequest.headers['x-forwarded-proto']).to.be('http'); diff --git a/test/lib-http-proxy-passes-ws-incoming-test.js b/test/lib-http-proxy-passes-ws-incoming-test.js index 87dfcef94..ee61234c6 100644 --- a/test/lib-http-proxy-passes-ws-incoming-test.js +++ b/test/lib-http-proxy-passes-ws-incoming-test.js @@ -107,7 +107,7 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { describe('#XHeaders', function () { it('return if no forward request', function () { - var returnValue = httpProxy.XHeaders({}, {}, {}); + var returnValue = httpProxy.XHeaders.call({ options: {}}, {}, {}); expect(returnValue).to.be(undefined); }); @@ -119,7 +119,7 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { }, headers: {} } - httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); + httpProxy.XHeaders.call({ options: { xfwd: true }}, stubRequest, {}); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); expect(stubRequest.headers['x-forwarded-proto']).to.be('ws'); @@ -136,7 +136,7 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { }, headers: {} }; - httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); + httpProxy.XHeaders.call({ options: { xfwd: true }}, stubRequest, {}); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.3'); expect(stubRequest.headers['x-forwarded-port']).to.be('8181'); expect(stubRequest.headers['x-forwarded-proto']).to.be('wss'); From 2bf20d61d53201e9820c5f9215e641fcf88f5172 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 14:44:49 -0500 Subject: [PATCH 141/210] Revert "[tests] fix test to use the new way to pass options" This reverts commit 52ecd52ee5aa78603e44ba8d5ff9187410351622. --- lib/http-proxy/passes/web-incoming.js | 2 ++ test/lib-http-proxy-passes-web-incoming-test.js | 6 +++--- test/lib-http-proxy-passes-ws-incoming-test.js | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index ca74bfff1..03725710f 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -28,6 +28,8 @@ web_o = Object.keys(web_o).map(function(pass) { */ function deleteLength(req, res) { + // Now the options are stored on this + var options = this.options; if(req.method === 'DELETE' && !req.headers['content-length']) { req.headers['content-length'] = '0'; } diff --git a/test/lib-http-proxy-passes-web-incoming-test.js b/test/lib-http-proxy-passes-web-incoming-test.js index cd2bacd85..4269d5f8b 100644 --- a/test/lib-http-proxy-passes-web-incoming-test.js +++ b/test/lib-http-proxy-passes-web-incoming-test.js @@ -8,7 +8,7 @@ describe('lib/http-proxy/passes/web.js', function() { method: 'DELETE', headers: {} }; - httpProxy.deleteLength(stubRequest, {}); + httpProxy.deleteLength(stubRequest, {}, {}); expect(stubRequest.headers['content-length']).to.eql('0'); }) }); @@ -21,7 +21,7 @@ describe('lib/http-proxy/passes/web.js', function() { } } - httpProxy.timeout.call({ options: { timeout: 5000 }}, stubRequest, {}); + httpProxy.timeout(stubRequest, {}, { timeout: 5000}); expect(done).to.eql(5000); }); }); @@ -36,7 +36,7 @@ describe('lib/http-proxy/passes/web.js', function() { } it('set the correct x-forwarded-* headers', function () { - httpProxy.XHeaders.call({ options: { xfwd: true }}, stubRequest, {}); + httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); expect(stubRequest.headers['x-forwarded-proto']).to.be('http'); diff --git a/test/lib-http-proxy-passes-ws-incoming-test.js b/test/lib-http-proxy-passes-ws-incoming-test.js index ee61234c6..87dfcef94 100644 --- a/test/lib-http-proxy-passes-ws-incoming-test.js +++ b/test/lib-http-proxy-passes-ws-incoming-test.js @@ -107,7 +107,7 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { describe('#XHeaders', function () { it('return if no forward request', function () { - var returnValue = httpProxy.XHeaders.call({ options: {}}, {}, {}); + var returnValue = httpProxy.XHeaders({}, {}, {}); expect(returnValue).to.be(undefined); }); @@ -119,7 +119,7 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { }, headers: {} } - httpProxy.XHeaders.call({ options: { xfwd: true }}, stubRequest, {}); + httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); expect(stubRequest.headers['x-forwarded-proto']).to.be('ws'); @@ -136,7 +136,7 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { }, headers: {} }; - httpProxy.XHeaders.call({ options: { xfwd: true }}, stubRequest, {}); + httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.3'); expect(stubRequest.headers['x-forwarded-port']).to.be('8181'); expect(stubRequest.headers['x-forwarded-proto']).to.be('wss'); From babdf531fecd32f9af0963902909fcfa2cd374f1 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 14:45:03 -0500 Subject: [PATCH 142/210] Revert "[fix] fixed options and server reference to can access them from passes functions" This reverts commit 90fb01d38ac5af7ef395547b24e985b6f63b4abc. --- lib/http-proxy/index.js | 2 +- lib/http-proxy/passes/web-incoming.js | 31 +++++++++++---------------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index ba3425eca..17c96cf07 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -73,7 +73,7 @@ function createRightProxy(type) { * refer to the connection socket * pass(req, socket, options, head) */ - if(passes[i].call(this, req, res, head, cbl)) { // passes can return a truthy value to halt the loop + if(passes[i](req, res, cbl ? false : this, head, cbl)) { // passes can return a truthy value to halt the loop break; } } diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index 03725710f..b8f936d51 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -23,13 +23,12 @@ web_o = Object.keys(web_o).map(function(pass) { * * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy * * @api private */ - function deleteLength(req, res) { - // Now the options are stored on this - var options = this.options; + function deleteLength(req, res, options) { if(req.method === 'DELETE' && !req.headers['content-length']) { req.headers['content-length'] = '0'; } @@ -40,13 +39,12 @@ web_o = Object.keys(web_o).map(function(pass) { * * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy * * @api private */ - function timeout(req, res) { - // Now the options are stored on this - var options = this.options; + function timeout(req, res, options) { if(options.timeout) { req.socket.setTimeout(options.timeout); } @@ -57,13 +55,12 @@ web_o = Object.keys(web_o).map(function(pass) { * * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy * * @api private */ - function XHeaders(req, res) { - // Now the options are stored on this - var options = this.options; + function XHeaders(req, res, options) { if(!options.xfwd) return; var values = { @@ -87,26 +84,24 @@ web_o = Object.keys(web_o).map(function(pass) { * * @param {ClientRequest} Req Request object * @param {IncomingMessage} Res Response object + * @param {Object} Options Config object passed to the proxy * * @api private */ - function stream(req, res, head, clb) { - var server = this; - // Now the options are stored on this - var options = this.options; - if(options.forward) { + function stream(req, res, server, _, clb) { + if(server.options.forward) { // If forward enable, so just pipe the request - var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req, 'forward') + var forwardReq = (server.options.forward.protocol === 'https:' ? https : http).request( + common.setupOutgoing(server.options.ssl || {}, server.options, req, 'forward') ); req.pipe(forwardReq); return res.end(); } // Request initalization - var proxyReq = (options.target.protocol === 'https:' ? https : http).request( - common.setupOutgoing(options.ssl || {}, options, req) + var proxyReq = (server.options.target.protocol === 'https:' ? https : http).request( + common.setupOutgoing(server.options.ssl || {}, server.options, req) ); // Error Handler From 5e130de8548ad41b821da49299b4fd1c9536c5f0 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 14:49:39 -0500 Subject: [PATCH 143/210] Revert "[fix] fixed passes functions, now 'this' can be used and options are stored on 'this.options'" This reverts commit 9b3e1eb247df29d18ea299ff4ebb2f10eeb71269. --- lib/http-proxy/passes/ws-incoming.js | 9 ++++----- test/lib-http-proxy-test.js | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index 3e715c902..66c75d506 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -64,13 +64,12 @@ var passes = exports; * * @param {ClientRequest} Req Request object * @param {Socket} Websocket + * @param {Object} Options Config object passed to the proxy * * @api private */ - function XHeaders(req, socket) { - // Now the options are stored on this - var options = this.options; + function XHeaders(req, socket, options) { if(!options.xfwd) return; var values = { @@ -93,11 +92,11 @@ var passes = exports; * * @param {ClientRequest} Req Request object * @param {Socket} Websocket + * @param {Object} Options Config object passed to the proxy * * @api private */ - function stream(req, socket, head, clb) { - var server = this; + function stream(req, socket, server, head, clb) { common.setupSocket(socket); if (head && head.length) socket.unshift(head); diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index a74276e82..73ee11345 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -122,7 +122,7 @@ describe('lib/http-proxy.js', function() { proxy.on('error', function (err) { expect(err).to.be.an(Error); expect(err.code).to.be('ECONNREFUSED'); - proxy._server.close(); + proxyServer._server.close(); done(); }) @@ -148,7 +148,9 @@ describe('lib/http-proxy.js', function() { setTimeout(function () { res.end('At this point the socket should be closed'); }, 5) - }).listen('8080'); + }); + + source.listen('8080'); var testReq = http.request({ hostname: '127.0.0.1', @@ -159,8 +161,6 @@ describe('lib/http-proxy.js', function() { testReq.on('error', function (e) { expect(e).to.be.an(Error); expect(e.code).to.be.eql('ECONNRESET'); - proxy._server.close(); - source.close(); done(); }); From 5d66ce11bb7eef7e704a2de2c0ef3b5f754843e9 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 14:51:45 -0500 Subject: [PATCH 144/210] [fix] minor typo --- test/lib-http-proxy-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index 73ee11345..c592728d1 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -122,7 +122,7 @@ describe('lib/http-proxy.js', function() { proxy.on('error', function (err) { expect(err).to.be.an(Error); expect(err.code).to.be('ECONNREFUSED'); - proxyServer._server.close(); + proxy._server.close(); done(); }) From 0bfb9be418926f2113489e92504038127d4c04bb Mon Sep 17 00:00:00 2001 From: mmoulton Date: Mon, 21 Oct 2013 10:05:06 -0700 Subject: [PATCH 145/210] Fixed issue where error callback would not invoke, including new test cases. Added req/res values to error events. Conflicts: lib/http-proxy/passes/web-incoming.js --- lib/http-proxy/passes/web-incoming.js | 9 ++- test/lib-http-proxy-integration-test.js | 92 +++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 test/lib-http-proxy-integration-test.js diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index b8f936d51..eceb1636e 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -106,11 +106,10 @@ web_o = Object.keys(web_o).map(function(pass) { // Error Handler proxyReq.on('error', function(err){ - if(server) { - server.emit('error', err); - } - else {  - clb(err); + if (clb) { + clb(err); + } else { + server.emit('error', err, req, res); } }); diff --git a/test/lib-http-proxy-integration-test.js b/test/lib-http-proxy-integration-test.js new file mode 100644 index 000000000..f6ea70693 --- /dev/null +++ b/test/lib-http-proxy-integration-test.js @@ -0,0 +1,92 @@ +var httpProxy = require('../lib/http-proxy'), + expect = require('expect.js'), + http = require('http'); + + +describe('lib/http-proxy.js', function() { + + describe('#createProxyServer with target options for integration into existing server', function () { + it('should proxy the request using the web proxy handler', function (done) { + var proxy = httpProxy.createProxyServer({ + target: 'http://127.0.0.1:8080' + }); + + function requestHandler(req, res) { + proxy.web(req, res); + } + + var proxyServer = http.createServer(requestHandler); + + var source = http.createServer(function(req, res) { + source.close(); + proxyServer.close(); + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql('8082'); + done(); + }); + + proxyServer.listen('8082'); + source.listen('8080'); + + http.request('http://127.0.0.1:8082', function() {}).end(); + }); + }); + + describe('#createProxyServer() for integration into existing server with error response', function () { + it('should proxy the request and handle error via callback', function(done) { + var proxy = httpProxy.createProxyServer({ + target: 'http://127.0.0.1:8080' + }); + + var proxyServer = http.createServer(requestHandler); + + function requestHandler(req, res) { + proxy.web(req, res, function (err) { + proxyServer.close(); + expect(err).to.be.an(Error); + expect(err.code).to.be('ECONNREFUSED'); + done(); + }); + } + + proxyServer.listen('8082'); + + http.request({ + hostname: '127.0.0.1', + port: '8082', + method: 'GET', + }, function() {}).end(); + }); + + it('should proxy the request and handle error event listener', function(done) { + var proxy = httpProxy.createProxyServer({ + target: 'http://127.0.0.1:8080' + }); + + var proxyServer = http.createServer(requestHandler); + + function requestHandler(req, res) { + proxy.once('error', function (err, errReq, errRes) { + proxyServer.close(); + expect(err).to.be.an(Error); + expect(errReq).to.be.equal(req); + expect(errRes).to.be.equal(res); + expect(err.code).to.be('ECONNREFUSED'); + done(); + }); + + proxy.web(req, res); + } + + proxyServer.listen('8082'); + + http.request({ + hostname: '127.0.0.1', + port: '8082', + method: 'GET', + }, function() {}).end(); + }); + + }); + +}); \ No newline at end of file From 7c72f3b407a084a896e420c23ababc3e9357feca Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 21:11:19 -0500 Subject: [PATCH 146/210] [tests] move contributions of @mmoulton to correct place --- ...lib-http-proxy-passes-web-incoming-test.js | 93 ++++++++++++++++++- 1 file changed, 88 insertions(+), 5 deletions(-) diff --git a/test/lib-http-proxy-passes-web-incoming-test.js b/test/lib-http-proxy-passes-web-incoming-test.js index 4269d5f8b..19fa669b7 100644 --- a/test/lib-http-proxy-passes-web-incoming-test.js +++ b/test/lib-http-proxy-passes-web-incoming-test.js @@ -1,5 +1,7 @@ -var httpProxy = require('../lib/http-proxy/passes/web-incoming'), - expect = require('expect.js'); +var webPasses = require('../lib/http-proxy/passes/web-incoming'), + httpProxy = require('../lib/http-proxy'), + expect = require('expect.js'), + http = require('http'); describe('lib/http-proxy/passes/web.js', function() { describe('#deleteLength', function() { @@ -8,7 +10,7 @@ describe('lib/http-proxy/passes/web.js', function() { method: 'DELETE', headers: {} }; - httpProxy.deleteLength(stubRequest, {}, {}); + webPasses.deleteLength(stubRequest, {}, {}); expect(stubRequest.headers['content-length']).to.eql('0'); }) }); @@ -21,7 +23,7 @@ describe('lib/http-proxy/passes/web.js', function() { } } - httpProxy.timeout(stubRequest, {}, { timeout: 5000}); + webPasses.timeout(stubRequest, {}, { timeout: 5000}); expect(done).to.eql(5000); }); }); @@ -36,10 +38,91 @@ describe('lib/http-proxy/passes/web.js', function() { } it('set the correct x-forwarded-* headers', function () { - httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); + webPasses.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); expect(stubRequest.headers['x-forwarded-port']).to.be('8080'); expect(stubRequest.headers['x-forwarded-proto']).to.be('http'); }); }); }); + +describe('#createProxyServer.web() using own http server', function () { + it('should proxy the request using the web proxy handler', function (done) { + var proxy = httpProxy.createProxyServer({ + target: 'http://127.0.0.1:8080' + }); + + function requestHandler(req, res) { + proxy.web(req, res); + } + + var proxyServer = http.createServer(requestHandler); + + var source = http.createServer(function(req, res) { + source.close(); + proxyServer.close(); + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql('8081'); + done(); + }); + + proxyServer.listen('8081'); + source.listen('8080'); + + http.request('http://127.0.0.1:8081', function() {}).end(); + }); + + it('should proxy the request and handle error via callback', function(done) { + var proxy = httpProxy.createProxyServer({ + target: 'http://127.0.0.1:8080' + }); + + var proxyServer = http.createServer(requestHandler); + + function requestHandler(req, res) { + proxy.web(req, res, function (err) { + proxyServer.close(); + expect(err).to.be.an(Error); + expect(err.code).to.be('ECONNREFUSED'); + done(); + }); + } + + proxyServer.listen('8081'); + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'GET', + }, function() {}).end(); + }); + + it('should proxy the request and handle error via event listener', function(done) { + var proxy = httpProxy.createProxyServer({ + target: 'http://127.0.0.1:8080' + }); + + var proxyServer = http.createServer(requestHandler); + + function requestHandler(req, res) { + proxy.once('error', function (err, errReq, errRes) { + proxyServer.close(); + expect(err).to.be.an(Error); + expect(errReq).to.be.equal(req); + expect(errRes).to.be.equal(res); + expect(err.code).to.be('ECONNREFUSED'); + done(); + }); + + proxy.web(req, res); + } + + proxyServer.listen('8081'); + + http.request({ + hostname: '127.0.0.1', + port: '8081', + method: 'GET', + }, function() {}).end(); + }); +}); \ No newline at end of file From cc09ae6a345cfde1689e1d8731c5822675c59d4d Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 21:19:29 -0500 Subject: [PATCH 147/210] [fix] use the correct arguments order --- lib/http-proxy/index.js | 2 +- lib/http-proxy/passes/web-incoming.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 17c96cf07..7861503dc 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -73,7 +73,7 @@ function createRightProxy(type) { * refer to the connection socket * pass(req, socket, options, head) */ - if(passes[i](req, res, cbl ? false : this, head, cbl)) { // passes can return a truthy value to halt the loop + if(passes[i](req, res, this.options, head, cbl ? false : this, cbl)) { // passes can return a truthy value to halt the loop break; } } diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index eceb1636e..a3516aba1 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -89,19 +89,19 @@ web_o = Object.keys(web_o).map(function(pass) { * @api private */ - function stream(req, res, server, _, clb) { - if(server.options.forward) { + function stream(req, res, options, head, server, clb) { + if(options.forward) { // If forward enable, so just pipe the request - var forwardReq = (server.options.forward.protocol === 'https:' ? https : http).request( - common.setupOutgoing(server.options.ssl || {}, server.options, req, 'forward') + var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req, 'forward') ); req.pipe(forwardReq); return res.end(); } // Request initalization - var proxyReq = (server.options.target.protocol === 'https:' ? https : http).request( - common.setupOutgoing(server.options.ssl || {}, server.options, req) + var proxyReq = (options.target.protocol === 'https:' ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req) ); // Error Handler From 881c7e62e0bef7b4b9f81b6fd121f7ad6641bd77 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 21:22:39 -0500 Subject: [PATCH 148/210] [tests] this file is not necessary anymore --- test/lib-http-proxy-integration-test.js | 92 ------------------------- 1 file changed, 92 deletions(-) delete mode 100644 test/lib-http-proxy-integration-test.js diff --git a/test/lib-http-proxy-integration-test.js b/test/lib-http-proxy-integration-test.js deleted file mode 100644 index f6ea70693..000000000 --- a/test/lib-http-proxy-integration-test.js +++ /dev/null @@ -1,92 +0,0 @@ -var httpProxy = require('../lib/http-proxy'), - expect = require('expect.js'), - http = require('http'); - - -describe('lib/http-proxy.js', function() { - - describe('#createProxyServer with target options for integration into existing server', function () { - it('should proxy the request using the web proxy handler', function (done) { - var proxy = httpProxy.createProxyServer({ - target: 'http://127.0.0.1:8080' - }); - - function requestHandler(req, res) { - proxy.web(req, res); - } - - var proxyServer = http.createServer(requestHandler); - - var source = http.createServer(function(req, res) { - source.close(); - proxyServer.close(); - expect(req.method).to.eql('GET'); - expect(req.headers.host.split(':')[1]).to.eql('8082'); - done(); - }); - - proxyServer.listen('8082'); - source.listen('8080'); - - http.request('http://127.0.0.1:8082', function() {}).end(); - }); - }); - - describe('#createProxyServer() for integration into existing server with error response', function () { - it('should proxy the request and handle error via callback', function(done) { - var proxy = httpProxy.createProxyServer({ - target: 'http://127.0.0.1:8080' - }); - - var proxyServer = http.createServer(requestHandler); - - function requestHandler(req, res) { - proxy.web(req, res, function (err) { - proxyServer.close(); - expect(err).to.be.an(Error); - expect(err.code).to.be('ECONNREFUSED'); - done(); - }); - } - - proxyServer.listen('8082'); - - http.request({ - hostname: '127.0.0.1', - port: '8082', - method: 'GET', - }, function() {}).end(); - }); - - it('should proxy the request and handle error event listener', function(done) { - var proxy = httpProxy.createProxyServer({ - target: 'http://127.0.0.1:8080' - }); - - var proxyServer = http.createServer(requestHandler); - - function requestHandler(req, res) { - proxy.once('error', function (err, errReq, errRes) { - proxyServer.close(); - expect(err).to.be.an(Error); - expect(errReq).to.be.equal(req); - expect(errRes).to.be.equal(res); - expect(err.code).to.be('ECONNREFUSED'); - done(); - }); - - proxy.web(req, res); - } - - proxyServer.listen('8082'); - - http.request({ - hostname: '127.0.0.1', - port: '8082', - method: 'GET', - }, function() {}).end(); - }); - - }); - -}); \ No newline at end of file From 02df9a33c5cce17ea32a892017acbe5ce57ab2e5 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 23:22:32 -0500 Subject: [PATCH 149/210] [fix] fix the correct order of arguments in ws-incoming passes --- lib/http-proxy/passes/ws-incoming.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index 66c75d506..ebdbe2ab0 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -96,22 +96,21 @@ var passes = exports; * * @api private */ - function stream(req, socket, server, head, clb) { + function stream(req, socket, options, server, head, clb) { common.setupSocket(socket); if (head && head.length) socket.unshift(head); - var proxyReq = (~['https:', 'wss:'].indexOf(server.options.target.protocol) ? https : http).request( - common.setupOutgoing(server.options.ssl || {}, server.options, req) + var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( + common.setupOutgoing(options.ssl || {}, options, req) ); // Error Handler proxyReq.on('error', function(err){ - if(server) { - server.emit('error', err); - } - else { + if (clb) { clb(err); + } else { + server.emit('error', err, req, res); } }); From d60353f80bbbcba128a2c51066e107365270e878 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 21 Oct 2013 23:22:59 -0500 Subject: [PATCH 150/210] [tests] tests fixed --- test/lib-http-proxy-test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index c592728d1..cc772b863 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -161,6 +161,8 @@ describe('lib/http-proxy.js', function() { testReq.on('error', function (e) { expect(e).to.be.an(Error); expect(e.code).to.be.eql('ECONNRESET'); + proxy._server.close(); + source.close(); done(); }); From c75d06c5f92eb7c814deb49bb33cf9fffc632d97 Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 22 Oct 2013 15:57:52 -0500 Subject: [PATCH 151/210] [tests] now each test use a different port to avoid some slow opening and closing ports --- test/lib-http-proxy-test.js | 83 ++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index cc772b863..83e8c5d6f 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -5,6 +5,17 @@ var httpProxy = require('../lib/http-proxy'), io = require('socket.io'), ioClient = require('socket.io-client'); +// +// Expose a port number generator. +// thanks to @3rd-Eden +// +var initialPort = 1024, gen = {}; +Object.defineProperty(gen, 'port', { + get: function get() { + return initialPort++; + } +}); + describe('lib/http-proxy.js', function() { describe('#createProxyServer', function() { @@ -32,44 +43,45 @@ describe('lib/http-proxy.js', function() { describe('#createProxyServer with forward options and using web-incoming passes', function () { it('should pipe the request using web-incoming#stream method', function (done) { + var ports = { source: gen.port, proxy: gen.port }; var proxy = httpProxy.createProxyServer({ - forward: 'http://127.0.0.1:8080' - }).listen('8081') + forward: 'http://127.0.0.1:' + ports.source + }).listen(ports.proxy); var source = http.createServer(function(req, res) { expect(req.method).to.eql('GET'); - expect(req.headers.host.split(':')[1]).to.eql('8081'); + expect(req.headers.host.split(':')[1]).to.eql(ports.proxy); source.close(); proxy._server.close(); done(); }); - source.listen('8080'); - - http.request('http://127.0.0.1:8081', function() {}).end(); + source.listen(ports.source); + http.request('http://127.0.0.1:' + ports.proxy, function() {}).end(); }) }); describe('#createProxyServer using the web-incoming passes', function () { it('should make the request on pipe and finish it', function(done) { + var ports = { source: gen.port, proxy: gen.port }; var proxy = httpProxy.createProxyServer({ - target: 'http://127.0.0.1:8080' - }).listen('8081'); + target: 'http://127.0.0.1:' + ports.source + }).listen(ports.proxy); var source = http.createServer(function(req, res) { expect(req.method).to.eql('POST'); expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1'); - expect(req.headers.host.split(':')[1]).to.eql('8081'); + expect(req.headers.host.split(':')[1]).to.eql(ports.proxy); source.close(); proxy._server.close(); done(); }); - source.listen('8080'); + source.listen(ports.source); http.request({ hostname: '127.0.0.1', - port: '8081', + port: ports.proxy, method: 'POST', headers: { 'x-forwarded-for': '127.0.0.1' @@ -80,28 +92,29 @@ describe('lib/http-proxy.js', function() { describe('#createProxyServer using the web-incoming passes', function () { it('should make the request, handle response and finish it', function(done) { + var ports = { source: gen.port, proxy: gen.port }; var proxy = httpProxy.createProxyServer({ - target: 'http://127.0.0.1:8080' - }).listen('8081'); + target: 'http://127.0.0.1:' + ports.source + }).listen(ports.proxy); var source = http.createServer(function(req, res) { expect(req.method).to.eql('GET'); - expect(req.headers.host.split(':')[1]).to.eql('8081'); + expect(req.headers.host.split(':')[1]).to.eql(ports.proxy); res.writeHead(200, {'Content-Type': 'text/plain'}) res.end('Hello from ' + source.address().port); }); - source.listen('8080'); + source.listen(ports.source); http.request({ hostname: '127.0.0.1', - port: '8081', - method: 'GET', + port: ports.proxy, + method: 'GET' }, function(res) { expect(res.statusCode).to.eql(200); res.on('data', function (data) { - expect(data.toString()).to.eql('Hello from 8080'); + expect(data.toString()).to.eql('Hello from ' + ports.source); }); res.on('end', function () { @@ -115,8 +128,9 @@ describe('lib/http-proxy.js', function() { describe('#createProxyServer() method with error response', function () { it('should make the request and emit the error event', function(done) { + var ports = { source: gen.port, proxy: gen.port }; var proxy = httpProxy.createProxyServer({ - target: 'http://127.0.0.1:8080' + target: 'http://127.0.0.1:' + ports.source }); proxy.on('error', function (err) { @@ -126,11 +140,11 @@ describe('lib/http-proxy.js', function() { done(); }) - proxy.listen('8081'); + proxy.listen(ports.proxy); http.request({ hostname: '127.0.0.1', - port: '8081', + port: ports.proxy, method: 'GET', }, function() {}).end(); }); @@ -139,10 +153,11 @@ describe('lib/http-proxy.js', function() { describe('#createProxyServer setting the correct timeout value', function () { it('should hang up the socket at the timeout', function (done) { this.timeout(30); + var ports = { source: gen.port, proxy: gen.port }; var proxy = httpProxy.createProxyServer({ - target: 'http://127.0.0.1:8080', + target: 'http://127.0.0.1:' + ports.source, timeout: 3 - }).listen('8081'); + }).listen(ports.proxy); var source = http.createServer(function(req, res) { setTimeout(function () { @@ -150,11 +165,11 @@ describe('lib/http-proxy.js', function() { }, 5) }); - source.listen('8080'); + source.listen(ports.source); var testReq = http.request({ hostname: '127.0.0.1', - port: '8081', + port: ports.proxy, method: 'GET', }, function() {}); @@ -217,13 +232,14 @@ describe('lib/http-proxy.js', function() { describe('#createProxyServer using the ws-incoming passes', function () { it('should proxy the websockets stream', function (done) { + var ports = { source: gen.port, proxy: gen.port }; var proxy = httpProxy.createProxyServer({ - target: 'ws://127.0.0.1:8080', + target: 'ws://127.0.0.1:' + ports.source, ws: true }), - proxyServer = proxy.listen('8081'), - destiny = new ws.Server({ port: 8080 }, function () { - var client = new ws('ws://127.0.0.1:8081'); + proxyServer = proxy.listen(ports.proxy), + destiny = new ws.Server({ port: ports.source }, function () { + var client = new ws('ws://127.0.0.1:' + ports.proxy); client.on('open', function () { client.send('hello there'); @@ -249,13 +265,14 @@ describe('lib/http-proxy.js', function() { describe('#createProxyServer using the ws-incoming passes', function () { it('should proxy a socket.io stream', function (done) { + var ports = { source: gen.port, proxy: gen.port }; var proxy = httpProxy.createProxyServer({ - target: 'ws://127.0.0.1:8080', + target: 'ws://127.0.0.1:' + ports.source, ws: true }), - proxyServer = proxy.listen('8081'), - destiny = io.listen(8080, function () { - var client = ioClient.connect('ws://127.0.0.1:8081'); + proxyServer = proxy.listen(ports.proxy), + destiny = io.listen(ports.source, function () { + var client = ioClient.connect('ws://127.0.0.1:' + ports.proxy); client.on('connect', function () { client.emit('incoming', 'hello there'); From 920f1e7707aa1751577533cd368529f8a704d7af Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 22 Oct 2013 16:09:11 -0500 Subject: [PATCH 152/210] [tests] this test is already in web-incoming tests --- test/lib-http-proxy-test.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index 83e8c5d6f..26f9d34b5 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -294,8 +294,4 @@ describe('lib/http-proxy.js', function() { }) }); }) -}); - -describe('#createProxyServer using the ws-incoming passes', function () { - it('should call the callback with the error'); -}) \ No newline at end of file +}); \ No newline at end of file From a1b25a123b4ff71e731f9beb27c5e078acfead65 Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 22 Oct 2013 17:04:18 -0500 Subject: [PATCH 153/210] [examples] update the error-handling example using the new error handle way --- examples/error-handling.js | 43 ++++++++++++++++++++++---------------- examples/stand-alone.js | 2 +- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/examples/error-handling.js b/examples/error-handling.js index f646a8de4..76a09d955 100644 --- a/examples/error-handling.js +++ b/examples/error-handling.js @@ -1,26 +1,33 @@ -var httpProxy = require('../index'); +var httpProxy = require('../lib/http-proxy'), + http = require('http'); /* * Create your proxy server */ -var proxyServer = httpProxy.createProxyServer({target:'http://localhost:30404', ws:true}); +var proxy = httpProxy.createProxyServer({target:'http://localhost:30404', ws:true}); -// Register an error handler for web requests -proxyServer.ee.on("http-proxy:outgoing:web:error", function(err, req, res){ - res.writeHead(502); - res.end("There was an error proxying your request"); -}); +var proxyServer = http.createServer(requestHandler); -// Register an error handler for web-socket requests -proxyServer.ee.on("http-proxy:outgoing:ws:error", function(err, req, socket, head){ - socket.close(); -}); - -// You may also use a wild card -proxyServer.ee.on("*:*:*:error", function(err, req){ - console.log("The error event '" + this.event + "' happened errno: " + err.errno); -}); +function requestHandler(req, res) { + // Pass a callback to the web proxy method + // and catch the error there. + proxy.web(req, res, function (err) { + // Now you can get the err + // and handle it by your self + // if (err) throw err; + res.writeHead(502); + res.end("There was an error proxying your request"); + }); + // In a websocket request case + req.on('upgrade', function (req, socket, head) { + proxy.ws(req, socket, head, function (err) { + // Now you can get the err + // and handle it by your self + // if (err) throw err; + socket.close(); + }) + }) +} console.log("Proxy server is listening on port 8000"); -proxyServer.listen(8000); - +proxyServer.listen(8000) \ No newline at end of file diff --git a/examples/stand-alone.js b/examples/stand-alone.js index 3bf6ddccf..e5239c460 100644 --- a/examples/stand-alone.js +++ b/examples/stand-alone.js @@ -1,5 +1,5 @@ var http = require('http'), - httpProxy = require('http-proxy'); + httpProxy = require('../lib/http-proxy'); // // Create your proxy server // From e3f8d5fdbe1ebc4f04188d95bbef768d09718d2c Mon Sep 17 00:00:00 2001 From: yawnt Date: Sat, 26 Oct 2013 17:20:30 +0200 Subject: [PATCH 154/210] [feature] add buffer support --- lib/http-proxy/passes/web-incoming.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index a3516aba1..97329348a 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -106,6 +106,7 @@ web_o = Object.keys(web_o).map(function(pass) { // Error Handler proxyReq.on('error', function(err){ + if(options.buffer) { options.buffer.destroy(); } if (clb) { clb(err); } else { @@ -123,6 +124,10 @@ web_o = Object.keys(web_o).map(function(pass) { proxyRes.pipe(res); }); + if(options.buffer) { + options.buffer.resume(); + } + //proxyReq.end(); } From 33a2462d28c7d1fa26b03bcf290242ff7cd83e7a Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 28 Oct 2013 13:18:21 -0500 Subject: [PATCH 155/210] [wip] Initial HTTPS->HTTP test, updated https-secure example. Work in progress, need to add more https tests --- examples/https-secure.js | 2 +- test/fixtures/agent2-cert.pem | 13 +++++++ test/fixtures/agent2-csr.pem | 10 ++++++ test/fixtures/agent2-key.pem | 9 +++++ test/fixtures/agent2.cnf | 19 ++++++++++ test/lib-https-proxy-test.js | 65 +++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/agent2-cert.pem create mode 100644 test/fixtures/agent2-csr.pem create mode 100644 test/fixtures/agent2-key.pem create mode 100644 test/fixtures/agent2.cnf create mode 100644 test/lib-https-proxy-test.js diff --git a/examples/https-secure.js b/examples/https-secure.js index 4ddbe8d23..72bcbb625 100644 --- a/examples/https-secure.js +++ b/examples/https-secure.js @@ -1,4 +1,4 @@ -var httpProxy = require('http-proxy'), +var httpProxy = require('../lib/http-proxy'), https = require('https'); /* * Create your proxy server pointing to a secure domain diff --git a/test/fixtures/agent2-cert.pem b/test/fixtures/agent2-cert.pem new file mode 100644 index 000000000..8e4354db4 --- /dev/null +++ b/test/fixtures/agent2-cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB7DCCAZYCCQC7gs0MDNn6MTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJV +UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO +BgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEgMB4GCSqGSIb3DQEJARYR +cnlAdGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEy +WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYD +VQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEg +MB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEF +AANLADBIAkEAyXb8FrRdKbhrKLgLSsn61i1C7w7fVVVd7OQsmV/7p9WB2lWFiDlC +WKGU9SiIz/A6wNZDUAuc2E+VwtpCT561AQIDAQABMA0GCSqGSIb3DQEBBQUAA0EA +C8HzpuNhFLCI3A5KkBS5zHAQax6TFUOhbpBCR0aTDbJ6F1liDTK1lmU/BjvPoj+9 +1LHwrmh29rK8kBPEjmymCQ== +-----END CERTIFICATE----- diff --git a/test/fixtures/agent2-csr.pem b/test/fixtures/agent2-csr.pem new file mode 100644 index 000000000..a670c4c63 --- /dev/null +++ b/test/fixtures/agent2-csr.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBXTCCAQcCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH +EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD +EwZhZ2VudDIxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMFwwDQYJ +KoZIhvcNAQEBBQADSwAwSAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf ++6fVgdpVhYg5QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAaAlMCMGCSqG +SIb3DQEJBzEWExRBIGNoYWxsZW5nZSBwYXNzd29yZDANBgkqhkiG9w0BAQUFAANB +AJnll2pt5l0pzskQSpjjLVTlFDFmJr/AZ3UK8v0WxBjYjCe5Jx4YehkChpxIyDUm +U3J9q9MDUf0+Y2+EGkssFfk= +-----END CERTIFICATE REQUEST----- diff --git a/test/fixtures/agent2-key.pem b/test/fixtures/agent2-key.pem new file mode 100644 index 000000000..522903c63 --- /dev/null +++ b/test/fixtures/agent2-key.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOgIBAAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf+6fVgdpVhYg5 +QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAQJBAMT6Bf34+UHKY1ObpsbH +9u2jsVblFq1rWvs8GPMY6oertzvwm3DpuSUp7PTgOB1nLTLYtCERbQ4ovtN8tn3p +OHUCIQDzIEGsoCr5vlxXvy2zJwu+fxYuhTZWMVuo1397L0VyhwIhANQh+yzqUgaf +WRtSB4T2W7ADtJI35ET61jKBty3CqJY3AiAIwju7dVW3A5WeD6Qc1SZGKZvp9yCb +AFI2BfVwwaY11wIgXF3PeGcvACMyMWsuSv7aPXHfliswAbkWuzcwA4TW01ECIGWa +cgsDvVFxmfM5NPSuT/UDTa6R5BFISB5ea0N0AR3I +-----END RSA PRIVATE KEY----- diff --git a/test/fixtures/agent2.cnf b/test/fixtures/agent2.cnf new file mode 100644 index 000000000..0a9f2c737 --- /dev/null +++ b/test/fixtures/agent2.cnf @@ -0,0 +1,19 @@ +[ req ] +default_bits = 1024 +days = 999 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no + +[ req_distinguished_name ] +C = US +ST = CA +L = SF +O = Joyent +OU = Node.js +CN = agent2 +emailAddress = ry@tinyclouds.org + +[ req_attributes ] +challengePassword = A challenge password + diff --git a/test/lib-https-proxy-test.js b/test/lib-https-proxy-test.js new file mode 100644 index 000000000..6a544bf4d --- /dev/null +++ b/test/lib-https-proxy-test.js @@ -0,0 +1,65 @@ +var httpProxy = require('../lib/http-proxy'), + expect = require('expect.js'), + http = require('http') + https = require('https'), + path = require('path'), + fs = require('fs'); + +// +// Expose a port number generator. +// thanks to @3rd-Eden +// +var initialPort = 1024, gen = {}; +Object.defineProperty(gen, 'port', { + get: function get() { + return initialPort++; + } +}); + +describe('lib/http-proxy.js', function() { + describe('#createProxyServer using HTTPS', function() { + describe('HTTPS to HTTP', function () { + it('should proxy the request en send back the response', function (done) { + var ports = { source: gen.port, proxy: gen.port }; + var source = http.createServer(function(req, res) { + console.log('Request:', req.headers); + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql(ports.proxy); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello from ' + ports.source); + }); + + source.listen(ports.source); + + var proxy = httpProxy.createProxyServer({ + forward: 'http://127.0.0.1:' + ports.source, + ssl: { + key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')), + cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')), + } + }).listen(ports.proxy); + + var req = https.request({ + host: 'localhost', + port: ports.proxy, + path: '/', + method: 'GET', + localAddress: '127.0.0.1', + rejectUnauthorized: false + }, function(res) { + console.log(res); + res.on('data', function (ch) { + console.log('Chunks', ch) + }) + console.log('Response:', res.statusCode); + source.close(); + proxy._server.close(); + done(); + }); + + req.on('error', function (err) { console.log('Erroring', err); }); + req.end(); + }) + }) + }); +}); \ No newline at end of file From 1204a35e467c6c1855ba0dac8f55d79f899148a6 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 29 Oct 2013 18:12:23 +0100 Subject: [PATCH 156/210] [fix] support buffer --- lib/http-proxy/passes/web-incoming.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index 97329348a..92737525f 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -95,7 +95,7 @@ web_o = Object.keys(web_o).map(function(pass) { var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req, 'forward') ); - req.pipe(forwardReq); + (options.buffer || req).pipe(forwardReq); return res.end(); } @@ -114,7 +114,7 @@ web_o = Object.keys(web_o).map(function(pass) { } }); - req.pipe(proxyReq); + (options.buffer || req).pipe(proxyReq); proxyReq.on('response', function(proxyRes) { for(var i=0; i < web_o.length; i++) { @@ -124,10 +124,6 @@ web_o = Object.keys(web_o).map(function(pass) { proxyRes.pipe(res); }); - if(options.buffer) { - options.buffer.resume(); - } - //proxyReq.end(); } From 8085178dc2c24567adfb872a583863709ce60b5b Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 29 Oct 2013 16:46:27 -0500 Subject: [PATCH 157/210] [tests] Using target field, tests now pass. We are missing the tests using forward field --- test/lib-https-proxy-test.js | 61 +++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/test/lib-https-proxy-test.js b/test/lib-https-proxy-test.js index 6a544bf4d..e4a250f89 100644 --- a/test/lib-https-proxy-test.js +++ b/test/lib-https-proxy-test.js @@ -22,7 +22,6 @@ describe('lib/http-proxy.js', function() { it('should proxy the request en send back the response', function (done) { var ports = { source: gen.port, proxy: gen.port }; var source = http.createServer(function(req, res) { - console.log('Request:', req.headers); expect(req.method).to.eql('GET'); expect(req.headers.host.split(':')[1]).to.eql(ports.proxy); res.writeHead(200, { 'Content-Type': 'text/plain' }); @@ -32,7 +31,7 @@ describe('lib/http-proxy.js', function() { source.listen(ports.source); var proxy = httpProxy.createProxyServer({ - forward: 'http://127.0.0.1:' + ports.source, + target: 'http://127.0.0.1:' + ports.source, ssl: { key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')), cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')), @@ -44,22 +43,62 @@ describe('lib/http-proxy.js', function() { port: ports.proxy, path: '/', method: 'GET', - localAddress: '127.0.0.1', rejectUnauthorized: false }, function(res) { - console.log(res); - res.on('data', function (ch) { - console.log('Chunks', ch) + expect(res.statusCode).to.eql(200); + + res.on('data', function (data) { + expect(data.toString()).to.eql('Hello from ' + ports.source); + }); + + res.on('end', function () { + source.close(); + proxy._server.close(); + done(); }) - console.log('Response:', res.statusCode); - source.close(); - proxy._server.close(); - done(); }); req.on('error', function (err) { console.log('Erroring', err); }); req.end(); }) - }) + }); + describe('HTTP to HTTPS', function () { + it('should proxy the request en send back the response', function (done) { + var ports = { source: gen.port, proxy: gen.port }; + var source = https.createServer({ + key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')), + cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')), + }, function (req, res) { + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql(ports.proxy); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello from ' + ports.source); + }); + + source.listen(ports.source); + + var proxy = httpProxy.createProxyServer({ + target: 'https://127.0.0.1:' + ports.source, + }).listen(ports.proxy); + + http.request({ + hostname: '127.0.0.1', + port: ports.proxy, + method: 'GET' + }, function(res) { + expect(res.statusCode).to.eql(200); + + res.on('data', function (data) { + expect(data.toString()).to.eql('Hello from ' + ports.source); + }); + + res.on('end', function () { + source.close(); + proxy._server.close(); + done(); + }); + }).end(); + }) + }) }); }); \ No newline at end of file From dda6f7a45a46d2bf63e482d0b47b7c36ae548546 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 5 Nov 2013 17:44:04 +0100 Subject: [PATCH 158/210] [feature] add emit proxyRes --- lib/http-proxy/passes/web-incoming.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index 92737525f..c3bc063dd 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -117,6 +117,7 @@ web_o = Object.keys(web_o).map(function(pass) { (options.buffer || req).pipe(proxyReq); proxyReq.on('response', function(proxyRes) { + server.emit('proxyRes', proxyRes); for(var i=0; i < web_o.length; i++) { if(web_o[i](req, res, proxyRes)) { break; } } From b8c6397a9415e1ba1e8dda89cd2aea971a186713 Mon Sep 17 00:00:00 2001 From: Jarrett Cruger Date: Tue, 5 Nov 2013 16:30:39 -0500 Subject: [PATCH 159/210] [fix] pass proper options object that extend the global options and parse the per proxy args into options. fixes #510 --- lib/http-proxy/index.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 7861503dc..c02735b02 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -6,7 +6,7 @@ var httpProxy = exports, https = require('https'), web = require('./passes/web-incoming'), ws = require('./passes/ws-incoming'); - + httpProxy.Server = ProxyServer; /** @@ -33,7 +33,7 @@ function createRightProxy(type) { cntr = args.length - 1, head, cbl; - /* optional args parse begin */ + /* optional args parse begin */ if(typeof args[cntr] === 'function') { cbl = args[cntr]; @@ -64,7 +64,7 @@ function createRightProxy(type) { }); - for(var i=0; i < passes.length; i++) { + for(var i=0; i < passes.length; i++) { /** * Call of passes functions * pass(req, res, options, head) @@ -73,7 +73,7 @@ function createRightProxy(type) { * refer to the connection socket * pass(req, socket, options, head) */ - if(passes[i](req, res, this.options, head, cbl ? false : this, cbl)) { // passes can return a truthy value to halt the loop + if(passes[i](req, res, options, head, cbl ? false : this, cbl)) { // passes can return a truthy value to halt the loop break; } } @@ -103,9 +103,9 @@ require('util').inherits(ProxyServer, EE3); ProxyServer.prototype.listen = function(port) { var self = this, closure = function(req, res) { self.web(req, res); }; - - this._server = this.options.ssl ? - https.createServer(this.options.ssl, closure) : + + this._server = this.options.ssl ? + https.createServer(this.options.ssl, closure) : http.createServer(closure); if(this.options.ws) { @@ -119,7 +119,7 @@ ProxyServer.prototype.listen = function(port) { ProxyServer.prototype.before = function(passName, callback) { var i = false; - this.passes.forEach(function(v, idx) { + this.passes.forEach(function(v, idx) { if(v.name === passName) i = idx; }) @@ -129,7 +129,7 @@ ProxyServer.prototype.before = function(passName, callback) { }; ProxyServer.prototype.after = function(passName, callback) { var i = false; - this.passes.forEach(function(v, idx) { + this.passes.forEach(function(v, idx) { if(v.name === passName) i = idx; }) From 590bb604dae11223a0ae80469b59d6d341488f1f Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 7 Nov 2013 19:00:01 +0100 Subject: [PATCH 160/210] [fix] _ because it is unused --- lib/http-proxy/passes/web-incoming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index c3bc063dd..efcdc396a 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -89,7 +89,7 @@ web_o = Object.keys(web_o).map(function(pass) { * @api private */ - function stream(req, res, options, head, server, clb) { + function stream(req, res, options, _, server, clb) { if(options.forward) { // If forward enable, so just pipe the request var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( From cde08fb2ee2df03c9457678d8e6776a5d89165b2 Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 7 Nov 2013 19:05:59 +0100 Subject: [PATCH 161/210] [fix] closes number #487 --- lib/http-proxy.js | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index 24efb4687..9620b7050 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -20,24 +20,23 @@ var http = require('http'), */ proxy.createProxyServer = proxy.createServer = function createProxyServer(options) { - if(!options) { - throw new Error([ - "`options` is needed and it must have the following layout:", - " ", - " { ", - " target : ", - " forward: ", - " agent : ", - " ssl : ", - " ws : ", - " xfwd : ", - " } ", - " ", - "NOTE: `options.ws` and `options.ssl` are optional. ", - " `options.target and `options.forward` cannot be ", - " both missing " - ].join("\n")); - } + /* + * `options` is needed and it must have the following layout: + * + * { + * target : + * forward: + * agent : + * ssl : + * ws : + * xfwd : + * } + * + * NOTE: `options.ws` and `options.ssl` are optional. + * `options.target and `options.forward` cannot be + * both missing + * } + */ return new httpProxy.Server(options); }; From d0862aff0c693366dcb11649b6abe1d011268953 Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 7 Nov 2013 19:13:09 +0100 Subject: [PATCH 162/210] [fix] merge #495, thanks @glasser --- lib/http-proxy.js | 2 +- lib/http-proxy/passes/ws-incoming.js | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index 9620b7050..bef543fe3 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -36,7 +36,7 @@ proxy.createProxyServer = proxy.createServer = function createProxyServer(option * `options.target and `options.forward` cannot be * both missing * } - */ + */ return new httpProxy.Server(options); }; diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index ebdbe2ab0..e51c397e5 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -106,15 +106,11 @@ var passes = exports; common.setupOutgoing(options.ssl || {}, options, req) ); // Error Handler - proxyReq.on('error', function(err){ - if (clb) { - clb(err); - } else { - server.emit('error', err, req, res); - } - }); + proxyReq.on('error', onError); proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { + proxySocket.on('error', onError); + common.setupSocket(proxySocket); if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); @@ -126,7 +122,15 @@ var passes = exports; proxySocket.pipe(socket).pipe(proxySocket); }); - proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT + return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT + + function onError(err) { + if (clb) { + clb(err); + } else { + server.emit('error', err, req, res); + } + } } ] // <-- From a2b1f0a4c9079342db6255c5f92db4a0cb992707 Mon Sep 17 00:00:00 2001 From: cronopio Date: Thu, 7 Nov 2013 15:06:45 -0500 Subject: [PATCH 163/210] [tests] disable test, by now is not throwing without options --- test/lib-http-proxy-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib-http-proxy-test.js b/test/lib-http-proxy-test.js index 26f9d34b5..6b2ce035e 100644 --- a/test/lib-http-proxy-test.js +++ b/test/lib-http-proxy-test.js @@ -19,7 +19,7 @@ Object.defineProperty(gen, 'port', { describe('lib/http-proxy.js', function() { describe('#createProxyServer', function() { - it('should throw without options', function() { + it.skip('should throw without options', function() { var error; try { httpProxy.createProxyServer(); From fd42dcef01fec5aeeb4fdcbf0bd7cdb1d48e78f7 Mon Sep 17 00:00:00 2001 From: cronopio Date: Thu, 7 Nov 2013 15:09:37 -0500 Subject: [PATCH 164/210] [tests] https test pass, fix #511. Exposed the rejectUnauthorized flag --- lib/http-proxy.js | 1 + lib/http-proxy/common.js | 5 +++++ test/lib-https-proxy-test.js | 32 +++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index bef543fe3..36589b39f 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -30,6 +30,7 @@ proxy.createProxyServer = proxy.createServer = function createProxyServer(option * ssl : * ws : * xfwd : + * secure : * } * * NOTE: `options.ws` and `options.ssl` are optional. diff --git a/lib/http-proxy/common.js b/lib/http-proxy/common.js index ed18aa418..58c520317 100644 --- a/lib/http-proxy/common.js +++ b/lib/http-proxy/common.js @@ -37,6 +37,11 @@ common.setupOutgoing = function(outgoing, options, req, forward) { extend(outgoing.headers, options.headers); } + if (options[forward || 'target'].protocol == 'https:') { + outgoing.rejectUnauthorized = (typeof options.secure === "undefined") ? true : options.secure; + } + + outgoing.agent = options.agent || false; outgoing.path = req.url; diff --git a/test/lib-https-proxy-test.js b/test/lib-https-proxy-test.js index e4a250f89..a9209b026 100644 --- a/test/lib-https-proxy-test.js +++ b/test/lib-https-proxy-test.js @@ -17,7 +17,7 @@ Object.defineProperty(gen, 'port', { }); describe('lib/http-proxy.js', function() { - describe('#createProxyServer using HTTPS', function() { + describe('HTTPS #createProxyServer', function() { describe('HTTPS to HTTP', function () { it('should proxy the request en send back the response', function (done) { var ports = { source: gen.port, proxy: gen.port }; @@ -79,6 +79,8 @@ describe('lib/http-proxy.js', function() { var proxy = httpProxy.createProxyServer({ target: 'https://127.0.0.1:' + ports.source, + // Allow to use SSL self signed + secure: false }).listen(ports.proxy); http.request({ @@ -100,5 +102,33 @@ describe('lib/http-proxy.js', function() { }).end(); }) }) + describe('HTTPS not allow SSL self signed', function () { + it('should fail with error', function (done) { + var ports = { source: gen.port, proxy: gen.port }; + var source = https.createServer({ + key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')), + cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')), + }).listen(ports.source); + + var proxy = httpProxy.createProxyServer({ + target: 'https://127.0.0.1:' + ports.source, + secure: true + }); + + proxy.listen(ports.proxy); + + proxy.on('error', function (err, req, res) { + expect(err).to.be.an(Error); + expect(err.toString()).to.be('Error: DEPTH_ZERO_SELF_SIGNED_CERT') + done(); + }) + + http.request({ + hostname: '127.0.0.1', + port: ports.proxy, + method: 'GET' + }).end(); + }) + }) }); }); \ No newline at end of file From a467b7b4a9614a7cbfdc256524e1495616e3d4d9 Mon Sep 17 00:00:00 2001 From: cronopio Date: Thu, 7 Nov 2013 17:20:28 -0500 Subject: [PATCH 165/210] [examples] fixed https examples --- examples/https-secure.js | 8 ++++---- examples/https.js | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/https-secure.js b/examples/https-secure.js index 72bcbb625..1027cd40f 100644 --- a/examples/https-secure.js +++ b/examples/https-secure.js @@ -4,10 +4,10 @@ var httpProxy = require('../lib/http-proxy'), * Create your proxy server pointing to a secure domain * Enable ssl validation */ -var options = {target : 'https://google.com', - agent : https.globalAgent, - headers: {host: 'google.com'} - }; +var options = { target : 'https://google.com', + agent : https.globalAgent, + headers: {host: 'google.com'} + }; var proxyServer = httpProxy.createProxyServer(options); console.log("Proxy server listening on port 8000"); diff --git a/examples/https.js b/examples/https.js index 7f0aed3e2..cccc24683 100644 --- a/examples/https.js +++ b/examples/https.js @@ -1,8 +1,10 @@ -var httpProxy = require('http-proxy'); +var httpProxy = require('../lib/http-proxy'); /* * Create your proxy server pointing to a secure domain */ -var options = {target:'https://google.com'}; +var options = { target:'https://google.com', + headers: {host: 'google.com'} + }; var proxyServer = httpProxy.createProxyServer(options); console.log("Proxy server listening on port 8000"); From 6a6dfbb79dc156679f75dd519344d19a5b61613b Mon Sep 17 00:00:00 2001 From: cronopio Date: Thu, 7 Nov 2013 18:06:12 -0500 Subject: [PATCH 166/210] [examples] fix styling and bad spaces --- examples/https-secure.js | 9 +++++---- examples/https.js | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/https-secure.js b/examples/https-secure.js index 1027cd40f..83b87eb7c 100644 --- a/examples/https-secure.js +++ b/examples/https-secure.js @@ -4,10 +4,11 @@ var httpProxy = require('../lib/http-proxy'), * Create your proxy server pointing to a secure domain * Enable ssl validation */ -var options = { target : 'https://google.com', - agent : https.globalAgent, - headers: {host: 'google.com'} - }; +var options = { + target : 'https://google.com', + agent : https.globalAgent, + headers: {host: 'google.com'} +}; var proxyServer = httpProxy.createProxyServer(options); console.log("Proxy server listening on port 8000"); diff --git a/examples/https.js b/examples/https.js index cccc24683..364d7cb2f 100644 --- a/examples/https.js +++ b/examples/https.js @@ -2,9 +2,10 @@ var httpProxy = require('../lib/http-proxy'); /* * Create your proxy server pointing to a secure domain */ -var options = { target:'https://google.com', - headers: {host: 'google.com'} - }; +var options = { + target:'https://google.com', + headers: {host: 'google.com'} +}; var proxyServer = httpProxy.createProxyServer(options); console.log("Proxy server listening on port 8000"); From 31d919b0a3d0b7f574e88fc5eed093c6b1a53548 Mon Sep 17 00:00:00 2001 From: cronopio Date: Thu, 7 Nov 2013 18:06:51 -0500 Subject: [PATCH 167/210] [tests] added HTTPS to HTTPS test --- test/lib-https-proxy-test.js | 52 ++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/test/lib-https-proxy-test.js b/test/lib-https-proxy-test.js index a9209b026..78da72d4e 100644 --- a/test/lib-https-proxy-test.js +++ b/test/lib-https-proxy-test.js @@ -38,7 +38,7 @@ describe('lib/http-proxy.js', function() { } }).listen(ports.proxy); - var req = https.request({ + https.request({ host: 'localhost', port: ports.proxy, path: '/', @@ -56,10 +56,7 @@ describe('lib/http-proxy.js', function() { proxy._server.close(); done(); }) - }); - - req.on('error', function (err) { console.log('Erroring', err); }); - req.end(); + }).end(); }) }); describe('HTTP to HTTPS', function () { @@ -102,6 +99,51 @@ describe('lib/http-proxy.js', function() { }).end(); }) }) + describe('HTTPS to HTTPS', function () { + it('should proxy the request en send back the response', function (done) { + var ports = { source: gen.port, proxy: gen.port }; + var source = https.createServer({ + key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')), + cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')), + }, function(req, res) { + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql(ports.proxy); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello from ' + ports.source); + }); + + source.listen(ports.source); + + var proxy = httpProxy.createProxyServer({ + target: 'https://127.0.0.1:' + ports.source, + ssl: { + key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')), + cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')), + }, + secure: false + }).listen(ports.proxy); + + https.request({ + host: 'localhost', + port: ports.proxy, + path: '/', + method: 'GET', + rejectUnauthorized: false + }, function(res) { + expect(res.statusCode).to.eql(200); + + res.on('data', function (data) { + expect(data.toString()).to.eql('Hello from ' + ports.source); + }); + + res.on('end', function () { + source.close(); + proxy._server.close(); + done(); + }) + }).end(); + }) + }); describe('HTTPS not allow SSL self signed', function () { it('should fail with error', function (done) { var ports = { source: gen.port, proxy: gen.port }; From bbe3bfdf98255b82a185a798ff9f29e74615b6ca Mon Sep 17 00:00:00 2001 From: cronopio Date: Thu, 7 Nov 2013 21:27:50 -0500 Subject: [PATCH 168/210] [tests] added test HTTPS to HTTP using own server --- test/lib-https-proxy-test.js | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/lib-https-proxy-test.js b/test/lib-https-proxy-test.js index 78da72d4e..192504921 100644 --- a/test/lib-https-proxy-test.js +++ b/test/lib-https-proxy-test.js @@ -172,5 +172,51 @@ describe('lib/http-proxy.js', function() { }).end(); }) }) + describe('HTTPS to HTTP using own server', function () { + it('should proxy the request en send back the response', function (done) { + var ports = { source: gen.port, proxy: gen.port }; + var source = http.createServer(function(req, res) { + expect(req.method).to.eql('GET'); + expect(req.headers.host.split(':')[1]).to.eql(ports.proxy); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello from ' + ports.source); + }); + + source.listen(ports.source); + + var proxy = httpProxy.createServer({ + agent: new http.Agent({ maxSockets: 2 }) + }); + + var ownServer = https.createServer({ + key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')), + cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')), + }, function (req, res) { + proxy.web(req, res, { + target: 'http://127.0.0.1:' + ports.source + }) + }).listen(ports.proxy); + + https.request({ + host: 'localhost', + port: ports.proxy, + path: '/', + method: 'GET', + rejectUnauthorized: false + }, function(res) { + expect(res.statusCode).to.eql(200); + + res.on('data', function (data) { + expect(data.toString()).to.eql('Hello from ' + ports.source); + }); + + res.on('end', function () { + source.close(); + ownServer.close(); + done(); + }) + }).end(); + }) + }) }); }); \ No newline at end of file From 961d2f9400b4cfd236c3c8ccbf401d37f8e871b8 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 8 Nov 2013 20:56:26 +0100 Subject: [PATCH 169/210] [fix] support target and forward --- lib/http-proxy/passes/web-incoming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index efcdc396a..68d4e1db7 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -96,7 +96,7 @@ web_o = Object.keys(web_o).map(function(pass) { common.setupOutgoing(options.ssl || {}, options, req, 'forward') ); (options.buffer || req).pipe(forwardReq); - return res.end(); + if(!options.target) { return res.end(); } } // Request initalization From 04c10113f7a3b568fb95b18f30e4aca3e059d961 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 11 Nov 2013 11:14:42 -0500 Subject: [PATCH 170/210] [examples] added concurrent proxy example --- examples/concurrent-proxy.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 examples/concurrent-proxy.js diff --git a/examples/concurrent-proxy.js b/examples/concurrent-proxy.js new file mode 100644 index 000000000..0d1889f89 --- /dev/null +++ b/examples/concurrent-proxy.js @@ -0,0 +1,36 @@ +var http = require('http'), + httpProxy = require('../lib/http-proxy'); + +var connections = [], + go; + + +// +// Target Http Server +// +// to check apparent problems with concurrent connections +// make a server which only responds when there is a given nubmer on connections +// +http.createServer(function (req, res) { + connections.push(function () { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); + }); + + process.stdout.write(connections.length + ', '); + + if (connections.length > 10 || go) { + go = true; + while (connections.length) { + connections.shift()(); + } + } +}).listen(9000); +console.log("Web server listening on port 9000"); + +// +// Basic Http Proxy Server +// +httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(8000); +console.log("Proxy server listening on port 8000"); \ No newline at end of file From 7a3f6dfbcc80ba32fa81004438c637e8d29eb029 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 11 Nov 2013 11:22:04 -0500 Subject: [PATCH 171/210] [examples] added forward example --- examples/forward-proxy.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 examples/forward-proxy.js diff --git a/examples/forward-proxy.js b/examples/forward-proxy.js new file mode 100644 index 000000000..4cb516f50 --- /dev/null +++ b/examples/forward-proxy.js @@ -0,0 +1,33 @@ +var http = require('http'), + httpProxy = require('../lib/http-proxy'); + +// +// Target Http Server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); +console.log("Web server listening on port 9000"); + +// +// Target Http Forwarding Server +// +http.createServer(function (req, res) { + console.log('Receiving forward for: ' + req.url); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9001); + +// +// Basic Http Proxy Server +// Forward example: send requests without care about response +// +httpProxy.createServer({ + target: 'http://localhost:9000', + forward: 'http://localhost:9001' +}).listen(8000) +console.log("Proxy server listening on port 8000"); + From dcb873ad9992b1534615d59b8a0a70e8b87d7884 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 13 Nov 2013 18:29:15 +0100 Subject: [PATCH 172/210] [doc] update README.md --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 344d6c32c..904b77905 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ An object will be returned with four values: * web `req, res, [options]` (used for proxying regular HTTP(S) requests) * ws `req, socket, head, [options]` (used for proxying WS(S) requests) -* ee (an EventEmitter2 that emits events, you can hook into them to customize behaviour) * listen `port` (a function that wraps the object in a webserver, for your convenience) Is it then possible to proxy requests by calling these functions @@ -44,15 +43,26 @@ require('http').createServer(function(req, res) { }); ``` +Errors can be listened on either using the Event Emitter API + +```javascript +proxy.on('error', function(e) { + ... +}); +``` + +or using the callback API + +```javascript +proxy.web(req, res, { target: 'http://mytarget.com:8080' }, function(e) { ... }); +``` + When a request is proxied it follows two different pipelines ([available here](lib/http-proxy/passes)) which apply transformations to both the `req` and `res` object. The first pipeline (ingoing) is responsible for the creation and manipulation of the stream that connects your client to the target. The second pipeline (outgoing) is responsible for the creation and manipulation of the stream that, from your target, returns data to the client. -You can easily add a `pass` (stages) into both the pipelines (XXX: ADD API). - -In addition, every stage emits a corresponding event so introspection during the process is always available. #### Setup a basic stand-alone proxy server From cb7af4f4d76744cd3348297ce8284e97d17c9304 Mon Sep 17 00:00:00 2001 From: Ben Firshman Date: Mon, 18 Nov 2013 23:04:51 +0000 Subject: [PATCH 173/210] Fix websocket error handing Websockets have sockets, not responses. --- lib/http-proxy/passes/ws-incoming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index e51c397e5..ab99aa2cb 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -128,7 +128,7 @@ var passes = exports; if (clb) { clb(err); } else { - server.emit('error', err, req, res); + server.emit('error', err, req, socket); } } } From 10c0f11b68e39552051e508c7bf20d65d2d59177 Mon Sep 17 00:00:00 2001 From: yawnt Date: Tue, 19 Nov 2013 20:05:46 +0100 Subject: [PATCH 174/210] [fix] remove duplicate --- lib/http-proxy/passes/ws-incoming.js | 18 ------------ .../lib-http-proxy-passes-ws-incoming-test.js | 29 ------------------- 2 files changed, 47 deletions(-) diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index ab99aa2cb..99a6f8711 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -41,24 +41,6 @@ var passes = exports; } }, - /** - * Set the proper configuration for sockets, - * set no delay and set keep alive, also set - * the timeout to 0. - * - * @param {ClientRequest} Req Request object - * @param {Socket} Websocket - * - * @api private - */ - - function setupSocket(req, socket) { - socket.setTimeout(0); - socket.setNoDelay(true); - - socket.setKeepAlive(true, 0); - }, - /** * Sets `x-forwarded-*` headers if specified in config. * diff --git a/test/lib-http-proxy-passes-ws-incoming-test.js b/test/lib-http-proxy-passes-ws-incoming-test.js index 87dfcef94..2f696c7be 100644 --- a/test/lib-http-proxy-passes-ws-incoming-test.js +++ b/test/lib-http-proxy-passes-ws-incoming-test.js @@ -76,35 +76,6 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { }) }); - describe('#setupSocket', function () { - it('Set the correct config to the socket', function () { - var stubSocket = { - setTimeout: function (num) { - // Simulate Socket.setTimeout() - socketConfig.timeout = num; - }, - setNoDelay: function (bol) { - // Simulate Socket.setNoDelay() - socketConfig.nodelay = bol; - }, - setKeepAlive: function (bol) { - // Simulate Socket.setKeepAlive() - socketConfig.keepalive = bol; - } - }, - socketConfig = { - timeout: null, - nodelay: false, - keepalive: false - }, - returnValue = httpProxy.setupSocket({}, stubSocket); - expect(returnValue).to.be(undefined); - expect(socketConfig.timeout).to.eql(0); - expect(socketConfig.nodelay).to.eql(true); - expect(socketConfig.keepalive).to.eql(true); - }); - }); - describe('#XHeaders', function () { it('return if no forward request', function () { var returnValue = httpProxy.XHeaders({}, {}, {}); From 584ce76e7576c906e25cdd04a2e079f97bcf86ff Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 19 Nov 2013 20:03:23 -0500 Subject: [PATCH 175/210] [misc] add a LICENSE file --- LICENSE | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..2bab4b9b1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ + + node-http-proxy + + Copyright (c) Nodejitsu 2013 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From ae0faef5aa0080d742a9740f9cb38bfd54b7d97e Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 9 Dec 2013 12:33:16 -0500 Subject: [PATCH 176/210] [docs] Update readme with more how to --- README.md | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 904b77905..e95b2d6fa 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ to the client. var http = require('http'), httpProxy = require('http-proxy'); // -// Create your proxy server +// Create your proxy server and set the target in the options. // httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(8000); @@ -85,6 +85,9 @@ http.createServer(function (req, res) { ``` #### Setup a stand-alone proxy server with custom server logic +This example show how you can proxy a request using your own HTTP server +and also you can put your own logic to hanlde the request. This example +could show how to proxy requests within another http server. ``` js var http = require('http'), @@ -95,7 +98,14 @@ var http = require('http'), // var proxy = httpProxy.createProxyServer({}); +// +// Create your custom server and just call `proxy.web()` to proxy +// a web request to the target passed in the options +// also you can use `proxy.ws()` to proxy a websockets request +// var server = require('http').createServer(function(req, res) { + // You can define here your custom logic to handle the request + // and then proxy the request. proxy.web(req, res, { target: 'http://127.0.0.1:5060' }); }); @@ -103,6 +113,40 @@ console.log("listening on port 5050") server.listen(5050); ``` +#### Setup a stand-alone proxy server with latency + +``` +var http = require('http'), + httpProxy = require('http-proxy'); + +// +// Create a proxy server with latency +// +var proxy = httpProxy.createProxyServer(); + +// +// Create your server that make an operation that take a while +// and then proxy de request +// +http.createServer(function (req, res) { + // This simulate an operation that take 500ms in execute + setTimeout(function () { + proxy.web(req, res, { + target: 'http://localhost:9008' + }); + }, 500); +}).listen(8008); + +// +// Create your target server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9008); +``` + ### Contributing and Issues * Search on Google/Github @@ -118,6 +162,7 @@ server.listen(5050); * **target**: url string to be parsed with the url module * **forward**: url string to be parsed with the url module * **agent**: object to be passed to http(s).request (see Node's [https agent](http://nodejs.org/api/https.html#https_class_https_agent) and [http agent](http://nodejs.org/api/http.html#http_class_http_agent) objects) + * **secure**: true/false, if you want to verify the SSL Certs If you are using the `proxyServer.listen` method, the following options are also applicable: From 0393b5da990bb45e873bb80d87a0bc9e4dd6a477 Mon Sep 17 00:00:00 2001 From: cronopio Date: Fri, 13 Dec 2013 12:58:37 -0500 Subject: [PATCH 177/210] [docs] more short examples to the Readme --- README.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e95b2d6fa..c9943566c 100644 --- a/README.md +++ b/README.md @@ -86,10 +86,9 @@ http.createServer(function (req, res) { #### Setup a stand-alone proxy server with custom server logic This example show how you can proxy a request using your own HTTP server -and also you can put your own logic to hanlde the request. This example -could show how to proxy requests within another http server. +and also you can put your own logic to hanlde the request. -``` js +```js var http = require('http'), httpProxy = require('http-proxy'); @@ -147,6 +146,122 @@ http.createServer(function (req, res) { }).listen(9008); ``` +#### Listening for proxy events + +* `error`: The error event is emitted if the request to the target fail. +* `proxyRes`: This event is emitted if the request to the target got a response. + +```js +var httpProxy = require('http-proxy'); +// Error example +// +// Http Proxy Server with bad target +// +var proxy = httpProxy.createServer({ + target:'http://localhost:9005' +}); + +// +// Tell the proxy to listen on port 8000 +// +proxy.listen(8005); + +// +// Listen for the `error` event on `proxy`. +proxy.on('error', function (err, req, res) { + res.writeHead(500, { + 'Content-Type': 'text/plain' + }); + + res.end('Something went wrong. And we are reporting a custom error message.'); +}); + +// +// Listen for the `proxyRes` event on `proxy`. +// +proxy.on('proxyRes', function (res) { + console.log('RAW Response from the target', res.headers); +}); + +``` + +#### Using HTTPS +You can activate the validation of a secure SSL certificate to the target connection (avoid self signed certs), just set `secure: true` in the options. + +##### HTTPS -> HTTP + +```js +// +// Create the HTTPS proxy server in front of a HTTP server +// +httpProxy.createServer({ + target: { + host: 'localhost', + port: 9009 + }, + ssl: { + key: fs.readFileSync('valid-ssl-key.pem'), 'utf8'), + cert: fs.readFileSync('valid-ssl-cert.pem'), 'utf8') + } +}).listen(8009); +``` + +##### HTTPS -> HTTPS + +```js +// +// Create the proxy server listening on port 443 +// +httpProxy.createServer({ + ssl: { + key: fs.readFileSync('valid-ssl-key.pem'), 'utf8'), + cert: fs.readFileSync('valid-ssl-cert.pem'), 'utf8') + }, + target: 'https://localhost:9010', + secure: true // Depends on your needs, could be false. +}).listen(443); +``` + +#### Proxying WebSockets +You can activate the websocket support for the proxy using `ws:true` in the options. + +```js +// +// Create a proxy server for websockets +// +httpProxy.createServer({ + target: 'ws://localhost:9014', + ws: true +}).listen(8014); +``` + +Also you can proxy the websocket requests just calling the `ws(req, socket, head)` method. + +```js +// +// Setup our server to proxy standard HTTP requests +// +var proxy = new httpProxy.createProxyServer({ + target: { + host: 'localhost', + port: 9015 + } +}); +var proxyServer = http.createServer(function (req, res) { + proxy.web(req, res); +}); + +// +// Listen to the `upgrade` event and proxy the +// WebSocket requests as well. +// +proxyServer.on('upgrade', function (req, socket, head) { + proxy.ws(req, socket, head); +}); + +proxyServer.listen(8015); +``` + ### Contributing and Issues * Search on Google/Github From 03880d8d069e9e17ca7d7aea6eb760f6626a869c Mon Sep 17 00:00:00 2001 From: cronopio Date: Fri, 13 Dec 2013 13:06:19 -0500 Subject: [PATCH 178/210] [docs] typos, typos everywhere... --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c9943566c..ed25eb240 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ http.createServer(function (req, res) { #### Setup a stand-alone proxy server with custom server logic This example show how you can proxy a request using your own HTTP server -and also you can put your own logic to hanlde the request. +and also you can put your own logic to handle the request. ```js var http = require('http'), @@ -114,7 +114,7 @@ server.listen(5050); #### Setup a stand-alone proxy server with latency -``` +```js var http = require('http'), httpProxy = require('http-proxy'); @@ -161,9 +161,6 @@ var proxy = httpProxy.createServer({ target:'http://localhost:9005' }); -// -// Tell the proxy to listen on port 8000 -// proxy.listen(8005); // @@ -180,7 +177,7 @@ proxy.on('error', function (err, req, res) { // Listen for the `proxyRes` event on `proxy`. // proxy.on('proxyRes', function (res) { - console.log('RAW Response from the target', res.headers); + console.log('RAW Response from the target', JSON.stringify(res.headers, true, 2)); }); ``` From 97e4600e944b1de3e3984e8e29d09658c02de606 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 18 Dec 2013 12:33:23 +0100 Subject: [PATCH 179/210] [fix] fixes #341 --- lib/http-proxy/common.js | 7 +++++++ lib/http-proxy/passes/web-incoming.js | 2 +- lib/http-proxy/passes/ws-incoming.js | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/http-proxy/common.js b/lib/http-proxy/common.js index 58c520317..1698c1ade 100644 --- a/lib/http-proxy/common.js +++ b/lib/http-proxy/common.js @@ -73,3 +73,10 @@ common.setupSocket = function(socket) { return socket; }; + +common.getPort = function(req) { + var res = req.headers.host.match(/:(\d+)/); + return res ? + res[1] : + req.connection.pair ? '443' : '80' ; +} diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index 68d4e1db7..2b14c874c 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -65,7 +65,7 @@ web_o = Object.keys(web_o).map(function(pass) { var values = { for : req.connection.remoteAddress || req.socket.remoteAddress, - port : req.connection.remotePort || req.socket.remotePort, + port : common.getPort(req), proto: req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http') }; diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index 99a6f8711..720ac2745 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -56,7 +56,7 @@ var passes = exports; var values = { for : req.connection.remoteAddress || req.socket.remoteAddress, - port : req.connection.remotePort || req.socket.remotePort, + port : common.getPort(req), proto: req.connection.pair ? 'wss' : 'ws' }; From 9e74a633a75d466cf266b1c57c68edd20a883f69 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 18 Dec 2013 12:45:20 +0100 Subject: [PATCH 180/210] [fix] closes #529 --- lib/http-proxy/common.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/http-proxy/common.js b/lib/http-proxy/common.js index 1698c1ade..789757880 100644 --- a/lib/http-proxy/common.js +++ b/lib/http-proxy/common.js @@ -1,4 +1,5 @@ var common = exports, + url = require('url'), extend = require('util')._extend; /** @@ -43,8 +44,7 @@ common.setupOutgoing = function(outgoing, options, req, forward) { outgoing.agent = options.agent || false; - outgoing.path = req.url; - + outgoing.path = url.parse(req.url).path; return outgoing; }; From c4d56a5faf1e89cdeb911f0ece0efe065eb58c45 Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 18 Dec 2013 11:50:49 -0500 Subject: [PATCH 181/210] [tests] fix test using undefined url --- test/lib-http-proxy-common-test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/lib-http-proxy-common-test.js b/test/lib-http-proxy-common-test.js index 2f9fa1ed8..7398a0ebe 100644 --- a/test/lib-http-proxy-common-test.js +++ b/test/lib-http-proxy-common-test.js @@ -38,7 +38,9 @@ describe('lib/http-proxy/common.js', function () { it('should set the agent to false if none is given', function () { var outgoing = {}; - common.setupOutgoing(outgoing, {target: {},}, {}); + common.setupOutgoing(outgoing, {target: + 'http://localhost' + }, { url: '/' }); expect(outgoing.agent).to.eql(false); }); From cfd417de2352b0f05535b979dc15abff60c1fb96 Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 18 Dec 2013 12:17:41 -0500 Subject: [PATCH 182/210] [tests] fix tests set correct host headers --- test/lib-http-proxy-passes-web-incoming-test.js | 12 +++++++----- test/lib-http-proxy-passes-ws-incoming-test.js | 8 ++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/test/lib-http-proxy-passes-web-incoming-test.js b/test/lib-http-proxy-passes-web-incoming-test.js index 19fa669b7..b25ad6e12 100644 --- a/test/lib-http-proxy-passes-web-incoming-test.js +++ b/test/lib-http-proxy-passes-web-incoming-test.js @@ -34,7 +34,9 @@ describe('lib/http-proxy/passes/web.js', function() { remoteAddress: '192.168.1.2', remotePort: '8080' }, - headers: {} + headers: { + host: '192.168.1.2:8080' + } } it('set the correct x-forwarded-* headers', function () { @@ -88,11 +90,11 @@ describe('#createProxyServer.web() using own http server', function () { }); } - proxyServer.listen('8081'); + proxyServer.listen('8082'); http.request({ hostname: '127.0.0.1', - port: '8081', + port: '8082', method: 'GET', }, function() {}).end(); }); @@ -117,11 +119,11 @@ describe('#createProxyServer.web() using own http server', function () { proxy.web(req, res); } - proxyServer.listen('8081'); + proxyServer.listen('8083'); http.request({ hostname: '127.0.0.1', - port: '8081', + port: '8083', method: 'GET', }, function() {}).end(); }); diff --git a/test/lib-http-proxy-passes-ws-incoming-test.js b/test/lib-http-proxy-passes-ws-incoming-test.js index 2f696c7be..bfed888c0 100644 --- a/test/lib-http-proxy-passes-ws-incoming-test.js +++ b/test/lib-http-proxy-passes-ws-incoming-test.js @@ -88,7 +88,9 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { remoteAddress: '192.168.1.2', remotePort: '8080' }, - headers: {} + headers: { + host: '192.168.1.2:8080' + } } httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2'); @@ -105,7 +107,9 @@ describe('lib/http-proxy/passes/ws-incoming.js', function () { connection: { pair: true }, - headers: {} + headers: { + host: '192.168.1.3:8181' + } }; httpProxy.XHeaders(stubRequest, {}, { xfwd: true }); expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.3'); From bdeabb767a537bcb9f98ef74f6efe9762a9b1c34 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 18 Nov 2013 15:21:25 -0500 Subject: [PATCH 183/210] [examples] deleted this examples --- examples/concurrent-proxy.js | 36 ------------------------------------ examples/error-handling.js | 33 --------------------------------- examples/forward-proxy.js | 33 --------------------------------- examples/https-secure.js | 16 ---------------- examples/https.js | 13 ------------- examples/stand-alone.js | 17 ----------------- 6 files changed, 148 deletions(-) delete mode 100644 examples/concurrent-proxy.js delete mode 100644 examples/error-handling.js delete mode 100644 examples/forward-proxy.js delete mode 100644 examples/https-secure.js delete mode 100644 examples/https.js delete mode 100644 examples/stand-alone.js diff --git a/examples/concurrent-proxy.js b/examples/concurrent-proxy.js deleted file mode 100644 index 0d1889f89..000000000 --- a/examples/concurrent-proxy.js +++ /dev/null @@ -1,36 +0,0 @@ -var http = require('http'), - httpProxy = require('../lib/http-proxy'); - -var connections = [], - go; - - -// -// Target Http Server -// -// to check apparent problems with concurrent connections -// make a server which only responds when there is a given nubmer on connections -// -http.createServer(function (req, res) { - connections.push(function () { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); - }); - - process.stdout.write(connections.length + ', '); - - if (connections.length > 10 || go) { - go = true; - while (connections.length) { - connections.shift()(); - } - } -}).listen(9000); -console.log("Web server listening on port 9000"); - -// -// Basic Http Proxy Server -// -httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(8000); -console.log("Proxy server listening on port 8000"); \ No newline at end of file diff --git a/examples/error-handling.js b/examples/error-handling.js deleted file mode 100644 index 76a09d955..000000000 --- a/examples/error-handling.js +++ /dev/null @@ -1,33 +0,0 @@ -var httpProxy = require('../lib/http-proxy'), - http = require('http'); -/* - * Create your proxy server - */ -var proxy = httpProxy.createProxyServer({target:'http://localhost:30404', ws:true}); - -var proxyServer = http.createServer(requestHandler); - -function requestHandler(req, res) { - // Pass a callback to the web proxy method - // and catch the error there. - proxy.web(req, res, function (err) { - // Now you can get the err - // and handle it by your self - // if (err) throw err; - res.writeHead(502); - res.end("There was an error proxying your request"); - }); - - // In a websocket request case - req.on('upgrade', function (req, socket, head) { - proxy.ws(req, socket, head, function (err) { - // Now you can get the err - // and handle it by your self - // if (err) throw err; - socket.close(); - }) - }) -} - -console.log("Proxy server is listening on port 8000"); -proxyServer.listen(8000) \ No newline at end of file diff --git a/examples/forward-proxy.js b/examples/forward-proxy.js deleted file mode 100644 index 4cb516f50..000000000 --- a/examples/forward-proxy.js +++ /dev/null @@ -1,33 +0,0 @@ -var http = require('http'), - httpProxy = require('../lib/http-proxy'); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); -console.log("Web server listening on port 9000"); - -// -// Target Http Forwarding Server -// -http.createServer(function (req, res) { - console.log('Receiving forward for: ' + req.url); - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9001); - -// -// Basic Http Proxy Server -// Forward example: send requests without care about response -// -httpProxy.createServer({ - target: 'http://localhost:9000', - forward: 'http://localhost:9001' -}).listen(8000) -console.log("Proxy server listening on port 8000"); - diff --git a/examples/https-secure.js b/examples/https-secure.js deleted file mode 100644 index 83b87eb7c..000000000 --- a/examples/https-secure.js +++ /dev/null @@ -1,16 +0,0 @@ -var httpProxy = require('../lib/http-proxy'), - https = require('https'); -/* - * Create your proxy server pointing to a secure domain - * Enable ssl validation - */ -var options = { - target : 'https://google.com', - agent : https.globalAgent, - headers: {host: 'google.com'} -}; - -var proxyServer = httpProxy.createProxyServer(options); -console.log("Proxy server listening on port 8000"); -proxyServer.listen(8000); - diff --git a/examples/https.js b/examples/https.js deleted file mode 100644 index 364d7cb2f..000000000 --- a/examples/https.js +++ /dev/null @@ -1,13 +0,0 @@ -var httpProxy = require('../lib/http-proxy'); -/* - * Create your proxy server pointing to a secure domain - */ -var options = { - target:'https://google.com', - headers: {host: 'google.com'} -}; - -var proxyServer = httpProxy.createProxyServer(options); -console.log("Proxy server listening on port 8000"); -proxyServer.listen(8000); - diff --git a/examples/stand-alone.js b/examples/stand-alone.js deleted file mode 100644 index e5239c460..000000000 --- a/examples/stand-alone.js +++ /dev/null @@ -1,17 +0,0 @@ -var http = require('http'), - httpProxy = require('../lib/http-proxy'); -// -// Create your proxy server -// -console.log("Proxy server listening on port 8000"); -httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(8000); - -// -// Create your target server -// -console.log("Web server listening on port 9000"); -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); \ No newline at end of file From 7e44d3669bbd1b13e6452f265d52b22396f68b5d Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 18 Nov 2013 15:37:34 -0500 Subject: [PATCH 184/210] [examples] update old examples --- examples/http/basic-proxy.js | 60 +++++++++++++++++++++++++++ examples/http/concurrent-proxy.js | 68 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 examples/http/basic-proxy.js create mode 100644 examples/http/concurrent-proxy.js diff --git a/examples/http/basic-proxy.js b/examples/http/basic-proxy.js new file mode 100644 index 000000000..8d781604d --- /dev/null +++ b/examples/http/basic-proxy.js @@ -0,0 +1,60 @@ +/* + basic-proxy.js: Basic example of proxying over HTTP + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +var welcome = [ + '# # ##### ##### ##### ##### ##### #### # # # #', + '# # # # # # # # # # # # # # # # ', + '###### # # # # ##### # # # # # # ## # ', + '# # # # ##### ##### ##### # # ## # ', + '# # # # # # # # # # # # # ', + '# # # # # # # # #### # # # ' +].join('\n'); + +util.puts(welcome.rainbow.bold); + +// +// Basic Http Proxy Server +// +httpProxy.createServer({ + target:'http://localhost:9000' +}).listen(8000); + +// +// Target Http Server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); + +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/http/concurrent-proxy.js b/examples/http/concurrent-proxy.js new file mode 100644 index 000000000..fd05442e3 --- /dev/null +++ b/examples/http/concurrent-proxy.js @@ -0,0 +1,68 @@ +/* + concurrent-proxy.js: check levelof concurrency through proxy. + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +// +// Basic Http Proxy Server +// +httpProxy.createServer({ + target:'http://localhost:9000' +}).listen(8000); + +// +// Target Http Server +// +// to check apparent problems with concurrent connections +// make a server which only responds when there is a given nubmer on connections +// + + +var connections = [], + go; + +http.createServer(function (req, res) { + connections.push(function () { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); + }); + + process.stdout.write(connections.length + ', '); + + if (connections.length > 110 || go) { + go = true; + while (connections.length) { + connections.shift()(); + } + } +}).listen(9000); + +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); From b7261161343c3471201d6de36ba1030aced26425 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 18 Nov 2013 15:58:44 -0500 Subject: [PATCH 185/210] [examples] update forward and custom error examples --- examples/http/custom-proxy-error.js | 55 +++++++++++++++++++ examples/http/forward-and-target-proxy.js | 67 +++++++++++++++++++++++ examples/http/forward-proxy.js | 53 ++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 examples/http/custom-proxy-error.js create mode 100644 examples/http/forward-and-target-proxy.js create mode 100644 examples/http/forward-proxy.js diff --git a/examples/http/custom-proxy-error.js b/examples/http/custom-proxy-error.js new file mode 100644 index 000000000..5835e881c --- /dev/null +++ b/examples/http/custom-proxy-error.js @@ -0,0 +1,55 @@ +/* + custom-proxy-error.js: Example of using the custom `proxyError` event. + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +// +// Http Proxy Server with bad target +// +var proxy = httpProxy.createServer({ + target:'http://localhost:9000' +}); + +// +// Tell the proxy to listen on port 8000 +// +proxy.listen(8000); + +// +// Listen for the `error` event on `proxy`. +proxy.on('error', function (err, req, res) { + res.writeHead(500, { + 'Content-Type': 'text/plain' + }); + + res.end('Something went wrong. And we are reporting a custom error message.'); +}); + + +util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with custom error message'.magenta.underline); \ No newline at end of file diff --git a/examples/http/forward-and-target-proxy.js b/examples/http/forward-and-target-proxy.js new file mode 100644 index 000000000..9ecf4dcec --- /dev/null +++ b/examples/http/forward-and-target-proxy.js @@ -0,0 +1,67 @@ +/* + forward-and-target-proxy.js: Example of proxying over HTTP with additional forward proxy + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +// +// Setup proxy server with forwarding +// +httpProxy.createServer({ + target: { + port: 9000, + host: 'localhost' + }, + forward: { + port: 9001, + host: 'localhost' + } +}).listen(8000); + +// +// Target Http Server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); + +// +// Target Http Forwarding Server +// +http.createServer(function (req, res) { + util.puts('Receiving forward for: ' + req.url); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9001); + +util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with forward proxy'.magenta.underline); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9001 '.yellow); \ No newline at end of file diff --git a/examples/http/forward-proxy.js b/examples/http/forward-proxy.js new file mode 100644 index 000000000..d10a40208 --- /dev/null +++ b/examples/http/forward-proxy.js @@ -0,0 +1,53 @@ +/* + forward-proxy.js: Example of proxying over HTTP with additional forward proxy + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +// +// Setup proxy server with forwarding +// +httpProxy.createServer({ + forward: { + port: 9000, + host: 'localhost' + } +}).listen(8000); + +// +// Target Http Forwarding Server +// +http.createServer(function (req, res) { + util.puts('Receiving forward for: ' + req.url); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); + +util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with forward proxy'.magenta.underline); +util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); \ No newline at end of file From e02317ce86ff2dabd496cf7e2741e219a22ac817 Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 19 Nov 2013 11:06:06 -0500 Subject: [PATCH 186/210] [examples] updated old proxy examples --- examples/http/proxy-https-to-http.js | 60 +++++++++++++++++++++++++++ examples/http/proxy-https-to-https.js | 59 ++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 examples/http/proxy-https-to-http.js create mode 100644 examples/http/proxy-https-to-https.js diff --git a/examples/http/proxy-https-to-http.js b/examples/http/proxy-https-to-http.js new file mode 100644 index 000000000..7c9d1fd93 --- /dev/null +++ b/examples/http/proxy-https-to-http.js @@ -0,0 +1,60 @@ +/* + proxy-https-to-http.js: Basic example of proxying over HTTPS to a target HTTP server + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var https = require('https'), + http = require('http'), + util = require('util'), + path = require('path'), + fs = require('fs'), + colors = require('colors'), + httpProxy = require('../../lib/http-proxy'), + fixturesDir = path.join(__dirname, '..', '..', 'test', 'fixtures'); + +// +// Create the target HTTP server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello http over https\n'); + res.end(); +}).listen(9000); + +// +// Create the HTTPS proxy server listening on port 8000 +// +httpProxy.createServer({ + target: { + host: 'localhost', + port: 9000 + }, + ssl: { + key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'), + cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8') + } +}).listen(8000); + +util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/http/proxy-https-to-https.js b/examples/http/proxy-https-to-https.js new file mode 100644 index 000000000..2ff9151e2 --- /dev/null +++ b/examples/http/proxy-https-to-https.js @@ -0,0 +1,59 @@ +/* + proxy-https-to-https.js: Basic example of proxying over HTTPS to a target HTTPS server + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var https = require('https'), + http = require('http'), + util = require('util'), + fs = require('fs'), + path = require('path'), + colors = require('colors'), + httpProxy = require('../../lib/http-proxy'), + fixturesDir = path.join(__dirname, '..', '..', 'test', 'fixtures'), + httpsOpts = { + key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'), + cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8') + }; + +// +// Create the target HTTPS server +// +https.createServer(httpsOpts, function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello https\n'); + res.end(); +}).listen(9000); + +// +// Create the proxy server listening on port 443 +// +httpProxy.createServer({ + ssl: httpsOpts, + target: 'https://localhost:9000', + secure: false +}).listen(8000); + +util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); +util.puts('https server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); From 588327c2c4392618b515164989f08ef20a30842b Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 19 Nov 2013 11:29:06 -0500 Subject: [PATCH 187/210] [examples] updated old examples --- examples/http/latent-proxy.js | 54 +++++++++++++++++++++++++++++++ examples/http/standalone-proxy.js | 54 +++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 examples/http/latent-proxy.js create mode 100644 examples/http/standalone-proxy.js diff --git a/examples/http/latent-proxy.js b/examples/http/latent-proxy.js new file mode 100644 index 000000000..e2aef9b72 --- /dev/null +++ b/examples/http/latent-proxy.js @@ -0,0 +1,54 @@ +/* + latent-proxy.js: Example of proxying over HTTP with latency + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +// +// Http Proxy Server with Latency +// +var proxy = httpProxy.createProxyServer(); +http.createServer(function (req, res) { + setTimeout(function () { + proxy.web(req, res, { + target: 'http://localhost:9000' + }); + }, 500); +}).listen(8000); + +// +// Target Http Server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); + +util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with latency'.magenta.underline); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/http/standalone-proxy.js b/examples/http/standalone-proxy.js new file mode 100644 index 000000000..05c440698 --- /dev/null +++ b/examples/http/standalone-proxy.js @@ -0,0 +1,54 @@ +/* + standalone-proxy.js: Example of proxying over HTTP with a standalone HTTP server. + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +// +// Http Server with proxyRequest Handler and Latency +// +var proxy = new httpProxy.createProxyServer(); +http.createServer(function (req, res) { + setTimeout(function () { + proxy.web(req, res, { + target: 'http://localhost:9000' + }); + }, 200); +}).listen(8000); + +// +// Target Http Server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); + +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with proxy.web() handler'.cyan.underline + ' and latency'.magenta); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); From ed8c9eeba99d60f39f5c36c4f34ed1a781d2cfd8 Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 19 Nov 2013 11:42:26 -0500 Subject: [PATCH 188/210] [examples] updated websockets examples --- examples/websocket/latent-websocket-proxy.js | 91 +++++++++++++++++++ .../websocket/standalone-websocket-proxy.js | 88 ++++++++++++++++++ examples/websocket/websocket-proxy.js | 70 ++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 examples/websocket/latent-websocket-proxy.js create mode 100644 examples/websocket/standalone-websocket-proxy.js create mode 100644 examples/websocket/websocket-proxy.js diff --git a/examples/websocket/latent-websocket-proxy.js b/examples/websocket/latent-websocket-proxy.js new file mode 100644 index 000000000..06569bf5f --- /dev/null +++ b/examples/websocket/latent-websocket-proxy.js @@ -0,0 +1,91 @@ +/* + standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server. + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + http = require('http'), + colors = require('colors'), + httpProxy = require('../../lib/http-proxy'); + +try { + var io = require('socket.io'), + client = require('socket.io-client'); +} +catch (ex) { + console.error('Socket.io is required for this example:'); + console.error('npm ' + 'install'.green); + process.exit(1); +} + +// +// Create the target HTTP server and setup +// socket.io on it. +// +var server = io.listen(9000); +server.sockets.on('connection', function (client) { + util.debug('Got websocket connection'); + + client.on('message', function (msg) { + util.debug('Got message from client: ' + msg); + }); + + client.send('from server'); +}); + +// +// Setup our server to proxy standard HTTP requests +// +var proxy = new httpProxy.createProxyServer({ + target: { + host: 'localhost', + port: 9000 + } +}); + +var proxyServer = http.createServer(function (req, res) { + proxy.web(req, res); +}); + +// +// Listen to the `upgrade` event and proxy the +// WebSocket requests as well. +// +proxyServer.on('upgrade', function (req, socket, head) { + setTimeout(function () { + proxy.ws(req, socket, head); + }, 1000); +}); + +proxyServer.listen(8000); + +// +// Setup the socket.io client against our proxy +// +var ws = client.connect('ws://localhost:8000'); + +ws.on('message', function (msg) { + util.debug('Got message: ' + msg); + ws.send('I am the client'); +}); diff --git a/examples/websocket/standalone-websocket-proxy.js b/examples/websocket/standalone-websocket-proxy.js new file mode 100644 index 000000000..e844ab097 --- /dev/null +++ b/examples/websocket/standalone-websocket-proxy.js @@ -0,0 +1,88 @@ +/* + standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server. + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + http = require('http'), + colors = require('colors'), + httpProxy = require('../../lib/http-proxy'); + +try { + var io = require('socket.io'), + client = require('socket.io-client'); +} +catch (ex) { + console.error('Socket.io is required for this example:'); + console.error('npm ' + 'install'.green); + process.exit(1); +} + +// +// Create the target HTTP server and setup +// socket.io on it. +// +var server = io.listen(9000); +server.sockets.on('connection', function (client) { + util.debug('Got websocket connection'); + + client.on('message', function (msg) { + util.debug('Got message from client: ' + msg); + }); + + client.send('from server'); +}); + +// +// Setup our server to proxy standard HTTP requests +// +var proxy = new httpProxy.createProxyServer({ + target: { + host: 'localhost', + port: 9000 + } +}); +var proxyServer = http.createServer(function (req, res) { + proxy.web(req, res); +}); + +// +// Listen to the `upgrade` event and proxy the +// WebSocket requests as well. +// +proxyServer.on('upgrade', function (req, socket, head) { + proxy.ws(req, socket, head); +}); + +proxyServer.listen(8000); + +// +// Setup the socket.io client against our proxy +// +var ws = client.connect('ws://localhost:8000'); + +ws.on('message', function (msg) { + util.debug('Got message: ' + msg); + ws.send('I am the client'); +}); diff --git a/examples/websocket/websocket-proxy.js b/examples/websocket/websocket-proxy.js new file mode 100644 index 000000000..767f58c00 --- /dev/null +++ b/examples/websocket/websocket-proxy.js @@ -0,0 +1,70 @@ +/* + web-socket-proxy.js: Example of proxying over HTTP and WebSockets. + + Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + http = require('http'), + colors = require('colors'), + httpProxy = require('../../lib/http-proxy'); + +try { + var io = require('socket.io'), + client = require('socket.io-client'); +} +catch (ex) { + console.error('Socket.io is required for this example:'); + console.error('npm ' + 'install'.green); + process.exit(1); +} + +// +// Create the target HTTP server and setup +// socket.io on it. +// +var server = io.listen(9000); +server.sockets.on('connection', function (client) { + util.debug('Got websocket connection'); + + client.on('message', function (msg) { + util.debug('Got message from client: ' + msg); + }); + + client.send('from server'); +}); + +// +// Create a proxy server with node-http-proxy +// +httpProxy.createServer({ target: 'ws://localhost:9000', ws: true }).listen(8000); + +// +// Setup the socket.io client against our proxy +// +var ws = client.connect('ws://localhost:8000'); + +ws.on('message', function (msg) { + util.debug('Got message: ' + msg); + ws.send('I am the client'); +}); From 831a44b3c8c3acf6c046c47703a07cd6362a0d1c Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 19 Nov 2013 12:13:10 -0500 Subject: [PATCH 189/210] [examples] updated balancer examples --- .../simple-balancer-with-websockets.js | 58 +++++++++++++++++++ examples/balancer/simple-balancer.js | 38 ++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 examples/balancer/simple-balancer-with-websockets.js create mode 100644 examples/balancer/simple-balancer.js diff --git a/examples/balancer/simple-balancer-with-websockets.js b/examples/balancer/simple-balancer-with-websockets.js new file mode 100644 index 000000000..ffe54ec56 --- /dev/null +++ b/examples/balancer/simple-balancer-with-websockets.js @@ -0,0 +1,58 @@ +var http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +// +// A simple round-robin load balancing strategy. +// +// First, list the servers you want to use in your rotation. +// +var addresses = [ + { + host: 'ws1.0.0.0', + port: 80 + }, + { + host: 'ws2.0.0.0', + port: 80 + } +]; + +// +// Create a HttpProxy object for each target +// + +var proxies = addresses.map(function (target) { + return new httpProxy.createProxyServer({ + target: target + }); +}); + +// +// Get the proxy at the front of the array, put it at the end and return it +// If you want a fancier balancer, put your code here +// + +function nextProxy() { + var proxy = proxies.shift(); + proxies.push(proxy); + return proxy; +} + +// +// Get the 'next' proxy and send the http request +// + +var server = http.createServer(function (req, res) { + nextProxy().web(req, res); +}); + +// +// Get the 'next' proxy and send the upgrade request +// + +server.on('upgrade', function (req, socket, head) { + nextProxy().ws(req, socket, head); +}); + +server.listen(8080); + \ No newline at end of file diff --git a/examples/balancer/simple-balancer.js b/examples/balancer/simple-balancer.js new file mode 100644 index 000000000..5660fbc29 --- /dev/null +++ b/examples/balancer/simple-balancer.js @@ -0,0 +1,38 @@ +var http = require('http'), + httpProxy = require('../../lib/http-proxy'); +// +// A simple round-robin load balancing strategy. +// +// First, list the servers you want to use in your rotation. +// +var addresses = [ + { + host: 'ws1.0.0.0', + port: 80 + }, + { + host: 'ws2.0.0.0', + port: 80 + } +]; +var proxy = httpProxy.createServer(); + +http.createServer(function (req, res) { + // + // On each request, get the first location from the list... + // + var target = { target: addresses.shift() }; + + // + // ...then proxy to the server whose 'turn' it is... + // + console.log('balancing request to: ', target); + proxy.web(req, res, target); + + // + // ...and then the server you just used becomes the last item in the list. + // + addresses.push(target); +}).listen(8000); + +// Rinse; repeat; enjoy. \ No newline at end of file From d85ccdd333edcfc7551bcf8e0ffd7dc166e38e61 Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 19 Nov 2013 12:38:20 -0500 Subject: [PATCH 190/210] [examples] added package.json with the dependencies needed by examples --- examples/package.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 examples/package.json diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 000000000..be054638e --- /dev/null +++ b/examples/package.json @@ -0,0 +1,10 @@ +{ + "name": "http-proxy-examples", + "description": "packages required to run the examples", + "version": "0.0.0", + "dependencies": { + "colors": "~0.6.2", + "socket.io": "~0.9.16", + "socket.io-client": "~0.9.16" + } +} From e592c53d1a23b7920d603a9e9ac294fc0e841f6d Mon Sep 17 00:00:00 2001 From: cronopio Date: Tue, 19 Nov 2013 19:58:28 -0500 Subject: [PATCH 191/210] [examples] fix the copyright header of example files --- .../simple-balancer-with-websockets.js | 26 +++++++++++++++++++ examples/balancer/simple-balancer.js | 26 +++++++++++++++++++ examples/http/basic-proxy.js | 2 +- examples/http/concurrent-proxy.js | 2 +- examples/http/custom-proxy-error.js | 2 +- examples/http/forward-and-target-proxy.js | 2 +- examples/http/forward-proxy.js | 2 +- examples/http/latent-proxy.js | 2 +- examples/http/proxy-https-to-http.js | 2 +- examples/http/proxy-https-to-https.js | 2 +- examples/http/standalone-proxy.js | 2 +- examples/websocket/latent-websocket-proxy.js | 2 +- .../websocket/standalone-websocket-proxy.js | 2 +- examples/websocket/websocket-proxy.js | 2 +- 14 files changed, 64 insertions(+), 12 deletions(-) diff --git a/examples/balancer/simple-balancer-with-websockets.js b/examples/balancer/simple-balancer-with-websockets.js index ffe54ec56..b17afc772 100644 --- a/examples/balancer/simple-balancer-with-websockets.js +++ b/examples/balancer/simple-balancer-with-websockets.js @@ -1,3 +1,29 @@ +/* + simple-balancer.js: Example of a simple round robin balancer for websockets + + Copyright (c) Nodejitsu 2013 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + var http = require('http'), httpProxy = require('../../lib/http-proxy'); diff --git a/examples/balancer/simple-balancer.js b/examples/balancer/simple-balancer.js index 5660fbc29..80b91760c 100644 --- a/examples/balancer/simple-balancer.js +++ b/examples/balancer/simple-balancer.js @@ -1,3 +1,29 @@ +/* + simple-balancer.js: Example of a simple round robin balancer + + Copyright (c) Nodejitsu 2013 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + var http = require('http'), httpProxy = require('../../lib/http-proxy'); // diff --git a/examples/http/basic-proxy.js b/examples/http/basic-proxy.js index 8d781604d..640318c11 100644 --- a/examples/http/basic-proxy.js +++ b/examples/http/basic-proxy.js @@ -1,7 +1,7 @@ /* basic-proxy.js: Basic example of proxying over HTTP - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/http/concurrent-proxy.js b/examples/http/concurrent-proxy.js index fd05442e3..5ca1054b8 100644 --- a/examples/http/concurrent-proxy.js +++ b/examples/http/concurrent-proxy.js @@ -1,7 +1,7 @@ /* concurrent-proxy.js: check levelof concurrency through proxy. - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/http/custom-proxy-error.js b/examples/http/custom-proxy-error.js index 5835e881c..dd62273a7 100644 --- a/examples/http/custom-proxy-error.js +++ b/examples/http/custom-proxy-error.js @@ -1,7 +1,7 @@ /* custom-proxy-error.js: Example of using the custom `proxyError` event. - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/http/forward-and-target-proxy.js b/examples/http/forward-and-target-proxy.js index 9ecf4dcec..0d5acd1f6 100644 --- a/examples/http/forward-and-target-proxy.js +++ b/examples/http/forward-and-target-proxy.js @@ -1,7 +1,7 @@ /* forward-and-target-proxy.js: Example of proxying over HTTP with additional forward proxy - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/http/forward-proxy.js b/examples/http/forward-proxy.js index d10a40208..5f93c49f6 100644 --- a/examples/http/forward-proxy.js +++ b/examples/http/forward-proxy.js @@ -1,7 +1,7 @@ /* forward-proxy.js: Example of proxying over HTTP with additional forward proxy - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/http/latent-proxy.js b/examples/http/latent-proxy.js index e2aef9b72..85de6338f 100644 --- a/examples/http/latent-proxy.js +++ b/examples/http/latent-proxy.js @@ -1,7 +1,7 @@ /* latent-proxy.js: Example of proxying over HTTP with latency - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/http/proxy-https-to-http.js b/examples/http/proxy-https-to-http.js index 7c9d1fd93..1b6ac92bd 100644 --- a/examples/http/proxy-https-to-http.js +++ b/examples/http/proxy-https-to-http.js @@ -1,7 +1,7 @@ /* proxy-https-to-http.js: Basic example of proxying over HTTPS to a target HTTP server - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/http/proxy-https-to-https.js b/examples/http/proxy-https-to-https.js index 2ff9151e2..f0d06e8e4 100644 --- a/examples/http/proxy-https-to-https.js +++ b/examples/http/proxy-https-to-https.js @@ -1,7 +1,7 @@ /* proxy-https-to-https.js: Basic example of proxying over HTTPS to a target HTTPS server - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/http/standalone-proxy.js b/examples/http/standalone-proxy.js index 05c440698..7fc97b3d7 100644 --- a/examples/http/standalone-proxy.js +++ b/examples/http/standalone-proxy.js @@ -1,7 +1,7 @@ /* standalone-proxy.js: Example of proxying over HTTP with a standalone HTTP server. - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/websocket/latent-websocket-proxy.js b/examples/websocket/latent-websocket-proxy.js index 06569bf5f..a80a16edd 100644 --- a/examples/websocket/latent-websocket-proxy.js +++ b/examples/websocket/latent-websocket-proxy.js @@ -1,7 +1,7 @@ /* standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server. - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/websocket/standalone-websocket-proxy.js b/examples/websocket/standalone-websocket-proxy.js index e844ab097..a78ecedbc 100644 --- a/examples/websocket/standalone-websocket-proxy.js +++ b/examples/websocket/standalone-websocket-proxy.js @@ -1,7 +1,7 @@ /* standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server. - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/websocket/websocket-proxy.js b/examples/websocket/websocket-proxy.js index 767f58c00..dfd46e064 100644 --- a/examples/websocket/websocket-proxy.js +++ b/examples/websocket/websocket-proxy.js @@ -1,7 +1,7 @@ /* web-socket-proxy.js: Example of proxying over HTTP and WebSockets. - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + Copyright (c) Nodejitsu 2013 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From 2142c506e08f56d52e1995da5506c3e032f19c3c Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 27 Nov 2013 09:42:56 -0500 Subject: [PATCH 192/210] [examples] add example of gzip using the connect.compress() middleware --- examples/middleware/gzip-middleware.js | 65 ++++++++++++++++++++++++++ examples/package.json | 3 +- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 examples/middleware/gzip-middleware.js diff --git a/examples/middleware/gzip-middleware.js b/examples/middleware/gzip-middleware.js new file mode 100644 index 000000000..ec8725940 --- /dev/null +++ b/examples/middleware/gzip-middleware.js @@ -0,0 +1,65 @@ +/* + gzip-middleware.js: Basic example of `connect-gzip` middleware in node-http-proxy + + Copyright (c) Nodejitsu 2013 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + connect = require('connect') + httpProxy = require('../../lib/http-proxy'); + +// +// Basic Connect App +// +connect.createServer( + connect.compress({ + // Pass to connect.compress() the options + // that you need, just for show the example + // we use threshold to 1 + threshold: 1 + }), + function (req, res) { + proxy.web(req, res); + } +).listen(8000); + +// +// Basic HTTP Proxy +// +var proxy = httpProxy.createProxyServer({ + target: 'http://localhost:9000' +}); + +// +// Target Http Server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); + res.end(); +}).listen(9000); + +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/package.json b/examples/package.json index be054638e..7db2ae68a 100644 --- a/examples/package.json +++ b/examples/package.json @@ -5,6 +5,7 @@ "dependencies": { "colors": "~0.6.2", "socket.io": "~0.9.16", - "socket.io-client": "~0.9.16" + "socket.io-client": "~0.9.16", + "connect": "~2.11.0" } } From de3ff11656b4a847de3a63b28feed39b6c816480 Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 27 Nov 2013 09:56:35 -0500 Subject: [PATCH 193/210] [examples] updated the modifyResponse-middleware example --- examples/middleware/gzip-middleware.js | 2 +- .../middleware/modifyResponse-middleware.js | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 examples/middleware/modifyResponse-middleware.js diff --git a/examples/middleware/gzip-middleware.js b/examples/middleware/gzip-middleware.js index ec8725940..ee32e445b 100644 --- a/examples/middleware/gzip-middleware.js +++ b/examples/middleware/gzip-middleware.js @@ -46,7 +46,7 @@ connect.createServer( ).listen(8000); // -// Basic HTTP Proxy +// Basic Http Proxy Server // var proxy = httpProxy.createProxyServer({ target: 'http://localhost:9000' diff --git a/examples/middleware/modifyResponse-middleware.js b/examples/middleware/modifyResponse-middleware.js new file mode 100644 index 000000000..c4a59a2f0 --- /dev/null +++ b/examples/middleware/modifyResponse-middleware.js @@ -0,0 +1,67 @@ +/* + modifyBody-middleware.js: Example of middleware which modifies response + + Copyright (c) Nodejitsu 2013 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + connect = require('connect'), + httpProxy = require('../../lib/http-proxy'); + +// +// Basic Connect App +// +connect.createServer( + function (req, res, next) { + var _write = res.write; + + res.write = function (data) { + _write.call(res, data.toString().replace("Ruby", "nodejitsu")); + } + next(); + }, + function (req, res) { + proxy.web(req, res); + } +).listen(8000); + +// +// Basic Http Proxy Server +// +var proxy = httpProxy.createProxyServer({ + target: 'http://localhost:9000' +}); + +// +// Target Http Server +// +http.createServer(function (req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello, I know Ruby\n'); +}).listen(9000); + +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); + From d7064f2e1e149fe870cbb158932cb99f9f192fce Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 27 Nov 2013 16:35:38 -0500 Subject: [PATCH 194/210] [examples] added error-handling using callbacks and HTTP-to-HTTPS examples --- examples/http/error-handling.js | 63 ++++++++++++++++++++++++++++ examples/http/proxy-http-to-https.js | 46 ++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 examples/http/error-handling.js create mode 100644 examples/http/proxy-http-to-https.js diff --git a/examples/http/error-handling.js b/examples/http/error-handling.js new file mode 100644 index 000000000..292fb6144 --- /dev/null +++ b/examples/http/error-handling.js @@ -0,0 +1,63 @@ +/* + error-handling.js: Example of handle erros for HTTP and WebSockets + + Copyright (c) Nodejitsu 2013 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('../../lib/http-proxy'); + +// +// HTTP Proxy Server +// +var proxy = httpProxy.createProxyServer({target:'http://localhost:9000', ws:true}); + +// +// Example of error handling +// +function requestHandler(req, res) { + // Pass a callback to the web proxy method + // and catch the error there. + proxy.web(req, res, function (err) { + // Now you can get the err + // and handle it by your self + // if (err) throw err; + res.writeHead(502); + res.end("There was an error proxying your request"); + }); + + // In a websocket request case + req.on('upgrade', function (req, socket, head) { + proxy.ws(req, socket, head, function (err) { + // Now you can get the err + // and handle it by your self + // if (err) throw err; + socket.close(); + }) + }) +} + +http.createServer(requestHandler).listen(8000); +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); diff --git a/examples/http/proxy-http-to-https.js b/examples/http/proxy-http-to-https.js new file mode 100644 index 000000000..ffaa6fa04 --- /dev/null +++ b/examples/http/proxy-http-to-https.js @@ -0,0 +1,46 @@ +/* + proxy-http-to-https.js: Basic example of proxying over HTTP to a target HTTPS server + + Copyright (c) Nodejitsu 2013 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var https = require('https'), + http = require('http'), + util = require('util'), + path = require('path'), + fs = require('fs'), + colors = require('colors'), + httpProxy = require('../../lib/http-proxy'); + +// +// Create a HTTP Proxy server with a HTTPS target +// +httpProxy.createProxyServer({ + target: 'https://google.com', + agent : https.globalAgent, + headers: { + host: 'google.com' + } +}).listen(8000); + +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); \ No newline at end of file From c82ff2c3c0c0165421fbc4e7e94fa3f59d59aa38 Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 27 Nov 2013 17:02:06 -0500 Subject: [PATCH 195/210] [examples] updated bodyDecoder middleware example --- examples/helpers/store.js | 64 ++++++++++ examples/middleware/bodyDecoder-middleware.js | 119 ++++++++++++++++++ examples/package.json | 4 +- 3 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 examples/helpers/store.js create mode 100644 examples/middleware/bodyDecoder-middleware.js diff --git a/examples/helpers/store.js b/examples/helpers/store.js new file mode 100644 index 000000000..e2860573f --- /dev/null +++ b/examples/helpers/store.js @@ -0,0 +1,64 @@ + +// +// just to make these example a little bit interesting, +// make a little key value store with an http interface +// (see couchbd for a grown-up version of this) +// +// API: +// GET / +// retrive list of keys +// +// GET /[url] +// retrive object stored at [url] +// will respond with 404 if there is nothing stored at [url] +// +// POST /[url] +// +// JSON.parse the body and store it under [url] +// will respond 400 (bad request) if body is not valid json. +// +// TODO: cached map-reduce views and auto-magic sharding. +// +var Store = module.exports = function Store () { + this.store = {}; +}; + +Store.prototype = { + get: function (key) { + return this.store[key] + }, + set: function (key, value) { + return this.store[key] = value + }, + handler:function () { + var store = this + return function (req, res) { + function send (obj, status) { + res.writeHead(200 || status,{'Content-Type': 'application/json'}) + res.write(JSON.stringify(obj) + '\n') + res.end() + } + var url = req.url.split('?').shift() + if (url === '/') { + console.log('get index') + return send(Object.keys(store.store)) + } else if (req.method == 'GET') { + var obj = store.get (url) + send(obj || {error: 'not_found', url: url}, obj ? 200 : 404) + } else { + //post: buffer body, and parse. + var body = '', obj + req.on('data', function (c) { body += c}) + req.on('end', function (c) { + try { + obj = JSON.parse(body) + } catch (err) { + return send (err, 400) + } + store.set(url, obj) + send({ok: true}) + }) + } + } + } +} diff --git a/examples/middleware/bodyDecoder-middleware.js b/examples/middleware/bodyDecoder-middleware.js new file mode 100644 index 000000000..555c3d154 --- /dev/null +++ b/examples/middleware/bodyDecoder-middleware.js @@ -0,0 +1,119 @@ +/* + bodyDecoder-middleware.js: Basic example of `connect.bodyParser()` middleware in node-http-proxy + + Copyright (c) Nodejitsu 2013 + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +var http = require('http'), + connect = require('connect'), + request = require('request'), + colors = require('colors'), + util = require('util'), + Store = require('../helpers/store'), + httpProxy = require('../../lib/http-proxy'), + proxy = httpProxy.createProxyServer({}); + +http.createServer(new Store().handler()).listen(7531, function () { + util.puts('http '.blue + 'greetings '.green + 'server'.blue + ' started '.green.bold + 'on port '.blue + '7531'.yellow); +//try these commands: +// get index: +// curl localhost:7531 +// [] +// +// get a doc: +// curl localhost:7531/foo +// {"error":"not_found"} +// +// post an doc: +// curl -X POST localhost:7531/foo -d '{"content": "hello", "type": "greeting"}' +// {"ok":true} +// +// get index (now, not empty) +// curl localhost:7531 +// ["/foo"] +// +// get doc +// curl localhost:7531/foo +// {"content": "hello", "type": "greeting"} + +// +// now, suppose we wanted to direct all objects where type == "greeting" to a different store +// than where type == "insult" +// +// we can use connect connect-bodyDecoder and some custom logic to send insults to another Store. + +//insult server: + + http.createServer(new Store().handler()).listen(2600, function () { + util.puts('http '.blue + 'insults '.red + 'server'.blue + ' started '.green.bold + 'on port '.blue + '2600'.yellow); + + //greetings -> 7531, insults-> 2600 + + // now, start a proxy server. + + //don't worry about incoming contont type + //bodyParser.parse[''] = JSON.parse + + connect.createServer( + //refactor the body parser and re-streamer into a separate package + connect.bodyParser(), + //body parser absorbs the data and end events before passing control to the next + // middleware. if we want to proxy it, we'll need to re-emit these events after + //passing control to the middleware. + require('connect-restreamer')(), + function (req, res) { + //if your posting an obect which contains type: "insult" + //it will get redirected to port 2600. + //normal get requests will go to 7531 nad will not return insults. + var port = (req.body && req.body.type === 'insult' ? 2600 : 7531) + proxy.web(req, res, { target: { host: 'localhost', port: port }}); + } + ).listen(1337, function () { + util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '1337'.yellow); + //bodyParser needs content-type set to application/json + //if we use request, it will set automatically if we use the 'json:' field. + function post (greeting, type) { + request.post({ + url: 'http://localhost:1337/' + greeting, + json: {content: greeting, type: type || "greeting"} + }) + } + post("hello") + post("g'day") + post("kiora") + post("houdy") + post("java", "insult") + + //now, the insult should have been proxied to 2600 + + //curl localhost:2600 + //["/java"] + + //but the greetings will be sent to 7531 + + //curl localhost:7531 + //["/hello","/g%27day","/kiora","/houdy"] + + }) + }) +}); \ No newline at end of file diff --git a/examples/package.json b/examples/package.json index 7db2ae68a..3daede1f1 100644 --- a/examples/package.json +++ b/examples/package.json @@ -6,6 +6,8 @@ "colors": "~0.6.2", "socket.io": "~0.9.16", "socket.io-client": "~0.9.16", - "connect": "~2.11.0" + "connect": "~2.11.0", + "request": "~2.27.0", + "connect-restreamer": "~1.0.0" } } From bc236d7e95ef10bc17cf551eea2cd2fb9bf265eb Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 27 Nov 2013 17:58:00 -0500 Subject: [PATCH 196/210] [tests] Added a test case for run all the examples * I changed all the ports across examples to be different and can run at same time --- .../simple-balancer-with-websockets.js | 2 +- examples/balancer/simple-balancer.js | 2 +- examples/http/basic-proxy.js | 10 +-- examples/http/concurrent-proxy.js | 10 +-- examples/http/custom-proxy-error.js | 6 +- examples/http/forward-and-target-proxy.js | 16 ++--- examples/http/forward-proxy.js | 10 +-- examples/http/latent-proxy.js | 10 +-- examples/http/proxy-http-to-https.js | 4 +- examples/http/proxy-https-to-http.js | 10 +-- examples/http/proxy-https-to-https.js | 10 +-- examples/http/standalone-proxy.js | 10 +-- examples/middleware/gzip-middleware.js | 10 +-- .../middleware/modifyResponse-middleware.js | 10 +-- examples/websocket/latent-websocket-proxy.js | 8 +-- .../websocket/standalone-websocket-proxy.js | 8 +-- examples/websocket/websocket-proxy.js | 6 +- package.json | 3 +- test/examples-test.js | 71 +++++++++++++++++++ 19 files changed, 144 insertions(+), 72 deletions(-) create mode 100644 test/examples-test.js diff --git a/examples/balancer/simple-balancer-with-websockets.js b/examples/balancer/simple-balancer-with-websockets.js index b17afc772..cc13f4b5c 100644 --- a/examples/balancer/simple-balancer-with-websockets.js +++ b/examples/balancer/simple-balancer-with-websockets.js @@ -80,5 +80,5 @@ server.on('upgrade', function (req, socket, head) { nextProxy().ws(req, socket, head); }); -server.listen(8080); +server.listen(8001); \ No newline at end of file diff --git a/examples/balancer/simple-balancer.js b/examples/balancer/simple-balancer.js index 80b91760c..3f53cc63d 100644 --- a/examples/balancer/simple-balancer.js +++ b/examples/balancer/simple-balancer.js @@ -59,6 +59,6 @@ http.createServer(function (req, res) { // ...and then the server you just used becomes the last item in the list. // addresses.push(target); -}).listen(8000); +}).listen(8021); // Rinse; repeat; enjoy. \ No newline at end of file diff --git a/examples/http/basic-proxy.js b/examples/http/basic-proxy.js index 640318c11..e9be0d79b 100644 --- a/examples/http/basic-proxy.js +++ b/examples/http/basic-proxy.js @@ -44,8 +44,8 @@ util.puts(welcome.rainbow.bold); // Basic Http Proxy Server // httpProxy.createServer({ - target:'http://localhost:9000' -}).listen(8000); + target:'http://localhost:9003' +}).listen(8003); // // Target Http Server @@ -54,7 +54,7 @@ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); res.end(); -}).listen(9000); +}).listen(9003); -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8003'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9003 '.yellow); diff --git a/examples/http/concurrent-proxy.js b/examples/http/concurrent-proxy.js index 5ca1054b8..30aa53dd6 100644 --- a/examples/http/concurrent-proxy.js +++ b/examples/http/concurrent-proxy.js @@ -33,8 +33,8 @@ var util = require('util'), // Basic Http Proxy Server // httpProxy.createServer({ - target:'http://localhost:9000' -}).listen(8000); + target:'http://localhost:9004' +}).listen(8004); // // Target Http Server @@ -62,7 +62,7 @@ http.createServer(function (req, res) { connections.shift()(); } } -}).listen(9000); +}).listen(9004); -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8004'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9004 '.yellow); diff --git a/examples/http/custom-proxy-error.js b/examples/http/custom-proxy-error.js index dd62273a7..1c54b5ab8 100644 --- a/examples/http/custom-proxy-error.js +++ b/examples/http/custom-proxy-error.js @@ -33,13 +33,13 @@ var util = require('util'), // Http Proxy Server with bad target // var proxy = httpProxy.createServer({ - target:'http://localhost:9000' + target:'http://localhost:9005' }); // // Tell the proxy to listen on port 8000 // -proxy.listen(8000); +proxy.listen(8005); // // Listen for the `error` event on `proxy`. @@ -52,4 +52,4 @@ proxy.on('error', function (err, req, res) { }); -util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with custom error message'.magenta.underline); \ No newline at end of file +util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8005 '.yellow + 'with custom error message'.magenta.underline); \ No newline at end of file diff --git a/examples/http/forward-and-target-proxy.js b/examples/http/forward-and-target-proxy.js index 0d5acd1f6..c564bfbbd 100644 --- a/examples/http/forward-and-target-proxy.js +++ b/examples/http/forward-and-target-proxy.js @@ -34,14 +34,14 @@ var util = require('util'), // httpProxy.createServer({ target: { - port: 9000, + port: 9006, host: 'localhost' }, forward: { - port: 9001, + port: 9007, host: 'localhost' } -}).listen(8000); +}).listen(8006); // // Target Http Server @@ -50,7 +50,7 @@ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); res.end(); -}).listen(9000); +}).listen(9006); // // Target Http Forwarding Server @@ -60,8 +60,8 @@ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); res.end(); -}).listen(9001); +}).listen(9007); -util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with forward proxy'.magenta.underline); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); -util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9001 '.yellow); \ No newline at end of file +util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8006 '.yellow + 'with forward proxy'.magenta.underline); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9006 '.yellow); +util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9007 '.yellow); \ No newline at end of file diff --git a/examples/http/forward-proxy.js b/examples/http/forward-proxy.js index 5f93c49f6..d94f48414 100644 --- a/examples/http/forward-proxy.js +++ b/examples/http/forward-proxy.js @@ -34,10 +34,10 @@ var util = require('util'), // httpProxy.createServer({ forward: { - port: 9000, + port: 9019, host: 'localhost' } -}).listen(8000); +}).listen(8019); // // Target Http Forwarding Server @@ -47,7 +47,7 @@ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); res.end(); -}).listen(9000); +}).listen(9019); -util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with forward proxy'.magenta.underline); -util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); \ No newline at end of file +util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8019 '.yellow + 'with forward proxy'.magenta.underline); +util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9019 '.yellow); \ No newline at end of file diff --git a/examples/http/latent-proxy.js b/examples/http/latent-proxy.js index 85de6338f..01ec93cc7 100644 --- a/examples/http/latent-proxy.js +++ b/examples/http/latent-proxy.js @@ -36,10 +36,10 @@ var proxy = httpProxy.createProxyServer(); http.createServer(function (req, res) { setTimeout(function () { proxy.web(req, res, { - target: 'http://localhost:9000' + target: 'http://localhost:9008' }); }, 500); -}).listen(8000); +}).listen(8008); // // Target Http Server @@ -48,7 +48,7 @@ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); res.end(); -}).listen(9000); +}).listen(9008); -util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with latency'.magenta.underline); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8008 '.yellow + 'with latency'.magenta.underline); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9008 '.yellow); diff --git a/examples/http/proxy-http-to-https.js b/examples/http/proxy-http-to-https.js index ffaa6fa04..ba5c83816 100644 --- a/examples/http/proxy-http-to-https.js +++ b/examples/http/proxy-http-to-https.js @@ -41,6 +41,6 @@ httpProxy.createProxyServer({ headers: { host: 'google.com' } -}).listen(8000); +}).listen(8011); -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); \ No newline at end of file +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8011'.yellow); \ No newline at end of file diff --git a/examples/http/proxy-https-to-http.js b/examples/http/proxy-https-to-http.js index 1b6ac92bd..d2a2d5c0d 100644 --- a/examples/http/proxy-https-to-http.js +++ b/examples/http/proxy-https-to-http.js @@ -40,7 +40,7 @@ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('hello http over https\n'); res.end(); -}).listen(9000); +}).listen(9009); // // Create the HTTPS proxy server listening on port 8000 @@ -48,13 +48,13 @@ http.createServer(function (req, res) { httpProxy.createServer({ target: { host: 'localhost', - port: 9000 + port: 9009 }, ssl: { key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'), cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8') } -}).listen(8000); +}).listen(8009); -util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8009'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9009 '.yellow); diff --git a/examples/http/proxy-https-to-https.js b/examples/http/proxy-https-to-https.js index f0d06e8e4..e543f98a7 100644 --- a/examples/http/proxy-https-to-https.js +++ b/examples/http/proxy-https-to-https.js @@ -44,16 +44,16 @@ https.createServer(httpsOpts, function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('hello https\n'); res.end(); -}).listen(9000); +}).listen(9010); // // Create the proxy server listening on port 443 // httpProxy.createServer({ ssl: httpsOpts, - target: 'https://localhost:9000', + target: 'https://localhost:9010', secure: false -}).listen(8000); +}).listen(8010); -util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('https server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8010'.yellow); +util.puts('https server '.blue + 'started '.green.bold + 'on port '.blue + '9010 '.yellow); diff --git a/examples/http/standalone-proxy.js b/examples/http/standalone-proxy.js index 7fc97b3d7..410d70b31 100644 --- a/examples/http/standalone-proxy.js +++ b/examples/http/standalone-proxy.js @@ -36,10 +36,10 @@ var proxy = new httpProxy.createProxyServer(); http.createServer(function (req, res) { setTimeout(function () { proxy.web(req, res, { - target: 'http://localhost:9000' + target: 'http://localhost:9002' }); }, 200); -}).listen(8000); +}).listen(8002); // // Target Http Server @@ -48,7 +48,7 @@ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); res.end(); -}).listen(9000); +}).listen(9002); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow + 'with proxy.web() handler'.cyan.underline + ' and latency'.magenta); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with proxy.web() handler'.cyan.underline + ' and latency'.magenta); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9001 '.yellow); diff --git a/examples/middleware/gzip-middleware.js b/examples/middleware/gzip-middleware.js index ee32e445b..756b68fa3 100644 --- a/examples/middleware/gzip-middleware.js +++ b/examples/middleware/gzip-middleware.js @@ -43,13 +43,13 @@ connect.createServer( function (req, res) { proxy.web(req, res); } -).listen(8000); +).listen(8012); // // Basic Http Proxy Server // var proxy = httpProxy.createProxyServer({ - target: 'http://localhost:9000' + target: 'http://localhost:9012' }); // @@ -59,7 +59,7 @@ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); res.end(); -}).listen(9000); +}).listen(9012); -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8012'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9012 '.yellow); diff --git a/examples/middleware/modifyResponse-middleware.js b/examples/middleware/modifyResponse-middleware.js index c4a59a2f0..fdd7e6596 100644 --- a/examples/middleware/modifyResponse-middleware.js +++ b/examples/middleware/modifyResponse-middleware.js @@ -45,13 +45,13 @@ connect.createServer( function (req, res) { proxy.web(req, res); } -).listen(8000); +).listen(8013); // // Basic Http Proxy Server // var proxy = httpProxy.createProxyServer({ - target: 'http://localhost:9000' + target: 'http://localhost:9013' }); // @@ -60,8 +60,8 @@ var proxy = httpProxy.createProxyServer({ http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello, I know Ruby\n'); -}).listen(9000); +}).listen(9013); -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); +util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8013'.yellow); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9013 '.yellow); diff --git a/examples/websocket/latent-websocket-proxy.js b/examples/websocket/latent-websocket-proxy.js index a80a16edd..64d3d7ce0 100644 --- a/examples/websocket/latent-websocket-proxy.js +++ b/examples/websocket/latent-websocket-proxy.js @@ -43,7 +43,7 @@ catch (ex) { // Create the target HTTP server and setup // socket.io on it. // -var server = io.listen(9000); +var server = io.listen(9016); server.sockets.on('connection', function (client) { util.debug('Got websocket connection'); @@ -60,7 +60,7 @@ server.sockets.on('connection', function (client) { var proxy = new httpProxy.createProxyServer({ target: { host: 'localhost', - port: 9000 + port: 9016 } }); @@ -78,12 +78,12 @@ proxyServer.on('upgrade', function (req, socket, head) { }, 1000); }); -proxyServer.listen(8000); +proxyServer.listen(8016); // // Setup the socket.io client against our proxy // -var ws = client.connect('ws://localhost:8000'); +var ws = client.connect('ws://localhost:8016'); ws.on('message', function (msg) { util.debug('Got message: ' + msg); diff --git a/examples/websocket/standalone-websocket-proxy.js b/examples/websocket/standalone-websocket-proxy.js index a78ecedbc..81d019650 100644 --- a/examples/websocket/standalone-websocket-proxy.js +++ b/examples/websocket/standalone-websocket-proxy.js @@ -43,7 +43,7 @@ catch (ex) { // Create the target HTTP server and setup // socket.io on it. // -var server = io.listen(9000); +var server = io.listen(9015); server.sockets.on('connection', function (client) { util.debug('Got websocket connection'); @@ -60,7 +60,7 @@ server.sockets.on('connection', function (client) { var proxy = new httpProxy.createProxyServer({ target: { host: 'localhost', - port: 9000 + port: 9015 } }); var proxyServer = http.createServer(function (req, res) { @@ -75,12 +75,12 @@ proxyServer.on('upgrade', function (req, socket, head) { proxy.ws(req, socket, head); }); -proxyServer.listen(8000); +proxyServer.listen(8015); // // Setup the socket.io client against our proxy // -var ws = client.connect('ws://localhost:8000'); +var ws = client.connect('ws://localhost:8015'); ws.on('message', function (msg) { util.debug('Got message: ' + msg); diff --git a/examples/websocket/websocket-proxy.js b/examples/websocket/websocket-proxy.js index dfd46e064..33d78c675 100644 --- a/examples/websocket/websocket-proxy.js +++ b/examples/websocket/websocket-proxy.js @@ -43,7 +43,7 @@ catch (ex) { // Create the target HTTP server and setup // socket.io on it. // -var server = io.listen(9000); +var server = io.listen(9014); server.sockets.on('connection', function (client) { util.debug('Got websocket connection'); @@ -57,12 +57,12 @@ server.sockets.on('connection', function (client) { // // Create a proxy server with node-http-proxy // -httpProxy.createServer({ target: 'ws://localhost:9000', ws: true }).listen(8000); +httpProxy.createServer({ target: 'ws://localhost:9014', ws: true }).listen(8014); // // Setup the socket.io client against our proxy // -var ws = client.connect('ws://localhost:8000'); +var ws = client.connect('ws://localhost:8014'); ws.on('message', function (msg) { util.debug('Got message: ' + msg); diff --git a/package.json b/package.json index 98cd9b5d4..5c7c3a1eb 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "blanket" : "*", "ws" : "*", "socket.io" : "*", - "socket.io-client" : "*" + "socket.io-client" : "*", + "async" : "*" }, "scripts" : { "coveralls" : "mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js", diff --git a/test/examples-test.js b/test/examples-test.js new file mode 100644 index 000000000..234587bcb --- /dev/null +++ b/test/examples-test.js @@ -0,0 +1,71 @@ +/* + examples-test.js: Test to run all the examples + + Copyright (c) Nodejitsu 2013 + +*/ +var path = require('path'), + fs = require('fs'), + spawn = require('child_process').spawn, + expect = require('expect.js'), + async = require('async'); + +var rootDir = path.join(__dirname, '..'), + examplesDir = path.join(rootDir, 'examples'); + +describe('http-proxy examples', function () { + describe('Before testing examples', function () { + // Set a timeout to avoid this error + this.timeout(30 * 1000); + it('should have installed dependencies', function (done) { + async.waterfall([ + // + // 1. Read files in examples dir + // + async.apply(fs.readdir, examplesDir), + // + // 2. If node_modules exists, continue. Otherwise + // exec `npm` to install them + // + function checkNodeModules(files, next) { + if (files.indexOf('node_modules') !== -1) { + return next(); + } + + console.log('Warning: installing dependencies, this operation could take a while'); + + var child = spawn('npm', ['install', '-f'], { + cwd: examplesDir + }); + + child.on('exit', function (code) { + return code + ? next(new Error('npm install exited with non-zero exit code')) + : next(); + }); + }, + // + // 3. Read files in examples dir again to ensure the install + // worked as expected. + // + async.apply(fs.readdir, examplesDir), + ], done); + }) + }); + + describe('Requiring all the examples', function () { + it('should have no errors', function (done) { + async.each(['balancer', 'http', 'middleware', 'websocket'], function (dir, cb) { + var name = 'examples/' + dir, + files = fs.readdirSync(path.join(rootDir, 'examples', dir)); + + async.each(files, function (file, callback) { + var example; + expect(function () { example = require(path.join(examplesDir, dir, file)); }).to.not.throwException(); + expect(example).to.be.an('object'); + callback(); + }, cb); + }, done); + }) + }) +}) \ No newline at end of file From d83fdf69a1121bfcfba72bbffcd3105ae5852c56 Mon Sep 17 00:00:00 2001 From: cronopio Date: Mon, 9 Dec 2013 12:34:28 -0500 Subject: [PATCH 197/210] [tests] disabled the examples-test by now --- test/examples-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/examples-test.js b/test/examples-test.js index 234587bcb..8464e3fe5 100644 --- a/test/examples-test.js +++ b/test/examples-test.js @@ -13,7 +13,7 @@ var path = require('path'), var rootDir = path.join(__dirname, '..'), examplesDir = path.join(rootDir, 'examples'); -describe('http-proxy examples', function () { +describe.skip('http-proxy examples', function () { describe('Before testing examples', function () { // Set a timeout to avoid this error this.timeout(30 * 1000); From e2a5d513cac3ebceff446787fa106c7f00caf785 Mon Sep 17 00:00:00 2001 From: cronopio Date: Wed, 18 Dec 2013 11:19:17 -0500 Subject: [PATCH 198/210] Set travis to run `npm test` while we fix coveralss.io integration --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d7eeacc3f..ba5be4174 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,4 +9,4 @@ notifications: irc: "irc.freenode.org#nodejitsu" script: - npm run-script coveralls + npm test From db12f6c24e22c034c698457cc28ff60c990b55a5 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 20 Dec 2013 20:22:40 +0100 Subject: [PATCH 199/210] [docs] add UPGRADING.md --- README.md | 2 ++ UPGRADING.md | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 UPGRADING.md diff --git a/README.md b/README.md index ed25eb240..4c6f95751 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ proxies and load balancers.

+## Looking to Upgrade from 0.8.x ? Click [here](UPGRADING.md) + ### Core Concept A new proxy is created by calling `createProxyServer` and passing diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 000000000..8fb525c61 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,26 @@ +`caronte` is a from-scratch implementation of `http-proxy` and, as such +brings some breaking changes to APIs. + +## Server creation + +Available through `.createServer()` or `.createProxyServer()`. +Check the README.md for a more detailed explanation of the parameters. + +## Proxying + +Web proying is done by calling the `.web()` method on a Proxy instance. Websockets +are proxied by the `.ws()` method. + +## Error Handling + +It is possible to listen globally on the `error` event on the server. In alternative, a +callback passed to `.web()` or `.ws()` as last parameter is also accepted. + +## Dropped + +Since the API was rewritten to be extremely flexible we decided to drop some features +which were in the core and delegate them to eventual "user-land" modules. + +- Middleware API +- ProxyTable API + From 9243444ac006f73c00b0f1f78c4a77f342b0b4e4 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 20 Dec 2013 20:24:49 +0100 Subject: [PATCH 200/210] fix docs --- CHANGELOG.md | 4 ++++ README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..21d6bb03c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +### v 1.0.0-alpha + +- Complete refactor with new API + diff --git a/README.md b/README.md index 4c6f95751..3ec4ed54d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ proxies and load balancers.

-## Looking to Upgrade from 0.8.x ? Click [here](UPGRADING.md) +### Looking to Upgrade from 0.8.x ? Click [here](UPGRADING.md) ### Core Concept From 162a42f58f515c5418ccfac0b68f4c928103b1e1 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 20 Dec 2013 21:33:49 +0100 Subject: [PATCH 201/210] [fix] legacy --- lib/http-proxy/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index c02735b02..eb1c5dddb 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -85,8 +85,8 @@ function createRightProxy(type) { function ProxyServer(options) { EE3.call(this); - this.web = createRightProxy('web')(options); - this.ws = createRightProxy('ws')(options); + this.web = this.proxyRequest = createRightProxy('web')(options); + this.ws = this.proxyWebsocketRequest = createRightProxy('ws')(options); this.options = options; this.webPasses = Object.keys(web).map(function(pass) { From e5991519dbc7838aa4b8aeb5077d1c1ec5a13813 Mon Sep 17 00:00:00 2001 From: yawnt Date: Fri, 27 Dec 2013 23:38:48 +0100 Subject: [PATCH 202/210] [docs] upgrade UPGRADING.md --- UPGRADING.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 8fb525c61..8c0db431c 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,26 +1,96 @@ -`caronte` is a from-scratch implementation of `http-proxy` and, as such +Looking to upgrade from `http-proxy@0.x.x` to `http-proxy@1.0`? You've come to the right place! +`http-proxy@1.0` is a from-scratch implementation of `http-proxy` and, as such brings some breaking changes to APIs. ## Server creation Available through `.createServer()` or `.createProxyServer()`. -Check the README.md for a more detailed explanation of the parameters. + +```javascript +httpProxy.createServer({ + target:'http://localhost:9003' +}).listen(8003); +``` + +Check the [README.md](https://github.com/nodejitsu/node-http-proxy/blob/caronte/README.md) for a more detailed explanation of the parameters. ## Proxying -Web proying is done by calling the `.web()` method on a Proxy instance. Websockets -are proxied by the `.ws()` method. +Web proying is done by calling the `.web()` method on a Proxy instance. You can check among some use cases in the [examples folder](https://github.com/nodejitsu/node-http-proxy/tree/caronte/examples/http) + +```javascript +// +// Create a HTTP Proxy server with a HTTPS target +// +httpProxy.createProxyServer({ + target: 'https://google.com', + agent : https.globalAgent, + headers: { + host: 'google.com' + } +}).listen(8011); + +``` + +Websockets are proxied by the `.ws()` method. The [examples folder](https://github.com/nodejitsu/node-http-proxy/tree/caronte/examples/websocket) again provides a lot of useful snippets! + +```javascript +var proxy = new httpProxy.createProxyServer({ + target: { + host: 'localhost', + port: 9015 + } +}); +var proxyServer = http.createServer(function (req, res) { + proxy.web(req, res); +}); + +// +// Listen to the `upgrade` event and proxy the +// WebSocket requests as well. +// +proxyServer.on('upgrade', function (req, socket, head) { + proxy.ws(req, socket, head); +}); +``` ## Error Handling It is possible to listen globally on the `error` event on the server. In alternative, a callback passed to `.web()` or `.ws()` as last parameter is also accepted. +```javascript +var proxy = httpProxy.createServer({ + target:'http://localhost:9005' +}); + +// +// Tell the proxy to listen on port 8000 +// +proxy.listen(8005); + +// +// Listen for the `error` event on `proxy`. +proxy.on('error', function (err, req, res) { + res.writeHead(500, { + 'Content-Type': 'text/plain' + }); + + res.end('Something went wrong. And we are reporting a custom error message.'); +}); +``` + ## Dropped Since the API was rewritten to be extremely flexible we decided to drop some features -which were in the core and delegate them to eventual "user-land" modules. +which were in the core and delegate them to eventual "userland" modules. - Middleware API - ProxyTable API +### Middleware API + +The new API makes it really easy to implement code that behaves like the old Middleware API. You can check some examples [here](https://github.com/nodejitsu/node-http-proxy/tree/caronte/examples/middleware) + + + From 182c76cd2322d4d4c041c2a964d51db396c5c96b Mon Sep 17 00:00:00 2001 From: Jarrett Cruger Date: Fri, 27 Dec 2013 19:01:28 -0500 Subject: [PATCH 203/210] [api] export the httpProxy.Server as the main export but preserve the createServer factory --- lib/http-proxy.js | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index 36589b39f..f59aca8b4 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -1,8 +1,14 @@ var http = require('http'), https = require('https'), url = require('url'), - httpProxy = require('./http-proxy/'), - proxy = exports; + httpProxy = require('./http-proxy/'); + +/** + * Export the "Server" so we can have an intuitive api for just creating + * a proxy + * + */ +module.exports = httpProxy.Server; /** * Creates the proxy server. @@ -19,23 +25,23 @@ var http = require('http'), * @api public */ -proxy.createProxyServer = proxy.createServer = function createProxyServer(options) { +module.exports.createProxyServer = module.exports.createServer = function createProxyServer(options) { /* * `options` is needed and it must have the following layout: - * - * { - * target : - * forward: - * agent : - * ssl : + * + * { + * target : + * forward: + * agent : + * ssl : * ws : - * xfwd : + * xfwd : * secure : - * } - * - * NOTE: `options.ws` and `options.ssl` are optional. - * `options.target and `options.forward` cannot be - * both missing + * } + * + * NOTE: `options.ws` and `options.ssl` are optional. + * `options.target and `options.forward` cannot be + * both missing * } */ From 6fa23e11f6dc0b9c09766b268611ade919bfaa08 Mon Sep 17 00:00:00 2001 From: Jarrett Cruger Date: Fri, 27 Dec 2013 19:04:44 -0500 Subject: [PATCH 204/210] [fix] comments --- lib/http-proxy.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index f59aca8b4..196dded44 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -4,9 +4,7 @@ var http = require('http'), httpProxy = require('./http-proxy/'); /** - * Export the "Server" so we can have an intuitive api for just creating - * a proxy - * + * Export the the proxy "Server" as the main export */ module.exports = httpProxy.Server; From c47adac391ca2f80ac1adad52e4fd652d85ac2a4 Mon Sep 17 00:00:00 2001 From: Jarrett Cruger Date: Sun, 29 Dec 2013 15:59:33 -0500 Subject: [PATCH 205/210] [fix] add `type` to before and after to grab correct `passes`, fixes #537 --- lib/http-proxy/index.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index eb1c5dddb..bd8b8a991 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -117,23 +117,33 @@ ProxyServer.prototype.listen = function(port) { return this; }; -ProxyServer.prototype.before = function(passName, callback) { - var i = false; - this.passes.forEach(function(v, idx) { +ProxyServer.prototype.before = function(type, passName, callback) { + if (type !== 'ws' || type !== 'web') { + throw new Error('type must be `web` or `ws`'); + } + var passes = (type === 'ws') ? this.wsPasses : this.webPasses, + i = false; + + passes.forEach(function(v, idx) { if(v.name === passName) i = idx; }) if(!i) throw new Error('No such pass'); - this.passes.splice(i, 0, callback); + passes.splice(i, 0, callback); }; -ProxyServer.prototype.after = function(passName, callback) { - var i = false; - this.passes.forEach(function(v, idx) { +ProxyServer.prototype.after = function(type, passName, callback) { + if (type !== 'ws' || type !== 'web') { + throw new Error('type must be `web` or `ws`'); + } + var passes = (type === 'ws') ? this.wsPasses : this.webPasses, + i = false; + + passes.forEach(function(v, idx) { if(v.name === passName) i = idx; }) if(!i) throw new Error('No such pass'); - this.passes.splice(i++, 0, callback); + passes.splice(i++, 0, callback); }; From a4ee8f9d82f71ef423c401b1f5e9f712b13cbc98 Mon Sep 17 00:00:00 2001 From: yawnt Date: Wed, 8 Jan 2014 10:19:06 +0100 Subject: [PATCH 206/210] [nuke] old files --- .gitignore | 3 - .travis.yml | 10 - CHANGELOG.md | 97 -- LICENSE | 23 - README.md | 646 ------------ benchmark/websockets-throughput.js | 88 -- bin/node-http-proxy | 113 -- config.sample.json | 10 - .../simple-balancer-with-websockets.js | 58 -- examples/balancer/simple-balancer.js | 36 - examples/helpers/store.js | 64 -- examples/http/basic-proxy.js | 58 -- examples/http/concurrent-proxy.js | 66 -- examples/http/custom-proxy-error.js | 54 - examples/http/forward-proxy.js | 63 -- examples/http/latent-proxy.js | 56 - examples/http/proxy-https-to-http.js | 51 - examples/http/proxy-https-to-https.js | 55 - examples/http/proxy-table.js | 51 - examples/http/standalone-proxy.js | 57 - examples/middleware/bodyDecoder-middleware.js | 87 -- .../middleware/gzip-middleware-proxytable.js | 54 - examples/middleware/gzip-middleware.js | 50 - examples/middleware/jsonp-middleware.js | 30 - .../middleware/modifyResponse-middleware.js | 57 - examples/middleware/url-middleware.js | 58 -- examples/middleware/url-middleware2.js | 30 - examples/package.json | 12 - examples/websocket/latent-websocket-proxy.js | 92 -- .../websocket/standalone-websocket-proxy.js | 87 -- examples/websocket/websocket-proxy.js | 69 -- lib/node-http-proxy.js | 397 ------- lib/node-http-proxy/http-proxy.js | 983 ------------------ lib/node-http-proxy/proxy-table.js | 282 ----- lib/node-http-proxy/routing-proxy.js | 322 ------ package.json | 47 - test/core/README.md | 10 - test/core/common.js | 190 ---- test/core/pummel/test-http-upload-timeout.js | 69 -- test/core/run | 90 -- test/core/run-single | 70 -- test/core/simple/test-http-chunked.js | 63 -- test/core/simple/test-http-client-abort.js | 80 -- test/core/simple/test-http-client-abort2.js | 41 - .../simple/test-http-client-upload-buf.js | 74 -- test/core/simple/test-http-client-upload.js | 77 -- test/core/simple/test-http-contentLength0.js | 42 - test/core/simple/test-http-eof-on-connect.js | 40 - test/core/simple/test-http-extra-response.js | 86 -- test/core/simple/test-http-head-request.js | 56 - ...test-http-head-response-has-no-body-end.js | 61 -- .../test-http-head-response-has-no-body.js | 58 -- test/core/simple/test-http-host-headers.js | 101 -- .../test-http-many-keep-alive-connections.js | 68 -- .../simple/test-http-multi-line-headers.js | 59 -- test/core/simple/test-http-proxy.js | 109 -- test/core/simple/test-http-response-close.js | 55 - .../simple/test-http-server-multiheaders.js | 59 -- test/core/simple/test-http-set-cookies.js | 84 -- test/core/simple/test-http-status-code.js | 69 -- test/core/simple/test-http-upgrade-server2.js | 72 -- test/core/simple/test-http.js | 108 -- test/examples-test.js | 26 - test/fixtures/agent2-cert.pem | 13 - test/fixtures/agent2-csr.pem | 10 - test/fixtures/agent2-key.pem | 9 - test/fixtures/agent2.cnf | 19 - test/helpers/http.js | 182 ---- test/helpers/index.js | 105 -- test/helpers/ws.js | 112 -- test/http/http-test.js | 102 -- test/http/routing-table-test.js | 107 -- test/macros/examples.js | 101 -- test/macros/http.js | 531 ---------- test/macros/index.js | 11 - test/macros/ws.js | 232 ----- test/ws/routing-table-test.js | 25 - test/ws/socket.io-test.js | 20 - test/ws/ws-test.js | 23 - 79 files changed, 7835 deletions(-) delete mode 100644 .gitignore delete mode 100644 .travis.yml delete mode 100644 CHANGELOG.md delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 benchmark/websockets-throughput.js delete mode 100755 bin/node-http-proxy delete mode 100644 config.sample.json delete mode 100644 examples/balancer/simple-balancer-with-websockets.js delete mode 100644 examples/balancer/simple-balancer.js delete mode 100644 examples/helpers/store.js delete mode 100644 examples/http/basic-proxy.js delete mode 100644 examples/http/concurrent-proxy.js delete mode 100644 examples/http/custom-proxy-error.js delete mode 100644 examples/http/forward-proxy.js delete mode 100644 examples/http/latent-proxy.js delete mode 100644 examples/http/proxy-https-to-http.js delete mode 100644 examples/http/proxy-https-to-https.js delete mode 100644 examples/http/proxy-table.js delete mode 100644 examples/http/standalone-proxy.js delete mode 100644 examples/middleware/bodyDecoder-middleware.js delete mode 100644 examples/middleware/gzip-middleware-proxytable.js delete mode 100644 examples/middleware/gzip-middleware.js delete mode 100644 examples/middleware/jsonp-middleware.js delete mode 100644 examples/middleware/modifyResponse-middleware.js delete mode 100644 examples/middleware/url-middleware.js delete mode 100644 examples/middleware/url-middleware2.js delete mode 100644 examples/package.json delete mode 100644 examples/websocket/latent-websocket-proxy.js delete mode 100644 examples/websocket/standalone-websocket-proxy.js delete mode 100644 examples/websocket/websocket-proxy.js delete mode 100644 lib/node-http-proxy.js delete mode 100644 lib/node-http-proxy/http-proxy.js delete mode 100644 lib/node-http-proxy/proxy-table.js delete mode 100644 lib/node-http-proxy/routing-proxy.js delete mode 100644 package.json delete mode 100644 test/core/README.md delete mode 100644 test/core/common.js delete mode 100644 test/core/pummel/test-http-upload-timeout.js delete mode 100755 test/core/run delete mode 100755 test/core/run-single delete mode 100644 test/core/simple/test-http-chunked.js delete mode 100644 test/core/simple/test-http-client-abort.js delete mode 100644 test/core/simple/test-http-client-abort2.js delete mode 100644 test/core/simple/test-http-client-upload-buf.js delete mode 100644 test/core/simple/test-http-client-upload.js delete mode 100644 test/core/simple/test-http-contentLength0.js delete mode 100644 test/core/simple/test-http-eof-on-connect.js delete mode 100644 test/core/simple/test-http-extra-response.js delete mode 100644 test/core/simple/test-http-head-request.js delete mode 100644 test/core/simple/test-http-head-response-has-no-body-end.js delete mode 100644 test/core/simple/test-http-head-response-has-no-body.js delete mode 100644 test/core/simple/test-http-host-headers.js delete mode 100644 test/core/simple/test-http-many-keep-alive-connections.js delete mode 100644 test/core/simple/test-http-multi-line-headers.js delete mode 100644 test/core/simple/test-http-proxy.js delete mode 100644 test/core/simple/test-http-response-close.js delete mode 100644 test/core/simple/test-http-server-multiheaders.js delete mode 100644 test/core/simple/test-http-set-cookies.js delete mode 100644 test/core/simple/test-http-status-code.js delete mode 100644 test/core/simple/test-http-upgrade-server2.js delete mode 100644 test/core/simple/test-http.js delete mode 100644 test/examples-test.js delete mode 100644 test/fixtures/agent2-cert.pem delete mode 100644 test/fixtures/agent2-csr.pem delete mode 100644 test/fixtures/agent2-key.pem delete mode 100644 test/fixtures/agent2.cnf delete mode 100644 test/helpers/http.js delete mode 100644 test/helpers/index.js delete mode 100644 test/helpers/ws.js delete mode 100644 test/http/http-test.js delete mode 100644 test/http/routing-table-test.js delete mode 100644 test/macros/examples.js delete mode 100644 test/macros/http.js delete mode 100644 test/macros/index.js delete mode 100644 test/macros/ws.js delete mode 100644 test/ws/routing-table-test.js delete mode 100644 test/ws/socket.io-test.js delete mode 100644 test/ws/ws-test.js diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 468b525c9..000000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -config.json -node_modules/ -npm-debug.log diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index efd470846..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: node_js -node_js: - - 0.8 - - "0.10" - - "0.11" - -notifications: - email: - - travis@nodejitsu.com - irc: "irc.freenode.org#nodejitsu" diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 2205ee874..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,97 +0,0 @@ -## ChangeLog for: node-http-proxy - -## Version 0.10.0 - 3/18/2013 - -- Breaking change: `proxyResponse` events are emitted on the `HttpProxy` or `RoutingProxy` instances as originally was intended in `0.9.x`. - -## Version 0.9.1 - 3/9/2013 - -- Ensure that `webSocketProxyError` and `proxyError` both receive the error (indexzero). - -## Version 0.9.0 - 3/9/2013 -- Fix #276 Ensure response.headers.location is defined (indexzero) -- Fix #248 Make options immutable in RoutingProxy (indexzero) -- Fix #359 Do not modify the protocol in redirect request for external sites. (indexzero) -- Fix #373 Do not use "Transfer-Encoding: chunked" header for proxied DELETE requests with no "Content-Length" header. (indexzero) -- Fix #338 Set "content-length" header to "0" if it is not already set on DELETE requests. (indexzero) -- Updates to README.md and Examples (ramitos, jamie-stackhouse, oost, indexzero) -- Fixes to ProxyTable and Routing Proxy (adjohnson916, otavoijr) -- New API for ProxyTable (mikkel, tglines) -- Add `options.timeout` for specifying socket timeouts (pdoran) -- Improve bin/node-http-proxy (niallo) -- Don't emit `proxyError` twice (erasmospunk) -- Fix memory leaks in WebSocket proxying -- Support UNIX Sockets (yosefd) -- Fix truncated chunked respones (jpetazzo) -- Allow upstream listeners to get `proxyResponse` (colinmollenhour) - -## Version 0.8.1 - 6/5/2012 -- Fix re-emitting of events in RoutingProxy (coderarity) -- New load balancer and middleware examples (marak) -- Docs updated including changelog (lot of gently people) - -## Version 0.8.0 - 12/23/2011 -- Improve support and tests for url segment routing (maxogden) -- Fix aborting connections when request close (c4milo) -- Avoid 'Transfer-Encoding' on HTTP/1.0 clients (koichik). -- Support for Node.js 0.6.x (mmalecki) - -## Version 0.7.3 - 10/4/2011 -- Fix setting x-forwarded headers (jesusabdullah) -- Updated examples (AvianFlu) - -## Version 0.7.0 - 9/10/2011 -- Handles to every throw-able resume() call (isaacs) -- Updated tests, README and package.json (indexzero) -- Added HttpProxy.close() method (indexzero) - -## Version 0.6.6 - 8/31/2011 -- Add more examples (dominictarr) -- Use of 'pkginfo' (indexzero) -- Handle cases where res.write throws (isaacs) -- Handles to every throw-able res.end call (isaacs) - -## Version 0.5.11 - 6/21/2011 -- Add more examples with WebSockets (indexzero) -- Update the documentation (indexzero) - -## Version 0.5.7 - 5/19/2011 -- Fix to README related to markup and fix some examples (benatkin) -- Improve WebSockets handling (indexzero) -- Improve WebSockets tests (indexzero) -- Improve https tests (olauzon) -- Add devDependencies to package.json (olauzon) -- Add 'proxyError' event (indexzero) -- Add 'x-forwarded-{port|proto}' headers support (indexzero) -- Keep-Alive connection supported (indexzero) - -## Version 0.5.0 - 4/15/2011 -- Remove winston in favor of custom events (indexzero) -- Add x-forwarded-for Header (indexzero) -- Fix WebSocket support (indexzero) -- Add tests / examples for WebSocket support (indexzero) -- Update .proxyRequest() and .proxyWebSocketRequest() APIs (indexzero) -- Add HTTPS support (indexzero) -- Add tests / examples for HTTPS support (indexzero) - -## Version 0.4.1 - 3/20/2011 -- Include missing dependency in package.json (indexzero) - -## Version 0.4.0 - 3/20/2011 -- Update for node.js 0.4.0 (indexzero) -- Remove pool dependency in favor of http.Agent (indexzero) -- Store buffered data using `.buffer()` instead of on the HttpProxy instance (indexzero) -- Change the ProxyTable to be a lookup table instead of actively proxying (indexzero) -- Allow for pure host-only matching in ProxyTable (indexzero) -- Use winston for logging (indexzero) -- Improve tests with async setup and more coverage (indexzero) -- Improve code documentation (indexzero) - -### Version 0.3.1 - 11/22/2010 -- Added node-http-proxy binary script (indexzero) -- Added experimental WebSocket support (indutny) -- Added forward proxy functionality (indexzero) -- Added proxy table for multiple target lookup (indexzero) -- Simplified tests using helpers.js (indexzero) -- Fixed uncaughtException bug with invalid proxy target (indutny) -- Added configurable logging for HttpProxy and ProxyTable (indexzero) \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index db9b0b1ed..000000000 --- a/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ - - node-http-proxy - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 594d02ad1..000000000 --- a/README.md +++ /dev/null @@ -1,646 +0,0 @@ -# node-http-proxy [![Build Status](https://secure.travis-ci.org/nodejitsu/node-http-proxy.png)](http://travis-ci.org/nodejitsu/node-http-proxy) - - - -## Battle-hardened node.js http proxy - -### Features - -* Reverse proxies incoming http.ServerRequest streams -* Can be used as a CommonJS module in node.js -* Reverse or Forward Proxy based on simple JSON-based configuration -* Supports [WebSockets][1] -* Supports [HTTPS][2] -* Minimal request overhead and latency -* Full suite of functional tests -* Battled-hardened through __production usage__ @ [nodejitsu.com][0] -* Written entirely in Javascript -* Easy to use API - - -node-http-proxy is `<= 0.8.x` compatible, if you're looking for a `>= 0.10` compatible version please check [caronte](https://github.com/nodejitsu/node-http-proxy/tree/caronte) - -### When to use node-http-proxy - -Let's suppose you were running multiple http application servers, but you only wanted to expose one machine to the internet. You could setup node-http-proxy on that one machine and then reverse-proxy the incoming http requests to locally running services which were not exposed to the outside network. - -### Installing npm (node package manager) - -``` -curl https://npmjs.org/install.sh | sh -``` - -### Installing node-http-proxy - -``` -npm install http-proxy -``` - -## Using node-http-proxy - -There are several ways to use node-http-proxy; the library is designed to be flexible so that it can be used by itself, or in conjunction with other node.js libraries / tools: - -1. Standalone HTTP Proxy server -2. Inside of another HTTP server (like Connect) -3. In conjunction with a Proxy Routing Table -4. As a forward-proxy with a reverse proxy -5. From the command-line as a long running process -6. customized with 3rd party middleware. - -In each of these scenarios node-http-proxy can handle any of these types of requests: - -1. HTTP Requests (http://) -2. HTTPS Requests (https://) -3. WebSocket Requests (ws://) -4. Secure WebSocket Requests (wss://) - -See the [examples][3] for more working sample code. - -### Setup a basic stand-alone proxy server - -``` js -var http = require('http'), - httpProxy = require('http-proxy'); -// -// Create your proxy server -// -httpProxy.createServer(9000, 'localhost').listen(8000); - -// -// Create your target server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); -``` - -### Setup a stand-alone proxy server with custom server logic - -``` js -var http = require('http'), - httpProxy = require('http-proxy'); - -// -// Create a proxy server with custom application logic -// -httpProxy.createServer(function (req, res, proxy) { - // - // Put your custom server logic here - // - proxy.proxyRequest(req, res, { - host: 'localhost', - port: 9000 - }); -}).listen(8000); - -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied: ' + req.url +'\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); -``` - -### Setup a stand-alone proxy server with latency (e.g. IO, etc) - -``` js -var http = require('http'), - httpProxy = require('http-proxy'); - -// -// Create a proxy server with custom application logic -// -httpProxy.createServer(function (req, res, proxy) { - // - // Buffer the request so that `data` and `end` events - // are not lost during async operation(s). - // - var buffer = httpProxy.buffer(req); - - // - // Wait for two seconds then respond: this simulates - // performing async actions before proxying a request - // - setTimeout(function () { - proxy.proxyRequest(req, res, { - host: 'localhost', - port: 9000, - buffer: buffer - }); - }, 2000); -}).listen(8000); - -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied: ' + req.url +'\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); -``` - -### Proxy requests within another http server - -``` js -var http = require('http'), - httpProxy = require('http-proxy'); - -// -// Create a new instance of HttProxy to use in your server -// -var proxy = new httpProxy.RoutingProxy(); - -// -// Create a regular http server and proxy its handler -// -http.createServer(function (req, res) { - // - // Put your custom server logic here, then proxy - // - proxy.proxyRequest(req, res, { - host: 'localhost', - port: 9000 - }); -}).listen(8001); - -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied: ' + req.url +'\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); -``` - -### Proxy requests using a ProxyTable -A Proxy Table is a simple lookup table that maps incoming requests to proxy target locations. Take a look at an example of the options you need to pass to httpProxy.createServer: - -``` js -var options = { - router: { - 'foo.com/baz': '127.0.0.1:8001', - 'foo.com/buz': '127.0.0.1:8002', - 'bar.com/buz': '127.0.0.1:8003' - } -}; -``` - -The above route table will take incoming requests to 'foo.com/baz' and forward them to '127.0.0.1:8001'. Likewise it will take incoming requests to 'foo.com/buz' and forward them to '127.0.0.1:8002'. The routes themselves are later converted to regular expressions to enable more complex matching functionality. We can create a proxy server with these options by using the following code: - -``` js -var proxyServer = httpProxy.createServer(options); -proxyServer.listen(80); -``` - -### Proxy requests using a 'Hostname Only' ProxyTable -As mentioned in the previous section, all routes passes to the ProxyTable are by default converted to regular expressions that are evaluated at proxy-time. This is good for complex URL rewriting of proxy requests, but less efficient when one simply wants to do pure hostname routing based on the HTTP 'Host' header. If you are only concerned with hostname routing, you change the lookup used by the internal ProxyTable: - -``` js -var options = { - hostnameOnly: true, - router: { - 'foo.com': '127.0.0.1:8001', - 'bar.com': '127.0.0.1:8002' - } -} -``` - -Notice here that I have not included paths on the individual domains because this is not possible when using only the HTTP 'Host' header. Care to learn more? See [RFC2616: HTTP/1.1, Section 14.23, "Host"][4]. - -### Proxy requests using a 'Pathname Only' ProxyTable - -If you dont care about forwarding to different hosts, you can redirect based on the request path. - -``` js -var options = { - pathnameOnly: true, - router: { - '/wiki': '127.0.0.1:8001', - '/blog': '127.0.0.1:8002', - '/api': '127.0.0.1:8003' - } -} -``` - -This comes in handy if you are running separate services or applications on separate paths. Note, using this option disables routing by hostname entirely. - - -### Proxy requests with an additional forward proxy -Sometimes in addition to a reverse proxy, you may want your front-facing server to forward traffic to another location. For example, if you wanted to load test your staging environment. This is possible when using node-http-proxy using similar JSON-based configuration to a proxy table: - -``` js -var proxyServerWithForwarding = httpProxy.createServer(9000, 'localhost', { - forward: { - port: 9000, - host: 'staging.com' - } -}); -proxyServerWithForwarding.listen(80); -``` - -The forwarding option can be used in conjunction with the proxy table options by simply including both the 'forward' and 'router' properties in the options passed to 'createServer'. - -### Listening for proxy events -Sometimes you want to listen to an event on a proxy. For example, you may want to listen to the 'end' event, which represents when the proxy has finished proxying a request. - -``` js -var httpProxy = require('http-proxy'); - -var server = httpProxy.createServer(function (req, res, proxy) { - var buffer = httpProxy.buffer(req); - - proxy.proxyRequest(req, res, { - host: '127.0.0.1', - port: 9000, - buffer: buffer - }); -}); - -server.proxy.on('end', function () { - console.log("The request was proxied."); -}); - -server.listen(8000); -``` - -It's important to remember not to listen for events on the proxy object in the function passed to `httpProxy.createServer`. Doing so would add a new listener on every request, which would end up being a disaster. - -## Using HTTPS -You have all the full flexibility of node-http-proxy offers in HTTPS as well as HTTP. The two basic scenarios are: with a stand-alone proxy server or in conjunction with another HTTPS server. - -### Proxying to HTTP from HTTPS -This is probably the most common use-case for proxying in conjunction with HTTPS. You have some front-facing HTTPS server, but all of your internal traffic is HTTP. In this way, you can reduce the number of servers to which your CA and other important security files are deployed and reduce the computational overhead from HTTPS traffic. - -Using HTTPS in `node-http-proxy` is relatively straight-forward: - -``` js -var fs = require('fs'), - http = require('http'), - https = require('https'), - httpProxy = require('http-proxy'); - -var options = { - https: { - key: fs.readFileSync('path/to/your/key.pem', 'utf8'), - cert: fs.readFileSync('path/to/your/cert.pem', 'utf8') - } -}; - -// -// Create a standalone HTTPS proxy server -// -httpProxy.createServer(8000, 'localhost', options).listen(8001); - -// -// Create an instance of HttpProxy to use with another HTTPS server -// -var proxy = new httpProxy.HttpProxy({ - target: { - host: 'localhost', - port: 8000 - } -}); -https.createServer(options.https, function (req, res) { - proxy.proxyRequest(req, res) -}).listen(8002); - -// -// Create the target HTTPS server for both cases -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('hello https\n'); - res.end(); -}).listen(8000); -``` - -### Using two certificates - -Suppose that your reverse proxy will handle HTTPS traffic for two different domains `fobar.com` and `barbaz.com`. -If you need to use two different certificates you can take advantage of [Server Name Indication](http://en.wikipedia.org/wiki/Server_Name_Indication). - -``` js -var https = require('https'), - path = require("path"), - fs = require("fs"), - crypto = require("crypto"); - -// -// generic function to load the credentials context from disk -// -function getCredentialsContext (cer) { - return crypto.createCredentials({ - key: fs.readFileSync(path.join(__dirname, 'certs', cer + '.key')), - cert: fs.readFileSync(path.join(__dirname, 'certs', cer + '.crt')) - }).context; -} - -// -// A certificate per domain hash -// -var certs = { - "fobar.com": getCredentialsContext("foobar"), - "barbaz.com": getCredentialsContext("barbaz") -}; - -// -// Proxy options -// -// This section assumes that myCert, myKey and myCa are defined (they are not -// in this example). With a SNICallback, the proxy needs a default set of -// certificates to use. -// -var options = { - https: { - SNICallback: function (hostname) { - return certs[hostname]; - }, - cert: myCert, - key: myKey, - ca: [myCa] - }, - hostnameOnly: true, - router: { - 'fobar.com': '127.0.0.1:8001', - 'barbaz.com': '127.0.0.1:8002' - } -}; - -// -// Create a standalone HTTPS proxy server -// -httpProxy.createServer(options).listen(8001); - -// -// Create the target HTTPS server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('hello https\n'); - res.end(); -}).listen(8000); - -``` - -### Proxying to HTTPS from HTTPS -Proxying from HTTPS to HTTPS is essentially the same as proxying from HTTPS to HTTP, but you must include the `target` option in when calling `httpProxy.createServer` or instantiating a new instance of `HttpProxy`. - -``` js -var fs = require('fs'), - https = require('https'), - httpProxy = require('http-proxy'); - -var options = { - https: { - key: fs.readFileSync('path/to/your/key.pem', 'utf8'), - cert: fs.readFileSync('path/to/your/cert.pem', 'utf8') - }, - target: { - https: true // This could also be an Object with key and cert properties - } -}; - -// -// Create a standalone HTTPS proxy server -// -httpProxy.createServer(8000, 'localhost', options).listen(8001); - -// -// Create an instance of HttpProxy to use with another HTTPS server -// -var proxy = new httpProxy.HttpProxy({ - target: { - host: 'localhost', - port: 8000, - https: true - } -}); - -https.createServer(options.https, function (req, res) { - proxy.proxyRequest(req, res); -}).listen(8002); - -// -// Create the target HTTPS server for both cases -// -https.createServer(options.https, function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('hello https\n'); - res.end(); -}).listen(8000); -``` -## Middleware - -`node-http-proxy` now supports connect middleware. Add middleware functions to your createServer call: - -``` js -httpProxy.createServer( - require('connect-gzip').gzip(), - 9000, 'localhost' -).listen(8000); -``` - -A regular request we receive is to support the modification of html/xml content that is returned in the response from an upstream server. - -[Harmon](https://github.com/No9/harmon/) is a stream based middleware plugin that is designed to solve that problem in the most effective way possible. - -If you would like to handle errors passed to `next()` then attach a listener to the proxy: - - server = httpProxy.createServer( - myMiddleWare, - 9000, 'localhost' - ).listen(8000); - - server.proxy.on('middlewareError', function (err, req, res) { - // handle the error here and call res.end() - }); - -## Proxying WebSockets -Websockets are handled automatically when using `httpProxy.createServer()`, however, if you supply a callback inside the createServer call, you will need to handle the 'upgrade' proxy event yourself. Here's how: - -```js - -var options = { - .... -}; - -var server = httpProxy.createServer( - callback/middleware, - options -); - -server.listen(port, function () { ... }); -server.on('upgrade', function (req, socket, head) { - server.proxy.proxyWebSocketRequest(req, socket, head); -}); -``` - -If you would rather not use createServer call, and create the server that proxies yourself, see below: - -``` js -var http = require('http'), - httpProxy = require('http-proxy'); - -// -// Create an instance of node-http-proxy -// -var proxy = new httpProxy.HttpProxy({ - target: { - host: 'localhost', - port: 8000 - } -}); - -var server = http.createServer(function (req, res) { - // - // Proxy normal HTTP requests - // - proxy.proxyRequest(req, res); -}); - -server.on('upgrade', function (req, socket, head) { - // - // Proxy websocket requests too - // - proxy.proxyWebSocketRequest(req, socket, head); -}); - -server.listen(8080); -``` - -### with custom server logic - -``` js -var httpProxy = require('http-proxy') - -var server = httpProxy.createServer(function (req, res, proxy) { - // - // Put your custom server logic here - // - proxy.proxyRequest(req, res, { - host: 'localhost', - port: 9000 - }); -}) - -server.on('upgrade', function (req, socket, head) { - // - // Put your custom server logic here - // - server.proxy.proxyWebSocketRequest(req, socket, head, { - host: 'localhost', - port: 9000 - }); -}); - -server.listen(8080); -``` - -### Configuring your Socket limits - -By default, `node-http-proxy` will set a 100 socket limit for all `host:port` proxy targets. You can change this in two ways: - -1. By passing the `maxSockets` option to `httpProxy.createServer()` -2. By calling `httpProxy.setMaxSockets(n)`, where `n` is the number of sockets you with to use. - -## POST requests and buffering - -express.bodyParser will interfere with proxying of POST requests (and other methods that have a request -body). With bodyParser active, proxied requests will never send anything to the upstream server, and -the original client will just hang. See https://github.com/nodejitsu/node-http-proxy/issues/180 for options. - -## Using node-http-proxy from the command line -When you install this package with npm, a node-http-proxy binary will become available to you. Using this binary is easy with some simple options: - -``` js -usage: node-http-proxy [options] - -All options should be set with the syntax --option=value - -options: - --port PORT Port that the proxy server should run on - --target HOST:PORT Location of the server the proxy will target - --config OUTFILE Location of the configuration file for the proxy server - --silent Silence the log output from the proxy server - -h, --help You're staring at it -``` - -
-## Why doesn't node-http-proxy have more advanced features like x, y, or z? - -If you have a suggestion for a feature currently not supported, feel free to open a [support issue][6]. node-http-proxy is designed to just proxy http requests from one server to another, but we will be soon releasing many other complimentary projects that can be used in conjunction with node-http-proxy. - -## Options - -### Http Proxy - -`createServer()` supports the following options - -```javascript -{ - forward: { // options for forward-proxy - port: 8000, - host: 'staging.com' - }, - target : { // options for proxy target - port : 8000, - host : 'localhost', - }; - source : { // additional options for websocket proxying - host : 'localhost', - port : 8000, - https: true - }, - enable : { - xforward: true // enables X-Forwarded-For - }, - changeOrigin: false, // changes the origin of the host header to the target URL - timeout: 120000 // override the default 2 minute http socket timeout value in milliseconds -} -``` - -## Run Tests -The test suite is designed to fully cover the combinatoric possibilities of HTTP and HTTPS proxying: - -1. HTTP --> HTTP -2. HTTPS --> HTTP -3. HTTPS --> HTTPS -4. HTTP --> HTTPS - -``` -vows test/*-test.js --spec -vows test/*-test.js --spec --https -vows test/*-test.js --spec --https --target=https -vows test/*-test.js --spec --target=https -``` - -
-### License - -(The MIT License) - -Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -[0]: http://nodejitsu.com -[1]: https://github.com/nodejitsu/node-http-proxy/blob/master/examples/websocket/websocket-proxy.js -[2]: https://github.com/nodejitsu/node-http-proxy/blob/master/examples/http/proxy-https-to-http.js -[3]: https://github.com/nodejitsu/node-http-proxy/tree/master/examples -[4]: http://www.ietf.org/rfc/rfc2616.txt -[5]: http://socket.io -[6]: http://github.com/nodejitsu/node-http-proxy/issues diff --git a/benchmark/websockets-throughput.js b/benchmark/websockets-throughput.js deleted file mode 100644 index 6787385ad..000000000 --- a/benchmark/websockets-throughput.js +++ /dev/null @@ -1,88 +0,0 @@ -var crypto = require('crypto'), - WebSocket = require('ws'), - async = require('async'), - httpProxy = require('../'); - -var SERVER_PORT = 8415, - PROXY_PORT = 8514; - -var testSets = [ - { - size: 1024 * 1024, // 1 MB - count: 128 // 128 MB - }, - { - size: 1024, // 1 KB, - count: 1024 // 1 MB - }, - { - size: 128, // 128 B - count: 1024 * 8 // 1 MB - } -]; - -testSets.forEach(function (set) { - set.buffer = new Buffer(crypto.randomBytes(set.size)); - - set.buffers = []; - for (var i = 0; i < set.count; i++) { - set.buffers.push(set.buffer); - } -}); - -function runSet(set, callback) { - function runAgainst(port, callback) { - function send(sock) { - sock.send(set.buffers[got++]); - if (got === set.count) { - t = new Date() - t; - - server.close(); - proxy.close(); - - callback(null, t); - } - } - - var server = new WebSocket.Server({ port: SERVER_PORT }), - proxy = httpProxy.createServer(SERVER_PORT, 'localhost').listen(PROXY_PORT), - client = new WebSocket('ws://localhost:' + port), - got = 0, - t = new Date(); - - server.on('connection', function (ws) { - send(ws); - - ws.on('message', function (msg) { - send(ws); - }); - }); - - client.on('message', function () { - send(client); - }); - } - - async.series({ - server: async.apply(runAgainst, SERVER_PORT), - proxy: async.apply(runAgainst, PROXY_PORT) - }, function (err, results) { - if (err) { - throw err; - } - - var mb = (set.size * set.count) / (1024 * 1024); - console.log(set.size / (1024) + ' KB * ' + set.count + ' (' + mb + ' MB)'); - - Object.keys(results).forEach(function (key) { - var t = results[key], - throughput = mb / (t / 1000); - - console.log(' ' + key + ' took ' + t + ' ms (' + throughput + ' MB/s)'); - }); - - callback(); - }); -} - -async.forEachLimit(testSets, 1, runSet); diff --git a/bin/node-http-proxy b/bin/node-http-proxy deleted file mode 100755 index 07d199ca6..000000000 --- a/bin/node-http-proxy +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env node - -var path = require('path'), - fs = require('fs'), - util = require('util'), - argv = require('optimist').argv, - httpProxy = require('../lib/node-http-proxy'); - -var help = [ - "usage: node-http-proxy [options] ", - "", - "Starts a node-http-proxy server using the specified command-line options", - "", - "options:", - " --port PORT Port that the proxy server should run on", - " --host HOST Host that the proxy server should run on", - " --target HOST:PORT Location of the server the proxy will target", - " --config OUTFILE Location of the configuration file for the proxy server", - " --silent Silence the log output from the proxy server", - " --user USER User to drop privileges to once server socket is bound", - " -h, --help You're staring at it" -].join('\n'); - -if (argv.h || argv.help || Object.keys(argv).length === 2) { - return util.puts(help); -} - -var location, config = {}, - port = argv.port || 80, - host = argv.host || undefined, - target = argv.target; - user = argv.user; - -// -// If we were passed a config, parse it -// -if (argv.config) { - try { - var data = fs.readFileSync(argv.config); - config = JSON.parse(data.toString()); - } catch (ex) { - util.puts('Error starting node-http-proxy: ' + ex); - process.exit(1); - } -} - -// -// If `config.https` is set, then load the required file contents into the config options. -// -if (config.https) { - Object.keys(config.https).forEach(function (key) { - // If CA certs are specified, load those too. - if (key === "ca") { - for (var i=0; i < config.https.ca.length; i++) { - if (config.https.ca === undefined) { - config.https.ca = []; - } - config.https.ca[i] = fs.readFileSync(config.https[key][i], 'utf8'); - } - } else { - config.https[key] = fs.readFileSync(config.https[key], 'utf8'); - } - }); -} - -// -// Check to see if we should silence the logs -// -config.silent = typeof argv.silent !== 'undefined' ? argv.silent : config.silent; - -// -// If we were passed a target, parse the url string -// -if (typeof target === 'string') location = target.split(':'); - -// -// Create the server with the specified options -// -var server; -if (location) { - var targetPort = location.length === 1 ? 80 : parseInt(location[1]); - server = httpProxy.createServer(targetPort, location[0], config); -} -else if (config.router) { - server = httpProxy.createServer(config); -} -else { - return util.puts(help); -} - -// -// Start the server -// -if (host) { - server.listen(port, host); -} else { - server.listen(port); -} - - -// -// Drop privileges if requested -// -if (typeof user === 'string') { - process.setuid(user); -} - -// -// Notify that the server is started -// -if (!config.silent) { - util.puts('node-http-proxy server now listening on port: ' + port); -} diff --git a/config.sample.json b/config.sample.json deleted file mode 100644 index 8f15f882b..000000000 --- a/config.sample.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "silent": false, - "router": { - "localhost": "localhost:9000" - }, - "forward": { - "port": 9001, - "host": "localhost" - } -} \ No newline at end of file diff --git a/examples/balancer/simple-balancer-with-websockets.js b/examples/balancer/simple-balancer-with-websockets.js deleted file mode 100644 index 04564cef2..000000000 --- a/examples/balancer/simple-balancer-with-websockets.js +++ /dev/null @@ -1,58 +0,0 @@ -var http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// A simple round-robin load balancing strategy. -// -// First, list the servers you want to use in your rotation. -// -var addresses = [ - { - host: 'ws1.0.0.0', - port: 80 - }, - { - host: 'ws2.0.0.0', - port: 80 - } -]; - -// -// Create a HttpProxy object for each target -// - -var proxies = addresses.map(function (target) { - return new httpProxy.HttpProxy({ - target: target - }); -}); - -// -// Get the proxy at the front of the array, put it at the end and return it -// If you want a fancier balancer, put your code here -// - -function nextProxy() { - var proxy = proxies.shift(); - proxies.push(proxy); - return proxy; -} - -// -// Get the 'next' proxy and send the http request -// - -var server = http.createServer(function (req, res) { - nextProxy().proxyRequest(req, res); -}); - -// -// Get the 'next' proxy and send the upgrade request -// - -server.on('upgrade', function (req, socket, head) { - nextProxy().proxyWebSocketRequest(req, socket, head); -}); - -server.listen(8080); - \ No newline at end of file diff --git a/examples/balancer/simple-balancer.js b/examples/balancer/simple-balancer.js deleted file mode 100644 index f1fa0c06b..000000000 --- a/examples/balancer/simple-balancer.js +++ /dev/null @@ -1,36 +0,0 @@ -var httpProxy = require('../../lib/node-http-proxy'); -// -// A simple round-robin load balancing strategy. -// -// First, list the servers you want to use in your rotation. -// -var addresses = [ - { - host: 'ws1.0.0.0', - port: 80 - }, - { - host: 'ws2.0.0.0', - port: 80 - } -]; - -httpProxy.createServer(function (req, res, proxy) { - // - // On each request, get the first location from the list... - // - var target = addresses.shift(); - - // - // ...then proxy to the server whose 'turn' it is... - // - console.log('balancing request to: ', target); - proxy.proxyRequest(req, res, target); - - // - // ...and then the server you just used becomes the last item in the list. - // - addresses.push(target); -}).listen(8000); - -// Rinse; repeat; enjoy. \ No newline at end of file diff --git a/examples/helpers/store.js b/examples/helpers/store.js deleted file mode 100644 index e2860573f..000000000 --- a/examples/helpers/store.js +++ /dev/null @@ -1,64 +0,0 @@ - -// -// just to make these example a little bit interesting, -// make a little key value store with an http interface -// (see couchbd for a grown-up version of this) -// -// API: -// GET / -// retrive list of keys -// -// GET /[url] -// retrive object stored at [url] -// will respond with 404 if there is nothing stored at [url] -// -// POST /[url] -// -// JSON.parse the body and store it under [url] -// will respond 400 (bad request) if body is not valid json. -// -// TODO: cached map-reduce views and auto-magic sharding. -// -var Store = module.exports = function Store () { - this.store = {}; -}; - -Store.prototype = { - get: function (key) { - return this.store[key] - }, - set: function (key, value) { - return this.store[key] = value - }, - handler:function () { - var store = this - return function (req, res) { - function send (obj, status) { - res.writeHead(200 || status,{'Content-Type': 'application/json'}) - res.write(JSON.stringify(obj) + '\n') - res.end() - } - var url = req.url.split('?').shift() - if (url === '/') { - console.log('get index') - return send(Object.keys(store.store)) - } else if (req.method == 'GET') { - var obj = store.get (url) - send(obj || {error: 'not_found', url: url}, obj ? 200 : 404) - } else { - //post: buffer body, and parse. - var body = '', obj - req.on('data', function (c) { body += c}) - req.on('end', function (c) { - try { - obj = JSON.parse(body) - } catch (err) { - return send (err, 400) - } - store.set(url, obj) - send({ok: true}) - }) - } - } - } -} diff --git a/examples/http/basic-proxy.js b/examples/http/basic-proxy.js deleted file mode 100644 index b890e69a0..000000000 --- a/examples/http/basic-proxy.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - basic-proxy.js: Basic example of proxying over HTTP - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -var welcome = [ - '# # ##### ##### ##### ##### ##### #### # # # #', - '# # # # # # # # # # # # # # # # ', - '###### # # # # ##### # # # # # # ## # ', - '# # # # ##### ##### ##### # # ## # ', - '# # # # # # # # # # # # # ', - '# # # # # # # # #### # # # ' -].join('\n'); - -util.puts(welcome.rainbow.bold); - -// -// Basic Http Proxy Server -// -httpProxy.createServer(9000, 'localhost').listen(8000); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/http/concurrent-proxy.js b/examples/http/concurrent-proxy.js deleted file mode 100644 index 230dfc651..000000000 --- a/examples/http/concurrent-proxy.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - concurrent-proxy.js: check levelof concurrency through proxy. - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Basic Http Proxy Server -// -httpProxy.createServer(9000, 'localhost').listen(8000); - -// -// Target Http Server -// -// to check apparent problems with concurrent connections -// make a server which only responds when there is a given nubmer on connections -// - - -var connections = [], - go; - -http.createServer(function (req, res) { - connections.push(function () { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); - }); - - process.stdout.write(connections.length + ', '); - - if (connections.length > 110 || go) { - go = true; - while (connections.length) { - connections.shift()(); - } - } -}).listen(9000); - -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/http/custom-proxy-error.js b/examples/http/custom-proxy-error.js deleted file mode 100644 index dc439ea19..000000000 --- a/examples/http/custom-proxy-error.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - custom-proxy-error.js: Example of using the custom `proxyError` event. - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Http Proxy Server with Latency -// -var server = httpProxy.createServer(9000, 'localhost'); - -// -// Tell the server to listen on port 8002 -// -server.listen(8002); - -// -// Listen for the `proxyError` event on `server.proxy`. _It will not -// be raised on the server itself._ -server.proxy.on('proxyError', function (err, req, res) { - res.writeHead(500, { - 'Content-Type': 'text/plain' - }); - - res.end('Something went wrong. And we are reporting a custom error message.'); -}); - - -util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with custom error message'.magenta.underline); \ No newline at end of file diff --git a/examples/http/forward-proxy.js b/examples/http/forward-proxy.js deleted file mode 100644 index ecc20fd4c..000000000 --- a/examples/http/forward-proxy.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - forward-proxy.js: Example of proxying over HTTP with additional forward proxy - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Setup proxy server with forwarding -// -httpProxy.createServer(9000, 'localhost', { - forward: { - port: 9001, - host: 'localhost' - } -}).listen(8003); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -// -// Target Http Forwarding Server -// -http.createServer(function (req, res) { - util.puts('Receiving forward for: ' + req.url); - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9001); - -util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8003 '.yellow + 'with forward proxy'.magenta.underline); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); -util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9001 '.yellow); \ No newline at end of file diff --git a/examples/http/latent-proxy.js b/examples/http/latent-proxy.js deleted file mode 100644 index 2063d0e5b..000000000 --- a/examples/http/latent-proxy.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - latent-proxy.js: Example of proxying over HTTP with latency - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Http Proxy Server with Latency -// -httpProxy.createServer(function (req, res, proxy) { - var buffer = httpProxy.buffer(req); - setTimeout(function () { - proxy.proxyRequest(req, res, { - port: 9000, - host: 'localhost', - buffer: buffer - }); - }, 200); -}).listen(8002); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with latency'.magenta.underline); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/http/proxy-https-to-http.js b/examples/http/proxy-https-to-http.js deleted file mode 100644 index 378de0362..000000000 --- a/examples/http/proxy-https-to-http.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - proxy-https-to-http.js: Basic example of proxying over HTTPS to a target HTTP server - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var https = require('https'), - http = require('http'), - util = require('util'), - colors = require('colors'), - httpProxy = require('../../lib/node-http-proxy'), - helpers = require('../../test/helpers'); - -// -// Create the target HTTPS server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('hello http over https\n'); - res.end(); -}).listen(8000); - -// -// Create the proxy server listening on port 443 -// -httpProxy.createServer(8000, 'localhost', { - https: helpers.https -}).listen(8080); - -util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8080'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow); diff --git a/examples/http/proxy-https-to-https.js b/examples/http/proxy-https-to-https.js deleted file mode 100644 index af0d92275..000000000 --- a/examples/http/proxy-https-to-https.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - proxy-https-to-https.js: Basic example of proxying over HTTPS to a target HTTPS server - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var https = require('https'), - http = require('http'), - util = require('util'), - colors = require('colors'), - httpProxy = require('../../lib/node-http-proxy'), - helpers = require('../../test/helpers'); - -// -// Create the target HTTPS server -// -https.createServer(helpers.https, function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('hello https\n'); - res.end(); -}).listen(8000); - -// -// Create the proxy server listening on port 443 -// -httpProxy.createServer(8000, 'localhost', { - https: helpers.https, - target: { - https: true, - rejectUnauthorized: false - } -}).listen(8080); - -util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8080'.yellow); -util.puts('https server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow); diff --git a/examples/http/proxy-table.js b/examples/http/proxy-table.js deleted file mode 100644 index 55d97aed5..000000000 --- a/examples/http/proxy-table.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - proxy-table.js: Example of proxying over HTTP with proxy table - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Http Proxy Server with Proxy Table -// -httpProxy.createServer({ - router: { - 'localhost': 'localhost:9000' - } -}).listen(8001); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8001 '.yellow + 'with proxy table'.magenta.underline); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/http/standalone-proxy.js b/examples/http/standalone-proxy.js deleted file mode 100644 index e1b1011c5..000000000 --- a/examples/http/standalone-proxy.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - standalone-proxy.js: Example of proxying over HTTP with a standalone HTTP server. - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Http Server with proxyRequest Handler and Latency -// -var proxy = new httpProxy.RoutingProxy(); -http.createServer(function (req, res) { - var buffer = httpProxy.buffer(req); - setTimeout(function () { - proxy.proxyRequest(req, res, { - port: 9000, - host: 'localhost', - buffer: buffer - }); - }, 200); -}).listen(8004); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8004 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/middleware/bodyDecoder-middleware.js b/examples/middleware/bodyDecoder-middleware.js deleted file mode 100644 index d889548a0..000000000 --- a/examples/middleware/bodyDecoder-middleware.js +++ /dev/null @@ -1,87 +0,0 @@ - -var Store = require('../helpers/store') - , http = require('http') - -http.createServer(new Store().handler()).listen(7531, function () { -//try these commands: -// get index: -// curl localhost:7531 -// [] -// -// get a doc: -// curl localhost:7531/foo -// {"error":"not_found"} -// -// post an doc: -// curl -X POST localhost:7531/foo -d '{"content": "hello", "type": "greeting"}' -// {"ok":true} -// -// get index (now, not empty) -// curl localhost:7531 -// ["/foo"] -// -// get doc -// curl localhost:7531/foo -// {"content": "hello", "type": "greeting"} - -// -// now, suppose we wanted to direct all objects where type == "greeting" to a different store -// than where type == "insult" -// -// we can use connect connect-bodyDecoder and some custom logic to send insults to another Store. - -//insult server: - - http.createServer(new Store().handler()).listen(2600, function () { - - //greetings -> 7531, insults-> 2600 - - // now, start a proxy server. - - var bodyParser = require('connect/lib/middleware/bodyParser') - //don't worry about incoming contont type - //bodyParser.parse[''] = JSON.parse - - require('../../lib/node-http-proxy').createServer( - //refactor the body parser and re-streamer into a separate package - bodyParser(), - //body parser absorbs the data and end events before passing control to the next - // middleware. if we want to proxy it, we'll need to re-emit these events after - //passing control to the middleware. - require('connect-restreamer')(), - function (req, res, proxy) { - //if your posting an obect which contains type: "insult" - //it will get redirected to port 2600. - //normal get requests will go to 7531 nad will not return insults. - var port = (req.body && req.body.type === 'insult' ? 2600 : 7531) - proxy.proxyRequest(req, res, {host: 'localhost', port: port}) - } - ).listen(1337, function () { - var request = require('request') - //bodyParser needs content-type set to application/json - //if we use request, it will set automatically if we use the 'json:' field. - function post (greeting, type) { - request.post({ - url: 'http://localhost:1337/' + greeting, - json: {content: greeting, type: type || "greeting"} - }) - } - post("hello") - post("g'day") - post("kiora") - post("houdy") - post("java", "insult") - - //now, the insult should have been proxied to 2600 - - //curl localhost:2600 - //["/java"] - - //but the greetings will be sent to 7531 - - //curl localhost:7531 - //["/hello","/g%27day","/kiora","/houdy"] - - }) - }) -}) diff --git a/examples/middleware/gzip-middleware-proxytable.js b/examples/middleware/gzip-middleware-proxytable.js deleted file mode 100644 index 527d3d7b8..000000000 --- a/examples/middleware/gzip-middleware-proxytable.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - gzip-middleware-proxytable.js: Basic example of `connect-gzip` middleware in node-http-proxy - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, Marak Squires, & Dominic Tarr. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Basic Http Proxy Server -// -httpProxy.createServer( - require('connect-gzip').gzip({ matchType: /.?/ }), - { - router: { - "localhost/fun": "localhost:9000" - } - } -).listen(8000); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/middleware/gzip-middleware.js b/examples/middleware/gzip-middleware.js deleted file mode 100644 index 29097ecd3..000000000 --- a/examples/middleware/gzip-middleware.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - gzip-middleware.js: Basic example of `connect-gzip` middleware in node-http-proxy - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, Marak Squires, & Dominic Tarr. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Basic Http Proxy Server -// -httpProxy.createServer( - require('connect-gzip').gzip({ matchType: /.?/ }), - 9000, 'localhost' -).listen(8000); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/middleware/jsonp-middleware.js b/examples/middleware/jsonp-middleware.js deleted file mode 100644 index 15ab9ee05..000000000 --- a/examples/middleware/jsonp-middleware.js +++ /dev/null @@ -1,30 +0,0 @@ -var Store = require('../helpers/store') - , http = require('http') - -// -// jsonp is a handy technique for getting around the limitations of the same-origin policy. -// (http://en.wikipedia.org/wiki/Same_origin_policy) -// -// normally, to dynamically update a page you use an XmlHttpRequest. this has flakey support -// is some browsers and is restricted by the same origin policy. you cannot perform XHR requests to -// someone else's server. one way around this would be to proxy requests to all the servers you want -// to xhr to, and your core server - so that everything has the same port and host. -// -// another way, is to turn json into javascript. (which is exempt from the same origin policy) -// this is done by wrapping the json object in a function call, and then including a script tag. -// -// here we're proxing our own JSON returning server, but we could proxy any server on the internet, -// and our client side app would be slurping down JSONP from anywhere. -// -// curl localhost:1337/whatever?callback=alert -// alert([]) //which is valid javascript! -// -// also see http://en.wikipedia.org/wiki/JSONP#JSONP -// - -http.createServer(new Store().handler()).listen(7531) - -require('../../lib/node-http-proxy').createServer( - require('connect-jsonp')(true), - 'localhost', 7531 -).listen(1337) diff --git a/examples/middleware/modifyResponse-middleware.js b/examples/middleware/modifyResponse-middleware.js deleted file mode 100644 index af21236ef..000000000 --- a/examples/middleware/modifyResponse-middleware.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - modifyBody-middleware.js: Example of middleware which modifies response - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, Marak Squires, & Dominic Tarr. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Basic Http Proxy Server -// -httpProxy.createServer( - function (req, res, next) { - var _write = res.write; - - res.write = function (data) { - _write.call(res, data.toString().replace("Ruby", "nodejitsu")); - } - next(); - }, - 9000, 'localhost' -).listen(8000); - -// -// Target Http Server -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.end('Hello, I know Ruby\n'); -}).listen(9000); - -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); - diff --git a/examples/middleware/url-middleware.js b/examples/middleware/url-middleware.js deleted file mode 100644 index b4f304562..000000000 --- a/examples/middleware/url-middleware.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - url-middleware.js: Example of a simple url routing middleware for node-http-proxy - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'); - -// -// Now we set up our proxy. -// -httpProxy.createServer( - // - // This is where our middlewares go, with any options desired - in this case, - // the list of routes/URLs and their destinations. - // - require('proxy-by-url')({ - '/hello': { port: 9000, host: 'localhost' }, - '/charlie': { port: 80, host: 'charlieistheman.com' }, - '/google': { port: 80, host: 'google.com' } - }) -).listen(8000); - -// -// Target Http Server (to listen for requests on 'localhost') -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -// And finally, some colored startup output. -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/middleware/url-middleware2.js b/examples/middleware/url-middleware2.js deleted file mode 100644 index 2c894e1af..000000000 --- a/examples/middleware/url-middleware2.js +++ /dev/null @@ -1,30 +0,0 @@ -var util = require('util'), - colors = require('colors'), - http = require('http'), - httpProxy = require('../../lib/node-http-proxy'), - Store = require('../helpers/store') - -http.createServer(new Store().handler()).listen(7531) - -// Now we set up our proxy. -httpProxy.createServer( - // This is where our middlewares go, with any options desired - in this case, - // the list of routes/URLs and their destinations. - require('proxy-by-url')({ - '/store': { port: 7531, host: 'localhost' }, - '/': { port: 9000, host: 'localhost' } - }) -).listen(8000); - -// -// Target Http Server (to listen for requests on 'localhost') -// -http.createServer(function (req, res) { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); - res.end(); -}).listen(9000); - -// And finally, some colored startup output. -util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); -util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); diff --git a/examples/package.json b/examples/package.json deleted file mode 100644 index ca95fd8cf..000000000 --- a/examples/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "http-proxy-examples", - "description": "packages required to run the examples", - "version": "0.0.0", - "dependencies": { - "connect": "1.6", - "connect-gzip": "0.1", - "connect-jsonp": "0.0.5", - "connect-restreamer": "1", - "proxy-by-url": ">= 0.0.1" - } -} \ No newline at end of file diff --git a/examples/websocket/latent-websocket-proxy.js b/examples/websocket/latent-websocket-proxy.js deleted file mode 100644 index 99a07281e..000000000 --- a/examples/websocket/latent-websocket-proxy.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server. - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - http = require('http'), - colors = require('colors'), - httpProxy = require('../../lib/node-http-proxy'); - -try { - var io = require('socket.io'), - client = require('socket.io-client'); -} -catch (ex) { - console.error('Socket.io is required for this example:'); - console.error('npm ' + 'install'.green); - process.exit(1); -} - -// -// Create the target HTTP server and setup -// socket.io on it. -// -var server = io.listen(8080); -server.sockets.on('connection', function (client) { - util.debug('Got websocket connection'); - - client.on('message', function (msg) { - util.debug('Got message from client: ' + msg); - }); - - client.send('from server'); -}); - -// -// Setup our server to proxy standard HTTP requests -// -var proxy = new httpProxy.HttpProxy({ - target: { - host: 'localhost', - port: 8080 - } -}); - -var proxyServer = http.createServer(function (req, res) { - proxy.proxyRequest(req, res); -}); - -// -// Listen to the `upgrade` event and proxy the -// WebSocket requests as well. -// -proxyServer.on('upgrade', function (req, socket, head) { - var buffer = httpProxy.buffer(socket); - - setTimeout(function () { - proxy.proxyWebSocketRequest(req, socket, head, buffer); - }, 1000); -}); - -proxyServer.listen(8081); - -// -// Setup the socket.io client against our proxy -// -var ws = client.connect('ws://localhost:8081'); - -ws.on('message', function (msg) { - util.debug('Got message: ' + msg); -}); diff --git a/examples/websocket/standalone-websocket-proxy.js b/examples/websocket/standalone-websocket-proxy.js deleted file mode 100644 index acf43b979..000000000 --- a/examples/websocket/standalone-websocket-proxy.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server. - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - http = require('http'), - colors = require('colors'), - httpProxy = require('../../lib/node-http-proxy'); - -try { - var io = require('socket.io'), - client = require('socket.io-client'); -} -catch (ex) { - console.error('Socket.io is required for this example:'); - console.error('npm ' + 'install'.green); - process.exit(1); -} - -// -// Create the target HTTP server and setup -// socket.io on it. -// -var server = io.listen(8080); -server.sockets.on('connection', function (client) { - util.debug('Got websocket connection'); - - client.on('message', function (msg) { - util.debug('Got message from client: ' + msg); - }); - - client.send('from server'); -}); - -// -// Setup our server to proxy standard HTTP requests -// -var proxy = new httpProxy.HttpProxy({ - target: { - host: 'localhost', - port: 8080 - } -}); -var proxyServer = http.createServer(function (req, res) { - proxy.proxyRequest(req, res); -}); - -// -// Listen to the `upgrade` event and proxy the -// WebSocket requests as well. -// -proxyServer.on('upgrade', function (req, socket, head) { - proxy.proxyWebSocketRequest(req, socket, head); -}); - -proxyServer.listen(8081); - -// -// Setup the socket.io client against our proxy -// -var ws = client.connect('ws://localhost:8081'); - -ws.on('message', function (msg) { - util.debug('Got message: ' + msg); -}); diff --git a/examples/websocket/websocket-proxy.js b/examples/websocket/websocket-proxy.js deleted file mode 100644 index 4e3cf6fd1..000000000 --- a/examples/websocket/websocket-proxy.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - web-socket-proxy.js: Example of proxying over HTTP and WebSockets. - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - http = require('http'), - colors = require('colors'), - httpProxy = require('../../lib/node-http-proxy'); - -try { - var io = require('socket.io'), - client = require('socket.io-client'); -} -catch (ex) { - console.error('Socket.io is required for this example:'); - console.error('npm ' + 'install'.green); - process.exit(1); -} - -// -// Create the target HTTP server and setup -// socket.io on it. -// -var server = io.listen(8080); -server.sockets.on('connection', function (client) { - util.debug('Got websocket connection'); - - client.on('message', function (msg) { - util.debug('Got message from client: ' + msg); - }); - - client.send('from server'); -}); - -// -// Create a proxy server with node-http-proxy -// -httpProxy.createServer(8080, 'localhost').listen(8081); - -// -// Setup the socket.io client against our proxy -// -var ws = client.connect('ws://localhost:8081'); - -ws.on('message', function (msg) { - util.debug('Got message: ' + msg); -}); diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js deleted file mode 100644 index b5de6bb66..000000000 --- a/lib/node-http-proxy.js +++ /dev/null @@ -1,397 +0,0 @@ -/* - node-http-proxy.js: http proxy for node.js - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - http = require('http'), - https = require('https'), - events = require('events'), - maxSockets = 100; - -// -// Expose version information through `pkginfo`. -// -require('pkginfo')(module, 'version'); - -// -// ### Export the relevant objects exposed by `node-http-proxy` -// -var HttpProxy = exports.HttpProxy = require('./node-http-proxy/http-proxy').HttpProxy, - ProxyTable = exports.ProxyTable = require('./node-http-proxy/proxy-table').ProxyTable, - RoutingProxy = exports.RoutingProxy = require('./node-http-proxy/routing-proxy').RoutingProxy; - -// -// ### function createServer ([port, host, options, handler]) -// #### @port {number} **Optional** Port to use on the proxy target host. -// #### @host {string} **Optional** Host of the proxy target. -// #### @options {Object} **Optional** Options for the HttpProxy instance used -// #### @handler {function} **Optional** Request handler for the server -// Returns a server that manages an instance of HttpProxy. Flexible arguments allow for: -// -// * `httpProxy.createServer(9000, 'localhost')` -// * `httpProxy.createServer(9000, 'localhost', options) -// * `httpPRoxy.createServer(function (req, res, proxy) { ... })` -// -exports.createServer = function () { - var args = Array.prototype.slice.call(arguments), - handlers = [], - callback, - options = {}, - message, - handler, - server, - proxy, - host, - port; - - // - // Liberally parse arguments of the form: - // - // httpProxy.createServer('localhost', 9000, callback); - // httpProxy.createServer({ host: 'localhost', port: 9000 }, callback); - // **NEED MORE HERE!!!** - // - args.forEach(function (arg) { - arg = Number(arg) || arg; - switch (typeof arg) { - case 'string': host = arg; break; - case 'number': port = arg; break; - case 'object': options = arg || {}; break; - case 'function': callback = arg; handlers.push(callback); break; - }; - }); - - // - // Helper function to create intelligent error message(s) - // for the very liberal arguments parsing performed by - // `require('http-proxy').createServer()`. - // - function validArguments() { - var conditions = { - 'port and host': function () { - return port && host; - }, - 'options.target or options.router': function () { - return options && (options.router || - (options.target && options.target.host && options.target.port)); - }, - 'or proxy handlers': function () { - return handlers && handlers.length; - } - } - - var missing = Object.keys(conditions).filter(function (name) { - return !conditions[name](); - }); - - if (missing.length === 3) { - message = 'Cannot proxy without ' + missing.join(', '); - return false; - } - - return true; - } - - if (!validArguments()) { - // - // If `host`, `port` and `options` are all not passed (with valid - // options) then this server is improperly configured. - // - throw new Error(message); - return; - } - - // - // Hoist up any explicit `host` or `port` arguments - // that have been passed in to the options we will - // pass to the `httpProxy.HttpProxy` constructor. - // - options.target = options.target || {}; - options.target.port = options.target.port || port; - options.target.host = options.target.host || host; - - if (options.target && options.target.host && options.target.port) { - // - // If an explicit `host` and `port` combination has been passed - // to `.createServer()` then instantiate a hot-path optimized - // `HttpProxy` object and add the "proxy" middleware layer. - // - proxy = new HttpProxy(options); - handlers.push(function (req, res) { - proxy.proxyRequest(req, res); - }); - } - else { - // - // If no explicit `host` or `port` combination has been passed then - // we have to assume that this is a "go-anywhere" Proxy (i.e. a `RoutingProxy`). - // - proxy = new RoutingProxy(options); - - if (options.router) { - // - // If a routing table has been supplied than we assume - // the user intends us to add the "proxy" middleware layer - // for them - // - handlers.push(function (req, res) { - proxy.proxyRequest(req, res); - }); - - proxy.on('routes', function (routes) { - server.emit('routes', routes); - }); - } - } - - // - // Create the `http[s].Server` instance which will use - // an instance of `httpProxy.HttpProxy`. - // - handler = handlers.length > 1 - ? exports.stack(handlers, proxy) - : function (req, res) { handlers[0](req, res, proxy) }; - - server = options.https - ? https.createServer(options.https, handler) - : http.createServer(handler); - - server.on('close', function () { - proxy.close(); - }); - - if (!callback) { - // - // If an explicit callback has not been supplied then - // automagically proxy the request using the `HttpProxy` - // instance we have created. - // - server.on('upgrade', function (req, socket, head) { - proxy.proxyWebSocketRequest(req, socket, head); - }); - } - - // - // Set the proxy on the server so it is available - // to the consumer of the server - // - server.proxy = proxy; - return server; -}; - -// -// ### function buffer (obj) -// #### @obj {Object} Object to pause events from -// Buffer `data` and `end` events from the given `obj`. -// Consumers of HttpProxy performing async tasks -// __must__ utilize this utility, to re-emit data once -// the async operation has completed, otherwise these -// __events will be lost.__ -// -// var buffer = httpProxy.buffer(req); -// fs.readFile(path, function () { -// httpProxy.proxyRequest(req, res, host, port, buffer); -// }); -// -// __Attribution:__ This approach is based heavily on -// [Connect](https://github.com/senchalabs/connect/blob/master/lib/utils.js#L157). -// However, this is not a big leap from the implementation in node-http-proxy < 0.4.0. -// This simply chooses to manage the scope of the events on a new Object literal as opposed to -// [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154). -// -exports.buffer = function (obj) { - var events = [], - onData, - onEnd; - - obj.on('data', onData = function (data, encoding) { - events.push(['data', data, encoding]); - }); - - obj.on('end', onEnd = function (data, encoding) { - events.push(['end', data, encoding]); - }); - - return { - end: function () { - obj.removeListener('data', onData); - obj.removeListener('end', onEnd); - }, - destroy: function () { - this.end(); - this.resume = function () { - console.error("Cannot resume buffer after destroying it."); - }; - - onData = onEnd = events = obj = null; - }, - resume: function () { - this.end(); - for (var i = 0, len = events.length; i < len; ++i) { - obj.emit.apply(obj, events[i]); - } - } - }; -}; - -// -// ### function getMaxSockets () -// Returns the maximum number of sockets -// allowed on __every__ outgoing request -// made by __all__ instances of `HttpProxy` -// -exports.getMaxSockets = function () { - return maxSockets; -}; - -// -// ### function setMaxSockets () -// Sets the maximum number of sockets -// allowed on __every__ outgoing request -// made by __all__ instances of `HttpProxy` -// -exports.setMaxSockets = function (value) { - maxSockets = value; -}; - -// -// ### function stack (middlewares, proxy) -// #### @middlewares {Array} Array of functions to stack. -// #### @proxy {HttpProxy|RoutingProxy} Proxy instance to -// Iteratively build up a single handler to the `http.Server` -// `request` event (i.e. `function (req, res)`) by wrapping -// each middleware `layer` into a `child` middleware which -// is in invoked by the parent (i.e. predecessor in the Array). -// -// adapted from https://github.com/creationix/stack -// -exports.stack = function stack (middlewares, proxy) { - var handle; - middlewares.reverse().forEach(function (layer) { - var child = handle; - handle = function (req, res) { - var next = function (err) { - if (err) { - if (! proxy.emit('middlewareError', err, req, res)) { - console.error('Error in middleware(s): %s', err.stack); - } - - if (res._headerSent) { - res.destroy(); - } - else { - res.statusCode = 500; - res.setHeader('Content-Type', 'text/plain'); - res.end('Internal Server Error'); - } - - return; - } - - if (child) { - child(req, res); - } - }; - - // - // Set the prototype of the `next` function to the instance - // of the `proxy` so that in can be used interchangably from - // a `connect` style callback and a true `HttpProxy` object. - // - // e.g. `function (req, res, next)` vs. `function (req, res, proxy)` - // - next.__proto__ = proxy; - layer(req, res, next); - }; - }); - - return handle; -}; - -// -// ### function _getAgent (host, port, secure) -// #### @options {Object} Options to use when creating the agent. -// -// { -// host: 'localhost', -// port: 9000, -// https: true, -// maxSockets: 100 -// } -// -// Createsan agent from the `http` or `https` module -// and sets the `maxSockets` property appropriately. -// -exports._getAgent = function _getAgent (options) { - if (!options || !options.host) { - throw new Error('`options.host` is required to create an Agent.'); - } - - if (!options.port) { - options.port = options.https ? 443 : 80; - } - - var Agent = options.https ? https.Agent : http.Agent, - agent; - - // require('http-proxy').setMaxSockets() should override http's default - // configuration value (which is pretty low). - options.maxSockets = options.maxSockets || maxSockets; - agent = new Agent(options); - - return agent; -} - -// -// ### function _getProtocol (options) -// #### @options {Object} Options for the proxy target. -// Returns the appropriate node.js core protocol module (i.e. `http` or `https`) -// based on the `options` supplied. -// -exports._getProtocol = function _getProtocol (options) { - return options.https ? https : http; -}; - - -// -// ### function _getBase (options) -// #### @options {Object} Options for the proxy target. -// Returns the relevate base object to create on outgoing proxy request. -// If `options.https` are supplied, this function respond with an object -// containing the relevant `ca`, `key`, and `cert` properties. -// -exports._getBase = function _getBase (options) { - var result = function () {}; - - if (options.https && typeof options.https === 'object') { - ['ca', 'cert', 'key'].forEach(function (key) { - if (options.https[key]) { - result.prototype[key] = options.https[key]; - } - }); - } - - return result; -}; diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js deleted file mode 100644 index 92541baec..000000000 --- a/lib/node-http-proxy/http-proxy.js +++ /dev/null @@ -1,983 +0,0 @@ -/* - node-http-proxy.js: http proxy for node.js - - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var events = require('events'), - http = require('http'), - util = require('util'), - url = require('url'), - httpProxy = require('../node-http-proxy'); - -// -// @private {RegExp} extractPort -// Reusable regular expression for getting the -// port from a host string. -// -var extractPort = /:(\d+)$/; - -// -// ### function HttpProxy (options) -// #### @options {Object} Options for this instance. -// Constructor function for new instances of HttpProxy responsible -// for managing the life-cycle of streaming reverse proxyied HTTP requests. -// -// Example options: -// -// { -// target: { -// host: 'localhost', -// port: 9000 -// }, -// forward: { -// host: 'localhost', -// port: 9001 -// } -// } -// -var HttpProxy = exports.HttpProxy = function (options) { - if (!options || !options.target) { - throw new Error('Both `options` and `options.target` are required.'); - } - - events.EventEmitter.call(this); - - var self = this; - - // - // Setup basic proxying options: - // - // * forward {Object} Options for a forward-proxy (if-any) - // * target {Object} Options for the **sole** proxy target of this instance - // - this.forward = options.forward; - this.target = options.target; - this.timeout = options.timeout; - - // - // Setup the necessary instances instance variables for - // the `target` and `forward` `host:port` combinations - // used by this instance. - // - // * agent {http[s].Agent} Agent to be used by this instance. - // * protocol {http|https} Core node.js module to make requests with. - // * base {Object} Base object to create when proxying containing any https settings. - // - function setupProxy (key) { - self[key].agent = httpProxy._getAgent(self[key]); - self[key].protocol = httpProxy._getProtocol(self[key]); - self[key].base = httpProxy._getBase(self[key]); - } - - setupProxy('target'); - if (this.forward) { - setupProxy('forward'); - } - - // - // Setup opt-in features - // - this.enable = options.enable || {}; - this.enable.xforward = typeof this.enable.xforward === 'boolean' - ? this.enable.xforward - : true; - - // if event listener is set then use it else unlimited. - this.eventListenerCount = typeof options.eventListenerCount === 'number'? options.eventListenerCount : 0 ; - - // - // Setup additional options for WebSocket proxying. When forcing - // the WebSocket handshake to change the `sec-websocket-location` - // and `sec-websocket-origin` headers `options.source` **MUST** - // be provided or the operation will fail with an `origin mismatch` - // by definition. - // - this.source = options.source || { host: 'localhost', port: 80 }; - this.source.https = this.source.https || options.https; - this.changeOrigin = options.changeOrigin || false; -}; - -// Inherit from events.EventEmitter -util.inherits(HttpProxy, events.EventEmitter); - -// -// ### function proxyRequest (req, res, buffer) -// #### @req {ServerRequest} Incoming HTTP Request to proxy. -// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to. -// #### @buffer {Object} Result from `httpProxy.buffer(req)` -// -HttpProxy.prototype.proxyRequest = function (req, res, buffer) { - var self = this, - errState = false, - outgoing = new(this.target.base), - reverseProxy, - location; - - // If this is a DELETE request then set the "content-length" - // header (if it is not already set) - if (req.method === 'DELETE') { - req.headers['content-length'] = req.headers['content-length'] || '0'; - } - - // - // Add common proxy headers to the request so that they can - // be availible to the proxy target server. If the proxy is - // part of proxy chain it will append the address: - // - // * `x-forwarded-for`: IP Address of the original request - // * `x-forwarded-proto`: Protocol of the original request - // * `x-forwarded-port`: Port of the original request. - // - if (this.enable.xforward && req.connection && req.socket) { - if (req.headers['x-forwarded-for']) { - var addressToAppend = "," + req.connection.remoteAddress || req.socket.remoteAddress; - req.headers['x-forwarded-for'] += addressToAppend; - } - else { - req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress; - } - - if (req.headers['x-forwarded-port']) { - var portToAppend = "," + getPortFromHostHeader(req); - req.headers['x-forwarded-port'] += portToAppend; - } - else { - req.headers['x-forwarded-port'] = getPortFromHostHeader(req); - } - - if (req.headers['x-forwarded-proto']) { - var protoToAppend = "," + getProto(req); - req.headers['x-forwarded-proto'] += protoToAppend; - } - else { - req.headers['x-forwarded-proto'] = getProto(req); - } - } - - if (this.timeout) { - req.socket.setTimeout(this.timeout); - } - - // - // Emit the `start` event indicating that we have begun the proxy operation. - // - this.emit('start', req, res, this.target); - - // - // If forwarding is enabled for this instance, foward proxy the - // specified request to the address provided in `this.forward` - // - if (this.forward) { - this.emit('forward', req, res, this.forward); - this._forwardRequest(req); - } - - // - // #### function proxyError (err) - // #### @err {Error} Error contacting the proxy target - // Short-circuits `res` in the event of any error when - // contacting the proxy target at `host` / `port`. - // - function proxyError(err) { - errState = true; - - // - // Emit an `error` event, allowing the application to use custom - // error handling. The error handler should end the response. - // - if (self.emit('proxyError', err, req, res)) { - return; - } - - res.writeHead(500, { 'Content-Type': 'text/plain' }); - - if (req.method !== 'HEAD') { - // - // This NODE_ENV=production behavior is mimics Express and - // Connect. - // - if (process.env.NODE_ENV === 'production') { - res.write('Internal Server Error'); - } - else { - res.write('An error has occurred: ' + JSON.stringify(err)); - } - } - - try { res.end() } - catch (ex) { console.error("res.end error: %s", ex.message) } - } - - // - // Setup outgoing proxy with relevant properties. - // - outgoing.host = this.target.host; - outgoing.hostname = this.target.hostname; - outgoing.port = this.target.port; - outgoing.socketPath = this.target.socketPath; - outgoing.agent = this.target.agent; - outgoing.method = req.method; - outgoing.path = url.parse(req.url).path; - outgoing.headers = req.headers; - - // - // If the changeOrigin option is specified, change the - // origin of the host header to the target URL! Please - // don't revert this without documenting it! - // - if (this.changeOrigin) { - outgoing.headers.host = this.target.host; - // Only add port information to the header if not default port - // for this protocol. - // See https://github.com/nodejitsu/node-http-proxy/issues/458 - if (this.target.port !== 443 && this.target.https || - this.target.port !== 80 && !this.target.https) { - outgoing.headers.host += ':' + this.target.port; - } - } - - // - // Open new HTTP request to internal resource with will act - // as a reverse proxy pass - // - reverseProxy = this.target.protocol.request(outgoing, function (response) { - // - // Process the `reverseProxy` `response` when it's received. - // - if (req.httpVersion === '1.0') { - if (req.headers.connection) { - response.headers.connection = req.headers.connection - } else { - response.headers.connection = 'close' - } - } else if (!response.headers.connection) { - if (req.headers.connection) { response.headers.connection = req.headers.connection } - else { - response.headers.connection = 'keep-alive' - } - } - - // Remove `Transfer-Encoding` header if client's protocol is HTTP/1.0 - // or if this is a DELETE request with no content-length header. - // See: https://github.com/nodejitsu/node-http-proxy/pull/373 - if (req.httpVersion === '1.0' || (req.method === 'DELETE' - && !req.headers['content-length'])) { - delete response.headers['transfer-encoding']; - } - - if ((response.statusCode === 301 || response.statusCode === 302) - && typeof response.headers.location !== 'undefined') { - location = url.parse(response.headers.location); - if (location.host === req.headers.host) { - if (self.source.https && !self.target.https) { - response.headers.location = response.headers.location.replace(/^http\:/, 'https:'); - } - if (self.target.https && !self.source.https) { - response.headers.location = response.headers.location.replace(/^https\:/, 'http:'); - } - } - } - - // - // When the `reverseProxy` `response` ends, end the - // corresponding outgoing `res` unless we have entered - // an error state. In which case, assume `res.end()` has - // already been called and the 'error' event listener - // removed. - // - var ended = false; - response.on('close', function () { - if (!ended) { response.emit('end') } - }); - - // - // After reading a chunked response, the underlying socket - // will hit EOF and emit a 'end' event, which will abort - // the request. If the socket was paused at that time, - // pending data gets discarded, truncating the response. - // This code makes sure that we flush pending data. - // - response.connection.on('end', function () { - if (response.readable && response.resume) { - response.resume(); - } - }); - - response.on('end', function () { - ended = true; - if (!errState) { - try { res.end() } - catch (ex) { console.error("res.end error: %s", ex.message) } - - // Emit the `end` event now that we have completed proxying - self.emit('end', req, res, response); - } - }); - - // Allow observer to modify headers or abort response - try { self.emit('proxyResponse', req, res, response) } - catch (ex) { - errState = true; - return; - } - - // Set the headers of the client response - if (res.sentHeaders !== true) { - Object.keys(response.headers).forEach(function (key) { - res.setHeader(key, response.headers[key]); - }); - res.writeHead(response.statusCode); - } - - function ondata(chunk) { - if (res.writable) { - // Only pause if the underlying buffers are full, - // *and* the connection is not in 'closing' state. - // Otherwise, the pause will cause pending data to - // be discarded and silently lost. - if (false === res.write(chunk) && response.pause - && response.connection.readable) { - response.pause(); - } - } - } - - response.on('data', ondata); - - function ondrain() { - if (response.readable && response.resume) { - response.resume(); - } - } - - res.on('drain', ondrain); - }); - - // allow unlimited listeners ... - reverseProxy.setMaxListeners(this.eventListenerCount); - - // - // Handle 'error' events from the `reverseProxy`. Setup timeout override if needed - // - reverseProxy.once('error', proxyError); - - // Set a timeout on the socket if `this.timeout` is specified. - reverseProxy.once('socket', function (socket) { - if (self.timeout) { - socket.setTimeout(self.timeout); - } - }); - - // - // Handle 'error' events from the `req` (e.g. `Parse Error`). - // - req.on('error', proxyError); - - // - // If `req` is aborted, we abort our `reverseProxy` request as well. - // - req.on('aborted', function () { - reverseProxy.abort(); - }); - - // - // For each data `chunk` received from the incoming - // `req` write it to the `reverseProxy` request. - // - req.on('data', function (chunk) { - if (!errState) { - var flushed = reverseProxy.write(chunk); - if (!flushed) { - req.pause(); - reverseProxy.once('drain', function () { - try { req.resume() } - catch (er) { console.error("req.resume error: %s", er.message) } - }); - - // - // Force the `drain` event in 100ms if it hasn't - // happened on its own. - // - setTimeout(function () { - reverseProxy.emit('drain'); - }, 100); - } - } - }); - - // - // When the incoming `req` ends, end the corresponding `reverseProxy` - // request unless we have entered an error state. - // - req.on('end', function () { - if (!errState) { - reverseProxy.end(); - } - }); - - //Aborts reverseProxy if client aborts the connection. - req.on('close', function () { - if (!errState) { - reverseProxy.abort(); - } - }); - - // - // If we have been passed buffered data, resume it. - // - if (buffer) { - return !errState - ? buffer.resume() - : buffer.destroy(); - } -}; - -// -// ### function proxyWebSocketRequest (req, socket, head, buffer) -// #### @req {ServerRequest} Websocket request to proxy. -// #### @socket {net.Socket} Socket for the underlying HTTP request -// #### @head {string} Headers for the Websocket request. -// #### @buffer {Object} Result from `httpProxy.buffer(req)` -// Performs a WebSocket proxy operation to the location specified by -// `this.target`. -// -HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, upgradeHead, buffer) { - var self = this, - outgoing = new(this.target.base), - listeners = {}, - errState = false, - CRLF = '\r\n', - //copy upgradeHead to avoid retention of large slab buffers used in node core - head = new Buffer(upgradeHead.length); - upgradeHead.copy(head); - - // - // WebSocket requests must have the `GET` method and - // the `upgrade:websocket` header - // - if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') { - // - // This request is not WebSocket request - // - return socket.destroy(); - } - - // - // Add common proxy headers to the request so that they can - // be availible to the proxy target server. If the proxy is - // part of proxy chain it will append the address: - // - // * `x-forwarded-for`: IP Address of the original request - // * `x-forwarded-proto`: Protocol of the original request - // * `x-forwarded-port`: Port of the original request. - // - if (this.enable.xforward && req.connection) { - if (req.headers['x-forwarded-for']) { - var addressToAppend = "," + req.connection.remoteAddress || socket.remoteAddress; - req.headers['x-forwarded-for'] += addressToAppend; - } - else { - req.headers['x-forwarded-for'] = req.connection.remoteAddress || socket.remoteAddress; - } - - if (req.headers['x-forwarded-port']) { - var portToAppend = "," + getPortFromHostHeader(req); - req.headers['x-forwarded-port'] += portToAppend; - } - else { - req.headers['x-forwarded-port'] = getPortFromHostHeader(req); - } - - if (req.headers['x-forwarded-proto']) { - var protoToAppend = "," + (req.connection.pair ? 'wss' : 'ws'); - req.headers['x-forwarded-proto'] += protoToAppend; - } - else { - req.headers['x-forwarded-proto'] = req.connection.pair ? 'wss' : 'ws'; - } - } - - self.emit('websocket:start', req, socket, head, this.target); - - // - // Helper function for setting appropriate socket values: - // 1. Turn of all bufferings - // 2. For server set KeepAlive - // - function _socket(socket, keepAlive) { - socket.setTimeout(0); - socket.setNoDelay(true); - - if (keepAlive) { - if (socket.setKeepAlive) { - socket.setKeepAlive(true, 0); - } - else if (socket.pair.cleartext.socket.setKeepAlive) { - socket.pair.cleartext.socket.setKeepAlive(true, 0); - } - } - } - - // - // Setup the incoming client socket. - // - _socket(socket, true); - - // - // On `upgrade` from the Agent socket, listen to - // the appropriate events. - // - function onUpgrade (reverseProxy, proxySocket) { - if (!reverseProxy) { - proxySocket.end(); - socket.end(); - return; - } - - // - // Any incoming data on this WebSocket to the proxy target - // will be written to the `reverseProxy` socket. - // - proxySocket.on('data', listeners.onIncoming = function (data) { - if (reverseProxy.incoming.socket.writable) { - try { - self.emit('websocket:outgoing', req, socket, head, data); - var flushed = reverseProxy.incoming.socket.write(data); - if (!flushed) { - proxySocket.pause(); - reverseProxy.incoming.socket.once('drain', function () { - try { proxySocket.resume() } - catch (er) { console.error("proxySocket.resume error: %s", er.message) } - }); - - // - // Force the `drain` event in 100ms if it hasn't - // happened on its own. - // - setTimeout(function () { - reverseProxy.incoming.socket.emit('drain'); - }, 100); - } - } - catch (ex) { - detach(); - } - } - }); - - // - // Any outgoing data on this Websocket from the proxy target - // will be written to the `proxySocket` socket. - // - reverseProxy.incoming.socket.on('data', listeners.onOutgoing = function (data) { - try { - self.emit('websocket:incoming', reverseProxy, reverseProxy.incoming, head, data); - var flushed = proxySocket.write(data); - if (!flushed) { - reverseProxy.incoming.socket.pause(); - proxySocket.once('drain', function () { - try { reverseProxy.incoming.socket.resume() } - catch (er) { console.error("reverseProxy.incoming.socket.resume error: %s", er.message) } - }); - - // - // Force the `drain` event in 100ms if it hasn't - // happened on its own. - // - setTimeout(function () { - proxySocket.emit('drain'); - }, 100); - } - } - catch (ex) { - detach(); - } - }); - - // - // Helper function to detach all event listeners - // from `reverseProxy` and `proxySocket`. - // - function detach() { - proxySocket.destroySoon(); - proxySocket.removeListener('end', listeners.onIncomingClose); - proxySocket.removeListener('data', listeners.onIncoming); - reverseProxy.incoming.socket.destroySoon(); - reverseProxy.incoming.socket.removeListener('end', listeners.onOutgoingClose); - reverseProxy.incoming.socket.removeListener('data', listeners.onOutgoing); - } - - // - // If the incoming `proxySocket` socket closes, then - // detach all event listeners. - // - listeners.onIncomingClose = function () { - reverseProxy.incoming.socket.destroy(); - detach(); - - // Emit the `end` event now that we have completed proxying - self.emit('websocket:end', req, socket, head); - } - - // - // If the `reverseProxy` socket closes, then detach all - // event listeners. - // - listeners.onOutgoingClose = function () { - proxySocket.destroy(); - detach(); - } - - proxySocket.on('end', listeners.onIncomingClose); - proxySocket.on('close', listeners.onIncomingClose); - reverseProxy.incoming.socket.on('end', listeners.onOutgoingClose); - reverseProxy.incoming.socket.on('close', listeners.onOutgoingClose); - } - - function getPort (port) { - port = port || 80; - return port - 80 === 0 ? '' : ':' + port; - } - - // - // Get the protocol, and host for this request and create an instance - // of `http.Agent` or `https.Agent` from the pool managed by `node-http-proxy`. - // - var agent = this.target.agent, - protocolName = this.target.https ? 'https' : 'http', - portUri = getPort(this.source.port), - remoteHost = this.target.host + portUri; - - // - // Change headers (if requested). - // - if (this.changeOrigin) { - req.headers.host = remoteHost; - req.headers.origin = protocolName + '://' + remoteHost; - } - - // - // Make the outgoing WebSocket request - // - outgoing.host = this.target.host; - outgoing.port = this.target.port; - outgoing.agent = agent; - outgoing.method = 'GET'; - outgoing.path = req.url; - outgoing.headers = req.headers; - outgoing.agent = agent; - - var reverseProxy = this.target.protocol.request(outgoing); - - // - // On any errors from the `reverseProxy` emit the - // `webSocketProxyError` and close the appropriate - // connections. - // - function proxyError (err) { - reverseProxy.destroy(); - - process.nextTick(function () { - // - // Destroy the incoming socket in the next tick, in case the error handler - // wants to write to it. - // - socket.destroy(); - }); - - self.emit('webSocketProxyError', err, req, socket, head); - } - - // - // Here we set the incoming `req`, `socket` and `head` data to the outgoing - // request so that we can reuse this data later on in the closure scope - // available to the `upgrade` event. This bookkeeping is not tracked anywhere - // in nodejs core and is **very** specific to proxying WebSockets. - // - reverseProxy.incoming = { - request: req, - socket: socket, - head: head - }; - - // - // Here we set the handshake `headers` and `statusCode` data to the outgoing - // request so that we can reuse this data later. - // - reverseProxy.handshake = { - headers: {}, - statusCode: null, - } - - // - // If the agent for this particular `host` and `port` combination - // is not already listening for the `upgrade` event, then do so once. - // This will force us not to disconnect. - // - // In addition, it's important to note the closure scope here. Since - // there is no mapping of the socket to the request bound to it. - // - reverseProxy.on('upgrade', function (res, remoteSocket, head) { - // - // Prepare handshake response 'headers' and 'statusCode'. - // - reverseProxy.handshake = { - headers: res.headers, - statusCode: res.statusCode, - } - - // - // Prepare the socket for the reverseProxy request and begin to - // stream data between the two sockets. Here it is important to - // note that `remoteSocket._httpMessage === reverseProxy`. - // - _socket(remoteSocket, true); - onUpgrade(remoteSocket._httpMessage, remoteSocket); - }); - - // - // If the reverseProxy connection has an underlying socket, - // then execute the WebSocket handshake. - // - reverseProxy.once('socket', function (revSocket) { - revSocket.on('data', function handshake (data) { - // Set empty headers - var headers = ''; - - // - // If the handshake statusCode 101, concat headers. - // - if (reverseProxy.handshake.statusCode && reverseProxy.handshake.statusCode == 101) { - headers = [ - 'HTTP/1.1 101 Switching Protocols', - 'Upgrade: websocket', - 'Connection: Upgrade', - 'Sec-WebSocket-Accept: ' + reverseProxy.handshake.headers['sec-websocket-accept'] - ]; - - headers = headers.concat('', '').join('\r\n'); - } - - // - // Ok, kind of harmfull part of code. Socket.IO sends a hash - // at the end of handshake if protocol === 76, but we need - // to replace 'host' and 'origin' in response so we split - // data to printable data and to non-printable. (Non-printable - // will come after double-CRLF). - // - var sdata = data.toString(); - - // Get the Printable data - sdata = sdata.substr(0, sdata.search(CRLF + CRLF)); - - // Get the Non-Printable data - data = data.slice(Buffer.byteLength(sdata), data.length); - - if (self.source.https && !self.target.https) { - // - // If the proxy server is running HTTPS but the client is running - // HTTP then replace `ws` with `wss` in the data sent back to the client. - // - sdata = sdata.replace('ws:', 'wss:'); - } - - try { - // - // Write the printable and non-printable data to the socket - // from the original incoming request. - // - self.emit('websocket:handshake', req, socket, head, sdata, data); - // add headers to the socket - socket.write(headers + sdata); - var flushed = socket.write(data); - if (!flushed) { - revSocket.pause(); - socket.once('drain', function () { - try { revSocket.resume() } - catch (er) { console.error("reverseProxy.socket.resume error: %s", er.message) } - }); - - // - // Force the `drain` event in 100ms if it hasn't - // happened on its own. - // - setTimeout(function () { - socket.emit('drain'); - }, 100); - } - } - catch (ex) { - // - // Remove data listener on socket error because the - // 'handshake' has failed. - // - revSocket.removeListener('data', handshake); - return proxyError(ex); - } - - // - // Remove data listener now that the 'handshake' is complete - // - revSocket.removeListener('data', handshake); - }); - }); - - // - // Handle 'error' events from the `reverseProxy`. - // - reverseProxy.on('error', proxyError); - - // - // Handle 'error' events from the `req` (e.g. `Parse Error`). - // - req.on('error', proxyError); - - try { - // - // Attempt to write the upgrade-head to the reverseProxy - // request. This is small, and there's only ever one of - // it; no need for pause/resume. - // - // XXX This is very wrong and should be fixed in node's core - // - reverseProxy.write(head); - if (head && head.length === 0) { - reverseProxy._send(''); - } - } - catch (ex) { - return proxyError(ex); - } - - // - // If we have been passed buffered data, resume it. - // - if (buffer) { - return !errState - ? buffer.resume() - : buffer.destroy(); - } -}; - -// -// ### function close() -// Closes all sockets associated with the Agents -// belonging to this instance. -// -HttpProxy.prototype.close = function () { - [this.forward, this.target].forEach(function (proxy) { - if (proxy && proxy.agent) { - for (var host in proxy.agent.sockets) { - proxy.agent.sockets[host].forEach(function (socket) { - socket.end(); - }); - } - } - }); -}; - -// -// ### @private function _forwardRequest (req) -// #### @req {ServerRequest} Incoming HTTP Request to proxy. -// Forwards the specified `req` to the location specified -// by `this.forward` ignoring errors and the subsequent response. -// -HttpProxy.prototype._forwardRequest = function (req) { - var self = this, - outgoing = new(this.forward.base), - forwardProxy; - - // - // Setup outgoing proxy with relevant properties. - // - outgoing.host = this.forward.host; - outgoing.port = this.forward.port, - outgoing.agent = this.forward.agent; - outgoing.method = req.method; - outgoing.path = req.url; - outgoing.headers = req.headers; - - // - // Open new HTTP request to internal resource with will - // act as a reverse proxy pass. - // - forwardProxy = this.forward.protocol.request(outgoing, function (response) { - // - // Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy. - // Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning. - // - }); - - // - // Add a listener for the connection timeout event. - // - // Remark: Ignoring this error in the event - // forward target doesn't exist. - // - forwardProxy.once('error', function (err) { }); - - // - // Chunk the client request body as chunks from - // the proxied request come in - // - req.on('data', function (chunk) { - var flushed = forwardProxy.write(chunk); - if (!flushed) { - req.pause(); - forwardProxy.once('drain', function () { - try { req.resume() } - catch (er) { console.error("req.resume error: %s", er.message) } - }); - - // - // Force the `drain` event in 100ms if it hasn't - // happened on its own. - // - setTimeout(function () { - forwardProxy.emit('drain'); - }, 100); - } - }); - - // - // At the end of the client request, we are going to - // stop the proxied request - // - req.on('end', function () { - forwardProxy.end(); - }); -}; - -function getPortFromHostHeader(req) { - var match; - if ((match = extractPort.exec(req.headers.host))) { - return parseInt(match[1]); - } - - return getProto(req) === 'https' ? 443 : 80; -} - -function getProto(req) { - return req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http'); -} diff --git a/lib/node-http-proxy/proxy-table.js b/lib/node-http-proxy/proxy-table.js deleted file mode 100644 index 320396fe8..000000000 --- a/lib/node-http-proxy/proxy-table.js +++ /dev/null @@ -1,282 +0,0 @@ -/* - node-http-proxy.js: Lookup table for proxy targets in node.js - - Copyright (c) 2010 Charlie Robbins - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - events = require('events'), - fs = require('fs'), - url = require('url'); - -// -// ### function ProxyTable (router, silent) -// #### @router {Object} Object containing the host based routes -// #### @silent {Boolean} Value indicating whether we should suppress logs -// #### @hostnameOnly {Boolean} Value indicating if we should route based on __hostname string only__ -// #### @pathnameOnly {Boolean} Value indicating if we should route based on only the pathname. __This causes hostnames to be ignored.__. Using this along with hostnameOnly wont work at all. -// Constructor function for the ProxyTable responsible for getting -// locations of proxy targets based on ServerRequest headers; specifically -// the HTTP host header. -// -var ProxyTable = exports.ProxyTable = function (options) { - events.EventEmitter.call(this); - - this.silent = options.silent || options.silent !== true; - this.target = options.target || {}; - this.pathnameOnly = options.pathnameOnly === true; - this.hostnameOnly = options.hostnameOnly === true; - - if (typeof options.router === 'object') { - // - // If we are passed an object literal setup - // the routes with RegExps from the router - // - this.setRoutes(options.router); - } - else if (typeof options.router === 'string') { - // - // If we are passed a string then assume it is a - // file path, parse that file and watch it for changes - // - var self = this; - this.routeFile = options.router; - this.setRoutes(JSON.parse(fs.readFileSync(options.router)).router); - - fs.watchFile(this.routeFile, function () { - fs.readFile(self.routeFile, function (err, data) { - if (err) { - self.emit('error', err); - } - - self.setRoutes(JSON.parse(data).router); - self.emit('routes', self.hostnameOnly === false ? self.routes : self.router); - }); - }); - } - else { - throw new Error('Cannot parse router with unknown type: ' + typeof router); - } -}; - -// -// Inherit from `events.EventEmitter` -// -util.inherits(ProxyTable, events.EventEmitter); - -// -// ### function addRoute (route, target) -// #### @route {String} String containing route coming in -// #### @target {String} String containing the target -// Adds a host-based route to this instance. -// -ProxyTable.prototype.addRoute = function (route, target) { - if (!this.router) { - throw new Error('Cannot update ProxyTable routes without router.'); - } - - this.router[route] = target; - this.setRoutes(this.router); -}; - -// -// ### function removeRoute (route) -// #### @route {String} String containing route to remove -// Removes a host-based route from this instance. -// -ProxyTable.prototype.removeRoute = function (route) { - if (!this.router) { - throw new Error('Cannot update ProxyTable routes without router.'); - } - - delete this.router[route]; - this.setRoutes(this.router); -}; - -// -// ### function setRoutes (router) -// #### @router {Object} Object containing the host based routes -// Sets the host-based routes to be used by this instance. -// -ProxyTable.prototype.setRoutes = function (router) { - if (!router) { - throw new Error('Cannot update ProxyTable routes without router.'); - } - - var self = this; - this.router = router; - - if (this.hostnameOnly === false) { - this.routes = []; - - Object.keys(router).forEach(function (path) { - if (!/http[s]?/.test(router[path])) { - router[path] = (self.target.https ? 'https://' : 'http://') - + router[path]; - } - - var target = url.parse(router[path]), - defaultPort = self.target.https ? 443 : 80; - - // - // Setup a robust lookup table for the route: - // - // { - // source: { - // regexp: /^foo.com/i, - // sref: 'foo.com', - // url: { - // protocol: 'http:', - // slashes: true, - // host: 'foo.com', - // hostname: 'foo.com', - // href: 'http://foo.com/', - // pathname: '/', - // path: '/' - // } - // }, - // { - // target: { - // sref: '127.0.0.1:8000/', - // url: { - // protocol: 'http:', - // slashes: true, - // host: '127.0.0.1:8000', - // hostname: '127.0.0.1', - // href: 'http://127.0.0.1:8000/', - // pathname: '/', - // path: '/' - // } - // }, - // - self.routes.push({ - source: { - regexp: new RegExp('^' + path, 'i'), - sref: path, - url: url.parse('http://' + path) - }, - target: { - sref: target.hostname + ':' + (target.port || defaultPort) + target.path, - url: target - } - }); - }); - } -}; - -// -// ### function getProxyLocation (req) -// #### @req {ServerRequest} The incoming server request to get proxy information about. -// Returns the proxy location based on the HTTP Headers in the ServerRequest `req` -// available to this instance. -// -ProxyTable.prototype.getProxyLocation = function (req) { - if (!req || !req.headers || !req.headers.host) { - return null; - } - - var targetHost = req.headers.host.split(':')[0]; - if (this.hostnameOnly === true) { - var target = targetHost; - if (this.router.hasOwnProperty(target)) { - var location = this.router[target].split(':'), - host = location[0], - port = location.length === 1 ? 80 : location[1]; - - return { - port: port, - host: host - }; - } - } - else if (this.pathnameOnly === true) { - var target = req.url; - for (var i in this.routes) { - var route = this.routes[i]; - // - // If we are matching pathname only, we remove the matched pattern. - // - // IE /wiki/heartbeat - // is redirected to - // /heartbeat - // - // for the route "/wiki" : "127.0.0.1:8020" - // - if (target.match(route.source.regexp)) { - req.url = url.format(target.replace(route.source.regexp, '')); - return { - protocol: route.target.url.protocol.replace(':', ''), - host: route.target.url.hostname, - port: route.target.url.port - || (this.target.https ? 443 : 80) - }; - } - } - - } - else { - var target = targetHost + req.url; - for (var i in this.routes) { - var route = this.routes[i]; - if (target.match(route.source.regexp)) { - // - // Attempt to perform any path replacement for differences - // between the source path and the target path. This replaces the - // path's part of the URL to the target's part of the URL. - // - // 1. Parse the request URL - // 2. Replace any portions of the source path with the target path - // 3. Set the request URL to the formatted URL with replacements. - // - var parsed = url.parse(req.url); - - parsed.pathname = parsed.pathname.replace( - route.source.url.pathname, - route.target.url.pathname - ); - - req.url = url.format(parsed); - - return { - protocol: route.target.url.protocol.replace(':', ''), - host: route.target.url.hostname, - port: route.target.url.port - || (this.target.https ? 443 : 80) - }; - } - } - } - - return null; -}; - -// -// ### close function () -// Cleans up the event listeneners maintained -// by this instance. -// -ProxyTable.prototype.close = function () { - if (typeof this.routeFile === 'string') { - fs.unwatchFile(this.routeFile); - } -}; diff --git a/lib/node-http-proxy/routing-proxy.js b/lib/node-http-proxy/routing-proxy.js deleted file mode 100644 index b294fb15d..000000000 --- a/lib/node-http-proxy/routing-proxy.js +++ /dev/null @@ -1,322 +0,0 @@ -/* - * routing-proxy.js: A routing proxy consuming a RoutingTable and multiple HttpProxy instances - * - * (C) 2011 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var events = require('events'), - utile = require('utile'), - HttpProxy = require('./http-proxy').HttpProxy, - ProxyTable = require('./proxy-table').ProxyTable; - -// -// ### function RoutingProxy (options) -// #### @options {Object} Options for this instance -// Constructor function for the RoutingProxy object, a higher level -// reverse proxy Object which can proxy to multiple hosts and also interface -// easily with a RoutingTable instance. -// -var RoutingProxy = exports.RoutingProxy = function (options) { - events.EventEmitter.call(this); - - var self = this; - options = options || {}; - - if (options.router) { - this.proxyTable = new ProxyTable(options); - this.proxyTable.on('routes', function (routes) { - self.emit('routes', routes); - }); - } - - // - // Create a set of `HttpProxy` objects to be used later on calls - // to `.proxyRequest()` and `.proxyWebSocketRequest()`. - // - this.proxies = {}; - - // - // Setup default target options (such as `https`). - // - this.target = {}; - this.target.https = options.target && options.target.https; - this.target.maxSockets = options.target && options.target.maxSockets; - - // - // Setup other default options to be used for instances of - // `HttpProxy` created by this `RoutingProxy` instance. - // - this.source = options.source || { host: 'localhost', port: 8000 }; - this.https = this.source.https || options.https; - this.enable = options.enable; - this.forward = options.forward; - this.changeOrigin = options.changeOrigin || false; - - // - // Listen for 'newListener' events so that we can bind 'proxyError' - // listeners to each HttpProxy's 'proxyError' event. - // - this.on('newListener', function (evt) { - if (evt === 'proxyError' || evt === 'webSocketProxyError') { - Object.keys(self.proxies).forEach(function (key) { - self.proxies[key].on(evt, self.emit.bind(self, evt)); - }); - } - }); -}; - - -// -// Inherit from `events.EventEmitter`. -// -utile.inherits(RoutingProxy, events.EventEmitter); - -// -// ### function add (options) -// #### @options {Object} Options for the `HttpProxy` to add. -// Adds a new instance of `HttpProxy` to this `RoutingProxy` instance -// for the specified `options.host` and `options.port`. -// -RoutingProxy.prototype.add = function (options) { - var self = this, - key = this._getKey(options); - - // - // TODO: Consume properties in `options` related to the `ProxyTable`. - // - options.target = options.target || {}; - options.target.host = options.target.host || options.host; - options.target.port = options.target.port || options.port; - options.target.socketPath = options.target.socketPath || options.socketPath; - options.target.https = this.target && this.target.https || - options.target && options.target.https; - options.target.maxSockets = this.target && this.target.maxSockets; - - // - // Setup options to pass-thru to the new `HttpProxy` instance - // for the specified `options.host` and `options.port` pair. - // - ['https', 'enable', 'forward', 'changeOrigin'].forEach(function (key) { - if (options[key] !== false && self[key]) { - options[key] = self[key]; - } - }); - - this.proxies[key] = new HttpProxy(options); - - if (this.listeners('proxyError').length > 0) { - this.proxies[key].on('proxyError', this.emit.bind(this, 'proxyError')); - } - - if (this.listeners('webSocketProxyError').length > 0) { - this.proxies[key].on('webSocketProxyError', this.emit.bind(this, 'webSocketProxyError')); - } - - [ - 'start', - 'forward', - 'end', - 'proxyResponse', - 'websocket:start', - 'websocket:end', - 'websocket:incoming', - 'websocket:outgoing' - ].forEach(function (event) { - this.proxies[key].on(event, this.emit.bind(this, event)); - }, this); -}; - -// -// ### function remove (options) -// #### @options {Object} Options mapping to the `HttpProxy` to remove. -// Removes an instance of `HttpProxy` from this `RoutingProxy` instance -// for the specified `options.host` and `options.port` (if they exist). -// -RoutingProxy.prototype.remove = function (options) { - var key = this._getKey(options), - proxy = this.proxies[key]; - - delete this.proxies[key]; - return proxy; -}; - -// -// ### function close() -// Cleans up any state left behind (sockets, timeouts, etc) -// associated with this instance. -// -RoutingProxy.prototype.close = function () { - var self = this; - - if (this.proxyTable) { - // - // Close the `RoutingTable` associated with - // this instance (if any). - // - this.proxyTable.close(); - } - - // - // Close all sockets for all `HttpProxy` object(s) - // associated with this instance. - // - Object.keys(this.proxies).forEach(function (key) { - self.proxies[key].close(); - }); -}; - -// -// ### function proxyRequest (req, res, [port, host, paused]) -// #### @req {ServerRequest} Incoming HTTP Request to proxy. -// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to. -// #### @options {Object} Options for the outgoing proxy request. -// -// options.port {number} Port to use on the proxy target host. -// options.host {string} Host of the proxy target. -// options.buffer {Object} Result from `httpProxy.buffer(req)` -// options.https {Object|boolean} Settings for https. -// -RoutingProxy.prototype.proxyRequest = function (req, res, options) { - options = options || {}; - - var location; - - // - // Check the proxy table for this instance to see if we need - // to get the proxy location for the request supplied. We will - // always ignore the proxyTable if an explicit `port` and `host` - // arguments are supplied to `proxyRequest`. - // - if (this.proxyTable && !options.host) { - location = this.proxyTable.getProxyLocation(req); - - // - // If no location is returned from the ProxyTable instance - // then respond with `404` since we do not have a valid proxy target. - // - if (!location) { - try { - if (!this.emit('notFound', req, res)) { - res.writeHead(404); - res.end(); - } - } - catch (er) { - console.error("res.writeHead/res.end error: %s", er.message); - } - - return; - } - - // - // When using the ProxyTable in conjunction with an HttpProxy instance - // only the following arguments are valid: - // - // * `proxy.proxyRequest(req, res, { host: 'localhost' })`: This will be skipped - // * `proxy.proxyRequest(req, res, { buffer: buffer })`: Buffer will get updated appropriately - // * `proxy.proxyRequest(req, res)`: Options will be assigned appropriately. - // - options.port = location.port; - options.host = location.host; - } - - var key = this._getKey(options), - proxy; - - if ((this.target && this.target.https) - || (location && location.protocol === 'https')) { - options.target = options.target || {}; - options.target.https = true; - } - - if (!this.proxies[key]) { - this.add(utile.clone(options)); - } - - proxy = this.proxies[key]; - proxy.proxyRequest(req, res, options.buffer); -}; - -// -// ### function proxyWebSocketRequest (req, socket, head, options) -// #### @req {ServerRequest} Websocket request to proxy. -// #### @socket {net.Socket} Socket for the underlying HTTP request -// #### @head {string} Headers for the Websocket request. -// #### @options {Object} Options to use when proxying this request. -// -// options.port {number} Port to use on the proxy target host. -// options.host {string} Host of the proxy target. -// options.buffer {Object} Result from `httpProxy.buffer(req)` -// options.https {Object|boolean} Settings for https. -// -RoutingProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) { - options = options || {}; - - var location, - proxy, - key; - - if (this.proxyTable && !options.host) { - location = this.proxyTable.getProxyLocation(req); - - if (!location) { - return socket.destroy(); - } - - options.port = location.port; - options.host = location.host; - } - - key = this._getKey(options); - - if (!this.proxies[key]) { - this.add(utile.clone(options)); - } - - proxy = this.proxies[key]; - proxy.proxyWebSocketRequest(req, socket, head, options.buffer); -}; - -// -// ### function addHost (host, target) -// #### @host {String} Host to add to proxyTable -// #### @target {String} Target to add to proxyTable -// Adds a host to proxyTable -// -RoutingProxy.prototype.addHost = function (host, target) { - if (this.proxyTable) { - this.proxyTable.addRoute(host, target); - } -}; - -// -// ### function removeHost (host) -// #### @host {String} Host to remove from proxyTable -// Removes a host to proxyTable -// -RoutingProxy.prototype.removeHost = function (host) { - if (this.proxyTable) { - this.proxyTable.removeRoute(host); - } -}; - -// -// ### @private function _getKey (options) -// #### @options {Object} Options to extract the key from -// Ensures that the appropriate options are present in the `options` -// provided and responds with a string key representing the `host`, `port` -// combination contained within. -// -RoutingProxy.prototype._getKey = function (options) { - if (!options || ((!options.host || !options.port) - && (!options.target || !options.target.host || !options.target.port))) { - throw new Error('options.host and options.port or options.target are required.'); - } - - return [ - options.host || options.target.host, - options.port || options.target.port - ].join(':'); -}; diff --git a/package.json b/package.json deleted file mode 100644 index cd3ff0add..000000000 --- a/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "http-proxy", - "version": "0.10.4", - "description": "A full-featured http reverse proxy for node.js", - "author": "Nodejitsu Inc. ", - "maintainers": [ - "indexzero ", - "AvianFlu " - ], - "repository": { - "type": "git", - "url": "http://github.com/nodejitsu/node-http-proxy.git" - }, - "keywords": [ - "reverse", - "proxy", - "http" - ], - "dependencies": { - "colors": "0.x.x", - "optimist": "0.6.x", - "pkginfo": "0.3.x", - "utile": "~0.2.1" - }, - "devDependencies": { - "request": "2.14.x", - "vows": "0.7.x", - "async": "0.2.x", - "socket.io": "0.9.11", - "socket.io-client": "0.9.11", - "ws": "0.4.23" - }, - "main": "./lib/node-http-proxy", - "bin": { - "node-http-proxy": "./bin/node-http-proxy" - }, - "scripts": { - "test": "npm run-script test-http && npm run-script test-https && npm run-script test-core", - "test-http": "vows --spec && vows --spec --target=https", - "test-https": "vows --spec --proxy=https && vows --spec --proxy=https --target=https", - "test-core": "test/core/run" - }, - "engines": { - "node": ">= 0.6.6" - } -} - diff --git a/test/core/README.md b/test/core/README.md deleted file mode 100644 index 152e5c668..000000000 --- a/test/core/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# `test/core` - -`test/core` directory is a place where tests from node.js core go. They are -here to ensure that node-http-proxy works just fine with all kinds of -different situations, which are covered in core tests, but are not covered in -our tests. - -All these tests require little modifications to make them test node-http-proxy, -but we try to keep them as vanilla as possible. - diff --git a/test/core/common.js b/test/core/common.js deleted file mode 100644 index 3f584a543..000000000 --- a/test/core/common.js +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var path = require('path'); -var assert = require('assert'); - -exports.testDir = path.dirname(__filename); -exports.fixturesDir = path.join(exports.testDir, 'fixtures'); -exports.libDir = path.join(exports.testDir, '../lib'); -exports.tmpDir = path.join(exports.testDir, 'tmp'); -exports.PORT = 12346; -exports.PROXY_PORT = 1234567; - -if (process.platform === 'win32') { - exports.PIPE = '\\\\.\\pipe\\libuv-test'; -} else { - exports.PIPE = exports.tmpDir + '/test.sock'; -} - -var util = require('util'); -for (var i in util) exports[i] = util[i]; -//for (var i in exports) global[i] = exports[i]; - -function protoCtrChain(o) { - var result = []; - for (; o; o = o.__proto__) { result.push(o.constructor); } - return result.join(); -} - -exports.indirectInstanceOf = function (obj, cls) { - if (obj instanceof cls) { return true; } - var clsChain = protoCtrChain(cls.prototype); - var objChain = protoCtrChain(obj); - return objChain.slice(-clsChain.length) === clsChain; -}; - - -exports.ddCommand = function (filename, kilobytes) { - if (process.platform === 'win32') { - var p = path.resolve(exports.fixturesDir, 'create-file.js'); - return '"' + process.argv[0] + '" "' + p + '" "' + - filename + '" ' + (kilobytes * 1024); - } else { - return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes; - } -}; - - -exports.spawnPwd = function (options) { - var spawn = require('child_process').spawn; - - if (process.platform === 'win32') { - return spawn('cmd.exe', ['/c', 'cd'], options); - } else { - return spawn('pwd', [], options); - } -}; - - -// Turn this off if the test should not check for global leaks. -exports.globalCheck = true; - -process.on('exit', function () { - if (!exports.globalCheck) return; - var knownGlobals = [setTimeout, - setInterval, - clearTimeout, - clearInterval, - console, - Buffer, - process, - global]; - - if (global.setImmediate) { - knownGlobals.push(setImmediate); - knownGlobals.push(clearImmediate); - } - - if (global.errno) { - knownGlobals.push(errno); - } - - if (global.gc) { - knownGlobals.push(gc); - } - - if (global.DTRACE_HTTP_SERVER_RESPONSE) { - knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE); - knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST); - knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE); - knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST); - knownGlobals.push(DTRACE_NET_STREAM_END); - knownGlobals.push(DTRACE_NET_SERVER_CONNECTION); - knownGlobals.push(DTRACE_NET_SOCKET_READ); - knownGlobals.push(DTRACE_NET_SOCKET_WRITE); - } - - if (global.ArrayBuffer) { - knownGlobals.push(ArrayBuffer); - knownGlobals.push(Int8Array); - knownGlobals.push(Uint8Array); - knownGlobals.push(Int16Array); - knownGlobals.push(Uint16Array); - knownGlobals.push(Int32Array); - knownGlobals.push(Uint32Array); - knownGlobals.push(Float32Array); - knownGlobals.push(Float64Array); - knownGlobals.push(DataView); - - if (global.Uint8ClampedArray) { - knownGlobals.push(Uint8ClampedArray); - } - } - - for (var x in global) { - var found = false; - - for (var y in knownGlobals) { - if (global[x] === knownGlobals[y]) { - found = true; - break; - } - } - - if (!found) { - console.error('Unknown global: %s', x); - assert.ok(false, 'Unknown global found'); - } - } -}); - - -var mustCallChecks = []; - - -function runCallChecks() { - var failed = mustCallChecks.filter(function (context) { - return context.actual !== context.expected; - }); - - failed.forEach(function (context) { - console.log('Mismatched %s function calls. Expected %d, actual %d.', - context.name, - context.expected, - context.actual); - console.log(context.stack.split('\n').slice(2).join('\n')); - }); - - if (failed.length) process.exit(1); -} - - -exports.mustCall = function (fn, expected) { - if (typeof expected !== 'number') expected = 1; - - var context = { - expected: expected, - actual: 0, - stack: (new Error).stack, - name: fn.name || '' - }; - - // add the exit listener only once to avoid listener leak warnings - if (mustCallChecks.length === 0) process.on('exit', runCallChecks); - - mustCallChecks.push(context); - - return function () { - context.actual++; - return fn.apply(this, arguments); - }; -}; diff --git a/test/core/pummel/test-http-upload-timeout.js b/test/core/pummel/test-http-upload-timeout.js deleted file mode 100644 index 74b458f8b..000000000 --- a/test/core/pummel/test-http-upload-timeout.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// This tests setTimeout() by having multiple clients connecting and sending -// data in random intervals. Clients are also randomly disconnecting until there -// are no more clients left. If no false timeout occurs, this test has passed. -var common = require('../common'), - assert = require('assert'), - http = require('http'), - server = http.createServer(), - connections = 0; - -server.on('request', function (req, res) { - req.socket.setTimeout(1000); - req.socket.on('timeout', function () { - throw new Error('Unexpected timeout'); - }); - req.on('end', function () { - connections--; - res.writeHead(200); - res.end('done\n'); - if (connections == 0) { - server.close(); - } - }); -}); - -server.listen(common.PORT, '127.0.0.1', function () { - for (var i = 0; i < 10; i++) { - connections++; - - setTimeout(function () { - var request = http.request({ - port: common.PROXY_PORT, - method: 'POST', - path: '/' - }); - - function ping() { - var nextPing = (Math.random() * 900).toFixed(); - if (nextPing > 600) { - request.end(); - return; - } - request.write('ping'); - setTimeout(ping, nextPing); - } - ping(); - }, i * 50); - } -}); diff --git a/test/core/run b/test/core/run deleted file mode 100755 index adec53bab..000000000 --- a/test/core/run +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env node -/* - run.js: test runner for core tests - - Copyright (c) 2011 Nodejitsu - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var fs = require('fs'), - path = require('path'), - spawn = require('child_process').spawn, - async = require('async'), - colors = require('colors'), - optimist = require('optimist'); - -optimist.argv.color && (colors.mode = optimist.argv.color); - -var testTimeout = 15000; -var results = {}; - -function runTest(test, callback) { - var child = spawn(path.join(__dirname, 'run-single'), [ test ]); - - var killTimeout = setTimeout(function () { - child.kill(); - console.log(' ' + path.basename(test).yellow + ' timed out'.red); - }, testTimeout); - - child.on('exit', function (exitCode) { - clearTimeout(killTimeout); - - console.log(' ' + ((exitCode) ? '✘'.red : '✔'.green) + ' ' + - path.basename(test) + - (exitCode ? (' (exit code: ' + exitCode + ')') : '')); - results[test] = { exitCode: exitCode }; - callback(); - // - // We don't want tests to be stopped after first failure, and that's what - // async does when it receives truthy value in callback. - // - }); -}; - -var tests = process.argv.slice(2).filter(function (test) { - return test.substr(0, 2) != '--'; -}); - -if (!tests.length) { - var pathPrefix = path.join(__dirname, 'simple'); - tests = fs.readdirSync(pathPrefix).map(function (test) { - return path.join(pathPrefix, test); - }); - // - // We only run simple tests by default. - // -} - -console.log('Running tests:'.bold); -async.forEachSeries(tests, runTest, function () { - var failed = [], ok = []; - for (var test in results) { - (results[test].exitCode != 0 ? failed : ok).push(test); - } - - console.log('\nSummary:'.bold); - console.log((' ' + ok.length + '\tpassed tests').green); - console.log((' ' + failed.length + '\tfailed tests').red); -}); - -// vim:filetype=javascript - diff --git a/test/core/run-single b/test/core/run-single deleted file mode 100755 index ae53588af..000000000 --- a/test/core/run-single +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env node -/* - run-single.js: test runner for core tests - - Copyright (c) 2011 Nodejitsu - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -// -// Basic idea behind core test runner is to modify core tests as little as -// possible. That's why we start up node-http-proxy here instead of embeeding -// this code in tests. -// -// In most cases only modification to core tests you'll need is changing port -// of http client to common.PROXY_PORT. -// - -var path = require('path'), - spawn = require('child_process').spawn, - httpProxy = require('../../'), - common = require('./common'); - -var test = process.argv[2], - testProcess; - -if (!test) { - return console.error('Need test to run'); -} - -console.log('Running test ' + test); - -var proxy = httpProxy.createServer(common.PORT, 'localhost'); -proxy.listen(common.PROXY_PORT); - -proxy.on('listening', function () { - console.log('Proxy server listening on ' + common.PROXY_PORT); - testProcess = spawn(process.argv[0], [ process.argv[2] ]); - testProcess.stdout.pipe(process.stdout); - testProcess.stderr.pipe(process.stderr); - - testProcess.on('exit', function (code) { - process.exit(code); - }); -}); - -process.on('SIGTERM', function () { - testProcess.kill(); - process.exit(1); -}); - -// vim:filetype=javascript diff --git a/test/core/simple/test-http-chunked.js b/test/core/simple/test-http-chunked.js deleted file mode 100644 index b1cbf0dbb..000000000 --- a/test/core/simple/test-http-chunked.js +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var UTF8_STRING = '南越国是前203年至前111年存在于岭南地区的一个国家,' + - '国都位于番禺,疆域包括今天中国的广东、广西两省区的大部份地区,福建省、湖南、' + - '贵州、云南的一小部份地区和越南的北部。南越国是秦朝灭亡后,' + - '由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。前196年和前179年,' + - '南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,' + - '南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。' + - '南越国共存在93年,历经五代君主。南越国是岭南地区的第一个有记载的政权国家,' + - '采用封建制和郡县制并存的制度,它的建立保证了秦末乱世岭南地区社会秩序的稳定,' + - '有效的改善了岭南地区落后的政治、经济现状。'; - -var server = http.createServer(function (req, res) { - res.writeHead(200, {'Content-Type': 'text/plain; charset=utf8'}); - res.end(UTF8_STRING, 'utf8'); -}); -server.listen(common.PORT, function () { - var data = ''; - var get = http.get({ - path: '/', - host: 'localhost', - port: common.PROXY_PORT - }, function (x) { - x.setEncoding('utf8'); - x.on('data', function (c) {data += c}); - x.on('error', function (e) { - throw e; - }); - x.on('end', function () { - assert.equal('string', typeof data); - console.log('here is the response:'); - assert.equal(UTF8_STRING, data); - console.log(data); - server.close(); - }); - }); - get.on('error', function (e) {throw e}); - get.end(); - -}); diff --git a/test/core/simple/test-http-client-abort.js b/test/core/simple/test-http-client-abort.js deleted file mode 100644 index 78d0a6f93..000000000 --- a/test/core/simple/test-http-client-abort.js +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var clientAborts = 0; - -var server = http.Server(function (req, res) { - console.log('Got connection'); - res.writeHead(200); - res.write('Working on it...'); - - // I would expect an error event from req or res that the client aborted - // before completing the HTTP request / response cycle, or maybe a new - // event like "aborted" or something. - req.on('aborted', function () { - clientAborts++; - console.log('Got abort ' + clientAborts); - if (clientAborts === N) { - console.log('All aborts detected, you win.'); - server.close(); - } - }); - - // since there is already clientError, maybe that would be appropriate, - // since "error" is magical - req.on('clientError', function () { - console.log('Got clientError'); - }); -}); - -var responses = 0; -var N = http.Agent.defaultMaxSockets - 1; -var requests = []; - -server.listen(common.PORT, function () { - console.log('Server listening.'); - - for (var i = 0; i < N; i++) { - console.log('Making client ' + i); - var options = { port: common.PROXY_PORT, path: '/?id=' + i }; - var req = http.get(options, function (res) { - console.log('Client response code ' + res.statusCode); - - if (++responses == N) { - console.log('All clients connected, destroying.'); - requests.forEach(function (outReq) { - console.log('abort'); - outReq.abort(); - }); - } - }); - - requests.push(req); - } -}); - -process.on('exit', function () { - assert.equal(N, clientAborts); -}); diff --git a/test/core/simple/test-http-client-abort2.js b/test/core/simple/test-http-client-abort2.js deleted file mode 100644 index eef05fb46..000000000 --- a/test/core/simple/test-http-client-abort2.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// libuv-broken - - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var server = http.createServer(function (req, res) { - res.end('Hello'); -}); - -server.listen(common.PORT, function () { - var req = http.get({port: common.PROXY_PORT}, function (res) { - res.on('data', function (data) { - req.abort(); - server.close(); - }); - }); -}); - diff --git a/test/core/simple/test-http-client-upload-buf.js b/test/core/simple/test-http-client-upload-buf.js deleted file mode 100644 index 3b8e9abce..000000000 --- a/test/core/simple/test-http-client-upload-buf.js +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var N = 1024; -var bytesRecieved = 0; -var server_req_complete = false; -var client_res_complete = false; - -var server = http.createServer(function (req, res) { - assert.equal('POST', req.method); - - req.on('data', function (chunk) { - bytesRecieved += chunk.length; - }); - - req.on('end', function () { - server_req_complete = true; - console.log('request complete from server'); - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.write('hello\n'); - res.end(); - }); -}); -server.listen(common.PORT); - -server.on('listening', function () { - var req = http.request({ - port: common.PROXY_PORT, - method: 'POST', - path: '/' - }, function (res) { - res.setEncoding('utf8'); - res.on('data', function (chunk) { - console.log(chunk); - }); - res.on('end', function () { - client_res_complete = true; - server.close(); - }); - }); - - req.write(new Buffer(N)); - req.end(); - - common.error('client finished sending request'); -}); - -process.on('exit', function () { - assert.equal(N, bytesRecieved); - assert.equal(true, server_req_complete); - assert.equal(true, client_res_complete); -}); diff --git a/test/core/simple/test-http-client-upload.js b/test/core/simple/test-http-client-upload.js deleted file mode 100644 index fef706f2a..000000000 --- a/test/core/simple/test-http-client-upload.js +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var sent_body = ''; -var server_req_complete = false; -var client_res_complete = false; - -var server = http.createServer(function (req, res) { - assert.equal('POST', req.method); - req.setEncoding('utf8'); - - req.on('data', function (chunk) { - console.log('server got: ' + JSON.stringify(chunk)); - sent_body += chunk; - }); - - req.on('end', function () { - server_req_complete = true; - console.log('request complete from server'); - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.write('hello\n'); - res.end(); - }); -}); -server.listen(common.PORT); - -server.on('listening', function () { - var req = http.request({ - port: common.PROXY_PORT, - method: 'POST', - path: '/' - }, function (res) { - res.setEncoding('utf8'); - res.on('data', function (chunk) { - console.log(chunk); - }); - res.on('end', function () { - client_res_complete = true; - server.close(); - }); - }); - - req.write('1\n'); - req.write('2\n'); - req.write('3\n'); - req.end(); - - common.error('client finished sending request'); -}); - -process.on('exit', function () { - assert.equal('1\n2\n3\n', sent_body); - assert.equal(true, server_req_complete); - assert.equal(true, client_res_complete); -}); diff --git a/test/core/simple/test-http-contentLength0.js b/test/core/simple/test-http-contentLength0.js deleted file mode 100644 index abc3747e1..000000000 --- a/test/core/simple/test-http-contentLength0.js +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var http = require('http'); - -// Simple test of Node's HTTP Client choking on a response -// with a 'Content-Length: 0 ' response header. -// I.E. a space character after the 'Content-Length' throws an `error` event. - - -var s = http.createServer(function (req, res) { - res.writeHead(200, {'Content-Length': '0 '}); - res.end(); -}); -s.listen(common.PORT, function () { - - var request = http.request({ port: common.PROXY_PORT }, function (response) { - console.log('STATUS: ' + response.statusCode); - s.close(); - }); - - request.end(); -}); diff --git a/test/core/simple/test-http-eof-on-connect.js b/test/core/simple/test-http-eof-on-connect.js deleted file mode 100644 index d02ee6d16..000000000 --- a/test/core/simple/test-http-eof-on-connect.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var net = require('net'); -var http = require('http'); - -// This is a regression test for http://github.com/ry/node/issues/#issue/44 -// It is separate from test-http-malformed-request.js because it is only -// reproduceable on the first packet on the first connection to a server. - -var server = http.createServer(function (req, res) {}); -server.listen(common.PORT); - -server.on('listening', function () { - net.createConnection(common.PROXY_PORT).on('connect', function () { - this.destroy(); - }).on('close', function () { - server.close(); - }); -}); diff --git a/test/core/simple/test-http-extra-response.js b/test/core/simple/test-http-extra-response.js deleted file mode 100644 index a3d27462f..000000000 --- a/test/core/simple/test-http-extra-response.js +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); -var net = require('net'); - -// If an HTTP server is broken and sends data after the end of the response, -// node should ignore it and drop the connection. -// Demos this bug: https://github.com/ry/node/issues/680 - -var body = 'hello world\r\n'; -var fullResponse = - 'HTTP/1.1 500 Internal Server Error\r\n' + - 'Content-Length: ' + body.length + '\r\n' + - 'Content-Type: text/plain\r\n' + - 'Date: Fri + 18 Feb 2011 06:22:45 GMT\r\n' + - 'Host: 10.20.149.2\r\n' + - 'Access-Control-Allow-Credentials: true\r\n' + - 'Server: badly broken/0.1 (OS NAME)\r\n' + - '\r\n' + - body; - -var gotResponse = false; - - -var server = net.createServer(function (socket) { - var postBody = ''; - - socket.setEncoding('utf8'); - - socket.on('data', function (chunk) { - postBody += chunk; - - if (postBody.indexOf('\r\n') > -1) { - socket.write(fullResponse); - // omg, I wrote the response twice, what a terrible HTTP server I am. - socket.end(fullResponse); - } - }); -}); - - -server.listen(common.PORT, function () { - http.get({ port: common.PROXY_PORT }, function (res) { - var buffer = ''; - console.log('Got res code: ' + res.statusCode); - - res.setEncoding('utf8'); - res.on('data', function (chunk) { - buffer += chunk; - }); - - res.on('end', function () { - console.log('Response ended, read ' + buffer.length + ' bytes'); - assert.equal(body, buffer); - server.close(); - gotResponse = true; - }); - }); -}); - - -process.on('exit', function () { - assert.ok(gotResponse); -}); - diff --git a/test/core/simple/test-http-head-request.js b/test/core/simple/test-http-head-request.js deleted file mode 100644 index e8c203ef9..000000000 --- a/test/core/simple/test-http-head-request.js +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); -var util = require('util'); - - -var body = 'hello world\n'; - -var server = http.createServer(function (req, res) { - common.error('req: ' + req.method); - res.writeHead(200, {'Content-Length': body.length}); - res.end(); - server.close(); -}); - -var gotEnd = false; - -server.listen(common.PORT, function () { - var request = http.request({ - port: common.PROXY_PORT, - method: 'HEAD', - path: '/' - }, function (response) { - common.error('response start'); - response.on('end', function () { - common.error('response end'); - gotEnd = true; - }); - }); - request.end(); -}); - -process.on('exit', function () { - assert.ok(gotEnd); -}); diff --git a/test/core/simple/test-http-head-response-has-no-body-end.js b/test/core/simple/test-http-head-response-has-no-body-end.js deleted file mode 100644 index 6d89bee9f..000000000 --- a/test/core/simple/test-http-head-response-has-no-body-end.js +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// libuv-broken - - -var common = require('../common'); -var assert = require('assert'); - -var http = require('http'); - -// This test is to make sure that when the HTTP server -// responds to a HEAD request with data to res.end, -// it does not send any body. - -var server = http.createServer(function (req, res) { - res.writeHead(200); - res.end('FAIL'); // broken: sends FAIL from hot path. -}); -server.listen(common.PORT); - -var responseComplete = false; - -server.on('listening', function () { - var req = http.request({ - port: common.PROXY_PORT, - method: 'HEAD', - path: '/' - }, function (res) { - common.error('response'); - res.on('end', function () { - common.error('response end'); - server.close(); - responseComplete = true; - }); - }); - common.error('req'); - req.end(); -}); - -process.on('exit', function () { - assert.ok(responseComplete); -}); diff --git a/test/core/simple/test-http-head-response-has-no-body.js b/test/core/simple/test-http-head-response-has-no-body.js deleted file mode 100644 index aa7a281b6..000000000 --- a/test/core/simple/test-http-head-response-has-no-body.js +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); - -var http = require('http'); - -// This test is to make sure that when the HTTP server -// responds to a HEAD request, it does not send any body. -// In this case it was sending '0\r\n\r\n' - -var server = http.createServer(function (req, res) { - res.writeHead(200); // broken: defaults to TE chunked - res.end(); -}); -server.listen(common.PORT); - -var responseComplete = false; - -server.on('listening', function () { - var req = http.request({ - port: common.PROXY_PORT, - method: 'HEAD', - path: '/' - }, function (res) { - common.error('response'); - res.on('end', function () { - common.error('response end'); - server.close(); - responseComplete = true; - }); - }); - common.error('req'); - req.end(); -}); - -process.on('exit', function () { - assert.ok(responseComplete); -}); diff --git a/test/core/simple/test-http-host-headers.js b/test/core/simple/test-http-host-headers.js deleted file mode 100644 index 2dae1182e..000000000 --- a/test/core/simple/test-http-host-headers.js +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// libuv-broken - - -var http = require('http'), - common = require('../common'), - assert = require('assert'), - httpServer = http.createServer(reqHandler); - -function reqHandler(req, res) { - console.log('Got request: ' + req.headers.host + ' ' + req.url); - if (req.url === '/setHostFalse5') { - assert.equal(req.headers.host, undefined); - } else { - assert.equal(req.headers.host, 'localhost:' + common.PROXY_PORT, - 'Wrong host header for req[' + req.url + ']: ' + - req.headers.host); - } - res.writeHead(200, {}); - //process.nextTick(function () { res.end('ok'); }); - res.end('ok'); -} - -function thrower(er) { - throw er; -} - -testHttp(); - -function testHttp() { - - console.log('testing http on port ' + common.PROXY_PORT + ' (proxied to ' + - common.PORT + ')'); - - var counter = 0; - - function cb() { - counter--; - console.log('back from http request. counter = ' + counter); - if (counter === 0) { - httpServer.close(); - } - } - - httpServer.listen(common.PORT, function (er) { - console.error('listening on ' + common.PORT); - - if (er) throw er; - - http.get({ method: 'GET', - path: '/' + (counter++), - host: 'localhost', - //agent: false, - port: common.PROXY_PORT }, cb).on('error', thrower); - - http.request({ method: 'GET', - path: '/' + (counter++), - host: 'localhost', - //agent: false, - port: common.PROXY_PORT }, cb).on('error', thrower).end(); - - http.request({ method: 'POST', - path: '/' + (counter++), - host: 'localhost', - //agent: false, - port: common.PROXY_PORT }, cb).on('error', thrower).end(); - - http.request({ method: 'PUT', - path: '/' + (counter++), - host: 'localhost', - //agent: false, - port: common.PROXY_PORT }, cb).on('error', thrower).end(); - - http.request({ method: 'DELETE', - path: '/' + (counter++), - host: 'localhost', - //agent: false, - port: common.PROXY_PORT }, cb).on('error', thrower).end(); - }); -} - diff --git a/test/core/simple/test-http-many-keep-alive-connections.js b/test/core/simple/test-http-many-keep-alive-connections.js deleted file mode 100644 index 6b79619bb..000000000 --- a/test/core/simple/test-http-many-keep-alive-connections.js +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var expected = 10000; -var responses = 0; -var requests = 0; -var connection; - -var server = http.Server(function (req, res) { - requests++; - assert.equal(req.connection, connection); - res.writeHead(200); - res.end('hello world\n'); -}); - -server.once('connection', function (c) { - connection = c; -}); - -server.listen(common.PORT, function () { - var callee = arguments.callee; - var request = http.get({ - port: common.PROXY_PORT, - path: '/', - headers: { - 'Connection': 'Keep-alive' - } - }, function (res) { - res.on('end', function () { - if (++responses < expected) { - callee(); - } else { - process.exit(); - } - }); - }).on('error', function (e) { - console.log(e.message); - process.exit(1); - }); - request.agent.maxSockets = 1; -}); - -process.on('exit', function () { - assert.equal(expected, responses); - assert.equal(expected, requests); -}); diff --git a/test/core/simple/test-http-multi-line-headers.js b/test/core/simple/test-http-multi-line-headers.js deleted file mode 100644 index e0eeb2c18..000000000 --- a/test/core/simple/test-http-multi-line-headers.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); - -var http = require('http'); -var net = require('net'); - -var gotResponse = false; - -var server = net.createServer(function (conn) { - var body = 'Yet another node.js server.'; - - var response = - 'HTTP/1.1 200 OK\r\n' + - 'Connection: close\r\n' + - 'Content-Length: ' + body.length + '\r\n' + - 'Content-Type: text/plain;\r\n' + - ' x-unix-mode=0600;\r\n' + - ' name=\"hello.txt\"\r\n' + - '\r\n' + - body; - - conn.write(response, function () { - conn.destroy(); - server.close(); - }); -}); - -server.listen(common.PORT, function () { - http.get({host: '127.0.0.1', port: common.PROXY_PORT}, function (res) { - assert.equal(res.headers['content-type'], - 'text/plain;x-unix-mode=0600;name="hello.txt"'); - gotResponse = true; - }); -}); - -process.on('exit', function () { - assert.ok(gotResponse); -}); diff --git a/test/core/simple/test-http-proxy.js b/test/core/simple/test-http-proxy.js deleted file mode 100644 index fdddb3cdc..000000000 --- a/test/core/simple/test-http-proxy.js +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); -var url = require('url'); - -var PROXY_PORT = common.PORT; -var BACKEND_PORT = common.PORT + 1; - -var cookies = [ - 'session_token=; path=/; expires=Sun, 15-Sep-2030 13:48:52 GMT', - 'prefers_open_id=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT' -]; - -var headers = {'content-type': 'text/plain', - 'set-cookie': cookies, - 'hello': 'world' }; - -var backend = http.createServer(function (req, res) { - common.debug('backend request'); - res.writeHead(200, headers); - res.write('hello world\n'); - res.end(); -}); - -var proxy = http.createServer(function (req, res) { - common.debug('proxy req headers: ' + JSON.stringify(req.headers)); - var proxy_req = http.get({ - port: BACKEND_PORT, - path: url.parse(req.url).pathname - }, function (proxy_res) { - - common.debug('proxy res headers: ' + JSON.stringify(proxy_res.headers)); - - assert.equal('world', proxy_res.headers['hello']); - assert.equal('text/plain', proxy_res.headers['content-type']); - assert.deepEqual(cookies, proxy_res.headers['set-cookie']); - - res.writeHead(proxy_res.statusCode, proxy_res.headers); - - proxy_res.on('data', function (chunk) { - res.write(chunk); - }); - - proxy_res.on('end', function () { - res.end(); - common.debug('proxy res'); - }); - }); -}); - -var body = ''; - -var nlistening = 0; -function startReq() { - nlistening++; - if (nlistening < 2) return; - - var client = http.get({ - port: common.PROXY_PORT, - path: '/test' - }, function (res) { - common.debug('got res'); - assert.equal(200, res.statusCode); - - assert.equal('world', res.headers['hello']); - assert.equal('text/plain', res.headers['content-type']); - assert.deepEqual(cookies, res.headers['set-cookie']); - - res.setEncoding('utf8'); - res.on('data', function (chunk) { body += chunk; }); - res.on('end', function () { - proxy.close(); - backend.close(); - common.debug('closed both'); - }); - }); - common.debug('client req'); -} - -common.debug('listen proxy'); -proxy.listen(PROXY_PORT, startReq); - -common.debug('listen backend'); -backend.listen(BACKEND_PORT, startReq); - -process.on('exit', function () { - assert.equal(body, 'hello world\n'); -}); diff --git a/test/core/simple/test-http-response-close.js b/test/core/simple/test-http-response-close.js deleted file mode 100644 index b92abb011..000000000 --- a/test/core/simple/test-http-response-close.js +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var gotEnd = false; - -var server = http.createServer(function (req, res) { - res.writeHead(200); - res.write('a'); - - req.on('close', function () { - console.error('aborted'); - gotEnd = true; - }); -}); -server.listen(common.PORT); - -server.on('listening', function () { - console.error('make req'); - http.get({ - port: common.PROXY_PORT - }, function (res) { - console.error('got res'); - res.on('data', function (data) { - console.error('destroy res'); - res.destroy(); - server.close(); - }); - }); -}); - -process.on('exit', function () { - assert.ok(gotEnd); -}); diff --git a/test/core/simple/test-http-server-multiheaders.js b/test/core/simple/test-http-server-multiheaders.js deleted file mode 100644 index 6a5b8be72..000000000 --- a/test/core/simple/test-http-server-multiheaders.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Verify that the HTTP server implementation handles multiple instances -// of the same header as per RFC2616: joining the handful of fields by ', ' -// that support it, and dropping duplicates for other fields. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var srv = http.createServer(function (req, res) { - assert.equal(req.headers.accept, 'abc, def, ghijklmnopqrst'); - assert.equal(req.headers.host, 'foo'); - assert.equal(req.headers['x-foo'], 'bingo'); - assert.equal(req.headers['x-bar'], 'banjo, bango'); - - res.writeHead(200, {'Content-Type' : 'text/plain'}); - res.end('EOF'); - - srv.close(); -}); - -srv.listen(common.PORT, function () { - http.get({ - host: 'localhost', - port: common.PROXY_PORT, - path: '/', - headers: [ - ['accept', 'abc'], - ['accept', 'def'], - ['Accept', 'ghijklmnopqrst'], - ['host', 'foo'], - ['Host', 'bar'], - ['hOst', 'baz'], - ['x-foo', 'bingo'], - ['x-bar', 'banjo'], - ['x-bar', 'bango'] - ] - }); -}); diff --git a/test/core/simple/test-http-set-cookies.js b/test/core/simple/test-http-set-cookies.js deleted file mode 100644 index aac29940e..000000000 --- a/test/core/simple/test-http-set-cookies.js +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -var nresponses = 0; - -var server = http.createServer(function (req, res) { - if (req.url == '/one') { - res.writeHead(200, [['set-cookie', 'A'], - ['content-type', 'text/plain']]); - res.end('one\n'); - } else { - res.writeHead(200, [['set-cookie', 'A'], - ['set-cookie', 'B'], - ['content-type', 'text/plain']]); - res.end('two\n'); - } -}); -server.listen(common.PORT); - -server.on('listening', function () { - // - // one set-cookie header - // - http.get({ port: common.PROXY_PORT, path: '/one' }, function (res) { - // set-cookie headers are always return in an array. - // even if there is only one. - assert.deepEqual(['A'], res.headers['set-cookie']); - assert.equal('text/plain', res.headers['content-type']); - - res.on('data', function (chunk) { - console.log(chunk.toString()); - }); - - res.on('end', function () { - if (++nresponses == 2) { - server.close(); - } - }); - }); - - // two set-cookie headers - - http.get({ port: common.PROXY_PORT, path: '/two' }, function (res) { - assert.deepEqual(['A', 'B'], res.headers['set-cookie']); - assert.equal('text/plain', res.headers['content-type']); - - res.on('data', function (chunk) { - console.log(chunk.toString()); - }); - - res.on('end', function () { - if (++nresponses == 2) { - server.close(); - } - }); - }); - -}); - -process.on('exit', function () { - assert.equal(2, nresponses); -}); diff --git a/test/core/simple/test-http-status-code.js b/test/core/simple/test-http-status-code.js deleted file mode 100644 index dffbaf7c9..000000000 --- a/test/core/simple/test-http-status-code.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// libuv-broken - - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); - -// Simple test of Node's HTTP ServerResponse.statusCode -// ServerResponse.prototype.statusCode - -var testsComplete = 0; -var tests = [200, 202, 300, 404, 500]; -var testIdx = 0; - -var s = http.createServer(function (req, res) { - var t = tests[testIdx]; - res.writeHead(t, {'Content-Type': 'text/plain'}); - console.log('--\nserver: statusCode after writeHead: ' + res.statusCode); - assert.equal(res.statusCode, t); - res.end('hello world\n'); -}); - -s.listen(common.PORT, nextTest); - - -function nextTest() { - if (testIdx + 1 === tests.length) { - return s.close(); - } - var test = tests[testIdx]; - - http.get({ port: common.PROXY_PORT }, function (response) { - console.log('client: expected status: ' + test); - console.log('client: statusCode: ' + response.statusCode); - assert.equal(response.statusCode, test); - response.on('end', function () { - testsComplete++; - testIdx += 1; - nextTest(); - }); - }); -} - - -process.on('exit', function () { - assert.equal(4, testsComplete); -}); - diff --git a/test/core/simple/test-http-upgrade-server2.js b/test/core/simple/test-http-upgrade-server2.js deleted file mode 100644 index a52737618..000000000 --- a/test/core/simple/test-http-upgrade-server2.js +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); -var net = require('net'); - -var server = http.createServer(function (req, res) { - common.error('got req'); - throw new Error('This shouldn\'t happen.'); -}); - -server.on('upgrade', function (req, socket, upgradeHead) { - common.error('got upgrade event'); - // test that throwing an error from upgrade gets - // is uncaught - throw new Error('upgrade error'); -}); - -var gotError = false; - -process.on('uncaughtException', function (e) { - common.error('got \'clientError\' event'); - assert.equal('upgrade error', e.message); - gotError = true; - process.exit(0); -}); - - -server.listen(common.PORT, function () { - var c = net.createConnection(common.PROXY_PORT); - - c.on('connect', function () { - common.error('client wrote message'); - c.write('GET /blah HTTP/1.1\r\n' + - 'Upgrade: WebSocket\r\n' + - 'Connection: Upgrade\r\n' + - '\r\n\r\nhello world'); - }); - - c.on('end', function () { - c.end(); - }); - - c.on('close', function () { - common.error('client close'); - server.close(); - }); -}); - -process.on('exit', function () { - assert.ok(gotError); -}); diff --git a/test/core/simple/test-http.js b/test/core/simple/test-http.js deleted file mode 100644 index e082eb2ce..000000000 --- a/test/core/simple/test-http.js +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); -var url = require('url'); - -function p(x) { - common.error(common.inspect(x)); -} - -var responses_sent = 0; -var responses_recvd = 0; -var body0 = ''; -var body1 = ''; - -var server = http.Server(function (req, res) { - if (responses_sent == 0) { - assert.equal('GET', req.method); - assert.equal('/hello', url.parse(req.url).pathname); - - console.dir(req.headers); - assert.equal(true, 'accept' in req.headers); - assert.equal('*/*', req.headers['accept']); - - assert.equal(true, 'foo' in req.headers); - assert.equal('bar', req.headers['foo']); - } - - if (responses_sent == 1) { - assert.equal('POST', req.method); - assert.equal('/world', url.parse(req.url).pathname); - this.close(); - } - - req.on('end', function () { - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.write('The path was ' + url.parse(req.url).pathname); - res.end(); - responses_sent += 1; - }); - - //assert.equal('127.0.0.1', res.connection.remoteAddress); -}); -server.listen(common.PORT); - -server.on('listening', function () { - var agent = new http.Agent({ port: common.PROXY_PORT, maxSockets: 1 }); - http.get({ - port: common.PROXY_PORT, - path: '/hello', - headers: {'Accept': '*/*', 'Foo': 'bar'}, - agent: agent - }, function (res) { - assert.equal(200, res.statusCode); - responses_recvd += 1; - res.setEncoding('utf8'); - res.on('data', function (chunk) { body0 += chunk; }); - common.debug('Got /hello response'); - }); - - setTimeout(function () { - var req = http.request({ - port: common.PROXY_PORT, - method: 'POST', - path: '/world', - agent: agent - }, function (res) { - assert.equal(200, res.statusCode); - responses_recvd += 1; - res.setEncoding('utf8'); - res.on('data', function (chunk) { body1 += chunk; }); - common.debug('Got /world response'); - }); - req.end(); - }, 1); -}); - -process.on('exit', function () { - common.debug('responses_recvd: ' + responses_recvd); - assert.equal(2, responses_recvd); - - common.debug('responses_sent: ' + responses_sent); - assert.equal(2, responses_sent); - - assert.equal('The path was /hello', body0); - assert.equal('The path was /world', body1); -}); - diff --git a/test/examples-test.js b/test/examples-test.js deleted file mode 100644 index 36beb89a9..000000000 --- a/test/examples-test.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * examples.js: Tests which ensure all examples do not throw errors. - * - * (C) 2010, Charlie Robbins - * - */ - -var vows = require('vows') - macros = require('./macros'), - examples = macros.examples; - -// -// Suppress `EADDRINUSE` errors since -// we are just checking for require-time errors -// -process.on('uncaughtException', function (err) { - if (err.code !== 'EADDRINUSE') { - throw err; - } -}); - -vows.describe('node-http-proxy/examples').addBatch( - examples.shouldHaveDeps() -).addBatch( - examples.shouldHaveNoErrors() -).export(module); \ No newline at end of file diff --git a/test/fixtures/agent2-cert.pem b/test/fixtures/agent2-cert.pem deleted file mode 100644 index 8e4354db4..000000000 --- a/test/fixtures/agent2-cert.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB7DCCAZYCCQC7gs0MDNn6MTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJV -UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO -BgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEgMB4GCSqGSIb3DQEJARYR -cnlAdGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEy -WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYD -VQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEg -MB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEF -AANLADBIAkEAyXb8FrRdKbhrKLgLSsn61i1C7w7fVVVd7OQsmV/7p9WB2lWFiDlC -WKGU9SiIz/A6wNZDUAuc2E+VwtpCT561AQIDAQABMA0GCSqGSIb3DQEBBQUAA0EA -C8HzpuNhFLCI3A5KkBS5zHAQax6TFUOhbpBCR0aTDbJ6F1liDTK1lmU/BjvPoj+9 -1LHwrmh29rK8kBPEjmymCQ== ------END CERTIFICATE----- diff --git a/test/fixtures/agent2-csr.pem b/test/fixtures/agent2-csr.pem deleted file mode 100644 index a670c4c63..000000000 --- a/test/fixtures/agent2-csr.pem +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBXTCCAQcCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH -EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD -EwZhZ2VudDIxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMFwwDQYJ -KoZIhvcNAQEBBQADSwAwSAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf -+6fVgdpVhYg5QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAaAlMCMGCSqG -SIb3DQEJBzEWExRBIGNoYWxsZW5nZSBwYXNzd29yZDANBgkqhkiG9w0BAQUFAANB -AJnll2pt5l0pzskQSpjjLVTlFDFmJr/AZ3UK8v0WxBjYjCe5Jx4YehkChpxIyDUm -U3J9q9MDUf0+Y2+EGkssFfk= ------END CERTIFICATE REQUEST----- diff --git a/test/fixtures/agent2-key.pem b/test/fixtures/agent2-key.pem deleted file mode 100644 index 522903c63..000000000 --- a/test/fixtures/agent2-key.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIBOgIBAAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf+6fVgdpVhYg5 -QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAQJBAMT6Bf34+UHKY1ObpsbH -9u2jsVblFq1rWvs8GPMY6oertzvwm3DpuSUp7PTgOB1nLTLYtCERbQ4ovtN8tn3p -OHUCIQDzIEGsoCr5vlxXvy2zJwu+fxYuhTZWMVuo1397L0VyhwIhANQh+yzqUgaf -WRtSB4T2W7ADtJI35ET61jKBty3CqJY3AiAIwju7dVW3A5WeD6Qc1SZGKZvp9yCb -AFI2BfVwwaY11wIgXF3PeGcvACMyMWsuSv7aPXHfliswAbkWuzcwA4TW01ECIGWa -cgsDvVFxmfM5NPSuT/UDTa6R5BFISB5ea0N0AR3I ------END RSA PRIVATE KEY----- diff --git a/test/fixtures/agent2.cnf b/test/fixtures/agent2.cnf deleted file mode 100644 index 0a9f2c737..000000000 --- a/test/fixtures/agent2.cnf +++ /dev/null @@ -1,19 +0,0 @@ -[ req ] -default_bits = 1024 -days = 999 -distinguished_name = req_distinguished_name -attributes = req_attributes -prompt = no - -[ req_distinguished_name ] -C = US -ST = CA -L = SF -O = Joyent -OU = Node.js -CN = agent2 -emailAddress = ry@tinyclouds.org - -[ req_attributes ] -challengePassword = A challenge password - diff --git a/test/helpers/http.js b/test/helpers/http.js deleted file mode 100644 index aaf7a8042..000000000 --- a/test/helpers/http.js +++ /dev/null @@ -1,182 +0,0 @@ -/* - * http.js: Top level include for node-http-proxy http helpers - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var assert = require('assert'), - http = require('http'), - https = require('https'), - url = require('url'), - async = require('async'), - helpers = require('./index'), - protocols = helpers.protocols, - httpProxy = require('../../lib/node-http-proxy'); - -// -// ### function createServerPair (options, callback) -// #### @options {Object} Options to create target and proxy server. -// #### @callback {function} Continuation to respond to when complete. -// -// Creates http target and proxy servers -// -exports.createServerPair = function (options, callback) { - async.series([ - // - // 1. Create the target server - // - function createTarget(next) { - exports.createServer(options.target, next); - }, - // - // 2. Create the proxy server - // - function createTarget(next) { - exports.createProxyServer(options.proxy, next); - } - ], callback); -}; - -// -// ### function createServer (options, callback) -// #### @options {Object} Options for creatig an http server. -// #### @port {number} Port to listen on -// #### @output {string} String to write to each HTTP response -// #### @headers {Object} Headers to assert are sent by `node-http-proxy`. -// #### @callback {function} Continuation to respond to when complete. -// -// Creates a target server that the tests will proxy to. -// -exports.createServer = function (options, callback) { - // - // Request handler to use in either `http` - // or `https` server. - // - function requestHandler(req, res) { - if (options.headers) { - Object.keys(options.headers).forEach(function (key) { - assert.equal(req.headers[key], options.headers[key]); - }); - } - - if (options.outputHeaders) { - Object.keys(options.outputHeaders).forEach(function (header) { - res.setHeader(header, options.outputHeaders[header]); - }); - } - - setTimeout(function() { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write(options.output || 'hello proxy'); - res.end(); - }, options.latency || 1); - } - - var server = protocols.target === 'https' - ? https.createServer(helpers.https, requestHandler) - : http.createServer(requestHandler); - - server.listen(options.port, function () { - callback(null, this); - }); -}; - -// -// ### function createProxyServer (options, callback) -// #### @options {Object} Options for creatig an http server. -// #### @port {number} Port to listen on -// #### @latency {number} Latency of this server in milliseconds -// #### @proxy {Object} Options to pass to the HttpProxy. -// #### @routing {boolean} Enables `httpProxy.RoutingProxy` -// #### @callback {function} Continuation to respond to when complete. -// -// Creates a proxy server that the tests will request against. -// -exports.createProxyServer = function (options, callback) { - if (!options.latency) { - if (protocols.proxy === 'https') { - options.proxy.https = helpers.https; - } - options.proxy.rejectUnauthorized = false; - - return httpProxy - .createServer(options.proxy) - .listen(options.port, function () { - callback(null, this); - }); - } - - var server, - proxy; - - proxy = options.routing - ? new httpProxy.RoutingProxy(options.proxy) - : new httpProxy.HttpProxy(options.proxy); - - // - // Request handler to use in either `http` - // or `https` server. - // - function requestHandler(req, res) { - var buffer = httpProxy.buffer(req); - - if (options.outputHeaders) { - Object.keys(options.outputHeaders).forEach(function (header) { - res.setHeader(header, options.outputHeaders[header]); - }); - } - setTimeout(function () { - // - // Setup options dynamically for `RoutingProxy.prototype.proxyRequest` - // or `HttpProxy.prototype.proxyRequest`. - // - buffer = options.routing ? { buffer: buffer } : buffer; - proxy.proxyRequest(req, res, buffer); - }, options.latency); - } - - server = protocols.proxy === 'https' - ? https.createServer(helpers.https, requestHandler) - : http.createServer(requestHandler); - - server.listen(options.port, function () { - callback(null, this); - }); -}; - -// -// ### function assignPortsToRoutes (routes) -// #### @routes {Object} Routing table to assign ports to -// -// Assigns dynamic ports to the `routes` for runtime testing. -// -exports.assignPortsToRoutes = function (routes) { - Object.keys(routes).forEach(function (source) { - routes[source] = routes[source].replace('{PORT}', helpers.nextPort); - }); - - return routes; -}; - -// -// ### function parseRoutes (options) -// #### @options {Object} Options to use when parsing routes -// #### @protocol {string} Protocol to use in the routes -// #### @routes {Object} Routes to parse. -// -// Returns an Array of fully-parsed URLs for the source and -// target of `options.routes`. -// -exports.parseRoutes = function (options) { - var protocol = options.protocol || 'http', - routes = options.routes; - - return Object.keys(routes).map(function (source) { - return { - source: url.parse(protocol + '://' + source), - target: url.parse(protocol + '://' + routes[source]) - }; - }); -}; diff --git a/test/helpers/index.js b/test/helpers/index.js deleted file mode 100644 index 7e3c3f488..000000000 --- a/test/helpers/index.js +++ /dev/null @@ -1,105 +0,0 @@ -/* - * index.js: Top level include for node-http-proxy helpers - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var fs = require('fs'), - path = require('path'); - -var fixturesDir = path.join(__dirname, '..', 'fixtures'); - -// -// @https {Object} -// Returns the necessary `https` credentials. -// -Object.defineProperty(exports, 'https', { - get: function () { - delete this.https; - return this.https = { - key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'), - cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8') - }; - } -}); - -// -// @protocols {Object} -// Returns an object representing the desired protocols -// for the `proxy` and `target` server. -// -Object.defineProperty(exports, 'protocols', { - get: function () { - delete this.protocols; - return this.protocols = { - target: exports.argv.target || 'http', - proxy: exports.argv.proxy || 'http' - }; - } -}); - -// -// @nextPort {number} -// Returns an auto-incrementing port for tests. -// -Object.defineProperty(exports, 'nextPort', { - get: function () { - var current = this.port || 9050; - this.port = current + 1; - return current; - } -}); - -// -// @nextPortPair {Object} -// Returns an auto-incrementing pair of ports for tests. -// -Object.defineProperty(exports, 'nextPortPair', { - get: function () { - return { - target: this.nextPort, - proxy: this.nextPort - }; - } -}); - -// -// ### function describe(prefix) -// #### @prefix {string} Prefix to use before the description -// -// Returns a string representing the protocols that this suite -// is testing based on CLI arguments. -// -exports.describe = function (prefix, base) { - prefix = prefix || ''; - base = base || 'http'; - - function protocol(endpoint) { - return exports.protocols[endpoint] === 'https' - ? base + 's' - : base; - } - - return [ - 'node-http-proxy', - prefix, - [ - protocol('proxy'), - '-to-', - protocol('target') - ].join('') - ].filter(Boolean).join('/'); -}; - -// -// Expose the CLI arguments -// -exports.argv = require('optimist').argv; - -// -// Export additional helpers for `http` and `websockets`. -// -exports.http = require('./http'); -exports.ws = require('./ws'); \ No newline at end of file diff --git a/test/helpers/ws.js b/test/helpers/ws.js deleted file mode 100644 index a4905227d..000000000 --- a/test/helpers/ws.js +++ /dev/null @@ -1,112 +0,0 @@ -/* - * ws.js: Top level include for node-http-proxy websocket helpers - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var assert = require('assert'), - https = require('https'), - async = require('async'), - io = require('socket.io'), - ws = require('ws'), - helpers = require('./index'), - protocols = helpers.protocols, - http = require('./http'); - -// -// ### function createServerPair (options, callback) -// #### @options {Object} Options to create target and proxy server. -// #### @target {Object} Options for the target server. -// #### @proxy {Object} Options for the proxy server. -// #### @callback {function} Continuation to respond to when complete. -// -// Creates http target and proxy servers -// -exports.createServerPair = function (options, callback) { - async.series([ - // - // 1. Create the target server - // - function createTarget(next) { - exports.createServer(options.target, next); - }, - // - // 2. Create the proxy server - // - function createTarget(next) { - http.createProxyServer(options.proxy, next); - } - ], callback); -}; - -// -// ### function createServer (options, callback) -// #### @options {Object} Options for creating the socket.io or ws server. -// #### @raw {boolean} Enables ws.Websocket server. -// -// Creates a socket.io or ws server using the specified `options`. -// -exports.createServer = function (options, callback) { - return options.raw - ? exports.createWsServer(options, callback) - : exports.createSocketIoServer(options, callback); -}; - -// -// ### function createSocketIoServer (options, callback) -// #### @options {Object} Options for creating the socket.io server -// #### @port {number} Port to listen on -// #### @input {string} Input to expect from the only socket -// #### @output {string} Output to send the only socket -// -// Creates a socket.io server on the specified `options.port` which -// will expect `options.input` and then send `options.output`. -// -exports.createSocketIoServer = function (options, callback) { - var server = protocols.target === 'https' - ? io.listen(options.port, helpers.https, callback) - : io.listen(options.port, callback); - - server.sockets.on('connection', function (socket) { - socket.on('incoming', function (data) { - assert.equal(data, options.input); - socket.emit('outgoing', options.output); - }); - }); -}; - -// -// ### function createWsServer (options, callback) -// #### @options {Object} Options for creating the ws.Server instance -// #### @port {number} Port to listen on -// #### @input {string} Input to expect from the only socket -// #### @output {string} Output to send the only socket -// -// Creates a ws.Server instance on the specified `options.port` which -// will expect `options.input` and then send `options.output`. -// -exports.createWsServer = function (options, callback) { - var server, - wss; - - if (protocols.target === 'https') { - server = https.createServer(helpers.https, function (req, res) { - req.writeHead(200); - req.end(); - }).listen(options.port, callback); - - wss = new ws.Server({ server: server }); - } - else { - wss = new ws.Server({ port: options.port }, callback); - } - - wss.on('connection', function (socket) { - socket.on('message', function (data) { - assert.equal(data, options.input); - socket.send(options.output); - }); - }); -}; \ No newline at end of file diff --git a/test/http/http-test.js b/test/http/http-test.js deleted file mode 100644 index 81f8726a7..000000000 --- a/test/http/http-test.js +++ /dev/null @@ -1,102 +0,0 @@ -/* - node-http-proxy-test.js: http proxy for node.js - - Copyright (c) 2010 Charlie Robbins, Marak Squires and Fedor Indutny - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var assert = require('assert'), - fs = require('fs'), - path = require('path'), - async = require('async'), - request = require('request'), - vows = require('vows'), - macros = require('../macros'), - helpers = require('../helpers'); - -var routeFile = path.join(__dirname, 'config.json'); - -vows.describe(helpers.describe()).addBatch({ - "With a valid target server": { - "and no latency": { - "and no headers": macros.http.assertProxied(), - "and headers": macros.http.assertProxied({ - request: { headers: { host: 'unknown.com' } } - }), - "and request close connection header": macros.http.assertProxied({ - request: { headers: { connection: "close" } }, - outputHeaders: { connection: "close" } - }), - "and request keep alive connection header": macros.http.assertProxied({ - request: { headers: { connection: "keep-alive" } }, - outputHeaders: { connection: "keep-alive" } - }), - "and response close connection header": macros.http.assertProxied({ - request: { headers: { connection: "" } }, // Must explicitly set to "" because otherwise node will automatically add a "connection: keep-alive" header - targetHeaders: { connection: "close" }, - outputHeaders: { connection: "close" } - }), - "and response keep-alive connection header": macros.http.assertProxied({ - request: { headers: { connection: "" } }, // Must explicitly set to "" because otherwise node will automatically add a "connection: keep-alive" header - targetHeaders: { connection: "keep-alive" }, - outputHeaders: { connection: "keep-alive" } - }), - "and response keep-alive connection header from http 1.0 client": macros.http.assertRawHttpProxied({ - rawRequest: "GET / HTTP/1.0\r\n\r\n", - targetHeaders: { connection: "keep-alive" }, - match: /connection: close/i - }), - "and request keep alive from http 1.0 client": macros.http.assertRawHttpProxied({ - rawRequest: "GET / HTTP/1.0\r\nConnection: Keep-Alive\r\n\r\n", - targetHeaders: { connection: "keep-alive" }, - match: /connection: keep-alive/i - }), - "and no connection header": macros.http.assertProxied({ - request: { headers: { connection: "" } }, // Must explicitly set to "" because otherwise node will automatically add a "connection: keep-alive" header - outputHeaders: { connection: "keep-alive" } - }), - "and forwarding enabled": macros.http.assertForwardProxied() - }, - "and latency": { - "and no headers": macros.http.assertProxied({ - latency: 2000 - }), - "and response headers": macros.http.assertProxied({ - targetHeaders: { "x-testheader": "target" }, - proxyHeaders: { "X-TestHeader": "proxy" }, - outputHeaders: { "x-testheader": "target" }, - latency: 1000 - }) - }, - "and timeout set": macros.http.assertProxied({ - shouldFail: true, - timeout: 2000, - requestLatency: 4000 - }) - }, - "With a no valid target server": { - "and no latency": macros.http.assertInvalidProxy(), - "and latency": macros.http.assertInvalidProxy({ - latency: 2000 - }) - } -}).export(module); diff --git a/test/http/routing-table-test.js b/test/http/routing-table-test.js deleted file mode 100644 index f3dcf31e7..000000000 --- a/test/http/routing-table-test.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - * routing-table-test.js: Tests for the proxying using the ProxyTable object. - * - * (C) 2010, Charlie Robbins - * - */ - -var assert = require('assert'), - fs = require('fs'), - path = require('path'), - async = require('async'), - request = require('request'), - vows = require('vows'), - macros = require('../macros'), - helpers = require('../helpers'); - -var routeFile = path.join(__dirname, 'config.json'); - -vows.describe(helpers.describe('routing-table')).addBatch({ - "With a routing table": { - "with latency": macros.http.assertProxiedToRoutes({ - latency: 2000, - routes: { - "icanhaz.com": "127.0.0.1:{PORT}", - "latency.com": "127.0.0.1:{PORT}" - } - }), - "addHost() / removeHost()": macros.http.assertDynamicProxy({ - hostnameOnly: true, - routes: { - "static.com": "127.0.0.1:{PORT}", - "removed.com": "127.0.0.1:{PORT}" - } - }, { - add: [{ host: 'dynamic1.com', target: '127.0.0.1:' }], - drop: ['removed.com'] - }), - "using RegExp": macros.http.assertProxiedToRoutes({ - routes: { - "foo.com": "127.0.0.1:{PORT}", - "bar.com": "127.0.0.1:{PORT}", - "baz.com/taco": "127.0.0.1:{PORT}", - "pizza.com/taco/muffins": "127.0.0.1:{PORT}", - "blah.com/me": "127.0.0.1:{PORT}/remapped", - "bleh.com/remap/this": "127.0.0.1:{PORT}/remap/remapped", - "test.com/double/tap": "127.0.0.1:{PORT}/remap" - } - }), - "using hostnameOnly": macros.http.assertProxiedToRoutes({ - hostnameOnly: true, - routes: { - "foo.com": "127.0.0.1:{PORT}", - "bar.com": "127.0.0.1:{PORT}" - } - }), - "using pathnameOnly": macros.http.assertProxiedToRoutes({ - pathnameOnly: true, - routes: { - "/foo": "127.0.0.1:{PORT}", - "/bar": "127.0.0.1:{PORT}", - "/pizza": "127.0.0.1:{PORT}" - } - }), - "using a routing file": macros.http.assertProxiedToRoutes({ - filename: routeFile, - routes: { - "foo.com": "127.0.0.1:{PORT}", - "bar.com": "127.0.0.1:{PORT}" - } - }, { - "after the file has been modified": { - topic: function () { - var config = JSON.parse(fs.readFileSync(routeFile, 'utf8')), - protocol = helpers.protocols.proxy, - port = helpers.nextPort, - that = this; - - config.router['dynamic.com'] = "127.0.0.1:" + port; - fs.writeFileSync(routeFile, JSON.stringify(config)); - - async.parallel([ - function waitForRoutes(next) { - that.proxyServer.on('routes', next); - }, - async.apply( - helpers.http.createServer, - { - port: port, - output: 'hello from dynamic.com' - } - ) - ], function () { - request({ - uri: protocol + '://127.0.0.1:' + that.port, - headers: { - host: 'dynamic.com' - } - }, that.callback); - }); - }, - "should receive 'hello from dynamic.com'": function (err, res, body) { - assert.equal(body, 'hello from dynamic.com'); - } - } - }) - } -}).export(module); diff --git a/test/macros/examples.js b/test/macros/examples.js deleted file mode 100644 index 9d4202e6a..000000000 --- a/test/macros/examples.js +++ /dev/null @@ -1,101 +0,0 @@ -/* - * examples.js: Macros for testing code in examples/ - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var assert = require('assert'), - fs = require('fs'), - path = require('path'), - spawn = require('child_process').spawn, - async = require('async'); - -var rootDir = path.join(__dirname, '..', '..'), - examplesDir = path.join(rootDir, 'examples'); - -// -// ### function shouldHaveDeps () -// -// Ensures that all `npm` dependencies are installed in `/examples`. -// -exports.shouldHaveDeps = function () { - return { - "Before testing examples": { - topic: function () { - async.waterfall([ - // - // 1. Read files in examples dir - // - async.apply(fs.readdir, examplesDir), - // - // 2. If node_modules exists, continue. Otherwise - // exec `npm` to install them - // - function checkNodeModules(files, next) { - if (files.indexOf('node_modules') !== -1) { - return next(); - } - - var child = spawn('npm', ['install', '-f'], { - cwd: examplesDir - }); - - child.on('exit', function (code) { - return code - ? next(new Error('npm install exited with non-zero exit code')) - : next(); - }); - }, - // - // 3. Read files in examples dir again to ensure the install - // worked as expected. - // - async.apply(fs.readdir, examplesDir), - ], this.callback); - }, - "examples/node_modules should exist": function (err, files) { - assert.notEqual(files.indexOf('node_modules'), -1); - } - } - } -}; - -// -// ### function shouldRequire (file) -// #### @file {string} File to attempt to require -// -// Returns a test which attempts to require `file`. -// -exports.shouldRequire = function (file) { - return { - "should have no errors": function () { - try { assert.isObject(require(file)) } - catch (ex) { assert.isNull(ex) } - } - }; -}; - -// -// ### function shouldHaveNoErrors () -// -// Returns a vows context that attempts to require -// every relevant example file in `examples`. -// -exports.shouldHaveNoErrors = function () { - var context = {}; - - ['balancer', 'http', 'middleware', 'websocket'].forEach(function (dir) { - var name = 'examples/' + dir, - files = fs.readdirSync(path.join(rootDir, 'examples', dir)); - - files.forEach(function (file) { - context[name + '/' + file] = exports.shouldRequire(path.join( - examplesDir, dir, file - )); - }); - }); - - return context; -}; \ No newline at end of file diff --git a/test/macros/http.js b/test/macros/http.js deleted file mode 100644 index d3d83426a..000000000 --- a/test/macros/http.js +++ /dev/null @@ -1,531 +0,0 @@ -/* - * http.js: Macros for proxying HTTP requests - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var assert = require('assert'), - fs = require('fs'), - async = require('async'), - net = require('net'), - request = require('request'), - helpers = require('../helpers/index'); - -// -// ### function assertRequest (options) -// #### @options {Object} Options for this request assertion. -// #### @request {Object} Options to use for `request`. -// #### @assert {Object} Test assertions against the response. -// -// Makes a request using `options.request` and then asserts the response -// and body against anything in `options.assert`. -// -exports.assertRequest = function (options) { - return { - topic: function () { - // - // Now make the HTTP request and assert. - // - options.request.rejectUnauthorized = false; - request(options.request, this.callback); - }, - "should succeed": function (err, res, body) { - assert.isNull(err); - if (options.assert.headers) { - Object.keys(options.assert.headers).forEach(function(header){ - assert.equal(res.headers[header], options.assert.headers[header]); - }); - } - - if (options.assert.body) { - assert.equal(body, options.assert.body); - } - - if (options.assert.statusCode) { - assert.equal(res.statusCode, options.assert.statusCode); - } - } - }; -}; - -// -// ### function assertFailedRequest (options) -// #### @options {Object} Options for this failed request assertion. -// #### @request {Object} Options to use for `request`. -// #### @assert {Object} Test assertions against the response. -// -// Makes a request using `options.request` and then asserts the response -// and body against anything in `options.assert`. -// -exports.assertFailedRequest = function (options) { - return { - topic: function () { - // - // Now make the HTTP request and assert. - // - options.request.rejectUnauthorized = false; - request(options.request, this.callback); - }, - "should not succeed": function (err, res, body) { - assert.notStrictEqual(err,null); - } - }; -}; - -// -// ### function assertProxied (options) -// #### @options {Object} Options for this test -// #### @latency {number} Latency in milliseconds for the proxy server. -// #### @ports {Object} Ports for the request (target, proxy). -// #### @output {string} Output to assert from. -// #### @forward {Object} Options for forward proxying. -// -// Creates a complete end-to-end test for requesting against an -// http proxy. -// -exports.assertProxied = function (options) { - options = options || {}; - - var ports = options.ports || helpers.nextPortPair, - output = options.output || 'hello world from ' + ports.target, - outputHeaders = options.outputHeaders, - targetHeaders = options.targetHeaders, - proxyHeaders = options.proxyHeaders, - protocol = helpers.protocols.proxy, - req = options.request || {}, - timeout = options.timeout || null, - assertFn = options.shouldFail - ? exports.assertFailedRequest - : exports.assertRequest; - - req.uri = req.uri || protocol + '://127.0.0.1:' + ports.proxy; - - return { - topic: function () { - // - // Create a target server and a proxy server - // using the `options` supplied. - // - helpers.http.createServerPair({ - target: { - output: output, - outputHeaders: targetHeaders, - port: ports.target, - headers: req.headers, - latency: options.requestLatency - }, - proxy: { - latency: options.latency, - port: ports.proxy, - outputHeaders: proxyHeaders, - proxy: { - forward: options.forward, - target: { - https: helpers.protocols.target === 'https', - host: '127.0.0.1', - port: ports.target - }, - timeout: timeout - } - } - }, this.callback); - }, - "the proxy request": assertFn({ - request: req, - assert: { - headers: outputHeaders, - body: output - } - }) - }; -}; - -// -// ### function assertRawHttpProxied (options) -// #### @options {Object} Options for this test -// #### @rawRequest {string} Raw HTTP request to perform. -// #### @match {RegExp} Output to match in the response. -// #### @latency {number} Latency in milliseconds for the proxy server. -// #### @ports {Object} Ports for the request (target, proxy). -// #### @output {string} Output to assert from. -// #### @forward {Object} Options for forward proxying. -// -// Creates a complete end-to-end test for requesting against an -// http proxy. -// -exports.assertRawHttpProxied = function (options) { - // Don't test raw requests over HTTPS since options.rawRequest won't be - // encrypted. - if(helpers.protocols.proxy == 'https') { - return true; - } - - options = options || {}; - - var ports = options.ports || helpers.nextPortPair, - output = options.output || 'hello world from ' + ports.target, - outputHeaders = options.outputHeaders, - targetHeaders = options.targetHeaders, - proxyHeaders = options.proxyHeaders, - protocol = helpers.protocols.proxy, - timeout = options.timeout || null, - assertFn = options.shouldFail - ? exports.assertFailedRequest - : exports.assertRequest; - - return { - topic: function () { - var topicCallback = this.callback; - - // - // Create a target server and a proxy server - // using the `options` supplied. - // - helpers.http.createServerPair({ - target: { - output: output, - outputHeaders: targetHeaders, - port: ports.target, - latency: options.requestLatency - }, - proxy: { - latency: options.latency, - port: ports.proxy, - outputHeaders: proxyHeaders, - proxy: { - forward: options.forward, - target: { - https: helpers.protocols.target === 'https', - host: '127.0.0.1', - port: ports.target - }, - timeout: timeout - } - } - }, function() { - var response = ''; - var client = net.connect(ports.proxy, '127.0.0.1', function() { - client.write(options.rawRequest); - }); - - client.on('data', function(data) { - response += data.toString(); - }); - - client.on('end', function() { - topicCallback(null, options.match, response); - }); - }); - }, - "should succeed": function(err, match, response) { - assert.match(response, match); - } - }; -}; - -// -// ### function assertInvalidProxy (options) -// #### @options {Object} Options for this test -// #### @latency {number} Latency in milliseconds for the proxy server -// #### @ports {Object} Ports for the request (target, proxy) -// -// Creates a complete end-to-end test for requesting against an -// http proxy with no target server. -// -exports.assertInvalidProxy = function (options) { - options = options || {}; - - var ports = options.ports || helpers.nextPortPair, - req = options.request || {}, - protocol = helpers.protocols.proxy; - - - req.uri = req.uri || protocol + '://127.0.0.1:' + ports.proxy; - - return { - topic: function () { - // - // Only create the proxy server, simulating a reverse-proxy - // to an invalid location. - // - helpers.http.createProxyServer({ - latency: options.latency, - port: ports.proxy, - proxy: { - target: { - host: '127.0.0.1', - port: ports.target - } - } - }, this.callback); - }, - "the proxy request": exports.assertRequest({ - request: req, - assert: { - statusCode: 500 - } - }) - }; -}; - -// -// ### function assertForwardProxied (options) -// #### @options {Object} Options for this test. -// -// Creates a complete end-to-end test for requesting against an -// http proxy with both a valid and invalid forward target. -// -exports.assertForwardProxied = function (options) { - var forwardPort = helpers.nextPort; - - return { - topic: function () { - helpers.http.createServer({ - output: 'hello from forward', - port: forwardPort - }, this.callback); - }, - "and a valid forward target": exports.assertProxied({ - forward: { - port: forwardPort, - host: '127.0.0.1' - } - }), - "and an invalid forward target": exports.assertProxied({ - forward: { - port: 9898, - host: '127.0.0.1' - } - }) - }; -}; - -// -// ### function assertProxiedtoRoutes (options, nested) -// #### @options {Object} Options for this ProxyTable-based test -// #### @routes {Object|string} Routes to use for the proxy. -// #### @hostnameOnly {boolean} Enables hostnameOnly routing. -// #### @nested {Object} Nested vows to add to the returned context. -// -// Creates a complete end-to-end test for requesting against an -// http proxy using `options.routes`: -// -// 1. Creates target servers for all routes in `options.routes.` -// 2. Creates a proxy server. -// 3. Ensure requests to the proxy server for all route targets -// returns the unique expected output. -// -exports.assertProxiedToRoutes = function (options, nested) { - // - // Assign dynamic ports to the routes to use. - // - options.routes = helpers.http.assignPortsToRoutes(options.routes); - - // - // Parse locations from routes for making assertion requests. - // - var locations = helpers.http.parseRoutes(options), - port = options.pport || helpers.nextPort, - protocol = helpers.protocols.proxy, - context, - proxy; - - if (options.filename) { - // - // If we've been passed a filename write the routes to it - // and setup the proxy options to use that file. - // - fs.writeFileSync(options.filename, JSON.stringify({ router: options.routes })); - proxy = { router: options.filename }; - } - else { - // - // Otherwise just use the routes themselves. - // - proxy = { - hostnameOnly: options.hostnameOnly, - pathnameOnly: options.pathnameOnly, - router: options.routes - }; - } - - // - // Set the https options if necessary - // - if (helpers.protocols.target === 'https') { - proxy.target = { https: true }; - } - - // - // Create the test context which creates all target - // servers for all routes and a proxy server. - // - context = { - topic: function () { - var that = this; - - async.waterfall([ - // - // 1. Create all the target servers - // - async.apply( - async.forEach, - locations, - function createRouteTarget(location, next) { - helpers.http.createServer({ - port: location.target.port, - output: 'hello from ' + location.source.href - }, next); - } - ), - // - // 2. Create the proxy server - // - async.apply( - helpers.http.createProxyServer, - { - port: port, - latency: options.latency, - routing: true, - proxy: proxy - } - ) - ], function (_, server) { - // - // 3. Set the proxy server for later use - // - that.proxyServer = server; - that.callback(); - }); - - // - // 4. Assign the port to the context for later use - // - this.port = port; - }, - // - // Add an extra assertion to a route which - // should respond with 404 - // - "a request to unknown.com": exports.assertRequest({ - assert: { statusCode: 404 }, - request: { - uri: protocol + '://127.0.0.1:' + port, - headers: { - host: 'unknown.com' - } - } - }) - }; - - // - // Add test assertions for each of the route locations. - // - locations.forEach(function (location) { - context[location.source.href] = exports.assertRequest({ - request: { - uri: protocol + '://127.0.0.1:' + port + location.source.path, - headers: { - host: location.source.hostname - } - }, - assert: { - body: 'hello from ' + location.source.href - } - }); - }); - - // - // If there are any nested vows to add to the context - // add them before returning the full context. - // - if (nested) { - Object.keys(nested).forEach(function (key) { - context[key] = nested[key]; - }); - } - - return context; -}; - -// -// ### function assertDynamicProxy (static, dynamic) -// Asserts that after the `static` routes have been tested -// and the `dynamic` routes are added / removed the appropriate -// proxy responses are received. -// -exports.assertDynamicProxy = function (static, dynamic) { - var proxyPort = helpers.nextPort, - protocol = helpers.protocols.proxy, - context; - - if (dynamic.add) { - dynamic.add = dynamic.add.map(function (dyn) { - dyn.port = helpers.nextPort; - dyn.target = dyn.target + dyn.port; - return dyn; - }); - } - - context = { - topic: function () { - var that = this; - - setTimeout(function () { - if (dynamic.drop) { - dynamic.drop.forEach(function (dropHost) { - that.proxyServer.proxy.removeHost(dropHost); - }); - } - - if (dynamic.add) { - async.forEachSeries(dynamic.add, function addOne (dyn, next) { - that.proxyServer.proxy.addHost(dyn.host, dyn.target); - helpers.http.createServer({ - port: dyn.port, - output: 'hello ' + dyn.host - }, next); - }, that.callback); - } - else { - that.callback(); - } - }, 200); - } - }; - - if (dynamic.drop) { - dynamic.drop.forEach(function (dropHost) { - context[dropHost] = exports.assertRequest({ - assert: { statusCode: 404 }, - request: { - uri: protocol + '://127.0.0.1:' + proxyPort, - headers: { - host: dropHost - } - } - }); - }); - } - - if (dynamic.add) { - dynamic.add.forEach(function (dyn) { - context[dyn.host] = exports.assertRequest({ - assert: { body: 'hello ' + dyn.host }, - request: { - uri: protocol + '://127.0.0.1:' + proxyPort, - headers: { - host: dyn.host - } - } - }); - }); - } - - static.pport = proxyPort; - return exports.assertProxiedToRoutes(static, { - "once the server has started": context - }); -}; diff --git a/test/macros/index.js b/test/macros/index.js deleted file mode 100644 index c01f962b0..000000000 --- a/test/macros/index.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * index.js: Top level include for node-http-proxy macros - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -exports.examples = require('./examples'); -exports.http = require('./http'); -exports.ws = require('./ws'); \ No newline at end of file diff --git a/test/macros/ws.js b/test/macros/ws.js deleted file mode 100644 index 508725a49..000000000 --- a/test/macros/ws.js +++ /dev/null @@ -1,232 +0,0 @@ -/* - * ws.js: Macros for proxying Websocket requests - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var assert = require('assert'), - fs = require('fs'), - async = require('async'), - io = require('socket.io-client'), - WebSocket = require('ws'), - helpers = require('../helpers/index'); - -// -// ### function assertSendRecieve (options) -// #### @options {Object} Options for creating this assertion. -// #### @raw {boolean} Enables raw `ws.WebSocket`. -// #### @uri {string} URI of the proxy server. -// #### @input {string} Input to assert sent to the target ws server. -// #### @output {string} Output to assert from the taget ws server. -// -// Creates a `socket.io` or raw `WebSocket` connection and asserts that -// `options.input` is sent to and `options.output` is received from the -// connection. -// -exports.assertSendReceive = function (options) { - if (!options.raw) { - return { - topic: function () { - var socket = io.connect(options.uri); - socket.on('outgoing', this.callback.bind(this, null)); - socket.emit('incoming', options.input); - }, - "should send input and receive output": function (_, data) { - assert.equal(data, options.output); - } - }; - } - - return { - topic: function () { - var socket = new WebSocket(options.uri); - socket.on('message', this.callback.bind(this, null)); - socket.on('open', function () { - socket.send(options.input); - }); - }, - "should send input and recieve output": function (_, data, flags) { - assert.equal(data, options.output); - } - }; -}; - -// -// ### function assertProxied (options) -// #### @options {Object} Options for this test -// #### @latency {number} Latency in milliseconds for the proxy server. -// #### @ports {Object} Ports for the request (target, proxy). -// #### @input {string} Input to assert sent to the target ws server. -// #### @output {string} Output to assert from the taget ws server. -// #### @raw {boolean} Enables raw `ws.Server` usage. -// -// Creates a complete end-to-end test for requesting against an -// http proxy. -// -exports.assertProxied = function (options) { - options = options || {}; - - var ports = options.ports || helpers.nextPortPair, - input = options.input || 'hello world to ' + ports.target, - output = options.output || 'hello world from ' + ports.target, - protocol = helpers.protocols.proxy; - - if (options.raw) { - protocol = helpers.protocols.proxy === 'https' - ? 'wss' - : 'ws'; - } - - return { - topic: function () { - helpers.ws.createServerPair({ - target: { - input: input, - output: output, - port: ports.target, - raw: options.raw - }, - proxy: { - latency: options.latency, - port: ports.proxy, - proxy: { - target: { - https: helpers.protocols.target === 'https', - host: '127.0.0.1', - port: ports.target - } - } - } - }, this.callback); - }, - "the proxy Websocket connection": exports.assertSendReceive({ - uri: protocol + '://127.0.0.1:' + ports.proxy, - input: input, - output: output, - raw: options.raw - }) - }; -}; - -// -// ### function assertProxiedtoRoutes (options, nested) -// #### @options {Object} Options for this ProxyTable-based test -// #### @raw {boolean} Enables ws.Server usage. -// #### @routes {Object|string} Routes to use for the proxy. -// #### @hostnameOnly {boolean} Enables hostnameOnly routing. -// #### @nested {Object} Nested vows to add to the returned context. -// -// Creates a complete end-to-end test for requesting against an -// http proxy using `options.routes`: -// -// 1. Creates target servers for all routes in `options.routes.` -// 2. Creates a proxy server. -// 3. Ensure Websocket connections to the proxy server for all route targets -// can send input and recieve output. -// -exports.assertProxiedToRoutes = function (options, nested) { - // - // Assign dynamic ports to the routes to use. - // - options.routes = helpers.http.assignPortsToRoutes(options.routes); - - // - // Parse locations from routes for making assertion requests. - // - var locations = helpers.http.parseRoutes(options), - protocol = helpers.protocols.proxy, - port = helpers.nextPort, - context, - proxy; - - if (options.raw) { - protocol = helpers.protocols.proxy === 'https' - ? 'wss' - : 'ws'; - } - - if (options.filename) { - // - // If we've been passed a filename write the routes to it - // and setup the proxy options to use that file. - // - fs.writeFileSync(options.filename, JSON.stringify({ router: options.routes })); - proxy = { router: options.filename }; - } - else { - // - // Otherwise just use the routes themselves. - // - proxy = { - hostnameOnly: options.hostnameOnly, - router: options.routes - }; - } - - // - // Create the test context which creates all target - // servers for all routes and a proxy server. - // - context = { - topic: function () { - var that = this; - - async.waterfall([ - // - // 1. Create all the target servers - // - async.apply( - async.forEach, - locations, - function createRouteTarget(location, next) { - helpers.ws.createServer({ - raw: options.raw, - port: location.target.port, - output: 'hello from ' + location.source.href, - input: 'hello to ' + location.source.href - }, next); - } - ), - // - // 2. Create the proxy server - // - async.apply( - helpers.http.createProxyServer, - { - port: port, - latency: options.latency, - routing: true, - proxy: proxy - } - ) - ], function (_, server) { - // - // 3. Set the proxy server for later use - // - that.proxyServer = server; - that.callback(); - }); - - // - // 4. Assign the port to the context for later use - // - this.port = port; - } - }; - - // - // Add test assertions for each of the route locations. - // - locations.forEach(function (location) { - context[location.source.href] = exports.assertSendRecieve({ - uri: protocol + '://127.0.0.1:' + port + location.source.path, - output: 'hello from ' + location.source.href, - input: 'hello to ' + location.source.href, - raw: options.raw - }); - }); - - return context; -}; \ No newline at end of file diff --git a/test/ws/routing-table-test.js b/test/ws/routing-table-test.js deleted file mode 100644 index e04d64752..000000000 --- a/test/ws/routing-table-test.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * routing-tabletest.js: Test for proxying `socket.io` and raw `WebSocket` requests using a ProxyTable. - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var vows = require('vows'), - macros = require('../macros'), - helpers = require('../helpers/index'); - -vows.describe(helpers.describe('routing-proxy', 'ws')).addBatch({ - "With a valid target server": { - "and no latency": { - "using ws": macros.ws.assertProxied(), - "using socket.io": macros.ws.assertProxied({ - raw: true - }), - }, - // "and latency": macros.websocket.assertProxied({ - // latency: 2000 - // }) - } -}).export(module); \ No newline at end of file diff --git a/test/ws/socket.io-test.js b/test/ws/socket.io-test.js deleted file mode 100644 index d833109e6..000000000 --- a/test/ws/socket.io-test.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * socket.io-test.js: Test for proxying `socket.io` requests. - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var vows = require('vows'), - macros = require('../macros'), - helpers = require('../helpers/index'); - -vows.describe(helpers.describe('socket.io', 'ws')).addBatch({ - "With a valid target server": { - "and no latency": macros.ws.assertProxied(), - // "and latency": macros.ws.assertProxied({ - // latency: 2000 - // }) - } -}).export(module); \ No newline at end of file diff --git a/test/ws/ws-test.js b/test/ws/ws-test.js deleted file mode 100644 index f3549152f..000000000 --- a/test/ws/ws-test.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * ws-test.js: Tests for proxying raw Websocket requests. - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENCE - * - */ - -var vows = require('vows'), - macros = require('../macros'), - helpers = require('../helpers/index'); - -vows.describe(helpers.describe('websocket', 'ws')).addBatch({ - "With a valid target server": { - "and no latency": macros.ws.assertProxied({ - raw: true - }), - // "and latency": macros.ws.assertProxied({ - // raw: true, - // latency: 2000 - // }) - } -}).export(module); \ No newline at end of file From 969a623542030147e95d78fae164b3cdf061a77f Mon Sep 17 00:00:00 2001 From: Mike Moulton Date: Tue, 14 Jan 2014 00:45:42 -0700 Subject: [PATCH 207/210] Only emit response if a valid server is present --- lib/http-proxy/passes/web-incoming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http-proxy/passes/web-incoming.js b/lib/http-proxy/passes/web-incoming.js index 2b14c874c..305365f7a 100644 --- a/lib/http-proxy/passes/web-incoming.js +++ b/lib/http-proxy/passes/web-incoming.js @@ -117,7 +117,7 @@ web_o = Object.keys(web_o).map(function(pass) { (options.buffer || req).pipe(proxyReq); proxyReq.on('response', function(proxyRes) { - server.emit('proxyRes', proxyRes); + if(server) { server.emit('proxyRes', proxyRes); } for(var i=0; i < web_o.length; i++) { if(web_o[i](req, res, proxyRes)) { break; } } From 4351ed1c86c8336b3a2d9f80098dcb2c9180685d Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 16 Jan 2014 15:03:44 +0100 Subject: [PATCH 208/210] [fix] closes #547 --- lib/http-proxy.js | 7 +++++++ lib/http-proxy/index.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/http-proxy.js b/lib/http-proxy.js index 196dded44..34029e864 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -8,6 +8,13 @@ var http = require('http'), */ module.exports = httpProxy.Server; +module.exports.createProxy = function(options) { + return { + web: httpProxy.createRightProxy('web')(options), + ws: httpProxy.createRightProxy('ws')(options) + }; +} + /** * Creates the proxy server. * diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index bd8b8a991..95bc287b0 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -80,7 +80,7 @@ function createRightProxy(type) { }; }; } - +httpProxy.createRightProxy = createRightProxy; function ProxyServer(options) { EE3.call(this); From d23353d980d8aa1b2606e3d36a83d27432952bef Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 16 Jan 2014 15:05:52 +0100 Subject: [PATCH 209/210] [fix] ee3 error handling --- lib/http-proxy/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js index 95bc287b0..4436c1351 100644 --- a/lib/http-proxy/index.js +++ b/lib/http-proxy/index.js @@ -96,6 +96,10 @@ function ProxyServer(options) { this.wsPasses = Object.keys(ws).map(function(pass) { return ws[pass]; }); + + this.on('error', function(err) { + console.log(err); + }); } require('util').inherits(ProxyServer, EE3); From d6d2d0c8821bba9888eee7c3881fc408b3b2008e Mon Sep 17 00:00:00 2001 From: yawnt Date: Thu, 16 Jan 2014 16:28:08 +0100 Subject: [PATCH 210/210] [fix] remove caronte --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ec4ed54d..4d68f3d5e 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ proxies and load balancers.

-    +    - +

### Looking to Upgrade from 0.8.x ? Click [here](UPGRADING.md)