Skip to content

Commit

Permalink
Add function support for server addSoapHeader (#977)
Browse files Browse the repository at this point in the history
The purpose of this enhancement is to allow outgoing server headers to be
customized with information from the associated request. For servers only,
the `soapHeader` to addSoapHeader and changeSoapHeader can be a function,
which is called with the following arguments for each received request:

 - `methodName`     The name of the request method
 - `args`           The arguments of the request
 - `headers`        The headers in the request
 - `req`            The original request object

The return value of the function must be an Object({rootName: {name: 'value'}})
or strict xml-string, which will be inserted as an outgoing header of the
response to that request.
  • Loading branch information
atavakoli authored and herom committed Oct 3, 2017
1 parent 9aeceec commit 9399cad
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 22 deletions.
35 changes: 33 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,35 @@ They provide the following methods to manage the headers.

#### *addSoapHeader*(soapHeader[, name, namespace, xmlns]) - add soapHeader to soap:Header node
##### Parameters
- `soapHeader` Object({rootName: {name: 'value'}}) or strict xml-string
- `soapHeader` Object({rootName: {name: 'value'}}), strict xml-string,
or function (server only)

For servers only, `soapHeader` can be a function, which allows headers to be
dynamically generated from information in the request. This function will be
called with the following arguments for each received request:

- `methodName` The name of the request method
- `args` The arguments of the request
- `headers` The headers in the request
- `req` The original request object

The return value of the function must be an Object({rootName: {name: 'value'}})
or strict xml-string, which will be inserted as an outgoing header of the
response to that request.

For example:

``` javascript
server = soap.listen(...);
server.addSoapHeader(function(methodName, args, headers, req) {
console.log('Adding headers for method', methodName);
return {
MyHeader1: args.SomeValueFromArgs,
MyHeader2: headers.SomeRequestHeader
};
// or you can return "<MyHeader1>SomeValue</MyHeader1>"
});
```

##### Returns
The index where the header is inserted.
Expand All @@ -365,7 +393,10 @@ The index where the header is inserted.
#### *changeSoapHeader*(index, soapHeader[, name, namespace, xmlns]) - change an already existing soapHeader
##### Parameters
- `index` index of the header to replace with provided new value
- `soapHeader` Object({rootName: {name: 'value'}}) or strict xml-string
- `soapHeader` Object({rootName: {name: 'value'}}), strict xml-string
or function (server only)

See `addSoapHeader` for how to pass a function into `soapHeader`.

#### *getSoapHeaders*() - return all defined headers

Expand Down
56 changes: 40 additions & 16 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,23 +83,40 @@ var Server = function (server, path, services, wsdl, options) {
};
util.inherits(Server, events.EventEmitter);

Server.prototype._processSoapHeader = function (soapHeader, name, namespace, xmlns) {
var self = this;

switch (typeof soapHeader) {
case 'object':
return this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
case 'function':
return function() {
var result = soapHeader.apply(null, arguments);

if (typeof result === 'object') {
return self.wsdl.objectToXML(result, name, namespace, xmlns, true);
} else {
return result;
}
};
default:
return soapHeader;
}
};

Server.prototype.addSoapHeader = function (soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
if (typeof soapHeader === 'object') {
soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
}
soapHeader = this._processSoapHeader(soapHeader, name, namespace, xmlns);
return this.soapHeaders.push(soapHeader) - 1;
};

Server.prototype.changeSoapHeader = function (index, soapHeader, name, namespace, xmlns) {
if (!this.soapHeaders) {
this.soapHeaders = [];
}
if (typeof soapHeader === 'object') {
soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
}
soapHeader = this._processSoapHeader(soapHeader, name, namespace, xmlns);
this.soapHeaders[index] = soapHeader;
};

Expand Down Expand Up @@ -320,7 +337,7 @@ Server.prototype._process = function (input, req, callback) {
Server.prototype._executeMethod = function (options, req, callback, includeTimestamp) {
options = options || {};
var self = this,
method, body,
method, body, headers,
serviceName = options.serviceName,
portName = options.portName,
methodName = options.methodName,
Expand All @@ -329,10 +346,20 @@ Server.prototype._executeMethod = function (options, req, callback, includeTimes
style = options.style,
handled = false;

if (this.soapHeaders) {
headers = this.soapHeaders.map(function(header) {
if (typeof header === 'function') {
return header(methodName, args, options.headers, req);
} else {
return header;
}
}).join("\n");
}

try {
method = this.services[serviceName][portName][methodName];
} catch (error) {
return callback(this._envelope('', includeTimestamp));
return callback(this._envelope('', headers, includeTimestamp));
}

function handleResult(error, result) {
Expand All @@ -354,7 +381,7 @@ Server.prototype._executeMethod = function (options, req, callback, includeTimes
var element = self.wsdl.definitions.services[serviceName].ports[portName].binding.methods[methodName].output;
body = self.wsdl.objectToDocumentXML(outputName, result, element.targetNSAlias, element.targetNamespace);
}
callback(self._envelope(body, includeTimestamp));
callback(self._envelope(body, headers, includeTimestamp));
}

if (!self.wsdl.definitions.services[serviceName].ports[portName].binding.methods[methodName].output) {
Expand All @@ -369,7 +396,7 @@ Server.prototype._executeMethod = function (options, req, callback, includeTimes
}
};

Server.prototype._envelope = function (body, includeTimestamp) {
Server.prototype._envelope = function (body, headers, includeTimestamp) {
var defs = this.wsdl.definitions,
ns = defs.$targetNamespace,
encoding = '',
Expand All @@ -378,7 +405,8 @@ Server.prototype._envelope = function (body, includeTimestamp) {
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>';
var headers = '';

headers = headers || '';

if (includeTimestamp) {
var now = new Date();
Expand All @@ -395,10 +423,6 @@ Server.prototype._envelope = function (body, includeTimestamp) {
" </o:Security>\n";
}

if (this.soapHeaders) {
headers += this.soapHeaders.join("\n");
}

if (headers !== '') {
xml += "<soap:Header>" + headers + "</soap:Header>";
}
Expand Down Expand Up @@ -433,7 +457,7 @@ Server.prototype._sendError = function (soapFault, callback, includeTimestamp) {
fault = self.wsdl.objectToDocumentXML("Fault", soapFault, "soap");
}

return callback(self._envelope(fault, includeTimestamp), statusCode);
return callback(self._envelope(fault, '', includeTimestamp), statusCode);
};

exports.Server = Server;
25 changes: 21 additions & 4 deletions test/server-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,29 @@ describe('SOAP Server', function() {

it('should return predefined headers in response', function(done) {
soap.createClient(test.baseUrl + '/stockquote?wsdl', function(err, client) {
assert.ok(!err);
var clientArgs = { tickerSymbol: 'AAPL'};

assert.ifError(err);
test.soapServer.addSoapHeader('<header1>ONE</header1>');
test.soapServer.changeSoapHeader(1, { header2: 'TWO' });
client.GetLastTradePrice({ tickerSymbol: 'AAPL'}, function(err, result, raw, headers) {
assert.ok(!err);
assert.deepEqual(headers, { header1: 'ONE', header2: 'TWO' });
test.soapServer.addSoapHeader(function() { return { header3: 'THREE' }; });

client.addSoapHeader({ headerFromClient: 'FOUR' });
test.soapServer.changeSoapHeader(3, function(methodName, args, headers, req) {
assert.equal(methodName, 'GetLastTradePrice');
assert.deepEqual(clientArgs, args);
assert.deepEqual(headers, { headerFromClient: 'FOUR' });
return { header4: headers.headerFromClient };
});

client.GetLastTradePrice(clientArgs, function(err, result, raw, headers) {
assert.ifError(err);
assert.deepEqual(headers, {
header1: 'ONE',
header2: 'TWO',
header3: 'THREE',
header4: 'FOUR'
});
done();
});
});
Expand Down

0 comments on commit 9399cad

Please sign in to comment.