@tulip/node-red-tulip-api
Version:
Node-RED nodes for sending data to the Tulip API
144 lines (128 loc) • 4.08 kB
JavaScript
;
const https = require('https');
const http = require('http');
const HttpsProxyAgent = require('https-proxy-agent');
const url = require('url');
/**
* Makes an HTTP request to the designated endpoint with options. Passes on the HTTP response
* in an outgoing message.
* @param {Object} httpLib - http or https library, depending on which protocol to use
* @param {URL} endpoint - URL to send the request to
* @param {Object} options - options for the http(s) request
* @param {string} [body] - stringified JSON body
* @param {function} error - function to log errors
* @param {function} send - function to pass the outgoing message to.
* @param {function} done - function to call after the message has been sent
*/
const doHttpRequest = function (
httpLib,
endpoint,
options,
body,
error,
send,
done
) {
const req = httpLib.request(endpoint, options, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
// Request returns error code
error(new Error(`Response status code ${res.statusCode}`));
}
let respBody = '';
res.on('data', (chunk) => {
respBody += chunk;
});
// At the end of response, pass response body as msg.payload
res.on('end', () => {
try {
const convertJSON =
res.headers['content-type'] &&
res.headers['content-type'].includes('application/json');
// if response is JSON, parse body as JSON
const payload = convertJSON ? JSON.parse(respBody) : respBody;
const msg = {
response: res,
payload,
};
send(msg);
done();
} catch (err) {
done(err);
}
});
});
req.on('error', (err) => {
done(err);
});
if (body) {
req.write(body);
}
req.end();
};
/**
* Add the Machine API endpoint path to a Tulip factory url
* @param {URL} url - the base factory url
* @returns {URL} url with added path for Machine API endpoint
*/
function getMachineAttributeEndpoint(url) {
const urlCopy = new URL(url);
urlCopy.pathname = '/api/v3/attributes/report';
return urlCopy;
}
/**
* @param {string} protocol - either https or http
* @returns library for sending http(s) requests
*/
const getHttpLib = function (protocol) {
const httpLibs = {
http,
https,
};
const httpLib = httpLibs[protocol];
if (!httpLib) {
throw new Error(`Expected protocol of http or https, got: ${protocol}`);
}
return httpLib;
};
/**
* Returns the agent for http(s) requests.
*
* Respects the http_proxy environment variable. If no proxy, then returns an agent for the
* specified protocol with given keep-alive options. If present, then returns an HttpProxyAgent
* for the proxy specified by http_proxy. If there is an issue with the http_proxy url,
* then defaults back to the normal agent with no proxy & logs an error.
*
* @param {Object} httpLib either http or https library
* @param {boolean} keepAlive whether to keep sockets around even when there are no
outstanding requests, so they can be used for future requests without
having to reestablish a TCP connection.
* @param {number} keepAliveMsecs initial delay (in milliseconds) for TCP keep-alive packets
*/
const getHttpAgent = function (httpLib, keepAlive, keepAliveMsecs) {
// honor any http proxy settings passed from env
const proxyUrl = process.env['http_proxy'];
if (proxyUrl) {
// TODO: Switch to hpagent, which is what the newer Node-RED HTTP nodes use.
// https-proxy-agent doesn't support keep alive settings, so ignore them.
try {
const options = url.parse(proxyUrl);
const agent = HttpsProxyAgent(options);
return agent;
} catch (err) {
console.error(
`could not create HttpsProxyAgent from env http_proxy=${proxyUrl}`
);
}
}
// No proxy, use keep alive options
return new httpLib.Agent({
keepAlive: keepAlive,
keepAliveMsecs: keepAliveMsecs,
});
};
module.exports = {
doHttpRequest,
getHttpAgent,
getHttpLib,
getMachineAttributeEndpoint,
};