Breaks the mocha test into individual files so tests can be run independently. Uses Promises in the mocha test. Adds a listen method to all servers

This commit is contained in:
Zachary Boyd 2018-09-09 15:42:34 -04:00
parent 1c29679024
commit b65fa27ba8
21 changed files with 208 additions and 109 deletions

View file

@ -5,4 +5,5 @@ npm-debug.log
docker-compose.yml docker-compose.yml
.env .env
README.md README.md
Jenkinsfile .vscode
.DS_Store

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
node_modules node_modules
npm-debug.log npm-debug.log
.env .env
.vscode
.DS_Store

View file

@ -1,4 +1,5 @@
node_modules node_modules
npm-debug.log npm-debug.log
.env .env
Jenkinsfile .vscode
.DS_Store

View file

@ -2,10 +2,14 @@
## [4.0.0] - 2018-09-09 ## [4.0.0] - 2018-09-09
### Added
- All servers (DNS, HTTP, SOCKS and Control) all have a `listen` method which returns a Promise that will resolve when the server is listening.
### Changes ### Changes
- All methods now return promises instead of accepting callbacks. Methods now take advantage of async/await to increase readability. - All methods now return promises instead of accepting callbacks. Methods now take advantage of async/await to increase readability.
- The `logger` argument to the constructor's of all classes is now optional - The `logger` argument to the constructor's of all classes is now optional
- The mocha test has been split into individual files - The `Config` property of instance definitions will now inherit from `TorPool.default_tor_config`.
- The mocha test has been split into individual files all under `test/`
### Removes ### Removes
- The `new_ips` and `new_ip_at` TorPool and `new_ip` TorProcess have been removed. Use `new_identites`, `new_identity_at` and `new_identity` instead. - The `new_ips` and `new_ip_at` TorPool and `new_ip` TorProcess have been removed. Use `new_identites`, `new_identity_at` and `new_identity` instead.

View file

@ -41,7 +41,7 @@ For example: `tor-router -j 3 -s 9050` would start the proxy with 3 tor instance
## Testing ## Testing
Tests are written in mocha and can be found under `test/test.js` and can be run with `npm test` Tests are written in mocha and can be found under `test/` and can be run with `npm test`
## Configuration ## Configuration

View file

