endpointjs
Version:
Endpoint.js enables modules within a web application to discover and use each other, whether that be on the same web page, other browser windows and tabs, iframes, servers and web workers in a reactive way by providing robust discovery, execution and stre
174 lines (157 loc) • 6.12 kB
JavaScript
/*
* (C) 2016
* Booz Allen Hamilton, All rights reserved
* Powered by InnoVision, created by the GIAT
*
* Endpoint.js was developed at the
* National Geospatial-Intelligence Agency (NGA) in collaboration with
* Booz Allen Hamilton [http://www.boozallen.com]. The government has
* "unlimited rights" and is releasing this software to increase the
* impact of government investments by providing developers with the
* opportunity to take things in new directions.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* jshint -W097 */
/* globals __filename */
'use strict';
var isArray = require('util').isArray,
appUtils = require('../util/appUtils'),
log = appUtils.getLogger(__filename);
module.exports = Address;
/**
* A remote address tells Endpoint.js how to route a packet to a given remote
* endpoint.js instance. When a message is sent to an external host, the path vector will
* be like so: [host1, host2, host3], where successive hosts are appended. This needs to
* be reversed before messages can be sent to an external: [host3, host2, host1].
* To save for performance, we don't reverse the vector until it's needed.
* @param pathVector - array of boundary routers defining the path to the remote instance
* @param reversed - whether the vector has been reversed already
* @constructor
*/
function Address(pathVector, reversed) {
if (!(this instanceof Address)) { return new Address(pathVector, reversed); }
if (!pathVector) {
this._pathVector = [];
}
else if (isArray(pathVector)) {
this._pathVector = pathVector.slice(0);
}
else {
this._pathVector = [pathVector.toString()];
}
this._reversed = !!reversed;
}
/**
* A representation of the path vector as a string.
*/
Address.prototype.toString = function() {
return this.getPathVector().join('.');
};
/**
* Return the identifier, which identifies how to get to the local network.
*/
Address.prototype.getPathVector = function() {
if (!this._reversed) {
this._pathVector = this._pathVector.reverse();
this._reversed = true;
}
return this._pathVector.slice(0);
};
/**
* Create a new address that routes through the given route. The
* assumption is that this route ends at the same place the
* next one begins. This function will attempt to find a way
* to reduce the path to the given host by using common instances
* along the way.
* @param {Address} address
*/
Address.prototype.routeThrough = function(address) {
if (!this.isValid()) {
throw new Error('invalid address');
}
var thisVector = this.getPathVector();
var thatVector = address.getPathVector();
if (thisVector[thisVector.length - 1] !== thatVector[0]) {
var msg = 'While merging two addresses, end of first must be beginning of second';
log.log(log.ERROR, msg);
throw new Error(msg);
}
else {
// Nominal:
// Route 1: A -> B -> C -> D
// Route 2: D -> C -> E -> F
// Route New: A -> B -> C -> E -> F
// External:
// Route 1: A -> B -> edge-ext -> C -> D
// Route 2: D -> C -> edge-ext -> E -> F
// Route New: A -> B -> E -> F (skip edge-ext)
// Create intermediate structure to map what index each item occurs at
var thatHash = {};
for (var i = 0; i < thatVector.length; i++) {
thatHash[thatVector[i]] = i;
}
var smallestScore = null;
var smallestThisIndex = null;
var smallestThatIndex = null;
for (var thisIndex = 0; thisIndex < thisVector.length; thisIndex++) {
var thatIndex = thatHash[thisVector[thisIndex]];
if (thatIndex !== undefined) {
var score = thisIndex + thatIndex;
if (smallestThisIndex === null || score < smallestScore) {
smallestThisIndex = thisIndex;
smallestThatIndex = thatIndex;
}
}
}
// If the item at the smallest index is an external edge, then skip that.
if (appUtils.isExtUuid(thatVector[smallestThatIndex])) {
smallestThatIndex += 1;
}
// Create a new array:
// thisVector: 0->smallestThisIndex
// thatVector: (smallestThatIndex -> end).reverse()
var newVector = thisVector.slice(0, smallestThisIndex)
.concat(thatVector.slice(smallestThatIndex));
return new Address(newVector, true);
}
};
/**
* This function protects us by ensuring that we do not allow affinity under the following
* circumstances:
* - loops or node revisits
* - invalid uuid (neither a uuid or an -ext address)
* - max hops
* @param address
* @private
*/
Address.prototype.isValid = function(maxHops) {
var vector = this.getPathVector();
if (typeof (maxHops) != 'undefined' && vector.length > maxHops) {
log.log(log.WARN, 'max hops violation: %s', this);
return false;
}
var hash = {};
for (var i = 0; i < vector.length; i++) {
if (!(appUtils.isUuid(vector[i]) || (appUtils.isExtUuid(vector[i])))) {
log.log(log.WARN, 'uuid violation: %s', this);
return false;
}
if (hash[vector[i]]) {
log.log(log.WARN, 'loop violation: %s', this);
return false;
}
hash[vector[i]] = true;
}
return true;
};