HTTP Echo モジュール¶
Name¶
ngx_echo - Brings “echo”, “sleep”, “time”, “exec” and more shell-style goodies to NGINX config file.
注意
このモジュールはNGIXのソースと一緒に配布されません。 インストレーションの説明を見てください。
状態¶
このモジュールはプロダクションの準備ができています。
概要¶
location /hello {
echo "hello, world!";
}
location /hello {
echo -n "hello, ";
echo "world!";
}
location /timed_hello {
echo_reset_timer;
echo hello world;
echo "'hello world' takes about $echo_timer_elapsed sec.";
echo hiya igor;
echo "'hiya igor' takes about $echo_timer_elapsed sec.";
}
location /echo_with_sleep {
echo hello;
echo_flush; # クライアントが以前の出力をすぐに見えるようにします
echo_sleep 2.5; # 秒
echo world;
}
# in the following example, accessing /echo yields
# hello
# world
# blah
# hiya
# igor
location /echo {
echo_before_body hello;
echo_before_body world;
proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more;
echo_after_body hiya;
echo_after_body igor;
}
location /echo/more {
echo blah;
}
# the output of /main might be
# hello
# world
# took 0.000 sec for total.
# and the whole request would take about 2 sec to complete.
location /main {
echo_reset_timer;
# subrequests in parallel
echo_location_async /sub1;
echo_location_async /sub2;
echo "took $echo_timer_elapsed sec for total.";
}
location /sub1 {
echo_sleep 2;
echo hello;
}
location /sub2 {
echo_sleep 1;
echo world;
}
# the output of /main might be
# hello
# world
# took 3.003 sec for total.
# and the whole request would take about 3 sec to complete.
location /main {
echo_reset_timer;
# subrequests in series (chained by CPS)
echo_location /sub1;
echo_location /sub2;
echo "took $echo_timer_elapsed sec for total.";
}
location /sub1 {
echo_sleep 2;
echo hello;
}
location /sub2 {
echo_sleep 1;
echo world;
}
# Accessing /dup gives
# ------ END ------
location /dup {
echo_duplicate 3 "--";
echo_duplicate 1 " END ";
echo_duplicate 3 "--";
echo;
}
# /bighello will generate 1000,000,000 hello's.
location /bighello {
echo_duplicate 1000_000_000 'hello';
}
# echo back the client request
location /echoback {
echo_duplicate 1 $echo_client_request_headers;
echo "\r";
echo_read_request_body;
echo_request_body;
}
# GET /multi will yields
# querystring: foo=Foo
# method: POST
# body: hi
# content length: 2
# ///
# querystring: bar=Bar
# method: PUT
# body: hello
# content length: 5
# ///
location /multi {
echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello';
}
location /sub {
echo "querystring: $query_string";
echo "method: $echo_request_method";
echo "body: $echo_request_body";
echo "content length: $http_content_length";
echo '///';
}
# GET /merge?/foo.js&/bar/blah.js&/yui/baz.js will merge the .js resources together
location /merge {
default_type 'text/javascript';
echo_foreach_split '&' $query_string;
echo "/* JS File $echo_it */";
echo_location_async $echo_it;
echo;
echo_end;
}
# accessing /if?val=abc yields the "hit" output
# while /if?val=bcd yields "miss":
location ^~ /if {
set $res miss;
if ($arg_val ~* '^a') {
set $res hit;
echo $res;
}
echo $res;
}
説明¶
このモジュールはストリーミング入力および出力、並行/順列サブリクエスト、タイマーおよびスリープ、メタデータアクセスのための多くのNGINX内部APIをラップします。
基本的に異なる種類の見せ掛けのサブリクエストの些細なエミュレートにより他のモジュールのテストとデバッグを手助けする様々なユーティリティを提供します。
以下のような実際のアプリケーションで必要とされる便利さも見つけるでしょう。
- メモリから直接静的コンテンツを提供する(NGINX設定ファイルからロード)。
- 独自のヘッダおよびフッタを使ってupstream応答をラップする(addition module のようなものですが、コンテンツを直接設定ファイルおよびNGINX変数から読み込みます)。
- 様々な“NGINX locations” (例えば、サブリクエスト)を(echo_location とその仲間)を使って一つのメインリクエストにマージします。
This is a special dual-role module that can lazily serve as a content handler or register itself as an output filter only upon demand. デフォルトでは、このモジュールは何もしません。
技術的には、このモジュールはモジュールの書き手にとって便利だろう以下の技術を実証します:
- コンテントハンドラーから直接並行のサブリクエストを発行します:
- サブリクエストのチェインと一緒に連続するものを渡すことで、コンテントハンドラーから直接チェインされたサブリクエストを発行します。
- 全てHTTP 1.1.メソッドをおよび任意の偽装したHTTPリクエストボディさえも使ってサブリクエストを発行します。
- 同時のイベントおよびタイマーを使ってコンテントハンドラーから直接NGINXのイベントモデルとやり取りをし、必要であればコンテントハンドラの後ろからやり直します。
- Dual-role module that can (lazily) serve as a content handler or an output filter or both.
- NGINX 設定ファイルの変数の生成と差し込み。
- output_chain, flushおよびその類型を使ったストリーミング出力の制御。
- コンテントハンドラからのクライアントリクエストボディの読み込み、終了後にコンテントハンドラへの(非同期の)返却。
- NGINX Cモジュールの開発を進めるためのPerlベースの宣言型テストスィートの使用。
コンテントハンドラーディレクティブ¶
以下のディレクティブの使用はコンテントハンドラとしてこのモジュールを現在のNGINX locationに登録します。standard proxyモジュールのような他のモジュールをコンテントハンドラとして使いたい場合は、このモジュールによって提供されるfilter ディレクティブを使ってください。
全てのコンテントハンドラディレクティブは一つのNGINX locationに混成することができ、それらはちょうどBashスクリプト言語のように順番に実行することになっています。
各コンテントハンドラディレクティブは(もしあれば)その引数の中で変数の差し込みをサポートします。
standard default_type ディレクティブで設定されたMIMEタイプは、以下のようにこのモジュールによって考慮されます:
location /hello {
default_type text/plain;
echo hello;
}
そして、クライアント側では:
$ curl -I 'http://localhost/echo'
HTTP/1.1 200 OK
Server: nginx/0.8.20
Date: Sat, 17 Oct 2009 03:40:19 GMT
Content-Type: text/plain
Connection: keep-alive
v0.22 リリースから、rewrite moduleのif ディレクティブの中に全てのディレクティブを入れることができるようになりました。例えば:
location ^~ /if {
set $res miss;
if ($arg_val ~* '^a') {
set $res hit;
echo $res;
}
echo $res;
}
echo¶
構文: | echo [options] <string>... |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
空白で結合された引数を改行付きでクライアントに送信します。
データがNGINXの後ろにあるバッファでバッファされるかも知れないことに注意してください。出力データを強制的にすぐにフラッシュするには、echoのすぐ後にecho_flushコマンドを使ってください
echo hello world;
echo_flush;
引数が指定されない場合は、echo はシェルの echo コマンドのように改行のみを出力します。
変数が引数の中に現れるかも知れません。例としては、
echo The current request uri is $request_uri;
$request_uri は HttpCoreModuleによって公開された変数です。
このコマンドは一つのlocation設定の中で以下のように複数回使うことができます。
location /echo {
echo hello;
echo world;
}
クライアント側の出力はこのように見えます
$ curl 'http://localhost/echo'
hello
world
改行のような特別な文字 (\n
) とタブ (\t
) はC形式のエスケープシーケンスを使ってエスケープすることができます。しかし、注目に値する例外はダラー記号($
)です。NGINX 0.8.20の時点では、この文字をエスケープする安全な方法はまだありません。(A work-around might be to use a $echo_dollor
variable that is always evaluated to the constant $
character. この機能はこのモジュールの将来のバージョンで導入されるかも知れません。)
echo v0.28 リリースの時点で、-n
オプションを使って出力内の改行文字を抑制することができます。
location /echo {
echo -n "hello, ";
echo "world";
}
/echo
へのアクセスは以下をもたらします
$ curl 'http://localhost/echo'
hello, world
Leading -n
in variable values won’t take effect and will be emitted literally, as in
location /echo {
set $opt -n;
echo $opt "hello,";
echo "world";
}
これは以下の出力をもたらします。
$ curl 'http://localhost/echo'
-n hello,
world
One can output leading -n
literals and other options using the special --
option like this
location /echo {
echo -- -n is an option;
}
which yields
$ curl 'http://localhost/echo'
-n is an option
echo_duplicate¶
構文: | echo_duplicate <count> <string> |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
最初の引数によって指定される回数を使って、2つ目の引数によって指示される文字列のデュプリケートを出力します。
例えば、
location /dup {
echo_duplicate 3 "abc";
}
出力は"abcabcabc"
となるでしょう。
カウント数の中のアンダスコアがPerlのように許可されます。例えば、"hello, world"
の1000,000,000インスタンスを出力するには:
location /many_hellos {
echo_duplicate 1000_000_000 "hello, world";
}
count
引数は0がありえますが、負数ではありません。同様に、二つ目の文字列
引数は空文字("")がありえます。
echo ディレクティブと異なり、結果に改行が追加されません。So it’s possible to “abuse” this directive as a no-trailing-newline version of echo by using “count” 1, as in
location /echo_art {
echo_duplicate 2 '---';
echo_duplicate 1 ' END '; # we don't want a trailing newline here
echo_duplicate 2 '---';
echo; # we want a trailing newline here...
}
以下を得ます
------ END ------
このディレクティブは最初 v0.11で導入されました。
echo_flush¶
構文: | echo_flush |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
ソケットを使ってNGINXが内部的に持つ出力フィルターにバッファされているデータをすぐにクライアント側に送信させます。
技術的にはそのコマンドは単にflush
スロットが1に設定されたngx_buf_t オブジェクトを出力します。つまり、ある奇妙なサードパーティの出力フィルタモジュールがNGINXの(最後の)writeフィルタにたどり着く前にブロックするかも知れません。
このディレクティブは何も引数を取りません。
次の例を考えます:
location /flush {
echo hello;
echo_flush;
echo_sleep 1;
echo world;
}
そしてクライアントサイドで、/flush
にアクセスするためにcurlを使うと、すぐに“hello”を見ますが1秒後に最後の"world"の行を見るでしょう。上の例でecho_flush
の呼び出し無しでは、ほとんどの場合NGINXの内部バッファのために1秒過ぎるまで何も出力を見ないでしょう。
このディレクティブはサブリクエストが含まれる場合には出力バッファのフラッシュに失敗するでしょう。次の例を考えます:
location /main {
echo_location_async /sub;
echo hello;
echo_flush;
}
location /sub {
echo_sleep 1;
}
そうすると、クライアントは/sub
へのサブリクエストが実際に実行を開始する前にたとえecho_flush
が実行されたとしても"hellow"を見ることはないでしょう。echo_location_asyncの後に送信された /main
の出力は延期されしっかりとバッファされるでしょう。
これはサブリクエストが初期化される前に送信された出力へは適用 されません。上で与えられた例の修正バージョンについては:
location /main {
echo hello;
echo_flush;
echo_location_async /sub;
}
location /sub {
echo_sleep 1;
}
クライアントは/sub
がsleepに入る前に"hello"をすぐに見るでしょう。
echo, echo_sleep および echo_location_asyncも見てください。
echo_sleep¶
構文: | echo_sleep <seconds> |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
引数で指定された時間、これは秒です、sleepします。
この操作はサーバサイドではecho_blocking_sleep ディレクティブと違ってブロックしないので、NGINXワーカープロセス全体をブロックしないでしょう。
期間は少数点の後に3つの数値を取るかも知れません。そして0.001より大きくなければなりません。
例としては、
location /echo_after_sleep {
echo_sleep 1.234;
echo resumed!;
}
裏でリクエストごとに"sleep" ngx_event_tオブジェクトをセットアップし、NGINXイベントモデルに独自のイベントを使ってタイマーを追加し、イベントのタイムアウトをただ待ちます。"sleep"イベントはリクエストごとのため、このディレクティブは並行のサブリクエストで動作します。
echo_blocking_sleep¶
構文: | echo_blocking_sleep <seconds> |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
これは echo_sleep ディレクティブのブロッキングバージョンです。
詳細はecho_sleepのドキュメントを見てください。
裏で、NGINXコアによって提供されるPOSIX互換システムのusleepにマップされるngx_msleepマクロを呼びます。
実行される間このディレクティブは現在のNGINXワーカープロセスを完全にブロックするため、プロダクション環境では絶対に使わないことに注意してください。
echo_reset_timer¶
構文: | echo_reset_timer |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
タイマーの開始時間をnow、つまりこのコマンドがリクエストで実行された時間、に再設定します。
タイマーの開始時間のデフォルトは現在のリクエストが開始した時間で、一つのlocationでもしかすると複数回このディレクティブによって上書きすることができます。例えば:
location /timed_sleep {
echo_sleep 0.03;
echo "$echo_timer_elapsed sec elapsed.";
echo_reset_timer;
echo_sleep 0.02;
echo "$echo_timer_elapsed sec elapsed.";
}
クライアント側の出力は以下のようになるかも知れません
$ curl 'http://localhost/timed_sleep'
0.032 sec elapsed.
0.020 sec elapsed.
取得する実際の数値はシステムの現在の活動状態によって少し変わるかもしれません。
このディレクティブの呼び出しは裏にあるNGINXタイマーを(設定ファイル内のいたるところで指定されたタイマーの解像度に関係なく)現在のシステム時間に強制的に更新させるでしょう。更に、$echo_timer_elapsed変数の参照もタイマーの更新を強制的に引き起こすでしょう。
echo_sleep と $echo_timer_elapsedも見てください。
echo_read_request_body¶
構文: | echo_read_request_body |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
(ボディがそれほど大きくなくNGINXによってローカルの一時ファイルに保存することができる場合は)$request_body 変数が常に空以外の値を持つように、明示的にリクエストボディを読み込みます。
現在のリクエストは親によって指定された"artificial"ボディを持つサブリクエストかも知れないため、もとのクライアントのリクエストボディでは無いかも知れないことに注意してください。
このディレクティブは echo_sleepのように、それ自身では何も生成しません。
以下は、もとのHTTPクライアントリクエスト(ヘッダとボディが含まれます)をエコーバックする例です:
location /echoback {
echo_duplicate 1 $echo_client_request_headers;
echo "\r";
echo_read_request_body;
echo $request_body;
}
私側にはこの/echoback
のコンテントはこのように見えます(サーバ上でこのlocationにアクセスするためにPerlのLWPユーティリティを使っていました)。
$ (echo hello; echo world) | lwp-request -m POST 'http://localhost/echoback'
POST /echoback HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: localhost
User-Agent: lwp-request/5.818 libwww-perl/5.820
Content-Length: 12
Content-Type: application/x-www-form-urlencoded
hello
world
/echoback
がメインのリクエストのため、$request_body は元のクライアントのリクエストボディを保持します。
NGINX 0.7.56より前は、$request_body が NGINX 0.7.58 で初めて導入されたため、このディレクティブの使用は意味がありません。
このディレクティブ自身は v0.14で初めて導入されました。
echo_location_async¶
構文: | echo_location_async <location> [<url_args>] |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
二つ目の引数で指定されたurl引数を使って(一つ目の引数で)指定されたlocationにサブリクエストのGETを発行します。
NGINX 0.8.20 の時点では、location
引数はngx_http_subrequest
の制限により名前付きのlocationをサポート しません同じような echo_location ディレクティブに関しても同じことが言えます。
とても簡単な例
location /main {
echo_location_async /sub;
echo world;
}
location /sub {
echo hello;
}
/main
にアクセスすると以下を得ます
hello
world
並行して複数のlocationを呼び出すことも可能です:
location /main {
echo_reset_timer;
echo_location_async /sub1;
echo_location_async /sub2;
echo "took $echo_timer_elapsed sec for total.";
}
location /sub1 {
echo_sleep 2; # sleeps 2 sec
echo hello;
}
location /sub2 {
echo_sleep 1; # sleeps 1 sec
echo world;
}
/main
のアクセスは以下をもたらします
$ time curl 'http://localhost/main'
hello
world
took 0.000 sec for total.
real 0m2.006s
user 0m0.000s
sys 0m0.004s
メインハンドラ /main
がサブリクエスト /sub1
と /sub2
を完了するのを待た ない、そして素早く続けるので、"0.000 sec"の時間の結果となるのが見えるでしょう。リクエスト全体では、 /sub1
と /sub2
は並行して実行(あるいはもっと正確には"同時に")するため、完了するのに全体で約2秒かかります。
上の例で echo_blocking_sleep を代わりに使う場合、"blocking sleep"ブロックはNGINXワーカープロセス全体をブロックするため同じ出力を得ますが全体では3秒掛かります。
locationは任意のクエリ文字列の引数を取ることもできます。例えば、
location /main {
echo_location_async /sub 'foo=Foo&bar=Bar';
}
location /sub {
echo $arg_foo $arg_bar;
}
/main
のアクセスは以下をもたらします
$ curl 'http://localhost/main'
Foo Bar
クエリ文字列は直接"?"を付けてlocation
の引数につなげることは 許可されていません。例えば、/sub?foo=Foo&bar=Bar
は無効なlocatoinであり、このディレクティブの最初の引数として渡されてはなりません。
技術的に言うと、このディレクティブはNGINXのコンテントハンドラが1つ以上のサブリクエストを直接発行する例です。私の知る限りでは、fancyindex module も同じようなことをします ;)
@foo
のような名前付きのlocationはここではサポートされません。
このディレクティブは論理的にはecho_subrequest_asyncのGETバージョンと等価です。例えば:
echo_location_async /foo 'bar=Bar';
これは以下と論理的に等価です
echo_subrequest_async GET /foo -q 'bar=Bar';
しかしこのディレクティブの呼び出しはGET
のようなHTTPメソッド名および-q
のようなオプションのパースをしなくても良いため、GET
が付いたecho_subrequest_asyncよりもわずかに高速です。
標準のstandard SSI モジュールを無効にした場合にこのディレクティブには良く知られた問題があります。詳細は良く知られた問題を見てください。
このディレクティブはこのモジュールの v0.09で最初に導入され、少なくとも NGINX 0.7.46 を必要とします。
echo_location¶
構文: | echo_location <location> [<url_args>] |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
echo_location_asyncディレクティブに似ていますが、 echo_location
は並行ではなく 順番に サブリクエストを発行します。すなわち、このディレクティブに続くコンテントハンドラのディレクティブはこのディレクティブによって発行されたサブリクエストが完了するまで実行されないでしょう。
最終的な応答ボディは、出力の中でタイミング変数が使われた場合のみ、echo_location_asyncが代わりに使われた場合とほとんど同じになります。
次の例を考えます:
location /main {
echo_reset_timer;
echo_location /sub1;
echo_location /sub2;
echo "took $echo_timer_elapsed sec for total.";
}
location /sub1 {
echo_sleep 2;
echo hello;
}
location /sub2 {
echo_sleep 1;
echo world;
}
(もしecho_location_async を代わりに使った場合は2秒に対して)上の /main
は終了するのに全体で3秒掛かるでしょう。私のマシーン上での動きは以下の結果になります:
$ curl 'http://localhost/main'
hello
world
took 3.003 sec for total.
real 0m3.027s
user 0m0.020s
sys 0m0.004s
このディレクティブは論理的には echo_subrequestのGETバージョンと等価です。例えば:
echo_location /foo 'bar=Bar';
これは以下と論理的に等価です
echo_subrequest GET /foo -q 'bar=Bar';
しかしこのディレクティブの呼び出しはGET
のようなHTTPメソッド名および-q
のようなオプションのパースをしなくても良いため、GET
が付いたecho_subrequestよりもわずかに高速です。
裏ではそれはcontinuation としてngx_http_post_subrequest_t
オブジェクトを生成し、それを ngx_http_subrequest
関数呼び出しに渡します。NGINXはこの"continuation"を ngx_http_finalize_request
関数呼び出しの中で再び開くでしょう。親のリクエストのコンテントハンドラの実行を再開し、もしあれば次のディレクティブ(コマンド)の実行を開始します。
@foo
のような名前付きのlocationはここではサポートされません。
このディレクティブは v0.12で初めて導入されました。
引数の意味についての詳細は echo_location_async を見てください。
echo_subrequest_async¶
構文: | echo_subrequest_async <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>] |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
HTTPメソッドを使って非同期のサブリクエスト、任意のurl引数(あるいはクエリ文字列)、および文字列あるいはボディを含むファイルへのパスとして定義することができる任意のリクエストボディを初期化します。
このディレクティブは一般化された echo_location_async ディレクティブのバージョンととても良く似ています。
以下は使い方の例の小さな実演です:
location /multi {
# body defined as string
echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
# 絶対パスで無い場合はnginxのprefixパスに相対的な、ファイルパスとして定義されたボディ
echo_subrequest_async PUT '/sub' -q 'bar=Bar' -f '/tmp/hello.txt';
}
location /sub {
echo "querystring: $query_string";
echo "method: $echo_request_method";
echo "body: $echo_request_body";
echo "content length: $http_content_length";
echo '///';
}
そして、クライアント側では:
$ echo -n hello > /tmp/hello.txt
$ curl 'http://localhost/multi'
querystring: foo=Foo
method: POST
body: hi
content length: 2
///
querystring: bar=Bar
method: PUT
body: hello
content length: 5
///
以下はサブリクエストを処理するために標準的なproxy module を使った面白い例です:
location /main {
echo_subrequest_async POST /sub -b 'hello, world';
}
location /sub {
proxy_pass $scheme://127.0.0.1:$server_port/proxied;
}
location /proxied {
echo "method: $echo_request_method.";
# ここで明示的にbody、あるいは$echo_request_body を読み込む必要があります
# empty("")と同じでしょう
echo_read_request_body;
echo "body: $echo_request_body.";
}
そして、クライアント側では以下を見るかも知れません
$ curl 'http://localhost/main'
method: POST.
body: hello, world.
@foo
のような名前付きのlocationはここではサポートされません。
このディレクティブは幾つかのオプションを取ります:
-q <url_args> サブリクエストのためのURL引数(あるいはURLクエリ文字列)を指定します。
-f <path> ファイルの内容がサブリクエストのリクエストボディとして提供される
ファイルのパスを指定します。
-b <data> リクエストボディのデータを指定します。
このディレクティブは v0.15で初めて導入されました。
ボディのためのファイルパスを定義するための :github:-f
オプションはv0.35 <openresty/echo-nginx-module/tags>で導入されました。
echo_subrequest および echo_location_async ディレクティブも見てください。
標準のstandard SSI モジュールを無効にした場合にこのディレクティブには良く知られた問題があります。詳細は良く知られた問題を見てください。
echo_subrequest¶
構文: | echo_subrequest <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>] |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
これは echo_subrequest_async ディレクティブの非同期バージョンです。echo_locationのように、それはNGINXワーカプロセスをブロックせず(echo_blocking_sleepはします)、むしろサブリクエストのチェインと一緒に制御を渡すためにcontinuationを使用します。
詳細は echo_subrequest_asyncを見てください。
@foo
のような名前付きのlocationはここではサポートされません。
このディレクティブは v0.15で初めて導入されました。
echo_foreach_split¶
構文: | echo_foreach_split <delimiter> <string> |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
1つ目の引数で指定されるデリミタを使って、二つ目の引数のstring
を分割し、結果の項目を最初から最後まで繰り返します。例えば:
location /loop {
echo_foreach_split ',' $arg_list;
echo "item: $echo_it";
echo_end;
}
/main へのアクセスは以下をもたらします
$ curl 'http://localhost/loop?list=cat,dog,mouse'
item: cat
item: dog
item: mouse
前の例で見たように、このディレクティブはecho_end ディレクティブと常に一緒に無ければなりません。
平行するecho_foreach_split
ループは許可されますが、入れ子は現在のところ禁止されています。
delimiter
は以下のように 複数の 任意の文字を含むことができます。
# this outputs "cat\ndog\nmouse\n"
echo_foreach_split -- '-a-' 'cat-a-dog-a-mouse';
echo $echo_it;
echo_end;
論理的に言うと、このループ構造は(以前の例を使って)perlでの split
関数を組み合わせた、まさにforeach
ループです。
foreach (split ',', $arg_list) {
print "item $_\n";
}
複数の.js
あるいは .css
リソースを丸ごとまとめるのにも便利だと気づくでしょう。例は次のようになります:
location /merge {
default_type 'text/javascript';
echo_foreach_split '&' $query_string;
echo "/* JS File $echo_it */";
echo_location_async $echo_it;
echo;
echo_end;
}
そして、クエリ文字列内で指定される.js
リソースをマージするためにアクセス/マージします:
$ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js'
以前の例での/merge
location によって生成されたマージされた応答をキャッシュするために、サードパーティのNGINXキャッシュモジュールを使うこともできます。
このディレクティブは v0.17で初めて導入されました。
echo_end¶
構文: | echo_end |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
このディレクティブはループのボディおよび echo_foreach_splitのような条件付制御構造を終了させるために使われます。
このディレクティブは v0.17で初めて導入されました。
echo_request_body¶
構文: | echo_request_body |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
前に読み込んだリクエストボディの内容を出力します。
裏では、大まかに以下のような実装がされています:
if (r->request_body && r->request_body->bufs) {
return ngx_http_output_filter(r, r->request_body->bufs);
}
$echo_request_body と $request_body 変数と異なり、このディレクティブはもしリクエストボディの一部分あるいは全ての部分がディスク上の一時ファイルに保存されている場合、全てのリクエストボディを表示するでしょう。
まだリクエストボディが読み込まれていない場合は、"no-op"です。
このディレクティブはv0.18で初めて導入されました。
echo_read_request_bodyも見てください。
echo_exec¶
構文: | echo_exec <location> [<query_string>] |
---|---|
構文: | echo_exec <named_location> |
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
指定されたlocationへの内部リダイレクトをします。以下のように、通常のlocationのために任意のクエリ文字列を指定することができます
location /foo {
echo_exec /bar weight=5;
}
location /bar {
echo $arg_weight;
}
あるいは、以下も等価です
location /foo {
echo_exec /bar?weight=5;
}
location /bar {
echo $arg_weight;
}
名前付きのlocationもサポートされます。例は次のようになります:
location /foo {
echo_exec @bar;
}
location @bar {
# @barではなく /foo を取得するでしょう
# これはnginxの潜在的なバグのためです
echo $echo_request_uri;
}
しかし、名前付きのlocationのリダイレクトに関して(どのような)クエリ文字列もngx_http_named_location
関数の制限のために常に無視されるでしょう。
echo_exec
ディレクティブの前に何かを出力しようとしてはいけません。そうでなければリダイレクトしたい先のlocationの適切な応答を見れないでしょう。何らかの出力をするとリダイレクトが起こる前に元のlocationハンドラにHTTPヘッダを送信させることになるだろうからです。
技術的に言うと、このディレクティブはNGINX内部API関数 ngx_http_internal_redirect
と ngx_http_named_location
を公開します。
このディレクティブは v0.21で初めて導入されました。
echo_status¶
構文: | echo_status <status-num> |
---|---|
デフォルト: | 200 |
コンテキスト: | location, location if |
Phase: | content |
デフォルトの応答ステータスコードを指定します。デフォルトは 200
です。このディレクティブは宣言的で、他のechoのようなディレクティブとの相対的な順番は重要ではありません。
例は以下のようになります。
location = /bad {
echo_status 404;
echo "Something is missing...";
}
このような応答を得ます:
HTTP/1.1 404 Not Found
Server: nginx/1.2.1
Date: Sun, 24 Jun 2012 03:58:18 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
Something is missing...
このディレクティブは v0.40
リリースで初めて導入されました。
フィルタ ディレクティブ¶
以下のディレクティブの使用はこのモジュールのフィルタ登録を行います。デフォルトでは、このモジュールによって何もフィルタは登録されません。
各フィルタのディレクティブは(もし引数があれば)その引数の変数の補間をサポートします。
echo_before_body¶
構文: | echo_before_body [options] [argument]... |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | 出力フィルタ |
echo ディレクティブのフィルタバージョンです。裏で動くコンテントハンドラによって生成された元の出力の最初にその出力を追加します。
例としては、
location /echo {
echo_before_body hello;
proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more;
}
location /echo/more {
echo world
}
クライアント側からの/echo
アクセスは、以下をもたらします。
hello
world
以前の例では、"主要なコンテンツ"を生成する裏で実行されるコンテントハンドラとして 標準プロキシモジュール を借りています。
このフィルタ ディレクティブの複数のインスタンスも許可されます:
location /echo {
echo_before_body hello;
echo_before_body world;
echo !;
}
クライアント側では、出力は以下のようになります。
$ curl 'http://localhost/echo'
hello
world
!
この例では、裏で動くコンテントハンドラとしてこのモジュールによって提供される コンテント ハンドラ ディレクティブ も使用します。
このディレクティブは、echoディレクティブのような -n
および --
オプションもサポートします。
このディレクティブは同じようなディレクティブ echo_after_body と混ぜることができます。
echo_after_body¶
構文: | echo_after_body [argument]... |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | 出力フィルタ |
echo_before_body ディレクティブにとても似ていますが、裏で実行されるコンテントハンドラによって生成された元の出力の最後にその出力を追加します
以下は簡単な例です:
location /echo {
echo_after_body hello;
proxy_pass http://127.0.0.1:$server_port$request_uri/more;
}
location /echo/more {
echo world
}
クライアント側からの/echo
アクセスは、以下をもたらします。
world
hello
複数のインスタンスが許可されます:
location /echo {
echo_after_body hello;
echo_after_body world;
echo i;
echo say;
}
/echo
location へアクセスする間のクライアント側の出力は以下のようになります。
i
say
hello
world
このディレクティブは、echoディレクティブのような -n
および --
オプションもサポートします。
このディレクティブは同じようなディレクティブ echo_before_body と混ぜることができます。
変数¶
$echo_it¶
これは、ちょうどPerlでの$_
のような、echo_foreach_splitによって使われる"topic variable"です。
$echo_timer_elapsed¶
この変数は、現在のリクエストの開始(サブリクエストかも知れません)、あるいはecho_reset_timerコマンドの最後の起動から経過した秒数を保持します。
結果の時間は小数点の後に3つの数値を取ります。
この変数の参照は、echo_reset_timerディレクティブと同じように、設定ファイル内のいたるところで設定されているタイマーの解像度に関係なく、裏で動いているNGINXタイマーに現在のシステム時間を更新させるでしょう。
$echo_request_body¶
リクエストボディが一時ファイルに全く保存されていない場合は、前もって読み込まれた(サブ)リクエストのリクエストボディが評価されます。リクエストボディがとても大きな場合でも表示するには、echo_request_body ディレクティブを使ってください。
$echo_request_method¶
現在のリクエスト(サブリクエストかも知れません)のHTTPリクエストメソッドを評価します。
裏では、r->method_name
に保存されている文字列データを取るだけです。
それを$echo_client_request_method 変数と比較します。
少なくともNGINX 0.8.20とそれ以前は、http core moduleによって提供される$request_method 変数は実際のところ$echo_client_request_method がすることをしています。
この変数は v0.15で初めて導入されました。
$echo_client_request_method¶
現在のリクエストがサブリクエストであってもメインのリクエストのHTTPメソッドを常に評価します。
裏では、r->main->method_name
に保存されている文字列データを取るだけです。
それを$echo_request_method 変数と比較します。
この変数は v0.15で初めて導入されました。
$echo_client_request_headers¶
元のクライアントのリクエストヘッダを評価します。
名前が示すとおり、もし現在サブリクエストが実行されている場合でも、常にメインのリクエスト(あるいはクライアントリクエスト)をとるでしょう。
簡単な例は以下の通りです:
location /echoback {
echo "headers are:"
echo $echo_client_request_headers;
}
/echoback
のアクセスは以下をもたらします
$ curl 'http://localhost/echoback'
headers are
GET /echoback HTTP/1.1
User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g
Host: localhost:1984
Accept: */*
Behind the scene, it recovers r->main->header_in
(or the large header buffers, if any) on the C level and does not construct the headers itself by traversing parsed results in the request object.
この変数はv0.15で初めて導入されました。
$echo_cacheable_request_uri¶
現在の(サブ)リクエストのURI(通常は/
から始まる)のパースされた形式を評価します。$echo_request_uri 変数と異なり、キャッシュ可能です。
詳細は$echo_request_uri を見てください。
この変数はv0.17で初めて導入されました。
$echo_request_uri¶
現在の(サブ)リクエストのURI(通常は/
から始まる)のパースされた形式を評価します。$echo_cacheable_request_uri 変数と異なり、キャッシュ可能ではありません。
$request_uri
は現在のリクエストのURIのパースされていない形式のため、これはHttpCoreModuleによって公開される$request_uri変数とかなり異なります。
この変数はv0.17で初めて導入されました。
$echo_incr¶
1から始まる現在のカウント数を常に生成するカウンターです。サブリクエストでアクセスされた場合でも、カウンターはメインリクエストに常に関連します。
以下の例を考えてみましょう
location /main {
echo "main pre: $echo_incr";
echo_location_async /sub;
echo_location_async /sub;
echo "main post: $echo_incr";
}
location /sub {
echo "sub: $echo_incr";
}
/main
のアクセスは以下をもたらします
main pre: 1
sub: 3
sub: 4
main post: 2
このディレクティブはv0.18で初めて導入されました。
インストール
(NGINXコアおよび他の多くの良いものと同じく)このモジュールをngx_openresty bundleを使ってインストールすることをお勧めします。ダウロードとシステムにnxg_openrestyをインストールする方法は詳細な説明を見てください。これは準備をするのに最も簡単で最も安全な方法です。
他のやり方として、NGINXソースを使って手動でこのモジュールをインストールすることができます:
例えば、バージョン1.7.7 (NGINX互換性を見てください)のNGINXのソースコードをnginx.orgからダウンロードし、このモジュールを使ってソースをビルドします:
$ wget 'http://nginx.org/download/nginx-1.7.7.tar.gz'
$ tar -xzvf nginx-1.7.7.tar.gz
$ cd nginx-1.7.7/
# Here we assume you would install you nginx under /opt/nginx/.
$ ./configure --prefix=/opt/nginx \
--add-module=/path/to/echo-nginx-module
$ make -j2
$ make install
echo-nginx-module file listからこのモジュールのリリースtarballの最新バージョンをダウンロードします。
また、このモジュールはngx_openresty bundleの中に含まれていてデフォルトで有効です。
互換性¶
NGINXの以下のバージョンがこのモジュールで動作するはずです:
- 1.7.x (最後のテスト: 1.7.7)
- 1.6.x
- 1.5.x (最後のテスト: 1.5.12)
- 1.4.x (最後のテスト: 1.4.4)
- 1.3.x (最後のテスト: 1.3.7)
- 1.2.x (最後のテスト: 1.2.9)
- 1.1.x (最後のテスト: 1.1.5)
- 1.0.x (最後のテスト: 1.0.11)
- 0.9.x (最後のテスト: 0.9.4)
- 0.8.x (最後のテスト: 0.8.54)
- 0.7.x >= 0.7.21 (最後のテスト: 0.7.68)
特に、
- ディレクティブecho_location_async とそれと同じ echo_subrequest_asyncは 0.7.x < 0.7.46で動作 しません。
- echo_after_body ディレクティブはNGINX < 0.8.7と全く動作しません。
- echo_sleep ディレクティブはNGINX < 0.8.11に関して、echo_location あるいは echo_subrequest の後で使うことができません。
0.6.x および 0.5.x のNGINXの早期バージョンは全く動作しない でしょう。
0.7.21より上のNGINXのいずれかバージョンでこのモジュールが動作しないことに気づいた場合は、echo.reporting-a-bugを考えてみてください。
知られている問題¶
NGINXの未知のバグ(NGINX 1.7.7にもまだあります)のために、標準の SSI モジュールは echo_location_async および echo_subrequest_async が出力チェインをメインの出力に正しくマージされることを必要とします。幸いなことに、SSIモジュールはNGINXの configure
プロセスの間にデフォルトで有効にされます。
SSIモジュールを有効にせずにこのディレクティブを呼び出すと、どのサブリクエストの内容も無しに不完全な応答を取得し、以下のように NGINXの error.log
にアラートメッセージを得るでしょう:
[alert] 24212#0: *1 the http output chain is empty, client: 127.0.0.1, ...
テストのためにこのモジュールを使うモジュール¶
以下のモジュールはテストスィートの中でこの echo
モジュールを利用します:
- Memc モジュールはほとんど全てのmemcached TCP プロトコルをサポートします。
- Headers More モジュールは指定した条件での入力および出力ヘッダの追加、設定、消去をすることができます。
echo
モジュール自身。
どのような形でも echo
を使用する他のモジュールがあればメールしてください。上のリストにそれらを追加するでしょう :)
コミュニティ¶
英語のメーリングリスト¶
openresty-en メーリングリストは英語を話す人のためのものです。
バグのレポート¶
テストとコードの調整に多くの努力がされていますが、このモジュールのどこかにいくつかの深刻なバグが潜んでいるに違いありません。なんらかのおかしな動きがあった場合は、躊躇しないでください
- GitHubによって提供される issue tracking interface でチケットを作成する。
- あるいは、 OpenResty Communityにバグレポート、質問、あるいはパッチを送信する。
ソースリポジトリ¶
github上のopenresty/echo-nginx-moduleで利用可能です。
テストスィート¶
このモジュールはPerl駆動テストスィートが付いています。test cases も declarative です。Perl世界の Test::Nginx <http://search.cpan.org/perldoc?Test::Nginx> モジュールに感謝します。
アナタの側でそれを実行するには:
$ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t
NGINXサーバ バイナリを変更した場合はテストスィートを実行する前に全てのNGINXプロセスを終了する必要があります。
単一のNGINX サーバ (デフォルトでは localhost:1984
) は全てのテストスクリプト(.t
ファイル)に渡って使用されるため、prove
ユーティリティを起動する時に-jN
を指定することで並行してテストスィートを実行することは意味がありません。
テストスィートの幾つかの部分はNGINXをビルドする時に標準モジュール proxy, rewrite および SSI を有効にする必要があります。
TODO¶
サブリクエストのecho_after_body ディレクティブを修正します。
ディレクティブecho_read_client_request_body および echo_request_headers を追加します。
NGINXのログ機能を直接設定ファイルから使うために echo_log を追加し、特定のログレベルが指定できます:
echo_log debug "I am being called.";
echo_subrequest_async および echo_subrequest にオプション
-h
および-t
のオプションのサポートを追加。例えば、echo_subrequest POST /sub -q 'foo=Foo&bar=Bar' -b 'hello' -t 'text/plan' -h 'X-My-Header: blah blah'
サブリクエストが親のリクエスト(つまり、当のサブリクエストを呼び出している現在のリクエスト)からキャッシュされた変数を継承しなければならないかを制御するオプションを追加。現在のところ、このモジュールによって発行されたサブリクエストは親のリクエストからキャッシュされた変数を継承しません。
r->main->count - 1
を表示するために、新しい変数$echo_active_subrequests を追加。echo_file およびecho_cached_file ディレクティブを追加。
既存の$echo_client_request_headers変数と一緒にするために新しい変数$echo_request_headers を追加。
新しいディレクティブecho_foreachを追加
echo_foreach 'cat' 'dog' 'mouse'; echo_location_async "/animals/$echo_it"; echo_end;
新しいディレクティブecho_foreach_rangeを追加
echo_foreach_range '[1..100]' '[a-zA-z0-9]'; echo_location_async "/item/$echo_it"; echo_end;
新しいディレクティブecho_repeatを追加
echo_repeat 10 $i { echo "Page $i"; echo_location "/path/to/page/$i"; }
これは言い換えると
echo_foreach_range $i [1..10]; echo "Page $i"; echo_location "/path/to/page/$i"; echo_end;
このアイデアを提供してくれて、Marcus Clyne ありがとう。
新しいディレクティブ
echo_random_min
およびecho_random_max
による下限/上限を持つ非負数のランダム整数値を常に返す、新しい変数$echo_randomを追加。例えば:echo_random_min 10 echo_random_max 200 echo "random number: $echo_random";
このアイデアを提供してくれて、Marcus Clyne ありがとう。
Getting involved¶
AuthorにパッチをサブミットするかGitHub上の echo.source-repository に少しコミットするように依頼することはとても歓迎します。
Author¶
Yichun “agentzh” Zhang (章亦春) <agentzh@gmail.com>, CloudFlare Inc.
このwikiページもauthor自身によって整備されており、同様に誰でもこのページを改善することは奨励されています。
Copyright & License¶
Copyright (c) 2009-2014, Yichun “agentzh” Zhang (章亦春) <agentzh@gmail.com>, CloudFlare Inc.
このモジュールはBSDライセンスの条件でライセンスされます。
修正の如何に関係なくソースおよびバイナリ形式での再配布および使用は以下の条件に合う限り許可されます:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
参照
- このモジュールの初期の開発についての元の blogの投稿。
- 標準の 追加フィルタモジュール。
- 標準のプロキシ モジュール。
- ngx_openrestyのバンドル。