Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
fce04e5
Adds in the ability to have multiple wdsl
cbrammer Sep 23, 2012
e885466
Makes our soap match our previous soap client! rb: Branden
cbrammer Sep 25, 2012
a58da33
Fixed saop to return 'xls:nil="true"' as part of empty tags so that t…
bevans Sep 26, 2012
c2259a9
takes out logs
grobot Oct 12, 2012
e3fd3ff
Merge pull request #1 from ifit/noMoarLogz
grobot Oct 12, 2012
ee4093b
bundled
cbrammer Oct 19, 2012
d27b235
bundled
cbrammer Oct 19, 2012
18fea6a
Merge branch 'master' of github.com:ifit/node-soap
cbrammer Oct 19, 2012
b9154a9
getting rid of modules for now
cbrammer Oct 19, 2012
3423912
[fixes #43254575]
grobot Feb 26, 2013
eb60b88
[fixes #43254575]
grobot Feb 26, 2013
140b9bf
Merge pull request #2 from ifit/nullxml
regality Mar 5, 2013
f4f01a3
[fixes #43254575] - Null value
cbrammer Mar 15, 2013
64581cb
Merge pull request #4 from ifit/null
grobot Mar 15, 2013
80f4370
[fixes #48415187] Modified the objectToXML to check for a value of "r…
bevans Apr 30, 2013
e1e35c9
Merge pull request #5 from ifit/80f4370e6cd69922f8d007eec07f99cb7b7d16e6
grobot May 2, 2013
1e8eae0
rename package as it as diverged significantly
jondavidjohn Dec 31, 2014
382c48e
Merge pull request #6 from ifit/rename
jondavidjohn Dec 31, 2014
2bc7c31
add in option for debugging
grobot Feb 25, 2015
d4e84e8
Merge pull request #7 from ifit/add-debug
grobot Feb 26, 2015
92a60de
remove tests that don't work, give passing tests enough time to complete
boblauer Jan 5, 2016
337f585
upgrade node-expat, and a major version bump just incase that could b…
boblauer Jan 5, 2016
cbd668e
Merge pull request #8 from ifit/latest-expat
boblauer Jan 6, 2016
65986ce
feat: Add ifit-repo-checks action
mark-archer Feb 24, 2022
fb091ba
feat: Delete action because public repo
mark-archer Mar 1, 2022
04ef524
Update package.json
johnbuildsthings Oct 2, 2025
4d631db
Update index.js
johnbuildsthings Oct 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,35 @@
node_modules
lib-cov
*.seed
*.log
*.node
*.o
*.o.d
*.mk
*.gypi
*.cache.py
*.wafpickle-7
*.lock-wscript
*.csv
*.dat
*.out
*.pid
*.gz
*.swp
.DS_Store
dump.rdb
.jshintrc

pids
logs
results

/public/js/app.js
/public/css/style.css
npm-debug.log
/config/version.json
/config/local.json
/client/config.js
.build
/docs

/node_modules
168 changes: 90 additions & 78 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,95 +15,102 @@ try { compress = require("compress"); } catch(e) {}
var Server = function(server, path, services, wsdl) {
var self = this,
listeners = server.listeners('request');

this.services = services;
this.wsdl = wsdl;

if (path[path.length-1] != '/') path += '/';
// Not sure why you are adding a trailing slash?
//if (path[path.length-1] != '/') path += '/';
wsdl.onReady(function(err) {
server.removeAllListeners('request');
server.addListener('request', function(req, res) {
if (typeof self.authorizeConnection === 'function') {
if (!self.authorizeConnection(req.connection.remoteAddress)) {
res.end();
return;
}
}
var reqPath = url.parse(req.url).pathname;
if (reqPath[reqPath.length-1] != '/') reqPath += '/';
if (path === reqPath) {
self._requestListener(req, res);
}
else {
for (var i = 0, len = listeners.length; i < len; i++){
listeners[i].call(this, req, res);
}

// GET request
server.get(path, function(req, res){
var search = url.parse(req.url).search;
if (search && search.toLowerCase() === '?wsdl') {
res.setHeader("Content-Type", "application/xml");
res.write(self.wsdl.toXML());
}
});
})
}
res.end();
});

Server.prototype._requestListener = function(req, res) {
var self = this;
if (req.method === 'GET') {
var search = url.parse(req.url).search;
if (search && search.toLowerCase() === '?wsdl') {
res.setHeader("Content-Type", "application/xml");
res.write(self.wsdl.toXML());
}
res.end();
}
else if (req.method === 'POST') {
res.setHeader('Content-Type', req.headers['content-type']);
var chunks = [], gunzip;
if (compress && req.headers["content-encoding"] == "gzip") {
gunzip = new compress.Gunzip;
gunzip.init();
}
req.on('data', function(chunk) {
if (gunzip) chunk = gunzip.inflate(chunk, "binary");
chunks.push(chunk);
})
req.on('end', function() {
var xml = chunks.join(''), result;
if (gunzip) {
gunzip.end();
gunzip = null
// POST request
server.post(path, function(req, res){
if(wsdl.options.debug) console.log(process.pid, Date.now(), path);
res.setHeader('Content-Type', req.headers['content-type']);
var chunks = [], gunzip;
if (compress && req.headers["content-encoding"] == "gzip") {
gunzip = new compress.Gunzip;
gunzip.init();
}
try {
self._process(xml, req.url, function(result) {
res.write(result);
req.on('data', function(chunk) {
if(wsdl.options.debug) console.log(process.pid, Date.now(), 'data');
if (gunzip) chunk = gunzip.inflate(chunk, "binary");
chunks.push(chunk);
})
req.on('end', function() {
if(wsdl.options.debug) console.log(process.pid, Date.now(), 'end');
var xml = chunks.join(''), result;
xml = xml.replace(/[\x00-\x1F]/g,'');
if (gunzip) {
gunzip.end();
gunzip = null
}
try {
self._process(xml, req.url, function(result) {
if(wsdl.options.debug) console.log(process.pid, Date.now(), 'write');
res.write(result);
res.end();
if (typeof self.log === 'function') {
self.log("received", xml);
self.log("replied", result);
}
});
}
catch(err) {
err = err.stack || err;
res.write(err);
res.end();
if (typeof self.log === 'function') {
self.log("received", xml);
self.log("replied", result);
self.log("error", err);
}
});
}
catch(err) {
err = err.stack || err;
res.write(err);
res.end();
if (typeof self.log === 'function') {
self.log("error", err);
}
}
});
});
}
else {
res.end();
}

// Leaving this block of original code in because I am not sure
// what the authorizeConnection is doing...
//
// server.removeAllListeners('request');
// server.addListener('request', function(req, res) {
// if (typeof self.authorizeConnection === 'function') {
// if (!self.authorizeConnection(req.connection.remoteAddress)) {
// res.end();
// return;
// }
// }
// var reqPath = url.parse(req.url).pathname;
// if (reqPath[reqPath.length-1] != '/') reqPath += '/';
// if (path === reqPath) {
// self._requestListener(req, res);
// }
// else {
// for (var i = 0, len = listeners.length; i < len; i++){
// listeners[i].call(this, req, res);
// }
// }
// });
})
}

Server.prototype._process = function(input, URL, callback) {
var self = this,
pathname = url.parse(URL).pathname.replace(/\/$/,''),
obj = this.wsdl.xmlToObject(input),
body = obj.Body,
bindings = this.wsdl.definitions.bindings, binding,
bindings = this.wsdl.definitions.bindings, binding,
methods, method, methodName,
serviceName, portName;

if (typeof self.authenticate === 'function') {
if (obj.Header == null || obj.Header.Security == null) {
throw new Error('No security header');
Expand All @@ -122,7 +129,7 @@ Server.prototype._process = function(input, URL, callback) {
for(portName in ports) {
var port = ports[portName];
var portPathname = url.parse(port.location).pathname.replace(/\/$/,'');
if(portPathname===pathname)
if(portPathname===pathname)
return port.binding;
}
}
Expand Down Expand Up @@ -176,11 +183,12 @@ Server.prototype._executeMethod = function(options, callback) {
if (handled) return;
handled = true;


if(style==='rpc') {
body = self.wsdl.objectToRpcXML(outputName, result, '', self.wsdl.definitions.$targetNamespace);
} else {
var element = self.wsdl.definitions.services[serviceName].ports[portName].binding.methods[methodName].output;
body = self.wsdl.objectToDocumentXML(outputName, result, element.targetNSAlias, element.targetNamespace);
body = self.wsdl.objectToDocumentXML(outputName, result, '', element.targetNamespace);
}
callback(self._envelope(body));
}
Expand All @@ -196,15 +204,19 @@ Server.prototype._envelope = function(body) {
ns = defs.$targetNamespace,
encoding = '',
alias = findKey(defs.xmlns, ns);
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
encoding +
this.wsdl.xmlnsInEnvelope + '>' +
"<soap:Body>" +
body +
"</soap:Body>" +
"</soap:Envelope>";

var xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<soapenv:Envelope " +
"xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<soapenv:Body>" +
body +
"</soapenv:Body>" +
"</soapenv:Envelope>";
return xml;

}

exports.Server = Server;
91 changes: 63 additions & 28 deletions lib/wsdl.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,23 +720,23 @@ WSDL.prototype.xmlToObject = function(xml) {
topSchema = message.description(self.definitions);
objectName = originalName;
}
if(attrs.href) {
id = attrs.href.substr(1);
if(!refs[id]) refs[id] = {hrefs:[],obj:null};
refs[id].hrefs.push({par:top.object,key:name});
}
if(id=attrs.id) {
if(!refs[id]) refs[id] = {hrefs:[],obj:null};
}
if(attrs.href) {
id = attrs.href.substr(1);
if(!refs[id]) refs[id] = {hrefs:[],obj:null};
refs[id].hrefs.push({par:top.object,key:name});
}
if(id=attrs.id) {
if(!refs[id]) refs[id] = {hrefs:[],obj:null};
}

if (topSchema && topSchema[name+'[]']) name = name + '[]';
stack.push({name: originalName, object: obj, schema: topSchema && topSchema[name], id:attrs.id});
})

p.on('endElement', function(nsName) {
var cur = stack.pop(),
obj = cur.object,
obj = cur.object,
top = stack[stack.length-1],
topObject = top.object,
topSchema = top.schema,
Expand All @@ -755,10 +755,10 @@ WSDL.prototype.xmlToObject = function(xml) {
else {
topObject[name] = obj;
}
if(cur.id) {
refs[cur.id].obj = obj;
}
if(cur.id) {
refs[cur.id].obj = obj;
}
})

p.on('text', function(text) {
Expand Down Expand Up @@ -786,15 +786,15 @@ WSDL.prototype.xmlToObject = function(xml) {
if (!p.parse(xml, false)) {
throw new Error(p.getError());
}
for(var n in refs) {
var ref = refs[n];
var obj = ref.obj;
ref.hrefs.forEach(function(href) {
href.par[href.key] = obj;
});
}
for(var n in refs) {
var ref = refs[n];
var obj = ref.obj;
ref.hrefs.forEach(function(href) {
href.par[href.key] = obj;
});
}
var body = root.Envelope.Body;
if (body.Fault) {
throw new Error(body.Fault.faultcode+': '+body.Fault.faultstring+(body.Fault.detail ? ': ' + body.Fault.detail : ''));
Expand Down Expand Up @@ -832,7 +832,7 @@ WSDL.prototype.objectToRpcXML = function(name, params, namespace, xmlns) {
WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns) {
var self = this,
parts = [],
xmlnsAttrib = false ? ' xmlns:'+namespace+'="'+xmlns+'"'+' xmlns="'+xmlns+'"' : '',
xmlnsAttrib = !xmlns ? '' : ' xmlns=\"'+xmlns+'\"',
ns = namespace ? namespace + ':' : '';

if (Array.isArray(obj)) {
Expand All @@ -844,12 +844,47 @@ WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns) {
parts.push(self.objectToXML(item, name));
}
}
else if (typeof obj === 'object') {
else if (typeof obj === 'object') {

// Added recursive check to see if we are getting a response or a request. This helps it to be dynamic instead of hardcoding certain things here.
function recursiveForAllProperties(obj) {
var req = false;
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (typeof obj[prop] === 'object') {
if(prop.toLowerCase().indexOf('request') != -1) {
req = true;
}
recursiveForAllProperties(obj[prop]);
} else {
if(prop.toLowerCase().indexOf('request') != -1) {
req = true;
}
}
}
}
return req;
}
var reqTest = recursiveForAllProperties(obj);

for (var name in obj) {
var child = obj[name];
parts.push(['<',ns,name,xmlnsAttrib,'>'].join(''));
parts.push(self.objectToXML(child, name));
parts.push(['</',ns,name,'>'].join(''));

// If it is a request, it needs to be formatted like normal. This is so that International payments will work.
if (reqTest) {
parts.push(['<',ns,name,xmlnsAttrib,'>'].join(''));
parts.push(self.objectToXML(child, name));
parts.push(['</',ns,name,'>'].join(''));
} else {
// If here, it is a response and needs to be changed so that null object return 'xls:nil="true"' for machines requests.
if (child !== '') {
parts.push(['<',ns,name,xmlnsAttrib,'>'].join(''));
parts.push(self.objectToXML(child, name));
parts.push(['</',ns,name,'>'].join(''));
} else {
parts.push(['<',ns,name + ' xsi:nil="true"',xmlnsAttrib,'/>'].join(''));
}
}
}
}
else if (obj) {
Expand Down
Loading