特定のカスタマーアプリケーションでは、リクエスト内の複数のヘッダーを処理できません。また、同じヘッダー値を持つ重複したヘッダー、またはリクエスト内の同じ名前で値が異なる複数のヘッダーを解析すると、時間とネットワークリソースが消費されます。ポリシー拡張機能により、お客様はこれらのヘッダーを元の値を組み合わせた値を持つ単一のヘッダーにまとめる機能を追加できます。たとえば、ヘッダー 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 リクエストの最初の行 (例: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
上記のサンプルの 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
この汎用の 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
forループ内のこれらのステートメントは、ヘッダー名と値をヘッダーテーブルに入れます。ヘッダー名が初めて解析されるとき(たとえば、入力例ではH2: h2val1)、名前のヘッダーエントリはなく、headers
は nil です。
nilはfalseとして扱われるので、else節が実行されます。これは、name のヘッダエントリを 1 つの文字列値 name:value を持つ配列に設定します。
注: else ループの配列コンストラクターは {
= name と同じです。「:」.. value}: 配列の最初の要素を設定します。)最初の H2 ヘッダーには、ヘッダー
= {" h2: H2val1"} を設定します。
ヘッダーの後続のインスタンス (たとえば、入力例の H2: h2val2) では。headers
は nil ではないので、then 句が実行されます。これにより、headers
の配列値で次に利用できるインデックスを決定し、そのインデックスにヘッダー値を格納します。2 番目の 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 ヘッダーを終了する 2 つの\ r\ n を追加します。
return result_str
COMBINE_HEADERS 拡張関数の結果として文字列を返します。