├── .gitattributes ├── .gitignore ├── .travis.yml ├── Changelog ├── LICENCE ├── README.md ├── config ├── src ├── ddebug.h └── ngx_http_eval_module.c ├── t ├── bug.t ├── crash.t ├── memc.t └── sanity.t ├── util ├── build.sh └── wiki2pod.pl └── valgrind.suppress /.gitattributes: -------------------------------------------------------------------------------- 1 | *.t linguist-language=Text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | reindex 2 | .libs 3 | *.swp 4 | *.slo 5 | *.la 6 | *.swo 7 | *.lo 8 | *~ 9 | *.o 10 | print.txt 11 | .rsync 12 | *.tar.gz 13 | dist 14 | build[789] 15 | build 16 | tags 17 | update-readme 18 | *.tmp 19 | test/Makefile 20 | test/blib 21 | test.sh 22 | t.sh 23 | t/t.sh 24 | test/t/servroot/ 25 | releng 26 | reset 27 | *.t_ 28 | genmobi.sh 29 | *.mobi 30 | misc/chunked 31 | ctags 32 | src/module.c 33 | releng 34 | *.html 35 | all.sh 36 | build10 37 | buildroot/ 38 | go 39 | work/ 40 | t/servroot/ 41 | build1[57] 42 | Makefile 43 | nginx 44 | *.plist 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: focal 3 | 4 | os: linux 5 | 6 | language: c 7 | 8 | compiler: 9 | - gcc 10 | 11 | addons: 12 | apt: 13 | packages: [ axel, cpanminus, libtest-base-perl, libtext-diff-perl, liburi-perl, libwww-perl, libtest-longstring-perl, liblist-moreutils-perl ] 14 | 15 | cache: 16 | apt: true 17 | 18 | env: 19 | global: 20 | - LUAJIT_PREFIX=/opt/luajit21 21 | - LUAJIT_LIB=$LUAJIT_PREFIX/lib 22 | - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH 23 | - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1 24 | - LUA_INCLUDE_DIR=$LUAJIT_INC 25 | - LUA_CMODULE_DIR=/lib 26 | - JOBS=3 27 | - NGX_BUILD_JOBS=$JOBS 28 | - TEST_NGINX_SLEEP=0.006 29 | matrix: 30 | - NGINX_VERSION=1.27.1 31 | - NGINX_VERSION=1.25.3 32 | 33 | services: 34 | - memcache 35 | 36 | install: 37 | - git clone https://github.com/openresty/openresty-devel-utils.git 38 | - git clone https://github.com/openresty/openresty.git ../openresty 39 | - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx 40 | - git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module 41 | - git clone https://github.com/openresty/test-nginx.git 42 | - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git 43 | - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module 44 | - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module 45 | - git clone https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module 46 | - git clone https://github.com/openresty/openresty.git ../ngx_openresty 47 | 48 | script: 49 | - cd luajit2 50 | - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1) 51 | - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1) 52 | - cd ../test-nginx && sudo cpanm . && cd .. 53 | - export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH 54 | - export NGX_BUILD_CC=$CC 55 | - sh util/build.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1) 56 | - nginx -V 57 | - prove -I. -r t 58 | -------------------------------------------------------------------------------- /Changelog: -------------------------------------------------------------------------------- 1 | 2 | Version 1.0.2 3 | * Change: restore $uri variable in subrequests 4 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | * Copyright (c) 2009, Valery Kholodkov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Valery Kholodkov nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY VALERY KHOLODKOV ''AS IS'' AND ANY 16 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL VALERY KHOLODKOV BE LIABLE FOR ANY 19 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | ngx_eval - Capturing subrequest response bodies into NGINX variables 5 | 6 | *This module is not distributed with the Nginx source.* 7 | 8 | Table of Contents 9 | ================= 10 | 11 | * [Name](#name) 12 | * [Status](#status) 13 | * [Synopsis](#synopsis) 14 | * [Description](#description) 15 | * [Use Lua instead](#use-lua-instead) 16 | * [Limitations](#limitations) 17 | * [Nginx Compatibility](#nginx-compatibility) 18 | * [Community](#community) 19 | * [English Mailing List](#english-mailing-list) 20 | * [Chinese Mailing List](#chinese-mailing-list) 21 | * [Bugs and Patches](#bugs-and-patches) 22 | * [Original ngx_eval documentation](#original-ngx_eval-documentation) 23 | * [Copyright and License](#copyright-and-license) 24 | * [See Also](#see-also) 25 | 26 | Status 27 | ====== 28 | 29 | This module is experimental and production use is discouraged. 30 | If you want similar (but more powerful) functionalities, see 31 | the [ngx_lua](http://github.com/openresty/lua-nginx-module) module instead. 32 | 33 | Synopsis 34 | ======== 35 | 36 | ```nginx 37 | # an example for working with the ngx_drizzle + ngx_rds_json 38 | # modules, but you must put ngx_rds_json *after* 39 | # ngx_eval during nginx configure, for example: 40 | # ./configure --add-module=/path/to/nginx-eval-module \ 41 | # --add-module=/path/to/rds-json-nginx-module \ 42 | # --add-module=/path/to/drizzle-nginx-module 43 | location = /mysql { 44 | eval_subrequest_in_memory off; 45 | eval_override_content_type text/plain; 46 | eval_buffer_size 4k; # default 4k, truncated if overflown 47 | eval $res { 48 | drizzle_query "select * from cats"; 49 | drizzle_pass my_mysql_backend; 50 | rds_json on; 51 | } 52 | # now $res holds the JSON formatted result set 53 | if ($res ~ '"Tom"') { 54 | echo "Found the Tom cat!"; 55 | break; 56 | } 57 | echo "The Tom cat is missing!"; 58 | } 59 | 60 | # an example for working with the ngx_postgres module 61 | location = /login { 62 | eval_subrequest_in_memory off; 63 | eval_override_content_type text/plain; 64 | eval_buffer_size 1k; 65 | eval $uid { 66 | postgres_query "select id 67 | from users 68 | where name=$arg_name and pass=$arg_pass"; 69 | postgres_pass pg_backend; 70 | postgres_output value 0 0; 71 | } 72 | if ($uid !~ '^\d+$') { 73 | rewrite ^ /relogin redirect; break; 74 | } 75 | # your content handler settings... 76 | } 77 | ``` 78 | 79 | Description 80 | =========== 81 | 82 | This fork of ngx_eval can work with any content handlers and 83 | even with filters enabled as long as you put `ngx_eval` *before* 84 | your filter modules during nginx configure, for instance 85 | 86 | ``` 87 | ./configure --prefix=/opt/nginx \ 88 | --add-module=/path/to/this/nginx-eval-module \ 89 | --add-module=/path/to/your/filter/module \ 90 | --add-module=/path/to/your/other/filters 91 | ``` 92 | 93 | such that `ngx_eval`'s filter works *after* your filter modules. 94 | 95 | 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 96 | `./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) 97 | directive, for example, 98 | 99 | ```nginx 100 | load_module /path/to/modules/ngx_http_eval_module.so; 101 | ``` 102 | 103 | [Back to TOC](#table-of-contents) 104 | 105 | Use Lua instead 106 | --------------- 107 | 108 | Nowadays, however, we prefer [ngx_lua](http://github.com/openresty/lua-nginx-module) to do the tasks originally done by `ngx_eval` 109 | because of various inherent limitations in `ngx_eval`. 110 | 111 | Here's a small example using ngx_lua: 112 | 113 | ```nginx 114 | location = /filter-spam { 115 | internal; 116 | proxy_pass http://blah.blah/checker; 117 | } 118 | 119 | location / { 120 | rewrite_by_lua ' 121 | local res = ngx.location.capture("/filter-spam") 122 | if res.status ~= ngx.HTTP_OK or res.body == nil then 123 | return 124 | end 125 | if string.match(res.body, "SPAM") then 126 | return ngx.redirect("/terms-of-use") 127 | end 128 | '; 129 | 130 | fastcgi_pass ...; 131 | } 132 | ``` 133 | 134 | this is roughly equivalent to 135 | 136 | ```nginx 137 | location / { 138 | eval $res { 139 | proxy_pass http://blah.blah/checker; 140 | } 141 | if ($res ~ 'SPAM') { 142 | rewrite ^ /terms-of-use redirect; 143 | break; 144 | } 145 | 146 | fastcgi_pass ...; 147 | } 148 | ``` 149 | 150 | Even though the Lua example is a little longer but is much more flexible and stable. 151 | 152 | [Back to TOC](#table-of-contents) 153 | 154 | 155 | [Back to TOC](#table-of-contents) 156 | 157 | Limitations 158 | =========== 159 | 160 | * The contents of subrequests issued from within the eval block 161 | (like those fired by echo_subrequest) won't be captured properly. 162 | 163 | [Back to TOC](#table-of-contents) 164 | 165 | Nginx Compatibility 166 | =================== 167 | 168 | The following versions of Nginx should work with this module: 169 | 170 | * 1.9.x (last tested: 1.9.7) 171 | * 1.8.x 172 | * 1.7.x (last tested: 1.7.7) 173 | * 1.5.x (last tested: 1.5.12) 174 | * 1.4.x (last tested: 1.4.4) 175 | * 1.3.x (last tested: 1.3.11) 176 | * 1.2.x (last tested: 1.2.9) 177 | * 1.1.x (last tested: 1.1.5) 178 | * 1.0.x (last tested: 1.0.15) 179 | * 0.9.x (last tested: 0.9.4) 180 | * 0.8.0 ~ 0.8.41, 0.8.54+ (0.8.42 ~ 0.8.53 requires patching, see below) (last tested: 0.8.54) 181 | * 0.7.x >= 0.7.21 (last tested: 0.7.68) 182 | 183 | Note that nginx 0.8.42 ~ 0.8.53 won't work due to a [famous regression](http://forum.nginx.org/read.php?29,103078,103078) appeared 184 | since 0.8.42. 185 | 186 | [Back to TOC](#table-of-contents) 187 | 188 | Community 189 | ========= 190 | 191 | [Back to TOC](#table-of-contents) 192 | 193 | English Mailing List 194 | -------------------- 195 | 196 | The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers. 197 | 198 | [Back to TOC](#table-of-contents) 199 | 200 | Chinese Mailing List 201 | -------------------- 202 | 203 | The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers. 204 | 205 | [Back to TOC](#table-of-contents) 206 | 207 | Bugs and Patches 208 | ================ 209 | 210 | Please submit bug reports, wishlists, or patches by 211 | 212 | 1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/nginx-eval-module/issues), 213 | 1. or posting to the [OpenResty community](#community). 214 | 215 | [Back to TOC](#table-of-contents) 216 | 217 | Original ngx_eval documentation 218 | =============================== 219 | 220 | Documentation for this module could be found under following URLs: 221 | 222 | * English: http://www.grid.net.ru/nginx/eval.en.html 223 | 224 | * Russian: http://www.grid.net.ru/nginx/eval.ru.html 225 | 226 | This work is commissioned by gadu-gadu.pl 227 | 228 | [Back to TOC](#table-of-contents) 229 | 230 | Copyright and License 231 | ===================== 232 | 233 | This module is licensed under the BSD license. 234 | 235 | Copyright (C) 2009-2010, by Valery Kholodkov valery+nginx@grid.net.ru 236 | 237 | Copyright (C) 2010-2016, by Yichun "agentzh" Zhang (章亦春) , CloudFlare Inc. 238 | 239 | All rights reserved. 240 | 241 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 242 | 243 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 244 | 245 | * 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. 246 | 247 | 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. 248 | 249 | [Back to TOC](#table-of-contents) 250 | 251 | See Also 252 | ======== 253 | * the [ngx_lua](http://github.com/openresty/lua-nginx-module) module. 254 | 255 | [Back to TOC](#table-of-contents) 256 | 257 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_eval_module 2 | 3 | EVAL_SRCS="$ngx_addon_dir/src/ngx_http_eval_module.c" 4 | EVAL_DEPS="$ngx_addon_dir/src/ddebug.h" 5 | 6 | CFLAGS="$CFLAGS -DNDK_SET_VAR" 7 | 8 | if test -n "$ngx_module_link"; then 9 | ngx_module_type=HTTP_AUX_FILTER 10 | ngx_module_name=$ngx_addon_name 11 | ngx_module_incs= 12 | ngx_module_deps="$EVAL_DEPS" 13 | ngx_module_srcs="$EVAL_SRCS" 14 | ngx_module_libs= 15 | 16 | . auto/module 17 | else 18 | HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES $ngx_addon_name" 19 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $EVAL_SRCS" 20 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $EVAL_DEPS" 21 | fi 22 | -------------------------------------------------------------------------------- /src/ddebug.h: -------------------------------------------------------------------------------- 1 | #ifndef DDEBUG_H 2 | #define DDEBUG_H 3 | 4 | #include 5 | #include 6 | 7 | #if defined(DDEBUG) && (DDEBUG) 8 | 9 | # if (NGX_HAVE_VARIADIC_MACROS) 10 | 11 | # define dd(...) fprintf(stderr, "eval *** "); \ 12 | fprintf(stderr, __VA_ARGS__); \ 13 | fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) 14 | 15 | # else 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | static void dd(const char * fmt, ...) { 23 | } 24 | 25 | # endif 26 | 27 | #else 28 | 29 | # if (NGX_HAVE_VARIADIC_MACROS) 30 | 31 | # define dd(...) 32 | 33 | # else 34 | 35 | #include 36 | 37 | static void dd(const char * fmt, ...) { 38 | } 39 | 40 | # endif 41 | 42 | #endif 43 | 44 | #if defined(DDEBUG) && (DDEBUG) 45 | 46 | #define dd_check_read_event_handler(r) \ 47 | dd("r->read_event_handler = %s", \ 48 | r->read_event_handler == ngx_http_block_reading ? \ 49 | "ngx_http_block_reading" : \ 50 | r->read_event_handler == ngx_http_test_reading ? \ 51 | "ngx_http_test_reading" : \ 52 | r->read_event_handler == ngx_http_request_empty_handler ? \ 53 | "ngx_http_request_empty_handler" : "UNKNOWN") 54 | 55 | #define dd_check_write_event_handler(r) \ 56 | dd("r->write_event_handler = %s", \ 57 | r->write_event_handler == ngx_http_handler ? \ 58 | "ngx_http_handler" : \ 59 | r->write_event_handler == ngx_http_core_run_phases ? \ 60 | "ngx_http_core_run_phases" : \ 61 | r->write_event_handler == ngx_http_request_empty_handler ? \ 62 | "ngx_http_request_empty_handler" : "UNKNOWN") 63 | 64 | #else 65 | 66 | #define dd_check_read_event_handler(r) 67 | #define dd_check_write_event_handler(r) 68 | 69 | #endif 70 | 71 | #endif /* DDEBUG_H */ 72 | 73 | -------------------------------------------------------------------------------- /src/ngx_http_eval_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Valery Kholodkov 4 | * Copyright (C) Yichun Zhang 5 | */ 6 | 7 | 8 | #ifndef DDEBUG 9 | #define DDEBUG 0 10 | #endif 11 | #include "ddebug.h" 12 | 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | static void ngx_unescape_uri_patched(u_char **dst, u_char **src, 21 | size_t size, ngx_uint_t type); 22 | 23 | 24 | typedef struct { 25 | ngx_http_variable_t *variable; 26 | ngx_uint_t index; 27 | } ngx_http_eval_variable_t; 28 | 29 | 30 | typedef struct { 31 | ngx_array_t *variables; 32 | ngx_str_t eval_location; 33 | ngx_flag_t escalate; 34 | ngx_str_t override_content_type; 35 | ngx_flag_t subrequest_in_memory; 36 | size_t buffer_size; 37 | } ngx_http_eval_loc_conf_t; 38 | 39 | 40 | typedef struct { 41 | ngx_http_eval_loc_conf_t *base_conf; 42 | ngx_http_variable_value_t **values; 43 | unsigned int done:1; 44 | unsigned int in_progress:1; 45 | ngx_int_t status; 46 | ngx_buf_t buffer; 47 | } ngx_http_eval_ctx_t; 48 | 49 | 50 | typedef ngx_int_t (*ngx_http_eval_format_handler_pt)(ngx_http_request_t *r, 51 | ngx_http_eval_ctx_t *ctx); 52 | 53 | 54 | typedef struct { 55 | ngx_str_t content_type; 56 | ngx_http_eval_format_handler_pt handler; 57 | } ngx_http_eval_format_t; 58 | 59 | 60 | static ngx_int_t 61 | ngx_http_eval_init_variables(ngx_http_request_t *r, ngx_http_eval_ctx_t *ctx, 62 | ngx_http_eval_loc_conf_t *ecf); 63 | 64 | static ngx_int_t ngx_http_eval_post_subrequest_handler(ngx_http_request_t *r, 65 | void *data, ngx_int_t rc); 66 | 67 | static void *ngx_http_eval_create_loc_conf(ngx_conf_t *cf); 68 | static char *ngx_http_eval_merge_loc_conf(ngx_conf_t *cf, void *parent, 69 | void *child); 70 | 71 | static char *ngx_http_eval_block(ngx_conf_t *cf, ngx_command_t *cmd, 72 | void *conf); 73 | 74 | static char *ngx_http_eval_subrequest_in_memory(ngx_conf_t *cf, 75 | ngx_command_t *cmd, void *conf); 76 | 77 | static ngx_int_t ngx_http_eval_header_filter(ngx_http_request_t *r); 78 | static ngx_int_t ngx_http_eval_body_filter(ngx_http_request_t *r, 79 | ngx_chain_t *in); 80 | 81 | static void ngx_http_eval_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in); 82 | 83 | static ngx_int_t ngx_http_eval_init(ngx_conf_t *cf); 84 | 85 | static ngx_int_t ngx_http_eval_octet_stream(ngx_http_request_t *r, 86 | ngx_http_eval_ctx_t *ctx); 87 | static ngx_int_t ngx_http_eval_plain_text(ngx_http_request_t *r, 88 | ngx_http_eval_ctx_t *ctx); 89 | static ngx_int_t ngx_http_eval_urlencoded(ngx_http_request_t *r, 90 | ngx_http_eval_ctx_t *ctx); 91 | 92 | 93 | static ngx_flag_t ngx_http_eval_requires_filter = 1; 94 | 95 | static ngx_http_output_header_filter_pt ngx_http_next_header_filter; 96 | static ngx_http_output_body_filter_pt ngx_http_next_body_filter; 97 | 98 | 99 | static ngx_http_eval_format_t ngx_http_eval_formats[] = { 100 | { ngx_string("application/octet-stream"), ngx_http_eval_octet_stream }, 101 | { ngx_string("text/plain"), ngx_http_eval_plain_text }, 102 | { ngx_string("application/x-www-form-urlencoded"), 103 | ngx_http_eval_urlencoded }, 104 | 105 | { ngx_null_string, ngx_http_eval_plain_text } 106 | }; 107 | 108 | 109 | static ngx_command_t ngx_http_eval_commands[] = { 110 | 111 | { ngx_string("eval"), 112 | NGX_HTTP_LOC_CONF|NGX_CONF_1MORE|NGX_CONF_BLOCK, 113 | ngx_http_eval_block, 114 | NGX_HTTP_LOC_CONF_OFFSET, 115 | 0, 116 | NULL }, 117 | 118 | { ngx_string("eval_escalate"), 119 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 120 | ngx_conf_set_flag_slot, 121 | NGX_HTTP_LOC_CONF_OFFSET, 122 | offsetof(ngx_http_eval_loc_conf_t, escalate), 123 | NULL }, 124 | 125 | { ngx_string("eval_override_content_type"), 126 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 127 | ngx_conf_set_str_slot, 128 | NGX_HTTP_LOC_CONF_OFFSET, 129 | offsetof(ngx_http_eval_loc_conf_t, override_content_type), 130 | NULL }, 131 | 132 | { ngx_string("eval_subrequest_in_memory"), 133 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 134 | ngx_http_eval_subrequest_in_memory, 135 | NGX_HTTP_LOC_CONF_OFFSET, 136 | offsetof(ngx_http_eval_loc_conf_t, subrequest_in_memory), 137 | NULL }, 138 | 139 | { ngx_string("eval_buffer_size"), 140 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 141 | ngx_conf_set_size_slot, 142 | NGX_HTTP_LOC_CONF_OFFSET, 143 | offsetof(ngx_http_eval_loc_conf_t, buffer_size), 144 | NULL }, 145 | 146 | ngx_null_command 147 | }; 148 | 149 | 150 | static ngx_http_module_t ngx_http_eval_module_ctx = { 151 | NULL, /* preconfiguration */ 152 | ngx_http_eval_init, /* postconfiguration */ 153 | 154 | NULL, /* create main configuration */ 155 | NULL, /* init main configuration */ 156 | 157 | NULL, /* create server configuration */ 158 | NULL, /* merge server configuration */ 159 | 160 | ngx_http_eval_create_loc_conf, /* create location configuration */ 161 | ngx_http_eval_merge_loc_conf /* merge location configuration */ 162 | }; 163 | 164 | 165 | ngx_module_t ngx_http_eval_module = { 166 | NGX_MODULE_V1, 167 | &ngx_http_eval_module_ctx, /* module context */ 168 | ngx_http_eval_commands, /* module directives */ 169 | NGX_HTTP_MODULE, /* module type */ 170 | NULL, /* init master */ 171 | NULL, /* init module */ 172 | NULL, /* init process */ 173 | NULL, /* init thread */ 174 | NULL, /* exit thread */ 175 | NULL, /* exit process */ 176 | NULL, /* exit master */ 177 | NGX_MODULE_V1_PADDING 178 | }; 179 | 180 | 181 | static ngx_int_t 182 | ngx_http_eval_handler(ngx_http_request_t *r) 183 | { 184 | /* size_t loc_len; */ 185 | ngx_str_t args; 186 | ngx_str_t subrequest_uri; 187 | ngx_uint_t flags; 188 | /* ngx_http_core_loc_conf_t *clcf; */ 189 | ngx_http_eval_loc_conf_t *ecf; 190 | ngx_http_eval_ctx_t *ctx; 191 | ngx_http_eval_ctx_t *sr_ctx; 192 | ngx_http_request_t *sr; 193 | ngx_int_t rc; 194 | ngx_http_post_subrequest_t *psr; 195 | u_char *p; 196 | 197 | /* 198 | if (r != r->main) { 199 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 200 | 201 | loc_len = r->valid_location ? clcf->name.len : 0; 202 | 203 | if (r->uri.len != loc_len) { 204 | r->uri.data += loc_len; 205 | r->uri.len -= loc_len; 206 | 207 | } else { 208 | r->uri.len = 1; 209 | } 210 | } 211 | */ 212 | 213 | ecf = ngx_http_get_module_loc_conf(r, ngx_http_eval_module); 214 | 215 | if (ecf->variables == NULL || !ecf->variables->nelts) { 216 | return NGX_DECLINED; 217 | } 218 | 219 | ctx = ngx_http_get_module_ctx(r, ngx_http_eval_module); 220 | 221 | if (ctx == NULL) { 222 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_eval_ctx_t)); 223 | if (ctx == NULL) { 224 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 225 | } 226 | 227 | ctx->base_conf = ecf; 228 | 229 | ngx_http_set_ctx(r, ctx, ngx_http_eval_module); 230 | } 231 | 232 | if (ctx->done) { 233 | dd("subrequest done"); 234 | if (!ecf->escalate 235 | || ctx->status == NGX_OK 236 | || ctx->status == NGX_HTTP_OK) 237 | { 238 | return NGX_DECLINED; 239 | } 240 | 241 | dd("status: %d", (int) ctx->status); 242 | 243 | return ctx->status; 244 | } 245 | 246 | if (ctx->in_progress) { 247 | dd("still in progress"); 248 | return NGX_DONE; 249 | } 250 | 251 | psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); 252 | if (psr == NULL) { 253 | return NGX_ERROR; 254 | } 255 | 256 | if (ngx_http_eval_init_variables(r, ctx, ecf) != NGX_OK) { 257 | return NGX_ERROR; 258 | } 259 | 260 | args = r->args; 261 | flags = 0; 262 | 263 | subrequest_uri.len = ecf->eval_location.len + r->uri.len; 264 | 265 | p = ngx_palloc(r->pool, subrequest_uri.len); 266 | if (p == NULL) { 267 | return NGX_ERROR; 268 | } 269 | 270 | subrequest_uri.data = p; 271 | 272 | p = ngx_copy(p, ecf->eval_location.data, ecf->eval_location.len); 273 | ngx_memcpy(p, r->uri.data, r->uri.len); 274 | 275 | if (ngx_http_parse_unsafe_uri(r, &subrequest_uri, &args, &flags) 276 | != NGX_OK) 277 | { 278 | return NGX_ERROR; 279 | } 280 | 281 | psr->handler = ngx_http_eval_post_subrequest_handler; 282 | psr->data = ctx; 283 | 284 | flags |= NGX_HTTP_SUBREQUEST_WAITED; 285 | 286 | dd("subrequest in memory : %d", (int) ecf->subrequest_in_memory); 287 | 288 | if (ecf->subrequest_in_memory) { 289 | flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY; 290 | } else { 291 | } 292 | 293 | dd("issue subrequest"); 294 | 295 | rc = ngx_http_subrequest(r, &subrequest_uri, &args, &sr, psr, flags); 296 | 297 | if (rc == NGX_ERROR || rc == NGX_DONE) { 298 | return rc; 299 | } 300 | 301 | sr->discard_body = 1; 302 | 303 | /* we don't want to forward certain request headers to the subrequest */ 304 | sr->headers_in.content_length_n = 0; 305 | sr->headers_in.content_length = NULL; 306 | sr->headers_in.content_type = NULL; 307 | #if (NGX_HTTP_GZIP) 308 | sr->headers_in.accept_encoding = NULL; 309 | #endif 310 | 311 | ctx->in_progress = 1; 312 | 313 | /* XXX we don't allow eval in subrequests, i think? */ 314 | sr_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_eval_ctx_t)); 315 | if (sr_ctx == NULL) { 316 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 317 | } 318 | 319 | ngx_http_set_ctx(sr, sr_ctx, ngx_http_eval_module); 320 | 321 | dd("wait for subrequest to complete"); 322 | 323 | return NGX_DONE; 324 | } 325 | 326 | 327 | static ngx_int_t 328 | ngx_http_eval_init_variables(ngx_http_request_t *r, ngx_http_eval_ctx_t *ctx, 329 | ngx_http_eval_loc_conf_t *ecf) 330 | { 331 | ngx_uint_t i; 332 | ngx_http_eval_variable_t *variable; 333 | 334 | ctx->values = ngx_pcalloc(r->pool, 335 | ecf->variables->nelts 336 | * sizeof(ngx_http_variable_value_t *)); 337 | 338 | if (ctx->values == NULL) { 339 | return NGX_ERROR; 340 | } 341 | 342 | variable = ecf->variables->elts; 343 | 344 | for (i = 0;ivariables->nelts;i++) { 345 | ctx->values[i] = r->variables + variable[i].index; 346 | 347 | ctx->values[i]->valid = 0; 348 | ctx->values[i]->not_found = 1; 349 | } 350 | 351 | return NGX_OK; 352 | } 353 | 354 | 355 | static ngx_int_t 356 | ngx_http_eval_post_subrequest_handler(ngx_http_request_t *r, void *data, 357 | ngx_int_t rc) 358 | { 359 | ngx_http_eval_ctx_t *ctx = data; 360 | ngx_http_eval_format_t *f = ngx_http_eval_formats; 361 | ngx_str_t content_type; 362 | 363 | if (ctx->base_conf->override_content_type.len) { 364 | content_type.data = ctx->base_conf->override_content_type.data; 365 | content_type.len = ctx->base_conf->override_content_type.len; 366 | 367 | } else if (r->headers_out.content_type.len) { 368 | content_type.data = r->headers_out.content_type.data; 369 | content_type.len = r->headers_out.content_type.len; 370 | 371 | } else { 372 | content_type.data = (u_char *) "application/octet-stream"; 373 | content_type.len = sizeof("application/octet-stream") - 1; 374 | } 375 | 376 | dd("content_type: %.*s", (int) content_type.len, content_type.data); 377 | 378 | while (f->content_type.len) { 379 | 380 | if (ngx_strncasecmp(f->content_type.data, content_type.data, 381 | f->content_type.len) 382 | == 0) 383 | { 384 | f->handler(r, ctx); 385 | break; 386 | } 387 | 388 | f++; 389 | } 390 | 391 | ctx->done = 1; 392 | /* dd("rc == %d", rc); */ 393 | ctx->status = rc; 394 | 395 | #if 0 396 | /* work-around a bug in the nginx core (ngx_http_named_location) 397 | */ 398 | r->parent->write_event_handler = ngx_http_core_run_phases; 399 | #endif 400 | 401 | return NGX_OK; 402 | } 403 | 404 | 405 | /* 406 | * The next two evaluation methods assume we have at least one varible. 407 | * 408 | * ngx_http_eval_handler must guarantee this. * 409 | */ 410 | static ngx_int_t 411 | ngx_http_eval_octet_stream(ngx_http_request_t *r, ngx_http_eval_ctx_t *ctx) 412 | { 413 | ngx_http_variable_value_t *value = ctx->values[0]; 414 | ngx_http_eval_ctx_t *sr_ctx; 415 | 416 | dd("eval octet stream"); 417 | 418 | sr_ctx = ngx_http_get_module_ctx(r, ngx_http_eval_module); 419 | 420 | if (sr_ctx && sr_ctx->buffer.start) { 421 | value->len = sr_ctx->buffer.last - sr_ctx->buffer.pos; 422 | value->data = sr_ctx->buffer.pos; 423 | value->valid = 1; 424 | value->not_found = 0; 425 | 426 | return NGX_OK; 427 | } 428 | 429 | if (r->upstream) { 430 | value->len = r->upstream->buffer.last - r->upstream->buffer.pos; 431 | value->data = r->upstream->buffer.pos; 432 | dd("found upstream buffer %d: %.*s", (int) value->len, 433 | (int) value->len, value->data); 434 | value->valid = 1; 435 | value->not_found = 0; 436 | dd("XXX no cacheable: %d", (int) value->no_cacheable); 437 | } 438 | 439 | return NGX_OK; 440 | } 441 | 442 | 443 | static ngx_int_t 444 | ngx_http_eval_plain_text(ngx_http_request_t *r, ngx_http_eval_ctx_t *ctx) 445 | { 446 | u_char *p; 447 | ngx_int_t rc; 448 | 449 | ngx_http_variable_value_t *value = ctx->values[0]; 450 | 451 | dd("eval plain text"); 452 | 453 | rc = ngx_http_eval_octet_stream(r, ctx); 454 | if (rc != NGX_OK) { 455 | return rc; 456 | } 457 | 458 | /* 459 | * Remove trailing spaces and control characters 460 | */ 461 | if (value->valid) { 462 | p = value->data + value->len; 463 | 464 | while (p != value->data) { 465 | p--; 466 | 467 | if (*p != CR && *p != LF && *p != '\t' && *p != ' ') { 468 | break; 469 | } 470 | 471 | value->len--; 472 | } 473 | } 474 | 475 | return NGX_OK; 476 | } 477 | 478 | 479 | static ngx_int_t 480 | ngx_http_eval_set_variable_value(ngx_http_request_t *r, 481 | ngx_http_eval_ctx_t *ctx, ngx_str_t *name, ngx_str_t *value) 482 | { 483 | ngx_uint_t i; 484 | ngx_http_eval_variable_t *variable; 485 | 486 | variable = ctx->base_conf->variables->elts; 487 | 488 | for (i = 0; ibase_conf->variables->nelts; i++) { 489 | if (ngx_strncasecmp(variable[i].variable->name.data, name->data, 490 | variable[i].variable->name.len) 491 | == 0) 492 | { 493 | ctx->values[i]->len = value->len; 494 | ctx->values[i]->data = value->data; 495 | ctx->values[i]->valid = 1; 496 | ctx->values[i]->not_found = 0; 497 | 498 | return NGX_OK; 499 | } 500 | } 501 | 502 | ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, 503 | "eval: ignored undefined variable \"%V\"", value); 504 | 505 | return NGX_OK; 506 | } 507 | 508 | 509 | static ngx_int_t 510 | ngx_http_eval_parse_param(ngx_http_request_t *r, ngx_http_eval_ctx_t *ctx, 511 | ngx_str_t *param) 512 | { 513 | u_char *p, *src, *dst; 514 | 515 | ngx_str_t name; 516 | ngx_str_t value; 517 | 518 | /* param->data is not \0 terminated */ 519 | p = (u_char *) memchr(param->data, '=', param->len); 520 | 521 | if (p == NULL) { 522 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 523 | "eval: invalid param \"%V\"", param); 524 | return NGX_ERROR; 525 | } 526 | 527 | name.data = param->data; 528 | name.len = p - param->data; 529 | 530 | value.data = p + 1; 531 | value.len = param->len - (p - param->data) - 1; 532 | 533 | src = dst = value.data; 534 | 535 | ngx_unescape_uri_patched(&dst, &src, value.len, NGX_UNESCAPE_URI); 536 | 537 | value.len = dst - value.data; 538 | 539 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 540 | "eval param: \"%V\"=\"%V\"", &name, &value); 541 | 542 | return ngx_http_eval_set_variable_value(r, ctx, &name, &value); 543 | } 544 | 545 | 546 | static ngx_int_t 547 | ngx_http_eval_urlencoded(ngx_http_request_t *r, ngx_http_eval_ctx_t *ctx) 548 | { 549 | u_char *pos, *last; 550 | ngx_str_t param; 551 | ngx_int_t rc; 552 | 553 | ngx_http_eval_ctx_t *sr_ctx; 554 | 555 | sr_ctx = ngx_http_get_module_ctx(r, ngx_http_eval_module); 556 | 557 | if (sr_ctx && sr_ctx->buffer.start) { 558 | pos = sr_ctx->buffer.pos; 559 | last = sr_ctx->buffer.last; 560 | 561 | } else { 562 | if (!r->upstream 563 | || r->upstream->buffer.last == r->upstream->buffer.pos) 564 | { 565 | return NGX_OK; 566 | } 567 | 568 | pos = r->upstream->buffer.pos; 569 | last = r->upstream->buffer.last; 570 | } 571 | 572 | do { 573 | param.data = pos; 574 | param.len = 0; 575 | 576 | while (pos != last) { 577 | if (*pos == '&') { 578 | pos++; 579 | break; 580 | } 581 | 582 | if (*pos == CR || *pos == LF) { 583 | pos = last; 584 | break; 585 | } 586 | 587 | param.len++; 588 | pos++; 589 | } 590 | 591 | if (param.len != 0) { 592 | rc = ngx_http_eval_parse_param(r, ctx, ¶m); 593 | 594 | if (rc != NGX_OK) { 595 | return rc; 596 | } 597 | } 598 | 599 | } while (pos != last); 600 | 601 | return NGX_OK; 602 | } 603 | 604 | 605 | static void * 606 | ngx_http_eval_create_loc_conf(ngx_conf_t *cf) 607 | { 608 | ngx_http_eval_loc_conf_t *conf; 609 | 610 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_eval_loc_conf_t)); 611 | 612 | if (conf == NULL) { 613 | return NULL; 614 | } 615 | 616 | conf->escalate = NGX_CONF_UNSET; 617 | conf->buffer_size = NGX_CONF_UNSET_SIZE; 618 | conf->subrequest_in_memory = NGX_CONF_UNSET; 619 | 620 | return conf; 621 | } 622 | 623 | 624 | static char * 625 | ngx_http_eval_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 626 | { 627 | ngx_http_eval_loc_conf_t *prev = parent; 628 | ngx_http_eval_loc_conf_t *conf = child; 629 | 630 | ngx_conf_merge_value(conf->escalate, prev->escalate, 0); 631 | ngx_conf_merge_str_value(conf->override_content_type, 632 | prev->override_content_type, ""); 633 | ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 634 | (size_t) ngx_pagesize); 635 | ngx_conf_merge_value(conf->subrequest_in_memory, 636 | prev->subrequest_in_memory, 0); 637 | 638 | return NGX_CONF_OK; 639 | } 640 | 641 | 642 | static ngx_int_t 643 | ngx_http_eval_variable(ngx_http_request_t *r, 644 | ngx_http_variable_value_t *v, uintptr_t data) 645 | { 646 | dd("XXX eval variable get_handle"); 647 | 648 | v->valid = 1; 649 | v->no_cacheable = 0; 650 | v->not_found = 0; 651 | 652 | v->len = 0; 653 | v->data = (u_char *) ""; 654 | 655 | return NGX_OK; 656 | } 657 | 658 | 659 | static char * 660 | ngx_http_eval_add_variables(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 661 | { 662 | ngx_http_eval_loc_conf_t *ecf = conf; 663 | 664 | ngx_uint_t i; 665 | ngx_int_t index; 666 | ngx_str_t *value; 667 | ngx_http_variable_t *v; 668 | ngx_http_eval_variable_t *variable; 669 | 670 | value = cf->args->elts; 671 | 672 | ecf->variables = ngx_array_create(cf->pool, 673 | cf->args->nelts, sizeof(ngx_http_eval_variable_t)); 674 | 675 | if (ecf->variables == NULL) { 676 | return NGX_CONF_ERROR; 677 | } 678 | 679 | for (i = 1;iargs->nelts;i++) { 680 | if (value[i].data[0] != '$') { 681 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 682 | "invalid variable name \"%V\"", &value[1]); 683 | return NGX_CONF_ERROR; 684 | } 685 | 686 | variable = ngx_array_push(ecf->variables); 687 | if (variable == NULL) { 688 | return NGX_CONF_ERROR; 689 | } 690 | 691 | value[i].len--; 692 | value[i].data++; 693 | 694 | v = ngx_http_add_variable(cf, &value[i], NGX_HTTP_VAR_CHANGEABLE); 695 | if (v == NULL) { 696 | return NGX_CONF_ERROR; 697 | } 698 | 699 | index = ngx_http_get_variable_index(cf, &value[i]); 700 | if (index == NGX_ERROR) { 701 | return NGX_CONF_ERROR; 702 | } 703 | 704 | if (v->get_handler == NULL) { 705 | v->get_handler = ngx_http_eval_variable; 706 | v->data = index; 707 | } 708 | 709 | variable->variable = v; 710 | variable->index = index; 711 | } 712 | 713 | return NGX_CONF_OK; 714 | } 715 | 716 | 717 | static char * 718 | ngx_http_eval_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 719 | { 720 | ngx_http_eval_loc_conf_t *pecf = conf; 721 | 722 | char *rv; 723 | void *mconf; 724 | ngx_str_t name; 725 | ngx_uint_t i; 726 | ngx_conf_t save; 727 | ngx_module_t **modules; 728 | ngx_http_module_t *module; 729 | ngx_http_conf_ctx_t *ctx, *pctx; 730 | ngx_http_core_loc_conf_t *clcf, *rclcf; 731 | ngx_http_core_srv_conf_t *cscf; 732 | 733 | #if defined(nginx_version) && nginx_version >= 8042 && nginx_version <= 8053 734 | return "does not work with " NGINX_VER; 735 | #endif 736 | 737 | if (ngx_http_eval_add_variables(cf, cmd, conf) != NGX_CONF_OK) { 738 | return NGX_CONF_ERROR; 739 | } 740 | 741 | ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); 742 | if (ctx == NULL) { 743 | return NGX_CONF_ERROR; 744 | } 745 | 746 | pctx = cf->ctx; 747 | ctx->main_conf = pctx->main_conf; 748 | ctx->srv_conf = pctx->srv_conf; 749 | 750 | ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); 751 | if (ctx->loc_conf == NULL) { 752 | return NGX_CONF_ERROR; 753 | } 754 | 755 | #if defined(nginx_version) && nginx_version >= 1009011 756 | modules = cf->cycle->modules; 757 | #else 758 | modules = ngx_modules; 759 | #endif 760 | 761 | for (i = 0; modules[i]; i++) { 762 | if (modules[i]->type != NGX_HTTP_MODULE) { 763 | continue; 764 | } 765 | 766 | module = modules[i]->ctx; 767 | 768 | if (module->create_loc_conf) { 769 | 770 | mconf = module->create_loc_conf(cf); 771 | if (mconf == NULL) { 772 | return NGX_CONF_ERROR; 773 | } 774 | 775 | ctx->loc_conf[modules[i]->ctx_index] = mconf; 776 | } 777 | } 778 | 779 | clcf = ctx->loc_conf[ngx_http_core_module.ctx_index]; 780 | 781 | name.len = sizeof("/eval_") - 1 + NGX_OFF_T_LEN; 782 | 783 | name.data = ngx_palloc(cf->pool, name.len); 784 | 785 | if (name.data == NULL) { 786 | return NGX_CONF_ERROR; 787 | } 788 | 789 | name.len = ngx_sprintf(name.data, "/eval_%O", (off_t)(uintptr_t) clcf) 790 | - name.data; 791 | 792 | clcf->loc_conf = ctx->loc_conf; 793 | clcf->name = name; 794 | clcf->exact_match = 0; 795 | clcf->noname = 0; 796 | clcf->internal = 1; 797 | clcf->noregex = 1; 798 | 799 | cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); 800 | if (cscf == NULL || cscf->ctx == NULL) { 801 | return NGX_CONF_ERROR; 802 | } 803 | 804 | rclcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; 805 | if (rclcf == NULL) { 806 | return NGX_CONF_ERROR; 807 | } 808 | 809 | if (ngx_http_add_location(cf, &rclcf->locations, clcf) != NGX_OK) { 810 | return NGX_CONF_ERROR; 811 | } 812 | 813 | pecf->eval_location = clcf->name; 814 | 815 | save = *cf; 816 | cf->ctx = ctx; 817 | cf->cmd_type = NGX_HTTP_LOC_CONF; 818 | 819 | rv = ngx_conf_parse(cf, NULL); 820 | 821 | *cf = save; 822 | 823 | return rv; 824 | } 825 | 826 | 827 | static char * 828 | ngx_http_eval_subrequest_in_memory(ngx_conf_t *cf, ngx_command_t *cmd, 829 | void *conf) 830 | { 831 | char *res; 832 | 833 | dd("eval subrequest in memory"); 834 | 835 | res = ngx_conf_set_flag_slot(cf, cmd, conf); 836 | if (res != NGX_CONF_OK) { 837 | return res; 838 | } 839 | 840 | return NGX_CONF_OK; 841 | } 842 | 843 | 844 | static ngx_int_t 845 | ngx_http_eval_init(ngx_conf_t *cf) 846 | { 847 | ngx_http_handler_pt *h; 848 | ngx_http_core_main_conf_t *cmcf; 849 | 850 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 851 | 852 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); 853 | if (h == NULL) { 854 | return NGX_ERROR; 855 | } 856 | 857 | *h = ngx_http_eval_handler; 858 | 859 | if (ngx_http_eval_requires_filter) { 860 | dd("requires filter"); 861 | ngx_http_next_header_filter = ngx_http_top_header_filter; 862 | ngx_http_top_header_filter = ngx_http_eval_header_filter; 863 | 864 | ngx_http_next_body_filter = ngx_http_top_body_filter; 865 | ngx_http_top_body_filter = ngx_http_eval_body_filter; 866 | } 867 | 868 | return NGX_OK; 869 | } 870 | 871 | 872 | static ngx_int_t 873 | ngx_http_eval_header_filter(ngx_http_request_t *r) 874 | { 875 | ngx_http_eval_ctx_t *ctx; 876 | 877 | dd("header filter"); 878 | 879 | if (r == r->main) { 880 | return ngx_http_next_header_filter(r); 881 | } 882 | 883 | ctx = ngx_http_get_module_ctx(r, ngx_http_eval_module); 884 | if (ctx == NULL) { 885 | dd("WARNING: ctx is null, forwarding header filter"); 886 | return ngx_http_next_header_filter(r); 887 | } 888 | 889 | r->filter_need_in_memory = 1; 890 | 891 | /* suppress header output */ 892 | 893 | dd("header filter called: type: %.*s", 894 | (int) r->headers_out.content_type.len, 895 | r->headers_out.content_type.data); 896 | 897 | return NGX_OK; 898 | } 899 | 900 | 901 | static ngx_int_t 902 | ngx_http_eval_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 903 | { 904 | ngx_http_eval_ctx_t *ctx; 905 | ngx_chain_t *cl; 906 | ngx_buf_t *b; 907 | ngx_http_eval_loc_conf_t *conf; 908 | size_t len; 909 | ssize_t rest; 910 | 911 | dd("in body filter"); 912 | 913 | if (r == r->main) { 914 | return ngx_http_next_body_filter(r, in); 915 | } 916 | 917 | ctx = ngx_http_get_module_ctx(r, ngx_http_eval_module); 918 | if (ctx == NULL) { 919 | return ngx_http_next_body_filter(r, in); 920 | } 921 | 922 | dd("in body filter"); 923 | 924 | conf = ngx_http_get_module_loc_conf(r->parent, ngx_http_eval_module); 925 | 926 | if (conf->subrequest_in_memory) { 927 | return ngx_http_next_body_filter(r, in); 928 | } 929 | 930 | b = &ctx->buffer; 931 | 932 | if (b->start == NULL) { 933 | dd("allocate buffer"); 934 | 935 | b->start = ngx_palloc(r->pool, conf->buffer_size); 936 | if (b->start == NULL) { 937 | return NGX_ERROR; 938 | } 939 | 940 | b->end = b->start + conf->buffer_size; 941 | b->pos = b->last = b->start; 942 | } 943 | 944 | for (cl = in; cl; cl = cl->next) { 945 | rest = b->end - b->last; 946 | if (rest == 0) { 947 | break; 948 | } 949 | 950 | if (!ngx_buf_in_memory(cl->buf)) { 951 | dd("buf not in memory!"); 952 | continue; 953 | } 954 | 955 | len = cl->buf->last - cl->buf->pos; 956 | 957 | if (len == 0) { 958 | continue; 959 | } 960 | 961 | if (len > (size_t) rest) { 962 | /* we truncate the exceeding part of the response body */ 963 | dd("truncate and ignore exceeding bufs"); 964 | len = rest; 965 | } 966 | 967 | dd("copied data '%.*s' (len %d, c0: %d)", (int) len, cl->buf->pos, 968 | (int) len, (int) *(cl->buf->pos)); 969 | b->last = ngx_copy(b->last, cl->buf->pos, len); 970 | } 971 | 972 | ngx_http_eval_discard_bufs(r->pool, in); 973 | 974 | return NGX_OK; 975 | } 976 | 977 | 978 | static void 979 | ngx_http_eval_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in) 980 | { 981 | ngx_chain_t *cl; 982 | 983 | for (cl = in; cl; cl = cl->next) { 984 | #if 0 985 | if (cl->buf->temporary && cl->buf->memory 986 | && ngx_buf_size(cl->buf) > 0) 987 | { 988 | ngx_pfree(pool, cl->buf->start); 989 | } 990 | #endif 991 | 992 | cl->buf->pos = cl->buf->last; 993 | } 994 | } 995 | 996 | 997 | /* XXX we also decode '+' to ' ' */ 998 | static void 999 | ngx_unescape_uri_patched(u_char **dst, u_char **src, size_t size, 1000 | ngx_uint_t type) 1001 | { 1002 | u_char *d, *s, ch, c, decoded; 1003 | enum { 1004 | sw_usual = 0, 1005 | sw_quoted, 1006 | sw_quoted_second 1007 | } state; 1008 | 1009 | d = *dst; 1010 | s = *src; 1011 | 1012 | state = 0; 1013 | decoded = 0; 1014 | 1015 | while (size--) { 1016 | 1017 | ch = *s++; 1018 | 1019 | switch (state) { 1020 | case sw_usual: 1021 | if (ch == '?' 1022 | && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT))) 1023 | { 1024 | *d++ = ch; 1025 | goto done; 1026 | } 1027 | 1028 | if (ch == '%') { 1029 | state = sw_quoted; 1030 | break; 1031 | } 1032 | 1033 | if (ch == '+') { 1034 | *d++ = ' '; 1035 | break; 1036 | } 1037 | 1038 | *d++ = ch; 1039 | break; 1040 | 1041 | case sw_quoted: 1042 | 1043 | if (ch >= '0' && ch <= '9') { 1044 | decoded = (u_char) (ch - '0'); 1045 | state = sw_quoted_second; 1046 | break; 1047 | } 1048 | 1049 | c = (u_char) (ch | 0x20); 1050 | if (c >= 'a' && c <= 'f') { 1051 | decoded = (u_char) (c - 'a' + 10); 1052 | state = sw_quoted_second; 1053 | break; 1054 | } 1055 | 1056 | /* the invalid quoted character */ 1057 | 1058 | state = sw_usual; 1059 | 1060 | *d++ = ch; 1061 | 1062 | break; 1063 | 1064 | case sw_quoted_second: 1065 | 1066 | state = sw_usual; 1067 | 1068 | if (ch >= '0' && ch <= '9') { 1069 | ch = (u_char) ((decoded << 4) + ch - '0'); 1070 | 1071 | if (type & NGX_UNESCAPE_REDIRECT) { 1072 | if (ch > '%' && ch < 0x7f) { 1073 | *d++ = ch; 1074 | break; 1075 | } 1076 | 1077 | *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); 1078 | 1079 | break; 1080 | } 1081 | 1082 | *d++ = ch; 1083 | 1084 | break; 1085 | } 1086 | 1087 | c = (u_char) (ch | 0x20); 1088 | if (c >= 'a' && c <= 'f') { 1089 | ch = (u_char) ((decoded << 4) + c - 'a' + 10); 1090 | 1091 | if (type & NGX_UNESCAPE_URI) { 1092 | if (ch == '?') { 1093 | *d++ = ch; 1094 | goto done; 1095 | } 1096 | 1097 | *d++ = ch; 1098 | break; 1099 | } 1100 | 1101 | if (type & NGX_UNESCAPE_REDIRECT) { 1102 | if (ch == '?') { 1103 | *d++ = ch; 1104 | goto done; 1105 | } 1106 | 1107 | if (ch > '%' && ch < 0x7f) { 1108 | *d++ = ch; 1109 | break; 1110 | } 1111 | 1112 | *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); 1113 | break; 1114 | } 1115 | 1116 | *d++ = ch; 1117 | 1118 | break; 1119 | } 1120 | 1121 | /* the invalid quoted character */ 1122 | 1123 | break; 1124 | } 1125 | } 1126 | 1127 | done: 1128 | 1129 | *dst = d; 1130 | *src = s; 1131 | } 1132 | -------------------------------------------------------------------------------- /t/bug.t: -------------------------------------------------------------------------------- 1 | # vi:filetype= 2 | 3 | use lib 'lib'; 4 | use Test::Nginx::Socket; # skip_all => 'ngx_memc storage commands do not work with the ngx_eval module'; 5 | 6 | repeat_each(2); 7 | 8 | plan tests => repeat_each() * 2 * blocks(); 9 | 10 | $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; 11 | 12 | no_long_string(); 13 | #no_diff; 14 | 15 | run_tests(); 16 | 17 | __DATA__ 18 | 19 | === TEST 1: bug 20 | --- config 21 | location = /eval { 22 | eval_escalate on; 23 | eval $var { 24 | set $foo bar; 25 | return 403; 26 | } 27 | return 405; 28 | } 29 | --- request 30 | GET /eval 31 | --- response_body_like: 403 Forbidden 32 | --- error_code: 403 33 | 34 | 35 | 36 | === TEST 2: bug 37 | --- SKIP 38 | --- config 39 | location /eval/ { 40 | rewrite /eval(.*)$ $1 break; 41 | eval $res { 42 | set $memc_key $host:$request_uri; 43 | set $memc_value 1; 44 | set $memc_cmd incr; 45 | memc_pass 127.0.0.1:11211; 46 | } 47 | resolver 10.40.6.72; 48 | proxy_pass http://www.yahoo.com/; 49 | } 50 | --- request 51 | GET /eval 52 | --- response_body 53 | 54 | 55 | 56 | === TEST 3: github issue #1: "eval" hangs in named locations. 57 | --- config 58 | location @eval { 59 | eval $var { 60 | proxy_pass http://127.0.0.1:$server_port/echo; 61 | } 62 | echo $var; 63 | } 64 | 65 | location /echo { 66 | echo hello; 67 | } 68 | 69 | location /t { 70 | echo_exec @eval; 71 | } 72 | --- request 73 | GET /t 74 | --- response_body 75 | hello 76 | 77 | -------------------------------------------------------------------------------- /t/crash.t: -------------------------------------------------------------------------------- 1 | # vi:filetype= 2 | 3 | use lib 'lib'; 4 | use Test::Nginx::Socket; # skip_all => 'ngx_memc storage commands do not work with the ngx_eval module'; 5 | 6 | repeat_each(2); 7 | 8 | plan tests => blocks() * (repeat_each() * 2); 9 | 10 | #no_shuffle(); 11 | run_tests(); 12 | 13 | __DATA__ 14 | 15 | === TEST 1: in server {} 16 | --- config 17 | eval $var { echo hi; } 18 | location /t { 19 | echo $var; 20 | } 21 | --- request 22 | GET /t 23 | --- must_die 24 | --- error_log eval 25 | qr/\[emerg\] .*? "eval" directive is not allowed here/ 26 | 27 | 28 | 29 | === TEST 2: in http {} 30 | --- http_config 31 | eval $var { echo hi; } 32 | --- config 33 | location = /t { 34 | echo $var; 35 | } 36 | --- request 37 | GET /t 38 | --- must_die 39 | --- error_log eval 40 | qr/\[emerg\] .*? "eval" directive is not allowed here/ 41 | 42 | -------------------------------------------------------------------------------- /t/memc.t: -------------------------------------------------------------------------------- 1 | # vi:filetype= 2 | 3 | use lib 'lib'; 4 | use Test::Nginx::Socket; # skip_all => 'ngx_memc storage commands do not work with the ngx_eval module'; 5 | 6 | plan tests => repeat_each() * (3 * blocks()); 7 | 8 | $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; 9 | 10 | no_long_string(); 11 | #no_diff; 12 | 13 | run_tests(); 14 | 15 | __DATA__ 16 | 17 | === TEST 1: set in eval (NO subrequest in memory) 18 | --- http_config 19 | upstream mc { 20 | server 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT; 21 | } 22 | 23 | --- config 24 | location = /main { 25 | echo_location /eval; 26 | echo_location /eval; 27 | } 28 | location = /eval { 29 | eval_subrequest_in_memory off; 30 | eval_override_content_type text/plain; 31 | eval $res { 32 | default_type 'text/plain'; 33 | set $memc_cmd 'set'; 34 | set $memc_key 'foo'; 35 | set $memc_value 'myvalue'; 36 | set $memc_exptime 24; 37 | memc_pass mc; 38 | } 39 | echo [$res]; 40 | } 41 | 42 | --- request 43 | GET /eval 44 | --- response_body 45 | [STORED] 46 | --- timeout: 3 47 | --- error_code: 200 48 | --- no_error_log 49 | [error] 50 | 51 | 52 | 53 | === TEST 2: set in eval (subrequest in memory) 54 | --- http_config 55 | upstream mc { 56 | server 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT; 57 | } 58 | 59 | --- config 60 | location = /main { 61 | echo_location /eval; 62 | echo_location /eval; 63 | } 64 | location = /eval { 65 | eval_subrequest_in_memory on; 66 | eval_override_content_type text/plain; 67 | eval $res { 68 | default_type 'text/plain'; 69 | set $memc_cmd 'set'; 70 | set $memc_key 'foo'; 71 | set $memc_value 'myvalue'; 72 | set $memc_exptime 24; 73 | memc_pass mc; 74 | } 75 | echo [$res]; 76 | } 77 | 78 | --- request 79 | GET /eval 80 | --- response_body 81 | [STORED] 82 | --- timeout: 3 83 | --- error_code: 200 84 | --- no_error_log 85 | [error] 86 | 87 | -------------------------------------------------------------------------------- /t/sanity.t: -------------------------------------------------------------------------------- 1 | # vi:ft= 2 | 3 | use lib 'lib'; 4 | use Test::Nginx::Socket; 5 | 6 | plan tests => repeat_each() * 2 * blocks(); 7 | 8 | no_long_string(); 9 | 10 | run_tests(); 11 | 12 | __DATA__ 13 | 14 | === TEST 1: eval 15 | --- config 16 | location /echo { 17 | eval_subrequest_in_memory off; 18 | eval $a { 19 | echo_before_body BEFORE; 20 | proxy_pass $scheme://127.0.0.1:$server_port/hi; 21 | } 22 | echo '!!! [$a]'; 23 | } 24 | location /hi { 25 | echo hi; 26 | } 27 | --- request 28 | GET /echo 29 | --- response_body 30 | !!! [BEFORE 31 | hi] 32 | 33 | 34 | 35 | === TEST 2: eval 36 | --- config 37 | location /echo { 38 | eval_subrequest_in_memory off; 39 | eval $a { 40 | default_type 'application/x-www-form-urlencoded'; 41 | echo_before_body a=32; 42 | echo howdy; 43 | } 44 | echo '!!! [$a]'; 45 | } 46 | --- request 47 | GET /echo 48 | --- response_body 49 | !!! [32] 50 | 51 | 52 | 53 | === TEST 3: eval with subrequest in memory 54 | --- config 55 | location /echo { 56 | eval_subrequest_in_memory on; 57 | eval $a { 58 | echo_before_body BEFORE; 59 | proxy_pass $scheme://127.0.0.1:$server_port/hi; 60 | } 61 | echo '!!! [$a]'; 62 | } 63 | location /hi { 64 | echo hi; 65 | } 66 | --- request 67 | GET /echo 68 | --- response_body 69 | !!! [hi] 70 | 71 | 72 | 73 | === TEST 4: eval with subrequest in memory 74 | --- config 75 | location /echo { 76 | eval_subrequest_in_memory on; 77 | eval $a { 78 | echo_before_body BEFORE; 79 | proxy_pass $scheme://127.0.0.1:$server_port/hi; 80 | } 81 | echo '!!! [$a]'; 82 | } 83 | location /hi { 84 | echo hi; 85 | } 86 | --- request 87 | GET /echo 88 | --- response_body 89 | !!! [hi] 90 | 91 | 92 | 93 | === TEST 5: eval with explicit buffer size 94 | --- config 95 | location /echo { 96 | eval_subrequest_in_memory off; 97 | eval_buffer_size 3; 98 | eval $a { 99 | echo_before_body BEFORE; 100 | proxy_pass $scheme://127.0.0.1:$server_port/hi; 101 | } 102 | echo '!!! [$a]'; 103 | } 104 | location /hi { 105 | echo hi; 106 | } 107 | --- request 108 | GET /echo 109 | --- response_body 110 | !!! [BEF] 111 | 112 | 113 | 114 | === TEST 6: eval + exec bug 115 | --- config 116 | location /test 117 | { 118 | echo_exec /initialize; 119 | } 120 | 121 | location /initialize 122 | { 123 | internal; 124 | eval_override_content_type 'text/plain'; 125 | 126 | eval $id 127 | { 128 | #rewrite ^(.*)$ /id; 129 | proxy_pass http://127.0.0.1:$server_port/id; 130 | } 131 | echo $id; 132 | } 133 | location /id { 134 | echo hi; 135 | } 136 | --- request 137 | GET /test 138 | --- response_body 139 | --- SKIP 140 | 141 | 142 | 143 | === TEST 7: inherit parent request's query_string 144 | --- config 145 | location /eval { 146 | eval_subrequest_in_memory off; 147 | eval $a { 148 | echo $arg_user; 149 | } 150 | echo '[$a]'; 151 | } 152 | --- request 153 | GET /eval?user=howdy 154 | --- response_body 155 | [howdy] 156 | 157 | 158 | 159 | === TEST 8: eval in subrequests 160 | --- config 161 | location /foo { 162 | add_before_body /bah; 163 | #echo_location_async /bah; 164 | echo done; 165 | } 166 | location /bah { 167 | eval_override_content_type 'text/plain'; 168 | eval $foo { 169 | proxy_pass $scheme://127.0.0.1:$server_port/baz; 170 | } 171 | echo [$foo]; 172 | } 173 | location /baz { 174 | echo baz; 175 | } 176 | --- request 177 | GET /foo 178 | --- response_body 179 | --- SKIP 180 | 181 | 182 | 183 | === TEST 9: unescape uri 184 | --- config 185 | location /echo { 186 | eval $a $b $c { 187 | proxy_pass $scheme://127.0.0.1:$server_port/encoded; 188 | } 189 | echo "a=[$a], b=[$b], c=[$c]"; 190 | } 191 | location /encoded { 192 | default_type 'application/x-www-form-urlencoded'; 193 | echo "a=&b=2&c=a+b%20c"; 194 | } 195 | --- request 196 | GET /echo 197 | --- response_body 198 | a=[], b=[2], c=[a b c] 199 | 200 | 201 | 202 | === TEST 10: sanity check 203 | --- config 204 | location /echo { 205 | eval_subrequest_in_memory off; 206 | #eval_subrequest_in_memory on; 207 | #eval_buffer_size 3; 208 | eval $a { 209 | #echo_before_body BEFORE; 210 | proxy_pass $scheme://127.0.0.1:$server_port/hi; 211 | #proxy_pass $scheme://127.0.0.1:1234/hi; 212 | } 213 | echo '!!! [$a]'; 214 | } 215 | location /hi { 216 | echo helloooooooooooooooooooo; 217 | } 218 | --- request 219 | GET /echo 220 | --- response_body 221 | !!! [helloooooooooooooooooooo] 222 | --- timeout: 10 223 | 224 | 225 | 226 | === TEST 11: eval with subrequest in memory 227 | --- config 228 | location /echo { 229 | eval_subrequest_in_memory on; 230 | eval $a { 231 | proxy_connect_timeout 10ms; 232 | proxy_pass http://www.taobao.com:1234; 233 | } 234 | echo '!!! [$a]'; 235 | } 236 | location /hi { 237 | echo hi; 238 | } 239 | --- request 240 | GET /echo 241 | --- response_body 242 | !!! [hi] 243 | --- SKIP 244 | 245 | -------------------------------------------------------------------------------- /util/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this file is mostly meant to be used by the author himself. 4 | 5 | root=`pwd` 6 | home=~ 7 | version=$1 8 | force=$2 9 | disable_pcre2=--without-pcre2 10 | answer=`../openresty/util/ver-ge "$version" 1.25.1` 11 | if [ "$answer" = "N" ]; then 12 | disable_pcre2=""; 13 | fi 14 | 15 | ngx-build $force $version \ 16 | $disable_pcre2 \ 17 | --with-http_addition_module \ 18 | --without-mail_pop3_module \ 19 | --without-mail_imap_module \ 20 | --without-mail_smtp_module \ 21 | --without-http_upstream_ip_hash_module \ 22 | --without-http_empty_gif_module \ 23 | --without-http_memcached_module \ 24 | --without-http_referer_module \ 25 | --without-http_autoindex_module \ 26 | --without-http_auth_basic_module \ 27 | --without-http_userid_module \ 28 | --add-module=$root $opts \ 29 | --add-module=$root/../echo-nginx-module \ 30 | --add-module=$root/../memc-nginx-module \ 31 | --with-debug 32 | #--add-module=$root/../vallery/eval-nginx-module \ 33 | #--add-module=$root/../ndk-nginx-module \ 34 | #--add-module=$home/work/nginx/nginx_upstream_hash-0.3 \ 35 | #--without-http_ssi_module # we cannot disable ssi because echo_location_async depends on it (i dunno why?!) 36 | 37 | -------------------------------------------------------------------------------- /util/wiki2pod.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use bytes; 6 | 7 | my @nl_counts; 8 | my $last_nl_count_level; 9 | 10 | my @bl_counts; 11 | my $last_bl_count_level; 12 | 13 | sub fmt_pos ($) { 14 | (my $s = $_[0]) =~ s{\#(.*)}{/"$1"}; 15 | $s; 16 | } 17 | 18 | sub fmt_mark ($$) { 19 | my ($tag, $s) = @_; 20 | my $max_level = 0; 21 | while ($s =~ /([<>])\1*/g) { 22 | my $level = length $&; 23 | if ($level > $max_level) { 24 | $max_level = $level; 25 | } 26 | } 27 | 28 | my $times = $max_level + 1; 29 | if ($times > 1) { 30 | $s = " $s "; 31 | } 32 | return $tag . ('<' x $times) . $s . ('>' x $times); 33 | } 34 | 35 | print "=encoding utf-8\n\n"; 36 | 37 | while (<>) { 38 | if ($. == 1) { 39 | # strip the leading U+FEFF byte in MS-DOS text files 40 | my $first = ord(substr($_, 0, 1)); 41 | #printf STDERR "0x%x", $first; 42 | #my $second = ord(substr($_, 2, 1)); 43 | #printf STDERR "0x%x", $second; 44 | if ($first == 0xEF) { 45 | substr($_, 0, 1, ''); 46 | #warn "Hit!"; 47 | } 48 | } 49 | s{\[(http[^ \]]+) ([^\]]*)\]}{$2 (L<$1>)}gi; 50 | s{ \[\[ ( [^\]\|]+ ) \| ([^\]]*) \]\] }{"L<$2|" . fmt_pos($1) . ">"}gixe; 51 | s{(.*?)}{fmt_mark('C', $1)}gie; 52 | s{'''(.*?)'''}{fmt_mark('B', $1)}ge; 53 | s{''(.*?)''}{fmt_mark('I', $1)}ge; 54 | if (s{^\s*<[^>]+>\s*$}{}) { 55 | next; 56 | } 57 | 58 | if (/^\s*$/) { 59 | print "\n"; 60 | next; 61 | } 62 | 63 | =begin cmt 64 | 65 | if ($. == 1) { 66 | warn $_; 67 | for my $i (0..length($_) - 1) { 68 | my $chr = substr($_, $i, 1); 69 | warn "chr ord($i): ".ord($chr)." \"$chr\"\n"; 70 | } 71 | } 72 | 73 | =end cmt 74 | =cut 75 | 76 | if (/(=+) (.*) \1$/) { 77 | #warn "HERE! $_" if $. == 1; 78 | my ($level, $title) = (length $1, $2); 79 | collapse_lists(); 80 | 81 | print "\n=head$level $title\n\n"; 82 | } elsif (/^(\#+) (.*)/) { 83 | my ($level, $txt) = (length($1) - 1, $2); 84 | if (defined $last_nl_count_level && $level != $last_nl_count_level) { 85 | print "\n=back\n\n"; 86 | } 87 | $last_nl_count_level = $level; 88 | $nl_counts[$level] ||= 0; 89 | if ($nl_counts[$level] == 0) { 90 | print "\n=over\n\n"; 91 | } 92 | $nl_counts[$level]++; 93 | print "\n=item $nl_counts[$level].\n\n"; 94 | print "$txt\n"; 95 | } elsif (/^(\*+) (.*)/) { 96 | my ($level, $txt) = (length($1) - 1, $2); 97 | if (defined $last_bl_count_level && $level != $last_bl_count_level) { 98 | print "\n=back\n\n"; 99 | } 100 | $last_bl_count_level = $level; 101 | $bl_counts[$level] ||= 0; 102 | if ($bl_counts[$level] == 0) { 103 | print "\n=over\n\n"; 104 | } 105 | $bl_counts[$level]++; 106 | print "\n=item *\n\n"; 107 | print "$txt\n"; 108 | } else { 109 | collapse_lists(); 110 | print; 111 | } 112 | } 113 | 114 | collapse_lists(); 115 | 116 | sub collapse_lists { 117 | while (defined $last_nl_count_level && $last_nl_count_level >= 0) { 118 | print "\n=back\n\n"; 119 | $last_nl_count_level--; 120 | } 121 | undef $last_nl_count_level; 122 | undef @nl_counts; 123 | 124 | while (defined $last_bl_count_level && $last_bl_count_level >= 0) { 125 | print "\n=back\n\n"; 126 | $last_bl_count_level--; 127 | } 128 | undef $last_bl_count_level; 129 | undef @bl_counts; 130 | } 131 | 132 | -------------------------------------------------------------------------------- /valgrind.suppress: -------------------------------------------------------------------------------- 1 | { 2 | 3 | Memcheck:Param 4 | socketcall.sendmsg(msg.msg_iov[i]) 5 | fun:sendmsg 6 | fun:ngx_write_channel 7 | fun:ngx_signal_worker_processes 8 | fun:ngx_master_process_cycle 9 | fun:main 10 | } 11 | { 12 | 13 | Memcheck:Param 14 | socketcall.sendmsg(msg.msg_iov[i]) 15 | fun:__sendmsg_nocancel 16 | fun:ngx_write_channel 17 | fun:ngx_signal_worker_processes 18 | fun:ngx_master_process_cycle 19 | fun:main 20 | } 21 | { 22 | 23 | Memcheck:Param 24 | socketcall.sendmsg(msg.msg_iov[i]) 25 | fun:__sendmsg_nocancel 26 | fun:ngx_write_channel 27 | fun:ngx_master_process_cycle 28 | fun:main 29 | } 30 | { 31 | 32 | Memcheck:Param 33 | socketcall.sendmsg(msg.msg_iov[i]) 34 | fun:__sendmsg_nocancel 35 | fun:ngx_write_channel 36 | fun:ngx_pass_open_channel 37 | fun:ngx_start_worker_processes 38 | fun:ngx_master_process_cycle 39 | fun:main 40 | } 41 | { 42 | 43 | Memcheck:Leak 44 | fun:malloc 45 | fun:ngx_alloc 46 | fun:ngx_event_process_init 47 | } 48 | { 49 | 50 | Memcheck:Leak 51 | fun:malloc 52 | fun:ngx_alloc 53 | fun:ngx_calloc 54 | fun:ngx_event_process_init 55 | } 56 | { 57 | 58 | Memcheck:Param 59 | epoll_ctl(event) 60 | fun:epoll_ctl 61 | } 62 | { 63 | 64 | Memcheck:Leak 65 | fun:malloc 66 | fun:ngx_alloc 67 | fun:ngx_create_pool 68 | fun:ngx_http_init_request 69 | } 70 | { 71 | 72 | Memcheck:Leak 73 | fun:malloc 74 | fun:ngx_alloc 75 | fun:ngx_create_pool 76 | fun:ngx_event_accept 77 | } 78 | { 79 | nginx-core-process-init 80 | Memcheck:Leak 81 | fun:malloc 82 | fun:ngx_alloc 83 | fun:ngx_event_process_init 84 | } 85 | { 86 | nginx-core-crc32-init 87 | Memcheck:Leak 88 | fun:malloc 89 | fun:ngx_alloc 90 | fun:ngx_crc32_table_init 91 | fun:main 92 | } 93 | { 94 | palloc_large_for_init_request 95 | Memcheck:Leak 96 | fun:malloc 97 | fun:ngx_alloc 98 | fun:ngx_palloc_large 99 | fun:ngx_palloc 100 | fun:ngx_pcalloc 101 | fun:ngx_http_init_request 102 | fun:ngx_epoll_process_events 103 | fun:ngx_process_events_and_timers 104 | } 105 | { 106 | palloc_large_for_create_temp_buf 107 | Memcheck:Leak 108 | fun:malloc 109 | fun:ngx_alloc 110 | fun:ngx_palloc_large 111 | fun:ngx_palloc 112 | fun:ngx_create_temp_buf 113 | fun:ngx_http_init_request 114 | fun:ngx_epoll_process_events 115 | fun:ngx_process_events_and_timers 116 | } 117 | { 118 | accept_create_pool 119 | Memcheck:Leak 120 | fun:memalign 121 | fun:posix_memalign 122 | fun:ngx_memalign 123 | fun:ngx_create_pool 124 | fun:ngx_event_accept 125 | fun:ngx_epoll_process_events 126 | fun:ngx_process_events_and_timers 127 | } 128 | { 129 | create_pool_for_init_req 130 | Memcheck:Leak 131 | fun:memalign 132 | fun:posix_memalign 133 | fun:ngx_memalign 134 | fun:ngx_create_pool 135 | fun:ngx_http_init_request 136 | fun:ngx_epoll_process_events 137 | fun:ngx_process_events_and_timers 138 | } 139 | { 140 | 141 | Memcheck:Cond 142 | fun:index 143 | fun:expand_dynamic_string_token 144 | fun:_dl_map_object 145 | fun:map_doit 146 | fun:_dl_catch_error 147 | fun:do_preload 148 | fun:dl_main 149 | } 150 | { 151 | 152 | Memcheck:Leak 153 | match-leak-kinds: definite 154 | fun:malloc 155 | fun:ngx_alloc 156 | fun:ngx_set_environment 157 | fun:ngx_single_process_cycle 158 | } 159 | { 160 | 161 | Memcheck:Leak 162 | match-leak-kinds: definite 163 | fun:malloc 164 | fun:ngx_alloc 165 | fun:ngx_set_environment 166 | fun:ngx_worker_process_init 167 | fun:ngx_worker_process_cycle 168 | } 169 | --------------------------------------------------------------------------------