@otpjs/transports-socket.io
Version:
A transport for communicating with OTP nodes over socket.io
330 lines (327 loc) • 11.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.register = register;
var otp = _interopRequireWildcard(require("@otpjs/core"));
var matching = _interopRequireWildcard(require("@otpjs/matching"));
var _serializerJson = require("@otpjs/serializer-json");
var _types = require("@otpjs/types");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const {
DOWN,
EXIT,
demonitor,
discover,
link,
lost,
monitor,
relay,
shutdown,
temporary,
trap_exit,
unlink
} = otp.Symbols;
const {
_
} = matching.Symbols;
const disconnect = Symbol.for('disconnect');
const TRANSPORT_COST = 1;
function log(ctx, ...args) {
return ctx.log.extend('transports:socket.io')(...args);
}
function defaultOptions() {
return {
bridge: false,
type: temporary
};
}
function register(node, socket, options = defaultOptions()) {
const {
serialize,
deserialize
} = (0, _serializerJson.make)(node, {
stringify: false
});
let ctx;
let running = false;
const process = matching.clauses(function routeProcess(route) {
route((0, _types.t)(relay, _, _, _)).to(relayMessage);
route((0, _types.t)(link, _, _)).to(relayLink);
route((0, _types.t)(unlink, _, _)).to(relayUnlink);
route((0, _types.t)(monitor, _, _, _)).to(relayMonitor);
route((0, _types.t)(demonitor, _, _, _)).to(relayDemonitor);
route((0, _types.t)(EXIT, _, _, _)).to(relayEXIT);
route((0, _types.t)(DOWN, _, _, _, _)).to(relayDOWN);
});
const forward = matching.clauses(function routeForward(route) {
route((0, _types.t)(relay, _)).to(([, op]) => process(op));
route((0, _types.t)(lost, _)).to(relayLost);
route((0, _types.t)(discover, _, _, _, _, _)).to(relayDiscovery);
return 'socket-io.process';
});
const {
bridge,
type
} = options;
socket.on('otp-message', handleMessage);
socket.on('otp-link', handleLink);
socket.on('otp-unlink', handleUnlink);
socket.on('otp-monitor', handleMonitor);
socket.on('otp-demonitor', handleDemonitor);
socket.on('otp-discover', handleDiscover);
socket.on('otp-lost', handleLost);
socket.on('otp-EXIT', handleEXIT);
socket.on('otp-DOWN', handleDOWN);
socket.on('connect', handleConnect);
socket.on('disconnect', handleDisconnect);
if (socket.connected) {
handleConnect();
}
return destroy;
function recycle() {
ctx.receive().then(forward).then(recycle).catch(err => log(ctx, 'recycle() : error : %o', err));
}
function destroy(reason = shutdown) {
log(ctx, 'destroy(reason: %o)', reason);
socket.disconnect();
handleDisconnect();
}
function relayMessage([, fromPid, toPid, message]) {
log(ctx, 'relayMessage(fromPid: %o, toPid: %o, message: %o)', fromPid, toPid, message);
const {
replace: serialize,
buffers
} = replacer();
fromPid = serialize(fromPid);
toPid = serialize(toPid);
message = serialize(message);
socket.emit('otp-message', fromPid, toPid, message, ...buffers);
}
function relayLink([, fromPid, toPid]) {
const {
replace: serialize,
buffers
} = replacer();
fromPid = serialize(fromPid);
toPid = serialize(toPid);
socket.emit('otp-link', fromPid, toPid, ...buffers);
}
function relayUnlink([, fromPid, toPid]) {
const {
replace: serialize,
buffers
} = replacer();
fromPid = serialize(fromPid);
toPid = serialize(toPid);
socket.emit('otp-unlink', fromPid, toPid, ...buffers);
}
function relayMonitor([, fromPid, toPid, ref]) {
const {
replace: serialize,
buffers
} = replacer();
fromPid = serialize(fromPid);
toPid = serialize(toPid);
ref = serialize(ref);
socket.emit('otp-monitor', fromPid, toPid, ref, ...buffers);
}
function relayDemonitor([, fromPid, toPid, ref]) {
const {
replace: serialize,
buffers
} = replacer();
fromPid = serialize(fromPid);
toPid = serialize(toPid);
ref = serialize(ref);
socket.emit('otp-demonitor', toPid, ref, fromPid, ...buffers);
}
function relayEXIT([, fromPid, toPid, reason]) {
const {
replace: serialize,
buffers
} = replacer();
fromPid = serialize(fromPid);
toPid = serialize(toPid);
reason = serialize(reason);
socket.emit('otp-EXIT', fromPid, toPid, reason, ...buffers);
}
function relayDOWN([, fromPid, toPid, ref, reason]) {
log(ctx, 'relayDOWN(fromPid: %o, toPid: %o, ref: %o, reason: %o)', fromPid, toPid, ref, reason);
const {
replace: serialize,
buffers
} = replacer();
fromPid = serialize(fromPid);
toPid = serialize(toPid);
ref = serialize(ref);
reason = serialize(reason);
socket.emit('otp-DOWN', fromPid, toPid, ref, reason, ...buffers);
}
function relayDiscovery([, source, score, name, type, pid]) {
const {
replace: serialize,
buffers
} = replacer();
log(ctx, 'relayDiscover(source: %o, score: %o, name: %o, type: %o, pid: %o)', source, score, name, type, pid);
source = serialize(source);
score = serialize(score);
name = serialize(name);
type = serialize(type);
pid = serialize(pid);
log(ctx, 'relayDiscover(source: %o, score: %o, name: %o, type: %o, pid: %o)', source, score, name, type, pid);
socket.emit('otp-discover', source, score, name, type, pid, ...buffers);
}
function relayLost([, pid]) {
const {
replace: serialize,
buffers
} = replacer();
pid = serialize(pid);
socket.emit('otp-lost', pid, ...buffers);
}
function handleConnect() {
ctx = node.makeContext();
ctx.log = ctx.logger('transports:socket.io');
ctx.processFlag(trap_exit, true);
let name = node.name;
let score = 0;
let source = null;
let ourType = type;
let pid = null;
const {
replace: serialize,
buffers
} = replacer();
log(ctx, 'handleConnect(source: %o, score: %o, name: %o, type: %o, pid: %o)', source, score, name, ourType, pid);
name = serialize(name);
score = serialize(score);
source = serialize(source);
ourType = serialize(ourType);
pid = serialize(pid);
log(ctx, 'handleConnect(source: %o, score: %o, name: %o, type: %o, pid: %o)', source, score, name, ourType, pid);
socket.emit('otp-discover', source, score, name, ourType, pid, ...buffers);
running = true;
recycle();
}
function handleLost(pid, ...buffers) {
const deserialize = reviver(buffers);
pid = deserialize(pid);
node.unregisterRouter(pid);
}
function handleDiscover(source, score, name, theirType, pid, ...buffers) {
const deserialize = reviver(buffers);
log(ctx, 'handleDiscover(source: %o, score: %o, name: %o, type: %o, pid: %o)', source, score, name, theirType, pid);
source = deserialize(source) ?? node.name;
name = deserialize(name);
score = deserialize(score);
theirType = deserialize(theirType);
pid = deserialize(pid) ?? ctx.self();
log(ctx, 'handleDiscover(source: %o, score: %o, name: %o, type: %o, pid: %o)', source, score, name, theirType, pid);
// Apply "transportation cost" to score to account for indirect connections
score += TRANSPORT_COST;
node.registerRouter(source, score, name, pid, {
bridge,
type: theirType
});
}
function handleDisconnect() {
running = false;
node.unregisterRouter(ctx.self());
// drain the messagebox
ctx.drain(disconnect);
ctx.exit(disconnect);
}
function handleLink(fromPid, toPid, ...buffers) {
const deserialize = reviver(buffers);
fromPid = deserialize(fromPid);
toPid = deserialize(toPid);
node.signal(fromPid, link, toPid);
}
function handleUnlink(fromPid, toPid, ...buffers) {
const deserialize = reviver(buffers);
fromPid = deserialize(fromPid);
toPid = deserialize(toPid);
node.signal(fromPid, unlink, toPid);
}
function handleMessage(fromPid, toPid, message, ...buffers) {
const deserialize = reviver(buffers);
log(ctx, 'handleMessage(fromPid: %o, toPid: %o, message: %o)', fromPid, toPid, message);
fromPid = deserialize(fromPid);
toPid = deserialize(toPid);
message = deserialize(message);
log(ctx, 'handleMessage(fromPid: %o, toPid: %o, message: %o)', fromPid, toPid, message);
node.deliver(fromPid, toPid, message);
}
function handleMonitor(fromPid, toPid, ref, ...buffers) {
const deserialize = reviver(buffers);
fromPid = deserialize(fromPid);
toPid = deserialize(toPid);
ref = deserialize(ref);
node.signal(fromPid, monitor, toPid, ref);
}
function handleDemonitor(toPid, ref, fromPid, ...buffers) {
const deserialize = reviver(buffers);
fromPid = deserialize(fromPid);
toPid = deserialize(toPid);
ref = deserialize(ref);
node.signal(fromPid, demonitor, toPid, ref);
}
function handleEXIT(fromPid, toPid, reason, ...buffers) {
const deserialize = reviver(buffers);
fromPid = deserialize(fromPid);
toPid = deserialize(toPid);
reason = deserialize(reason);
node.signal(fromPid, EXIT, toPid, reason);
}
function handleDOWN(fromPid, toPid, ref, reason, ...buffers) {
const deserialize = reviver(buffers);
fromPid = deserialize(fromPid);
toPid = deserialize(toPid);
ref = deserialize(ref);
reason = deserialize(reason);
log(ctx, 'handleDOWN(fromPid: %o, toPid: %o, ref: %o, reason: %o)', fromPid, toPid, ref, reason);
node.signal(fromPid, DOWN, toPid, ref, reason);
}
function handleQuery() {}
function reviver(buffers) {
return function revive(value) {
return deserialize(value, (key, value) => {
if (typeof value === 'object' && value !== null && matching.compare({
type: '$otp.buffer',
index: Number.isInteger
}, value)) {
return buffers[value.index];
}
});
};
}
function replacer() {
const buffers = [];
return {
replace,
buffers
};
function replace(value) {
return serialize(value, (key, value) => {
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
const index = buffers.indexOf(value);
if (index >= 0) {
return {
type: '$otp.buffer',
index
};
} else {
const index = buffers.length;
buffers.push(value);
return {
type: '$otp.buffer',
index
};
}
}
});
}
}
}