1. ホーム
  2. スクリプト・コラム
  3. ルア

OpenRestyの2つの正規パターンマッチの方法を解説

2022-01-06 16:11:22

プリアンブル

この記事では、OpenRestyの2種類の正規パターンマッチを紹介します。

まず最初に、OpenRestyスイートには、主にFFI APIの実装に基づいたOpenRestyの構文と、ネイティブのLua系スクリプト言語用の構文の2つがあることに注意する必要があります。

この記事では、この2つの構文の正規のパターン・マッチングを、それぞれngx.re.findとstring.findとします。

この2つのルールは全く同じ目的を果たします。指定されたパターンの文字列をサブジェクトの文字列から探し、マッチした場合は開始と終了の数字を返し、そうでない場合はnil null値2つを返します。2つの値は、パターンが見つかった場合にのみ生成されることに注意してください。例えば、変数が1つしかない場合は、開始位置の数字またはnil null値のみが生成されます。

Luaに慣れていても、string.findのようなLuaの正規構文を使うことは、もはや推奨されません。これは、Luaの正規表現は実装の違いからngx.re.*に比べて性能がかなり劣ること、また、Luaの正規構文はPOSIXに準拠していないのに対し、ngx.re.*は標準POSIX仕様で実装されていて、一般性や今日的な関連性がかなり高いためです。

もう一つの重要な理由は、毎回再コンパイルが必要なstring.*に対して、OpenRestyが提供するngx.re.*仕様はコンパイル後にPatternをキャッシュできること("o"パラメータを使用)と、さらにパフォーマンス向上のために"j "パラメータでJITを有効にできる(pcre JITサポート必要)ことです。

文字列検索

本当はもうstring.findを使う必要はないのですが(前波はビーチで死んだ)、今使っているのがこれなので簡単に紹介します(理由は後述)。

-- syntax
from, to, err = string.find(s, pattern, start, [plain])

-- context
init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_ lua*, ngx.timer.\*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*

-- example
string.find(ngx.var.http_user_agent, "360")

上記の例は、UAに"360"が含まれる場合にマッチし、マッチがヒットしたときに返される値が、文字列の先頭と末尾のビット数(左から右へ)であるという仕組みになっています。例として、ngx.sayを使って出力を表示するには、まず以下のコードを完成させます。

-- Define the variables
var = string.find(ngx.var.http_user_agent, "360")

-- output
ngx.say("var=" . var)

Nginx サイトの /example パスの下に置いてください。

location = /example {
 access_by_lua_block {
 var = string.find(ngx.var.http_user_agent, "360")
 ngx.say("var=" . var)
 }
}

次に、curl を使用してレスポンスをテストします。

# Send a request, specifying the UA as 360 by the way
curl example.com -A "360"

# Return the response with the string echoed by ngx.say
# The "360" string matched here is at the beginning of the word and has 1 digit
var=1

ngx.re.find

ngx.re.find仕様の利点はすでに上で説明したとおりですので、ここでは基本的な構文を説明します(詳しくは 公式ドキュメント ) と、それを利用するための要件 ("o" パラメータ キャッシュの使用と pcre JIT の使用) を説明します。

-- syntax
from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)

-- context
init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_ lua*, ngx.timer.\*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*

-- example
ngx.re.find(ngx.var.http_user_agent, "360", "jo")

ngx.re.*の仕様を使い、より高いパフォーマンスを得るためには、次の3つの条件を満たす必要があります。 -with-pcre-jit 引数でコンパイルして pcre JITサポートを有効にし、 lua-resty-core サポート (OpenResty で直接インストール)、Luaコードを使う場合は require 'resty.XXX' を導入し、'resty.XXX' を使用する必要があります。 init_by_luaセクションにrequire 'core.regex' ステートメントを導入し(lua-resty-core APIサポートを導入するため)、コードを構築する際に"jo"パラメータを使用する習慣をつけてください、これらは両方ともpcre JITとパターンキャッシュのスイッチを提供するものです。上記の例で使用されているように

また、先ほどの例の実装として、Luaのコードは以下のようになります。

-- Define variables
var = ngx.re.find(ngx.var.http_user_agent, "360", "jo")

-- Output
ngx.say("var=" . var)

マイピット

最後に、なぜ私がいまだにstring.find構文を使っているのかを説明します。理由はかなり恥ずかしく、使いたくないわけではないのですが、使えないのです。私は次のようなコードを使っています。

if (ngx.re.find(ngx.var.request_uri, "^/admin/", "jo") ~= nil or ngx.re.find(ngx.var.request_uri, "^/tools/" , "jo") ~= nil) then
 return ngx.exit(ngx.HTTP_CLOSE)
end

その時、マッチングがうまくいっていないことに気がつきました。コードを単体で取り出すと、/admin/xxxや/tools/xxxにアクセスしても拒否されるのに、コードビルドに入れた途端、うまくいかなくなったのです。string.findに切り替えた後は大丈夫なので、私のコードの他の部分が原因ではないことは確かです。

間違ったルールのせいでないことを確認するために、次のようなテストも行いました。

if (ngx.var.request_uri == "/test1/") then
 if (ngx.re.find("/admin/test/", "^/admin/", "jo") ~= nil) then
  ngx.say("1=" . ngx.re.find("/admin/test/", "^/admin/", "jo"))
 end
elseif (ngx.var.request_uri == "/test2/") then
 if (ngx.re.find("/admintest/", "^/admin/", "jo") ~= nil) then
  ngx.say("2=" . ngx.re.find("/admintest/", "^/admin/", "jo"))
 end
elseif (ngx.var.request_uri == "/test3/") then
 if (ngx.re.find("/artic/", "^/admin/", "jo") ~= nil) then
  ngx.say("3=" . ngx.re.find("/artic/", "^/admin/", "jo"))
 end
elseif (ngx.var.request_uri == "/test4/") then
 if (ngx.re.find("/artic", "^/admin/", "jo") ~= nil) then
  ngx.say("4=" . ngx.re.find("/artic", "^/admin/", "jo"))
 end
elseif (ngx.var.request_uri == "/test5/") then
 if (ngx.re.find("/offline/admin/", "^/admin/", "jo") ~= nil) then
  ngx.say("5=" . ngx.re.find("/offline/admin/", "^/admin/", "jo"))
 end
elseif (ngx.var.request_uri == "/test6/") then
 if (ngx.re.find("/offline/", "^/admin/", "jo") ~= nil) then
  ngx.say("6=" . ngx.re.find("/offline/", "^/admin/", "jo"))
 end
elseif (ngx.var.request_uri == "/test7/") then
 if (ngx.re.find("/admin/", "^/admin/", "jo") ~= nil) then
  ngx.say("7=" . ngx.re.find("/admin/", "^/admin/", "jo"))
 end
elseif (ngx.var.request_uri == "/test8/") then
 if (ngx.re.find("/adm/in", "^/admin/", "jo") ~= nil) then
  ngx.say("8=" . ngx.re.find("/adm/in", "^/admin/", "jo"))
 end
else
 if (ngx.var.request_uri == "/test9/") then
  if (ngx.re.find("/admin", "^/admin/", "jo") ~= nil) then
   ngx.say("9=" . ngx.re.find("/admin", "^/admin/", "jo"))
  end
 end
end

テストの結果、間違いはなく、エコーの結果、^/admin/ が /admin/xxx に一意に一致することがわかりました。

要約すると

上記はこの記事のすべての内容です、私はあなたの勉強や仕事のためのこの記事の内容は、特定の参照学習価値があることを願って、あなたが交換するメッセージを残すことができます質問がある場合は、BinaryDevelopのあなたのサポートに感謝します。