Adds jsdoc for TorPool.

This commit is contained in:
Zachary Boyd 2018-09-13 22:35:40 -04:00
parent 4aec5c3d0f
commit 5b598f612a
7 changed files with 528 additions and 98 deletions

64
package-lock.json generated
View file

@ -145,8 +145,7 @@
"babylon": { "babylon": {
"version": "7.0.0-beta.19", "version": "7.0.0-beta.19",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz",
"integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==", "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A=="
"dev": true
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
@ -244,7 +243,6 @@
"version": "0.8.9", "version": "0.8.9",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz",
"integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=",
"dev": true,
"requires": { "requires": {
"underscore-contrib": "~0.3.0" "underscore-contrib": "~0.3.0"
} }
@ -651,8 +649,7 @@
"escape-string-regexp": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
"dev": true
}, },
"esprima": { "esprima": {
"version": "2.7.3", "version": "2.7.3",
@ -866,8 +863,7 @@
"graceful-fs": { "graceful-fs": {
"version": "4.1.11", "version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
"dev": true
}, },
"granax": { "granax": {
"version": "3.1.4", "version": "3.1.4",
@ -980,6 +976,34 @@
"which": "^1.2.9" "which": "^1.2.9"
} }
}, },
"jsdoc": {
"version": "3.5.5",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz",
"integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==",
"dev": true,
"requires": {
"babylon": "7.0.0-beta.19",
"bluebird": "~3.5.0",
"catharsis": "~0.8.9",
"escape-string-regexp": "~1.0.5",
"js2xmlparser": "~3.0.0",
"klaw": "~2.0.0",
"marked": "~0.3.6",
"mkdirp": "~0.5.1",
"requizzle": "~0.2.1",
"strip-json-comments": "~2.0.1",
"taffydb": "2.6.2",
"underscore": "~1.8.3"
},
"dependencies": {
"marked": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
"integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
"dev": true
}
}
},
"marked": { "marked": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.5.0.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-0.5.0.tgz",
@ -1313,7 +1337,6 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz",
"integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=",
"dev": true,
"requires": { "requires": {
"xmlcreate": "^1.0.1" "xmlcreate": "^1.0.1"
} }
@ -1329,7 +1352,6 @@
"version": "3.5.5", "version": "3.5.5",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz",
"integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==", "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==",
"dev": true,
"requires": { "requires": {
"babylon": "7.0.0-beta.19", "babylon": "7.0.0-beta.19",
"bluebird": "~3.5.0", "bluebird": "~3.5.0",
@ -1387,7 +1409,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz",
"integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=",
"dev": true,
"requires": { "requires": {
"graceful-fs": "^4.1.9" "graceful-fs": "^4.1.9"
} }
@ -1535,8 +1556,7 @@
"marked": { "marked": {
"version": "0.3.19", "version": "0.3.19",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
"integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg=="
"dev": true
}, },
"mem": { "mem": {
"version": "1.1.0", "version": "1.1.0",
@ -2166,7 +2186,6 @@
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz",
"integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=",
"dev": true,
"requires": { "requires": {
"underscore": "~1.6.0" "underscore": "~1.6.0"
}, },
@ -2174,8 +2193,7 @@
"underscore": { "underscore": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
"dev": true
} }
} }
}, },
@ -2435,8 +2453,7 @@
"strip-json-comments": { "strip-json-comments": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
"dev": true
}, },
"supports-color": { "supports-color": {
"version": "5.4.0", "version": "5.4.0",
@ -2450,8 +2467,7 @@
"taffydb": { "taffydb": {
"version": "2.6.2", "version": "2.6.2",
"resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
"integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg="
"dev": true
}, },
"temp": { "temp": {
"version": "0.8.3", "version": "0.8.3",
@ -2512,14 +2528,12 @@
"underscore": { "underscore": {
"version": "1.8.3", "version": "1.8.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
"dev": true
}, },
"underscore-contrib": { "underscore-contrib": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz",
"integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=",
"dev": true,
"requires": { "requires": {
"underscore": "1.6.0" "underscore": "1.6.0"
}, },
@ -2527,8 +2541,7 @@
"underscore": { "underscore": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
"dev": true
} }
} }
}, },
@ -2669,8 +2682,7 @@
"xmlcreate": { "xmlcreate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz",
"integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=", "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8="
"dev": true
}, },
"xtend": { "xtend": {
"version": "4.0.1", "version": "4.0.1",

View file

@ -21,7 +21,6 @@
"grunt": "^1.0.3", "grunt": "^1.0.3",
"grunt-jsdoc": "^2.3.0", "grunt-jsdoc": "^2.3.0",
"ink-docstrap": "^1.3.2", "ink-docstrap": "^1.3.2",
"jsdoc": "^3.5.5",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"request": "^2.79.0", "request": "^2.79.0",
"request-promise": "^4.2.2" "request-promise": "^4.2.2"
@ -34,6 +33,7 @@
"granax": "^3.1.4", "granax": "^3.1.4",
"jrpc2": "git+https://github.com/znetstar/jrpc2.git#f1521bd3f2fa73d716e74bf8f746d08d5e03e7d7", "jrpc2": "git+https://github.com/znetstar/jrpc2.git#f1521bd3f2fa73d716e74bf8f746d08d5e03e7d7",
"js-weighted-list": "^0.1.1", "js-weighted-list": "^0.1.1",
"jsdoc": "^3.5.5",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"nanoid": "^1.2.3", "nanoid": "^1.2.3",
"native-dns": "git+https://github.com/znetstar/node-dns.git#336f1d3027b2a3da719b5cd65380219267901aeb", "native-dns": "git+https://github.com/znetstar/node-dns.git#336f1d3027b2a3da719b5cd65380219267901aeb",

View file

@ -29,7 +29,25 @@ const TorProcess = require('./TorProcess');
Promise.promisifyAll(fs); Promise.promisifyAll(fs);
/**
* Class that represents a pool of Tor processes.
* @extends EventEmitter
*/
class TorPool extends EventEmitter { class TorPool extends EventEmitter {
/**
* Creates an instance of TorPool.
*
* @param {string} tor_path - Path to the Tor executable.
* @param {Object|Function} [default_config] - Default configuration that will be passed to all Tor instances created. Can be a function. See {@link https://bit.ly/2QrmI3o|Tor Documentation} for all possible options
* @param {string} data_directory - Parent directory for the data directory of each proccess.
* @param {string} load_balance_method - Name of the load balance method to use. See {@link TorPool#load_balance_methods}.
* @param {string} [granax_options] - Object containing options that will be passed to granax.
* @param {string} [logger] - A winston logger. If not provided no logging will occur.
*
* @throws If "data_directory" is not provided.
*/
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) if (!data_directory)
throw new Error('Invalid "data_directory"'); throw new Error('Invalid "data_directory"');
@ -43,11 +61,28 @@ class TorPool extends EventEmitter {
this.granax_options = granax_options; this.granax_options = granax_options;
} }
/**
* Returns a Set containing the names of all of the groups.
*
* @readonly
* @type {Set<string>}
*/
get group_names() { get group_names() {
return new Set(_.flatten(this.instances.map((instance) => instance.instance_group).filter(Boolean))); return new Set(_.flatten(this.instances.map((instance) => instance.instance_group).filter(Boolean)));
} }
/**
* Returns an array containing all of the instances in a group.
*
* @param {string} group_name - The group to query.
* @returns {string[]}
*
* @throws If the provided group does not exist
*/
instances_by_group(group_name) { instances_by_group(group_name) {
if (!this.group_names.has(group_name))
throw new Error(`Group "${group_name}" doesn't exist`);
let group = this.groups[group_name]; let group = this.groups[group_name];
let arr = []; let arr = [];
@ -58,10 +93,24 @@ class TorPool extends EventEmitter {
return arr; return arr;
} }
/**
* Adds an instance to a group. If the group doesn't exist it will be created.
*
* @param {string} group - The group to add the instance to.
* @param {TorProcess} instance - The instance in question.
*/
add_instance_to_group(group, instance) { add_instance_to_group(group, instance) {
instance.definition.Group = _.union(instance.instance_group, [group]); instance.definition.Group = _.union(instance.instance_group, [group]);
} }
/**
* Adds an instance to a group by the {@link TorProcess#instance_name} property on the instance. If the group doesn't exist it will be created.
*
* @param {string} group - The group to add the instance to.
* @param {string} instance_name - The name of the instance in question.
*
* @throws If an instance with the name provided does not exist
*/
add_instance_to_group_by_name(group, instance_name) { add_instance_to_group_by_name(group, instance_name) {
let instance = this.instance_by_name(instance_name); let instance = this.instance_by_name(instance_name);
@ -70,6 +119,14 @@ class TorPool extends EventEmitter {
return this.add_instance_to_group(group, instance); return this.add_instance_to_group(group, instance);
} }
/**
* Adds an instance to a group by the index of the instance in the pool. If the group doesn't exist it will be created.
*
* @param {string} group - The group to add the instance to.
* @param {number} instance_index - The index of the instance in question.
*
* @throws If an instance with the index provided does not exist.
*/
add_instance_to_group_at(group, instance_index) { add_instance_to_group_at(group, instance_index) {
let instance = this.instance_at(instance_index); let instance = this.instance_at(instance_index);
@ -78,10 +135,24 @@ class TorPool extends EventEmitter {
return this.add_instance_to_group(group, instance); return this.add_instance_to_group(group, instance);
} }
/**
* Removes an instance from a group.
*
* @param {string} group - The group to remove the instance from.
* @param {TorProcess} instance - The instance in question.
*/
remove_instance_from_group(group, instance) { remove_instance_from_group(group, instance) {
_.remove(instance.definition.Group, (g) => g === group); _.remove(instance.definition.Group, (g) => g === group);
} }
/**
* Removes an instance from a group by the {@link TorProcess#instance_name} property on the instance.
*
* @param {string} group - The group to remove the instance from.
* @param {string} instance_name - The name of the instance in question.
*
* @throws If an instance with the name provided does not exist.
*/
remove_instance_from_group_by_name(group, instance_name) { remove_instance_from_group_by_name(group, instance_name) {
let instance = this.instance_by_name(instance_name); let instance = this.instance_by_name(instance_name);
@ -90,6 +161,14 @@ class TorPool extends EventEmitter {
return this.remove_instance_from_group(group, instance); return this.remove_instance_from_group(group, instance);
} }
/**
* Removes an instance from a group by the index of the instance in the pool.
*
* @param {string} group - The group to remove the instance from.
* @param {number} instance_index - The index of the instance in question.
*
* @throws If an instance with the index provided does not exist.
*/
remove_instance_from_group_at(group, instance_index) { remove_instance_from_group_at(group, instance_index) {
let instance = this.instance_at(instance_index); let instance = this.instance_at(instance_index);
@ -98,6 +177,34 @@ class TorPool extends EventEmitter {
return this.remove_instance_from_group(group, instance); return this.remove_instance_from_group(group, instance);
} }
/**
* Represents a group of instances. Group is a Proxy with an array as its object. The array is generated by calling {@link TorPool#instances_in_group}.
* When called with an index (e.g. `Group[0]`) will return the instance at that index.
* Helper functions are available as properties.
* @typedef {TorProcess[]} Group
*
* @property {Function} add - Adds an instance to the group.
* @property {Function} remove - Removes an instance from the group.
* @property {Function} add_by_name - Adds an instance to the group by the {@link TorProcess#instance_name} property on the instance.
* @property {Function} remove_by_name - Removes an instance from the group by the {@link TorProcess#instance_name} property on the instance.
* @property {Function} remove_at - Removes an instance from the group by the index of the instance in the group.
* @property {number} length - The size of the group of instances
* @property {Function} rotate - Rotates the array of instances
*/
/**
* Represents a collection of groups as an associative array. GroupCollection is a Proxy with a Set as its object. The Set is {@link TorPool#group_names}.
* If a non-existant group is referenced (e.g. `Groups["doesn't exist"]`) it will be created. So `Groups["doesn't exist"].add(my_instance)` will create the group and add the instance to it.
* @typedef {Group[]} GroupCollection
*/
/**
* Represents all groups currently in the pool.
*
* @readonly
* @type {GroupCollection}
*/
get groups() { get groups() {
let groupHandler = { let groupHandler = {
get: (instances, prop) => { get: (instances, prop) => {
@ -178,6 +285,15 @@ class TorPool extends EventEmitter {
return new Proxy(this.group_names, groupsHandler); return new Proxy(this.group_names, groupsHandler);
} }
/**
* The default configuration that will be passed to each instance. Values from "definition.Config" on each instance will override the default config
*
*/
/** Getter
*
* @type {Object|Function}
*/
get default_tor_config() { get default_tor_config() {
if (typeof(this._default_tor_config) === 'function') if (typeof(this._default_tor_config) === 'function')
return this._default_tor_config(); return this._default_tor_config();
@ -187,10 +303,22 @@ class TorPool extends EventEmitter {
return {}; return {};
} }
/**
* Setter
*
* @param {Object|Function} value
*/
set default_tor_config(value) { this._default_tor_config = value; } set default_tor_config(value) { this._default_tor_config = value; }
/**
* Returns an enumeration of load balance methods as functions
*
* @readonly
* @enum {Function}
* @static
*/
static get load_balance_methods() { static get load_balance_methods() {
return { return Object.freeze({
round_robin: function (instances) { round_robin: function (instances) {
return instances.rotate(1); return instances.rotate(1);
}, },
@ -206,21 +334,45 @@ class TorPool extends EventEmitter {
i._weighted_list = instances._weighted_list; i._weighted_list = instances._weighted_list;
return i; return i;
} }
}; });
} }
/**
* An array containing all instances in the pool.
*
* @readonly
* @type {TorProcess[]}
*/
get instances() { get instances() {
return this._instances.slice(0); return this._instances.slice(0);
} }
/**
* An array containing the names of the instances in the pool.
*
* @readonly
* @type {string[]}
*/
get instance_names() { get instance_names() {
return this.instances.map((i) => i.instance_name); return this.instances.map((i) => i.instance_name);
} }
/**
* Creates an instance then adds it to the pool from the provided definiton.
* Instance will be added (and Promise will resolve) after the instance is fully bootstrapped.
*
* @async
* @param {InstanceDefinition} [instance_definition={}] - Instance definition that will be used to create the instance.
* @returns {Promise<TorProcess>} - The instance that was created.
*
* @throws If an instance with the same {@link InstanceDefinition#Name} already exists.
*/
async create_instance(instance_definition) { async create_instance(instance_definition) {
if (!(fs.existsSync(this.data_directory))) if (!(fs.existsSync(this.data_directory)))
await fs.mkdirAsync(this.data_directory); await fs.mkdirAsync(this.data_directory);
instance_definition = instance_definition || {};
if (instance_definition.Name && this.instance_names.indexOf(instance_definition.Name) !== -1) if (instance_definition.Name && this.instance_names.indexOf(instance_definition.Name) !== -1)
throw new Error(`Instance named ${instance_definition.Name} already exists`); throw new Error(`Instance named ${instance_definition.Name} already exists`);
@ -237,48 +389,107 @@ class TorPool extends EventEmitter {
instance.once('error', reject); instance.once('error', reject);
instance.once('ready', () => { instance.once('ready', () => {
/**
* Fires when an instance has been created.
*
* @event TorPool#instance_created
* @type {TorProcess}
* @param {TorProcess} instance - The instance that was created.
*/
this.emit('instance_created', instance); this.emit('instance_created', instance);
resolve(instance); resolve(instance);
}); });
}); });
} }
/**
* Adds one or more instances to the pool from an array of definitions or single definition.
* @param {InstanceDefinition[]|InstanceDefinition} instance_definitions
*
* @async
* @return {Promise<TorProcess[]>}
* @throws If `instance_definitions` is falsy.
*/
async add(instance_definitions) { async add(instance_definitions) {
if (!instance_definitions)
throw new Error('Invalid "instance_definitions"');
return await Promise.all([].concat(instance_definitions).map((instance_definition) => this.create_instance(instance_definition))); return await Promise.all([].concat(instance_definitions).map((instance_definition) => this.create_instance(instance_definition)));
} }
/**
* Creates one or more instances to the pool from either an array of definitions, a single definition or a number.
* If a number is provided it will create n instances with empty definitions (e.g. `TorPool.create(5)` will create 5 instances).
* @param {InstanceDefinition[]|InstanceDefinition|number} instance_definitions
*
* @async
* @return {Promise<TorProcess[]>}
* @throws If `instances` is falsy.
*/
async create(instances) { async create(instances) {
if (!instances)
throw new Error('Invalid "instances"');
if (typeof(instances) === 'number') { if (typeof(instances) === 'number') {
instances = Array.from(Array(instances)).map(() => { instances = Array.from(Array(instances)).map(() => ({}));
return {
Config: {}
};
});
} }
return await this.add(instances); return await this.add(instances);
} }
/**
* Searches for an instance with the matching {@link TorProcess#instance_name} property.
* @param {string} name - Name of the instance to search for
* @returns {TorProcess} - Matching instance
*/
instance_by_name(name) { instance_by_name(name) {
return this._instances.filter((i) => i.instance_name === name)[0]; return this._instances.filter((i) => i.instance_name === name)[0];
} }
/**
* Returns the instance located at the provided index in the pool.
* Is equivalent to `{@link TorPool#instances}[index]`
* @param {number} index - Index of the instance in the pool
* @returns {TorProcess} - Matching instance
*/
instance_at(index) { instance_at(index) {
return this._instances[index]; return this._instances[index];
} }
async remove(instances) { /**
* Removes a number of instances from the pool and kills their Tor processes.
* @param {number} instances - Number of instances to remove
* @param {number} [start_at=0] - Index to start removing from
* @async
* @returns {Promise} - Promise will resolve when the processes are dead
*/
async remove(instances, start_at) {
this._instances._weighted_list = void(0); this._instances._weighted_list = void(0);
let instances_to_remove = this._instances.splice(0, instances); let instances_to_remove = this._instances.splice((start_at || 0), instances);
await Promise.all(instances_to_remove.map((instance) => instance.exit())); await Promise.all(instances_to_remove.map((instance) => instance.exit()));
} }
/**
* Removes an instance at the provided index and kills its Tor process.
* @param {number} instance_index - Index of the instance to remove
* @async
* @returns {Promise} - Promise will resolve when the process is dead
*/
async remove_at(instance_index) { async remove_at(instance_index) {
this._instances._weighted_list = void(0); this._instances._weighted_list = void(0);
let instance = this._instances.splice(instance_index, 1)[0]; let instance = this._instances.splice(instance_index, 1)[0];
if (!instance)
throw new Error(`No instance at "${instance_index}"`);
await instance.exit(); await instance.exit();
} }
/**
* Removes an instance whose {@link TorProcess#instance_name} property matches the provided name and kills its Tor process.
* @param {string} instance_name - Name of the instance to remove
* @async
* @returns {Promise} - Promise will resolve when the process is dead
*/
async remove_by_name(instance_name) { async remove_by_name(instance_name) {
let instance = this.instance_by_name(instance_name); let instance = this.instance_by_name(instance_name);
if (!instance) if (!instance)
@ -287,33 +498,82 @@ class TorPool extends EventEmitter {
await this.remove_at(instance_index); await this.remove_at(instance_index);
} }
/**
* Runs the load balance function ({@link TorPool#load_balance_method}) on the array of instances in the pool and returns the first instance in the array.
*
* @returns {TorProcess} - The first instance in the modified array.
*/
next() { next() {
this._instances = TorPool.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];
} }
/**
* Rotates the array containing instances in the group provided so that the second element becomes the first element and the first element becomes the last element.
* [1,2,3] -> [2,3,1]
* @todo Load balance methods other than "round_robin" to be used
* @param {string} group - Name of the group
* @returns {TorProcess} - The first element in the modified array
*/
next_by_group(group) { next_by_group(group) {
this.groups[group].rotate(1); this.groups[group].rotate(1);
return this.groups[group][0]; return this.groups[group][0];
} }
/**
* Kills the Tor processes of all instances in the pool.
*
* @async
* @returns {Promise} - Resolves when all instances have been killed.
*/
async exit() { async exit() {
await Promise.all(this._instances.map((instance) => instance.exit())); await Promise.all(this._instances.map((instance) => instance.exit()));
this._instances = []; this._instances = [];
} }
/**
* Gets new identities for all instances in the pool.
*
* @async
* @returns {Promise} - Resolves when all instances have new identities.
*/
async new_identites() { async new_identites() {
await Promise.all(this.instances.map((instance) => instance.new_identity())); await Promise.all(this.instances.map((instance) => instance.new_identity()));
} }
/**
* Gets new identities for all instances in a group.
*
* @async
* @param {string} - Name of the group.
* @returns {Promise} - Resolves when all instances in the group have new identities.
*/
async new_identites_by_group(group) { async new_identites_by_group(group) {
await Promise.all(this.instances_by_group(group).map((instance) => instance.new_identity())); await Promise.all(this.instances_by_group(group).map((instance) => instance.new_identity()));
} }
/**
* Gets a new identity for the instance at the provided index in the pool.
*
* @async
* @param {number} - Index of the instance in the pool.
* @returns {Promise} - Resolves when the instance has a new identity.
*/
async new_identity_at(index) { async new_identity_at(index) {
await this.instances[index].new_identity(); await this.instances[index].new_identity();
} }
/**
* Gets a new identity for the instance whose {@link TorProcess.instance_name} matches the provided name.
*
* @async
* @param {string} - Name of the instance.
* @returns {Promise} - Resolves when the instance has a new identity.
*
* @throws When no instance matched the provided name.
*/
async new_identity_by_name(name) { async new_identity_by_name(name) {
let instance = this.instance_by_name(name); let instance = this.instance_by_name(name);
@ -323,7 +583,16 @@ class TorPool extends EventEmitter {
await instance.new_identity(); await instance.new_identity();
} }
/**
* Get a configuration value from the instance whose {@link TorProcess.instance_name} matches the provided name via the control protocol.
*
* @async
* @param {string} name - Name of the instance.
* @param {string} keyword - Name of the configuration property.
*
* @returns {Promise<string[]>} - The configuration property's value.
* @throws When no instance matched the provided name.
*/
async get_config_by_name(name, keyword) { async get_config_by_name(name, keyword) {
let instance = this.instance_by_name(name); let instance = this.instance_by_name(name);
if (!instance) if (!instance)
@ -332,6 +601,17 @@ class TorPool extends EventEmitter {
return await instance.get_config(keyword); return await instance.get_config(keyword);
} }
/**
* Set a configuration value for the instance whose {@link TorProcess.instance_name} matches the provided name via the control protocol.
*
* @async
* @param {string} name - Name of the instance.
* @param {string} keyword - Name of the configuration property.
* @param {*} value - Value to set the configuration property to.
*
* @returns {Promise}
* @throws When no instance matched the provided name.
*/
async set_config_by_name(name, keyword, value) { async set_config_by_name(name, keyword, value) {
let instance = this.instance_by_name(name); let instance = this.instance_by_name(name);
if (!instance) if (!instance)
@ -340,6 +620,16 @@ class TorPool extends EventEmitter {
return await instance.set_config(keyword, value); return await instance.set_config(keyword, value);
} }
/**
* Get a configuration value from the instance at the index in the pool via the control protocol.
*
* @async
* @param {number} index - Index of the instance in the pool.
* @param {string} keyword - Name of the configuration property.
*
* @returns {Promise<string[]>} - The configuration property's value.
* @throws When no instance exists at the provided index.
*/
async get_config_at(index, keyword) { async get_config_at(index, keyword) {
let instance = this.instances[index]; let instance = this.instances[index];
if (!instance) if (!instance)
@ -348,6 +638,17 @@ class TorPool extends EventEmitter {
return await instance.get_config(keyword); return await instance.get_config(keyword);
} }
/**
* Set a configuration value for the instance at the index in the pool via the control protocol.
*
* @async
* @param {number} index - Index of the instance in the pool.
* @param {string} keyword - Name of the configuration property.
* @param {*} value - Value to set the configuration property to.
*
* @returns {Promise}
* @throws When no instance exists at the provided index.
*/
async set_config_at(index, keyword, value) { async set_config_at(index, keyword, value) {
let instance = this.instances[index]; let instance = this.instances[index];
if (!instance) if (!instance)
@ -355,22 +656,55 @@ class TorPool extends EventEmitter {
return await instance.set_config(keyword, value); return await instance.set_config(keyword, value);
} }
/**
* Set a configuration value for all instances in the provided group via the control protocol.
*
* @async
* @param {string} group - Name of the group.
* @param {string} keyword - Name of the configuration property.
* @param {*} value - Value to set the configuration property to.
*
* @returns {Promise}
* @throws When the provided group does not exist.
*/
async set_config_by_group(group, keyword, value) { async set_config_by_group(group, keyword, value) {
return await Promise.all(this.instances_by_group(group).map((instance) => instance.set_config(keyword, value))); return await Promise.all(this.instances_by_group(group).map((instance) => instance.set_config(keyword, value)));
} }
async signal_by_group(group, signal) { /**
await Promise.all(this.instances_by_group(group).map((instance) => instance.signal(signal))); * Set a configuration value for all instances in the pool via the control protocol.
} *
* @async
* @param {string} keyword - Name of the configuration property.
* @param {*} value - Value to set the configuration property to.
*
* @returns {Promise}
*/
async set_config_all(keyword, value) { async set_config_all(keyword, value) {
return await Promise.all(this.instances.map((instance) => instance.set_config(keyword, value))); return await Promise.all(this.instances.map((instance) => instance.set_config(keyword, value)));
} }
/**
* Send a signal via the control protocol to all instances in the pool.
*
* @async
* @param {string} signal - The signal to send.
*
* @returns {Promise}
*/
async signal_all(signal) { async signal_all(signal) {
await Promise.all(this.instances.map((instance) => instance.signal(signal))); await Promise.all(this.instances.map((instance) => instance.signal(signal)));
} }
/**
* Send a signal via the control protocol to an instance whose {@link TorProcess#instance_name} property matches the provided name.
*
* @async
* @param {string} name - Name of the instance.
* @param {string} signal - The signal to send.
*
* @returns {Promise}
*/
async signal_by_name(name, signal) { async signal_by_name(name, signal) {
let instance = this.instance_by_name(name); let instance = this.instance_by_name(name);
if (!instance) if (!instance)
@ -379,6 +713,15 @@ class TorPool extends EventEmitter {
await instance.signal(signal); await instance.signal(signal);
} }
/**
* Send a signal via the control protocol to an instance at the provided index in the pool.
*
* @async
* @param {number} index - Index of the instance in the pool.
* @param {string} signal - The signal to send.
*
* @returns {Promise}
*/
async signal_at(index, signal) { async signal_at(index, signal) {
let instance = this.instances[index]; let instance = this.instances[index];
if (!instance) if (!instance)
@ -386,6 +729,20 @@ class TorPool extends EventEmitter {
await instance.signal(signal); await instance.signal(signal);
} }
/**
* Send a signal via the control protocol to all instances in the provided group.
*
* @async
* @param {string} group - Name of the group.
* @param {string} signal - The signal to send.
*
* @returns {Promise}
* @throws When the provided group does not exist.
*/
async signal_by_group(group, signal) {
await Promise.all(this.instances_by_group(group).map((instance) => instance.signal(signal)));
}
}; };
module.exports = TorPool; module.exports = TorPool;

View file

@ -16,24 +16,32 @@ const temp = require('temp');
const { TorController } = require('granax'); const { TorController } = require('granax');
const nanoid = require("nanoid"); const nanoid = require("nanoid");
const winston = require('winston'); const winston = require('winston');
winston.
Promise.promisifyAll(temp); Promise.promisifyAll(temp);
Promise.promisifyAll(fs); Promise.promisifyAll(fs);
temp.track(); temp.track();
/**
* @typedef {Object} InstanceDefinition
* @property {string} [Name] - Name of the instance.
* @property {string[]|string} [Group=[]] - Groups the instance belongs to.
* @property {Object} [Config={}] - Configuration that will be passed to Tor. See {@link https://bit.ly/2QrmI3o|Tor Documentation} for all possible options.
* @property {Number} [Weight] - Weight of the instance for "weighted" load balancing.
*/
/** /**
* Class that represents an individual Tor process. * Class that represents an individual Tor process.
*
* @extends EventEmitter * @extends EventEmitter
*/ */
class TorProcess extends EventEmitter { class TorProcess extends EventEmitter {
/** /**
* Creates a TorProcess Object * Creates an instance of TorProcess
* *
* @param {String} tor_path - Path to the Tor executable. * @param {string} tor_path - Path to the Tor executable.
* @param {Object} [definition={}] - Object containing various options for the instance. See {@link https://github.com/znetstar/tor-router/wiki/Configuration|the wiki} for more info * @param {InstanceDefinition} [definition] - Object containing various options for the instance. See {@link InstanceDefinition} for more info.
* @param {Object} [granax_options] - Object containing options that will be passed to granax * @param {Object} [granax_options] - Object containing options that will be passed to granax.
* @param {Logger} [logger] - A winston logger used for logging. * @param {Logger} [logger] - A winston logger. If not provided no logging will occur.
*/ */
constructor(tor_path, definition, granax_options, logger) { constructor(tor_path, definition, granax_options, logger) {
super(); super();
@ -41,11 +49,32 @@ class TorProcess extends EventEmitter {
definition = definition || {}; definition = definition || {};
definition.Group = definition.Group ? [].concat(definition.Group) : []; definition.Group = definition.Group ? [].concat(definition.Group) : [];
definition.Config = definition.Config || {};
this._definition = definition; this._definition = definition;
/**
* Path to the Tor executable.
*
* @type {string}
* @public
*/
this.tor_path = tor_path; this.tor_path = tor_path;
/**
* Object containing options that will be passed to granax.
*
* @type {Object}
* @public
*/
this.granax_options = granax_options; this.granax_options = granax_options;
/**
* The password that will be set for the control protocol.
*
* @type {string}
* @public
*/
this.control_password = crypto.randomBytes(128).toString('base64'); this.control_password = crypto.randomBytes(128).toString('base64');
this._id = nanoid(12); this._id = nanoid(12);
@ -53,7 +82,7 @@ class TorProcess extends EventEmitter {
} }
/** /**
* Kills the Tor process * Kills the Tor process.
* *
* @async * @async
* @returns {Promise} * @returns {Promise}
@ -71,101 +100,101 @@ class TorProcess extends EventEmitter {
} }
/** /**
* The unique identifier assigned to each instance * The unique identifier assigned to each instance.
* *
* @readonly * @readonly
* @returns {String} * @type {string}
*/ */
get id() { return this._id; } get id() { return this._id; }
/** /**
* Groups the instance are currently in * Groups the instance are currently in.
* *
* @readonly * @readonly
* @returns {String[]} * @type {string[]}
*/ */
get instance_group() { get instance_group() {
return (this.definition && this.definition.Group) || []; return (this.definition && this.definition.Group);
} }
/** /**
* Either the "Name" property of the definition or the "id" property * Either the "Name" property of the definition or the {@link TorProcess#id} property.
* *
* @readonly * @readonly
* @returns {String} * @type {string}
*/ */
get instance_name() { get instance_name() {
return (this.definition && this.definition.Name) || this.id; return (this.definition && this.definition.Name) || this.id;
} }
/** /**
* The definition used to create the instance * The definition used to create the instance.
* *
* @readonly * @readonly
* @returns {String} * @type {string}
*/ */
get definition() { return this._definition; } get definition() { return this._definition; }
/** /**
* The configuration passed to Tor. The same value as "definition.Config" * The configuration passed to Tor. The same value as `definition.Config`.
* *
* @readonly * @readonly
* @returns {Object} * @type {Object}
*/ */
get tor_config() { return this.definition.Config; } get tor_config() { return this.definition.Config; }
/** /**
* Port Tor is bound to for DNS traffic * Port Tor is bound to for DNS traffic.
* *
* @readonly * @readonly
* @returns {Number} * @type {number}
*/ */
get dns_port() { get dns_port() {
return this._ports.dns_port; return this._ports.dns_port;
} }
/** /**
* Port Tor is bound to for SOCKS5 traffic * Port Tor is bound to for SOCKS5 traffic.
* *
* @readonly * @readonly
* @returns {Number} * @type {number}
*/ */
get socks_port() { get socks_port() {
return this._ports.socks_port; return this._ports.socks_port;
} }
/** /**
* Port Tor is bound to for API access * Port Tor is bound to for API access.
* *
* @readonly * @readonly
* @returns {Number} * @type {number}
*/ */
get control_port() { get control_port() {
return this._ports.control_port; return this._ports.control_port;
} }
/** /**
* Instance of granax connected to the Tor process * Instance of granax.TorController connected to the Tor process.
* *
* @readonly * @readonly
* @returns {Object} * @type {TorController}
*/ */
get controller() { get controller() {
return this._controller; return this._controller;
} }
/** /**
* Property identifiyng whether Tor has started * Property identifiyng whether Tor has started.
* *
* @readonly * @readonly
* @returns {Boolean} * @type {boolean}
*/ */
get ready() { return this._ready; } get ready() { return this._ready; }
/* Passthrough to granax */ /* Passthrough to granax */
/** /**
* Requests a new identity via the control interface * Requests a new identity via the control protocol.
* *
* @async * @async
*/ */
@ -176,12 +205,13 @@ class TorProcess extends EventEmitter {
} }
/** /**
* Retrieves a configuration value from the instance via the control interface * Retrieves a configuration value from the instance via the control protocol.
* *
* @async * @async
* @param {String} keyword - The name of the configuration property to retrieve * @throws Will throw an error if not connected to the control protocol.
* @param {string} keyword - The name of the configuration property to retrieve.
* *
* @returns {Promise<String[]>} * @returns {Promise<string[]>}
*/ */
async get_config(keyword) { async get_config(keyword) {
if (!this.controller) if (!this.controller)
@ -191,11 +221,12 @@ class TorProcess extends EventEmitter {
} }
/** /**
* Sets a configuration value for the instance via the control interface * Sets a configuration value for the instance via the control protocol.
* *
* @async * @async
* @param {String} keyword - The name of the configuration property to retrieve * @throws Will throw an error if not connected to the control protocol.
* @param value - Value to set the property to * @param {string} keyword - The name of the configuration property to retrieve.
* @param {*} value - Value to set the property to.
* *
* @returns {Promise} * @returns {Promise}
*/ */
@ -208,10 +239,11 @@ class TorProcess extends EventEmitter {
} }
/** /**
* Sends a signal via the control tnterface * Sends a signal via the control tnterface.
* *
* @async * @async
* @param {String} signal - The signal to send * @throws Will throw an error if not connected to the control protocol.
* @param {string} signal - The signal to send.
* *
* @returns {Promise} * @returns {Promise}
*/ */
@ -224,11 +256,11 @@ class TorProcess extends EventEmitter {
} }
/** /**
* Creates the Tor process based on the configuration provided. Promise is resolved when the process has been started * Creates the Tor process based on the configuration provided. Promise is resolved when the process has been started.
* *
* @async * @async
* *
* @returns {Promise<ChildProcess>} - The process that has been created * @returns {Promise<ChildProcess>} - The process that has been created.
*/ */
async create() { async create() {
this._ports = {}; this._ports = {};
@ -262,11 +294,11 @@ class TorProcess extends EventEmitter {
} }
/** /**
* An event that fires when the process has closed * An event that fires when the process has closed.
* *
* @event TorProcess#process_exit * @event TorProcess#process_exit
* @type {Number} * @type {number}
* @returns {Number} - The exit code from the process * @param {number} code - The exit code from the process.
*/ */
this.emit('process_exit', code); this.emit('process_exit', code);
})); }));
@ -297,7 +329,7 @@ class TorProcess extends EventEmitter {
this.logger.debug(`[tor-${this.instance_name}]: authenticated with tor instance via the control port`); this.logger.debug(`[tor-${this.instance_name}]: authenticated with tor instance via the control port`);
this.control_port_connected = true; this.control_port_connected = true;
/** /**
* An event that fires when a connection has been established to the control interface * An event that fires when a connection has been established to the control protocol.
* *
* @event TorProcess#controller_ready * @event TorProcess#controller_ready
*/ */
@ -313,7 +345,7 @@ class TorProcess extends EventEmitter {
if (text.indexOf('Bootstrapped 100%: Done') !== -1){ if (text.indexOf('Bootstrapped 100%: Done') !== -1){
this.bootstrapped = true; this.bootstrapped = true;
/** /**
* An event that fires when the Tor process is fully bootstrapped (and ready for traffic) * An event that fires when the Tor process is fully bootstrapped (and ready for traffic).
* *
* @event TorProcess#ready * @event TorProcess#ready
*/ */
@ -323,7 +355,7 @@ class TorProcess extends EventEmitter {
if (text.indexOf('Opening Control listener on') !== -1) { if (text.indexOf('Opening Control listener on') !== -1) {
this.control_port_listening = true; this.control_port_listening = true;
/** /**
* An event that fires when the Tor process has started listening for control interface traffic * An event that fires when the Tor process has started listening for control interface traffic.
* *
* @event TorProcess#control_listen * @event TorProcess#control_listen
*/ */
@ -333,7 +365,7 @@ class TorProcess extends EventEmitter {
if (text.indexOf('Opening Socks listener on') !== -1) { if (text.indexOf('Opening Socks listener on') !== -1) {
this.socks_port_listening = true; this.socks_port_listening = true;
/** /**
* An event that fires when the Tor process has started listening for SOCKS5 traffic * An event that fires when the Tor process has started listening for SOCKS5 traffic.
* *
* @event TorProcess#socks_listen * @event TorProcess#socks_listen
*/ */
@ -342,7 +374,7 @@ class TorProcess extends EventEmitter {
if (text.indexOf('Opening DNS listener on') !== -1) { if (text.indexOf('Opening DNS listener on') !== -1) {
/** /**
* An event that fires when the Tor process has started listening for DNS traffic * An event that fires when the Tor process has started listening for DNS traffic.
* *
* @event TorProcess#dns_listen * @event TorProcess#dns_listen
*/ */
@ -352,7 +384,7 @@ class TorProcess extends EventEmitter {
if (text.indexOf('[err]') !== -1) { if (text.indexOf('[err]') !== -1) {
/** /**
* An event that fires the Tor process has written an error to stdout or stderr or an error occured connecting to the control interface * An event that fires the Tor process has written an error to stdout or stderr or an error occured connecting to the control protocol.
* *
* @event TorProcess#error * @event TorProcess#error
* @type {Error} * @type {Error}

View file

@ -23,20 +23,19 @@ module.exports = {
/* Taken from https://github.com/bookchin/granax/blob/master/index.js */ /* Taken from https://github.com/bookchin/granax/blob/master/index.js */
switch (platform) { switch (platform) {
case 'win32': case 'win32':
torpath = path.join(BIN_PATH, 'Browser', 'TorBrowser', 'Tor', 'tor.exe'); return path.join(BIN_PATH, 'Browser', 'TorBrowser', 'Tor', 'tor.exe');
break; break;
case 'darwin': case 'darwin':
torpath = path.join(BIN_PATH, '.tbb.app', 'Contents', 'Resources', return path.join(BIN_PATH, '.tbb.app', 'Contents', 'Resources',
'TorBrowser', 'Tor', 'tor'); 'TorBrowser', 'Tor', 'tor');
break; break;
case 'android': case 'android':
case 'linux': case 'linux':
torpath = path.join(BIN_PATH, 'tor-browser_en-US', 'Browser', 'TorBrowser', 'Tor', 'tor'); return path.join(BIN_PATH, 'tor-browser_en-US', 'Browser', 'TorBrowser', 'Tor', 'tor');
break; break;
default: default:
throw new Error(`Unsupported platform "${platform}"`); return "tor";
} }
return torpath;
})(), })(),
"instances": null, "instances": null,
"dns": { "dns": {

View file

@ -12,7 +12,7 @@ const package_json = JSON.parse(fs.readFileSync(`${__dirname}/../package.json`,
function extractHost (host) { function extractHost (host) {
if (typeof(host) === 'number') if (typeof(host) === 'number')
return { hostname: (typeof(default_ports.default_host) === 'string' ? default_ports.default_host : ''), port: host }; return { hostname: (typeof(default_ports.default_host) === 'string' ? default_ports.default_host : '0.0.0.0'), port: host };
else if (typeof(host) === 'string' && host.indexOf(':') !== -1) else if (typeof(host) === 'string' && host.indexOf(':') !== -1)
return { hostname: host.split(':').shift(), port: Number(host.split(':').pop()) }; return { hostname: host.split(':').shift(), port: Number(host.split(':').pop()) };
else else
@ -36,7 +36,7 @@ async function main(nconf, logger) {
if (typeof(control_host) === 'boolean') { if (typeof(control_host) === 'boolean') {
control_host = extractHost(9077); control_host = extractHost(9077);
nconf.set('controlHost', assembleHost(control_port)); nconf.set('controlHost', assembleHost(control_host));
} }
if (typeof(control_host_ws) === 'boolean') { if (typeof(control_host_ws) === 'boolean') {

View file

@ -96,6 +96,18 @@ describe('TorPool', function () {
let torPool; let torPool;
before('create tor pool', () => { torPool = torPoolFactory(); }) before('create tor pool', () => { torPool = torPoolFactory(); })
it('should throw if the "instance_defintions" field is falsy', async function () {
let fn = () => {};
try {
await torPool.add();
} catch (error) {
fn = () => { throw error };
}
finally {
assert.throws(fn);
}
});
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(_.cloneDeep(instance_defintions)) await torPool.add(_.cloneDeep(instance_defintions))
@ -128,13 +140,25 @@ describe('TorPool', function () {
}); });
}); });
describe('#create(number_of_instances)', function () { describe('#create(instances)', function () {
let torPool; let torPool;
before('create tor pool', () => { before('create tor pool', () => {
torPool = torPoolFactory(); torPool = torPoolFactory();
torPool.default_tor_config = { TestSocks: 1 }; torPool.default_tor_config = { TestSocks: 1 };
}) });
it('should throw if the "number_of_instances" field is falsy', async function () {
let fn = () => {};
try {
await torPool.create();
} catch (error) {
fn = () => { throw error; }
}
finally {
assert.throws(fn);
}
});
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);
@ -169,6 +193,12 @@ describe('TorPool', function () {
instances = (await tor_pool.add([ { Name: 'instance-1', Group: [ "bar", "foo" ] }, { Name: 'instance-2', Group: ["foo", "bar"] } ])); instances = (await tor_pool.add([ { Name: 'instance-1', Group: [ "bar", "foo" ] }, { Name: 'instance-2', Group: ["foo", "bar"] } ]));
}); });
it('should throw if the provided group does not exist', function () {
assert.throws(() => {
tor_pool.instances_by_group('baz');
});
});
it('should return both instances', function () { it('should return both instances', function () {
assert.deepEqual(tor_pool.instances_by_group('bar'), instances); assert.deepEqual(tor_pool.instances_by_group('bar'), instances);
}); });