transport.js 5.68 KB
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

const MODULE_NAME = 'TRANSPORT';

const http = require('http');
const url = require('url');
const request = require('request');

const config = require('komodo-sdk/config');
const logger = require('tektrans-logger');
const uniqid = require('uniqid');

let callback;

const maxSendRetry = Number(config.max_send_retry) || 5;
const sleepBeforeRetryMs = (Number(config.sleep_before_retry_secs) || 60) * 1000;

function sleep(ms) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(null);
        }, ms);
    });
}

function cleanResponseToPartner(_data) {
    const data = JSON.parse(JSON.stringify(_data));

    delete data.origin;
    delete data.origin_report_ip;
    delete data.origin_report_port;
    delete data.reverse_url;
    delete data.misc;
    delete data.inquiry_only;

    // eslint-disable-next-line no-restricted-syntax
    for (const key in data) {
        // eslint-disable-next-line no-prototype-builtins
        if (data.hasOwnProperty(key)) {
            if (key.indexOf('[') >= 0) { delete data[key]; }
        }
    }

    return data;
}

function onRequestFromPartner(req, res) {
    const xid = uniqid();

    const methodName = 'onRequestFromPartner';

    const remoteAddress = req.connection.remoteAddress.replace(/^::ffff:/, '');

    const qs = url.parse(req.url, true).query;

    logger.verbose('Got a request from partner', {
        xid,
        module_name: MODULE_NAME,
        method_name: methodName,
        remote_address: remoteAddress,
        url: req.url,
        qs,
    });

    const partner = `${qs.terminal_name}@${remoteAddress}`;
    const {
        password,
        reverse_url: reverseUrl,
        product_name: productName,
        destination,
        request_id: requestId,
    } = qs;

    if (
        !qs.terminal_name || !password || !reverseUrl || !productName || !destination || !requestId
    ) {
        logger.verbose('Invalid request from partner', {
            xid,
            hasTerminalName: !!qs.terminal_name,
            hasPassword: !!password,
            hasProductName: !!productName,
            hasDestination: !!destination,
            hasRequestId: !!requestId,
        });

        res.end('INVALID REQUEST');
        return;
    }

    const msg = [productName, destination, password, requestId].join('.');

    callback.onIncomingMessage(
        {
            me: config.username,
            partner,
            msg: msg.trim(),
            reverse_url: reverseUrl,
        },

        (err, _result) => {
            const result = cleanResponseToPartner(_result);

            logger.verbose('C2DEEE9D Forwarding CORE response to partner as direct response', {
                xid,
                result: _result,
            });

            res.end(JSON.stringify(result));
        },
    );
}

function createHttpServer() {
    const listenPort = config.center_port;

    http.createServer(onRequestFromPartner).listen(listenPort, () => {
        logger.info('Center HTTP server listen on tcp port', { listenPort });
    });
}

function init(cb) {
    if (!cb) {
        logger.warn('Callback is not defined');
        // eslint-disable-next-line no-console
        console.trace();
        process.exit(1);
        return;
    }

    callback = cb;

    createHttpServer();
}

async function sleepAndResend(xid, partner, msg, params, retry) {
    if (retry > maxSendRetry) {
        logger.verbose('Retry exceeded', {
            xid,
            retry: retry || 0,
            params,
        });
        return;
    }

    logger.verbose('Sleep before resend', { xid, sleepBeforeRetryMs });

    await sleep(sleepBeforeRetryMs);
    // eslint-disable-next-line no-use-before-define
    send(partner, msg, params, retry + 1);
}

function send(partner, msg, params, retry) {
    const xid = uniqid();

    if (retry) {
        logger.verbose('F39C9082: Resending', {
            xid,
            partner,
            msg,
            params,
            retry,
        });
    } else {
        logger.verbose('Got response from CORE', {
            xid,
            partner,
            msg,
            params,
            retried: retry || 0,
        });
    }

    if (!params || !params.reverse_url) {
        logger.verbose('Undefined reverse_url, not forwarding message to partner', { xid });
        return;
    }

    if (params.reverse_url.indexOf('http') < 0) {
        logger.verbose('Invalid reverse_url, not forwarding message to partner', { xid });
        return;
    }

    if (params.rc === '68') {
        logger.verbose('Ignoring RC 68 reverse report', { xid });
        return;
    }

    const reqOptions = {
        url: params.reverse_url,
        qs: cleanResponseToPartner(params),
    };

    logger.verbose('Sending reverse report to partner', {
        xid,
        request: reqOptions,
    });

    request(reqOptions, (err, res, body) => {
        if (err) {
            logger.warn('Error sending reverse report to partner', {
                xid,
                request: reqOptions,
                err,
            });

            sleepAndResend(xid, partner, msg, params, retry || 0);
            return;
        }

        /*
        if (res.statusCode != 200) {
            logger.warn('Partner not returning HTTP status code 200 on reverse report', {
                request: reqOptions, http_status: res.statusCode,
            });

            _sleepAndResend(partner, msg, params, retry);
            return;
        }
        */

        logger.verbose('Reverse report has been sent to partner', {
            xid,
            request: reqOptions,
            httpStatus: res && res.statusCode,
            responseBody: body,
        });
    });
}

exports.init = init;
exports.send = send;