Skip to content

Commit 4d5cc3f

Browse files
committed
Fix potential bugs with rewriting API backend redirects.
There were 2 bugs that could crop up when rewriting API backend redirects to match the frontend URL prefix: 1. The first "URL match" that matched the redirect's path would be used, but that wasn't necessarily the "URL match" that was actually being used for routing to the API backend. This could be problematic for API backends with multiple URL matches, if a more general prefix matched before the actual match being used. Fix this by only using the "URL match" belonging to the active one that was used during routing. 2. If the API backend responded with URL paths that were already rewritten to match the frontend prefix, the frontend prefix could end up getting duplicated twice in the resulting URL. We now try to better detect this situation and skip redirect rewriting if it appears like the redirect being returned is already valid for the given frontend prefix. Add more comprehensive redirect tests to try and better capture more of the possible redirect scenarios. Also make a couple tweaks to prefer regexes over lua string.gsub (since regexes can be compiled and should be more efficient). In the test suite, improve the unique hostname generation and better ensure thread-safety for generating unique IPs and numbers.
1 parent 4e5a231 commit 4d5cc3f

File tree

7 files changed

+340
-78
lines changed

7 files changed

+340
-78
lines changed

src/api-umbrella/proxy/middleware/api_matcher.lua

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ local stringx = require "pl.stringx"
33
local utils = require "api-umbrella.proxy.utils"
44

55
local append_array = utils.append_array
6-
local gsub = string.gsub
76
local set_uri = utils.set_uri
87
local startswith = stringx.startswith
98

@@ -50,8 +49,10 @@ return function(active_config)
5049

5150
if api and url_match then
5251
-- Rewrite the URL prefix path.
53-
local new_path = gsub(request_path, url_match["_frontend_prefix_matcher"], url_match["backend_prefix"], 1)
54-
if new_path ~= request_path then
52+
local new_path, _, new_path_err = ngx.re.sub(request_path, url_match["_frontend_prefix_regex"], url_match["backend_prefix"], "jo")
53+
if new_path_err then
54+
ngx.log(ngx.ERR, "regex error: ", new_path_err)
55+
elseif new_path ~= request_path then
5556
set_uri(new_path)
5657
end
5758

src/api-umbrella/proxy/middleware/rewrite_response.lua

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ local url = require "socket.url"
33
local utils = require "api-umbrella.proxy.utils"
44

55
local append_args = utils.append_args
6-
local gsub = string.gsub
76
local startswith = stringx.startswith
87
local url_build = url.build
98
local url_parse = url.parse
@@ -126,14 +125,30 @@ local function rewrite_redirects()
126125
changed = true
127126
end
128127

129-
if host_matches or relative then
130-
if matched_api and matched_api["url_matches"] then
131-
for _, url_match in ipairs(matched_api["url_matches"]) do
132-
if startswith(parsed["path"], url_match["backend_prefix"]) then
133-
parsed["path"] = gsub(parsed["path"], url_match["_backend_prefix_matcher"], url_match["frontend_prefix"], 1)
134-
changed = true
135-
break
136-
end
128+
-- If the redirect being returned possibly contains paths for the underlying
129+
-- backend URL, then rewrite the path.
130+
if (host_matches or relative) and matched_api then
131+
-- If the redirect path begins with the backend prefix, then consider it
132+
-- for rewriting.
133+
local url_match = ngx.ctx.matched_api_url_match
134+
if url_match and startswith(parsed["path"], url_match["backend_prefix"]) then
135+
-- As long as the patah matches the backend prefix, mark as changed, so
136+
-- the api key is appended (regardless of whether we actually replaced
137+
-- the path).
138+
changed = true
139+
140+
-- Don't rewrite the path if the frontend prefix contains the backend
141+
-- prefix and the redirect path already contains the frontend prefix.
142+
--
143+
-- This helps ensure that if the API backend is already returning
144+
-- public/frontend URLs, we don't try to rewrite these again. -
145+
local rewrite_path = true
146+
if url_match["_frontend_prefix_contains_backend_prefix"] and startswith(parsed["path"], url_match["frontend_prefix"]) then
147+
rewrite_path = false
148+
end
149+
150+
if rewrite_path then
151+
parsed["path"] = ngx.re.sub(parsed["path"], url_match["_backend_prefix_regex"], url_match["frontend_prefix"], "jo")
137152
end
138153
end
139154
end

src/api-umbrella/proxy/models/active_config.lua

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ local random_token = require "api-umbrella.utils.random_token"
99
local resolve_backend_dns = require "api-umbrella.proxy.jobs.resolve_backend_dns"
1010
local tablex = require "pl.tablex"
1111
local utils = require "api-umbrella.proxy.utils"
12+
local startswith = require("pl.stringx").startswith
1213

1314
local append_array = utils.append_array
1415
local cache_computed_settings = utils.cache_computed_settings
1516
local deepcopy = tablex.deepcopy
16-
local escape = plutils.escape
1717
local set_packed = utils.set_packed
1818
local size = tablex.size
1919
local split = plutils.split
@@ -55,8 +55,18 @@ local function cache_computed_api(api)
5555

5656
if api["url_matches"] then
5757
for _, url_match in ipairs(api["url_matches"]) do
58-
url_match["_frontend_prefix_matcher"] = "^" .. escape(url_match["frontend_prefix"])
59-
url_match["_backend_prefix_matcher"] = "^" .. escape(url_match["backend_prefix"])
58+
url_match["_frontend_prefix_regex"] = "^" .. escape_regex(url_match["frontend_prefix"])
59+
url_match["_backend_prefix_regex"] = "^" .. escape_regex(url_match["backend_prefix"])
60+
61+
url_match["_frontend_prefix_contains_backend_prefix"] = false
62+
if startswith(url_match["frontend_prefix"], url_match["backend_prefix"]) then
63+
url_match["_frontend_prefix_contains_backend_prefix"] = true
64+
end
65+
66+
url_match["_backend_prefix_contains_frontend_prefix"] = false
67+
if startswith(url_match["backend_prefix"], url_match["frontend_prefix"]) then
68+
url_match["_backend_prefix_contains_frontend_prefix"] = true
69+
end
6070
end
6171
end
6272

templates/etc/test-env/nginx/apis.conf.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ server {
9292
echo -n "Hello World";
9393
}
9494

95-
location = /redirect {
95+
location /redirect {
9696
rewrite_by_lua_block {
9797
ngx.redirect(ngx.unescape_uri(ngx.var.arg_to or "/hello"))
9898
}

0 commit comments

Comments
 (0)