pax_global_header00006660000000000000000000000064143347257210014522gustar00rootroot0000000000000052 comment=d9a04a1f551e8bdb31544c5a3cdcde3a98bf4d66 libnginx-mod-http-echo-0.63/000077500000000000000000000000001433472572100157325ustar00rootroot00000000000000libnginx-mod-http-echo-0.63/.gitattributes000066400000000000000000000000331433472572100206210ustar00rootroot00000000000000*.t linguist-language=Text libnginx-mod-http-echo-0.63/.gitignore000066400000000000000000000011521433472572100177210ustar00rootroot00000000000000.libs *.swp *.slo *.la *.swo *.lo *.mobi genmobi.sh *~ *.o print.txt .rsync *.tar.gz dist build[789] build tags update-readme *.tmp test/Makefile test/blib test.sh t.sh t/t.sh test/t/servroot/ releng reset *.t_ reindex src/location.h src/filter.c src/subrequest.h src/sleep.h src/util.c src/echo.c src/info.c src/util.h src/var.h src/filter.h src/sleep.c src/var.c src/timer.c src/module.h src/echo.h src/info.h src/foreach.c src/location.c src/timer.h src/module.c src/subrequest.c src/handler.h src/foreach.h src/handler.c nginx *.html ctags t/servroot all buildroot/ go Changes build1[0-9] analyze Makefile *.plist libnginx-mod-http-echo-0.63/.travis.yml000066400000000000000000000042621433472572100200470ustar00rootroot00000000000000sudo: required dist: bionic os: linux language: c compiler: - gcc env: global: - LUAJIT_PREFIX=/opt/luajit21 - LUAJIT_LIB=$LUAJIT_PREFIX/lib - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1 - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH matrix: - NGINX_VERSION=1.17.8 - NGINX_VERSION=1.19.9 before_install: - sudo apt-get install -qq -y cpanminus libgd-dev ca-certificates - sudo cpanm -v --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1) install: - wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && tar -xzf nginx-${NGINX_VERSION}.tar.gz - git clone https://github.com/simpl/ngx_devel_kit.git - git clone https://github.com/openresty/set-misc-nginx-module.git - git clone https://github.com/openresty/xss-nginx-module.git - git clone https://github.com/openresty/rds-json-nginx-module.git - git clone https://github.com/openresty/headers-more-nginx-module.git - git clone https://github.com/openresty/lua-nginx-module.git - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/nginx-eval-module.git - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2 script: - cd luajit2/ - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2' > build.log 2>&1 || (cat build.log && exit 1) - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1) - cd .. - cd nginx-${NGINX_VERSION}/ - ./configure --without-http_ssi_module --with-debug --with-select_module --with-poll_module --with-http_stub_status_module --with-http_image_filter_module --add-module=../ngx_devel_kit --add-module=../set-misc-nginx-module --add-module=../nginx-eval-module --add-module=../xss-nginx-module --add-module=../rds-json-nginx-module --add-module=../headers-more-nginx-module --add-module=../lua-nginx-module --add-module=.. > build.log 2>&1 || (cat build.log && exit 1) - make -j2 > build.log 2>&1 || (cat build.log && exit 1) - export PATH=$PATH:`pwd`/objs - cd .. - prove -I. -r t libnginx-mod-http-echo-0.63/LICENSE000066400000000000000000000025011433472572100167350ustar00rootroot00000000000000Copyright (C) 2009-2014, Yichun "agentzh" Zhang . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * 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. libnginx-mod-http-echo-0.63/README.markdown000066400000000000000000001523461433472572100204460ustar00rootroot00000000000000Name ==== **ngx_echo** - Brings "echo", "sleep", "time", "exec" and more shell-style goodies to Nginx config file. *This module is not distributed with the Nginx source.* See [the installation instructions](#installation). Table of Contents ================= * [Name](#name) * [Status](#status) * [Version](#version) * [Synopsis](#synopsis) * [Description](#description) * [Content Handler Directives](#content-handler-directives) * [echo](#echo) * [echo_duplicate](#echo_duplicate) * [echo_flush](#echo_flush) * [echo_sleep](#echo_sleep) * [echo_blocking_sleep](#echo_blocking_sleep) * [echo_reset_timer](#echo_reset_timer) * [echo_read_request_body](#echo_read_request_body) * [echo_location_async](#echo_location_async) * [echo_location](#echo_location) * [echo_subrequest_async](#echo_subrequest_async) * [echo_subrequest](#echo_subrequest) * [echo_foreach_split](#echo_foreach_split) * [echo_end](#echo_end) * [echo_request_body](#echo_request_body) * [echo_exec](#echo_exec) * [echo_status](#echo_status) * [Filter Directives](#filter-directives) * [echo_before_body](#echo_before_body) * [echo_after_body](#echo_after_body) * [Variables](#variables) * [$echo_it](#echo_it) * [$echo_timer_elapsed](#echo_timer_elapsed) * [$echo_request_body](#echo_request_body) * [$echo_request_method](#echo_request_method) * [$echo_client_request_method](#echo_client_request_method) * [$echo_client_request_headers](#echo_client_request_headers) * [$echo_cacheable_request_uri](#echo_cacheable_request_uri) * [$echo_request_uri](#echo_request_uri) * [$echo_incr](#echo_incr) * [$echo_response_status](#echo_response_status) * [Installation](#installation) * [Compatibility](#compatibility) * [Modules that use this module for testing](#modules-that-use-this-module-for-testing) * [Community](#community) * [English Mailing List](#english-mailing-list) * [Chinese Mailing List](#chinese-mailing-list) * [Report Bugs](#report-bugs) * [Source Repository](#source-repository) * [Changes](#changes) * [Test Suite](#test-suite) * [TODO](#todo) * [Getting involved](#getting-involved) * [Author](#author) * [Copyright & License](#copyright--license) * [See Also](#see-also) Status ====== This module is production ready. Version ======= This document describes ngx_echo [v0.62](https://github.com/openresty/echo-nginx-module/tags) released on 2 July, 2020. Synopsis ======== ```nginx location /hello { echo "hello, world!"; } ``` ```nginx location /hello { echo -n "hello, "; echo "world!"; } ``` ```nginx 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."; } ``` ```nginx location /echo_with_sleep { echo hello; echo_flush; # ensure the client can see previous output immediately echo_sleep 2.5; # in sec echo world; } ``` ```nginx # 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; } ``` ```nginx # 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; } ``` ```nginx # 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; } ``` ```nginx # Accessing /dup gives # ------ END ------ location /dup { echo_duplicate 3 "--"; echo_duplicate 1 " END "; echo_duplicate 3 "--"; echo; } ``` ```nginx # /bighello will generate 1000,000,000 hello's. location /bighello { echo_duplicate 1000_000_000 'hello'; } ``` ```nginx # echo back the client request location /echoback { echo_duplicate 1 $echo_client_request_headers; echo "\r"; echo_read_request_body; echo_request_body; } ``` ```nginx # 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 '///'; } ``` ```nginx # 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; } ``` ```nginx # 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; } ``` [Back to TOC](#table-of-contents) Description =========== This module wraps lots of Nginx internal APIs for streaming input and output, parallel/sequential subrequests, timers and sleeping, as well as various meta data accessing. Basically it provides various utilities that help testing and debugging of other modules by trivially emulating different kinds of faked subrequest locations. People will also find it useful in real-world applications that need to 1. serve static contents directly from memory (loading from the Nginx config file). 1. wrap the upstream response with custom header and footer (kinda like the [addition module](http://nginx.org/en/docs/http/ngx_http_addition_module.html) but with contents read directly from the config file and Nginx variables). 1. merge contents of various "Nginx locations" (i.e., subrequests) together in a single main request (using [echo_location](#echo_location) and its friends). 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. By default, this module does not do anything at all. Technically, this module has also demonstrated the following techniques that might be helpful for module writers: 1. Issue parallel subrequests directly from content handler. 1. Issue chained subrequests directly from content handler, by passing continuation along the subrequest chain. 1. Issue subrequests with all HTTP 1.1 methods and even an optional faked HTTP request body. 1. Interact with the Nginx event model directly from content handler using custom events and timers, and resume the content handler back if necessary. 1. Dual-role module that can (lazily) serve as a content handler or an output filter or both. 1. Nginx config file variable creation and interpolation. 1. Streaming output control using output_chain, flush and its friends. 1. Read client request body from the content handler, and returns back (asynchronously) to the content handler after completion. 1. Use Perl-based declarative [test suite](#test-suite) to drive the development of Nginx C modules. [Back to TOC](#table-of-contents) Content Handler Directives ========================== Use of the following directives register this module to the current Nginx location as a content handler. If you want to use another module, like the [standard proxy module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html), as the content handler, use the [filter directives](#filter-directives) provided by this module. All the content handler directives can be mixed together in a single Nginx location and they're supposed to run sequentially just as in the Bash scripting language. Every content handler directive supports variable interpolation in its arguments (if any). The MIME type set by the [standard default_type directive](http://nginx.org/en/docs/http/ngx_http_core_module.html#default_type) is respected by this module, as in: ```nginx location /hello { default_type text/plain; echo hello; } ``` Then on the client side: ```bash $ 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 ``` Since the [v0.22](#v022) release, all of the directives are allowed in the [rewrite module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)'s [if](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if) directive block, for instance: ```nginx location ^~ /if { set $res miss; if ($arg_val ~* '^a') { set $res hit; echo $res; } echo $res; } ``` [Back to TOC](#table-of-contents) echo ---- **syntax:** *echo \[options\] <string>...* **default:** *no* **context:** *location, location if* **phase:** *content* Sends arguments joined by spaces, along with a trailing newline, out to the client. Note that the data might be buffered by Nginx's underlying buffer. To force the output data flushed immediately, use the [echo_flush](#echo_flush) command just after `echo`, as in ```nginx echo hello world; echo_flush; ``` When no argument is specified, *echo* emits the trailing newline alone, just like the *echo* command in shell. Variables may appear in the arguments. An example is ```nginx echo The current request uri is $request_uri; ``` where [$request_uri](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_uri) is a variable exposed by the [ngx_http_core_module](http://nginx.org/en/docs/http/ngx_http_core_module.html). This command can be used multiple times in a single location configuration, as in ```nginx location /echo { echo hello; echo world; } ``` The output on the client side looks like this ```bash $ curl 'http://localhost/echo' hello world ``` Special characters like newlines (`\n`) and tabs (`\t`) can be escaped using C-style escaping sequences. But a notable exception is the dollar sign (`$`). As of Nginx 0.8.20, there's still no clean way to escape this character. (A work-around might be to use a `$echo_dollor` variable that is always evaluated to the constant `$` character. This feature will possibly be introduced in a future version of this module.) As of the echo [v0.28](#v028) release, one can suppress the trailing newline character in the output by using the `-n` option, as in ```nginx location /echo { echo -n "hello, "; echo "world"; } ``` Accessing `/echo` gives ```bash $ curl 'http://localhost/echo' hello, world ``` Leading `-n` in variable values won't take effect and will be emitted literally, as in ```nginx location /echo { set $opt -n; echo $opt "hello,"; echo "world"; } ``` This gives the following output ```bash $ curl 'http://localhost/echo' -n hello, world ``` One can output leading `-n` literals and other options using the special `--` option like this ```nginx location /echo { echo -- -n is an option; } ``` which yields ```bash $ curl 'http://localhost/echo' -n is an option ``` Use this form when you want to output anything leading with a dash (`-`). [Back to TOC](#table-of-contents) echo_duplicate -------------- **syntax:** *echo_duplicate <count> <string>* **default:** *no* **context:** *location, location if* **phase:** *content* Outputs duplication of a string indicated by the second argument, using the count specified in the first argument. For instance, ```nginx location /dup { echo_duplicate 3 "abc"; } ``` will lead to the output of `"abcabcabc"`. Underscores are allowed in the count number, just like in Perl. For example, to emit 1000,000,000 instances of `"hello, world"`: ```nginx location /many_hellos { echo_duplicate 1000_000_000 "hello, world"; } ``` The `count` argument could be zero, but not negative. The second `string` argument could be an empty string ("") likewise. Unlike the [echo](#echo) directive, no trailing newline is appended to the result. So it's possible to "abuse" this directive as a no-trailing-newline version of [echo](#echo) by using "count" 1, as in ```nginx 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... } ``` You get ```bash ------ END ------ ``` But use of the `-n` option in [echo](#echo) is more appropriate for this purpose. This directive was first introduced in [version 0.11](#v011). [Back to TOC](#table-of-contents) echo_flush ---------- **syntax:** *echo_flush* **default:** *no* **context:** *location, location if* **phase:** *content* Forces the data potentially buffered by underlying Nginx output filters to send immediately to the client side via socket. Note that techically the command just emits a ngx_buf_t object with `flush` slot set to 1, so certain weird third-party output filter module could still block it before it reaches Nginx's (last) write filter. This directive does not take any argument. Consider the following example: ```nginx location /flush { echo hello; echo_flush; echo_sleep 1; echo world; } ``` Then on the client side, using curl to access `/flush`, you'll see the "hello" line immediately, but only after 1 second, the last "world" line. Without calling `echo_flush` in the example above, you'll most likely see no output until 1 second is elapsed due to the internal buffering of Nginx. This directive will fail to flush the output buffer in case of subrequests get involved. Consider the following example: ```nginx location /main { echo_location_async /sub; echo hello; echo_flush; } location /sub { echo_sleep 1; } ``` Then the client won't see "hello" appear even if `echo_flush` has been executed before the subrequest to `/sub` has actually started executing. The outputs of `/main` that are sent *after* [echo_location_async](#echo_location_async) will be postponed and buffered firmly. This does *not* apply to outputs sent before the subrequest initiated. For a modified version of the example given above: ```nginx location /main { echo hello; echo_flush; echo_location_async /sub; } location /sub { echo_sleep 1; } ``` The client will immediately see "hello" before `/sub` enters sleeping. See also [echo](#echo), [echo_sleep](#echo_sleep), and [echo_location_async](#echo_location_async). [Back to TOC](#table-of-contents) echo_sleep ---------- **syntax:** *echo_sleep <seconds>* **default:** *no* **context:** *location, location if* **phase:** *content* Sleeps for the time period specified by the argument, which is in seconds. This operation is non-blocking on server side, so unlike the [echo_blocking_sleep](#echo_blocking_sleep) directive, it won't block the whole Nginx worker process. The period might takes three digits after the decimal point and must be greater than 0.001. An example is ```nginx location /echo_after_sleep { echo_sleep 1.234; echo resumed!; } ``` Behind the scene, it sets up a per-request "sleep" ngx_event_t object, and adds a timer using that custom event to the Nginx event model and just waits for a timeout on that event. Because the "sleep" event is per-request, this directive can work in parallel subrequests. [Back to TOC](#table-of-contents) echo_blocking_sleep ------------------- **syntax:** *echo_blocking_sleep <seconds>* **default:** *no* **context:** *location, location if* **phase:** *content* This is a blocking version of the [echo_sleep](#echo_sleep) directive. See the documentation of [echo_sleep](#echo_sleep) for more detail. Behind the curtain, it calls the ngx_msleep macro provided by the Nginx core which maps to usleep on POSIX-compliant systems. Note that this directive will block the current Nginx worker process completely while being executed, so never use it in production environment. [Back to TOC](#table-of-contents) echo_reset_timer ---------------- **syntax:** *echo_reset_timer* **default:** *no* **context:** *location, location if* **phase:** *content* Reset the timer begin time to *now*, i.e., the time when this command is executed during request. The timer begin time is default to the starting time of the current request and can be overridden by this directive, potentially multiple times in a single location. For example: ```nginx 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."; } ``` The output on the client side might be ```bash $ curl 'http://localhost/timed_sleep' 0.032 sec elapsed. 0.020 sec elapsed. ``` The actual figures you get on your side may vary a bit due to your system's current activities. Invocation of this directive will force the underlying Nginx timer to get updated to the current system time (regardless the timer resolution specified elsewhere in the config file). Furthermore, references of the [$echo_timer_elapsed](#echo_timer_elapsed) variable will also trigger timer update forcibly. See also [echo_sleep](#echo_sleep) and [$echo_timer_elapsed](#echo_timer_elapsed). [Back to TOC](#table-of-contents) echo_read_request_body ---------------------- **syntax:** *echo_read_request_body* **default:** *no* **context:** *location, location if* **phase:** *content* Explicitly reads request body so that the [$request_body](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_body) variable will always have non-empty values (unless the body is so big that it has been saved by Nginx to a local temporary file). Note that this might not be the original client request body because the current request might be a subrequest with a "artificial" body specified by its parent. This directive does not generate any output itself, just like [echo_sleep](#echo_sleep). Here's an example for echo'ing back the original HTTP client request (both headers and body are included): ```nginx location /echoback { echo_duplicate 1 $echo_client_request_headers; echo "\r"; echo_read_request_body; echo $request_body; } ``` The content of `/echoback` looks like this on my side (I was using Perl's LWP utility to access this location on the server): ```bash $ (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 ``` Because `/echoback` is the main request, [$request_body](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_body) holds the original client request body. Before Nginx 0.7.56, it makes no sense to use this directive because [$request_body](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_body) was first introduced in Nginx 0.7.58. This directive itself was first introduced in the echo module's [v0.14 release](#v014). [Back to TOC](#table-of-contents) echo_location_async ------------------- **syntax:** *echo_location_async <location> [<url_args>]* **default:** *no* **context:** *location, location if* **phase:** *content* Issue GET subrequest to the location specified (first argument) with optional url arguments specified in the second argument. As of Nginx 0.8.20, the `location` argument does *not* support named location, due to a limitation in the `ngx_http_subrequest` function. The same is true for its brother, the [echo_location](#echo_location) directive. A very simple example is ```nginx location /main { echo_location_async /sub; echo world; } location /sub { echo hello; } ``` Accessing `/main` gets ```bash hello world ``` Calling multiple locations in parallel is also possible: ```nginx 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; } ``` Accessing `/main` yields ```bash $ time curl 'http://localhost/main' hello world took 0.000 sec for total. real 0m2.006s user 0m0.000s sys 0m0.004s ``` You can see that the main handler `/main` does *not* wait the subrequests `/sub1` and `/sub2` to complete and quickly goes on, hence the "0.000 sec" timing result. The whole request, however takes approximately 2 sec in total to complete because `/sub1` and `/sub2` run in parallel (or "concurrently" to be more accurate). If you use [echo_blocking_sleep](#echo_blocking_sleep) in the previous example instead, then you'll get the same output, but with 3 sec total response time, because "blocking sleep" blocks the whole Nginx worker process. Locations can also take an optional querystring argument, for instance ```nginx location /main { echo_location_async /sub 'foo=Foo&bar=Bar'; } location /sub { echo $arg_foo $arg_bar; } ``` Accessing `/main` yields ```bash $ curl 'http://localhost/main' Foo Bar ``` Querystrings is *not* allowed to be concatenated onto the `location` argument with "?" directly, for example, `/sub?foo=Foo&bar=Bar` is an invalid location, and shouldn't be fed as the first argument to this directive. Technically speaking, this directive is an example that Nginx content handler issues one or more subrequests directly. AFAIK, the [fancyindex module](https://connectical.com/projects/ngx-fancyindex/wiki) also does such kind of things ;) Nginx named locations like `@foo` is *not* supported here. This directive is logically equivalent to the GET version of [echo_subrequest_async](#echo_subrequest_async). For example, ```nginx echo_location_async /foo 'bar=Bar'; ``` is logically equivalent to ```nginx echo_subrequest_async GET /foo -q 'bar=Bar'; ``` But calling this directive is slightly faster than calling [echo_subrequest_async](#echo_subrequest_async) using `GET` because we don't have to parse the HTTP method names like `GET` and options like `-q`. This directive is first introduced in [version 0.09](#v009) of this module and requires at least Nginx 0.7.46. [Back to TOC](#table-of-contents) echo_location ------------- **syntax:** *echo_location <location> [<url_args>]* **default:** *no* **context:** *location, location if* **phase:** *content* Just like the [echo_location_async](#echo_location_async) directive, but `echo_location` issues subrequests *in series* rather than in parallel. That is, the content handler directives following this directive won't be executed until the subrequest issued by this directive completes. The final response body is almost always equivalent to the case when [echo_location_async](#echo_location_async) is used instead, only if timing variables is used in the outputs. Consider the following example: ```nginx 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; } ``` The location `/main` above will take for total 3 sec to complete (compared to 2 sec if [echo_location_async](#echo_location_async) is used instead here). Here's the result in action on my machine: ```bash $ curl 'http://localhost/main' hello world took 3.003 sec for total. real 0m3.027s user 0m0.020s sys 0m0.004s ``` This directive is logically equivalent to the GET version of [echo_subrequest](#echo_subrequest). For example, ```nginx echo_location /foo 'bar=Bar'; ``` is logically equivalent to ```nginx echo_subrequest GET /foo -q 'bar=Bar'; ``` But calling this directive is slightly faster than calling [echo_subrequest](#echo_subrequest) using `GET` because we don't have to parse the HTTP method names like `GET` and options like `-q`. Behind the scene, it creates an `ngx_http_post_subrequest_t` object as a *continuation* and passes it into the `ngx_http_subrequest` function call. Nginx will later reopen this "continuation" in the subrequest's `ngx_http_finalize_request` function call. We resumes the execution of the parent-request's content handler and starts to run the next directive (command) if any. Nginx named locations like `@foo` is *not* supported here. This directive was first introduced in the [release v0.12](#v012). See also [echo_location_async](#echo_location_async) for more details about the meaning of the arguments. [Back to TOC](#table-of-contents) echo_subrequest_async --------------------- **syntax:** *echo_subrequest_async <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>]* **default:** *no* **context:** *location, location if* **phase:** *content* Initiate an asynchronous subrequest using HTTP method, an optional url arguments (or querystring) and an optional request body which can be defined as a string or as a path to a file which contains the body. This directive is very much like a generalized version of the [echo_location_async](#echo_location_async) directive. Here's a small example demonstrating its usage: ```nginx location /multi { # body defined as string echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; # body defined as path to a file, relative to nginx prefix path if not absolute 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 '///'; } ``` Then on the client side: ```bash $ 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 /// ``` Here's more funny example using the standard [proxy module](#httpproxymodule) to handle the subrequest: ```nginx 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."; # we need to read body explicitly here...or $echo_request_body # will evaluate to empty ("") echo_read_request_body; echo "body: $echo_request_body."; } ``` Then on the client side, we can see that ```bash $ curl 'http://localhost/main' method: POST. body: hello, world. ``` Nginx named locations like `@foo` is *not* supported here. This directive takes several options: -q Specify the URL arguments (or URL querystring) for the subrequest. -f Specify the path for the file whose content will be serve as the subrequest's request body. -b Specify the request body data This directive was first introduced in the [release v0.15](#v015). The `-f` option to define a file path for the body was introduced in the [release v0.35](#v035). See also the [echo_subrequest](#echo_subrequest) and [echo_location_async](#echo_location_async) directives. [Back to TOC](#table-of-contents) echo_subrequest --------------- **syntax:** *echo_subrequest <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>]* **default:** *no* **context:** *location, location if* **phase:** *content* This is the synchronous version of the [echo_subrequest_async](#echo_subrequest_async) directive. And just like [echo_location](#echo_location), it does not block the Nginx worker process (while [echo_blocking_sleep](#echo_blocking_sleep) does), rather, it uses continuation to pass control along the subrequest chain. See [echo_subrequest_async](#echo_subrequest_async) for more details. Nginx named locations like `@foo` is *not* supported here. This directive was first introduced in the [release v0.15](#v015). [Back to TOC](#table-of-contents) echo_foreach_split ------------------ **syntax:** *echo_foreach_split <delimiter> <string>* **default:** *no* **context:** *location, location if* **phase:** *content* Split the second argument `string` using the delimiter specified in the first argument, and then iterate through the resulting items. For instance: ```nginx location /loop { echo_foreach_split ',' $arg_list; echo "item: $echo_it"; echo_end; } ``` Accessing /main yields ```bash $ curl 'http://localhost/loop?list=cat,dog,mouse' item: cat item: dog item: mouse ``` As seen in the previous example, this directive should always be accompanied by an [echo_end](#echo_end) directive. Parallel `echo_foreach_split` loops are allowed, but nested ones are currently forbidden. The `delimiter` argument could contain *multiple* arbitrary characters, like ```nginx # this outputs "cat\ndog\nmouse\n" echo_foreach_split -- '-a-' 'cat-a-dog-a-mouse'; echo $echo_it; echo_end; ``` Logically speaking, this looping structure is just the `foreach` loop combined with a `split` function call in Perl (using the previous example): ```perl foreach (split ',', $arg_list) { print "item $_\n"; } ``` People will also find it useful in merging multiple `.js` or `.css` resources into a whole. Here's an example: ```nginx location /merge { default_type 'text/javascript'; echo_foreach_split '&' $query_string; echo "/* JS File $echo_it */"; echo_location_async $echo_it; echo; echo_end; } ``` Then accessing /merge to merge the `.js` resources specified in the query string: ```bash $ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js' ``` One can also use third-party Nginx cache module to cache the merged response generated by the `/merge` location in the previous example. This directive was first introduced in the [release v0.17](#v017). [Back to TOC](#table-of-contents) echo_end -------- **syntax:** *echo_end* **default:** *no* **context:** *location, location if* **phase:** *content* This directive is used to terminate the body of looping and conditional control structures like [echo_foreach_split](#echo_foreach_split). This directive was first introduced in the [release v0.17](#v017). [Back to TOC](#table-of-contents) echo_request_body ----------------- **syntax:** *echo_request_body* **default:** *no* **context:** *location, location if* **phase:** *content* Outputs the contents of the request body previous read. Behind the scene, it's implemented roughly like this: ```C if (r->request_body && r->request_body->bufs) { return ngx_http_output_filter(r, r->request_body->bufs); } ``` Unlike the [$echo_request_body](#echo_request_body) and $request_body variables, this directive will show the whole request body even if some parts or all parts of it are saved in temporary files on the disk. It is a "no-op" if no request body has been read yet. This directive was first introduced in the [release v0.18](#v018). See also [echo_read_request_body](#echo_read_request_body) and the [chunkin module](http://github.com/agentzh/chunkin-nginx-module). [Back to TOC](#table-of-contents) echo_exec --------- **syntax:** *echo_exec <location> [<query_string>]* **syntax:** *echo_exec <named_location>* **default:** *no* **context:** *location, location if* **phase:** *content* Does an internal redirect to the location specified. An optional query string can be specified for normal locations, as in ```nginx location /foo { echo_exec /bar weight=5; } location /bar { echo $arg_weight; } ``` Or equivalently ```nginx location /foo { echo_exec /bar?weight=5; } location /bar { echo $arg_weight; } ``` Named locations are also supported. Here's an example: ```nginx location /foo { echo_exec @bar; } location @bar { # you'll get /foo rather than @bar # due to a potential bug in nginx. echo $echo_request_uri; } ``` But query string (if any) will always be ignored for named location redirects due to a limitation in the `ngx_http_named_location` function. Never try to echo things before the `echo_exec` directive or you won't see the proper response of the location you want to redirect to. Because any echoing will cause the original location handler to send HTTP headers before the redirection happens. Technically speaking, this directive exposes the Nginx internal API functions `ngx_http_internal_redirect` and `ngx_http_named_location`. This directive was first introduced in the [v0.21 release](#v021). [Back to TOC](#table-of-contents) echo_status ----------- **syntax:** *echo_status <status-num>* **default:** *echo_status 200* **context:** *location, location if* **phase:** *content* Specify the default response status code. Default to `200`. This directive is declarative and the relative order with other echo-like directives is not important. Here is an example, ```nginx location = /bad { echo_status 404; echo "Something is missing..."; } ``` then we get a response like this: 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... This directive was first introduced in the `v0.40` release. [Back to TOC](#table-of-contents) Filter Directives ================= Use of the following directives trigger the filter registration of this module. By default, no filter will be registered by this module. Every filter directive supports variable interpolation in its arguments (if any). [Back to TOC](#table-of-contents) echo_before_body ---------------- **syntax:** *echo_before_body \[options\] \[argument\]...* **default:** *no* **context:** *location, location if* **phase:** *output filter* It's the filter version of the [echo](#echo) directive, and prepends its output to the beginning of the original outputs generated by the underlying content handler. An example is ```nginx location /echo { echo_before_body hello; proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; } location /echo/more { echo world } ``` Accessing `/echo` from the client side yields ```bash hello world ``` In the previous sample, we borrow the [standard proxy module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) to serve as the underlying content handler that generates the "main contents". Multiple instances of this filter directive are also allowed, as in: ```nginx location /echo { echo_before_body hello; echo_before_body world; echo !; } ``` On the client side, the output is like ```bash $ curl 'http://localhost/echo' hello world ! ``` In this example, we also use the [content handler directives](#content-handler-directives) provided by this module as the underlying content handler. This directive also supports the `-n` and `--` options like the [echo](#echo) directive. This directive can be mixed with its brother directive [echo_after_body](#echo_after_body). [Back to TOC](#table-of-contents) echo_after_body --------------- **syntax:** *echo_after_body \[argument\]...* **default:** *no* **context:** *location, location if* **phase:** *output filter* It's very much like the [echo_before_body](#echo_before_body) directive, but *appends* its output to the end of the original outputs generated by the underlying content handler. Here's a simple example: ```nginx location /echo { echo_after_body hello; proxy_pass http://127.0.0.1:$server_port$request_uri/more; } location /echo/more { echo world } ``` Accessing `/echo` from the client side yields world hello Multiple instances are allowed, as in: ```nginx location /echo { echo_after_body hello; echo_after_body world; echo i; echo say; } ``` The output on the client side while accessing the `/echo` location looks like i say hello world This directive also supports the `-n` and `--` options like the [echo](#echo) directive. This directive can be mixed with its brother directive [echo_before_body](#echo_before_body). [Back to TOC](#table-of-contents) Variables ========= [Back to TOC](#table-of-contents) $echo_it -------- This is a "topic variable" used by [echo_foreach_split](#echo_foreach_split), just like the `$_` variable in Perl. [Back to TOC](#table-of-contents) $echo_timer_elapsed ------------------- This variable holds the seconds elapsed since the start of the current request (might be a subrequest though) or the last invocation of the [echo_reset_timer](#echo_reset_timer) command. The timing result takes three digits after the decimal point. References of this variable will force the underlying Nginx timer to update to the current system time, regardless the timer resolution settings elsewhere in the config file, just like the [echo_reset_timer](#echo_reset_timer) directive. [Back to TOC](#table-of-contents) $echo_request_body ------------------ Evaluates to the current (sub)request's request body previously read if no part of the body has been saved to a temporary file. To always show the request body even if it's very large, use the [echo_request_body](#echo_request_body) directive. [Back to TOC](#table-of-contents) $echo_request_method -------------------- Evaluates to the HTTP request method of the current request (it can be a subrequest). Behind the scene, it just takes the string data stored in `r->method_name`. Compare it to the [$echo_client_request_method](#echo_client_request_method) variable. At least for Nginx 0.8.20 and older, the [$request_method](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_method) variable provided by the [http core module](http://nginx.org/en/docs/http/ngx_http_core_module.html) is actually doing what our [$echo_client_request_method](#echo_client_request_method) is doing. This variable was first introduced in our [v0.15 release](#v015). [Back to TOC](#table-of-contents) $echo_client_request_method --------------------------- Always evaluates to the main request's HTTP method even if the current request is a subrequest. Behind the scene, it just takes the string data stored in `r->main->method_name`. Compare it to the [$echo_request_method](#echo_request_method) variable. This variable was first introduced in our [v0.15 release](#v015). [Back to TOC](#table-of-contents) $echo_client_request_headers ---------------------------- Evaluates to the original client request's headers. Just as the name suggests, it will always take the main request (or the client request) even if it's currently executed in a subrequest. A simple example is below: ```nginx location /echoback { echo "headers are:" echo $echo_client_request_headers; } ``` Accessing `/echoback` yields ```bash $ 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. This varible is always evaluated to an empty value in HTTP/2 requests for now due to the current implementation. This variable was first introduced in [version 0.15](#v015). [Back to TOC](#table-of-contents) $echo_cacheable_request_uri --------------------------- Evaluates to the parsed form of the URI (usually led by `/`) of the current (sub-)request. Unlike the [$echo_request_uri](#echo_request_uri) variable, it is cacheable. See [$echo_request_uri](#echo_request_uri) for more details. This variable was first introduced in [version 0.17](#v017). [Back to TOC](#table-of-contents) $echo_request_uri ----------------- Evaluates to the parsed form of the URI (usually led by `/`) of the current (sub-)request. Unlike the [$echo_cacheable_request_uri](#echo_cacheable_request_uri) variable, it is *not* cacheable. This is quite different from the [$request_uri](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_uri) variable exported by the [ngx_http_core_module](http://nginx.org/en/docs/http/ngx_http_core_module.html), because `$request_uri` is the *unparsed* form of the current request's URI. This variable was first introduced in [version 0.17](#v017). [Back to TOC](#table-of-contents) $echo_incr ---------- It is a counter that always generate the current counting number, starting from 1. The counter is always associated with the main request even if it is accessed within a subrequest. Consider the following example ```Nginx 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"; } ``` Accessing `/main` yields main pre: 1 sub: 3 sub: 4 main post: 2 This directive was first introduced in the [v0.18 release](#v018). [Back to TOC](#table-of-contents) $echo_response_status --------------------- Evaluates to the status code of the current (sub)request, null if not any. Behind the scene, it's just the textual representation of `r->headers_out->status`. This directive was first introduced in the [v0.23 release](#v023). [Back to TOC](#table-of-contents) Installation ============ You're recommended to install this module (as well as the Nginx core and many other goodies) via the [OpenResty bundle](http://openresty.org). See [the detailed instructions](http://openresty.org/#Installation) for downloading and installing OpenResty into your system. This is the easiest and most safe way to set things up. Alternatively, you can install this module manually with the Nginx source: Grab the nginx source code from [nginx.org](http://nginx.org/), for example, the version 1.11.2 (see [nginx compatibility](#compatibility)), and then build the source with this module: ```bash $ wget 'http://nginx.org/download/nginx-1.11.2.tar.gz' $ tar -xzvf nginx-1.11.2.tar.gz $ cd nginx-1.11.2/ # 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 ``` Download the latest version of the release tarball of this module from [echo-nginx-module file list](https://github.com/openresty/echo-nginx-module/tags). Starting from NGINX 1.9.11, you can also compile this module as a dynamic module, by using the `--add-dynamic-module=PATH` option instead of `--add-module=PATH` on the `./configure` command line above. And then you can explicitly load the module in your `nginx.conf` via the [load_module](http://nginx.org/en/docs/ngx_core_module.html#load_module) directive, for example, ```nginx load_module /path/to/modules/ngx_http_echo_module.so; ``` Also, this module is included and enabled by default in the [OpenResty bundle](http://openresty.org). [Back to TOC](#table-of-contents) Compatibility ============= The following versions of Nginx should work with this module: * **1.16.x** * **1.15.x** (last tested: 1.15.8) * **1.14.x** * **1.13.x** (last tested: 1.13.6) * **1.12.x** * **1.11.x** (last tested: 1.11.2) * **1.10.x** * **1.9.x** (last tested: 1.9.15) * **1.8.x** * **1.7.x** (last tested: 1.7.10) * **1.6.x** * **1.5.x** (last tested: 1.5.12) * **1.4.x** (last tested: 1.4.4) * **1.3.x** (last tested: 1.3.7) * **1.2.x** (last tested: 1.2.9) * **1.1.x** (last tested: 1.1.5) * **1.0.x** (last tested: 1.0.11) * **0.9.x** (last tested: 0.9.4) * **0.8.x** (last tested: 0.8.54) * **0.7.x >= 0.7.21** (last tested: 0.7.68) In particular, * the directive [echo_location_async](#echo_location_async) and its brother [echo_subrequest_async](#echo_subrequest_async) do *not* work with **0.7.x < 0.7.46**. * the [echo_after_body](#echo_after_body) directive does *not* work at all with nginx **< 0.8.7**. * the [echo_sleep](#echo_sleep) directive cannot be used after [echo_location](#echo_location) or [echo_subrequest](#echo_subrequest) for nginx **< 0.8.11**. Earlier versions of Nginx like 0.6.x and 0.5.x will *not* work at all. If you find that any particular version of Nginx above 0.7.21 does not work with this module, please consider [reporting a bug](#report-bugs). [Back to TOC](#table-of-contents) Modules that use this module for testing ======================================== The following modules take advantage of this `echo` module in their test suite: * The [memc](http://github.com/openresty/memc-nginx-module) module that supports almost the whole memcached TCP protocol. * The [chunkin](http://github.com/agentzh/chunkin-nginx-module) module that adds HTTP 1.1 chunked input support to Nginx. * The [headers_more](http://github.com/openresty/headers-more-nginx-module) module that allows you to add, set, and clear input and output headers under the conditions that you specify. * The `echo` module itself. Please mail me other modules that use `echo` in any form and I'll add them to the list above :) [Back to TOC](#table-of-contents) Community ========= [Back to TOC](#table-of-contents) English Mailing List -------------------- The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers. [Back to TOC](#table-of-contents) Chinese Mailing List -------------------- The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers. [Back to TOC](#table-of-contents) Report Bugs =========== Although a lot of effort has been put into testing and code tuning, there must be some serious bugs lurking somewhere in this module. So whenever you are bitten by any quirks, please don't hesitate to 1. create a ticket on the [issue tracking interface](https://github.com/openresty/echo-nginx-module/issues) provided by GitHub, 1. or send a bug report, questions, or even patches to the [OpenResty Community](#community). [Back to TOC](#table-of-contents) Source Repository ================= Available on github at [openresty/echo-nginx-module](https://github.com/openresty/echo-nginx-module). [Back to TOC](#table-of-contents) Changes ======= The changes of every release of this module can be obtained from the OpenResty bundle's change logs: [Back to TOC](#table-of-contents) Test Suite ========== This module comes with a Perl-driven test suite. The [test cases](https://github.com/openresty/echo-nginx-module/tree/master/t/) are [declarative](https://github.com/openresty/echo-nginx-module/blob/master/t/echo.t) too. Thanks to the [Test::Nginx](http://search.cpan.org/perldoc?Test::Nginx) module in the Perl world. To run it on your side: ```bash $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t ``` You need to terminate any Nginx processes before running the test suite if you have changed the Nginx server binary. Because a single nginx server (by default, `localhost:1984`) is used across all the test scripts (`.t` files), it's meaningless to run the test suite in parallel by specifying `-jN` when invoking the `prove` utility. Some parts of the test suite requires standard modules [proxy](http://nginx.org/en/docs/http/ngx_http_proxy_module.html), [rewrite](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html) and [SSI](http://nginx.org/en/docs/http/ngx_http_ssi_module.html) to be enabled as well when building Nginx. [Back to TOC](#table-of-contents) TODO ==== * Fix the [echo_after_body](#echo_after_body) directive in subrequests. * Add directives *echo_read_client_request_body* and *echo_request_headers*. * Add new directive *echo_log* to use Nginx's logging facility directly from the config file and specific loglevel can be specified, as in ```nginx echo_log debug "I am being called."; ``` * Add support for options `-h` and `-t` to [echo_subrequest_async](#echo_subrequest_async) and [echo_subrequest](#echo_subrequest). For example ```nginx echo_subrequest POST /sub -q 'foo=Foo&bar=Bar' -b 'hello' -t 'text/plan' -h 'X-My-Header: blah blah' ``` * Add options to control whether a subrequest should inherit cached variables from its parent request (i.e. the current request that is calling the subrequest in question). Currently none of the subrequests issued by this module inherit the cached variables from the parent request. * Add new variable *$echo_active_subrequests* to show `r->main->count - 1`. * Add the *echo_file* and *echo_cached_file* directives. * Add new varaible *$echo_request_headers* to accompany the existing [$echo_client_request_headers](#echo_client_request_headers) variable. * Add new directive *echo_foreach*, as in ```nginx echo_foreach 'cat' 'dog' 'mouse'; echo_location_async "/animals/$echo_it"; echo_end; ``` * Add new directive *echo_foreach_range*, as in ```nginx echo_foreach_range '[1..100]' '[a-zA-z0-9]'; echo_location_async "/item/$echo_it"; echo_end; ``` * Add new directive *echo_repeat*, as in ```nginx echo_repeat 10 $i { echo "Page $i"; echo_location "/path/to/page/$i"; } ``` This is just another way of saying ```nginx echo_foreach_range $i [1..10]; echo "Page $i"; echo_location "/path/to/page/$i"; echo_end; ``` Thanks Marcus Clyne for providing this idea. * Add new variable $echo_random which always returns a random non-negative integer with the lower/upper limit specified by the new directives `echo_random_min` and `echo_random_max`. For example, ```nginx echo_random_min 10 echo_random_max 200 echo "random number: $echo_random"; ``` Thanks Marcus Clyne for providing this idea. [Back to TOC](#table-of-contents) Getting involved ================ You'll be very welcomed to submit patches to the [author](#author) or just ask for a commit bit to the [source repository](#source-repository) on GitHub. [Back to TOC](#table-of-contents) Author ====== Yichun "agentzh" Zhang (章亦春) *<agentzh@gmail.com>*, OpenResty Inc. This wiki page is also maintained by the author himself, and everybody is encouraged to improve this page as well. [Back to TOC](#table-of-contents) Copyright & License =================== Copyright (c) 2009-2018, Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. This module is licensed under the terms of the BSD license. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * 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. [Back to TOC](#table-of-contents) See Also ======== * The original [blog post](http://agentzh.blogspot.com/2009/10/hacking-on-nginx-echo-module.html) about this module's initial development. * The standard [addition filter module](http://nginx.org/en/docs/http/ngx_http_addition_module.html). * The standard [proxy module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html). * The [OpenResty](http://openresty.org) bundle. [Back to TOC](#table-of-contents) libnginx-mod-http-echo-0.63/config000066400000000000000000000061601433472572100171250ustar00rootroot00000000000000ngx_addon_name=ngx_http_echo_module ECHO_SRCS=" \ $ngx_addon_dir/src/ngx_http_echo_module.c \ $ngx_addon_dir/src/ngx_http_echo_util.c \ $ngx_addon_dir/src/ngx_http_echo_timer.c \ $ngx_addon_dir/src/ngx_http_echo_var.c \ $ngx_addon_dir/src/ngx_http_echo_handler.c \ $ngx_addon_dir/src/ngx_http_echo_filter.c \ $ngx_addon_dir/src/ngx_http_echo_sleep.c \ $ngx_addon_dir/src/ngx_http_echo_location.c \ $ngx_addon_dir/src/ngx_http_echo_echo.c \ $ngx_addon_dir/src/ngx_http_echo_request_info.c \ $ngx_addon_dir/src/ngx_http_echo_subrequest.c \ $ngx_addon_dir/src/ngx_http_echo_foreach.c \ " ECHO_DEPS=" \ $ngx_addon_dir/src/ddebug.h \ $ngx_addon_dir/src/ngx_http_echo_module.h \ $ngx_addon_dir/src/ngx_http_echo_handler.h \ $ngx_addon_dir/src/ngx_http_echo_util.h \ $ngx_addon_dir/src/ngx_http_echo_sleep.h \ $ngx_addon_dir/src/ngx_http_echo_filter.h \ $ngx_addon_dir/src/ngx_http_echo_var.h \ $ngx_addon_dir/src/ngx_http_echo_location.h \ $ngx_addon_dir/src/ngx_http_echo_echo.h \ $ngx_addon_dir/src/ngx_http_echo_request_info.h \ $ngx_addon_dir/src/ngx_http_echo_subrequest.h \ $ngx_addon_dir/src/ngx_http_echo_foreach.h \ " # nginx 1.17.0+ unconditionally enables the postpone filter if [ ! -z "$HTTP_POSTPONE" ]; then # nginx won't have HTTP_POSTPONE_FILTER_MODULE & HTTP_POSTPONE_FILTER_SRCS # defined since 1.9.11 if [ -z "$HTTP_POSTPONE_FILTER_MODULE" ]; then HTTP_POSTPONE_FILTER_MODULE=ngx_http_postpone_filter_module HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c fi # This module depends upon the postpone filter being activated if [ "$HTTP_POSTPONE" != YES ]; then HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_POSTPONE_FILTER_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_POSTPONE_FILTER_SRCS" HTTP_POSTPONE=YES fi fi if [ -n "$ngx_module_link" ]; then ngx_module_type=HTTP_AUX_FILTER ngx_module_name=$ngx_addon_name ngx_module_incs= ngx_module_deps="$ECHO_DEPS" ngx_module_srcs="$ECHO_SRCS" ngx_module_libs= . auto/module else HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES $ngx_addon_name" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ECHO_SRCS" NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ECHO_DEPS" fi libnginx-mod-http-echo-0.63/src/000077500000000000000000000000001433472572100165215ustar00rootroot00000000000000libnginx-mod-http-echo-0.63/src/ddebug.h000066400000000000000000000046761433472572100201410ustar00rootroot00000000000000#ifndef DDEBUG_H #define DDEBUG_H #include #include #include #if defined(DDEBUG) && (DDEBUG) # if (NGX_HAVE_VARIADIC_MACROS) # define dd(...) fprintf(stderr, "echo *** %s: ", __func__); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) # else #include #include #include static ngx_inline void dd(const char * fmt, ...) { } # endif # if DDEBUG > 1 # define dd_enter() dd_enter_helper(r, __func__) static ngx_inline void dd_enter_helper(ngx_http_request_t *r, const char *func) { ngx_http_posted_request_t *pr; fprintf(stderr, ">enter %s %.*s %.*s?%.*s c:%d m:%p r:%p ar:%p pr:%p", func, (int) r->method_name.len, r->method_name.data, (int) r->uri.len, r->uri.data, (int) r->args.len, r->args.data, 0/*(int) r->main->count*/, r->main, r, r->connection->data, r->parent); if (r->posted_requests) { fprintf(stderr, " posted:"); for (pr = r->posted_requests; pr; pr = pr->next) { fprintf(stderr, "%p,", pr); } } fprintf(stderr, "\n"); } # else # define dd_enter() # endif #else # if (NGX_HAVE_VARIADIC_MACROS) # define dd(...) # define dd_enter() # else #include static ngx_inline void dd(const char * fmt, ...) { } static ngx_inline void dd_enter() { } # endif #endif #if defined(DDEBUG) && (DDEBUG) #define dd_check_read_event_handler(r) \ dd("r->read_event_handler = %s", \ r->read_event_handler == ngx_http_block_reading ? \ "ngx_http_block_reading" : \ r->read_event_handler == ngx_http_test_reading ? \ "ngx_http_test_reading" : \ r->read_event_handler == ngx_http_request_empty_handler ? \ "ngx_http_request_empty_handler" : "UNKNOWN") #define dd_check_write_event_handler(r) \ dd("r->write_event_handler = %s", \ r->write_event_handler == ngx_http_handler ? \ "ngx_http_handler" : \ r->write_event_handler == ngx_http_core_run_phases ? \ "ngx_http_core_run_phases" : \ r->write_event_handler == ngx_http_request_empty_handler ? \ "ngx_http_request_empty_handler" : "UNKNOWN") #else #define dd_check_read_event_handler(r) #define dd_check_write_event_handler(r) #endif #endif /* DDEBUG_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_echo.c000066400000000000000000000200701433472572100225130ustar00rootroot00000000000000#ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_echo.h" #include "ngx_http_echo_util.h" #include "ngx_http_echo_filter.h" #include static ngx_buf_t ngx_http_echo_space_buf; static ngx_buf_t ngx_http_echo_newline_buf; ngx_int_t ngx_http_echo_echo_init(ngx_conf_t *cf) { static u_char space_str[] = " "; static u_char newline_str[] = "\n"; dd("global init..."); ngx_memzero(&ngx_http_echo_space_buf, sizeof(ngx_buf_t)); ngx_http_echo_space_buf.memory = 1; ngx_http_echo_space_buf.start = ngx_http_echo_space_buf.pos = space_str; ngx_http_echo_space_buf.end = ngx_http_echo_space_buf.last = space_str + sizeof(space_str) - 1; ngx_memzero(&ngx_http_echo_newline_buf, sizeof(ngx_buf_t)); ngx_http_echo_newline_buf.memory = 1; ngx_http_echo_newline_buf.start = ngx_http_echo_newline_buf.pos = newline_str; ngx_http_echo_newline_buf.end = ngx_http_echo_newline_buf.last = newline_str + sizeof(newline_str) - 1; return NGX_OK; } ngx_int_t ngx_http_echo_exec_echo_sync(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { ngx_buf_t *buf; ngx_chain_t *cl = NULL; /* the head of the chain link */ buf = ngx_calloc_buf(r->pool); if (buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } buf->sync = 1; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cl->buf = buf; cl->next = NULL; return ngx_http_echo_send_chain_link(r, ctx, cl); } ngx_int_t ngx_http_echo_exec_echo(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args, ngx_flag_t in_filter, ngx_array_t *opts) { ngx_uint_t i; ngx_buf_t *space_buf; ngx_buf_t *newline_buf; ngx_buf_t *buf; ngx_str_t *computed_arg; ngx_str_t *computed_arg_elts; ngx_str_t *opt; ngx_chain_t *cl = NULL; /* the head of the chain link */ ngx_chain_t **ll = &cl; /* always point to the address of the last link */ dd_enter(); if (computed_args == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } computed_arg_elts = computed_args->elts; for (i = 0; i < computed_args->nelts; i++) { computed_arg = &computed_arg_elts[i]; if (computed_arg->len == 0) { buf = NULL; } else { buf = ngx_calloc_buf(r->pool); if (buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } buf->start = buf->pos = computed_arg->data; buf->last = buf->end = computed_arg->data + computed_arg->len; buf->memory = 1; } if (cl == NULL) { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cl->buf = buf; cl->next = NULL; ll = &cl->next; } else { /* append a space first */ *ll = ngx_alloc_chain_link(r->pool); if (*ll == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } space_buf = ngx_calloc_buf(r->pool); if (space_buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* nginx clears buf flags at the end of each request handling, * so we have to make a clone here. */ *space_buf = ngx_http_echo_space_buf; (*ll)->buf = space_buf; (*ll)->next = NULL; ll = &(*ll)->next; /* then append the buf only if it's non-empty */ if (buf) { *ll = ngx_alloc_chain_link(r->pool); if (*ll == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } (*ll)->buf = buf; (*ll)->next = NULL; ll = &(*ll)->next; } } } /* end for */ if (cl && cl->buf == NULL) { cl = cl->next; } if (opts && opts->nelts > 0) { opt = opts->elts; /* FIXME handle other unrecognized options here */ if (opt[0].len == 1 && opt[0].data[0] == 'n') { goto done; } } /* append the newline character */ newline_buf = ngx_calloc_buf(r->pool); if (newline_buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } *newline_buf = ngx_http_echo_newline_buf; if (cl == NULL) { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cl->buf = newline_buf; cl->next = NULL; /* ll = &cl->next; */ } else { *ll = ngx_alloc_chain_link(r->pool); if (*ll == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } (*ll)->buf = newline_buf; (*ll)->next = NULL; /* ll = &(*ll)->next; */ } done: if (cl == NULL || cl->buf == NULL) { return NGX_OK; } if (in_filter) { return ngx_http_echo_next_body_filter(r, cl); } return ngx_http_echo_send_chain_link(r, ctx, cl); } ngx_int_t ngx_http_echo_exec_echo_flush(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { return ngx_http_send_special(r, NGX_HTTP_FLUSH); } ngx_int_t ngx_http_echo_exec_echo_request_body(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { ngx_buf_t *b; ngx_chain_t *out, *cl, **ll; if (r->request_body == NULL || r->request_body->bufs == NULL) { return NGX_OK; } out = NULL; ll = &out; for (cl = r->request_body->bufs; cl; cl = cl->next) { if (ngx_buf_special(cl->buf)) { /* we do not want to create zero-size bufs */ continue; } *ll = ngx_alloc_chain_link(r->pool); if (*ll == NULL) { return NGX_ERROR; } b = ngx_alloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } (*ll)->buf = b; (*ll)->next = NULL; ngx_memcpy(b, cl->buf, sizeof(ngx_buf_t)); b->tag = (ngx_buf_tag_t) &ngx_http_echo_exec_echo_request_body; b->last_buf = 0; b->last_in_chain = 0; ll = &(*ll)->next; } if (out == NULL) { return NGX_OK; } return ngx_http_echo_send_chain_link(r, ctx, out); } ngx_int_t ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_str_t *computed_arg; ngx_str_t *computed_arg_elts; ssize_t i, count; ngx_str_t *str; u_char *p; ngx_int_t rc; ngx_buf_t *buf; ngx_chain_t *cl; dd_enter(); computed_arg_elts = computed_args->elts; computed_arg = &computed_arg_elts[0]; count = ngx_http_echo_atosz(computed_arg->data, computed_arg->len); if (count == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid size specified: \"%V\"", computed_arg); return NGX_HTTP_INTERNAL_SERVER_ERROR; } str = &computed_arg_elts[1]; if (count == 0 || str->len == 0) { rc = ngx_http_echo_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } return NGX_OK; } buf = ngx_create_temp_buf(r->pool, count * str->len); if (buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } p = buf->pos; for (i = 0; i < count; i++) { p = ngx_copy(p, str->data, str->len); } buf->last = p; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cl->next = NULL; cl->buf = buf; return ngx_http_echo_send_chain_link(r, ctx, cl); } libnginx-mod-http-echo-0.63/src/ngx_http_echo_echo.h000066400000000000000000000013661433472572100225270ustar00rootroot00000000000000#ifndef ECHO_ECHO_H #define ECHO_ECHO_H #include "ngx_http_echo_module.h" ngx_int_t ngx_http_echo_echo_init(ngx_conf_t *cf); ngx_int_t ngx_http_echo_exec_echo_sync(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx); ngx_int_t ngx_http_echo_exec_echo(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args, ngx_flag_t in_filter, ngx_array_t *opts); ngx_int_t ngx_http_echo_exec_echo_request_body(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx); ngx_int_t ngx_http_echo_exec_echo_flush(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx); ngx_int_t ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); #endif /* ECHO_ECHO_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_filter.c000066400000000000000000000170111433472572100230630ustar00rootroot00000000000000#ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_filter.h" #include "ngx_http_echo_util.h" #include "ngx_http_echo_echo.h" #include ngx_http_output_header_filter_pt ngx_http_echo_next_header_filter; ngx_http_output_body_filter_pt ngx_http_echo_next_body_filter; static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t *r); static ngx_int_t ngx_http_echo_body_filter(ngx_http_request_t *r, ngx_chain_t *in); /* filter handlers */ static ngx_int_t ngx_http_echo_exec_filter_cmds(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *cmds, ngx_uint_t *iterator); static volatile ngx_cycle_t *ngx_http_echo_prev_cycle = NULL; ngx_int_t ngx_http_echo_filter_init(ngx_conf_t *cf) { int multi_http_blocks; ngx_http_echo_main_conf_t *emcf; emcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_echo_module); if (ngx_http_echo_prev_cycle != ngx_cycle) { ngx_http_echo_prev_cycle = ngx_cycle; multi_http_blocks = 0; } else { multi_http_blocks = 1; } if (multi_http_blocks || emcf->requires_filter) { dd("top header filter: %ld", (unsigned long) ngx_http_top_header_filter); ngx_http_echo_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_echo_header_filter; dd("top body filter: %ld", (unsigned long) ngx_http_top_body_filter); ngx_http_echo_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_echo_body_filter; } return NGX_OK; } static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t *r) { ngx_http_echo_loc_conf_t *conf; ngx_http_echo_ctx_t *ctx; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "echo header filter, uri \"%V?%V\"", &r->uri, &r->args); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); /* XXX we should add option to insert contents for responses * of non-200 status code here... */ /* if (r->headers_out.status != NGX_HTTP_OK) { if (ctx != NULL) { ctx->skip_filter = 1; } return ngx_http_echo_next_header_filter(r); } */ conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); if (conf->before_body_cmds == NULL && conf->after_body_cmds == NULL) { if (ctx != NULL) { ctx->skip_filter = 1; } return ngx_http_echo_next_header_filter(r); } if (ctx == NULL) { ctx = ngx_http_echo_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_echo_module); } /* enable streaming here (use chunked encoding) */ ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_echo_next_header_filter(r); } static ngx_int_t ngx_http_echo_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_http_echo_ctx_t *ctx; ngx_int_t rc; ngx_http_echo_loc_conf_t *conf; unsigned last; ngx_chain_t *cl; ngx_buf_t *b; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "echo body filter, uri \"%V?%V\"", &r->uri, &r->args); if (in == NULL || r->header_only) { return ngx_http_echo_next_body_filter(r, in); } ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL || ctx->skip_filter) { return ngx_http_echo_next_body_filter(r, in); } conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); if (!ctx->before_body_sent) { ctx->before_body_sent = 1; if (conf->before_body_cmds != NULL) { rc = ngx_http_echo_exec_filter_cmds(r, ctx, conf->before_body_cmds, &ctx->next_before_body_cmd); if (rc != NGX_OK) { return NGX_ERROR; } } } if (conf->after_body_cmds == NULL) { ctx->skip_filter = 1; return ngx_http_echo_next_body_filter(r, in); } last = 0; for (cl = in; cl; cl = cl->next) { dd("cl %p, special %d", cl, ngx_buf_special(cl->buf)); if (cl->buf->last_buf || cl->buf->last_in_chain) { cl->buf->last_buf = 0; cl->buf->last_in_chain = 0; cl->buf->sync = 1; last = 1; } } dd("in %p, last %d", in, (int) last); if (in) { rc = ngx_http_echo_next_body_filter(r, in); #if 0 if (rc == NGX_AGAIN) { return NGX_ERROR; } #endif dd("next filter returns %d, last %d", (int) rc, (int) last); if (rc == NGX_ERROR || rc > NGX_OK || !last) { return rc; } } dd("exec filter cmds for after body cmds"); rc = ngx_http_echo_exec_filter_cmds(r, ctx, conf->after_body_cmds, &ctx->next_after_body_cmd); if (rc == NGX_ERROR || rc > NGX_OK) { dd("FAILED: exec filter cmds for after body cmds"); return NGX_ERROR; } ctx->skip_filter = 1; dd("after body cmds executed...terminating..."); /* XXX we can NOT use * ngx_http_send_special(r, NGX_HTTP_LAST) here * because we should bypass the upstream filters. */ b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } if (r == r->main && !r->post_action) { b->last_buf = 1; } else { b->sync = 1; b->last_in_chain = 1; } cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->next = NULL; cl->buf = b; return ngx_http_echo_next_body_filter(r, cl); } static ngx_int_t ngx_http_echo_exec_filter_cmds(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *cmds, ngx_uint_t *iterator) { ngx_int_t rc; ngx_array_t *opts = NULL; ngx_array_t *computed_args = NULL; ngx_http_echo_cmd_t *cmd; ngx_http_echo_cmd_t *cmd_elts; for (cmd_elts = cmds->elts; *iterator < cmds->nelts; (*iterator)++) { cmd = &cmd_elts[*iterator]; /* evaluate arguments for the current cmd (if any) */ if (cmd->args) { computed_args = ngx_array_create(r->pool, cmd->args->nelts, sizeof(ngx_str_t)); if (computed_args == NULL) { return NGX_ERROR; } opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t)); if (opts == NULL) { return NGX_ERROR; } rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to evaluate arguments for " "the directive."); return rc; } } /* do command dispatch based on the opcode */ switch (cmd->opcode) { case echo_opcode_echo_before_body: case echo_opcode_echo_after_body: dd("exec echo_before_body or echo_after_body..."); rc = ngx_http_echo_exec_echo(r, ctx, computed_args, 1 /* in filter */, opts); if (rc == NGX_ERROR || rc > NGX_OK) { return rc; } break; default: break; } } return NGX_OK; } libnginx-mod-http-echo-0.63/src/ngx_http_echo_filter.h000066400000000000000000000004671433472572100230770ustar00rootroot00000000000000#ifndef ECHO_FILTER_H #define ECHO_FILTER_H #include "ngx_http_echo_module.h" extern ngx_http_output_header_filter_pt ngx_http_echo_next_header_filter; extern ngx_http_output_body_filter_pt ngx_http_echo_next_body_filter; ngx_int_t ngx_http_echo_filter_init (ngx_conf_t *cf); #endif /* ECHO_FILTER_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_foreach.c000066400000000000000000000113731433472572100232120ustar00rootroot00000000000000#ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_foreach.h" #include "ngx_http_echo_util.h" #include ngx_int_t ngx_http_echo_it_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_echo_ctx_t *ctx; ngx_uint_t i; ngx_array_t *choices; ngx_str_t *choice_elts, *choice; ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx && ctx->foreach != NULL) { choices = ctx->foreach->choices; i = ctx->foreach->next_choice; if (i < choices->nelts) { choice_elts = choices->elts; choice = &choice_elts[i]; v->len = choice->len; v->data = choice->data; v->valid = 1; v->no_cacheable = 1; v->not_found = 0; return NGX_OK; } } v->not_found = 1; return NGX_OK; } ngx_int_t ngx_http_echo_exec_echo_foreach_split(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_http_echo_loc_conf_t *elcf; ngx_str_t *delimiter, *compound; u_char *pos, *last, *end; ngx_str_t *choice; ngx_str_t *computed_arg_elts; ngx_array_t *cmds; ngx_http_echo_cmd_t *cmd; ngx_http_echo_cmd_t *cmd_elts; if (ctx->foreach != NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Nested echo_foreach not supported yet."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (computed_args->nelts < 2) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "echo_foreach should take at least two arguments. " "(if your delimiter starts with \"-\", preceding it " "with a \"--\".)"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } computed_arg_elts = computed_args->elts; compound = &computed_arg_elts[1]; dd("HEY coumpound len: %u", (int) compound->len); ctx->foreach = ngx_palloc(r->pool, sizeof(ngx_http_echo_foreach_ctx_t)); if (ctx->foreach == NULL) { return NGX_ERROR; } ctx->foreach->cmd_index = ctx->next_handler_cmd; ctx->foreach->next_choice = 0; ctx->foreach->choices = ngx_array_create(r->pool, 10, sizeof(ngx_str_t)); if (ctx->foreach->choices == NULL) { return NGX_ERROR; } delimiter = &computed_arg_elts[0]; pos = compound->data; end = compound->data + compound->len; while ((last = ngx_http_echo_strlstrn(pos, end, delimiter->data, delimiter->len - 1)) != NULL) { dd("entered the loop"); if (last == pos) { dd("!!! len == 0"); pos = last + delimiter->len; continue; } choice = ngx_array_push(ctx->foreach->choices); if (choice == NULL) { return NGX_ERROR; } choice->data = pos; choice->len = last - pos; pos = last + delimiter->len; } if (pos < end) { choice = ngx_array_push(ctx->foreach->choices); if (choice == NULL) { return NGX_ERROR; } choice->data = pos; choice->len = end - pos; } if (ctx->foreach->choices->nelts == 0) { /* skip the foreach body entirely */ elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); cmds = elcf->handler_cmds; cmd_elts = cmds->elts; for (/* void */; ctx->next_handler_cmd < cmds->nelts; ctx->next_handler_cmd++) { cmd = &cmd_elts[ctx->next_handler_cmd + 1]; if (cmd->opcode == echo_opcode_echo_end) { return NGX_OK; } } } return NGX_OK; } ngx_int_t ngx_http_echo_exec_echo_end(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { if (ctx->foreach == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Found a echo_end that has no corresponding echo_foreach " "before it."); return NGX_ERROR; } ctx->foreach->next_choice++; if (ctx->foreach->next_choice >= ctx->foreach->choices->nelts) { /* TODO We need to explicitly free the foreach ctx from * the pool */ ctx->foreach = NULL; return NGX_OK; } dd("echo_end: ++ next_choice (total: %u): %u", (unsigned) ctx->foreach->choices->nelts, (unsigned) ctx->foreach->next_choice); /* the main handler dispatcher loop will increment * ctx->next_handler_cmd for us anyway. */ ctx->next_handler_cmd = ctx->foreach->cmd_index; return NGX_OK; } libnginx-mod-http-echo-0.63/src/ngx_http_echo_foreach.h000066400000000000000000000007121433472572100232120ustar00rootroot00000000000000#ifndef ECHO_FOREACH_H #define ECHO_FOREACH_H #include "ngx_http_echo_module.h" ngx_int_t ngx_http_echo_exec_echo_foreach_split(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ngx_int_t ngx_http_echo_exec_echo_end(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx); ngx_int_t ngx_http_echo_it_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); #endif /* ECHO_FOREACH_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_handler.c000066400000000000000000000263501433472572100232210ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_filter.h" #include "ngx_http_echo_handler.h" #include "ngx_http_echo_echo.h" #include "ngx_http_echo_util.h" #include "ngx_http_echo_sleep.h" #include "ngx_http_echo_var.h" #include "ngx_http_echo_timer.h" #include "ngx_http_echo_location.h" #include "ngx_http_echo_subrequest.h" #include "ngx_http_echo_request_info.h" #include "ngx_http_echo_foreach.h" #include #include void ngx_http_echo_wev_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_echo_ctx_t *ctx; dd("wev handler"); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { ngx_http_finalize_request(r, NGX_ERROR); return; } dd("waiting: %d, done: %d", (int) ctx->waiting, (int) ctx->done); if (ctx->waiting && ! ctx->done) { if (r == r->connection->data && r->postponed) { if (r->postponed->request) { r->connection->data = r->postponed->request; #if defined(nginx_version) && nginx_version >= 8012 ngx_http_post_request(r->postponed->request, NULL); #else ngx_http_post_request(r->postponed->request); #endif } else { ngx_http_echo_flush_postponed_outputs(r); } } return; } ctx->done = 0; ctx->next_handler_cmd++; rc = ngx_http_echo_run_cmds(r); dd("rc: %d", (int) rc); if (rc == NGX_ERROR || rc == NGX_DONE) { ngx_http_finalize_request(r, rc); return; } if (rc == NGX_AGAIN) { dd("mark busy %d for %.*s", (int) ctx->next_handler_cmd, (int) r->uri.len, r->uri.data); ctx->waiting = 1; ctx->done = 0; } else { dd("mark ready %d", (int) ctx->next_handler_cmd); ctx->waiting = 0; ctx->done = 1; dd("finalizing with rc %d", (int) rc); dd("finalize request %.*s with %d", (int) r->uri.len, r->uri.data, (int) rc); ngx_http_finalize_request(r, rc); } } ngx_int_t ngx_http_echo_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_echo_ctx_t *ctx; dd("subrequest in memory: %d", (int) r->subrequest_in_memory); rc = ngx_http_echo_run_cmds(r); dd("run cmds returned %d", (int) rc); if (rc == NGX_ERROR || rc == NGX_OK || rc == NGX_DONE || rc == NGX_DECLINED) { return rc; } ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { if (ctx && r->header_sent) { return NGX_ERROR; } return rc; } /* rc == NGX_AGAIN */ #if defined(nginx_version) && nginx_version >= 8011 r->main->count++; #endif dd("%d", r->connection->destroyed); dd("%d", r->done); if (ctx) { dd("mark busy %d for %.*s", (int) ctx->next_handler_cmd, (int) r->uri.len, r->uri.data); ctx->waiting = 1; ctx->done = 0; } return NGX_DONE; } ngx_int_t ngx_http_echo_run_cmds(ngx_http_request_t *r) { ngx_http_echo_loc_conf_t *elcf; ngx_http_echo_ctx_t *ctx; ngx_int_t rc; ngx_array_t *cmds; ngx_array_t *computed_args = NULL; ngx_http_echo_cmd_t *cmd; ngx_http_echo_cmd_t *cmd_elts; ngx_array_t *opts = NULL; elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); cmds = elcf->handler_cmds; if (cmds == NULL) { return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { ctx = ngx_http_echo_create_ctx(r); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_echo_module); } dd("exec handler: %.*s: %i", (int) r->uri.len, r->uri.data, (int) ctx->next_handler_cmd); cmd_elts = cmds->elts; for (; ctx->next_handler_cmd < cmds->nelts; ctx->next_handler_cmd++) { cmd = &cmd_elts[ctx->next_handler_cmd]; /* evaluate arguments for the current cmd (if any) */ if (cmd->args) { computed_args = ngx_array_create(r->pool, cmd->args->nelts, sizeof(ngx_str_t)); if (computed_args == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t)); if (opts == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to evaluate arguments for " "the directive."); return rc; } } if (computed_args == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* do command dispatch based on the opcode */ switch (cmd->opcode) { case echo_opcode_echo_sync: rc = ngx_http_echo_exec_echo_sync(r, ctx); break; case echo_opcode_echo: /* XXX moved the following code to a separate * function */ dd("found echo opcode"); rc = ngx_http_echo_exec_echo(r, ctx, computed_args, 0 /* in filter */, opts); break; case echo_opcode_echo_request_body: rc = ngx_http_echo_exec_echo_request_body(r, ctx); break; case echo_opcode_echo_location_async: if (!r->request_body) { /* we require reading the request body before doing * subrequests */ ctx->next_handler_cmd--; /* re-run the current cmd */ goto read_request_body; } dd("found opcode echo location async..."); rc = ngx_http_echo_exec_echo_location_async(r, ctx, computed_args); break; case echo_opcode_echo_location: if (!r->request_body) { /* we require reading the request body before doing * subrequests */ ctx->next_handler_cmd--; /* re-run the current cmd */ goto read_request_body; } return ngx_http_echo_exec_echo_location(r, ctx, computed_args); case echo_opcode_echo_subrequest_async: if (!r->request_body) { /* we require reading the request body before doing * subrequests */ ctx->next_handler_cmd--; /* re-run the current cmd */ goto read_request_body; } dd("found opcode echo subrequest async..."); rc = ngx_http_echo_exec_echo_subrequest_async(r, ctx, computed_args); break; case echo_opcode_echo_subrequest: if (!r->request_body) { /* we require reading the request body before doing * subrequests */ ctx->next_handler_cmd--; /* re-run the current cmd */ goto read_request_body; } return ngx_http_echo_exec_echo_subrequest(r, ctx, computed_args); case echo_opcode_echo_sleep: return ngx_http_echo_exec_echo_sleep(r, ctx, computed_args); case echo_opcode_echo_flush: rc = ngx_http_echo_exec_echo_flush(r, ctx); break; case echo_opcode_echo_blocking_sleep: rc = ngx_http_echo_exec_echo_blocking_sleep(r, ctx, computed_args); break; case echo_opcode_echo_reset_timer: rc = ngx_http_echo_exec_echo_reset_timer(r, ctx); break; case echo_opcode_echo_duplicate: rc = ngx_http_echo_exec_echo_duplicate(r, ctx, computed_args); break; case echo_opcode_echo_read_request_body: read_request_body: ctx->wait_read_request_body = 0; rc = ngx_http_echo_exec_echo_read_request_body(r, ctx); if (rc == NGX_ERROR) { return NGX_ERROR; } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { #if (nginx_version >= 8011 && nginx_version < 1002006) \ || (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif return rc; } #if nginx_version >= 8011 r->main->count--; #endif dd("read request body: %d", (int) rc); if (rc == NGX_OK) { continue; } /* rc == NGX_AGAIN */ ctx->wait_read_request_body = 1; return NGX_AGAIN; case echo_opcode_echo_foreach_split: rc = ngx_http_echo_exec_echo_foreach_split(r, ctx, computed_args); break; case echo_opcode_echo_end: rc = ngx_http_echo_exec_echo_end(r, ctx); break; case echo_opcode_echo_exec: dd("echo_exec"); return ngx_http_echo_exec_exec(r, ctx, computed_args); default: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unknown opcode: %d", cmd->opcode); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } rc = ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (!r->request_body) { if (ngx_http_discard_request_body(r) != NGX_OK) { return NGX_ERROR; } } return NGX_OK; } ngx_int_t ngx_http_echo_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc) { ngx_http_echo_ctx_t *ctx = data; ngx_http_request_t *pr; ngx_http_echo_ctx_t *pr_ctx; dd("echo post_subrequest: %.*s", (int) r->uri.len, r->uri.data); if (ctx->run_post_subrequest) { dd("already run post_subrequest: %p: %.*s", ctx, (int) r->uri.len, r->uri.data); return rc; } dd("setting run_post_subrequest to 1 for %p for %.*s", ctx, (int) r->uri.len, r->uri.data); ctx->run_post_subrequest = 1; pr = r->parent; pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_echo_module); if (pr_ctx == NULL) { return NGX_ERROR; } dd("mark ready %d", (int) pr_ctx->next_handler_cmd); pr_ctx->waiting = 0; pr_ctx->done = 1; pr->write_event_handler = ngx_http_echo_wev_handler; /* work-around issues in nginx's event module */ if (r != r->connection->data && r->postponed && (r->main->posted_requests == NULL || r->main->posted_requests->request != pr)) { #if defined(nginx_version) && nginx_version >= 8012 ngx_http_post_request(pr, NULL); #else ngx_http_post_request(pr); #endif } return rc; } libnginx-mod-http-echo-0.63/src/ngx_http_echo_handler.h000066400000000000000000000005771433472572100232310ustar00rootroot00000000000000#ifndef ECHO_HANDLER_H #define ECHO_HANDLER_H #include "ngx_http_echo_module.h" void ngx_http_echo_wev_handler(ngx_http_request_t *r); ngx_int_t ngx_http_echo_handler(ngx_http_request_t *r); ngx_int_t ngx_http_echo_run_cmds(ngx_http_request_t *r); ngx_int_t ngx_http_echo_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc); #endif /* ECHO_HANDLER_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_location.c000066400000000000000000000106321433472572100234100ustar00rootroot00000000000000#ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_util.h" #include "ngx_http_echo_location.h" #include "ngx_http_echo_handler.h" #include static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr); ngx_int_t ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_int_t rc; ngx_http_request_t *sr; /* subrequest object */ ngx_str_t *computed_arg_elts; ngx_str_t location; ngx_str_t *url_args; ngx_str_t args; ngx_uint_t flags = 0; dd_enter(); computed_arg_elts = computed_args->elts; location = computed_arg_elts[0]; if (location.len == 0) { return NGX_ERROR; } if (computed_args->nelts > 1) { url_args = &computed_arg_elts[1]; } else { url_args = NULL; } args.data = NULL; args.len = 0; if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "echo_location_async sees unsafe uri: \"%V\"", &location); return NGX_ERROR; } if (args.len > 0 && url_args == NULL) { url_args = &args; } rc = ngx_http_echo_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } rc = ngx_http_subrequest(r, &location, url_args, &sr, NULL, 0); if (rc != NGX_OK) { return NGX_ERROR; } rc = ngx_http_echo_adjust_subrequest(sr); if (rc != NGX_OK) { return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_http_echo_exec_echo_location(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_int_t rc; ngx_http_request_t *sr; /* subrequest object */ ngx_str_t *computed_arg_elts; ngx_str_t location; ngx_str_t *url_args; ngx_http_post_subrequest_t *psr; ngx_str_t args; ngx_uint_t flags = 0; ngx_http_echo_ctx_t *sr_ctx; if (computed_args == NULL) { return NGX_ERROR; } computed_arg_elts = computed_args->elts; location = computed_arg_elts[0]; if (location.len == 0) { return NGX_ERROR; } if (computed_args->nelts > 1) { url_args = &computed_arg_elts[1]; } else { url_args = NULL; } args.data = NULL; args.len = 0; if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "echo_location sees unsafe uri: \"%V\"", &location); return NGX_ERROR; } if (args.len > 0 && url_args == NULL) { url_args = &args; } rc = ngx_http_echo_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } sr_ctx = ngx_http_echo_create_ctx(r); psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (psr == NULL) { return NGX_ERROR; } psr->handler = ngx_http_echo_post_subrequest; psr->data = sr_ctx; rc = ngx_http_subrequest(r, &location, url_args, &sr, psr, 0); if (rc != NGX_OK) { return NGX_ERROR; } rc = ngx_http_echo_adjust_subrequest(sr); if (rc != NGX_OK) { return NGX_ERROR; } return NGX_AGAIN; } static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr) { ngx_http_core_main_conf_t *cmcf; ngx_http_request_t *r; /* we do not inherit the parent request's variables */ cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); r = sr->parent; sr->header_in = r->header_in; /* XXX work-around a bug in ngx_http_subrequest */ if (r->headers_in.headers.last == &r->headers_in.headers.part) { sr->headers_in.headers.last = &sr->headers_in.headers.part; } sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (sr->variables == NULL) { return NGX_ERROR; } return NGX_OK; } libnginx-mod-http-echo-0.63/src/ngx_http_echo_location.h000066400000000000000000000005741433472572100234210ustar00rootroot00000000000000#ifndef ECHO_LOCATION_H #define ECHO_LOCATION_H #include "ngx_http_echo_module.h" ngx_int_t ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ngx_int_t ngx_http_echo_exec_echo_location(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); #endif /* ECHO_LOCATION_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_module.c000066400000000000000000000464211433472572100230720ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_handler.h" #include "ngx_http_echo_filter.h" #include "ngx_http_echo_echo.h" #include "ngx_http_echo_request_info.h" #include "ngx_http_echo_var.h" #include "ngx_http_echo_util.h" #include #include #include /* config init handler */ static void *ngx_http_echo_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static void *ngx_http_echo_create_main_conf(ngx_conf_t *cf); static ngx_int_t ngx_http_echo_post_config(ngx_conf_t *cf); /* config directive handlers */ static char *ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_request_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_reset_timer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_before_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_after_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_location_async(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_location(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_subrequest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_duplicate(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_read_request_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_foreach_split(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_end(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_echo_exec(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_echo_helper(ngx_http_echo_opcode_t opcode, ngx_http_echo_cmd_category_t cat, ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_http_module_t ngx_http_echo_module_ctx = { NULL, /* preconfiguration */ ngx_http_echo_post_config, /* postconfiguration */ ngx_http_echo_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_echo_create_loc_conf, /* create location configuration */ ngx_http_echo_merge_loc_conf /* merge location configuration */ }; static ngx_command_t ngx_http_echo_commands[] = { { ngx_string("echo"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, ngx_http_echo_echo, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_request_body"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ngx_http_echo_echo_request_body, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_sleep"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_http_echo_echo_sleep, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_flush"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ngx_http_echo_echo_flush, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_blocking_sleep"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_http_echo_echo_blocking_sleep, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_reset_timer"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ngx_http_echo_echo_reset_timer, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_before_body"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, ngx_http_echo_echo_before_body, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, before_body_cmds), NULL }, { ngx_string("echo_after_body"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, ngx_http_echo_echo_after_body, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, after_body_cmds), NULL }, { ngx_string("echo_location_async"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, ngx_http_echo_echo_location_async, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_location"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, ngx_http_echo_echo_location, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_subrequest_async"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, ngx_http_echo_echo_subrequest_async, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_subrequest"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, ngx_http_echo_echo_subrequest, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_duplicate"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, ngx_http_echo_echo_duplicate, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_read_request_body"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ngx_http_echo_echo_read_request_body, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_foreach_split"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, ngx_http_echo_echo_foreach_split, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_end"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ngx_http_echo_echo_end, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_abort_parent"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ngx_http_echo_echo_abort_parent, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_exec"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, ngx_http_echo_echo_exec, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, handler_cmds), NULL }, { ngx_string("echo_status"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echo_loc_conf_t, status), NULL }, ngx_null_command }; ngx_module_t ngx_http_echo_module = { NGX_MODULE_V1, &ngx_http_echo_module_ctx, /* module context */ ngx_http_echo_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static void * ngx_http_echo_create_loc_conf(ngx_conf_t *cf) { ngx_http_echo_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t)); if (conf == NULL) { return NULL; } /* set by ngx_pcalloc * conf->handler_cmds = NULL * conf->before_body_cmds = NULL * conf->after_body_cmds = NULL * conf->seen_leading_output = 0 * conf->seen_trailing_output = 0 */ conf->status = NGX_CONF_UNSET; return conf; } static char * ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_echo_loc_conf_t *prev = parent; ngx_http_echo_loc_conf_t *conf = child; if (conf->handler_cmds == NULL) { conf->handler_cmds = prev->handler_cmds; conf->seen_leading_output = prev->seen_leading_output; } if (conf->before_body_cmds == NULL) { conf->before_body_cmds = prev->before_body_cmds; } if (conf->after_body_cmds == NULL) { conf->after_body_cmds = prev->after_body_cmds; } ngx_conf_merge_value(conf->status, prev->status, 200); return NGX_CONF_OK; } static char * ngx_http_echo_helper(ngx_http_echo_opcode_t opcode, ngx_http_echo_cmd_category_t cat, ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_str_t *raw_args; ngx_uint_t i, n; ngx_array_t **args_ptr; ngx_array_t **cmds_ptr; ngx_http_echo_cmd_t *echo_cmd; ngx_http_core_loc_conf_t *clcf; ngx_http_script_compile_t sc; ngx_http_echo_main_conf_t *emcf; ngx_http_echo_arg_template_t *arg; emcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_echo_module); /* cmds_ptr points to ngx_http_echo_loc_conf_t's * handler_cmds, before_body_cmds, or after_body_cmds * array, depending on the actual offset */ cmds_ptr = (ngx_array_t **) (((u_char *) conf) + cmd->offset); if (*cmds_ptr == NULL) { *cmds_ptr = ngx_array_create(cf->pool, 1, sizeof(ngx_http_echo_cmd_t)); if (*cmds_ptr == NULL) { return NGX_CONF_ERROR; } if (cat == echo_handler_cmd) { dd("registering the content handler"); /* register the content handler */ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); dd("registering the content handler (2)"); clcf->handler = ngx_http_echo_handler; } else { dd("filter used = 1"); emcf->requires_filter = 1; } } echo_cmd = ngx_array_push(*cmds_ptr); if (echo_cmd == NULL) { return NGX_CONF_ERROR; } echo_cmd->opcode = opcode; args_ptr = &echo_cmd->args; *args_ptr = ngx_array_create(cf->pool, 1, sizeof(ngx_http_echo_arg_template_t)); if (*args_ptr == NULL) { return NGX_CONF_ERROR; } raw_args = cf->args->elts; /* we skip the first arg and start from the second */ for (i = 1 ; i < cf->args->nelts; i++) { arg = ngx_array_push(*args_ptr); if (arg == NULL) { return NGX_CONF_ERROR; } arg->raw_value = raw_args[i]; dd("found raw arg %s", raw_args[i].data); arg->lengths = NULL; arg->values = NULL; n = ngx_http_script_variables_count(&arg->raw_value); if (n > 0) { ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); sc.cf = cf; sc.source = &arg->raw_value; sc.lengths = &arg->lengths; sc.values = &arg->values; sc.variables = n; sc.complete_lengths = 1; sc.complete_values = 1; if (ngx_http_script_compile(&sc) != NGX_OK) { return NGX_CONF_ERROR; } } } /* end for */ return NGX_CONF_OK; } static char * ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_echo_loc_conf_t *elcf = conf; if (!elcf->seen_leading_output) { elcf->seen_leading_output = 1; } dd("in echo_echo..."); return ngx_http_echo_helper(echo_opcode_echo, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_request_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_echo_loc_conf_t *elcf = conf; if (!elcf->seen_leading_output) { elcf->seen_leading_output = 1; } dd("in echo_echo_request_body..."); return ngx_http_echo_helper(echo_opcode_echo_request_body, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { dd("in echo_sleep..."); return ngx_http_echo_helper(echo_opcode_echo_sleep, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_echo_loc_conf_t *elcf = conf; if (!elcf->seen_leading_output) { elcf->seen_leading_output = 1; } dd("in echo_flush..."); return ngx_http_echo_helper(echo_opcode_echo_flush, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { dd("in echo_blocking_sleep..."); return ngx_http_echo_helper(echo_opcode_echo_blocking_sleep, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_reset_timer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_echo_helper(echo_opcode_echo_reset_timer, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_before_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { dd("processing echo_before_body directive..."); return ngx_http_echo_helper(echo_opcode_echo_before_body, echo_filter_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_after_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_echo_helper(echo_opcode_echo_after_body, echo_filter_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_location_async(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_echo_loc_conf_t *elcf = conf; char *ret; if (!elcf->seen_leading_output) { elcf->seen_leading_output = 1; ret = ngx_http_echo_helper(echo_opcode_echo_sync, echo_handler_cmd, cf, cmd, conf); if (ret != NGX_CONF_OK) { return ret; } } return ngx_http_echo_helper(echo_opcode_echo_location_async, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_location(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_echo_loc_conf_t *elcf = conf; char *ret; if (!elcf->seen_leading_output) { elcf->seen_leading_output = 1; ret = ngx_http_echo_helper(echo_opcode_echo_sync, echo_handler_cmd, cf, cmd, conf); if (ret != NGX_CONF_OK) { return ret; } } return ngx_http_echo_helper(echo_opcode_echo_location, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *ret; ngx_http_echo_loc_conf_t *elcf = conf; if (!elcf->seen_leading_output) { elcf->seen_leading_output = 1; ret = ngx_http_echo_helper(echo_opcode_echo_sync, echo_handler_cmd, cf, cmd, conf); if (ret != NGX_CONF_OK) { return ret; } } return ngx_http_echo_helper(echo_opcode_echo_subrequest_async, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_subrequest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_echo_loc_conf_t *elcf = conf; char *ret; if (!elcf->seen_leading_output) { elcf->seen_leading_output = 1; ret = ngx_http_echo_helper(echo_opcode_echo_sync, echo_handler_cmd, cf, cmd, conf); if (ret != NGX_CONF_OK) { return ret; } } return ngx_http_echo_helper(echo_opcode_echo_subrequest, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_duplicate(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_echo_loc_conf_t *elcf = conf; if (!elcf->seen_leading_output) { elcf->seen_leading_output = 1; } return ngx_http_echo_helper(echo_opcode_echo_duplicate, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_read_request_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_echo_helper(echo_opcode_echo_read_request_body, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_foreach_split(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_echo_helper(echo_opcode_echo_foreach_split, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_end(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_echo_helper(echo_opcode_echo_end, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_echo_helper(echo_opcode_echo_abort_parent, echo_handler_cmd, cf, cmd, conf); } static char * ngx_http_echo_echo_exec(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_echo_helper(echo_opcode_echo_exec, echo_handler_cmd, cf, cmd, conf); } static void * ngx_http_echo_create_main_conf(ngx_conf_t *cf) { #if nginx_version >= 1011011 ngx_pool_cleanup_t *cln; #endif ngx_http_echo_main_conf_t *emcf; emcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_main_conf_t)); if (emcf == NULL) { return NULL; } /* set by ngx_pcalloc: * hmcf->requires_filter = 0; */ #if nginx_version >= 1011011 cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NULL; } cln->data = emcf; cln->handler = ngx_http_echo_request_headers_cleanup; #endif return emcf; } static ngx_int_t ngx_http_echo_post_config(ngx_conf_t *cf) { ngx_int_t rc; rc = ngx_http_echo_filter_init(cf); if (rc != NGX_OK) { return rc; } rc = ngx_http_echo_echo_init(cf); if (rc != NGX_OK) { return rc; } ngx_http_echo_content_length_hash = ngx_http_echo_hash_literal("content-length"); return ngx_http_echo_add_variables(cf); } libnginx-mod-http-echo-0.63/src/ngx_http_echo_module.h000066400000000000000000000101111433472572100230620ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef NGX_HTTP_ECHO_MODULE_H #define NGX_HTTP_ECHO_MODULE_H #include #include #include extern ngx_module_t ngx_http_echo_module; /* config directive's opcode */ typedef enum { echo_opcode_echo_sync, echo_opcode_echo, echo_opcode_echo_request_body, echo_opcode_echo_sleep, echo_opcode_echo_flush, echo_opcode_echo_blocking_sleep, echo_opcode_echo_reset_timer, echo_opcode_echo_before_body, echo_opcode_echo_after_body, echo_opcode_echo_location_async, echo_opcode_echo_location, echo_opcode_echo_subrequest_async, echo_opcode_echo_subrequest, echo_opcode_echo_duplicate, echo_opcode_echo_read_request_body, echo_opcode_echo_foreach_split, echo_opcode_echo_end, echo_opcode_echo_abort_parent, echo_opcode_echo_exec } ngx_http_echo_opcode_t; /* all the various config directives (or commands) are * divided into two categories: "handler commands", * and "filter commands". For instance, the "echo" * directive is a handler command while * "echo_before_body" is a filter one. */ typedef enum { echo_handler_cmd, echo_filter_cmd } ngx_http_echo_cmd_category_t; /* compiled form of a config directive argument's value */ typedef struct { /* holds the raw string of the argument value */ ngx_str_t raw_value; /* fields "lengths" and "values" are set by * the function ngx_http_script_compile, * iff the argument value indeed contains * nginx variables like "$foo" */ ngx_array_t *lengths; ngx_array_t *values; } ngx_http_echo_arg_template_t; /* represent a config directive (or command) like "echo". */ typedef struct { ngx_http_echo_opcode_t opcode; /* each argument is of type echo_arg_template_t: */ ngx_array_t *args; } ngx_http_echo_cmd_t; /* location config struct */ typedef struct { /* elements of the following arrays are of type * ngx_http_echo_cmd_t */ ngx_array_t *handler_cmds; ngx_array_t *before_body_cmds; ngx_array_t *after_body_cmds; unsigned seen_leading_output; ngx_int_t status; } ngx_http_echo_loc_conf_t; typedef struct { ngx_int_t requires_filter; #if nginx_version >= 1011011 ngx_buf_t **busy_buf_ptrs; ngx_int_t busy_buf_ptr_count; #endif } ngx_http_echo_main_conf_t; typedef struct { ngx_array_t *choices; /* items after splitting */ ngx_uint_t next_choice; /* current item index */ ngx_uint_t cmd_index; /* cmd index for the echo_foreach direcitve */ } ngx_http_echo_foreach_ctx_t; /* context struct in the request handling cycle, holding * the current states of the command evaluator */ typedef struct { /* index of the next handler command in * ngx_http_echo_loc_conf_t's "handler_cmds" array. */ ngx_uint_t next_handler_cmd; /* index of the next before-body filter command in * ngx_http_echo_loc_conf_t's "before_body_cmds" array. */ ngx_uint_t next_before_body_cmd; /* index of the next after-body filter command in * ngx_http_echo_loc_conf_t's "after_body_cmds" array. */ ngx_uint_t next_after_body_cmd; ngx_http_echo_foreach_ctx_t *foreach; ngx_time_t timer_begin; ngx_event_t sleep; ngx_uint_t counter; unsigned before_body_sent:1; unsigned skip_filter:1; unsigned wait_read_request_body:1; unsigned waiting:1; unsigned done:1; unsigned run_post_subrequest:1; unsigned header_sent:1; /* r->header_sent is not sufficient * because special header filters like * ngx_http_image_filter_module's may * intercept the whole header filter chain * leaving r->header_sent unset. So we * should always test both flags. */ } ngx_http_echo_ctx_t; #endif /* NGX_HTTP_ECHO_MODULE_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_request_info.c000066400000000000000000000277541433472572100243200ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_request_info.h" #include "ngx_http_echo_util.h" #include "ngx_http_echo_handler.h" #include static void ngx_http_echo_post_read_request_body(ngx_http_request_t *r); #if nginx_version >= 1011011 void ngx_http_echo_request_headers_cleanup(void *data); #endif ngx_int_t ngx_http_echo_exec_echo_read_request_body(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { return ngx_http_read_client_request_body(r, ngx_http_echo_post_read_request_body); } static void ngx_http_echo_post_read_request_body(ngx_http_request_t *r) { ngx_http_echo_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); dd("wait read request body %d", (int) ctx->wait_read_request_body); if (ctx->wait_read_request_body) { ctx->waiting = 0; ctx->done = 1; r->write_event_handler = ngx_http_echo_wev_handler; ngx_http_echo_wev_handler(r); } } /* this function's implementation is borrowed from nginx 0.8.20 * and modified a bit to work with subrequests. * Copyrighted (C) by Igor Sysoev */ ngx_int_t ngx_http_echo_request_method_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { if (r->method_name.data) { v->len = r->method_name.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->data = r->method_name.data; } else { v->not_found = 1; } return NGX_OK; } /* this function's implementation is borrowed from nginx 0.8.20 * and modified a bit to work with subrequests. * Copyrighted (C) by Igor Sysoev */ ngx_int_t ngx_http_echo_client_request_method_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { if (r->main->method_name.data) { v->len = r->main->method_name.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->data = r->main->method_name.data; } else { v->not_found = 1; } return NGX_OK; } /* this function's implementation is borrowed from nginx 0.8.20 * and modified a bit to work with subrequests. * Copyrighted (C) by Igor Sysoev */ ngx_int_t ngx_http_echo_request_body_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { u_char *p; size_t len; ngx_buf_t *b; ngx_chain_t *cl; ngx_chain_t *in; if (r->request_body == NULL || r->request_body->bufs == NULL || r->request_body->temp_file) { v->not_found = 1; return NGX_OK; } in = r->request_body->bufs; len = 0; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (!ngx_buf_in_memory(b)) { if (b->in_file) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "variable echo_request_body sees in-file only " "buffers and discard the whole body data"); v->not_found = 1; return NGX_OK; } } else { len += b->last - b->pos; } } p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } v->data = p; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (ngx_buf_in_memory(b)) { p = ngx_copy(p, b->pos, b->last - b->pos); } } if (p - v->data != (ssize_t) len) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "variable echo_request_body: buffer error"); v->not_found = 1; return NGX_OK; } v->len = len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; return NGX_OK; } ngx_int_t ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { int line_break_len; size_t size; u_char *p, *last, *pos; ngx_int_t i, j; ngx_buf_t *b, *first = NULL; unsigned found; #if nginx_version >= 1011011 ngx_buf_t **bb; ngx_chain_t *cl; ngx_http_echo_main_conf_t *emcf; #endif ngx_connection_t *c; ngx_http_request_t *mr; ngx_http_connection_t *hc; mr = r->main; hc = r->main->http_connection; c = mr->connection; #if (NGX_HTTP_V2) /* TODO */ if (mr->stream) { v->not_found = 1; return NGX_OK; } #endif #if nginx_version >= 1011011 emcf = ngx_http_get_module_main_conf(r, ngx_http_echo_module); #endif size = 0; b = c->buffer; if (mr->request_line.data[mr->request_line.len] == CR) { line_break_len = 2; } else { line_break_len = 1; } if (mr->request_line.data >= b->start && mr->request_line.data + mr->request_line.len + line_break_len <= b->pos) { first = b; size += b->pos - mr->request_line.data; } if (hc->nbusy) { b = NULL; #if nginx_version >= 1011011 if (hc->nbusy > emcf->busy_buf_ptr_count) { if (emcf->busy_buf_ptrs) { ngx_free(emcf->busy_buf_ptrs); } emcf->busy_buf_ptrs = ngx_alloc(hc->nbusy * sizeof(ngx_buf_t *), r->connection->log); if (emcf->busy_buf_ptrs == NULL) { return NGX_ERROR; } emcf->busy_buf_ptr_count = hc->nbusy; } bb = emcf->busy_buf_ptrs; for (cl = hc->busy; cl; cl = cl->next) { *bb++ = cl->buf; } bb = emcf->busy_buf_ptrs; for (i = hc->nbusy; i > 0; i--) { b = bb[i - 1]; #else for (i = 0; i < hc->nbusy; i++) { b = hc->busy[i]; #endif if (first == NULL) { if (mr->request_line.data >= b->pos || mr->request_line.data + mr->request_line.len + line_break_len <= b->start) { continue; } dd("found first at %d", (int) i); first = b; } size += b->pos - b->start; } } size++; /* plus the null terminator, as required by the later ngx_strstr() call */ v->data = ngx_palloc(r->pool, size); if (v->data == NULL) { return NGX_ERROR; } last = v->data; b = c->buffer; found = 0; if (first == b) { found = 1; pos = b->pos; last = ngx_copy(v->data, mr->request_line.data, pos - mr->request_line.data); if (b != mr->header_in) { /* skip truncated header entries (if any) */ while (last > v->data && last[-1] != LF) { last--; } } i = 0; for (p = v->data; p != last; p++) { if (*p == '\0') { i++; if (p + 1 != last && *(p + 1) == LF) { *p = CR; } else if (i % 2 == 1) { *p = ':'; } else { *p = LF; } } } } if (hc->nbusy) { #if nginx_version >= 1011011 bb = emcf->busy_buf_ptrs; for (i = hc->nbusy; i > 0; i--) { b = bb[i - 1]; #else for (i = 0; i < hc->nbusy; i++) { b = hc->busy[i]; #endif if (!found) { if (b != first) { continue; } dd("found first"); found = 1; } p = last; pos = b->pos; if (b == first) { dd("request line: %.*s", (int) mr->request_line.len, mr->request_line.data); last = ngx_copy(last, mr->request_line.data, pos - mr->request_line.data); } else { last = ngx_copy(last, b->start, pos - b->start); } #if 1 /* skip truncated header entries (if any) */ while (last > p && last[-1] != LF) { last--; } #endif j = 0; for (; p != last; p++) { if (*p == '\0') { j++; if (p + 1 == last) { /* XXX this should not happen */ dd("found string end!!"); } else if (*(p + 1) == LF) { *p = CR; } else if (j % 2 == 1) { *p = ':'; } else { *p = LF; } } } if (b == mr->header_in) { break; } } } *last++ = '\0'; if (last - v->data > (ssize_t) size) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "buffer error when evaluating " "$echo_client__request_headers: \"%V\"", (ngx_int_t) (last - v->data - size)); return NGX_ERROR; } /* strip the leading part (if any) of the request body in our header. * the first part of the request body could slip in because nginx core's * ngx_http_request_body_length_filter and etc can move r->header_in->pos * in case that some of the body data has been preread into r->header_in. */ if ((p = (u_char *) ngx_strstr(v->data, CRLF CRLF)) != NULL) { last = p + sizeof(CRLF CRLF) - 1; } else if ((p = (u_char *) ngx_strstr(v->data, CRLF "\n")) != NULL) { last = p + sizeof(CRLF "\n") - 1; } else if ((p = (u_char *) ngx_strstr(v->data, "\n" CRLF)) != NULL) { last = p + sizeof("\n" CRLF) - 1; } else { for (p = last - 1; p - v->data >= 2; p--) { if (p[0] == LF && p[-1] == CR) { p[-1] = LF; last = p + 1; break; } if (p[0] == LF && p[-1] == LF) { last = p + 1; break; } } } v->len = last - v->data; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; return NGX_OK; } ngx_int_t ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { if (r->uri.len) { v->len = r->uri.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->data = r->uri.data; } else { v->not_found = 1; } return NGX_OK; } ngx_int_t ngx_http_echo_request_uri_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { if (r->uri.len) { v->len = r->uri.len; v->valid = 1; v->no_cacheable = 1; v->not_found = 0; v->data = r->uri.data; } else { v->not_found = 1; } return NGX_OK; } ngx_int_t ngx_http_echo_response_status_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { u_char *p; if (r->headers_out.status) { dd("headers out status: %d", (int) r->headers_out.status); p = ngx_palloc(r->pool, NGX_INT_T_LEN); if (p == NULL) { return NGX_ERROR; } v->len = ngx_sprintf(p, "%ui", r->headers_out.status) - p; v->data = p; v->valid = 1; v->no_cacheable = 1; v->not_found = 0; } else { v->not_found = 1; } return NGX_OK; } #if nginx_version >= 1011011 void ngx_http_echo_request_headers_cleanup(void *data) { ngx_http_echo_main_conf_t *emcf; emcf = (ngx_http_echo_main_conf_t *) data; if (emcf->busy_buf_ptrs) { ngx_free(emcf->busy_buf_ptrs); emcf->busy_buf_ptrs = NULL; } } #endif /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_request_info.h000066400000000000000000000022671433472572100243150ustar00rootroot00000000000000#ifndef ECHO_REQUEST_INFO_H #define ECHO_REQUEST_INFO_H #include "ngx_http_echo_module.h" ngx_int_t ngx_http_echo_exec_echo_read_request_body( ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx); ngx_int_t ngx_http_echo_request_method_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); ngx_int_t ngx_http_echo_client_request_method_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); ngx_int_t ngx_http_echo_request_body_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); ngx_int_t ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); ngx_int_t ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); ngx_int_t ngx_http_echo_request_uri_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); ngx_int_t ngx_http_echo_response_status_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); #if nginx_version >= 1011011 void ngx_http_echo_request_headers_cleanup(void *data); #endif #endif /* ECHO_REQUEST_INFO_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_sleep.c000066400000000000000000000110241433472572100227040ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_sleep.h" #include "ngx_http_echo_handler.h" #include #include /* event handler for echo_sleep */ static void ngx_http_echo_post_sleep(ngx_http_request_t *r); static void ngx_http_echo_sleep_cleanup(void *data); ngx_int_t ngx_http_echo_exec_echo_sleep(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_str_t *computed_arg; ngx_str_t *computed_arg_elts; ngx_int_t delay; /* in msec */ ngx_http_cleanup_t *cln; computed_arg_elts = computed_args->elts; computed_arg = &computed_arg_elts[0]; delay = ngx_atofp(computed_arg->data, computed_arg->len, 3); if (delay == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid sleep duration \"%V\"", &computed_arg_elts[0]); return NGX_HTTP_BAD_REQUEST; } dd("adding timer with delay %lu ms, r:%.*s", (unsigned long) delay, (int) r->uri.len, r->uri.data); ngx_add_timer(&ctx->sleep, (ngx_msec_t) delay); /* we don't check broken downstream connections * ourselves so even if the client shuts down * the connection prematurely, nginx will still * go on waiting for our timers to get properly * expired. However, we'd still register a * cleanup handler for completeness. */ cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_echo_sleep_cleanup; cln->data = r; return NGX_AGAIN; } static void ngx_http_echo_post_sleep(ngx_http_request_t *r) { ngx_http_echo_ctx_t *ctx; /* ngx_int_t rc; */ dd("post sleep, r:%.*s", (int) r->uri.len, r->uri.data); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { return; } ctx->waiting = 0; ctx->done = 1; dd("sleep: after get module ctx"); dd("timed out? %d", ctx->sleep.timedout); dd("timer set? %d", ctx->sleep.timer_set); if (!ctx->sleep.timedout) { dd("HERE reached!"); return; } ctx->sleep.timedout = 0; if (ctx->sleep.timer_set) { dd("deleting timer for echo_sleep"); ngx_del_timer(&ctx->sleep); } /* r->write_event_handler = ngx_http_request_empty_handler; */ ngx_http_echo_wev_handler(r); } void ngx_http_echo_sleep_event_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; r = ev->data; c = r->connection; if (c->destroyed) { return; } if (c->error) { ngx_http_finalize_request(r, NGX_ERROR); return; } ctx = c->log->data; ctx->current_request = r; /* XXX when r->done == 1 we should do cleaning immediately * and delete our timer and then quit. */ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "echo sleep event handler: \"%V?%V\"", &r->uri, &r->args); /* if (r->done) { return; } */ ngx_http_echo_post_sleep(r); #if defined(nginx_version) dd("before run posted requests"); ngx_http_run_posted_requests(c); dd("after run posted requests"); #endif } ngx_int_t ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_str_t *computed_arg; ngx_str_t *computed_arg_elts; ngx_int_t delay; /* in msec */ computed_arg_elts = computed_args->elts; computed_arg = &computed_arg_elts[0]; delay = ngx_atofp(computed_arg->data, computed_arg->len, 3); if (delay == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid sleep duration \"%V\"", &computed_arg_elts[0]); return NGX_HTTP_BAD_REQUEST; } dd("blocking delay: %lu ms", (unsigned long) delay); ngx_msleep((ngx_msec_t) delay); return NGX_OK; } static void ngx_http_echo_sleep_cleanup(void *data) { ngx_http_request_t *r = data; ngx_http_echo_ctx_t *ctx; dd("echo sleep cleanup"); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { return; } if (ctx->sleep.timer_set) { dd("cleanup: deleting timer for echo_sleep"); ngx_del_timer(&ctx->sleep); return; } dd("cleanup: timer not set"); } libnginx-mod-http-echo-0.63/src/ngx_http_echo_sleep.h000066400000000000000000000006631433472572100227200ustar00rootroot00000000000000#ifndef ECHO_SLEEP_H #define ECHO_SLEEP_H #include "ngx_http_echo_module.h" ngx_int_t ngx_http_echo_exec_echo_sleep( ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ngx_int_t ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); void ngx_http_echo_sleep_event_handler(ngx_event_t *ev); #endif /* ECHO_SLEEP_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_subrequest.c000066400000000000000000000527741433472572100240170ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_util.h" #include "ngx_http_echo_subrequest.h" #include "ngx_http_echo_handler.h" #include #define ngx_http_echo_method_name(m) { sizeof(m) - 1, (u_char *) m " " } ngx_str_t ngx_http_echo_content_length_header_key = ngx_string("Content-Length"); ngx_str_t ngx_http_echo_get_method = ngx_http_echo_method_name("GET"); ngx_str_t ngx_http_echo_put_method = ngx_http_echo_method_name("PUT"); ngx_str_t ngx_http_echo_post_method = ngx_http_echo_method_name("POST"); ngx_str_t ngx_http_echo_head_method = ngx_http_echo_method_name("HEAD"); ngx_str_t ngx_http_echo_copy_method = ngx_http_echo_method_name("COPY"); ngx_str_t ngx_http_echo_move_method = ngx_http_echo_method_name("MOVE"); ngx_str_t ngx_http_echo_lock_method = ngx_http_echo_method_name("LOCK"); ngx_str_t ngx_http_echo_mkcol_method = ngx_http_echo_method_name("MKCOL"); ngx_str_t ngx_http_echo_trace_method = ngx_http_echo_method_name("TRACE"); ngx_str_t ngx_http_echo_delete_method = ngx_http_echo_method_name("DELETE"); ngx_str_t ngx_http_echo_unlock_method = ngx_http_echo_method_name("UNLOCK"); ngx_str_t ngx_http_echo_options_method = ngx_http_echo_method_name("OPTIONS"); ngx_str_t ngx_http_echo_propfind_method = ngx_http_echo_method_name("PROPFIND"); ngx_str_t ngx_http_echo_proppatch_method = ngx_http_echo_method_name("PROPPATCH"); typedef struct ngx_http_echo_subrequest_s { ngx_uint_t method; ngx_str_t *method_name; ngx_str_t *location; ngx_str_t *query_string; ssize_t content_length_n; ngx_http_request_body_t *request_body; } ngx_http_echo_subrequest_t; static ngx_int_t ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr); static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr, ngx_http_echo_subrequest_t *parsed_sr); static ngx_int_t ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r, ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr); static ngx_int_t ngx_http_echo_set_content_length_header(ngx_http_request_t *r, off_t len); ngx_int_t ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_int_t rc; ngx_http_echo_subrequest_t *parsed_sr; ngx_http_request_t *sr; /* subrequest object */ ngx_str_t args; ngx_uint_t flags = 0; dd_enter(); rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr); if (rc != NGX_OK) { return rc; } dd("location: %.*s", (int) parsed_sr->location->len, parsed_sr->location->data); args.data = NULL; args.len = 0; if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "echo_subrequest_async sees unsafe uri: \"%V\"", parsed_sr->location); return NGX_ERROR; } if (args.len > 0 && parsed_sr->query_string == NULL) { parsed_sr->query_string = &args; } rc = ngx_http_echo_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string, &sr, NULL, 0); if (rc != NGX_OK) { return NGX_ERROR; } rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr); if (rc != NGX_OK) { return rc; } return NGX_OK; } ngx_int_t ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_int_t rc; ngx_http_request_t *sr; /* subrequest object */ ngx_http_post_subrequest_t *psr; ngx_http_echo_subrequest_t *parsed_sr; ngx_str_t args; ngx_uint_t flags = 0; ngx_http_echo_ctx_t *sr_ctx; dd_enter(); rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr); if (rc != NGX_OK) { return rc; } args.data = NULL; args.len = 0; if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "echo_subrequest sees unsafe uri: \"%V\"", parsed_sr->location); return NGX_ERROR; } if (args.len > 0 && parsed_sr->query_string == NULL) { parsed_sr->query_string = &args; } rc = ngx_http_echo_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } sr_ctx = ngx_http_echo_create_ctx(r); /* set by ngx_http_echo_create_ctx * sr_ctx->run_post_subrequest = 0 */ dd("creating sr ctx for %.*s: %p", (int) parsed_sr->location->len, parsed_sr->location->data, sr_ctx); psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (psr == NULL) { return NGX_ERROR; } psr->handler = ngx_http_echo_post_subrequest; psr->data = sr_ctx; rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string, &sr, psr, 0); if (rc != NGX_OK) { return NGX_ERROR; } sr_ctx->sleep.data = sr; ngx_http_set_ctx(sr, sr_ctx, ngx_http_echo_module); rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr); if (rc != NGX_OK) { return NGX_ERROR; } return NGX_AGAIN; } static ngx_int_t ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r, ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr) { ngx_str_t *computed_arg_elts, *arg; ngx_str_t **to_write = NULL; ngx_str_t *method_name; ngx_str_t *body_str = NULL; ngx_str_t *body_file = NULL; ngx_uint_t i; ngx_flag_t expecting_opt; ngx_http_request_body_t *rb = NULL; ngx_buf_t *b; ngx_http_echo_subrequest_t *parsed_sr; ngx_open_file_info_t of; ngx_http_core_loc_conf_t *clcf; size_t len; *parsed_sr_ptr = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_subrequest_t)); if (*parsed_sr_ptr == NULL) { return NGX_ERROR; } parsed_sr = *parsed_sr_ptr; computed_arg_elts = computed_args->elts; method_name = &computed_arg_elts[0]; parsed_sr->location = &computed_arg_elts[1]; if (parsed_sr->location->len == 0) { return NGX_ERROR; } expecting_opt = 1; for (i = 2; i < computed_args->nelts; i++) { arg = &computed_arg_elts[i]; if (!expecting_opt) { if (to_write == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "echo_subrequest_async: to_write should NOT be NULL"); return NGX_ERROR; } *to_write = arg; to_write = NULL; expecting_opt = 1; continue; } if (arg->len == 2) { if (ngx_strncmp("-q", arg->data, arg->len) == 0) { to_write = &parsed_sr->query_string; expecting_opt = 0; continue; } if (ngx_strncmp("-b", arg->data, arg->len) == 0) { to_write = &body_str; expecting_opt = 0; continue; } if (ngx_strncmp("-f", arg->data, arg->len) == 0) { dd("found option -f"); to_write = &body_file; expecting_opt = 0; continue; } } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unknown option for echo_subrequest*: %V", arg); return NGX_ERROR; } if (body_str != NULL && body_str->len) { rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { return NGX_ERROR; } parsed_sr->content_length_n = body_str->len; b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->temporary = 1; /* b->memory = 1; */ b->start = b->pos = body_str->data; b->end = b->last = body_str->data + body_str->len; rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return NGX_ERROR; } rb->bufs->buf = b; rb->bufs->next = NULL; rb->buf = b; } else if (body_file != NULL && body_file->len) { dd("body_file defined %.*s", (int) body_file->len, body_file->data); body_file->data = ngx_http_echo_rebase_path(r->pool, body_file->data, body_file->len, &len); if (body_file->data == NULL) { return NGX_ERROR; } body_file->len = len; dd("after rebase, the path becomes %.*s", (int) body_file->len, body_file->data); rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { return NGX_ERROR; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_memzero(&of, sizeof(ngx_open_file_info_t)); #if defined(nginx_version) && nginx_version >= 8018 of.read_ahead = clcf->read_ahead; #endif of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; if (ngx_open_cached_file(clcf->open_file_cache, body_file, &of, r->pool) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err, "%s \"%V\" failed", of.failed, body_file); return NGX_ERROR; } dd("file content size: %d", (int) of.size); parsed_sr->content_length_n = (ssize_t) of.size; b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_ERROR; } b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); if (b->file == NULL) { return NGX_ERROR; } b->file_pos = 0; b->file_last = of.size; b->in_file = b->file_last ? 1: 0; #if 0 b->last_buf = (r == r->main) ? 1: 0; b->last_in_chain = 1; #endif b->file->fd = of.fd; b->file->name = *body_file; b->file->log = r->connection->log; b->file->directio = of.is_directio; rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return NGX_ERROR; } rb->bufs->buf = b; rb->bufs->next = NULL; rb->buf = b; } parsed_sr->request_body = rb; parsed_sr->method = ngx_http_echo_parse_method_name(&method_name); parsed_sr->method_name = method_name; return NGX_OK; } static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr, ngx_http_echo_subrequest_t *parsed_sr) { ngx_http_core_main_conf_t *cmcf; ngx_http_request_t *r; ngx_http_request_body_t *body; ngx_int_t rc; sr->method = parsed_sr->method; sr->method_name = *(parsed_sr->method_name); if (sr->method == NGX_HTTP_HEAD) { sr->header_only = 1; } r = sr->parent; sr->header_in = r->header_in; /* XXX work-around a bug in ngx_http_subrequest */ if (r->headers_in.headers.last == &r->headers_in.headers.part) { sr->headers_in.headers.last = &sr->headers_in.headers.part; } /* we do not inherit the parent request's variables */ cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (sr->variables == NULL) { return NGX_ERROR; } body = parsed_sr->request_body; if (body) { sr->request_body = body; rc = ngx_http_echo_set_content_length_header(sr, body->buf ? ngx_buf_size(body->buf) : 0); if (rc != NGX_OK) { return NGX_ERROR; } } dd("subrequest body: %p", sr->request_body); return NGX_OK; } static ngx_int_t ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr) { const ngx_str_t *method_name = *method_name_ptr; switch (method_name->len) { case 3: if (ngx_http_echo_strcmp_const(method_name->data, "GET") == 0) { *method_name_ptr = &ngx_http_echo_get_method; return NGX_HTTP_GET; } if (ngx_http_echo_strcmp_const(method_name->data, "PUT") == 0) { *method_name_ptr = &ngx_http_echo_put_method; return NGX_HTTP_PUT; } return NGX_HTTP_UNKNOWN; case 4: if (ngx_http_echo_strcmp_const(method_name->data, "POST") == 0) { *method_name_ptr = &ngx_http_echo_post_method; return NGX_HTTP_POST; } if (ngx_http_echo_strcmp_const(method_name->data, "HEAD") == 0) { *method_name_ptr = &ngx_http_echo_head_method; return NGX_HTTP_HEAD; } if (ngx_http_echo_strcmp_const(method_name->data, "COPY") == 0) { *method_name_ptr = &ngx_http_echo_copy_method; return NGX_HTTP_COPY; } if (ngx_http_echo_strcmp_const(method_name->data, "MOVE") == 0) { *method_name_ptr = &ngx_http_echo_move_method; return NGX_HTTP_MOVE; } if (ngx_http_echo_strcmp_const(method_name->data, "LOCK") == 0) { *method_name_ptr = &ngx_http_echo_lock_method; return NGX_HTTP_LOCK; } return NGX_HTTP_UNKNOWN; case 5: if (ngx_http_echo_strcmp_const(method_name->data, "MKCOL") == 0) { *method_name_ptr = &ngx_http_echo_mkcol_method; return NGX_HTTP_MKCOL; } if (ngx_http_echo_strcmp_const(method_name->data, "TRACE") == 0) { *method_name_ptr = &ngx_http_echo_trace_method; return NGX_HTTP_TRACE; } return NGX_HTTP_UNKNOWN; case 6: if (ngx_http_echo_strcmp_const(method_name->data, "DELETE") == 0) { *method_name_ptr = &ngx_http_echo_delete_method; return NGX_HTTP_DELETE; } if (ngx_http_echo_strcmp_const(method_name->data, "UNLOCK") == 0) { *method_name_ptr = &ngx_http_echo_unlock_method; return NGX_HTTP_UNLOCK; } return NGX_HTTP_UNKNOWN; case 7: if (ngx_http_echo_strcmp_const(method_name->data, "OPTIONS") == 0) { *method_name_ptr = &ngx_http_echo_options_method; return NGX_HTTP_OPTIONS; } return NGX_HTTP_UNKNOWN; case 8: if (ngx_http_echo_strcmp_const(method_name->data, "PROPFIND") == 0) { *method_name_ptr = &ngx_http_echo_propfind_method; return NGX_HTTP_PROPFIND; } return NGX_HTTP_UNKNOWN; case 9: if (ngx_http_echo_strcmp_const(method_name->data, "PROPPATCH") == 0) { *method_name_ptr = &ngx_http_echo_proppatch_method; return NGX_HTTP_PROPPATCH; } return NGX_HTTP_UNKNOWN; default: return NGX_HTTP_UNKNOWN; } } /* XXX extermely evil and not working yet */ ngx_int_t ngx_http_echo_exec_abort_parent(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { #if 0 ngx_http_postponed_request_t *pr, *ppr; ngx_http_request_t *saved_data = NULL; ngx_chain_t *out = NULL; /* ngx_int_t rc; */ dd("aborting parent..."); if (r == r->main || r->parent == NULL) { return NGX_OK; } if (r->parent->postponed) { dd("Found parent->postponed..."); saved_data = r->connection->data; ppr = NULL; for (pr = r->parent->postponed; pr->next; pr = pr->next) { if (pr->request == NULL) { continue; } if (pr->request == r) { /* r->parent->postponed->next = pr; */ dd("found the current subrequest"); out = pr->out; continue; } /* r->connection->data = pr->request; */ dd("finalizing the subrequest..."); ngx_http_upstream_create(pr->request); pr->request->upstream = NULL; if (ppr == NULL) { r->parent->postponed = pr->next; ppr = pr->next; } else { ppr->next = pr->next; ppr = pr->next; } } } r->parent->postponed->next = NULL; /* r->connection->data = r->parent; r->connection->buffered = 0; if (out != NULL) { dd("trying to send more stuffs for the parent"); ngx_http_output_filter(r->parent, out); } */ /* ngx_http_send_special(r->parent, NGX_HTTP_LAST); */ if (saved_data) { r->connection->data = saved_data; } dd("terminating the parent request"); return ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */); /* ngx_http_upstream_create(r); */ /* ngx_http_finalize_request(r->parent, NGX_ERROR); */ #endif return NGX_OK; } ngx_int_t ngx_http_echo_exec_exec(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_str_t *uri; ngx_str_t *user_args; ngx_str_t args; ngx_uint_t flags = 0; ngx_str_t *computed_arg; computed_arg = computed_args->elts; uri = &computed_arg[0]; if (uri->len == 0) { return NGX_HTTP_BAD_REQUEST; } if (computed_args->nelts > 1) { user_args = &computed_arg[1]; } else { user_args = NULL; } args.data = NULL; args.len = 0; if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "echo_exec sees unsafe uri: \"%V\"", uri); return NGX_ERROR; } if (args.len > 0 && user_args == NULL) { user_args = &args; } r->write_event_handler = ngx_http_request_empty_handler; if (uri->data[0] == '@') { if (user_args && user_args->len > 0) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "querystring %V ignored when exec'ing named " "location %V", user_args, uri); } #if 1 /* clear the modules contexts */ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); #endif dd("named location: %.*s, c:%d", (int) uri->len, uri->data, (int) r->main->count); return ngx_http_named_location(r, uri); } return ngx_http_internal_redirect(r, uri, user_args); } static ngx_int_t ngx_http_echo_set_content_length_header(ngx_http_request_t *r, off_t len) { ngx_table_elt_t *h, *header; u_char *p; ngx_list_part_t *part; ngx_http_request_t *pr; ngx_uint_t i; r->headers_in.content_length_n = len; if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { return NGX_ERROR; } h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } h->key = ngx_http_echo_content_length_header_key; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); r->headers_in.content_length = h; p = ngx_palloc(r->pool, NGX_OFF_T_LEN); if (p == NULL) { return NGX_ERROR; } h->value.data = p; h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data; h->hash = ngx_http_echo_content_length_hash; dd("r content length: %.*s", (int) r->headers_in.content_length->value.len, r->headers_in.content_length->value.data); pr = r->parent; if (pr == NULL) { return NGX_OK; } /* forward the parent request's all other request headers */ part = &pr->headers_in.headers.part; header = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (header[i].key.len == sizeof("Content-Length") - 1 && ngx_strncasecmp(header[i].key.data, (u_char *) "Content-Length", sizeof("Content-Length") - 1) == 0) { continue; } h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } *h = header[i]; } /* XXX maybe we should set those built-in header slot in * ngx_http_headers_in_t too? */ return NGX_OK; } libnginx-mod-http-echo-0.63/src/ngx_http_echo_subrequest.h000066400000000000000000000011441433472572100240050ustar00rootroot00000000000000#ifndef ECHO_SUBREQUEST_H #define ECHO_SUBREQUEST_H #include "ngx_http_echo_module.h" ngx_int_t ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ngx_int_t ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ngx_int_t ngx_http_echo_exec_abort_parent(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx); ngx_int_t ngx_http_echo_exec_exec(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); #endif /* ECHO_SUBREQUEST_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_timer.c000066400000000000000000000041121433472572100227140ustar00rootroot00000000000000#ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_timer.h" #include "ngx_http_echo_util.h" #include #include #include ngx_int_t ngx_http_echo_timer_elapsed_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_echo_ctx_t *ctx; ngx_msec_int_t ms; u_char *p; ngx_time_t *tp; size_t size; ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { ctx = ngx_http_echo_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_echo_module); } if (ctx->timer_begin.sec == 0) { ctx->timer_begin.sec = r->start_sec; ctx->timer_begin.msec = (ngx_msec_t) r->start_msec; } /* force the ngx timer to update */ #if (nginx_version >= 8035) || (nginx_version < 8000 && nginx_version >= 7066) ngx_time_update(); #else ngx_time_update(0, 0); #endif tp = ngx_timeofday(); dd("old sec msec: %ld %d\n", (long) ctx->timer_begin.sec, (int) ctx->timer_begin.msec); dd("new sec msec: %ld %d\n", (long) tp->sec, (int) tp->msec); ms = (ngx_msec_int_t) ((tp->sec - ctx->timer_begin.sec) * 1000 + (tp->msec - ctx->timer_begin.msec)); ms = (ms >= 0) ? ms : 0; size = sizeof("-9223372036854775808.000") - 1; p = ngx_palloc(r->pool, size); if (p == NULL) { return NGX_ERROR; } v->len = ngx_snprintf(p, size, "%T.%03M", ms / 1000, ms % 1000) - p; v->data = p; v->valid = 1; v->no_cacheable = 1; v->not_found = 0; return NGX_OK; } ngx_int_t ngx_http_echo_exec_echo_reset_timer(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { dd("Exec timer..."); /* force the ngx timer to update */ #if (nginx_version >= 8035) || (nginx_version < 8000 && nginx_version >= 7066) ngx_time_update(); #else ngx_time_update(0, 0); #endif ctx->timer_begin = *ngx_timeofday(); return NGX_OK; } libnginx-mod-http-echo-0.63/src/ngx_http_echo_timer.h000066400000000000000000000005201433472572100227200ustar00rootroot00000000000000#ifndef ECHO_TIMER_H #define ECHO_TIMER_H #include "ngx_http_echo_module.h" ngx_int_t ngx_http_echo_timer_elapsed_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); ngx_int_t ngx_http_echo_exec_echo_reset_timer(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx); #endif /* ECHO_TIMER_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_util.c000066400000000000000000000146351433472572100225640ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_util.h" #include "ngx_http_echo_sleep.h" ngx_uint_t ngx_http_echo_content_length_hash = 0; ngx_http_echo_ctx_t * ngx_http_echo_create_ctx(ngx_http_request_t *r) { ngx_http_echo_ctx_t *ctx; ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_ctx_t)); if (ctx == NULL) { return NULL; } ctx->sleep.handler = ngx_http_echo_sleep_event_handler; ctx->sleep.data = r; ctx->sleep.log = r->connection->log; return ctx; } ngx_int_t ngx_http_echo_eval_cmd_args(ngx_http_request_t *r, ngx_http_echo_cmd_t *cmd, ngx_array_t *computed_args, ngx_array_t *opts) { unsigned expecting_opts = 1; ngx_uint_t i; ngx_array_t *args = cmd->args; ngx_str_t *arg, *raw, *opt; ngx_http_echo_arg_template_t *value; value = args->elts; for (i = 0; i < args->nelts; i++) { raw = &value[i].raw_value; if (value[i].lengths == NULL && raw->len > 0) { if (expecting_opts) { if (raw->len == 1 || raw->data[0] != '-') { expecting_opts = 0; } else if (raw->data[1] == '-') { expecting_opts = 0; continue; } else { opt = ngx_array_push(opts); if (opt == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } opt->len = raw->len - 1; opt->data = raw->data + 1; dd("pushing opt: %.*s", (int) opt->len, opt->data); continue; } } } else { expecting_opts = 0; } arg = ngx_array_push(computed_args); if (arg == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (value[i].lengths == NULL) { /* does not contain vars */ dd("Using raw value \"%.*s\"", (int) raw->len, raw->data); *arg = *raw; } else { if (ngx_http_script_run(r, arg, value[i].lengths->elts, 0, value[i].values->elts) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } dd("pushed arg: %.*s", (int) arg->len, arg->data); } return NGX_OK; } ngx_int_t ngx_http_echo_send_chain_link(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_chain_t *in) { ngx_int_t rc; rc = ngx_http_echo_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } if (in == NULL) { #if defined(nginx_version) && nginx_version <= 8004 /* earlier versions of nginx does not allow subrequests to send last_buf themselves */ if (r != r->main) { return NGX_OK; } #endif rc = ngx_http_send_special(r, NGX_HTTP_LAST); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } return NGX_OK; } /* FIXME we should udpate chains to recycle chain links and bufs */ return ngx_http_output_filter(r, in); } ngx_int_t ngx_http_echo_send_header_if_needed(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { ngx_int_t rc; ngx_http_echo_loc_conf_t *elcf; if (!r->header_sent && !ctx->header_sent) { elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); r->headers_out.status = (ngx_uint_t) elcf->status; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); rc = ngx_http_send_header(r); ctx->header_sent = 1; return rc; } return NGX_OK; } ssize_t ngx_http_echo_atosz(u_char *line, size_t n) { ssize_t value; if (n == 0) { return NGX_ERROR; } for (value = 0; n--; line++) { if (*line == '_') { /* we ignore undercores */ continue; } if (*line < '0' || *line > '9') { return NGX_ERROR; } value = value * 10 + (*line - '0'); } if (value < 0) { return NGX_ERROR; } return value; } /* Modified from the ngx_strlcasestrn function in ngx_string.h * Copyright (C) by Igor Sysoev */ u_char * ngx_http_echo_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n) { ngx_uint_t c1, c2; c2 = (ngx_uint_t) *s2++; last -= n; do { do { if (s1 >= last) { return NULL; } c1 = (ngx_uint_t) *s1++; } while (c1 != c2); } while (ngx_strncmp(s1, s2, n) != 0); return --s1; } ngx_int_t ngx_http_echo_post_request_at_head(ngx_http_request_t *r, ngx_http_posted_request_t *pr) { dd_enter(); if (pr == NULL) { pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t)); if (pr == NULL) { return NGX_ERROR; } } pr->request = r; pr->next = r->main->posted_requests; r->main->posted_requests = pr; return NGX_OK; } u_char * ngx_http_echo_rebase_path(ngx_pool_t *pool, u_char *src, size_t osize, size_t *nsize) { u_char *p, *dst; if (osize == 0) { return NULL; } if (src[0] == '/') { /* being an absolute path already, just add a trailing '\0' */ *nsize = osize; dst = ngx_palloc(pool, *nsize + 1); if (dst == NULL) { *nsize = 0; return NULL; } p = ngx_copy(dst, src, osize); *p = '\0'; return dst; } *nsize = ngx_cycle->prefix.len + osize; dst = ngx_palloc(pool, *nsize + 1); if (dst == NULL) { *nsize = 0; return NULL; } p = ngx_copy(dst, ngx_cycle->prefix.data, ngx_cycle->prefix.len); p = ngx_copy(p, src, osize); *p = '\0'; return dst; } ngx_int_t ngx_http_echo_flush_postponed_outputs(ngx_http_request_t *r) { if (r == r->connection->data && r->postponed) { /* notify the downstream postpone filter to flush the postponed * outputs of the current request */ return ngx_http_output_filter(r, NULL); } /* do nothing */ return NGX_OK; } libnginx-mod-http-echo-0.63/src/ngx_http_echo_util.h000066400000000000000000000027751433472572100225730ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef NGX_HTTP_ECHO_UTIL_H #define NGX_HTTP_ECHO_UTIL_H #include "ngx_http_echo_module.h" #define ngx_http_echo_strcmp_const(a, b) \ ngx_strncmp(a, b, sizeof(b) - 1) #define ngx_http_echo_hash_literal(s) \ ngx_http_echo_hash_str((u_char *) s, sizeof(s) - 1) static ngx_inline ngx_uint_t ngx_http_echo_hash_str(u_char *src, size_t n) { ngx_uint_t key; key = 0; while (n--) { key = ngx_hash(key, *src); src++; } return key; } extern ngx_uint_t ngx_http_echo_content_length_hash; ngx_http_echo_ctx_t *ngx_http_echo_create_ctx(ngx_http_request_t *r); ngx_int_t ngx_http_echo_eval_cmd_args(ngx_http_request_t *r, ngx_http_echo_cmd_t *cmd, ngx_array_t *computed_args, ngx_array_t *opts); ngx_int_t ngx_http_echo_send_header_if_needed(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx); ngx_int_t ngx_http_echo_send_chain_link(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_chain_t *cl); ssize_t ngx_http_echo_atosz(u_char *line, size_t n); u_char *ngx_http_echo_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n); ngx_int_t ngx_http_echo_post_request_at_head(ngx_http_request_t *r, ngx_http_posted_request_t *pr); u_char *ngx_http_echo_rebase_path(ngx_pool_t *pool, u_char *src, size_t osize, size_t *nsize); ngx_int_t ngx_http_echo_flush_postponed_outputs(ngx_http_request_t *r); #endif /* NGX_HTTP_ECHO_UTIL_H */ libnginx-mod-http-echo-0.63/src/ngx_http_echo_var.c000066400000000000000000000051751433472572100223760ustar00rootroot00000000000000#ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_var.h" #include "ngx_http_echo_timer.h" #include "ngx_http_echo_request_info.h" #include "ngx_http_echo_foreach.h" static ngx_int_t ngx_http_echo_incr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_http_variable_t ngx_http_echo_variables[] = { { ngx_string("echo_timer_elapsed"), NULL, ngx_http_echo_timer_elapsed_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("echo_request_method"), NULL, ngx_http_echo_request_method_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("echo_cacheable_request_uri"), NULL, ngx_http_echo_cacheable_request_uri_variable, 0, 0, 0 }, { ngx_string("echo_request_uri"), NULL, ngx_http_echo_request_uri_variable, 0, 0, 0 }, { ngx_string("echo_client_request_method"), NULL, ngx_http_echo_client_request_method_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("echo_request_body"), NULL, ngx_http_echo_request_body_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("echo_client_request_headers"), NULL, ngx_http_echo_client_request_headers_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("echo_it"), NULL, ngx_http_echo_it_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("echo_incr"), NULL, ngx_http_echo_incr_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("echo_response_status"), NULL, ngx_http_echo_response_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_null_string, NULL, NULL, 0, 0, 0 } }; ngx_int_t ngx_http_echo_add_variables(ngx_conf_t *cf) { ngx_http_variable_t *var, *v; for (v = ngx_http_echo_variables; v->name.len; v++) { var = ngx_http_add_variable(cf, &v->name, v->flags); if (var == NULL) { return NGX_ERROR; } var->get_handler = v->get_handler; var->data = v->data; } return NGX_OK; } static ngx_int_t ngx_http_echo_incr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_echo_ctx_t *ctx; u_char *p; ctx = ngx_http_get_module_ctx(r->main, ngx_http_echo_module); if (ctx == NULL) { return NGX_ERROR; } ctx->counter++; p = ngx_palloc(r->pool, NGX_INT_T_LEN); if (p == NULL) { return NGX_ERROR; } v->len = ngx_sprintf(p, "%ui", ctx->counter) - p; v->data = p; v->valid = 1; v->not_found = 0; v->no_cacheable = 1; return NGX_OK; } libnginx-mod-http-echo-0.63/src/ngx_http_echo_var.h000066400000000000000000000002331433472572100223710ustar00rootroot00000000000000#ifndef ECHO_VAR_H #define ECHO_VAR_H #include "ngx_http_echo_module.h" ngx_int_t ngx_http_echo_add_variables(ngx_conf_t *cf); #endif /* ECHO_VAR_H */ libnginx-mod-http-echo-0.63/t/000077500000000000000000000000001433472572100161755ustar00rootroot00000000000000libnginx-mod-http-echo-0.63/t/abort-parent.t000066400000000000000000000021131433472572100207550ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::LWP skip_all => 'not working at all'; plan tests => 2 * blocks(); run_tests(); __DATA__ === TEST 1: sanity --- config location /abort { echo hello; echo_flush; echo_location_async '/foo'; echo_location_async '/bar'; echo_location_async '/baz'; echo world; echo_flush; } location /proxy { proxy_pass "http://127.0.0.1:$server_port/sleep?$query_string"; } location /sleep { echo_sleep $arg_sleep; echo $arg_echo; echo_flush; } location /foo { echo_location '/proxy?sleep=1&echo=foo'; #echo_flush; echo_abort_parent; } location /bar { proxy_pass 'http://127.0.0.1:$server_port/sleep_bar'; } location /baz { proxy_pass 'http://127.0.0.1:$server_port/sleep_baz'; } location /sleep_bar { echo_sleep 2; echo bar; } location /sleep_baz { echo_sleep 3; echo baz; } --- request GET /abort --- response_body hello bar libnginx-mod-http-echo-0.63/t/blocking-sleep.t000066400000000000000000000043461433472572100212670ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; plan tests => 2 * blocks(); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: sanity --- config location /echo { echo_blocking_sleep 1; } --- request GET /echo --- response_body === TEST 2: fractional delay --- config location /echo { echo_blocking_sleep 0.01; } --- request GET /echo --- response_body === TEST 3: leading echo --- config location /echo { echo before...; echo_blocking_sleep 0.01; } --- request GET /echo --- response_body before... === TEST 4: trailing echo --- config location /echo { echo_blocking_sleep 0.01; echo after...; } --- request GET /echo --- response_body after... === TEST 5: two echos around sleep --- config location /echo { echo before...; echo_blocking_sleep 0.01; echo after...; } --- request GET /echo --- response_body before... after... === TEST 6: interleaving sleep and echo --- config location /echo { echo 1; echo_blocking_sleep 0.01; echo 2; echo_blocking_sleep 0.01; } --- request GET /echo --- response_body 1 2 === TEST 7: interleaving sleep and echo with echo at the end... --- config location /echo { echo 1; echo_blocking_sleep 0.01; echo 2; echo_blocking_sleep 0.01; echo 3; } --- request GET /echo --- response_body 1 2 3 === TEST 8: flush before sleep we didn't really test the actual effect of "echo_flush" here... merely checks if it croaks if appears. --- config location /flush { echo hi; echo_flush; echo_blocking_sleep 0.01; echo trees; } --- request GET /flush --- response_body hi trees === TEST 9: flush does not increment opcode pointer itself --- config location /flush { echo hi; echo_flush; echo trees; } --- request GET /flush --- response_body hi trees === TEST 10: blocking sleep by variable --- config location ~ ^/sleep/(.+) { echo before...; echo_blocking_sleep $1; echo after...; } --- request GET /sleep/0.01 --- response_body before... after... libnginx-mod-http-echo-0.63/t/echo-after-body.t000066400000000000000000000100241433472572100213270ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (2 * blocks() + 1); no_long_string(); log_level('warn'); #master_on(); #workers(1); run_tests(); __DATA__ === TEST 1: sanity --- http_config postpone_output 1; --- config location /echo { echo_after_body hello; echo world; } --- request GET /echo --- response_body world hello === TEST 2: echo after proxy --- config location /echo { echo_after_body hello; proxy_pass http://127.0.0.1:$server_port$request_uri/more; } location /echo/more { echo world; } --- request GET /echo --- response_body world hello === TEST 3: with variables --- config location /echo { echo_after_body $request_method; echo world; } --- request GET /echo --- response_body world GET === TEST 4: w/o args --- config location /echo { echo_after_body; echo world; } --- request GET /echo --- response_body eval "world\n\n" === TEST 5: order is not important --- config location /reversed { echo world; echo_after_body hello; } --- request GET /reversed --- response_body world hello === TEST 6: multiple echo_after_body instances --- config location /echo { echo_after_body hello; echo_after_body world; echo !; } --- request GET /echo --- response_body ! hello world === TEST 7: multiple echo_after_body instances with multiple echo cmds --- config location /echo { echo_after_body hello; echo_after_body world; echo i; echo say; } --- request GET /echo --- response_body i say hello world === TEST 8: echo-after-body & echo-before-body --- config location /mixed { echo_before_body hello; echo_after_body world; echo_before_body hiya; echo_after_body igor; echo ////////; } --- request GET /mixed --- response_body hello hiya //////// world igor === TEST 9: echo around proxy --- config location /echo { echo_before_body hello; echo_before_body world; #echo $scheme://$host:$server_port$request_uri/more; 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; } --- request GET /echo --- response_body hello world blah hiya igor === TEST 10: with $echo_response_status --- config location /status { echo_after_body "status: $echo_response_status"; return 404; } --- request GET /status --- response_body_like .*404 Not Found.* status: 404$ --- error_code: 404 === TEST 11: in subrequests --- config location /main { echo_location_async /hello; } location /hello { echo_after_body 'world!'; echo 'hello'; } --- request GET /main --- response_body hello world! === TEST 12: echo_after_body + gzip --- config gzip on; gzip_min_length 1; location /main { echo_after_body 'world!'; echo_duplicate 1024 'hello'; } --- request GET /main --- response_body_like hello --- SKIP === TEST 13: echo_after_body + proxy output --- config #gzip on; #gzip_min_length 1; location /main { echo_after_body 'world'; proxy_pass http://127.0.0.1:$server_port/foo; } location /foo { echo_duplicate 10 hello; } --- request GET /main --- response_body_like ^(?:hello){10}world$ === TEST 14: in subrequests (we get last_in_chain set properly) --- config location /main { echo_location_async /hello; } location /hello { echo 'hello'; echo_after_body 'world!'; body_filter_by_lua ' local eof = ngx.arg[2] if eof then print("lua: eof found in body") end '; } --- request GET /main --- response_body hello world! --- log_level: notice --- error_log lua: eof found in body libnginx-mod-http-echo-0.63/t/echo-before-body.t000066400000000000000000000102501433472572100214710ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; plan tests => 2 * blocks(); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: sanity --- config location /echo { echo_before_body hello; echo world; } --- request GET /echo --- response_body hello world === TEST 2: echo before proxy --- config location /echo { echo_before_body hello; proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; } location /echo/more { echo world; } --- request GET /echo --- response_body hello world === TEST 3: with variables --- config location /echo { echo_before_body $request_method; echo world; } --- request GET /echo --- response_body GET world === TEST 4: w/o args --- config location /echo { echo_before_body; echo world; } --- request GET /echo --- response_body eval "\nworld\n" === TEST 5: order is not important --- config location /reversed { echo world; echo_before_body hello; } --- request GET /reversed --- response_body hello world === TEST 6: multiple echo_before_body instances --- config location /echo { echo_before_body hello; echo_before_body world; echo !; } --- request GET /echo --- response_body hello world ! === TEST 7: multiple echo_before_body instances with multiple echo cmds --- config location /echo { echo_before_body hello; echo_before_body world; echo i; echo say; } --- request GET /echo --- response_body hello world i say === TEST 8: with $echo_response_status --- config location /status { echo_before_body "status: $echo_response_status"; return 404; } --- request GET /status --- response_body_like status: 404 .*404 Not Found.*$ --- error_code: 404 === TEST 9: $echo_response_status in echo_before_body in subrequests --- config location /main { echo_location '/status?val=403'; echo_location '/status?val=500'; } location /status { if ($arg_val = 500) { echo_before_body "status: $echo_response_status"; return 500; break; } if ($arg_val = 403) { echo_before_body "status: $echo_response_status"; return 403; break; } return 200; } --- request GET /main --- response_body_like ^status: 403.*?status: 500.*$ === TEST 10: echo -n --- config location /echo { echo_before_body -n hello; echo_before_body -n world; echo ==; } --- request GET /echo --- response_body helloworld== === TEST 11: echo a -n --- config location /echo { echo_before_body a -n hello; echo_before_body b -n world; echo ==; } --- request GET /echo --- response_body a -n hello b -n world == === TEST 12: -n in a var --- config location /echo { set $opt -n; echo_before_body $opt hello; echo_before_body $opt world; echo ==; } --- request GET /echo --- response_body -n hello -n world == === TEST 13: -n only --- config location /echo { echo_before_body -n; echo_before_body -n; echo ==; } --- request GET /echo --- response_body == === TEST 14: -n with an empty string --- config location /echo { echo_before_body -n ""; set $empty ""; echo_before_body -n $empty; echo ==; } --- request GET /echo --- response_body == === TEST 15: -- -n --- config location /echo { echo_before_body -- -n hello; echo_before_body -- -n world; echo ==; } --- request GET /echo --- response_body -n hello -n world == === TEST 16: -n -n --- config location /echo { echo_before_body -n -n hello; echo_before_body -n -n world; echo ==; } --- request GET /echo --- response_body helloworld== === TEST 17: -n -- -n --- config location /echo { echo_before_body -n -- -n hello; echo_before_body -n -- -n world; echo ==; } --- request GET /echo --- response_body -n hello-n world== libnginx-mod-http-echo-0.63/t/echo-duplicate.t000066400000000000000000000022551433472572100212540ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; plan tests => 2 * blocks(); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: sanity --- config location /dup { echo_duplicate 3 a; } --- request GET /dup --- response_body: aaa === TEST 2: abc abc --- config location /dup { echo_duplicate 2 abc; } --- request GET /dup --- response_body: abcabc === TEST 3: big size with underscores --- config location /dup { echo_duplicate 10_000 A; } --- request GET /dup --- response_body eval 'A' x 10_000 === TEST 4: 0 duplicate 0 empty strings --- config location /dup { echo_duplicate 0 ""; } --- request GET /dup --- response_body === TEST 5: 0 duplicate non-empty strings --- config location /dup { echo_duplicate 0 "abc"; } --- request GET /dup --- response_body === TEST 6: duplication of empty strings --- config location /dup { echo_duplicate 2 ""; } --- request GET /dup --- response_body === TEST 7: sanity (HEAD) --- config location /dup { echo_duplicate 3 a; } --- request HEAD /dup --- response_body libnginx-mod-http-echo-0.63/t/echo-timer.t000066400000000000000000000040101433472572100204110ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; plan tests => 2 * blocks(); run_tests(); __DATA__ === TEST 1: timer without explicit reset --- config location /timer { echo_sleep 0.03; echo "elapsed $echo_timer_elapsed sec."; } --- request GET /timer --- response_body_like ^elapsed 0\.0(2[6-9]|3[0-6]) sec\.$ === TEST 2: timer without explicit reset and sleep --- config location /timer { echo "elapsed $echo_timer_elapsed sec."; } --- request GET /timer --- response_body_like ^elapsed 0\.00[0-5] sec\.$ === TEST 3: timing accumulated sleeps --- config location /timer { echo_sleep 0.03; echo_sleep 0.02; echo "elapsed $echo_timer_elapsed sec."; } --- request GET /timer --- response_body_like ^elapsed 0\.0(4[6-9]|5[0-6]) sec\.$ === TEST 4: timer with explicit reset but without sleep --- config location /timer { echo_reset_timer; echo "elapsed $echo_timer_elapsed sec."; } --- request GET /timer --- response_body_like ^elapsed 0\.00[0-5] sec\.$ === TEST 5: reset timer between sleeps --- config location /timer { echo_sleep 0.02; echo "elapsed $echo_timer_elapsed sec."; echo_reset_timer; echo_sleep 0.03; echo "elapsed $echo_timer_elapsed sec."; } --- request GET /timer --- response_body_like ^elapsed 0\.0(1[6-9]|2[0-6]) sec\. elapsed 0\.0(2[6-9]|3[0-6]) sec\.$ === TEST 6: reset timer between blocking sleeps --- config location /timer { echo_blocking_sleep 0.02; echo "elapsed $echo_timer_elapsed sec."; echo_reset_timer; echo_blocking_sleep 0.03; echo "elapsed $echo_timer_elapsed sec."; } --- request GET /timer --- response_body_like ^elapsed 0\.0(1[6-9]|2[0-9]) sec\. elapsed 0\.0(2[6-9]|3[0-6]) sec\.$ === TEST 7: timer without explicit reset --- config location = /timer { return 200 "$echo_timer_elapsed"; } --- request GET /timer --- response_body_like chop ^0(\.0\d*)$ libnginx-mod-http-echo-0.63/t/echo.t000066400000000000000000000144471433472572100173120ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (2 * blocks() + 6); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: sanity --- config location /echo { echo hello; } --- request GET /echo --- response_body hello === TEST 2: multiple args --- config location /echo { echo say hello world; } --- request GET /echo --- response_body say hello world === TEST 3: multiple directive instances --- config location /echo { echo say that; echo hello; echo world !; } --- request GET /echo --- response_body say that hello world ! === TEST 4: echo without arguments --- config location /echo { echo; echo; } --- request GET /echo --- response_body eval "\n\n" === TEST 5: escaped newline --- config location /echo { echo "hello\nworld"; } --- request GET /echo --- response_body hello world === TEST 6: escaped tabs and \r and " wihtin "..." --- config location /echo { echo "i say \"hello\tworld\"\r"; } --- request GET /echo --- response_body eval: "i say \"hello\tworld\"\r\n" === TEST 7: escaped tabs and \r and " in single quotes --- config location /echo { echo 'i say \"hello\tworld\"\r'; } --- request GET /echo --- response_body eval: "i say \"hello\tworld\"\r\n" === TEST 8: escaped tabs and \r and " w/o any quotes --- config location /echo { echo i say \"hello\tworld\"\r; } --- request GET /echo --- response_body eval: "i say \"hello\tworld\"\r\n" === TEST 9: escaping $ As of Nginx 0.8.20, there's still no way to escape the '$' character. --- config location /echo { echo \$; } --- request GET /echo --- response_body $ --- SKIP === TEST 10: XSS --- config location /blah { echo_duplicate 1 "$arg_callback("; echo_location_async "/data?$uri"; echo_duplicate 1 ")"; } location /data { echo_duplicate 1 '{"dog":"$query_string"}'; } --- request GET /blah/9999999.json?callback=ding1111111 --- response_body chomp ding1111111({"dog":"/blah/9999999.json"}) === TEST 11: XSS - filter version --- config location /blah { echo_before_body "$arg_callback("; echo_duplicate 1 '{"dog":"$uri"}'; echo_after_body ")"; } --- request GET /blah/9999999.json?callback=ding1111111 --- response_body ding1111111( {"dog":"/blah/9999999.json"}) === TEST 12: if --- config location /first { echo "before"; echo_location_async /second $request_uri; echo "after"; } location = /second { if ($query_string ~ '([^?]+)') { set $memcached_key $1; # needing this to be keyed on the request_path, not the entire uri echo $memcached_key; } } --- request GET /first/9999999.json?callback=ding1111111 --- response_body before /first/9999999.json after === TEST 13: echo -n --- config location /echo { echo -n hello; echo -n world; } --- request GET /echo --- response_body chop helloworld === TEST 14: echo a -n --- config location /echo { echo a -n hello; echo b -n world; } --- request GET /echo --- response_body a -n hello b -n world === TEST 15: -n in a var --- config location /echo { set $opt -n; echo $opt hello; echo $opt world; } --- request GET /echo --- response_body -n hello -n world === TEST 16: -n only --- config location /echo { echo -n; echo -n; } --- request GET /echo --- response_body chop === TEST 17: -n with an empty string --- config location /echo { echo -n ""; set $empty ""; echo -n $empty; } --- request GET /echo --- response_body chop === TEST 18: -- -n --- config location /echo { echo -- -n hello; echo -- -n world; } --- request GET /echo --- response_body -n hello -n world === TEST 19: -n -n --- config location /echo { echo -n -n hello; echo -n -n world; } --- request GET /echo --- response_body chop helloworld === TEST 20: -n -- -n --- config location /echo { echo -n -- -n hello; echo -n -- -n world; } --- request GET /echo --- response_body chop -n hello-n world === TEST 21: proxy --- config location /main { proxy_pass http://127.0.0.1:$server_port/echo; } location /echo { echo hello; echo world; } --- request GET /main --- response_headers !Content-Length --- response_body hello world === TEST 22: if is evil --- config location /test { set $a 3; set_by_lua $a ' if ngx.var.a == "3" then return 4 end '; echo $a; } --- request GET /test --- response_body 4 --- SKIP === TEST 23: HEAD --- config location /echo { echo hello; echo world; } --- request HEAD /echo --- response_body === TEST 24: POST --- config location /echo { echo hello; echo world; } --- pipelined_requests eval ["POST /echo blah blah", "POST /echo foo bar baz"] --- response_body eval ["hello\nworld\n","hello\nworld\n"] === TEST 25: POST --- config location /echo { echo_sleep 0.001; echo hello; echo world; } --- pipelined_requests eval ["POST /echo blah blah", "POST /echo foo bar baz"] --- response_body eval ["hello\nworld\n","hello\nworld\n"] === TEST 26: empty arg after -n (github issue #33) --- config location = /t { set $empty ""; echo -n $empty hello world; } --- request GET /t --- response_body chop hello world === TEST 27: image filter --- config location = /gif { empty_gif; } location = /t { default_type image/gif; image_filter resize 10 10; set $gif1 ''; set $gif2 ''; rewrite_by_lua ' local res = ngx.location.capture("/gif") local data = res.body ngx.var.gif1 = string.sub(data, 1, #data - 1) ngx.var.gif2 = string.sub(data, #data) '; echo -n $gif1; echo -n $gif2; } --- request GET /t --- stap F(ngx_http_image_header_filter) { println("image header filter") } --- stap_out image header filter --- response_body_like: . libnginx-mod-http-echo-0.63/t/exec.t000066400000000000000000000064511433472572100173140ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (2 * blocks() + 1); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: exec normal location --- config location /main { echo_exec /bar; echo end; } location = /bar { echo "$echo_request_uri:"; echo bar; } --- request GET /main --- response_body /bar: bar === TEST 2: location with args (inlined in uri) --- config location /main { echo_exec /bar?a=32; echo end; } location /bar { echo "a: [$arg_a]"; } --- request GET /main --- response_body a: [32] === TEST 3: location with args (in separate arg) --- config location /main { echo_exec /bar a=56; echo end; } location /bar { echo "a: [$arg_a]"; } --- request GET /main --- response_body a: [56] === TEST 4: exec named location --- config location /main { echo_exec @bar; echo end; } location @bar { echo bar; } --- request GET /main --- response_body bar === TEST 5: query string ignored for named locations --- config location /main { echo_exec @bar?a=32; echo end; } location @bar { echo "a: [$arg_a]"; } --- request GET /main --- response_body a: [] --- error_log querystring a=32 ignored when exec'ing named location @bar === TEST 6: query string ignored for named locations --- config location /foo { echo_exec @bar; } location @bar { echo "uri: [$echo_request_uri]"; } --- request GET /foo --- response_body uri: [/foo] === TEST 7: exec(named location) in subrequests --- config location /entry { echo_location /foo; echo_sleep 0.001; echo_location /foo2; } location /foo { echo_exec @bar; } location /foo2 { echo_exec @bar; } location @bar { proxy_pass http://127.0.0.1:$server_port/bar; } location /bar { echo_sleep 0.01; echo hello; } --- request GET /entry --- response_body hello hello === TEST 8: exec(normal loctions) in subrequests --- config location /entry { echo_location /foo; echo_sleep 0.001; echo_location /foo2; } location /foo { echo_exec /baz; } location /foo2 { echo_exec /baz; } location /baz { proxy_pass http://127.0.0.1:$server_port/bar; } location /bar { echo_sleep 0.01; echo hello; } --- request GET /entry --- response_body hello hello === TEST 9: exec should clear ctx --- config location @bar { echo hello; echo world; echo heh; } location /foo { #echo_sleep 0.001; echo_reset_timer; echo_exec @bar; } --- request GET /foo --- response_body hello world heh === TEST 10: reset ctx --- config location @proxy { rewrite_by_lua return; echo hello; } location /main { rewrite_by_lua return; echo_exec @proxy; } --- request GET /main --- response_body hello === TEST 11: yield before exec --- config location @bar { echo hello; echo world; echo heh; } location /foo { echo_sleep 0.001; echo_exec @bar; } --- request GET /foo --- response_body hello world heh libnginx-mod-http-echo-0.63/t/filter-used.t000066400000000000000000000015331433472572100206070ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (3 * blocks()); no_long_string(); log_level('warn'); #master_on(); #workers(1); run_tests(); __DATA__ === TEST 1: filter indeed used --- http_config postpone_output 1; --- config location /echo { echo_after_body hello; echo world; } --- request GET /echo --- stap F(ngx_http_echo_header_filter) { println("echo header filter called") } --- stap_out echo header filter called --- response_body world hello === TEST 2: filter not used --- http_config postpone_output 1; --- config location /echo { #echo_after_body hello; echo world; } --- request GET /echo --- stap F(ngx_http_echo_header_filter) { println("echo header filter called") } --- stap_out --- response_body world libnginx-mod-http-echo-0.63/t/foreach-split.t000066400000000000000000000116201433472572100211220ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * 2 * blocks(); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: sanity --- config location /main { echo_foreach_split '&' $query_string; echo_location_async $echo_it; echo '/* end */'; echo_end; } location /sub/1.css { echo "body { font-size: 12pt; }"; } location /sub/2.css { echo "table { color: 'red'; }"; } --- request GET /main?/sub/1.css&/sub/2.css --- response_body body { font-size: 12pt; } /* end */ table { color: 'red'; } /* end */ === TEST 2: split in a url argument (echo_location_async) --- config location /main_async { echo_foreach_split ',' $arg_cssfiles; echo_location_async $echo_it; echo_end; } location /foo.css { echo foo; } location /bar.css { echo bar; } location /baz.css { echo baz; } --- request GET /main_async?cssfiles=/foo.css,/bar.css,/baz.css --- response_body foo bar baz === TEST 3: split in a url argument (echo_location) --- config location /main_sync { echo_foreach_split ',' $arg_cssfiles; echo_location $echo_it; echo_end; } location /foo.css { echo foo; } location /bar.css { echo bar; } location /baz.css { echo baz; } --- request GET /main_sync?cssfiles=/foo.css,/bar.css,/baz.css --- response_body foo bar baz --- SKIP === TEST 4: empty loop --- config location /main { echo "start"; echo_foreach_split ',' $arg_cssfiles; echo_end; echo "end"; } --- request GET /main?cssfiles=/foo.css,/bar.css,/baz.css --- response_body start end === TEST 5: trailing delimiter --- config location /main_t { echo_foreach_split ',' $arg_cssfiles; echo_location_async $echo_it; echo_end; } location /foo.css { echo foo; } --- request GET /main_t?cssfiles=/foo.css, --- response_body foo === TEST 6: multi-char delimiter --- config location /main_sleep { echo_foreach_split '-a-' $arg_list; echo $echo_it; echo_end; } --- request GET /main_sleep?list=foo-a-bar-a-baz --- error_code: 500 --- response_body_like: 500 Internal Server Error === TEST 7: multi-char delimiter (the right way) --- config location /main_sleep { echo_foreach_split -- '-a-' $arg_list; echo $echo_it; echo_end; } --- request GET /main_sleep?list=foo-a-bar-a-baz --- response_body foo bar baz === TEST 8: loop with sleep --- config location /main_sleep { echo_foreach_split '-' $arg_list; echo_sleep 0.001; echo $echo_it; echo_end; } --- request GET /main_sleep?list=foo-a-bar-A-baz --- response_body foo a bar A baz === TEST 9: empty --- config location /merge { default_type 'text/javascript'; echo_foreach_split '&' $query_string; echo "/* JS File $echo_it */"; echo_location_async $echo_it; echo; echo_end; } --- request GET /merge --- response_body === TEST 10: single & --- config location /merge { default_type 'text/javascript'; echo_foreach_split '&' $query_string; echo "/* JS File $echo_it */"; echo_location_async $echo_it; echo; echo_end; } --- request GET /merge?& --- response_body === TEST 11: pure &'s --- config location /merge { default_type 'text/javascript'; echo_foreach_split '&' $query_string; echo "/* JS File $echo_it */"; echo_location_async $echo_it; echo; echo_end; } --- request GET /merge?&&& --- response_body === TEST 12: pure & and spaces TODO: needs to uri_decode $echo_it... --- config location /merge { default_type 'text/javascript'; echo_foreach_split '&' $query_string; echo "/* JS File $echo_it */"; echo_location_async $echo_it; echo; echo_end; } --- request GET /merge?&%20&%20& --- response_body --- SKIP === TEST 13: multiple foreach_split --- config location /multi { echo_foreach_split '&' $query_string; echo [$echo_it]; echo_end; echo '...'; echo_foreach_split '-' $query_string; echo [$echo_it]; echo_end; } --- request GET /multi?a-b&c-d --- response_body [a-b] [c-d] ... [a] [b&c] [d] === TEST 14: github issue #2: setting a variable from $echo_it results to crashing --- config location = /getFile { set $filelist "a,b,c"; echo_foreach_split ',' $filelist; set $file $echo_it; echo_subrequest GET '/getFile2' -q 'sha256=$file'; echo_end; } location = /getFile2 { echo "sha256: $arg_sha256"; } --- request GET /getFile --- response_body sha256: sha256: sha256: libnginx-mod-http-echo-0.63/t/gzip.t000066400000000000000000000007531433472572100173400ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => repeat_each() * 2 * blocks(); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: sanity --- config location /gzip { gzip on; gzip_min_length 10; gzip_types text/plain; echo_duplicate 1000 hello; } --- request GET /gzip --- more_headers Accept-Encoding: gzip --- response_headers Content-Encoding: gzip --- timeout: 20 libnginx-mod-http-echo-0.63/t/if.t000066400000000000000000000047541433472572100167720ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; plan tests => 2 * blocks(); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: sanity (hit) --- config location ^~ /if { set $res miss; if ($arg_val ~* '^a') { set $res hit; echo $res; } echo $res; } --- request GET /if?val=abc --- response_body hit === TEST 2: sanity (miss) --- config location ^~ /if { set $res miss; if ($arg_val ~* '^a') { set $res hit; echo $res; } echo $res; } --- request GET /if?val=bcd --- response_body miss === TEST 3: proxy in if (hit) --- config location ^~ /if { set $res miss; if ($arg_val ~* '^a') { set $res hit; proxy_pass $scheme://127.0.0.1:$server_port/foo?res=$res; } proxy_pass $scheme://127.0.0.1:$server_port/foo?res=$res; } location /foo { echo "res = $arg_res"; } --- request GET /if?val=abc --- response_body res = hit === TEST 4: proxy in if (miss) --- config location ^~ /if { set $res miss; if ($arg_val ~* '^a') { set $res hit; proxy_pass $scheme://127.0.0.1:$server_port/foo?res=$res; } proxy_pass $scheme://127.0.0.1:$server_port/foo?res=$res; } location /foo { echo "res = $arg_res"; } --- request GET /if?val=bcd --- response_body res = miss === TEST 5: if too long url (hit) --- config location /foo { if ($request_uri ~ '.{20,}') { echo too long; } echo ok; } --- request GET /foo?a=12345678901234567890 --- response_body too long === TEST 6: if too long url (miss) --- config location /foo { if ($request_uri ~ '.{20,}') { echo too long; } echo ok; } --- request GET /foo?a=1234567890 --- response_body ok === TEST 7: echo should be inherited by if blocks --- config location /foo { if ($uri ~ 'foo') { } echo ok; } --- request GET /foo --- response_body ok === TEST 8: echo_after_body and echo_before_body should be inherited by if blocks --- config location /foo { if ($uri ~ 'foo') { } echo_before_body -n 'hello'; echo_location /comma; echo_after_body 'world'; } location = /comma { internal; echo -n ', '; } --- request GET /foo --- response_body hello, world libnginx-mod-http-echo-0.63/t/incr.t000066400000000000000000000007551433472572100173240ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; plan tests => 2 * blocks(); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: sanity --- config 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"; } --- request GET /main --- response_body main pre: 1 sub: 3 sub: 4 main post: 2 libnginx-mod-http-echo-0.63/t/location-async.t000066400000000000000000000161361433472572100213140ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (2 * blocks() + 1); run_tests(); __DATA__ === TEST 1: sanity --- config location /main { echo_location_async /sub; } location /sub { echo hello; } --- request GET /main --- response_body hello === TEST 2: trailing echo --- config location /main { echo_location_async /sub; echo after subrequest; } location /sub { echo hello; } --- request GET /main --- response_body hello after subrequest === TEST 3: leading echo --- config location /main { echo before subrequest; echo_location_async /sub; } location /sub { echo hello; } --- request GET /main --- response_body before subrequest hello === TEST 4: leading & trailing echo --- config location /main { echo before subrequest; echo_location_async /sub; echo after subrequest; } location /sub { echo hello; } --- request GET /main --- response_body before subrequest hello after subrequest === TEST 5: multiple subrequests --- config location /main { echo before sr 1; echo_location_async /sub; echo after sr 1; echo before sr 2; echo_location_async /sub; echo after sr 2; } location /sub { echo hello; } --- request GET /main --- response_body before sr 1 hello after sr 1 before sr 2 hello after sr 2 === TEST 6: timed multiple subrequests (blocking sleep) --- config location /main { echo_reset_timer; echo_location_async /sub1; echo_location_async /sub2; echo "took $echo_timer_elapsed sec for total."; } location /sub1 { echo_blocking_sleep 0.02; echo hello; } location /sub2 { echo_blocking_sleep 0.01; echo world; } --- request GET /main --- response_body_like ^hello world took 0\.00[0-5] sec for total\.$ === TEST 7: timed multiple subrequests (non-blocking sleep) --- config 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 0.02; echo hello; } location /sub2 { echo_sleep 0.01; echo world; } --- request GET /main --- response_body_like ^hello world took 0\.00[0-5] sec for total\.$ === TEST 8: location with args --- config location /main { echo_location_async /sub 'foo=Foo&bar=Bar'; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body Foo Bar === TEST 9: encoded chars in query strings --- config location /main { echo_location_async /sub 'foo=a%20b&bar=Bar'; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body a%20b Bar === TEST 10: UTF-8 chars in query strings --- config location /main { echo_location_async /sub 'foo=你好'; } location /sub { echo $arg_foo; } --- request GET /main --- response_body 你好 === TEST 11: encoded chars in location url --- config location /main { echo_location_async /sub%31 'foo=Foo&bar=Bar'; } location /sub%31 { echo 'sub%31'; } location /sub1 { echo 'sub1'; } --- request GET /main --- response_body sub1 === TEST 12: querystring in url --- config location /main { echo_location_async /sub?foo=Foo&bar=Bar; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body Foo Bar === TEST 13: querystring in url *AND* an explicit querystring --- config location /main { echo_location_async /sub?foo=Foo&bar=Bar blah=Blah; } location /sub { echo $arg_foo $arg_bar $arg_blah; } --- request GET /main --- response_body Blah === TEST 14: explicit flush in main request flush won't really flush the buffer... --- config location /main_flush { echo 'pre main'; echo_location_async /sub; echo 'post main'; echo_flush; } location /sub { echo_sleep 0.02; echo 'sub'; } --- request GET /main_flush --- response_body pre main sub post main === TEST 15: no varaiable inheritance --- config location /main { echo $echo_cacheable_request_uri; echo_location_async /sub; echo_location_async /sub2; } location /sub { echo $echo_cacheable_request_uri; } location /sub2 { echo $echo_cacheable_request_uri; } --- request GET /main --- response_body /main /sub /sub2 === TEST 16: unsafe uri --- config location /unsafe { echo_location_async '/../foo'; } --- request GET /unsafe --- stap2 F(ngx_http_send_header) { printf("send header on req %p (header sent: %d)\n", $r, $r->header_sent) print_ubacktrace() } --- ignore_response --- error_log echo_location_async sees unsafe uri: "/../foo" --- no_error_log [error] [alert] === TEST 17: access/deny (access phase handlers skipped in subrequests) --- config location /main { echo_location_async /denied; } location /denied { deny all; echo No no no; } --- request GET /main --- error_code: 200 --- response_body No no no === TEST 18: rewrite is honored. --- config location /main { echo_location_async /rewrite; } location /rewrite { rewrite ^ /foo break; echo $uri; } --- request GET /main --- response_body /foo === TEST 19: let subrequest to read the main request's request body --- SKIP --- config location /main { echo_location_async /sub; } location /sub { echo_read_request_body; echo_request_body; } --- request POST /main hello, body! --- response_body chomp hello, body! === TEST 20: leading subrequest & echo_before_body --- config location /main { echo_before_body hello; echo_location_async /foo; } location /foo { echo world; } --- request GET /main --- response_body hello world === TEST 21: leading subrequest & xss --- config location /main { default_type 'application/json'; xss_get on; xss_callback_arg c; echo_location_async /foo; } location /foo { echo -n world; } --- request GET /main?c=hi --- response_body chop hi(world); === TEST 22: multiple leading subrequest & xss --- config location /main { default_type 'application/json'; xss_get on; xss_callback_arg c; echo_location_async /foo; echo_location_async /bar; } location /foo { echo -n world; } location /bar { echo -n ' people'; } --- request GET /main?c=hi --- response_body chop hi(world people); === TEST 23: sanity (HEAD) --- config location /main { echo_location_async /sub; echo_location_async /sub; } location /sub { echo hello; } --- request HEAD /main --- response_body libnginx-mod-http-echo-0.63/t/location.t000066400000000000000000000215541433472572100202010ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (2 * blocks() + 2); #$Test::Nginx::LWP::LogLevel = 'debug'; #no_diff(); run_tests(); __DATA__ === TEST 1: sanity --- config location /main { echo_location /sub; } location /sub { echo hello; } --- request GET /main --- response_body hello === TEST 2: sanity with proxy in the middle --- config location /main { echo_location /proxy; } location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/sub; } location /sub { echo hello; } --- request GET /main --- response_body hello === TEST 3: trailing echo --- config location /main { echo_location /sub; echo after subrequest; } location /sub { echo hello; } --- request GET /main --- response_body hello after subrequest === TEST 4: leading echo --- config location /main { echo before subrequest; echo_location /sub; } location /sub { echo hello; } --- request GET /main --- response_body before subrequest hello === TEST 5: leading & trailing echo --- config location /main { echo before subrequest; echo_location /sub; echo after subrequest; } location /sub { echo hello; } --- request GET /main --- response_body before subrequest hello after subrequest === TEST 6: multiple subrequests --- config location /main { echo before sr 1; echo_location /sub; echo after sr 1; echo before sr 2; echo_location /sub; echo after sr 2; } location /sub { echo hello; } --- request GET /main --- response_body before sr 1 hello after sr 1 before sr 2 hello after sr 2 === TEST 7: timed multiple subrequests (blocking sleep) --- config location /main { echo_reset_timer; echo_location /sub1; echo_location /sub2; echo "took $echo_timer_elapsed sec for total."; } location /sub1 { echo_blocking_sleep 0.02; echo hello; } location /sub2 { echo_blocking_sleep 0.01; echo world; } --- request GET /main --- response_body_like ^hello world took 0\.0(?:2[5-9]|3[0-6]) sec for total\.$ === TEST 8: timed multiple subrequests (non-blocking sleep) --- config location /main { echo_reset_timer; echo_location /sub1; echo_location /sub2; echo "took $echo_timer_elapsed sec for total."; } location /sub1 { echo_sleep 0.02; echo hello; } location /sub2 { echo_sleep 0.01; echo world; } --- request GET /main --- response_body_like ^hello world took 0\.0(?:2[5-9]|3[0-6]) sec for total\.$ === TEST 9: location with args --- config location /main { echo_location /sub 'foo=Foo&bar=Bar'; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body Foo Bar === TEST 10: chained subrequests --- config location /main { echo 'pre main'; echo_location /sub; echo 'post main'; } location /sub { echo 'pre sub'; echo_location /subsub; echo 'post sub'; } location /subsub { echo 'subsub'; } --- request GET /main --- response_body pre main pre sub subsub post sub post main === TEST 11: chained subrequests using named locations as of 0.8.20, ngx_http_subrequest still does not support named location. sigh. this case is a TODO. --- config location /main { echo 'pre main'; echo_location @sub; echo 'post main'; } location @sub { echo 'pre sub'; echo_location @subsub; echo 'post sub'; } location @subsub { echo 'subsub'; } --- request GET /main --- response_body pre main pre sub subsub post sub post main --- SKIP === TEST 12: explicit flush in main request --- config location /main { echo 'pre main'; echo_location /sub; echo 'post main'; echo_flush; } location /sub { echo_sleep 0.02; echo 'sub'; } --- request GET /main --- response_body pre main sub post main === TEST 13: no varaiable inheritance --- config location /main { echo $echo_cacheable_request_uri; echo_location /sub; echo_location /sub2; } location /sub { echo $echo_cacheable_request_uri; } location /sub2 { echo $echo_cacheable_request_uri; } --- request GET /main --- response_body /main /sub /sub2 === TEST 14: unsafe uri --- config location /unsafe { echo_location '/../foo'; } --- request GET /unsafe --- ignore_response --- error_log echo_location sees unsafe uri: "/../foo" --- no_error_log [error] [alert] === TEST 15: querystring in url --- config location /main { echo_location /sub?foo=Foo&bar=Bar; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body Foo Bar === TEST 16: querystring in url *AND* an explicit querystring --- config location /main { echo_location /sub?foo=Foo&bar=Bar blah=Blah; } location /sub { echo $arg_foo $arg_bar $arg_blah; } --- request GET /main --- response_body Blah === TEST 17: let subrequest to read the main request's request body --- SKIP --- config location /main { echo_location /sub; } location /sub { echo_read_request_body; echo_request_body; } --- request POST /main hello, body! --- response_body chomp hello, body! === TEST 18: sleep after location --- config location /main { echo_location /sub; echo_sleep 0.001; echo_location /sub; } location /sub { echo_sleep 0.001; echo sub; } --- request GET /main --- response_body sub sub --- skip_nginx: 2: < 0.8.11 === TEST 19: deep nested echo_location/echo_location_async --- config location /main { echo_location /bar; echo_location_async /bar; echo_location_async /bar; echo_location /group; echo_location_async /group; } location /group { echo_location /bar; echo_location_async /bar; } location /bar { #echo_sleep 0.001; echo $echo_incr; } --- request GET /main --- response_body 1 2 3 4 5 6 7 --- timeout: 2 === TEST 20: deep nested echo_location/echo_location_async (with sleep) --- config location /main { echo_location /bar; echo_location_async /bar; echo_location_async /bar; echo_location /group; echo_location_async /group; } location /group { echo_location /baz; echo_location_async /bah; } location ~ '^/ba[rzh]' { echo_sleep 0.001; echo $echo_incr; } --- request GET /main --- response_body 1 2 3 4 5 6 7 --- timeout: 2 === TEST 21: deep nested echo_location (with sleep) --- config location /main { echo_location /bar; echo_location /bar; echo_location /bar; echo_location /group; echo_location /group; } location /group { echo_location /bar; echo_location /bar; } location /incr { echo_sleep 0.001; echo $echo_incr; } location /bar { proxy_pass $scheme://127.0.0.1:$server_port/incr; } --- request GET /main --- response_body 1 1 1 1 1 1 1 --- timeout: 5 --- no_error_log [error] === TEST 22: leading subrequest & echo_before_body --- config location /main { echo_before_body hello; echo_location /foo; } location /foo { echo world; } --- request GET /main --- response_body hello world === TEST 23: leading subrequest & xss --- config location /main { default_type 'application/json'; xss_get on; xss_callback_arg c; echo_location /foo; } location /foo { echo -n world; } --- request GET /main?c=hi --- response_body chop hi(world); === TEST 24: multiple leading subrequest & xss --- config location /main { default_type 'application/json'; xss_get on; xss_callback_arg c; echo_location /foo; echo_location /bar; } location /main2 { content_by_lua ' local res = ngx.location.capture("/foo") local res2 = ngx.location.capture("/bar") ngx.say(res.body) ngx.say(res2.body) '; } location /foo { echo -n world; } location /bar { echo -n ' people'; } --- request GET /main?c=hi --- response_body chop hi(world people); === TEST 25: sanity (HEAD) --- config location /main { echo_location /sub; echo_location /sub; } location /sub { echo hello; } --- request HEAD /main --- response_body libnginx-mod-http-echo-0.63/t/mixed.t000066400000000000000000000024641433472572100174760ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; plan tests => 2 * blocks(); run_tests(); __DATA__ === TEST 1: echo before echo_client_request_headers --- config location /echo { echo "headers:"; echo -n $echo_client_request_headers; } --- request GET /echo --- response_body eval "headers: GET /echo HTTP/1.1\r Host: localhost\r Connection: close\r \r " === TEST 2: echo_client_request_headers before echo --- config location /echo { echo -n $echo_client_request_headers; echo "...these are the headers"; } --- request GET /echo --- response_body eval "GET /echo HTTP/1.1\r Host: localhost\r Connection: close\r \r ...these are the headers " === TEST 3: echo & headers & echo --- config location /echo { echo "headers are"; echo -n $echo_client_request_headers; echo "...these are the headers"; } --- request GET /echo --- response_body eval "headers are GET /echo HTTP/1.1\r Host: localhost\r Connection: close\r \r ...these are the headers " === TEST 4: mixed with echo_duplicate --- config location /mixed { echo hello; echo_duplicate 2 ---; echo_duplicate 1 ' END '; echo_duplicate 2 ---; echo; } --- request GET /mixed --- response_body hello ------ END ------ libnginx-mod-http-echo-0.63/t/request-body.t000066400000000000000000000020031433472572100210000ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * 2 * blocks(); run_tests(); __DATA__ === TEST 1: big client body buffered into temp files --- config location /echo { client_body_buffer_size 1k; echo_read_request_body; echo_request_body; } --- request eval "POST /echo " . 'a' x 4096 . 'end'; --- response_body eval 'a' x 4096 . 'end' === TEST 2: in memory request body (trailing echo) --- config location /echo { client_body_buffer_size 1k; echo_read_request_body; echo_request_body; echo done; } --- request POST /echo hello world --- response_body hello worlddone === TEST 3: big client body buffered into temp files (trailing echo) --- config location /echo { client_body_buffer_size 1k; echo_read_request_body; echo_request_body; echo done; } --- request eval "POST /echo " . 'a' x 4096 . "end\n"; --- response_body eval 'a' x 4096 . "enddone\n" libnginx-mod-http-echo-0.63/t/request-info.t000066400000000000000000000362751433472572100210200ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (3 * blocks() + 14); run_tests(); __DATA__ === TEST 1: standalone directive --- config location /echo { echo -n $echo_client_request_headers; } --- request GET /echo --- response_body eval "GET /echo HTTP/1.1\r Host: localhost\r Connection: close\r \r " --- no_error_log [error] === TEST 2: multiple instances --- config location /echo { echo -n $echo_client_request_headers; echo -n $echo_client_request_headers; } --- request GET /echo --- response_body eval "GET /echo HTTP/1.1\r Host: localhost\r Connection: close\r \r GET /echo HTTP/1.1\r Host: localhost\r Connection: close\r \r " --- no_error_log [error] === TEST 3: does not explicitly request_body --- config location /echo { echo [$echo_request_body]; } --- request POST /echo body here heh --- response_body [] --- no_error_log [error] === TEST 4: let proxy read request_body --- config location /echo { echo_before_body [$echo_request_body]; proxy_pass $scheme://127.0.0.1:$server_port/blah; } location /blah { echo_duplicate 0 ''; } --- request POST /echo body here heh --- response_body [body here heh] --- no_error_log [error] === TEST 5: use echo_read_request_body to read it! --- config location /echo { echo_read_request_body; echo [$echo_request_body]; } --- request POST /echo body here heh --- response_body [body here heh] --- no_error_log [error] === TEST 6: how about sleep after that? --- config location /echo { echo_read_request_body; echo_sleep 0.002; echo [$echo_request_body]; } --- request POST /echo body here heh --- response_body [body here heh] --- no_error_log [error] === TEST 7: echo back the whole client request --- config # echo back the client request location /echoback { echo -n $echo_client_request_headers; echo_read_request_body; echo $echo_request_body; } --- request POST /echoback body here haha --- response_body eval "POST /echoback HTTP/1.1\r Host: localhost\r Connection: close\r Content-Length: 14\r \r body here haha " --- no_error_log [error] === TEST 8: echo_request_body --- config location /body { client_body_buffer_size 5; echo_read_request_body; echo "[$echo_request_body]"; echo_request_body; } --- request eval "POST /body " . ('a' x 2048) . "b" --- response_body eval "[]\n" . ('a' x 2048) . "b" --- no_error_log [error] === TEST 9: $echo_response_status in content handler --- config location /status { echo "status: $echo_response_status"; } --- request GET /status --- response_body status: --- no_error_log [error] === TEST 10: echo_request_body (empty body) --- config location /body { echo_read_request_body; echo_request_body; } location /main { proxy_pass http://127.0.0.1:$server_port/body; } --- request eval "POST /main" --- response_body eval "" --- no_error_log [error] === TEST 11: small header --- config location /t { echo -n $echo_client_request_headers; } --- request GET /t --- response_body eval qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r \r } --- no_error_log [error] --- no_error_log [error] === TEST 12: large header --- config client_header_buffer_size 10; large_client_header_buffers 30 561; location /t { echo -n $echo_client_request_headers; } --- request GET /t --- more_headers eval CORE::join "\n", map { "Header$_: value-$_" } 1..512 --- response_body eval qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r } .(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n" --- no_error_log [error] === TEST 13: small header, with leading CRLF --- config location /t { echo -n $echo_client_request_headers; } --- raw_request eval "\r\nGET /t HTTP/1.1\r Host: localhost\r Connection: close\r \r " --- response_body eval qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r \r } --- no_error_log [error] === TEST 14: large header, with leading CRLF --- config client_header_buffer_size 10; large_client_header_buffers 30 561; location /t { echo -n $echo_client_request_headers; } --- raw_request eval "\r\nGET /t HTTP/1.1\r Host: localhost\r Connection: close\r ". (CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n" --- response_body eval qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r } .(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n" --- no_error_log [error] === TEST 15: small header, pipelined --- config location /t { echo -n $echo_client_request_headers; } --- pipelined_requests eval ["GET /t", "GET /th"] --- more_headers Foo: bar --- response_body eval [qq{GET /t HTTP/1.1\r Host: localhost\r Connection: keep-alive\r Foo: bar\r \r }, qq{GET /th HTTP/1.1\r Host: localhost\r Connection: close\r Foo: bar\r \r }] --- no_error_log [error] === TEST 16: large header, pipelined --- config client_header_buffer_size 10; large_client_header_buffers 30 561; location /t { echo -n $echo_client_request_headers; } --- pipelined_requests eval ["GET /t", "GET /t"] --- more_headers eval CORE::join "\n", map { "Header$_: value-$_" } 1..512 --- response_body eval my $headers = (CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\n\r\n"; [qq{GET /t HTTP/1.1\r Host: localhost\r Connection: keep-alive\r $headers}, qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r $headers}] --- no_error_log [error] === TEST 17: small header, multi-line header multi-line header is not supported since 1.21 --- config location /t { echo -n $echo_client_request_headers; } --- raw_request eval "GET /t HTTP/1.1\r Host: localhost\r Connection: close\r Foo: bar baz\r blah\r \r " --- response_body eval qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r Foo: bar baz\r blah\r \r } --- no_error_log [error] --- skip_nginx 3: >= 1.21.1 === TEST 18: large header, multi-line header multi-line header is not supported since 1.21 --- config client_header_buffer_size 10; large_client_header_buffers 50 567; location /t { echo -n $echo_client_request_headers; } --- raw_request eval my $headers = (CORE::join "\r\n", map { "Header$_: value-$_\r\n hello $_ world blah blah" } 1..512) . "\r\n\r\n"; qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r $headers} --- response_body eval qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r } .(CORE::join "\r\n", map { "Header$_: value-$_\r\n hello $_ world blah blah" } 1..512) . "\r\n\r\n" --- no_error_log [error] --- skip_nginx 3: >= 1.21.1 === TEST 19: small header (POST body) --- config location /t { echo_read_request_body; echo -n $echo_client_request_headers; } --- request POST /t hello --- response_body eval qq{POST /t HTTP/1.1\r Host: localhost\r Connection: close\r Content-Length: 5\r \r } --- no_error_log [error] === TEST 20: small header (POST body) - in subrequests (location) --- config location /t { echo -n $echo_client_request_headers; } location /main { echo_location /t; } --- request POST /main hello --- response_body eval qq{POST /main HTTP/1.1\r Host: localhost\r Connection: close\r Content-Length: 5\r \r } --- no_error_log [error] === TEST 21: large header (POST body) --- config client_header_buffer_size 10; large_client_header_buffers 30 561; location /t { echo_read_request_body; echo -n $echo_client_request_headers; } --- request POST /t hello --- more_headers eval CORE::join"\n", map { "Header$_: value-$_" } 1..512 --- response_body eval qq{POST /t HTTP/1.1\r Host: localhost\r Connection: close\r } .(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\nContent-Length: 5\r\n\r\n" --- no_error_log [error] --- timeout: 5 === TEST 22: large header (POST body) - in subrequests --- config client_header_buffer_size 10; large_client_header_buffers 30 561; location /t { echo_read_request_body; echo -n $echo_client_request_headers; } location /main { echo_location /t; } --- request POST /main hello --- more_headers eval CORE::join"\n", map { "Header$_: value-$_" } 1..512 --- response_body eval qq{POST /main HTTP/1.1\r Host: localhost\r Connection: close\r } .(CORE::join "\r\n", map { "Header$_: value-$_" } 1..512) . "\r\nContent-Length: 5\r\n\r\n" --- no_error_log [error] --- timeout: 5 === TEST 23: raw headers - the default header buffer can hold the request line, but not the header entries --- config location /t { echo_read_request_body; echo -n $echo_client_request_headers; } --- request GET /t --- more_headers eval my $s = "User-Agent: curl\nBah: bah\n"; $s .= "Accept: */*\n"; $s .= "Cookie: " . "C" x 1200 . "\n"; $s --- response_body eval "GET /t HTTP/1.1\r Host: localhost\r Connection: close\r User-Agent: curl\r Bah: bah\r Accept: */*\r Cookie: " . ("C" x 1200) . "\r\n\r\n" --- no_error_log [error] === TEST 24: small header (POST body) - in subrequests (location_async) --- config location /t { echo -n $echo_client_request_headers; } location /main { echo_location_async /t; } --- request POST /main hello --- response_body eval qq{POST /main HTTP/1.1\r Host: localhost\r Connection: close\r Content-Length: 5\r \r } --- no_error_log [error] === TEST 25: small header (POST body) - in subrequests (subrequest) --- config location /t { echo -n $echo_client_request_headers; } location /main { echo_subrequest GET /t; } --- request POST /main hello --- response_body eval qq{POST /main HTTP/1.1\r Host: localhost\r Connection: close\r Content-Length: 5\r \r } --- no_error_log [error] === TEST 26: small header (POST body) - in subrequests (subrequest_async) --- config location /t { echo -n $echo_client_request_headers; } location /main { echo_subrequest_async GET /t; } --- request POST /main hello --- response_body eval qq{POST /main HTTP/1.1\r Host: localhost\r Connection: close\r Content-Length: 5\r \r } --- no_error_log [error] === TEST 27: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers --- config location = /t { proxy_buffering off; proxy_pass http://127.0.0.1:$server_port/bad; proxy_intercept_errors on; error_page 500 = /500; } location = /bad { return 500; } location = /500 { echo -n $echo_client_request_headers; } --- request GET /t --- response_body eval "GET /t HTTP/1.1\r Host: localhost\r Connection: close\r \r " --- no_error_log [error] === TEST 28: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers (exclusive LF in the request data) --- config location = /t { proxy_buffering off; proxy_pass http://127.0.0.1:$server_port/bad; proxy_intercept_errors on; error_page 500 = /500; } location = /bad { return 500; } location = /500 { internal; echo -n $echo_client_request_headers; } --- raw_request eval "GET /t HTTP/1.1 Host: localhost Connection: close Content-Length: 5 hello" --- response_body eval "GET /t HTTP/1.1 Host: localhost Connection: close Content-Length: 5 " --- no_error_log [error] === TEST 29: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers (mixed LF and CRLF in the request data) --- config location = /t { proxy_buffering off; proxy_pass http://127.0.0.1:$server_port/bad; proxy_intercept_errors on; error_page 500 = /500; } location = /bad { return 500; } location = /500 { internal; echo -n $echo_client_request_headers; } --- raw_request eval "GET /t HTTP/1.1\r Host: localhost Connection: close\r Content-Length: 5\r hello" --- response_body eval "GET /t HTTP/1.1\r Host: localhost Connection: close\r Content-Length: 5\r " --- no_error_log [error] === TEST 30: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers (another way of mixing LF and CRLF in the request data) --- config location = /t { proxy_buffering off; proxy_pass http://127.0.0.1:$server_port/bad; proxy_intercept_errors on; error_page 500 = /500; } location = /bad { return 500; } location = /500 { internal; echo -n $echo_client_request_headers; } --- raw_request eval "GET /t HTTP/1.1\r Host: localhost Connection: close\r Content-Length: 5 \r hello" --- response_body eval "GET /t HTTP/1.1\r Host: localhost Connection: close\r Content-Length: 5 \r " --- no_error_log [error] === TEST 31: two pipelined requests with large headers --- config client_header_buffer_size 10; large_client_header_buffers 3 5610; location /t { echo -n $echo_client_request_headers; } --- pipelined_requests eval ["GET /t", "GET /t"] --- more_headers eval CORE::join "\n", map { "Header$_: value-$_" } 1..585 --- response_body eval [qq{GET /t HTTP/1.1\r Host: localhost\r Connection: keep-alive\r } .(CORE::join "\r\n", map { "Header$_: value-$_" } 1..585) . "\r\n\r\n", qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r } .(CORE::join "\r\n", map { "Header$_: value-$_" } 1..585) . "\r\n\r\n", , ] --- no_error_log [error] --- timeout: 5 === TEST 32: a request with large header and a smaller pipelined request following --- config client_header_buffer_size 10; large_client_header_buffers 2 1921; location /t { echo -n $echo_client_request_headers; } --- pipelined_requests eval ["GET /t", "GET /t"] --- more_headers eval [CORE::join("\n", map { "Header$_: value-$_" } 1..170), "Foo: bar\n"] --- response_body eval [qq{GET /t HTTP/1.1\r Host: localhost\r Connection: keep-alive\r } .(CORE::join "\r\n", map { "Header$_: value-$_" } 1..170) . "\r\n\r\n", qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r Foo: bar\r \r }, ] --- no_error_log [error] --- timeout: 5 === TEST 33: a request with large header and a smaller pipelined request following --- config client_header_buffer_size 10; large_client_header_buffers 2 1921; location /t { echo -n $echo_client_request_headers; } --- pipelined_requests eval ["GET /t", "GET /t" . ("a" x 512)] --- more_headers eval [CORE::join("\n", map { "Header$_: value-$_" } 1..170), "Foo: bar\n"] --- response_body eval [qq{GET /t HTTP/1.1\r Host: localhost\r Connection: keep-alive\r } .(CORE::join "\r\n", map { "Header$_: value-$_" } 1..170) . "\r\n\r\n", qq{GET /t} . ("a" x 512) . qq{ HTTP/1.1\r Host: localhost\r Connection: close\r Foo: bar\r \r }, ] --- no_error_log [error] --- timeout: 5 === TEST 34: invalid header line started with whitespace since nginx 1.21.1 --- config client_header_buffer_size 10; large_client_header_buffers 50 567; location /t { echo -n $echo_client_request_headers; } --- raw_request eval my $headers = (CORE::join "\r\n", map { "Header$_: value-$_\r\n hello $_ world blah blah" } 1..512) . "\r\n\r\n"; qq{GET /t HTTP/1.1\r Host: localhost\r Connection: close\r $headers} --- error_code: 400 --- no_error_log [error] --- skip_nginx 2: < 1.21.1 libnginx-mod-http-echo-0.63/t/sleep.t000066400000000000000000000054601433472572100174770ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; plan tests => 2 * blocks(); run_tests(); __DATA__ === TEST 1: sanity --- config location /echo { echo_sleep 1; } --- request GET /echo --- response_body === TEST 2: fractional delay --- config location /echo { echo_sleep 0.01; } --- request GET /echo --- response_body === TEST 3: leading echo --- config location /echo { echo before...; echo_sleep 0.01; } --- request GET /echo --- response_body before... === TEST 4: trailing echo --- config location /echo { echo_sleep 0.01; echo after...; } --- request GET /echo --- response_body after... === TEST 5: two echos around sleep --- config location /echo { echo before...; echo_sleep 0.01; echo after...; } --- request GET /echo --- response_body before... after... === TEST 6: interleaving sleep and echo --- config location /echo { echo 1; echo_sleep 0.01; echo 2; echo_sleep 0.01; } --- request GET /echo --- response_body 1 2 === TEST 7: interleaving sleep and echo with echo at the end... --- config location /echo { echo 1; echo_sleep 0.01; echo 2; echo_sleep 0.01; echo 3; } --- request GET /echo --- response_body 1 2 3 === TEST 8: flush before sleep we didn't really test the actual effect of "echo_flush" here... merely checks if it croaks if appears. --- config location /flush { echo hi; echo_flush; echo_sleep 0.01; echo trees; } --- request GET /flush --- response_body hi trees === TEST 9: flush does not increment opcode pointer itself --- config location /flush { echo hi; echo_flush; echo trees; } --- request GET /flush --- response_body hi trees === TEST 10: sleep through a proxy this reveals a bug in v0.19 and the bug is fixed in v0.20. --- config location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/entry'; } location /entry { echo_sleep 0.001; echo done; } --- request GET /proxy --- response_body_like done === TEST 11: abnormally quit --- config location /quit { echo before; echo_flush; echo_sleep 1; echo after; } --- request GET /quit --- response_body before after === TEST 12: two echos around sleep (HEAD) --- config location /echo { echo before...; echo_sleep 0.01; echo after...; } --- request HEAD /echo --- response_body === TEST 13: sleep by variable --- config location ~ ^/sleep/(.+) { echo before...; echo_sleep $1; echo after...; } --- request GET /sleep/0.01 --- response_body before... after... libnginx-mod-http-echo-0.63/t/status.t000066400000000000000000000036161433472572100177130ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => repeat_each() * (2 * blocks()); #$Test::Nginx::LWP::LogLevel = 'debug'; run_tests(); __DATA__ === TEST 1: 200 --- config location /echo { echo_status 200; echo hello; } --- request GET /echo --- response_body hello --- error_code: 200 === TEST 2: if location (200) --- config location /echo { set $true 1; if ($true) { } echo_status 200; echo hello; } --- request GET /echo --- response_body hello --- error_code: 200 === TEST 3: 404 --- config location /echo { echo_status 404; echo hello; } --- request GET /echo --- response_body hello --- error_code: 404 === TEST 4: if location (404) --- config location /echo { set $true 1; if ($true) { } echo_status 404; echo hello; } --- request GET /echo --- response_body hello --- error_code: 404 === TEST 5: 500 --- config location /echo { echo_status 500; echo hello; } --- request GET /echo --- response_body hello --- error_code: 500 === TEST 6: if location (500) --- config location /echo { set $true 1; if ($true) { } echo_status 500; echo hello; } --- request GET /echo --- response_body hello --- error_code: 500 === TEST 7: if location (500) no inherit --- config location /echo { set $true 1; if ($true) { echo_status 503; } echo_status 500; echo hello; } --- request GET /echo --- response_body hello --- error_code: 503 === TEST 8: subrequest --- config location /echo { echo_location /sub; echo_status 503; } location /sub { echo blah blah; } --- request GET /echo --- response_body blah blah --- error_code: 503 libnginx-mod-http-echo-0.63/t/subrequest-async.t000066400000000000000000000260361433472572100217060ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (blocks() * 2 + 1); #$Test::Nginx::LWP::LogLevel = 'debug'; $ENV{TEST_NGINX_HTML_DIR} = html_dir; run_tests(); __DATA__ === TEST 1: sanity - GET --- config location /main { echo_subrequest_async GET /sub; } location /sub { echo "sub method: $echo_request_method"; echo "main method: $echo_client_request_method"; } --- request GET /main --- response_body sub method: GET main method: GET === TEST 2: sanity - DELETE --- config location /main { echo_subrequest_async DELETE /sub; } location /sub { echo "sub method: $echo_request_method"; echo "main method: $echo_client_request_method"; } --- request GET /main --- response_body sub method: DELETE main method: GET === TEST 3: trailing echo --- config location /main { echo_subrequest_async GET /sub; echo after subrequest; } location /sub { echo hello; } --- request GET /main --- response_body hello after subrequest === TEST 4: leading echo --- config location /main { echo before subrequest; echo_subrequest_async GET /sub; } location /sub { echo hello; } --- request GET /main --- response_body before subrequest hello === TEST 5: leading & trailing echo --- config location /main { echo before subrequest; echo_subrequest_async GET /sub; echo after subrequest; } location /sub { echo hello; } --- request GET /main --- response_body before subrequest hello after subrequest === TEST 6: multiple subrequests --- config location /main { echo before sr 1; echo_subrequest_async GET /sub; echo after sr 1; echo before sr 2; echo_subrequest_async GET /sub; echo after sr 2; } location /sub { echo hello; } --- request GET /main --- response_body before sr 1 hello after sr 1 before sr 2 hello after sr 2 === TEST 7: timed multiple subrequests (blocking sleep) --- config location /main { echo_reset_timer; echo_subrequest_async GET /sub1; echo_subrequest_async GET /sub2; echo "took $echo_timer_elapsed sec for total."; } location /sub1 { echo_blocking_sleep 0.02; echo hello; } location /sub2 { echo_blocking_sleep 0.01; echo world; } --- request GET /main --- response_body_like ^hello world took 0\.00[0-5] sec for total\.$ === TEST 8: timed multiple subrequests (non-blocking sleep) --- config location /main { echo_reset_timer; echo_subrequest_async GET /sub1; echo_subrequest_async GET /sub2; echo "took $echo_timer_elapsed sec for total."; } location /sub1 { echo_sleep 0.02; echo hello; } location /sub2 { echo_sleep 0.01; echo world; } --- request GET /main --- response_body_like ^hello world took 0\.00[0-5] sec for total\.$ === TEST 9: location with args --- config location /main { echo_subrequest_async GET /sub -q 'foo=Foo&bar=Bar'; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body Foo Bar === TEST 10: encoded chars in query strings --- config location /main { echo_subrequest_async GET /sub -q 'foo=a%20b&bar=Bar'; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body a%20b Bar === TEST 11: UTF-8 chars in query strings --- config location /main { echo_subrequest_async GET /sub -q 'foo=你好'; } location /sub { echo $arg_foo; } --- request GET /main --- response_body 你好 === TEST 12: encoded chars in location url --- config location /main { echo_subrequest_async GET /sub%31 -q 'foo=Foo&bar=Bar'; } location /sub%31 { echo 'sub%31'; } location /sub1 { echo 'sub1'; } --- request GET /main --- response_body sub1 === TEST 13: querystring in url --- config location /main { echo_subrequest_async GET /sub?foo=Foo&bar=Bar; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body Foo Bar === TEST 14: querystring in url *AND* an explicit querystring --- config location /main { echo_subrequest_async GET /sub?foo=Foo&bar=Bar -q blah=Blah; } location /sub { echo $arg_foo $arg_bar $arg_blah; } --- request GET /main --- response_body Blah === TEST 15: explicit flush in main request flush won't really flush the buffer... --- config location /main_flush { echo 'pre main'; echo_subrequest_async GET /sub; echo 'post main'; echo_flush; } location /sub { echo_sleep 0.02; echo 'sub'; } --- request GET /main_flush --- response_body pre main sub post main === TEST 16: POST subrequest with body (with proxy in the middle) and without read body explicitly --- config location /main { echo_subrequest_async POST /proxy -b 'hello, world'; } location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/sub; } location /sub { echo "sub method: $echo_request_method."; # we need to read body explicitly here...or $echo_request_body # will evaluate to empty ("") echo "sub body: $echo_request_body."; } --- request GET /main --- response_body sub method: POST. sub body: . === TEST 17: POST subrequest with body (with proxy in the middle) and read body explicitly --- config location /main { echo_subrequest_async POST /proxy -b 'hello, world'; } location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/sub; } location /sub { echo "sub method: $echo_request_method."; # we need to read body explicitly here...or $echo_request_body # will evaluate to empty ("") echo_read_request_body; echo "sub body: $echo_request_body."; } --- request GET /main --- response_body sub method: POST. sub body: hello, world. === TEST 18: multiple subrequests --- config 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 '///'; } --- request GET /multi --- response_body querystring: foo=Foo method: POST body: hi content length: 2 /// querystring: bar=Bar method: PUT body: hello content length: 5 /// === TEST 19: no varaiable inheritance --- config location /main { echo $echo_cacheable_request_uri; echo_subrequest_async GET /sub; echo_subrequest_async GET /sub2; } location /sub { echo $echo_cacheable_request_uri; } location /sub2 { echo $echo_cacheable_request_uri; } --- request GET /main --- response_body /main /sub /sub2 === TEST 20: unsafe uri --- config location /unsafe { echo_subrequest_async GET '/../foo'; } --- request GET /unsafe --- ignore_response --- error_log echo_subrequest_async sees unsafe uri: "/../foo" --- no_error_log [error] [alert] === TEST 21: let subrequest to read the main request's request body --- SKIP --- config location /main { echo_subrequest_async POST /sub; } location /sub { echo_read_request_body; echo_request_body; } --- request POST /main hello, body! --- response_body chomp hello, body! === TEST 22: POST subrequest with file body (relative paths) --- config location /main { echo_subrequest_async POST /sub -f html/blah.txt; } location /sub { echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo_request_body; } --- user_files >>> blah.txt Hello, world --- request GET /main --- response_body sub method: POST Hello, world === TEST 23: POST subrequest with file body (absolute paths) --- config location /main { echo_subrequest_async POST /sub -f $TEST_NGINX_HTML_DIR/blah.txt; } location /sub { echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo_request_body; } --- user_files >>> blah.txt Hello, world! Haha --- request GET /main --- response_body sub method: POST Hello, world! Haha === TEST 24: POST subrequest with file body (file not found) --- config location /main { echo_subrequest_async POST /sub -f html/blah/blah.txt; } location /sub { echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo_request_body; } --- user_files >>> blah.txt Hello, world --- request GET /main --- ignore_response --- error_log eval qr/open\(\) ".*?" failed/ --- no_error_log [alert] === TEST 25: POST subrequest with file body (absolute paths in vars) --- config location /main { set $path $TEST_NGINX_HTML_DIR/blah.txt; echo_subrequest_async POST /sub -f $path; } location /sub { echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo_request_body; } --- user_files >>> blah.txt Hello, world! Haha --- request GET /main --- response_body sub method: POST Hello, world! Haha === TEST 26: leading subrequest & echo_before_body --- config location /main { echo_before_body hello; echo_subrequest_async GET /foo; } location /foo { echo world; } --- request GET /main --- response_body hello world === TEST 27: leading subrequest & xss --- config location /main { default_type 'application/json'; xss_get on; xss_callback_arg c; echo_subrequest_async GET /foo; } location /foo { echo -n world; } --- request GET /main?c=hi --- response_body chop hi(world); === TEST 28: multiple leading subrequest & xss --- config location /main { default_type 'application/json'; xss_get on; xss_callback_arg c; echo_subrequest_async GET /foo; echo_subrequest_async GET /bar; } location /foo { echo -n world; } location /bar { echo -n ' people'; } --- request GET /main?c=hi --- response_body chop hi(world people); === TEST 29: sanity (HEAD) --- config location /main { echo_subrequest_async GET /sub; echo_subrequest_async GET /sub; } location /sub { echo hello; } --- request HEAD /main --- response_body === TEST 30: HEAD subrequest --- config location /main { echo_subrequest_async HEAD /sub; echo_subrequest_async HEAD /sub; } location /sub { echo hello; } --- request GET /main --- response_body libnginx-mod-http-echo-0.63/t/subrequest.t000066400000000000000000000325701433472572100205730ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (2 * blocks() + 1); $ENV{TEST_NGINX_HTML_DIR} = html_dir; $ENV{TEST_NGINX_CLIENT_PORT} ||= server_port(); run_tests(); __DATA__ === TEST 1: sanity --- config location /main { echo_subrequest GET /sub; } location /sub { echo hello; } --- request GET /main --- response_body hello === TEST 2: trailing echo --- config location /main { echo_subrequest GET /sub; echo after subrequest; } location /sub { echo hello; } --- request GET /main --- response_body hello after subrequest === TEST 3: leading echo --- config location /main { echo before subrequest; echo_subrequest GET /sub; } location /sub { echo hello; } --- request GET /main --- response_body before subrequest hello === TEST 4: leading & trailing echo --- config location /main { echo before subrequest; echo_subrequest GET /sub; echo after subrequest; } location /sub { echo hello; } --- request GET /main --- response_body before subrequest hello after subrequest === TEST 5: multiple subrequests --- config location /main { echo before sr 1; echo_subrequest GET /sub; echo after sr 1; echo before sr 2; echo_subrequest GET /sub; echo after sr 2; } location /sub { echo hello; } --- request GET /main --- response_body before sr 1 hello after sr 1 before sr 2 hello after sr 2 === TEST 6: timed multiple subrequests (blocking sleep) --- config location /main { echo_reset_timer; echo_subrequest GET /sub1; echo_subrequest GET /sub2; echo "took $echo_timer_elapsed sec for total."; } location /sub1 { echo_blocking_sleep 0.02; echo hello; } location /sub2 { echo_blocking_sleep 0.01; echo world; } --- request GET /main --- response_body_like ^hello world took 0\.0(?:2[5-9]|3[0-6]) sec for total\.$ === TEST 7: timed multiple subrequests (non-blocking sleep) --- config location /main { echo_reset_timer; echo_subrequest GET /sub1; echo_subrequest GET /sub2; echo "took $echo_timer_elapsed sec for total."; } location /sub1 { echo_sleep 0.02; echo hello; } location /sub2 { echo_sleep 0.01; echo world; } --- request GET /main --- response_body_like ^hello world took 0\.0(?:2[5-9]|3[0-6]) sec for total\.$ === TEST 8: location with args --- config location /main { echo_subrequest GET /sub -q 'foo=Foo&bar=Bar'; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body Foo Bar === TEST 9: chained subrequests --- config location /main { echo 'pre main'; echo_subrequest GET /sub; echo 'post main'; } location /sub { echo 'pre sub'; echo_subrequest GET /subsub; echo 'post sub'; } location /subsub { echo 'subsub'; } --- request GET /main --- response_body pre main pre sub subsub post sub post main === TEST 10: chained subrequests using named locations as of 0.8.20, ngx_http_subrequest still does not support named location. sigh. this case is a TODO. --- config location /main { echo 'pre main'; echo_subrequest GET @sub; echo 'post main'; } location @sub { echo 'pre sub'; echo_subrequest GET @subsub; echo 'post sub'; } location @subsub { echo 'subsub'; } --- request GET /main --- response_body pre main pre sub subsub post sub post main --- SKIP === TEST 11: explicit flush in main request --- config location /main { echo 'pre main'; echo_subrequest GET /sub; echo 'post main'; echo_flush; } location /sub { echo_sleep 0.02; echo 'sub'; } --- request GET /main --- response_body pre main sub post main === TEST 12: DELETE subrequest --- config location /main { echo_subrequest DELETE /sub; } location /sub { echo "sub method: $echo_request_method"; echo "main method: $echo_client_request_method"; } --- request GET /main --- response_body sub method: DELETE main method: GET === TEST 13: DELETE subrequest --- config location /main { echo "main method: $echo_client_request_method"; echo_subrequest GET /proxy; echo_subrequest DELETE /proxy; } location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/sub; } location /sub { echo "sub method: $echo_request_method"; } --- request GET /main --- response_body main method: GET sub method: GET sub method: DELETE === TEST 14: POST subrequest with body --- config location /main { echo_subrequest POST /sub -b 'hello, world'; } location /sub { echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo "sub body: $echo_request_body"; } --- request GET /main --- response_body sub method: POST sub body: hello, world === TEST 15: POST subrequest with body (explicitly read the body) --- config location /main { echo_subrequest POST /sub -b 'hello, world'; } location /sub { echo "sub method: $echo_request_method"; # we call echo_read_client_body explicitly here even # though it's not necessary. echo_read_request_body; echo "sub body: $echo_request_body"; } --- request GET /main --- response_body sub method: POST sub body: hello, world === TEST 16: POST subrequest with body (with proxy in the middle) and without read body explicitly --- config location /main { echo_subrequest POST /proxy -b 'hello, world'; } location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/sub; } location /sub { echo "sub method: $echo_request_method."; # we need to read body explicitly here...or $echo_request_body # will evaluate to empty ("") echo "sub body: $echo_request_body."; } --- request GET /main --- response_body sub method: POST. sub body: . === TEST 17: POST subrequest with body (with proxy in the middle) and read body explicitly --- config location /main { echo_subrequest POST /proxy -b 'hello, world'; } location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/sub; } location /sub { echo "sub method: $echo_request_method."; # we need to read body explicitly here...or $echo_request_body # will evaluate to empty ("") echo_read_request_body; echo "sub body: $echo_request_body."; } --- request GET /main --- response_body sub method: POST. sub body: hello, world. === TEST 18: multiple subrequests --- config location /multi { echo_subrequest POST '/sub' -q 'foo=Foo' -b 'hi'; echo_subrequest 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 '///'; } --- request GET /multi --- response_body querystring: foo=Foo method: POST body: hi content length: 2 /// querystring: bar=Bar method: PUT body: hello content length: 5 /// === TEST 19: unsafe uri --- config location /unsafe { echo_subrequest GET '/../foo'; } --- request GET /unsafe --- ignore_response --- error_log echo_subrequest sees unsafe uri: "/../foo" --- no_error_log [error] === TEST 20: querystring in url --- config location /main { echo_subrequest GET /sub?foo=Foo&bar=Bar; } location /sub { echo $arg_foo $arg_bar; } --- request GET /main --- response_body Foo Bar === TEST 21: querystring in url *AND* an explicit querystring --- config location /main { echo_subrequest GET /sub?foo=Foo&bar=Bar -q blah=Blah; } location /sub { echo $arg_foo $arg_bar $arg_blah; } --- request GET /main --- response_body Blah === TEST 22: let subrequest to read the main request's request body --- SKIP --- config location /main { echo_subrequest POST /sub; } location /sub { echo_read_request_body; echo_request_body; } --- request POST /main hello, body! --- response_body chomp hello, body! === TEST 23: deep nested echo_subrequest/echo_subrequest_async --- config location /main { echo_subrequest GET /bar; echo_subrequest_async GET /bar; echo_subrequest_async GET /bar; echo_subrequest GET /group; echo_subrequest_async GET /group; } location /group { echo_subrequest GET /bar; echo_subrequest_async GET /bar; } location /bar { echo $echo_incr; } --- request GET /main --- response_body 1 2 3 4 5 6 7 === TEST 24: deep nested echo_subrequest/echo_subrequest_async --- config location /main { echo_subrequest GET /bar?a; echo_subrequest_async GET /bar?b; echo_subrequest_async GET /bar?c; echo_subrequest GET /group?a=d&b=e; echo_subrequest_async GET /group?a=f&b=g; } location /group { echo_subrequest GET /bar?$arg_a; echo_subrequest_async GET /bar?$arg_b; } location /bar { echo -n $query_string; } --- request GET /main --- response_body: abcdefg --- timeout: 2 === TEST 25: POST subrequest with file body (relative paths) --- config location /main { echo_subrequest POST /sub -f html/blah.txt; } location /sub { echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo_request_body; } --- user_files >>> blah.txt Hello, world --- request GET /main --- response_body sub method: POST Hello, world === TEST 26: POST subrequest with file body (absolute paths) --- config location /main { echo_subrequest POST /sub -f $TEST_NGINX_HTML_DIR/blah.txt; } location /sub { echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo_request_body; } --- user_files >>> blah.txt Hello, world! Haha --- request GET /main --- response_body sub method: POST Hello, world! Haha === TEST 27: POST subrequest with file body (file not found) --- config location /main { echo_subrequest POST /sub -f html/blah/blah.txt; } location /sub { echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo_request_body; } --- user_files >>> blah.txt Hello, world --- request GET /main --- ignore_response --- error_log eval qr/open\(\) ".*?" failed/ --- no_error_log [alert] === TEST 28: leading subrequest & echo_before_body --- config location /main { echo_before_body hello; echo_subrequest GET /foo; } location /foo { echo world; } --- request GET /main --- response_body hello world === TEST 29: leading subrequest & xss --- config location /main { default_type 'application/json'; xss_get on; xss_callback_arg c; echo_subrequest GET /foo; } location /foo { echo -n world; } --- request GET /main?c=hi --- response_body chop hi(world); === TEST 30: multiple leading subrequest & xss --- config location /main { default_type 'application/json'; xss_get on; xss_callback_arg c; echo_subrequest GET /foo; echo_subrequest GET /bar; } location /foo { echo -n world; } location /bar { echo -n ' people'; } --- request GET /main?c=hi --- response_body chop hi(world people); === TEST 31: sanity (HEAD) --- config location /main { echo_subrequest GET /sub; echo_subrequest GET /sub; } location /sub { echo hello; } --- request HEAD /main --- response_body === TEST 32: POST subrequest to ngx_proxy --- config location /hello { default_type text/plain; echo_subrequest POST '/proxy' -q 'foo=Foo&bar=baz' -b 'request_body=test&test=3'; } location /proxy { proxy_pass http://127.0.0.1:$TEST_NGINX_CLIENT_PORT/sub; #proxy_pass http://127.0.0.1:1113/sub; } location /sub { echo_read_request_body; echo "sub method: $echo_request_method"; # we don't need to call echo_read_client_body explicitly here echo "sub body: $echo_request_body"; } --- request GET /hello --- response_body sub method: POST sub body: request_body=test&test=3 === TEST 33: HEAD subrequest --- config location /main { echo_subrequest HEAD /sub; echo_subrequest HEAD /sub; } location /sub { echo hello; } --- request GET /main --- response_body === TEST 34: method name as an nginx variable (github issue #34) --- config location ~ ^/delay/(?[0-9.]+)/(?.*)$ { # echo_blocking_sleep $delay; echo_subrequest '$echo_request_method' '/$originalURL' -q '$args'; } location /api { echo "args: $args"; } --- request GET /delay/0.343/api/?a=b --- response_body args: a=b --- no_error_log [error] libnginx-mod-http-echo-0.63/t/unused.t000066400000000000000000000036641433472572100176760ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (5 * blocks()); no_long_string(); log_level('warn'); #master_on(); #workers(1); run_tests(); __DATA__ === TEST 1: filters used --- http_config postpone_output 1; --- config location /echo { echo world; echo_after_body hello; } --- request GET /echo?blah --- response_body world hello --- error_log echo header filter, uri "/echo?blah" echo body filter, uri "/echo?blah" --- no_error_log [error] --- log_level: debug === TEST 2: filters not used --- http_config postpone_output 1; --- config location /echo { echo world; #echo_after_body hello; } --- request GET /echo?blah --- response_body world --- no_error_log echo header filter, uri "/echo?blah" echo body filter, uri "/echo?blah" [error] --- log_level: debug === TEST 3: (after) filters used (multiple http {} blocks) This test case won't run with nginx 1.9.3+ since duplicate http {} blocks have been prohibited since then. --- SKIP --- http_config postpone_output 1; --- config location /echo { echo world; echo_after_body hello; } --- post_main_config http { } --- request GET /echo?blah --- response_body world hello --- error_log echo header filter, uri "/echo?blah" echo body filter, uri "/echo?blah" --- no_error_log [error] --- log_level: debug === TEST 4: (before) filters used (multiple http {} blocks) This test case won't run with nginx 1.9.3+ since duplicate http {} blocks have been prohibited since then. --- SKIP --- http_config postpone_output 1; --- config location /echo { echo world; echo_before_body hello; } --- post_main_config http { } --- request GET /echo?blah --- response_body hello world --- error_log echo header filter, uri "/echo?blah" echo body filter, uri "/echo?blah" --- no_error_log [error] --- log_level: debug libnginx-mod-http-echo-0.63/util/000077500000000000000000000000001433472572100167075ustar00rootroot00000000000000libnginx-mod-http-echo-0.63/util/build.sh000077500000000000000000000032231433472572100203450ustar00rootroot00000000000000#!/usr/bin/env bash # this file is mostly meant to be used by the author himself. root=`pwd` version=$1 force=$2 home=~ #--with-cc=gcc46 \ ngx-build $force $version \ --with-ld-opt="-L$PCRE_LIB -Wl,-rpath,$PCRE_LIB:$LIBDRIZZLE_LIB:/usr/local/lib" \ --with-cc-opt="-DDEBUG_MALLOC" \ --with-http_stub_status_module \ --with-http_image_filter_module \ --without-mail_pop3_module \ --without-mail_imap_module \ --without-mail_smtp_module \ --without-http_upstream_ip_hash_module \ --without-http_memcached_module \ --without-http_referer_module \ --without-http_autoindex_module \ --without-http_auth_basic_module \ --without-http_userid_module \ --add-module=$root/../ndk-nginx-module \ --add-module=$root/../set-misc-nginx-module \ --add-module=$root/../eval-nginx-module \ --add-module=$root/../xss-nginx-module \ --add-module=$root/../rds-json-nginx-module \ --add-module=$root/../headers-more-nginx-module \ --add-module=$root/../lua-nginx-module \ --add-module=$root $opts \ --with-http_v2_module \ --with-select_module \ --with-poll_module \ --without-http_ssi_module \ --with-debug || exit 1 #--add-module=$root/../lz-session-nginx-module \ #--add-module=$home/work/ndk \ #--add-module=$home/work/ndk/examples/http/set_var \ #--add-module=$root/../eval-nginx-module \ #--add-module=/home/agentz/work/nginx_eval_module-1.0.1 \ libnginx-mod-http-echo-0.63/util/releng000077500000000000000000000003311433472572100201060ustar00rootroot00000000000000#!/bin/bash ./update-readme ack '.{81}' src/ngx_http_*.[ch] ack '(?<=\#define)\s*DDEBUG\s*[12]' src echo ======================================= ack '(?<=This document describes echo-nginx-module v)\d+\.\d+' README libnginx-mod-http-echo-0.63/util/wiki2pod.pl000066400000000000000000000060641433472572100210020ustar00rootroot00000000000000#!/usr/bin/env perl use strict; use warnings; use bytes; my @nl_counts; my $last_nl_count_level; my @bl_counts; my $last_bl_count_level; sub fmt_pos ($) { (my $s = $_[0]) =~ s{\#(.*)}{/"$1"}; $s; } sub fmt_mark ($$) { my ($tag, $s) = @_; my $max_level = 0; while ($s =~ /([<>])\1*/g) { my $level = length $&; if ($level > $max_level) { $max_level = $level; } } my $times = $max_level + 1; if ($times > 1) { $s = " $s "; } return $tag . ('<' x $times) . $s . ('>' x $times); } print "=encoding utf-8\n\n"; while (<>) { if ($. == 1) { # strip the leading U+FEFF byte in MS-DOS text files my $first = ord(substr($_, 0, 1)); #printf STDERR "0x%x", $first; #my $second = ord(substr($_, 2, 1)); #printf STDERR "0x%x", $second; if ($first == 0xEF) { substr($_, 0, 1, ''); #warn "Hit!"; } } s{\[(http[^ \]]+) ([^\]]*)\]}{$2 (L<$1>)}gi; s{ \[\[ ( [^\]\|]+ ) \| ([^\]]*) \]\] }{"L<$2|" . fmt_pos($1) . ">"}gixe; s{(.*?)}{fmt_mark('C', $1)}gie; s{'''(.*?)'''}{fmt_mark('B', $1)}ge; s{''(.*?)''}{fmt_mark('I', $1)}ge; if (s{^\s*<[^>]+>\s*$}{}) { next; } if (/^\s*$/) { print "\n"; next; } =begin cmt if ($. == 1) { warn $_; for my $i (0..length($_) - 1) { my $chr = substr($_, $i, 1); warn "chr ord($i): ".ord($chr)." \"$chr\"\n"; } } =end cmt =cut if (/(=+) (.*) \1$/) { #warn "HERE! $_" if $. == 1; my ($level, $title) = (length $1, $2); collapse_lists(); print "\n=head$level $title\n\n"; } elsif (/^(\#+) (.*)/) { my ($level, $txt) = (length($1) - 1, $2); if (defined $last_nl_count_level && $level != $last_nl_count_level) { print "\n=back\n\n"; } $last_nl_count_level = $level; $nl_counts[$level] ||= 0; if ($nl_counts[$level] == 0) { print "\n=over\n\n"; } $nl_counts[$level]++; print "\n=item $nl_counts[$level].\n\n"; print "$txt\n"; } elsif (/^(\*+) (.*)/) { my ($level, $txt) = (length($1) - 1, $2); if (defined $last_bl_count_level && $level != $last_bl_count_level) { print "\n=back\n\n"; } $last_bl_count_level = $level; $bl_counts[$level] ||= 0; if ($bl_counts[$level] == 0) { print "\n=over\n\n"; } $bl_counts[$level]++; print "\n=item *\n\n"; print "$txt\n"; } else { collapse_lists(); print; } } collapse_lists(); sub collapse_lists { while (defined $last_nl_count_level && $last_nl_count_level >= 0) { print "\n=back\n\n"; $last_nl_count_level--; } undef $last_nl_count_level; undef @nl_counts; while (defined $last_bl_count_level && $last_bl_count_level >= 0) { print "\n=back\n\n"; $last_bl_count_level--; } undef $last_bl_count_level; undef @bl_counts; } libnginx-mod-http-echo-0.63/valgrind.suppress000066400000000000000000000017321433472572100213510ustar00rootroot00000000000000{ Memcheck:Param epoll_ctl(event) fun:epoll_ctl } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_event_process_init } { Memcheck:Cond fun:index fun:expand_dynamic_string_token fun:_dl_map_object fun:map_doit fun:_dl_catch_error fun:do_preload fun:dl_main } { Memcheck:Leak match-leak-kinds: definite fun:malloc fun:ngx_alloc fun:ngx_set_environment fun:ngx_single_process_cycle fun:main } { Memcheck:Leak match-leak-kinds: definite fun:malloc fun:ngx_alloc fun:ngx_set_environment fun:ngx_single_process_cycle } { Memcheck:Leak match-leak-kinds: definite fun:malloc fun:ngx_alloc fun:ngx_set_environment fun:ngx_worker_process_init fun:ngx_worker_process_cycle }