Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
Checking mergeability… Don’t worry, you can still create the pull request.
  • 20 commits
  • 14 files changed
  • 0 commit comments
  • 1 contributor
Showing with 553 additions and 460 deletions.
  1. +1 −0 CONTRIBUTORS
  2. +193 −48 README.md
  3. +23 −0 example/authenticate.js
  4. +20 −0 example/callbacks_PlayerConnect.js
  5. +13 −0 example/callbacks_all.js
  6. +47 −0 example/chat.js
  7. +0 −215 example/client_server.js
  8. +11 −0 example/queryBeforeConnect.js
  9. +212 −55 lib/client.js
  10. +8 −12 lib/deserializer.js
  11. +14 −0 lib/gbxremote.js
  12. +0 −63 lib/server.js
  13. +0 −57 lib/xmlrpc.js
  14. +11 −10 package.json
View
1 CONTRIBUTORS
@@ -10,4 +10,5 @@ Asko Soukka <asko.soukka@iki.fi> (https://github.com/datakurre)
Raymond Vellener <raymond@brightin.nl> (https://github.com/tlray)
David Siegel (https://github.com/agnat)
Scott González <scott.gonzalez@gmail.com> (https://github.com/scottgonzalez)
+Kristjan Broder Lund <kristjan.1234@gmail.com> (https://github.com/MiniGod)
View
241 README.md
@@ -1,80 +1,224 @@
-## The What
+Node-GbxRemote
+===
-The xmlrpc module is a pure JavaScript XML-RPC server and client for node.js.
+JavaScript ([node.js](http://nodejs.org)) port of [GbxRemote](http://code.google.com/p/manialive/source/browse/trunk/libraries/DedicatedApi/Xmlrpc/Client.php) by [Nadeo](http://www.nadeo.com),
+which is built on [Incutio XML-RPC Library](http://scripts.incutio.com/xmlrpc/).
-Pure JavaScript means that the [XML parsing](https://github.com/isaacs/sax-js)
-and [XML building](https://github.com/robrighter/node-xml) use pure JavaScript
-libraries, so no extra C dependencies or build requirements. The xmlrpc module
-can be used as an XML-RPC server, receiving method calls and responding with
-method responses, or as an XML-RPC client, making method calls and receiving
-method responses, or as both.
+Used to communicate with [ManiaPlanet](http://www.maniaplanet.com) servers.
+*Note: The API may, or may not change!*
-## The How
-
-### To Install
+Install
+---
```bash
-npm install xmlrpc
+npm install gbxremote
```
-### To Use
+To Use
+---
+
+Look in [/examples/](https://github.com/MiniGod/node-gbxremote/tree/master/example) for all examples.
+
+---
+
+The following examples expects that `var gbxremote = require('gbxremote')`.
+
+### Connecting:
-The file client_server.js in the example directory has a nicely commented
-example of using xmlrpc as an XML-RPC server and client (they even talk to each
-other!).
+To connect to a server, use `var client = gbxremote.createClient(port, [host], [callback]);`
-A brief example:
+*Examples of ways to connect to the server:*
```javascript
-var xmlrpc = require('xmlrpc')
+// Connect with port only
+var client = gbxremote.createClient(5000);
+
+// Connect with port and hostname
+var client = gbxremote.createClient(5000, 'localhost');
+
+// Connect with port and ip
+var client = gbxremote.createClient(5000, '127.0.0.1');
+
+// Connect with port only, and a callback
+var client = gbxremote.createClient(5000, function(err) {
+ // This callback is called both on connect and on error so we should check it.
+ if (err) {
+ console.error('Could not connect to server:', err);
+ } else {
+ console.log('Connection to server was successfull! Ready to send queries..');
+ }
+});
+
+// Connect with port, ip and a callback
+var client = gbxremote.createClient(5000, '127.0.0.1', function(err) {
+ // Callback...
+});
+```
+
+### Querying:
+
+Queries are sent to the server by calling `client.query(method, [params], [callback]);`
+
+*Queries before the connect event has been emitted will be queued and sent on connect!*
-// Creates an XML-RPC server to listen to XML-RPC method calls
-var server = xmlrpc.createServer({ host: 'localhost', port: 9090 })
+[See the full list of methods.](http://methods.xaseco.org/methodstmc.php)
-// Handle method calls by listening for events with the method call name
-server.on('anAction', function (err, params, callback) {
- console.log('Method call params for \'anAction\': ' + params)
+```javascript
+var client = gbxremote.createClient(5000);
+
+client.on('connect', function() {
+
+ // GetVersion does not take any params.
+ client.query('GetVersion', function(err, res) {
+ if (err) {
+ console.error('Error when querying server:', err);
+ } else {
+ console.log('Server version:', res.join(', '));
+ }
+ });
+
+ // GetPlayerInfo takes 2 parameters, 1 optional.
+ // GetPlayerInfo(string login, [int compatibility])
+ client.query('GetPlayerInfo', ['minigod'], function(err, res) {
+ if (err) {
+ console.error('Error getting player info:', err);
+ } else {
+ console.log('Player info:');
+ console.log(res);
+ }
+ });
+});
+```
+
+### Disconnecting:
- // ...perform an action...
+`client.terminate();`
- // Send a method response with a value
- callback(null, 'aResult')
-})
-console.log('XML-RPC server listening on port 9091')
+### Events:
-// Waits briefly to give the XML-RPC server time to start up and start
-// listening
-setTimeout(function () {
- // Creates an XML-RPC client. Passes the host information on where to
- // make the XML-RPC calls.
- var client = xmlrpc.createClient({ host: 'localhost', port: 9090, path: '/'})
+#### Event: connect()
- // Sends a method call to the XML-RPC server
- client.methodCall('anAction', ['aParam'], function (error, value) {
- // Results of the method response
- console.log('Method response for \'anAction\': ' + value)
- })
+Emitted when connection to the server is successfull.
+Ready to receive queries!
-}, 1000)
+```javascript
+var client = gbxremote.createClient(5000);
+
+client.on('connect', function() {
+ console.log('Connection successfull! Lets do some queries!');
+ client.query('GetVersion', function(err, res) {
+ if (err)
+ console.log(err);
+ else
+ console.log(res);
+ });
+});
```
+If there is a problem connecting, the 'connect' event will not be emitted, the 'error' event will be emitted with the exception.
+
+#### Event: error(err)
-Output from the example:
+Emitted when:
+* Socket errors *(host is not listening on that port, loose connection, etc.)*
+* Handshake fails *(host* ***is*** *listening on that port, but its not a ManiaPlanet (GbxRemote 2) server)*
+```javascript
+var client = gbxremote.createClient(80);
+
+client.on('error', function(err) {
+ console.error('Connection failed: ' + err);
+});
```
-XML-RPC server listening on port 9090
-Method call params for 'anAction': aParam
-Method response for 'anAction': aResult
+
+#### Event: callback(method, params)
+
+After sending `EnableCallbacks(true)` to the server, it will send you callbacks when stuff happend on the server.
+Eg:
+* `ManiaPlanet.ServerStart`
+* `ManiaPlanet.ServerStop`
+* `ManiaPlanet.PlayerConnect`
+* `ManiaPlanet.PlayerChat`
+
+[See the full list of callbacks](http://server.xaseco.org/callbacks2.php)
+
+```javascript
+var client = gbxremote.createClient(5000);
+
+client.on('connect', function() {
+ client.query('SetApiVersion', ['2012-06-19']);
+ client.query('EnableCallbacks', [true]);
+});
+
+client.on('callback', function(method, params) {
+ console.log("Callback from server: %s - %d params", method, params.length);
+
+ // This would be the typical place to have a switch statement. Please dont do that. Use the events, as shown below.
+});
```
-### To Test
+#### Event: \<method\>(params)
+
+Callbacks will also emit separate events for each method. It's hard to explain. Learn from example:
+
+```javascript
+var client = gbxremote.createClient(5000);
+
+client.on('connect', function() {
+ // Before enabling callbacks, make sure you set the latest API.
+ client.query('SetApiVersion', ['2012-06-19']);
+ client.query('EnableCallbacks', [true]);
+});
+
+// ManiaPlanet.PlayerConnect(string Login, bool IsSpectator);
+client.on('ManiaPlanet.PlayerConnect', function(params) {
+ console.log('%s just joined as a %s', params[0], params[1] ? 'spectator' : 'player');
+});
+
+// ManiaPlanet.PlayerDisconnect(string Login);
+client.on('ManiaPlanet.PlayerDisconnect', function(params) {
+ console.log('%s left the server', params[0]);
+});
+```
+
+These events can basically take over the big switch statements that is normal in todays server controllers.
+
+#### Event: close(had_error)
+
+Emitted once the socket is fully closed.
+The argument had_error is a boolean which says if the socket was closed due to a transmission error.
+
+```javascript
+var client = gbxremote.createClient(5000);
+
+client.on('connect', function() {
+ // Connected...
+
+ // Do stuff?
+
+ // Disconnect
+ client.terminate();
+});
+
+client.on('close', function(had_error) {
+ console.log('Connection to the server has been closed');
+});
+```
+
+Testing
+---
+
+*This section does not currently apply, because tests are not being maintained atm*
+*Note: Tests have not been changed since fork, hence will not pass.*
+
+***TODO: Fix tests - Figure out how to do it with travis (and in general), since we need a running ManiaPlanet server to run tests - and we need to know*** *exactly* ***what the server will return.***
[![Build
-Status](https://secure.travis-ci.org/baalexander/node-xmlrpc.png)](http://travis-ci.org/baalexander/node-xmlrpc)
+Status](https://secure.travis-ci.org/MiniGod/node-gbxremote.png)](http://travis-ci.org/MiniGod/node-gbxremote)
XML-RPC must be precise so there are an extensive set of test cases in the test
directory. [Vows](http://vowsjs.org/) is the testing framework and [Travis
-CI](http://travis-ci.org/baalexander/node-xmlrpc) is used for Continuous
+CI](http://travis-ci.org/MiniGod/node-gbxremote) is used for Continuous
Integration.
To run the test suite:
@@ -84,7 +228,8 @@ To run the test suite:
If submitting a bug fix, please update the appropriate test file too.
-## The License (MIT)
+The License (MIT)
+---
Released under the MIT license. See the LICENSE file for the complete wording.
View
23 example/authenticate.js
@@ -0,0 +1,23 @@
+var gbxremote = require('../lib/gbxremote.js');
+
+var client = gbxremote.createClient(5000, 'localhost');
+
+client.on('error', function(err) {
+ console.log(err);
+})
+
+.on('connect', function() {
+ // Authenticate(string user, string password)
+ client.query("Authenticate", ["SuperAdmin", "SuperAdmin"], function(err, res) {
+ if (err) {
+ console.log(err);
+ } else {
+ if (res === true) {
+ console.log("Authenticated!");
+ }
+ }
+
+ // Disconnect
+ client.terminate();
+ });
+});
View
20 example/callbacks_PlayerConnect.js
@@ -0,0 +1,20 @@
+var gbxremote = require('../lib/gbxremote.js');
+
+// Connect to localhost:5000
+var client = gbxremote.createClient(5000);
+
+client.on('connect', function() {
+ // Before enabling callbacks, make sure you set the latest API.
+ client.query('SetApiVersion', ['2012-06-19']);
+ client.query('EnableCallbacks', [true]);
+});
+
+// ManiaPlanet.PlayerConnect(string Login, bool IsSpectator);
+client.on('ManiaPlanet.PlayerConnect', function(params) {
+ console.log('%s just joined as a %s', params[0], params[1] ? 'spectator' : 'player');
+});
+
+// ManiaPlanet.PlayerDisconnect(string Login);
+client.on('ManiaPlanet.PlayerDisconnect', function(params) {
+ console.log('%s left the server', params[0]);
+});
View
13 example/callbacks_all.js
@@ -0,0 +1,13 @@
+var gbxremote = require('../lib/gbxremote.js');
+
+// Connect to localhost:5000
+var client = gbxremote.createClient(5000);
+
+client.on('connect', function() {
+ client.query('SetApiVersion', ['2012-06-19']);
+ client.query('EnableCallbacks', [true]);
+});
+
+client.on('callback', function(method, params) {
+ console.log("Callback from server: %s - %d params", method, params.length);
+});
View
47 example/chat.js
@@ -0,0 +1,47 @@
+var gbxremote = require('../lib/gbxremote.js');
+
+// Connect to localhost:5000
+var client = gbxremote.createClient(5000);
+
+client.on('connect', function() {
+ // Before enabling callbacks, make sure you set the latest API.
+ client.query('SetApiVersion', ['2012-06-19']);
+ client.query('EnableCallbacks', [true]);
+
+ // Authenticate so that we can send chat messages - SuperAdmin is not needed to chat, just Admin...
+ client.query('Authenticate', ['Admin', 'Admin'], function(err, res) {
+ if (res === true) {
+ // Listen to stdin
+ process.stdin.resume();
+ process.stdin.setEncoding('utf8');
+
+ process.stdin.on('data', function (chunk) {
+ client.query('ChatSend', [chunk.trim()], function(err) {
+ if (err) {
+ console.error('Error occured when sending chat message!');
+ }
+ });
+ });
+
+ console.log('*** Authenticated! You may now chat with the server.');
+ } else {
+ console.log('*** Could not authenticate to server. You can read but not write!');
+ }
+ });
+});
+
+// ManiaPlanet.PlayerChat(int PlayerUid, string Login, string Text, bool IsRegistredCmd);
+client.on('ManiaPlanet.PlayerChat', function(params) {
+ var PlayerUid = params[0]
+ , Login = params[1]
+ , Text = params[2]
+ , IsRegistredCmd = params[3]
+ ;
+
+ // Normal chat message, or did the player call a registred chat command?
+ if (!IsRegistredCmd) {
+ console.log('[%s] %s', params[1], params[2]);
+ } else {
+ console.log('%s executed command: %s', params[1], params[2]);
+ }
+});
View
215 example/client_server.js
@@ -1,215 +0,0 @@
-/**
- * The purpose of this example is to demonstrate an XML-RPC client interacting
- * with an XML-RPC server. Both client and server are using node-xmlrpc.
- *
- * The XML-RPC server is a basic parameter server. It exposes getter and setter
- * methods to get and set different data types. The XML-RPC client can then set
- * and get these values using method calls.
- */
-
-var fs = require('fs')
- , xmlrpc = require('../lib/xmlrpc.js')
-
-
-////////////////////////////////////////////////////////////////////////
-// The XML-RPC Server
-////////////////////////////////////////////////////////////////////////
-
-// To simulate a simple parameter server, where values can be setted and getted
-// through XML-RPC, the contents of the calls are stored in an object
-var serverContents = {
- calls: []
-, arrayValue: null
-, booleanValue: null
-, dateTimeValue: null
-, doubleValue: null
-, integerValue: null
-, stringValue: null
-, structValue: null
-}
-
-// Creates an XML-RPC server to listen for XML-RPC method calls
-var serverOptions = {
- host: 'localhost'
-, port: 9090
-}
-// Can use a String too:
-// var serverOptions = 'http://localhost:9090'
-var server = xmlrpc.createServer(serverOptions)
-
-// To use an HTTPS server instead, use createSecureServer:
-/*
-var secureServerOptions = {
- host: 'localhost'
-, port: 443
-, key: fs.readFileSync('./test-key.pem')
-, cert: fs.readFileSync('./test-cert.pem')
-}
-var server = xmlrpc.createSecureServer(secureServerOptions)
-*/
-
-// Handle method calls by listening for events with the method call name
-// Array handling
-// 'setArray' is the method call to listen for
-server.on('setArray', function (err, params, callback) {
- serverContents.calls.push('setArray')
- serverContents.arrayValue = params[0]
- callback()
-})
-server.on('getArray', function (err, params, callback) {
- serverContents.calls.push('getArray')
- callback(null, serverContents.arrayValue)
-})
-// Boolean handling
-server.on('setBoolean', function (err, params, callback) {
- serverContents.calls.push('setBoolean')
- serverContents.booleanValue = params[0]
- callback()
-})
-server.on('getBoolean', function (err, params, callback) {
- serverContents.calls.push('getBoolean')
- callback(null, serverContents.booleanValue)
-})
-// Date handling
-server.on('setDate', function (err, params, callback) {
- serverContents.calls.push('setDate')
- serverContents.dateValue = params[0]
- callback()
-})
-server.on('getDate', function (err, params, callback) {
- serverContents.calls.push('getDate')
- callback(null, serverContents.dateValue)
-})
-// Double handling
-server.on('setDouble', function (err, params, callback) {
- serverContents.calls.push('setDouble')
- serverContents.doubleValue = params[0]
- callback()
-})
-server.on('getDouble', function (err, params, callback) {
- serverContents.calls.push('getDouble')
- callback(null, serverContents.doubleValue)
-})
-// Integer handling
-server.on('setInteger', function (err, params, callback) {
- serverContents.calls.push('setInteger')
- serverContents.integerValue = params[0]
- callback()
-})
-server.on('getInteger', function (err, params, callback) {
- serverContents.calls.push('getInteger')
- callback(null, serverContents.integerValue)
-})
-// String handling
-server.on('setString', function (err, params, callback) {
- serverContents.calls.push('setString')
- serverContents.stringValue = params[0]
- callback()
-})
-server.on('getString', function (err, params, callback) {
- serverContents.calls.push('getString')
- callback(null, serverContents.stringValue)
-})
-// Struct handling
-server.on('setStruct', function (err, params, callback) {
- serverContents.calls.push('setStruct')
- serverContents.structValue = params[0]
- callback()
-})
-server.on('getStruct', function (err, params, callback) {
- serverContents.calls.push('getStruct')
- callback(null, serverContents.structValue)
-})
-// Call log handling
-server.on('getCallLog', function (err, params, callback) {
- serverContents.calls.push('getCallLog')
- callback(null, serverContents.calls)
-})
-// Return a fault message
-server.on('fakeFault', function (error, params, callback) {
- serverContents.calls.push('fakeFault')
- callback({ faultCode: 2, faultString: 'Uh oh.'}, null)
-})
-
-
-////////////////////////////////////////////////////////////////////////
-// The XML-RPC Client
-////////////////////////////////////////////////////////////////////////
-
-// Waits briefly to give the XML-RPC server time to start up and start listening
-setTimeout(function () {
- // Creates an XML-RPC client. Passes the host information on where to make the
- // XML-RPC calls.
- var clientOptions = {
- host: 'localhost'
- , port: 9090
- , path: '/'
- }
- // Can use a String too:
- // var clientOptions = 'http://localhost:9090'
- var client = xmlrpc.createClient(clientOptions)
-
- // To use HTTPS to make the call, use createSecureClient instead:
- /*
- var secureClientOptions = {
- host: 'localhost'
- , port: 443
- , path: '/'
- }
- var client = xmlrpc.createSecureClient(secureClientOptions)
- */
-
- client.methodCall('setArray', [['value1', 'value2']], function(error, value) {
- client.methodCall('getArray', null, function (error, value) {
- console.log('Get Array Response: ' + value)
- })
- })
-
- client.methodCall('setBoolean', [true], function (error, value) {
- client.methodCall('getBoolean', null, function (error, value) {
- console.log('Get Boolean Response: ' + value)
- })
- })
-
- client.methodCall('setDate', [new Date(2016, 05, 08, 11, 35, 10)], function (error, value) {
- client.methodCall('getDate', null, function (error, value) {
- console.log('Get Date Response: ' + value)
- })
- })
-
- client.methodCall('setDouble', [24.99], function (error, value) {
- client.methodCall('getDouble', null, function (error, value) {
- console.log('Get Double Response: ' + value)
- })
- })
-
- client.methodCall('setInteger', [23], function (error, value) {
- client.methodCall('getInteger', null, function (error, value) {
- console.log('Get Integer Response: ' + value)
- })
- })
-
- client.methodCall('setString', ['testString1'], function (error, value) {
- client.methodCall('getString', null, function (error, value) {
- console.log('Get String Response: ' + value)
- })
- })
-
- client.methodCall('setStruct', [{ nameOfValue: 'Go 1998!' }], function (error, value) {
- client.methodCall('getStruct', null, function (error, value) {
- console.log('Get Struct Response (on next line): ')
- console.log(value)
- })
- })
-
- client.methodCall('fakeFault', null, function (error, value) {
- console.log('Fake Fault Response as Error (on next line): ')
- console.log(error)
- })
-
- client.methodCall('getCallLog', null, function (error, value) {
- console.log('Get Call Log Response: ' + value)
- })
-
-}, 1000)
-
View
11 example/queryBeforeConnect.js
@@ -0,0 +1,11 @@
+var gbxremote = require('../lib/gbxremote.js');
+
+var client = gbxremote.createClient(5000);
+
+client.query('GetStatus', function(err, res) {
+ if (err) console.error('Error:', err);
+ else console.log(res);
+ client.terminate();
+});
+
+client.on('error', console.log);
View
267 lib/client.js
@@ -1,8 +1,8 @@
-var http = require('http')
- , https = require('https')
- , url = require('url')
+var net = require('net')
+ , barse = require('barse')
, Serializer = require('./serializer')
, Deserializer = require('./deserializer')
+ ;
/**
* Creates a Client object for making XML-RPC method calls.
@@ -18,49 +18,173 @@ var http = require('http')
* otherwise false.
* @return {Client}
*/
-function Client(options, isSecure) {
+function Client(port, host, callback) {
+ // Invokes with new if called without
+ if (false === (this instanceof Client)) {
+ return new Client(port, host, callback)
+ }
+
+ // Set the options
+
+ if (typeof host === 'function') {
+ callback = host;
+ host = 'localhost';
+ }
+
+ this.host = host || 'localhost';
+ this.port = port;
+
+ this.isConnected = false;
+ this.isReady = false;
+ this.reqhandle = 0x80000000;
+ this.callbacks = {};
+
+ this.connect(callback);
+}
+
+// Inherit EventEmitter
+Client.prototype = Object.create(require('events').EventEmitter.prototype);
+
+/**
+ * Connects to the server
+ *
+ * @param {Function} callback (optional) - function(error) { ... }
+ * - {Number} timeout (optional) - Timeout to connect
+ */
+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() {};
+ }
+ })();
+ }
+
+ // 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);
+ self.emit('error', err);
+
+ self.terminate();
+ }, timeout);
+
+ self.on('connect', function() {
+ clearTimeout(to);
+ });
+ });
+
+ 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);
+ });
+
+ var handshakeParser = barse()
+ .readUInt32LE('length')
+ .string('handshake', 'length')
+ ;
- // Invokes with new if called without
- if (false === (this instanceof Client)) {
- return new Client(options, isSecure)
- }
+ var dataParser = barse()
+ .readUInt32LE('length')
+ .readUInt32LE('reqhandle')
+ .string('xml', 'length')
+ ;
- // If a string URI is passed in, converts to URI fields
- if (typeof options === 'string') {
- options = url.parse(options)
- options.host = options.hostname
- options.path = options.pathname
- }
+ // Pipe data to handshakeParser
+ this.socket.pipe(handshakeParser);
+ // Then switch to dataParser once handshakeParser is done
+ handshakeParser.once('data', function() {
+ self.socket.unpipe(handshakeParser);
+ self.socket.pipe(dataParser);
+ })
- // Set the HTTP request headers
- var headers = {
- 'User-Agent' : 'NodeJS XML-RPC Client'
- , 'Content-Type' : 'text/xml'
- , 'Accept' : 'text/xml'
- , 'Accept-Charset' : 'UTF8'
- , 'Connection' : 'Keep-Alive'
- }
- options.headers = options.headers || {}
+ // HANDSHAKE
+ 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);
+ return;
+ }
- if (options.headers.Authorization == null &&
- options.basic_auth != null &&
- options.basic_auth.user != null &&
- options.basic_auth.pass != null)
- {
- var auth = options.basic_auth.user + ":" + options.basic_auth.pass
- options.headers['Authorization'] = 'Basic ' + new Buffer(auth).toString('base64')
- }
+ self.protocol = 2;
+ self.isReady = true;
+ callback();
+ self.emit('connect');
+ });
- for (var attribute in headers) {
- if (options.headers[attribute] === undefined) {
- options.headers[attribute] = headers[attribute]
- }
- }
+ dataParser.on('data', function(data) {
+ var deserializer = new Deserializer();
+
+ // TODO: Use reqhandle to determine if its a response or callback
+ // 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];
+ });
+ }
+ // Callback
+ else {
+ deserializer.deserializeMethodCall(data.xml, 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));
+ return;
+ } else {
+ // its a callback from the server
+ self.emit('callback', method, res);
+ self.emit(method, res);
+ }
+ });
+ }
+ });
+};
- options.method = 'POST'
- this.options = options
- this.isSecure = isSecure
+Client.prototype.terminate = function() {
+ if (this.socket) {
+ this.socket.end();
+ this.isConnected = false;
+ }
}
/**
@@ -72,22 +196,55 @@ function Client(options, isSecure) {
* - {Object|null} error - Any errors when making the call, otherwise null.
* - {mixed} value - The value returned in the method response.
*/
-Client.prototype.methodCall = function methodCall(method, params, callback) {
- var xml = Serializer.serializeMethodCall(method, params)
- , transport = this.isSecure ? https : http
- , options = this.options
-
- options.headers['Content-Length'] = Buffer.byteLength(xml, 'utf8')
+Client.prototype.query = function(method, params, callback) {
+
+ 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 false;
+ }
+
+ // An attempt on method overloading
+ if (typeof params === 'function') {
+ callback = 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;
+ }
+
+ this.reqhandle++;
+ this.callbacks[this.reqhandle] = (typeof callback === 'function') ? callback : function() {};
+
+ // $bytes = pack('VVa*', strlen($xml), $this->reqhandle, $xml);
+ var len = Buffer.byteLength(xml);
+ var buf = new Buffer(8 + len);
+ buf.writeInt32LE(len, 0);
+ buf.writeUInt32LE(this.reqhandle, 4);
+ buf.write(xml, 8);
+
+ this.socket.write(buf, 'utf8');
+
+ return true;
+};
- var request = transport.request(options, function(response) {
- var deserializer = new Deserializer(options.responseEncoding)
- deserializer.deserializeMethodResponse(response, callback)
- })
-
- request.on('error', callback)
- request.write(xml, 'utf8')
- request.end()
-}
+// DEPRECATED - Function name changed.
+Client.prototype.methodCall = Client.prototype.query;
-module.exports = Client
+module.exports = Client;
View
20 lib/deserializer.js
@@ -13,12 +13,12 @@ var Deserializer = function(encoding) {
this.callback = null
this.error = null
- this.parser = sax.createStream()
- this.parser.on('opentag', this.onOpentag.bind(this))
- this.parser.on('closetag', this.onClosetag.bind(this))
- this.parser.on('text', this.onText.bind(this))
- this.parser.on('end', this.onDone.bind(this))
- this.parser.on('error', this.onError.bind(this))
+ this.parser = sax.parser()
+ this.parser.onopentag = this.onOpentag.bind(this)
+ this.parser.onclosetag = this.onClosetag.bind(this)
+ this.parser.ontext = this.onText.bind(this)
+ this.parser.onend = this.onDone.bind(this)
+ this.parser.onerror = this.onError.bind(this)
}
Deserializer.prototype.deserializeMethodResponse = function(stream, callback) {
@@ -42,9 +42,7 @@ Deserializer.prototype.deserializeMethodResponse = function(stream, callback) {
}
}
- stream.setEncoding(this.encoding)
- stream.on('error', this.onError.bind(this))
- stream.pipe(this.parser)
+ this.parser.write(stream).close();
}
Deserializer.prototype.deserializeMethodCall = function(stream, callback) {
@@ -65,9 +63,7 @@ Deserializer.prototype.deserializeMethodCall = function(stream, callback) {
}
}
- stream.setEncoding(this.encoding)
- stream.on('error', this.onError.bind(this))
- stream.pipe(this.parser)
+ this.parser.write(stream).close();
}
Deserializer.prototype.onDone = function() {
View
14 lib/gbxremote.js
@@ -0,0 +1,14 @@
+var Client = require('./client');
+
+/**
+ * Creates an GbxRemote client.
+ *
+ * @param {Number} port
+ * @param {String} host
+ * @param {Function} callback
+ * @return {Client}
+ * @see Client
+ */
+exports.createClient = function(port, host, callback) {
+ return new Client(port, host, callback);
+}
View
63 lib/server.js
@@ -1,63 +0,0 @@
-var http = require('http')
- , https = require('https')
- , url = require('url')
- , EventEmitter = require('events').EventEmitter
- , Serializer = require('./serializer')
- , Deserializer = require('./deserializer')
-
-/**
- * Creates a new Server object. Also creates an HTTP server to start listening
- * for XML-RPC method calls. Will emit an event with the XML-RPC call's method
- * name when receiving a method call.
- *
- * @constructor
- * @param {Object|String} options - The HTTP server options. 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.
- * @return {Server}
- */
-function Server(options, isSecure) {
-
- if (false === (this instanceof Server)) {
- return new Server(options, isSecure)
- }
-
- var that = this
-
- // If a string URI is passed in, converts to URI fields
- if (typeof options === 'string') {
- options = url.parse(options)
- options.host = options.hostname
- options.path = options.pathname
- }
-
- function handleMethodCall(request, response) {
- var deserializer = new Deserializer()
- deserializer.deserializeMethodCall(request, function(error, methodName, params) {
- that.emit(methodName, null, params, function(error, value) {
- var xml = null
- if (error !== null) {
- xml = Serializer.serializeFault(error)
- } else {
- xml = Serializer.serializeMethodResponse(value)
- }
- response.writeHead(200, {'Content-Type': 'text/xml'})
- response.end(xml)
- })
- })
- }
- var httpServer = isSecure ? https.createServer(options, handleMethodCall)
- : http.createServer(handleMethodCall)
-
- httpServer.listen(options.port, options.host)
-}
-
-// Inherit from EventEmitter to emit and listen
-Server.prototype.__proto__ = EventEmitter.prototype
-
-module.exports = Server
-
View
57 lib/xmlrpc.js
@@ -1,57 +0,0 @@
-var Client = require('./client')
- , Server = require('./server')
-
-var xmlrpc = exports
-
-/**
- * Creates an XML-RPC client.
- *
- * @param {Object} options - server options to make the HTTP request to
- * - {String} host
- * - {Number} port
- * @return {Client}
- * @see Client
- */
-xmlrpc.createClient = function(options) {
- return new Client(options, false)
-}
-
-/**
- * Creates an XML-RPC client that makes calls using HTTPS.
- *
- * @param {Object} options - server options to make the HTTP request to
- * - {String} host
- * - {Number} port
- * @return {Client}
- * @see Client
- */
-xmlrpc.createSecureClient = function(options) {
- return new Client(options, true)
-}
-
-/**
- * Creates an XML-RPC server.
- *
- * @param {Object}options - the HTTP server options
- * - {String} host
- * - {Number} port
- * @return {Server}
- * @see Server
- */
-xmlrpc.createServer = function(options) {
- return new Server(options, false)
-}
-
-/**
- * Creates an XML-RPC server that uses HTTPS.
- *
- * @param {Object}options - the HTTP server options
- * - {String} host
- * - {Number} port
- * @return {Server}
- * @see Server
- */
-xmlrpc.createSecureServer = function(options) {
- return new Server(options, true)
-}
-
View
21 package.json
@@ -1,24 +1,25 @@
-{ "name" : "xmlrpc"
-, "description" : "A pure JavaScript XML-RPC client and server."
-, "keywords" : [ "xml-rpc", "xmlrpc", "xml", "rpc" ]
-, "version" : "1.0.1"
+{ "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/baalexander/node-xmlrpc"
-, "author" : "Brandon Alexander <baalexander@gmail.com> (https://github.com/baalexander)"
+, "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/baalexander/node-xmlrpc.git"
+ , "url" : "git://github.com/MiniGod/node-gbxremote.git"
}
, "bugs" : {
- "url" : "https://github.com/baalexander/node-xmlrpc/issues"
+ "url" : "https://github.com/MiniGod/node-gbxremote/issues"
}
, "directories" : {
"lib" : "./lib"
}
-, "main" : "./lib/xmlrpc.js"
+, "main" : "./lib/gbxremote.js"
, "dependencies" : {
"sax" : "0.4.x"
, "xmlbuilder" : "0.3.1"
+ , "barse" : "~0.4.2"
}
, "devDependencies" : {
"vows" : "0.6.x"
@@ -32,7 +33,7 @@
}
, "licenses" : [ {
"type" : "MIT"
- , "url" : "https://github.com/baalexander/node-xmlrpc/raw/master/LICENSE"
+ , "url" : "https://github.com/MiniGod/node-gbxremote/raw/master/LICENSE"
}
]
}

No commit comments for this range

Something went wrong with that request. Please try again.