function BasicAuthSecurity(username, password, defaults) {
this._username = username;
this._password = password;
this.defaults = {};
_.merge(this.defaults, defaults);
}...
as well. The interface is quite simple. Each protocol defines 2 methods:
* `addOptions` - a method that accepts an options arg that is eventually passed directly to `request`
* `toXML` - a method that returns a string of XML.
### BasicAuthSecurity
``` javascript
client.setSecurity(new soap.BasicAuthSecurity('username', 'password
x27;));
```
### BearerSecurity
``` javascript
client.setSecurity(new soap.BearerSecurity('token'));
```
...function BearerSecurity(token, defaults) {
this._token = token;
this.defaults = {};
_.merge(this.defaults, defaults);
}...
``` javascript
client.setSecurity(new soap.BasicAuthSecurity('username', 'password'));
```
### BearerSecurity
``` javascript
client.setSecurity(new soap.BearerSecurity('token'));
```
### ClientSSLSecurity
_Note_: If you run into issues using this protocol, consider passing these options
as default request options to the constructor:
* `rejectUnauthorized: false`
...Client = function (wsdl, endpoint, options) {
events.EventEmitter.call(this);
options = options || {};
this.wsdl = wsdl;
this._initializeOptions(options);
this._initializeServices(endpoint);
this.httpClient = options.httpClient || new HttpClient(options);
}n/a
function ClientSSLSecurity(key, cert, ca, defaults) {
if (key) {
if(Buffer.isBuffer(key)) {
this.key = key;
} else if (typeof key === 'string') {
this.key = fs.readFileSync(key);
} else {
throw new Error('key should be a buffer or a string!');
}
}
if (cert) {
if(Buffer.isBuffer(cert)) {
this.cert = cert;
} else if (typeof cert === 'string') {
this.cert = fs.readFileSync(cert);
} else {
throw new Error('cert should be a buffer or a string!');
}
}
if (ca) {
if(Buffer.isBuffer(ca) || Array.isArray(ca)) {
this.ca = ca;
} else if (typeof ca === 'string') {
this.ca = fs.readFileSync(ca);
} else {
defaults = ca;
this.ca = null;
}
}
this.defaults = {};
_.merge(this.defaults, defaults);
}...
_Note_: If you run into issues using this protocol, consider passing these options
as default request options to the constructor:
* `rejectUnauthorized: false`
* `strictSSL: false`
* `secureOptions: constants.SSL_OP_NO_TLSv1_2` (this is likely needed for node >= 10.0)
``` javascript
client.setSecurity(new soap.ClientSSLSecurity(
'/path/to/key'
, '/path/to/cert'
, {/*default request options*/}
));
```
### WSSecurity
...function ClientSSLSecurityPFX(pfx, passphrase, defaults) {
if (typeof passphrase === 'object') {
defaults = passphrase;
}
if (pfx) {
if (Buffer.isBuffer(pfx)) {
this.pfx = pfx;
} else if (typeof pfx === 'string') {
this.pfx = fs.readFileSync(pfx);
} else {
throw new Error('supplied pfx file should be a buffer or a file location');
}
}
if (passphrase) {
if (typeof passphrase === 'string') {
this.passphrase = passphrase;
}
}
this.defaults = {};
_.merge(this.defaults, defaults);
}n/a
function HttpClient(options) {
options = options || {};
this._request = options.request || req;
}n/a
Server = function (server, path, services, wsdl, options) {
var self = this;
events.EventEmitter.call(this);
options = options || {};
this.path = path;
this.services = services;
this.wsdl = wsdl;
this.suppressStack = options && options.suppressStack;
if (path[path.length - 1] !== '/')
path += '/';
wsdl.onReady(function (err) {
if (typeof server.route === 'function' && typeof server.use === 'function') {
//handle only the required URL path for express server
server.route(path).all(function (req, res, next) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
res.end();
return;
}
}
self._requestListener(req, res);
});
} else {
var listeners = server.listeners('request').slice();
server.removeAllListeners('request');
server.addListener('request', function (req, res) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
res.end();
return;
}
}
var reqPath = url.parse(req.url).pathname;
if (reqPath[reqPath.length - 1] !== '/') {
reqPath += '/';
}
if (path === reqPath) {
self._requestListener(req, res);
} else {
for (var i = 0, len = listeners.length; i < len; i++) {
listeners[i].call(this, req, res);
}
}
});
}
});
this._initializeOptions(options);
}n/a
WSDL = function (definition, uri, options) {
var self = this,
fromFunc;
this.uri = uri;
this.callback = function() {
};
this._includesWsdl = [];
// initialize WSDL cache
this.WSDL_CACHE = (options || {}).WSDL_CACHE || {};
this._initializeOptions(options);
if (typeof definition === 'string') {
definition = stripBom(definition);
fromFunc = this._fromXML;
}
else if (typeof definition === 'object') {
fromFunc = this._fromServices;
}
else {
throw new Error('WSDL constructor takes either an XML string or service definition');
}
process.nextTick(function() {
try {
fromFunc.call(self, definition);
} catch (e) {
return self.callback(e.message);
}
self.processIncludes(function(err) {
var name;
if (err) {
return self.callback(err);
}
self.definitions.deleteFixedAttrs();
var services = self.services = self.definitions.services;
if (services) {
for (name in services) {
services[name].postProcess(self.definitions);
}
}
var complexTypes = self.definitions.complexTypes;
if (complexTypes) {
for (name in complexTypes) {
complexTypes[name].deleteFixedAttrs();
}
}
// for document style, for every binding, prepare input message element name to (methodName, output message element name)
mapping
var bindings = self.definitions.bindings;
for (var bindingName in bindings) {
var binding = bindings[bindingName];
if (typeof binding.style === 'undefined') {
binding.style = 'document';
}
if (binding.style !== 'document')
continue;
var methods = binding.methods;
var topEls = binding.topElements = {};
for (var methodName in methods) {
if (methods[methodName].input) {
var inputName = methods[methodName].input.$name;
var outputName="";
if(methods[methodName].output )
outputName = methods[methodName].output.$name;
topEls[inputName] = {"methodName": methodName, "outputName": outputName};
}
}
}
// prepare soap envelope xmlns definition string
self.xmlnsInEnvelope = self._xmlnsMap();
self.callback(err, self);
});
});
}n/a
function WSSecurity(username, password, options) {
options = options || {};
this._username = username;
this._password = password;
//must account for backward compatibility for passwordType String param as well as object options defaults: passwordType = 'PasswordText
', hasTimeStamp = true
if (typeof options === 'string') {
this._passwordType = options ? options : 'PasswordText';
options = {};
} else {
this._passwordType = options.passwordType ? options.passwordType : 'PasswordText';
}
if (validPasswordTypes.indexOf(this._passwordType) === -1) {
this._passwordType = 'PasswordText';
}
this._hasTimeStamp = options.hasTimeStamp || typeof options.hasTimeStamp === 'boolean' ? !!options.hasTimeStamp : true;
/*jshint eqnull:true */
if (options.hasNonce != null) {
this._hasNonce = !!options.hasNonce;
}
this._hasTokenCreated = options.hasTokenCreated || typeof options.hasTokenCreated === 'boolean' ? !!options.hasTokenCreated :
true;
if (options.actor != null) {
this._actor = options.actor;
}
if (options.mustUnderstand != null) {
this._mustUnderstand = !!options.mustUnderstand;
}
}n/a
function WSSecurityCert(privatePEM, publicP12PEM, password, encoding) {
if (!ursa) {
throw new Error('Module ursa must be installed to use WSSecurityCert');
}
this.privateKey = ursa.createPrivateKey(privatePEM, password, encoding);
this.publicP12PEM = publicP12PEM.toString().replace('-----BEGIN CERTIFICATE-----', '').replace('-----END CERTIFICATE-----', '').
replace(/(\r\n|\n|\r)/gm, '');
this.signer = new SignedXml();
this.signer.signingKey = this.privateKey.toPrivatePem();
this.x509Id = "x509-" + generateId();
var _this = this;
this.signer.keyInfoProvider = {};
this.signer.keyInfoProvider.getKeyInfo = function (key) {
return wsseSecurityTokenTemplate({ x509Id: _this.x509Id });
};
}...
WS-Security X509 Certificate support.
``` javascript
var privateKey = fs.readFileSync(privateKeyPath);
var publicKey = fs.readFileSync(publicKeyPath);
var password = ''; // optional password
var wsSecurity = new soap.WSSecurityCert(privateKey, publicKey, password, 'utf8
');
client.setSecurity(wsSecurity);
```
_Note_: Optional dependency 'ursa' is required to be installed successfully when WSSecurityCert is used.
## Handling XML Attributes, Value and XML (wsdlOptions).
Sometimes it is necessary to override the default behaviour of `node-soap` in order to deal with the special requirements
...function createClient(url, options, callback, endpoint) {
if (typeof options === 'function') {
endpoint = callback;
callback = options;
options = {};
}
endpoint = options.endpoint || endpoint;
_requestWSDL(url, options, function(err, wsdl) {
callback(err, wsdl && new Client(wsdl, endpoint, options));
});
}...
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Features:](#features)
- [Install](#install)
- [Where can I file an issue?](#where-can-i-file-an-issue)
- [Module](#module)
- [soap.createClient(url[, options], callback) - create a new SOAP client from a WSDL
url. Also supports a local filesystem path.](#soapcreateclienturl-options-callback---create-a-new-soap-client-from-a-wsdl-url-also
-supports-a-local-filesystem-path)
- [soap.listen(*server*, *path*, *services*, *wsdl*) - create a new SOAP server that listens on *path* and provides *services*.](#
soaplistenserver-path-services-wsdl---create-a-new-soap-server-that-listens-on-path-and-provides-services)
- [Options](#options)
- [Server Logging](#server-logging)
- [Server Events](#server-events)
- [SOAP Fault](#soap-fault)
- [Server security example using PasswordDigest](#server-security-example-using-passworddigest)
- [Server connection authorization](#server-connection-authorization)
...function listen(server, pathOrOptions, services, xml) {
var options = {},
path = pathOrOptions,
uri = null;
if (typeof pathOrOptions === 'object') {
options = pathOrOptions;
path = options.path;
services = options.services;
xml = options.xml;
uri = options.uri;
}
var wsdl = new WSDL(xml || services, uri, options);
return new Server(server, path, services, wsdl, options);
}...
- [Features:](#features)
- [Install](#install)
- [Where can I file an issue?](#where-can-i-file-an-issue)
- [Module](#module)
- [soap.createClient(url[, options], callback) - create a new SOAP client from a WSDL url. Also supports a local filesystem path
.](#soapcreateclienturl-options-callback---create-a-new-soap-client-from-a-wsdl-url-also-supports-a-local-filesystem-path)
- [soap.listen(*server*, *path*, *services*, *wsdl*) - create a new SOAP server that
listens on *path* and provides *services*.](#soaplistenserver-path-services-wsdl---create-a-new-soap-server-that-listens-on-path
-and-provides-services)
- [Options](#options)
- [Server Logging](#server-logging)
- [Server Events](#server-events)
- [SOAP Fault](#soap-fault)
- [Server security example using PasswordDigest](#server-security-example-using-passworddigest)
- [Server connection authorization](#server-connection-authorization)
- [SOAP Headers](#soap-headers)
...function NamespaceContext() {
if (!(this instanceof NamespaceContext)) {
return new NamespaceContext();
}
this.scopes = [];
this.pushContext();
this.prefixCount = 0;
}n/a
function passwordDigest(nonce, created, password) {
// digest = base64 ( sha1 ( nonce + created + password ) )
var pwHash = crypto.createHash('sha1');
var rawNonce = new Buffer(nonce || '', 'base64').toString('binary');
pwHash.update(rawNonce + created + password);
return pwHash.digest('base64');
}...
``` javascript
server = soap.listen(...)
server.authenticate = function(security) {
var created, nonce, password, user, token;
token = security.UsernameToken, user = token.Username,
password = token.Password, nonce = token.Nonce, created = token.Created;
return user === 'user' && password === soap.passwordDigest(nonce
, created, 'password');
};
```
### Server connection authorization
The `server.authorizeConnection` method is called prior to the soap service method.
If the method is defined and returns `false` then the incoming connection is
...function BasicAuthSecurity(username, password, defaults) {
this._username = username;
this._password = password;
this.defaults = {};
_.merge(this.defaults, defaults);
}...
as well. The interface is quite simple. Each protocol defines 2 methods:
* `addOptions` - a method that accepts an options arg that is eventually passed directly to `request`
* `toXML` - a method that returns a string of XML.
### BasicAuthSecurity
``` javascript
client.setSecurity(new soap.BasicAuthSecurity('username', 'password
x27;));
```
### BearerSecurity
``` javascript
client.setSecurity(new soap.BearerSecurity('token'));
```
...addHeaders = function (headers) {
headers.Authorization = 'Basic ' + new Buffer((this._username + ':' + this._password) || '').toString('base64');
}...
//Add extra headers
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 (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.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 = self.wsdl.objectToRpcXML(name, args, alias, ns,(input.name!=="element" ));
(method.inputSoap === 'encoded') && (encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/
soap/encoding/" ');
...addOptions = function (options) {
_.merge(options, this.defaults);
}...
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 (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.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 = self.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');
...toXML = function () {
return '';
}...
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
((self.soapHeaders || self.security) ?
(
"<" + envelopeKey + ":Header>" +
(self.soapHeaders ? self.soapHeaders.join("\n") : "") +
(self.security && !self.security.postProcess ? self.security.toXML() :
x22;") +
"</" + envelopeKey + ":Header>"
)
:
''
) +
"<" + envelopeKey + ":Body" +
(self.bodyAttributes ? self.bodyAttributes.join(' ') : '') +
...function BearerSecurity(token, defaults) {
this._token = token;
this.defaults = {};
_.merge(this.defaults, defaults);
}...
``` javascript
client.setSecurity(new soap.BasicAuthSecurity('username', 'password'));
```
### BearerSecurity
``` javascript
client.setSecurity(new soap.BearerSecurity('token'));
```
### ClientSSLSecurity
_Note_: If you run into issues using this protocol, consider passing these options
as default request options to the constructor:
* `rejectUnauthorized: false`
...addHeaders = function (headers) {
headers.Authorization = "Bearer " + this._token;
}...
//Add extra headers
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 (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.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 = self.wsdl.objectToRpcXML(name, args, alias, ns,(input.name!=="element" ));
(method.inputSoap === 'encoded') && (encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/
soap/encoding/" ');
...addOptions = function (options) {
_.merge(options, this.defaults);
}...
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 (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.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 = self.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');
...toXML = function () {
return '';
}...
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
((self.soapHeaders || self.security) ?
(
"<" + envelopeKey + ":Header>" +
(self.soapHeaders ? self.soapHeaders.join("\n") : "") +
(self.security && !self.security.postProcess ? self.security.toXML() :
x22;") +
"</" + envelopeKey + ":Header>"
)
:
''
) +
"<" + envelopeKey + ":Body" +
(self.bodyAttributes ? self.bodyAttributes.join(' ') : '') +
...Client = function (wsdl, endpoint, options) {
events.EventEmitter.call(this);
options = options || {};
this.wsdl = wsdl;
this._initializeOptions(options);
this._initializeServices(endpoint);
this.httpClient = options.httpClient || new HttpClient(options);
}n/a
function EventEmitter() {
EventEmitter.init.call(this);
}n/a
_defineMethod = function (method, location) {
var self = 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;
}
self._invoke(method, args, location, function(error, result, raw, soapHeader) {
callback(error, result, raw, soapHeader);
}, options, extraHeaders);
};
}...
Client.prototype._definePort = function(port, endpoint) {
var location = endpoint,
binding = port.binding,
methods = binding.methods,
def = {};
for (var name in methods) {
def[name] = this._defineMethod(methods[name], location);
this[name] = def[name];
}
return def;
};
Client.prototype._defineMethod = function(method, location) {
var self = this;
..._definePort = function (port, endpoint) {
var location = endpoint,
binding = port.binding,
methods = binding.methods,
def = {};
for (var name in methods) {
def[name] = this._defineMethod(methods[name], location);
this[name] = def[name];
}
return def;
}...
this.wsdl.options.forceSoap12Headers = !!options.forceSoap12Headers;
};
Client.prototype._defineService = function(service, endpoint) {
var ports = service.ports,
def = {};
for (var name in ports) {
def[name] = this._definePort(ports[name], endpoint ? endpoint : ports[name].location
);
}
return def;
};
Client.prototype._definePort = function(port, endpoint) {
var location = endpoint,
binding = port.binding,
..._defineService = function (service, endpoint) {
var ports = service.ports,
def = {};
for (var name in ports) {
def[name] = this._definePort(ports[name], endpoint ? endpoint : ports[name].location);
}
return def;
}...
this.SOAPAction = SOAPAction;
};
Client.prototype._initializeServices = function(endpoint) {
var definitions = this.wsdl.definitions,
services = definitions.services;
for (var name in services) {
this[name] = this._defineService(services[name], endpoint);
}
};
Client.prototype._initializeOptions = function(options) {
this.streamAllowed = options.stream;
this.wsdl.options.attributesKey = options.attributesKey || 'attributes';
this.wsdl.options.envelopeKey = options.envelopeKey || 'soap';
..._getArgsScheme = function (methodName) {
var methodRequestName = _.result(this.wsdl.definitions, 'messages.'+methodName+'.$name');
var args = _.result(this.wsdl.definitions, 'messages.' + methodRequestName + '.parts');
if(typeof args === 'undefined' && typeof _.pick(args, 'params') !== 'undefined') {
return [];
}
if(Object.keys(args).length === 1) {
return [];
}
return args;
}...
alias = findPrefix(defs.xmlns, ns),
headers = {
"Content-Type": "text/xml; charset=utf-8"
},
xmlnsSoap = "xmlns:" + envelopeKey + "=\"http://schemas.xmlsoap.org/soap/envelope/\"";
if(this._isSequenceRequired(name)) {
var argsScheme = this._getArgsScheme(name);
if(argsScheme) {
args = this._setSequenceArgs(argsScheme, args);
}
}
if (this.wsdl.options.forceSoap12Headers) {
headers["Content-Type"] = "application/soap+xml; charset=utf-8";
..._initializeOptions = function (options) {
this.streamAllowed = options.stream;
this.wsdl.options.attributesKey = options.attributesKey || 'attributes';
this.wsdl.options.envelopeKey = options.envelopeKey || 'soap';
if(options.ignoredNamespaces !== undefined) {
if(options.ignoredNamespaces.override !== undefined) {
if(options.ignoredNamespaces.override === true) {
if(options.ignoredNamespaces.namespaces !== undefined) {
this.wsdl.options.ignoredNamespaces = options.ignoredNamespaces.namespaces;
}
}
}
}
if(options.overrideRootElement !== undefined) {
this.wsdl.options.overrideRootElement = options.overrideRootElement;
}
this.wsdl.options.forceSoap12Headers = !!options.forceSoap12Headers;
}...
concatStream = require('concat-stream'),
uuid = require('uuid');
var Client = function(wsdl, endpoint, options) {
events.EventEmitter.call(this);
options = options || {};
this.wsdl = wsdl;
this._initializeOptions(options);
this._initializeServices(endpoint);
this.httpClient = options.httpClient || new HttpClient(options);
};
util.inherits(Client, events.EventEmitter);
Client.prototype.addSoapHeader = function(soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
..._initializeServices = function (endpoint) {
var definitions = this.wsdl.definitions,
services = definitions.services;
for (var name in services) {
this[name] = this._defineService(services[name], endpoint);
}
}...
uuid = require('uuid');
var Client = function(wsdl, endpoint, options) {
events.EventEmitter.call(this);
options = options || {};
this.wsdl = wsdl;
this._initializeOptions(options);
this._initializeServices(endpoint);
this.httpClient = options.httpClient || new HttpClient(options);
};
util.inherits(Client, events.EventEmitter);
Client.prototype.addSoapHeader = function(soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
..._invoke = function (method, args, location, callback, options, extraHeaders) {
var self = this,
name = method.$name,
input = method.input,
output = method.output,
style = method.style,
defs = this.wsdl.definitions,
envelopeKey = this.wsdl.options.envelopeKey,
ns = defs.$targetNamespace,
encoding = '',
message = '',
xml = null,
req = null,
soapAction,
alias = findPrefix(defs.xmlns, ns),
headers = {
"Content-Type": "text/xml; charset=utf-8"
},
xmlnsSoap = "xmlns:" + envelopeKey + "=\"http://schemas.xmlsoap.org/soap/envelope/\"";
if(this._isSequenceRequired(name)) {
var argsScheme = this._getArgsScheme(name);
if(argsScheme) {
args = this._setSequenceArgs(argsScheme, args);
}
}
if (this.wsdl.options.forceSoap12Headers) {
headers["Content-Type"] = "application/soap+xml; charset=utf-8";
xmlnsSoap = "xmlns:" + envelopeKey + "=\"http://www.w3.org/2003/05/soap-envelope\"";
}
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.SOAPAction = '"' + soapAction + '"';
}
options = options || {};
//Add extra headers
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 (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.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 = self.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 = self.wsdl.objectToDocumentXML(input.$name, args, input.targetNSAlias, input.targetNamespace, (input.$type || input
.$lookupType));
}
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<" + envelopeKey + ":Envelope " +
xmlnsSoap + " " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
((self.soapHeaders || self.security) ?
(
"<" + envelopeKey + ":Header>" +
(self.soapHeaders ? self.soapHeaders.join("\n") : "") +
(self.security && !self.security.postProcess ? self.security.toXML() : "") +
"</" + envelopeKey + ":Header>"
)
:
''
) +
"<" + envelopeKey + ":Body" +
(self.bodyAttributes ? self.bodyAttributes.join(' ') : '') +
(self.security && self.security.postProcess ? ' Id="_0"' : '') +
">" +
message +
"</" + envelopeKey + ":Body>" +
"</" + envelopeKey + ":Envelope>";
if(self.security && self.security.postProcess){
xml = self.security.postProcess(xml, envelopeKey);
}
self.lastMessage = message;
self.lastRequest = xml;
self.lastEndpoint = location;
var eid = options.exchangeId || uuid.v4();
self.emit('message', message, eid);
self.emit('request', xml, eid);
var tryJSONparse = function(body) {
try {
return JSON.parse(body);
}
catch(err) {
return undefined;
}
};
if (this.streamAllowed && typeof self.httpClient.requestStream === 'function') {
callback = _.once(callback);
var startTime = Date.now();
req = self.httpClient.requestStream(locat ......
options = temp;
} else if (typeof extraHeaders === 'function') {
temp = callback;
callback = extraHeaders;
extraHeaders = options;
options = temp;
}
self._invoke(method, args, location, function(error, result, raw, soapHeader) {
callback(error, result, raw, soapHeader);
}, options, extraHeaders);
};
};
Client.prototype._isSequenceRequired = function(methodName) {
var tns = this.wsdl.definitions.$targetNamespace;
..._isSequenceRequired = function (methodName) {
var tns = this.wsdl.definitions.$targetNamespace;
var methodRequestName = _.result(this.wsdl.definitions, 'messages.' + methodName + '.$name');
var args = _.result(this.wsdl.definitions, 'messages.' + methodRequestName + '.parts');
if(typeof args === 'undefined' && typeof _.pick(args, 'params') !== 'undefined') {
return false;
}
if(Object.keys(args).length === 1) {
return false;
}
var complexTypeName = _.result(this.wsdl.definitions, 'messages.' + methodRequestName + '.element.$name');
var modeOfComplexType = _.result(
this.wsdl.definitions,
'schemas[\'' + tns + '\'].elements.' + complexTypeName + '.children[0].children[0].name');
if(modeOfComplexType === 'sequence') {
return true;
}
return false;
}...
soapAction,
alias = findPrefix(defs.xmlns, ns),
headers = {
"Content-Type": "text/xml; charset=utf-8"
},
xmlnsSoap = "xmlns:" + envelopeKey + "=\"http://schemas.xmlsoap.org/soap/envelope/\"";
if(this._isSequenceRequired(name)) {
var argsScheme = this._getArgsScheme(name);
if(argsScheme) {
args = this._setSequenceArgs(argsScheme, args);
}
}
if (this.wsdl.options.forceSoap12Headers) {
..._setSequenceArgs = function (argsScheme, args) {
var result = {};
if(typeof argsScheme !== 'object') {
return args;
}
for (var partIndex in argsScheme) {
if(typeof args[partIndex] === 'undefined') {
continue;
}
if(typeof argsScheme[partIndex] !== 'object') {
result[partIndex] = args[partIndex];
} else {
result[partIndex] = this._setSequenceArgs(argsScheme[partIndex], args[partIndex]);
}
}
return result;
}...
for (var partIndex in argsScheme) {
if(typeof args[partIndex] === 'undefined') {
continue;
}
if(typeof argsScheme[partIndex] !== 'object') {
result[partIndex] = args[partIndex];
} else {
result[partIndex] = this._setSequenceArgs(argsScheme[partIndex], args[partIndex]);
}
}
return result;
};
Client.prototype._getArgsScheme = function(methodName) {
var methodRequestName = _.result(this.wsdl.definitions, 'messages.'+methodName+'.$name');
...addBodyAttribute = function (bodyAttribute, name, namespace, xmlns) {
if (!this.bodyAttributes) {
this.bodyAttributes = [];
}
if (typeof bodyAttribute === 'object') {
var composition = '';
Object.getOwnPropertyNames(bodyAttribute).forEach(function(prop, idx, array) {
composition += ' ' + prop + '="' + bodyAttribute[prop] + '"';
});
bodyAttribute = composition;
}
if (bodyAttribute.substr(0, 1) !== ' ') bodyAttribute = ' ' + bodyAttribute;
this.bodyAttributes.push(bodyAttribute);
}n/a
addHttpHeader = function (name, value) {
if (!this.httpHeaders) {
this.httpHeaders = {};
}
this.httpHeaders[name] = value;
}...
#### Extra Headers (optional)
Object properties define extra HTTP headers to be sent on the request.
- Add custom User-Agent:
```javascript
client.addHttpHeader('User-Agent', `CustomUserAgent`);
```
#### Alternative method call using callback-last pattern
To align method call signature with node' standard callback-last patter and event allow promisification of method calls, the
following method signatures are also supported:
```javascript
...addSoapHeader = function (soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
if (typeof soapHeader === 'object') {
soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
}
return this.soapHeaders.push(soapHeader) - 1;
}n/a
changeSoapHeader = function (index, soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
if (typeof soapHeader === 'object') {
soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
}
this.soapHeaders[index] = soapHeader;
}n/a
clearBodyAttributes = function () {
this.bodyAttributes = null;
}n/a
clearHttpHeaders = function () {
this.httpHeaders = {};
}n/a
clearSoapHeaders = function () {
this.soapHeaders = null;
}n/a
describe = function () {
var types = this.wsdl.definitions.types;
return this.wsdl.describeServices();
}...
- [SOAP Fault](#soap-fault)
- [Server security example using PasswordDigest](#server-security-example-using-passworddigest)
- [Server connection authorization](#server-connection-authorization)
- [SOAP Headers](#soap-headers)
- [Received SOAP Headers](#received-soap-headers)
- [Outgoing SOAP Headers](#outgoing-soap-headers)
- [Client](#client)
- [Client.describe() - description of services, ports and methods as a JavaScript object
](#clientdescribe---description-of-services-ports-and-methods-as-a-javascript-object)
- [Client.setSecurity(security) - use the specified security protocol](#clientsetsecuritysecurity---use-the-specified-security
-protocol)
- [Client.*method*(args, callback) - call *method* on the SOAP service.](#clientmethodargs-callback---call-method-on-the-soap-
service)
- [Client.*service*.*port*.*method*(args, callback[, options[, extraHeaders]]) - call a *method* using a specific *service* and
*port*](#clientserviceportmethodargs-callback-options-extraheaders---call-a-method-using-a-specific-service-and-port)
- [Client.*lastRequest* - the property that contains last full soap request for client logging](#clientlastrequest---the-property
-that-contains-last-full-soap-request-for-client-logging)
- [Client.setEndpoint(url) - overwrite the SOAP service endpoint address](#clientsetendpointurl---overwrite-the-soap-service-endpoint
-address)
- [Client Events](#client-events)
- [Security](#security)
...getBodyAttributes = function () {
return this.bodyAttributes;
}n/a
getHttpHeaders = function () {
return this.httpHeaders;
}n/a
getSoapHeaders = function () {
return this.soapHeaders;
}n/a
setEndpoint = function (endpoint) {
this.endpoint = endpoint;
this._initializeServices(endpoint);
}...
- [Outgoing SOAP Headers](#outgoing-soap-headers)
- [Client](#client)
- [Client.describe() - description of services, ports and methods as a JavaScript object](#clientdescribe---description-of-services
-ports-and-methods-as-a-javascript-object)
- [Client.setSecurity(security) - use the specified security protocol](#clientsetsecuritysecurity---use-the-specified-security-protocol
)
- [Client.*method*(args, callback) - call *method* on the SOAP service.](#clientmethodargs-callback---call-method-on-the-soap-service
)
- [Client.*service*.*port*.*method*(args, callback[, options[, extraHeaders]]) - call a *method* using a specific *service* and *
port*](#clientserviceportmethodargs-callback-options-extraheaders---call-a-method-using-a-specific-service-and-port)
- [Client.*lastRequest* - the property that contains last full soap request for client logging](#clientlastrequest---the-property
-that-contains-last-full-soap-request-for-client-logging)
- [Client.setEndpoint(url) - overwrite the SOAP service endpoint address](#clientsetendpointurl
---overwrite-the-soap-service-endpoint-address)
- [Client Events](#client-events)
- [Security](#security)
- [BasicAuthSecurity](#basicauthsecurity)
- [BearerSecurity](#bearersecurity)
- [ClientSSLSecurity](#clientsslsecurity)
- [WSSecurity](#wssecurity)
- [WSSecurityCert](#wssecuritycert)
...setSOAPAction = function (SOAPAction) {
this.SOAPAction = SOAPAction;
}n/a
setSecurity = function (security) {
this.security = security;
}...
- [Server security example using PasswordDigest](#server-security-example-using-passworddigest)
- [Server connection authorization](#server-connection-authorization)
- [SOAP Headers](#soap-headers)
- [Received SOAP Headers](#received-soap-headers)
- [Outgoing SOAP Headers](#outgoing-soap-headers)
- [Client](#client)
- [Client.describe() - description of services, ports and methods as a JavaScript object](#clientdescribe---description-of-services
-ports-and-methods-as-a-javascript-object)
- [Client.setSecurity(security) - use the specified security protocol](#clientsetsecuritysecurity
---use-the-specified-security-protocol)
- [Client.*method*(args, callback) - call *method* on the SOAP service.](#clientmethodargs-callback---call-method-on-the-soap-service
)
- [Client.*service*.*port*.*method*(args, callback[, options[, extraHeaders]]) - call a *method* using a specific *service* and *
port*](#clientserviceportmethodargs-callback-options-extraheaders---call-a-method-using-a-specific-service-and-port)
- [Client.*lastRequest* - the property that contains last full soap request for client logging](#clientlastrequest---the-property
-that-contains-last-full-soap-request-for-client-logging)
- [Client.setEndpoint(url) - overwrite the SOAP service endpoint address](#clientsetendpointurl---overwrite-the-soap-service-endpoint
-address)
- [Client Events](#client-events)
- [Security](#security)
- [BasicAuthSecurity](#basicauthsecurity)
...function ClientSSLSecurity(key, cert, ca, defaults) {
if (key) {
if(Buffer.isBuffer(key)) {
this.key = key;
} else if (typeof key === 'string') {
this.key = fs.readFileSync(key);
} else {
throw new Error('key should be a buffer or a string!');
}
}
if (cert) {
if(Buffer.isBuffer(cert)) {
this.cert = cert;
} else if (typeof cert === 'string') {
this.cert = fs.readFileSync(cert);
} else {
throw new Error('cert should be a buffer or a string!');
}
}
if (ca) {
if(Buffer.isBuffer(ca) || Array.isArray(ca)) {
this.ca = ca;
} else if (typeof ca === 'string') {
this.ca = fs.readFileSync(ca);
} else {
defaults = ca;
this.ca = null;
}
}
this.defaults = {};
_.merge(this.defaults, defaults);
}...
_Note_: If you run into issues using this protocol, consider passing these options
as default request options to the constructor:
* `rejectUnauthorized: false`
* `strictSSL: false`
* `secureOptions: constants.SSL_OP_NO_TLSv1_2` (this is likely needed for node >= 10.0)
``` javascript
client.setSecurity(new soap.ClientSSLSecurity(
'/path/to/key'
, '/path/to/cert'
, {/*default request options*/}
));
```
### WSSecurity
...addOptions = function (options) {
options.key = this.key;
options.cert = this.cert;
options.ca = this.ca;
_.merge(options, this.defaults);
options.agent = new https.Agent(options);
}...
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 (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.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 = self.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');
...toXML = function (headers) {
return '';
}...
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
((self.soapHeaders || self.security) ?
(
"<" + envelopeKey + ":Header>" +
(self.soapHeaders ? self.soapHeaders.join("\n") : "") +
(self.security && !self.security.postProcess ? self.security.toXML() :
x22;") +
"</" + envelopeKey + ":Header>"
)
:
''
) +
"<" + envelopeKey + ":Body" +
(self.bodyAttributes ? self.bodyAttributes.join(' ') : '') +
...function ClientSSLSecurityPFX(pfx, passphrase, defaults) {
if (typeof passphrase === 'object') {
defaults = passphrase;
}
if (pfx) {
if (Buffer.isBuffer(pfx)) {
this.pfx = pfx;
} else if (typeof pfx === 'string') {
this.pfx = fs.readFileSync(pfx);
} else {
throw new Error('supplied pfx file should be a buffer or a file location');
}
}
if (passphrase) {
if (typeof passphrase === 'string') {
this.passphrase = passphrase;
}
}
this.defaults = {};
_.merge(this.defaults, defaults);
}n/a
addOptions = function (options) {
options.pfx = this.pfx;
if (this.passphrase) {
options.passphrase = this.passphrase;
}
_.merge(options, this.defaults);
options.agent = new https.Agent(options);
}...
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 (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.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 = self.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');
...toXML = function (headers) {
return '';
}...
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
((self.soapHeaders || self.security) ?
(
"<" + envelopeKey + ":Header>" +
(self.soapHeaders ? self.soapHeaders.join("\n") : "") +
(self.security && !self.security.postProcess ? self.security.toXML() :
x22;") +
"</" + envelopeKey + ":Header>"
)
:
''
) +
"<" + envelopeKey + ":Body" +
(self.bodyAttributes ? self.bodyAttributes.join(' ') : '') +
...function HttpClient(options) {
options = options || {};
this._request = options.request || req;
}n/a
buildRequest = function (rurl, data, exheaders, exoptions) {
var curl = url.parse(rurl);
var secure = curl.protocol === 'https:';
var host = curl.hostname;
var port = parseInt(curl.port, 10);
var path = [curl.pathname || '/', curl.search || '', curl.hash || ''].join('');
var method = data ? 'POST' : 'GET';
var headers = {
'User-Agent': 'node-soap/' + VERSION,
'Accept': 'text/html,application/xhtml+xml,application/xml,text/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'none',
'Accept-Charset': 'utf-8',
'Connection': 'close',
'Host': host + (isNaN(port) ? '' : ':' + port)
};
var attr;
var header;
var mergeOptions = ['headers'];
if (typeof data === 'string') {
headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
exheaders = exheaders || {};
for (attr in exheaders) {
headers[attr] = exheaders[attr];
}
var options = {
uri: curl,
method: method,
headers: headers,
followAllRedirects: true
};
options.body = data;
exoptions = exoptions || {};
for (attr in exoptions) {
if (mergeOptions.indexOf(attr) !== -1) {
for (header in exoptions[attr]) {
options[attr][header] = exoptions[attr][header];
}
} else {
options[attr] = exoptions[attr];
}
}
debug('Http request: %j', options);
return options;
}n/a
handleResponse = function (req, res, body) {
debug('Http response body: %j', body);
if (typeof body === 'string') {
// Remove any extra characters that appear before or after the SOAP
// envelope.
var match =
body.replace(/<!--[\s\S]*?-->/, "").match(/(?:<\?[^?]*\?>[\s]*)?<([^:]*):Envelope([\S\s]*)<\/\1:Envelope>/i);
if (match) {
body = match[0];
}
}
return body;
}n/a
request = function (rurl, data, callback, exheaders, exoptions) {
var self = this;
var options = self.buildRequest(rurl, data, exheaders, exoptions);
var headers = options.headers;
var req = self._request(options, function(err, res, body) {
if (err) {
return callback(err);
}
body = self.handleResponse(req, res, body);
callback(null, res, body);
});
return req;
}...
return finish(obj, '<stream>', response);
});
});
return;
}
req = self.httpClient.request(location, xml, function(err, response, body) {
self.lastResponse = body;
self.lastResponseHeaders = response && response.headers;
self.lastElapsedTime = response && response.elapsedTime;
self.emit('response', body, response, eid);
if (err) {
callback(err);
...requestStream = function (rurl, data, exheaders, exoptions) {
var self = this;
var options = self.buildRequest(rurl, data, exheaders, exoptions);
return self._request(options);
}...
return undefined;
}
};
if (this.streamAllowed && typeof self.httpClient.requestStream === 'function') {
callback = _.once(callback);
var startTime = Date.now();
req = self.httpClient.requestStream(location, xml, headers, options, self);
self.lastRequestHeaders = req.headers;
var onError = function onError(err) {
self.lastResponse = null;
self.lastResponseHeaders = null;
self.lastElapsedTime = null;
self.emit('response', null, null, eid);
...Server = function (server, path, services, wsdl, options) {
var self = this;
events.EventEmitter.call(this);
options = options || {};
this.path = path;
this.services = services;
this.wsdl = wsdl;
this.suppressStack = options && options.suppressStack;
if (path[path.length - 1] !== '/')
path += '/';
wsdl.onReady(function (err) {
if (typeof server.route === 'function' && typeof server.use === 'function') {
//handle only the required URL path for express server
server.route(path).all(function (req, res, next) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
res.end();
return;
}
}
self._requestListener(req, res);
});
} else {
var listeners = server.listeners('request').slice();
server.removeAllListeners('request');
server.addListener('request', function (req, res) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
res.end();
return;
}
}
var reqPath = url.parse(req.url).pathname;
if (reqPath[reqPath.length - 1] !== '/') {
reqPath += '/';
}
if (path === reqPath) {
self._requestListener(req, res);
} else {
for (var i = 0, len = listeners.length; i < len; i++) {
listeners[i].call(this, req, res);
}
}
});
}
});
this._initializeOptions(options);
}n/a
function EventEmitter() {
EventEmitter.init.call(this);
}n/a
_envelope = function (body, includeTimestamp) {
var defs = this.wsdl.definitions,
ns = defs.$targetNamespace,
encoding = '',
alias = findPrefix(defs.xmlns, ns);
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>';
var headers = '';
if (includeTimestamp) {
var now = new Date();
var created = getDateString(now);
var expires = getDateString(new Date(now.getTime() + (1000 * 600)));
headers += "<o:Security soap:mustUnderstand=\"1\" " +
"xmlns:o=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" " +
"xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
" <u:Timestamp u:Id=\"_0\">" +
" <u:Created>" + created + "</u:Created>" +
" <u:Expires>" + expires + "</u:Expires>" +
" </u:Timestamp>" +
" </o:Security>\n";
}
if (this.soapHeaders) {
headers += this.soapHeaders.join("\n");
}
if (headers !== '') {
xml += "<soap:Header>" + headers + "</soap:Header>";
}
xml += "<soap:Body>" +
body +
"</soap:Body>" +
"</soap:Envelope>";
return xml;
}...
args = options.args,
style = options.style,
handled = false;
try {
method = this.services[serviceName][portName][methodName];
} catch (error) {
return callback(this._envelope('', includeTimestamp));
}
function handleResult(error, result) {
if (handled)
return;
handled = true;
..._executeMethod = function (options, req, callback, includeTimestamp) {
options = options || {};
var self = this,
method, body,
serviceName = options.serviceName,
portName = options.portName,
methodName = options.methodName,
outputName = options.outputName,
args = options.args,
style = options.style,
handled = false;
try {
method = this.services[serviceName][portName][methodName];
} catch (error) {
return callback(this._envelope('', includeTimestamp));
}
function handleResult(error, result) {
if (handled)
return;
handled = true;
if (error && error.Fault !== undefined) {
return self._sendError(error.Fault, callback, includeTimestamp);
}
else if (result === undefined) {
// Backward compatibility to support one argument callback style
result = error;
}
if (style === 'rpc') {
body = self.wsdl.objectToRpcXML(outputName, result, '', self.wsdl.definitions.$targetNamespace);
} else {
var element = self.wsdl.definitions.services[serviceName].ports[portName].binding.methods[methodName].output;
body = self.wsdl.objectToDocumentXML(outputName, result, element.targetNSAlias, element.targetNamespace);
}
callback(self._envelope(body, includeTimestamp));
}
if (!self.wsdl.definitions.services[serviceName].ports[portName].binding.methods[methodName].output) {
// no output defined = one-way operation so return empty response
handled = true;
callback('');
}
var result = method(args, handleResult, options.headers, req);
if (typeof result !== 'undefined') {
handleResult(result);
}
}...
if (binding.style === 'rpc') {
methodName = Object.keys(body)[0];
self.emit('request', obj, methodName);
if (headers)
self.emit('headers', headers, methodName);
self._executeMethod({
serviceName: serviceName,
portName: portName,
methodName: methodName,
outputName: methodName + 'Response',
args: body[methodName],
headers: headers,
style: 'rpc'
..._initializeOptions = function (options) {
this.wsdl.options.attributesKey = options.attributesKey || 'attributes';
}...
concatStream = require('concat-stream'),
uuid = require('uuid');
var Client = function(wsdl, endpoint, options) {
events.EventEmitter.call(this);
options = options || {};
this.wsdl = wsdl;
this._initializeOptions(options);
this._initializeServices(endpoint);
this.httpClient = options.httpClient || new HttpClient(options);
};
util.inherits(Client, events.EventEmitter);
Client.prototype.addSoapHeader = function(soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
..._process = function (input, req, callback) {
var self = this,
pathname = url.parse(req.url).pathname.replace(/\/$/, ''),
obj = this.wsdl.xmlToObject(input),
body = obj.Body,
headers = obj.Header,
bindings = this.wsdl.definitions.bindings, binding,
method, methodName,
serviceName, portName,
includeTimestamp = obj.Header && obj.Header.Security && obj.Header.Security.Timestamp;
if (typeof self.authenticate === 'function') {
if (!obj.Header || !obj.Header.Security) {
throw new Error('No security header');
}
if (!self.authenticate(obj.Header.Security)) {
throw new Error('Invalid username or password');
}
}
if (typeof self.log === 'function') {
self.log("info", "Attempting to bind to " + pathname);
}
//Avoid Cannot convert undefined or null to object due to Object.keys(body)
//and throw more meaningful error
if (!body) {
throw new Error('Failed to parse the SOAP Message body');
}
// use port.location and current url to find the right binding
binding = (function (self) {
var services = self.wsdl.definitions.services;
var firstPort;
var name;
for (name in services) {
serviceName = name;
var service = services[serviceName];
var ports = service.ports;
for (name in ports) {
portName = name;
var port = ports[portName];
var portPathname = url.parse(port.location).pathname.replace(/\/$/, '');
if (typeof self.log === 'function') {
self.log("info", "Trying " + portName + " from path " + portPathname);
}
if (portPathname === pathname)
return port.binding;
// The port path is almost always wrong for generated WSDLs
if (!firstPort) {
firstPort = port;
}
}
}
return !firstPort ? void 0 : firstPort.binding;
})(this);
if (!binding) {
throw new Error('Failed to bind to WSDL');
}
try {
if (binding.style === 'rpc') {
methodName = Object.keys(body)[0];
self.emit('request', obj, methodName);
if (headers)
self.emit('headers', headers, methodName);
self._executeMethod({
serviceName: serviceName,
portName: portName,
methodName: methodName,
outputName: methodName + 'Response',
args: body[methodName],
headers: headers,
style: 'rpc'
}, req, callback);
} else {
var messageElemName = (Object.keys(body)[0] === 'attributes' ? Object.keys(body)[1] : Object.keys(body)[0]);
var pair = binding.topElements[messageElemName];
self.emit('request', obj, pair.methodName);
if (headers)
self.emit('headers', headers, pair.methodName);
self._executeMethod({
serviceName: serviceName,
portName: portName,
methodName: pair.methodName,
outputName: pair.outputName,
args: body[messageElemName],
headers: headers,
style: 'document'
}, req, callback, includeTimestamp);
}
}
catch (error) {
if (error.Fault !== undefined) {
return self._sendError(error.Fault, callback, includeTimestamp);
}
throw error;
}
}...
var self = this;
var result;
var error;
try {
if (typeof self.log === 'function') {
self.log("received", xml);
}
self._process(xml, req, function (result, statusCode) {
if (statusCode) {
res.statusCode = statusCode;
}
res.write(result);
res.end();
if (typeof self.log === 'function') {
self.log("replied", result);
..._processRequestXml = function (req, res, xml) {
var self = this;
var result;
var error;
try {
if (typeof self.log === 'function') {
self.log("received", xml);
}
self._process(xml, req, function (result, statusCode) {
if (statusCode) {
res.statusCode = statusCode;
}
res.write(result);
res.end();
if (typeof self.log === 'function') {
self.log("replied", result);
}
});
} catch (err) {
if (err.Fault !== undefined) {
return self._sendError(err.Fault, function (result, statusCode) {
res.statusCode = statusCode || 500;
res.write(result);
res.end();
if (typeof self.log === 'function') {
self.log("error", err);
}
}, new Date().toISOString());
} else {
error = err.stack ? (self.suppressStack === true ? err.message : err.stack) : err;
res.statusCode = 500;
res.write(error);
res.end();
if (typeof self.log === 'function') {
self.log("error", error);
}
}
}
}...
} else {
res.setHeader('Content-Type', "application/xml");
}
//request body is already provided by an express middleware
//in this case unzipping should also be done by the express middleware itself
if (req.body) {
return self._processRequestXml(req, res, req.body.toString());
}
var chunks = [], gunzip;
if (compress && req.headers["content-encoding"] === "gzip") {
gunzip = new compress.Gunzip();
gunzip.init();
}
..._requestListener = function (req, res) {
var self = this;
var reqParse = url.parse(req.url);
var reqPath = reqParse.pathname;
var reqQuery = reqParse.search;
if (typeof self.log === 'function') {
self.log("info", "Handling " + req.method + " on " + req.url);
}
if (req.method === 'GET') {
if (reqQuery && reqQuery.toLowerCase() === '?wsdl') {
if (typeof self.log === 'function') {
self.log("info", "Wants the WSDL");
}
res.setHeader("Content-Type", "application/xml");
res.write(self.wsdl.toXML());
}
res.end();
} else if (req.method === 'POST') {
if (typeof req.headers['content-type'] !== "undefined") {
res.setHeader('Content-Type', req.headers['content-type']);
} else {
res.setHeader('Content-Type', "application/xml");
}
//request body is already provided by an express middleware
//in this case unzipping should also be done by the express middleware itself
if (req.body) {
return self._processRequestXml(req, res, req.body.toString());
}
var chunks = [], gunzip;
if (compress && req.headers["content-encoding"] === "gzip") {
gunzip = new compress.Gunzip();
gunzip.init();
}
req.on('data', function (chunk) {
if (gunzip)
chunk = gunzip.inflate(chunk, "binary");
chunks.push(chunk);
});
req.on('end', function () {
var xml = chunks.join('');
var result;
var error;
if (gunzip) {
gunzip.end();
gunzip = null;
}
self._processRequestXml(req, res, xml);
});
}
else {
res.end();
}
}...
server.route(path).all(function (req, res, next) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
res.end();
return;
}
}
self._requestListener(req, res);
});
} else {
var listeners = server.listeners('request').slice();
server.removeAllListeners('request');
server.addListener('request', function (req, res) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
..._sendError = function (soapFault, callback, includeTimestamp) {
var self = this,
fault;
var statusCode;
if (soapFault.statusCode) {
statusCode = soapFault.statusCode;
soapFault.statusCode = undefined;
}
if (soapFault.faultcode) {
// Soap 1.1 error style
// Root element will be prependend with the soap NS
// It must match the NS defined in the Envelope (set by the _envelope method)
fault = self.wsdl.objectToDocumentXML("soap:Fault", soapFault, undefined);
}
else {
// Soap 1.2 error style.
// 3rd param is the NS prepended to all elements
// It must match the NS defined in the Envelope (set by the _envelope method)
fault = self.wsdl.objectToDocumentXML("Fault", soapFault, "soap");
}
return callback(self._envelope(fault, includeTimestamp), statusCode);
}...
res.end();
if (typeof self.log === 'function') {
self.log("replied", result);
}
});
} catch (err) {
if (err.Fault !== undefined) {
return self._sendError(err.Fault, function (result, statusCode) {
res.statusCode = statusCode || 500;
res.write(result);
res.end();
if (typeof self.log === 'function') {
self.log("error", err);
}
}, new Date().toISOString());
...addSoapHeader = function (soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
if (typeof soapHeader === 'object') {
soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
}
return this.soapHeaders.push(soapHeader) - 1;
}n/a
changeSoapHeader = function (index, soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
if (typeof soapHeader === 'object') {
soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
}
this.soapHeaders[index] = soapHeader;
}n/a
clearSoapHeaders = function () {
this.soapHeaders = null;
}n/a
getSoapHeaders = function () {
return this.soapHeaders;
}n/a
WSDL = function (definition, uri, options) {
var self = this,
fromFunc;
this.uri = uri;
this.callback = function() {
};
this._includesWsdl = [];
// initialize WSDL cache
this.WSDL_CACHE = (options || {}).WSDL_CACHE || {};
this._initializeOptions(options);
if (typeof definition === 'string') {
definition = stripBom(definition);
fromFunc = this._fromXML;
}
else if (typeof definition === 'object') {
fromFunc = this._fromServices;
}
else {
throw new Error('WSDL constructor takes either an XML string or service definition');
}
process.nextTick(function() {
try {
fromFunc.call(self, definition);
} catch (e) {
return self.callback(e.message);
}
self.processIncludes(function(err) {
var name;
if (err) {
return self.callback(err);
}
self.definitions.deleteFixedAttrs();
var services = self.services = self.definitions.services;
if (services) {
for (name in services) {
services[name].postProcess(self.definitions);
}
}
var complexTypes = self.definitions.complexTypes;
if (complexTypes) {
for (name in complexTypes) {
complexTypes[name].deleteFixedAttrs();
}
}
// for document style, for every binding, prepare input message element name to (methodName, output message element name)
mapping
var bindings = self.definitions.bindings;
for (var bindingName in bindings) {
var binding = bindings[bindingName];
if (typeof binding.style === 'undefined') {
binding.style = 'document';
}
if (binding.style !== 'document')
continue;
var methods = binding.methods;
var topEls = binding.topElements = {};
for (var methodName in methods) {
if (methods[methodName].input) {
var inputName = methods[methodName].input.$name;
var outputName="";
if(methods[methodName].output )
outputName = methods[methodName].output.$name;
topEls[inputName] = {"methodName": methodName, "outputName": outputName};
}
}
}
// prepare soap envelope xmlns definition string
self.xmlnsInEnvelope = self._xmlnsMap();
self.callback(err, self);
});
});
}n/a
_fromServices = function (services) {
}n/a
_fromXML = function (xml) {
this.definitions = this._parse(xml);
this.definitions.descriptions = {
types:{}
};
this.xml = xml;
}n/a
_initializeOptions = function (options) {
this._originalIgnoredNamespaces = (options || {}).ignoredNamespaces;
this.options = {};
var ignoredNamespaces = options ? options.ignoredNamespaces : null;
if (ignoredNamespaces &&
(Array.isArray(ignoredNamespaces.namespaces) || typeof ignoredNamespaces.namespaces === 'string')) {
if (ignoredNamespaces.override) {
this.options.ignoredNamespaces = ignoredNamespaces.namespaces;
} else {
this.options.ignoredNamespaces = this.ignoredNamespaces.concat(ignoredNamespaces.namespaces);
}
} else {
this.options.ignoredNamespaces = this.ignoredNamespaces;
}
this.options.valueKey = options.valueKey || this.valueKey;
this.options.xmlKey = options.xmlKey || this.xmlKey;
if (options.escapeXML !== undefined) {
this.options.escapeXML = options.escapeXML;
} else {
this.options.escapeXML = true;
}
// Allow any request headers to keep passing through
this.options.wsdl_headers = options.wsdl_headers;
this.options.wsdl_options = options.wsdl_options;
if (options.httpClient) {
this.options.httpClient = options.httpClient;
}
// The supplied request-object should be passed through
if (options.request) {
this.options.request = options.request;
}
var ignoreBaseNameSpaces = options ? options.ignoreBaseNameSpaces : null;
if (ignoreBaseNameSpaces !== null && typeof ignoreBaseNameSpaces !== 'undefined') {
this.options.ignoreBaseNameSpaces = ignoreBaseNameSpaces;
} else {
this.options.ignoreBaseNameSpaces = this.ignoreBaseNameSpaces;
}
// Works only in client
this.options.forceSoap12Headers = options.forceSoap12Headers;
this.options.customDeserializer = options.customDeserializer;
if (options.overrideRootElement !== undefined) {
this.options.overrideRootElement = options.overrideRootElement;
}
}...
concatStream = require('concat-stream'),
uuid = require('uuid');
var Client = function(wsdl, endpoint, options) {
events.EventEmitter.call(this);
options = options || {};
this.wsdl = wsdl;
this._initializeOptions(options);
this._initializeServices(endpoint);
this.httpClient = options.httpClient || new HttpClient(options);
};
util.inherits(Client, events.EventEmitter);
Client.prototype.addSoapHeader = function(soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
..._parse = function (xml) {
var self = this,
p = sax.parser(true),
stack = [],
root = null,
types = null,
schema = null,
options = self.options;
p.onopentag = function(node) {
var nsName = node.name;
var attrs = node.attributes;
var top = stack[stack.length - 1];
var name;
if (top) {
try {
top.startElement(stack, nsName, attrs, options);
} catch (e) {
if (self.options.strict) {
throw e;
} else {
stack.push(new Element(nsName, attrs, options));
}
}
} else {
name = splitQName(nsName).name;
if (name === 'definitions') {
root = new DefinitionsElement(nsName, attrs, options);
stack.push(root);
} else if (name === 'schema') {
// Shim a structure in here to allow the proper objects to be created when merging back.
root = new DefinitionsElement('definitions', {}, {});
types = new TypesElement('types', {}, {});
schema = new SchemaElement(nsName, attrs, options);
types.addChild(schema);
root.addChild(types);
stack.push(schema);
} else {
throw new Error('Unexpected root element of WSDL or include');
}
}
};
p.onclosetag = function(name) {
var top = stack[stack.length - 1];
assert(top, 'Unmatched close tag: ' + name);
top.endElement(stack, name);
};
p.write(xml).close();
return root;
}...
p.write(xml).close();
return root;
};
WSDL.prototype._fromXML = function(xml) {
this.definitions = this._parse(xml);
this.definitions.descriptions = {
types:{}
};
this.xml = xml;
};
WSDL.prototype._fromServices = function(services) {
..._processNextInclude = function (includes, callback) {
var self = this,
include = includes.shift(),
options;
if (!include)
return callback();
var includePath;
if (!/^https?:/.test(self.uri) && !/^https?:/.test(include.location)) {
includePath = path.resolve(path.dirname(self.uri), include.location);
} else {
includePath = url.resolve(self.uri||'', include.location);
}
options = _.assign({}, this.options);
// follow supplied ignoredNamespaces option
options.ignoredNamespaces = this._originalIgnoredNamespaces || this.options.ignoredNamespaces;
options.WSDL_CACHE = this.WSDL_CACHE;
open_wsdl_recursive(includePath, options, function(err, wsdl) {
if (err) {
return callback(err);
}
self._includesWsdl.push(wsdl);
if (wsdl.definitions instanceof DefinitionsElement) {
_.merge(self.definitions, wsdl.definitions, function(a,b) {
return (a instanceof SchemaElement) ? a.merge(b) : undefined;
});
} else {
self.definitions.schemas[include.namespace || wsdl.definitions.$targetNamespace] = deepMerge(self.definitions.schemas[include
.namespace || wsdl.definitions.$targetNamespace], wsdl.definitions);
}
self._processNextInclude(includes, function(err) {
callback(err);
});
});
}...
if (wsdl.definitions instanceof DefinitionsElement) {
_.merge(self.definitions, wsdl.definitions, function(a,b) {
return (a instanceof SchemaElement) ? a.merge(b) : undefined;
});
} else {
self.definitions.schemas[include.namespace || wsdl.definitions.$targetNamespace] = deepMerge(self.definitions.schemas[include
.namespace || wsdl.definitions.$targetNamespace], wsdl.definitions);
}
self._processNextInclude(includes, function(err) {
callback(err);
});
});
};
WSDL.prototype.processIncludes = function(callback) {
var schemas = this.definitions.schemas,
...function splitQName(nsName) {
var i = typeof nsName === 'string' ? nsName.indexOf(':') : -1;
return i < 0 ? {prefix: TNS_PREFIX, name: nsName} :
{prefix: nsName.substring(0, i), name: nsName.substring(i + 1)};
}n/a
_xmlnsMap = function () {
var xmlns = this.definitions.xmlns;
var str = '';
for (var alias in xmlns) {
if (alias === '' || alias === TNS_PREFIX) {
continue;
}
var ns = xmlns[alias];
switch (ns) {
case "http://xml.apache.org/xml-soap" : // apachesoap
case "http://schemas.xmlsoap.org/wsdl/" : // wsdl
case "http://schemas.xmlsoap.org/wsdl/soap/" : // wsdlsoap
case "http://schemas.xmlsoap.org/wsdl/soap12/": // wsdlsoap12
case "http://schemas.xmlsoap.org/soap/encoding/" : // soapenc
case "http://www.w3.org/2001/XMLSchema" : // xsd
continue;
}
if (~ns.indexOf('http://schemas.xmlsoap.org/')) {
continue;
}
if (~ns.indexOf('http://www.w3.org/')) {
continue;
}
if (~ns.indexOf('http://xml.apache.org/')) {
continue;
}
str += ' xmlns:' + alias + '="' + ns + '"';
}
return str;
}...
outputName = methods[methodName].output.$name;
topEls[inputName] = {"methodName": methodName, "outputName": outputName};
}
}
}
// prepare soap envelope xmlns definition string
self.xmlnsInEnvelope = self._xmlnsMap();
self.callback(err, self);
});
});
};
...describeServices = function () {
var services = {};
for (var name in this.services) {
var service = this.services[name];
services[name] = service.description(this.definitions);
}
return services;
}...
Client.prototype.setEndpoint = function(endpoint) {
this.endpoint = endpoint;
this._initializeServices(endpoint);
};
Client.prototype.describe = function() {
var types = this.wsdl.definitions.types;
return this.wsdl.describeServices();
};
Client.prototype.setSecurity = function(security) {
this.security = security;
};
Client.prototype.setSOAPAction = function(SOAPAction) {
...filterOutIgnoredNameSpace = function (ns) {
var namespace = noColonNameSpace(ns);
return this.isIgnoredNameSpace(namespace) ? '' : namespace;
}n/a
findChildSchemaObject = function (parameterTypeObj, childName) {
if (!parameterTypeObj || !childName) {
return null;
}
var found = null,
i = 0,
child,
ref;
if (Array.isArray(parameterTypeObj.$lookupTypes) && parameterTypeObj.$lookupTypes.length) {
var types = parameterTypeObj.$lookupTypes;
for(i = 0; i < types.length; i++) {
var typeObj = types[i];
if(typeObj.$name === childName) {
found = typeObj;
break;
}
}
}
var object = parameterTypeObj;
if (object.$name === childName && object.name === 'element') {
return object;
}
if (object.$ref) {
ref = splitQName(object.$ref);
if (ref.name === childName) {
return object;
}
}
var childNsURI;
if (object.$type) {
var typeInfo = splitQName(object.$type);
if (typeInfo.prefix === TNS_PREFIX) {
childNsURI = parameterTypeObj.$targetNamespace;
} else {
childNsURI = this.definitions.xmlns[typeInfo.prefix];
}
var typeDef = this.findSchemaType(typeInfo.name, childNsURI);
if (typeDef) {
return this.findChildSchemaObject(typeDef, childName);
}
}
if (object.children) {
for (i = 0, child; child = object.children[i]; i++) {
found = this.findChildSchemaObject(child, childName);
if (found) {
break;
}
if (child.$base) {
var baseQName = splitQName(child.$base);
var childNameSpace = baseQName.prefix === TNS_PREFIX ? '' : baseQName.prefix;
childNsURI = this.definitions.xmlns[baseQName.prefix];
var foundBase = this.findSchemaType(baseQName.name, childNsURI);
if (foundBase) {
found = this.findChildSchemaObject(foundBase, childName);
if (found) {
found.$baseNameSpace = childNameSpace;
found.$type = childNameSpace + ':' + childName;
break;
}
}
}
}
}
if (!found && object.$name === childName) {
return object;
}
return found;
}...
if (isFirst) {
value = self.objectToXML(child, name, nsPrefix, nsURI, false, null, schemaObject, nsContext);
} else {
if (self.definitions.schemas) {
if (schema) {
var childSchemaObject = self.findChildSchemaObject(schemaObject, name);
//find sub namespace if not a primitive
if (childSchemaObject &&
((childSchemaObject.$type && (childSchemaObject.$type.indexOf('xsd:') === -1)) ||
childSchemaObject.$ref || childSchemaObject.$name)) {
/*if the base name space of the children is not in the ingoredSchemaNamspaces we use it.
This is because in some services the child nodes do not need the baseNameSpace.
*/
...findSchemaObject = function (nsURI, qname) {
if (!nsURI || !qname) {
return null;
}
var def = null;
if (this.definitions.schemas) {
var schema = this.definitions.schemas[nsURI];
if (schema) {
if (qname.indexOf(':') !== -1) {
qname = qname.substring(qname.indexOf(':') + 1, qname.length);
}
// if the client passed an input element which has a `$lookupType` property instead of `$type`
// the `def` is found in `schema.elements`.
def = schema.complexTypes[qname] || schema.types[qname] || schema.elements[qname];
}
}
return def;
}...
var typeURI;
if (type.prefix === TNS_PREFIX) {
// In case of xsi:type = "MyType"
typeURI = xmlns[type.prefix] || xmlns.xmlns;
} else {
typeURI = xmlns[type.prefix];
}
var typeDef = self.findSchemaObject(typeURI, type.name);
if (typeDef) {
xsiTypeSchema = typeDef.description(self.definitions);
}
}
if (topSchema && topSchema[name + '[]']) {
name = name + '[]';
...findSchemaType = function (name, nsURI) {
if (!this.definitions.schemas || !name || !nsURI) {
return null;
}
var schema = this.definitions.schemas[nsURI];
if (!schema || !schema.complexTypes) {
return null;
}
return schema.complexTypes[name];
}...
var typeURI = schema.xmlns[typePrefix] || self.definitions.xmlns[typePrefix];
childNsURI = typeURI;
if (typeURI !== 'http://www.w3.org/2001/XMLSchema' && typePrefix !== TNS_PREFIX) {
// Add the prefix/namespace mapping, but not declare it
nsContext.addNamespace(typePrefix, typeURI);
}
resolvedChildSchemaObject =
self.findSchemaType(typeQName.name, typeURI) || childSchemaObject;
} else {
resolvedChildSchemaObject =
self.findSchemaObject(childNsURI, childName) || childSchemaObject;
}
if (childSchemaObject.$baseNameSpace && this.options.ignoreBaseNameSpaces) {
childNsPrefix = nsPrefix;
...isIgnoredNameSpace = function (ns) {
return this.options.ignoredNamespaces.indexOf(ns) > -1;
}...
WSDL.prototype.isIgnoredNameSpace = function(ns) {
return this.options.ignoredNamespaces.indexOf(ns) > -1;
};
WSDL.prototype.filterOutIgnoredNameSpace = function(ns) {
var namespace = noColonNameSpace(ns);
return this.isIgnoredNameSpace(namespace) ? '' : namespace;
};
/**
* Convert an object to XML. This is a recursive method as it calls itself.
*
...objectToDocumentXML = function (name, params, nsPrefix, nsURI, type) {
var args = {};
args[name] = params;
var parameterTypeObj = type ? this.findSchemaObject(nsURI, type) : null;
return this.objectToXML(args, null, nsPrefix, nsURI, true, null, parameterTypeObj);
}...
if ((style === 'rpc')&& ( ( input.parts || input.name==="element" ) || args === null) ) {
assert.ok(!style || style === 'rpc', 'invalid message definition for document style binding');
message = self.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 = self.wsdl.objectToDocumentXML(input.$name, args, input.targetNSAlias, input
.targetNamespace, (input.$type || input.$lookupType));
}
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<" + envelopeKey + ":Envelope " +
xmlnsSoap + " " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
...objectToRpcXML = function (name, params, nsPrefix, nsURI, isParts) {
var parts = [];
var defs = this.definitions;
var nsAttrName = '_xmlns';
nsPrefix = nsPrefix || findPrefix(defs.xmlns, nsURI);
nsURI = nsURI || defs.xmlns[nsPrefix];
nsPrefix = nsPrefix === TNS_PREFIX ? '' : (nsPrefix + ':');
parts.push(['<', nsPrefix, name, '>'].join(''));
for (var key in params) {
if (!params.hasOwnProperty(key)) {
continue;
}
if (key !== nsAttrName) {
var value = params[key];
var prefixedKey = (isParts ? '' : nsPrefix) + key;
parts.push(['<', prefixedKey, '>'].join(''));
parts.push((typeof value === 'object') ? this.objectToXML(value, key, nsPrefix, nsURI) : xmlEscape(value));
parts.push(['</', prefixedKey, '>'].join(''));
}
}
parts.push(['</', nsPrefix, name, '>'].join(''));
return parts.join('');
}...
if (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.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 = self.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 = self.wsdl.objectToDocumentXML(input.$name, args, input.targetNSAlias, input.targetNamespace, (input.$type || input.$
lookupType));
}
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
...objectToXML = function (obj, name, nsPrefix, nsURI, isFirst, xmlnsAttr, schemaObject, nsContext) {
var self = this;
var schema = this.definitions.schemas[nsURI];
var parentNsPrefix = nsPrefix ? nsPrefix.parent : undefined;
if (typeof parentNsPrefix !== 'undefined') {
//we got the parentNsPrefix for our array. setting the namespace-variable back to the current namespace string
nsPrefix = nsPrefix.current;
}
parentNsPrefix = noColonNameSpace(parentNsPrefix);
if (this.isIgnoredNameSpace(parentNsPrefix)) {
parentNsPrefix = '';
}
var soapHeader = !schema;
var qualified = schema && schema.$elementFormDefault === 'qualified';
var parts = [];
var prefixNamespace = (nsPrefix || qualified) && nsPrefix !== TNS_PREFIX;
var xmlnsAttrib = '';
if (nsURI && isFirst) {
if(self.options.overrideRootElement && self.options.overrideRootElement.xmlnsAttributes) {
self.options.overrideRootElement.xmlnsAttributes.forEach(function(attribute) {
xmlnsAttrib += ' ' + attribute.name + '="' + attribute.value + '"';
});
} else {
if (prefixNamespace && !this.isIgnoredNameSpace(nsPrefix)) {
// resolve the prefix namespace
xmlnsAttrib += ' xmlns:' + nsPrefix + '="' + nsURI + '"';
}
// only add default namespace if the schema elementFormDefault is qualified
if (qualified || soapHeader) xmlnsAttrib += ' xmlns="' + nsURI + '"';
}
}
if (!nsContext) {
nsContext = new NamespaceContext();
nsContext.declareNamespace(nsPrefix, nsURI);
} else {
nsContext.pushContext();
}
// explicitly use xmlns attribute if available
if (xmlnsAttr && !(self.options.overrideRootElement && self.options.overrideRootElement.xmlnsAttributes)) {
xmlnsAttrib = xmlnsAttr;
}
var ns = '';
if (self.options.overrideRootElement && isFirst) {
ns = self.options.overrideRootElement.namespace;
} else if (prefixNamespace && (qualified || isFirst || soapHeader) && !this.isIgnoredNameSpace(nsPrefix)) {
ns = nsPrefix;
}
var i, n;
// start building out XML string.
if (Array.isArray(obj)) {
for (i = 0, n = obj.length; i < n; i++) {
var item = obj[i];
var arrayAttr = self.processAttributes(item, nsContext),
correctOuterNsPrefix = parentNsPrefix || ns; //using the parent namespace prefix if given
parts.push(['<', appendColon(correctOuterNsPrefix), name, arrayAttr, xmlnsAttrib, '>'].join(''));
parts.push(self.objectToXML(item, name, nsPrefix, nsURI, false, null, schemaObject, nsContext));
parts.push(['</', appendColon(correctOuterNsPrefix), name, '>'].join(''));
}
} else if (typeof obj === 'object') {
for (name in obj) {
if (!obj.hasOwnProperty(name)) continue;
//don't process attributes as element
if (name === self.options.attributesKey) {
continue;
}
//Its the value of a xml object. Return it directly.
if (name === self.options.xmlKey){
nsContext.popContext();
return obj[name];
}
//Its the value of an item. Return it directly.
if (name === self.options.valueKey) {
nsContext.popContext();
return xmlEscape(obj[name]);
}
var child = obj[name];
if (typeof child === 'undefined') {
continue;
}
var attr = self.processAttributes(child, nsContext);
var value = '';
var nonSubNameSpace = '';
var emptyNonSubNameSpace = false;
var nameWithNsRegex = /^([^:]+):([^:]+)$/.exec(name);
if (nameWithNsRegex) {
nonSubNameSpace = nameWithNsRegex[1] + ':';
name = nameWithNsRegex[2];
} else if(name[0] === ':'){
emptyNonSubNameSpace = true;
name = name.substr(1);
}
if (isFirst) {
value = self.objectToXML(child, name, nsPrefix, nsURI, false, null, schemaObject, nsContext);
} else {
if (self.definitions.schemas) {
if (schema) {
var childSchemaObject = sel ......
util.inherits(Client, events.EventEmitter);
Client.prototype.addSoapHeader = function(soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
if (typeof soapHeader === 'object') {
soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
}
return this.soapHeaders.push(soapHeader) - 1;
};
Client.prototype.changeSoapHeader = function(index, soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
...onReady = function (callback) {
if (callback)
this.callback = callback;
}...
this.path = path;
this.services = services;
this.wsdl = wsdl;
this.suppressStack = options && options.suppressStack;
if (path[path.length - 1] !== '/')
path += '/';
wsdl.onReady(function (err) {
if (typeof server.route === 'function' && typeof server.use === 'function') {
//handle only the required URL path for express server
server.route(path).all(function (req, res, next) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
res.end();
return;
...processAttributes = function (child, nsContext) {
var attr = '';
if(child === null) {
child = [];
}
var attrObj = child[this.options.attributesKey];
if (attrObj && attrObj.xsi_type) {
var xsiType = attrObj.xsi_type;
var prefix = xsiType.prefix || xsiType.namespace;
// Generate a new namespace for complex extension if one not provided
if (!prefix) {
prefix = nsContext.registerNamespace(xsiType.xmlns);
} else {
nsContext.declareNamespace(prefix, xsiType.xmlns);
}
xsiType.prefix = prefix;
}
if (attrObj) {
for (var attrKey in attrObj) {
//handle complex extension separately
if (attrKey === 'xsi_type') {
var attrValue = attrObj[attrKey];
attr += ' xsi:type="' + attrValue.prefix + ':' + attrValue.type + '"';
attr += ' xmlns:' + attrValue.prefix + '="' + attrValue.xmlns + '"';
continue;
} else {
attr += ' ' + attrKey + '="' + xmlEscape(attrObj[attrKey]) + '"';
}
}
}
return attr;
}...
}
var i, n;
// start building out XML string.
if (Array.isArray(obj)) {
for (i = 0, n = obj.length; i < n; i++) {
var item = obj[i];
var arrayAttr = self.processAttributes(item, nsContext),
correctOuterNsPrefix = parentNsPrefix || ns; //using the parent namespace prefix if given
parts.push(['<', appendColon(correctOuterNsPrefix), name, arrayAttr, xmlnsAttrib, '>'].join(
x27;'));
parts.push(self.objectToXML(item, name, nsPrefix, nsURI, false, null, schemaObject, nsContext));
parts.push(['</', appendColon(correctOuterNsPrefix), name, '>'].join(''));
}
} else if (typeof obj === 'object') {
...processIncludes = function (callback) {
var schemas = this.definitions.schemas,
includes = [];
for (var ns in schemas) {
var schema = schemas[ns];
includes = includes.concat(schema.includes || []);
}
this._processNextInclude(includes, callback);
}...
process.nextTick(function() {
try {
fromFunc.call(self, definition);
} catch (e) {
return self.callback(e.message);
}
self.processIncludes(function(err) {
var name;
if (err) {
return self.callback(err);
}
self.definitions.deleteFixedAttrs();
var services = self.services = self.definitions.services;
...toXML = function () {
return this.xml || '';
}...
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
((self.soapHeaders || self.security) ?
(
"<" + envelopeKey + ":Header>" +
(self.soapHeaders ? self.soapHeaders.join("\n") : "") +
(self.security && !self.security.postProcess ? self.security.toXML() :
x22;") +
"</" + envelopeKey + ":Header>"
)
:
''
) +
"<" + envelopeKey + ":Body" +
(self.bodyAttributes ? self.bodyAttributes.join(' ') : '') +
...xmlToObject = function (xml, callback) {
var self = this;
var p = typeof callback === 'function' ? {} : sax.parser(true);
var objectName = null;
var root = {};
var schema = {
Envelope: {
Header: {
Security: {
UsernameToken: {
Username: 'string',
Password: 'string'
}
}
},
Body: {
Fault: {
faultcode: 'string',
faultstring: 'string',
detail: 'string'
}
}
}
};
var stack = [{name: null, object: root, schema: schema}];
var xmlns = {};
var refs = {}, id; // {id:{hrefs:[],obj:}, ...}
p.onopentag = function(node) {
var nsName = node.name;
var attrs = node.attributes;
var name = splitQName(nsName).name,
attributeName,
top = stack[stack.length - 1],
topSchema = top.schema,
elementAttributes = {},
hasNonXmlnsAttribute = false,
hasNilAttribute = false,
obj = {};
var originalName = name;
if (!objectName && top.name === 'Body' && name !== 'Fault') {
var message = self.definitions.messages[name];
// Support RPC/literal messages where response body contains one element named
// after the operation + 'Response'. See http://www.w3.org/TR/wsdl#_names
if (!message) {
// Determine if this is request or response
var isInput = false;
var isOutput = false;
if ((/Response$/).test(name)) {
isOutput = true;
name = name.replace(/Response$/, '');
} else if ((/Request$/).test(name)) {
isInput = true;
name = name.replace(/Request$/, '');
} else if ((/Solicit$/).test(name)) {
isInput = true;
name = name.replace(/Solicit$/, '');
}
// Look up the appropriate message as given in the portType's operations
var portTypes = self.definitions.portTypes;
var portTypeNames = Object.keys(portTypes);
// Currently this supports only one portType definition.
var portType = portTypes[portTypeNames[0]];
if (isInput) {
name = portType.methods[name].input.$name;
} else {
name = portType.methods[name].output.$name;
}
message = self.definitions.messages[name];
// 'cache' this alias to speed future lookups
self.definitions.messages[originalName] = self.definitions.messages[name];
}
topSchema = message.description(self.definitions);
objectName = originalName;
}
if (attrs.href) {
id = attrs.href.substr(1);
if (!refs[id]) {
refs[id] = {hrefs: [], obj: null};
}
refs[id].hrefs.push({par: top.object, key: name, obj: obj});
}
if (id = attrs.id) {
if (!refs[id]) {
refs[id] = {hrefs: [], obj: null};
}
}
//Handle element attributes
for (attributeName in attrs) {
if (/^xmlns:|^xmlns$/.test(attributeName)) {
xmlns[splitQName(attributeName).name] = attrs[attributeName];
continue;
}
hasNonXmlnsAttribute = true;
elementAttributes[attributeName] = attrs[attributeName];
}
for(attributeName in elementAttributes){
var res = splitQName(attributeName);
if (res.name === 'nil' && xmlns[res.prefix] === 'http://www.w3.org/2001/XMLSchema-instance') {
hasNilAttribute = true;
break;
}
}
if (hasNonXmlnsAttribute) {
obj[self.options.attributesKey] = elementAttributes;
}
// Pick up the schema for the type specified in element's xsi:type attribute.
var xsiTypeSchema;
var xsiType = elementAttributes['xsi:type'];
if (xsiType) {
var type = splitQName(xsiType);
var typeURI;
if (type.prefix === TNS_PREFIX) {
// In case of xsi:type = "MyType"
typeURI = xmlns[type.prefix] || xmlns.xmlns;
} else {
typeURI = xmlns[type.prefix];
}
var typeDef = self.findSchemaObject( ......
return parseSync(body, response);
}));
return;
}
self.wsdl.xmlToObject(response, function (error, obj) {
self.lastResponse = response;
self.lastResponseHeaders = response && response.headers;
self.lastElapsedTime = Date.now() - startTime;
self.emit('response', '<stream>', response, eid);
if (error) {
error.response = response;
...function WSSecurity(username, password, options) {
options = options || {};
this._username = username;
this._password = password;
//must account for backward compatibility for passwordType String param as well as object options defaults: passwordType = 'PasswordText
', hasTimeStamp = true
if (typeof options === 'string') {
this._passwordType = options ? options : 'PasswordText';
options = {};
} else {
this._passwordType = options.passwordType ? options.passwordType : 'PasswordText';
}
if (validPasswordTypes.indexOf(this._passwordType) === -1) {
this._passwordType = 'PasswordText';
}
this._hasTimeStamp = options.hasTimeStamp || typeof options.hasTimeStamp === 'boolean' ? !!options.hasTimeStamp : true;
/*jshint eqnull:true */
if (options.hasNonce != null) {
this._hasNonce = !!options.hasNonce;
}
this._hasTokenCreated = options.hasTokenCreated || typeof options.hasTokenCreated === 'boolean' ? !!options.hasTokenCreated :
true;
if (options.actor != null) {
this._actor = options.actor;
}
if (options.mustUnderstand != null) {
this._mustUnderstand = !!options.mustUnderstand;
}
}n/a
toXML = function () {
// avoid dependency on date formatting libraries
function getDate(d) {
function pad(n) {
return n < 10 ? '0' + n : n;
}
return d.getUTCFullYear() + '-'
+ pad(d.getUTCMonth() + 1) + '-'
+ pad(d.getUTCDate()) + 'T'
+ pad(d.getUTCHours()) + ':'
+ pad(d.getUTCMinutes()) + ':'
+ pad(d.getUTCSeconds()) + 'Z';
}
var now = new Date();
var created = getDate(now);
var timeStampXml = '';
if (this._hasTimeStamp) {
var expires = getDate( new Date(now.getTime() + (1000 * 600)) );
timeStampXml = "<wsu:Timestamp wsu:Id=\"Timestamp-"+created+"\">" +
"<wsu:Created>"+created+"</wsu:Created>" +
"<wsu:Expires>"+expires+"</wsu:Expires>" +
"</wsu:Timestamp>";
}
var password, nonce;
if (this._hasNonce || this._passwordType !== 'PasswordText') {
// nonce = base64 ( sha1 ( created + random ) )
var nHash = crypto.createHash('sha1');
nHash.update(created + Math.random());
nonce = nHash.digest('base64');
}
if (this._passwordType === 'PasswordText') {
password = "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" + this._password + "</wsse:Password>";
if (nonce) {
password += "<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#
Base64Binary\">" + nonce + "</wsse:Nonce>";
}
} else {
password = "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest\">" + passwordDigest(nonce, created, this._password) + "</wsse:Password>" +
"<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" + nonce + "</wsse:Nonce>";
}
return "<wsse:Security " + (this._actor ? "soap:actor=\"" + this._actor + "\" " : "") +
(this._mustUnderstand ? "soap:mustUnderstand=\"1\" " : "") +
"xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis
-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
timeStampXml +
"<wsse:UsernameToken xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" wsu:Id
=\"SecurityToken-" + created + "\">" +
"<wsse:Username>" + this._username + "</wsse:Username>" +
password +
(this._hasTokenCreated ? "<wsu:Created>" + created + "</wsu:Created>" : "") +
"</wsse:UsernameToken>" +
"</wsse:Security>";
}...
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
((self.soapHeaders || self.security) ?
(
"<" + envelopeKey + ":Header>" +
(self.soapHeaders ? self.soapHeaders.join("\n") : "") +
(self.security && !self.security.postProcess ? self.security.toXML() :
x22;") +
"</" + envelopeKey + ":Header>"
)
:
''
) +
"<" + envelopeKey + ":Body" +
(self.bodyAttributes ? self.bodyAttributes.join(' ') : '') +
...function WSSecurityCert(privatePEM, publicP12PEM, password, encoding) {
if (!ursa) {
throw new Error('Module ursa must be installed to use WSSecurityCert');
}
this.privateKey = ursa.createPrivateKey(privatePEM, password, encoding);
this.publicP12PEM = publicP12PEM.toString().replace('-----BEGIN CERTIFICATE-----', '').replace('-----END CERTIFICATE-----', '').
replace(/(\r\n|\n|\r)/gm, '');
this.signer = new SignedXml();
this.signer.signingKey = this.privateKey.toPrivatePem();
this.x509Id = "x509-" + generateId();
var _this = this;
this.signer.keyInfoProvider = {};
this.signer.keyInfoProvider.getKeyInfo = function (key) {
return wsseSecurityTokenTemplate({ x509Id: _this.x509Id });
};
}...
WS-Security X509 Certificate support.
``` javascript
var privateKey = fs.readFileSync(privateKeyPath);
var publicKey = fs.readFileSync(publicKeyPath);
var password = ''; // optional password
var wsSecurity = new soap.WSSecurityCert(privateKey, publicKey, password, 'utf8
');
client.setSecurity(wsSecurity);
```
_Note_: Optional dependency 'ursa' is required to be installed successfully when WSSecurityCert is used.
## Handling XML Attributes, Value and XML (wsdlOptions).
Sometimes it is necessary to override the default behaviour of `node-soap` in order to deal with the special requirements
...postProcess = function (xml, envelopeKey) {
this.created = generateCreated();
this.expires = generateExpires();
var secHeader = wsseSecurityHeaderTemplate({
binaryToken: this.publicP12PEM,
created: this.created,
expires: this.expires,
id: this.x509Id
});
var xmlWithSec = insertStr(secHeader, xml, xml.indexOf('</soap:Header>'));
var references = ["http://www.w3.org/2000/09/xmldsig#enveloped-signature",
"http://www.w3.org/2001/10/xml-exc-c14n#"];
this.signer.addReference("//*[name(.)='" + envelopeKey + ":Body']", references);
this.signer.addReference("//*[name(.)='wsse:Security']/*[local-name(.)='Timestamp']", references);
this.signer.computeSignature(xmlWithSec);
return insertStr(this.signer.getSignatureXml(), xmlWithSec, xmlWithSec.indexOf('</wsse:Security>'));
}...
(self.security && self.security.postProcess ? ' Id="_0"' : '') +
">" +
message +
"</" + envelopeKey + ":Body>" +
"</" + envelopeKey + ":Envelope>";
if(self.security && self.security.postProcess){
xml = self.security.postProcess(xml, envelopeKey);
}
self.lastMessage = message;
self.lastRequest = xml;
self.lastEndpoint = location;
var eid = options.exchangeId || uuid.v4();
...Client = function (wsdl, endpoint, options) {
events.EventEmitter.call(this);
options = options || {};
this.wsdl = wsdl;
this._initializeOptions(options);
this._initializeServices(endpoint);
this.httpClient = options.httpClient || new HttpClient(options);
}n/a
function NamespaceContext() {
if (!(this instanceof NamespaceContext)) {
return new NamespaceContext();
}
this.scopes = [];
this.pushContext();
this.prefixCount = 0;
}n/a
addNamespace = function (prefix, nsUri, localOnly) {
if (this.getNamespaceURI(prefix, localOnly) === nsUri) {
return false;
}
if (this.currentScope) {
this.currentScope.namespaces[prefix] = {
uri: nsUri,
prefix: prefix,
declared: false
};
return true;
}
return false;
}...
prefix = 'ns' + (++this.prefixCount);
if (!this.getNamespaceURI(prefix)) {
// The prefix is not used
break;
}
}
}
this.addNamespace(prefix, nsUri, true);
return prefix;
};
/**
* Declare a namespace prefix/uri mapping
* @param {String} prefix Namespace prefix
* @param {String} nsUri Namespace URI
...declareNamespace = function (prefix, nsUri) {
if (this.currentScope) {
var mapping = this.currentScope.getNamespaceMapping(prefix);
if (mapping && mapping.uri === nsUri && mapping.declared) {
return false;
}
this.currentScope.namespaces[prefix] = {
uri: nsUri,
prefix: prefix,
declared: true
};
return true;
}
return false;
}...
// only add default namespace if the schema elementFormDefault is qualified
if (qualified || soapHeader) xmlnsAttrib += ' xmlns="' + nsURI + '"';
}
}
if (!nsContext) {
nsContext = new NamespaceContext();
nsContext.declareNamespace(nsPrefix, nsURI);
} else {
nsContext.pushContext();
}
// explicitly use xmlns attribute if available
if (xmlnsAttr && !(self.options.overrideRootElement && self.options.overrideRootElement.xmlnsAttributes)) {
xmlnsAttrib = xmlnsAttr;
...getNamespaceURI = function (prefix, localOnly) {
return this.currentScope && this.currentScope.getNamespaceURI(prefix, localOnly);
}...
return 'http://www.w3.org/2000/xmlns/';
default:
var nsUri = this.namespaces[prefix];
/*jshint -W116 */
if (nsUri != null) {
return nsUri.uri;
} else if (!localOnly && this.parent) {
return this.parent.getNamespaceURI(prefix);
} else {
return null;
}
}
};
NamespaceScope.prototype.getNamespaceMapping = function(prefix) {
...getPrefix = function (nsUri, localOnly) {
return this.currentScope && this.currentScope.getPrefix(nsUri, localOnly);
}...
default:
for (var p in this.namespaces) {
if (this.namespaces[p].uri === nsUri) {
return p;
}
}
if (!localOnly && this.parent) {
return this.parent.getPrefix(nsUri);
} else {
return null;
}
}
};
/**
...popContext = function () {
var scope = this.scopes.pop();
if (scope) {
this.currentScope = scope.parent;
} else {
this.currentScope = null;
}
return scope;
}...
if (!obj.hasOwnProperty(name)) continue;
//don't process attributes as element
if (name === self.options.attributesKey) {
continue;
}
//Its the value of a xml object. Return it directly.
if (name === self.options.xmlKey){
nsContext.popContext();
return obj[name];
}
//Its the value of an item. Return it directly.
if (name === self.options.valueKey) {
nsContext.popContext();
return xmlEscape(obj[name]);
}
...pushContext = function () {
var scope = new NamespaceScope(this.currentScope);
this.scopes.push(scope);
this.currentScope = scope;
return scope;
}...
* @constructor
*/
function NamespaceContext() {
if (!(this instanceof NamespaceContext)) {
return new NamespaceContext();
}
this.scopes = [];
this.pushContext();
this.prefixCount = 0;
}
/**
* Look up the namespace URI by prefix
* @param {String} prefix Namespace prefix
* @param {Boolean} [localOnly] Search current scope only
...registerNamespace = function (nsUri) {
var prefix = this.getPrefix(nsUri);
if (prefix) {
// If the namespace has already mapped to a prefix
return prefix;
} else {
// Try to generate a unique namespace
while (true) {
prefix = 'ns' + (++this.prefixCount);
if (!this.getNamespaceURI(prefix)) {
// The prefix is not used
break;
}
}
}
this.addNamespace(prefix, nsUri, true);
return prefix;
}...
var elementQName = childSchemaObject.$ref || childSchemaObject.$name;
if (elementQName) {
elementQName = splitQName(elementQName);
childName = elementQName.name;
if (elementQName.prefix === TNS_PREFIX) {
// Local element
childNsURI = childSchemaObject.$targetNamespace;
childNsPrefix = nsContext.registerNamespace(childNsURI);
if (this.isIgnoredNameSpace(childNsPrefix)) {
childNsPrefix = nsPrefix;
}
} else {
childNsPrefix = elementQName.prefix;
if (this.isIgnoredNameSpace(childNsPrefix)) {
childNsPrefix = nsPrefix;
...function BasicAuthSecurity(username, password, defaults) {
this._username = username;
this._password = password;
this.defaults = {};
_.merge(this.defaults, defaults);
}...
as well. The interface is quite simple. Each protocol defines 2 methods:
* `addOptions` - a method that accepts an options arg that is eventually passed directly to `request`
* `toXML` - a method that returns a string of XML.
### BasicAuthSecurity
``` javascript
client.setSecurity(new soap.BasicAuthSecurity('username', 'password
x27;));
```
### BearerSecurity
``` javascript
client.setSecurity(new soap.BearerSecurity('token'));
```
...function BearerSecurity(token, defaults) {
this._token = token;
this.defaults = {};
_.merge(this.defaults, defaults);
}...
``` javascript
client.setSecurity(new soap.BasicAuthSecurity('username', 'password'));
```
### BearerSecurity
``` javascript
client.setSecurity(new soap.BearerSecurity('token'));
```
### ClientSSLSecurity
_Note_: If you run into issues using this protocol, consider passing these options
as default request options to the constructor:
* `rejectUnauthorized: false`
...function ClientSSLSecurity(key, cert, ca, defaults) {
if (key) {
if(Buffer.isBuffer(key)) {
this.key = key;
} else if (typeof key === 'string') {
this.key = fs.readFileSync(key);
} else {
throw new Error('key should be a buffer or a string!');
}
}
if (cert) {
if(Buffer.isBuffer(cert)) {
this.cert = cert;
} else if (typeof cert === 'string') {
this.cert = fs.readFileSync(cert);
} else {
throw new Error('cert should be a buffer or a string!');
}
}
if (ca) {
if(Buffer.isBuffer(ca) || Array.isArray(ca)) {
this.ca = ca;
} else if (typeof ca === 'string') {
this.ca = fs.readFileSync(ca);
} else {
defaults = ca;
this.ca = null;
}
}
this.defaults = {};
_.merge(this.defaults, defaults);
}...
_Note_: If you run into issues using this protocol, consider passing these options
as default request options to the constructor:
* `rejectUnauthorized: false`
* `strictSSL: false`
* `secureOptions: constants.SSL_OP_NO_TLSv1_2` (this is likely needed for node >= 10.0)
``` javascript
client.setSecurity(new soap.ClientSSLSecurity(
'/path/to/key'
, '/path/to/cert'
, {/*default request options*/}
));
```
### WSSecurity
...function ClientSSLSecurityPFX(pfx, passphrase, defaults) {
if (typeof passphrase === 'object') {
defaults = passphrase;
}
if (pfx) {
if (Buffer.isBuffer(pfx)) {
this.pfx = pfx;
} else if (typeof pfx === 'string') {
this.pfx = fs.readFileSync(pfx);
} else {
throw new Error('supplied pfx file should be a buffer or a file location');
}
}
if (passphrase) {
if (typeof passphrase === 'string') {
this.passphrase = passphrase;
}
}
this.defaults = {};
_.merge(this.defaults, defaults);
}n/a
function WSSecurity(username, password, options) {
options = options || {};
this._username = username;
this._password = password;
//must account for backward compatibility for passwordType String param as well as object options defaults: passwordType = 'PasswordText
', hasTimeStamp = true
if (typeof options === 'string') {
this._passwordType = options ? options : 'PasswordText';
options = {};
} else {
this._passwordType = options.passwordType ? options.passwordType : 'PasswordText';
}
if (validPasswordTypes.indexOf(this._passwordType) === -1) {
this._passwordType = 'PasswordText';
}
this._hasTimeStamp = options.hasTimeStamp || typeof options.hasTimeStamp === 'boolean' ? !!options.hasTimeStamp : true;
/*jshint eqnull:true */
if (options.hasNonce != null) {
this._hasNonce = !!options.hasNonce;
}
this._hasTokenCreated = options.hasTokenCreated || typeof options.hasTokenCreated === 'boolean' ? !!options.hasTokenCreated :
true;
if (options.actor != null) {
this._actor = options.actor;
}
if (options.mustUnderstand != null) {
this._mustUnderstand = !!options.mustUnderstand;
}
}n/a
function WSSecurityCert(privatePEM, publicP12PEM, password, encoding) {
if (!ursa) {
throw new Error('Module ursa must be installed to use WSSecurityCert');
}
this.privateKey = ursa.createPrivateKey(privatePEM, password, encoding);
this.publicP12PEM = publicP12PEM.toString().replace('-----BEGIN CERTIFICATE-----', '').replace('-----END CERTIFICATE-----', '').
replace(/(\r\n|\n|\r)/gm, '');
this.signer = new SignedXml();
this.signer.signingKey = this.privateKey.toPrivatePem();
this.x509Id = "x509-" + generateId();
var _this = this;
this.signer.keyInfoProvider = {};
this.signer.keyInfoProvider.getKeyInfo = function (key) {
return wsseSecurityTokenTemplate({ x509Id: _this.x509Id });
};
}...
WS-Security X509 Certificate support.
``` javascript
var privateKey = fs.readFileSync(privateKeyPath);
var publicKey = fs.readFileSync(publicKeyPath);
var password = ''; // optional password
var wsSecurity = new soap.WSSecurityCert(privateKey, publicKey, password, 'utf8
');
client.setSecurity(wsSecurity);
```
_Note_: Optional dependency 'ursa' is required to be installed successfully when WSSecurityCert is used.
## Handling XML Attributes, Value and XML (wsdlOptions).
Sometimes it is necessary to override the default behaviour of `node-soap` in order to deal with the special requirements
...Server = function (server, path, services, wsdl, options) {
var self = this;
events.EventEmitter.call(this);
options = options || {};
this.path = path;
this.services = services;
this.wsdl = wsdl;
this.suppressStack = options && options.suppressStack;
if (path[path.length - 1] !== '/')
path += '/';
wsdl.onReady(function (err) {
if (typeof server.route === 'function' && typeof server.use === 'function') {
//handle only the required URL path for express server
server.route(path).all(function (req, res, next) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
res.end();
return;
}
}
self._requestListener(req, res);
});
} else {
var listeners = server.listeners('request').slice();
server.removeAllListeners('request');
server.addListener('request', function (req, res) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req)) {
res.end();
return;
}
}
var reqPath = url.parse(req.url).pathname;
if (reqPath[reqPath.length - 1] !== '/') {
reqPath += '/';
}
if (path === reqPath) {
self._requestListener(req, res);
} else {
for (var i = 0, len = listeners.length; i < len; i++) {
listeners[i].call(this, req, res);
}
}
});
}
});
this._initializeOptions(options);
}n/a
function createClient(wsdlUrl, options, cb) {
if (!cb) {
cb = options;
options = {};
}
if (this.errOnCreateClient) {
return setTimeout(cb.bind(null, new Error('forced error on createClient')));
}
var client = getStub(wsdlUrl);
if (client) {
resetStubbedMethods(client);
setTimeout(cb.bind(null, null, client));
} else {
setTimeout(cb.bind(null, new Error('no client stubbed for ' + wsdlUrl)));
}
}...
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Features:](#features)
- [Install](#install)
- [Where can I file an issue?](#where-can-i-file-an-issue)
- [Module](#module)
- [soap.createClient(url[, options], callback) - create a new SOAP client from a WSDL
url. Also supports a local filesystem path.](#soapcreateclienturl-options-callback---create-a-new-soap-client-from-a-wsdl-url-also
-supports-a-local-filesystem-path)
- [soap.listen(*server*, *path*, *services*, *wsdl*) - create a new SOAP server that listens on *path* and provides *services*.](#
soaplistenserver-path-services-wsdl---create-a-new-soap-server-that-listens-on-path-and-provides-services)
- [Options](#options)
- [Server Logging](#server-logging)
- [Server Events](#server-events)
- [SOAP Fault](#soap-fault)
- [Server security example using PasswordDigest](#server-security-example-using-passworddigest)
- [Server connection authorization](#server-connection-authorization)
...function createErroringStub(err) {
return function() {
this.args.forEach(function(argSet) {
setTimeout(argSet[1].bind(null, err));
});
this.yields(err);
};
}...
var soapStub = require('soap/soap-stub');
var urlMyApplicationWillUseWithCreateClient = 'http://path-to-my-wsdl';
var clientStub = {
SomeOperation: sinon.stub()
};
clientStub.SomeOperation.respondWithError = soapStub.createErroringStub({..error json
...});
clientStub.SomeOperation.respondWithSuccess = soapStub.createRespondingStub({..success json...});
soapStub.registerClient('my client alias', urlMyApplicationWillUseWithCreateClient, clientStub);
// test.js
var soapStub = require('soap/soap-stub');
...function createRespondingStub(object, body) {
return function() {
this.args.forEach(function(argSet) {
setTimeout(argSet[1].bind(null, null, object));
});
this.yields(null, object, body);
};
}...
var urlMyApplicationWillUseWithCreateClient = 'http://path-to-my-wsdl';
var clientStub = {
SomeOperation: sinon.stub()
};
clientStub.SomeOperation.respondWithError = soapStub.createErroringStub({..error json...});
clientStub.SomeOperation.respondWithSuccess = soapStub.createRespondingStub({..success
json...});
soapStub.registerClient('my client alias', urlMyApplicationWillUseWithCreateClient, clientStub);
// test.js
var soapStub = require('soap/soap-stub');
describe('myService', function() {
...function getStub(aliasOrWsdlUrl) {
return aliasedClientStubs[aliasOrWsdlUrl] || clientStubs[aliasOrWsdlUrl];
}...
var soapStub = require('soap/soap-stub');
describe('myService', function() {
var clientStub;
var myService;
beforeEach(function() {
clientStub = soapStub.getStub('my client alias');
soapStub.reset();
myService.init(clientStub);
});
describe('failures', function() {
beforeEach(function() {
clientStub.SomeOperation.respondWithError();
...function registerClient(alias, urlToWsdl, clientStub) {
aliasedClientStubs[alias] = clientStub;
clientStubs[urlToWsdl] = clientStub;
}...
var clientStub = {
SomeOperation: sinon.stub()
};
clientStub.SomeOperation.respondWithError = soapStub.createErroringStub({..error json...});
clientStub.SomeOperation.respondWithSuccess = soapStub.createRespondingStub({..success json...});
soapStub.registerClient('my client alias', urlMyApplicationWillUseWithCreateClient
, clientStub);
// test.js
var soapStub = require('soap/soap-stub');
describe('myService', function() {
var clientStub;
var myService;
...function reset() {
_.forEach(clientStubs, resetStubbedMethods);
this.errOnCreateClient = false;
}...
describe('myService', function() {
var clientStub;
var myService;
beforeEach(function() {
clientStub = soapStub.getStub('my client alias');
soapStub.reset();
myService.init(clientStub);
});
describe('failures', function() {
beforeEach(function() {
clientStub.SomeOperation.respondWithError();
});
...findPrefix = function (xmlnsMapping, nsURI) {
for (var n in xmlnsMapping) {
if (n === TNS_PREFIX) continue;
if (xmlnsMapping[n] === nsURI) {
return n;
}
}
}n/a
function passwordDigest(nonce, created, password) {
// digest = base64 ( sha1 ( nonce + created + password ) )
var pwHash = crypto.createHash('sha1');
var rawNonce = new Buffer(nonce || '', 'base64').toString('binary');
pwHash.update(rawNonce + created + password);
return pwHash.digest('base64');
}...
``` javascript
server = soap.listen(...)
server.authenticate = function(security) {
var created, nonce, password, user, token;
token = security.UsernameToken, user = token.Username,
password = token.Password, nonce = token.Nonce, created = token.Created;
return user === 'user' && password === soap.passwordDigest(nonce
, created, 'password');
};
```
### Server connection authorization
The `server.authorizeConnection` method is called prior to the soap service method.
If the method is defined and returns `false` then the incoming connection is
...WSDL = function (definition, uri, options) {
var self = this,
fromFunc;
this.uri = uri;
this.callback = function() {
};
this._includesWsdl = [];
// initialize WSDL cache
this.WSDL_CACHE = (options || {}).WSDL_CACHE || {};
this._initializeOptions(options);
if (typeof definition === 'string') {
definition = stripBom(definition);
fromFunc = this._fromXML;
}
else if (typeof definition === 'object') {
fromFunc = this._fromServices;
}
else {
throw new Error('WSDL constructor takes either an XML string or service definition');
}
process.nextTick(function() {
try {
fromFunc.call(self, definition);
} catch (e) {
return self.callback(e.message);
}
self.processIncludes(function(err) {
var name;
if (err) {
return self.callback(err);
}
self.definitions.deleteFixedAttrs();
var services = self.services = self.definitions.services;
if (services) {
for (name in services) {
services[name].postProcess(self.definitions);
}
}
var complexTypes = self.definitions.complexTypes;
if (complexTypes) {
for (name in complexTypes) {
complexTypes[name].deleteFixedAttrs();
}
}
// for document style, for every binding, prepare input message element name to (methodName, output message element name)
mapping
var bindings = self.definitions.bindings;
for (var bindingName in bindings) {
var binding = bindings[bindingName];
if (typeof binding.style === 'undefined') {
binding.style = 'document';
}
if (binding.style !== 'document')
continue;
var methods = binding.methods;
var topEls = binding.topElements = {};
for (var methodName in methods) {
if (methods[methodName].input) {
var inputName = methods[methodName].input.$name;
var outputName="";
if(methods[methodName].output )
outputName = methods[methodName].output.$name;
topEls[inputName] = {"methodName": methodName, "outputName": outputName};
}
}
}
// prepare soap envelope xmlns definition string
self.xmlnsInEnvelope = self._xmlnsMap();
self.callback(err, self);
});
});
}n/a
function open_wsdl(uri, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
// initialize cache when calling open_wsdl directly
var WSDL_CACHE = options.WSDL_CACHE || {};
var request_headers = options.wsdl_headers;
var request_options = options.wsdl_options;
var wsdl;
if (!/^https?:/.test(uri)) {
debug('Reading file: %s', uri);
fs.readFile(uri, 'utf8', function(err, definition) {
if (err) {
callback(err);
}
else {
wsdl = new WSDL(definition, uri, options);
WSDL_CACHE[ uri ] = wsdl;
wsdl.WSDL_CACHE = WSDL_CACHE;
wsdl.onReady(callback);
}
});
}
else {
debug('Reading url: %s', uri);
var httpClient = options.httpClient || new HttpClient(options);
httpClient.request(uri, null /* options */, function(err, response, definition) {
if (err) {
callback(err);
} else if (response && response.statusCode === 200) {
wsdl = new WSDL(definition, uri, options);
WSDL_CACHE[ uri ] = wsdl;
wsdl.WSDL_CACHE = WSDL_CACHE;
wsdl.onReady(callback);
} else {
callback(new Error('Invalid WSDL URL: ' + uri + "\n\n\r Code: " + response.statusCode + "\n\n\r Response Body: " + response
.body));
}
}, request_headers, request_options);
}
return wsdl;
}n/a