UNPKG

@foxglove/xmlrpc

Version:

TypeScript library implementing an XMLRPC client and server with pluggable server backend

293 lines 10.7 kB
"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]); 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