Tweak rate limiting implementation
diff --git a/api-gateway-config/scripts/lua/lib/filemgmt.lua b/api-gateway-config/scripts/lua/lib/filemgmt.lua
index 0db3451..4fb96f7 100644
--- a/api-gateway-config/scripts/lua/lib/filemgmt.lua
+++ b/api-gateway-config/scripts/lua/lib/filemgmt.lua
@@ -35,7 +35,8 @@
 function _M.createRouteConf(baseConfDir, namespace, gatewayPath, routeObj)
     routeObj = utils.serializeTable(cjson.decode(routeObj))
     local prefix = utils.concatStrings({"\t", "include /etc/api-gateway/conf.d/commons/common-headers.conf;", "\n",
-                                        "\t", "set $upstream https://172.17.0.1;", "\n\n"})
+                                        "\t", "set $upstream https://172.17.0.1;", "\n",
+                                        "\t", "set $namespace ", namespace, ";\n\n"})
     -- Set route headers and mapping by calling routing.processCall()
     local outgoingRoute = utils.concatStrings({"\t",   "access_by_lua_block {",                   "\n",
                                                "\t\t", "local routing = require \"routing\"",     "\n",
@@ -50,7 +51,7 @@
     local file, err = io.open(utils.concatStrings({baseConfDir, namespace, "/", gatewayPath, ".conf"}), "w")
     if not file then
         ngx.status(500)
-        ngx.say("Error adding to endpoint conf file: " .. err)
+        ngx.say(utils.concatStrings({"Error adding to endpoint conf file: ", err}))
         ngx.exit(ngx.status)
     end
     local location = utils.concatStrings({"location /api/", namespace, "/", gatewayPath, " {\n",
@@ -58,7 +59,7 @@
                                           outgoingRoute,
                                           proxyPass,
                                           "}\n"})
-    file:write(location .. "\n")
+    file:write(utils.concatStrings({location, "\n"}))
     file:close()
 end
 
diff --git a/api-gateway-config/scripts/lua/lib/redis.lua b/api-gateway-config/scripts/lua/lib/redis.lua
index 0576bab..4cf63db 100644
--- a/api-gateway-config/scripts/lua/lib/redis.lua
+++ b/api-gateway-config/scripts/lua/lib/redis.lua
@@ -47,7 +47,7 @@
     local connect, err = red:connect(host, port)
     if not connect then
         ngx.status = 500
-        ngx.say("Failed to connect to redis: " .. err)
+        ngx.say(utils.concatStrings({"Failed to connect to redis: ", err}))
         ngx.exit(ngx.status)
     end
 
@@ -56,7 +56,7 @@
         local res, err = red:auth(password)
         if not res then
             ngx.status = 500
-            ngx.say("Failed to authenticate: " .. err)
+            ngx.say(utils.concatStrings({"Failed to authenticate: ", err}))
             ngx.exit(ngx.status)
         end
     end
@@ -118,7 +118,7 @@
     local ok, err = red:hset(key, field, routeObj)
     if not ok then
         ngx.status = 500
-        ngx.say("Failed adding Route to redis: " .. err)
+        ngx.say(utils.concatStrings({"Failed adding Route to redis: ", err}))
         ngx.exit(ngx.status)
     end
 end
@@ -238,8 +238,8 @@
             local routeObj = _M.getRoute(red, redisKey, "route", ngx)            
             _M.createRoute(red, redisKey, "route", routeObj, ngx)
             filemgmt.createRouteConf(BASE_CONF_DIR, namespace, gatewayPath, routeObj)
-            ngx.say(redisKey .. " updated")
-            ngx.log(ngx.INFO, redisKey .. " updated")
+            ngx.say(utils.concatStrings({redisKey, " updated"}))
+            ngx.log(ngx.INFO, utils.concatStrings({redisKey, " updated"}))
             ngx.flush(true)
             
             local ok, err = red:psubscribe("__keyspace@0__:routes:*:*")
diff --git a/api-gateway-config/scripts/lua/lib/resty/limit/req.lua b/api-gateway-config/scripts/lua/lib/resty/limit/req.lua
new file mode 100644
index 0000000..5e130e0
--- /dev/null
+++ b/api-gateway-config/scripts/lua/lib/resty/limit/req.lua
@@ -0,0 +1,186 @@
+--  Copyright (C) 2014 Monkey Zhang (timebug), UPYUN Inc.
+--  All rights reserved.
+--
+--   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+--
+--   Redistributions of source code must retain the above copyright notice,
+--   this list of conditions and the following disclaimer.
+--
+--   Redistributions in binary form must reproduce the above copyright notice,
+--   this list of conditions and the following disclaimer in the documentation
+--   and/or other materials provided with the distribution.
+--
+--   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+--   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+--   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+--   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+--   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+--   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+--   DEALINGS IN THE SOFTWARE.
+
+local floor = math.floor
+local tonumber = tonumber
+
+
+local _M = { _VERSION = "0.01", OK = 1, BUSY = 2, FORBIDDEN = 3 }
+
+
+local redis_limit_req_script_sha
+local redis_limit_req_script = [==[
+local key = KEYS[1]
+local rate = tonumber(KEYS[2])
+local now, interval = tonumber(KEYS[3]), tonumber(KEYS[4])
+
+local excess, last, forbidden = 0, 0, 0
+
+local res = redis.pcall('GET', key)
+if type(res) == "table" and res.err then
+    return {err=res.err}
+end
+
+if res and type(res) == "string" then
+    local v = cjson.decode(res)
+    if v and #v > 2 then
+        excess, last, forbidden = v[1], v[2], v[3]
+    end
+
+    if forbidden == 1 then
+        return {3, excess} -- FORBIDDEN
+    end
+
+    local ms = math.abs(now - last)
+    excess = excess - rate * ms / 1000 + 1000
+
+    if excess < 0 then
+        excess = 0
+    end
+
+    if excess > 0 then
+        if interval > 0 then
+            local res = redis.pcall('SET', key,
+                                    cjson.encode({excess, now, 1}))
+            if type(res) == "table" and res.err then
+                return {err=res.err}
+            end
+
+            local res = redis.pcall('EXPIRE', key, interval)
+            if type(res) == "table" and res.err then
+                return {err=res.err}
+            end
+        end
+
+        return {2, excess} -- BUSY
+    end
+end
+
+local res = redis.pcall('SET', key, cjson.encode({excess, now, 0}))
+if type(res) == "table" and res.err then
+    return {err=res.err}
+end
+
+local res = redis.pcall('EXPIRE', key, 60)
+if type(res) == "table" and res.err then
+    return {err=res.err}
+end
+
+return {1, excess}
+]==]
+
+
+local function redis_lookup(conn, zone, key, rate, duration)
+    local red = conn
+
+    if not redis_limit_req_script_sha then
+        local res, err = red:script("LOAD", redis_limit_req_script)
+        if not res then
+            return nil, err
+        end
+
+        ngx.log(ngx.NOTICE, "load redis limit req script")
+
+        redis_limit_req_script_sha = res
+    end
+
+    local now = ngx.now() * 1000
+    local res, err = red:evalsha(redis_limit_req_script_sha, 4,
+                                 zone .. ":" .. key, rate, now, duration)
+    if not res then
+        redis_limit_req_script_sha = nil
+        return nil, err
+    end
+
+    -- put it into the connection pool of size 100,
+    -- with 10 seconds max idle timeout
+    local ok, err = red:set_keepalive(10000, 100)
+    if not ok then
+        ngx.log(ngx.WARN, "failed to set keepalive: ", err)
+    end
+
+    return res
+end
+
+
+function _M.limit(cfg)
+    if not cfg.conn then
+        local ok, redis = pcall(require, "resty.redis")
+        if not ok then
+            ngx.log(ngx.ERR, "failed to require redis")
+            return _M.OK
+        end
+
+        local rds = cfg.rds or {}
+        rds.timeout = rds.timeout or 1
+        rds.host = rds.host or "127.0.0.1"
+        rds.port = rds.port or 6379
+
+        local red = redis:new()
+
+        red:set_timeout(rds.timeout * 1000)
+
+        local ok, err = red:connect(rds.host, rds.port)
+        if not ok then
+            ngx.log(ngx.WARN, "redis connect err: ", err)
+            return _M.OK
+        end
+
+        cfg.conn = red
+    end
+
+    local conn = cfg.conn
+    local zone = cfg.zone or "limit_req"
+    local key = cfg.key or ngx.var.remote_addr
+    local rate = cfg.rate or "1r/s"
+    local interval = cfg.interval or 0
+    local log_level = cfg.log_level or ngx.NOTICE
+
+    local scale = 1
+    local len = #rate
+
+    if len > 3 and rate:sub(len - 2) == "r/s" then
+        scale = 1
+        rate = rate:sub(1, len - 3)
+    elseif len > 3 and rate:sub(len - 2) == "r/m" then
+        scale = 60
+        rate = rate:sub(1, len - 3)
+    end
+
+    rate = floor((tonumber(rate) or 1) * 1000 / scale)
+
+    local res, err = redis_lookup(conn, zone, key, rate, interval)
+    if res and (res[1] == _M.BUSY or res[1] == _M.FORBIDDEN) then
+        if res[1] == _M.BUSY then
+            ngx.log(log_level, 'limiting requests, excess ' ..
+                        res[2]/1000 .. ' by zone "' .. zone .. '"')
+        end
+        return
+    end
+
+    if not res and err then
+        ngx.log(ngx.WARN, "redis lookup err: ", err)
+    end
+
+    return _M.OK
+end
+
+
+return _M
\ No newline at end of file
diff --git a/api-gateway-config/scripts/lua/lib/utils.lua b/api-gateway-config/scripts/lua/lib/utils.lua
index 5ab512e..5f34e53 100644
--- a/api-gateway-config/scripts/lua/lib/utils.lua
+++ b/api-gateway-config/scripts/lua/lib/utils.lua
@@ -61,7 +61,7 @@
       logger.debug(concatStrings({'did not find table: ', tostring(k), '; ', tostring(v)}))
       tt[#tt+1] = concatStrings({'"', tostring(v), '"'})
     else
-      logger.debug(concatStrings({'did not find table: ' .. tostring(k) .. '; ' .. tostring(v)}))
+      logger.debug(concatStrings({'did not find table: ', tostring(k), '; ', tostring(v)}))
       tt[#tt+1] = tostring(v)
     end
   end
diff --git a/api-gateway-config/scripts/lua/management.lua b/api-gateway-config/scripts/lua/management.lua
index 95b8b1a..b4d1a1e 100644
--- a/api-gateway-config/scripts/lua/management.lua
+++ b/api-gateway-config/scripts/lua/management.lua
@@ -165,6 +165,7 @@
 --
 function _M.subscribe()
     -- Initialize and connect to redis
+    logger.debug(utils.concatStrings({'Subscribing to Redis with host: ' , REDIS_HOST, '; port: ', REDIS_PORT, '; pass:', REDIS_PASS}))
     local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 60000, ngx)
     redis.subscribe(red, ngx)
 end
@@ -175,6 +176,7 @@
 --
 function _M.unsubscribe()
     -- Initialize and connect to redis
+    logger.debug(utils.concatStrings({'Unsubscribing to Redis with host: ' , REDIS_HOST, '; port: ', REDIS_PORT, '; pass:', REDIS_PASS}))
     local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000, ngx)
     redis.unsubscribe(red, ngx)
 
@@ -221,10 +223,10 @@
     local decoded = nil
     local jsonStringList = {}
     for key, value in pairs(args) do
-	table.insert(jsonStringList, key)
+    table.insert(jsonStringList, key)
         -- Handle case where the "=" character is inside any of the strings in the json body
         if(value ~= true) then
-            table.insert(jsonStringList, "=" .. value)
+            table.insert(jsonStringList, utils.concatStrings({"=", value}))
         end
     end
     return cjson.decode(utils.concatStrings(jsonStringList))
diff --git a/api-gateway-config/scripts/lua/policies/rateLimit.lua b/api-gateway-config/scripts/lua/policies/rateLimit.lua
index d376b70..c240fbe 100644
--- a/api-gateway-config/scripts/lua/policies/rateLimit.lua
+++ b/api-gateway-config/scripts/lua/policies/rateLimit.lua
@@ -20,24 +20,40 @@
 
 --- @module rateLimit
 -- Process a rateLimit object, setting the rate and interval to the request
-
-local redis    = require "lib/redis"
+-- @author David Green (greend)
 
 local REDIS_HOST = os.getenv("REDIS_HOST")
 local REDIS_PORT = os.getenv("REDIS_PORT")
 local REDIS_PASS = os.getenv("REDIS_PASS")
+local request = require "lib/resty/limit/req"
+local utils = require "lib/utils"
+local logger = require "lib/logger"
 
-local logger = require("logger")
-local request = require "resty.rate.limit"
+local _M = {}
 
 function limit(obj)
+  local i = 60 / obj.interval
+  local r = i * obj.rate
+  r = utils.concatStrings({tostring(r), 'r/m'})
+  logger.debug(utils.concatStrings({'Limiting using rate: ', r}))
+  logger.debug(utils.concatStrings({'Limiting using interval: ', obj.interval}))
+  logger.debug(utils.concatStrings({'Limiting using redis config host: ' , REDIS_HOST, '; port: ', REDIS_PORT, '; pass:', REDIS_PASS}))
 
-  request.limit ({ key = ngx.var.namespace,
-                  rate = obj.rate,
-                  interval = obj.interval,
-                  log_level = ngx.NOTICE,
-                  redis_config = { host = REDIS_HOST, port = REDIS_PORT, timeout = 1, pool_size = 100 },
-                  whitelisted_api_keys = {} })
-
+  local config = {
+    key = ngx.var.namespace,
+    zone = 'rateLimiting',
+    rate = r,
+    interval = obj.interval,
+    log_level = ngx.NOTICE,
+    rds = { host = REDIS_HOST, port = REDIS_PORT }
+  }
+  local ok = request.limit (config)
+  if not ok then
+    logger.err('Rate limit exceeded. Sending 429')
+    ngx.exit(429)
+  end
 end
 
+_M.limit = limit
+
+return _M
\ No newline at end of file
diff --git a/api-gateway-config/scripts/lua/routing.lua b/api-gateway-config/scripts/lua/routing.lua
index f4969d3..9cead45 100644
--- a/api-gateway-config/scripts/lua/routing.lua
+++ b/api-gateway-config/scripts/lua/routing.lua
@@ -26,6 +26,7 @@
 local mapping = require "policies/mapping"
 local rateLimit = require "policies/rateLimit"
 local logger = require "lib/logger"
+local utils = require "lib/utils"
 local url = require "url"
 local cjson = require "cjson"
 
@@ -45,26 +46,26 @@
   local found = false
   for k, v in pairs(obj) do
     if k == verb then
-      logger.debug( 'found verb: ' .. k)
-      logger.debug( 'found backendUrl: ' .. v.backendUrl)
+      logger.debug(utils.concatStrings({'found verb: ', k}))
+      logger.debug(utils.concatStrings({'found backendUrl: ', v.backendUrl}))
       local u = url.parse(v.backendUrl)
       ngx.req.set_uri(u.path)
-      ngx.var.upstream = u.scheme .. '://' .. u.host
-      logger.debug('upstream: ' .. ngx.var.upstream)
+      ngx.var.upstream = utils.concatStrings({u.scheme, '://', u.host})
+      logger.debug(utils.concatStrings({'upstream: ', ngx.var.upstream}))
       if v.backendMethod ~= nil then
-        logger.debug('Setting a backend method: ' .. v.backendMethod)
+        logger.debug(utils.concatStrings({'Setting a backend method: ', v.backendMethod}))
         setVerb(v.backendMethod)
       end
       parsePolicies(v.policies)
       found = true
       break
     else
-      logger.debug( 'verb not found: ' .. k)
+      logger.debug(utils.concatStrings({'verb not found: ', k}))
     end
   end
   if found == false then
-    logger.debug( 'Finished loop without finding.')
-    ngx.say("Whoops. Verb not supported.")
+    logger.debug('Finished loop without finding.')
+    ngx.say('Whoops. Verb not supported.')
     ngx.exit(404)
   end
 end
@@ -86,11 +87,11 @@
 --- Given a verb, transforms the backend request to use that method
 -- @param v Verb to set on the backend request
 function setVerb(v)
-  if (string.lower(v) == "post") then
+  if (string.lower(v) == 'post') then
     ngx.req.set_method(ngx.HTTP_POST)
-  elseif (string.lower(v) == "put") then
+  elseif (string.lower(v) == 'put') then
     ngx.req.set_method(ngx.HTTP_PUT)
-  elseif (string.lower(v) == "delete") then
+  elseif (string.lower(v) == 'delete') then
     ngx.req.set_method(ngx.HTTP_DELETE)
   else
     ngx.req.set_method(ngx.HTTP_GET)