UNPKG

rethinkdbdash

Version:

A Node.js driver for RethinkDB with promises and a connection pool

248 lines (224 loc) 8.26 kB
/* * Rough version of reqlite * We use it here only with `r.expr(number)` to have a deterministic * way to predict the behavior of the pools * Do not use :) */ var net = require('net'); var protodef = require(__dirname+"/../../../lib/protodef.js"); var Query = require(__dirname+"/query.js"); var util = require('util'); var _util = require(__dirname+'/../common.js'); // Create a new TCP server -- used for tests function Server(options) { var self = this; self.authKey = ""; self.version = protodef.VersionDummy.Version.V0_4; self.protocol = protodef.VersionDummy.Protocol.JSON; // Support for JSON protocol only self.port = options['port'] || 28015; self.host = options['host'] || 'localhost'; self.numConnections = 0; self._connections = {}; self.databases = {}; self._mock = []; self.id = _util.uuid(); var index = 0; self.server = net.createServer(function(connection) { //'connection' listener self._connections[index] = new Connection(connection, self, { version: self.version, authKey: self.authKey, protocol: self.protocol, id: index }); index++ }); self.server.listen(self.port, function() { //'listening' listener }); self.server.on('error', function(error) { }); } Server.prototype.close = function() { var self = this; this.server.close(); for(var id in this._connections) { this._connections[id].connection.end(); } } Server.prototype.destroy = function() { this.server.close(); for(var id in this._connections) { this._connections[id].connection.destroy(); } } Server.prototype.mockServersStatus = function(servers) { var response = []; for(var i=0; i<servers.length; i++) { var server = servers[i]; response.push({ "connection": { "time_connected":{ "$reql_type$":"TIME", "epoch_time":1425627107.941, "timezone":"+00:00" }, "time_disconnected":null }, "id":server.id, "name":'name_'+server.id, "network":{ "canonical_addresses": [ {"host":server.host,"port":server.port+1000}, {"host":"::1","port":server.port+1000} ], "cluster_port":server.port+1000, "hostname":"xone", "http_admin_port":8080, "reql_port":server.port}, "process":{ "argv": ["rethinkdb"], "cache_size_mb":2645.6015625, "pid":5065, "time_started": { "$reql_type$":"TIME", "epoch_time":1425627107.94, "timezone":"+00:00" }, "version":"rethinkdb 1.16.2-1 (GCC 4.9.2)" }, "status":"connected" }) } this._mock.push(response); } Server.prototype.cleanMockServersStatus = function(servers) { this._mock = []; } function Connection(connection, server, options) { var self = this; self.connection = connection; self.options = options; self.server = server; self.id = options.id self.open = false; self.buffer = new Buffer(0); self.version; self.auth; self.protocol; self.connection.on('connect', function() { self.open = true; self.numConnections++; }); self.connection.on('data', function(data) { self.buffer = Buffer.concat([self.buffer, data]) self.read(); }); self.connection.on("end", function() { delete self.server._connections[self.id] self.numConnections--; }); } Connection.prototype.read = function() { var self = this; if (self.version === undefined) { if (self.buffer.length >= 4) { var version = self.buffer.readUInt32LE(0) self.buffer = self.buffer.slice(4); if (version !== self.options.version) { //TODO Send appropriate error self.connection.end(); } else { self.version = version; } self.read(); } // else, we need more data } else if (self.auth === undefined) { if (self.buffer.length >= 4) { var authKeyLength = self.buffer.readUInt32LE(0) if (self.buffer.length >= authKeyLength+4) { self.buffer = self.buffer.slice(4); var authKey = self.buffer.slice(0, authKeyLength).toString(); if (authKey !== self.options.authKey) { //TODO Send appropriate error self.connection.end(); } else { self.auth = true; self.read(); } } } } else if (self.protocol === undefined) { if (self.buffer.length >= 4) { var protocol = self.buffer.readUInt32LE(0) self.buffer = self.buffer.slice(4); if (protocol !== self.options.protocol) { //TODO Send appropriate error self.connection.end(); } else { self.protocol = protocol; } self.connection.write("SUCCESS\u0000"); self.read(); } } else { if (self.buffer.length >= 8+4) { // 8 for the token, 4 for the query's length var token = self.buffer.readUInt32LE(0); var queryLength = self.buffer.readUInt32LE(8); if (self.buffer.length >= queryLength+8+4) { self.buffer = self.buffer.slice(8+4); var queryStr = self.buffer.slice(0, queryLength).toString(); self.buffer = self.buffer.slice(queryLength); try { if (queryStr === '[1,[15,[[14,["rethinkdb"]],"server_status"]],{"db":[14,["test"]]}]') { var result = self.server._mock.shift(); var response = { t: protodef.Response.ResponseType.SUCCESS_SEQUENCE, r: result } } else { var query = JSON.parse(queryStr); var response = new Query(self.server, query).run(); } function sendResult() { var tokenBuffer = new Buffer(8); tokenBuffer.writeUInt32LE(token, 0) tokenBuffer.writeUInt32LE(0, 4) var responseBuffer = new Buffer(JSON.stringify(response)); var responseLengthBuffer = new Buffer(4); responseLengthBuffer.writeUInt32LE(responseBuffer.length, 0); try { self.connection.write(Buffer.concat([tokenBuffer, responseLengthBuffer, responseBuffer])) } catch(err) { //console.log("Failed to send back the result."); //console.log(err); } } // If a query return a number, we wait this number before // sending back the result. This simulate asynchronous, // concurrent operations. if ((response.r.length === 1) && (typeof response.r[0] === 'number') && (response.r[0] >= 0)) { setTimeout(sendResult, response.r[0]); } else { sendResult(); } } catch(err) { console.log("Fake server crashed."); console.log(err); console.log(err.stack); self.connection.write("Error, could not parse query"); } } } } } module.exports = Server;