478 lines
20 KiB
JavaScript
Raw Normal View History

2025-04-23 09:34:08 +08:00
"use strict";
/*
* Copyright (c) 2011 Vinay Pulim <vinay@milewise.com>
* MIT Licensed
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
exports.__esModule = true;
exports.Client = void 0;
var assert = require("assert");
var debugBuilder = require("debug");
var events_1 = require("events");
var getStream = require("get-stream");
var _ = require("lodash");
var uuid_1 = require("uuid");
var http_1 = require("./http");
var utils_1 = require("./utils");
var debug = debugBuilder('node-soap');
var nonIdentifierChars = /[^a-z$_0-9]/i;
var Client = /** @class */ (function (_super) {
__extends(Client, _super);
function Client(wsdl, endpoint, options) {
var _this_1 = _super.call(this) || this;
options = options || {};
_this_1.wsdl = wsdl;
_this_1._initializeOptions(options);
_this_1._initializeServices(endpoint);
_this_1.httpClient = options.httpClient || new http_1.HttpClient(options);
return _this_1;
}
/** add soapHeader to soap:Header node */
Client.prototype.addSoapHeader = function (soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
soapHeader = this._processSoapHeader(soapHeader, name, namespace, xmlns);
return this.soapHeaders.push(soapHeader) - 1;
};
Client.prototype.changeSoapHeader = function (index, soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
soapHeader = this._processSoapHeader(soapHeader, name, namespace, xmlns);
this.soapHeaders[index] = soapHeader;
};
/** return all defined headers */
Client.prototype.getSoapHeaders = function () {
return this.soapHeaders;
};
/** remove all defined headers */
Client.prototype.clearSoapHeaders = function () {
this.soapHeaders = null;
};
Client.prototype.addHttpHeader = function (name, value) {
if (!this.httpHeaders) {
this.httpHeaders = {};
}
this.httpHeaders[name] = value;
};
Client.prototype.getHttpHeaders = function () {
return this.httpHeaders;
};
Client.prototype.clearHttpHeaders = function () {
this.httpHeaders = null;
};
Client.prototype.addBodyAttribute = function (bodyAttribute, name, namespace, xmlns) {
if (!this.bodyAttributes) {
this.bodyAttributes = [];
}
if (typeof bodyAttribute === 'object') {
var composition_1 = '';
Object.getOwnPropertyNames(bodyAttribute).forEach(function (prop, idx, array) {
composition_1 += ' ' + prop + '="' + bodyAttribute[prop] + '"';
});
bodyAttribute = composition_1;
}
if (bodyAttribute.substr(0, 1) !== ' ') {
bodyAttribute = ' ' + bodyAttribute;
}
this.bodyAttributes.push(bodyAttribute);
};
Client.prototype.getBodyAttributes = function () {
return this.bodyAttributes;
};
Client.prototype.clearBodyAttributes = function () {
this.bodyAttributes = null;
};
/** overwrite the SOAP service endpoint address */
Client.prototype.setEndpoint = function (endpoint) {
this.endpoint = endpoint;
this._initializeServices(endpoint);
};
/** description of services, ports and methods as a JavaScript object */
Client.prototype.describe = function () {
return this.wsdl.describeServices();
};
/** use the specified security protocol */
Client.prototype.setSecurity = function (security) {
this.security = security;
};
Client.prototype.setSOAPAction = function (SOAPAction) {
this.SOAPAction = SOAPAction;
};
Client.prototype._initializeServices = function (endpoint) {
var definitions = this.wsdl.definitions;
var services = definitions.services;
for (var name_1 in services) {
this[name_1] = this._defineService(services[name_1], endpoint);
}
};
Client.prototype._initializeOptions = function (options) {
this.streamAllowed = options.stream;
this.returnSaxStream = options.returnSaxStream;
this.normalizeNames = options.normalizeNames;
this.overridePromiseSuffix = options.overridePromiseSuffix || 'Async';
this.wsdl.options.attributesKey = options.attributesKey || 'attributes';
this.wsdl.options.envelopeKey = options.envelopeKey || 'soap';
this.wsdl.options.preserveWhitespace = !!options.preserveWhitespace;
var igNs = options.ignoredNamespaces;
if (igNs !== undefined && typeof igNs === 'object') {
if ('override' in igNs) {
if (igNs.override === true) {
if (igNs.namespaces !== undefined) {
this.wsdl.options.ignoredNamespaces = igNs.namespaces;
}
}
}
}
if (options.overrideRootElement !== undefined) {
this.wsdl.options.overrideRootElement = options.overrideRootElement;
}
this.wsdl.options.forceSoap12Headers = !!options.forceSoap12Headers;
};
Client.prototype._defineService = function (service, endpoint) {
var ports = service.ports;
var def = {};
for (var name_2 in ports) {
def[name_2] = this._definePort(ports[name_2], endpoint ? endpoint : ports[name_2].location);
}
return def;
};
Client.prototype._definePort = function (port, endpoint) {
var location = endpoint;
var binding = port.binding;
var methods = binding.methods;
var def = {};
for (var name_3 in methods) {
def[name_3] = this._defineMethod(methods[name_3], location);
var methodName = this.normalizeNames ? name_3.replace(nonIdentifierChars, '_') : name_3;
this[methodName] = def[name_3];
if (!nonIdentifierChars.test(methodName)) {
var suffix = this.overridePromiseSuffix;
this[methodName + suffix] = this._promisifyMethod(def[name_3]);
}
}
return def;
};
Client.prototype._promisifyMethod = function (method) {
return function (args, options, extraHeaders) {
return new Promise(function (resolve, reject) {
var callback = function (err, result, rawResponse, soapHeader, rawRequest) {
if (err) {
reject(err);
}
else {
resolve([result, rawResponse, soapHeader, rawRequest]);
}
};
method(args, callback, options, extraHeaders);
});
};
};
Client.prototype._defineMethod = function (method, location) {
var _this_1 = this;
var temp;
return function (args, callback, options, extraHeaders) {
if (typeof args === 'function') {
callback = args;
args = {};
}
else if (typeof options === 'function') {
temp = callback;
callback = options;
options = temp;
}
else if (typeof extraHeaders === 'function') {
temp = callback;
callback = extraHeaders;
extraHeaders = options;
options = temp;
}
_this_1._invoke(method, args, location, function (error, result, rawResponse, soapHeader, rawRequest) {
callback(error, result, rawResponse, soapHeader, rawRequest);
}, options, extraHeaders);
};
};
Client.prototype._processSoapHeader = function (soapHeader, name, namespace, xmlns) {
switch (typeof soapHeader) {
case 'object':
return this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
case 'function':
var _this_2 = this;
// arrow function does not support arguments variable
// tslint:disable-next-line
return function () {
var result = soapHeader.apply(null, arguments);
if (typeof result === 'object') {
return _this_2.wsdl.objectToXML(result, name, namespace, xmlns, true);
}
else {
return result;
}
};
default:
return soapHeader;
}
};
Client.prototype._invoke = function (method, args, location, callback, options, extraHeaders) {
var _this_1 = this;
var name = method.$name;
var input = method.input;
var output = method.output;
var style = method.style;
var defs = this.wsdl.definitions;
var envelopeKey = this.wsdl.options.envelopeKey;
var ns = defs.$targetNamespace;
var encoding = '';
var message = '';
var xml = null;
var req;
var soapAction;
var alias = utils_1.findPrefix(defs.xmlns, ns);
var headers = {
'Content-Type': 'text/xml; charset=utf-8'
};
var xmlnsSoap = 'xmlns:' + envelopeKey + '="http://schemas.xmlsoap.org/soap/envelope/"';
var finish = function (obj, body, response) {
var result;
if (!output) {
// one-way, no output expected
return callback(null, null, body, obj.Header, xml);
}
// If it's not HTML and Soap Body is empty
if (!obj.html && !obj.Body) {
if (response.statusCode >= 400) {
var error = new Error('Error http status codes');
error.response = response;
error.body = body;
_this_1.emit('soapError', error, eid);
return callback(error, obj, body, obj.Header);
}
return callback(null, obj, body, obj.Header);
}
if (typeof obj.Body !== 'object') {
var error = new Error('Cannot parse response');
error.response = response;
error.body = body;
return callback(error, obj, body, undefined, xml);
}
result = obj.Body[output.$name];
// RPC/literal response body may contain elements with added suffixes I.E.
// 'Response', or 'Output', or 'Out'
// This doesn't necessarily equal the ouput message name. See WSDL 1.1 Section 2.4.5
if (!result) {
result = obj.Body[output.$name.replace(/(?:Out(?:put)?|Response)$/, '')];
}
if (!result) {
['Response', 'Out', 'Output'].forEach(function (term) {
if (obj.Body.hasOwnProperty(name + term)) {
return result = obj.Body[name + term];
}
});
}
callback(null, result, body, obj.Header, xml);
};
var parseSync = function (body, response) {
var obj;
try {
obj = _this_1.wsdl.xmlToObject(body);
}
catch (error) {
// When the output element cannot be looked up in the wsdl and the body is JSON
// instead of sending the error, we pass the body in the response.
if (!output || !output.$lookupTypes) {
debug('Response element is not present. Unable to convert response xml to json.');
// If the response is JSON then return it as-is.
var json = _.isObject(body) ? body : tryJSONparse(body);
if (json) {
return callback(null, response, json, undefined, xml);
}
}
error.response = response;
error.body = body;
_this_1.emit('soapError', error, eid);
return callback(error, response, body, undefined, xml);
}
return finish(obj, body, response);
};
if (this.SOAPAction) {
soapAction = this.SOAPAction;
}
else if (method.soapAction !== undefined && method.soapAction !== null) {
soapAction = method.soapAction;
}
else {
soapAction = ((ns.lastIndexOf('/') !== ns.length - 1) ? ns + '/' : ns) + name;
}
if (this.wsdl.options.forceSoap12Headers) {
headers['Content-Type'] = "application/soap+xml; charset=utf-8; action=\"" + soapAction + "\"";
xmlnsSoap = 'xmlns:' + envelopeKey + '="http://www.w3.org/2003/05/soap-envelope"';
}
else {
headers.SOAPAction = '"' + soapAction + '"';
}
options = options || {};
// Add extra headers
if (this.httpHeaders === null) {
headers = {};
}
else {
for (var header in this.httpHeaders) {
headers[header] = this.httpHeaders[header];
}
for (var attr in extraHeaders) {
headers[attr] = extraHeaders[attr];
}
}
// Allow the security object to add headers
if (this.security && this.security.addHeaders) {
this.security.addHeaders(headers);
}
if (this.security && this.security.addOptions) {
this.security.addOptions(options);
}
if ((style === 'rpc') && ((input.parts || input.name === 'element') || args === null)) {
assert.ok(!style || style === 'rpc', 'invalid message definition for document style binding');
message = this.wsdl.objectToRpcXML(name, args, alias, ns, (input.name !== 'element'));
(method.inputSoap === 'encoded') && (encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ');
}
else {
assert.ok(!style || style === 'document', 'invalid message definition for rpc style binding');
// pass `input.$lookupType` if `input.$type` could not be found
message = this.wsdl.objectToDocumentXML(input.$name, args, input.targetNSAlias, input.targetNamespace, (input.$type || input.$lookupType));
}
var decodedHeaders;
if (this.soapHeaders) {
decodedHeaders = this.soapHeaders.map(function (header) {
if (typeof header === 'function') {
return header(method, location, soapAction, args);
}
else {
return header;
}
}).join(' ');
}
xml = '<?xml version="1.0" encoding="utf-8"?>' +
'<' + envelopeKey + ':Envelope ' +
xmlnsSoap + ' ' +
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
((decodedHeaders || this.security) ?
('<' + envelopeKey + ':Header' + (this.wsdl.xmlnsInHeader ? (' ' + this.wsdl.xmlnsInHeader) : '') + '>' +
(decodedHeaders ? decodedHeaders : '') +
(this.security && !this.security.postProcess ? this.security.toXML() : '') +
'</' + envelopeKey + ':Header>')
:
'') +
'<' + envelopeKey + ':Body' +
(this.bodyAttributes ? this.bodyAttributes.join(' ') : '') +
(this.security && this.security.postProcess ? ' Id="_0"' : '') +
'>' +
message +
'</' + envelopeKey + ':Body>' +
'</' + envelopeKey + ':Envelope>';
if (this.security && this.security.postProcess) {
xml = this.security.postProcess(xml, envelopeKey);
}
if (options && options.postProcess) {
xml = options.postProcess(xml);
}
this.lastMessage = message;
this.lastRequest = xml;
this.lastEndpoint = location;
var eid = options.exchangeId || uuid_1.v4();
this.emit('message', message, eid);
this.emit('request', xml, eid);
var tryJSONparse = function (body) {
try {
return JSON.parse(body);
}
catch (err) {
return undefined;
}
};
if (this.streamAllowed && typeof this.httpClient.requestStream === 'function') {
callback = _.once(callback);
var startTime_1 = Date.now();
req = this.httpClient.requestStream(location, xml, headers, options, this);
this.lastRequestHeaders = req.headers;
var onError_1 = function (err) {
_this_1.lastResponse = null;
_this_1.lastResponseHeaders = null;
_this_1.lastElapsedTime = null;
_this_1.emit('response', null, null, eid);
callback(err, undefined, undefined, undefined, xml);
};
req.on('error', onError_1);
req.on('response', function (response) {
response.on('error', onError_1);
// When the output element cannot be looked up in the wsdl, play it safe and
// don't stream
if (response.statusCode !== 200 || !output || !output.$lookupTypes) {
getStream(response).then(function (body) {
_this_1.lastResponse = body;
_this_1.lastResponseHeaders = response && response.headers;
_this_1.lastElapsedTime = Date.now() - startTime_1;
_this_1.emit('response', body, response, eid);
return parseSync(body, response);
});
return;
}
if (_this_1.returnSaxStream) {
// directly return the saxStream allowing the end user to define
// the parsing logics and corresponding errors managements
var saxStream = _this_1.wsdl.getSaxStream(response);
return finish({ saxStream: saxStream }, '<stream>', response);
}
else {
_this_1.wsdl.xmlToObject(response, function (error, obj) {
_this_1.lastResponse = response;
_this_1.lastResponseHeaders = response && response.headers;
_this_1.lastElapsedTime = Date.now() - startTime_1;
_this_1.emit('response', '<stream>', response, eid);
if (error) {
error.response = response;
error.body = '<stream>';
_this_1.emit('soapError', error, eid);
return callback(error, response, undefined, undefined, xml);
}
return finish(obj, '<stream>', response);
});
}
});
return;
}
req = this.httpClient.request(location, xml, function (err, response, body) {
_this_1.lastResponse = body;
_this_1.lastResponseHeaders = response && response.headers;
_this_1.lastElapsedTime = response && response.elapsedTime;
_this_1.emit('response', body, response, eid);
if (err) {
callback(err, undefined, undefined, undefined, xml);
}
else {
return parseSync(body, response);
}
}, headers, options, this);
// Added mostly for testability, but possibly useful for debugging
if (req && req.headers && !options.ntlm) { // fixes an issue when req or req.headers is undefined, doesn't apply to ntlm requests
this.lastRequestHeaders = req.headers;
}
};
return Client;
}(events_1.EventEmitter));
exports.Client = Client;
//# sourceMappingURL=client.js.map