From be4a4e3d61fdd636aa22c84816a64c6839e819fc Mon Sep 17 00:00:00 2001 From: Ryan Fink Date: Thu, 14 May 2015 11:53:42 -0400 Subject: [PATCH 01/48] Add last endpoint --- lib/client.js | 1 + test/client-test.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lib/client.js b/lib/client.js index 87d0a9060..a97a12136 100644 --- a/lib/client.js +++ b/lib/client.js @@ -209,6 +209,7 @@ Client.prototype._invoke = function(method, args, location, callback, options, e self.lastMessage = message; self.lastRequest = xml; + self.lastEndpoint = location; self.emit('message', message); self.emit('request', xml); diff --git a/test/client-test.js b/test/client-test.js index c79297928..c737fd6b0 100644 --- a/test/client-test.js +++ b/test/client-test.js @@ -261,12 +261,14 @@ describe('SOAP Client', function() { client.MyOperation(data, function(err, result) { assert.ok(client.lastRequest); assert.ok(client.lastMessage); + assert.ok(client.lastEndpoint); assert.equal(client.lastMessage, message); delete data.attributes.xsi_type.namespace; client.MyOperation(data, function(err, result) { assert.ok(client.lastRequest); assert.ok(client.lastMessage); + assert.ok(client.lastEndpoint); assert.equal(client.lastMessage, message); done(); From ff71681a51f513b710befa676a0d952199aa8337 Mon Sep 17 00:00:00 2001 From: Sasha Vincic Date: Wed, 22 Apr 2015 14:29:06 +0200 Subject: [PATCH 02/48] fix for empty strings that were returnd as empty json objects fixed tests now when we get empty string for strings instead of empty objects when converting from soap to json --- lib/wsdl.js | 5 +++++ .../Message__Messages_suffixed_with_In_Out/response.json | 2 +- .../response.json | 2 +- .../response.json | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/wsdl.js b/lib/wsdl.js index 908bdbd1c..dfd196a42 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1294,10 +1294,15 @@ WSDL.prototype.xmlToObject = function(xml) { topSchema = top.schema, name = splitNSName(nsName).name; + if (typeof cur.schema === 'string' && (cur.schema === 'string' || cur.schema.split(':')[1] === 'string')) { + if (typeof obj === 'object' && Object.keys(obj).length === 0) obj = cur.object = ''; + } + if(cur.nil === true) { return; } + if (topSchema && topSchema[name + '[]']) { if (!topObject[name]) topObject[name] = []; diff --git a/test/request-response-samples/Message__Messages_suffixed_with_In_Out/response.json b/test/request-response-samples/Message__Messages_suffixed_with_In_Out/response.json index d3aacc842..79fdba308 100644 --- a/test/request-response-samples/Message__Messages_suffixed_with_In_Out/response.json +++ b/test/request-response-samples/Message__Messages_suffixed_with_In_Out/response.json @@ -1,4 +1,4 @@ { "return":"-1", - "bstrError":{} + "bstrError":"" } diff --git a/test/request-response-samples/Message__Messages_suffixed_with_Input_Output/response.json b/test/request-response-samples/Message__Messages_suffixed_with_Input_Output/response.json index d3aacc842..79fdba308 100644 --- a/test/request-response-samples/Message__Messages_suffixed_with_Input_Output/response.json +++ b/test/request-response-samples/Message__Messages_suffixed_with_Input_Output/response.json @@ -1,4 +1,4 @@ { "return":"-1", - "bstrError":{} + "bstrError":"" } diff --git a/test/request-response-samples/Message__Messages_suffixed_with_Request_Response/response.json b/test/request-response-samples/Message__Messages_suffixed_with_Request_Response/response.json index d3aacc842..79fdba308 100644 --- a/test/request-response-samples/Message__Messages_suffixed_with_Request_Response/response.json +++ b/test/request-response-samples/Message__Messages_suffixed_with_Request_Response/response.json @@ -1,4 +1,4 @@ { "return":"-1", - "bstrError":{} + "bstrError":"" } From cdfe7e1ed0b306a30d490aa569bb21ed7236b5ed Mon Sep 17 00:00:00 2001 From: herom Date: Sat, 30 May 2015 21:58:17 +0200 Subject: [PATCH 03/48] Release 0.9.1 --- History.md | 32 ++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index a254fe5ff..3ad2118a2 100644 --- a/History.md +++ b/History.md @@ -1,19 +1,51 @@ +0.9.1 / 2015-05-30 +================= +* [FIX] Received empty Strings are now returned as empty String rather than an empty Object. (#637) + +* [FIX] Get current namespace when parent namespace is an empty String. Fixes #533. (#661) + +* [DOC] Update README.md with documentation for #660 introduced customization of `httpClient` and `request` libs in `client.options`. (#664) + +* [FIX] Take configured "ignored namespaces" into account when processing `objectToXml()`. Fixes #537. (#662) + +* [LIC] Update license attribute to follow the new [npm conventions](https://docs.npmjs.com/files/package.json#license). (#663) + +* [ENHANCEMENT] Add ability to customize `http` client / `request` lib on client creation. (#660) + +* [FIX] Support `xsi:type` Schema on Element. Fixes #606. (#639) + +* [FIX] Make parsing of recursive Elements in `wsdl` work. (#658) + 0.9.0 / 2015-05-18 ================= * [FIX] Fix to allow request options and headers to persist for all includes. Fix to properly handle when an import/include starts with a schema element. (#608) + * [FIX] Do not end request for keep-alive connections (#600) + * [ENHANCEMENT] Added Client 'response' event (#610) + * [FIX] If response is json, then error should not be thrown. Fix issue #580 (#581) + * [FIX] Sub-namespace should be correct regardless of order of enumeration i.e. should not be overriden by other prop's namespace (#607) + * [DOC] Added a section about Server Events to README.md (#596) + * [ENHANCEMENT] Added Server 'request' event (#595) + * [ENHANCEMENT] Add support for One-Way Operations (#590) + * [FIX] `lib/wsdl.js` util function `extend()` doesn't throw an Error when handling elements that are not objects. (#589) + * [ENHANCEMENT] ClientSSLSecurity now accepts a `ca`-certificate. (#588) + * [ENHANCEMENT] ClientSSLSecurity should be able to take a Buffer as `key` and `cert` parameter. Additionally the certificates are checked whether they are correct or not (starting with `-----BEGIN`). (#586) + * [ENHANCEMENT] Add support for sending NULL values (#578) + * [ENHANCEMENT] Follow 302 redirects, don't mix quotes (#577) + * [DOC] Update CONTRIBUTING.md + * [FIX] Respond with security timestamp if request had one (#579) diff --git a/package.json b/package.json index 1912b7421..cc7600b2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soap", - "version": "0.9.0", + "version": "0.9.1", "description": "A minimal node SOAP client", "engines": { "node": ">=0.8.0" From 0a494c949ca42b5464102abd513e54a26dc03317 Mon Sep 17 00:00:00 2001 From: Johan Brodin Date: Mon, 15 Jun 2015 14:49:40 +0200 Subject: [PATCH 04/48] Use diffrent namespace styles for soap fault 1.1 and 1.2 * SOAP fault 1.1 spec http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383507 * SOAP fault 1.2 spec http://www.w3.org/TR/soap12-part1/#soapfault --- lib/server.js | 40 ++++++++++++++++++++++++++++------------ test/server-test.js | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/lib/server.js b/lib/server.js index 1e4ace801..517bdde89 100644 --- a/lib/server.js +++ b/lib/server.js @@ -30,7 +30,7 @@ var url = require('url'), try { compress = require("compress"); -} catch (e) { +} catch (error) { } var Server = function(server, path, services, wsdl, options) { @@ -237,15 +237,12 @@ Server.prototype._process = function(input, URL, callback) { }, callback, includeTimestamp); } } - catch (e) { - if (e.Fault !== undefined) { - // 3rd param is the NS prepended to all elements - // It must match the NS defined in the Envelope (set by the _envelope method) - var fault = self.wsdl.objectToDocumentXML("Fault", e.Fault, "soap"); - callback(self._envelope(fault, includeTimestamp)); + catch (error) { + if (error.Fault !== undefined) { + return self._sendError(error.Fault, callback, includeTimestamp); } - else - throw e; + + throw error; } }; @@ -263,7 +260,7 @@ Server.prototype._executeMethod = function(options, callback, includeTimestamp) try { method = this.services[serviceName][portName][methodName]; - } catch (e) { + } catch (error) { return callback(this._envelope('', includeTimestamp)); } @@ -273,8 +270,7 @@ Server.prototype._executeMethod = function(options, callback, includeTimestamp) handled = true; if (error && error.Fault !== undefined) { - var fault = self.wsdl.objectToDocumentXML("Fault", error.Fault, "soap"); - return callback(self._envelope(fault, includeTimestamp)); + return self._sendError(error.Fault, callback, includeTimestamp); } else if (result === undefined) { // Backward compatibility to support one argument callback style @@ -334,4 +330,24 @@ Server.prototype._envelope = function(body, includeTimestamp) { return xml; }; +Server.prototype._sendError = function(soapFault, callback, includeTimestamp) { + var self = this, + fault; + + 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)); +}; + exports.Server = Server; diff --git a/test/server-test.js b/test/server-test.js index 8a6522e14..2ef5933d6 100644 --- a/test/server-test.js +++ b/test/server-test.js @@ -18,7 +18,7 @@ test.service = { throw new Error('triggered server error'); } else if (args.tickerSymbol === 'Async') { return cb({ price: 19.56 }); - } else if (args.tickerSymbol === 'SOAP Fault') { + } else if (args.tickerSymbol === 'SOAP Fault v1.2') { throw { Fault: { Code: { @@ -28,6 +28,13 @@ test.service = { Reason: { Text: "Processing Error" } } }; + } else if (args.tickerSymbol === 'SOAP Fault v1.1') { + throw { + Fault: { + faultcode: "soap:Client.BadArguments", + faultstring: "Error while processing arguments" + } + }; } else { return { price: 19.56 }; } @@ -259,14 +266,37 @@ describe('SOAP Server', function() { }); }); - it('should return SOAP Fault body', function(done) { + it('should return SOAP Fault body for SOAP 1.2', function(done) { soap.createClient(test.baseUrl + '/stockquote?wsdl', function(err, client) { assert.ok(!err); - client.GetLastTradePrice({ tickerSymbol: 'SOAP Fault' }, function(err, response, body) { + client.GetLastTradePrice({ tickerSymbol: 'SOAP Fault v1.2' }, function(err, response, body) { assert.ok(err); var fault = err.root.Envelope.Body.Fault; assert.equal(fault.Code.Value, "soap:Sender"); assert.equal(fault.Reason.Text, "Processing Error"); + // Verify namespace on elements set according to fault spec 1.2 + assert.ok(body.match(/.*<\/soap:Code>/g), + "Body should contain Code-element with namespace"); + assert.ok(body.match(/.*<\/soap:Reason>/g), + "Body should contain Reason-element with namespace"); + done(); + }); + }); + }); + + it('should return SOAP Fault body for SOAP 1.1', function(done) { + soap.createClient(test.baseUrl + '/stockquote?wsdl', function(err, client) { + assert.ok(!err); + client.GetLastTradePrice({ tickerSymbol: 'SOAP Fault v1.1' }, function(err, response, body) { + assert.ok(err); + var fault = err.root.Envelope.Body.Fault; + assert.equal(fault.faultcode, "soap:Client.BadArguments"); + assert.equal(fault.faultstring, "Error while processing arguments"); + // Verify namespace on elements set according to fault spec 1.1 + assert.ok(body.match(/.*<\/faultcode>/g), + "Body should contain faultcode-element without namespace"); + assert.ok(body.match(/.*<\/faultstring>/g), + "Body should contain faultstring-element without namespace"); done(); }); }); From ba2698e2b048d6320eebb6e923027eeb73433946 Mon Sep 17 00:00:00 2001 From: Ivan Erceg Date: Wed, 17 Jun 2015 15:43:48 -0400 Subject: [PATCH 05/48] Fixed SOAP Fault errors not being raised as errors --- lib/client.js | 23 +++++++++++++++++++---- lib/wsdl.js | 6 +++++- package.json | 5 +++-- test/client-test.js | 30 ++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/lib/client.js b/lib/client.js index a6c9c719c..c14be60d2 100644 --- a/lib/client.js +++ b/lib/client.js @@ -16,7 +16,8 @@ var HttpClient = require('./http'), events = require('events'), util = require('util'), debug = require('debug')('node-soap'), - url = require('url'); + url = require('url'), + _ = require('lodash'); var Client = function(wsdl, endpoint, options) { events.EventEmitter.call(this); @@ -215,6 +216,15 @@ Client.prototype._invoke = function(method, args, location, callback, options, e self.emit('message', message); self.emit('request', xml); + var tryJSONparse = function(body) { + try { + return JSON.parse(body); + } + catch(err) { + return undefined; + } + }; + req = self.httpClient.request(location, xml, function(err, response, body) { var result; var obj; @@ -225,14 +235,19 @@ Client.prototype._invoke = function(method, args, location, callback, options, e if (err) { callback(err); } else { + try { obj = self.wsdl.xmlToObject(body); } catch (error) { - //When the output element cannot be looked up in the wsdl, - //Then instead of sending the error, We pass the body in the response. + // When the output element cannot be looked up in the wsdl and the body is JSON + // instead of sending the error, we pass the body in the response. if(!output.$lookupTypes) { debug('Response element is not present. Unable to convert response xml to json.'); - return callback(null,response,body); + // If the response is JSON then return it as-is. + var json = _.isObject(body) ? body : tryJSONparse(body); + if (json) { + return callback(null, response, json); + } } error.response = response; error.body = body; diff --git a/lib/wsdl.js b/lib/wsdl.js index dfd196a42..d454f28aa 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -17,6 +17,7 @@ var assert = require('assert').ok; var stripBom = require('strip-bom'); var debug = require('debug')('node-soap'); var _ = require('lodash'); +var selectn = require('selectn'); var Primitives = { string: 1, @@ -1393,7 +1394,10 @@ WSDL.prototype.xmlToObject = function(xml) { if (root.Envelope) { var body = root.Envelope.Body; if (body.Fault) { - var error = new Error(body.Fault.faultcode + ': ' + body.Fault.faultstring + (body.Fault.detail ? ': ' + body.Fault.detail : '')); + var code = selectn('faultcode.$value', body.Fault); + var string = selectn('faultstring.$value', body.Fault); + var detail = selectn('detail.$value', body.Fault); + var error = new Error(code + ': ' + string + (detail ? ': ' + detail : '')); error.root = root; throw error; } diff --git a/package.json b/package.json index cc7600b2c..3914cc8c5 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,12 @@ }, "author": "Vinay Pulim ", "dependencies": { + "debug": "~0.7.4", "lodash": "~2.4.1", "request": ">=2.9.0", "sax": ">=0.6", - "strip-bom": "~0.3.1", - "debug": "~0.7.4" + "selectn": "^0.9.6", + "strip-bom": "~0.3.1" }, "repository": { "type": "git", diff --git a/test/client-test.js b/test/client-test.js index 1444b9fe8..c632636ac 100644 --- a/test/client-test.js +++ b/test/client-test.js @@ -516,4 +516,34 @@ describe('SOAP Client', function() { }); }); + + it('should return error in the call when Fault was returned', function(done) { + var server = null; + var hostname = '127.0.0.1'; + var port = 15099; + var baseUrl = 'http://' + hostname + ':' + port; + + server = http.createServer(function (req, res) { + res.statusCode = 200; + res.write("\nTesttest errortest detail"); + res.end(); + }).listen(port, hostname, function() { + soap.createClient(__dirname+'/wsdl/json_response.wsdl', function(err, client) { + assert.ok(client); + assert.ok(!err); + + client.MyOperation({}, function(err, result, body) { + server.close(); + server = null; + assert.ok(err); + assert.strictEqual(err.message, 'Test: test error: test detail'); + assert.ok(result); + assert.ok(body); + done(); + }); + }, baseUrl); + }); + + }); + }); From 001dd02573c65296b7a10558c1fb6753f0625827 Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Mon, 20 Jul 2015 21:16:24 +0200 Subject: [PATCH 06/48] Fix a typo in WSDL#findChildParameterObject --- lib/wsdl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wsdl.js b/lib/wsdl.js index d454f28aa..7eccade33 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1673,7 +1673,7 @@ WSDL.prototype.findChildParameterObject = function(parameterTypeObj, childName) i = 0, child; - if(parameterTypeObj.$lookupTypes && Array.isArray(parameterTypeObj.$lookupTypes && parameterTypeObj.$lookupTypes.length)) { + if(parameterTypeObj.$lookupTypes && Array.isArray(parameterTypeObj.$lookupTypes) && parameterTypeObj.$lookupTypes.length) { var types = parameterTypeObj.$lookupTypes; for(i = 0; i < types.length; i++) { From 10410c59c5161cbf522546ff304dada6581821c5 Mon Sep 17 00:00:00 2001 From: Jimmy Jones Date: Tue, 11 Aug 2015 20:04:52 +0100 Subject: [PATCH 07/48] Allow WSDL to be loaded from HTTPS sites --- lib/wsdl.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wsdl.js b/lib/wsdl.js index 7eccade33..60b2c0f6d 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1115,7 +1115,7 @@ WSDL.prototype._processNextInclude = function(includes, callback) { return callback(); var includePath; - if (!/^http/.test(self.uri) && !/^http/.test(include.location)) { + 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); @@ -1827,7 +1827,7 @@ function open_wsdl(uri, options, callback) { var request_options = options.wsdl_options; var wsdl; - if (!/^http/.test(uri)) { + if (!/^https?/.test(uri)) { debug('Reading file: %s', uri); fs.readFile(uri, 'utf8', function(err, definition) { if (err) { From 79cc0757ad1dc48bf9610ea04d4c3accbd6c64c9 Mon Sep 17 00:00:00 2001 From: Claudia Hardman Date: Tue, 18 Aug 2015 13:26:36 -0400 Subject: [PATCH 08/48] Prevent sending Object prototype methods as XML Adds `Object.prototype.hasOwnProperty` check in `WSDL.prototype.objectToRpcXML` and `WSDL.prototype.objectToXML`. This prevents sending arbitrary Javascript has XML nodes. --- lib/wsdl.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/wsdl.js b/lib/wsdl.js index 7eccade33..7d11fb59c 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1451,6 +1451,7 @@ WSDL.prototype.objectToRpcXML = function(name, params, namespace, xmlns) { parts.push(['<', namespace, name, '>'].join('')); for (var key in params) { + if (!params.hasOwnProperty(key)) continue; if (key !== nsAttrName) { var value = params[key]; parts.push(['<', key, '>'].join('')); @@ -1513,6 +1514,7 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, first, xmlnsA } } 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; From 67139780df048c16e899a90dd1b388438ba7f5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20De=20Caluw=C3=A9?= Date: Tue, 25 Aug 2015 02:28:58 +0200 Subject: [PATCH 09/48] Add the correct namespace alias for operations without parameters by simply removing the special case where input.parts is empty. If special logic is wanted for this case, it should be contained in objectToRpcXML in any case. --- lib/client.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/client.js b/lib/client.js index c14be60d2..51215a85b 100644 --- a/lib/client.js +++ b/lib/client.js @@ -174,14 +174,12 @@ Client.prototype._invoke = function(method, args, location, callback, options, e if (self.security && self.security.addOptions) self.security.addOptions(options); - if (input.parts) { + if (input.parts || args === null) { assert.ok(!style || style === 'rpc', 'invalid message definition for document style binding'); message = self.wsdl.objectToRpcXML(name, args, alias, ns); (method.inputSoap === 'encoded') && (encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" '); } else if (typeof (args) === 'string') { message = args; - } else if (args === null) { - message = "<" + name + " />"; } else { assert.ok(!style || style === 'document', 'invalid message definition for rpc style binding'); // pass `input.$lookupType` if `input.$type` could not be found From d48297a61605d49bf4ef2437d673e5ad92bc8552 Mon Sep 17 00:00:00 2001 From: Dave Kerr Date: Thu, 3 Sep 2015 16:13:15 -0400 Subject: [PATCH 10/48] update spelling and formatting to clarify several sections of Readme --- Readme.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Readme.md b/Readme.md index 80d2c0e4b..e5903d85b 100644 --- a/Readme.md +++ b/Readme.md @@ -81,9 +81,9 @@ The `options` argument allows you to customize the client with the following pro soap.listen(server, '/wsdl', myService, xml); ``` -### server logging +### Server Logging -If the log method is defined it will be called with 'received' and 'replied' +If the `log` method is defined it will be called with 'received' and 'replied' along with data. ``` javascript @@ -153,9 +153,9 @@ First parameter is the Headers object; second parameter is the name of the SOAP method that will called (in case you need to handle the headers differently based on the method). -### server security example using PasswordDigest +### Server security example using PasswordDigest -If server.authenticate is not defined no authentation will take place. +If `server.authenticate` is not defined then no authentication will take place. ``` javascript server = soap.listen(...) @@ -167,10 +167,10 @@ If server.authenticate is not defined no authentation will take place. }; ``` -### server connection authorization +### Server connection authorization -This is called prior to soap service method -If the method is defined and returns false the incoming connection is +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 terminated. ``` javascript @@ -183,7 +183,7 @@ terminated. ## Client -An instance of Client is passed to the soap.createClient callback. It is used to execute methods on the soap service. +An instance of `Client` is passed to the `soap.createClient` callback. It is used to execute methods on the soap service. ### Client.describe() - description of services, ports and methods as a JavaScript object @@ -205,8 +205,8 @@ An instance of Client is passed to the soap.createClient callback. It is used t ### Client.setSecurity(security) - use the specified security protocol `node-soap` has several default security protocols. You can easily add your own 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 reurns a string of XML. +* `addOptions` - a method that accepts an options arg that is eventually passed directly to `request` +* `toXML` - a method that returns a string of XML. By default there are 3 protocols: @@ -219,9 +219,9 @@ By default there are 3 protocols: ####ClientSSLSecurity _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 +* `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( From 9761124fa5e308f6eb42498f5f8cd1b15c53b423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20De=20Caluw=C3=A9?= Date: Sat, 5 Sep 2015 04:21:02 +0200 Subject: [PATCH 11/48] Add request sample for an operation without any parameters. --- .../request.json | 1 + .../request.xml | 1 + .../soap.wsdl | 75 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/request.json create mode 100644 test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/request.xml create mode 100644 test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/soap.wsdl diff --git a/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/request.json b/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/request.json new file mode 100644 index 000000000..19765bd50 --- /dev/null +++ b/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/request.json @@ -0,0 +1 @@ +null diff --git a/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/request.xml b/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/request.xml new file mode 100644 index 000000000..b198acbc3 --- /dev/null +++ b/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/request.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/soap.wsdl b/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/soap.wsdl new file mode 100644 index 000000000..99cf04677 --- /dev/null +++ b/test/request-response-samples/NoParams__add_correct_namespace_for_operations_without_parameters/soap.wsdl @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 46420c0ce608171f97b7eb9b2be24f7b0d40e541 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Tue, 8 Sep 2015 10:08:04 -0700 Subject: [PATCH 12/48] Moving travis build to containers. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3a6836590..22141dee5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +sudo: false language: node_js node_js: - 0.8 From 0703a9dac52a3bdb2c6f0475a87549a05c62ecf7 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 18 Aug 2015 11:34:40 -0700 Subject: [PATCH 13/48] Add support for xsd element ref --- lib/wsdl.js | 39 +++++++++++++++++++++++--- test/wsdl-test.js | 16 +++++++++++ test/wsdl/elementref/bar.xsd | 26 +++++++++++++++++ test/wsdl/elementref/bar1.xsd | 40 ++++++++++++++++++++++++++ test/wsdl/elementref/foo.wsdl | 53 +++++++++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 test/wsdl/elementref/bar.xsd create mode 100644 test/wsdl/elementref/bar1.xsd create mode 100755 test/wsdl/elementref/foo.wsdl diff --git a/lib/wsdl.js b/lib/wsdl.js index 7eccade33..663331308 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1542,12 +1542,14 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, first, xmlnsA if (schema) { var childParameterTypeObject = self.findChildParameterObject(parameterTypeObject, name); //find sub namespace if not a primitive - if (childParameterTypeObject && childParameterTypeObject.$type && (childParameterTypeObject.$type.indexOf('xsd') === -1)) { + if (childParameterTypeObject && + ((childParameterTypeObject.$type && (childParameterTypeObject.$type.indexOf('xsd') === -1)) || + childParameterTypeObject.$ref)) { if(childParameterTypeObject.$baseNameSpace) { //this element has a base with another namespace (the correct one) ns = childParameterTypeObject.$baseNameSpace + ':'; } - var childParameterType = childParameterTypeObject.$type; + var childParameterType = childParameterTypeObject.$type || childParameterTypeObject.$ref; var childNamespace = ''; var childName = ''; @@ -1563,7 +1565,22 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, first, xmlnsA ancXmlns.push(childXmlns); } } - var completeChildParameterTypeObject = self.findChildParameterObjectFromSchema(childName, childXmlns) || childParameterTypeObject; + // There is matching element ref + if (childParameterTypeObject.$ref) { + ns = childNamespace ? childNamespace + ':' : ''; + xmlnsAttrib = childXmlnsAttrib; + } + + var completeChildParameterTypeObject; + if (childParameterTypeObject.$type) { + completeChildParameterTypeObject = + self.findChildParameterObjectFromSchema(childName, childXmlns) || + childParameterTypeObject; + } else { + completeChildParameterTypeObject = + self.findParameterObject(childXmlns, childName) || + childParameterTypeObject; + } for(var ignoredNamespacesIndex = 0, ignoredNamespacesLength = this.ignoredNamespaces.length; ignoredNamespacesIndex < ignoredNamespacesLength; ignoredNamespacesIndex++) { if(this.ignoredNamespaces[ignoredNamespacesIndex] === childNamespace) { @@ -1671,7 +1688,8 @@ WSDL.prototype.findChildParameterObject = function(parameterTypeObj, childName) } var found = null, i = 0, - child; + child, + ref; if(parameterTypeObj.$lookupTypes && Array.isArray(parameterTypeObj.$lookupTypes) && parameterTypeObj.$lookupTypes.length) { var types = parameterTypeObj.$lookupTypes; @@ -1683,12 +1701,25 @@ WSDL.prototype.findChildParameterObject = function(parameterTypeObj, childName) found = typeObj; break; } + if(typeObj.$ref) { + ref = splitNSName(typeObj.$ref); + if (ref.name === childName) { + found = typeObj; + break; + } + } } } else { var object = parameterTypeObj; if (object.$name === childName) { return object; } + if (object.$ref) { + ref = splitNSName(object.$ref); + if (ref.name === childName) { + return object; + } + } if (object.children) { for (i = 0, child; child = object.children[i]; i++) { diff --git a/test/wsdl-test.js b/test/wsdl-test.js index a01c0f662..75ac17146 100644 --- a/test/wsdl-test.js +++ b/test/wsdl-test.js @@ -73,6 +73,22 @@ wsdlStrictTests['should get the parent namespace when parent namespace is empty }); }; +wsdlStrictTests['should handle element ref'] = function(done) { + var expectedMsg = '' + + '' + + '001' + + ''; + soap.createClient(__dirname + '/wsdl/elementref/foo.wsdl', {strict: true}, function(err, client) { + assert.ok(!err); + client.fooOp({paymentRq: {bankSvcRq: {requestUID: '001'}}}, function(err, result) { + assert.equal(client.lastMessage, expectedMsg); + done(); + }); + }); +}; + module.exports = { 'WSDL Parser (strict)': wsdlStrictTests, 'WSDL Parser (non-strict)': wsdlNonStrictTests diff --git a/test/wsdl/elementref/bar.xsd b/test/wsdl/elementref/bar.xsd new file mode 100644 index 000000000..051f1e1a3 --- /dev/null +++ b/test/wsdl/elementref/bar.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/test/wsdl/elementref/bar1.xsd b/test/wsdl/elementref/bar1.xsd new file mode 100644 index 000000000..a8432d487 --- /dev/null +++ b/test/wsdl/elementref/bar1.xsd @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/wsdl/elementref/foo.wsdl b/test/wsdl/elementref/foo.wsdl new file mode 100755 index 000000000..0f81b1913 --- /dev/null +++ b/test/wsdl/elementref/foo.wsdl @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 44d061e3271f8b46d2ced7a4a99145eb6c3413b9 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Tue, 8 Sep 2015 14:16:53 -0700 Subject: [PATCH 14/48] Release v0.9.2 --- History.md | 59 +++++++++++++++++++++++++++++++--------------------- package.json | 2 +- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/History.md b/History.md index 3ad2118a2..f734e2491 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,14 @@ +0.9.2 / 2015-09-08 +================= +* [ENHANCEMENT] Add support for xsd element ref. (#700) +* [MAINTENANCE] Moving travis build to containers. +* [MAINTENANCE] Add request sample for an operation without any parameters. (#703) +* [DOC] update spelling and formatting to clarify several sections of Readme. (#708) +* [ENHANCEMENT] Add the correct namespace alias for operations without parameters by simply removing the special case where input.parts is empty. If special logic is wanted for this case, it should be contained in objectToRpcXML in any case. (#703) +* [FIX] Fix a typo in WSDL#findChildParameterObject. (#686) +* [FIX] Fixed SOAP Fault errors not being raised as errors. (#676) +* [FIX] Use diffrent namespace styles for soap fault 1.1 and 1.2. (#674) + 0.9.1 / 2015-05-30 ================= * [FIX] Received empty Strings are now returned as empty String rather than an empty Object. (#637) @@ -100,39 +111,39 @@ The `$xml` key is used to pass an `XML` Object to the request without adding a n 0.6.0 / 2014-10-29 ================= -* Enhancement: Adding bearer security type Exporting security type for usage. -* Enhancement: The qualified elementFormQualified must be respected only when the current element is not a global element. The namespace attribute is only needed if it's not included in the xmlns. -* Fix: Remove automatic port appending to "Host" header. -* Fix: Avoid creating soap:Header container when there are no children. -* Fix: Allowing a 'null' argument for WSDL methods that take no arguments. -* Fix: Wrong initialization of xmlns array when handling rpc stype wsdl. -* Fix: Fault handling. err should be used less frequently now. -* Fix: Added checking if there is input and output for operations under bindings section. -* Fix: XSD conflict with same namespace. +* [ENHANCEMENT] Adding bearer security type Exporting security type for usage. +* [ENHANCEMENT] The qualified elementFormQualified must be respected only when the current element is not a global element. The namespace attribute is only needed if it's not included in the xmlns. +* [FIX] Remove automatic port appending to "Host" header. +* [FIX] Avoid creating soap:Header container when there are no children. +* [FIX] Allowing a 'null' argument for WSDL methods that take no arguments. +* [FIX] Wrong initialization of xmlns array when handling rpc stype wsdl. +* [FIX] Fault handling. err should be used less frequently now. +* [FIX] Added checking if there is input and output for operations under bindings section. +* [FIX] XSD conflict with same namespace. 0.5.1 / 2014-07-11 ================= -* Enhancement: Add "defaults" parameter to BasicAuthSecurity's constructor -* Enhancement: Added possibility to set a custom `valueKey` for the parsed values from XML SOAP Response -* Fix: don't append port 80 to Host if not needed -* Fix: Remove possible existing BOM characters from XML String before passing it to `WSDL#_fromXML()` and parsing it. -* Fix: Handling nil attributes in response xml +* [ENHANCEMENT] Add "defaults" parameter to BasicAuthSecurity's constructor +* [ENHANCEMENT] Added possibility to set a custom `valueKey` for the parsed values from XML SOAP Response +* [FIX] don't append port 80 to Host if not needed +* [FIX] Remove possible existing BOM characters from XML String before passing it to `WSDL#_fromXML()` and parsing it. +* [FIX] Handling nil attributes in response xml 0.5.0 / 2014-07-11 ================= -* Enhancement: Allowing namespace prefixes to be ignored via config. -* Enhancement: wsdl should handle more types -* Fix: Handle defined messages ending with "Response", "Out", or "Output" -* Fix: Adding default attributesKey to server and allowing the property to be configurable fixing issue #406 -* Fix: Remove extra characters before and after soap envelope -* Fix: Allow operations to not have definitions -* Fix: Ignore unknown elements -* Fix: Keep ns from top-level -* Fix: Check status code of invocation response +* [ENHANCEMENT] Allowing namespace prefixes to be ignored via config. +* [ENHANCEMENT] wsdl should handle more types +* [FIX] Handle defined messages ending with "Response", "Out", or "Output" +* [FIX] Adding default attributesKey to server and allowing the property to be configurable fixing issue #406 +* [FIX] Remove extra characters before and after soap envelope +* [FIX] Allow operations to not have definitions +* [FIX] Ignore unknown elements +* [FIX] Keep ns from top-level +* [FIX] Check status code of invocation response 0.4.7 / 2014-06-16 ================= -* Allow request elements to have both content and attributes. +* [ENHANCEMENT] Allow request elements to have both content and attributes. 0.4.6 / 2014-06-16 ================= diff --git a/package.json b/package.json index 3914cc8c5..60f2e8983 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soap", - "version": "0.9.1", + "version": "0.9.2", "description": "A minimal node SOAP client", "engines": { "node": ">=0.8.0" From 0145d9f52c22f62c67fa1ee37778c60a7a76bfd1 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Tue, 8 Sep 2015 17:42:04 -0700 Subject: [PATCH 15/48] Turning off email notifications for travis. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 22141dee5..46b3b9bd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ sudo: false language: node_js +notifications: + email: false node_js: - 0.8 - 0.10 From f45035082e266a442abcc43eb9905a70969fe632 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Fri, 4 Sep 2015 17:52:02 -0700 Subject: [PATCH 16/48] Allowing namespace overriding for elements. --- Readme.md | 14 ++++- lib/wsdl.js | 53 +++++++++++++------ test/request-response-samples-test.js | 3 +- .../request.json | 3 ++ .../request.xml | 1 + .../response.json | 4 ++ .../response.xml | 1 + .../soap.wsdl | 51 ++++++++++++++++++ 8 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 test/request-response-samples/Message__overriding_namespace_prefix/request.json create mode 100644 test/request-response-samples/Message__overriding_namespace_prefix/request.xml create mode 100644 test/request-response-samples/Message__overriding_namespace_prefix/response.json create mode 100644 test/request-response-samples/Message__overriding_namespace_prefix/response.xml create mode 100644 test/request-response-samples/Message__overriding_namespace_prefix/soap.wsdl diff --git a/Readme.md b/Readme.md index e5903d85b..3a904594b 100644 --- a/Readme.md +++ b/Readme.md @@ -259,6 +259,16 @@ as default request options to the constructor: // result is a javascript object }) ``` +###Overriding the namespace prefix +`node-soap` is still working out some kinks regarding namespaces. If you find that an element is given the wrong namespace prefix in the request body, you can add the prefix to it's name in the containing object. I.E.: + +```javascript + client.MyService.MyPort.MyFunction({'ns1:name': 'value'}, function(err, result) { + // request body sent with ` usage. * - * NB: 'Element' (and subtypes) don't have any prototyped properties: there's - * no need to process a 'hasOwnProperties' call, we should just iterate over the + * NB: 'Element' (and subtypes) don't have any prototyped properties: there's + * no need to process a 'hasOwnProperties' call, we should just iterate over the * keys. */ function extend(base, obj) { @@ -569,12 +569,12 @@ MessageElement.prototype.postProcess = function(definitions) { } else { this.parts[part.$name] = part.$type; } - + if (typeof this.parts[part.$name] === 'object') { this.parts[part.$name].namespace = nsName.namespace; this.parts[part.$name].xmlns = ns; } - + this.children.splice(i--, 1); } } @@ -887,7 +887,7 @@ ElementElement.prototype.description = function(definitions, xmlns) { else { element[name] = elem; } - + if (typeof elem === 'object') { elem.targetNSAlias = type.namespace; elem.targetNamespace = ns; @@ -1303,7 +1303,7 @@ WSDL.prototype.xmlToObject = function(xml) { return; } - + if (topSchema && topSchema[name + '[]']) { if (!topObject[name]) topObject[name] = []; @@ -1323,12 +1323,12 @@ WSDL.prototype.xmlToObject = function(xml) { refs[cur.id].obj = obj; } }; - + p.oncdata = function (text) { text = trim(text); if (!text.length) return; - + if (/<\?xml[\s\S]+\?>/.test(text)) { var top = stack[stack.length - 1]; var value = self.xmlToObject(text); @@ -1463,7 +1463,20 @@ WSDL.prototype.objectToRpcXML = function(name, params, namespace, xmlns) { return parts.join(''); }; -WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, first, xmlnsAttr, parameterTypeObject, ancestorXmlns) { +/** + * Convert an object to XML. This is a recursive method as it calls itself. + * + * @param {Object} obj the object to convert. + * @param {String} name the name of the element (if the object being traversed is + * an element). + * @param {String} namespace the namespace prefix of the object I.E. xsd. + * @param {String} xmlns the full namespace of the object I.E. http://w3.org/schema. + * @param {Boolean} isFirst whether or not this is the first item being traversed. + * @param {?} xmlnsAttr + * @param {?} parameterTypeObject + * @param {?} ancestorXmlns + */ +WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, isFirst, xmlnsAttr, parameterTypeObject, ancestorXmlns) { var self = this; var schema = this.definitions.schemas[xmlns]; @@ -1479,7 +1492,7 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, first, xmlnsA var prefixNamespace = (namespace || qualified) && namespace !== 'xmlns'; var xmlnsAttrib = ''; - if (xmlns && first) { + if (xmlns && isFirst) { if (prefixNamespace && this.options.ignoredNamespaces.indexOf(namespace) === -1) { // resolve the prefix namespace @@ -1490,18 +1503,19 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, first, xmlnsA } var ancXmlns = ancestorXmlns ? ancestorXmlns : new Array(xmlns); - + // explicitly use xmlns attribute if available if (xmlnsAttr) { xmlnsAttrib = xmlnsAttr; } var ns = ''; - if (prefixNamespace && ((qualified || first) || soapHeader) && this.options.ignoredNamespaces.indexOf(namespace) === -1) { + if (prefixNamespace && ((qualified || isFirst) || soapHeader) && this.options.ignoredNamespaces.indexOf(namespace) === -1) { // prefix element ns = namespace.indexOf(":") === -1 ? namespace + ':' : namespace; } + // start building out XML string. if (Array.isArray(obj)) { for (var i = 0, item; item = obj[i]; i++) { var arrayAttr = self.processAttributes(item), @@ -1534,7 +1548,14 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, first, xmlnsA var value = ''; var nonSubNameSpace = ''; - if (first) { + + var nameWithNsRegex = /^([^:]+):([^:]+)$/.exec(name); + if (nameWithNsRegex) { + nonSubNameSpace = nameWithNsRegex[1] + ':'; + name = nameWithNsRegex[2]; + } + + if (isFirst) { value = self.objectToXML(child, name, namespace, xmlns, false, null, parameterTypeObject, ancXmlns); } else { @@ -1548,7 +1569,7 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, first, xmlnsA if(childParameterTypeObject.$baseNameSpace) { //this element has a base with another namespace (the correct one) ns = childParameterTypeObject.$baseNameSpace + ':'; } - + var childParameterType = childParameterTypeObject.$type || childParameterTypeObject.$ref; var childNamespace = ''; diff --git a/test/request-response-samples-test.js b/test/request-response-samples-test.js index d847f98ae..6ed07592d 100644 --- a/test/request-response-samples-test.js +++ b/test/request-response-samples-test.js @@ -23,7 +23,8 @@ var requestContext = { requestHandler:function(req, res){ var chunks = []; req.on('data', function(chunk){ - chunks.push(chunk); + // ignore eol on sample files. + chunks.push(chunk.toString().replace(/\r?\n$/m, '')); }); req.on('end', function(){ if(!requestContext.expectedRequest)return res.end(requestContext.responseToSend); diff --git a/test/request-response-samples/Message__overriding_namespace_prefix/request.json b/test/request-response-samples/Message__overriding_namespace_prefix/request.json new file mode 100644 index 000000000..138f385fd --- /dev/null +++ b/test/request-response-samples/Message__overriding_namespace_prefix/request.json @@ -0,0 +1,3 @@ +{ + "s1:bstrResourceId": "034b7ea5-8a04-11e3-9710-0050569575d8" +} diff --git a/test/request-response-samples/Message__overriding_namespace_prefix/request.xml b/test/request-response-samples/Message__overriding_namespace_prefix/request.xml new file mode 100644 index 000000000..e11e9cee0 --- /dev/null +++ b/test/request-response-samples/Message__overriding_namespace_prefix/request.xml @@ -0,0 +1 @@ +034b7ea5-8a04-11e3-9710-0050569575d8 \ No newline at end of file diff --git a/test/request-response-samples/Message__overriding_namespace_prefix/response.json b/test/request-response-samples/Message__overriding_namespace_prefix/response.json new file mode 100644 index 000000000..79fdba308 --- /dev/null +++ b/test/request-response-samples/Message__overriding_namespace_prefix/response.json @@ -0,0 +1,4 @@ +{ + "return":"-1", + "bstrError":"" +} diff --git a/test/request-response-samples/Message__overriding_namespace_prefix/response.xml b/test/request-response-samples/Message__overriding_namespace_prefix/response.xml new file mode 100644 index 000000000..17169f0b0 --- /dev/null +++ b/test/request-response-samples/Message__overriding_namespace_prefix/response.xml @@ -0,0 +1 @@ +-1 \ No newline at end of file diff --git a/test/request-response-samples/Message__overriding_namespace_prefix/soap.wsdl b/test/request-response-samples/Message__overriding_namespace_prefix/soap.wsdl new file mode 100644 index 000000000..85deeaa7e --- /dev/null +++ b/test/request-response-samples/Message__overriding_namespace_prefix/soap.wsdl @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 37d0831bc2e69535e54f33cc1cc1f362028c33ba Mon Sep 17 00:00:00 2001 From: jsdevel Date: Tue, 8 Sep 2015 19:40:42 -0700 Subject: [PATCH 17/48] Release v0.9.3 --- History.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index f734e2491..f8b9b183d 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +0.9.3 / 2015-09-08 +================= +* [ENHANCEMENT] Allow namespace overriding for elements. (#709) +* [MAINTENANCE] Disable travis emails. + 0.9.2 / 2015-09-08 ================= * [ENHANCEMENT] Add support for xsd element ref. (#700) diff --git a/package.json b/package.json index 60f2e8983..f5aabb302 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soap", - "version": "0.9.2", + "version": "0.9.3", "description": "A minimal node SOAP client", "engines": { "node": ">=0.8.0" From 0d6390bc19d7f0a9d5f5ab4f1d3006388e708942 Mon Sep 17 00:00:00 2001 From: Kevin Forest Date: Thu, 10 Sep 2015 14:04:10 -0400 Subject: [PATCH 18/48] Fix for wsdl retrieval using soap.createClient with special options.httpClient. Before this, the specified client was not used when fetching the wsdl file. This fix will force the wsdl to use the specified httpClient. --- lib/wsdl.js | 2 +- package.json | 5 +- test/client-customHttp-test.js | 111 +++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 test/client-customHttp-test.js diff --git a/lib/wsdl.js b/lib/wsdl.js index 7070859d8..554bbdc19 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1893,7 +1893,7 @@ function open_wsdl(uri, options, callback) { } else { debug('Reading url: %s', uri); - var httpClient = new HttpClient(options); + var httpClient = options.httpClient || new HttpClient(options); httpClient.request(uri, null /* options */, function(err, response, definition) { if (err) { callback(err); diff --git a/package.json b/package.json index f5aabb302..744154902 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "jshint": "2.3.0", "glob": "~3.2.8", "should": "~3.3.0", - "timekeeper": "~0.0.4" + "timekeeper": "~0.0.4", + "duplexer": "~0.1.1", + "readable-stream": "~2.0.2", + "semver": "~5.0.3" } } diff --git a/test/client-customHttp-test.js b/test/client-customHttp-test.js new file mode 100644 index 000000000..81df60e5d --- /dev/null +++ b/test/client-customHttp-test.js @@ -0,0 +1,111 @@ +'use strict'; + +var fs = require('fs'), + soap = require('..'), + http = require('http'), + assert = require('assert'), + duplexer = require('duplexer'), + req = require('request'), + httpClient = require('../lib/http.js'), + // stream = require('stream'), + stream = require('readable-stream'), + util = require('util'), + events = require('events'), + semver = require('semver'), + should = require('should'); + +it('should allow customization of httpClient and the wsdl file download should pass through it', function(done) { + +//Make a custom http agent to use streams instead on net socket + function CustomAgent(options, socket){ + var self = this; + events.EventEmitter.call(this); + self.requests = []; + self.maxSockets = 1; + self.proxyStream = socket; + self.options = options || {}; + self.proxyOptions = {}; + } + + util.inherits(CustomAgent, events.EventEmitter); + + CustomAgent.prototype.addRequest = function(req, options) { + req.onSocket(this.proxyStream); + }; + + //Create a duplex stream + + var httpReqStream = new stream.PassThrough(); + var httpResStream = new stream.PassThrough(); + var socketStream = duplexer(httpReqStream, httpResStream); + + + //Custom httpClient + function MyHttpClient (options, socket){ + httpClient.call(this,options); + this.agent = new CustomAgent(options, socket); + } + + util.inherits(MyHttpClient, httpClient); + + MyHttpClient.prototype.request = function(rurl, data, callback, exheaders, exoptions) { + var self = this; + var options = self.buildRequest(rurl, data, exheaders, exoptions); + //Specify agent to use + options.agent = this.agent; + 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); + }); + if (headers.Connection !== 'keep-alive') { + req.end(data); + } + return req; + }; + + var wsdl = fs.readFileSync('./test/wsdl/default_namespace.wsdl').toString('utf8'); + //Should be able to read from stream the request + httpReqStream.once('readable', function readRequest() { + var chunk = httpReqStream.read(); + should.exist(chunk); + + //This is for compatibility with old node releases <= 0.10 + //Hackish + if(semver.lt(process.version, '0.11.0')) + { + socketStream.on('data', function(data) { + socketStream.ondata(data,0,1984); + }); + } + //Now write the response with the wsdl + var state = httpResStream.write('HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\nContent-Length: 1904\r\n\r\n'+wsdl); + }); + + var httpCustomClient = new MyHttpClient({}, socketStream); + var url = 'http://localhost:50000/Platform.asmx?wsdl'; + soap.createClient(url, + {httpClient: httpCustomClient}, + function(err, client) { + assert.ok(client); + assert.ok(!err); + assert.equal(client.httpClient, httpCustomClient); + var description = (client.describe()); + assert.deepEqual(client.describe(), { + MyService: { + MyServicePort: { + MyOperation: { + input: { + }, + output: { + } + } + } + } + }); + done(); + }); +}); \ No newline at end of file From cecc28f5bd93069a31aa63efa4184e4c9e51824b Mon Sep 17 00:00:00 2001 From: Gangstead Date: Fri, 11 Sep 2015 18:46:17 -0500 Subject: [PATCH 19/48] add optional statusCode on soap fault --- Readme.md | 15 +++++++++++++++ lib/server.js | 13 +++++++++++-- test/server-test.js | 8 ++++++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index 3a904594b..3e32f761a 100644 --- a/Readme.md +++ b/Readme.md @@ -122,6 +122,21 @@ object with a `Fault` property. }; ``` +To change the HTTP statusCode of the response include it on the fault. The statusCode property will not be put on the xml message. + +``` javascript + throw { + Fault: { + Code: { + Value: "soap:Sender", + Subcode: { value: "rpc:BadArguments" } + }, + Reason: { Text: "Processing Error" }, + statusCode: 500 + } + }; +``` + ### SOAP Headers A service method can look at the SOAP headers by providing a 3rd arguments. diff --git a/lib/server.js b/lib/server.js index 517bdde89..dbd0ea6c4 100644 --- a/lib/server.js +++ b/lib/server.js @@ -120,7 +120,10 @@ Server.prototype._requestListener = function(req, res) { if (typeof self.log === 'function') { self.log("received", xml); } - self._process(xml, req.url, function(result) { + self._process(xml, req.url, function(result, statusCode) { + if(statusCode) { + res.statusCode = statusCode; + } res.write(result); res.end(); if (typeof self.log === 'function') { @@ -334,6 +337,12 @@ Server.prototype._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 @@ -347,7 +356,7 @@ Server.prototype._sendError = function(soapFault, callback, includeTimestamp) { fault = self.wsdl.objectToDocumentXML("Fault", soapFault, "soap"); } - return callback(self._envelope(fault, includeTimestamp)); + return callback(self._envelope(fault, includeTimestamp), statusCode); }; exports.Server = Server; diff --git a/test/server-test.js b/test/server-test.js index 2ef5933d6..2ea70a6d1 100644 --- a/test/server-test.js +++ b/test/server-test.js @@ -50,14 +50,16 @@ test.service = { Value: "soap:Sender", Subcode: { value: "rpc:BadArguments" } }, - Reason: { Text: "Processing Error" } + Reason: { Text: "Processing Error" }, + statusCode: 500 } }; var isValidPrice = function() { var price = args.price; - if(isNaN(price) || (price === ' ')) + if(isNaN(price) || (price === ' ')) { return cb(validationError); + } price = parseInt(price, 10); var validPrice = (price > 0 && price < Math.pow(10, 5)); @@ -185,6 +187,7 @@ describe('SOAP Server', function() { client.IsValidPrice({ price: "invalid_price"}, function(err, result) { assert.ok(err); assert.ok(err.root.Envelope.Body.Fault); + assert.equal(err.response.statusCode, 500); done(); }); }); @@ -279,6 +282,7 @@ describe('SOAP Server', function() { "Body should contain Code-element with namespace"); assert.ok(body.match(/.*<\/soap:Reason>/g), "Body should contain Reason-element with namespace"); + assert.equal(err.response.statusCode, 200); done(); }); }); From 335c3fa0a782c1f55b6617895a5a6e7547e413c6 Mon Sep 17 00:00:00 2001 From: laure Date: Tue, 15 Sep 2015 01:12:11 +0200 Subject: [PATCH 20/48] Update Readme Update Readme request reponse samples Update wsdl New Test ignore namespace --- Readme.md | 27 ++++++++ lib/wsdl.js | 14 +++- test/request-response-samples/README.md | 1 + .../Common.xsd | 45 +++++++++++++ .../Name.xsd | 64 +++++++++++++++++++ .../request.json | 44 +++++++++++++ .../request.xml | 1 + .../response.json | 17 +++++ .../response.xml | 1 + .../soap.wsdl | 43 +++++++++++++ .../wsdl_options.json | 3 + 11 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/Common.xsd create mode 100644 test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/Name.xsd create mode 100644 test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/request.json create mode 100644 test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/request.xml create mode 100644 test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/response.json create mode 100644 test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/response.xml create mode 100644 test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/soap.wsdl create mode 100644 test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/wsdl_options.json diff --git a/Readme.md b/Readme.md index 3e32f761a..545a5c09d 100644 --- a/Readme.md +++ b/Readme.md @@ -464,6 +464,33 @@ namespace prefix is used to identify this Element. This is not much of a problem ``` This would override the default `ignoredNamespaces` of the `WSDL` processor to `['namespaceToIgnore', 'someOtherNamespace']`. (This shouldn't be necessary, anyways). + If you want to override the default ignored namespaces you would simply pass the following `ignoredNamespaces` object within the `options`: + ``` + var options = { + ignoredNamespaces: { + namespaces: ['namespaceToIgnore', 'someOtherNamespace'], + override: true + } + } + ``` + This would override the default `ignoredNamespaces` of the `WSDL` processor to `['namespaceToIgnore', 'someOtherNamespace']`. (This shouldn't be necessary, anyways). + +## Handling "ignoreBaseNameSpaces" attribute +If an Element in a `schema` definition depends has a basenamespace defined but the request does not need that value, for example you have a "sentJob" with basenamespace "v20" +but the request need only: set in the tree structure, you need to set the ignoreBaseNameSpaces to true. This is set because in a lot of workaround the wsdl structure is not correctly +set or the webservice bring errors. + +By default the attribute is set to true. +An example to use: + +A simple `ignoredNamespaces` object, which only adds certain namespaces could look like this: +``` +var options = { +ignoredNamespaces: true +} +``` + + ## Contributors * Author: [Vinay Pulim](https://github.com/vpulim) diff --git a/lib/wsdl.js b/lib/wsdl.js index 7070859d8..a2ed3d3fe 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1075,6 +1075,8 @@ var WSDL = function(definition, uri, options) { WSDL.prototype.ignoredNamespaces = ['tns', 'targetNamespace', 'typedNamespace']; +WSDL.prototype.ignoreBaseNameSpaces = false; + WSDL.prototype.valueKey = '$value'; WSDL.prototype.xmlKey = '$xml'; @@ -1100,6 +1102,13 @@ WSDL.prototype._initializeOptions = function (options) { // Allow any request headers to keep passing through this.options.wsdl_headers = options.wsdl_headers; this.options.wsdl_options = options.wsdl_options; + + var ignoreBaseNameSpaces = options ? options.ignoreBaseNameSpaces : null; + if(ignoreBaseNameSpaces !== null && typeof ignoreBaseNameSpaces !== 'undefined' ) + this.options.ignoreBaseNameSpaces = ignoreBaseNameSpaces; + else + this.options.ignoreBaseNameSpaces = this.ignoreBaseNameSpaces; + }; WSDL.prototype.onReady = function(callback) { @@ -1566,7 +1575,10 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, isFirst, xmln if (childParameterTypeObject && ((childParameterTypeObject.$type && (childParameterTypeObject.$type.indexOf('xsd') === -1)) || childParameterTypeObject.$ref)) { - if(childParameterTypeObject.$baseNameSpace) { //this element has a base with another namespace (the correct one) + /*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. + */ + if(childParameterTypeObject.$baseNameSpace && !this.options.ignoreBaseNameSpaces) { ns = childParameterTypeObject.$baseNameSpace + ':'; } diff --git a/test/request-response-samples/README.md b/test/request-response-samples/README.md index 429125b95..74f03ce67 100644 --- a/test/request-response-samples/README.md +++ b/test/request-response-samples/README.md @@ -18,3 +18,4 @@ Follow this process to add samples: * `response.json` - This is the expected JSON from parsing the response XML * `error_response.json` - This is the expected JSON root attached to the error object when a fault occurs * `soap.wsdl` - This is the WSDL that defines the operation and messages + * `wsdl_options.wsdl` - This is the wsdl options to create request. (ignorenamespacers, ignoredbasednamespaces). diff --git a/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/Common.xsd b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/Common.xsd new file mode 100644 index 000000000..7c5b0cc6f --- /dev/null +++ b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/Common.xsd @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/Name.xsd b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/Name.xsd new file mode 100644 index 000000000..aad085bd0 --- /dev/null +++ b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/Name.xsd @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + Collection of NamePhone + + + + + + + + + + + + + + + + + Collection of NameAddress + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/request.json b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/request.json new file mode 100644 index 000000000..4e041341d --- /dev/null +++ b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/request.json @@ -0,0 +1,44 @@ +{ + "Profile": { + "IDs": { + "UniqueID": [ + { + "attributes": { + "source": "TESTSOURCE" + }, + "$value": 100 + } + ] + }, + "Addresses": { + "NameAddress": [ + { + "AddressLine": "Another Address" + }, + { + "AddressLine": "My Address" + } + ] + }, + "Phones": { + "NamePhone": [ + { + "attributes": { + "primary": true + }, + "PhoneData": { + "PhoneNumber": "123" + } + }, + { + "attributes": { + "primary": false + }, + "PhoneData": { + "PhoneNumber": "456" + } + } + ] + } + } +} diff --git a/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/request.xml b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/request.xml new file mode 100644 index 000000000..8ed251f36 --- /dev/null +++ b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/request.xml @@ -0,0 +1 @@ +100Another AddressMy Address123456 \ No newline at end of file diff --git a/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/response.json b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/response.json new file mode 100644 index 000000000..8e70dca20 --- /dev/null +++ b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/response.json @@ -0,0 +1,17 @@ +{ + "Result":{ + "attributes": { + "resultStatusFlag": "SUCCESS" + }, + "IDs": { + "UniqueID": [ + { + "attributes": { + "source": "TESTSOURCE" + }, + "$value": "100" + } + ] + } + } +} \ No newline at end of file diff --git a/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/response.xml b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/response.xml new file mode 100644 index 000000000..223079d90 --- /dev/null +++ b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/response.xml @@ -0,0 +1 @@ +100 \ No newline at end of file diff --git a/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/soap.wsdl b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/soap.wsdl new file mode 100644 index 000000000..2076cd354 --- /dev/null +++ b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/soap.wsdl @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/wsdl_options.json b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/wsdl_options.json new file mode 100644 index 000000000..4252c1564 --- /dev/null +++ b/test/request-response-samples/UpdateProfile__correct_namespaces_for_elements_with_base_ignorednamespaces/wsdl_options.json @@ -0,0 +1,3 @@ +{ + "ignoreBaseNameSpaces": true +} From 1ad81304e31bb9d57d917d6d4d6f811e88f27e3e Mon Sep 17 00:00:00 2001 From: Vinay Pulim Date: Wed, 23 Sep 2015 10:43:52 -0400 Subject: [PATCH 21/48] Update Readme.md --- Readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 545a5c09d..d8bd06bc9 100644 --- a/Readme.md +++ b/Readme.md @@ -494,7 +494,9 @@ ignoredNamespaces: true ## Contributors * Author: [Vinay Pulim](https://github.com/vpulim) - * Lead Maintainer: [Joe Spencer](https://github.com/jsdevel) + * Maintainers: + - [Joe Spencer](https://github.com/jsdevel) + - [Heinz Romirer](https://github.com/herom) * [All Contributors](https://github.com/vpulim/node-soap/graphs/contributors) [downloads-image]: http://img.shields.io/npm/dm/soap.svg From 42a40394d64d45a9fccbdc5c93b1c4cadb57a7da Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 24 Sep 2015 14:10:27 -0700 Subject: [PATCH 22/48] Resolve element references when other types are referenced --- lib/wsdl.js | 62 ++++++++++++++++-------------------- test/wsdl/elementref/bar.xsd | 2 ++ 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/lib/wsdl.js b/lib/wsdl.js index a37838379..a67fbcde9 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1724,7 +1724,7 @@ WSDL.prototype.findChildParameterObject = function(parameterTypeObj, childName) child, ref; - if(parameterTypeObj.$lookupTypes && Array.isArray(parameterTypeObj.$lookupTypes) && parameterTypeObj.$lookupTypes.length) { + if(Array.isArray(parameterTypeObj.$lookupTypes) && parameterTypeObj.$lookupTypes.length) { var types = parameterTypeObj.$lookupTypes; for(i = 0; i < types.length; i++) { @@ -1734,51 +1734,45 @@ WSDL.prototype.findChildParameterObject = function(parameterTypeObj, childName) found = typeObj; break; } - if(typeObj.$ref) { - ref = splitNSName(typeObj.$ref); - if (ref.name === childName) { - found = typeObj; - break; - } - } } - } else { - var object = parameterTypeObj; - if (object.$name === childName) { + } + + var object = parameterTypeObj; + if (object.$name === childName) { + return object; + } + if (object.$ref) { + ref = splitNSName(object.$ref); + if (ref.name === childName) { return object; } - if (object.$ref) { - ref = splitNSName(object.$ref); - if (ref.name === childName) { - return object; - } - } + } - if (object.children) { - for (i = 0, child; child = object.children[i]; i++) { - found = this.findChildParameterObject(child, childName); - if (found) { - break; - } + if (object.children) { + for (i = 0, child; child = object.children[i]; i++) { + found = this.findChildParameterObject(child, childName); + if (found) { + break; + } - if(child.$base) { - var childNameSpace = child.$base.substr(0, child.$base.indexOf(':')), - childXmlns = this.definitions.xmlns[childNameSpace]; + if (child.$base) { + var childNameSpace = child.$base.substr(0, child.$base.indexOf(':')), + childXmlns = this.definitions.xmlns[childNameSpace]; - var foundBase = this.findChildParameterObjectFromSchema(child.$base.substr(child.$base.indexOf(':') + 1), childXmlns); + var foundBase = this.findChildParameterObjectFromSchema(child.$base.substr(child.$base.indexOf(':') + 1), childXmlns); - if (foundBase) { - found = this.findChildParameterObject(foundBase, childName); + if (foundBase) { + found = this.findChildParameterObject(foundBase, childName); - if (found) { - found.$baseNameSpace = childNameSpace; - found.$type = childNameSpace + ':' + childName; - break; - } + if (found) { + found.$baseNameSpace = childNameSpace; + found.$type = childNameSpace + ':' + childName; + break; } } } } + } return found; diff --git a/test/wsdl/elementref/bar.xsd b/test/wsdl/elementref/bar.xsd index 051f1e1a3..f87405d4f 100644 --- a/test/wsdl/elementref/bar.xsd +++ b/test/wsdl/elementref/bar.xsd @@ -12,6 +12,7 @@ maxOccurs="unbounded"> + @@ -21,6 +22,7 @@ maxOccurs="unbounded"> + From 7a2723dec6b3dd0662aa8224d224af89125d7a90 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Mon, 28 Sep 2015 10:55:40 -0700 Subject: [PATCH 23/48] Increasing mocha test timeout to 10 seconds. * I believe garbage collection was causing some of the request response sample tests to take a long time. This happened randomly. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f5aabb302..e405c76b5 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "scripts": { "pretest": "jshint index.js lib test", - "test": "mocha test/*-test.js test/security/*.js" + "test": "mocha --timeout 10000 test/*-test.js test/security/*.js" }, "keywords": [ "soap" From 9475cf83117a5970438378a2f8e252f578c68f48 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Mon, 28 Sep 2015 09:30:26 -0700 Subject: [PATCH 24/48] Adding node v4.0 to .travis.yml. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 46b3b9bd3..42863e8b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ node_js: - 0.8 - 0.10 - 0.12 + - 4.0 - iojs before_install: - npm install -g npm@~1.4.6 From 0fe27063cb8dfeadee0428679ce59fb3ca9fd203 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Mon, 28 Sep 2015 09:50:26 -0700 Subject: [PATCH 25/48] Adding gitter badge in preparation of disabling issues. Here are the main reasons we're doing this: * We get lots of issues from users that are of the 'Hey debug this for me' type. * The amount of issues we get prevents us from adequately addressing PRs. * If we solely focused our efforts on PRs, we'd likely get more PRs in and advance the library faster. * Given the volume of issues we receive and the number of open issues we have open, users are less inclined to use in the fear that is is overly buggy. * We have so many open issues it's almost impossible to know which ones are still valid. * Github issues aren't the best mechanism for community support. Something like Google Groups or Gitter would best serve the interests of most issues we receive. --- Readme.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index d8bd06bc9..972908454 100644 --- a/Readme.md +++ b/Readme.md @@ -1,4 +1,5 @@ -# Soap [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] +# Soap [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Gitter chat][gitter-image]][gitter-url] + > A SOAP client and server for node.js. This module lets you connect to web services using SOAP. It also provides a server that allows you to run your own SOAP services. @@ -18,6 +19,15 @@ Install with [npm](http://github.com/isaacs/npm): ``` npm install soap ``` + +## Where can I file an issue? + +We've disabled issues in the repository and are now solely reviewing pull requests. The reasons why we disabled issues can be found here [#731](https://github.com/vpulim/node-soap/pull/731). + +If you're in need of support we encourage you to join us and other `node-soap` users on gitter: + +[![Gitter chat][gitter-image]][gitter-url] + ## Module ### soap.createClient(url[, options], callback) - create a new SOAP client from a WSDL url. Also supports a local filesystem path. @@ -505,3 +515,6 @@ ignoredNamespaces: true [travis-url]: https://travis-ci.org/vpulim/node-soap [travis-image]: http://img.shields.io/travis/vpulim/node-soap.svg + +[gitter-url]: https://gitter.im/vpulim/node-soap +[gitter-image]: https://badges.gitter.im/vpulim/node-soap.png From 262b6fa89e065528e1ec4d25cad2ed99f872da24 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Mon, 28 Sep 2015 15:54:10 -0700 Subject: [PATCH 26/48] Release v0.9.4 --- History.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index f8b9b183d..c721d575a 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,14 @@ +0.9.4 / 2015-09-28 +================= +[MAINTENANCE] Adding node v4.0 to .travis.yml. (#729) +[MAINTENANCE] Increasing mocha test timeout to 10 seconds. (#732) +[FIX] Resolve element references when other types are referenced. (#725) +[DOC] Update Readme.md +[ENHANCEMENT] New Ignorebasenamespaces option. (#716) +[ENHANCEMENT] Add optional statusCode on soap fault. (#715) +[FIX] Fix for wsdl retrieval using soap.createClient with special options.httpClient. Before this, the specified client was not used when fetching the wsdl file. This fix will force the wsdl to use the specified httpClient. (#714) +[FIX] Allow WSDL to be loaded from HTTPS sites. (#694) + 0.9.3 / 2015-09-08 ================= * [ENHANCEMENT] Allow namespace overriding for elements. (#709) diff --git a/package.json b/package.json index 9d54d5633..bfcfba33f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soap", - "version": "0.9.3", + "version": "0.9.4", "description": "A minimal node SOAP client", "engines": { "node": ">=0.8.0" From 505f50fe39b8273f5dabcbb52abc335fe671157e Mon Sep 17 00:00:00 2001 From: jsdevel Date: Mon, 28 Sep 2015 15:56:18 -0700 Subject: [PATCH 27/48] Formatting History.md 0.9.4 notes. --- History.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/History.md b/History.md index c721d575a..85af2a367 100644 --- a/History.md +++ b/History.md @@ -1,13 +1,13 @@ 0.9.4 / 2015-09-28 ================= -[MAINTENANCE] Adding node v4.0 to .travis.yml. (#729) -[MAINTENANCE] Increasing mocha test timeout to 10 seconds. (#732) -[FIX] Resolve element references when other types are referenced. (#725) -[DOC] Update Readme.md -[ENHANCEMENT] New Ignorebasenamespaces option. (#716) -[ENHANCEMENT] Add optional statusCode on soap fault. (#715) -[FIX] Fix for wsdl retrieval using soap.createClient with special options.httpClient. Before this, the specified client was not used when fetching the wsdl file. This fix will force the wsdl to use the specified httpClient. (#714) -[FIX] Allow WSDL to be loaded from HTTPS sites. (#694) +* [MAINTENANCE] Adding node v4.0 to .travis.yml. (#729) +* [MAINTENANCE] Increasing mocha test timeout to 10 seconds. (#732) +* [FIX] Resolve element references when other types are referenced. (#725) +* [DOC] Update Readme.md +* [ENHANCEMENT] New Ignorebasenamespaces option. (#716) +* [ENHANCEMENT] Add optional statusCode on soap fault. (#715) +* [FIX] Fix for wsdl retrieval using soap.createClient with special options.httpClient. Before this, the specified client was not used when fetching the wsdl file. This fix will force the wsdl to use the specified httpClient. (#714) +* [FIX] Allow WSDL to be loaded from HTTPS sites. (#694) 0.9.3 / 2015-09-08 ================= From b7132b19d9319c14be22bef20ef01742d4771c70 Mon Sep 17 00:00:00 2001 From: "Jason D. Harper" Date: Tue, 29 Sep 2015 11:31:04 -0500 Subject: [PATCH 28/48] Timestamp is now optional Adding test for basic security no timestamp Adding test basic security backward compatible check Adding test for basic security invalid options Fixing WSSecurity() function checks for backward compatibility and default values --- Readme.md | 6 ++- lib/security/WSSecurity.js | 35 ++++++++++---- test/request-response-samples-test.js | 2 +- .../request.json | 1 + .../request.xml | 1 + .../response.json | 1 + .../response.xml | 1 + .../security.json | 6 +++ .../soap.wsdl | 47 +++++++++++++++++++ .../request.json | 1 + .../request.xml | 1 + .../response.json | 1 + .../response.xml | 1 + .../security.json | 9 ++++ .../soap.wsdl | 47 +++++++++++++++++++ .../request.json | 1 + .../request.xml | 1 + .../response.json | 1 + .../response.xml | 1 + .../security.json | 8 ++++ .../soap.wsdl | 47 +++++++++++++++++++ 21 files changed, 207 insertions(+), 12 deletions(-) create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.xml create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/response.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/response.xml create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/security.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/soap.wsdl create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.xml create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/response.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/response.xml create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/security.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/soap.wsdl create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.xml create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/response.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/response.xml create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/security.json create mode 100644 test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/soap.wsdl diff --git a/Readme.md b/Readme.md index d8bd06bc9..37b88429c 100644 --- a/Readme.md +++ b/Readme.md @@ -324,8 +324,10 @@ errors). WSSecurity implements WS-Security. UsernameToken and PasswordText/PasswordDigest is supported. An instance of WSSecurity is passed to Client.setSecurity. ``` javascript - new WSSecurity(username, password, passwordType) - //'PasswordDigest' or 'PasswordText' default is PasswordText + new WSSecurity(username, password, options) + //the 'options' object is optional and contains properties: + //passwordType: 'PasswordDigest' or 'PasswordText' default is PasswordText + //hasTimeStamp: true or false default is true ``` ## Handling XML Attributes, Value and XML (wsdlOptions). diff --git a/lib/security/WSSecurity.js b/lib/security/WSSecurity.js index b1bca95b5..3a671cbcf 100644 --- a/lib/security/WSSecurity.js +++ b/lib/security/WSSecurity.js @@ -1,12 +1,25 @@ "use strict"; -var crypto = require('crypto') - , passwordDigest = require('../utils').passwordDigest; +var crypto = require('crypto'); +var passwordDigest = require('../utils').passwordDigest; +var validPasswordTypes = ['PasswordDigest', 'PasswordText']; -function WSSecurity(username, password, passwordType) { +function WSSecurity(username, password, options) { + options = options || {}; this._username = username; this._password = password; - this._passwordType = passwordType || 'PasswordText'; + //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'; + } 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; } WSSecurity.prototype.toXML = function() { @@ -24,7 +37,14 @@ WSSecurity.prototype.toXML = function() { } var now = new Date(); var created = getDate(now); - var expires = getDate(new Date(now.getTime() + (1000 * 600))); + var timeStampXml = ''; + if (this._hasTimeStamp) { + var expires = getDate( new Date(now.getTime() + (1000 * 600)) ); + timeStampXml = "" + + ""+created+"" + + ""+expires+"" + + ""; + } var password; if(this._passwordType === 'PasswordText') { @@ -39,10 +59,7 @@ WSSecurity.prototype.toXML = function() { } return "" + - "" + - "" + created + "" + - "" + expires + "" + - "" + + timeStampXml + "" + "" + this._username + "" + password + diff --git a/test/request-response-samples-test.js b/test/request-response-samples-test.js index 6ed07592d..a8fd0bc90 100644 --- a/test/request-response-samples-test.js +++ b/test/request-response-samples-test.js @@ -105,7 +105,7 @@ function generateTest(name, methodName, wsdlPath, headerJSON, securityJSON, requ } } if (securityJSON && securityJSON.type === 'ws') { - client.setSecurity(new WSSecurity(securityJSON.username, securityJSON.password)); + client.setSecurity(new WSSecurity(securityJSON.username, securityJSON.password, securityJSON.options)); } client[methodName](requestJSON, function(err, json, body, soapHeader){ if(requestJSON){ diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.json b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.json new file mode 100644 index 000000000..6f31cf5a2 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.json @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.xml new file mode 100644 index 000000000..8a802f488 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.xml @@ -0,0 +1 @@ +2014-10-12T01:02:03Z2014-10-12T01:12:03Zbasicuserbasicpass2014-10-12T01:02:03Z \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/response.json b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/response.json new file mode 100644 index 000000000..6f31cf5a2 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/response.json @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/response.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/response.xml new file mode 100644 index 000000000..43591321a --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/response.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/security.json b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/security.json new file mode 100644 index 000000000..48ca3a285 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/security.json @@ -0,0 +1,6 @@ +{ + "type": "ws", + "username": "basicuser", + "password": "basicpass", + "options": "PasswordText" +} diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/soap.wsdl b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/soap.wsdl new file mode 100644 index 000000000..809710161 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/soap.wsdl @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.json b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.json new file mode 100644 index 000000000..6f31cf5a2 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.json @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.xml new file mode 100644 index 000000000..8a802f488 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.xml @@ -0,0 +1 @@ +2014-10-12T01:02:03Z2014-10-12T01:12:03Zbasicuserbasicpass2014-10-12T01:02:03Z \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/response.json b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/response.json new file mode 100644 index 000000000..6f31cf5a2 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/response.json @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/response.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/response.xml new file mode 100644 index 000000000..43591321a --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/response.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/security.json b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/security.json new file mode 100644 index 000000000..e4cd00427 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/security.json @@ -0,0 +1,9 @@ +{ + "type": "ws", + "username": "basicuser", + "password": "basicpass", + "options": { + "passwordType": "invalid", + "hasTimeStamp": "" + } +} diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/soap.wsdl b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/soap.wsdl new file mode 100644 index 000000000..809710161 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/soap.wsdl @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.json b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.json new file mode 100644 index 000000000..6f31cf5a2 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.json @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.xml new file mode 100644 index 000000000..550fc3ece --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.xml @@ -0,0 +1 @@ +basicuserbasicpass2014-10-12T01:02:03Z \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/response.json b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/response.json new file mode 100644 index 000000000..6f31cf5a2 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/response.json @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/response.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/response.xml new file mode 100644 index 000000000..43591321a --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/response.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/security.json b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/security.json new file mode 100644 index 000000000..2d7e2cc40 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/security.json @@ -0,0 +1,8 @@ +{ + "type": "ws", + "username": "basicuser", + "password": "basicpass", + "options": { + "hasTimeStamp": false + } +} diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/soap.wsdl b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/soap.wsdl new file mode 100644 index 000000000..809710161 --- /dev/null +++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/soap.wsdl @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From c3625a71910a9ce5e2e5dcc4581cc3539477ce02 Mon Sep 17 00:00:00 2001 From: Fabian Cook Date: Tue, 13 Oct 2015 17:12:22 +1300 Subject: [PATCH 29/48] Fixes 743, Allow circular XSD files to be loaded --- lib/wsdl.js | 44 +++++++++++++++++++++++++++++- test/wsdl-parse-test.js | 7 +++++ test/wsdl/recursive/A.xsd | 25 ++++++++++++++++++ test/wsdl/recursive/B.xsd | 25 ++++++++++++++++++ test/wsdl/recursive/file.wsdl | 50 +++++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 test/wsdl/recursive/A.xsd create mode 100644 test/wsdl/recursive/B.xsd create mode 100644 test/wsdl/recursive/file.wsdl diff --git a/lib/wsdl.js b/lib/wsdl.js index a67fbcde9..6752869f6 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1130,7 +1130,7 @@ WSDL.prototype._processNextInclude = function(includes, callback) { includePath = url.resolve(self.uri, include.location); } - open_wsdl(includePath, this.options, function(err, wsdl) { + open_wsdl_recursive(includePath, this.options, function(err, wsdl) { if (err) { return callback(err); } @@ -1875,7 +1875,47 @@ WSDL.prototype._xmlnsMap = function() { return str; }; +var WSDL_CACHE = { }; + +/* + Have another function to load previous WSDLs as we + don't want this to be invoked externally (expect for tests) + This will attempt to fix circular dependencies with XSD files, + Given + + file.wsdl + xs:import namespace="A" schemaLocation: A.xsd + A.xsd + xs:import namespace="B" schemaLocation: B.xsd + B.xsd + xs:import namespace="A" schemaLocation: A.xsd + + file.wsdl will start loading, import A, then A will import B, which will then import A + Because A has already started to load previously it will be returned right away and + have an internal circular reference + + B would then complete loading, then A, then file.wsdl + + By the time file A starts processing its includes its definitions will be already loaded, + this is the only thing that B will depend on when "opening" A +*/ +function open_wsdl_recursive(uri, options, callback) { + var fromCache; + + if (typeof options === 'function') { + callback = options; + options = {}; + } + + if (fromCache = WSDL_CACHE[ uri ]) { + return callback.call(fromCache, null, fromCache); + } + + return open_wsdl(uri, options, callback); +} + function open_wsdl(uri, options, callback) { + if (typeof options === 'function') { callback = options; options = {}; @@ -1893,6 +1933,7 @@ function open_wsdl(uri, options, callback) { } else { wsdl = new WSDL(definition, uri, options); + WSDL_CACHE[ uri ] = wsdl; wsdl.onReady(callback); } }); @@ -1905,6 +1946,7 @@ function open_wsdl(uri, options, callback) { callback(err); } else if (response && response.statusCode === 200) { wsdl = new WSDL(definition, uri, options); + WSDL_CACHE[ uri ] = wsdl; wsdl.onReady(callback); } else { callback(new Error('Invalid WSDL URL: ' + uri + "\n\n\r Code: " + response.statusCode + "\n\n\r Response Body: " + response.body)); diff --git a/test/wsdl-parse-test.js b/test/wsdl-parse-test.js index 1178a0079..eeb90080d 100644 --- a/test/wsdl-parse-test.js +++ b/test/wsdl-parse-test.js @@ -14,4 +14,11 @@ describe(__filename, function () { done(); }); }); + + it('should parse recursive wsdls', function (done) { + open_wsdl(path.resolve(__dirname, 'wsdl/recursive/file.wsdl'), function (err, def) { + // If we get here then we succeeded + done( err ); + }); + }); }); diff --git a/test/wsdl/recursive/A.xsd b/test/wsdl/recursive/A.xsd new file mode 100644 index 000000000..fac598a6e --- /dev/null +++ b/test/wsdl/recursive/A.xsd @@ -0,0 +1,25 @@ + + + + + + Defines the method of contact to reach a party (in + their specified role) + + + + + + Indicates that this contact medium is the + preferred (if multiple are present). Lower integer takes + precedence, "equals are equals" so the value does not need to be + unique. + + + + + + + \ No newline at end of file diff --git a/test/wsdl/recursive/B.xsd b/test/wsdl/recursive/B.xsd new file mode 100644 index 000000000..97ef0a23c --- /dev/null +++ b/test/wsdl/recursive/B.xsd @@ -0,0 +1,25 @@ + + + + + + + Defines the method of contact to reach a party (in + their specified role) + + + + + + Indicates that this contact medium is the + preferred (if multiple are present). Lower integer takes + precedence, "equals are equals" so the value does not need to be + unique. + + + + + + + \ No newline at end of file diff --git a/test/wsdl/recursive/file.wsdl b/test/wsdl/recursive/file.wsdl new file mode 100644 index 000000000..554f6d81a --- /dev/null +++ b/test/wsdl/recursive/file.wsdl @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 8cb398b7740646ce36e9d3699292c8f55aa5bd67 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Thu, 15 Oct 2015 11:08:05 -0700 Subject: [PATCH 30/48] Release v0.9.5 --- History.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 85af2a367..39332bbc7 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,9 @@ +0.9.5 / 2015-10-15 +================= +* [FIX] Allow circular XSD files to be loaded. (#745) +* [ENHANCEMENT] Timestamp is now optional. (#735) +* [DOC] Formatting History.md 0.9.4 notes. + 0.9.4 / 2015-09-28 ================= * [MAINTENANCE] Adding node v4.0 to .travis.yml. (#729) diff --git a/package.json b/package.json index bfcfba33f..e530c9014 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soap", - "version": "0.9.4", + "version": "0.9.5", "description": "A minimal node SOAP client", "engines": { "node": ">=0.8.0" From d0bf6ff175129a4f1163464716cbdaf927cbd804 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 20 Oct 2015 14:41:44 -0700 Subject: [PATCH 31/48] Fix xml namespace/element/type handling --- lib/client.js | 9 +- lib/nscontext.js | 223 ++++++++ lib/server.js | 11 +- lib/utils.js | 21 + lib/wsdl.js | 498 +++++++++++------- .../request.json | 2 +- .../request.xml | 2 +- .../request.json | 4 +- .../request.xml | 2 +- .../request.json | 2 +- .../request.xml | 2 +- .../request.json | 4 +- .../request.xml | 2 +- .../request.json | 4 +- .../request.xml | 2 +- .../Dummy__should_handle_nil/request.json | 4 +- .../Dummy__should_handle_nil/request.xml | 2 +- .../request.json | 4 +- .../request.xml | 2 +- .../request.json | 4 +- .../request.xml | 2 +- .../Dummy__should_parse_nil/request.json | 2 +- .../Dummy__should_parse_nil/request.xml | 2 +- .../request.json | 4 +- .../request.xml | 2 +- .../request.json | 4 +- .../request.xml | 2 +- .../Common.xsd | 0 .../Name.xsd | 0 .../request.json | 0 .../request.xml | 0 .../response.json | 0 .../response.xml | 0 .../soap.wsdl | 0 .../request.xml | 2 +- .../request.xml | 2 +- .../request.json | 2 +- .../request.xml | 2 +- .../request.xml | 2 +- test/wsdl-test.js | 14 +- test/wsdl/typeref/ns1.xsd | 60 +++ test/wsdl/typeref/ns2.xsd | 16 + test/wsdl/typeref/order.wsdl | 52 ++ test/wsdl/typeref/request.json | 15 + test/wsdl/typeref/request.xml.js | 6 + 45 files changed, 742 insertions(+), 253 deletions(-) create mode 100644 lib/nscontext.js rename test/request-response-samples/{UpdateProfile__correct_nanespaces_in_sequence_with_imports => UpdateProfile__correct_namespaces_in_sequence_with_imports}/Common.xsd (100%) rename test/request-response-samples/{UpdateProfile__correct_nanespaces_in_sequence_with_imports => UpdateProfile__correct_namespaces_in_sequence_with_imports}/Name.xsd (100%) rename test/request-response-samples/{UpdateProfile__correct_nanespaces_in_sequence_with_imports => UpdateProfile__correct_namespaces_in_sequence_with_imports}/request.json (100%) rename test/request-response-samples/{UpdateProfile__correct_nanespaces_in_sequence_with_imports => UpdateProfile__correct_namespaces_in_sequence_with_imports}/request.xml (100%) rename test/request-response-samples/{UpdateProfile__correct_nanespaces_in_sequence_with_imports => UpdateProfile__correct_namespaces_in_sequence_with_imports}/response.json (100%) rename test/request-response-samples/{UpdateProfile__correct_nanespaces_in_sequence_with_imports => UpdateProfile__correct_namespaces_in_sequence_with_imports}/response.xml (100%) rename test/request-response-samples/{UpdateProfile__correct_nanespaces_in_sequence_with_imports => UpdateProfile__correct_namespaces_in_sequence_with_imports}/soap.wsdl (100%) create mode 100644 test/wsdl/typeref/ns1.xsd create mode 100644 test/wsdl/typeref/ns2.xsd create mode 100755 test/wsdl/typeref/order.wsdl create mode 100644 test/wsdl/typeref/request.json create mode 100755 test/wsdl/typeref/request.xml.js diff --git a/lib/client.js b/lib/client.js index 51215a85b..03a249116 100644 --- a/lib/client.js +++ b/lib/client.js @@ -5,18 +5,13 @@ "use strict"; -function findKey(obj, val) { - for (var n in obj) - if (obj[n] === val) - return n; -} var HttpClient = require('./http'), assert = require('assert'), events = require('events'), util = require('util'), debug = require('debug')('node-soap'), - url = require('url'), + findPrefix = require('./utils').findPrefix, _ = require('lodash'); var Client = function(wsdl, endpoint, options) { @@ -148,7 +143,7 @@ Client.prototype._invoke = function(method, args, location, callback, options, e xml = null, req = null, soapAction, - alias = findKey(defs.xmlns, ns), + alias = findPrefix(defs.xmlns, ns), headers = { 'Content-Type': "text/xml; charset=utf-8" }; diff --git a/lib/nscontext.js b/lib/nscontext.js new file mode 100644 index 000000000..c58171f12 --- /dev/null +++ b/lib/nscontext.js @@ -0,0 +1,223 @@ +'use strict'; + +module.exports = NamespaceContext; + +/** + * Scope for XML namespaces + * @param {NamespaceScope} [parent] Parent scope + * @returns {NamespaceScope} + * @constructor + */ +function NamespaceScope(parent) { + if (!(this instanceof NamespaceScope)) { + return new NamespaceScope(parent); + } + this.parent = parent; + this.namespaces = {}; +} + +/** + * Namespace context that manages hierarchical scopes + * @returns {NamespaceContext} + * @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 + * @returns {String} Namespace URI + */ +NamespaceScope.prototype.getNamespaceURI = function(prefix, localOnly) { + switch (prefix) { + case 'xml': + return 'http://www.w3.org/XML/1998/namespace'; + case 'xmlns': + 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) { + switch (prefix) { + case 'xml': + return { + uri: 'http://www.w3.org/XML/1998/namespace', + prefix: 'xml', + declared: true + }; + case 'xmlns': + return { + uri: 'http://www.w3.org/2000/xmlns/', + prefix: 'xmlns', + declared: true + }; + default: + var mapping = this.namespaces[prefix]; + /*jshint -W116 */ + if (mapping != null) { + return mapping; + } else if (this.parent) { + return this.parent.getNamespaceMapping(prefix); + } else { + return null; + } + } +}; + +/** + * Look up the namespace prefix by URI + * @param {String} nsUri Namespace URI + * @param {Boolean} [localOnly] Search current scope only + * @returns {String} Namespace prefix + */ +NamespaceScope.prototype.getPrefix = function(nsUri, localOnly) { + switch (nsUri) { + case 'http://www.w3.org/XML/1998/namespace': + return 'xml'; + case 'http://www.w3.org/2000/xmlns/': + return 'xmlns'; + 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; + } + } +}; + +/** + * Add a prefix/URI namespace mapping + * @param {String} prefix Namespace prefix + * @param {String} nsUri Namespace URI + * @param {Boolean} [localOnly] Search current scope only + * @returns {boolean} true if the mapping is added or false if the mapping + * already exists + */ +NamespaceContext.prototype.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; +}; + +/** + * Push a scope into the context + * @returns {NamespaceScope} The current scope + */ +NamespaceContext.prototype.pushContext = function() { + var scope = new NamespaceScope(this.currentScope); + this.scopes.push(scope); + this.currentScope = scope; + return scope; +}; + +/** + * Pop a scope out of the context + * @returns {NamespaceScope} The removed scope + */ +NamespaceContext.prototype.popContext = function() { + var scope = this.scopes.pop(); + if (scope) { + this.currentScope = scope.parent; + } else { + this.currentScope = null; + } + return scope; +}; + +/** + * Look up the namespace URI by prefix + * @param {String} prefix Namespace prefix + * @param {Boolean} [localOnly] Search current scope only + * @returns {String} Namespace URI + */ +NamespaceContext.prototype.getNamespaceURI = function(prefix, localOnly) { + return this.currentScope && this.currentScope.getNamespaceURI(prefix, localOnly); +}; + +/** + * Look up the namespace prefix by URI + * @param {String} nsURI Namespace URI + * @param {Boolean} [localOnly] Search current scope only + * @returns {String} Namespace prefix + */ +NamespaceContext.prototype.getPrefix = function(nsUri, localOnly) { + return this.currentScope && this.currentScope.getPrefix(nsUri, localOnly); +}; + +/** + * Register a namespace + * @param {String} nsUri Namespace URI + * @returns {String} The matching or generated namespace prefix + */ +NamespaceContext.prototype.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; +}; + +/** + * Declare a namespace prefix/uri mapping + * @param {String} prefix Namespace prefix + * @param {String} nsUri Namespace URI + * @returns {Boolean} true if the declaration is created + */ +NamespaceContext.prototype.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; +}; diff --git a/lib/server.js b/lib/server.js index dbd0ea6c4..8784f0553 100644 --- a/lib/server.js +++ b/lib/server.js @@ -5,12 +5,6 @@ "use strict"; -function findKey(obj, val) { - for (var n in obj) - if (obj[n] === val) - return n; -} - function getDateString(d) { function pad(n) { return n < 10 ? '0' + n : n; @@ -26,7 +20,8 @@ function getDateString(d) { var url = require('url'), compress = null, events = require('events'), - util = require('util'); + util = require('util'), + findPrefix = require('./utils').findPrefix; try { compress = require("compress"); @@ -305,7 +300,7 @@ Server.prototype._envelope = function(body, includeTimestamp) { var defs = this.wsdl.definitions, ns = defs.$targetNamespace, encoding = '', - alias = findKey(defs.xmlns, ns); + alias = findPrefix(defs.xmlns, ns); var xml = "" + "'].join('')); + 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 (key !== nsAttrName) { var value = params[key]; parts.push(['<', key, '>'].join('')); - parts.push((typeof value === 'object') ? this.objectToXML(value, key, namespace, xmlns) : xmlEscape(value)); + parts.push((typeof value === 'object') ? this.objectToXML(value, key, nsPrefix, nsURI) : xmlEscape(value)); parts.push([''].join('')); } } - parts.push([''].join('')); + parts.push([''].join('')); return parts.join(''); }; @@ -1478,40 +1508,45 @@ WSDL.prototype.objectToRpcXML = function(name, params, namespace, xmlns) { * @param {Object} obj the object to convert. * @param {String} name the name of the element (if the object being traversed is * an element). - * @param {String} namespace the namespace prefix of the object I.E. xsd. - * @param {String} xmlns the full namespace of the object I.E. http://w3.org/schema. + * @param {String} nsPrefix the namespace prefix of the object I.E. xsd. + * @param {String} nsURI the full namespace of the object I.E. http://w3.org/schema. * @param {Boolean} isFirst whether or not this is the first item being traversed. * @param {?} xmlnsAttr * @param {?} parameterTypeObject - * @param {?} ancestorXmlns + * @param {NamespaceContext} nsContext Namespace context */ -WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, isFirst, xmlnsAttr, parameterTypeObject, ancestorXmlns) { +WSDL.prototype.objectToXML = function(obj, name, nsPrefix, nsURI, isFirst, xmlnsAttr, schemaObject, nsContext) { var self = this; - var schema = this.definitions.schemas[xmlns]; + var schema = this.definitions.schemas[nsURI]; - var parentNamespace = namespace ? namespace.parent : undefined; - if(typeof parentNamespace !== 'undefined') { - //we got the parentNamespace for our array. setting the namespace-variable back to the current namespace string - namespace = namespace.current; + 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; } var soapHeader = !schema; var qualified = schema && schema.$elementFormDefault === 'qualified'; var parts = []; - var prefixNamespace = (namespace || qualified) && namespace !== 'xmlns'; + var prefixNamespace = (nsPrefix || qualified) && nsPrefix !== TNS_PREFIX; var xmlnsAttrib = ''; - if (xmlns && isFirst) { + if (nsURI && isFirst) { - if (prefixNamespace && this.options.ignoredNamespaces.indexOf(namespace) === -1) { + if (prefixNamespace && this.options.ignoredNamespaces.indexOf(nsPrefix) === -1) { // resolve the prefix namespace - xmlnsAttrib += ' xmlns:' + namespace + '="' + xmlns + '"'; + xmlnsAttrib += ' xmlns:' + nsPrefix + '="' + nsURI + '"'; } // only add default namespace if the schema elementFormDefault is qualified - if (qualified || soapHeader) xmlnsAttrib += ' xmlns="' + xmlns + '"'; + if (qualified || soapHeader) xmlnsAttrib += ' xmlns="' + nsURI + '"'; } - var ancXmlns = ancestorXmlns ? ancestorXmlns : new Array(xmlns); + if (!nsContext) { + nsContext = new NamespaceContext(); + nsContext.declareNamespace(nsPrefix, nsURI); + } else { + nsContext.pushContext(); + } // explicitly use xmlns attribute if available if (xmlnsAttr) { @@ -1519,20 +1554,22 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, isFirst, xmln } var ns = ''; - if (prefixNamespace && ((qualified || isFirst) || soapHeader) && this.options.ignoredNamespaces.indexOf(namespace) === -1) { + if (prefixNamespace && ((qualified || isFirst) || soapHeader) && this.options.ignoredNamespaces.indexOf(nsPrefix) === -1) { // prefix element - ns = namespace.indexOf(":") === -1 ? namespace + ':' : namespace; + ns = nsPrefix.indexOf(":") === -1 ? nsPrefix + ':' : nsPrefix; } + var i, n; // start building out XML string. if (Array.isArray(obj)) { - for (var i = 0, item; item = obj[i]; i++) { - var arrayAttr = self.processAttributes(item), - correctOuterNamespace = parentNamespace || ns; //using the parent namespace if given + 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(['<', correctOuterNamespace, name, arrayAttr, xmlnsAttrib, '>'].join('')); - parts.push(self.objectToXML(item, name, namespace, xmlns, false, null, parameterTypeObject, ancXmlns)); - parts.push([''].join('')); + parts.push(['<', correctOuterNsPrefix, name, arrayAttr, xmlnsAttrib, '>'].join('')); + parts.push(self.objectToXML(item, name, nsPrefix, nsURI, false, null, schemaObject, nsContext)); + parts.push([''].join('')); } } else if (typeof obj === 'object') { for (name in obj) { @@ -1553,7 +1590,7 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, isFirst, xmln if (typeof child === 'undefined') continue; - var attr = self.processAttributes(child); + var attr = self.processAttributes(child, nsContext); var value = ''; var nonSubNameSpace = ''; @@ -1565,90 +1602,130 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, isFirst, xmln } if (isFirst) { - value = self.objectToXML(child, name, namespace, xmlns, false, null, parameterTypeObject, ancXmlns); + value = self.objectToXML(child, name, nsPrefix, nsURI, false, null, schemaObject, nsContext); } else { if (self.definitions.schemas) { if (schema) { - var childParameterTypeObject = self.findChildParameterObject(parameterTypeObject, name); + var childSchemaObject = self.findChildSchemaObject(schemaObject, name); //find sub namespace if not a primitive - if (childParameterTypeObject && - ((childParameterTypeObject.$type && (childParameterTypeObject.$type.indexOf('xsd') === -1)) || - childParameterTypeObject.$ref)) { + 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. */ - if(childParameterTypeObject.$baseNameSpace && !this.options.ignoreBaseNameSpaces) { - ns = childParameterTypeObject.$baseNameSpace + ':'; - } - - var childParameterType = childParameterTypeObject.$type || childParameterTypeObject.$ref; - var childNamespace = ''; + var childNsPrefix = ''; var childName = ''; - if (childParameterType.indexOf(':') !== -1) { - childNamespace = childParameterType.substring(0, childParameterType.indexOf(':')); - childName = childParameterType.substring(childParameterType.indexOf(':') + 1); - } - var childXmlns = schema.xmlns[childNamespace] || self.definitions.xmlns[childNamespace]; + var childNsURI; var childXmlnsAttrib = ''; - if ((ancXmlns.indexOf(childXmlns) === -1) && (childXmlns)) { - childXmlnsAttrib = ' xmlns:' + childNamespace + '="' + childXmlns+ '"'; - if ((childXmlns)) { - ancXmlns.push(childXmlns); + + 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); + for (i = 0, n = this.ignoredNamespaces.length; i < n; i++) { + if (this.ignoredNamespaces[i] === childNsPrefix) { + childNsPrefix = nsPrefix; + break; + } + } + } else { + childNsPrefix = elementQName.prefix; + for (i = 0, n = this.ignoredNamespaces.length; i < n; i++) { + if (this.ignoredNamespaces[i] === childNsPrefix) { + childNsPrefix = nsPrefix; + break; + } + } + childNsURI = schema.xmlns[childNsPrefix] || self.definitions.xmlns[childNsPrefix]; + } + + var unqualified = false; + // Check qualification form for local elements + if (childSchemaObject.$name && childSchemaObject.targetNamespace === undefined) { + if (childSchemaObject.$form === 'unqualified') { + unqualified = true; + } else if (childSchemaObject.$form === 'qualified') { + unqualified = false; + } else { + unqualified = schema.$elementFormDefault === 'unqualified'; + } + } + if (unqualified) { + childNsPrefix = ''; + } + + if (childNsURI && childNsPrefix) { + if (nsContext.declareNamespace(childNsPrefix, childNsURI)) { + childXmlnsAttrib = ' xmlns:' + childNsPrefix + '="' + childNsURI + '"'; + xmlnsAttrib += childXmlnsAttrib; + } } - } - // There is matching element ref - if (childParameterTypeObject.$ref) { - ns = childNamespace ? childNamespace + ':' : ''; - xmlnsAttrib = childXmlnsAttrib; } - var completeChildParameterTypeObject; - if (childParameterTypeObject.$type) { - completeChildParameterTypeObject = - self.findChildParameterObjectFromSchema(childName, childXmlns) || - childParameterTypeObject; + var resolvedChildSchemaObject; + if (childSchemaObject.$type) { + var typeQName = splitQName(childSchemaObject.$type); + var typePrefix = typeQName.prefix; + var typeURI = schema.xmlns[typePrefix] || self.definitions.xmlns[typePrefix]; + childNsURI = typeURI; + if (typeURI !== 'http://www.w3.org/2001/XMLSchema') { + // Add the prefix/namespace mapping, but not declare it + nsContext.addNamespace(typeQName.prefix, typeURI); + } + resolvedChildSchemaObject = + self.findSchemaType(typeQName.name, typeURI) || childSchemaObject; } else { - completeChildParameterTypeObject = - self.findParameterObject(childXmlns, childName) || - childParameterTypeObject; + resolvedChildSchemaObject = + self.findSchemaObject(childNsURI, childName) || childSchemaObject; } - for(var ignoredNamespacesIndex = 0, ignoredNamespacesLength = this.ignoredNamespaces.length; ignoredNamespacesIndex < ignoredNamespacesLength; ignoredNamespacesIndex++) { - if(this.ignoredNamespaces[ignoredNamespacesIndex] === childNamespace) { - childNamespace = namespace; - - break; - } + if (childSchemaObject.$baseNameSpace && this.options.ignoreBaseNameSpaces) { + childNsPrefix = nsPrefix; + childNsURI = nsURI; } + ns = childNsPrefix ? childNsPrefix + ':' : ''; + if(Array.isArray(child)) { //for arrays, we need to remember the current namespace - childNamespace = { - current: childNamespace, + childNsPrefix = { + current: childNsPrefix, parent: ns }; } - value = self.objectToXML(child, name, childNamespace, childXmlns, false, childXmlnsAttrib, completeChildParameterTypeObject, ancXmlns); - } else if (obj[self.options.attributesKey] && obj[self.options.attributesKey].xsi_type) { //if parent object has complex type defined and child not found in parent - var completeChildParamTypeObject = self.findChildParameterObjectFromSchema(obj[self.options.attributesKey].xsi_type.type, obj[self.options.attributesKey].xsi_type.xmlns); - - nonSubNameSpace = obj[self.options.attributesKey].xsi_type.namespace + ':'; - ancXmlns.push(obj[self.options.attributesKey].xsi_type.xmlns); - value = self.objectToXML(child, name, obj[self.options.attributesKey].xsi_type.namespace, obj[self.options.attributesKey].xsi_type.xmlns, false, null, null, ancXmlns); + value = self.objectToXML(child, name, childNsPrefix, childNsURI, + false, null, resolvedChildSchemaObject, nsContext); + } else if (obj[self.options.attributesKey] && obj[self.options.attributesKey].xsi_type) { + //if parent object has complex type defined and child not found in parent + var completeChildParamTypeObject = self.findChildSchemaObject( + obj[self.options.attributesKey].xsi_type.type, + obj[self.options.attributesKey].xsi_type.xmlns); + + nonSubNameSpace = obj[self.options.attributesKey].xsi_type.prefix + ':'; + nsContext.addNamespace(obj[self.options.attributesKey].xsi_type.prefix, + obj[self.options.attributesKey].xsi_type.xmlns); + value = self.objectToXML(child, name, obj[self.options.attributesKey].xsi_type.prefix, + obj[self.options.attributesKey].xsi_type.xmlns, false, null, null, nsContext); } else { - value = self.objectToXML(child, name, namespace, xmlns, false, null, null, ancXmlns); + value = self.objectToXML(child, name, nsPrefix, nsURI, false, null, null, nsContext); } } else { - value = self.objectToXML(child, name, namespace, xmlns, false, null, null, ancXmlns); + value = self.objectToXML(child, name, nsPrefix, nsURI, false, null, null, nsContext); } } } if (!Array.isArray(child)) { - parts.push(['<', nonSubNameSpace || ns, name, attr, xmlnsAttrib, (child === null ? ' xsi:nil="true"' : ''), '>'].join('')); + parts.push(['<', nonSubNameSpace || ns, name, attr, xmlnsAttrib, + (child === null ? ' xsi:nil="true"' : ''), '>'].join('')); } parts.push(value); @@ -1659,42 +1736,43 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns, isFirst, xmln } else if (obj !== undefined) { parts.push(xmlEscape(obj)); } + nsContext.popContext(); return parts.join(''); }; -WSDL.prototype.processAttributes = function(child) { - var self = this; +WSDL.prototype.processAttributes = function(child, nsContext) { var attr = ''; if(child === null) { child = []; } - if (child[this.options.attributesKey] && child[this.options.attributesKey].xsi_type) { - var xsiType = child[this.options.attributesKey].xsi_type; + 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 (!xsiType.namespace) { - if (self.namespaceNumber) { - self.namespaceNumber++; - } else { - self.namespaceNumber = 1; - } - xsiType.namespace = 'ns' + self.namespaceNumber; + if (!prefix) { + prefix = nsContext.registerNamespace(xsiType.xmlns); + } else { + nsContext.declareNamespace(prefix, xsiType.xmlns); } + xsiType.prefix = prefix; } - if (child[this.options.attributesKey]) { - for (var attrKey in child[this.options.attributesKey]) { + + if (attrObj) { + for (var attrKey in attrObj) { //handle complex extension separately if (attrKey === 'xsi_type') { - var attrValue = child[this.options.attributesKey][attrKey]; - attr += ' xsi:type="' + attrValue.namespace + ':' + attrValue.type + '"'; - attr += ' xmlns:' + attrValue.namespace + '="' + attrValue.xmlns + '"'; + var attrValue = attrObj[attrKey]; + attr += ' xsi:type="' + attrValue.prefix + ':' + attrValue.type + '"'; + attr += ' xmlns:' + attrValue.prefix + '="' + attrValue.xmlns + '"'; continue; } else { - attr += ' ' + attrKey + '="' + xmlEscape(child[this.options.attributesKey][attrKey]) + '"'; + attr += ' ' + attrKey + '="' + xmlEscape(attrObj[attrKey]) + '"'; } } } @@ -1702,12 +1780,18 @@ WSDL.prototype.processAttributes = function(child) { return attr; }; -WSDL.prototype.findChildParameterObjectFromSchema = function(name, xmlns) { - if (!this.definitions.schemas || !name || !xmlns) { +/** + * Look up a schema type definition + * @param name + * @param nsURI + * @returns {*} + */ +WSDL.prototype.findSchemaType = function(name, nsURI) { + if (!this.definitions.schemas || !name || !nsURI) { return null; } - var schema = this.definitions.schemas[xmlns]; + var schema = this.definitions.schemas[nsURI]; if (!schema || !schema.complexTypes) { return null; } @@ -1715,7 +1799,7 @@ WSDL.prototype.findChildParameterObjectFromSchema = function(name, xmlns) { return schema.complexTypes[name]; }; -WSDL.prototype.findChildParameterObject = function(parameterTypeObj, childName) { +WSDL.prototype.findChildSchemaObject = function(parameterTypeObj, childName) { if (!parameterTypeObj || !childName) { return null; } @@ -1742,27 +1826,42 @@ WSDL.prototype.findChildParameterObject = function(parameterTypeObj, childName) return object; } if (object.$ref) { - ref = splitNSName(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.findChildParameterObject(child, childName); + found = this.findChildSchemaObject(child, childName); if (found) { break; } if (child.$base) { - var childNameSpace = child.$base.substr(0, child.$base.indexOf(':')), - childXmlns = this.definitions.xmlns[childNameSpace]; + var baseQName = splitQName(child.$base); + var childNameSpace = baseQName.prefix === TNS_PREFIX ? '' : baseQName.prefix; + childNsURI = this.definitions.xmlns[baseQName.prefix]; - var foundBase = this.findChildParameterObjectFromSchema(child.$base.substr(child.$base.indexOf(':') + 1), childXmlns); + var foundBase = this.findSchemaType(baseQName.name, childNsURI); if (foundBase) { - found = this.findChildParameterObject(foundBase, childName); + found = this.findChildSchemaObject(foundBase, childName); if (found) { found.$baseNameSpace = childNameSpace; @@ -1804,7 +1903,7 @@ WSDL.prototype._parse = function(xml) { } } } else { - name = splitNSName(nsName).name; + name = splitQName(nsName).name; if (name === 'definitions') { root = new DefinitionsElement(nsName, attrs, options); stack.push(root); @@ -1852,7 +1951,7 @@ WSDL.prototype._xmlnsMap = function() { var xmlns = this.definitions.xmlns; var str = ''; for (var alias in xmlns) { - if (alias === '' || alias === 'xmlns') + if (alias === '' || alias === TNS_PREFIX) continue; var ns = xmlns[alias]; switch (ns) { @@ -1877,28 +1976,24 @@ WSDL.prototype._xmlnsMap = function() { var WSDL_CACHE = { }; -/* - Have another function to load previous WSDLs as we - don't want this to be invoked externally (expect for tests) - This will attempt to fix circular dependencies with XSD files, - Given - - file.wsdl - xs:import namespace="A" schemaLocation: A.xsd - A.xsd - xs:import namespace="B" schemaLocation: B.xsd - B.xsd - xs:import namespace="A" schemaLocation: A.xsd - - file.wsdl will start loading, import A, then A will import B, which will then import A - Because A has already started to load previously it will be returned right away and - have an internal circular reference - - B would then complete loading, then A, then file.wsdl - - By the time file A starts processing its includes its definitions will be already loaded, - this is the only thing that B will depend on when "opening" A -*/ +/* + * Have another function to load previous WSDLs as we + * don't want this to be invoked externally (expect for tests) + * This will attempt to fix circular dependencies with XSD files, + * Given + * - file.wsdl + * - xs:import namespace="A" schemaLocation: A.xsd + * - A.xsd + * - xs:import namespace="B" schemaLocation: B.xsd + * - B.xsd + * - xs:import namespace="A" schemaLocation: A.xsd + * file.wsdl will start loading, import A, then A will import B, which will then import A + * Because A has already started to load previously it will be returned right away and + * have an internal circular reference + * B would then complete loading, then A, then file.wsdl + * By the time file A starts processing its includes its definitions will be already loaded, + * this is the only thing that B will depend on when "opening" A + */ function open_wsdl_recursive(uri, options, callback) { var fromCache; @@ -1915,7 +2010,6 @@ function open_wsdl_recursive(uri, options, callback) { } function open_wsdl(uri, options, callback) { - if (typeof options === 'function') { callback = options; options = {}; diff --git a/test/request-response-samples/Dummy__should_add_prefix_for_unqualified_global_elements/request.json b/test/request-response-samples/Dummy__should_add_prefix_for_unqualified_global_elements/request.json index a73e2e8ca..21af9df45 100644 --- a/test/request-response-samples/Dummy__should_add_prefix_for_unqualified_global_elements/request.json +++ b/test/request-response-samples/Dummy__should_add_prefix_for_unqualified_global_elements/request.json @@ -6,6 +6,6 @@ } }, "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" } } diff --git a/test/request-response-samples/Dummy__should_add_prefix_for_unqualified_global_elements/request.xml b/test/request-response-samples/Dummy__should_add_prefix_for_unqualified_global_elements/request.xml index 5d901d852..04a5ce950 100644 --- a/test/request-response-samples/Dummy__should_add_prefix_for_unqualified_global_elements/request.xml +++ b/test/request-response-samples/Dummy__should_add_prefix_for_unqualified_global_elements/request.xml @@ -1 +1 @@ -HumptyDumpty \ No newline at end of file +HumptyDumpty \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_extract_envelope_from_response/request.json b/test/request-response-samples/Dummy__should_extract_envelope_from_response/request.json index 1f3a2941d..52d6f57f7 100644 --- a/test/request-response-samples/Dummy__should_extract_envelope_from_response/request.json +++ b/test/request-response-samples/Dummy__should_extract_envelope_from_response/request.json @@ -1,6 +1,6 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" } -} \ No newline at end of file +} diff --git a/test/request-response-samples/Dummy__should_extract_envelope_from_response/request.xml b/test/request-response-samples/Dummy__should_extract_envelope_from_response/request.xml index c28cceff9..e377aaa35 100644 --- a/test/request-response-samples/Dummy__should_extract_envelope_from_response/request.xml +++ b/test/request-response-samples/Dummy__should_extract_envelope_from_response/request.xml @@ -1 +1 @@ -HumptyDumpty \ No newline at end of file +HumptyDumpty \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_handle_attributes_in_request/request.json b/test/request-response-samples/Dummy__should_handle_attributes_in_request/request.json index a73e2e8ca..21af9df45 100644 --- a/test/request-response-samples/Dummy__should_handle_attributes_in_request/request.json +++ b/test/request-response-samples/Dummy__should_handle_attributes_in_request/request.json @@ -6,6 +6,6 @@ } }, "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" } } diff --git a/test/request-response-samples/Dummy__should_handle_attributes_in_request/request.xml b/test/request-response-samples/Dummy__should_handle_attributes_in_request/request.xml index 5c351cf7e..e3d0981b0 100644 --- a/test/request-response-samples/Dummy__should_handle_attributes_in_request/request.xml +++ b/test/request-response-samples/Dummy__should_handle_attributes_in_request/request.xml @@ -1 +1 @@ -HumptyDumpty \ No newline at end of file +HumptyDumpty \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_handle_attributes_in_response/request.json b/test/request-response-samples/Dummy__should_handle_attributes_in_response/request.json index 1f3a2941d..52d6f57f7 100644 --- a/test/request-response-samples/Dummy__should_handle_attributes_in_response/request.json +++ b/test/request-response-samples/Dummy__should_handle_attributes_in_response/request.json @@ -1,6 +1,6 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" } -} \ No newline at end of file +} diff --git a/test/request-response-samples/Dummy__should_handle_attributes_in_response/request.xml b/test/request-response-samples/Dummy__should_handle_attributes_in_response/request.xml index c28cceff9..e377aaa35 100644 --- a/test/request-response-samples/Dummy__should_handle_attributes_in_response/request.xml +++ b/test/request-response-samples/Dummy__should_handle_attributes_in_response/request.xml @@ -1 +1 @@ -HumptyDumpty \ No newline at end of file +HumptyDumpty \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_handle_cdata_xml_in_response/request.json b/test/request-response-samples/Dummy__should_handle_cdata_xml_in_response/request.json index 1f3a2941d..52d6f57f7 100644 --- a/test/request-response-samples/Dummy__should_handle_cdata_xml_in_response/request.json +++ b/test/request-response-samples/Dummy__should_handle_cdata_xml_in_response/request.json @@ -1,6 +1,6 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" } -} \ No newline at end of file +} diff --git a/test/request-response-samples/Dummy__should_handle_cdata_xml_in_response/request.xml b/test/request-response-samples/Dummy__should_handle_cdata_xml_in_response/request.xml index c28cceff9..e377aaa35 100644 --- a/test/request-response-samples/Dummy__should_handle_cdata_xml_in_response/request.xml +++ b/test/request-response-samples/Dummy__should_handle_cdata_xml_in_response/request.xml @@ -1 +1 @@ -HumptyDumpty \ No newline at end of file +HumptyDumpty \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_handle_nil/request.json b/test/request-response-samples/Dummy__should_handle_nil/request.json index 1f3a2941d..52d6f57f7 100644 --- a/test/request-response-samples/Dummy__should_handle_nil/request.json +++ b/test/request-response-samples/Dummy__should_handle_nil/request.json @@ -1,6 +1,6 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" } -} \ No newline at end of file +} diff --git a/test/request-response-samples/Dummy__should_handle_nil/request.xml b/test/request-response-samples/Dummy__should_handle_nil/request.xml index c28cceff9..e377aaa35 100644 --- a/test/request-response-samples/Dummy__should_handle_nil/request.xml +++ b/test/request-response-samples/Dummy__should_handle_nil/request.xml @@ -1 +1 @@ -HumptyDumpty \ No newline at end of file +HumptyDumpty \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_ignore_custom_defined_namespaces/request.json b/test/request-response-samples/Dummy__should_ignore_custom_defined_namespaces/request.json index 5d29a0b5d..1a89d6b8a 100644 --- a/test/request-response-samples/Dummy__should_ignore_custom_defined_namespaces/request.json +++ b/test/request-response-samples/Dummy__should_ignore_custom_defined_namespaces/request.json @@ -1,11 +1,11 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" }, "DummyAccount": { "DummyName": "Dummy", "DummyPassword": "1234" } -} \ No newline at end of file +} diff --git a/test/request-response-samples/Dummy__should_ignore_custom_defined_namespaces/request.xml b/test/request-response-samples/Dummy__should_ignore_custom_defined_namespaces/request.xml index b9c5a2b8b..2bb3fb2e8 100644 --- a/test/request-response-samples/Dummy__should_ignore_custom_defined_namespaces/request.xml +++ b/test/request-response-samples/Dummy__should_ignore_custom_defined_namespaces/request.xml @@ -1 +1 @@ -HumptyDumptyDummy1234 \ No newline at end of file +HumptyDumptyDummy1234 \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_ignore_defined_namespaces/request.json b/test/request-response-samples/Dummy__should_ignore_defined_namespaces/request.json index 5d29a0b5d..1a89d6b8a 100644 --- a/test/request-response-samples/Dummy__should_ignore_defined_namespaces/request.json +++ b/test/request-response-samples/Dummy__should_ignore_defined_namespaces/request.json @@ -1,11 +1,11 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" }, "DummyAccount": { "DummyName": "Dummy", "DummyPassword": "1234" } -} \ No newline at end of file +} diff --git a/test/request-response-samples/Dummy__should_ignore_defined_namespaces/request.xml b/test/request-response-samples/Dummy__should_ignore_defined_namespaces/request.xml index b9c5a2b8b..2bb3fb2e8 100644 --- a/test/request-response-samples/Dummy__should_ignore_defined_namespaces/request.xml +++ b/test/request-response-samples/Dummy__should_ignore_defined_namespaces/request.xml @@ -1 +1 @@ -HumptyDumptyDummy1234 \ No newline at end of file +HumptyDumptyDummy1234 \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_parse_nil/request.json b/test/request-response-samples/Dummy__should_parse_nil/request.json index 85fc023a8..1b52c5098 100644 --- a/test/request-response-samples/Dummy__should_parse_nil/request.json +++ b/test/request-response-samples/Dummy__should_parse_nil/request.json @@ -1,6 +1,6 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": null + "DummyStringFilter": null } } diff --git a/test/request-response-samples/Dummy__should_parse_nil/request.xml b/test/request-response-samples/Dummy__should_parse_nil/request.xml index 94e9e418b..aabb3bf04 100644 --- a/test/request-response-samples/Dummy__should_parse_nil/request.xml +++ b/test/request-response-samples/Dummy__should_parse_nil/request.xml @@ -1 +1 @@ -Humpty \ No newline at end of file +Humpty \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_use_defined_value_key/request.json b/test/request-response-samples/Dummy__should_use_defined_value_key/request.json index 5d29a0b5d..1a89d6b8a 100644 --- a/test/request-response-samples/Dummy__should_use_defined_value_key/request.json +++ b/test/request-response-samples/Dummy__should_use_defined_value_key/request.json @@ -1,11 +1,11 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" }, "DummyAccount": { "DummyName": "Dummy", "DummyPassword": "1234" } -} \ No newline at end of file +} diff --git a/test/request-response-samples/Dummy__should_use_defined_value_key/request.xml b/test/request-response-samples/Dummy__should_use_defined_value_key/request.xml index b9c5a2b8b..2bb3fb2e8 100644 --- a/test/request-response-samples/Dummy__should_use_defined_value_key/request.xml +++ b/test/request-response-samples/Dummy__should_use_defined_value_key/request.xml @@ -1 +1 @@ -HumptyDumptyDummy1234 \ No newline at end of file +HumptyDumptyDummy1234 \ No newline at end of file diff --git a/test/request-response-samples/Dummy__should_work_with_imported_schemas/request.json b/test/request-response-samples/Dummy__should_work_with_imported_schemas/request.json index 1f3a2941d..52d6f57f7 100644 --- a/test/request-response-samples/Dummy__should_work_with_imported_schemas/request.json +++ b/test/request-response-samples/Dummy__should_work_with_imported_schemas/request.json @@ -1,6 +1,6 @@ { "DummyField1": "Humpty", "DummyFilter": { - "DummyFilterString": "Dumpty" + "DummyStringFilter": "Dumpty" } -} \ No newline at end of file +} diff --git a/test/request-response-samples/Dummy__should_work_with_imported_schemas/request.xml b/test/request-response-samples/Dummy__should_work_with_imported_schemas/request.xml index c28cceff9..e377aaa35 100644 --- a/test/request-response-samples/Dummy__should_work_with_imported_schemas/request.xml +++ b/test/request-response-samples/Dummy__should_work_with_imported_schemas/request.xml @@ -1 +1 @@ -HumptyDumpty \ No newline at end of file +HumptyDumpty \ No newline at end of file diff --git a/test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/Common.xsd b/test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/Common.xsd similarity index 100% rename from test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/Common.xsd rename to test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/Common.xsd diff --git a/test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/Name.xsd b/test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/Name.xsd similarity index 100% rename from test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/Name.xsd rename to test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/Name.xsd diff --git a/test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/request.json b/test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/request.json similarity index 100% rename from test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/request.json rename to test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/request.json diff --git a/test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/request.xml b/test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/request.xml similarity index 100% rename from test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/request.xml rename to test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/request.xml diff --git a/test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/response.json b/test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/response.json similarity index 100% rename from test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/response.json rename to test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/response.json diff --git a/test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/response.xml b/test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/response.xml similarity index 100% rename from test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/response.xml rename to test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/response.xml diff --git a/test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/soap.wsdl b/test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/soap.wsdl similarity index 100% rename from test/request-response-samples/UpdateProfile__correct_nanespaces_in_sequence_with_imports/soap.wsdl rename to test/request-response-samples/UpdateProfile__correct_namespaces_in_sequence_with_imports/soap.wsdl diff --git a/test/request-response-samples/addList__complex_extension_namespace_for_arrays/request.xml b/test/request-response-samples/addList__complex_extension_namespace_for_arrays/request.xml index 4b4157ac4..32f13950d 100644 --- a/test/request-response-samples/addList__complex_extension_namespace_for_arrays/request.xml +++ b/test/request-response-samples/addList__complex_extension_namespace_for_arrays/request.xml @@ -1 +1 @@ -First-1400867779067Sullivan800-555-2819aLFirst-1400867779067Sullivan800-555-2819 \ No newline at end of file +First-1400867779067Sullivan800-555-2819aLFirst-1400867779067Sullivan800-555-2819 \ No newline at end of file diff --git a/test/request-response-samples/multiCall__rpc_stype_request_xsi_attributes/request.xml b/test/request-response-samples/multiCall__rpc_stype_request_xsi_attributes/request.xml index 60592e7d9..1925d93a2 100644 --- a/test/request-response-samples/multiCall__rpc_stype_request_xsi_attributes/request.xml +++ b/test/request-response-samples/multiCall__rpc_stype_request_xsi_attributes/request.xml @@ -1 +1 @@ -sessionIdproduct_stock.updateHTC Touch Diamondqty9199is_in_stock1 \ No newline at end of file +sessionIdproduct_stock.updateHTC Touch Diamondqty9199is_in_stock1 \ No newline at end of file diff --git a/test/request-response-samples/readMetadata__should_respect_xsi_type_attribute/request.json b/test/request-response-samples/readMetadata__should_respect_xsi_type_attribute/request.json index be09765bc..4038e679d 100644 --- a/test/request-response-samples/readMetadata__should_respect_xsi_type_attribute/request.json +++ b/test/request-response-samples/readMetadata__should_respect_xsi_type_attribute/request.json @@ -1,4 +1,4 @@ { - "metadataType": "CustomObject", + "type": "CustomObject", "fullNames": "Opportunity" } diff --git a/test/request-response-samples/readMetadata__should_respect_xsi_type_attribute/request.xml b/test/request-response-samples/readMetadata__should_respect_xsi_type_attribute/request.xml index be2906b27..1957d562b 100644 --- a/test/request-response-samples/readMetadata__should_respect_xsi_type_attribute/request.xml +++ b/test/request-response-samples/readMetadata__should_respect_xsi_type_attribute/request.xml @@ -1 +1 @@ -CustomObjectOpportunity \ No newline at end of file +CustomObjectOpportunity \ No newline at end of file diff --git a/test/request-response-samples/update__complex_extension_with_array_attributes/request.xml b/test/request-response-samples/update__complex_extension_with_array_attributes/request.xml index beff89e9d..2eccb794b 100644 --- a/test/request-response-samples/update__complex_extension_with_array_attributes/request.xml +++ b/test/request-response-samples/update__complex_extension_with_array_attributes/request.xml @@ -1 +1 @@ -AllisterSullivanofficephonemobilephone800-555-2819falsetrueBilling CommunicationtrueMarketing \ No newline at end of file +AllisterSullivanofficephonemobilephone800-555-2819falsetrueBilling CommunicationtrueMarketing \ No newline at end of file diff --git a/test/wsdl-test.js b/test/wsdl-test.js index 75ac17146..693c6df3b 100644 --- a/test/wsdl-test.js +++ b/test/wsdl-test.js @@ -77,7 +77,7 @@ wsdlStrictTests['should handle element ref'] = function(done) { var expectedMsg = '' + - '' + + '' + '001' + ''; soap.createClient(__dirname + '/wsdl/elementref/foo.wsdl', {strict: true}, function(err, client) { @@ -89,6 +89,18 @@ wsdlStrictTests['should handle element ref'] = function(done) { }); }; +wsdlStrictTests['should handle type ref'] = function(done) { + var expectedMsg = require('./wsdl/typeref/request.xml.js'); + var reqJson = require('./wsdl/typeref/request.json'); + soap.createClient(__dirname + '/wsdl/typeref/order.wsdl', {strict: true}, function(err, client) { + assert.ok(!err); + client.order(reqJson, function(err, result) { + assert.equal(client.lastMessage, expectedMsg); + done(); + }); + }); +}; + module.exports = { 'WSDL Parser (strict)': wsdlStrictTests, 'WSDL Parser (non-strict)': wsdlNonStrictTests diff --git a/test/wsdl/typeref/ns1.xsd b/test/wsdl/typeref/ns1.xsd new file mode 100644 index 000000000..243ec1947 --- /dev/null +++ b/test/wsdl/typeref/ns1.xsd @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/wsdl/typeref/ns2.xsd b/test/wsdl/typeref/ns2.xsd new file mode 100644 index 000000000..503fe1412 --- /dev/null +++ b/test/wsdl/typeref/ns2.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/wsdl/typeref/order.wsdl b/test/wsdl/typeref/order.wsdl new file mode 100755 index 000000000..210bafe00 --- /dev/null +++ b/test/wsdl/typeref/order.wsdl @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/wsdl/typeref/request.json b/test/wsdl/typeref/request.json new file mode 100644 index 000000000..7177d08d6 --- /dev/null +++ b/test/wsdl/typeref/request.json @@ -0,0 +1,15 @@ +{ + "itemRq": { + "ecomRq": { + "rqUID": "001" + }, + "item": { + "qty": 100, + "itemId": "item01" + }, + "backupItem": { + "qty": 50, + "itemId": "item02" + } + } +} diff --git a/test/wsdl/typeref/request.xml.js b/test/wsdl/typeref/request.xml.js new file mode 100755 index 000000000..8d19de678 --- /dev/null +++ b/test/wsdl/typeref/request.xml.js @@ -0,0 +1,6 @@ +module.exports = '' + + '001' + + '100' + + 'item01' + + '50' + + 'item02'; From eb574be62b96cd2b2ba3be54ff54137993de361b Mon Sep 17 00:00:00 2001 From: jsdevel Date: Wed, 21 Oct 2015 13:19:27 -0700 Subject: [PATCH 32/48] Release v0.10.0 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 39332bbc7..6bba2d589 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,7 @@ +0.10.0 / 2015-10-21 +================= +* [FIX] xml namespace/element/type handling. (#756) + 0.9.5 / 2015-10-15 ================= * [FIX] Allow circular XSD files to be loaded. (#745) diff --git a/package.json b/package.json index e530c9014..c526868b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soap", - "version": "0.9.5", + "version": "0.10.0", "description": "A minimal node SOAP client", "engines": { "node": ">=0.8.0" From 3d04c45208673f026b6fe916d4dec257bdd4b0c5 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 19 Oct 2015 21:41:27 +0300 Subject: [PATCH 33/48] add SOAP v1.2 support for client remove extra slash fix boolean cast rename forceSoap12 to forceSoap12Headers --- Readme.md | 1 + lib/client.js | 13 +++++-- lib/wsdl.js | 3 ++ test/client-test.js | 17 +++++++++ test/wsdl/default_namespace_soap12.wsdl | 47 +++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 test/wsdl/default_namespace_soap12.wsdl diff --git a/Readme.md b/Readme.md index 37b88429c..a2273772f 100644 --- a/Readme.md +++ b/Readme.md @@ -40,6 +40,7 @@ The `options` argument allows you to customize the client with the following pro - endpoint: to override the SOAP service's host specified in the `.wsdl` file. - request: to override the [request](https://github.com/request/request) module. - httpClient: to provide your own http client that implements `request(rurl, data, callback, exheaders, exoptions)`. +- forceSoap12Headers: to set proper headers for SOAP v1.2 ### soap.listen(*server*, *path*, *services*, *wsdl*) - create a new SOAP server that listens on *path* and provides *services*. *wsdl* is an xml string that defines the service. diff --git a/lib/client.js b/lib/client.js index 51215a85b..05ff0900f 100644 --- a/lib/client.js +++ b/lib/client.js @@ -99,6 +99,7 @@ Client.prototype._initializeServices = function(endpoint) { Client.prototype._initializeOptions = function(options) { this.wsdl.options.attributesKey = options.attributesKey || 'attributes'; + this.wsdl.options.forceSoap12Headers = !!options.forceSoap12Headers; }; Client.prototype._defineService = function(service, endpoint) { @@ -150,8 +151,14 @@ Client.prototype._invoke = function(method, args, location, callback, options, e soapAction, alias = findKey(defs.xmlns, ns), headers = { - 'Content-Type': "text/xml; charset=utf-8" - }; + "Content-Type": "text/xml; charset=utf-8" + }, + xmlnsSoap = "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""; + + if (this.wsdl.options.forceSoap12Headers) { + headers["Content-Type"] = "application/soap+xml; charset=utf-8"; + xmlnsSoap = "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\""; + } if (this.SOAPAction) { soapAction = this.SOAPAction; @@ -186,7 +193,7 @@ Client.prototype._invoke = function(method, args, location, callback, options, e message = self.wsdl.objectToDocumentXML(input.$name, args, input.targetNSAlias, input.targetNamespace, (input.$type || input.$lookupType)); } xml = "' + diff --git a/lib/wsdl.js b/lib/wsdl.js index 6752869f6..685aaa013 100644 --- a/lib/wsdl.js +++ b/lib/wsdl.js @@ -1109,6 +1109,9 @@ WSDL.prototype._initializeOptions = function (options) { else this.options.ignoreBaseNameSpaces = this.ignoreBaseNameSpaces; + // Works only in client + this.options.forceSoap12Headers = options.forceSoap12Headers; + }; WSDL.prototype.onReady = function(callback) { diff --git a/test/client-test.js b/test/client-test.js index c632636ac..7f22bed42 100644 --- a/test/client-test.js +++ b/test/client-test.js @@ -210,6 +210,23 @@ describe('SOAP Client', function() { }, null, {"test-header": 'test'}); }, baseUrl); }); + + it('should add proper headers for soap12', function(done) { + soap.createClient(__dirname+'/wsdl/default_namespace_soap12.wsdl', {forceSoap12Headers: true}, function(err, client) { + assert.ok(client); + assert.ok(!err); + + client.MyOperation({}, function(err, result) { + assert.ok(result); + assert.ok(client.lastRequestHeaders); + assert.ok(client.lastRequest); + assert.equal(client.lastRequestHeaders['Content-Type'], 'application/soap+xml; charset=utf-8'); + assert.notEqual(client.lastRequest.indexOf('xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\"'), -1); + + done(); + }, null, {'test-header': 'test'}); + }, baseUrl); + }); }); it('should add soap headers', function (done) { diff --git a/test/wsdl/default_namespace_soap12.wsdl b/test/wsdl/default_namespace_soap12.wsdl new file mode 100644 index 000000000..8e57b6fc1 --- /dev/null +++ b/test/wsdl/default_namespace_soap12.wsdl @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From aee5546c7a89287557fa266a31e1c3ab9ecb607a Mon Sep 17 00:00:00 2001 From: jsdevel Date: Thu, 22 Oct 2015 12:03:49 -0700 Subject: [PATCH 34/48] Adding soap-stub. --- Readme.md | 50 +++++++++++++++++++++ soap-stub.js | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 soap-stub.js diff --git a/Readme.md b/Readme.md index 37b88429c..8fc6d8e28 100644 --- a/Readme.md +++ b/Readme.md @@ -491,6 +491,56 @@ var options = { ignoredNamespaces: true } ``` + +## soap-stub + +Unit testing services that use soap clients can be very cumbersome. In order to get +around this you can use `soap-stub` in conjunction with `sinon` to stub soap with +your clients. + +### Example + +```javascript +// test-initialization-script.js +var sinon = require('sinon'); +var soapStub = require('soap/soap-stub'); + +var urlMyApplicationWillUseWithCreateClient = 'http://path-to-my-wsdl'; +var clientStub = { + SomeOperation: sinon.stub() +}; + +clientStub.SomeOperation.respondWithError = soapStub.createRespondingStub({..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; + + beforeEach(function() { + clientStub = soapStub.getStub('my client alias'); + soapStub.reset(); + myService.init(clientStub); + }); + + describe('failures', function() { + beforeEach(function() { + clientStub.SomeOperation.respondWithError(); + }); + + it('should handle error responses', function() { + myService.somethingThatCallsSomeOperation(function(err, response) { + // handle the error response. + }); + }); + }); +}); +``` ## Contributors diff --git a/soap-stub.js b/soap-stub.js new file mode 100644 index 000000000..e5310d55f --- /dev/null +++ b/soap-stub.js @@ -0,0 +1,122 @@ +var _ = require('lodash'); + +var aliasedClientStubs = {}; +var clientStubs = {}; + +/** + * This module stubs the soap module to allow for offline + * testing and stubbed clients. All clients' methods are stubbed with sinon and have + * additional functionality: + * + *
    + *
  • .respondWithError() - Responds with the mocked client's sample error response.
  • + *
  • .respondWithSuccess() - Responds with the mocked client's sample success response.
  • + *
+ * + * Register a client by calling `.registerClient(urlToWsdl, clientStub)`. For an + * example client stub, see ./soap-stub-client-example.js. + * + * @property {Boolean} errOnCreateClient returns an error to the createClient method when set to true. + */ +module.exports = { + createClient: createClient, + createRespondingStub: createRespondingStub, + errOnCreateClient: false, + getStub: getStub, + registerClient: registerClient, + reset: reset +}; + +/** + * Return a stubbed client based on the value of wsdlUrl. + * + * @throws if wsdlUrl is unknown. + * + * @param {String} wsdlUrl + * @param {Object} options + * @param {Function} cb + * @return {Object} + */ +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))); + } +} + +/** + * Returns a method that calls all callbacks given to the method it is attached to. + * This allows you to trigger responses from your tests. + * + *
+ * myClientStub.someMethod.respondWithError = createRespondingStub(errorResponse);
+ *
+ * // elsewhere
+ *
+ * myClientStub.someMethod.respondWithError();
+ * 
+ * + * @param {?} object anything + * @return {Function} + */ +function createRespondingStub(object) { + return function() { + this.args.forEach(function(argSet) { + setTimeout(argSet[1].bind(null, null, object)); + }); + this.yields(null, object); + }; +} + +/** + * Registers a stubbed client with soap-stub. urlToWsdl is the path you will use + * in your app. + * + * @param {String} alias A simple name to refer to the clientStub as. + * @param {String} urlToWsdl May be file system URL or http URL. + * @param {Object} clientStub A client with stubbed methods. + */ +function registerClient(alias, urlToWsdl, clientStub) { + aliasedClientStubs[alias] = clientStub; + clientStubs[urlToWsdl] = clientStub; +} + +/** + * Resets state associated with clientStubs. + */ +function reset() { + _.forEach(clientStubs, resetStubbedMethods); + this.errOnCreateClient = false; +} + +/** + * Returns a previously registered client stub. + * + * @param {String} aliasOrWsdlUrl + * @return {Object} clientStub + */ +function getStub(aliasOrWsdlUrl) { + return aliasedClientStubs[aliasOrWsdlUrl] || clientStubs[aliasOrWsdlUrl]; +} + +function resetStubbedMethods(client) { + Object.keys(client).forEach(function(method) { + method = client[method]; + if (typeof method === 'function' && typeof method.reset === 'function') { + method.reset(); + } + }); +} From 23d4291da87dc053c3681395287f35129b417a13 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Thu, 22 Oct 2015 12:11:50 -0700 Subject: [PATCH 35/48] Release v0.10.1 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 6bba2d589..7630d95ed 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,7 @@ +0.10.1 / 2015-10-22 +================= +* [ENHANCEMENT] Adding soap-stub. (#763) + 0.10.0 / 2015-10-21 ================= * [FIX] xml namespace/element/type handling. (#756) diff --git a/package.json b/package.json index c526868b0..73f677417 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soap", - "version": "0.10.0", + "version": "0.10.1", "description": "A minimal node SOAP client", "engines": { "node": ">=0.8.0" From f138da3f38993b3d0fffe050b6543188037a81ff Mon Sep 17 00:00:00 2001 From: jsdevel Date: Thu, 22 Oct 2015 19:15:52 -0700 Subject: [PATCH 36/48] Adding security to soap-stub. --- soap-stub.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/soap-stub.js b/soap-stub.js index e5310d55f..c9d3880a0 100644 --- a/soap-stub.js +++ b/soap-stub.js @@ -24,7 +24,8 @@ module.exports = { errOnCreateClient: false, getStub: getStub, registerClient: registerClient, - reset: reset + reset: reset, + security: require('./lib/security') }; /** From 55880928c07808f895bbdd1e7419548a8436c7fb Mon Sep 17 00:00:00 2001 From: jsdevel Date: Thu, 22 Oct 2015 22:58:30 -0700 Subject: [PATCH 37/48] Release v0.10.2 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 7630d95ed..2ed55b859 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,7 @@ +0.10.2 / 2015-10-22 +================= +* [ENHANCEMENT] Adding security to soap-stub. (#764) + 0.10.1 / 2015-10-22 ================= * [ENHANCEMENT] Adding soap-stub. (#763) diff --git a/package.json b/package.json index 73f677417..34296440c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soap", - "version": "0.10.1", + "version": "0.10.2", "description": "A minimal node SOAP client", "engines": { "node": ">=0.8.0" From 02aa90f1cd02d0fdfa6c0736c7e443faf9aa4a61 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Fri, 23 Oct 2015 11:07:29 -0700 Subject: [PATCH 38/48] Adding createErroringStub to soap-stub. --- soap-stub.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/soap-stub.js b/soap-stub.js index c9d3880a0..36000c133 100644 --- a/soap-stub.js +++ b/soap-stub.js @@ -20,6 +20,7 @@ var clientStubs = {}; */ module.exports = { createClient: createClient, + createErroringStub: createErroringStub, createRespondingStub: createRespondingStub, errOnCreateClient: false, getStub: getStub, @@ -59,8 +60,32 @@ function createClient(wsdlUrl, options, cb) { } /** - * Returns a method that calls all callbacks given to the method it is attached to. - * This allows you to trigger responses from your tests. + * Returns a method that calls all callbacks given to the method it is attached + * to with the given error. + * + *
+ * myClientStub.someMethod.errorOnCall = createErroringStub(error);
+ *
+ * // elsewhere
+ *
+ * myClientStub.someMethod.errorOnCall();
+ * 
+ * + * @param {?} object anything + * @return {Function} + */ +function createErroringStub(err) { + return function() { + this.args.forEach(function(argSet) { + setTimeout(argSet[1].bind(null, err)); + }); + this.yields(err); + }; +} + +/** + * Returns a method that calls all callbacks given to the method it is attached + * to with the given response. * *
  * myClientStub.someMethod.respondWithError = createRespondingStub(errorResponse);

From fd632a08cf24865611d02837d9cfe0a1c1377532 Mon Sep 17 00:00:00 2001
From: jsdevel 
Date: Fri, 23 Oct 2015 11:14:18 -0700
Subject: [PATCH 39/48] Release v0.10.3

---
 History.md   | 5 +++++
 package.json | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/History.md b/History.md
index 2ed55b859..74e3f22ef 100644
--- a/History.md
+++ b/History.md
@@ -1,3 +1,8 @@
+0.10.3 / 2015-10-23
+=================
+* [ENHANCEMENT] Adding createErroringStub to soap-stub. (#765)
+* [ENHANCEMENT] `forceSoap12Headers` option to add SOAP v1.2 request headers. (#755)
+
 0.10.2 / 2015-10-22
 =================
 * [ENHANCEMENT] Adding security to soap-stub. (#764)
diff --git a/package.json b/package.json
index 34296440c..1dc2815c1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "soap",
-  "version": "0.10.2",
+  "version": "0.10.3",
   "description": "A minimal node SOAP client",
   "engines": {
     "node": ">=0.8.0"

From e9a5d8b27005ea44f33c2d4fde16dc3204cc79b2 Mon Sep 17 00:00:00 2001
From: Mike Borozdin 
Date: Fri, 30 Oct 2015 14:18:40 -0700
Subject: [PATCH 40/48] added the ability to add HTTP headers to the client.

---
 lib/client.js       | 17 +++++++++++++++++
 test/client-test.js | 19 +++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/lib/client.js b/lib/client.js
index a92c05708..07d43f447 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -43,6 +43,22 @@ Client.prototype.clearSoapHeaders = function() {
   this.soapHeaders = null;
 };
 
+Client.prototype.addHttpHeader = function(name, value) {
+  if (!this.httpHeaders) {
+    this.httpHeaders = {};
+  }
+  this.httpHeaders[name] = value;
+};
+
+Client.prototype.getHttpHeaders = function() {
+  return this.httpHeaders;
+};
+
+Client.prototype.clearHttpHeaders = function() {
+  this.httpHeaders = {};
+};
+
+
 Client.prototype.addBodyAttribute = function(bodyAttribute, name, namespace, xmlns) {
   if (!this.bodyAttributes) {
     this.bodyAttributes = [];
@@ -168,6 +184,7 @@ Client.prototype._invoke = function(method, args, location, callback, options, e
   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
diff --git a/test/client-test.js b/test/client-test.js
index 7f22bed42..841d129ab 100644
--- a/test/client-test.js
+++ b/test/client-test.js
@@ -267,6 +267,25 @@ describe('SOAP Client', function() {
       done();
     });
   });
+  
+  it('should add http headers', function(done) {
+    soap.createClient(__dirname+'/wsdl/default_namespace.wsdl', function(err, client) {
+      assert.ok(client);
+      assert.ok(!client.getHttpHeaders());
+
+      client.addHttpHeader('foo', 'bar');
+
+      assert.ok(client.getHttpHeaders());
+      console.log('http headers');
+      console.log(client.getHttpHeaders());
+      console.log(client.getHttpHeaders().foo);
+      assert.equal(client.getHttpHeaders().foo, 'bar');
+
+      client.clearHttpHeaders();
+      assert.equal(Object.keys(client.getHttpHeaders()).length, 0);
+      done();
+    });
+  });
 
   describe('Namespace number', function() {
     var server = null;

From 0406a15bf38ab408b0477f9d013b4a09eecf5037 Mon Sep 17 00:00:00 2001
From: Mike Borozdin 
Date: Fri, 30 Oct 2015 15:11:09 -0700
Subject: [PATCH 41/48] removed console logs

---
 test/client-test.js | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/test/client-test.js b/test/client-test.js
index 841d129ab..37c9894e5 100644
--- a/test/client-test.js
+++ b/test/client-test.js
@@ -276,9 +276,6 @@ describe('SOAP Client', function() {
       client.addHttpHeader('foo', 'bar');
 
       assert.ok(client.getHttpHeaders());
-      console.log('http headers');
-      console.log(client.getHttpHeaders());
-      console.log(client.getHttpHeaders().foo);
       assert.equal(client.getHttpHeaders().foo, 'bar');
 
       client.clearHttpHeaders();

From fa2e22cb9f20192324020f60a3a3a8e2f3d0cb34 Mon Sep 17 00:00:00 2001
From: Evan Tahler 
Date: Fri, 30 Oct 2015 14:46:10 -0700
Subject: [PATCH 42/48] pass req to service

note new option in readme

test for req within Service

spacing

update test for ipv6
---
 Readme.md           |  8 ++++++++
 lib/server.js       | 14 +++++++-------
 test/server-test.js | 19 +++++++++++++++++--
 3 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/Readme.md b/Readme.md
index a2adf1764..5e6602e56 100644
--- a/Readme.md
+++ b/Readme.md
@@ -79,6 +79,14 @@ The `options` argument allows you to customize the client with the following pro
                       name: headers.Token
                   };
               }
+
+              // You can also inspect the original `req`
+              reallyDeatailedFunction: function(args, cb, headers, req) {
+                  console.log('SOAP `reallyDeatailedFunction` request from ' + req.connection.remoteAddress)
+                  return {
+                      name: headers.Token
+                  };
+              }
           }
       }
   }
diff --git a/lib/server.js b/lib/server.js
index 8784f0553..eaf64a01a 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -115,7 +115,7 @@ Server.prototype._requestListener = function(req, res) {
         if (typeof self.log === 'function') {
           self.log("received", xml);
         }
-        self._process(xml, req.url, function(result, statusCode) {
+        self._process(xml, req, function(result, statusCode) {
           if(statusCode) {
             res.statusCode = statusCode;
           }
@@ -141,9 +141,9 @@ Server.prototype._requestListener = function(req, res) {
   }
 };
 
-Server.prototype._process = function(input, URL, callback) {
+Server.prototype._process = function(input, req, callback) {
   var self = this,
-    pathname = url.parse(URL).pathname.replace(/\/$/, ''),
+    pathname = url.parse(req.url).pathname.replace(/\/$/, ''),
     obj = this.wsdl.xmlToObject(input),
     body = obj.Body,
     headers = obj.Header,
@@ -215,7 +215,7 @@ Server.prototype._process = function(input, URL, callback) {
         args: body[methodName],
         headers: headers,
         style: 'rpc'
-      }, callback);
+      }, req, callback);
     } else {
       var messageElemName = (Object.keys(body)[0] === 'attributes' ? Object.keys(body)[1] : Object.keys(body)[0]);
       var pair = binding.topElements[messageElemName];
@@ -232,7 +232,7 @@ Server.prototype._process = function(input, URL, callback) {
         args: body[messageElemName],
         headers: headers,
         style: 'document'
-      }, callback, includeTimestamp);
+      }, req, callback, includeTimestamp);
     }
   }
   catch (error) {
@@ -244,7 +244,7 @@ Server.prototype._process = function(input, URL, callback) {
   }
 };
 
-Server.prototype._executeMethod = function(options, callback, includeTimestamp) {
+Server.prototype._executeMethod = function(options, req, callback, includeTimestamp) {
   options = options || {};
   var self = this,
     method, body,
@@ -290,7 +290,7 @@ Server.prototype._executeMethod = function(options, callback, includeTimestamp)
     callback('');
   }
 
-  var result = method(args, handleResult, options.headers);
+  var result = method(args, handleResult, options.headers, req);
   if (typeof result !== 'undefined') {
     handleResult(result);
   }
diff --git a/test/server-test.js b/test/server-test.js
index 2ea70a6d1..b999ff9f9 100644
--- a/test/server-test.js
+++ b/test/server-test.js
@@ -4,7 +4,8 @@ var fs = require('fs'),
     soap = require('..'),
     assert = require('assert'),
     request = require('request'),
-    http = require('http');
+    http = require('http'),
+    lastReqAddress;
 
 var test = {};
 test.server = null;
@@ -43,7 +44,9 @@ test.service = {
       SetTradePrice: function(args, cb, soapHeader) {
       },
 
-      IsValidPrice: function(args, cb, soapHeader) {
+      IsValidPrice: function(args, cb, soapHeader, req) {
+        lastReqAddress = req.connection.remoteAddress;
+
         var validationError = {
           Fault: {
             Code: {
@@ -181,6 +184,18 @@ describe('SOAP Server', function() {
     });
   });
 
+  it('should pass the original req to async methods', function(done) {
+    soap.createClient(test.baseUrl + '/stockquote?wsdl', function(err, client) {
+      assert.ok(!err);
+      client.IsValidPrice({ price: 50000 }, function(err, result) {
+        // node V3.x+ reports addresses as IPV6
+        var addressParts = lastReqAddress.split(':');
+        addressParts[(addressParts.length - 1)].should.equal('127.0.0.1');
+        done();
+      });
+    });
+  });
+
   it('should return correct async errors', function(done) {
     soap.createClient(test.baseUrl + '/stockquote?wsdl', function(err, client) {
       assert.ok(!err);

From 8c8be33e2d15df891c0cb70f6cc30a13d4f8ccb9 Mon Sep 17 00:00:00 2001
From: jsdevel 
Date: Sat, 31 Oct 2015 12:37:36 -0700
Subject: [PATCH 43/48] Release v0.11.0

---
 History.md   | 7 +++++++
 package.json | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/History.md b/History.md
index 74e3f22ef..200b9e1b2 100644
--- a/History.md
+++ b/History.md
@@ -1,3 +1,10 @@
+0.11.0 / 2015-10-31
+=================
+* [ENHANCEMENT] Now passing request to services in server.js. (#769)
+* [ENHANCEMENT] Adding the ability to add headers in client requests. (#770)
+* [MAINTENANCE] Adding gitter badge to README and disabling issues. (#731)
+* [FIX] Stop sending Object prototype methods as XML. (#699)
+
 0.10.3 / 2015-10-23
 =================
 * [ENHANCEMENT] Adding createErroringStub to soap-stub. (#765)
diff --git a/package.json b/package.json
index 1dc2815c1..8626239e1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "soap",
-  "version": "0.10.3",
+  "version": "0.11.0",
   "description": "A minimal node SOAP client",
   "engines": {
     "node": ">=0.8.0"

From 9e762ab26a9125331224483b051e33af8865f0c2 Mon Sep 17 00:00:00 2001
From: wmcmurray 
Date: Mon, 19 Oct 2015 14:00:08 -0400
Subject: [PATCH 44/48] Add missing type attribute for PasswordText in
 WSSecurity and update related tests

---
 lib/security/WSSecurity.js                                      | 2 +-
 .../request.xml                                                 | 2 +-
 .../RequestHeaders__propogate_basic_security/request.xml        | 2 +-
 .../request.xml                                                 | 2 +-
 .../request.xml                                                 | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/lib/security/WSSecurity.js b/lib/security/WSSecurity.js
index 3a671cbcf..d860c08ad 100644
--- a/lib/security/WSSecurity.js
+++ b/lib/security/WSSecurity.js
@@ -48,7 +48,7 @@ WSSecurity.prototype.toXML = function() {
 
   var password;
   if(this._passwordType === 'PasswordText') {
-    password = "" + this._password + "";
+    password = "" + this._password + "";
   } else {
     // nonce = base64 ( sha1 ( created + random ) )
     var nHash = crypto.createHash('sha1');
diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.xml
index 8a802f488..a31a27bb6 100644
--- a/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.xml	
+++ b/test/request-response-samples/RequestHeaders__propogate_basic_security backward compatible check/request.xml	
@@ -1 +1 @@
-2014-10-12T01:02:03Z2014-10-12T01:12:03Zbasicuserbasicpass2014-10-12T01:02:03Z
\ No newline at end of file
+2014-10-12T01:02:03Z2014-10-12T01:12:03Zbasicuserbasicpass2014-10-12T01:02:03Z
\ No newline at end of file
diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security/request.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security/request.xml
index 8a802f488..a31a27bb6 100644
--- a/test/request-response-samples/RequestHeaders__propogate_basic_security/request.xml
+++ b/test/request-response-samples/RequestHeaders__propogate_basic_security/request.xml
@@ -1 +1 @@
-2014-10-12T01:02:03Z2014-10-12T01:12:03Zbasicuserbasicpass2014-10-12T01:02:03Z
\ No newline at end of file
+2014-10-12T01:02:03Z2014-10-12T01:12:03Zbasicuserbasicpass2014-10-12T01:02:03Z
\ No newline at end of file
diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.xml
index 8a802f488..a31a27bb6 100644
--- a/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.xml
+++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_invalid_options/request.xml
@@ -1 +1 @@
-2014-10-12T01:02:03Z2014-10-12T01:12:03Zbasicuserbasicpass2014-10-12T01:02:03Z
\ No newline at end of file
+2014-10-12T01:02:03Z2014-10-12T01:12:03Zbasicuserbasicpass2014-10-12T01:02:03Z
\ No newline at end of file
diff --git a/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.xml b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.xml
index 550fc3ece..dce627ca4 100644
--- a/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.xml
+++ b/test/request-response-samples/RequestHeaders__propogate_basic_security_no_timestamp/request.xml
@@ -1 +1 @@
-basicuserbasicpass2014-10-12T01:02:03Z
\ No newline at end of file
+basicuserbasicpass2014-10-12T01:02:03Z
\ No newline at end of file

From 605ec59130d020417d83ff0c6a8006951042acc1 Mon Sep 17 00:00:00 2001
From: Tom Gallacher 
Date: Mon, 2 Nov 2015 12:02:06 +0000
Subject: [PATCH 45/48] Fixed issue when an error was undefined: undefined

This occurs on responses that from what I can tell do not have an
xsi:type of string, Thus these objects would not have an internal $value
and would return undefined.

This commit keeps backwards compatibility and falls back to assuming the
error was a string.
---
 lib/wsdl.js                           | 6 +++---
 test/request-response-samples-test.js | 1 +
 test/server-test.js                   | 2 ++
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/lib/wsdl.js b/lib/wsdl.js
index 2f388f34a..af962f628 100644
--- a/lib/wsdl.js
+++ b/lib/wsdl.js
@@ -1418,9 +1418,9 @@ WSDL.prototype.xmlToObject = function(xml) {
   if (root.Envelope) {
     var body = root.Envelope.Body;
     if (body.Fault) {
-      var code = selectn('faultcode.$value', body.Fault);
-      var string = selectn('faultstring.$value', body.Fault);
-      var detail = selectn('detail.$value', body.Fault);
+      var code = selectn('faultcode.$value', body.Fault) || selectn('faultcode', body.Fault);
+      var string = selectn('faultstring.$value', body.Fault) || selectn('faultstring', body.Fault);
+      var detail = selectn('detail.$value', body.Fault) || selectn('detail.message', body.Fault);
       var error = new Error(code + ': ' + string + (detail ? ': ' + detail : ''));
       error.root = root;
       throw error;
diff --git a/test/request-response-samples-test.js b/test/request-response-samples-test.js
index a8fd0bc90..2a81b2cb1 100644
--- a/test/request-response-samples-test.js
+++ b/test/request-response-samples-test.js
@@ -110,6 +110,7 @@ function generateTest(name, methodName, wsdlPath, headerJSON, securityJSON, requ
       client[methodName](requestJSON, function(err, json, body, soapHeader){
         if(requestJSON){
           if (err) {
+            assert.notEqual('undefined: undefined', err.message);
             assert.deepEqual(err.root, responseJSON);
           } else {
             // assert.deepEqual(json, responseJSON);
diff --git a/test/server-test.js b/test/server-test.js
index b999ff9f9..8bd0a79df 100644
--- a/test/server-test.js
+++ b/test/server-test.js
@@ -290,6 +290,7 @@ describe('SOAP Server', function() {
       client.GetLastTradePrice({ tickerSymbol: 'SOAP Fault v1.2' }, function(err, response, body) {
         assert.ok(err);
         var fault = err.root.Envelope.Body.Fault;
+        assert.equal(err.message, fault.faultcode + ': ' + fault.faultstring);
         assert.equal(fault.Code.Value, "soap:Sender");
         assert.equal(fault.Reason.Text, "Processing Error");
         // Verify namespace on elements set according to fault spec 1.2
@@ -309,6 +310,7 @@ describe('SOAP Server', function() {
       client.GetLastTradePrice({ tickerSymbol: 'SOAP Fault v1.1' }, function(err, response, body) {
         assert.ok(err);
         var fault = err.root.Envelope.Body.Fault;
+        assert.equal(err.message, fault.faultcode + ': ' + fault.faultstring);
         assert.equal(fault.faultcode, "soap:Client.BadArguments");
         assert.equal(fault.faultstring, "Error while processing arguments");
         // Verify namespace on elements set according to fault spec 1.1

From df902dd405290e9397ce3a4db2d1e6dbbe64c7dd Mon Sep 17 00:00:00 2001
From: Tom Gallacher 
Date: Wed, 28 Oct 2015 11:25:34 +0000
Subject: [PATCH 46/48] Adding ClientSSLSecurityPFX for use in requests

This adds supports for PFX files as an alternative to cert and keys.

Support for passphrase's
Fixed issues with ClientSSLSecurityPFX in 0.1 and 0.8
Additional documentation and tests
---
 lib/security/ClientSSLSecurityPFX.js  |  51 +++++++++++++
 lib/security/index.js                 |   1 +
 lib/soap.js                           |   1 +
 test/certs/client-password.pfx        | Bin 0 -> 5693 bytes
 test/certs/pfk-buffer.pfx             | Bin 0 -> 5717 bytes
 test/certs/server-password.pfx        | Bin 0 -> 5701 bytes
 test/security/ClientSSLSecurityPFX.js | 100 ++++++++++++++++++++++++++
 7 files changed, 153 insertions(+)
 create mode 100644 lib/security/ClientSSLSecurityPFX.js
 create mode 100644 test/certs/client-password.pfx
 create mode 100644 test/certs/pfk-buffer.pfx
 create mode 100644 test/certs/server-password.pfx
 create mode 100644 test/security/ClientSSLSecurityPFX.js

diff --git a/lib/security/ClientSSLSecurityPFX.js b/lib/security/ClientSSLSecurityPFX.js
new file mode 100644
index 000000000..3ce7af535
--- /dev/null
+++ b/lib/security/ClientSSLSecurityPFX.js
@@ -0,0 +1,51 @@
+'use strict';
+
+var fs = require('fs')
+  , https = require('https')
+  , _ = require('lodash');
+
+/**
+ * activates SSL for an already existing client using a PFX cert
+ *
+ * @module ClientSSLSecurityPFX
+ * @param {Buffer|String}   pfx
+ * @param {String}   passphrase
+ * @constructor
+ */
+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);
+}
+
+ClientSSLSecurityPFX.prototype.toXML = function(headers) {
+  return '';
+};
+
+ClientSSLSecurityPFX.prototype.addOptions = function(options) {
+  options.pfx = this.pfx;
+  if (this.passphrase) {
+    options.passphrase = this.passphrase;
+  }
+  _.merge(options, this.defaults);
+  options.agent = new https.Agent(options);
+};
+
+module.exports = ClientSSLSecurityPFX;
diff --git a/lib/security/index.js b/lib/security/index.js
index 6b8af894d..d0167d3f1 100644
--- a/lib/security/index.js
+++ b/lib/security/index.js
@@ -3,6 +3,7 @@
 module.exports = {
   BasicAuthSecurity: require('./BasicAuthSecurity')
 , ClientSSLSecurity: require('./ClientSSLSecurity')
+, ClientSSLSecurityPFX: require('./ClientSSLSecurityPFX')
 , WSSecurity: require('./WSSecurity')
 , BearerSecurity: require('./BearerSecurity')
 };
diff --git a/lib/soap.js b/lib/soap.js
index 4e91973e8..5b4a813e9 100644
--- a/lib/soap.js
+++ b/lib/soap.js
@@ -70,6 +70,7 @@ exports.security = security;
 exports.BasicAuthSecurity = security.BasicAuthSecurity;
 exports.WSSecurity = security.WSSecurity;
 exports.ClientSSLSecurity = security.ClientSSLSecurity;
+exports.ClientSSLSecurityPFX = security.ClientSSLSecurityPFX;
 exports.BearerSecurity = security.BearerSecurity;
 exports.createClient = createClient;
 exports.passwordDigest = passwordDigest;
diff --git a/test/certs/client-password.pfx b/test/certs/client-password.pfx
new file mode 100644
index 0000000000000000000000000000000000000000..c3d8ae13e17e29bcd7b9a72f1e5cc183b089f968
GIT binary patch
literal 5693
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
bMYAN-Wbeqc6b+L_%;@`?wffV4TH5~rl@IZf

literal 0
HcmV?d00001

diff --git a/test/certs/pfk-buffer.pfx b/test/certs/pfk-buffer.pfx
new file mode 100644
index 0000000000000000000000000000000000000000..561366cecdde1694d4a6a4f35d403e70d856b5aa
GIT binary patch
literal 5717
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
F{TC>E)eQgu

literal 0
HcmV?d00001

diff --git a/test/certs/server-password.pfx b/test/certs/server-password.pfx
new file mode 100644
index 0000000000000000000000000000000000000000..81d2c5070594185001fd5e21ae48d9de47772d6e
GIT binary patch
literal 5701
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

literal 0
HcmV?d00001

diff --git a/test/security/ClientSSLSecurityPFX.js b/test/security/ClientSSLSecurityPFX.js
new file mode 100644
index 000000000..d98694222
--- /dev/null
+++ b/test/security/ClientSSLSecurityPFX.js
@@ -0,0 +1,100 @@
+'use strict';
+
+var fs = require('fs'),
+    join = require('path').join;
+
+describe('ClientSSLSecurityPFX', function() {
+  var ClientSSLSecurityPFX = require('../../').ClientSSLSecurityPFX;
+  var pfx = __filename;
+
+  it('should be function', function() {
+    ClientSSLSecurityPFX.should.be.type('function');
+  });
+
+  describe('defaultOption param', function() {
+    it('should be accepted as the second param', function() {
+      new ClientSSLSecurityPFX(null, {});
+    });
+
+    it('should be used in addOptions', function() {
+      var options = {};
+      var defaultOptions = { foo: 5 };
+      var instance = new ClientSSLSecurityPFX(null, defaultOptions);
+      instance.addOptions(options);
+      options.should.have.property("foo", 5);
+    });
+  });
+
+  it('should throw if invalid pfk file is given', function () {
+    var instanceCert = null;
+
+    try {
+      instanceCert = new ClientSSLSecurityPFX({});
+    } catch (e) {
+      //should happen!
+      instanceCert = false;
+    }
+
+    if (instanceCert !== false) {
+      throw new Error('accepted wrong pfk');
+    }
+  });
+
+  it('should be usable in a request', function (done) {
+    var https = require('https');
+    var pfkBuffer = fs.readFileSync(join(__dirname, '..', 'certs', 'client-password.pfx')),
+      instance;
+
+    instance = new ClientSSLSecurityPFX(pfkBuffer, 'test2test');
+    var soptions = {
+      host: 'localhost',
+      port: 1338,
+      requestCert: true,
+      rejectUnauthorized: false,
+      pfx: fs.readFileSync(join(__dirname, '..', 'certs', 'server-password.pfx')),
+      passphrase: 'test2test',
+    };
+    var options = {
+      port: 1338
+    };
+    instance.addOptions(options);
+
+    var server = https.createServer(soptions, function(req, res) {
+      req.socket.should.have.property('authorized', true);
+      // Doesn't work in older versions of nodejs
+      // req.socket.should.have.property('authorizationError', null);
+      res.writeHead(200);
+      res.end('OK');
+    });
+
+    server.listen(soptions.port, soptions.host, function() {
+      var data = '';
+
+      https.get(options, function(res) {
+        res.on('data', function(data_) { data += data_; });
+        res.on('end', function() {
+          server.close();
+          data.should.equal('OK');
+          done();
+        });
+      });
+    });
+  });
+
+  it('should accept a passphrase as argument for the pfx cert', function () {
+    var pfkBuffer = fs.readFileSync(join(__dirname, '..', 'certs', 'client-password.pfx')),
+      instance;
+
+    instance = new ClientSSLSecurityPFX(pfkBuffer, 'test2est');
+    instance.should.have.property("pfx", pfkBuffer);
+    instance.should.have.property("passphrase", 'test2est');
+  });
+
+  it('should accept a Buffer as argument for the pfx cert', function () {
+    var pfkBuffer = fs.readFileSync(join(__dirname, '..', 'certs', 'pfk-buffer.pfx')),
+      instance;
+
+    instance = new ClientSSLSecurityPFX(pfkBuffer);
+    instance.should.have.property("pfx", pfkBuffer);
+  });
+});

From b03577f94b4d871a884d0404c3939b717d187b85 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20L=C3=B3pez?= 
Date: Wed, 4 Nov 2015 16:54:30 +0100
Subject: [PATCH 47/48] When an error occur, send HTTP 500 status code

---
 lib/server.js       |  1 +
 test/server-test.js | 21 +++++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/lib/server.js b/lib/server.js
index eaf64a01a..598186ad2 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -128,6 +128,7 @@ Server.prototype._requestListener = function(req, res) {
       }
       catch (err) {
         error = err.stack || err;
+        res.statusCode = 500;
         res.write(error);
         res.end();
         if (typeof self.log === 'function') {
diff --git a/test/server-test.js b/test/server-test.js
index 8bd0a79df..b3b37b91b 100644
--- a/test/server-test.js
+++ b/test/server-test.js
@@ -131,6 +131,27 @@ describe('SOAP Server', function() {
     });
   });
 
+  it('should 500 on wrong message', function(done) {
+    request.post({
+        url: test.baseUrl + '/stockquote?wsdl',
+        body : '' +
+                '  ' +
+                '  ' +
+                '    ' +
+                '  ' +
+                '',
+        headers: {'Content-Type': 'text/xml'}
+      }, function(err, res, body) {
+        assert.ok(!err);
+        assert.equal(res.statusCode, 500);
+        assert.ok(body.length);
+        done();
+      }
+    );
+  });
+
   it('should server up WSDL', function(done) {
     request(test.baseUrl + '/stockquote?wsdl', function(err, res, body) {
       assert.ok(!err);

From 02ea8c2a83ccf2d513a8889f50fd6fd5f746d690 Mon Sep 17 00:00:00 2001
From: Michael Szlapa 
Date: Wed, 11 Nov 2015 14:21:10 -0500
Subject: [PATCH 48/48] Remove SOAPAction http header in SOAP 1.2, extra header
 was causing some servers to trip

---
 lib/client.js       | 4 +++-
 test/client-test.js | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/lib/client.js b/lib/client.js
index 07d43f447..5856fd5cc 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -179,7 +179,9 @@ Client.prototype._invoke = function(method, args, location, callback, options, e
     soapAction = ((ns.lastIndexOf("/") !== ns.length - 1) ? ns + "/" : ns) + name;
   }
 
-  headers.SOAPAction = '"' + soapAction + '"';
+  if (!this.wsdl.options.forceSoap12Headers) {
+    headers.SOAPAction = '"' + soapAction + '"';
+  }
 
   options = options || {};
 
diff --git a/test/client-test.js b/test/client-test.js
index 37c9894e5..bf54df31c 100644
--- a/test/client-test.js
+++ b/test/client-test.js
@@ -222,7 +222,7 @@ describe('SOAP Client', function() {
           assert.ok(client.lastRequest);
           assert.equal(client.lastRequestHeaders['Content-Type'], 'application/soap+xml; charset=utf-8');
           assert.notEqual(client.lastRequest.indexOf('xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\"'), -1);
-
+          assert( !client.lastRequestHeaders.SOAPAction );
           done();
         }, null, {'test-header': 'test'});
       }, baseUrl);