ポリシー拡張 - ユースケース
特定の顧客アプリケーションには、既存のポリシーや表現では対応できない要件があります。ポリシー拡張機能により、お客様は要件に合わせてカスタマイズされた機能をアプリケーションに追加できます。
次の使用例は、NetScalerアプライアンスのポリシー拡張機能を使用して新しい機能を追加する方法を示しています。
- ケース 1: カスタムハッシュ
- ケース 2: URL の二重スラッシュを折りたたむ
- ケース 3: ヘッダーの結合
ケース 1: カスタムハッシュ
CUSTOM_HASH 関数は、クライアントに送信される応答に任意のタイプのハッシュ値を挿入するメカニズムを提供します。このユースケースでは、ハッシュ関数を使用して HTTP 書き換えリクエストのクエリ文字列のハッシュを計算し、その計算値を含む CUSTOM_HASH という名前の HTTP ヘッダーを挿入します。CUSTOM_HASH 関数は DJB2 ハッシュアルゴリズムを実装しています。
カスタムハッシュの使用例:
> add rewrite action test_custom_hash insert_http_header "CUSTOM_HASH" "HTTP.REQ.URL.QUERY.CUSTOM_HASH"
<!--NeedCopy-->
カスタムハッシュ () のサンプル定義:
-- 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-->
上記のサンプルの 1 行ごとの説明:
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の二重スラッシュをシングルスラッシュに置き換える機能を追加できます。次の例は、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-->
上記のサンプルの 1 行ごとの説明:
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 リクエストの最初の行 (例:GET /combine_headers HTTP/1.1) が含まれているため、AFTER_STR (「HTTP/1.1rN」) が必要です。
ストリングビルダーの式は 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
<!--NeedCopy-->
上記のサンプルの 1 行ごとの説明:
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 つのパラメータを取ります。1 つは検索する文字列で、もう 1 つは文字列の一部を一致させるために使用するパターンです。検索する文字列は、関数に入力されるヘッダーのテキストである暗黙のセルフパラメーターによって提供されます。
パターンは正規表現 (略してregex) を使用して表現されます。この正規表現は、HTTP **標準では*name*: value\ r\ nと定義されている各ヘッダーのヘッダー名と値と一致します。**正規表現の括弧は抽出する一致する部分を指定するので、正規表現の回路図は (match-name): (*match-value)\ r\ n になります。**match-name* パターンは、コロン以外のすべての文字と一致する必要があります。これは[^:]+
が書かれています。[^:]
は:
を除く任意の文字で、+
は1 回以上繰り返されます。同様に、マッチバリューパターンは 、\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
<!--NeedCopy-->
forループ内のこれらのステートメントは、ヘッダー名と値をヘッダーテーブルに入れます。ヘッダー名が初めて解析されるとき(たとえば、入力例ではH2: h2val1)、名前のヘッダーエントリはなく、headers [name] は nil です。
nilはfalseとして扱われるので、else節が実行されます。これは、name のヘッダエントリを 1 つの文字列値 name:value を持つ配列に設定します。
注: else ループの配列コンストラクターは {[1] = name と同じです。「:」.. value}: 配列の最初の要素を設定します。)最初の H2 ヘッダーには、ヘッダー [“H2”] = {“ h2: H2val1”} を設定します。
ヘッダーの後続のインスタンス (たとえば、入力例の H2: h2val2) では。headers [name] は nil ではないので、then 句が実行されます。これにより、headers [name] の配列値で次に利用できるインデックスを決定し、そのインデックスにヘッダー値を格納します。2 番目の 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
<!--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 ヘッダーを終了する 2 つの\ r\ n を追加します。
return result_str
<!--NeedCopy-->
COMBINE_HEADERS 拡張関数の結果として文字列を返します。