Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

upgrade to amino 1.x

  • Loading branch information...
commit 8134bd031cf9b611d6b8414d8c20f188d3345c95 1 parent 6c7a79b
@carlos8f carlos8f authored
View
16 .gitignore
@@ -1,15 +1 @@
-lib-cov
-*.seed
-*.log
-*.csv
-*.dat
-*.out
-*.pid
-*.gz
-
-pids
-logs
-results
-
-node_modules
-npm-debug.log
+node_modules
View
9 Makefile
@@ -1,9 +1,8 @@
-
-REPORTER = spec
-
test:
- @NODE_ENV=test ./node_modules/.bin/mocha \
- --reporter $(REPORTER)
+ @./node_modules/.bin/mocha \
+ --reporter spec \
+ --require test/common.js \
+ --bail
.PHONY: test
View
130 README.md
@@ -1,26 +1,18 @@
amino-gateway
=============
-Clusterable http proxy for [Amino](https://github.com/cantina/amino) services
+Clusterable load-balancer for [Amino](https://github.com/amino/amino) services
-[![build status](https://secure.travis-ci.org/cantina/amino-gateway.png)](http://travis-ci.org/cantina/amino-gateway)
+[![build status](https://secure.travis-ci.org/amino/amino-gateway.png)](http://travis-ci.org/amino/amino-gateway)
-Idea
-----
-
- - First, use Amino's `respond()` API to create an "app" service.
- - Start one or more of those servers.
- - Start one or more `amino-gateway` servers.
- - HTTP requests to your gateway servers will pipe to your app servers,
- auto-loadbalancing between them without any further configuration. Easy!
-
-Requirements
-------------
-
-You'll also need one or more Amino processes implementing the `respond()` API
-to provide an "app" service (or a service specified by `--service`).
+Features
+--------
-See [Amino](https://github.com/cantina/amino) for more information.
+- Round-robin load-balancer for an [Amino](https://github.com/amino/amino) service.
+- Backend servers added/removed to the rotation automatically
+- Sticky sessions via cookie, IP, GET variable, or header
+- Supports websockets, streaming
+- Multi-threaded, high performance
Install
-------
@@ -29,77 +21,59 @@ Install
$ npm install -g amino-gateway
```
-Also make sure you have an `/etc/amino.conf` set up which has the same drivers/options
-as used by your app services.
-
Usage
-----
```bash
-$ amino-gateway [[--port] [--service] [--threads] [--conf]]
+$ amino-gateway [OPTIONS]
```
-Examples
---------
-
-**Start a gateway with default settings:**
-
-```bash
-$ amino-gateway
-```
-
-**Start a gateway on port 8000:**
-
-```bash
-$ amino-gateway -p 8000
-```
-
-**Start a gateway to proxy to "foo" service:**
-
-```bash
-$ amino-gateway --service foo
-```
-
-**Run on specific number of threads**
-
-```bash
-$ amino-gateway --threads=8
-```
-
-**Specify a conf file:**
-
-See `etc/gateway.json` for the file's syntax.
-
-```bash
-$ amino-gateway --conf ../path/to/my/conf.json
-```
-
-The conf file can also have an "amino" key, corresponding to the Amino configuration to
-use. This will override `/etc/amino.conf`.
-
-If you'd rather not pass a conf path every time, you can put a system-wide conf at
-`/etc/amino/gateway.json`.
+Options
+-------
-Sticky Sessions
----------------
+- `--service` (`-s`): Name of the service to . Optionally, you can add `@version` to
+ limit to a specific [semver](http://semver.org/) range. (Default: `app`)
+- `--threads` (`-t`): Number of threads to use. (Default: number of CPU cores)
+- `--version` (`-v`): Display the version and exit.
+- `--port` (`-p`): Port to listen on. (Default: `8080`)
+- `--redis=host:port`: Specify host and port of Amino's redis server(s). Use
+ multiple `--redis` args for multiple servers. (Default: `localhost:6379`)
+- `--sockets`: Max number of sockets to simultaneously open with backends.
+ (Default: `25000`)
+- `--sticky.ip`: Enable sticky sessions based on remote IP address.
+- `--sticky.cookie`: Specify the name of a cookie to be used for sticky sessions.
+- `--sticky.query`: Specify a GET variable to be used for sticky sessions.
-`amino-gateway` supports
-[consistent hash](http://en.wikipedia.org/wiki/Consistent_hashing)-based sticky
-sessions.
+---
-__To enable sticky sessions, first add `"stickyEnable": true` to your
-`gateway.json`.__
+Developed by [Terra Eclipse](http://www.terraeclipse.com)
+---------------------------------------------------------
-You may use **one** of the following methods:
+Terra Eclipse, Inc. is a nationally recognized political technology and
+strategy firm located in Aptos, CA and Washington, D.C.
-- cookie: Add `"stickyCookie": "your_cookie_name"` to maintain sticky sessions
- based on the value of a cookie.
-- IP: Add `"stickyIP": true` to hash sticky sessions based on the remote IP
- address.
-- query string: Add `"stickyQuery": "your_querystring_variable_name"` to hash
- sticky sessions based on the value of a GET variable.
+[http://www.terraeclipse.com](http://www.terraeclipse.com)
-LICENSE
--------
+License: MIT
+------------
-MIT
+- Copyright (C) 2012 Carlos Rodriguez (http://s8f.org/)
+- Copyright (C) 2012 Terra Eclipse, Inc. (http://www.terraeclipse.com/)
+
+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.
View
44 bin/gateway.js
@@ -1,44 +1,46 @@
#!/usr/bin/env node
-var numCPUs = require('os').cpus().length
+var argv = require('optimist')
+ .alias('p', 'port')
+ .alias('s', 'service')
+ .alias('t', 'threads')
+ .alias('v', 'version')
+ .default('port', 8080)
+ .default('service', 'app')
+ .default('threads', require('os').cpus().length)
+ .default('sockets', 25000)
+ .argv
, amino = require('amino')
- .argv({
- p: {alias: 'port'},
- s: {alias: 'service'},
- t: {alias: 'threads', default: numCPUs},
- v: {alias: 'version'}
+ .init({
+ redis: argv.redis,
+ request: argv.request,
+ service: false
})
- .conf('/etc/amino/gateway.json')
- .conf('../etc/gateway.json', __dirname)
- , port = amino.get('port')
- , threads = amino.get('threads')
- , service = amino.get('service')
- , gateway = require('../')
+ , createGateway = require('../')
, cluster = require('cluster')
- ;
-if (amino.get('v')) {
+if (argv.version) {
console.log(require(require('path').join(__dirname, '../package.json')).version);
process.exit();
}
if (cluster.isMaster) {
// Fork workers.
- for (var i = 0; i < threads; i++) {
+ for (var i = 0; i < argv.threads; i++) {
cluster.fork();
}
- cluster.on('exit', function(worker, code, signal) {
+ cluster.on('exit', function (worker, code, signal) {
var exitCode = worker.process.exitCode;
console.log('worker ' + worker.pid + ' died (' + exitCode + '). restarting...');
cluster.fork();
});
- console.log(service + ' gateway listening (' + (threads > 1 ? threads + ' threads' : 'single thread') + ') on port ' + port + '...');
+ console.log(argv.service + ' gateway listening (' + (argv.threads > 1 ? argv.threads + ' threads' : 'single thread') + ') on port ' + argv.port + '...');
} else {
- gateway.createGateway(service)
- .on('error', function(err) {
- console.error(err, 'bouncy error');
+ createGateway(argv)
+ .on('error', function (err) {
+ console.error(err, 'server error');
})
- .listen(port);
+ .listen(argv.port);
}
View
4 etc/gateway.json
@@ -1,4 +0,0 @@
-{
- "port": 8080,
- "service": "app"
-}
View
38 examples/README.md
@@ -1,38 +0,0 @@
-amino-gateway examples
-----------------------
-
-To use the example, first install the dependency:
-
-```bash
-$ cd examples
-$ npm install
-```
-
-Start up two app servers in separate windows:
-
-```bash
-$ node app.js --debug
-```
-
-Install `amino-gateaway` if you haven't already, then start:
-
-```bash
-$ npm install -g amino-gateway
-$ amino-gateway --debug
-```
-
-Try making curl requests to the gateway:
-
-```bash
-$ curl --url http://localhost:8080/
-```
-
-You should be able to stop one of the app servers, and still get responses.
-
-For even more fun, you can start multiple gateways:
-
-```bash
-$ amino-gateway -p 8081 --debug
-```
-
-Et cetera!
View
16 examples/app.js
@@ -1,8 +1,12 @@
-var amino = require('amino');
+// Start one or more of these servers, and start the gateway.
+// The gateway will load-balance between servers, with the default configuration.
-amino.respond('app', function(router, spec) {
- router.get('/', function() {
- this.res.text("hello world, sincerely " + spec.toString() + "\n");
- });
- console.log('app started on ' + spec.toString());
+var amino = require('amino').init();
+
+var server = require('http').createServer(function (req, res) {
+ res.end('hello world! sincerely, ' + service.spec + '\n');
+});
+var service = amino.createService('app', server);
+service.on('listening', function () {
+ console.log('app started: ' + service.spec);
});
View
8 examples/package.json
@@ -1,8 +0,0 @@
-{
- "name": "examples",
- "private": true,
- "version": "0.0.0",
- "dependencies": {
- "amino": "~0.8.0"
- }
-}
View
46 index.js
@@ -3,39 +3,35 @@ var amino = require('amino')
, parseUrl = require('url').parse
, httpProxy = require('http-proxy')
, addr = require('addr')
+ , Spec = require('amino-spec')
-module.exports.createGateway = function (service, onError) {
- var stickyEnable = amino.get('stickyEnable')
- , stickyCookie = amino.get('stickyCookie')
- , stickyIP = amino.get('stickyIP')
- , stickyQuery = amino.get('stickyQuery')
- , maxSockets = amino.get('maxSockets')
+module.exports = function createGateway (options) {
+ options || (options = {});
- if (maxSockets !== false && typeof maxSockets !== 'number') {
- maxSockets = 25000;
- }
+ var serviceSpec = new Spec(options.service);
function setupRequest (req, cb) {
req.on('error', function (err) {
console.error(err, '#error');
});
- var clientId;
- if (stickyEnable) {
- if (stickyCookie && req.headers.cookie) {
- clientId = cookie.parse(req.headers.cookie)[stickyCookie];
+ var stickyId;
+ if (options.sticky) {
+ if (options.sticky.cookie && req.headers.cookie) {
+ stickyId = cookie.parse(req.headers.cookie)[options.sticky.cookie];
}
- else if (stickyIP) {
- clientId = addr(req);
+ else if (options.sticky.ip) {
+ stickyId = addr(req);
}
- else if (stickyQuery) {
+ else if (options.sticky.query) {
var query = parseUrl(req.url, true).query;
- if (query && query[stickyQuery]) {
- clientId = query[stickyQuery];
- }
+ stickyId = query[options.sticky.query];
}
}
- req._sReq = amino.requestService(service, req.headers['x-amino-version'], clientId)
- req._sReq.on('spec', cb);
+ req._sReq = amino.requestService({
+ service: serviceSpec.service,
+ version: req.headers['x-amino-version'] || serviceSpec.version,
+ stickyId: req.headers['x-amino-stickyid'] || stickyId
+ }, cb);
}
function onReqError (err, req, res, sReq, spec) {
@@ -57,9 +53,10 @@ module.exports.createGateway = function (service, onError) {
}
}
- if (maxSockets) {
- httpProxy.setMaxSockets(maxSockets);
+ if (options.sockets) {
+ httpProxy.setMaxSockets(options.sockets);
}
+
var server = httpProxy.createServer(function (req, res, proxy) {
var buffer = httpProxy.buffer(req);
setupRequest(req, function (spec) {
@@ -67,6 +64,7 @@ module.exports.createGateway = function (service, onError) {
proxy.proxyRequest(req, res, {host: spec.host, port: spec.port, buffer: buffer});
});
});
+
server.on('upgrade', function(req, socket, head) {
var buffer = httpProxy.buffer(req);
setupRequest(req, function (spec) {
@@ -74,8 +72,10 @@ module.exports.createGateway = function (service, onError) {
server.proxy.proxyWebSocketRequest(req, socket, head, {host: spec.host, port: spec.port, buffer: buffer});
});
});
+
server.proxy.on('proxyError', function (err, req, res) {
onReqError(err, req, res, req._sReq, req._spec);
});
+
return server;
};
View
30 package.json
@@ -1,27 +1,43 @@
{
"author": "Carlos Rodriguez <carlos@s8f.org> (http://s8f.org/)",
"name": "amino-gateway",
- "description": "Clusterable http proxy for amino services",
+ "description": "Clusterable load-balancer for Amino services",
"version": "0.7.4",
"repository": {
"type": "git",
- "url": "git://github.com/cantina/amino-gateway.git"
+ "url": "git://github.com/amino/amino-gateway.git"
},
+ "homepage": "https://github.com/amino/amino-gateway",
+ "keywords": [
+ "load-balancing",
+ "load balancer",
+ "failover",
+ "cluster",
+ "scalability",
+ "performance",
+ "service",
+ "pub/sub",
+ "high-availability",
+ "cloud",
+ "redundancy",
+ "ipc"
+ ],
"bin": "./bin/gateway.js",
"dependencies": {
- "amino": "~0.13.0",
+ "amino": "~1.0.2",
"cookie": "~0.0.4",
"http-proxy": "~0.8.2",
- "addr": "~1.0.0"
+ "addr": "~1.0.0",
+ "optimist": "~0.3.4",
+ "amino-spec": "0.0.1"
},
"devDependencies": {
"mocha": "*",
- "idgen": "~1.0.2"
+ "async": "~0.1.22"
},
"bugs": {
- "url": "https://github.com/cantina/amino-gateway/issues"
+ "url": "https://github.com/amino/amino-gateway/issues"
},
- "optionalDependencies": {},
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
View
60 test/basic.js
@@ -0,0 +1,60 @@
+describe('simple proxy', function () {
+ var gateway, service;
+
+ before(function (done) {
+ gateway = execFile('./bin/gateway.js', ['-s', 'argyle@0.1.x', '-p', '50234']);
+ gateway.stdout.once('data', function (chunk) {
+ assert.ok(chunk.toString().match(/^argyle@0.1.x gateway listening .*on port 50234\.\.\.\n$/), 'settings overridden');
+ done();
+ });
+ });
+
+ before(function (done) {
+ var server = createServer(function (req, res) {
+ if (req.url === '/robots.txt') {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end('User-agent: *\nDisallow: /');
+ }
+ else if (req.url === '/post') {
+ assert.equal(req.method, 'POST');
+ req.pipe(res);
+ }
+ });
+ service = amino.createService('argyle@0.1.5', server);
+ service.once('listening', done);
+ });
+
+ after(function (done) {
+ gateway.kill();
+ service.close(done);
+ });
+
+ it('waits a bit', function (done) {
+ setTimeout(done, 500);
+ });
+
+ it('should proxy basic request', function (done) {
+ amino.request('http://localhost:50234/robots.txt', function (err, res, body) {
+ assert.ifError(err);
+ assert.strictEqual(body, 'User-agent: *\nDisallow: /');
+ done();
+ });
+ });
+
+ it('should stream a request', function (done) {
+ var inputStream = new ValidationStream('abcd');
+ var outputStream = new ValidationStream('abcd', done);
+
+ var options = {
+ method: 'POST',
+ url: 'http://localhost:50234/post'
+ };
+
+ inputStream.pipe(amino.request(options)).pipe(outputStream);
+
+ for (var i = 0, len = inputStream.str.length; i < len; i++) {
+ inputStream.write(inputStream.str[i]);
+ }
+ inputStream.end();
+ });
+});
View
15 test/common.js
@@ -0,0 +1,15 @@
+assert = require('assert');
+
+amino = require('amino').init();
+
+execFile = require('child_process').execFile;
+
+util = require('util');
+
+cookie = require('cookie');
+
+ValidationStream = require('./helpers/ValidationStream');
+
+createServer = require('http').createServer;
+
+async = require('async');
View
23 test/helpers/ValidationStream.js
@@ -0,0 +1,23 @@
+function ValidationStream (str, cb) {
+ this.str = str;
+ this.buf = '';
+ this.on('data', function (data) {
+ this.buf += data
+ })
+ this.on('end', function () {
+ assert.strictEqual(this.str, this.buf);
+ if (cb) cb();
+ })
+ this.writable = true;
+}
+util.inherits(ValidationStream, require('stream').Stream);
+module.exports = ValidationStream;
+
+ValidationStream.prototype.write = function (chunk) {
+ this.emit('data', chunk);
+};
+
+ValidationStream.prototype.end = function (chunk) {
+ if (chunk) emit('data', chunk);
+ this.emit('end');
+};
View
6 test/sticky-cookie.conf
@@ -1,6 +0,0 @@
-{
- "port": 58402,
- "service": "sticky-test-cookie",
- "stickyEnable": true,
- "stickyCookie": "connect.sid"
-}
View
69 test/sticky-cookie.js
@@ -1,48 +1,51 @@
-var assert = require('assert')
- , amino = require('amino')
- , child_process = require('child_process')
- , util = require('util')
- , idgen = require('idgen')
- , cookie = require('cookie')
- ;
+describe('sticky session (cookie-based)', function () {
+ var gateway, services, specIds = [];
-describe('sticky session (cookie-based)', function() {
- var gateway, specIds = [];
- it('can start with alternate conf file', function(done) {
- process.on('exit', function() {
- if (gateway) {
- gateway.kill();
- }
- });
- gateway = child_process.execFile('./bin/gateway.js', ['--conf', 'test/sticky-cookie.conf']);
- gateway.stdout.once('data', function(chunk) {
+ before(function (done) {
+ gateway = execFile('./bin/gateway.js', ['-s', 'sticky-test-cookie', '-p', '58402', '--sticky.cookie', 'connect.sid']);
+ gateway.stdout.once('data', function (chunk) {
assert.ok(chunk.toString().match(/^sticky-test-cookie gateway listening .*on port 58402\.\.\.\n$/), 'settings overridden');
done();
});
});
- it('can set up servers', function(done) {
- var numServers = 3, started = 0;
- for (var i = 0; i < numServers; i++) {
- amino.respond('sticky-test-cookie', function(router, spec) {
- specIds.push(spec.id);
- router.get('/specId', function() {
- this.res.text(spec.id);
+
+ before(function (done) {
+ var tasks = [];
+ for (var i = 0; i < 3; i++) {
+ tasks.push(function (cb) {
+ var server = createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end(service.spec.id);
+ });
+ var service = amino.createService('sticky-test-cookie', server);
+ service.once('listening', function () {
+ specIds.push(service.spec.id);
+ cb(null, service);
});
- if (++started === numServers) {
- done();
- }
});
}
+ async.parallel(tasks, function (err, results) {
+ services = results;
+ done(err);
+ });
+ });
+
+ after(function (done) {
+ gateway.kill();
+ var tasks = services.map(function (service) { return service.close.bind(service); });
+ async.parallel(tasks, done);
});
- it('waits a bit', function(done) {
+
+ it('waits a bit', function (done) {
setTimeout(done, 500);
});
- it('only routes to one server', function(done) {
- var clientId = idgen(), numRequests = 100, started = 0, completed = 0, specId;
- process.nextTick(function nextRequest() {
- amino.request({url: 'http://localhost:58402/specId', headers: {cookie: cookie.serialize('connect.sid', clientId)}}, function(err, response, body) {
+
+ it('only routes to one server', function (done) {
+ var clientId = amino.utils.idgen(), numRequests = 100, started = 0, completed = 0, specId;
+ process.nextTick(function nextRequest () {
+ amino.request({url: 'http://localhost:58402/', headers: {cookie: cookie.serialize('connect.sid', clientId)}}, function (err, res, body) {
assert.ifError(err);
- assert.strictEqual(response.statusCode, 200, 'status is 200');
+ assert.strictEqual(res.statusCode, 200, 'status is 200');
assert.ok(specIds.indexOf(body) !== -1, 'spec known');
if (specId) {
assert.strictEqual(body, specId, 'routed to only one spec');
View
6 test/sticky-ip.conf
@@ -1,6 +0,0 @@
-{
- "port": 58403,
- "service": "sticky-test-ip",
- "stickyEnable": true,
- "stickyIP": true
-}
View
69 test/sticky-ip.js
@@ -1,48 +1,51 @@
-var assert = require('assert')
- , amino = require('amino')
- , child_process = require('child_process')
- , util = require('util')
- , idgen = require('idgen')
- , cookie = require('cookie')
- ;
+describe('sticky session (ip-based)', function () {
+ var gateway, services, specIds = [];
-describe('sticky session (ip-based)', function() {
- var gateway, specIds = [];
- it('can start with alternate conf file', function(done) {
- process.on('exit', function() {
- if (gateway) {
- gateway.kill();
- }
- });
- gateway = child_process.execFile('./bin/gateway.js', ['--conf', 'test/sticky-ip.conf']);
- gateway.stdout.once('data', function(chunk) {
+ before(function (done) {
+ gateway = execFile('./bin/gateway.js', ['-s', 'sticky-test-ip', '-p', '58403', '--sticky.ip']);
+ gateway.stdout.once('data', function (chunk) {
assert.ok(chunk.toString().match(/^sticky-test-ip gateway listening .*on port 58403\.\.\.\n$/), 'settings overridden');
done();
});
});
- it('can set up servers', function(done) {
- var numServers = 3, started = 0;
- for (var i = 0; i < numServers; i++) {
- amino.respond('sticky-test-ip', function(router, spec) {
- specIds.push(spec.id);
- router.get('/specId', function() {
- this.res.text(spec.id);
+
+ before(function (done) {
+ var tasks = [];
+ for (var i = 0; i < 3; i++) {
+ tasks.push(function (cb) {
+ var server = createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end(service.spec.id);
+ });
+ var service = amino.createService('sticky-test-ip', server);
+ service.once('listening', function () {
+ specIds.push(service.spec.id);
+ cb(null, service);
});
- if (++started === numServers) {
- done();
- }
});
}
+ async.parallel(tasks, function (err, results) {
+ services = results;
+ done(err);
+ });
+ });
+
+ after(function (done) {
+ gateway.kill();
+ var tasks = services.map(function (service) { return service.close.bind(service); });
+ async.parallel(tasks, done);
});
- it('waits a bit', function(done) {
+
+ it('waits a bit', function (done) {
setTimeout(done, 500);
});
- it('only routes to one server', function(done) {
- var numRequests = 100, started = 0, completed = 0, specId;
- process.nextTick(function nextRequest() {
- amino.request({url: 'http://localhost:58403/specId', headers: {'x-forwarded-for': '42.35.23.13, 62.44.11.5'}}, function(err, response, body) {
+
+ it('only routes to one server', function (done) {
+ var clientId = amino.utils.idgen(), numRequests = 100, started = 0, completed = 0, specId;
+ process.nextTick(function nextRequest () {
+ amino.request({url: 'http://localhost:58403/', headers: {'X-Forwarded-For': '42.35.23.13, 62.44.11.5'}}, function (err, res, body) {
assert.ifError(err);
- assert.strictEqual(response.statusCode, 200, 'status is 200');
+ assert.strictEqual(res.statusCode, 200, 'status is 200');
assert.ok(specIds.indexOf(body) !== -1, 'spec known');
if (specId) {
assert.strictEqual(body, specId, 'routed to only one spec');
View
6 test/sticky-query.conf
@@ -1,6 +0,0 @@
-{
- "port": 58404,
- "service": "sticky-test-query",
- "stickyEnable": true,
- "stickyQuery": "sid"
-}
View
70 test/sticky-query.js
@@ -1,47 +1,51 @@
-var assert = require('assert')
- , amino = require('amino')
- , child_process = require('child_process')
- , util = require('util')
- , idgen = require('idgen')
- ;
+describe('sticky session (query-based)', function () {
+ var gateway, services, specIds = [];
-describe('sticky session (query-based)', function() {
- var gateway, specIds = [];
- it('can start with alternate conf file', function(done) {
- process.on('exit', function() {
- if (gateway) {
- gateway.kill();
- }
- });
- gateway = child_process.execFile('./bin/gateway.js', ['--conf', 'test/sticky-query.conf']);
- gateway.stdout.once('data', function(chunk) {
- assert.ok(chunk.toString().match(/^sticky-test-query gateway listening .*on port 58404\.\.\.\n$/), 'settings overridden');
+ before(function (done) {
+ gateway = execFile('./bin/gateway.js', ['-s', 'sticky-test-query', '-p', '20523', '--sticky.query', 'sid']);
+ gateway.stdout.once('data', function (chunk) {
+ assert.ok(chunk.toString().match(/^sticky-test-query gateway listening .*on port 20523\.\.\.\n$/), 'settings overridden');
done();
});
});
- it('can set up servers', function(done) {
- var numServers = 3, started = 0;
- for (var i = 0; i < numServers; i++) {
- amino.respond('sticky-test-query', function(router, spec) {
- specIds.push(spec.id);
- router.get('/specId', function() {
- this.res.text(spec.id);
+
+ before(function (done) {
+ var tasks = [];
+ for (var i = 0; i < 3; i++) {
+ tasks.push(function (cb) {
+ var server = createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end(service.spec.id);
+ });
+ var service = amino.createService('sticky-test-query', server);
+ service.once('listening', function () {
+ specIds.push(service.spec.id);
+ cb(null, service);
});
- if (++started === numServers) {
- done();
- }
});
}
+ async.parallel(tasks, function (err, results) {
+ services = results;
+ done(err);
+ });
+ });
+
+ after(function (done) {
+ gateway.kill();
+ var tasks = services.map(function (service) { return service.close.bind(service); });
+ async.parallel(tasks, done);
});
- it('waits a bit', function(done) {
+
+ it('waits a bit', function (done) {
setTimeout(done, 500);
});
- it('only routes to one server', function(done) {
- var clientId = idgen(), numRequests = 100, started = 0, completed = 0, specId;
- process.nextTick(function nextRequest() {
- amino.request({url: 'http://localhost:58404/specId?sid=' + clientId}, function(err, response, body) {
+
+ it('only routes to one server', function (done) {
+ var clientId = amino.utils.idgen(), numRequests = 100, started = 0, completed = 0, specId;
+ process.nextTick(function nextRequest () {
+ amino.request('http://localhost:20523/?sid=' + clientId, function (err, res, body) {
assert.ifError(err);
- assert.strictEqual(response.statusCode, 200, 'status is 200');
+ assert.strictEqual(res.statusCode, 200, 'status is 200');
assert.ok(specIds.indexOf(body) !== -1, 'spec known');
if (specId) {
assert.strictEqual(body, specId, 'routed to only one spec');
View
4 test/test.conf
@@ -1,4 +0,0 @@
-{
- "port": 50234,
- "service": "argyle"
-}
View
104 test/test.js
@@ -1,104 +0,0 @@
-var assert = require('assert')
- , amino = require('amino')
- , child_process = require('child_process')
- , stream = require('stream')
- , util = require('util')
- ;
-
-function ValidationStream(str, cb) {
- this.str = str
- this.buf = ''
- this.on('data', function (data) {
- this.buf += data
- })
- this.on('end', function () {
- assert.strictEqual(this.str, this.buf)
- if (cb) cb();
- })
- this.writable = true
-}
-util.inherits(ValidationStream, stream.Stream)
-ValidationStream.prototype.write = function (chunk) {
- this.emit('data', chunk)
-}
-ValidationStream.prototype.end = function (chunk) {
- if (chunk) emit('data', chunk)
- this.emit('end')
-}
-
-describe('conf', function() {
- var gateway;
- it('supports alternate conf file', function(done) {
- process.on('exit', function() {
- if (gateway) {
- gateway.kill();
- }
- });
- gateway = child_process.execFile('./bin/gateway.js', ['--conf', 'test/test.conf']);
- gateway.stdout.once('data', function(chunk) {
- assert.ok(chunk.toString().match(/^argyle gateway listening .*on port 50234\.\.\.\n$/), 'settings overridden');
- done();
- });
- });
-});
-
-describe('simple proxy', function() {
- var gateway;
- before(function(done) {
- process.on('exit', function() {
- if (gateway) {
- gateway.kill();
- }
- });
- amino.respond('app', function(router) {
- router.get('/robots.txt', function() {
- this.res.text("User-agent: *\nDisallow: /");
- });
- router.post('/post', {stream: true}, function() {
- var chunks = [], req = this.req, res = this.res;
- req.on('data', function(chunk) {
- chunks.push(chunk);
- });
- req.on('end', function() {
- assert.strictEqual(chunks.length, 4, 'correct number of chunks received');
- assert.strictEqual(chunks.join(''), 'abcd', 'request body OK');
- ['e', 'f', 'g', 'h'].forEach(function(chunk) {
- res.write(chunk);
- });
- res.end();
- });
- });
- });
- gateway = child_process.execFile('./bin/gateway.js', ['-p', '55201', '-s', 'app']);
- gateway.stdout.once('data', function(chunk) {
- assert.ok(chunk.toString().match(/^app gateway listening .*on port 55201\.\.\.\n$/), 'settings overridden');
- done();
- });
- });
- it('should proxy basic request', function(done) {
- setTimeout(function() {
- amino.request('http://localhost:55201/robots.txt', function(err, response, body) {
- assert.ifError(err);
- assert.strictEqual(response.statusCode, 200, 'status is 200');
- assert.strictEqual(body, "User-agent: *\nDisallow: /", 'text is OK');
- done();
- });
- }, 500);
- });
- it('should stream a request', function(done) {
- var inputStream = new ValidationStream('abcd');
- var outputStream = new ValidationStream('efgh', done);
-
- var options = {
- method: 'POST',
- url: 'http://localhost:55201/post'
- };
-
- inputStream.pipe(amino.request(options)).pipe(outputStream);
-
- for (var i = 0, len = inputStream.str.length; i < len; i++) {
- inputStream.write(inputStream.str[i]);
- }
- inputStream.end();
- });
-});
Please sign in to comment.
Something went wrong with that request. Please try again.