XML RPC on Nodejs

xmlrpc

lib/xmlrpc.js

Module dependencies.

var libxml = require('libxmljs')
  , http = require('http')
  , https = require('https')
  , urlutil = require('url');

function Client(rpc_url) {
	var info = urlutil.parse(rpc_url);
	this.http_request = http.request;
	var port = info.port || 80;
	if(info.protocol == 'https:') {
		this.http_request = https.request;
		if(!info.port) {
			port = 443;
		}
	}
	this.options = {
		host: info.hostname,
		path: info.pathname || '/',
		method: 'POST',
		port: port
	};
	if(info.query) {
		this.options.path += info.search;
	}
};

create xml rpc request data

  • param: String method

  • param: param1 , param2, ...

  • param: Function callback(result)

  • return: String xml string

  • api: public

Client.prototype.request = function() {
	var len = arguments.length - 1;
	var callback = arguments[len];
	var params = [];
	for(var i = 0; i < len; i++) {
		params.push(arguments[i]);
	}
	var body = this.get_xml.apply(this, params);
	var options = this.options;
	var that = this;
	var req = this.http_request(options, function(res) {
		var chunks = [], length = 0;
		res.on('data', function(chunk) {
			length += chunk.length;
			chunks.push(chunk);
		});
		res.on('end', function(){
			var data = new Buffer(length);
			var error = null;
			// 延后copy
			for(var i=0, pos=0, size=chunks.length; i<size; i++) {
				chunks[i].copy(data, pos);
				pos += chunks[i].length;
			}
			data = data.toString();
			if(res.statusCode == 200 || res.statusCode == 201) {
				data = that.parse(libxml.parseXmlString(data));
			} else {
				error = data;
				data = null;
			}
			if(callback) {
				callback(error, data, res);
			}
		});
	});
	req.write(body);
	req.end();
};

create xml rpc request data

  • param: String method

  • param: param1 , param2, ...

  • return: String xml string

  • api: public

Client.prototype.get_xml = function() {
	var method = arguments[0];
	var doc = new libxml.Document();
	var d = doc.node('methodCall')
    	.node('methodName', method)
    	.parent();
	if(arguments.length > 1) {
		d = d.node('params');
		for(var i = 1, len = arguments.length; i < len; i++) {
			this.serialize(arguments[i], d.node('param').node('value'));
		}
	}
	return doc.toString();
};

Client.prototype.serialize = function(data, d) {
	switch (data.constructor.name) {
	    case 'Array':
	        var data_xml = d.node('array').node('data');
	        for(var i = 0, len = data.length; i < len; i++) {
	            this.serialize(data[i], data_xml.node('value'));
	        }
	        break;
	    case 'Object':
	        var struct_xml = d.node('struct');
	        for(var i in data) {
	            this.serialize(data[i], struct_xml.node('member').node('name', i).parent().node('value'));
	        }
	        break;
	    case 'Number':
	        d.node('int', data.toString());
	        break;
	    case 'String':
	    	d.node('string', data);
	    	break;
	    default:
	        d.node('string', data.toString());
	}
};

Client.prototype.parse = function(xml) {
	var childNodes;
    if(xml.constructor.name === 'Array') {
        childNodes = xml;
    } else {
        childNodes = xml.childNodes();
    }
    for (var i = 0, len = childNodes.length; i < len; i ++) {
    	var child = childNodes[i];
    	var name = child.name();
        if (name === 'struct') {
            var members = child.find('member');
            var data = {};
            for (var m in members) {
                var k = members[m].find('name')[0].text();
                data[k] = this.parse(members[m].find('value/*'));
            }
            return data;
        } else if (name === 'array') {
            var values = child.find('data/value');
            var data = [];
            for (var v in values) {
            	data[v] = this.parse(values[v]);
            }
            return data; 
        } else if (name === 'string' || name === 'int' || name === 'i4' ||
        		name === 'dateTime.iso8601' || name === 'double') {
            return child.text();
        } else if (name !== 'text') {
            return this.parse(child);
        }
    }
};

exports.Client = Client;