Merge pull request #5 from srinandan/master

add debug statements for oauth
diff --git a/healthcheck/index.js b/healthcheck/index.js
new file mode 100644
index 0000000..cc2bcb9
--- /dev/null
+++ b/healthcheck/index.js
@@ -0,0 +1,30 @@
+'use strict';
+
+var toobusy = require('toobusy-js');
+var debug = require('debug')('gateway:healthcheck');
+
+const HEALTHCHECK_URL = '/healthcheck';
+
+module.exports.init = function(config, logger, stats) {
+  return {
+   onrequest: function(req, res, next) {
+     var healthcheck_url = config['healthcheck_url'] ||  HEALTHCHECK_URL
+      if(healthcheck_url == req.url) {
+        var statusCode = (this.toobusy() ? 503 : 200)
+        debug(statusCode)
+        var healthInfo = {
+          memoryUsage: process.memoryUsage(),
+          cpuUsage: process.cpuUsage(),
+          uptime: process.uptime(),
+          pid: process.pid
+        }
+        res.writeHead(statusCode, { 'Content-Type': 'application/json' })
+        res.write(JSON.stringify(healthInfo))
+        res.end()
+      }
+      else {
+        next()
+      }
+    }
+  }
+}
diff --git a/healthcheck/package.json b/healthcheck/package.json
new file mode 100644
index 0000000..ae4a118
--- /dev/null
+++ b/healthcheck/package.json
@@ -0,0 +1,22 @@
+{
+  "name": "edgemicro-healthcheck",
+  "version": "1.0.0",
+  "description": "A basic plugin for healthcheck",
+  "dependencies": {
+    "debug": "^2.2.0",
+    "toobusy-js": "^0.5.1"
+  },
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/apigee/microgateway-plugins.git"
+  },
+  "author": "srinandan",
+  "license": "Apache 2.0",
+  "bugs": {
+    "url": "https://github.com/apigee/microgateway-plugins/issues"
+  },
+  "homepage": "https://github.com/apigee/microgateway-plugins#readme"
+}
diff --git a/json2xml/index.js b/json2xml/index.js
new file mode 100644
index 0000000..2b78e72
--- /dev/null
+++ b/json2xml/index.js
@@ -0,0 +1,194 @@
+'use strict';
+/**
+ * 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");
+
+module.exports.init = function (config, logger, stats) {
+
+	//initialize the variables to false
+
+	//variables to control whether request and/or response transformation should take place
+	var requestXML = false;
+	var requestJSON = false;
+	var responseJSON = false;
+	var responseXML = false;
+	//use these variables to determine the content type of response sent by target server
+	var responseIsXML = false;
+	var responseIsJSON = false;
+	//flag to control whether transfornation should take place
+	var 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 (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'];
+
+			if (req.headers['x-apigee-json2xml-disable']) disable = true;
+
+			debug("accept header: " + acceptType);
+			debug("content-type header: " + contentType);
+
+			//if plugin is disabled don't process headers.
+			if (!disable) {
+				if (method === "get" && acceptType === "application/xml") {
+					responseXML = true;
+				} else if (method === "get" && acceptType === "application/json") {
+					responseJSON = true;
+				} else if (method !== "get" && contentType === "application/json") {
+					requestJSON = true;
+					responseJSON = true;
+				} else if (method !== "get" && contentType === "application/xml") {
+					requestXML = true;
+					responseXML = true;
+				}
+			}
+			
+			debug("requestJSON flag is " + requestJSON);
+			debug("responseJSON flag is " + responseJSON);
+
+			debug("requestXML flag is " + requestXML);
+			debug("responseXML flag is " + responseXML);
+
+			debug("plugin disabled: " + 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');
+			if (data && data.length > 0 && disable == false) accumulateRequest(req, data);
+			next(null, null);
+		},
+		//
+		//
+		ondata_response: function(req, res, data, next) {
+			debug('plugin ondata_response');
+			if (data && data.length > 0 && disable == false) accumulateResponse(res, data);
+			next(null, null);
+		},
+		//
+		onend_request: function(req, res, data, next) {
+			debug('plugin onend_request');
+			if (data && data.length > 0 && disable == false) accumulateRequest(res, data);
+			var content = null;
+			if(req._chunks && req._chunks.length) {
+				content = Buffer.concat(req._chunks);
+			}
+			delete req._chunks;
+
+			//if pugin is disabled, don't do anything
+			if (!disable) {
+				if (requestJSON) {
+					//the request needs to be transformed to xml before sending to 
+					//the target server.
+
+					//set request content type.
+					req.setHeader('Content-Type', 'application/xml');
+					next(null, js2xmlparser.parse("Root",JSON.parse(content)));
+				} else if (requestXML) {
+					//set request content type.
+					req.setHeader('Content-Type', 'application/json');
+					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 && disable == false) accumulateResponse(res, data);
+
+			var contentType = res.getHeader('content-type');
+			if (contentType === "application/xml") {
+				responseIsXML = true;
+			} else if (contentType === "application/json") {
+				responseIsJSON = true;
+			}
+
+			debug("responseIsJSON flag is " + responseIsJSON);
+			debug("responseIsXML flag is" + responseIsXML);
+
+
+			var content = null;
+			if(res._chunks && res._chunks.length) {
+				content = Buffer.concat(res._chunks);
+			}
+			delete res._chunks;
+
+			//if disabled don't do anything.
+			if (!disable) {
+				if (responseXML && responseIsJSON) {
+					res.setHeader('Content-Type', 'application/xml');
+					next(null, js2xmlparser.parse("Root",JSON.parse(content)));
+				} else if (responseJSON && 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);
+			}
+		}		
+	};
+}
\ No newline at end of file
diff --git a/json2xml/package.json b/json2xml/package.json
new file mode 100644
index 0000000..52013ed
--- /dev/null
+++ b/json2xml/package.json
@@ -0,0 +1,24 @@
+{
+  "name": "edgemicro-json2xml",
+  "version": "1.0.0",
+  "description": "This plugin transforms JSON to XML and vice versa based on the Accept and Content-Type http headers",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "srinandan",
+  "license": "Apache 2.0",
+  "dependencies": {
+    "debug": "^2.2.0",
+    "js2xmlparser": "^2.0.2",
+    "xml2js": "^0.4.17"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/apigee/microgateway-plugins.git"
+  },
+  "bugs": {
+    "url": "https://github.com/apigee/microgateway-plugins/issues"
+  },
+  "homepage": "https://github.com/apigee/microgateway-plugins#readme"
+}
diff --git a/oauth/index.js b/oauth/index.js
index 3a4ccc4..75d4ed0 100644
--- a/oauth/index.js
+++ b/oauth/index.js
@@ -32,11 +32,13 @@
       } else if (config.allowNoAuthorization) {
         return next();
       } else {
+        debug('missing_authorization');
         return sendError(req, res, next, logger, stats, 'missing_authorization', 'Missing Authorization header');
       }
     } else {
       var header = authHeaderRegex.exec(req.headers[authHeaderName]);
       if (!header || header.length < 2) {
+        debug('Invalid Authorization Header');
         return sendError(req, res, next, logger, stats, 'invalid_request', 'Invalid Authorization header');
       }
 
@@ -74,8 +76,14 @@
       json: { 'apiKey': apiKey },
       headers: { 'x-dna-api-key': apiKey }
     }, function (err, response, body) {
-      if (err) return sendError(req, res, next, logger, stats, 'gateway_timeout', err.message);
-      if (response.statusCode !== 200) return sendError(req, res, next, logger, stats, 'access_denied', response.statusMessage);
+      if (err) {
+        debug('verify apikey gateway timeout');
+        return sendError(req, res, next, logger, stats, 'gateway_timeout', err.message);
+      }
+      if (response.statusCode !== 200) {
+        debug('verify apikey access_denied');
+        return sendError(req, res, next, logger, stats, 'access_denied', response.statusMessage);
+      }
       verify(body, config, logger, stats, middleware, req, res, next, apiKey);
     });
   }
@@ -100,10 +108,12 @@
         } else {
 
           if (err.name === 'TokenExpiredError') {
+            debug('Token expired error');
             return sendError(req, res, next, logger, stats, 'access_denied');
           }
 
           // todo: check other properties and/or give client more info?
+          debug('invalid token');
           return sendError(req, res, next, logger, stats, 'invalid_token');
         }
       }
@@ -164,12 +174,12 @@
 // then check if that proxy is one of the authorized proxies in bootstrap
 const checkIfAuthorized = module.exports.checkIfAuthorized = function checkIfAuthorized(config, urlPath, proxy, decodedToken) {
 
-  if (!decodedToken.api_product_list) { return false; }
+  if (!decodedToken.api_product_list) { debug('no api product list'); return false; }
 
   return decodedToken.api_product_list.some(function (product) {
 
     const validProxyNames = config.product_to_proxy[product];
-    if (!validProxyNames) { return false; }
+    if (!validProxyNames) { debug('no proxies found for product'); return false; }
 
     const apiproxies = config.product_to_api_resource[product];
     var matchesProxyRules = false;
@@ -177,6 +187,7 @@
       apiproxies.forEach(function (tempApiProxy) {
           if(matchesProxyRules){
             //found one
+            debug('found matching proxy rule');
             return;
           }
           const apiproxy = tempApiProxy.includes(proxy.base_path)