🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

@browser-network/switchboard

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@browser-network/switchboard - npm Package Compare versions

Comparing version

to
0.1.0

208

dist/index.js

@@ -8,4 +8,5 @@ #!/usr/bin/env node

var http_1 = __importDefault(require("http"));
var MAX_NEGOTIATIONS = 500;
var MAX_NEGOTIATION_AGE = 1000 * 60 * 3;
var CLEAN_INTERVAL = 1000 * 60 * 1;
var MAX_NEGOTIATIONS_ITEMS_PER_NETWORK = 500;
var MAX_ADDRESS_AGE = 1000 * 30; // Keep it short
var CORS_HEADERS = {

@@ -22,3 +23,4 @@ 'Access-Control-Allow-Origin': '*',

var server = http_1["default"].createServer(function (req, res) {
var ok = function (json) {
var ok = function (networkId, json) {
console.log('ok for', networkId, json);
res.writeHead(200, HEADERS);

@@ -51,129 +53,64 @@ res.end(JSON.stringify(json));

req.on('end', function () {
var body;
var _a;
var request;
try {
body = JSON.parse(stringifiedBody);
request = JSON.parse(stringifiedBody);
}
catch (_a) {
catch (_b) {
return nope('json');
}
// Just the minimum
if (!body.sdp)
return nope('sdp');
if (!body.address)
// Shape check (if only typescript had runtime types)
if (!request.address)
return nope('address');
if (!body.networkId)
if (!request.networkId)
return nope('networkId');
if (!body.connectionId)
return nope('connectionId');
if (!['offer', 'answer'].includes(body.type))
return nope('type');
console.log('receiving request from', body.address);
// No random objects
var negotiation = {
sdp: body.sdp,
address: body.address,
networkId: body.networkId,
type: body.type,
timestamp: Date.now(),
connectionId: body.connectionId
};
// Now it's time to process the negotiation
var address = negotiation.address, networkId = negotiation.networkId, connectionId = negotiation.connectionId;
// Ensure the network exists
if (!book[networkId]) {
book[networkId] = {};
request.negotiationItems.forEach(function (item) {
if (!item.from)
return nope('from');
if (!item["for"])
return nope('for');
if (!item.negotiation)
return nope('negotiation');
var negotiation = item.negotiation;
if (!['offer', 'answer'].includes(negotiation.type))
return nope('type');
if (!negotiation.connectionId)
return nope('connectionId');
if (!negotiation.sdp)
return nope('sdp');
});
// The switchboard's actions
// Upon getting a request:
// 1) Take the address, and bring it into our list of addresses
// { [address: string]: number }
// 2) Add the negotiationItems to an array
// * Don't need to dedup or nothin, each will only be sent once.
// 3) Cull the expired addresses and networks
// 4) Accumulate negotiationItems for the requesting address
// 5) Send back response with all addresses and accumulated negotiationItems
// First We make sure the network has been seen before and set it up if it hasn't
if (!book[request.networkId]) {
book[request.networkId] = {
addresses: {},
negotiationItems: []
};
}
// Send back the pool. Convenience method for console verbosity.
var verboseOk = function () {
var okData = Object.values(book[networkId]);
console.log('returning ok: ' + networkId, okData.map(function (j) { return [j.type, j.address]; }));
ok(okData);
};
var getOfferByConId = function (conId, networkId, book) {
return Object.values(book[networkId]).find(function (negotiation) {
return negotiation.connectionId === conId && negotiation.type === 'offer';
});
};
var getAnswerByConId = function (conId, networkId, book) {
return Object.values(book[networkId]).find(function (negotiation) {
return negotiation.connectionId === conId && negotiation.type === 'answer';
});
};
// Here's the scheme:
//
// If we receive an answer, we remove the offer
// that answer is in response to and post the answer.
// This will ensure nobody else tries to connect to that offer
// for the immediate time being to preserve them from waiting
// on a futile connection.
//
// If we receive an offer and there's an existing answer to that offer,
// then we assume the two will connect and we remove both the answer and
// the offer with the answer's connectionId from the book. Of course this
// is after sending back the answer.
if (negotiation.type === 'answer') {
var relatedOffer = getOfferByConId(connectionId, networkId, book);
if (relatedOffer) {
// remove related offer
delete book[networkId][relatedOffer.address];
}
// post this answer
book[networkId][address] = negotiation;
// send back book
verboseOk();
// 1) Add address to our list
book[request.networkId].addresses[request.address] = Date.now();
// 2) Add negotiationItems to our list
(_a = book[request.networkId].negotiationItems).push.apply(_a, request.negotiationItems);
// 3) Clean the expired addresses in this network
cleanExpiredAddressesForNetworkId(request.networkId);
// 4) Accumulate negotiationItems for the requesting address
var negotiationItemsForRequester = book[request.networkId].negotiationItems.filter(function (item) { return item["for"] === request.address; });
// 5) Send back response with all addresses and accumulated negotiationItems
ok(request.networkId, {
addresses: Object.keys(book[request.networkId].addresses),
negotiationItems: negotiationItemsForRequester
});
// Now we trim the fat and remove all the oldest negotiationItems
var items = book[request.networkId].negotiationItems;
if (book[request.networkId].negotiationItems.length > MAX_NEGOTIATIONS_ITEMS_PER_NETWORK) {
items.splice(MAX_NEGOTIATIONS_ITEMS_PER_NETWORK, items.length);
}
else {
var relatedAnswer = getAnswerByConId(connectionId, networkId, book);
// If there's an existing answer to this offer
if (relatedAnswer) {
// - send back the book with that answer
verboseOk();
// - remove the answer
delete book[networkId][relatedAnswer.address];
// - remove the existing offer
delete book[networkId][address];
// - don't post the new offer
}
else {
// - add this to our book
book[networkId][address] = negotiation;
// - send back the book
verboseOk();
}
}
/** Garbage Collection **/
// Remove old ones from this network. We'll do just the current
// network.
for (var address_1 in book[networkId]) {
var negotiation_1 = book[networkId][address_1];
if (Date.now() - negotiation_1.timestamp > MAX_NEGOTIATION_AGE) {
delete book[networkId][address_1];
}
}
// Trim the fat (aka remove negotiations in excess of MAX_NEGOTIATIONS)
// Contrary to popular belief, we actually want to trim the _newer_
// negotiations. This is because in an active network, the negotiation
// needs time to go through. And this will only ever happen in an active
// network. In fact you could even define a network as active by whether
// this function is running or not.
var addresses = Object.keys(book[networkId]);
if (addresses.length > MAX_NEGOTIATIONS) {
// Ok so we're too long. We'll nix the entry with the most recent
// timestamp
var now = Date.now();
// Loop through each, remembering the most recent
// TODO just remember the highest date...
var mostRecentAddress = void 0;
var mostRecentDifference = Infinity;
for (var _i = 0, addresses_1 = addresses; _i < addresses_1.length; _i++) {
var address_2 = addresses_1[_i];
var difference = now - book[networkId][address_2].timestamp;
if (difference < mostRecentDifference) {
mostRecentAddress = address_2;
mostRecentDifference = difference;
}
}
// Finally, remove the newest entry
delete book[mostRecentAddress];
}
});

@@ -185,1 +122,28 @@ });

});
// We need to periodically clean this otherwise any transient networks will rock this thing. Testing...
setInterval(function () {
for (var networkId in book) {
cleanExpiredAddressesForNetworkId(networkId);
cleanNetworkIfEmpty(networkId);
}
}, CLEAN_INTERVAL);
function cleanExpiredAddressesForNetworkId(networkId) {
var _loop_1 = function (address) {
var expiry = book[networkId].addresses[address];
var isExpired = Date.now() - expiry > MAX_ADDRESS_AGE;
if (isExpired) {
// remove the address from our book
delete book[networkId].addresses[address];
// and remove all of the addresses lingering negotiations as well
book[networkId].negotiationItems = book[networkId].negotiationItems.filter(function (item) { return item.from !== address; });
}
};
for (var address in book[networkId].addresses) {
_loop_1(address);
}
}
function cleanNetworkIfEmpty(networkId) {
if (book[networkId].addresses.length === 0) {
delete book[networkId];
}
}
{
"name": "@browser-network/switchboard",
"version": "0.0.2",
"version": "0.1.0",
"description": "The switching server required for the onboarding of new nodes into a @browser-network/network",

@@ -40,2 +40,3 @@ "main": "dist/index.js",

"devDependencies": {
"nodemon": "^2.0.20",
"np": "^7.6.1",

@@ -42,0 +43,0 @@ "shx": "^0.3.4",