2018-11-13 21:27:26 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const UI = require("@syndicate-lang/driver-browser-ui");
|
|
|
|
// @jsx UI.html
|
|
|
|
// @jsxFrag UI.htmlFragment
|
|
|
|
|
|
|
|
const Http = activate require("@syndicate-lang/driver-http-node");
|
2018-11-26 13:10:43 +00:00
|
|
|
const S = activate require("@syndicate-lang/driver-streams-node");
|
2019-05-23 14:52:10 +00:00
|
|
|
const C = activate require("./client");
|
2019-05-28 12:21:33 +00:00
|
|
|
const P = activate require("./internal_protocol");
|
2019-05-28 15:01:44 +00:00
|
|
|
const D = activate require("./disco");
|
2019-05-23 09:33:59 +00:00
|
|
|
const Server = activate require("./server");
|
2019-05-23 12:51:16 +00:00
|
|
|
const Federation = activate require("./federation");
|
2019-05-23 14:52:10 +00:00
|
|
|
const fs = require('fs');
|
2019-06-20 22:09:00 +00:00
|
|
|
const os = require('os');
|
2018-12-12 17:16:10 +00:00
|
|
|
|
2019-06-20 22:09:00 +00:00
|
|
|
import { genUuid } from "@syndicate-lang/core";
|
2019-05-30 22:06:15 +00:00
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
let currentManagementScope = 'local';
|
|
|
|
|
|
|
|
function usage() {
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
console.info('Usage: syndicate-server [ OPTION [ OPTION ... ] ]');
|
|
|
|
console.info('');
|
|
|
|
console.info('where OPTION may be repeated any number of times and is drawn from:');
|
|
|
|
console.info('');
|
|
|
|
console.info(' --tcp PORTNUMBER Create a plain TCP service on the given port');
|
|
|
|
console.info(' --http PORTNUMBER Create an HTTP WebSocket service on the given port');
|
|
|
|
console.info(' --unix PATH Create a Unix socket service at the given path');
|
|
|
|
console.info('');
|
|
|
|
console.info(' --monitor PORTNUMBER Serve a simple HTML/JS monitoring app on the port');
|
|
|
|
console.info('');
|
|
|
|
console.info(' --management SCOPE Set the management scope for --uplink etc to use');
|
|
|
|
console.info(' --uplink LOCALSCOPE WEBSOCKETURL REMOTESCOPE');
|
|
|
|
console.info(' Establish a federation uplink from the named local');
|
|
|
|
console.info(' scope to the remote scope within the server at the URL');
|
2019-05-28 15:01:44 +00:00
|
|
|
console.info('');
|
|
|
|
console.info(' --overlay OVERLAYID WEBSOCKETURL');
|
|
|
|
console.info(' Participate in a self-assembling overlay with the');
|
|
|
|
console.info(' given ID and root node server URL');
|
2019-06-20 22:09:00 +00:00
|
|
|
console.info(' --id NODEID Set the ID of the new node; defaults to random');
|
2019-05-31 09:59:22 +00:00
|
|
|
console.info('');
|
|
|
|
console.info(' --help, -h Produce this message and terminate');
|
2019-05-23 14:52:10 +00:00
|
|
|
}
|
|
|
|
|
2019-06-20 22:09:00 +00:00
|
|
|
let localId = genUuid(os.hostname());
|
2019-05-23 14:52:10 +00:00
|
|
|
const uplinks = [];
|
2019-05-28 15:01:44 +00:00
|
|
|
const overlays = [];
|
2019-05-23 14:52:10 +00:00
|
|
|
function process_command_line(args) {
|
2019-05-28 15:01:44 +00:00
|
|
|
const notUndefined = (x, w) => {
|
|
|
|
if (x === void 0) {
|
|
|
|
console.error('Missing '+w+' argument on command line');
|
|
|
|
usage();
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
return x;
|
|
|
|
};
|
|
|
|
const strArg = (w) => notUndefined(args.shift(), w);
|
|
|
|
const numArg = (w) => Number.parseInt(notUndefined(args.shift(), w));
|
2019-05-23 14:52:10 +00:00
|
|
|
while (args.length) {
|
|
|
|
const opt = args.shift();
|
|
|
|
switch (opt) {
|
2019-06-20 22:09:00 +00:00
|
|
|
case "--id": localId = strArg('local node ID'); break;
|
2019-05-28 15:01:44 +00:00
|
|
|
case "--tcp": spawnTcpServer(numArg('TCP port')); break;
|
|
|
|
case "--http": spawnWebSocketServer(numArg('HTTP port')); break;
|
|
|
|
case "--unix": spawnUnixSocketServer(strArg('Unix socket path')); break;
|
|
|
|
case "--monitor": spawnMonitorAppServer(numArg('monitor HTTP port')); break;
|
|
|
|
case "--management": currentManagementScope = strArg('management scope'); break;
|
2019-05-23 14:52:10 +00:00
|
|
|
case "--uplink": {
|
2019-05-28 15:01:44 +00:00
|
|
|
const localScope = strArg('local scope');
|
|
|
|
const target = strArg('remote WebSocket URL');
|
|
|
|
const remoteScope = strArg('remote scope');
|
2019-05-23 14:52:10 +00:00
|
|
|
uplinks.push(Federation.Uplink(localScope,
|
|
|
|
C.WSServer(target, currentManagementScope),
|
|
|
|
remoteScope));
|
|
|
|
break;
|
|
|
|
}
|
2019-05-28 15:01:44 +00:00
|
|
|
case "--overlay": {
|
|
|
|
const overlayId = strArg('overlay id');
|
|
|
|
const rootUrl = strArg('overlay root WebSocket URL');
|
|
|
|
overlays.push(D.Overlay(overlayId, C.WSServer(rootUrl, currentManagementScope)));
|
|
|
|
break;
|
|
|
|
}
|
2019-05-23 14:52:10 +00:00
|
|
|
default:
|
|
|
|
console.error("Unsupported command-line argument: " + opt);
|
|
|
|
/* FALL THROUGH */
|
|
|
|
case '--help':
|
|
|
|
case '-h':
|
|
|
|
usage();
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-12 17:16:10 +00:00
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
process_command_line(process.argv.slice(2));
|
2018-11-21 14:23:30 +00:00
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
spawn named 'server' {
|
|
|
|
assert Federation.ManagementScope(currentManagementScope);
|
2019-06-24 13:48:25 +00:00
|
|
|
assert P.Proposal(currentManagementScope, D.OverlayNode(localId));
|
2019-05-23 14:52:10 +00:00
|
|
|
uplinks.forEach((link) => {
|
|
|
|
assert P.Proposal(currentManagementScope, link);
|
|
|
|
});
|
2019-05-30 22:06:15 +00:00
|
|
|
if (overlays.length > 0) {
|
|
|
|
assert D.OverlayNode(localId);
|
|
|
|
}
|
2019-05-28 15:01:44 +00:00
|
|
|
overlays.forEach((o) => {
|
|
|
|
assert P.Proposal(currentManagementScope, o);
|
|
|
|
});
|
2018-11-13 21:27:26 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 10:56:29 +00:00
|
|
|
function _spawnStreamServer(spec) {
|
|
|
|
spawn named spec {
|
2019-05-28 12:21:33 +00:00
|
|
|
assert D.AvailableTransport(spec);
|
2019-05-31 12:58:04 +00:00
|
|
|
on asserted S.Stream($id, S.Incoming(spec)) Server.streamServerActor(id, [spec, id]);
|
2019-05-12 22:26:01 +00:00
|
|
|
}
|
2019-05-23 14:52:10 +00:00
|
|
|
}
|
2019-05-12 22:26:01 +00:00
|
|
|
|
2019-05-28 10:56:29 +00:00
|
|
|
function spawnTcpServer(port) {
|
|
|
|
_spawnStreamServer(S.TcpListener(port));
|
|
|
|
}
|
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
function spawnUnixSocketServer(path) {
|
2019-05-28 10:56:29 +00:00
|
|
|
_spawnStreamServer(S.UnixSocketServer(path));
|
|
|
|
}
|
|
|
|
|
|
|
|
function spawnWebSocketServer(port) {
|
2019-05-28 12:21:33 +00:00
|
|
|
const spec = D.WebSocketTransport(port, '/');
|
2019-05-28 10:56:29 +00:00
|
|
|
spawn named spec {
|
|
|
|
const server = Http.HttpServer(null, port);
|
2019-05-28 12:21:33 +00:00
|
|
|
assert D.AvailableTransport(spec);
|
2019-05-28 10:56:29 +00:00
|
|
|
during Http.WebSocket($reqId, server, [], _) spawn named [spec, reqId] {
|
2019-06-11 17:48:29 +00:00
|
|
|
Server.websocketServerFacet.call(this, reqId);
|
2019-05-23 14:52:10 +00:00
|
|
|
}
|
2019-05-12 22:26:01 +00:00
|
|
|
}
|
2019-05-23 14:52:10 +00:00
|
|
|
}
|
2019-05-12 22:26:01 +00:00
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
function spawnMonitorAppServer(port) {
|
2019-05-28 10:56:29 +00:00
|
|
|
console.info('Monitor app on port', port);
|
2019-05-23 14:52:10 +00:00
|
|
|
spawn named ['monitorAppServer', port] {
|
|
|
|
const server = Http.HttpServer(null, port);
|
|
|
|
|
|
|
|
during Http.Request($reqId, server, 'get', [], _, _) {
|
|
|
|
assert :snapshot Http.Response(reqId, 200, "OK", {"Content-type": "text/html"},
|
|
|
|
'<!DOCTYPE html>' + UI.htmlToString(
|
|
|
|
<html>
|
|
|
|
<head><meta charset="utf-8"></meta></head>
|
2020-08-05 10:36:53 +00:00
|
|
|
<body><script type="module" src="dist/monitor.js"></script></body>
|
2019-05-23 14:52:10 +00:00
|
|
|
</html>));
|
|
|
|
}
|
2018-11-21 14:23:30 +00:00
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
function assertFileResponse(reqId, path) {
|
2020-08-05 10:36:53 +00:00
|
|
|
let type = 'application/octet-stream';
|
|
|
|
if (path.endsWith('.js')) {
|
|
|
|
type = 'text/javascript';
|
|
|
|
}
|
|
|
|
assert :snapshot Http.Response(reqId, 200, "OK", {"Content-type": type}, fs.readFileSync(path));
|
2019-05-23 14:52:10 +00:00
|
|
|
}
|
2018-11-13 21:27:26 +00:00
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
during Http.Request($reqId, server, 'get', ['chat.html'], _, _)
|
|
|
|
assertFileResponse(reqId, __dirname + '/../chat.html');
|
2018-12-12 17:16:10 +00:00
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
during Http.Request($reqId, server, 'get', ['style.css'], _, _)
|
|
|
|
assertFileResponse(reqId, __dirname + '/../style.css');
|
2018-11-13 21:27:26 +00:00
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
during Http.Request($reqId, server, 'get', ['dist', $file], _, _)
|
|
|
|
assertFileResponse(reqId, __dirname + '/../dist/' + file);
|
2018-11-19 16:57:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 14:52:10 +00:00
|
|
|
spawn named 'monitorApp' {
|
|
|
|
during P.POAScope($connId, $scope) assert P.Proposal('monitor', P.POAScope(connId, scope));
|
|
|
|
on message P.Envelope('monitor', P.Disconnect($connId)) send P.Disconnect(connId);
|
2019-06-06 14:55:01 +00:00
|
|
|
during Federation.ManagementScope($scope) {
|
|
|
|
assert P.Proposal('monitor', Federation.ManagementScope(scope));
|
|
|
|
}
|
2018-11-13 21:27:26 +00:00
|
|
|
}
|