Node.JS and Soap Services

Recently I worked on a project that was developed entirely using Node.JS. There was a need to integrate the application with an XDS registry/repository, which exposed its functionality via SOAP services. I investigated soap client interfaces for Node.JS, and found three; node-soap, easysoap, and douche. After testing each of them against the XDS registry/repository, none of them seemed capable of handling a WSDL that made use of complex objects. It seems to me that those soap client modules only work with SOAP services that expose very simple actions.

Ultimately, I had to scrap the notion of using a single module, and implemented my own solution with a couple different modules. The solution is not perfect, but it worked better than any of the others that I found.

Transmitting Messages

When you boil down a SOAP service, it is really just a simple HTTP POST of XML content, where the XML content contains structural information about the message itself. You can use the NET module to perform unsecured transaction of messages, and the HTTPS module to send secured messages.

For secured transport, I initially attempted to use the TLS module… That did not work. When using TLS, I found that although I was able to authenticate (even with mutual authentication), but the messages did not decode correctly. I could send a message just fine, but the message I received contained odd numbers mixed into the response. Other than the numbers, the message seemed accurate, so I believe my messages were being encoded/sent just fine; but the response was not formed well.

Fortunately, the NET and HTTPS modules are very similar. If Javascript had a concept for interfaces, I would say that these two modules used a common interface. Because of this similarity, I was able to add in some configuration parameters to very easily swap out secured vs. unsecured. The only thing that needed to change was the way the socket was initialized.

The only major difference I found between the two modules, was that in the NET module, I had to construct the POST request headers manually, where-as in the HTTPS module, it handles that automatically with some additional parameters.

var receiveData = function(sock) {
    var response = '';

    sock.on('data', function(data) {
        response += data;
    });

    sock.on('end', function() {
    	// Do something with 'response' variable
    });
};

var sendData = function(sock) {
    // req only used for standard HTTP
    var req =
        'POST ' + path + ' HTTP/1.1\r\n' +
        'Host: localhost\r\n' +
        'Content-Length: ' + data.length + '\r\n' +
        'Content-Type: application/soap+xml; charset=utf-8\r\n' +
        'SOAPAction: "http://www.w3.org/2003/05/soap-envelope"\r\n' +
        '\r\n';

    if (!config.use_tls) {
        sock.write(req);
    }

    sock.write(data);

    if (config.use_tls) {
        sock.end();
    }
};

if (config.use_tls) {
    var options = {
        hostname: config.host,
        path: path,
        port: config.port,
        method: 'POST',
        agent: false,
        ca: [fs.readFileSync(config.tls_ca)],
        key: fs.readFileSync(config.tls_key),
        cert: fs.readFileSync(config.tls_cert),
        rejectUnauthorized: false
    };

    var sock = https.request(options, function(connResponse) {
        receiveData(connResponse);
    });

    sock.on('error', function(err) {
        console.log("Socket error: " + err);
        callback(null, null, err);
    });

    sendData(sock);
} else {
    var sock = net.Socket();
    
    sock.connect(config.port, config.host, function() {
        sock.on('error', function(err) {
            console.log("Socket error: " + err);
        });

        receiveData(sock);
        sendData(sock);
    });
}

Constructing the request

I took the ultimate short-cut in forming the SOAP XML request message. I copied an example of a previously working request, and replaced values in the request with my own values.

Ideally, I could use a pre-existing module to take a JSON object model and serialize it into an XML string. However, no module seems to exist that handles namespaces properly.

If I had more time (and was more worried about “properly” constructing the request messages), I could have built my own custom object model and JSON->XML serializer. In my case, though, the need for that didn’t justify the time it would have taken to implement.

Parsing the response

After receiving a full response from the server, I used the xml2js module to deserialize the XML string into an object model. There were many pieces of information I needed to get at within the response; having an object model was critical to parsing the values I needed.

Traversing the object model within Node.JS is pretty straight forward. I attempted to use some XPATH-like modules for JSON, including JSONPath (xpath for JSON) and JSONSelect (CSS-like selectors for JSON). Neither of those modules seemed to work with the object model that xml2js constructs. Perhaps those modules work well with more straight-forward object models, but the object model that xml2js produces includes some odd characteristics to account for namespaces and the concept of “xml attributes”, which those modules just didn’t seem to like.

Eventually, I may want to construct my own Node.JS library for basic XPATH implementation for JSON…

Leave a Reply

Your email address will not be published. Required fields are marked *