Using SOAP in XULRunner 1.9

Since the native SOAP interface was removed from Gecko 1.9, those stuck speaking to SOAP APIs need a new place to turn. After some experimentation, the following seems to be the best way to speak SOAP in XULRunner.

Several alternatives were considered:

  • soapclient 2.4 - This library contains a few JavaScript mistakes but nevertheless seems (fairly) widely used, mature and tested. It requires servers to support WSDL which took it out of the running.
  • XULRunner 1.8.* - Using an old XULRunner is certainly an option but brings up a host of speed, stability and memory issues.
  • Manual implementation - The author doesn't know enough about SOAP to implement a robust client in an afternoon.

The best solution proved to be jqSOAPClient. This library doesn't require a WSDL. Though jQuery is listed as a dependency, it can be worked around easily. (There is a diff below.)

You'll need:

Making a SOAP call

var url = 'http://example.com/soap/';
var ns = 'http://example.com/soap/namespace';
var method = 'foo';
var params = {
    'foo': 'bar',
    'baz': 'bang'
};
var callback = function(obj) {
    Components.utils.reportError(obj.toSource());
};

SOAPClient.Proxy = url;
var body = new SOAPObject(method);
body.ns = ns;
for (var k in params) {
    body.appendChild(new SOAPObject(k).val(params[k]));
}
var req = new SOAPRequest(url, body);
req.Action = ns + '#' + method;
SOAPClient.SendRequest(req, callback);

Diff between jqSOAPClient.js and saSOAPClient.js

42c42
< 					var jsOut = $.xmlToJSON(xData.responseXML);
---
> 					var jsOut = XMLObjectifier.xmlToJSON(xData.responseXML);
46,60c46,62
< 			$.ajax({
< 				 type: "POST",
< 				 url: SOAPClient.Proxy,
< 				 dataType: "xml",
< 				 processData: false,
< 				 data: content,
< 				 complete: getResponse,
< 				 contentType: SOAPClient.ContentType + "; charset=\"" + SOAPClient.CharSet + "\"",
< 				 beforeSend: function(req) {
< 					req.setRequestHeader("Method", "POST");
< 				 	req.setRequestHeader("Content-Length", SOAPClient.ContentLength);					
< 					req.setRequestHeader("SOAPServer", SOAPClient.SOAPServer);
< 					req.setRequestHeader("SOAPAction", soapReq.Action);
< 				 }
< 			});
---
> 			var xhr = new XMLHttpRequest();
> 			xhr.mozBackgroundRequest = true;
> 			xhr.open('POST', SOAPClient.Proxy, true);
> 			xhr.onreadystatechange = function() {
> 				if (4 != xhr.readyState) { return; }
> 				getResponse(xhr);
> 			};
> 			var headers = {
> 				'Method': 'POST',
> 				'Content-Type': SOAPClient.ContentType + '; charset="' +
> 					SOAPClient.CharSet + '"',
> 				'Content-Length': SOAPClient.ContentLength,
> 				'SOAPServer': SOAPClient.SOAPServer,
> 				'SOAPAction': soapReq.Action
> 			};
> 			for (var h in headers) { xhr.setRequestHeader(h, headers[h]); }
> 			xhr.send(content);