microgateway-plugins
Version:
Plugins for Apige Edge Microgateway
215 lines (191 loc) • 7.26 kB
JavaScript
;
/**
* This plugin accumulates data chunks from the client into an array
* property on the request or response, concats them on end. Then
* transforms the request or response based on 'accept' or 'content-type'
* headers.
*
* If the HTTP verb is GET and the Accept type does not match the
* response type, the plugin will attempt to transform it (from
* xml to json or vice versa).
*
* If the HTTP verb is NOT GET then the content-type is used. If the
* content-type is set to 'json', then an attempt is made to transform to
* 'xml' and vice-versa.
*
* This plugin can be disabled by sending the 'x-apigee-json2xml' http
* header.
*
* Users should be aware that buffering large requests or responses in
* memory can cause Apigee Edge Microgateway to run out of memory under
* high load or with a large number of concurrent requests. So this plugin
* should only be used when it is known that request/response bodies are small.
*/
var debug = require('debug')('plugin:json2xml');
//library to convert json to xml
var js2xmlparser = require("js2xmlparser");
//library to convert xml to json
var parseString = require("xml2js").parseString;
//var util = require("util");
const helperFunctions = require('../lib/helperFunctions');
const LOG_TAG_COMP = 'json2xml';
module.exports.init = function (config, logger, stats) {
//initialize the variables to false
//variables to control whether request and/or response transformation should take place
const getControlFlags = () => {
return {
requestXML : false,
requestJSON : false,
responseJSON : false,
responseXML : false,
responseIsXML : false,
responseIsJSON : false,
disable : false
}
}
//method to accumulate responses
function accumulateResponse(res, data) {
debug('plugin accumulateResponse');
if (!res._chunks) res._chunks = [];
res._chunks.push(data);
}
//method to accumulate requests
function accumulateRequest(req, data) {
debug('plugin accumulateRequest');
if (req.jsonXmlFlags.disable) return;
if (!req._chunks) req._chunks = [];
req._chunks.push(data);
}
return {
onrequest: function(req, res, next) {
debug('plugin onrequest');
var method = req.method.toLowerCase();
var acceptType = req.headers['accept'];
var contentType = req.headers['content-type'];
req.jsonXmlFlags = getControlFlags();
if (req.headers['x-apigee-json2xml-disable']) req.jsonXmlFlags.disable = true;
debug("accept header: " + acceptType);
debug("content-type header: " + contentType);
//if plugin is disabled don't process headers.
if (!req.jsonXmlFlags.disable) {
if (method === "get" && acceptType.indexOf("application/xml") > -1) {
req.jsonXmlFlags.responseXML = true;
} else if (method === "get" && acceptType.indexOf("application/json") > -1) {
req.jsonXmlFlags.responseJSON = true;
} else if (method !== "get" && contentType.indexOf("application/json") > -1) {
req.jsonXmlFlags.requestJSON = true;
req.jsonXmlFlags.responseJSON = true;
//set request content type.
req.headers['content-type'] = 'application/xml';
} else if (method !== "get" && contentType.indexOf("application/xml") > -1) {
req.jsonXmlFlags.requestXML = true;
req.jsonXmlFlags.responseXML = true;
//set request content type.
req.headers['content-type'] = 'application/json';
}
}
debug("requestJSON flag is " + req.jsonXmlFlags.requestJSON);
debug("responseJSON flag is " + req.jsonXmlFlags.responseJSON);
debug("requestXML flag is " + req.jsonXmlFlags.requestXML);
debug("responseXML flag is " + req.jsonXmlFlags.responseXML);
debug("plugin disabled: " + req.jsonXmlFlags.disable);
next();
},
// indicates start of target response
// response headers and status code should be available at this time
onresponse: function(req, res, next) {
debug('plugin onresponse');
//nothing to do on response
next();
},
//
ondata_request: function(req, res, data, next) {
debug('plugin ondata_request');
data = helperFunctions.toBuffer(data);
if (data && data.length > 0 && req.jsonXmlFlags.disable === false) accumulateRequest(req, data);
next(null, null);
},
//
//
ondata_response: function(req, res, data, next) {
debug('plugin ondata_response');
data = helperFunctions.toBuffer(data);
if (data && data.length > 0 && req.jsonXmlFlags.disable === false) accumulateResponse(res, data);
next(null, null);
},
//
onend_request: function(req, res, data, next) {
debug('plugin onend_request');
if (data && data.length > 0 && req.jsonXmlFlags.disable === false) accumulateRequest(res, data);
var content = null;
if(req._chunks && req._chunks.length) {
try {
content = Buffer.concat(req._chunks);
} catch(err) {
debug('Error in creating buffered content', err);
content='';
logger.eventLog({level:'warn', req: req, res: res, err:err, component:LOG_TAG_COMP }, "Error in creating buffered content");
}
}
delete req._chunks;
//if pugin is disabled, don't do anything
if (!req.jsonXmlFlags.disable) {
if (req.jsonXmlFlags.requestJSON) {
//the request needs to be transformed to xml before sending to
//the target server.
next(null, js2xmlparser.parse("Root",JSON.parse(content)));
} else if (req.jsonXmlFlags.requestXML) {
parseString(content.toString(), function(err, js){
if (err) next (err);
next(null, JSON.stringify(js));
});
} else { //do nothing
next(null, content);
}
} else {
next(null, content);
}
},
//
onend_response: function(req, res, data, next) {
debug('plugin onend_request');
if (data && data.length > 0 && req.jsonXmlFlags.disable === false) accumulateResponse(res, data);
var contentType = res.getHeader('content-type');
if (contentType && contentType.indexOf("application/xml") > -1) {
req.jsonXmlFlags.responseIsXML = true;
} else if (contentType && contentType.indexOf("application/json") > -1) {
req.jsonXmlFlags.responseIsJSON = true;
}
debug("responseIsJSON flag is " + req.jsonXmlFlags.responseIsJSON);
debug("responseIsXML flag is" + req.jsonXmlFlags.responseIsXML);
var content = null;
if(res._chunks && res._chunks.length) {
try {
content = Buffer.concat(res._chunks);
} catch (err) {
debug('Error in creating buffered content', err);
content = '';
logger.eventLog({ level: 'warn', req: req, res: res, err: err, component: LOG_TAG_COMP }, "Error in creating buffered content");
}
}
delete res._chunks;
//if disabled don't do anything.
if (!req.jsonXmlFlags.disable) {
if (req.jsonXmlFlags.responseXML && req.jsonXmlFlags.responseIsJSON) {
res.setHeader('Content-Type', 'application/xml');
next(null, js2xmlparser.parse("Root",JSON.parse(content)));
} else if (req.jsonXmlFlags.responseJSON && req.jsonXmlFlags.responseIsXML) {
res.setHeader('Content-Type', 'application/json');
parseString(content.toString(), function(err, js){
if (err) next (err);
next(null, JSON.stringify(js));
});
} else {
next(null, content);
}
} else {
next(null, content);
}
}
};
}