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
25 changes: 12 additions & 13 deletions lib/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { path, events, vm, fs, fsp } = api;
const security = require('./security.js');

const SCRIPT_OPTIONS = { timeout: 5000 };
const EMPTY_CONTEXT = Object.freeze({});
const DIRS = ['static', 'domain', 'api'];

class Application extends events.EventEmitter {
Expand All @@ -17,6 +18,7 @@ class Application extends events.EventEmitter {
}

async init() {
this.createSandbox();
for (const name of DIRS) {
this[name] = new Map();
await this.loadPlace(name, path.join(this.path, name));
Expand All @@ -29,17 +31,15 @@ class Application extends events.EventEmitter {
}

createSandbox() {
const introspection = async () => [...this.api.keys()];
const context = Object.freeze({});
const application = { security, context, introspection };
const introspect = async () => [...this.api.keys()];
const application = { security, introspect };
for (const name of this.namespaces) application[name] = this[name];
const sandbox = {
console: this.logger, Buffer, application, api,
setTimeout, setImmediate, setInterval,
clearTimeout, clearImmediate, clearInterval,
};
sandbox.global = sandbox;
return vm.createContext(sandbox);
this.sandbox = vm.createContext(sandbox);
}

sandboxInject(name, module) {
Expand All @@ -49,21 +49,21 @@ class Application extends events.EventEmitter {

async createScript(fileName) {
const code = await fsp.readFile(fileName, 'utf8');
const src = '\'use strict\';\n' + code;
const src = '\'use strict\';\ncontext => ' + code;
const options = { filename: fileName, lineOffset: -1 };
try {
return new vm.Script(src, options);
const script = new vm.Script(src, options);
return script.runInContext(this.sandbox, SCRIPT_OPTIONS);
} catch (err) {
this.logger.error(err.stack);
return null;
}
}

runScript(methodName, session) {
const { sandbox } = session || this;
const script = this.api.get(methodName);
if (!script) return null;
const exp = script.runInContext(sandbox, SCRIPT_OPTIONS);
const exp = script(session ? session.context : EMPTY_CONTEXT);
return typeof exp !== 'object' ? { access: 'logged', method: exp } : exp;
}

Expand All @@ -89,11 +89,10 @@ class Application extends events.EventEmitter {
}
if (place === 'domain') {
const config = this.config.sections[name];
const sandbox = this.domainSandbox;
sandbox.application[name] = { config };
const exp = script.runInContext(sandbox, SCRIPT_OPTIONS);
this.sandbox.application[name] = { config };
const exp = script(EMPTY_CONTEXT);
if (config) exp.config = config;
sandbox.application[name] = exp;
this.sandbox.application[name] = exp;
this.sandboxInject(name, exp);
if (exp.start) exp.start();
} else {
Expand Down
40 changes: 10 additions & 30 deletions lib/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ const FUTURE = 'Fri, 01 Jan 2100 00:00:00 GMT';
const LOCATION = 'Path=/; Domain';
const COOKIE_DELETE = `${TOKEN}=deleted; Expires=${EPOCH}; ${LOCATION}=`;
const COOKIE_HOST = `Expires=${FUTURE}; ${LOCATION}`;
const SANDBOX_POOL = 20;

const sessions = new Map();
const cache = new WeakMap();
const pool = [];

const generateToken = () => {
const base = ALPHA_DIGIT.length;
Expand All @@ -46,45 +44,29 @@ const parseCookies = cookie => {
};

module.exports = () => {
let timer = null;
const { db } = application;

const fillPool = () => {
const need = SANDBOX_POOL - pool.length;
for (let i = 0; i < need; i++) {
pool.push(application.createSandbox());
}
};

const getSandbox = () => {
if (timer === null) {
timer = setTimeout(() => {
fillPool();
timer = null;
}, 0);
}
if (pool.length > 0) return pool.pop();
return application.createSandbox();
};

const save = (token, context) => {
const data = JSON.stringify(context);
db.update('Session', { data }, { token });
};

class Session {
constructor(token, sandbox, contextData = { token }) {
constructor(token, contextData = { token }) {
const contextHandler = {
get: (data, key) => {
if (key === 'token') return this.token;
return Reflect.get(data, key);
},
set: (data, key, value) => {
const res = Reflect.set(data, key, value);
save(token, this.data);
return res;
}
};
this.token = token;
this.sandbox = sandbox;
this.data = contextData;
sandbox.context = new Proxy(contextData, contextHandler);
this.context = new Proxy(contextData, contextHandler);
}
}

Expand All @@ -93,8 +75,7 @@ module.exports = () => {
const host = common.parseHost(client.req.headers.host);
const ip = client.req.connection.remoteAddress;
const cookie = `${TOKEN}=${token}; ${COOKIE_HOST}=${host}; HttpOnly`;
const sandbox = getSandbox();
const session = new Session(token, sandbox);
const session = new Session(token);
sessions.set(token, session);
cache.set(client.req, session);
const data = JSON.stringify(session.data);
Expand All @@ -109,15 +90,14 @@ module.exports = () => {
const { cookie } = client.req.headers;
if (!cookie) return null;
const cookies = parseCookies(cookie);
const token = cookies.token;
const { token } = cookies;
if (!token) return null;
let session = sessions.get(token);
if (!session) {
const [record] = await db.select('Session', ['Data'], { token });
if (record && record.data) {
const data = JSON.parse(record.data);
const sandbox = getSandbox();
session = new Session(token, sandbox, data);
session = new Session(token, data);
sessions.set(token, session);
}
}
Expand All @@ -141,5 +121,5 @@ module.exports = () => {
.select('SystemUser', ['Id', 'Password'], { login })
.then(([user]) => user);

return { fillPool, start, restore, remove, save, registerUser, getUser };
return Object.freeze({ start, restore, remove, save, registerUser, getUser });
};
3 changes: 0 additions & 3 deletions lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ const initAuth = require('./auth.js');
application.server = new Server(config.sections.server);
application.auth = initAuth();
application.sandboxInject('auth', application.auth);
application.sandbox = application.createSandbox();
application.domainSandbox = application.createSandbox();
await application.init();
application.auth.fillPool();
logger.log(`Application started in worker ${worker.threadId}`);

worker.parentPort.on('message', async message => {
Expand Down