UNPKG

node-q

Version:
286 lines (277 loc) 7.73 kB
var libc = require("./lib/c.js"); var net = require("net"); var tls = require("tls"); var events = require("events"); var util = require("util"); var assert = require("./lib/assert.js"); var typed = require("./lib/typed.js"); function Connection(socket, nanos2date, flipTables, emptyChar2null, long2number) { "use strict"; events.EventEmitter.call(this); this.socket = socket; this.nanos2date = nanos2date; this.flipTables = flipTables; this.emptyChar2null = emptyChar2null; this.long2number = long2number; this.nextRequestNo = 1; this.nextResponseNo = 1; var self = this; this.socket.on("end", function() { self.emit("end"); }); this.socket.on("timeout", function() { self.emit("timeout"); }); this.socket.on("error", function(err) { self.emit("error", err); }); this.socket.on("close", function(had_error) { self.emit("close", had_error); }); } util.inherits(Connection, events.EventEmitter); Connection.prototype.listen = function() { "use strict"; var self = this; this.chunk = new Buffer(0); this.socket.on("data", function(inbuffer) { var buffer, length, // current msg length o, // deserialized object err, // deserialize error responseNo; if (self.chunk.length !== 0) { buffer = new Buffer(self.chunk.length + inbuffer.length); self.chunk.copy(buffer); inbuffer.copy(buffer, self.chunk.length); } else { buffer = inbuffer; } while (buffer.length >= 8) { length = buffer.readUInt32LE(4); if (buffer.length >= length) { try { o = libc.deserialize(buffer, self.nanos2date, self.flipTables, self.emptyChar2null, self.long2number); err = undefined; } catch (e) { o = null; err = e; } if (buffer.readUInt8(1) === 2) { // MsgType: 2 := response responseNo = self.nextResponseNo; self.nextResponseNo += 1; self.emit("response:" + responseNo, err, o); } else { if (err === undefined && Array.isArray(o) && o[0] === "upd") { events.EventEmitter.prototype.emit.apply(self, o); } else { responseNo = self.nextResponseNo; self.nextResponseNo += 1; self.emit("response:" + responseNo, err, o); } } if (buffer.length > length) { buffer = buffer.slice(length); } else { buffer = new Buffer(0); } } else { break; } } self.chunk = buffer; }); }; Connection.prototype.auth = function(auth, cb) { "use strict"; var n = Buffer.byteLength(auth, "ascii"), b = new Buffer(n + 2), self = this; b.write(auth, 0, n, "ascii"); // auth (username:password) b.writeUInt8(0x3, n); // capability byte (compression, timestamp, timespan) http://code.kx.com/wiki/Reference/ipcprotocol#Handshake b.writeUInt8(0x0, n+1); // zero terminated this.socket.write(b); this.socket.once("data", function(buffer) { if (buffer.length === 1) { if (buffer[0] >= 1) { // capability byte must support at least (compression, timestamp, timespan) http://code.kx.com/wiki/Reference/ipcprotocol#Handshake self.listen(); cb(); } else { cb(new Error("Invalid capability byte from server")); } } else { cb(new Error("Invalid auth response from server")); } }); }; Connection.prototype.k = function(s, cb) { "use strict"; cb = arguments[arguments.length - 1]; assert.func(cb, "cb"); var self = this, payload, b, requestNo = this.nextRequestNo; this.nextRequestNo += 1; if (arguments.length === 1) { // Listen for async responses self.once("response:" + requestNo, function(err, o) { cb(err, o); }); } else { assert.string(s, "s"); if (arguments.length === 2) { payload = s; } else { payload = Array.prototype.slice.call(arguments, 0, arguments.length - 1); } b = libc.serialize(payload); b.writeUInt8(0x1, 1); // MsgType: 1 := sync this.socket.write(b, function() { self.once("response:" + requestNo, function(err, o) { cb(err, o); }); }); } }; Connection.prototype.ks = function(s, cb) { "use strict"; assert.string(s, "s"); cb = arguments[arguments.length - 1]; assert.func(cb, "cb"); var payload, b; if (arguments.length === 2) { payload = s; } else { payload = Array.prototype.slice.call(arguments, 0, arguments.length - 1); } b = libc.serialize(payload); this.socket.write(b, function() { cb(); }); }; Connection.prototype.close = function(cb) { "use strict"; assert.optionalFunc(cb, "cb"); this.socket.once("close", function() { if (cb) { cb(); } }); this.socket.end(); }; function connect(params, cb) { "use strict"; var auth, errorcb, closecb, socket, error = false, close = false; if (typeof params !== "object") { params = {}; if (arguments.length === 2) { params.unixSocket = arguments[0]; cb = arguments[1]; } else if (arguments.length === 3) { params.host = arguments[0]; params.port = arguments[1]; cb = arguments[2]; } else if (arguments.length === 5) { params.host = arguments[0]; params.port = arguments[1]; params.user = arguments[2]; params.password = arguments[3]; cb = arguments[4]; } else { throw new Error("only two, three or five arguments allowed"); } } assert.object(params, "params"); assert.optionalString(params.host, "params.host"); assert.optionalNumber(params.port, "params.port"); assert.optionalString(params.user, "params.user"); assert.optionalString(params.password, "password"); assert.optionalObject(params.ca, "params.ca"); assert.optionalBool(params.socketNoDelay, "params.socketNoDelay"); assert.optionalNumber(params.socketTimeout, "params.socketTimeout"); assert.optionalBool(params.nanos2date, "params.nanos2date"); assert.optionalBool(params.flipTables, "params.flipTables"); assert.optionalBool(params.emptyChar2null, "params.emptyChar2null"); assert.optionalBool(params.long2number, "params.long2number"); assert.optionalString(params.unixSocket, "params.unixSocket"); assert.optionalBool(params.useTLS, "params.useTLS"); if (params.user !== undefined) { assert.string(params.password, "password"); auth = params.user + ":" + params.password; } else { auth = "anonymous"; } assert.func(cb, "cb"); errorcb = function(err) { error = true; cb(err); }; closecb = function() { close = true; cb(new Error("Connection closes (wrong auth?)")); }; var socketArgs; if (params.useTLS) { socketArgs = {}; if (params.unixSocket) { socketArgs.path = params.unixSocket; } else { socketArgs.port = params.port; socketArgs.host = params.host; socketArgs.ca = params.ca; } } else { socketArgs = []; if (params.unixSocket) { socketArgs.push(params.unixSocket); } else { socketArgs.push(params.port, params.host); } } var connectionCBfunc = function() { socket.removeListener("error", errorcb); if (error === false) { socket.once("close", closecb); var con = new Connection(socket, params.nanos2date, params.flipTables, params.emptyChar2null, params.long2number); con.once("error", function(err) { socket.removeListener("close", closecb); cb(err); }); con.auth(auth, function() { socket.removeListener("close", closecb); if (close === false) { cb(undefined, con); } }); } }; if (params.useTLS) { socket = tls.connect(socketArgs, connectionCBfunc); } else { socketArgs.push(connectionCBfunc); socket = net.connect.apply(null, socketArgs); } if (params.socketTimeout !== undefined) { socket.setTimeout(params.socketTimeout); } if (params.socketNoDelay !== undefined) { socket.setNoDelay(params.socketNoDelay); } socket.once("error", errorcb); } exports.connect = connect; // export typed API Object.keys(typed).forEach(function(k) { "use strict"; if (/^[a-z]*$/.test(k[0])) { exports[k] = typed[k]; } });