策略扩展-用例

某些客户应用程序具有无法使用现有策略和表达式解决的要求。策略扩展功能使客户能够向其应用程序添加自定义功能,以满足他们的需求。

以下用例说明了使用 Citrix ADC 设备上的策略扩展功能添加的新功能。

  • 案例 1:自定义散列
  • 案例 2:折叠 URL 中的双斜杠
  • 案例 3:合并标题

案例 1:自定义散列

CONT_HASH 函数提供了一种机制,用于在发送到客户端的响应中插入任何类型的哈希值。在此用例中,哈希函数用于计算重写 HTTP 请求的查询字符串的哈希值,并插入一个名为 CONT_HASH 的 HTTP 标头和计算值。自定义哈希函数实现 DJB2 哈希算法。

CUSTOM_HASH 的示例用法:

> add rewrite action test_custom_hash insert_http_header "CUSTOM_HASH" "HTTP.REQ.URL.QUERY.CUSTOM_HASH"

CUSTOM_HASH() 的示例定义 ():

    -- Extension function to compute custom hash on the text

    -- Uses the djb2 string hash algorithm
    function NSTEXT:CUSTOM_HASH() : NSTEXT

        local hash = 5381

        local len = string.len(self)

        for i = 1, len do

             hash = bit32.bxor((hash * 33), string.byte(self, i))

        end

        return tostring(hash)

    end

上述示例的逐行描述:

function NSTEXT:CUSTOM_HASH() : NSTEXT

Defines the CUSTOM_HASH() function, with text input and a text return value.

local hash = 5381
local len = string.len(self)

Declares two local variables:

 - hash. Accumulates the compute hash value and is seeded with the number 5381

 - len. Sets to the length of the self input text string, using the built-in string.len() function.

for i = 1, len do
      hash = bit32.bxor((hash * 33), string.byte(self, i))
end

Iterates through each byte of the input string and adds the byte to the hash. It uses the built-in string.byte() function to get the byte and the built-in bit32.bxor() function to compute the XOR of the existing hash value (multiplied by 33) and the byte.

return tostring(hash)

Calls the built-in tostring() function to convert the numeric hash value to a string and returns the string as the value of the function.

案例 2:折叠 URL 中的双斜杠

在 URL 中折叠双斜杠可提高网站渲染时间,因为浏览器更有效地解析单斜杠 URL。单斜杠 URL 也可以保持与不接受双斜杠的应用程序的兼容性。策略扩展功能允许客户添加用 URL 中单斜杠替换双斜杠的函数。以下示例说明添加了一个策略扩展函数,该函数可折叠 URL 中的双斜杠。

COLLAPSE_DOUBLE_SLASHES() 的示例定义:

    -- Collapse double slashes in URL to a single slash and return the result
    function NSTEXT:COLLAPSE_DOUBLE_SLASHES() : NSTEXT

        local result = string.gsub(self, "//", "/")

        return result

    end

上述示例的逐行描述:

function NSTEXT:COLLAPSE_DOUBLE_SLASHES() : NSTEXT

Declares the COLLAPSE_DOUBLE_SLASHES() function with text input and return.

local result = string.gsub(self, "//", "/")

Declares a local variable named result and uses the built-in string.gsub() function to replace all double slashes with single slashes in the self input text.

The second parameter of string.gsub() is actually a regular expression pattern, although here a simple string is used for the pattern.

return result

Returns the resulting string.

案例 3:合并标题

某些客户应用程序无法处理请求中的多个标头。此外,解析具有相同标头值的重复标头或请求中具有相同名称但值不同的多个标头,会消耗时间和网络资源。策略扩展功能允许客户添加一个函数,将这些标头合并为单个标头,其值组合了原始值。例如,合并标头 H1 和 H2 的值。

原始请求:

GET /combine_headers HTTP/1.1
User-Agent: amigo unit test
Host: myhost
H2: h2val1
H1: abcd
Accept: \*/\*
H2: h2val2
Content-Length: 0
H2: h2val3
H1: 1234

修改的请求:

GET /combine_headers HTTP/1.1
User-Agent: amigo unit test
Host: myhost
H2: h2val1, h2val2, h2val3
H1: abcd, 1234
Accept: \*/\*
Content-Length: 0

通常,这种类型的请求修改是使用 Rewrite 功能完成的,并使用策略表达式描述要修改的请求部分(目标)和要执行的修改(字符串构建器表达式)。但是,策略表达式不能遍历任意数量的标头。

要解决这个问题,就需要扩大策略设施。要做到这一点,我们将定义一个扩展函数,称为 Combin_Header。使用此函数,我们可以设置以下重写操作:

> add rewrite action combine_headers_act replace 'HTTP.REQ.FULL_HEADER.AFTER_STR("HTTP/1.1rn")' 'HTTP.REQ.FULL_HEADER.AFTER_STR("HTTP/1.1rn").COMBINE_HEADERS'

在这里,重写目标是 HTTP.REQ.FULL_HEADER.AFTER_STR(“HTTP/1.1rn”)。之后 STR(“HTTP/1.1rn”)是必需的,因为完整的标头包含 HTTP 请求的第一行(例如 GET /combine_headers HTTP/1.1)。

