diff --git a/lib/application.js b/lib/application.js index 4a530a8..cad0be9 100644 --- a/lib/application.js +++ b/lib/application.js @@ -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 { @@ -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)); @@ -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) { @@ -49,10 +49,11 @@ 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; @@ -60,10 +61,9 @@ class Application extends events.EventEmitter { } 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; } @@ -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 { diff --git a/lib/auth.js b/lib/auth.js index b9a31c8..0faa28d 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -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; @@ -46,35 +44,20 @@ 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); @@ -82,9 +65,8 @@ module.exports = () => { } }; this.token = token; - this.sandbox = sandbox; this.data = contextData; - sandbox.context = new Proxy(contextData, contextHandler); + this.context = new Proxy(contextData, contextHandler); } } @@ -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); @@ -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); } } @@ -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 }); }; diff --git a/lib/worker.js b/lib/worker.js index a35c660..59ff23c 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -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 => {