echo: nginx Echo 模块
安装
您可以在任何基于 RHEL 的发行版中安装此模块,包括但不限于:
- RedHat Enterprise Linux 7, 8, 9 和 10
- CentOS 7, 8, 9
- AlmaLinux 8, 9
- Rocky Linux 8, 9
- Amazon Linux 2 和 Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-echo
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-echo
通过在 /etc/nginx/nginx.conf 的顶部添加以下内容来启用该模块:
load_module modules/ngx_http_echo_module.so;
本文档描述了 nginx-module-echo v0.64,于 2025 年 10 月 30 日发布。
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;
}
# 在以下示例中,访问 /echo 会得到
# 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;
}
# /main 的输出可能是
# hello
# world
# took 0.000 sec for total.
# 整个请求大约需要 2 秒完成。
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;
echo hello;
}
location /sub2 {
echo_sleep 1;
echo world;
}
# /main 的输出可能是
# hello
# world
# took 3.003 sec for total.
# 整个请求大约需要 3 秒完成。
location /main {
echo_reset_timer;
# 串行的子请求(通过 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;
}
# 访问 /dup 会得到
# ------ END ------
location /dup {
echo_duplicate 3 "--";
echo_duplicate 1 " END ";
echo_duplicate 3 "--";
echo;
}
# /bighello 将生成 1000,000,000 个 hello。
location /bighello {
echo_duplicate 1000_000_000 'hello';
}
# 回显客户端请求
location /echoback {
echo_duplicate 1 $echo_client_request_headers;
echo "\r";
echo_read_request_body;
echo_request_body;
}
# GET /multi 将产生
# 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 将合并 .js 资源
location /merge {
default_type 'text/javascript';
echo_foreach_split '&' $query_string;
echo "/* JS File $echo_it */";
echo_location_async $echo_it;
echo;
echo_end;
}
# 访问 /if?val=abc 会得到 "hit" 输出
# 而 /if?val=bcd 会得到 "miss":
location ^~ /if {
set $res miss;
if ($arg_val ~* '^a') {
set $res hit;
echo $res;
}
echo $res;
}
描述
该模块封装了许多 Nginx 内部 API,用于流式输入和输出、并行/顺序子请求、计时器和睡眠,以及各种元数据访问。
基本上,它提供了各种实用工具,帮助测试和调试其他模块,通过轻松模拟不同类型的伪子请求位置。
人们还会发现它在需要的实际应用中非常有用:
- 直接从内存提供静态内容(从 Nginx 配置文件加载)。
- 用自定义头和尾包装上游响应(有点像 addition module,但内容直接从配置文件和 Nginx 变量读取)。
- 将各种 "Nginx locations"(即子请求)的内容合并到一个主请求中(使用 echo_location 及其相关命令)。
这是一个特殊的双重角色模块,可以 懒惰地 作为内容处理程序提供服务,或仅在需要时注册为输出过滤器。默认情况下,该模块什么也不做。
从技术上讲,该模块还展示了以下技术,这可能对模块编写者有帮助:
- 直接从内容处理程序发出并行子请求。
- 直接从内容处理程序发出链式子请求,通过在子请求链中传递继续。
- 使用所有 HTTP 1.1 方法发出子请求,甚至是可选的伪 HTTP 请求体。
- 使用自定义事件和计时器直接与 Nginx 事件模型交互,并在必要时恢复内容处理程序。
- 双重角色模块,可以(懒惰地)作为内容处理程序或输出过滤器或两者同时提供服务。
- Nginx 配置文件变量的创建和插值。
- 使用 output_chain、flush 及其相关命令进行流式输出控制。
- 从内容处理程序读取客户端请求体,并在完成后(异步)返回到内容处理程序。
- 使用基于 Perl 的声明性 test suite 驱动 Nginx C 模块的开发。
内容处理程序指令
使用以下指令将此模块注册为当前 Nginx 位置的内容处理程序。如果您想使用其他模块,例如 standard proxy module 作为内容处理程序,请使用此模块提供的 filter directives。
所有内容处理程序指令可以在单个 Nginx 位置中混合使用,并且它们应该按顺序运行,就像 Bash 脚本语言一样。
每个内容处理程序指令都支持其参数(如果有)的变量插值。
由 standard default_type directive 设置的 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>...
默认: 无
上下文: location, location if
阶段: content
将用空格连接的参数以及一个换行符发送到客户端。
请注意,数据可能会被 Nginx 的底层缓冲区缓冲。要强制输出数据立即刷新,请在 echo 之后使用 echo_flush 命令,如下所示:
echo hello world;
echo_flush;
当未指定参数时,echo 仅输出换行符,就像 shell 中的 echo 命令一样。
变量可以出现在参数中。一个例子是:
echo The current request uri is $request_uri;
其中 $request_uri 是由 ngx_http_core_module 提供的变量。
此命令可以在单个位置配置中多次使用,如下所示:
location /echo {
echo hello;
echo world;
}
客户端的输出如下所示:
$ curl 'http://localhost/echo'
hello
world
特殊字符如换行符(\n)和制表符(\t)可以使用 C 风格的转义序列进行转义。但一个显著的例外是美元符号($)。自 Nginx 0.8.20 以来,仍然没有干净的方法来转义此字符。(一种变通方法可能是使用一个始终评估为常量 $ 字符的 $echo_dollor 变量。此功能可能会在未来版本中引入。)
自 echo v0.28 版本以来,可以通过使用 -n 选项来抑制输出中的换行符,如下所示:
location /echo {
echo -n "hello, ";
echo "world";
}
访问 /echo 会得到:
$ curl 'http://localhost/echo'
hello, world
在变量值中前导的 -n 不会生效,并将被字面输出,如下所示:
location /echo {
set $opt -n;
echo $opt "hello,";
echo "world";
}
这将产生以下输出:
$ curl 'http://localhost/echo'
-n hello,
world
可以使用特殊的 -- 选项输出前导的 -n 字面量和其他选项,如下所示:
location /echo {
echo -- -n is an option;
}
这将产生:
$ curl 'http://localhost/echo'
-n is an option
当您想输出任何以破折号(-)开头的内容时,请使用此形式。
echo_duplicate
语法: echo_duplicate <count> <string>
默认: 无
上下文: location, location if
阶段: content
输出由第二个参数指示的字符串的重复,使用第一个参数指定的计数。
例如:
location /dup {
echo_duplicate 3 "abc";
}
将导致输出 "abcabcabc"。
计数数字中允许使用下划线,就像在 Perl 中一样。例如,要发出 1000,000,000 个 "hello, world":
location /many_hellos {
echo_duplicate 1000_000_000 "hello, world";
}
count 参数可以为零,但不能为负。第二个 string 参数也可以是空字符串("")。
与 echo 指令不同,结果后面不会附加换行符。因此,可以通过使用 "count" 为 1 来“滥用”此指令,作为 echo 的无尾换行版本,如下所示:
location /echo_art {
echo_duplicate 2 '---';
echo_duplicate 1 ' END '; # 我们不想在这里有尾换行
echo_duplicate 2 '---';
echo; # 我们希望在这里有尾换行...
}
您将得到:
------ END ------
但在此目的上使用 echo 中的 -n 选项更为合适。
此指令首次在 version 0.11 中引入。
echo_flush
语法: echo_flush
默认: 无
上下文: location, location if
阶段: content
强制潜在被底层 Nginx 输出过滤器缓冲的数据立即通过套接字发送到客户端。
请注意,技术上该命令只是发出一个 ngx_buf_t 对象,其 flush 插槽设置为 1,因此某些奇怪的第三方输出过滤器模块仍然可能会在到达 Nginx 的(最后)写入过滤器之前阻止它。
此指令不接受任何参数。
考虑以下示例:
location /flush {
echo hello;
echo_flush;
echo_sleep 1;
echo world;
}
然后在客户端,使用 curl 访问 /flush,您将立即看到 "hello" 行,但最后的 "world" 行将在 1 秒后出现。在上面的示例中,如果不调用 echo_flush,您很可能在 1 秒内不会看到任何输出,因为 Nginx 的内部缓冲。
如果涉及子请求,此指令将无法刷新输出缓冲。考虑以下示例:
location /main {
echo_location_async /sub;
echo hello;
echo_flush;
}
location /sub {
echo_sleep 1;
}
然后客户端不会看到 "hello" 出现,即使 echo_flush 在对 /sub 的子请求实际开始执行之前已经执行。发送到 /main 的输出 在 echo_location_async 之后将被推迟并被牢牢缓冲。
这 不 适用于在子请求启动之前发送的输出。对于上述示例的修改版本:
location /main {
echo hello;
echo_flush;
echo_location_async /sub;
}
location /sub {
echo_sleep 1;
}
客户端将在 /sub 进入睡眠之前立即看到 "hello"。
另请参见 echo、echo_sleep 和 echo_location_async。
echo_sleep
语法: echo_sleep <seconds>
默认: 无
上下文: location, location if
阶段: content
根据参数指定的时间段(单位:秒)进行睡眠。
此操作在服务器端是非阻塞的,因此与 echo_blocking_sleep 指令不同,它不会阻塞整个 Nginx 工作进程。
该周期可以有三位小数,并且必须大于 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>
默认: 无
上下文: location, location if
阶段: content
这是 echo_sleep 指令的阻塞版本。
有关更多详细信息,请参见 echo_sleep 的文档。
在幕后,它调用 Nginx 核心提供的 ngx_msleep 宏,该宏在符合 POSIX 的系统上映射到 usleep。
请注意,此指令在执行时将完全阻塞当前的 Nginx 工作进程,因此在生产环境中绝不要使用它。
echo_reset_timer
语法: echo_reset_timer
默认: 无
上下文: location, location if
阶段: content
将计时器开始时间重置为 现在,即在请求期间执行此命令时的时间。
计时器开始时间默认为当前请求的开始时间,可以通过此指令覆盖,可能在单个位置中多次。例如:
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
默认: 无
上下文: location, location if
阶段: content
显式读取请求体,以便 $request_body 变量将始终具有非空值(除非请求体过大,以至于已被 Nginx 保存到本地临时文件中)。
请注意,这可能不是原始客户端请求体,因为当前请求可能是具有由其父级指定的“人工”主体的子请求。
此指令本身不生成任何输出,就像 echo_sleep 一样。
以下是回显原始 HTTP 客户端请求(包括头和主体)的示例:
location /echoback {
echo_duplicate 1 $echo_client_request_headers;
echo "\r";
echo_read_request_body;
echo $request_body;
}
在我的一侧,访问 /echoback 的内容如下(我使用 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 中引入。
此指令首次在 echo 模块的 v0.14 release 中引入。
echo_location_async
语法: echo_location_async <location> [<url_args>]
默认: 无
上下文: location, location if
阶段: content
向指定的 location(第一个参数)发出 GET 子请求,第二个参数指定可选的 URL 参数。
自 Nginx 0.8.20 以来,location 参数不支持命名位置,因为 ngx_http_subrequest 函数的限制。其兄弟 echo_location 指令也是如此。
一个非常简单的示例是:
location /main {
echo_location_async /sub;
echo world;
}
location /sub {
echo hello;
}
访问 /main 得到:
hello
world
并行调用多个位置也是可能的:
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; # 睡眠 2 秒
echo hello;
}
location /sub2 {
echo_sleep 1; # 睡眠 1 秒
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" 的计时结果。然而,整个请求大约需要 2 秒才能完成,因为 /sub1 和 /sub2 是并行运行的(或者更准确地说是“同时”运行)。
如果在前面的示例中使用 echo_blocking_sleep,则会得到相同的输出,但总响应时间为 3 秒,因为“阻塞睡眠”会阻塞整个 Nginx 工作进程。
位置还可以接受可选的查询字符串参数,例如:
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 是无效位置,不应作为此指令的第一个参数提供。
从技术上讲,此指令是一个示例,Nginx 内容处理程序直接发出一个或多个子请求。就我所知, fancyindex module 也做了这样的事情 ;)
Nginx 命名位置如 @foo 在这里 不 被支持。
此指令在逻辑上等同于 echo_subrequest_async 的 GET 版本。例如,
echo_location_async /foo 'bar=Bar';
在逻辑上等同于:
echo_subrequest_async GET /foo -q 'bar=Bar';
但调用此指令比使用 GET 调用 echo_subrequest_async 要稍快,因为我们不必解析 HTTP 方法名称,如 GET 和选项,如 -q。
此指令首次在 version 0.09 中引入,并且至少需要 Nginx 0.7.46。
echo_location
语法: echo_location <location> [<url_args>]
默认: 无
上下文: location, location if
阶段: 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;
}
上述 /main 位置将总共需要 3 秒才能完成(与使用 echo_location_async 的 2 秒相比)。这是我机器上的结果:
$ 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 调用 echo_subrequest 要稍快,因为我们不必解析 HTTP 方法名称,如 GET 和选项,如 -q。
在幕后,它创建一个 ngx_http_post_subrequest_t 对象作为 继续,并将其传递给 ngx_http_subrequest 函数调用。Nginx 将在子请求的 ngx_http_finalize_request 函数调用中重新打开此“继续”。我们恢复父请求的内容处理程序的执行,并开始运行下一个指令(如果有)。
Nginx 命名位置如 @foo 在这里 不 被支持。
此指令首次在 release v0.12 中引入。
另请参见 echo_location_async 以获取有关参数含义的更多详细信息。
echo_subrequest_async
语法: echo_subrequest_async <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>]
默认: 无
上下文: location, location if
阶段: content
使用 HTTP 方法、可选的 URL 参数(或查询字符串)和可选的请求体(可以定义为字符串或包含主体的文件路径)启动异步子请求。
此指令非常类似于 echo_location_async 指令的通用版本。
以下是演示其用法的小示例:
location /multi {
# 请求体定义为字符串
echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
# 请求体定义为文件路径,如果不是绝对路径则相对于 nginx 前缀路径
echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b '/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.";
# 我们需要在这里显式读取主体...否则 $echo_request_body
# 将评估为空("")
echo_read_request_body;
echo "body: $echo_request_body.";
}
然后在客户端,我们可以看到:
$ curl 'http://localhost/main'
method: POST.
body: hello, world.
Nginx 命名位置如 @foo 在这里 不 被支持。
此指令有几个选项:
-q <url_args> 指定子请求的 URL 参数(或 URL 查询字符串)。
-f <path> 指定将作为子请求请求体的内容的文件路径。
-b <data> 指定请求体数据。
此指令首次在 release v0.15 中引入。
用于定义请求体的文件路径的 -f 选项在 release v0.35 中引入。
另请参见 echo_subrequest 和 echo_location_async 指令。
echo_subrequest
语法: echo_subrequest <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>]
默认: 无
上下文: location, location if
阶段: content
这是 echo_subrequest_async 指令的同步版本。与 echo_location 一样,它不会阻塞 Nginx 工作进程(而 echo_blocking_sleep 会),而是使用继续将控制权传递沿子请求链。
有关更多详细信息,请参见 echo_subrequest_async。
Nginx 命名位置如 @foo 在这里 不 被支持。
此指令首次在 release v0.15 中引入。
echo_foreach_split
语法: echo_foreach_split <delimiter> <string>
默认: 无
上下文: location, location if
阶段: content
使用第一个参数指定的分隔符分割第二个参数 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 参数可以包含 多个 任意字符,例如:
# 这将输出 "cat\ndog\nmouse\n"
echo_foreach_split -- '-a-' 'cat-a-dog-a-mouse';
echo $echo_it;
echo_end;
从逻辑上讲,此循环结构只是 Perl 中的 foreach 循环与 split 函数调用的组合(使用前面的示例):
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;
}
然后访问 /merge 以合并查询字符串中指定的 .js 资源:
$ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js'
还可以使用第三方 Nginx 缓存模块来缓存前面示例中由 /merge 位置生成的合并响应。
此指令首次在 release v0.17 中引入。
echo_end
语法: echo_end
默认: 无
上下文: location, location if
阶段: content
此指令用于终止循环和条件控制结构的主体,如 echo_foreach_split。
此指令首次在 release v0.17 中引入。
echo_request_body
语法: echo_request_body
默认: 无
上下文: location, location if
阶段: content
输出先前读取的请求体的内容。
在后台,它的实现大致如下:
if (r->request_body && r->request_body->bufs) {
return ngx_http_output_filter(r, r->request_body->bufs);
}
与 $echo_request_body 和 $request_body 变量不同,此指令将显示整个请求体,即使其某些部分或全部已保存在磁盘上的临时文件中。
如果尚未读取任何请求体,则它是“无操作”。
此指令首次在 release v0.18 中引入。
另请参见 echo_read_request_body 和 chunkin module。
echo_exec
语法: echo_exec <location> [<query_string>]
语法: echo_exec <named_location>
默认: 无
上下文: location, location if
阶段: content
对指定的位置进行内部重定向。可以为正常位置指定可选的查询字符串,如下所示:
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 /foo {
echo_exec @bar;
}
location @bar {
# 由于 Nginx 中的潜在错误,您将得到 /foo 而不是 @bar。
echo $echo_request_uri;
}
但由于 ngx_http_named_location 函数的限制,命名位置重定向的查询字符串(如果有)将始终被忽略。
在 echo_exec 指令之前不要尝试输出内容,否则您将无法看到要重定向到的位置的正确响应。因为任何输出都会导致原始位置处理程序在重定向发生之前发送 HTTP 头。
从技术上讲,此指令暴露了 Nginx 内部 API 函数 ngx_http_internal_redirect 和 ngx_http_named_location。
此指令首次在 v0.21 release 中引入。
echo_status
语法: echo_status <status-num>
默认: echo_status 200
上下文: location, location if
阶段: 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]...
默认: 无
上下文: location, location if
阶段: output filter
这是 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
在前面的示例中,我们借用了 standard proxy module 作为生成“主要内容”的底层内容处理程序。
也允许多次使用此过滤器指令,如下所示:
location /echo {
echo_before_body hello;
echo_before_body world;
echo !;
}
在客户端,输出如下:
$ curl 'http://localhost/echo'
hello
world
!
在此示例中,我们还使用了此模块提供的 content handler directives 作为底层内容处理程序。
此指令还支持 -n 和 -- 选项,如 echo 指令。
此指令可以与其兄弟指令 echo_after_body 混合使用。
echo_after_body
语法: echo_after_body [argument]...
默认: 无
上下文: location, location if
阶段: output filter
它与 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 位置时,客户端的输出如下:
i
say
hello
world
此指令还支持 -n 和 -- 选项,如 echo 指令。
此指令可以与其兄弟指令 echo_before_body 混合使用。
变量
$echo_it
这是一个由 echo_foreach_split 使用的“主题变量”,就像 Perl 中的 $_ 变量。
$echo_timer_elapsed
此变量保存自当前请求(可能是子请求)或上次调用 echo_reset_timer 命令以来经过的秒数。
计时结果保留三位小数。
引用此变量将强制底层 Nginx 计时器更新为当前系统时间,无论配置文件中其他地方的计时器分辨率设置如何,就像 echo_reset_timer 指令一样。
$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 release 中引入。
$echo_client_request_method
始终评估为主请求的 HTTP 方法,即使当前请求是子请求。
在后台,它只是获取存储在 r->main->method_name 中的字符串数据。
将其与 $echo_request_method 变量进行比较。
此变量首次在我们的 v0.15 release 中引入。
$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: */*
在后台,它在 C 级别恢复 r->main->header_in(或大的头缓冲区,如果有的话),并且不会通过遍历请求对象中的解析结果来构造头。
在 HTTP/2 请求中,由于当前实现,此变量始终评估为空值。
此变量首次在 version 0.15 中引入。
$echo_cacheable_request_uri
评估为当前(子)请求的 URI 的解析形式(通常以 / 开头)。与 $echo_request_uri 变量不同,它是可缓存的。
有关更多详细信息,请参见 $echo_request_uri。
此变量首次在 version 0.17 中引入。
$echo_request_uri
评估为当前(子)请求的 URI 的解析形式(通常以 / 开头)。与 $echo_cacheable_request_uri 变量不同,它 不是 可缓存的。
这与由 ngx_http_core_module 导出的 $request_uri 变量有很大不同,因为 $request_uri 是当前请求的 未解析 形式的 URI。
此变量首次在 version 0.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 release 中引入。
$echo_response_status
评估为当前(子)请求的状态代码,如果没有则为 null。
在后台,它只是 r->headers_out->status 的文本表示。
此指令首次在 v0.23 release 中引入。
使用此模块进行测试的模块
以下模块在其测试套件中利用了此 echo 模块:
- memc 模块,支持几乎整个 memcached TCP 协议。
- chunkin 模块,为 Nginx 添加 HTTP 1.1 分块输入支持。
- headers_more 模块,允许您在指定的条件下添加、设置和清除输入和输出头。
echo模块本身。
请将其他以任何形式使用 echo 的模块邮件给我,我会将它们添加到上述列表中 :)
更改
此模块每个版本的更改可以从 OpenResty 包的变更日志中获得:
测试套件
此模块附带一个 Perl 驱动的测试套件。 test cases 也是 声明式。感谢 Perl 世界中的 Test::Nginx 模块。
要在您的一侧运行它:
$ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t
如果您更改了 Nginx 服务器二进制文件,则需要在运行测试套件之前终止任何 Nginx 进程。
由于在所有测试脚本(.t 文件)中使用了单个 nginx 服务器(默认情况下为 localhost:1984),因此在调用 prove 实用程序时通过指定 -jN 并行运行测试套件是没有意义的。
测试套件的某些部分还需要在构建 Nginx 时启用标准模块 proxy、rewrite 和 SSI。
另见
- 关于此模块初始开发的原始 blog post。
- 标准 addition filter module。
- 标准 proxy module。
- OpenResty 包。
GitHub
您可以在 GitHub repository for nginx-module-echo 中找到此模块的其他配置提示和文档。