ポリシー拡張 - ユースケース

特定のお客様のアプリケーションには、既存のポリシーや表現では対応できない要件があります。ポリシー拡張機能を使用すると、要件に合わせてカスタマイズした機能をアプリケーションに追加できます。

以下のユースケースは、Citrix ADCアプライアンスのポリシー拡張機能を使用した新しい機能の追加を示しています。

  • ケース 1: カスタムハッシュ
  • ケース 2: URL の二重スラッシュを折りたたむ
  • ケース3:ヘッダーを結合する

ケース 1: カスタムハッシュ

CUSTOM_HASH 関数は、クライアントに送信される応答に任意のタイプのハッシュ値を挿入するメカニズムを提供します。このユースケースでは、ハッシュ関数を使用して、書き換えHTTPリクエストのクエリ文字列のハッシュを計算し、計算された値を持つCUSTOM_HASHという名前のHTTPヘッダーを挿入します。CUSTOM_HASH 関数は 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 をより効率的に解析できるため、Web サイトのレンダリング時間が短縮されます。単一のスラッシュ URL は、二重スラッシュを受け入れないアプリケーションとの互換性を維持するためにも使用します。ポリシー拡張機能を使用すると、URL 内の二重スラッシュを 1 つのスラッシュに置き換える関数を追加できます。次の例は、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

一般に、このタイプの要求の変更は、ポリシー式を使用して、変更される要求の一部(ターゲット)と実行する変更(文字列ビルダー式)を描写します。ただし、ポリシー式には、任意の数のヘッダーを反復処理する機能はありません。

この問題を解決するには、ポリシー機能への拡張が必要です。これを行うには、COMBINE_HEADERSと呼ばれる拡張関数を定義します。この機能を使用すると、次の書き換えアクションを設定できます。

> 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」) です。FULL_HEADERにはHTTPリクエストの最初の行が含まれているため、AFTER_STR(「HTTP/1.1rn」)が必要です(例:GET /結合ヘッダー HTTP/1.1)。

文字列ビルダー式は HTTP.REQ.FULL_HEADER.AFTER_STR(「HTTP/1.1rn」).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 ループは、入力の各ヘッダーを解析します。イテレータは、組み込みのstring.gmatch()関数です。この関数は、検索する文字列と、文字列の一部を一致させるために使用するパターンの 2 つのパラメータを取ります。検索する文字列は、関数に入力されたヘッダーのテキストである暗黙のselfパラメータによって提供されます。

パターンは、正規表現(短い場合は正規表現)を使用して表現されます。この正規表現は、HTTP 標準では name: value rn として定義されている、各ヘッダーのヘッダー 名と**値に一致します。正規表現の括弧は抽出する一致する部分を指定するので、正規表現の回路図は(match-name):(match-value)rnです。match-name パターンは、コロン以外のすべての文字と一致する必要があります。これは[^:]+と記述されます([^:]は:と+を除く任意の文字で1回以上の繰り返しです)。同様に、 match-value パターンは、\r\n以外の任意の文字と一致する必要があるため、[^\r\n]と記述されます([^\r\n]は\rおよび\nを除くすべての文字に一致しは0回以上の繰り返しです)。これにより、完全な正規表現([^:]+):([^\r\n]*)\r\nになります。

for文は、string.gmatch()イテレータによって返された2つの一致に名前と値を設定するために、複数の割り当てを使用しています。これらは、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)、名前のヘッダーエントリはなく、ヘッダー[name]はnilです。

nilはfalseとして扱われるので、else節が実行されます。これは、name のヘッダエントリを 1 つの文字列値 name:value を持つ配列に設定します。

注: elseループの配列コンストラクタは、{[1] = name .. “:” .. value}と同等です。 “:”.. value}、配列の最初の要素を設定します。 最初のH2ヘッダーについては、headers[“H2”]= {“H2:h2val1”} を設定します。

ヘッダーの後続のインスタンス(たとえば、入力例ではH2:h2val2)。headers[name] はnilではないので、then 節が実行されます。これは、headers[name]の配列値の中で次に使用可能なインデックスを決定し、そのインデックスにヘッダー値を置きます。2番目のH2ヘッダーについては、headers[“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_headers 配列を構築します。これは、forループイテレータとしてpairs()関数を使用しています。

pairs()を呼び出すたびに、ヘッダーテーブル内の次のエントリの名前と値を返します。

次の行は、combined_headers 配列内の次に使用可能なインデックスを決定し、次の行は、結合されたヘッダーにその配列要素を設定します。組み込みの table.concat () 関数を使用します。この関数は、引数として文字列の配列と文字列をセパレータとして使用し、セパレータで区切られた配列文字列を連結した文字列を返します。

たとえば、値= {“H2:h2val1”、「h2val2”} の場合、これは「H2:h2val1、h2val2”を生成します

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

combined_headers配列が構築された後、要素を1つの文字列に連結し、HTTPヘッダーを終了する二重のrnを追加します。

return result_str

COMBINE_HEADERS 拡張関数の結果として文字列を返します。

ポリシー拡張 - ユースケース