Skip to content

Commit 05eb9bd

Browse files
smokhovAleksey MalashenokGamze Oguzw666
authored
Adding custom envelope key option for server and client header fix (#1208, #1170, #1330)
Co-authored-by: Aleksey Malashenok <[email protected]> Co-authored-by: Gamze Oguz <[email protected]> Co-authored-by: Vasily Martynov <[email protected]>
1 parent ac6fe86 commit 05eb9bd

File tree

5 files changed

+196
-12
lines changed

5 files changed

+196
-12
lines changed

src/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ export class Client extends EventEmitter {
443443
encoding +
444444
this.wsdl.xmlnsInEnvelope +
445445
'>' +
446-
(decodedHeaders || this.security
446+
(decodedHeaders || (this.security && this.security.toXML())
447447
? '<' +
448448
envelopeKey +
449449
':Header' +

src/server.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export class Server extends EventEmitter {
187187
this.wsdl.options.attributesKey = options.attributesKey || 'attributes';
188188
this.onewayOptions.statusCode = this.onewayOptions.responseCode || 200;
189189
this.onewayOptions.emptyBody = !!this.onewayOptions.emptyBody;
190+
this.wsdl.options.envelopeKey = options.envelopeKey || 'soap';
190191
}
191192

192193
private _processRequestXml(req: Request, res: Response, xml) {
@@ -616,10 +617,11 @@ export class Server extends EventEmitter {
616617

617618
private _envelope(body, headers, includeTimestamp) {
618619
const encoding = '';
620+
const envelopeKey = this.wsdl.options.envelopeKey;
619621

620622
const envelopeDefinition = this.wsdl.options.forceSoap12Headers ? 'http://www.w3.org/2003/05/soap-envelope' : 'http://schemas.xmlsoap.org/soap/envelope/';
621623

622-
let xml = '<?xml version="1.0" encoding="utf-8"?>' + '<soap:Envelope xmlns:soap="' + envelopeDefinition + '" ' + encoding + this.wsdl.xmlnsInEnvelope + '>';
624+
let xml = '<?xml version="1.0" encoding="utf-8"?>' + '<' + envelopeKey + ':Envelope' + ' xmlns:' + envelopeKey + '=' + '"' + envelopeDefinition + '" ' + encoding + this.wsdl.xmlnsInEnvelope + '>';
623625

624626
headers = headers || '';
625627

@@ -644,17 +646,16 @@ export class Server extends EventEmitter {
644646
}
645647

646648
if (headers !== '') {
647-
xml += '<soap:Header>' + headers + '</soap:Header>';
649+
xml += '<' + envelopeKey + ':Header>' + headers + '</' + envelopeKey + ':Header>';
648650
}
649-
650-
xml += body ? '<soap:Body>' + body + '</soap:Body>' : '<soap:Body/>';
651-
652-
xml += '</soap:Envelope>';
651+
xml += body ? '<' + envelopeKey + ':Body>' + body + '</' + envelopeKey + ':Body>' : '<' + envelopeKey + ':Body/>';
652+
xml += '</' + envelopeKey + ':Envelope>';
653653
return xml;
654654
}
655655

656656
private _sendError(soapFault: ISoapFault, callback: (result: any, statusCode?: number) => any, includeTimestamp) {
657657
let fault;
658+
const envelopeKey = this.wsdl.options.envelopeKey;
658659

659660
let statusCode: number;
660661
if (soapFault.statusCode) {
@@ -666,12 +667,12 @@ export class Server extends EventEmitter {
666667
// Soap 1.1 error style
667668
// Root element will be prependend with the soap NS
668669
// It must match the NS defined in the Envelope (set by the _envelope method)
669-
fault = this.wsdl.objectToDocumentXML('soap:Fault', soapFault, undefined);
670+
fault = this.wsdl.objectToDocumentXML(envelopeKey + ':Fault', soapFault, undefined);
670671
} else {
671672
// Soap 1.2 error style.
672673
// 3rd param is the NS prepended to all elements
673674
// It must match the NS defined in the Envelope (set by the _envelope method)
674-
fault = this.wsdl.objectToDocumentXML('Fault', soapFault, 'soap');
675+
fault = this.wsdl.objectToDocumentXML('Fault', soapFault, envelopeKey);
675676
}
676677

677678
return callback(this._envelope(fault, '', includeTimestamp), statusCode);

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export interface IServerOptions extends IWsdlBaseOptions {
159159
oneWay?: IOneWayOptions;
160160
/** A boolean for controlling chunked transfer encoding in response. Some client (such as Windows 10's MDM enrollment SOAP client) is sensitive to transfer-encoding mode and can't accept chunked response. This option let user disable chunked transfer encoding for such a client. Default to true for backward compatibility. */
161161
enableChunkedEncoding?: boolean;
162+
envelopeKey?: string;
162163
}
163164

164165
export interface IMTOMAttachments {

test/client-test.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ var fs = require('fs'),
1010
wsdl = require('../lib/wsdl');
1111

1212
[
13-
{ suffix: '', options: {} },
14-
{ suffix: ' (with streaming)', options: { stream: true } },
13+
{ suffix: '', options: { useEmptyTag: false } },
14+
{ suffix: ' (with streaming)', options: { stream: true, useEmptyTag: false } },
1515
].forEach(function (meta) {
1616
describe('SOAP Client' + meta.suffix, function () {
1717
var baseUrl = 'http://127.0.0.1:80';
@@ -91,7 +91,7 @@ var fs = require('fs'),
9191
});
9292
});
9393

94-
it('should allow customization of envelope', function (done) {
94+
it('should allow customization of client envelope key', function (done) {
9595
soap.createClient(
9696
__dirname + '/wsdl/default_namespace.wsdl',
9797
Object.assign({ envelopeKey: 'soapenv' }, meta.options),
@@ -108,6 +108,38 @@ var fs = require('fs'),
108108
);
109109
});
110110

111+
it('should skip creating header XML on empty <Header/> and security when toXML is empty', function (done) {
112+
soap.createClient(
113+
__dirname + '/wsdl/default_namespace.wsdl',
114+
Object.assign({ envelopeKey: 'soapenv', useEmptyTag: true, wsdl_headers: '<soapenv:Header/>' }, meta.options),
115+
function (err, client) {
116+
var join = require('path').join;
117+
var ClientSSLSecurity = require('../').ClientSSLSecurity;
118+
var certBuffer = fs.readFileSync(join(__dirname, '.', 'certs', 'agent2-cert.pem')),
119+
keyBuffer = fs.readFileSync(join(__dirname, '.', 'certs', 'agent2-key.pem')),
120+
instance;
121+
122+
// Creates a Security instance that has no toXML() (empty string)
123+
instance = new ClientSSLSecurity(keyBuffer, certBuffer, certBuffer);
124+
var xml = instance.toXML();
125+
xml.should.be.exactly('');
126+
127+
client.setSecurity(instance);
128+
client.addSoapHeader('');
129+
130+
assert.ok(client);
131+
assert.ifError(err);
132+
133+
client.MyOperation({}, function (err, result) {
134+
assert.equal(client.lastRequest.indexOf('soapenv:Header'), -1);
135+
assert.notEqual(client.lastRequest.indexOf('xmlns:soapenv='), -1);
136+
done();
137+
});
138+
},
139+
baseUrl,
140+
);
141+
});
142+
111143
it('should allow passing in XML strings', function (done) {
112144
var server = null;
113145
var hostname = '127.0.0.1';

test/server-options-test.js

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,4 +706,154 @@ describe('SOAP Server with Options', function () {
706706
});
707707
});
708708
});
709+
710+
it('should return soapenv as envelope key when it is set to soapenv', function (done) {
711+
var responseData =
712+
'<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://example.com/stockquote.wsdl" xmlns:xsd1="http://example.com/stockquote.xsd"><soapenv:Body><xsd1:TradePrice xmlns:xsd1="http://example.com/stockquote.xsd"><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns3:UpdateProfileResponse xmlns:ns3="http://www.bigdatacollect.or/Name/Types" xmlns="http://www.bigdatacollect.or/Common/Types"><ns3:Result resultStatusFlag="SUCCESS"><IDs><UniqueID source="TESTSOURCE">100</UniqueID></IDs></ns3:Result></ns3:UpdateProfileResponse></S:Body></S:Envelope></xsd1:TradePrice></soapenv:Body></soapenv:Envelope>';
713+
test.server.listen(15099, null, null, function () {
714+
test.soapServer = soap.listen(
715+
test.server,
716+
{
717+
path: '/stockquote',
718+
services: test.service,
719+
xml: test.wsdl,
720+
uri: __dirname + '/wsdl/strict/',
721+
useEmptyTag: true,
722+
escapeXML: false,
723+
envelopeKey: 'soapenv',
724+
},
725+
test.service,
726+
test.wsdl,
727+
);
728+
test.baseUrl = 'http://' + test.server.address().address + ':' + test.server.address().port;
729+
730+
// windows return 0.0.0.0 as address and that is not valid to use in a request
731+
if (test.server.address().address === '0.0.0.0' || test.server.address().address === '::') {
732+
test.baseUrl = 'http://127.0.0.1:' + test.server.address().port;
733+
}
734+
735+
soap.createClient(test.baseUrl + '/stockquote?wsdl', function (err, client) {
736+
assert.ifError(err);
737+
client.GetLastTradePrice({ tickerSymbol: 'xml response' }, function (err, response, body) {
738+
assert.ifError(err);
739+
assert.strictEqual(body, responseData);
740+
done();
741+
});
742+
});
743+
});
744+
});
745+
746+
it('should return soap as envelope key by default', function (done) {
747+
var responseData =
748+
'<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://example.com/stockquote.wsdl" xmlns:xsd1="http://example.com/stockquote.xsd"><soap:Body><xsd1:TradePrice xmlns:xsd1="http://example.com/stockquote.xsd"><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns3:UpdateProfileResponse xmlns:ns3="http://www.bigdatacollect.or/Name/Types" xmlns="http://www.bigdatacollect.or/Common/Types"><ns3:Result resultStatusFlag="SUCCESS"><IDs><UniqueID source="TESTSOURCE">100</UniqueID></IDs></ns3:Result></ns3:UpdateProfileResponse></S:Body></S:Envelope></xsd1:TradePrice></soap:Body></soap:Envelope>';
749+
test.server.listen(15099, null, null, function () {
750+
test.soapServer = soap.listen(
751+
test.server,
752+
{
753+
path: '/stockquote',
754+
services: test.service,
755+
xml: test.wsdl,
756+
uri: __dirname + '/wsdl/strict/',
757+
useEmptyTag: true,
758+
escapeXML: false,
759+
},
760+
test.service,
761+
test.wsdl,
762+
);
763+
test.baseUrl = 'http://' + test.server.address().address + ':' + test.server.address().port;
764+
765+
// windows return 0.0.0.0 as address and that is not valid to use in a request
766+
if (test.server.address().address === '0.0.0.0' || test.server.address().address === '::') {
767+
test.baseUrl = 'http://127.0.0.1:' + test.server.address().port;
768+
}
769+
770+
soap.createClient(test.baseUrl + '/stockquote?wsdl', { envelopeKey: 'soapenv' }, function (err, client) {
771+
assert.ifError(err);
772+
client.GetLastTradePrice({ tickerSymbol: 'xml response' }, function (err, response, body) {
773+
console.log(response);
774+
assert.ifError(err);
775+
assert.strictEqual(body, responseData);
776+
done();
777+
});
778+
});
779+
});
780+
});
781+
782+
it('should return soapenv:Fault in soapenv as envelope key when it is set to soapenv', function (done) {
783+
test.server.listen(15099, null, null, function () {
784+
test.soapServer = soap.listen(
785+
test.server,
786+
{
787+
path: '/stockquote',
788+
services: test.service,
789+
xml: test.wsdl,
790+
uri: __dirname + '/wsdl/strict/',
791+
useEmptyTag: true,
792+
envelopeKey: 'soapenv',
793+
},
794+
test.service,
795+
test.wsdl,
796+
);
797+
test.baseUrl = 'http://' + test.server.address().address + ':' + test.server.address().port;
798+
799+
// windows return 0.0.0.0 as address and that is not valid to use in a request
800+
if (test.server.address().address === '0.0.0.0' || test.server.address().address === '::') {
801+
test.baseUrl = 'http://127.0.0.1:' + test.server.address().port;
802+
}
803+
804+
axios
805+
.post(
806+
test.baseUrl + '/stockquote',
807+
'<soapenv:Envelope' + ' xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"' + ' xmlns:soap="http://service.applicationsnet.com/soap/">' + ' <soapenv:Header/>' + ' <soapenv:Body/>' + '</soapenv:Envelope>',
808+
)
809+
.then((res) => {
810+
// should not go this path, will fail by timeout
811+
})
812+
.catch((err) => {
813+
assert.equal(err.response.status, 500);
814+
assert.ok(err.response.data.indexOf('soapenv:Envelope') !== -1);
815+
assert.ok(err.response.data.indexOf('soapenv:Fault') !== -1);
816+
done();
817+
});
818+
});
819+
});
820+
821+
it('should return soap:Fault in soap as envelope key by default', function (done) {
822+
test.server.listen(15099, null, null, function () {
823+
test.soapServer = soap.listen(
824+
test.server,
825+
{
826+
path: '/stockquote',
827+
services: test.service,
828+
xml: test.wsdl,
829+
uri: __dirname + '/wsdl/strict/',
830+
useEmptyTag: true,
831+
forceSoap12Headers: true,
832+
},
833+
test.service,
834+
test.wsdl,
835+
);
836+
test.baseUrl = 'http://' + test.server.address().address + ':' + test.server.address().port;
837+
838+
// windows return 0.0.0.0 as address and that is not valid to use in a request
839+
if (test.server.address().address === '0.0.0.0' || test.server.address().address === '::') {
840+
test.baseUrl = 'http://127.0.0.1:' + test.server.address().port;
841+
}
842+
843+
axios
844+
.post(
845+
test.baseUrl + '/stockquote',
846+
'<soapenv:Envelope' + ' xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"' + ' xmlns:soap="http://service.applicationsnet.com/soap/">' + ' <soapenv:Header/>' + ' <soapenv:Body/>' + '</soapenv:Envelope>',
847+
)
848+
.then((res) => {
849+
// should not go this path, will fail by timeout
850+
})
851+
.catch((err) => {
852+
assert.equal(err.response.status, 500);
853+
assert.ok(err.response.data.indexOf('soap:Envelope') !== -1);
854+
assert.ok(err.response.data.indexOf('soap:Fault') !== -1);
855+
done();
856+
});
857+
});
858+
});
709859
});

0 commit comments

Comments
 (0)