字符串构建器表达式是 HTTP.REQ.FULL_HEADER.AFTER_STR(“HTTP/1.1rn”).COMBINE_HEADERS,其中标头(减去第一行)被送入 COMBINE_HEADERS 扩展函数,该函数组合并并返回标头的值。

COMBINE_HEADERS() 的示例定义:

    -- Extension function to combine multiple headers of the same name into one header.



    function NSTEXT:COMBINE_HEADERS(): NSTEXT

        local headers = {} -- headers

        local combined_headers = {} -- headers with final combined values
        -- Iterate over each header (format "name:valuer\r\n")

        -- and build a list of values for each unique header name.

        for name, value in string.gmatch(self, "([^:]+):([^\r\n]*)\r\n") do

            if headers[name] then

                local next_value_index = #(headers[name]) + 1

                headers[name][next_value_index] = value

            else

                headers[name] = {name .. ":" .. value}

            end

        end



        -- iterate over the headers and concat the values with separator ","

        for name, values in pairs(headers) do

            local next_header_index = #combined_headers + 1

            combined_headers[next_header_index] = table.concat(values, ",")

        end



        -- Construct the result headers using table.concat()

        local result_str = table.concat(combined_headers, "\r\n") .. "\r\n\r\n"

        return result_str

    end

上述示例的逐行描述:

function NSTEXT:COMBINE_HEADERS(): NSTEXT

Defines the COMBINE_HEADERS extension function, with the text input into the function from the policy expression and a text return type to the policy expression.

local headers = {} -- headers
local combined_headers = {} -- headers with final combined values

Declares local variables headers and combined_headers and initialize these variables to empty tables. headers will be a table of arrays of strings, where each array holds one or more values for a header. combined_headers will be an array of strings, where each array element is a header with its combined values.

for name, value in string.gmatch(self, "([^:]+):([^\r\n]*)\r\n") do
. . .
end

这个通用 for 循环解析输入中的每个标题。迭代器是内置的字符串 .gmatch()函数。此函数需要两个参数:一个要搜索的字符串和一个用于匹配字符串碎片的模式。要搜索的字符串由隐式 self 参数提供,该参数是函数中输入标题的文本。

该模式使用正则表达式(简称正则表达式)来表示。此正则表达式匹配每个标头的标头名称和值,HTTP 标准将其定义为 名称valuern。正则表达式中的括号指定要提取的匹配部分,因此正则表达式原理图为(匹配名称):(匹配值)rn。匹配名称模 式需要匹配除冒号之外的所有字符。这是写的[^:]+(除了:和 +[^:]是一个或多个重复之外的任何字符)。同样, 匹配值 模式必须匹配除 rn 之外的任何字符,所以它被写入[^ rn]* ([^ rn] 匹配除 r 和 n 以及 * 以外的任何字符是零或更多重复)。这使得完整的正则表达式([^:]+):([^ rn]*)rn。

for 语句使用多重赋值来设置字符串 .gmatch () 迭代器返回的两个匹配项的名称和值。这些隐式声明为 for 循环主体中的局部变量。

if headers[name] then
     local next_value_index = #(headers[name]) + 1
     headers[name][next_value_index] = value
else
     headers[name] = {name .. ":" .. value}
end

for 循环中的这些语句将标头名称和值放入标头表中。第一次解析标题名称(例如示例输入中的 H2: h2val1)时,名称没有标题条目,标题[名称]为零。

由于 nil 被视为 false,else 子句被执行。这将名称的标头条目设置为具有一个字符串值 名称value 的数组。

注意: else 循环中的数组构造函数等效于 {[1] = name .. “:” .. value},它设置了数组的第一个元素。) 对于第一个 H2 标头,它设置标头 [“H2”] = {“H2:h2val1”}。

在标头的后续实例上(例如示例输入中的 H2: h2val2)。标头不[名称] 是零,因此执行 then 子句。这将确定标头数组值中的下一个可用索引[名称],并将标头值放入该索引中。对于第二个 H2 标题,它设置标头[“H2”] = {“H2:h2val1”, “h2val2”}。

for name, values in pairs(headers) do
    local next_header_index = #combined_headers + 1
    combined_headers[next_header_index] = table.concat(values, ",")
end

在解析原始标题并填充标题表后,此循环构建 combined_header 数组。它使用 pairs()函数作为 for 循环迭代器。

对 pairs () 的每次调用都会返回标头表中下一个条目的名称和值。

下一行决定了 combined_header 数组中的下一个可用索引,下一行将该数组元素设置为组合标头。它使用内置 table.concat()函数,它将字符串数组和一个字符串作为参数,并返回一个字符串,该字符串是数组字符串的连接,由分隔符分隔。

例如,对于值 = “H2:h2val1”, “h2val2”},这会产生 “H2:h2val1, h2val2”

local result_str = table.concat(combined_headers, "\r\n") .. "\r\n\r\n"

构建了 combined_header 数组后,它将元素连接到一个字符串中,并添加一个双重 rn 来终止 HTTP 头。

return result_str

返回一个字符串作为 COBINE_HEST 扩展函数的结果。

策略扩展-用例