From 4dfc07c3ff5062fbe4f6cb02698b13d1762cf7a3 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sat, 13 Mar 2021 10:32:42 +0200 Subject: Use bitwise flags instead of the controlling variables --- bulkredirect.lua | 225 ++++++++++++++++++++++++------------------------------- 1 file changed, 96 insertions(+), 129 deletions(-) diff --git a/bulkredirect.lua b/bulkredirect.lua index 514a63b..ac24e42 100644 --- a/bulkredirect.lua +++ b/bulkredirect.lua @@ -85,6 +85,39 @@ local function url_path_encode (s) return table.concat(t) end +function constant(t) + return setmetatable({}, { + __index = t, + __newindex = function(table, key, value) + core.Alert("Attempt to modify read-only table") + end, + __metatable = false + }); +end + +RTFLAG = constant { + WWW = 0x1, -- The rules for a hostname "X" apply also to + -- hostname "www.X" + EXACT = 0x2, -- Skip prefix search: the redirection reply + -- is returned only if the input path is present + -- in the table. + STRIPPATH = 0x4, + --[[ By default the path components stripped during the look + up process are added back to the returned location. If + this flag is set, these components are dropped instead. ]] + STRIPQUERY = 0x8, + --[[ By default the query part (if any) is appended to the new + location when returning the redirection reply. Setting this + flag disables this ]] + TEMPORARY = 0x10, -- Return temporary (302) reply. + URLENCODE = 0x20, + --[[ Encode special characters in path parts of both source and + destination URLs as specified in RFC 3986 ("percent encoding"). + By default, bulkencode assumes all URLs are already properly + encoded. ]] + DEFAULT = 0 +} + -- -- Module global variables: -- @@ -105,19 +138,20 @@ rt = {} rt[P1] is found, or P1 is reduced to an empty string. The latter means there is no redirect for the given P?Q combination. - The lookup process is reduced to a single look up if the global - variable 'exact' is true (see below). + The search is reduced to a single look up if the EXACT flag is set + (see below). If the entry rt[P1] is found and is a string, it gives the location of the redirect. Prior to returning a 301 reply, this location is augmented by pathname part removed from P during the look up process, and the query part (if any). These modifications are controlled by - the global variables 'strippath' and 'stripquery'. If 'strippath' is - true, the removed pathname components are not added back to the - location. Similarly, if 'stripquery' is true, the query part is not - appended to the location. The 301 reply code is the default. If the - 'temporary' global variable is set to true, the code 302 will be used - instead. + the flags STRIPPATH and STRIPQUERY. If STRIPPATH is set, then + removed pathname components are not added back to the location. + Similarly, if STRIPQUERY is set, the query part is not appended to + the location. + + The TEMPORARY flag controls the HTTP reply code. The code is 301 + if it is not set (the default) and 302 otherwise. If rt[P1] is a table, it must contain a sequence of two elements. The element rt[P1][1] supplies the new location if Q is not present. @@ -125,46 +159,11 @@ rt = {} values of Q (including empty value). Both rt[P1][1] and rt[P1][2][Q] can contain either a string or a table value. The string value supplies the new location as described above. The table value - supplies the new location in the element [1]. Rest of elements - (2 up to 5) are boolean values overriding the default global variables - for this entry. They are located in the following order: - - 2 - exact - 3 - strippath - 4 - stripquery - 5 - temporary + supplies the new location in the element [1]. The element 2 gives + flag values for this redirect. ]] -www = false ---[[ If set to true, the rules for a hostname "X" apply also to - hostname "www.X" -]] - -exact = false ---[[ If set to true, no path prefix search is done: the redirection reply - is returned only if the input path is present in the table. -]] - -strippath = false ---[[ By default the path components stripped during the look up process - are added back to the returned location. If this variable is set - to true, these components are dropped instead. -]] - -stripquery = false ---[[ By default the query part (if any) is appended to the new - location when returning the redirection reply. Setting this variable - to true disables this -]] - -temporary = false --- Whether to return temporary (302) or permanent (301) reply. - -urlencode = true ---[[ Encode special characters in path parts of both source and destination - URLs as specified in RFC 3986 ("percent encoding"). By default, - bulkencode assumes all URLs are already properly encoded. -]] +rtflags = RTFLAG.DEFAULT -- -- Redirect the request if it matches one of the entries in the RT table. @@ -196,7 +195,7 @@ function bulkredirect.request (txn) -- Successively strip the trailing element off the path and look up -- the remaining part in the table. for i in prevsegm(path) do - local exact, strippath, stripquery, temporary = exact, strippath, stripquery, temporary + local rtflags = rtflags if rthost[i] then -- If the entry is found, it is either a table or a string @@ -219,16 +218,7 @@ function bulkredirect.request (txn) location = dt[1] if dt[2] ~= nil then - exact = dt[2] - end - if dt[3] ~= nil then - strippath = dt[3] - end - if dt[4] ~= nil then - stripquery = dt[4] - end - if dt[5] ~= nil then - temporary = dt[5] + rtflags = dt[2] end else location = rthost[i] @@ -241,16 +231,16 @@ function bulkredirect.request (txn) location = '/'..location end - if not exact or i == path or i..'/' == path then - if not strippath then + if (rtflags & RTFLAG.EXACT) == 0 or i == path or i..'/' == path then + if (rtflags & RTFLAG.STRIPPATH) == 0 then location = location .. path:sub(i:len() + 1) end - if not stripquery and txn.f:query() then + if (rtflags & RTFLAG.STRIPQUERY) == 0 and txn.f:query() then location = location .. '?' .. txn.f:query() end core.Debug("REDIRECT " .. host .. txn.f:path() .. " to " .. location) - if temporary then + if (rtflags & RTFLAG.TEMPORARY) ~= 0 then reply:set_status(302, "Moved Temporarily") else reply:set_status(301, "Moved Permanently") @@ -279,14 +269,14 @@ end HOSTNAME ::= ]] -local function parseopt (s, t, loc) +local function parseopt (s, flags, loc) local valid = { - ['www'] = true, - ['exact'] = true, - ['strippath'] = true, - ['stripquery'] = true, - ['temporary'] = true, - ['urlencode'] = true + ['www'] = RTFLAG.WWW, + ['exact'] = RTFLAG.EXACT, + ['strippath'] = RTFLAG.STRIPPATH, + ['stripquery'] = RTFLAG.STRIPQUERY, + ['temporary'] = RTFLAG.TEMPORARY, + ['urlencode'] = RTFLAG.URLENCODE } function options (str, loc) @@ -309,24 +299,23 @@ local function parseopt (s, t, loc) end end - if not t then - t = _ENV - end - for opt in options(s, loc) do local neg = opt:match("^no(%w+)$") - local val = true if neg then opt = neg - val = not val end - if not valid[opt] then + if valid[opt] then + if neg then + flags = flags & ~valid[opt] + else + flags = flags | valid[opt] + end + else error(loc .. ': invalid option ' .. opt, 0) end - - t[opt] = val end + return flags end local function set_dst (dt, src, dst) @@ -337,7 +326,7 @@ local function set_dst (dt, src, dst) elseif type(dt[path]) == 'string' then dt[path] = { { dt[path] }, {} } end --- core.Info(require('inspect')(dt[path])) + core.Info(require('inspect')(dt[path])) dt[path][2][query] = dst elseif type(dt[src]) == 'table' then dt[src][1] = dst @@ -376,6 +365,7 @@ local function populate_www_complements (rt, dup) if rt[compl] then for k,v in pairs(t) do -- FIXME: Error message if rt[compl][k] exists + if not crt[compl] then crt[compl] = {} end crt[compl][k] = clone(v) end elseif dup then @@ -392,35 +382,28 @@ end local function load_redirect_file (f, filename) local domain - local ln = 1 + local ln = 0 local rt = {} - local domopt + local domain_flags = rtflags local parsetab = { { '^#', function () end }, { '^%s*$', function () end }, { '^option%s+(.*)', function (s) - local t if domain then - t = domopt + domain_flags = parseopt(s, domain_flags, filename .. ':' .. ln) else - t = _ENV - end - parseopt(s, t, filename .. ':' .. ln) + rtflags = parseopt(s, rtflags, filename .. ':' .. ln) + end end }, { '^%s*%[(.+)%]%s*$', function (s) domain = s if not rt[domain] then rt[domain] = {} end - domopt = { - exact = exact, - strippath = strippath, - stripquery = stripquery, - temporary = temporary - } + domain_flags = rtflags end }, { '^%s*([^%s]+)%s+([^%s]+)%s*(.*)$', @@ -437,13 +420,12 @@ local function load_redirect_file (f, filename) dst = dst:sub(2) end - local optab = domopt + local rule_flags = domain_flags if optlist ~= '' then - optab = clone(domopt) - parseopt(optlist, optab, filename .. ':' .. ln) + rule_flags = parseopt(optlist, rule_flags, filename .. ':' .. ln) end - if optab['urlencode'] then + if (rule_flags & RTFLAG.URLENCODE) ~= 0 then src = url_path_encode(src) dst = url_path_encode(dst) else @@ -452,53 +434,38 @@ local function load_redirect_file (f, filename) local dpath, dquery = dst:match('^(.+)?(.*)') if dpath then - if optab['strippath'] == false and optab['strippath'] ~= domopt['strippath'] then + if (rule_flags & RTFLAG.STRIPPATH) == 0 and ((rule_flags ~ domain_flags) & RTFLAG.STRIPPATH) ~= 0 then core.Warning(filename .. ':' .. ln .. ': nostrippath ignored because of explicit query in the destination') end - if optab['stripquery'] == false and optab['stripquery'] ~= domopt['stripquery'] then + if (rule_flags & RTFLAG.STRIPQUERY) == 0 and ((rule_flags ~ domain_flags) & RTFLAG.STRIPQUERY) ~= 0 then core.Warning(filename .. ':' .. ln .. ': nostripquery ignored because of explicit query in the destination') end - - optab['strippath'] = true - optab['stripquery'] = true + + rule_flags = rule_flags | RTFLAG.STRIPPATH + rule_flags = rule_flags | RTFLAG.STRIPQUERY if dquery == '' then dst = dpath end end - if optab['exact'] ~= exact then - if type(dst) == 'string' then dst = { dst } end - dst[2] = optab['exact'] - end - if optab['strippath'] ~= strippath then + if ((rule_flags ~ rtflags) & ~RTFLAG.URLENCODE) ~= 0 then if type(dst) == 'string' then dst = { dst } end - dst[3] = optab['strippath'] - end - if optab['stripquery'] ~= stripquery then - if type(dst) == 'string' then dst = { dst } end - dst[4] = optab['stripquery'] - end - if optab['temporary'] ~= temporary then - if type(dst) == 'string' then dst = { dst } end - dst[5] = optab['temporary'] + dst[2] = rule_flags end - if optab['www'] ~= nil then - if www == optab['www'] then - return - elseif www then - populate_www_complements (rt, true) - www = nil + if (rule_flags & RTFLAG.WWW) ~= 0 then + if (rtflags & RTFLAG.WWW) == 0 then + local s = www_complement(domain) + if not rt[s] then rt[s] = {} end + set_dst(rt[s], src, dst) end - end - - set_dst(rt[domain], src, dst) - - if optab['www'] then + elseif (rtflags & RTFLAG.WWW) ~= 0 then + populate_www_complements(rt, true) + rtflags = rtflags & ~RTFLAG.WWW local s = www_complement(domain) if not rt[s] then rt[s] = {} end - set_dst(rt[s], src, dst) end + set_dst(rt[domain], src, dst) end } } @@ -517,9 +484,9 @@ local function load_redirect_file (f, filename) ::continue:: end - if www then + if (rtflags & RTFLAG.WWW) ~= 0 then populate_www_complements (rt, false) - www = nil + rtflags = rtflags & ~RTFLAG.WWW end _ENV['rt'] = rt @@ -531,7 +498,7 @@ local function load_redirect_table () name = '/etc/haproxy/bulkredirect.rt' end if name:match('%.lua$') then - rt, www, exact, strippath, stripquery, temporary = dofile(name) + rt, rtflags = dofile(name) else local file, err = io.open(name,"r") if file ~= nil then -- cgit v1.2.1