Comparing version 0.1.4 to 0.2.0
@@ -1,6 +0,8 @@ | ||
var net = require('net') | ||
, barse = require('barse') | ||
, Serializer = require('./serializer') | ||
, Deserializer = require('./deserializer') | ||
; | ||
var net = require('net'); | ||
var barse = require('barse'); | ||
var Promise = require('any-promise'); | ||
var toPromise = require('event-to-promise'); | ||
var toStream = require('string-to-stream'); | ||
var Serializer = require('xmlrpc/lib/serializer'); | ||
var Deserializer = require('xmlrpc/lib/deserializer'); | ||
@@ -11,28 +13,15 @@ /** | ||
* @constructor | ||
* @param {Object|String} options - Server options to make the HTTP request to. | ||
* Either a URI string | ||
* (e.g. 'http://localhost:9090') or an object | ||
* with fields: | ||
* - {String} host - (optional) | ||
* - {Number} port | ||
* @param {Boolean} isSecure - True if using https for making calls, | ||
* otherwise false. | ||
* @param {Number} port | ||
* @param {String} host (optional) | ||
* @return {Client} | ||
*/ | ||
function Client(port, host, callback) { | ||
function Client(port, host) { | ||
// Invokes with new if called without | ||
if (false === (this instanceof Client)) { | ||
return new Client(port, host, callback) | ||
if ((this instanceof Client) === false) { | ||
return new Client(port, host); | ||
} | ||
// Set the options | ||
if (typeof host === 'function') { | ||
callback = host; | ||
host = 'localhost'; | ||
} | ||
this.host = host || 'localhost'; | ||
this.port = port; | ||
this.isConnected = false; | ||
@@ -42,4 +31,2 @@ this.isReady = false; | ||
this.callbacks = {}; | ||
this.connect(callback); | ||
} | ||
@@ -52,69 +39,58 @@ | ||
* Connects to the server | ||
* | ||
* @param {Function} callback (optional) - function(error) { ... } | ||
* - {Number} timeout (optional) - Timeout to connect | ||
* @return {Promise} Resolves on connection | ||
*/ | ||
Client.prototype.connect = function(callback, timeout) { | ||
if (this.isConnected) | ||
return; | ||
var self = this | ||
, timeout = typeof timeout === 'number' ? timeout : typeof callback === 'number' ? callback : 2000; // TODO: test | ||
// Mess with the callback function so we never have to `if` it later | ||
// If: no callback given, make an empty function | ||
// Else: Untested: Only call the callback once. | ||
// - E.g. If successfully connected to the server, and later some weird socket error occurs, | ||
// then the callback function will be called twice. Once with success, and once with error. | ||
// Thats what we're trying to prevent... >>> ^^^^^ | ||
if (typeof callback !== 'function') { | ||
callback = function(){}; | ||
} else { | ||
// Closure to hide `cb` | ||
(function() { // TODO: test | ||
var cb = callback; | ||
callback = function() { | ||
// Forward call | ||
cb.apply(this, arguments); | ||
// If called again, ignore | ||
callback = function() {}; | ||
} | ||
})(); | ||
Client.prototype.connect = function (timeout) { | ||
if (this.isConnected) { | ||
return Promise.resolve(this); | ||
} | ||
// Connect to the server | ||
this.socket = net.connect(this.port, this.host); | ||
// TODO: Move timeout out of onConnect? (currently timeout is a handshake timeout) | ||
this.socket.on('connect', function() { | ||
self.isConnected = true; | ||
// Timeout for handshake | ||
var to = setTimeout(function() { | ||
var err = new Error('timeout - handshake timed out'); | ||
callback(err); | ||
var self = this; | ||
timeout = timeout || 2000; | ||
return new Promise(function (resolve, reject) { | ||
// Connect to the server | ||
self.socket = net.connect(self.port, self.host); | ||
self._setupParsers(); | ||
// TODO: Move timeout out of onConnect? (currently timeout is a handshake timeout) | ||
self.socket.on('connect', function () { | ||
self.isConnected = true; | ||
// Timeout for handshake | ||
var to = setTimeout(function () { | ||
var err = new Error('timeout - handshake timed out'); | ||
self.emit('error', err); | ||
self.terminate(); | ||
}, timeout); | ||
self.on('connect', function () { | ||
clearTimeout(to); | ||
}); | ||
}); | ||
self.socket.on('error', function (err) { | ||
self.isConnected = self.isReady = false; | ||
self.emit('error', err); | ||
self.terminate(); | ||
}, timeout); | ||
self.on('connect', function() { | ||
clearTimeout(to); | ||
}); | ||
self.socket.on('close', function (hadError) { | ||
self.emit('close', hadError); | ||
}); | ||
self.on('error', function () { | ||
reject(); | ||
}); | ||
self.on('connect', function () { | ||
resolve(this); | ||
}); | ||
}); | ||
this.socket.on('error', function(err) { | ||
self.isConnected = self.isReady = false; | ||
callback(err); | ||
self.emit('error', err); | ||
}); | ||
this.socket.on('close', function(had_error) { | ||
self.emit('close', had_error); | ||
}); | ||
}; | ||
Client.prototype._setupParsers = function () { | ||
var self = this; | ||
var handshakeParser = barse() | ||
@@ -134,12 +110,11 @@ .readUInt32LE('length') | ||
// Then switch to dataParser once handshakeParser is done | ||
handshakeParser.once('data', function() { | ||
handshakeParser.once('data', function () { | ||
self.socket.unpipe(handshakeParser); | ||
self.socket.pipe(dataParser); | ||
}) | ||
}); | ||
// HANDSHAKE | ||
handshakeParser.once('data', function(data) { | ||
if (data.handshake != 'GBXRemote 2') { | ||
handshakeParser.once('data', function (data) { | ||
if (data.handshake !== 'GBXRemote 2') { | ||
var err = new Error('transport error - wrong lowlevel protocol version'); | ||
callback(err); | ||
self.emit('error', err); | ||
@@ -151,32 +126,24 @@ return; | ||
self.isReady = true; | ||
callback(); | ||
self.emit('connect'); | ||
}); | ||
dataParser.on('data', function(data) { | ||
dataParser.on('data', function (data) { | ||
var deserializer = new Deserializer(); | ||
// TODO: Use reqhandle to determine if its a response or callback | ||
var stream = toStream(data.xml); | ||
// Reponse | ||
if (self.callbacks.hasOwnProperty(data.reqhandle)) { | ||
deserializer.deserializeMethodResponse(data.xml, function(a, b) { | ||
self.callbacks[data.reqhandle].apply(this, arguments); | ||
delete self.callbacks[data.reqhandle]; | ||
if (data.reqhandle > 0x80000000) { | ||
deserializer.deserializeMethodResponse(stream, function (err, res) { | ||
self.emit('response:' + data.reqhandle, [err, res]); | ||
}); | ||
} | ||
// Callback | ||
else { | ||
deserializer.deserializeMethodCall(data.xml, function(err, method, res) { | ||
} else { // Callback | ||
deserializer.deserializeMethodCall(stream, function (err, method, res) { | ||
if (err) { | ||
// This should never happen.... | ||
// There is nothing we can do about this one. | ||
// Its not a response to a request, and its not a valid callback (MethodCall) | ||
//console.log(err); | ||
console.warn('Not a response, nor a callback! (reqhandle: 0x%s)', data.reqhandle.toString(16)); | ||
// Ignore | ||
return; | ||
} else { | ||
// its a callback from the server | ||
self.emit('callback', method, res); | ||
self.emit(method, res); | ||
} | ||
// its a callback from the server | ||
self.emit('callback', method, res); | ||
self.emit(method, res); | ||
}); | ||
@@ -187,4 +154,3 @@ } | ||
Client.prototype.terminate = function() { | ||
Client.prototype.terminate = function () { | ||
if (this.socket) { | ||
@@ -194,3 +160,3 @@ this.socket.end(); | ||
} | ||
} | ||
}; | ||
@@ -200,43 +166,35 @@ /** | ||
* | ||
* @param {String} method - The method name. | ||
* @param {Array} params - Params to send in the call. | ||
* @param {Function} callback - function(error, value) { ... } | ||
* - {Object|null} error - Any errors when making the call, otherwise null. | ||
* - {mixed} value - The value returned in the method response. | ||
* @param {String} method - The method name. | ||
* @param {Array} params - Params to send in the call. | ||
* @return {Promise} | ||
*/ | ||
Client.prototype.query = function(method, params, callback) { | ||
Client.prototype.query = function (method, params) { | ||
var self = this; | ||
if (typeof (arguments[2] || arguments[1]) === 'function') { | ||
throw new TypeError('.query does no longer take a callback. It returns a Promise!'); | ||
} | ||
if (!this.isReady) { | ||
// Call again when ready instead of erroring?.. maybe? yes? no? | ||
var args = arguments; | ||
this.once('connect', function() { | ||
this.query.apply(this, args); | ||
return toPromise(this, 'connect').then(function () { | ||
return self.query.apply(self, args); | ||
}); | ||
return false; | ||
} | ||
// An attempt on method overloading | ||
if (typeof params === 'function') { | ||
callback = params; | ||
params = []; | ||
if (!Array.isArray(params)) { | ||
params = [params]; | ||
} | ||
params = params || []; | ||
// Make sure its an array | ||
if (typeof params !== 'object') | ||
params = [params]; | ||
// Returns JSON of the xml | ||
var xml = Serializer.serializeMethodCall(method, params); | ||
// Check if request (xml + header) is larger than 1024 Kbytes (limit of maniaplanet) | ||
if (xml.length + 8 > 1024*1024) { | ||
callback(new Error('transport error - request too large (' + xml.length + ')')); | ||
return false; | ||
if (xml.length + 8 > 1024 * 1024) { | ||
var error = new Error('transport error - request too large (' + xml.length + ')'); | ||
return Promise.reject(error); | ||
} | ||
this.reqhandle++; | ||
this.callbacks[this.reqhandle] = (typeof callback === 'function') ? callback : function() {}; | ||
// $bytes = pack('VVa*', strlen($xml), $this->reqhandle, $xml); | ||
@@ -248,12 +206,19 @@ var len = Buffer.byteLength(xml); | ||
buf.write(xml, 8); | ||
this.socket.write(buf, 'utf8'); | ||
return true; | ||
return toPromise(this, 'response:' + this.reqhandle, { | ||
ignoreErrors: true | ||
}).then(function (result) { | ||
var err = result[0]; | ||
var res = result[1]; | ||
if (err) { | ||
throw err; | ||
} | ||
return res; | ||
}); | ||
}; | ||
// DEPRECATED - Function name changed. | ||
Client.prototype.methodCall = Client.prototype.query; | ||
module.exports = Client; | ||
@@ -8,8 +8,12 @@ var Client = require('./client'); | ||
* @param {String} host | ||
* @param {Function} callback | ||
* @return {Client} | ||
* @see Client | ||
*/ | ||
exports.createClient = function(port, host, callback) { | ||
return new Client(port, host, callback); | ||
} | ||
exports.createClient = function createClient(port, host) { | ||
var client = new Client(port, host); | ||
client.connect(); | ||
return client; | ||
}; | ||
exports.Client = Client; |
@@ -1,40 +0,55 @@ | ||
{ "name" : "gbxremote" | ||
, "description" : "A pure JavaScript GBXRemote client." | ||
, "keywords" : [ "xml-rpc", "xmlrpc", "xml", "rpc", "gbxremote", "maniaplanet", "trackmania", "shootmania", "questmania", "nadeo" ] | ||
, "version" : "0.1.4" | ||
, "preferGlobal" : false | ||
, "homepage" : "https://github.com/MiniGod/node-gbxremote" | ||
, "author" : "Kristjan Broder Lund <kristjan.1234@gmail.com> (https://github.com/MiniGod)" | ||
, "repository" : { | ||
"type" : "git" | ||
, "url" : "git://github.com/MiniGod/node-gbxremote.git" | ||
} | ||
, "bugs" : { | ||
"url" : "https://github.com/MiniGod/node-gbxremote/issues" | ||
} | ||
, "directories" : { | ||
"lib" : "./lib" | ||
} | ||
, "main" : "./lib/gbxremote.js" | ||
, "dependencies" : { | ||
"sax" : "0.4.x" | ||
, "xmlbuilder" : "0.3.1" | ||
, "barse" : "~0.4.2" | ||
} | ||
, "devDependencies" : { | ||
"vows" : "0.6.x" | ||
} | ||
, "scripts" : { | ||
"test" : "make test" | ||
} | ||
, "engines" : { | ||
"node" : ">=0.4", | ||
"npm" : ">=1.0.0" | ||
} | ||
, "licenses" : [ { | ||
"type" : "MIT" | ||
, "url" : "https://github.com/MiniGod/node-gbxremote/raw/master/LICENSE" | ||
{ | ||
"name": "gbxremote", | ||
"description": "A pure JavaScript GBXRemote client.", | ||
"keywords": [ | ||
"xml-rpc", | ||
"xmlrpc", | ||
"xml", | ||
"rpc", | ||
"gbxremote", | ||
"maniaplanet", | ||
"trackmania", | ||
"shootmania", | ||
"questmania", | ||
"nadeo" | ||
], | ||
"version": "0.2.0", | ||
"preferGlobal": false, | ||
"homepage": "https://github.com/MiniGod/node-gbxremote", | ||
"author": "Kristjan Broder Lund <kristjan.1234@gmail.com> (https://github.com/MiniGod)", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/MiniGod/node-gbxremote.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/MiniGod/node-gbxremote/issues" | ||
}, | ||
"directories": { | ||
"lib": "./lib" | ||
}, | ||
"main": "./lib/gbxremote.js", | ||
"dependencies": { | ||
"any-promise": "^1.1.0", | ||
"barse": "~0.4.2", | ||
"event-to-promise": "^0.7.0", | ||
"string-to-stream": "^1.0.1", | ||
"xmlrpc": "^1.3.1" | ||
}, | ||
"devDependencies": { | ||
"nodemon": "^1.8.1", | ||
"xo": "^0.12.1" | ||
}, | ||
"scripts": { | ||
"watch": "nodemon -x npm test", | ||
"test": "xo" | ||
}, | ||
"engines": { | ||
"node": ">=0.10" | ||
}, | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "https://github.com/MiniGod/node-gbxremote/raw/master/LICENSE" | ||
} | ||
] | ||
} | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
14361
5
2
8
190
2
+ Addedany-promise@^1.1.0
+ Addedevent-to-promise@^0.7.0
+ Addedstring-to-stream@^1.0.1
+ Addedxmlrpc@^1.3.1
+ Addedany-promise@1.3.0(transitive)
+ Addedevent-to-promise@0.7.0(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedreadable-stream@2.3.8(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedsax@1.2.4(transitive)
+ Addedstring-to-stream@1.1.1(transitive)
+ Addedstring_decoder@1.1.1(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedxmlbuilder@8.2.2(transitive)
+ Addedxmlrpc@1.3.2(transitive)
- Removedsax@0.4.x
- Removedxmlbuilder@0.3.1
- Removedsax@0.4.3(transitive)
- Removedxmlbuilder@0.3.1(transitive)