blob: 646de942914a42f200fd2962b4a367695a491951 [file] [log] [blame]
--
-- 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 backendRouting
-- Used to set the backend Url either statically or dynamically
local url = require "url"
local utils = require "lib/utils"
local request = require "lib/request"
local logger = require "lib/logger"
local backendOverride = os.getenv("BACKEND_HOST")
local _M = {}
--- Set upstream based on the backendUrl
function _M.setRoute(backendUrl, gatewayPath)
_M.setRouteWithOverride(backendUrl, gatewayPath, backendOverride)
end
function _M.setRouteWithOverride(backendUrl, gatewayPath, override)
local u = url.parse(backendUrl)
if u.scheme == nil then
u = url.parse(utils.concatStrings({'http://', backendUrl}))
end
-- pass down gateway path to upstream path if $(request.path) is specified at the end of backendUrl
if u.path:sub(-15) == '$(request.path)' then
u.path = utils.concatStrings({u.path:sub(1, -16), u.path:sub(-16, -16) == '/' and '' or '/', gatewayPath})
ngx.req.set_uri(u.path)
else
ngx.req.set_uri(_M.getUriPath(u.path))
end
ngx.var.backendUrl = backendUrl
-- if there is a backend override then use that instead of actual backend from swagger
if override ~= nil then
local bou = url.parse(override)
u.scheme = bou.scheme
u:setAuthority(bou.authority)
-- update the copy in ngx var also to match upstream
local bu = url.parse(backendUrl)
bu.scheme = bou.scheme
bu:setAuthority(bou.authority)
ngx.var.backendUrl = bu:build()
end
setUpstream(u)
end
--- Set dynamic route based on the header that is passed in
function _M.setDynamicRoute(obj)
local whitelist = obj.whitelist
for k in pairs(whitelist) do
whitelist[k] = whitelist[k]:lower()
end
local header = obj.header ~= nil and obj.header or 'X-Cf-Forwarded-Url'
local dynamicBackend = ngx.req.get_headers()[header]
if dynamicBackend ~= nil and dynamicBackend ~= '' then
local u = url.parse(dynamicBackend)
if u.scheme == nil or u.scheme == '' then
u = url.parse(utils.concatStrings({'http://', dynamicBackend}))
end
if utils.tableContains(whitelist, u.host) then
ngx.req.set_uri(_M.getUriPath(u.path))
-- Split the dynamicBackend url to get the query parameters in the exact order that it was passed in.
-- Don't use u.query here because it returns the parameters in an unordered lua table.
local split = {string.match(dynamicBackend, '([^?]*)?(.*)')}
local qs = split[2]
if qs ~= nil then
ngx.req.set_uri_args(qs)
end
setUpstream(u)
else
request.err(403, 'Dynamic backend host not part of whitelist.')
end
else
logger.info('Header for dynamic routing not found. Defaulting to backendUrl.')
end
end
function _M.getUriPath(backendPath)
local gatewayPath = ngx.unescape_uri(ngx.var.gatewayPath)
gatewayPath = gatewayPath:gsub('-', '%%-')
local tenant = ngx.var.tenant:gsub('-', '%%-')
local uri = string.gsub(ngx.var.request_uri, '?.*', '')
uri = uri:gsub('/api/' .. tenant, '')
local _, j = uri:find(gatewayPath)
local incomingPath = ((j and uri:sub(j + 1)) or nil)
-- Check for backendUrl path
if backendPath == nil or backendPath == '' or backendPath == '/' then
incomingPath = (incomingPath and incomingPath ~= '') and incomingPath or '/'
incomingPath = string.sub(incomingPath, 1, 1) == '/' and incomingPath or utils.concatStrings({'/', incomingPath})
return incomingPath
else
return utils.concatStrings({backendPath, incomingPath})
end
end
function setUpstream(u)
local upstream = utils.concatStrings({u.scheme, '://', u.host})
if u.port ~= nil and u.port ~= '' then
upstream = utils.concatStrings({upstream, ':', u.port})
end
ngx.var.upstream = upstream
end
return _M