ポリシー拡張 - ユースケース
特定のお客様のアプリケーションには、既存のポリシーや表現では対応できない要件があります。ポリシー拡張機能を使用すると、要件に合わせてカスタマイズした機能をアプリケーションに追加できます。
以下のユースケースは、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"
<!--NeedCopy-->
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
<!--NeedCopy-->
上記のサンプルの行ごとの説明:
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.
<!--NeedCopy-->
ケース 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
<!--NeedCopy-->
上記のサンプルの行ごとの説明:
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.
<!--NeedCopy-->
ケース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
<!--NeedCopy-->
変更されたリクエスト:
GET /combine_headers HTTP/1.1
User-Agent: amigo unit test
Host: myhost
H2: h2val1, h2val2, h2val3
H1: abcd, 1234
Accept: \*/\*
Content-Length: 0
<!--NeedCopy-->
一般に、このタイプの要求の変更は、ポリシー式を使用して、変更される要求の一部(ターゲット)と実行する変更(文字列ビルダー式)を描写します。ただし、ポリシー式には、任意の数のヘッダーを反復処理する機能はありません。
この問題を解決するには、ポリシー機能への拡張が必要です。これを行うには、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
<!--NeedCopy-->
上記のサンプルの行ごとの説明:
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
<!--NeedCopy-->
この汎用的な for ループは、入力の各ヘッダーを解析します。イテレータは、組み込みのstring.gmatch()関数です。この関数は、検索する文字列と、文字列の一部を一致させるために使用するパターンの 2 つのパラメータを取ります。検索する文字列は、関数に入力されたヘッダーのテキストである暗黙のselfパラメータによって提供されます。
パターンは、正規表現(短い場合は正規表現)を使用して表現されます。この正規表現は、HTTP 標準では name: value rn として定義されている、各ヘッダーのヘッダー 名と**値に一致します。正規表現の括弧は抽出する一致する部分を指定するので、正規表現の回路図は(match-name):(match-value)rnです。match-name パターンは、コロン以外のすべての文字と一致する必要があります。これは [^: + ([^:] :]以外の任意の文字、+ は 1 つ以上の繰り返し) と書かれています。同様に、 マッチ値パターンは \ r\ n 以外の任意の文字と一致する必要があるため、 [^\ r\ n (\ r と\ n][^\ r\ n (\ r と\ n] 以外の任意の文字に一致し、 はゼロ以上の繰り返しです) と書かれます。これにより、完全な正規表現([^: + (^: :]+):([^\ r\ n (\ 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
<!--NeedCopy-->
forループ内のこれらのステートメントは、ヘッダー名と値をヘッダーテーブルに入れます。ヘッダー名を初めて解析する場合 (たとえば、入力例では H2: h2val1)、名前のヘッダーエントリはなく、[ヘッダー名は] nil です。
nilはfalseとして扱われるので、else節が実行されます。これは、name のヘッダエントリを 1 つの文字列値 name:value を持つ配列に設定します。
注意: else ループの配列コンストラクタは {[1] = name と同じです。「:」.. value}。配列の最初の要素を設定します。)最初の H2 ヘッダーには、headers[“H2”] = {“H2:h2val1”}を設定します。
ヘッダーの後続のインスタンス(たとえば、入力例ではH2:h2val2)。headers[ヘッダー名は] はnilではないので、then 節が実行されます。これは、headers[ヘッダー名は]の配列値の中で次に使用可能なインデックスを決定し、そのインデックスにヘッダー値を置きます。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
<!--NeedCopy-->
元のヘッダーが解析され、ヘッダーテーブルが入力された後、このループは 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"
<!--NeedCopy-->
combined_headers配列が構築された後、要素を1つの文字列に連結し、HTTPヘッダーを終了する二重のrnを追加します。
return result_str
<!--NeedCopy-->
COMBINE_HEADERS 拡張関数の結果として文字列を返します。