@ -11,7 +11,7 @@
}, },
"scripts": { "scripts": {
"start": "bin/tor-router -s -d -j 1", "start": "bin/tor-router -s -d -j 1",
"test": "mocha --exit test/index.js", "test": "mocha --exit test",
"debug": "node --inspect-brk bin/tor-router" "debug": "node --inspect-brk bin/tor-router"
}, },
"devDependencies": { "devDependencies": {

View file

@ -6,7 +6,7 @@ const rpc = require('jrpc2');
class ControlServer { class ControlServer {
constructor(logger, nconf) { constructor(logger, nconf) {
this.torPool = new TorPool(nconf.get('torPath'), null, nconf.get('parentDataDirectory'), nconf.get('loadBalanceMethod'), nconf.get('granaxOptions'),logger); this.torPool = new TorPool(nconf.get('torPath'), (() => nconf.get('torConfig')), nconf.get('parentDataDirectory'), nconf.get('loadBalanceMethod'), nconf.get('granaxOptions'), logger);
this.logger = logger || require('./winston-silent-logger'); this.logger = logger || require('./winston-silent-logger');
this.nconf = nconf; this.nconf = nconf;
@ -22,9 +22,9 @@ class ControlServer {
server.expose('queryInstances', (async () => { server.expose('queryInstances', (async () => {
if (!this.torPool) if (!this.torPool)
throw new Error('No instances created'); throw new Error('No Tor Pool created');
this.torPool.instances.map(instance_info); return this.torPool.instances.map(instance_info);
}).bind(this)); }).bind(this));
server.expose('queryInstanceByName', (async (instance_name) => { server.expose('queryInstanceByName', (async (instance_name) => {
@ -36,7 +36,7 @@ class ControlServer {
if (!instance) if (!instance)
throw new Error(`Instance "${instance_name}"" does not exist`); throw new Error(`Instance "${instance_name}"" does not exist`);
return instance_info(i) return instance_info(instance);
}).bind(this)); }).bind(this));
server.expose('queryInstanceAt', (async (index) => { server.expose('queryInstanceAt', (async (index) => {
@ -51,9 +51,17 @@ class ControlServer {
return instance_info(this.torPool.instance_at(index)); return instance_info(this.torPool.instance_at(index));
}).bind(this)); }).bind(this));
server.expose('createInstances', this.torPool.create.bind(this.torPool)); server.expose('createInstances', (async (num) => {
let instances = await this.torPool.create(num);
server.expose('addInstances', this.torPool.add.bind(this.torPool)); return instances.map(instance_info);
}).bind(this));
server.expose('addInstances', (async (defs) => {
let instances = await this.torPool.create(defs);
return instances.map(instance_info);
}).bind(this));
server.expose('removeInstances', this.torPool.remove.bind(this.torPool)); server.expose('removeInstances', this.torPool.remove.bind(this.torPool));
@ -67,7 +75,7 @@ class ControlServer {
server.expose('newIdentityByName', this.torPool.new_identity_by_name.bind(this.torPool)); server.expose('newIdentityByName', this.torPool.new_identity_by_name.bind(this.torPool));
server.expose('nextInstance', (async () => this.torPool.next()).bind(this)); server.expose('nextInstance', (async () => instance_info( await this.torPool.next() )).bind(this));
server.expose('closeInstances', (async () => this.torPool.exit()).bind(this)); server.expose('closeInstances', (async () => this.torPool.exit()).bind(this));
@ -81,7 +89,7 @@ class ControlServer {
server.expose('setTorConfig', (async (config) => { server.expose('setTorConfig', (async (config) => {
await Promise.all(Object.keys(config).map((key) => { await Promise.all(Object.keys(config).map((key) => {
var value = config[key]; let value = config[key];
return this.torPool.set_config_all(key, value); return this.torPool.set_config_all(key, value);
})); }));
@ -124,23 +132,23 @@ class ControlServer {
return this.torPool; return this.torPool;
} }
createSOCKSServer(port) { async createSOCKSServer(port) {
this.socksServer = new SOCKSServer(this.torPool, this.logger); this.socksServer = new SOCKSServer(this.torPool, this.logger);
this.socksServer.listen(port || 9050); await this.socksServer.listen(port || 9050);
this.logger.info(`[socks]: Listening on ${port}`); this.logger.info(`[socks]: Listening on ${port}`);
this.socksServer; this.socksServer;
} }
createHTTPServer(port) { async createHTTPServer(port) {
this.httpServer = new HTTPServer(this.torPool, this.logger); this.httpServer = new HTTPServer(this.torPool, this.logger);
this.httpServer.listen(port || 9080); await this.httpServer.listen(port || 9080);
this.logger.info(`[http]: Listening on ${port}`); this.logger.info(`[http]: Listening on ${port}`);
this.httpServer; this.httpServer;
} }
createDNSServer(port) { async createDNSServer(port) {
this.dnsServer = new DNSServer(this.torPool, this.nconf.get('dns:options'), this.nconf.get('dns:timeout'), this.logger); this.dnsServer = new DNSServer(this.torPool, this.nconf.get('dns:options'), this.nconf.get('dns:timeout'), this.logger);
this.dnsServer.serve(port || 9053); await this.dnsServer.serve(port || 9053);
this.logger.info(`[dns]: Listening on ${port}`); this.logger.info(`[dns]: Listening on ${port}`);
this.dnsServer; this.dnsServer;
} }

View file

@ -1,7 +1,29 @@
const dns = require('native-dns'); const dns = require('native-dns');
const { UDPServer } = require('native-dns'); const { UDPServer } = require('native-dns');
const Promise = require('bluebird');
class DNSServer extends UDPServer { class DNSServer extends UDPServer {
async listen() {
let args = Array.from(arguments);
let inner_func = super.serve;
if (!args[1])
args[1] = null;
return await new Promise((resolve, reject) => {
args.push(() => {
let args = Array.from(arguments);
resolve.apply(args);
});
try {
inner_func.apply(this, args);
} catch (error) {
reject(error);
}
});
}
constructor(tor_pool, dns_options, dns_timeout, logger) { constructor(tor_pool, dns_options, dns_timeout, logger) {
super(dns_options); super(dns_options);
this.logger = logger || require('./winston-silent-logger'); this.logger = logger || require('./winston-silent-logger');

View file

@ -2,12 +2,25 @@ const http = require('http');
const URL = require('url'); const URL = require('url');
const { Server } = http; const { Server } = http;
const Promise = require('bluebird');
const socks = require('socksv5'); const socks = require('socksv5');
const SocksProxyAgent = require('socks-proxy-agent'); const SocksProxyAgent = require('socks-proxy-agent');
const TOR_ROUTER_PROXY_AGENT = 'tor-router'; const TOR_ROUTER_PROXY_AGENT = 'tor-router';
class HTTPServer extends Server { class HTTPServer extends Server {
async listen() {
return await new Promise((resolve, reject) => {
let args = Array.from(arguments);
let inner_func = super.listen;
args.push(() => {
let args = Array.from(arguments);
resolve.apply(args);
});
inner_func.apply(this, args);
});
}
constructor(tor_pool, logger) { constructor(tor_pool, logger) {
let handle_http_connections = (req, res) => { let handle_http_connections = (req, res) => {
let url = URL.parse(req.url); let url = URL.parse(req.url);
@ -26,12 +39,12 @@ class HTTPServer extends Server {
req.on('data', onIncomingData); req.on('data', onIncomingData);
req.on('end', preConnectClosed); req.on('end', preConnectClosed);
req.on('error', function (err) { req.on('error', function (err) {
logger.error("[http-proxy]: an error occured: "+err.message); this.logger.error("[http-proxy]: an error occured: "+err.message);
}); });
let connect = (tor_instance) => { let connect = (tor_instance) => {
let socks_port = tor_instance.socks_port; let socks_port = tor_instance.socks_port;
logger.verbose(`[http-proxy]: ${req.connection.remoteAddress}:${req.connection.remotePort} → 127.0.0.1:${socks_port}${url.hostname}:${url.port}`); this.logger.verbose(`[http-proxy]: ${req.connection.remoteAddress}:${req.connection.remotePort} → 127.0.0.1:${socks_port}${url.hostname}:${url.port}`);
let proxy_req = http.request({ let proxy_req = http.request({
method: req.method, method: req.method,
@ -74,7 +87,7 @@ class HTTPServer extends Server {
if (tor_pool.instances.length) { if (tor_pool.instances.length) {
connect(tor_pool.next()); connect(tor_pool.next());
} else { } else {
logger.debug(`[http-proxy]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`); this.logger.debug(`[http-proxy]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
tor_pool.once('instance_created', connect); tor_pool.once('instance_created', connect);
} }
}; };
@ -85,7 +98,7 @@ class HTTPServer extends Server {
let connect = (tor_instance) => { let connect = (tor_instance) => {
let socks_port = tor_instance.socks_port; let socks_port = tor_instance.socks_port;
logger && logger.verbose(`[http-connect]: ${req.connection.remoteAddress}:${req.connection.remotePort} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' }${hostname}:${port}`) this.logger && this.logger.verbose(`[http-connect]: ${req.connection.remoteAddress}:${req.connection.remotePort} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' }${hostname}:${port}`)
var outbound_socket; var outbound_socket;
let onClose = (error) => { let onClose = (error) => {
@ -125,7 +138,7 @@ class HTTPServer extends Server {
if (tor_pool.instances.length) { if (tor_pool.instances.length) {
connect(tor_pool.next()); connect(tor_pool.next());
} else { } else {
logger.debug(`[http-connect]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`); this.logger.debug(`[http-connect]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
tor_pool.once('instance_created', connect); tor_pool.once('instance_created', connect);
} }
}; };

View file

@ -1,7 +1,20 @@
const socks = require('socksv5'); const socks = require('socksv5');
const Promise = require('bluebird');
const { Server } = socks; const { Server } = socks;
class SOCKSServer extends Server{ class SOCKSServer extends Server{
async listen() {
return await new Promise((resolve, reject) => {
let args = Array.from(arguments);
let inner_func = super.listen;
args.push(() => {
let args = Array.from(arguments);
resolve.apply(args);
});
inner_func.apply(this, args);
});
}
constructor(tor_pool, logger) { constructor(tor_pool, logger) {
let handleConnection = (info, accept, deny) => { let handleConnection = (info, accept, deny) => {
let inbound_socket = accept(true); let inbound_socket = accept(true);
@ -28,7 +41,7 @@ class SOCKSServer extends Server{
let connect = (tor_instance) => { let connect = (tor_instance) => {
let socks_port = tor_instance.socks_port; let socks_port = tor_instance.socks_port;
logger.verbose(`[socks]: ${info.srcAddr}:${info.srcPort} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' }${info.dstAddr}:${info.dstPort}`) this.logger.verbose(`[socks]: ${info.srcAddr}:${info.srcPort} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' }${info.dstAddr}:${info.dstPort}`)
socks.connect({ socks.connect({
host: info.dstAddr, host: info.dstAddr,
@ -60,7 +73,7 @@ class SOCKSServer extends Server{
if (tor_pool.instances.length) { if (tor_pool.instances.length) {
connect(tor_pool.next()); connect(tor_pool.next());
} else { } else {
logger.debug(`[socks]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`); this.logger.debug(`[socks]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
tor_pool.once('instance_created', connect); tor_pool.once('instance_created', connect);
} }
}; };

View file

@ -29,20 +29,33 @@ const WeightedList = require('js-weighted-list');
const TorProcess = require('./TorProcess'); const TorProcess = require('./TorProcess');
Promise.promisifyAll(fs);
class TorPool extends EventEmitter { class TorPool extends EventEmitter {
constructor(tor_path, default_config, data_directory, load_balance_method, granax_options, logger) { constructor(tor_path, default_config, data_directory, load_balance_method, granax_options, logger) {
if (!data_directory)
throw new Error('Invalid "data_directory"');
super(); super();
this._instances = []; this._instances = [];
default_config = _.extend({}, (default_config || {})); this._default_tor_config = default_config;
this.default_tor_config = default_config;
this.data_directory = data_directory; this.data_directory = data_directory;
this.load_balance_method = load_balance_method; this.load_balance_method = load_balance_method;
!fs.existsSync(this.data_directory) && fs.mkdirSync(this.data_directory);
this.tor_path = tor_path; this.tor_path = tor_path;
this.logger = logger || require('./winston-silent-logger'); this.logger = logger || require('./winston-silent-logger');
this.granax_options = granax_options; this.granax_options = granax_options;
} }
get default_tor_config() {
if (typeof(this._default_tor_config) === 'function')
return this._default_tor_config();
else if (this._default_tor_config)
return _.cloneDeep(this._default_tor_config);
else
return {};
}
set default_tor_config(value) { this._default_tor_config = value; }
static get load_balance_methods() { static get load_balance_methods() {
return { return {
round_robin: function (instances) { round_robin: function (instances) {
@ -68,9 +81,11 @@ class TorPool extends EventEmitter {
} }
async create_instance(instance_definition) { async create_instance(instance_definition) {
if (!(fs.existsSync(this.data_directory)))
await fs.mkdirAsync(this.data_directory);
this._instances._weighted_list = void(0); this._instances._weighted_list = void(0);
if (!instance_definition.Config) instance_definition.Config = _.extend(_.cloneDeep(this.default_tor_config), (instance_definition.Config || {}));
instance_definition.Config = _.extend({}, this.default_tor_config);
let instance_id = nanoid(); let instance_id = nanoid();
instance_definition.Config.DataDirectory = instance_definition.Config.DataDirectory || path.join(this.data_directory, (instance_definition.Name || instance_id)); instance_definition.Config.DataDirectory = instance_definition.Config.DataDirectory || path.join(this.data_directory, (instance_definition.Name || instance_id));
@ -138,7 +153,7 @@ class TorPool extends EventEmitter {
} }
next() { next() {
this._instances = load_balance_methods[this.load_balance_method](this._instances); this._instances = TorPool.load_balance_methods[this.load_balance_method](this._instances);
return this.instances[0]; return this.instances[0];
} }
@ -148,7 +163,7 @@ class TorPool extends EventEmitter {
} }
async new_identites() { async new_identites() {
await Promise.all(this.instances.map((instance) => instance.new_identity(next))); await Promise.all(this.instances.map((instance) => instance.new_identity()));
} }
async new_identity_at(index) { async new_identity_at(index) {
@ -201,7 +216,7 @@ class TorPool extends EventEmitter {
} }
async signal_all(signal) { async signal_all(signal) {
await Promise.all(this.instances.map((instance) => instance.signal(signal, next))); await Promise.all(this.instances.map((instance) => instance.signal(signal)));
} }
async signal_by_name(name, signal) { async signal_by_name(name, signal) {

View file

@ -112,7 +112,7 @@ class TorProcess extends EventEmitter {
let configFile = await temp.openAsync('tor-router'); let configFile = await temp.openAsync('tor-router');
let configPath = configFile.path; let configPath = configFile.path;
fs.writeFileAsync(configPath, text); await fs.writeFileAsync(configPath, text);
let tor = spawn(this.tor_path, ['-f', configPath], { let tor = spawn(this.tor_path, ['-f', configPath], {
stdio: ['ignore', 'pipe', 'pipe'], stdio: ['ignore', 'pipe', 'pipe'],

View file

@ -24,6 +24,7 @@ async function main(nconf, logger) {
let control = new ControlServer(logger, nconf); let control = new ControlServer(logger, nconf);
try { try {
await control.listen(control_port);
if (socks_port) { if (socks_port) {
if (typeof(socks_port) === 'boolean') { if (typeof(socks_port) === 'boolean') {
@ -50,13 +51,11 @@ async function main(nconf, logger) {
} }
if (instances) { if (instances) {
logger.info(`[tor]: starting ${Array.isArray(instances) ? instances.length : instances} tor instances...`) logger.info(`[tor]: starting ${Array.isArray(instances) ? instances.length : instances} tor instance(s)...`)
await control.torPool.create(instances); await control.torPool.create(instances);
logger.info('[tor]: tor started'); logger.info('[tor]: tor started');
} }
await control.listen(control_port);
logger.info(`[control]: control Server listening on ${control_port}`); logger.info(`[control]: control Server listening on ${control_port}`);
} catch (error) { } catch (error) {
logger.error(`[global]: error starting application: ${error.stack}`); logger.error(`[global]: error starting application: ${error.stack}`);
@ -160,6 +159,8 @@ nconf
let nconf_config = nconf.get('config'); let nconf_config = nconf.get('config');
if (nconf_config) { if (nconf_config) {
nconf.file(nconf_config); nconf.file(nconf_config);
} else {
nconf.use('memory');
} }
nconf.defaults(require(`${__dirname}/../src/default_config.js`)); nconf.defaults(require(`${__dirname}/../src/default_config.js`));

View file

@ -1,18 +1,17 @@
const _ = require('lodash'); const _ = require('lodash');
const assert = require('chai').assert; const assert = require('chai').assert;
const rpc = require('jrpc2');
const Promise = require('bluebird');
const nconf = require('nconf'); const nconf = require('nconf');
const getPort = require('get-port');
const logger = require('../src/winston-silent-logger'); nconf.use('memory');
require(`${__dirname}/../src/nconf_load_env.js`)(nconf); require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
nconf.defaults(require(`${__dirname}/../src/default_config.js`)); nconf.defaults(require(`${__dirname}/../src/default_config.js`));
const { ControlServer } = require('../'); const { ControlServer, TorPool, HTTPServer, SOCKSServer, DNSServer } = require('../');
let controlServer = new ControlServer(logger, nconf); let controlServer = new ControlServer(null, nconf);
let controlPort; let controlPort;
describe('ControlServer', function () { describe('ControlServer', function () {
describe('#listen(port)', function () { describe('#listen(port)', function () {
it('should bind to a given port', async function () { it('should bind to a given port', async function () {
@ -24,7 +23,7 @@ describe('ControlServer', function () {
it('should create a TorPool with a given configuration', function () { it('should create a TorPool with a given configuration', function () {
let torPool = controlServer.createTorPool({ ProtocolWarnings: 1 }); let torPool = controlServer.createTorPool({ ProtocolWarnings: 1 });
assert.ok((controlServer.torPool instanceof (TorRouter.TorPool))); assert.instanceOf(controlServer.torPool, TorPool);
assert.equal(1, torPool.default_tor_config.ProtocolWarnings); assert.equal(1, torPool.default_tor_config.ProtocolWarnings);
}); });
}); });
@ -60,5 +59,3 @@ describe('ControlServer', function () {
await controlServer.torPool.exit(); await controlServer.torPool.exit();
}); });
}); });
require('./RPCServer');

View file

@ -3,15 +3,18 @@ const nconf = require('nconf');
const getPort = require('get-port'); const getPort = require('get-port');
const dns = require('native-dns'); const dns = require('native-dns');
const logger = require('../src/winston-silent-logger');
const { TorPool, DNSServer } = require('../'); const { TorPool, DNSServer } = require('../');
const { WAIT_FOR_CREATE } = require('./constants'); const { WAIT_FOR_CREATE } = require('./constants');
nconf.use('memory');
require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
nconf.defaults(require(`${__dirname}/../src/default_config.js`));
let dnsServerTorPool; let dnsServerTorPool;
let dnsServer; let dnsServer;
describe('DNSServer', function () { describe('DNSServer', function () {
dnsServerTorPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null, logger); dnsServerTorPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null);
dnsServer = new DNSServer(dnsServerTorPool, {}, 10000, logger); dnsServer = new DNSServer(dnsServerTorPool, {}, 10000);
let dnsPort; let dnsPort;
before('start up server', async function (){ before('start up server', async function (){
this.timeout(WAIT_FOR_CREATE); this.timeout(WAIT_FOR_CREATE);
@ -19,7 +22,7 @@ describe('DNSServer', function () {
await dnsServerTorPool.create(1); await dnsServerTorPool.create(1);
dnsPort = await getPort(); dnsPort = await getPort();
dnsServer.serve(dnsPort); await dnsServer.listen(dnsPort);
}); });
describe('#handle_dns_request(req, res)', function () { describe('#handle_dns_request(req, res)', function () {
@ -32,7 +35,7 @@ describe('DNSServer', function () {
type: 'A' type: 'A'
}), }),
server: { address: '127.0.0.1', port: dnsPort, type: 'udp' }, server: { address: '127.0.0.1', port: dnsPort, type: 'udp' },
timeout: 1000, timeout: 10000,
}); });
req.on('timeout', function () { req.on('timeout', function () {

View file

@ -3,18 +3,18 @@ const request = require('request-promise');
const getPort = require('get-port'); const getPort = require('get-port');
const ProxyAgent = require('proxy-agent'); const ProxyAgent = require('proxy-agent');
const logger = require('../src/winston-silent-logger');
const { TorPool, HTTPServer } = require('../'); const { TorPool, HTTPServer } = require('../');
const { WAIT_FOR_CREATE, PAGE_LOAD_TIME } = require('./constants'); const { WAIT_FOR_CREATE, PAGE_LOAD_TIME } = require('./constants');
nconf.use('memory');
require(`${__dirname}/../src/nconf_load_env.js`)(nconf); require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
nconf.defaults(require(`${__dirname}/../src/default_config.js`)); nconf.defaults(require(`${__dirname}/../src/default_config.js`));
let httpServerTorPool; let httpServerTorPool;
let httpServer; let httpServer;
describe('HTTPServer', function () { describe('HTTPServer', function () {
httpServerTorPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null, logger); httpServerTorPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null);
httpServer = new HTTPServer(httpServerTorPool, logger); httpServer = new HTTPServer(httpServerTorPool);
let httpPort; let httpPort;
before('start up server', async function (){ before('start up server', async function (){
this.timeout(WAIT_FOR_CREATE); this.timeout(WAIT_FOR_CREATE);
@ -22,7 +22,7 @@ describe('HTTPServer', function () {
await httpServerTorPool.create(1); await httpServerTorPool.create(1);
httpPort = await getPort(); httpPort = await getPort();
httpServer.listen(httpPort); await httpServer.listen(httpPort);
}); });
describe('#handle_http_connections(req, res)', function () { describe('#handle_http_connections(req, res)', function () {
@ -31,7 +31,7 @@ describe('HTTPServer', function () {
await request({ await request({
url: 'http://example.com', url: 'http://example.com',
agent: new ProxyAgent(`http://localhost:${httpPort}`) agent: new ProxyAgent(`http://127.0.0.1:${httpPort}`)
}); });
}); });
}); });
@ -42,7 +42,7 @@ describe('HTTPServer', function () {
await request({ await request({
url: 'https://example.com', url: 'https://example.com',
agent: new ProxyAgent(`http://localhost:${httpPort}`) agent: new ProxyAgent(`http://127.0.0.1:${httpPort}`)
}); });
}); });
}); });

View file

@ -1,21 +1,21 @@
const _ = require('lodash'); const _ = require('lodash');
const assert = require('chai').assert; const assert = require('chai').assert;
const rpc = require('jrpc2');
const Promise = require('bluebird'); const Promise = require('bluebird');
const nconf = require('nconf'); const nconf = require('nconf');
const rpc = require('jrpc2'); const rpc = require('jrpc2');
const getPort = require('get-port');
const logger = require('../src/winston-silent-logger'); nconf.use('memory');
require(`${__dirname}/../src/nconf_load_env.js`)(nconf); require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
nconf.defaults(require(`${__dirname}/../src/default_config.js`)); nconf.defaults(require(`${__dirname}/../src/default_config.js`));
const { ControlServer } = require('../'); const { ControlServer } = require('../');
const { WAIT_FOR_CREATE } = require('./constants');
let rpcControlServer = new ControlServer(logger, nconf); let rpcControlServer = new ControlServer(null, nconf);
let rpcControlPort; let rpcControlPort;
let rpcClient; let rpcClient;
describe('ControlServer - RPC', function () { describe('ControlServer - RPC Interface', function () {
before('setup control server', async function () { before('setup control server', async function () {
rpcControlPort = await getPort(); rpcControlPort = await getPort();
await rpcControlServer.listen(rpcControlPort); await rpcControlServer.listen(rpcControlPort);
@ -35,11 +35,11 @@ describe('ControlServer - RPC', function () {
it('should return a list of instances', async function () { it('should return a list of instances', async function () {
let raw = await rpcClient.invokeAsync('queryInstances', []); let raw = await rpcClient.invokeAsync('queryInstances', []);
var instances = JSON.parse(raw).result; let instances = JSON.parse(raw).result;
assert.isArray(instances, 'Did not return an array'); assert.isArray(instances, 'Did not return an array');
assert.isNotEmpty(instances); assert.isNotEmpty(instances, 'Returned an empty array');
assert.isTrue(instances.every((instance) => ( typeof(instance.name) !== 'undefined' ) && ( instance.name !== null ))); assert.isTrue(instances.every((instance) => ( typeof(instance.name) !== 'undefined' ) && ( instance.name !== null )), 'Objects were not valid');
}); });
}); });
@ -62,7 +62,7 @@ describe('ControlServer - RPC', function () {
it('should return a single instance by name', async function () { it('should return a single instance by name', async function () {
let raw = await rpcClient.invokeAsync('queryInstanceByName', ['instance-1']); let raw = await rpcClient.invokeAsync('queryInstanceByName', ['instance-1']);
var instance = JSON.parse(raw).result; let instance = JSON.parse(raw).result;
assert.isOk(instance); assert.isOk(instance);
}); });
@ -73,7 +73,7 @@ describe('ControlServer - RPC', function () {
it('should return a single instance by index', async function () { it('should return a single instance by index', async function () {
let raw = await rpcClient.invokeAsync('queryInstanceAt', [0]); let raw = await rpcClient.invokeAsync('queryInstanceAt', [0]);
var instance = JSON.parse(raw).result; let instance = JSON.parse(raw).result;
assert.isOk(instance); assert.isOk(instance);
}); });
@ -132,9 +132,10 @@ describe('ControlServer - RPC', function () {
this.timeout(WAIT_FOR_CREATE); this.timeout(WAIT_FOR_CREATE);
await rpcControlServer.torPool.create_instance({ Name: 'config-test' }); await rpcControlServer.torPool.create_instance({ Name: 'config-test' });
let value = await rpcControlServer.torPool.instance_by_name('config-test').get_config('TestSocks'); let values = await rpcControlServer.torPool.instance_by_name('config-test').get_config('TestSocks');
assert.equal(value, 1); assert.isNotEmpty(values);
assert.equal(values[0], "1");
}); });
after('remove instance', async function () { after('remove instance', async function () {
@ -150,8 +151,8 @@ describe('ControlServer - RPC', function () {
}); });
it('should return a tor config with a modified property', async function () { it('should return a tor config with a modified property', async function () {
this.timeout(3000); this.timeout(6000);
let raw = await rpcClient.invokeAsync('getDefaultTorConfig', [ { } ]); let raw = await rpcClient.invokeAsync('getDefaultTorConfig', [ ]);
let config = JSON.parse(raw).result; let config = JSON.parse(raw).result;
assert.equal(config.TestSocks, 1); assert.equal(config.TestSocks, 1);
@ -198,11 +199,12 @@ describe('ControlServer - RPC', function () {
await rpcControlServer.torPool.instance_by_name('instance-1').set_config('TestSocks', 1); await rpcControlServer.torPool.instance_by_name('instance-1').set_config('TestSocks', 1);
}); });
it('should retrieve the property from the tor instance', async function (done) { it('should retrieve the property from the tor instance', async function () {
let raw = await rpcClient.invokeAsync('getInstanceConfigByName', ['instance-1']); let raw = await rpcClient.invokeAsync('getInstanceConfigByName', ['instance-1', "TestSocks"]);
let value = JSON.parse(raw).result; let values = JSON.parse(raw).result;
assert.equal(value, 1); assert.isNotEmpty(values);
assert.equal(values[0], "1");
}); });
after('unset config property', async function () { after('unset config property', async function () {
@ -218,10 +220,11 @@ describe('ControlServer - RPC', function () {
}); });
it('should retrieve the property from the tor instance', async function () { it('should retrieve the property from the tor instance', async function () {
let raw = await rpcClient.invokeAsync('getInstanceConfigByName', [0]); let raw = await rpcClient.invokeAsync('getInstanceConfigAt', [0, "TestSocks"]);
let value = JSON.parse(raw).result; let values = JSON.parse(raw).result;
assert.equal(value, 1); assert.isNotEmpty(values);
assert.equal(values[0], "1");
}); });
after('unset config property', async function () { after('unset config property', async function () {

View file

@ -3,18 +3,18 @@ const request = require('request-promise');
const getPort = require('get-port'); const getPort = require('get-port');
const ProxyAgent = require('proxy-agent'); const ProxyAgent = require('proxy-agent');
const logger = require('../src/winston-silent-logger');
const { TorPool, SOCKSServer } = require('../'); const { TorPool, SOCKSServer } = require('../');
const { WAIT_FOR_CREATE, PAGE_LOAD_TIME } = require('./constants'); const { WAIT_FOR_CREATE, PAGE_LOAD_TIME } = require('./constants');
nconf.use('memory');
require(`${__dirname}/../src/nconf_load_env.js`)(nconf); require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
nconf.defaults(require(`${__dirname}/../src/default_config.js`)); nconf.defaults(require(`${__dirname}/../src/default_config.js`));
let socksServerTorPool; let socksServerTorPool;
let socksServer; let socksServer;
describe('SOCKSServer', function () { describe('SOCKSServer', function () {
socksServerTorPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null, logger); socksServerTorPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null);
socksServer = new SOCKSServer(socksServerTorPool, logger); socksServer = new SOCKSServer(socksServerTorPool);
let socksPort; let socksPort;
before('start up server', async function (){ before('start up server', async function (){
this.timeout(WAIT_FOR_CREATE); this.timeout(WAIT_FOR_CREATE);
@ -22,7 +22,7 @@ describe('SOCKSServer', function () {
await socksServerTorPool.create(1); await socksServerTorPool.create(1);
socksPort = await getPort(); socksPort = await getPort();
socksServer.listen(socksPort); await socksServer.listen(socksPort);
}); });
describe('#handleConnection(socket)', function () { describe('#handleConnection(socket)', function () {
@ -31,7 +31,7 @@ describe('SOCKSServer', function () {
await request({ await request({
url: 'http://example.com', url: 'http://example.com',
agent: new ProxyAgent(`socks://localhost:${socksPort}`) agent: new ProxyAgent(`socks://127.0.0.1:${socksPort}`)
}); });
}); });
}); });

View file

@ -5,14 +5,13 @@ const _ = require('lodash');
const { TorPool } = require('../'); const { TorPool } = require('../');
const { WAIT_FOR_CREATE } = require('./constants'); const { WAIT_FOR_CREATE } = require('./constants');
const logger = require('../src/winston-silent-logger');
nconf.use('memory');
require(`${__dirname}/../src/nconf_load_env.js`)(nconf); require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
nconf.defaults(require(`${__dirname}/../src/default_config.js`)); nconf.defaults(require(`${__dirname}/../src/default_config.js`));
let torPool;
describe('TorPool', function () { describe('TorPool', function () {
torPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null, logger); const torPoolFactory = () => new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null);
describe('#create_instance(instance_defintion)', function () { describe('#create_instance(instance_defintion)', function () {
let instance_defintion = { let instance_defintion = {
@ -22,6 +21,9 @@ describe('TorPool', function () {
} }
}; };
let torPool;
before('create tor pool', () => { torPool = torPoolFactory(); })
it('should create one tor instance based on the provided definition', async function () { it('should create one tor instance based on the provided definition', async function () {
this.timeout(WAIT_FOR_CREATE); this.timeout(WAIT_FOR_CREATE);
await torPool.create_instance(instance_defintion); await torPool.create_instance(instance_defintion);
@ -51,9 +53,12 @@ describe('TorPool', function () {
{ Name: 'instance-2', Config: { ProtocolWarnings: 1 } } { Name: 'instance-2', Config: { ProtocolWarnings: 1 } }
]; ];
let torPool;
before('create tor pool', () => { torPool = torPoolFactory(); })
it('should create instances from several instance definitions', async function () { it('should create instances from several instance definitions', async function () {
this.timeout(WAIT_FOR_CREATE*2); this.timeout(WAIT_FOR_CREATE*2);
await torPool.add(instance_defintions) await torPool.add(_.cloneDeep(instance_defintions))
}); });
it('2 instances should exist in the pool', function () { it('2 instances should exist in the pool', function () {
@ -61,19 +66,21 @@ describe('TorPool', function () {
}); });
it('the created instances should have the same defintion properties as the input definitions', function () { it('the created instances should have the same defintion properties as the input definitions', function () {
assert.deepEqual(instance_defintions, torPool.instances.map((instance) => { let live_instance_definitions = torPool.instances.map((instance) => {
let def_clone = _.cloneDeep(instance.definition); let def_clone = _.cloneDeep(instance.definition);
delete def_clone.Config.DataDirectory; delete def_clone.Config.DataDirectory;
return def_clone; return def_clone;
}).sort(function(a,b) {return (a.Name > b.Name) ? 1 : ((b.Name > a.Name) ? -1 : 0);})); }).sort((a,b) => (a.Name > b.Name) ? 1 : ((b.Name > a.Name) ? -1 : 0));
assert.deepEqual(instance_defintions, live_instance_definitions);
}); });
it('the created instances should have the same config properties specified in the definiton', async function (done) { it('the created instances should have the same config properties specified in the definiton', async function () {
this.timeout(10000); this.timeout(10000);
let values = await Promise.all(torPool.instances.map((instance) => instance.get_config('ProtocolWarnings'))); let values = await Promise.all(torPool.instances.map((instance) => instance.get_config('ProtocolWarnings')));
values = _.flatten(values);
assert.isTrue( values.every((value) => value === 1) ); assert.isTrue( values.every((value) => value === "1") );
}); });
after('shutdown tor pool', async function () { after('shutdown tor pool', async function () {
@ -82,7 +89,13 @@ describe('TorPool', function () {
}); });
describe('#create(number_of_instances)', function () { describe('#create(number_of_instances)', function () {
let torPool;
before('create tor pool', () => {
torPool = torPoolFactory();
torPool.default_tor_config = { TestSocks: 1 }; torPool.default_tor_config = { TestSocks: 1 };
})
it('should create 2 instances with the default config', async function () { it('should create 2 instances with the default config', async function () {
this.timeout(WAIT_FOR_CREATE*2); this.timeout(WAIT_FOR_CREATE*2);
await torPool.create(2); await torPool.create(2);
@ -95,19 +108,21 @@ describe('TorPool', function () {
it('the created instances should have the same config properties specified in the default config', async function () { it('the created instances should have the same config properties specified in the default config', async function () {
this.timeout(10000); this.timeout(10000);
let values = await Promise.all(torPool.instances.map((instance) => instance.get_config('ProtocolWarnings'))); let values = await Promise.all(torPool.instances.map((instance) => instance.get_config('TestSocks')));
values = _.flatten(values);
assert.isTrue( values.every((value) => value === 1) ); assert.isTrue( values.every((value) => value === "1") );
}); });
after('shutdown tor pool', async function () { after('shutdown tor pool', async function () {
torPool.default_tor_config = {}; torPool.default_tor_config = {};
await torPool.exit(done); await torPool.exit();
}); });
}); });
let torPool;
describe('#next()', function () { describe('#next()', function () {
before('create tor pool', () => { torPool = torPoolFactory(); })
before('create tor instances', async function () { before('create tor instances', async function () {
this.timeout(WAIT_FOR_CREATE * 3); this.timeout(WAIT_FOR_CREATE * 3);
await torPool.add([ await torPool.add([
@ -127,8 +142,8 @@ describe('TorPool', function () {
}); });
it('result of next should be different if run twice', function () { it('result of next should be different if run twice', function () {
var t1 = torPool.next().instance_name; let t1 = torPool.next().instance_name;
var t2 = torPool.next().instance_name; let t2 = torPool.next().instance_name;
assert.notEqual(t1, t2); assert.notEqual(t1, t2);
}); });
}); });
@ -192,8 +207,8 @@ describe('TorPool', function () {
this.timeout(5000); this.timeout(5000);
let values = await Promise.all(torPool.instances.map((instance) => instance.get_config('TestSocks'))); let values = await Promise.all(torPool.instances.map((instance) => instance.get_config('TestSocks')));
values = _.flatten(values);
assert.isTrue( values.every((value) => value === 1) ); assert.isTrue( values.every((value) => value === "1") );
}); });
after('unset config options', async function () { after('unset config options', async function () {

View file

@ -3,13 +3,13 @@ const assert = require('chai').assert;
const { TorProcess } = require('../'); const { TorProcess } = require('../');
const { WAIT_FOR_CREATE } = require('./constants'); const { WAIT_FOR_CREATE } = require('./constants');
const logger = require('../src/winston-silent-logger');
nconf.use('memory');
require(`${__dirname}/../src/nconf_load_env.js`)(nconf); require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
nconf.defaults(require(`${__dirname}/../src/default_config.js`)); nconf.defaults(require(`${__dirname}/../src/default_config.js`));
describe('TorProcess', function () { describe('TorProcess', function () {
let tor = new TorProcess(nconf.get('torPath'), { DataDirectory: nconf.get('parentDataDirectory'), ProtocolWarnings: 0 }, null, logger); let tor = new TorProcess(nconf.get('torPath'), { DataDirectory: nconf.get('parentDataDirectory'), ProtocolWarnings: 0 }, null);
describe('#create()', function () { describe('#create()', function () {
this.timeout(WAIT_FOR_CREATE); this.timeout(WAIT_FOR_CREATE);

View file

@ -5,4 +5,5 @@ describe("TorRouter", function () {
require('./HTTPServer'); require('./HTTPServer');
require('./DNSServer'); require('./DNSServer');
require('./ControlServer'); require('./ControlServer');
require('./RPCInterface');
}); });