| -- |
| -- Licensed to the Apache Software Foundation (ASF) under one or more |
| -- contributor license agreements. See the NOTICE file distributed with |
| -- this work for additional information regarding copyright ownership. |
| -- The ASF licenses this file to You under the Apache License, Version 2.0 |
| -- (the "License"); you may not use this file except in compliance with |
| -- the License. You may obtain a copy of the License at |
| -- |
| -- http://www.apache.org/licenses/LICENSE-2.0 |
| -- |
| -- Unless required by applicable law or agreed to in writing, software |
| -- distributed under the License is distributed on an "AS IS" BASIS, |
| -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| -- See the License for the specific language governing permissions and |
| -- limitations under the License. |
| -- |
| |
| --- @module mapping |
| -- Process mapping object, turning implementation details into request transformations |
| |
| local logger = require "lib/logger" |
| local utils = require "lib/utils" |
| local cjson = require "cjson" |
| |
| local _M = {} |
| |
| local body |
| local query |
| local headers |
| local path |
| |
| --- Implementation for the mapping policy. |
| -- @param map The mapping object that contains details about request tranformations |
| function processMap(map) |
| getRequestParams() |
| for k, v in pairs(map) do |
| if v.action == "insert" then |
| insertParam(v) |
| elseif v.action == "remove" then |
| removeParam(v) |
| elseif v.action == "transform" then |
| transformParam(v) |
| elseif v.action == "default" then |
| checkDefault(v) |
| else |
| logger.err(utils.concatStrings({'Map action not recognized. Skipping... ', v.action})) |
| end |
| end |
| finalize() |
| end |
| |
| --- Get request body, params, and headers from incoming requests |
| function getRequestParams() |
| ngx.req.read_body() |
| body = ngx.req.get_body_data() |
| body = (body and cjson.decode(body)) or {} |
| headers = ngx.req.get_headers() |
| path = ngx.var.uri |
| query = parseUrl(ngx.var.backendUrl) |
| local incomingQuery = ngx.req.get_uri_args() |
| for k, v in pairs (incomingQuery) do |
| query[k] = v |
| end |
| end |
| |
| --- Insert parameter value to header, body, or query params into request |
| -- @param m Parameter value to add to request |
| function insertParam(m) |
| local v |
| local k = m.to.name |
| if m.from.value ~= nil then |
| v = m.from.value |
| elseif m.from.location == 'header' then |
| v = headers[m.from.name] |
| elseif m.from.location == 'query' then |
| v = query[m.from.name] |
| elseif m.from.location == 'body' then |
| v = body[m.from.name] |
| elseif m.from.location == 'path' then |
| v = ngx.ctx[m.from.name] |
| end |
| -- determine to where |
| if m.to.location == 'header' then |
| insertHeader(k, v) |
| elseif m.to.location == 'query' then |
| insertQuery(k, v) |
| elseif m.to.location == 'body' then |
| insertBody(k, v) |
| elseif m.to.location == 'path' then |
| insertPath(k,v) |
| end |
| end |
| |
| --- Remove parameter value to header, body, or query params from request |
| -- @param m Parameter value to remove from request |
| function removeParam(m) |
| if m.from.location == "header" then |
| removeHeader(m.from.name) |
| elseif m.from.location == "query" then |
| removeQuery(m.from.name) |
| elseif m.from.location == "body" then |
| removeBody(m.from.name) |
| end |
| end |
| |
| --- Move parameter value from one location to another in the request |
| -- @param m Parameter value to move within request |
| function transformParam(m) |
| if m.from.name == '*' then |
| transformAllParams(m.from.location, m.to.location) |
| else |
| insertParam(m) |
| removeParam(m) |
| end |
| end |
| |
| --- Checks if the header has been set, and sets the header to a value if found to be null. |
| -- @param m Header name and value to be set, if header is null. |
| function checkDefault(m) |
| if m.to.location == "header" and headers[m.to.name] == nil then |
| insertHeader(m.to.name, m.from.value) |
| elseif m.to.location == "query" and query[m.to.name] == nil then |
| insertQuery(m.to.name, m.from.value) |
| elseif m.to.location == "body" and body[m.to.name] == nil then |
| insertBody(m.to.name, m.from.value) |
| end |
| end |
| |
| --- Function to handle wildcarding in the transform process. |
| -- If the value in the from object is '*', this function will pull all values from the incoming request |
| -- and move them to the location provided in the to object |
| -- @param s The source object from which we pull all parameters |
| -- @param d The destination object that we will move all found parameters to. |
| function transformAllParams(s, d) |
| if s == 'query' then |
| for k, v in pairs(query) do |
| local t = {} |
| t.from = {} |
| t.from.name = k |
| t.from.location = s |
| t.to = {} |
| t.to.name = k |
| t.to.location = d |
| insertParam(t) |
| removeParam(t) |
| end |
| elseif s == 'header' then |
| for k, v in pairs(headers) do |
| local t = {} |
| t.from = {} |
| t.from.name = k |
| t.from.location = s |
| t.to = {} |
| t.to.name = k |
| t.to.location = d |
| insertParam(t) |
| removeParam(t) |
| end |
| elseif s == 'body' then |
| for k, v in pairs(body) do |
| local t = {} |
| t.from = {} |
| t.from.name = k |
| t.from.location = s |
| t.to = {} |
| t.to.name = k |
| t.to.location = d |
| insertParam(t) |
| removeParam(t) |
| end |
| elseif s == 'path' then |
| for k, v in pairs(path) do |
| local t = {} |
| t.from = {} |
| t.from.name = k |
| t.from.location = s |
| t.to = {} |
| t.to.name = k |
| t.to.location = d |
| insertParam(t) |
| removeParam(t) |
| end |
| end |
| end |
| |
| function finalize() |
| if next(body) ~= nil then |
| local bodyJson = cjson.encode(body) |
| ngx.req.set_body_data(bodyJson) |
| end |
| ngx.req.set_uri_args(query) |
| end |
| |
| function insertHeader(k, v) |
| ngx.req.set_header(k, v) |
| headers[k] = v |
| end |
| |
| function insertQuery(k, v) |
| query[k] = v |
| end |
| |
| function insertBody(k, v) |
| body[k] = v |
| end |
| |
| function insertPath(k, v) |
| v = ngx.unescape_uri(v) |
| path = path:gsub(utils.concatStrings({"%{", k ,"%}"}), v) |
| ngx.req.set_uri(path) |
| end |
| |
| function removeHeader(k) |
| ngx.req.clear_header(k) |
| end |
| |
| function removeQuery(k) |
| query[k] = nil |
| end |
| |
| function removeBody(k) |
| body[k] = nil |
| end |
| |
| function parseUrl(url) |
| local map = {} |
| for k,v in url:gmatch('([^&=?]+)=([^&=?]+)') do |
| map[ k ] = decodeQuery(v) |
| end |
| return map |
| end |
| |
| function decodeQuery(param) |
| local decoded = param:gsub('+', ' '):gsub('%%(%x%x)', |
| function(hex) return string.char(tonumber(hex, 16)) end) |
| return decoded |
| end |
| |
| _M.processMap = processMap |
| |
| return _M |