sqlite3-cluster
Advanced tools
Comparing version 1.1.1 to 2.1.0
507
client.js
'use strict'; | ||
/*global Promise*/ | ||
// I'm not a fan of singletons (as they are EVIL). | ||
// However, this module is, by intent, meant to | ||
// represent a worker process in a cluster. | ||
// Thus, it is appropriately a singleton. | ||
var processWebSocket; | ||
var promiseWebSocket; | ||
var PromiseA = Promise; | ||
try { | ||
PromiseA = require('bluebird').Promise; | ||
} catch(e) { | ||
console.warn("For better Promise support please use bluebird"); | ||
} | ||
// TODO iterate over the prototype | ||
// translate request / response | ||
var sqlite3real = require('sqlite3'); | ||
/* | ||
function createConnection(opts) { | ||
var server = ; | ||
return server.create(opts).then(function () { | ||
// created and listening | ||
}); | ||
} | ||
*/ | ||
function startServer(opts) { | ||
return require('./server').create(opts).then(function (server) { | ||
function startServer(opts, verbs, myServer) { | ||
if (myServer) { | ||
return verbs.Promise.resolve(myServer); | ||
} | ||
return require('./server').create(opts, verbs).then(function (server) { | ||
// this process doesn't need to connect to itself | ||
@@ -33,15 +21,48 @@ // through a socket | ||
function getConnection(opts) { | ||
return new PromiseA(function (resolve) { | ||
// connection is scoped per-process, nothing more | ||
function getConnection(opts, verbs, mySocket, retry) { | ||
function incr(ws) { | ||
if (!ws.__count) { | ||
ws.__count = 0; | ||
} | ||
ws.__count += 1; | ||
return ws; | ||
} | ||
if (!retry && (mySocket || processWebSocket)) { | ||
promiseWebSocket = verbs.Promise.resolve(mySocket || processWebSocket); | ||
return promiseWebSocket.then(incr); | ||
} | ||
if (!retry && promiseWebSocket) { | ||
return promiseWebSocket.then(incr); | ||
} | ||
promiseWebSocket = new verbs.Promise(function (resolve) { | ||
//setTimeout(function () { | ||
var WebSocket = require('ws'); | ||
var ws = new WebSocket('ws+unix:' + opts.sock); | ||
// TODO how to include path and such? | ||
// http://unix:/absolute/path/to/unix.socket:/request/path | ||
// https://github.com/websockets/ws/issues/236 | ||
var ws; | ||
var pathname = '/' + (require('cluster').worker||{}).id + '/' + opts.ipcKey; | ||
var queryparams = { | ||
ipcKey: opts.ipcKey | ||
, ipc_key: opts.ipcKey | ||
, worker_id: (require('cluster').worker||{}).id | ||
}; | ||
var search = require('querystring').stringify(queryparams); | ||
var wspath = 'ws+unix://' + opts.sock + ':' + pathname + '?' + search; | ||
// https://github.com/websockets/ws/blob/master/doc/ws.md#unix-domain-sockets | ||
// ws+unix:///absolule/path/to/uds_socket:/pathname?search_params | ||
console.log('ws connect', wspath); | ||
ws = new WebSocket(wspath); | ||
ws.on('error', function (err) { | ||
console.error('[ERROR] ws connection failed, retrying'); | ||
console.error(err); | ||
console.error(err.stack || err); | ||
function retry() { | ||
// TODO eventually throw up | ||
setTimeout(function () { | ||
getConnection(opts).then(resolve, retry); | ||
getConnection(opts, verbs, mySocket, true).then(resolve, retry); | ||
}, 100 + (Math.random() * 250)); | ||
@@ -52,3 +73,3 @@ } | ||
console.log('[NO SERVER] attempting to create a server #######################'); | ||
return startServer(opts).then(function (client) { | ||
return startServer(opts, verbs).then(function (client) { | ||
// ws.masterClient = client; | ||
@@ -58,3 +79,3 @@ resolve({ masterClient: client }); | ||
console.error('[ERROR] failed to connect to sqlite3-cluster service. retrying...'); | ||
console.error(err); | ||
console.error(err.stack || err); | ||
retry(); | ||
@@ -68,62 +89,272 @@ }); | ||
ws.on('open', function () { | ||
resolve(ws); | ||
}); | ||
//}, 100 + (Math.random() * 250)); | ||
}).then(function (ws) { | ||
if (!processWebSocket) { | ||
processWebSocket = ws; | ||
} | ||
return ws; | ||
}); | ||
return promiseWebSocket.then(incr); | ||
} | ||
function create(opts) { | ||
if (!opts.sock) { | ||
opts.sock = opts.filename + '.sock'; | ||
} | ||
module.exports.createClientFactory = function (conf, verbs, _socket) { | ||
// TODO distinguish between defaults and mandates | ||
var promise; | ||
var numcpus = require('os').cpus().length; | ||
if (opts.standalone || (1 === numcpus && !opts.serve && !opts.connect)) { | ||
return require('./wrapper').create(opts); | ||
if (!conf.ipcKey) { | ||
throw new Error("[E_NO_IPCKEY] Your config must specify an ipcKey."); | ||
} | ||
function retryServe() { | ||
return startServer(opts).then(function (client) { | ||
// ws.masterClient = client; | ||
return { masterClient: client }; | ||
}, function (err) { | ||
console.error('[ERROR] retryServe()'); | ||
console.error(err); | ||
retryServe(); | ||
}); | ||
return { | ||
create: function (opts, _s) { | ||
var copy = {}; | ||
if (_socket && _s) { | ||
throw new Error("[E_USR_SOCKET] Your parent has decided that you may not choose your own SOCKET. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.key && conf.key) { | ||
throw new Error("[E_USR_KEY] Your parent has decided that you may not choose your own KEY. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.algo && conf.algo) { | ||
throw new Error("[E_USR_ALGO] Your parent has decided that you may not choose your own ALGO. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.bits && conf.bits) { | ||
throw new Error("[E_USR_BITS] Your parent has decided that you may not choose your own BITS. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.dirname && conf.dirname) { | ||
throw new Error("[E_USR_TENANT] Your parent has decided that you may not choose your own TENANT. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.tenant && conf.tenant) { | ||
throw new Error("[E_USR_TENANT] Your parent has decided that you may not choose your own TENANT. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.prefix && conf.prefix) { | ||
throw new Error("[E_USR_PREFIX] Your parent has decided that you may not choose your own PREFIX. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.suffix && conf.suffix) { | ||
throw new Error("[E_USR_SUFFIX] Your parent has decided that you may not choose your own SUFFIX. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.ext && conf.ext) { | ||
throw new Error("[E_USR_EXT] Your parent has decided that you may not choose your own EXT. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.serve) { | ||
throw new Error("[E_USR_SERVE] Your parent have forbidden you to SERVE. Don't get mad at me, take it up with them."); | ||
} | ||
if (opts.sock && conf.sock) { | ||
throw new Error("[E_USR_SERVE] Your parent have forbidden you to choose your own SOCK. Don't get mad at me, take it up with them."); | ||
} | ||
Object.keys(conf).forEach(function (key) { | ||
copy[key] = conf[key]; | ||
}); | ||
Object.keys(opts).forEach(function (key) { | ||
copy[key] = opts[key]; | ||
}); | ||
if (!verbs) { | ||
verbs = { | ||
Promise: null | ||
}; | ||
} | ||
if (!verbs.Promise) { | ||
verbs.Promise = require('bluebird'); | ||
} | ||
copy.connect = (require('cluster').worker && true); | ||
copy.sock = conf.sock; | ||
copy.tenant = conf.tenant; | ||
copy.ipcKey = conf.ipcKey; | ||
return module.exports.create(copy, verbs, _socket || _s || processWebSocket); | ||
} | ||
}; | ||
}; | ||
module.exports.create = function (opts, verbs, mySocket) { | ||
if (!verbs) { | ||
verbs = {}; | ||
} | ||
if (opts.serve) { | ||
promise = retryServe(); | ||
} else { | ||
promise = getConnection(opts); | ||
var PromiseA = verbs && verbs.Promise || require('bluebird'); | ||
verbs.Promise = PromiseA; | ||
// TODO iterate over the prototype | ||
// translate request / response | ||
var sqlite3real = require('sqlite3'); | ||
if (!mySocket) { | ||
mySocket = processWebSocket; | ||
} | ||
/* | ||
if (opts.connect) { | ||
} | ||
*/ | ||
// TODO maybe use HTTP POST instead? | ||
return promise.then(function (ws) { | ||
if (ws.masterClient) { | ||
return ws.masterClient; | ||
function create(opts) { | ||
function retryServe() { | ||
return startServer(opts, verbs).then(function (client) { | ||
// ws.masterClient = client; | ||
return { masterClient: client }; | ||
}, function (err) { | ||
console.error('[ERROR] retryServe()'); | ||
console.error(err.stack || err); | ||
retryServe(); | ||
}); | ||
} | ||
var db = {}; | ||
var proto = sqlite3real.Database.prototype; | ||
var messages = []; | ||
if (opts.serve) { | ||
return retryServe(opts).then(function (servers) { | ||
return servers.masterClient; | ||
}); | ||
} | ||
function init(opts) { | ||
return new PromiseA(function (resolve, reject) { | ||
var id = Math.random(); | ||
if (!opts.tenant) { | ||
opts.tenant = ""; | ||
} | ||
if (!opts.subtenant) { | ||
opts.subtenant = ""; | ||
} | ||
if (!opts.prefix) { | ||
opts.prefix = ''; | ||
} | ||
if (!opts.suffix) { | ||
opts.suffix = ''; | ||
} | ||
if (!opts.ext) { | ||
opts.ext = '.sqlite3'; // also .sqlcipher | ||
} | ||
if (!opts.dbname) { | ||
throw new Error("Please specify opts.dbname as the name of the database"); | ||
} | ||
var promise; | ||
var numcpus = require('os').cpus().length; | ||
// if we're only on one core, use the lib directly, no socket | ||
if (opts.standalone || (1 === numcpus && !opts.serve && !opts.connect)) { | ||
return require('./wrapper').create(opts, verbs); | ||
} | ||
if (!opts.sock) { | ||
throw new Error("Please specify opts.sock as the path to the master socket. '/tmp/sqlite3-cluster' would do nicely."); | ||
} | ||
promise = getConnection(opts, verbs, mySocket, true).then(function (socket) { | ||
mySocket = socket; | ||
return mySocket; | ||
}); | ||
// TODO maybe use HTTP POST instead? | ||
return promise.then(function (ws) { | ||
var db = {}; | ||
var proto = sqlite3real.Database.prototype; | ||
var messages = []; | ||
var idprefix = require('crypto').randomBytes(12).toString('base64'); | ||
var idcount = 0; | ||
function genId() { | ||
idcount += 1; | ||
return idprefix + idcount; | ||
} | ||
function init(iopts) { | ||
//console.log('CLIENT INIT'); | ||
if (db._initPromise) { | ||
return db._initPromise; | ||
} | ||
db._initPromise = new PromiseA(function (resolve, reject) { | ||
// TODO needs to reject by a timeout | ||
var id = genId(); | ||
ws.send(JSON.stringify({ | ||
type: 'init' | ||
, args: [{ | ||
// encryption | ||
bits: opts.bits || iopts.bits | ||
, algorithm: opts.algo || opts.algorithm || iopts.algorithm || iopts.algo | ||
, algo: opts.algo || opts.algorithm || iopts.algorithm || iopts.algo | ||
, encmode: opts.mode || iopts.mode | ||
}] | ||
, func: 'init' | ||
// db | ||
, dirname: opts.dirname | ||
, prefix: opts.prefix | ||
, subtenant: opts.subtenant | ||
, tenant: opts.tenant | ||
, dbname: opts.dbname | ||
, suffix: opts.suffix | ||
, ext: opts.ext | ||
// session | ||
, id: id | ||
})); | ||
function onMessage(data) { | ||
var cmd; | ||
try { | ||
cmd = JSON.parse(data.toString('utf8')); | ||
} catch(e) { | ||
console.error('[ERROR] in client, from sql server parse json'); | ||
console.error(e.stack || e); | ||
console.error(data); | ||
console.error(); | ||
// ignore this message, it came out of order | ||
return reject(new Error("suxors to rejexors parse")); | ||
} | ||
if (cmd.id !== id) { | ||
// ignore this message, it came out of order | ||
return; | ||
} | ||
if (cmd.self) { | ||
cmd.args = [db]; | ||
} | ||
messages.splice(messages.indexOf(onMessage), 1); | ||
if ('error' === cmd.type) { | ||
//console.log('ERROR ARGS'); | ||
//console.log(cmd); | ||
reject(cmd.args[0]); | ||
return; | ||
} | ||
//console.log('CLIENT RESOLVING INIT'); | ||
resolve(cmd.args[0]); | ||
return; | ||
} | ||
messages.push(onMessage); | ||
}); | ||
return db._initPromise; | ||
} | ||
function rpcThunk(fname, args) { | ||
var id; | ||
var cb; | ||
if ('function' === typeof args[args.length - 1]) { | ||
id = genId(); | ||
cb = args.pop(); | ||
} | ||
ws.send(JSON.stringify({ | ||
type: 'init' | ||
, args: [opts] | ||
, func: 'init' | ||
, filename: opts.filename | ||
type: 'rpc' | ||
, func: fname | ||
, args: args | ||
, dirname: opts.dirname | ||
, prefix: opts.prefix | ||
, subtenant: opts.subtenant | ||
, tenant: opts.tenant | ||
, dbname: opts.dbname | ||
, suffix: opts.suffix | ||
, ext: opts.ext | ||
, id: id | ||
})); | ||
if (!cb) { | ||
return; | ||
} | ||
function onMessage(data) { | ||
@@ -136,3 +367,3 @@ var cmd; | ||
console.error('[ERROR] in client, from sql server parse json'); | ||
console.error(e); | ||
console.error(e.stack || e); | ||
console.error(data); | ||
@@ -146,2 +377,3 @@ console.error(); | ||
if (cmd.id !== id) { | ||
// ignore this message, it probably came out of order | ||
return; | ||
@@ -153,111 +385,68 @@ } | ||
} | ||
//console.log('RESOLVING RPC', cmd.this, cmd.args); | ||
cb.apply(cmd.this, cmd.args); | ||
messages.splice(messages.indexOf(onMessage), 1); | ||
if ('error' === cmd.type) { | ||
reject(cmd.args[0]); | ||
return; | ||
if ('on' !== fname) { | ||
var index = messages.indexOf(onMessage); | ||
messages.splice(index, 1); | ||
} | ||
resolve(cmd.args[0]); | ||
} | ||
messages.push(onMessage); | ||
}); | ||
} | ||
function rpcThunk(fname, args) { | ||
var id; | ||
var cb; | ||
if ('function' === typeof args[args.length - 1]) { | ||
id = Math.random(); | ||
cb = args.pop(); | ||
} | ||
ws.send(JSON.stringify({ | ||
type: 'rpc' | ||
, func: fname | ||
, args: args | ||
, filename: opts.filename | ||
, id: id | ||
})); | ||
db.sanitize = require('./wrapper').sanitize; | ||
db.escape = require('./wrapper').escape; | ||
if (!cb) { | ||
return; | ||
} | ||
// TODO get methods from server (cluster-store does this) | ||
// instead of using the prototype | ||
Object.keys(sqlite3real.Database.prototype).forEach(function (key) { | ||
function onMessage(data) { | ||
var cmd; | ||
if ('function' === typeof proto[key]) { | ||
db[key] = function () { | ||
rpcThunk(key, Array.prototype.slice.call(arguments)); | ||
}; | ||
} | ||
}); | ||
try { | ||
cmd = JSON.parse(data.toString('utf8')); | ||
} catch(e) { | ||
console.error('[ERROR] in client, from sql server parse json'); | ||
console.error(e); | ||
console.error(data); | ||
console.error(); | ||
db.init = init; | ||
//ws.send(JSON.stringify({ type: 'error', value: { message: e.message, code: "E_PARSE_JSON" } })); | ||
return; | ||
} | ||
ws.on('message', function (data) { | ||
messages.forEach(function (fn) { | ||
try { | ||
fn(data); | ||
} catch(e) { | ||
console.error("[ERROR] ws.on('message', fn) (multi-callback)"); | ||
console.error(e.stack || e); | ||
// ignore | ||
} | ||
}); | ||
}); | ||
if (cmd.id !== id) { | ||
return; | ||
} | ||
// serialize | ||
// parallel | ||
db.serialize = db.parallel = function () { | ||
throw new Error('NOT IMPLEMENTED in SQLITE3-remote'); | ||
}; | ||
if (cmd.self) { | ||
cmd.args = [db]; | ||
db.close = function (fn) { | ||
ws.__count -= 1; | ||
if (!ws.__count) { | ||
// waiting for https://github.com/websockets/ws/issues/613 to land | ||
// 1000 means 'normal' https://github.com/websockets/ws/blob/master/lib/ErrorCodes.js | ||
ws.close(1000, null, fn); | ||
} | ||
cb.apply(cmd.this, cmd.args); | ||
}; | ||
if ('on' !== fname) { | ||
var index = messages.indexOf(onMessage); | ||
messages.splice(index, 1); | ||
} | ||
if (opts.init) { | ||
return db.init(opts); | ||
} | ||
messages.push(onMessage); | ||
} | ||
db.sanitize = require('./wrapper').sanitize; | ||
db.escape = require('./wrapper').escape; | ||
// TODO get methods from server (cluster-store does this) | ||
// instead of using the prototype | ||
Object.keys(sqlite3real.Database.prototype).forEach(function (key) { | ||
if ('function' === typeof proto[key]) { | ||
db[key] = function () { | ||
rpcThunk(key, Array.prototype.slice.call(arguments)); | ||
}; | ||
} | ||
return db; | ||
}); | ||
} | ||
db.init = init; | ||
return create(opts); | ||
}; | ||
ws.on('message', function (data) { | ||
messages.forEach(function (fn) { | ||
try { | ||
fn(data); | ||
} catch(e) { | ||
console.error("[ERROR] ws.on('message', fn) (multi-callback)"); | ||
console.error(e); | ||
// ignore | ||
} | ||
}); | ||
}); | ||
// serialize | ||
// parallel | ||
db.serialize = db.parallel = function () { | ||
throw new Error('NOT IMPLEMENTED in SQLITE3-remote'); | ||
}; | ||
return db; | ||
}); | ||
} | ||
module.exports.sanitize = require('./wrapper').sanitize; | ||
module.exports.escape = require('./wrapper').escape; | ||
module.exports.create = create; |
@@ -23,1 +23,3 @@ 'use strict'; | ||
module.exports.create = create; | ||
module.exports.createServer = sqlite3.createServer; | ||
module.exports.createMasterClient = sqlite3.createMasterClient; |
{ | ||
"name": "sqlite3-cluster", | ||
"version": "1.1.1", | ||
"version": "2.1.0", | ||
"description": "A wrapper to enable the use of sqlite3 with node cluster via a socket server (i.e. for Raspberry Pi 2).", | ||
@@ -29,4 +29,4 @@ "main": "index.js", | ||
"sqlite3": "^3.0.9", | ||
"ws": "^0.7.2" | ||
"ws": "^2.3.1" | ||
} | ||
} |
201
server.js
'use strict'; | ||
/*global Promise*/ | ||
var PromiseA = Promise; | ||
try { | ||
PromiseA = require('bluebird').Promise; | ||
} catch(e) { | ||
console.warn("For better Promise support please use bluebird"); | ||
} | ||
var PromiseA = require('bluebird').Promise; | ||
var wsses = {}; | ||
function createApp(server, options) { | ||
function createApp(servers, options) { | ||
var url = require('url'); | ||
var wss = servers.wss; | ||
//var server = servers.server; | ||
if (wsses[options.filename]) { | ||
return PromiseA.resolve(wsses[options.filename]); | ||
//var express = require('express'); | ||
//var app = express(); | ||
/* | ||
function app(req, res) { | ||
res.end('NOT IMPLEMENTED'); | ||
} | ||
*/ | ||
return require('./wrapper').create(options).then(function (db) { | ||
wss.on('connection', function (ws) { | ||
var location = url.parse(ws.upgradeReq.url, true); | ||
// you might use location.query.access_token to authenticate or share sessions | ||
// or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312 | ||
var url = require('url'); | ||
//var express = require('express'); | ||
//var app = express(); | ||
var wss = server.wss; | ||
if (!options.ipcKey) { | ||
console.warn("[S] [SECURITY] please include { ipcKey: crypto.randomBytes(16).toString('base64') }" | ||
+ " in your options and pass it from master to worker processes with worker.send()"); | ||
ws._authorized = true; | ||
} else { | ||
ws._authorized = (options.ipcKey === (location.query.ipcKey || location.query.ipc_key)); | ||
} | ||
function app(req, res) { | ||
res.end('NOT IMPLEMENTED'); | ||
if (!ws._authorized) { | ||
ws.send(JSON.stringify({ error: { message: "Unauthorized: ipc_key does not match", code: 'E_UNAUTHORIZED_IPCKEY' } })); | ||
ws.close(); | ||
return; | ||
} | ||
wss.on('connection', function (ws) { | ||
if (!wss.__count) { | ||
wss.__count = 0; | ||
} | ||
wss.__count += 1; | ||
var location = url.parse(ws.upgradeReq.url, true); | ||
// you might use location.query.access_token to authenticate or share sessions | ||
// or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312 | ||
function decrWs() { | ||
wss.__count -= 1; | ||
if (!wss.__count) { | ||
console.log('[S] client count is zero, but server will be left open'); | ||
/* | ||
wss.close(function () { | ||
console.log('wss.closed'); | ||
}); | ||
server.close(function () { | ||
console.log('server closed, but will not exit due to bug? in wss.close()'); | ||
process.exit(0); | ||
}); | ||
*/ | ||
} | ||
} | ||
ws.__session_id = location.query.session_id || Math.random(); | ||
ws.on('error', function (err) { | ||
console.error('[S] [WebSocket error]'); | ||
console.error(err.stack); | ||
decrWs(); | ||
}); | ||
ws.on('close', decrWs); | ||
ws.on('message', function (buffer) { | ||
var cmd; | ||
var promise; | ||
ws.on('message', function (buffer) { | ||
var cmd; | ||
try { | ||
cmd = JSON.parse(buffer.toString('utf8')); | ||
} catch(e) { | ||
console.error('[S] [ERROR] parse json'); | ||
console.error(e.stack || e); | ||
console.error(buffer); | ||
console.error(); | ||
ws.send(JSON.stringify({ type: 'error', value: { message: e.message, code: "E_PARSE_JSON" } })); | ||
return; | ||
} | ||
try { | ||
cmd = JSON.parse(buffer.toString('utf8')); | ||
} catch(e) { | ||
console.error('[ERROR] parse json'); | ||
console.error(e); | ||
console.error(buffer); | ||
console.error(); | ||
ws.send(JSON.stringify({ type: 'error', value: { message: e.message, code: "E_PARSE_JSON" } })); | ||
return; | ||
} | ||
//console.log('cmd'); | ||
//console.log(cmd); | ||
// caching and create logic happens in the wrapper stored here below | ||
promise = require('./wrapper').create(cmd && cmd.dbname && cmd || options).then(function (db) { | ||
switch(cmd.type) { | ||
case 'init': | ||
//console.log('[S] init', cmd); | ||
db[cmd.func].apply(db, cmd.args).then(function () { | ||
@@ -62,2 +97,3 @@ var args = Array.prototype.slice.call(arguments); | ||
//console.log('[INIT HAPPENING]'); | ||
ws.send(JSON.stringify({ | ||
@@ -73,8 +109,9 @@ id: cmd.id | ||
case 'rpc': | ||
if (!db._initialized) { | ||
if ('close' !== cmd.func && !db._initialized) { | ||
//console.log('[RPC NOT HAPPENING]'); | ||
ws.send(JSON.stringify({ | ||
type: 'error' | ||
, id: cmd.id | ||
, args: [{ message: 'database has not been initialized' }] | ||
, error: { message: 'database has not been initialized' } | ||
, args: [{ message: 'database has not been initialized', code: 'E_NO_INIT' }] | ||
, error: { message: 'database has not been initialized', code: 'E_NO_INIT' } | ||
})); | ||
@@ -84,3 +121,3 @@ return; | ||
cmd.args.push(function () { | ||
cmd.args.push(function (err) { | ||
var args = Array.prototype.slice.call(arguments); | ||
@@ -94,7 +131,9 @@ var myself; | ||
//console.log('[RPC HAPPENING]', args, cmd.id); | ||
ws.send(JSON.stringify({ | ||
this: this | ||
this: (!err && this !== global) ? this : {} | ||
, args: args | ||
, self: myself | ||
, id: cmd.id | ||
, error: err | ||
})); | ||
@@ -112,37 +151,73 @@ }); | ||
}); | ||
}); | ||
}); | ||
ws.send(JSON.stringify({ type: 'session', value: ws.__session_id })); | ||
// wsses[options.sock] = app; | ||
return PromiseA.resolve(); | ||
} | ||
function newSocket(options) { | ||
if (wsses[options.sock]) { | ||
return PromiseA.resolve(wsses[options.sock]); | ||
} | ||
wsses[options.sock] = new PromiseA(function (resolve) { | ||
var fs = require('fs'); | ||
fs.unlink(options.sock, function () { | ||
var server = require('http').createServer(); | ||
// ignore error when socket doesn't exist | ||
server.listen(options.sock, function () { | ||
resolve(server); | ||
}); | ||
}); | ||
}).then(function (server) { | ||
var WebSocketServer = require('ws').Server; | ||
var servers = { | ||
server: require('http').createServer() | ||
, wss: new WebSocketServer({ server: server }) | ||
}; | ||
return createApp(servers, options).then(function (/*app*/) { | ||
// server.on('request', app); | ||
wsses[options.sock] = servers; | ||
app.masterClient = db; | ||
wsses[options.filename] = app; | ||
return wsses[options.sock]; | ||
}); | ||
}); | ||
return app; | ||
return wsses[options.sock]; | ||
} | ||
function createMasterClient(options) { | ||
return require('./wrapper').create(options, null).then(function (db) { | ||
return db; | ||
}); | ||
} | ||
function create(options) { | ||
var server = require('http').createServer(); | ||
var WebSocketServer = require('ws').Server; | ||
var wss = new WebSocketServer({ server: server }); | ||
//var port = process.env.PORT || process.argv[0] || 4080; | ||
function createServer(options) { | ||
if (!options.sock) { | ||
throw new Error("Please provide options.sock as the socket to serve from"); | ||
} | ||
options.server = options.sock; | ||
var fs = require('fs'); | ||
var ps = []; | ||
return newSocket(options).then(function () { | ||
var result = {}; | ||
ps.push(new PromiseA(function (resolve) { | ||
fs.unlink(options.sock, function () { | ||
// ignore error when socket doesn't exist | ||
server.listen(options.sock, resolve); | ||
Object.keys(wsses[options.sock]).forEach(function (key) { | ||
result[key] = wsses[options.sock][key]; | ||
}); | ||
})); | ||
}); | ||
} | ||
ps.push(createApp({ server: server, wss: wss }, options).then(function (app) { | ||
server.on('request', app); | ||
return { masterClient: app.masterClient }; | ||
})); | ||
function create(options) { | ||
return createServer(options).then(function (result) { | ||
if (!options.dbname) { | ||
return result; | ||
} | ||
return PromiseA.all(ps).then(function (results) { | ||
return results[1]; | ||
return createMasterClient(options).then(function (db) { | ||
result.masterClient = db; | ||
return result; | ||
}); | ||
}); | ||
@@ -152,1 +227,3 @@ } | ||
module.exports.create = create; | ||
module.exports.createMasterClient = createMasterClient; | ||
module.exports.createServer = createServer; |
'use strict'; | ||
/*global Promise*/ | ||
var PromiseA = Promise; | ||
try { | ||
PromiseA = require('bluebird').Promise; | ||
} catch(e) { | ||
console.warn("For better Promise support please use bluebird"); | ||
} | ||
var sqlite3 = require('sqlite3'); | ||
// TODO expire unused dbs from cache | ||
var dbs = {}; | ||
@@ -17,5 +11,6 @@ | ||
function create(opts) { | ||
var db; | ||
function create(opts, verbs) { | ||
if (!verbs) { | ||
verbs = {}; | ||
} | ||
if (!opts) { | ||
@@ -25,15 +20,23 @@ opts = {}; | ||
if (opts.verbose) { | ||
sqlite3.verbose(); | ||
} | ||
var db; | ||
var PromiseA = verbs.Promise || require('bluebird'); | ||
var dbname = ""; | ||
if (!dbs[opts.filename]) { | ||
dbs[opts.filename] = new sqlite3.Database(opts.filename); | ||
dbname += (opts.prefix || ''); | ||
if (opts.subtenant) { | ||
dbname += opts.subtenant + '.'; | ||
} | ||
if (opts.tenant) { | ||
dbname += opts.tenant + '.'; | ||
} | ||
dbname += (opts.dbname || ''); | ||
dbname += (opts.suffix || ''); | ||
dbname += (opts.ext || ''); | ||
dbname = require('path').resolve(opts.dirname || '', dbname); | ||
db = dbs[opts.filename]; | ||
db.sanitize = sanitize; | ||
db.escape = sanitize; | ||
function initDb(newOpts) { | ||
if (dbs[dbname].initPromise) { | ||
return dbs[dbname].initPromise; | ||
} | ||
db.init = function (newOpts) { | ||
if (!newOpts) { | ||
@@ -46,4 +49,4 @@ newOpts = {}; | ||
return new PromiseA(function (resolve, reject) { | ||
if (db._initialized) { | ||
dbs[dbname].initPromise = new PromiseA(function (resolve, reject) { | ||
if (dbs[dbname].db._initialized) { | ||
resolve(db); | ||
@@ -55,4 +58,7 @@ return; | ||
if (!bits) { | ||
db._initialized = true; | ||
//console.log("INITIALIZED WITHOUT KEY"); | ||
//console.log(opts); | ||
dbs[dbname].db._initialized = true; | ||
} | ||
dbs[dbname].db = db; | ||
resolve(db); | ||
@@ -64,3 +70,2 @@ return; | ||
db._initialized = true; | ||
db.serialize(function () { | ||
@@ -91,2 +96,5 @@ var setup = []; | ||
// restore original functions | ||
dbs[dbname].db._initialized = true; | ||
dbs[dbname].db = db; | ||
resolve(db); | ||
@@ -96,5 +104,37 @@ }, reject); | ||
}); | ||
}; | ||
return db.init(opts); | ||
return dbs[dbname].initPromise; | ||
} | ||
function newDb() { | ||
// dbs[dbname] = db // | ||
db = new sqlite3.Database(dbname); | ||
db.init = initDb; | ||
db.sanitize = sanitize; | ||
db.escape = sanitize; | ||
if (opts.verbose) { | ||
sqlite3.verbose(); | ||
} | ||
return db; | ||
} | ||
// Could be any of: | ||
// * db object | ||
// * init promise | ||
if (!dbs[dbname]) { | ||
dbs[dbname] = { db: newDb() }; | ||
} | ||
if (dbs[dbname].db._initialized) { | ||
return PromiseA.resolve(dbs[dbname].db); | ||
} | ||
if (opts.init || ('init' === opts.type) || (opts.bits && opts.key)) { | ||
dbs[dbname].initPromise = db.init(opts); | ||
} | ||
return dbs[dbname].initPromise || PromiseA.resolve(dbs[dbname].db); | ||
} | ||
@@ -101,0 +141,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
58009
22
1219
4
+ Addedsafe-buffer@5.0.1(transitive)
+ Addedultron@1.1.1(transitive)
+ Addedws@2.3.1(transitive)
- Removedbindings@1.2.1(transitive)
- Removedbufferutil@1.1.0(transitive)
- Removednan@1.8.4(transitive)
- Removedoptions@0.0.6(transitive)
- Removedultron@1.0.2(transitive)
- Removedutf-8-validate@1.1.0(transitive)
- Removedws@0.7.2(transitive)
Updatedws@^2.3.1