@foxglove/xmlrpc
Version:
TypeScript library implementing an XMLRPC client and server with pluggable server backend
293 lines • 10.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Deserializer = void 0;
const sax_1 = __importDefault(require("sax"));
const DateFormatter_1 = require("./DateFormatter");
const XmlRpcFault_1 = require("./XmlRpcFault");
class Deserializer {
constructor(encoding = "utf-8") {
this.dateFormatter = new DateFormatter_1.DateFormatter();
this._stack = [];
this._marks = [];
this._data = [];
this._value = false;
this._callback = () => {
return;
};
this._onDone = () => {
if (this._error == undefined) {
if (this._type == undefined || this._marks.length !== 0) {
this._callback(new Error(`Invalid XML-RPC ${this._type ?? "message"}`));
}
else if (this._responseType === "fault") {
const createFault = (fault) => {
const faultString = typeof fault.faultString === "string" ? fault.faultString : undefined;
const faultCode = typeof fault.faultCode === "number" ? fault.faultCode : undefined;
return new XmlRpcFault_1.XmlRpcFault(faultString, faultCode);
};
this._callback(createFault(this._stack[0]));
}
else {
this._callback(undefined, this._stack);
}
}
};
this._onError = (err) => {
if (this._error == undefined) {
this._error = err;
this._callback(this._error);
}
};
this._push = (value) => {
this._stack.push(value);
};
//==============================================================================
// SAX Handlers
//==============================================================================
this._onOpentag = (node) => {
if (node.name === "ARRAY" || node.name === "STRUCT") {
this._marks.push(this._stack.length);
}
this._data = [];
this._value = node.name === "VALUE";
};
this._onText = (text) => {
this._data.push(text);
};
this._onCDATA = (cdata) => {
this._data.push(cdata);
};
this._onClosetag = (el) => {
const data = this._data.join("");
try {
switch (el) {
case "BOOLEAN":
this._endBoolean(data);
break;
case "INT":
case "I4":
this._endInt(data);
break;
case "I8":
this._endI8(data);
break;
case "DOUBLE":
this._endDouble(data);
break;
case "STRING":
case "NAME":
this._endString(data);
break;
case "ARRAY":
this._endArray(data);
break;
case "STRUCT":
this._endStruct(data);
break;
case "BASE64":
this._endBase64(data);
break;
case "DATETIME.ISO8601":
this._endDateTime(data);
break;
case "VALUE":
this._endValue(data);
break;
case "PARAMS":
this._endParams(data);
break;
case "FAULT":
this._endFault(data);
break;
case "METHODRESPONSE":
this._endMethodResponse(data);
break;
case "METHODNAME":
this._endMethodName(data);
break;
case "METHODCALL":
this._endMethodCall(data);
break;
case "NIL":
this._endNil(data);
break;
case "DATA":
case "PARAM":
case "MEMBER":
// Ignored by design
break;
default:
this._onError(new Error(`Unknown XML-RPC tag "${el}"`));
break;
}
}
catch (e) {
this._onError(e);
}
};
this._endNil = (_data) => {
this._push(undefined);
this._value = false;
};
this._endBoolean = (data) => {
if (data === "1") {
this._push(true);
}
else if (data === "0") {
this._push(false);
}
else {
throw new Error("Illegal boolean value '" + data + "'");
}
this._value = false;
};
this._endInt = (data) => {
const value = parseInt(data, 10);
if (isNaN(value)) {
throw new Error("Expected an integer but got '" + data + "'");
}
else {
this._push(value);
this._value = false;
}
};
this._endDouble = (data) => {
const value = parseFloat(data);
if (isNaN(value)) {
const lower = data.toLowerCase();
if (lower === "nan") {
this._push(NaN);
this._value = false;
}
else if (lower === "-inf" || lower === "-infinity") {
this._push(-Infinity);
this._value = false;
}
else if (lower === "inf" || lower === "infinity") {
this._push(Infinity);
this._value = false;
}
else {
throw new Error("Expected a double but got '" + data + "'");
}
}
else {
this._push(value);
this._value = false;
}
};
this._endString = (data) => {
this._push(data);
this._value = false;
};
this._endArray = (_data) => {
const mark = this._marks.pop() ?? 0;
this._stack.splice(mark, this._stack.length - mark, this._stack.slice(mark));
this._value = false;
};
this._endStruct = (_data) => {
const mark = this._marks.pop() ?? 0;
const struct = {};
const items = this._stack.slice(mark);
for (let i = 0; i < items.length; i += 2) {
const key = String(items[i]); // workaround for https://github.com/typescript-eslint/typescript-eslint/issues/10632
struct[key] = items[i + 1];
}
this._stack.splice(mark, this._stack.length - mark, struct);
this._value = false;
};
this._endBase64 = (data) => {
const buffer = Buffer.from(data, "base64");
this._push(buffer);
this._value = false;
};
this._endDateTime = (data) => {
const date = this.dateFormatter.decodeIso8601(data);
this._push(date);
this._value = false;
};
this._endI8 = (data) => {
if (!Deserializer.isInteger.test(data)) {
throw new Error(`Expected integer (I8) value but got "${data}"`);
}
else {
this._endString(data);
}
};
this._endValue = (data) => {
if (this._value) {
this._endString(data);
}
};
this._endParams = (_data) => {
this._responseType = "params";
};
this._endFault = (_data) => {
this._responseType = "fault";
};
this._endMethodResponse = (_data) => {
this._type = "methodresponse";
};
this._endMethodName = (data) => {
this._methodname = data;
};
this._endMethodCall = (_data) => {
this._type = "methodcall";
};
this._encoding = encoding;
this._parser = sax_1.default.createStream();
this._parser.on("opentag", this._onOpentag);
this._parser.on("closetag", this._onClosetag);
this._parser.on("text", this._onText);
this._parser.on("cdata", this._onCDATA);
this._parser.on("end", this._onDone);
this._parser.on("error", this._onError);
}
async deserializeMethodResponse(data) {
return await new Promise((resolve, reject) => {
this._callback = (error, result) => {
if (error != undefined) {
reject(error);
}
else if (result != undefined && result.length > 1) {
reject(new Error("Response has more than one param"));
}
else if (this._type !== "methodresponse") {
reject(new Error("Not a method response"));
}
else if (this._responseType == undefined) {
reject(new Error("Invalid method response"));
}
else {
resolve(result?.[0]);
}
};
this._parser.end(data, this._encoding);
});
}
async deserializeMethodCall(data) {
return await new Promise((resolve, reject) => {
this._callback = (error, result) => {
if (error != undefined) {
reject(error);
}
else if (this._type !== "methodcall") {
reject(new Error("Not a method call"));
}
else if (this._methodname == undefined) {
reject(new Error("Method call did not contain a method name"));
}
else {
resolve([this._methodname, result ?? []]);
}
};
this._parser.end(data, this._encoding);
});
}
}
exports.Deserializer = Deserializer;
Deserializer.isInteger = /^-?\d+$/;
//# sourceMappingURL=Deserializer.js.map