Search code examples
node.jssoaphttp-proxy

Can't make HTTPS SOAP call through corporate proxy - getaddrinfo ENOTFOUND


I am trying to make a SOAP call over HTTPS. Normally I'm doing this from Azure, which works fine, but I'm developing a new module and need to call it locally through our corporate proxy. If I just make the call normally, I get a SELF_SIGNED_CERTIFICATE_IN_CHAIN error message. In some cases I've been able to get around this using the https-proxy-agent module, but when I set it up as below, I am getting the error message getaddrinfo ENOTFOUND undefined undefined:80. I am sure that the proxy URL is valid. As I debugged the call, I could see the proxy information being passed to the call (initiating SOAP call via easy-soap-request, which in turn makes http calls via axios). Here are some relevant code snippets:

const soapRequest = require('easy-soap-request');
let httpsProxyAgent = require('https-proxy-agent');
var agent = new httpsProxyAgent({hostname:'proxy.address.com', port:8080, rejectUnauthorized:false});

// Next lines are within the class of my helper function

await (async () => {
            var response = {};
            try {
                console.log('Converting catalog number...');
                console.log(url);
                response = await soapRequest(url, headers, requestXML, 10000, {httpsAgent:agent});

            } catch (err) {
                console.log(`Error converting catalog number: ${err}`);
                processObj.message = 'SERVICE_DOWN';
                processObj.error = err.message;
                return;
            }

// Then do some more stuff with the response, but I don't get this far

The easy-soap-request module itself doesn't really seem to be doing much. Makes me wonder why I don't just do a post with request-promise-native (which I use for all my other API calls), but I suppose that's beside the point. Here is the easy-soap-request module for reference. The only thing I noticed is that it's actually using axios-https-proxy-fix instead of axios.

const axios = require('axios-https-proxy-fix');

module.exports = function soapRequest(url, headers, xml, timeout = 10000, proxy = false) {
  return new Promise((resolve, reject) => {
    axios({
      method: 'post',
      url,
      headers,
      data: xml,
      timeout,
      proxy,
    }).then((response) => {
      resolve({
        response: {
          headers: response.headers,
          body: response.data,
          statusCode: response.status,
        },
      });
    }).catch((error) => {
      if (error.response) {
        console.error(`SOAP FAIL: ${error}`);
        reject(error.response.data);
      } else {
        console.error(`SOAP FAIL: ${error}`);
        reject(error);
      }
    });
  });
};

Solution

  • It seems my comment about that easy-soap-request not actually adding much value proved to be correct. I was easily able to use request-promise-native as a drop-in replacement. And because the latter respects my .env proxy variable, I didn't need to use an httpsAgent either. I still had to add rejectUnauthorized:false to the options. Here is the updated code, with the original line commented out:

    //const soapRequest = require('easy-soap-request');
    //let httpsProxyAgent = require('https-proxy-agent');
    //var agent = new httpsProxyAgent({hostname:'proxy.address.com', port:8080, rejectUnauthorized:false});
    const request = require('request-promise-native');
    
    // Next lines are within the class of my helper function
    
    await (async () => {
                var response = {};
                try {
                    console.log('Converting catalog number...');
                    console.log(url);
                    //response = await soapRequest(url, headers, requestXML, 10000, {httpsAgent:agent});
                    response = await request({
                        url: url,
                        method: 'POST',
                        headers: headers,
                        body: requestXML,
                        rejectUnauthorized: false
                    })
    
                } catch (err) {
                    console.log(`Error converting catalog number: ${err}`);
                    processObj.message = 'SERVICE_DOWN';
                    processObj.error = err.message;
                    return;
                }
    

    Works like a charm! I'm actually going to go back and update my other integration modules to use this method instead of easy-soap-request throughout the application.