Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions domain/resmon.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@

start() {
const { interval } = application.resmon.config;
setInterval(() => {
setTimeout(() => {
const stats = application.resmon.getStatistics();
const { heapTotal, heapUsed, external, contexts, detached } = stats;
const { heapTotal, heapUsed, external } = stats;
const total = application.utils.bytesToSize(heapTotal);
const used = application.utils.bytesToSize(heapUsed);
const ext = application.utils.bytesToSize(external);
console.log(`Heap: ${used} of ${total}, ext: ${ext}`);
console.log(`Contexts: ${contexts}, detached: ${detached}`);
}, interval);
}
});
27 changes: 19 additions & 8 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ class Client {
res.end();
}

error(status, err) {
error(status, err, callId = err) {
const { req: { url }, res, connection } = this;
const reason = http.STATUS_CODES[status];
if (typeof err === 'number') err = undefined;
const error = err ? err.stack : reason;
const msg = status === 403 ? err.message : `${url} - ${error} - ${status}`;
application.logger.error(msg);
const result = JSON.stringify({ result: 'error', reason });
const packet = { callback: callId, error: { message: reason } };
const result = JSON.stringify(packet);
if (connection) {
connection.send(result);
return;
Expand All @@ -66,36 +68,45 @@ class Client {
res.end(result);
}

async rpc(method, args) {
message(data) {
const packet = JSON.parse(data);
const [callType, methodName] = Object.keys(packet);
const callId = packet[callType];
const args = packet[methodName];
this.rpc(callId, methodName, args);
}

async rpc(callId, method, args) {
const { res, connection } = this;
const { semaphore } = application.server;
try {
await semaphore.enter();
} catch {
this.error(504);
this.error(504, callId);
return;
}
try {
const session = await application.auth.restore(this);
const proc = application.runMethod(method, session);
if (!proc) {
this.error(404);
this.error(404, callId);
return;
}
if (!session && proc.access !== 'public') {
this.error(403, new Error(`Forbidden: /api/${method}`));
this.error(403, new Error(`Forbidden: /api/${method}`), callId);
return;
}
const result = await proc.method(args);
if (!session && proc.access === 'public') {
const session = application.auth.start(this, result.userId);
result.token = session.token;
}
const data = JSON.stringify(result);
const packet = { callback: callId, result };
const data = JSON.stringify(packet);
if (connection) connection.send(data);
else res.end(data);
} catch (err) {
this.error(500, err);
this.error(500, err, callId);
} finally {
semaphore.leave();
}
Expand Down
14 changes: 5 additions & 9 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const Client = require('./client.js');

const SHUTDOWN_TIMEOUT = 5000;
const LONG_RESPONSE = 30000;
const METHOD_OFFSET = '/api/'.length;

const clients = new Map();

Expand Down Expand Up @@ -51,15 +50,13 @@ const listener = (req, res) => {
});

application.logger.log(`${method}\t${url}`);
if (url.startsWith('/api/')) {
if (url === '/api') {
if (method !== 'POST') {
client.error(403, new Error(`Forbidden: ${url}`));
return;
}
receiveBody(req).then(body => {
const method = url.substring(METHOD_OFFSET);
const args = JSON.parse(body);
client.rpc(method, args);
receiveBody(req).then(data => {
client.message(data);
});
} else {
if (url === '/' && !req.connection.encrypted) {
Expand All @@ -84,9 +81,8 @@ class Server {
this.ws = new WebSocket.Server({ server: this.instance });
this.ws.on('connection', (connection, req) => {
const client = new Client(req, null, connection);
connection.on('message', message => {
const { method, args } = JSON.parse(message);
client.rpc(method, args);
connection.on('message', data => {
client.message(data);
});
});
this.instance.listen(port, host);
Expand Down
5 changes: 5 additions & 0 deletions static/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"parserOptions": {
"sourceType": "module"
}
}
43 changes: 6 additions & 37 deletions static/console.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
'use strict';
import { Metacom } from './metacom.js';

// API Builder

const socket = new WebSocket('wss://' + location.host);

const buildAPI = (methods, socket = null) => {
const api = {};
for (const method of methods) {
api[method] = (args = {}) => new Promise((resolve, reject) => {
if (socket) {
socket.send(JSON.stringify({ method, args }));
socket.onmessage = event => {
const obj = JSON.parse(event.data);
if (obj.result !== 'error') resolve(obj);
else reject(new Error(`Status Code: ${obj.reason}`));
};
} else {
fetch(`/api/${method}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(args),
}).then(res => {
const { status } = res;
if (status === 200) resolve(res.json());
else reject(new Error(`Status Code: ${status}`));
});
}
});
}
return api;
};

let api = buildAPI(['status', 'signIn', 'introspection'], socket);

// Console Emulation
const metacom = new Metacom(location.host);
const { api } = metacom;
window.api = api;

const ALPHA_UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const ALPHA_LOWER = 'abcdefghijklmnopqrstuvwxyz';
Expand Down Expand Up @@ -276,12 +245,12 @@ function commandLoop() {

const signIn = async () => {
try {
await metacom.load('status', 'signIn', 'introspection');
await api.status();
} catch (err) {
await api.signIn({ login: 'marcus', password: 'marcus' });
}
const methods = await api.introspection();
api = buildAPI(methods, socket);
await metacom.load('example');
};

window.addEventListener('load', () => {
Expand Down
2 changes: 1 addition & 1 deletion static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta charset="utf-8">
<link rel="manifest" href="/manifest.json">
<link rel="stylesheet" href="/console.css">
<script src="console.js"></script>
<script src="console.js" type="module"></script>
<meta name="HandheldFriendly" content="true" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
Expand Down
59 changes: 59 additions & 0 deletions static/metacom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export class Metacom {
constructor(host) {
this.socket = new WebSocket('wss://' + host);
this.api = {};
this.callId = 0;
this.calls = new Map();
this.socket.onmessage = ({ data }) => {
try {
const packet = JSON.parse(data);
const { callback, event } = packet;
const callId = callback || event;
const [resolve, reject] = this.calls.get(callId);
if (packet.error) {
const { code, message } = packet.error;
const error = new Error(message);
error.code = code;
reject(error);
return;
}
resolve(packet.result);
} catch (err) {
console.error(err);
}
};
}

async load(...methods) {
for (const methodName of methods) {
this.api[methodName] = this.socketCall(methodName);
}
}

httpCall(methodName) {
return (args = {}) => {
const callId = ++this.callId;
const packet = { call: callId, [methodName]: args };
return fetch('/api', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(packet),
}).then(res => {
const { status } = res;
if (status === 200) return res.json().then(({ result }) => result);
throw new Error(`Status Code: ${status}`);
});
};
}

socketCall(methodName) {
return (args = {}) => {
const callId = ++this.callId;
return new Promise((resolve, reject) => {
this.calls.set(callId, [resolve, reject]);
const packet = { call: callId, [methodName]: args };
this.socket.send(JSON.stringify(packet));
});
};
}
}
12 changes: 8 additions & 4 deletions test/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const PORT = 8000;
const START_TIMEOUT = 1000;
const TEST_TIMEOUT = 3000;

let callId = 0;

console.log('System test started');
setTimeout(async () => {
worker.postMessage({ name: 'stop' });
Expand All @@ -24,8 +26,9 @@ const tasks = [
{ get: '/', status: 302 },
{ get: '/console.js' },
{
post: '/api/signIn',
data: { login: 'marcus', password: 'marcus' }
post: '/api',
method: 'signIn',
args: { login: 'marcus', password: 'marcus' }
}
];

Expand All @@ -42,8 +45,9 @@ const getRequest = task => {
request.method = 'POST';
request.path = task.post;
}
if (task.data) {
task.data = JSON.stringify(task.data);
if (task.args) {
const packet = { call: ++callId, [task.method]: task.args };
task.data = JSON.stringify(packet);
request.headers = {
'Content-Type': 'application/json',
'Content-Length': task.data.length
Expand Down