From e5b8c8f46e41a0d7a7735ac15b14da5369670b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Thu, 13 Oct 2011 11:08:57 +0800 Subject: [PATCH 01/46] bugfix: calling ngx.exec() to jump to a named location did not clear the context object of ngx_lua properly and might cause evil problems. thanks Nginx User. --- README | 4 ++-- README.markdown | 2 +- doc/HttpLuaModule.wiki | 2 +- src/ngx_http_lua_util.c | 4 ++++ t/014-bugs.t | 18 ++++++++++++++++++ t/016-resp-header.t | 2 +- 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/README b/README index 6f621f52..804a7cc6 100644 --- a/README +++ b/README @@ -8,9 +8,9 @@ Status This module is under active development and is already production ready. Version - This document describes ngx_lua v0.3.1rc9 + This document describes ngx_lua v0.3.1rc10 () released - on 9 October 2011. + on 13 October 2011. Synopsis # set search paths for pure Lua external libraries (';;' is the default path): diff --git a/README.markdown b/README.markdown index 56d3b1e3..76af95e5 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc9](https://github.com/chaoslawful/lua-nginx-module/downloads) released on 9 October 2011. +This document describes ngx_lua [v0.3.1rc10](https://github.com/chaoslawful/lua-nginx-module/downloads) released on 13 October 2011. Synopsis ======== diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 1ccf45f1..8322b8e6 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/downloads v0.3.1rc9] released on 9 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/downloads v0.3.1rc10] released on 13 October 2011. = Synopsis = diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 394b1d77..9267ce68 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1448,6 +1448,10 @@ ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, r->write_event_handler = ngx_http_request_empty_handler; + /* clear our ctx because ngx_http_named_location + * won't do that for us */ + ngx_http_set_ctx(r, NULL, ngx_http_lua_module); + rc = ngx_http_named_location(r, &ctx->exec_uri); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { diff --git a/t/014-bugs.t b/t/014-bugs.t index 08345996..e1d69789 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -485,3 +485,21 @@ world nil nil + + +=== TEST 23: jump to internal locations requires ctx cleared +--- config + location @proxy { + rewrite_by_lua return; + echo hello; + } + location /main { + content_by_lua ' + ngx.exec("@proxy") + '; + } +--- request + GET /main +--- response_body +hello + diff --git a/t/016-resp-header.t b/t/016-resp-header.t index 8f8504f3..6d1d6454 100644 --- a/t/016-resp-header.t +++ b/t/016-resp-header.t @@ -686,7 +686,7 @@ WWW-Authenticate: blah -=== TEST 34: set and clear the www-authenticate response header +=== TEST 35: set and clear the www-authenticate response header --- config location /lua { content_by_lua ' From 8f849641936b7df2262cab077c1b80821edb4065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Thu, 13 Oct 2011 11:29:55 +0800 Subject: [PATCH 02/46] fixed the download page links in docs. --- README | 8 ++++---- README.markdown | 6 +++--- doc/HttpLuaModule.wiki | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README b/README index 804a7cc6..785f80cc 100644 --- a/README +++ b/README @@ -9,8 +9,8 @@ Status Version This document describes ngx_lua v0.3.1rc10 - () released - on 13 October 2011. + () released on 13 + October 2011. Synopsis # set search paths for pure Lua external libraries (';;' is the default path): @@ -1953,11 +1953,11 @@ Installation 2. Download the latest version of the release tarball of the ngx_devel_kit (NDK) module from lua-nginx-module file list - (). + (). 3. Download the latest version of the release tarball of this module from lua-nginx-module file list - (). + (). 4. Grab the nginx source code from nginx.org (), for example, the version 1.0.5 (see nginx compatibility), and then build diff --git a/README.markdown b/README.markdown index 76af95e5..5bb8a243 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc10](https://github.com/chaoslawful/lua-nginx-module/downloads) released on 13 October 2011. +This document describes ngx_lua [v0.3.1rc10](https://github.com/chaoslawful/lua-nginx-module/tags) released on 13 October 2011. Synopsis ======== @@ -2087,8 +2087,8 @@ The installation steps are usually as simple as `./configure && make && make ins Alternatively, you can compile this module with nginx core's source by hand: 1. Install Lua or LuaJIT into your system. At least Lua 5.1 is required. Lua can be obtained freely from its project [homepage](http://www.lua.org/). For Ubuntu/Debian users, just install the liblua5.1-0-dev package (or something like that). -1. Download the latest version of the release tarball of the ngx_devel_kit (NDK) module from lua-nginx-module [file list](http://github.com/simpl/ngx_devel_kit/downloads). -1. Download the latest version of the release tarball of this module from lua-nginx-module [file list](http://github.com/chaoslawful/lua-nginx-module/downloads). +1. Download the latest version of the release tarball of the ngx_devel_kit (NDK) module from lua-nginx-module [file list](http://github.com/simpl/ngx_devel_kit/tags). +1. Download the latest version of the release tarball of this module from lua-nginx-module [file list](http://github.com/chaoslawful/lua-nginx-module/tags). 1. Grab the nginx source code from [nginx.org](http://nginx.org/), for example, the version 1.0.5 (see nginx compatibility), and then build the source with this module: $ wget 'http://nginx.org/download/nginx-1.0.5.tar.gz' diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 8322b8e6..b3756515 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/downloads v0.3.1rc10] released on 13 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc10] released on 13 October 2011. = Synopsis = @@ -2015,8 +2015,8 @@ The installation steps are usually as simple as ./configure && make && mak Alternatively, you can compile this module with nginx core's source by hand: # Install Lua or LuaJIT into your system. At least Lua 5.1 is required. Lua can be obtained freely from its project [http://www.lua.org/ homepage]. For Ubuntu/Debian users, just install the liblua5.1-0-dev package (or something like that). -# Download the latest version of the release tarball of the ngx_devel_kit (NDK) module from lua-nginx-module [http://github.com/simpl/ngx_devel_kit/downloads file list]. -# Download the latest version of the release tarball of this module from lua-nginx-module [http://github.com/chaoslawful/lua-nginx-module/downloads file list]. +# Download the latest version of the release tarball of the ngx_devel_kit (NDK) module from lua-nginx-module [http://github.com/simpl/ngx_devel_kit/tags file list]. +# Download the latest version of the release tarball of this module from lua-nginx-module [http://github.com/chaoslawful/lua-nginx-module/tags file list]. # Grab the nginx source code from [http://nginx.org/ nginx.org], for example, the version 1.0.5 (see nginx compatibility), and then build the source with this module: $ wget 'http://nginx.org/download/nginx-1.0.5.tar.gz' From 0457ff206db5aebf6a7b01e762242b864dec8e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Thu, 13 Oct 2011 14:11:53 +0800 Subject: [PATCH 03/46] more treatment of the previous fix. --- src/ngx_http_lua_util.c | 3 +++ t/014-bugs.t | 36 +++++++++++++++++++++++++++++++++++- util/build.sh | 3 ++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 9267ce68..9b0b1dfb 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -629,6 +629,7 @@ ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L, ctx->entered_rewrite_phase = 0; ctx->entered_access_phase = 0; + ctx->entered_content_phase = 0; ctx->exit_code = 0; ctx->exited = 0; @@ -1448,9 +1449,11 @@ ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, r->write_event_handler = ngx_http_request_empty_handler; +#if 1 /* clear our ctx because ngx_http_named_location * won't do that for us */ ngx_http_set_ctx(r, NULL, ngx_http_lua_module); +#endif rc = ngx_http_named_location(r, &ctx->exec_uri); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) diff --git a/t/014-bugs.t b/t/014-bugs.t index e1d69789..4adecd94 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -5,7 +5,7 @@ use Test::Nginx::Socket; #worker_connections(1014); #master_on(); -#log_level('warn'); +log_level('debug'); #repeat_each(120); #repeat_each(3); @@ -503,3 +503,37 @@ nil --- response_body hello + +=== TEST 23: reset ctx +--- config + location @proxy { + rewrite_by_lua return; + echo hello; + } + location /main { + rewrite_by_lua ' + ngx.exec("@proxy") + '; + } +--- request + GET /main +--- response_body +hello + + + +=== TEST 24: set special variables +--- config + location /main { + #set_unescape_uri $cookie_a "hello"; + set $http_a "hello"; + content_by_lua ' + ngx.say(ngx.var.http_a) + '; + } +--- request + GET /main +--- response_body +hello +--- SKIP + diff --git a/util/build.sh b/util/build.sh index 1e698eaa..66178177 100755 --- a/util/build.sh +++ b/util/build.sh @@ -22,7 +22,8 @@ if [[ "$BUILD_CLEAN" -eq 1 || ! -f Makefile || "$root/config" -nt Makefile || "$ ./configure --prefix=$root/work \ --add-module=$root \ --add-module=$root/deps/ngx_devel_kit \ - $opts + $opts \ + --with-debug fi if [ -f $root/work/sbin/nginx ]; then From c5fbee750cdfefedca7703825a313c084af6293f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Thu, 13 Oct 2011 20:38:01 +0800 Subject: [PATCH 04/46] now we explicitly clear all the modules' contexts before calling ngx_http_named_location. --- src/ngx_http_lua_util.c | 5 +-- t/014-bugs.t | 37 +--------------- t/017-exec.t | 98 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 39 deletions(-) diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 9b0b1dfb..6f981c22 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1450,9 +1450,8 @@ ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, r->write_event_handler = ngx_http_request_empty_handler; #if 1 - /* clear our ctx because ngx_http_named_location - * won't do that for us */ - ngx_http_set_ctx(r, NULL, ngx_http_lua_module); + /* clear the modules contexts */ + ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); #endif rc = ngx_http_named_location(r, &ctx->exec_uri); diff --git a/t/014-bugs.t b/t/014-bugs.t index 4adecd94..f260c28f 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -487,42 +487,7 @@ nil -=== TEST 23: jump to internal locations requires ctx cleared ---- config - location @proxy { - rewrite_by_lua return; - echo hello; - } - location /main { - content_by_lua ' - ngx.exec("@proxy") - '; - } ---- request - GET /main ---- response_body -hello - - -=== TEST 23: reset ctx ---- config - location @proxy { - rewrite_by_lua return; - echo hello; - } - location /main { - rewrite_by_lua ' - ngx.exec("@proxy") - '; - } ---- request - GET /main ---- response_body -hello - - - -=== TEST 24: set special variables +=== TEST 23: set special variables --- config location /main { #set_unescape_uri $cookie_a "hello"; diff --git a/t/017-exec.t b/t/017-exec.t index db3fccdc..a9fd925f 100644 --- a/t/017-exec.t +++ b/t/017-exec.t @@ -420,3 +420,101 @@ GET /lua foo = 3 bar = 4 + + +=== TEST 19: jump to internal locations requires ctx cleared +--- config + location @proxy { + rewrite_by_lua return; + echo hello; + } + location /main { + content_by_lua ' + ngx.exec("@proxy") + '; + } +--- request + GET /main +--- response_body +hello + + + +=== TEST 20: reset ctx +--- config + location @proxy { + rewrite_by_lua return; + echo hello; + } + location /main { + rewrite_by_lua ' + ngx.exec("@proxy") + '; + } +--- request + GET /main +--- response_body +hello + + + +=== TEST 21: exec(named location) in subrequests +--- config + location /entry { + echo_location /foo; + echo_location /foo2; + } + location /foo { + content_by_lua ' + ngx.exec("@bar") + '; + } + location /foo2 { + content_by_lua ' + ngx.exec("@bar") + '; + } + + location @bar { + proxy_pass http://127.0.0.1:$server_port/bar; + } + location /bar { + echo hello; + } +--- request + GET /entry +--- response_body +hello +hello + + + +=== TEST 22: exec(normal location) in subrequests +--- config + location /entry { + echo_location /foo; + echo_location /foo2; + } + location /foo { + content_by_lua ' + ngx.exec("/baz") + '; + } + location /foo2 { + content_by_lua ' + ngx.exec("/baz") + '; + } + + location /baz { + proxy_pass http://127.0.0.1:$server_port/bar; + } + location /bar { + echo hello; + } +--- request + GET /entry +--- response_body +hello +hello + From 3c4b1ff31d1445f243a1083201881731f697e31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sat, 15 Oct 2011 14:40:36 +0800 Subject: [PATCH 05/46] added one more (passing) test case. --- t/014-bugs.t | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/t/014-bugs.t b/t/014-bugs.t index f260c28f..258759d3 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -502,3 +502,25 @@ nil hello --- SKIP + + +=== TEST 24: rewrite last before rewrite_by_lua +--- config + location @pretty { + rewrite ^/main/xyz\.html$ /abc.html last; + rewrite_by_lua 'ngx.exec("@proxy")'; + } + location @proxy { + echo hello; + } + location /abc.html { + echo abc; + } + location /main { + try_files $uri $uri/ @pretty; + } +--- request + GET /main/xyz.html +--- response_body +abc + From 75f960b068db1010fec89f3860481dffa6fc811d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 06:56:45 +0800 Subject: [PATCH 06/46] added constant ngx.HTTP_METHOD_NOT_IMPLEMENTED. thanks Nginx User. --- src/ngx_http_lua_consts.c | 3 +++ t/005-exit.t | 28 ++++++++++++++++++++++++++++ t/014-bugs.t | 8 ++++++-- util/build2.sh | 4 ++-- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/ngx_http_lua_consts.c b/src/ngx_http_lua_consts.c index 03d413e5..f1dc2783 100644 --- a/src/ngx_http_lua_consts.c +++ b/src/ngx_http_lua_consts.c @@ -90,6 +90,9 @@ ngx_http_lua_inject_http_consts(lua_State *L) lua_pushinteger(L, NGX_HTTP_INTERNAL_SERVER_ERROR); lua_setfield(L, -2, "HTTP_INTERNAL_SERVER_ERROR"); + lua_pushinteger(L, 501); + lua_setfield(L, -2, "HTTP_METHOD_NOT_IMPLEMENTED"); + lua_pushinteger(L, NGX_HTTP_SERVICE_UNAVAILABLE); lua_setfield(L, -2, "HTTP_SERVICE_UNAVAILABLE"); /* }}} */ diff --git a/t/005-exit.t b/t/005-exit.t index addd45cd..83a8f168 100644 --- a/t/005-exit.t +++ b/t/005-exit.t @@ -439,3 +439,31 @@ GET /lua --- response_body hello + + +=== TEST 12: 501 Method Not Implemented +--- config + location /lua { + content_by_lua ' + ngx.exit(501) + '; + } +--- request +GET /lua +--- error_code: 501 +--- response_body_like: 501 Method Not Implemented + + + +=== TEST 13: 501 Method Not Implemented +--- config + location /lua { + content_by_lua ' + ngx.exit(ngx.HTTP_METHOD_NOT_IMPLEMENTED) + '; + } +--- request +GET /lua +--- error_code: 501 +--- response_body_like: 501 Method Not Implemented + diff --git a/t/014-bugs.t b/t/014-bugs.t index 258759d3..3402612a 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -511,9 +511,12 @@ hello rewrite_by_lua 'ngx.exec("@proxy")'; } location @proxy { - echo hello; + proxy_pass http://127.0.0.1:$server_port/blah; } - location /abc.html { + location ~ /abc.html { + rewrite_by_lua 'ngx.exec("@abc")'; + } + location @abc { echo abc; } location /main { @@ -523,4 +526,5 @@ hello GET /main/xyz.html --- response_body abc +--- SKIP diff --git a/util/build2.sh b/util/build2.sh index d40adc3a..e841dd28 100755 --- a/util/build2.sh +++ b/util/build2.sh @@ -35,6 +35,6 @@ ngx-build $force $version \ --add-module=$root/../drizzle-nginx-module \ --add-module=$home/work/nginx/ngx_http_upstream_keepalive-2ce9d8a1ca93 \ --add-module=$root/../rds-json-nginx-module \ - $opts #\ - #--with-debug + $opts \ + --with-debug From 0bb8899566a55e43f2d3bfa3b4b9647b1e7acb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 07:00:40 +0800 Subject: [PATCH 07/46] updated docs to reflect recent changes. --- README | 24 +++++++++++++++++++----- README.markdown | 19 ++++++++++++++----- doc/HttpLuaModule.wiki | 19 ++++++++++++++----- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/README b/README index 785f80cc..dd645944 100644 --- a/README +++ b/README @@ -8,8 +8,8 @@ Status This module is under active development and is already production ready. Version - This document describes ngx_lua v0.3.1rc10 - () released on 13 + This document describes ngx_lua v0.3.1rc12 + () released on 16 October 2011. Synopsis @@ -777,6 +777,7 @@ Nginx API for Lua (401) value = ngx.HTTP_FORBIDDEN (403) value = ngx.HTTP_NOT_FOUND (404) value = ngx.HTTP_NOT_ALLOWED (405) value = ngx.HTTP_GONE (410) value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) value = + ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) value = ngx.HTTP_SERVICE_UNAVAILABLE (503) Nginx log level constants @@ -1395,6 +1396,10 @@ Nginx API for Lua This is our own content + Number literals can be used directly as the argument, for instance, + + ngx.exit(501) + ngx.eof syntax: *ngx.eof()* @@ -2094,13 +2099,22 @@ Test Suite 10. rds-json-nginx-module TODO - * Add "ignore_resp_headers", "ignore_resp_body", and "ignore_resp" + * add "ignore_resp_headers", "ignore_resp_body", and "ignore_resp" options to ngx.location.capture and ngx.location.capture_multi` methods, to allow micro performance tuning on the user side. - * Add directives to run lua codes when nginx stops/reloads. + * add directives to run lua codes when nginx stops/reloads. - * Deal with TCP 3-second delay problem under great connection harness. + * deal with TCP 3-second delay problem under great connection harness. + + * add options to ngx.location.capture and ngx.location.capture_multi + in order to share and copy a particular set of nginx variables with + subrequests, specified by the user. + + * add an option to ngx.location.capture and ngx.location.capture_multi + so as to specify the ngx.ctx table for subrequests. + + * expose nginx's shared memory facility to the Lua land. Future Plan * Add the "lua_require" directive to load module into main thread's diff --git a/README.markdown b/README.markdown index 5bb8a243..70c9d6b2 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc10](https://github.com/chaoslawful/lua-nginx-module/tags) released on 13 October 2011. +This document describes ngx_lua [v0.3.1rc12](https://github.com/chaoslawful/lua-nginx-module/tags) released on 16 October 2011. Synopsis ======== @@ -801,6 +801,7 @@ HTTP status constants value = ngx.HTTP_NOT_ALLOWED (405) value = ngx.HTTP_GONE (410) value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) + value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) value = ngx.HTTP_SERVICE_UNAVAILABLE (503) @@ -1585,6 +1586,12 @@ The effect in action: This is our own content +Number literals can be used directly as the argument, for instance, + + + ngx.exit(501) + + ngx.eof ------- **syntax:** *ngx.eof()* @@ -2187,10 +2194,12 @@ filtering chain affects a lot. The correct configure adding order is: TODO ==== - -* Add `ignore_resp_headers`, `ignore_resp_body`, and `ignore_resp` options to [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) and ngx.location.capture_multi` methods, to allow micro performance tuning on the user side. -* Add directives to run lua codes when nginx stops/reloads. -* Deal with TCP 3-second delay problem under great connection harness. +* add `ignore_resp_headers`, `ignore_resp_body`, and `ignore_resp` options to [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) and ngx.location.capture_multi` methods, to allow micro performance tuning on the user side. +* add directives to run lua codes when nginx stops/reloads. +* deal with TCP 3-second delay problem under great connection harness. +* add options to [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) and [ngx.location.capture_multi](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi) in order to share and copy a particular set of nginx variables with subrequests, specified by the user. +* add an option to [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) and [ngx.location.capture_multi](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi) so as to specify the [ngx.ctx](http://wiki.nginx.org/HttpLuaModule#ngx.ctx) table for subrequests. +* expose nginx's shared memory facility to the Lua land. Future Plan =========== diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index b3756515..e5094962 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc10] released on 13 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc12] released on 16 October 2011. = Synopsis = @@ -774,6 +774,7 @@ These constants are usually used in [[#ngx.location.capture|ngx.location.catpure value = ngx.HTTP_NOT_ALLOWED (405) value = ngx.HTTP_GONE (410) value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) + value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) value = ngx.HTTP_SERVICE_UNAVAILABLE (503) @@ -1537,6 +1538,12 @@ The effect in action: This is our own content +Number literals can be used directly as the argument, for instance, + + + ngx.exit(501) + + == ngx.eof == '''syntax:''' ''ngx.eof()'' @@ -2110,10 +2117,12 @@ filtering chain affects a lot. The correct configure adding order is: # rds-json-nginx-module = TODO = - -* Add ignore_resp_headers, ignore_resp_body, and ignore_resp options to [[#ngx.location.capture|ngx.location.capture]] and ngx.location.capture_multi` methods, to allow micro performance tuning on the user side. -* Add directives to run lua codes when nginx stops/reloads. -* Deal with TCP 3-second delay problem under great connection harness. +* add ignore_resp_headers, ignore_resp_body, and ignore_resp options to [[#ngx.location.capture|ngx.location.capture]] and ngx.location.capture_multi` methods, to allow micro performance tuning on the user side. +* add directives to run lua codes when nginx stops/reloads. +* deal with TCP 3-second delay problem under great connection harness. +* add options to [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] in order to share and copy a particular set of nginx variables with subrequests, specified by the user. +* add an option to [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] so as to specify the [[#ngx.ctx|ngx.ctx]] table for subrequests. +* expose nginx's shared memory facility to the Lua land. = Future Plan = From 2df39a9e1038c0000020aedf5fe453997cb4b638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 09:11:46 +0800 Subject: [PATCH 08/46] now we implemented ngx.req.set_uri() and ngx.req.set_uri_args() to emulate ngx_rewrite's rewrite directive (without redirect or permanent modifiers). --- src/ngx_http_lua_args.c | 123 ++++++++++++++++++++++++- t/{030-query-args.t => 030-uri-args.t} | 102 +++++++++++++++++++- 2 files changed, 222 insertions(+), 3 deletions(-) rename t/{030-query-args.t => 030-uri-args.t} (78%) diff --git a/src/ngx_http_lua_args.c b/src/ngx_http_lua_args.c index 162fe2c9..c1139915 100644 --- a/src/ngx_http_lua_args.c +++ b/src/ngx_http_lua_args.c @@ -10,11 +10,124 @@ static int ngx_http_lua_parse_args(ngx_http_request_t *r, lua_State *L, u_char *buf, u_char *last); +static int ngx_http_lua_ngx_req_set_uri_args(lua_State *L); static int ngx_http_lua_ngx_req_get_uri_args(lua_State *L); static int ngx_http_lua_ngx_req_get_post_args(lua_State *L); +static int ngx_http_lua_ngx_req_set_uri(lua_State *L); -int +static int +ngx_http_lua_ngx_req_set_uri(lua_State *L) { + ngx_http_request_t *r; + size_t len; + u_char *p; + int n; + int break_cycle = 0; + + n = lua_gettop(L); + + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 argument but seen %d", n); + } + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + if (n == 2) { + luaL_checktype(L, 2, LUA_TBOOLEAN); + break_cycle = lua_toboolean(L, 2); + } + + p = (u_char *) luaL_checklstring(L, 1, &len); + + if (len == 0) { + return luaL_error(L, "attempt to use zero-length uri"); + } + + r->uri.data = ngx_palloc(r->pool, len); + ngx_memcpy(r->uri.data, p, len); + + r->uri.len = len; + + r->internal = 1; + r->valid_unparsed_uri = 0; + + if (break_cycle) { + r->valid_location = 0; + r->uri_changed = 0; + + } else { + r->uri_changed = 1; + } + + ngx_http_set_exten(r); + + return 0; +} + + +static int +ngx_http_lua_ngx_req_set_uri_args(lua_State *L) { + ngx_http_request_t *r; + ngx_str_t args; + const char *msg; + size_t len; + u_char *p; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument but seen %d", + lua_gettop(L)); + } + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = (u_char *) lua_tolstring(L, 1, &len); + + args.data = ngx_palloc(r->pool, len); + ngx_memcpy(args.data, p, len); + + args.len = len; + break; + + case LUA_TTABLE: + ngx_http_lua_process_args_option(r, L, 1, &args); + + dd("args: %.*s", (int) args.len, args.data); + + break; + + default: + msg = lua_pushfstring(L, "string, number, or table expected, " + "but got %s", luaL_typename(L, 2)); + return luaL_argerror(L, 1, msg); + } + + dd("args: %.*s", (int) args.len, args.data); + + r->args.data = args.data; + r->args.len = args.len; + + r->valid_unparsed_uri = 0; + + return 0; +} + + +static int ngx_http_lua_ngx_req_get_uri_args(lua_State *L) { ngx_http_request_t *r; u_char *buf; @@ -56,7 +169,7 @@ ngx_http_lua_ngx_req_get_uri_args(lua_State *L) { } -int +static int ngx_http_lua_ngx_req_get_post_args(lua_State *L) { ngx_http_request_t *r; @@ -253,6 +366,12 @@ ngx_http_lua_parse_args(ngx_http_request_t *r, lua_State *L, u_char *buf, void ngx_http_lua_inject_req_args_api(lua_State *L) { + lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri); + lua_setfield(L, -2, "set_uri"); + + lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri_args); + lua_setfield(L, -2, "set_uri_args"); + lua_pushcfunction(L, ngx_http_lua_ngx_req_get_uri_args); lua_setfield(L, -2, "get_uri_args"); diff --git a/t/030-query-args.t b/t/030-uri-args.t similarity index 78% rename from t/030-query-args.t rename to t/030-uri-args.t index fc46b836..725e17c9 100644 --- a/t/030-query-args.t +++ b/t/030-uri-args.t @@ -2,7 +2,7 @@ use lib 'lib'; use Test::Nginx::Socket; -#worker_connections(1014); +worker_connections(1014); #master_on(); #workers(2); #log_level('warn'); @@ -12,6 +12,8 @@ repeat_each(2); plan tests => repeat_each() * (blocks() * 2); +no_root_location(); + #no_diff(); #no_long_string(); run_tests(); @@ -341,3 +343,101 @@ a = 3 b = 4 done + + +=== TEST 12: rewrite args +--- config + location /bar { + echo $query_string; + } + location /foo { + #set $args 'hello'; + rewrite_by_lua ' + ngx.req.set_uri("/bar"); + ngx.req.set_uri_args("hello") + '; + proxy_pass http://127.0.0.1:$server_port; + } +--- request + GET /foo?world +--- response_body +hello + + + +=== TEST 13: rewrite args (not break cycle by default) +--- config + location /bar { + echo "bar: $uri?$args"; + } + location /foo { + #set $args 'hello'; + rewrite_by_lua ' + ngx.req.set_uri("/bar") + ngx.req.set_uri_args("hello") + '; + echo "foo: $uri?$args"; + } +--- request + GET /foo?world +--- response_body +bar: /bar?hello + + + +=== TEST 14: rewrite (not break cycle explicitly) +--- config + location /bar { + echo "bar: $uri?$args"; + } + location /foo { + #set $args 'hello'; + rewrite_by_lua ' + ngx.req.set_uri("/bar", false) + ngx.req.set_uri_args("hello") + '; + echo "foo: $uri?$args"; + } +--- request + GET /foo?world +--- response_body +bar: /bar?hello + + + +=== TEST 15: rewrite (break cycle explicitly) +--- config + location /bar { + echo "bar: $uri?$args"; + } + location /foo { + #set $args 'hello'; + rewrite_by_lua ' + ngx.req.set_uri("/bar", true) + ngx.req.set_uri_args("hello") + '; + echo "foo: $uri?$args"; + } +--- request + GET /foo?world +--- response_body +foo: /bar?hello + + + +=== TEST 16: rewrite uri (zero-length) +--- config + location /foo { + #set $args 'hello'; + rewrite_by_lua ' + local res, err = pcall(ngx.req.set_uri, "") + ngx.say("err: ", err) + '; + echo "foo: $uri?$args"; + } +--- request + GET /foo?world +--- response_body +err: attempt to use zero-length uri +foo: /foo?world + From a2d115e652f028c3129b504b44a23cf6b9b821e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 09:31:17 +0800 Subject: [PATCH 09/46] added more (passing) tests for ngx.req.set_uri and ngx.req.set_uri_args. --- t/030-uri-args.t | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/t/030-uri-args.t b/t/030-uri-args.t index 725e17c9..95259143 100644 --- a/t/030-uri-args.t +++ b/t/030-uri-args.t @@ -14,6 +14,8 @@ plan tests => repeat_each() * (blocks() * 2); no_root_location(); +$ENV{TEST_NGINX_CLIENT_PORT} ||= $ENV{TEST_NGINX} ||= server_port(); + #no_diff(); #no_long_string(); run_tests(); @@ -345,7 +347,7 @@ done -=== TEST 12: rewrite args +=== TEST 12: rewrite uri and args --- config location /bar { echo $query_string; @@ -356,7 +358,7 @@ done ngx.req.set_uri("/bar"); ngx.req.set_uri_args("hello") '; - proxy_pass http://127.0.0.1:$server_port; + proxy_pass http://www.taobao.com:5678; } --- request GET /foo?world @@ -441,3 +443,23 @@ foo: /bar?hello err: attempt to use zero-length uri foo: /foo?world + + +=== TEST 17: rewrite uri and args +--- config + location /bar { + echo $server_protocol $query_string; + } + location /foo { + #rewrite ^ /bar?hello? break; + rewrite_by_lua ' + ngx.req.set_uri("/bar", true) + ngx.req.set_uri_args("hello") + '; + proxy_pass http://127.0.0.1:$TEST_NGINX_CLIENT_PORT; + } +--- request + GET /foo?world +--- response_body +HTTP/1.0 hello + From 95faaaa2d4cef86d1aaed9e5ea2e7c53cdf0e2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 09:58:12 +0800 Subject: [PATCH 10/46] now we skip rewrite phase lua handlers altogether if ngx_rewrite's rewrite directive issue a location re-lookup by changing uris (but not including rewrite ... break). --- src/ngx_http_lua_rewriteby.c | 4 +++ t/014-bugs.t | 26 -------------- t/022-redirect.t | 17 ++++++++++ t/023-rewrite/sanity.t | 66 ++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 26 deletions(-) diff --git a/src/ngx_http_lua_rewriteby.c b/src/ngx_http_lua_rewriteby.c index fb89f361..ab10bead 100644 --- a/src/ngx_http_lua_rewriteby.c +++ b/src/ngx_http_lua_rewriteby.c @@ -153,6 +153,10 @@ ngx_http_lua_rewrite_handler(ngx_http_request_t *r) ngx_int_t rc; ngx_http_lua_main_conf_t *lmcf; + if (r->uri_changed) { + return NGX_DECLINED; + } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua rewrite handler, uri \"%V\"", &r->uri); diff --git a/t/014-bugs.t b/t/014-bugs.t index 3402612a..f260c28f 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -502,29 +502,3 @@ nil hello --- SKIP - - -=== TEST 24: rewrite last before rewrite_by_lua ---- config - location @pretty { - rewrite ^/main/xyz\.html$ /abc.html last; - rewrite_by_lua 'ngx.exec("@proxy")'; - } - location @proxy { - proxy_pass http://127.0.0.1:$server_port/blah; - } - location ~ /abc.html { - rewrite_by_lua 'ngx.exec("@abc")'; - } - location @abc { - echo abc; - } - location /main { - try_files $uri $uri/ @pretty; - } ---- request - GET /main/xyz.html ---- response_body -abc ---- SKIP - diff --git a/t/022-redirect.t b/t/022-redirect.t index 89fb77dd..57691d6e 100644 --- a/t/022-redirect.t +++ b/t/022-redirect.t @@ -128,3 +128,20 @@ GET /read --- response_body_like: 302 Found --- error_code: 302 + + +=== TEST 7: default 302 (with uri args) +--- config + location /read { + content_by_lua ' + ngx.redirect("http://www.taobao.com/foo?bar=3"); + ngx.say("hi") + '; + } +--- request +GET /read +--- response_headers +Location: http://www.taobao.com/foo?bar=3 +--- response_body_like: 302 Found +--- error_code: 302 + diff --git a/t/023-rewrite/sanity.t b/t/023-rewrite/sanity.t index 16ea5e27..92d1b739 100644 --- a/t/023-rewrite/sanity.t +++ b/t/023-rewrite/sanity.t @@ -669,3 +669,69 @@ GET / --- response_headers X-Foo: bar + + +=== TEST 35: rewrite last before rewrite_by_lua +--- config + location /main { + rewrite ^/main/xyz\.html$ /abc.html last; + rewrite_by_lua 'ngx.exit(503)'; + } + location ~ /abc.html { + echo abc; + } +--- request + GET /main/xyz.html +--- response_body +abc + + + +=== TEST 36: rewrite last before rewrite_by_lua_file +--- config + location /main { + rewrite ^/main/xyz\.html$ /abc.html last; + rewrite_by_lua_file html/exit.lua; + } + location ~ /abc.html { + echo abc; + } +--- user_files +>>> exit.lua +ngx.exit(503) +--- request + GET /main/xyz.html +--- response_body +abc + + + +=== TEST 37: rewrite before rewrite_by_lua +--- config + location /main { + rewrite ^/main/xyz\.html$ /abc.html; + rewrite_by_lua 'ngx.exit(503)'; + } + location ~ /abc.html { + echo abc; + } +--- request + GET /main/xyz.html +--- response_body +abc + + +=== TEST 37: rewrite break before rewrite_by_lua +--- config + location /main { + rewrite ^/main/xyz\.html$ /abc.html break; + rewrite_by_lua 'ngx.exit(503)'; + } + location ~ /abc.html { + echo abc; + } +--- request + GET /main/xyz.html +--- response_body_like: 503 Service Temporarily Unavailable +--- error_code: 503 + From f72eb473cb81929c54abf649943ccf08bbe93fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 10:07:50 +0800 Subject: [PATCH 11/46] documented that rewrite phase Lua code will be skipped if location re-lookup is triggered by the standard ngx_rewrite module's rewrite directive. thanks Nginx User. --- README | 30 +++++++++++++++++++++++++++--- README.markdown | 26 +++++++++++++++++++++++--- doc/HttpLuaModule.wiki | 26 +++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/README b/README index dd645944..8a1182b0 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ Status This module is under active development and is already production ready. Version - This document describes ngx_lua v0.3.1rc12 + This document describes ngx_lua v0.3.1rc13 () released on 16 October 2011. @@ -482,6 +482,26 @@ Directives "ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)" (or its friends) for failures. + If one uses [[HttpRewriteModule]]'s rewrite directive to change the URI + and initiate location re-lookups (kinda like internal redirections), + then rewrite_by_lua and rewrite_by_lua_file will be skipped altogether + in the current location. For example, + + location /foo { + rewrite ^ /bar; + rewrite_by_lua 'ngx.exit(503)'; + } + location /bar { + ... + } + + Here the Lua code "ngx.exit(503)" will never run while all the Lua code + (except access phase handlers) in the "/bar" location will not be + affected anyway. Similarly, "rewrite ^ /bar last" will also initiate a + location re-lookup. If you use the "break" modifier for the rewrite + directive, however, no location re-lookup will be triggered, and + therefore, the rewrite-phase Lua code will still be run as normal. + rewrite_by_lua_file syntax: *rewrite_by_lua_file * @@ -1264,7 +1284,7 @@ Nginx API for Lua modifier in the standard [[HttpRewriteModule]], for example, this "nginx.conf" snippet - rewrite ^ /foo redirect; # nginx config + rewrite ^ /foo? redirect; # nginx config is equivalent to the following Lua code @@ -1272,12 +1292,16 @@ Nginx API for Lua while - rewrite ^ /foo permanent; # nginx config + rewrite ^ /foo? permanent; # nginx config is equivalent to return ngx.redirect('/foo', ngx.HTTP_MOVED_PERMANENTLY) -- Lua code + URI arguments can be specified as well, for example: + + return ngx.redirect('/foo?a=3&b=4') + ngx.send_headers syntax: *ngx.send_headers()* diff --git a/README.markdown b/README.markdown index 70c9d6b2..45b3ea2f 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc12](https://github.com/chaoslawful/lua-nginx-module/tags) released on 16 October 2011. +This document describes ngx_lua [v0.3.1rc13](https://github.com/chaoslawful/lua-nginx-module/tags) released on 16 October 2011. Synopsis ======== @@ -490,6 +490,20 @@ Just as any other rewrite phase handlers, [rewrite_by_lua](http://wiki.nginx.org Note that calling `ngx.exit(ngx.OK)` just returning from the current [rewrite_by_lua](http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua) handler, and the nginx request processing control flow will still continue to the content handler. To terminate the current request from within the current [rewrite_by_lua](http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua) handler, calling [ngx.exit](http://wiki.nginx.org/HttpLuaModule#ngx.exit) with status >= 200 (`ngx.HTTP_OK`) and status < 300 (`ngx.HTTP_SPECIAL_RESPONSE`) for successful quits and `ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)` (or its friends) for failures. +If one uses [HttpRewriteModule](http://wiki.nginx.org/HttpRewriteModule)'s [rewrite](http://wiki.nginx.org/HttpRewriteModule#rewrite) directive to change the URI and initiate location re-lookups (kinda like internal redirections), then [rewrite_by_lua](http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua) and [rewrite_by_lua_file](http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua_file) will be skipped altogether in the current location. For example, + + + location /foo { + rewrite ^ /bar; + rewrite_by_lua 'ngx.exit(503)'; + } + location /bar { + ... + } + + +Here the Lua code `ngx.exit(503)` will never run while all the Lua code (except access phase handlers) in the `/bar` location will not be affected anyway. Similarly, `rewrite ^ /bar last` will also initiate a location re-lookup. If you use the `break` modifier for the [rewrite](http://wiki.nginx.org/HttpRewriteModule#rewrite) directive, however, no location re-lookup will be triggered, and therefore, the rewrite-phase Lua code will still be run as normal. + rewrite_by_lua_file ------------------- @@ -1449,7 +1463,7 @@ This method is very much like the [rewrite](http://wiki.nginx.org/HttpRewriteMod [HttpRewriteModule](http://wiki.nginx.org/HttpRewriteModule), for example, this `nginx.conf` snippet - rewrite ^ /foo redirect; # nginx config + rewrite ^ /foo? redirect; # nginx config is equivalent to the following Lua code @@ -1461,7 +1475,7 @@ is equivalent to the following Lua code while - rewrite ^ /foo permanent; # nginx config + rewrite ^ /foo? permanent; # nginx config is equivalent to @@ -1470,6 +1484,12 @@ is equivalent to return ngx.redirect('/foo', ngx.HTTP_MOVED_PERMANENTLY) -- Lua code +URI arguments can be specified as well, for example: + + + return ngx.redirect('/foo?a=3&b=4') + + ngx.send_headers ---------------- **syntax:** *ngx.send_headers()* diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index e5094962..e4bf1b90 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc12] released on 16 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc13] released on 16 October 2011. = Synopsis = @@ -475,6 +475,20 @@ Just as any other rewrite phase handlers, [[#rewrite_by_lua|rewrite_by_lua]] als Note that calling ngx.exit(ngx.OK) just returning from the current [[#rewrite_by_lua|rewrite_by_lua]] handler, and the nginx request processing control flow will still continue to the content handler. To terminate the current request from within the current [[#rewrite_by_lua|rewrite_by_lua]] handler, calling [[#ngx.exit|ngx.exit]] with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures. +If one uses [[HttpRewriteModule]]'s [[HttpRewriteModule#rewrite|rewrite]] directive to change the URI and initiate location re-lookups (kinda like internal redirections), then [[#rewrite_by_lua|rewrite_by_lua]] and [[#rewrite_by_lua_file|rewrite_by_lua_file]] will be skipped altogether in the current location. For example, + + + location /foo { + rewrite ^ /bar; + rewrite_by_lua 'ngx.exit(503)'; + } + location /bar { + ... + } + + +Here the Lua code ngx.exit(503) will never run while all the Lua code (except access phase handlers) in the /bar location will not be affected anyway. Similarly, rewrite ^ /bar last will also initiate a location re-lookup. If you use the break modifier for the [[HttpRewriteModule#rewrite|rewrite]] directive, however, no location re-lookup will be triggered, and therefore, the rewrite-phase Lua code will still be run as normal. + == rewrite_by_lua_file == '''syntax:''' ''rewrite_by_lua_file '' @@ -1408,7 +1422,7 @@ This method is very much like the [[HttpRewriteModule#rewrite|rewrite]] directiv [[HttpRewriteModule]], for example, this nginx.conf snippet - rewrite ^ /foo redirect; # nginx config + rewrite ^ /foo? redirect; # nginx config is equivalent to the following Lua code @@ -1420,7 +1434,7 @@ is equivalent to the following Lua code while - rewrite ^ /foo permanent; # nginx config + rewrite ^ /foo? permanent; # nginx config is equivalent to @@ -1429,6 +1443,12 @@ is equivalent to return ngx.redirect('/foo', ngx.HTTP_MOVED_PERMANENTLY) -- Lua code +URI arguments can be specified as well, for example: + + + return ngx.redirect('/foo?a=3&b=4') + + == ngx.send_headers == '''syntax:''' ''ngx.send_headers()'' From 89dcc5b8d3f3c29c7422f3c5dc0522b621abb7fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 10:31:50 +0800 Subject: [PATCH 12/46] documented the new APIs ngx.req.set_uri() and ngx.req.set_uri_args(). thanks Vladimir Protasov (utros) and Nginx User for suggesting this feature in github issue #62 and the nginx mailing list, respectively. --- README | 74 ++++++++++++++++++++++++++++++++++++++ README.markdown | 82 ++++++++++++++++++++++++++++++++++++++++++ doc/HttpLuaModule.wiki | 80 +++++++++++++++++++++++++++++++++++++++++ t/030-uri-args.t | 19 ++++++++++ 4 files changed, 255 insertions(+) diff --git a/README b/README index 8a1182b0..4434c28d 100644 --- a/README +++ b/README @@ -1096,6 +1096,80 @@ Nginx API for Lua For reading *request* headers, use the ngx.req.get_headers function instead. + ngx.req.set_uri + syntax: *ngx.req.set_uri(uri)* + + syntax: *ngx.req.set_uri(uri, break_cycle)* + + context: *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, + header_filter_by_lua** + + Rewrite the current request's (parsed) URI by the "uri" argument. The + "uri" argument must be a Lua string and cannot be of zero length, or a + Lua exception will be thrown. + + The optional boolean "break_cycle" argument plays a similar role as + [[HttpRewriteModule]]'s rewrite directive, that is, when "break_cycle" + is false (the default), then Nginx will try researching locations with + the new URI value at the later "post rewrite" phase, and otherwise + location re-lookup will be disabled. + + For example, the following nginx config snippet + + rewrite ^ /foo; + + can be coded in Lua like this: + + ngx.req.set_uri("/foo") + + Similarly, Nginx config + + rewrite ^ /foo break; + + can be coded in Lua as + + ngx.req.set_uri("/foo", true) + + Note that you cannot use this interface to rewrite URI arguments, and + you need to use ngx.req.set_uri_args for that. For instance, Nginx + config + + rewrite ^ /foo?a=3?; + + can be coded as + + ngx.req.set_uri("/foo") + ngx.req.set_uri_args("a=3") + + or + + ngx.req.set_uri("/foo") + ngx.req.set_uri_args({a = 3}) + + This interface was first introduced in the "v0.3.1rc13" release. + + ngx.req.set_uri_args + syntax: *ngx.req.set_uri_args(args)* + + context: *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, + header_filter_by_lua** + + Rewrite the current request's URI query arguments by the "args" + argument. The "args" argument can be either a Lua string, as in + + ngx.req.set_uri_args("a=3&b=hello%20world") + + or a Lua table holding the query arguments' key-value pairs, as in + + ngx.req.set_uri_args({ a = 3, b = "hello world" }) + + where in the latter case, this method will automatically escape argument + keys and values according to the URI escaping rule. + + This interface was first introduced in the "v0.3.1rc13" release. + + See also ngx.req.set_uri. + ngx.req.get_uri_args syntax: *args = ngx.req.get_uri_args()* diff --git a/README.markdown b/README.markdown index 45b3ea2f..cc1a794b 100644 --- a/README.markdown +++ b/README.markdown @@ -1191,6 +1191,88 @@ Note that `ngx.header` is not a normal Lua table so you cannot iterate through i For reading *request* headers, use the [ngx.req.get_headers](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_headers) function instead. +ngx.req.set_uri +--------------- +**syntax:** *ngx.req.set_uri(uri)* + +**syntax:** *ngx.req.set_uri(uri, break_cycle)* + +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua** + +Rewrite the current request's (parsed) URI by the `uri` argument. The `uri` argument must be a Lua string and cannot be of zero length, or a Lua exception will be thrown. + +The optional boolean `break_cycle` argument plays a similar role as [HttpRewriteModule](http://wiki.nginx.org/HttpRewriteModule)'s [rewrite](http://wiki.nginx.org/HttpRewriteModule#rewrite) directive, that is, when `break_cycle` is false (the default), then Nginx will try researching locations with the new URI value at the later `post rewrite` phase, and otherwise location re-lookup will be disabled. + +For example, the following nginx config snippet + + + rewrite ^ /foo; + + +can be coded in Lua like this: + + + ngx.req.set_uri("/foo") + + +Similarly, Nginx config + + + rewrite ^ /foo break; + + +can be coded in Lua as + + + ngx.req.set_uri("/foo", true) + + +Note that you cannot use this interface to rewrite URI arguments, and you need to use [ngx.req.set_uri_args](http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri_args) for that. For instance, Nginx config + + + rewrite ^ /foo?a=3?; + + +can be coded as + + + ngx.req.set_uri("/foo") + ngx.req.set_uri_args("a=3") + + +or + + + ngx.req.set_uri("/foo") + ngx.req.set_uri_args({a = 3}) + + +This interface was first introduced in the `v0.3.1rc13` release. + +ngx.req.set_uri_args +-------------------- +**syntax:** *ngx.req.set_uri_args(args)* + +**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua** + +Rewrite the current request's URI query arguments by the `args` argument. The `args` argument can be either a Lua string, as in + + + ngx.req.set_uri_args("a=3&b=hello%20world") + + +or a Lua table holding the query arguments' key-value pairs, as in + + + ngx.req.set_uri_args({ a = 3, b = "hello world" }) + + +where in the latter case, this method will automatically escape argument keys and values according to the URI escaping rule. + +This interface was first introduced in the `v0.3.1rc13` release. + +See also [ngx.req.set_uri](http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri). + ngx.req.get_uri_args -------------------- **syntax:** *args = ngx.req.get_uri_args()* diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index e4bf1b90..fb585411 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -1157,6 +1157,86 @@ Note that ngx.header is not a normal Lua table so you cannot iterat For reading ''request'' headers, use the [[#ngx.req.get_headers|ngx.req.get_headers]] function instead. +== ngx.req.set_uri == +'''syntax:''' ''ngx.req.set_uri(uri)'' + +'''syntax:''' ''ngx.req.set_uri(uri, break_cycle)'' + +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' + +Rewrite the current request's (parsed) URI by the uri argument. The uri argument must be a Lua string and cannot be of zero length, or a Lua exception will be thrown. + +The optional boolean break_cycle argument plays a similar role as [[HttpRewriteModule]]'s [[HttpRewriteModule#rewrite|rewrite]] directive, that is, when break_cycle is false (the default), then Nginx will try researching locations with the new URI value at the later post rewrite phase, and otherwise location re-lookup will be disabled. + +For example, the following nginx config snippet + + + rewrite ^ /foo; + + +can be coded in Lua like this: + + + ngx.req.set_uri("/foo") + + +Similarly, Nginx config + + + rewrite ^ /foo break; + + +can be coded in Lua as + + + ngx.req.set_uri("/foo", true) + + +Note that you cannot use this interface to rewrite URI arguments, and you need to use [[#ngx.req.set_uri_args|ngx.req.set_uri_args]] for that. For instance, Nginx config + + + rewrite ^ /foo?a=3?; + + +can be coded as + + + ngx.req.set_uri("/foo") + ngx.req.set_uri_args("a=3") + + +or + + + ngx.req.set_uri("/foo") + ngx.req.set_uri_args({a = 3}) + + +This interface was first introduced in the v0.3.1rc13 release. + +== ngx.req.set_uri_args == +'''syntax:''' ''ngx.req.set_uri_args(args)'' + +'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' + +Rewrite the current request's URI query arguments by the args argument. The args argument can be either a Lua string, as in + + + ngx.req.set_uri_args("a=3&b=hello%20world") + + +or a Lua table holding the query arguments' key-value pairs, as in + + + ngx.req.set_uri_args({ a = 3, b = "hello world" }) + + +where in the latter case, this method will automatically escape argument keys and values according to the URI escaping rule. + +This interface was first introduced in the v0.3.1rc13 release. + +See also [[#ngx.req.set_uri|ngx.req.set_uri]]. + == ngx.req.get_uri_args == '''syntax:''' ''args = ngx.req.get_uri_args()'' diff --git a/t/030-uri-args.t b/t/030-uri-args.t index 95259143..586d8eb8 100644 --- a/t/030-uri-args.t +++ b/t/030-uri-args.t @@ -463,3 +463,22 @@ foo: /foo?world --- response_body HTTP/1.0 hello + +=== TEST 17: rewrite uri and args (table args) +--- config + location /bar { + echo $server_protocol $query_string; + } + location /foo { + #rewrite ^ /bar?hello? break; + rewrite_by_lua ' + ngx.req.set_uri("/bar", true) + ngx.req.set_uri_args({["ca t"] = "%"}) + '; + proxy_pass http://127.0.0.1:$TEST_NGINX_CLIENT_PORT; + } +--- request + GET /foo?world +--- response_body +HTTP/1.0 ca%20t=%25 + From 6be7dad6267554669c202d7993c6136824ab8b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 11:08:56 +0800 Subject: [PATCH 13/46] added more complex samples to the docs for ngx.req.set_uri(). --- README | 17 +++++++++++++++++ README.markdown | 21 +++++++++++++++++++++ doc/HttpLuaModule.wiki | 21 +++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/README b/README index 4434c28d..2712624f 100644 --- a/README +++ b/README @@ -1130,6 +1130,23 @@ Nginx API for Lua ngx.req.set_uri("/foo", true) + A more sophisticated example involving regex substitutions is as follows + + location /test { + rewrite_by_lua ' + local uri = ngx.re.sub(ngx.var.uri, "^/test/(.*)", "$1", "o") + ngx.req.set_uri(uri, true) + '; + proxy_pass http://my_backend; + } + + which is functionally equivalent to + + location /test { + rewrite ^/test/(.*) /$1 break; + proxy_pass http://my_backend; + } + Note that you cannot use this interface to rewrite URI arguments, and you need to use ngx.req.set_uri_args for that. For instance, Nginx config diff --git a/README.markdown b/README.markdown index cc1a794b..c1554c30 100644 --- a/README.markdown +++ b/README.markdown @@ -1227,6 +1227,27 @@ can be coded in Lua as ngx.req.set_uri("/foo", true) +A more sophisticated example involving regex substitutions is as follows + + + location /test { + rewrite_by_lua ' + local uri = ngx.re.sub(ngx.var.uri, "^/test/(.*)", "$1", "o") + ngx.req.set_uri(uri, true) + '; + proxy_pass http://my_backend; + } + + +which is functionally equivalent to + + + location /test { + rewrite ^/test/(.*) /$1 break; + proxy_pass http://my_backend; + } + + Note that you cannot use this interface to rewrite URI arguments, and you need to use [ngx.req.set_uri_args](http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri_args) for that. For instance, Nginx config diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index fb585411..07b700b2 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -1192,6 +1192,27 @@ can be coded in Lua as ngx.req.set_uri("/foo", true) +A more sophisticated example involving regex substitutions is as follows + + + location /test { + rewrite_by_lua ' + local uri = ngx.re.sub(ngx.var.uri, "^/test/(.*)", "$1", "o") + ngx.req.set_uri(uri, true) + '; + proxy_pass http://my_backend; + } + + +which is functionally equivalent to + + + location /test { + rewrite ^/test/(.*) /$1 break; + proxy_pass http://my_backend; + } + + Note that you cannot use this interface to rewrite URI arguments, and you need to use [[#ngx.req.set_uri_args|ngx.req.set_uri_args]] for that. For instance, Nginx config From 6ff491bd19fb61281abf97c3e9fcddbc4867b10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 19:00:37 +0800 Subject: [PATCH 14/46] now we change the ngx.req.set_uri() API a bit by changing the optional argument "break_cycle" to "jump". so now it will not trigger location jump by default (because "jump" argument is false by default) and in case "jump" is given true, the function will re-search locations and jump to the new location and never return. --- README | 54 +++++++++++++++++++++++------------------ README.markdown | 45 +++++++++++++++++++--------------- doc/HttpLuaModule.wiki | 45 +++++++++++++++++++--------------- src/ngx_http_lua_args.c | 15 ++++++------ src/ngx_http_lua_util.c | 20 +++++++++++++++ t/023-rewrite/sanity.t | 3 ++- t/030-uri-args.t | 36 +++++++++++++++++++++------ 7 files changed, 140 insertions(+), 78 deletions(-) diff --git a/README b/README index 2712624f..db2b1b77 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ Status This module is under active development and is already production ready. Version - This document describes ngx_lua v0.3.1rc13 + This document describes ngx_lua v0.3.1rc14 () released on 16 October 2011. @@ -329,6 +329,8 @@ Directives context: *main, server, location, server if, location if* + phase: *rewrite* + Basically the same as set_by_lua, except the code to be executed is in the file specified by "". @@ -384,7 +386,7 @@ Directives context: *http, server, location, location if* - phase: *rewrite tail* + phase: *post-rewrite* Act as a rewrite phase handler and execute user code specified by "" for every request. The user code may call predefined @@ -507,7 +509,7 @@ Directives context: *http, server, location, location if* - phase: *rewrite tail* + phase: *post-rewrite* Same as rewrite_by_lua, except the code to be executed is in the file specified by "". @@ -527,7 +529,7 @@ Directives context: *http, server, location, location if* - phase: *access tail* + phase: *post-access* Act as an access phase handler and execute user code specified by "" for every request. The user code may call predefined @@ -602,7 +604,7 @@ Directives context: *http, server, location, location if* - phase: *access tail* + phase: *post-access* Same as access_by_lua, except the code to be executed is in the file specified by "". @@ -622,7 +624,7 @@ Directives context: *http, server, location, location if* - phase: *output header filter* + phase: *output-header-filter* Use Lua defined in "" to define an output header filter. For now, the following Nginx Lua APIs are disabled in this context: @@ -645,7 +647,7 @@ Directives context: *http, server, location, location if* - phase: *output header filter* + phase: *output-header-filter* Use Lua code defined in a separate file specified by "" to define an output header filter. @@ -1097,9 +1099,7 @@ Nginx API for Lua instead. ngx.req.set_uri - syntax: *ngx.req.set_uri(uri)* - - syntax: *ngx.req.set_uri(uri, break_cycle)* + syntax: *ngx.req.set_uri(uri, jump?)* context: *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua** @@ -1108,19 +1108,23 @@ Nginx API for Lua "uri" argument must be a Lua string and cannot be of zero length, or a Lua exception will be thrown. - The optional boolean "break_cycle" argument plays a similar role as - [[HttpRewriteModule]]'s rewrite directive, that is, when "break_cycle" - is false (the default), then Nginx will try researching locations with - the new URI value at the later "post rewrite" phase, and otherwise - location re-lookup will be disabled. + The optional boolean "jump" argument can trigger location rematch (or + location jump) as [[HttpRewriteModule]]'s rewrite directive, that is, + when "jump" is "true" (default to "false"), this function will never + return and it will tell Nginx to try re-searching locations with the new + URI value at the later "post-rewrite" phase and jumping to the new + location. Location jump will not be triggered otherwise, and only the + current request's URI will be modified, which is also the default + behavior. This function will return but with no returned values when the + "jump" argument is "false" or absent altogether. For example, the following nginx config snippet - rewrite ^ /foo; + rewrite ^ /foo last; can be coded in Lua like this: - ngx.req.set_uri("/foo") + ngx.req.set_uri("/foo", true) Similarly, Nginx config @@ -1128,14 +1132,18 @@ Nginx API for Lua can be coded in Lua as - ngx.req.set_uri("/foo", true) + ngx.req.set_uri("/foo", false) + + or equivalently, + + ngx.req.set_uri("/foo") A more sophisticated example involving regex substitutions is as follows location /test { rewrite_by_lua ' local uri = ngx.re.sub(ngx.var.uri, "^/test/(.*)", "$1", "o") - ngx.req.set_uri(uri, true) + ngx.req.set_uri(uri) '; proxy_pass http://my_backend; } @@ -1151,19 +1159,19 @@ Nginx API for Lua you need to use ngx.req.set_uri_args for that. For instance, Nginx config - rewrite ^ /foo?a=3?; + rewrite ^ /foo?a=3? last; can be coded as - ngx.req.set_uri("/foo") ngx.req.set_uri_args("a=3") + ngx.req.set_uri("/foo", true) or - ngx.req.set_uri("/foo") ngx.req.set_uri_args({a = 3}) + ngx.req.set_uri("/foo", true) - This interface was first introduced in the "v0.3.1rc13" release. + This interface was first introduced in the "v0.3.1rc14" release. ngx.req.set_uri_args syntax: *ngx.req.set_uri_args(args)* diff --git a/README.markdown b/README.markdown index c1554c30..26b2c754 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc13](https://github.com/chaoslawful/lua-nginx-module/tags) released on 16 October 2011. +This document describes ngx_lua [v0.3.1rc14](https://github.com/chaoslawful/lua-nginx-module/tags) released on 16 October 2011. Synopsis ======== @@ -336,11 +336,12 @@ This directive requires the [ngx_devel_kit](https://github.com/simpl/ngx_devel_k set_by_lua_file --------------- - **syntax:** *set_by_lua_file $res <path-to-lua-script> [$arg1 $arg2 ...]* **context:** *main, server, location, server if, location if* +**phase:** *rewrite* + Basically the same as [set_by_lua](http://wiki.nginx.org/HttpLuaModule#set_by_lua), except the code to be executed is in the file specified by ``. @@ -393,7 +394,7 @@ rewrite_by_lua **context:** *http, server, location, location if* -**phase:** *rewrite tail* +**phase:** *post-rewrite* Act as a rewrite phase handler and execute user code specified by `` for every request. The user code may call predefined APIs to generate response @@ -511,7 +512,7 @@ rewrite_by_lua_file **context:** *http, server, location, location if* -**phase:** *rewrite tail* +**phase:** *post-rewrite* Same as [rewrite_by_lua](http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua), except the code to be executed is in the file specified by ``. @@ -532,7 +533,7 @@ access_by_lua **context:** *http, server, location, location if* -**phase:** *access tail* +**phase:** *post-access* Act as an access phase handler and execute user code specified by `` for every request. The user code may call predefined APIs to generate response content. @@ -601,7 +602,7 @@ access_by_lua_file **context:** *http, server, location, location if* -**phase:** *access tail* +**phase:** *post-access* Same as [access_by_lua](http://wiki.nginx.org/HttpLuaModule#access_by_lua), except the code to be executed is in the file specified by ``. @@ -622,7 +623,7 @@ header_filter_by_lua **context:** *http, server, location, location if* -**phase:** *output header filter* +**phase:** *output-header-filter* Use Lua defined in `` to define an output header filter. For now, the following Nginx Lua APIs are disabled in this context: @@ -647,7 +648,7 @@ header_filter_by_lua_file **context:** *http, server, location, location if* -**phase:** *output header filter* +**phase:** *output-header-filter* Use Lua code defined in a separate file specified by `` to define an output header filter. @@ -1193,26 +1194,24 @@ For reading *request* headers, use the [ngx.req.get_headers](http://wiki.nginx.o ngx.req.set_uri --------------- -**syntax:** *ngx.req.set_uri(uri)* - -**syntax:** *ngx.req.set_uri(uri, break_cycle)* +**syntax:** *ngx.req.set_uri(uri, jump?)* **context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua** Rewrite the current request's (parsed) URI by the `uri` argument. The `uri` argument must be a Lua string and cannot be of zero length, or a Lua exception will be thrown. -The optional boolean `break_cycle` argument plays a similar role as [HttpRewriteModule](http://wiki.nginx.org/HttpRewriteModule)'s [rewrite](http://wiki.nginx.org/HttpRewriteModule#rewrite) directive, that is, when `break_cycle` is false (the default), then Nginx will try researching locations with the new URI value at the later `post rewrite` phase, and otherwise location re-lookup will be disabled. +The optional boolean `jump` argument can trigger location rematch (or location jump) as [HttpRewriteModule](http://wiki.nginx.org/HttpRewriteModule)'s [rewrite](http://wiki.nginx.org/HttpRewriteModule#rewrite) directive, that is, when `jump` is `true` (default to `false`), this function will never return and it will tell Nginx to try re-searching locations with the new URI value at the later `post-rewrite` phase and jumping to the new location. Location jump will not be triggered otherwise, and only the current request's URI will be modified, which is also the default behavior. This function will return but with no returned values when the `jump` argument is `false` or absent altogether. For example, the following nginx config snippet - rewrite ^ /foo; + rewrite ^ /foo last; can be coded in Lua like this: - ngx.req.set_uri("/foo") + ngx.req.set_uri("/foo", true) Similarly, Nginx config @@ -1224,7 +1223,13 @@ Similarly, Nginx config can be coded in Lua as - ngx.req.set_uri("/foo", true) + ngx.req.set_uri("/foo", false) + + +or equivalently, + + + ngx.req.set_uri("/foo") A more sophisticated example involving regex substitutions is as follows @@ -1233,7 +1238,7 @@ A more sophisticated example involving regex substitutions is as follows location /test { rewrite_by_lua ' local uri = ngx.re.sub(ngx.var.uri, "^/test/(.*)", "$1", "o") - ngx.req.set_uri(uri, true) + ngx.req.set_uri(uri) '; proxy_pass http://my_backend; } @@ -1251,24 +1256,24 @@ which is functionally equivalent to Note that you cannot use this interface to rewrite URI arguments, and you need to use [ngx.req.set_uri_args](http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri_args) for that. For instance, Nginx config - rewrite ^ /foo?a=3?; + rewrite ^ /foo?a=3? last; can be coded as - ngx.req.set_uri("/foo") ngx.req.set_uri_args("a=3") + ngx.req.set_uri("/foo", true) or - ngx.req.set_uri("/foo") ngx.req.set_uri_args({a = 3}) + ngx.req.set_uri("/foo", true) -This interface was first introduced in the `v0.3.1rc13` release. +This interface was first introduced in the `v0.3.1rc14` release. ngx.req.set_uri_args -------------------- diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 07b700b2..1eb0faa2 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc13] released on 16 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc14] released on 16 October 2011. = Synopsis = @@ -324,11 +324,12 @@ This directive can be freely mixed with all the directives of [[HttpRewriteModul This directive requires the [https://github.com/simpl/ngx_devel_kit ngx_devel_kit] module. == set_by_lua_file == - '''syntax:''' ''set_by_lua_file $res [$arg1 $arg2 ...]'' '''context:''' ''main, server, location, server if, location if'' +'''phase:''' ''rewrite'' + Basically the same as [[#set_by_lua|set_by_lua]], except the code to be executed is in the file specified by . @@ -378,7 +379,7 @@ When the Lua code cache is on (this is the default), the user code is loaded onc '''context:''' ''http, server, location, location if'' -'''phase:''' ''rewrite tail'' +'''phase:''' ''post-rewrite'' Act as a rewrite phase handler and execute user code specified by for every request. The user code may call predefined APIs to generate response @@ -495,7 +496,7 @@ Here the Lua code ngx.exit(503) will never run while all the Lua co '''context:''' ''http, server, location, location if'' -'''phase:''' ''rewrite tail'' +'''phase:''' ''post-rewrite'' Same as [[#rewrite_by_lua|rewrite_by_lua]], except the code to be executed is in the file specified by . @@ -515,7 +516,7 @@ Lua code cache by setting [[#lua_code_cache|lua_code_cache]] off in '''context:''' ''http, server, location, location if'' -'''phase:''' ''access tail'' +'''phase:''' ''post-access'' Act as an access phase handler and execute user code specified by for every request. The user code may call predefined APIs to generate response content. @@ -583,7 +584,7 @@ Note that calling ngx.exit(ngx.OK) just returning from the current '''context:''' ''http, server, location, location if'' -'''phase:''' ''access tail'' +'''phase:''' ''post-access'' Same as [[#access_by_lua|access_by_lua]], except the code to be executed is in the file specified by . @@ -603,7 +604,7 @@ Lua code cache by setting [[#lua_code_cache|lua_code_cache]] off in '''context:''' ''http, server, location, location if'' -'''phase:''' ''output header filter'' +'''phase:''' ''output-header-filter'' Use Lua defined in to define an output header filter. For now, the following Nginx Lua APIs are disabled in this context: @@ -627,7 +628,7 @@ This directive was first introduced in the v0.2.1rc20 release. '''context:''' ''http, server, location, location if'' -'''phase:''' ''output header filter'' +'''phase:''' ''output-header-filter'' Use Lua code defined in a separate file specified by to define an output header filter. @@ -1158,26 +1159,24 @@ Note that ngx.header is not a normal Lua table so you cannot iterat For reading ''request'' headers, use the [[#ngx.req.get_headers|ngx.req.get_headers]] function instead. == ngx.req.set_uri == -'''syntax:''' ''ngx.req.set_uri(uri)'' - -'''syntax:''' ''ngx.req.set_uri(uri, break_cycle)'' +'''syntax:''' ''ngx.req.set_uri(uri, jump?)'' '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' Rewrite the current request's (parsed) URI by the uri argument. The uri argument must be a Lua string and cannot be of zero length, or a Lua exception will be thrown. -The optional boolean break_cycle argument plays a similar role as [[HttpRewriteModule]]'s [[HttpRewriteModule#rewrite|rewrite]] directive, that is, when break_cycle is false (the default), then Nginx will try researching locations with the new URI value at the later post rewrite phase, and otherwise location re-lookup will be disabled. +The optional boolean jump argument can trigger location rematch (or location jump) as [[HttpRewriteModule]]'s [[HttpRewriteModule#rewrite|rewrite]] directive, that is, when jump is true (default to false), this function will never return and it will tell Nginx to try re-searching locations with the new URI value at the later post-rewrite phase and jumping to the new location. Location jump will not be triggered otherwise, and only the current request's URI will be modified, which is also the default behavior. This function will return but with no returned values when the jump argument is false or absent altogether. For example, the following nginx config snippet - rewrite ^ /foo; + rewrite ^ /foo last; can be coded in Lua like this: - ngx.req.set_uri("/foo") + ngx.req.set_uri("/foo", true) Similarly, Nginx config @@ -1189,7 +1188,13 @@ Similarly, Nginx config can be coded in Lua as - ngx.req.set_uri("/foo", true) + ngx.req.set_uri("/foo", false) + + +or equivalently, + + + ngx.req.set_uri("/foo") A more sophisticated example involving regex substitutions is as follows @@ -1198,7 +1203,7 @@ A more sophisticated example involving regex substitutions is as follows location /test { rewrite_by_lua ' local uri = ngx.re.sub(ngx.var.uri, "^/test/(.*)", "$1", "o") - ngx.req.set_uri(uri, true) + ngx.req.set_uri(uri) '; proxy_pass http://my_backend; } @@ -1216,24 +1221,24 @@ which is functionally equivalent to Note that you cannot use this interface to rewrite URI arguments, and you need to use [[#ngx.req.set_uri_args|ngx.req.set_uri_args]] for that. For instance, Nginx config - rewrite ^ /foo?a=3?; + rewrite ^ /foo?a=3? last; can be coded as - ngx.req.set_uri("/foo") ngx.req.set_uri_args("a=3") + ngx.req.set_uri("/foo", true) or - ngx.req.set_uri("/foo") ngx.req.set_uri_args({a = 3}) + ngx.req.set_uri("/foo", true) -This interface was first introduced in the v0.3.1rc13 release. +This interface was first introduced in the v0.3.1rc14 release. == ngx.req.set_uri_args == '''syntax:''' ''ngx.req.set_uri_args(args)'' diff --git a/src/ngx_http_lua_args.c b/src/ngx_http_lua_args.c index c1139915..eb801e3a 100644 --- a/src/ngx_http_lua_args.c +++ b/src/ngx_http_lua_args.c @@ -22,7 +22,7 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) { size_t len; u_char *p; int n; - int break_cycle = 0; + int jump = 0; n = lua_gettop(L); @@ -40,7 +40,7 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) { if (n == 2) { luaL_checktype(L, 2, LUA_TBOOLEAN); - break_cycle = lua_toboolean(L, 2); + jump = lua_toboolean(L, 2); } p = (u_char *) luaL_checklstring(L, 1, &len); @@ -57,15 +57,16 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) { r->internal = 1; r->valid_unparsed_uri = 0; - if (break_cycle) { - r->valid_location = 0; - r->uri_changed = 0; + ngx_http_set_exten(r); - } else { + if (jump) { r->uri_changed = 1; + + return lua_yield(L, 0); } - ngx_http_set_exten(r); + r->valid_location = 0; + r->uri_changed = 0; return 0; } diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 6f981c22..150c472b 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -35,6 +35,8 @@ static ngx_int_t ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int cc_ref); static ngx_int_t ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int cc_ref); +static ngx_int_t ngx_http_lua_handle_rewrite_jump(lua_State *L, + ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int cc_ref); #ifndef LUA_PATH_SEP @@ -775,6 +777,10 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua thread yielded"); + if (r->uri_changed) { + return ngx_http_lua_handle_rewrite_jump(L, r, ctx, cc_ref); + } + if (ctx->exited) { return ngx_http_lua_handle_exit(L, r, ctx, cc_ref); } @@ -1641,4 +1647,18 @@ ngx_http_lua_process_args_option(ngx_http_request_t *r, lua_State *L, } +static ngx_int_t +ngx_http_lua_handle_rewrite_jump(lua_State *L, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx, int cc_ref) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua thread aborting request with URI rewrite jump: \"%V?%V\"", + &r->uri, &r->args); + + ngx_http_lua_del_thread(r, L, cc_ref, 1 /* force quit */); + ctx->cc_ref = LUA_NOREF; + ngx_http_lua_request_cleanup(r); + + return NGX_OK; +} diff --git a/t/023-rewrite/sanity.t b/t/023-rewrite/sanity.t index 92d1b739..7b8a2f76 100644 --- a/t/023-rewrite/sanity.t +++ b/t/023-rewrite/sanity.t @@ -721,7 +721,8 @@ abc abc -=== TEST 37: rewrite break before rewrite_by_lua + +=== TEST 38: rewrite break before rewrite_by_lua --- config location /main { rewrite ^/main/xyz\.html$ /abc.html break; diff --git a/t/030-uri-args.t b/t/030-uri-args.t index 586d8eb8..f70b9201 100644 --- a/t/030-uri-args.t +++ b/t/030-uri-args.t @@ -355,8 +355,8 @@ done location /foo { #set $args 'hello'; rewrite_by_lua ' - ngx.req.set_uri("/bar"); ngx.req.set_uri_args("hello") + ngx.req.set_uri("/bar", true); '; proxy_pass http://www.taobao.com:5678; } @@ -375,8 +375,8 @@ hello location /foo { #set $args 'hello'; rewrite_by_lua ' - ngx.req.set_uri("/bar") ngx.req.set_uri_args("hello") + ngx.req.set_uri("/bar", true) '; echo "foo: $uri?$args"; } @@ -395,8 +395,8 @@ bar: /bar?hello location /foo { #set $args 'hello'; rewrite_by_lua ' - ngx.req.set_uri("/bar", false) ngx.req.set_uri_args("hello") + ngx.req.set_uri("/bar", true) '; echo "foo: $uri?$args"; } @@ -415,7 +415,7 @@ bar: /bar?hello location /foo { #set $args 'hello'; rewrite_by_lua ' - ngx.req.set_uri("/bar", true) + ngx.req.set_uri("/bar") ngx.req.set_uri_args("hello") '; echo "foo: $uri?$args"; @@ -453,8 +453,8 @@ foo: /foo?world location /foo { #rewrite ^ /bar?hello? break; rewrite_by_lua ' - ngx.req.set_uri("/bar", true) ngx.req.set_uri_args("hello") + ngx.req.set_uri("/bar") '; proxy_pass http://127.0.0.1:$TEST_NGINX_CLIENT_PORT; } @@ -464,7 +464,8 @@ foo: /foo?world HTTP/1.0 hello -=== TEST 17: rewrite uri and args (table args) + +=== TEST 18: rewrite uri and args (table args) --- config location /bar { echo $server_protocol $query_string; @@ -472,7 +473,7 @@ HTTP/1.0 hello location /foo { #rewrite ^ /bar?hello? break; rewrite_by_lua ' - ngx.req.set_uri("/bar", true) + ngx.req.set_uri("/bar") ngx.req.set_uri_args({["ca t"] = "%"}) '; proxy_pass http://127.0.0.1:$TEST_NGINX_CLIENT_PORT; @@ -482,3 +483,24 @@ HTTP/1.0 hello --- response_body HTTP/1.0 ca%20t=%25 + + +=== TEST 19: rewrite uri and args (never returns) +--- config + location /bar { + echo $query_string; + } + location /foo { + #set $args 'hello'; + rewrite_by_lua ' + ngx.req.set_uri_args("hello") + ngx.req.set_uri("/bar", true); + ngx.exit(503) + '; + proxy_pass http://www.taobao.com:5678; + } +--- request + GET /foo?world +--- response_body +hello + From 6c1c1666b0a87027e70132758f63763dfa55d2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 21:41:26 +0800 Subject: [PATCH 15/46] now we prohibit use of true jump argument in ngx.req.set_uri() in contexts other than rewrite_by_lua and rewrite_by_lua_file. a lua exception will be thrown if the context is incorrect. --- README | 4 ++ README.markdown | 2 + doc/HttpLuaModule.wiki | 2 + src/ngx_http_lua_args.c | 25 ++++++++- t/030-uri-args.t | 121 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 3 deletions(-) diff --git a/README b/README index db2b1b77..0dd8584d 100644 --- a/README +++ b/README @@ -1138,6 +1138,10 @@ Nginx API for Lua ngx.req.set_uri("/foo") + The "jump" can only be set to "true" in rewrite_by_lua and + rewrite_by_lua_file. Use of jump in other contexts is prohibited and + will throw out a Lua exception. + A more sophisticated example involving regex substitutions is as follows location /test { diff --git a/README.markdown b/README.markdown index 26b2c754..87d45244 100644 --- a/README.markdown +++ b/README.markdown @@ -1232,6 +1232,8 @@ or equivalently, ngx.req.set_uri("/foo") +The `jump` can only be set to `true` in [rewrite_by_lua](http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua) and [rewrite_by_lua_file](http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua_file). Use of jump in other contexts is prohibited and will throw out a Lua exception. + A more sophisticated example involving regex substitutions is as follows diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 1eb0faa2..490c5d14 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -1197,6 +1197,8 @@ or equivalently, ngx.req.set_uri("/foo") +The jump can only be set to true in [[#rewrite_by_lua|rewrite_by_lua]] and [[#rewrite_by_lua_file|rewrite_by_lua_file]]. Use of jump in other contexts is prohibited and will throw out a Lua exception. + A more sophisticated example involving regex substitutions is as follows diff --git a/src/ngx_http_lua_args.c b/src/ngx_http_lua_args.c index eb801e3a..08c1ae1d 100644 --- a/src/ngx_http_lua_args.c +++ b/src/ngx_http_lua_args.c @@ -23,6 +23,7 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) { u_char *p; int n; int jump = 0; + ngx_http_lua_ctx_t *ctx; n = lua_gettop(L); @@ -60,9 +61,29 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) { ngx_http_set_exten(r); if (jump) { - r->uri_changed = 1; - return lua_yield(L, 0); + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + +#if defined(DDEBUG) && DDEBUG + if (ctx) { + dd("rewrite: %d, access: %d, content: %d", + (int) ctx->entered_rewrite_phase, + (int) ctx->entered_access_phase, + (int) ctx->entered_content_phase); + } +#endif + + if (ctx && ctx->entered_rewrite_phase + && !ctx->entered_access_phase + && !ctx->entered_content_phase) + { + r->uri_changed = 1; + return lua_yield(L, 0); + } + + return luaL_error(L, "attempt to call ngx.req.set_uri to do " + "location jump in contexts other than rewrite_by_lua and " + "rewrite_by_lua_file"); } r->valid_location = 0; diff --git a/t/030-uri-args.t b/t/030-uri-args.t index f70b9201..56f6f1db 100644 --- a/t/030-uri-args.t +++ b/t/030-uri-args.t @@ -17,7 +17,7 @@ no_root_location(); $ENV{TEST_NGINX_CLIENT_PORT} ||= $ENV{TEST_NGINX} ||= server_port(); #no_diff(); -#no_long_string(); +no_long_string(); run_tests(); __DATA__ @@ -504,3 +504,122 @@ HTTP/1.0 ca%20t=%25 --- response_body hello + + +=== TEST 20: ngx.req.set_uri with jump not allowed in access phase +--- config + location /bar { + echo $query_string; + } + location /foo { + #set $args 'hello'; + set $err ''; + access_by_lua ' + res, err = pcall(ngx.req.set_uri, "/bar", true); + ngx.var.err = err + '; + echo "err: $err"; + } +--- request + GET /foo?world +--- response_body +err: attempt to call ngx.req.set_uri to do location jump in contexts other than rewrite_by_lua and rewrite_by_lua_file + + + +=== TEST 21: ngx.req.set_uri without jump allowed in access phase +--- config + location /bar { + echo $query_string; + } + location /foo { + #set $args 'hello'; + set $err ''; + access_by_lua ' + ngx.req.set_uri("/bar") + '; + echo "uri: $uri"; + } +--- request + GET /foo?world +--- response_body +uri: /bar + + + +=== TEST 22: ngx.req.set_uri with jump not allowed in content phase +--- config + location /bar { + echo $query_string; + } + location /foo { + #set $args 'hello'; + content_by_lua ' + res, err = pcall(ngx.req.set_uri, "/bar", true); + ngx.say("err: ", err) + '; + } +--- request + GET /foo?world +--- response_body +err: attempt to call ngx.req.set_uri to do location jump in contexts other than rewrite_by_lua and rewrite_by_lua_file + + + +=== TEST 23: ngx.req.set_uri without jump allowed in content phase +--- config + location /bar { + echo $query_string; + } + location /foo { + #set $args 'hello'; + set $err ''; + content_by_lua ' + ngx.req.set_uri("/bar") + ngx.say("uri: ", ngx.var.uri) + '; + } +--- request + GET /foo?world +--- response_body +uri: /bar + + + +=== TEST 24: ngx.req.set_uri with jump not allowed in set_by_lua +--- config + location /bar { + echo $query_string; + } + location /foo { + #set $args 'hello'; + set_by_lua $err ' + res, err = pcall(ngx.req.set_uri, "/bar", true); + return err + '; + echo "err: $err"; + } +--- request + GET /foo?world +--- response_body +err: attempt to call ngx.req.set_uri to do location jump in contexts other than rewrite_by_lua and rewrite_by_lua_file + + + +=== TEST 25: ngx.req.set_uri without jump is allowed in set_by_lua +--- config + location /bar { + echo $query_string; + } + location /foo { + set_by_lua $dummy ' + ngx.req.set_uri("/bar") + return "" + '; + echo "uri: $uri"; + } +--- request + GET /foo?world +--- response_body +uri: /bar + From b298984e034ca830fec4510f05a03cad8d4d0751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sun, 16 Oct 2011 21:45:45 +0800 Subject: [PATCH 16/46] fixed compilation failures when --with-debug is turned off. --- src/ngx_http_lua_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 150c472b..c6622249 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1651,7 +1651,7 @@ static ngx_int_t ngx_http_lua_handle_rewrite_jump(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int cc_ref) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua thread aborting request with URI rewrite jump: \"%V?%V\"", &r->uri, &r->args); From b45910b44e94fa2c9c9828414fe5dddd6f413cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Mon, 17 Oct 2011 15:32:47 +0800 Subject: [PATCH 17/46] minor code refactoring. --- config | 4 +- src/ngx_http_lua_args.c | 82 ----------------------------------- src/ngx_http_lua_uri.c | 96 +++++++++++++++++++++++++++++++++++++++++ src/ngx_http_lua_uri.h | 12 ++++++ src/ngx_http_lua_util.c | 3 ++ 5 files changed, 113 insertions(+), 84 deletions(-) create mode 100644 src/ngx_http_lua_uri.c create mode 100644 src/ngx_http_lua_uri.h diff --git a/config b/config index 033cdf7b..c7d0453e 100644 --- a/config +++ b/config @@ -140,8 +140,8 @@ fi ngx_addon_name=ngx_http_lua_module HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_lua_module" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_lua_script.c $ngx_addon_dir/src/ngx_http_lua_log.c $ngx_addon_dir/src/ngx_http_lua_subrequest.c $ngx_addon_dir/src/ngx_http_lua_ndk.c $ngx_addon_dir/src/ngx_http_lua_control.c $ngx_addon_dir/src/ngx_http_lua_time.c $ngx_addon_dir/src/ngx_http_lua_misc.c $ngx_addon_dir/src/ngx_http_lua_variable.c $ngx_addon_dir/src/ngx_http_lua_string.c $ngx_addon_dir/src/ngx_http_lua_output.c $ngx_addon_dir/src/ngx_http_lua_headers.c $ngx_addon_dir/src/ngx_http_lua_args.c $ngx_addon_dir/src/ngx_http_lua_ctx.c $ngx_addon_dir/src/ngx_http_lua_regex.c $ngx_addon_dir/src/ngx_http_lua_module.c $ngx_addon_dir/src/ngx_http_lua_headers_out.c $ngx_addon_dir/src/ngx_http_lua_headers_in.c $ngx_addon_dir/src/ngx_http_lua_directive.c $ngx_addon_dir/src/ngx_http_lua_consts.c $ngx_addon_dir/src/ngx_http_lua_exception.c $ngx_addon_dir/src/ngx_http_lua_util.c $ngx_addon_dir/src/ngx_http_lua_cache.c $ngx_addon_dir/src/ngx_http_lua_conf.c $ngx_addon_dir/src/ngx_http_lua_contentby.c $ngx_addon_dir/src/ngx_http_lua_rewriteby.c $ngx_addon_dir/src/ngx_http_lua_accessby.c $ngx_addon_dir/src/ngx_http_lua_setby.c $ngx_addon_dir/src/ngx_http_lua_capturefilter.c $ngx_addon_dir/src/ngx_http_lua_clfactory.c $ngx_addon_dir/src/ngx_http_lua_pcrefix.c $ngx_addon_dir/src/ngx_http_lua_headerfilterby.c" -NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_lua_script.h $ngx_addon_dir/src/ngx_http_lua_log.h $ngx_addon_dir/src/ngx_http_lua_subrequest.h $ngx_addon_dir/src/ngx_http_lua_ndk.h $ngx_addon_dir/src/ngx_http_lua_control.h $ngx_addon_dir/src/ngx_http_lua_time.h $ngx_addon_dir/src/ngx_http_lua_string.h $ngx_addon_dir/src/ngx_http_lua_misc.h $ngx_addon_dir/src/ngx_http_lua_variable.h $ngx_addon_dir/src/ngx_http_lua_output.h $ngx_addon_dir/src/ngx_http_lua_headers.h $ngx_addon_dir/src/ngx_http_lua_args.h $ngx_addon_dir/src/ngx_http_lua_ctx.h $ngx_addon_dir/src/ngx_http_lua_regex.h $ngx_addon_dir/src/ngx_http_lua_common.h $ngx_addon_dir/src/ngx_http_lua_directive.h $ngx_addon_dir/src/ngx_http_lua_headers_out.h $ngx_addon_dir/src/ngx_http_lua_headers_in.h $ngx_addon_dir/src/ngx_http_lua_consts.h $ngx_addon_dir/src/ngx_http_lua_exception.h $ngx_addon_dir/src/ngx_http_lua_util.h $ngx_addon_dir/src/ngx_http_lua_cache.h $ngx_addon_dir/src/ngx_http_lua_conf.h $ngx_addon_dir/src/ngx_http_lua_contentby.h $ngx_addon_dir/src/ngx_http_lua_rewriteby.h $ngx_addon_dir/src/ngx_http_lua_accessby.h $ngx_addon_dir/src/ngx_http_lua_setby.h $ngx_addon_dir/src/ngx_http_lua_capturefilter.h $ngx_addon_dir/src/ngx_http_lua_clfactory.h $ngx_addon_dir/src/ngx_http_lua_pcrefix.h $ngx_addon_dir/src/ngx_http_lua_headerfilterby.h" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_lua_script.c $ngx_addon_dir/src/ngx_http_lua_log.c $ngx_addon_dir/src/ngx_http_lua_subrequest.c $ngx_addon_dir/src/ngx_http_lua_ndk.c $ngx_addon_dir/src/ngx_http_lua_control.c $ngx_addon_dir/src/ngx_http_lua_time.c $ngx_addon_dir/src/ngx_http_lua_misc.c $ngx_addon_dir/src/ngx_http_lua_variable.c $ngx_addon_dir/src/ngx_http_lua_string.c $ngx_addon_dir/src/ngx_http_lua_output.c $ngx_addon_dir/src/ngx_http_lua_headers.c $ngx_addon_dir/src/ngx_http_lua_uri.c $ngx_addon_dir/src/ngx_http_lua_args.c $ngx_addon_dir/src/ngx_http_lua_ctx.c $ngx_addon_dir/src/ngx_http_lua_regex.c $ngx_addon_dir/src/ngx_http_lua_module.c $ngx_addon_dir/src/ngx_http_lua_headers_out.c $ngx_addon_dir/src/ngx_http_lua_headers_in.c $ngx_addon_dir/src/ngx_http_lua_directive.c $ngx_addon_dir/src/ngx_http_lua_consts.c $ngx_addon_dir/src/ngx_http_lua_exception.c $ngx_addon_dir/src/ngx_http_lua_util.c $ngx_addon_dir/src/ngx_http_lua_cache.c $ngx_addon_dir/src/ngx_http_lua_conf.c $ngx_addon_dir/src/ngx_http_lua_contentby.c $ngx_addon_dir/src/ngx_http_lua_rewriteby.c $ngx_addon_dir/src/ngx_http_lua_accessby.c $ngx_addon_dir/src/ngx_http_lua_setby.c $ngx_addon_dir/src/ngx_http_lua_capturefilter.c $ngx_addon_dir/src/ngx_http_lua_clfactory.c $ngx_addon_dir/src/ngx_http_lua_pcrefix.c $ngx_addon_dir/src/ngx_http_lua_headerfilterby.c" +NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_lua_script.h $ngx_addon_dir/src/ngx_http_lua_log.h $ngx_addon_dir/src/ngx_http_lua_subrequest.h $ngx_addon_dir/src/ngx_http_lua_ndk.h $ngx_addon_dir/src/ngx_http_lua_control.h $ngx_addon_dir/src/ngx_http_lua_time.h $ngx_addon_dir/src/ngx_http_lua_string.h $ngx_addon_dir/src/ngx_http_lua_misc.h $ngx_addon_dir/src/ngx_http_lua_variable.h $ngx_addon_dir/src/ngx_http_lua_output.h $ngx_addon_dir/src/ngx_http_lua_headers.h $ngx_addon_dir/src/ngx_http_lua_uri.h $ngx_addon_dir/src/ngx_http_lua_args.h $ngx_addon_dir/src/ngx_http_lua_ctx.h $ngx_addon_dir/src/ngx_http_lua_regex.h $ngx_addon_dir/src/ngx_http_lua_common.h $ngx_addon_dir/src/ngx_http_lua_directive.h $ngx_addon_dir/src/ngx_http_lua_headers_out.h $ngx_addon_dir/src/ngx_http_lua_headers_in.h $ngx_addon_dir/src/ngx_http_lua_consts.h $ngx_addon_dir/src/ngx_http_lua_exception.h $ngx_addon_dir/src/ngx_http_lua_util.h $ngx_addon_dir/src/ngx_http_lua_cache.h $ngx_addon_dir/src/ngx_http_lua_conf.h $ngx_addon_dir/src/ngx_http_lua_contentby.h $ngx_addon_dir/src/ngx_http_lua_rewriteby.h $ngx_addon_dir/src/ngx_http_lua_accessby.h $ngx_addon_dir/src/ngx_http_lua_setby.h $ngx_addon_dir/src/ngx_http_lua_capturefilter.h $ngx_addon_dir/src/ngx_http_lua_clfactory.h $ngx_addon_dir/src/ngx_http_lua_pcrefix.h $ngx_addon_dir/src/ngx_http_lua_headerfilterby.h" CFLAGS="$CFLAGS -DNDK_SET_VAR" ngx_feature="export symbols by default" diff --git a/src/ngx_http_lua_args.c b/src/ngx_http_lua_args.c index 08c1ae1d..2c2d0daf 100644 --- a/src/ngx_http_lua_args.c +++ b/src/ngx_http_lua_args.c @@ -1,7 +1,6 @@ #ifndef DDEBUG #define DDEBUG 0 #endif - #include "ddebug.h" #include "ngx_http_lua_args.h" @@ -13,84 +12,6 @@ static int ngx_http_lua_parse_args(ngx_http_request_t *r, lua_State *L, static int ngx_http_lua_ngx_req_set_uri_args(lua_State *L); static int ngx_http_lua_ngx_req_get_uri_args(lua_State *L); static int ngx_http_lua_ngx_req_get_post_args(lua_State *L); -static int ngx_http_lua_ngx_req_set_uri(lua_State *L); - - -static int -ngx_http_lua_ngx_req_set_uri(lua_State *L) { - ngx_http_request_t *r; - size_t len; - u_char *p; - int n; - int jump = 0; - ngx_http_lua_ctx_t *ctx; - - n = lua_gettop(L); - - if (n != 1 && n != 2) { - return luaL_error(L, "expecting 1 argument but seen %d", n); - } - - lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); - r = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - - if (n == 2) { - luaL_checktype(L, 2, LUA_TBOOLEAN); - jump = lua_toboolean(L, 2); - } - - p = (u_char *) luaL_checklstring(L, 1, &len); - - if (len == 0) { - return luaL_error(L, "attempt to use zero-length uri"); - } - - r->uri.data = ngx_palloc(r->pool, len); - ngx_memcpy(r->uri.data, p, len); - - r->uri.len = len; - - r->internal = 1; - r->valid_unparsed_uri = 0; - - ngx_http_set_exten(r); - - if (jump) { - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - -#if defined(DDEBUG) && DDEBUG - if (ctx) { - dd("rewrite: %d, access: %d, content: %d", - (int) ctx->entered_rewrite_phase, - (int) ctx->entered_access_phase, - (int) ctx->entered_content_phase); - } -#endif - - if (ctx && ctx->entered_rewrite_phase - && !ctx->entered_access_phase - && !ctx->entered_content_phase) - { - r->uri_changed = 1; - return lua_yield(L, 0); - } - - return luaL_error(L, "attempt to call ngx.req.set_uri to do " - "location jump in contexts other than rewrite_by_lua and " - "rewrite_by_lua_file"); - } - - r->valid_location = 0; - r->uri_changed = 0; - - return 0; -} static int @@ -388,9 +309,6 @@ ngx_http_lua_parse_args(ngx_http_request_t *r, lua_State *L, u_char *buf, void ngx_http_lua_inject_req_args_api(lua_State *L) { - lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri); - lua_setfield(L, -2, "set_uri"); - lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri_args); lua_setfield(L, -2, "set_uri_args"); diff --git a/src/ngx_http_lua_uri.c b/src/ngx_http_lua_uri.c new file mode 100644 index 00000000..7c607722 --- /dev/null +++ b/src/ngx_http_lua_uri.c @@ -0,0 +1,96 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + +#include "ngx_http_lua_args.h" +#include "ngx_http_lua_util.h" + + +static int ngx_http_lua_ngx_req_set_uri(lua_State *L); + + +void +ngx_http_lua_inject_req_uri_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri); + lua_setfield(L, -2, "set_uri"); +} + + +static int +ngx_http_lua_ngx_req_set_uri(lua_State *L) { + ngx_http_request_t *r; + size_t len; + u_char *p; + int n; + int jump = 0; + ngx_http_lua_ctx_t *ctx; + + n = lua_gettop(L); + + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 argument but seen %d", n); + } + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + if (n == 2) { + luaL_checktype(L, 2, LUA_TBOOLEAN); + jump = lua_toboolean(L, 2); + } + + p = (u_char *) luaL_checklstring(L, 1, &len); + + if (len == 0) { + return luaL_error(L, "attempt to use zero-length uri"); + } + + r->uri.data = ngx_palloc(r->pool, len); + ngx_memcpy(r->uri.data, p, len); + + r->uri.len = len; + + r->internal = 1; + r->valid_unparsed_uri = 0; + + ngx_http_set_exten(r); + + if (jump) { + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + +#if defined(DDEBUG) && DDEBUG + if (ctx) { + dd("rewrite: %d, access: %d, content: %d", + (int) ctx->entered_rewrite_phase, + (int) ctx->entered_access_phase, + (int) ctx->entered_content_phase); + } +#endif + + if (ctx && ctx->entered_rewrite_phase + && !ctx->entered_access_phase + && !ctx->entered_content_phase) + { + r->uri_changed = 1; + return lua_yield(L, 0); + } + + return luaL_error(L, "attempt to call ngx.req.set_uri to do " + "location jump in contexts other than rewrite_by_lua and " + "rewrite_by_lua_file"); + } + + r->valid_location = 0; + r->uri_changed = 0; + + return 0; +} + diff --git a/src/ngx_http_lua_uri.h b/src/ngx_http_lua_uri.h new file mode 100644 index 00000000..2582c1db --- /dev/null +++ b/src/ngx_http_lua_uri.h @@ -0,0 +1,12 @@ +#ifndef NGX_HTTP_LUA_URI_H +#define NGX_HTTP_LUA_URI_H + + +#include "ngx_http_lua_common.h" + + +void ngx_http_lua_inject_req_uri_api(lua_State *L); + + +#endif /* NGX_HTTP_LUA_URI_H */ + diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index c6622249..14f92f54 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -12,6 +12,7 @@ #include "ngx_http_lua_pcrefix.h" #include "ngx_http_lua_regex.h" #include "ngx_http_lua_args.h" +#include "ngx_http_lua_uri.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_output.h" #include "ngx_http_lua_time.h" @@ -1425,6 +1426,8 @@ ngx_http_lua_inject_req_api(lua_State *L) ngx_http_lua_inject_req_header_api(L); + ngx_http_lua_inject_req_uri_api(L); + ngx_http_lua_inject_req_args_api(L); lua_setfield(L, -2, "req"); From 0f18d9936be387be247a86683f145d78d510a611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Mon, 17 Oct 2011 22:10:20 +0800 Subject: [PATCH 18/46] implemented ngx.req.read_body() which does synchronous non-blocking request body reading in full buffered mode. --- .gitignore | 2 + config | 4 +- src/ngx_http_lua_accessby.c | 4 + src/ngx_http_lua_common.h | 1 + src/ngx_http_lua_contentby.c | 4 + src/ngx_http_lua_headerfilterby.c | 2 +- src/ngx_http_lua_req_body.c | 112 ++++++++++++++++++++++++ src/ngx_http_lua_req_body.h | 12 +++ src/ngx_http_lua_rewriteby.c | 4 + src/ngx_http_lua_setby.c | 2 +- src/ngx_http_lua_subrequest.c | 3 + src/ngx_http_lua_uri.c | 9 +- src/ngx_http_lua_util.c | 81 +++++++++++++++-- src/ngx_http_lua_util.h | 1 + t/023-rewrite/req-body.t | 103 ++++++++++++++++++++++ t/024-access/req-body.t | 103 ++++++++++++++++++++++ t/044-req-body.t | 139 ++++++++++++++++++++++++++++++ 17 files changed, 567 insertions(+), 19 deletions(-) create mode 100644 src/ngx_http_lua_req_body.c create mode 100644 src/ngx_http_lua_req_body.h create mode 100644 t/023-rewrite/req-body.t create mode 100644 t/024-access/req-body.t create mode 100644 t/044-req-body.t diff --git a/.gitignore b/.gitignore index 9231cd0d..b3b6aa1e 100644 --- a/.gitignore +++ b/.gitignore @@ -119,6 +119,8 @@ src/args.[ch] src/headers.[ch] src/script.[ch] src/filter.[ch] +src/body.[ch] +src/uri.[ch] a.patch all build1[01] diff --git a/config b/config index c7d0453e..cf2be450 100644 --- a/config +++ b/config @@ -140,8 +140,8 @@ fi ngx_addon_name=ngx_http_lua_module HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_lua_module" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_lua_script.c $ngx_addon_dir/src/ngx_http_lua_log.c $ngx_addon_dir/src/ngx_http_lua_subrequest.c $ngx_addon_dir/src/ngx_http_lua_ndk.c $ngx_addon_dir/src/ngx_http_lua_control.c $ngx_addon_dir/src/ngx_http_lua_time.c $ngx_addon_dir/src/ngx_http_lua_misc.c $ngx_addon_dir/src/ngx_http_lua_variable.c $ngx_addon_dir/src/ngx_http_lua_string.c $ngx_addon_dir/src/ngx_http_lua_output.c $ngx_addon_dir/src/ngx_http_lua_headers.c $ngx_addon_dir/src/ngx_http_lua_uri.c $ngx_addon_dir/src/ngx_http_lua_args.c $ngx_addon_dir/src/ngx_http_lua_ctx.c $ngx_addon_dir/src/ngx_http_lua_regex.c $ngx_addon_dir/src/ngx_http_lua_module.c $ngx_addon_dir/src/ngx_http_lua_headers_out.c $ngx_addon_dir/src/ngx_http_lua_headers_in.c $ngx_addon_dir/src/ngx_http_lua_directive.c $ngx_addon_dir/src/ngx_http_lua_consts.c $ngx_addon_dir/src/ngx_http_lua_exception.c $ngx_addon_dir/src/ngx_http_lua_util.c $ngx_addon_dir/src/ngx_http_lua_cache.c $ngx_addon_dir/src/ngx_http_lua_conf.c $ngx_addon_dir/src/ngx_http_lua_contentby.c $ngx_addon_dir/src/ngx_http_lua_rewriteby.c $ngx_addon_dir/src/ngx_http_lua_accessby.c $ngx_addon_dir/src/ngx_http_lua_setby.c $ngx_addon_dir/src/ngx_http_lua_capturefilter.c $ngx_addon_dir/src/ngx_http_lua_clfactory.c $ngx_addon_dir/src/ngx_http_lua_pcrefix.c $ngx_addon_dir/src/ngx_http_lua_headerfilterby.c" -NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_lua_script.h $ngx_addon_dir/src/ngx_http_lua_log.h $ngx_addon_dir/src/ngx_http_lua_subrequest.h $ngx_addon_dir/src/ngx_http_lua_ndk.h $ngx_addon_dir/src/ngx_http_lua_control.h $ngx_addon_dir/src/ngx_http_lua_time.h $ngx_addon_dir/src/ngx_http_lua_string.h $ngx_addon_dir/src/ngx_http_lua_misc.h $ngx_addon_dir/src/ngx_http_lua_variable.h $ngx_addon_dir/src/ngx_http_lua_output.h $ngx_addon_dir/src/ngx_http_lua_headers.h $ngx_addon_dir/src/ngx_http_lua_uri.h $ngx_addon_dir/src/ngx_http_lua_args.h $ngx_addon_dir/src/ngx_http_lua_ctx.h $ngx_addon_dir/src/ngx_http_lua_regex.h $ngx_addon_dir/src/ngx_http_lua_common.h $ngx_addon_dir/src/ngx_http_lua_directive.h $ngx_addon_dir/src/ngx_http_lua_headers_out.h $ngx_addon_dir/src/ngx_http_lua_headers_in.h $ngx_addon_dir/src/ngx_http_lua_consts.h $ngx_addon_dir/src/ngx_http_lua_exception.h $ngx_addon_dir/src/ngx_http_lua_util.h $ngx_addon_dir/src/ngx_http_lua_cache.h $ngx_addon_dir/src/ngx_http_lua_conf.h $ngx_addon_dir/src/ngx_http_lua_contentby.h $ngx_addon_dir/src/ngx_http_lua_rewriteby.h $ngx_addon_dir/src/ngx_http_lua_accessby.h $ngx_addon_dir/src/ngx_http_lua_setby.h $ngx_addon_dir/src/ngx_http_lua_capturefilter.h $ngx_addon_dir/src/ngx_http_lua_clfactory.h $ngx_addon_dir/src/ngx_http_lua_pcrefix.h $ngx_addon_dir/src/ngx_http_lua_headerfilterby.h" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_lua_script.c $ngx_addon_dir/src/ngx_http_lua_log.c $ngx_addon_dir/src/ngx_http_lua_subrequest.c $ngx_addon_dir/src/ngx_http_lua_ndk.c $ngx_addon_dir/src/ngx_http_lua_control.c $ngx_addon_dir/src/ngx_http_lua_time.c $ngx_addon_dir/src/ngx_http_lua_misc.c $ngx_addon_dir/src/ngx_http_lua_variable.c $ngx_addon_dir/src/ngx_http_lua_string.c $ngx_addon_dir/src/ngx_http_lua_output.c $ngx_addon_dir/src/ngx_http_lua_headers.c $ngx_addon_dir/src/ngx_http_lua_req_body.c $ngx_addon_dir/src/ngx_http_lua_uri.c $ngx_addon_dir/src/ngx_http_lua_args.c $ngx_addon_dir/src/ngx_http_lua_ctx.c $ngx_addon_dir/src/ngx_http_lua_regex.c $ngx_addon_dir/src/ngx_http_lua_module.c $ngx_addon_dir/src/ngx_http_lua_headers_out.c $ngx_addon_dir/src/ngx_http_lua_headers_in.c $ngx_addon_dir/src/ngx_http_lua_directive.c $ngx_addon_dir/src/ngx_http_lua_consts.c $ngx_addon_dir/src/ngx_http_lua_exception.c $ngx_addon_dir/src/ngx_http_lua_util.c $ngx_addon_dir/src/ngx_http_lua_cache.c $ngx_addon_dir/src/ngx_http_lua_conf.c $ngx_addon_dir/src/ngx_http_lua_contentby.c $ngx_addon_dir/src/ngx_http_lua_rewriteby.c $ngx_addon_dir/src/ngx_http_lua_accessby.c $ngx_addon_dir/src/ngx_http_lua_setby.c $ngx_addon_dir/src/ngx_http_lua_capturefilter.c $ngx_addon_dir/src/ngx_http_lua_clfactory.c $ngx_addon_dir/src/ngx_http_lua_pcrefix.c $ngx_addon_dir/src/ngx_http_lua_headerfilterby.c" +NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_lua_script.h $ngx_addon_dir/src/ngx_http_lua_log.h $ngx_addon_dir/src/ngx_http_lua_subrequest.h $ngx_addon_dir/src/ngx_http_lua_ndk.h $ngx_addon_dir/src/ngx_http_lua_control.h $ngx_addon_dir/src/ngx_http_lua_time.h $ngx_addon_dir/src/ngx_http_lua_string.h $ngx_addon_dir/src/ngx_http_lua_misc.h $ngx_addon_dir/src/ngx_http_lua_variable.h $ngx_addon_dir/src/ngx_http_lua_output.h $ngx_addon_dir/src/ngx_http_lua_headers.h $ngx_addon_dir/src/ngx_http_lua_req_body.h $ngx_addon_dir/src/ngx_http_lua_uri.h $ngx_addon_dir/src/ngx_http_lua_req_body.h $ngx_addon_dir/src/ngx_http_lua_args.h $ngx_addon_dir/src/ngx_http_lua_ctx.h $ngx_addon_dir/src/ngx_http_lua_regex.h $ngx_addon_dir/src/ngx_http_lua_common.h $ngx_addon_dir/src/ngx_http_lua_directive.h $ngx_addon_dir/src/ngx_http_lua_headers_out.h $ngx_addon_dir/src/ngx_http_lua_headers_in.h $ngx_addon_dir/src/ngx_http_lua_consts.h $ngx_addon_dir/src/ngx_http_lua_exception.h $ngx_addon_dir/src/ngx_http_lua_util.h $ngx_addon_dir/src/ngx_http_lua_cache.h $ngx_addon_dir/src/ngx_http_lua_conf.h $ngx_addon_dir/src/ngx_http_lua_contentby.h $ngx_addon_dir/src/ngx_http_lua_rewriteby.h $ngx_addon_dir/src/ngx_http_lua_accessby.h $ngx_addon_dir/src/ngx_http_lua_setby.h $ngx_addon_dir/src/ngx_http_lua_capturefilter.h $ngx_addon_dir/src/ngx_http_lua_clfactory.h $ngx_addon_dir/src/ngx_http_lua_pcrefix.h $ngx_addon_dir/src/ngx_http_lua_headerfilterby.h" CFLAGS="$CFLAGS -DNDK_SET_VAR" ngx_feature="export symbols by default" diff --git a/src/ngx_http_lua_accessby.c b/src/ngx_http_lua_accessby.c index 5fd7a175..00e6acc4 100644 --- a/src/ngx_http_lua_accessby.c +++ b/src/ngx_http_lua_accessby.c @@ -245,6 +245,10 @@ ngx_http_lua_access_handler(ngx_http_request_t *r) ! ctx->read_body_done && ((r->method & NGX_HTTP_POST) || (r->method & NGX_HTTP_PUT))) { + r->request_body_in_single_buf = 1; + r->request_body_in_persistent_file = 1; + r->request_body_in_clean_file = 1; + rc = ngx_http_read_client_request_body(r, ngx_http_lua_generic_phase_post_read); diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 8dda33a9..fa9ae233 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -171,6 +171,7 @@ typedef struct { unsigned waiting_more_body:1; /* 1: waiting for more data; 0: no need to wait */ + unsigned req_read_body_done:1; /* used by ngx.req.read_body */ unsigned headers_set:1; unsigned entered_rewrite_phase:1; diff --git a/src/ngx_http_lua_contentby.c b/src/ngx_http_lua_contentby.c index 4b5fb626..145165f0 100644 --- a/src/ngx_http_lua_contentby.c +++ b/src/ngx_http_lua_contentby.c @@ -147,6 +147,10 @@ ngx_http_lua_content_handler(ngx_http_request_t *r) ! ctx->read_body_done && ((r->method & NGX_HTTP_POST) || (r->method & NGX_HTTP_PUT))) { + r->request_body_in_single_buf = 1; + r->request_body_in_persistent_file = 1; + r->request_body_in_clean_file = 1; + rc = ngx_http_read_client_request_body(r, ngx_http_lua_content_phase_post_read); diff --git a/src/ngx_http_lua_headerfilterby.c b/src/ngx_http_lua_headerfilterby.c index 66b99b2f..bc880f5c 100644 --- a/src/ngx_http_lua_headerfilterby.c +++ b/src/ngx_http_lua_headerfilterby.c @@ -71,7 +71,7 @@ ngx_http_lua_header_filter_by_lua_env(lua_State *L, ngx_http_request_t *r) #if (NGX_PCRE) ngx_http_lua_inject_regex_api(L); #endif - ngx_http_lua_inject_req_api(L); + ngx_http_lua_inject_req_api_no_io(L); ngx_http_lua_inject_resp_header_api(L); ngx_http_lua_inject_variable_api(L); ngx_http_lua_inject_misc_api(L); diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c new file mode 100644 index 00000000..f4bc666b --- /dev/null +++ b/src/ngx_http_lua_req_body.c @@ -0,0 +1,112 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + +#include "ngx_http_lua_req_body.h" +#include "ngx_http_lua_util.h" + + +static int ngx_http_lua_ngx_req_read_body(lua_State *L); +static void ngx_http_lua_req_body_post_read(ngx_http_request_t *r); + + +void +ngx_http_lua_inject_req_body_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_http_lua_ngx_req_read_body); + lua_setfield(L, -2, "read_body"); +} + + +static int +ngx_http_lua_ngx_req_read_body(lua_State *L) { + ngx_http_request_t *r; + int n; + ngx_http_lua_ctx_t *ctx; + ngx_int_t rc; + + n = lua_gettop(L); + + if (n != 0) { + return luaL_error(L, "expecting 0 arguments but seen %d", n); + } + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + r->request_body_in_single_buf = 1; + r->request_body_in_persistent_file = 1; + r->request_body_in_clean_file = 1; + +#if 0 + if (r->request_body_in_file_only) { + r->request_body_file_log_level = 0; + } +#endif + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "request context is null"); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua start to read buffered request body"); + + rc = ngx_http_read_client_request_body(r, ngx_http_lua_req_body_post_read); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return luaL_error(L, "failed to read request body"); + } + + if (rc == NGX_AGAIN) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua read buffered request body requires I/O interruptions"); + + ctx->waiting_more_body = 1; + ctx->req_read_body_done = 0; + + return lua_yield(L, 0); + } + + /* rc == NGX_OK */ + + ctx->req_read_body_done = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua has read buffered request body in a single run"); + + return 0; +} + + +static void +ngx_http_lua_req_body_post_read(ngx_http_request_t *r) +{ + ngx_http_lua_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua req body post read"); + + r->read_event_handler = ngx_http_request_empty_handler; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + ctx->req_read_body_done = 1; + +#if defined(nginx_version) && nginx_version >= 8011 + r->main->count--; +#endif + + if (ctx->waiting_more_body) { + ctx->waiting_more_body = 0; + + if (ctx->entered_content_phase) { + ngx_http_lua_wev_handler(r); + + } else { + ngx_http_core_run_phases(r); + } + } +} + diff --git a/src/ngx_http_lua_req_body.h b/src/ngx_http_lua_req_body.h new file mode 100644 index 00000000..3ba52b67 --- /dev/null +++ b/src/ngx_http_lua_req_body.h @@ -0,0 +1,12 @@ +#ifndef NGX_HTTP_LUA_REQ_BODY_H +#define NGX_HTTP_LUA_REQ_BODY_H + + +#include "ngx_http_lua_common.h" + + +void ngx_http_lua_inject_req_body_api(lua_State *L); + + +#endif /* NGX_HTTP_LUA_REQ_BODY_H */ + diff --git a/src/ngx_http_lua_rewriteby.c b/src/ngx_http_lua_rewriteby.c index ab10bead..1a9842f3 100644 --- a/src/ngx_http_lua_rewriteby.c +++ b/src/ngx_http_lua_rewriteby.c @@ -241,6 +241,10 @@ ngx_http_lua_rewrite_handler(ngx_http_request_t *r) ! ctx->read_body_done && ((r->method & NGX_HTTP_POST) || (r->method & NGX_HTTP_PUT))) { + r->request_body_in_single_buf = 1; + r->request_body_in_persistent_file = 1; + r->request_body_in_clean_file = 1; + rc = ngx_http_read_client_request_body(r, ngx_http_lua_generic_phase_post_read); diff --git a/src/ngx_http_lua_setby.c b/src/ngx_http_lua_setby.c index 61874c7b..e3cf75aa 100644 --- a/src/ngx_http_lua_setby.c +++ b/src/ngx_http_lua_setby.c @@ -196,7 +196,7 @@ ngx_http_lua_set_by_lua_env(lua_State *L, ngx_http_request_t *r, size_t nargs, ngx_http_lua_inject_time_api(L); ngx_http_lua_inject_string_api(L); ngx_http_lua_inject_variable_api(L); - ngx_http_lua_inject_req_api(L); + ngx_http_lua_inject_req_api_no_io(L); ngx_http_lua_inject_arg_api(L, nargs, args); #if (NGX_PCRE) ngx_http_lua_inject_regex_api(L); diff --git a/src/ngx_http_lua_subrequest.c b/src/ngx_http_lua_subrequest.c index 437d2ae0..2889f48e 100644 --- a/src/ngx_http_lua_subrequest.c +++ b/src/ngx_http_lua_subrequest.c @@ -743,6 +743,9 @@ ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r, u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua handle subrequest responses"); + for (index = 0; index < ctx->nsubreqs; index++) { dd("summary: reqs %d, subquery %d, waiting %d, req %.*s", (int) ctx->nsubreqs, diff --git a/src/ngx_http_lua_uri.c b/src/ngx_http_lua_uri.c index 7c607722..b4538648 100644 --- a/src/ngx_http_lua_uri.c +++ b/src/ngx_http_lua_uri.c @@ -3,7 +3,7 @@ #endif #include "ddebug.h" -#include "ngx_http_lua_args.h" +#include "ngx_http_lua_uri.h" #include "ngx_http_lua_util.h" @@ -19,7 +19,8 @@ ngx_http_lua_inject_req_uri_api(lua_State *L) static int -ngx_http_lua_ngx_req_set_uri(lua_State *L) { +ngx_http_lua_ngx_req_set_uri(lua_State *L) +{ ngx_http_request_t *r; size_t len; u_char *p; @@ -37,10 +38,6 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L) { r = lua_touserdata(L, -1); lua_pop(L, 1); - if (r == NULL) { - return luaL_error(L, "no request object found"); - } - if (n == 2) { luaL_checktype(L, 2, LUA_TBOOLEAN); jump = lua_toboolean(L, 2); diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 14f92f54..e9125a85 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -13,6 +13,7 @@ #include "ngx_http_lua_regex.h" #include "ngx_http_lua_args.h" #include "ngx_http_lua_uri.h" +#include "ngx_http_lua_req_body.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_output.h" #include "ngx_http_lua_time.h" @@ -759,6 +760,8 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_pcre_malloc_init(r->pool); #endif + dd("calling lua_resume: vm %p, nret %d", cc, (int) nret); + /* run code */ rv = lua_resume(cc, nret); @@ -873,6 +876,7 @@ ngx_http_lua_wev_handler(ngx_http_request_t *r) ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ngx_http_lua_main_conf_t *lmcf; + int nret = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run write event handler"); @@ -908,6 +912,13 @@ ngx_http_lua_wev_handler(ngx_http_request_t *r) return NGX_OK; } + if (ctx->waiting_more_body && !ctx->req_read_body_done) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua write event handler waiting for more request body data"); + + return NGX_DONE; + } + dd("waiting: %d, done: %d", (int) ctx->waiting, ctx->done); @@ -945,27 +956,60 @@ ngx_http_lua_wev_handler(ngx_http_request_t *r) return NGX_DONE; } - ctx->done = 0; + dd("req read body done: %d", (int) ctx->req_read_body_done); - dd("nsubreqs: %d", (int) ctx->nsubreqs); + if (ctx->req_read_body_done) { + dd("turned off req read body done"); - ngx_http_lua_handle_subreq_responses(r, ctx); + ctx->req_read_body_done = 0; - dd("free sr_statues/headers/bodies memory ASAP"); + nret = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua read req body done, resuming lua thread"); + + goto run; + + } else if (ctx->done) { + ctx->done = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua run subrequests done, resuming lua thread"); + + dd("nsubreqs: %d", (int) ctx->nsubreqs); + + ngx_http_lua_handle_subreq_responses(r, ctx); + + dd("free sr_statues/headers/bodies memory ASAP"); #if 1 - ngx_pfree(r->pool, ctx->sr_statuses); + ngx_pfree(r->pool, ctx->sr_statuses); - ctx->sr_statuses = NULL; - ctx->sr_headers = NULL; - ctx->sr_bodies = NULL; + ctx->sr_statuses = NULL; + ctx->sr_headers = NULL; + ctx->sr_bodies = NULL; #endif + nret = ctx->nsubreqs; + + goto run; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "useless lua write event handler"); + + if (ctx->entered_content_phase) { + ngx_http_finalize_request(r, NGX_DONE); + } + + return NGX_OK; + +run: lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); dd("about to run thread for %.*s...", (int) r->uri.len, r->uri.data); - rc = ngx_http_lua_run_thread(lmcf->lua, r, ctx, ctx->nsubreqs); + rc = ngx_http_lua_run_thread(lmcf->lua, r, ctx, nret); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); @@ -1417,6 +1461,23 @@ done: } +void +ngx_http_lua_inject_req_api_no_io(lua_State *L) +{ + /* ngx.req table */ + + lua_newtable(L); /* .req */ + + ngx_http_lua_inject_req_header_api(L); + + ngx_http_lua_inject_req_uri_api(L); + + ngx_http_lua_inject_req_args_api(L); + + lua_setfield(L, -2, "req"); +} + + void ngx_http_lua_inject_req_api(lua_State *L) { @@ -1430,6 +1491,8 @@ ngx_http_lua_inject_req_api(lua_State *L) ngx_http_lua_inject_req_args_api(L); + ngx_http_lua_inject_req_body_api(L); + lua_setfield(L, -2, "req"); } diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index e832c9a5..02aa0f67 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -49,6 +49,7 @@ void ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size, uintptr_t ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type); void ngx_http_lua_inject_req_api(lua_State *L); +void ngx_http_lua_inject_req_api_no_io(lua_State *L); void ngx_http_lua_process_args_option(ngx_http_request_t *r, lua_State *L, int table, ngx_str_t *args); diff --git a/t/023-rewrite/req-body.t b/t/023-rewrite/req-body.t new file mode 100644 index 00000000..d58cf62e --- /dev/null +++ b/t/023-rewrite/req-body.t @@ -0,0 +1,103 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use lib 'lib'; +use Test::Nginx::Socket; + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2); + +#no_diff(); +#no_long_string(); +#master_on(); +#workers(2); +run_tests(); + +__DATA__ + +=== TEST 1: read buffered body +--- config + location = /test { + rewrite_by_lua ' + ngx.req.read_body() + ngx.say(ngx.var.request_body) + '; + content_by_lua return; + } +--- request +POST /test +hello, world +--- response_body +hello, world + + + +=== TEST 2: read buffered body (timed out) +--- config + client_body_timeout 1ms; + location = /test { + rewrite_by_lua ' + ngx.req.read_body() + ngx.say(ngx.var.request_body) + '; + content_by_lua return; + } +--- raw_request eval +"POST /test HTTP/1.1\r +Host: localhost\r +Content-Length: 100\r +Connection: close\r + +hello, world" +--- response_body: +--- error_code: + + + +=== TEST 3: read buffered body and then subrequest +--- config + location /foo { + echo -n foo; + } + location = /test { + rewrite_by_lua ' + ngx.req.read_body() + local res = ngx.location.capture("/foo"); + ngx.say(ngx.var.request_body) + ngx.say("sub: ", res.body) + '; + content_by_lua return; + } +--- request +POST /test +hello, world +--- response_body +hello, world +sub: foo + + + +=== TEST 4: first subrequest and then read buffered body +--- config + location /foo { + echo -n foo; + } + location = /test { + rewrite_by_lua ' + local res = ngx.location.capture("/foo"); + ngx.req.read_body() + ngx.say(ngx.var.request_body) + ngx.say("sub: ", res.body) + '; + content_by_lua return; + } +--- request +POST /test +hello, world +--- response_body +hello, world +sub: foo + diff --git a/t/024-access/req-body.t b/t/024-access/req-body.t new file mode 100644 index 00000000..65adc1a7 --- /dev/null +++ b/t/024-access/req-body.t @@ -0,0 +1,103 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use lib 'lib'; +use Test::Nginx::Socket; + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2); + +#no_diff(); +#no_long_string(); +#master_on(); +#workers(2); +run_tests(); + +__DATA__ + +=== TEST 1: read buffered body +--- config + location = /test { + access_by_lua ' + ngx.req.read_body() + ngx.say(ngx.var.request_body) + '; + content_by_lua return; + } +--- request +POST /test +hello, world +--- response_body +hello, world + + + +=== TEST 2: read buffered body (timed out) +--- config + client_body_timeout 1ms; + location = /test { + access_by_lua ' + ngx.req.read_body() + ngx.say(ngx.var.request_body) + '; + content_by_lua return; + } +--- raw_request eval +"POST /test HTTP/1.1\r +Host: localhost\r +Content-Length: 100\r +Connection: close\r + +hello, world" +--- response_body: +--- error_code: + + + +=== TEST 3: read buffered body and then subrequest +--- config + location /foo { + echo -n foo; + } + location = /test { + access_by_lua ' + ngx.req.read_body() + local res = ngx.location.capture("/foo"); + ngx.say(ngx.var.request_body) + ngx.say("sub: ", res.body) + '; + content_by_lua return; + } +--- request +POST /test +hello, world +--- response_body +hello, world +sub: foo + + + +=== TEST 4: first subrequest and then read buffered body +--- config + location /foo { + echo -n foo; + } + location = /test { + access_by_lua ' + local res = ngx.location.capture("/foo"); + ngx.req.read_body() + ngx.say(ngx.var.request_body) + ngx.say("sub: ", res.body) + '; + content_by_lua return; + } +--- request +POST /test +hello, world +--- response_body +hello, world +sub: foo + diff --git a/t/044-req-body.t b/t/044-req-body.t new file mode 100644 index 00000000..790fab4a --- /dev/null +++ b/t/044-req-body.t @@ -0,0 +1,139 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use lib 'lib'; +use Test::Nginx::Socket; + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2); + +#no_diff(); +#no_long_string(); +#master_on(); +#workers(2); +run_tests(); + +__DATA__ + +=== TEST 1: read buffered body +--- config + location = /test { + content_by_lua ' + ngx.req.read_body() + ngx.say(ngx.var.request_body) + '; + } +--- request +POST /test +hello, world +--- response_body +hello, world + + + +=== TEST 2: read buffered body (timed out) +--- config + client_body_timeout 1ms; + location = /test { + content_by_lua ' + ngx.req.read_body() + ngx.say(ngx.var.request_body) + '; + } +--- raw_request eval +"POST /test HTTP/1.1\r +Host: localhost\r +Content-Length: 100\r +Connection: close\r + +hello, world" +--- response_body: +--- error_code: + + + +=== TEST 3: read buffered body and then subrequest +--- config + location /foo { + echo -n foo; + } + location = /test { + content_by_lua ' + ngx.req.read_body() + local res = ngx.location.capture("/foo"); + ngx.say(ngx.var.request_body) + ngx.say("sub: ", res.body) + '; + } +--- request +POST /test +hello, world +--- response_body +hello, world +sub: foo + + + +=== TEST 4: first subrequest and then read buffered body +--- config + location /foo { + echo -n foo; + } + location = /test { + content_by_lua ' + local res = ngx.location.capture("/foo"); + ngx.req.read_body() + ngx.say(ngx.var.request_body) + ngx.say("sub: ", res.body) + '; + } +--- request +POST /test +hello, world +--- response_body +hello, world +sub: foo + + + +=== TEST 5: read_body not allowed in set_by_lua +--- config + location /foo { + echo -n foo; + } + location = /test { + set_by_lua $has_read_body ' + return ngx.req.read_body and "defined" or "undef" + '; + echo "ngx.req.read_body: $has_read_body"; + } +--- request +GET /test +--- response_body +ngx.req.read_body: undef + + + +=== TEST 6: read_body not allowed in set_by_lua +--- config + location /foo { + echo -n foo; + } + location = /test { + set $bool ''; + header_filter_by_lua ' + ngx.var.bool = (ngx.req.read_body and "defined" or "undef") + '; + content_by_lua ' + ngx.send_headers() + ngx.say("ngx.req.read_body: ", ngx.var.bool) + '; + } +--- request +GET /test +--- response_body +ngx.req.read_body: undef + From 18f30a16a9fbe41ef7bb81c0a194dd06249c7442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Tue, 18 Oct 2011 11:50:46 +0800 Subject: [PATCH 19/46] now we use ngx_http_block_reading instead of ngx_http_empty_request handler or read event handler. --- src/ngx_http_lua_req_body.c | 2 +- src/ngx_http_lua_util.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index f4bc666b..2fe772ea 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -89,7 +89,7 @@ ngx_http_lua_req_body_post_read(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua req body post read"); - r->read_event_handler = ngx_http_request_empty_handler; + r->read_event_handler = ngx_http_block_reading; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); ctx->req_read_body_done = 1; diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index e9125a85..3b271bdd 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -655,7 +655,7 @@ ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua post read for rewrite/access phases"); - r->read_event_handler = ngx_http_request_empty_handler; + r->read_event_handler = ngx_http_block_reading; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); From 38c832593895b1bfdf7069ecbabe06bceabbc002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Tue, 18 Oct 2011 19:31:20 +0800 Subject: [PATCH 20/46] implemented ngx.req.discard_body() function to let user lua code to discard request body. --- src/ngx_http_lua_req_body.c | 34 +++++++++++++++++++++- t/044-req-body.t | 56 ++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 2fe772ea..451cd315 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -9,6 +9,7 @@ static int ngx_http_lua_ngx_req_read_body(lua_State *L); static void ngx_http_lua_req_body_post_read(ngx_http_request_t *r); +static int ngx_http_lua_ngx_req_discard_body(lua_State *L); void @@ -16,11 +17,15 @@ ngx_http_lua_inject_req_body_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_req_read_body); lua_setfield(L, -2, "read_body"); + + lua_pushcfunction(L, ngx_http_lua_ngx_req_discard_body); + lua_setfield(L, -2, "discard_body"); } static int -ngx_http_lua_ngx_req_read_body(lua_State *L) { +ngx_http_lua_ngx_req_read_body(lua_State *L) +{ ngx_http_request_t *r; int n; ngx_http_lua_ctx_t *ctx; @@ -110,3 +115,30 @@ ngx_http_lua_req_body_post_read(ngx_http_request_t *r) } } + +static int +ngx_http_lua_ngx_req_discard_body(lua_State *L) +{ + ngx_http_request_t *r; + ngx_int_t rc; + int n; + + n = lua_gettop(L); + + if (n != 0) { + return luaL_error(L, "expecting 0 arguments but seen %d", n); + } + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + rc = ngx_http_discard_request_body(r); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return luaL_error(L, "failed to discard request body"); + } + + return 0; +} + diff --git a/t/044-req-body.t b/t/044-req-body.t index 790fab4a..8bf09d5e 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -8,7 +8,7 @@ use Test::Nginx::Socket; repeat_each(2); -plan tests => repeat_each() * (blocks() * 2); +plan tests => repeat_each() * (blocks() * 2 + 4); #no_diff(); #no_long_string(); @@ -137,3 +137,57 @@ GET /test --- response_body ngx.req.read_body: undef + + +=== TEST 7: discard body +--- config + location = /foo { + content_by_lua ' + ngx.req.discard_body() + ngx.say("body: ", ngx.var.request_body) + '; + } + location = /bar { + content_by_lua ' + ngx.req.read_body() + ngx.say("body: ", ngx.var.request_body) + '; + + } +--- pipelined_requests eval +["POST /foo +hello, world", +"POST /bar +hiya, world"] +--- response_body eval +["body: nil\n", +"body: hiya, world\n"] + + + +=== TEST 8: not discard body +--- config + location = /foo { + content_by_lua ' + -- ngx.req.discard_body() + ngx.say("body: ", ngx.var.request_body) + '; + } + location = /bar { + content_by_lua ' + ngx.req.read_body() + ngx.say("body: ", ngx.var.request_body) + '; + + } +--- pipelined_requests eval +["POST /foo +hello, world", +"POST /bar +hiya, world"] +--- response_body eval +["body: nil\n", +qr/400 Bad Request/] +--- error_code eval +[200, ''] + From aefb1c49067a62957424286448b263c92ed4808a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Tue, 18 Oct 2011 20:02:45 +0800 Subject: [PATCH 21/46] implemented new method ngx.req.get_body_data. --- src/ngx_http_lua_req_body.c | 41 +++++++++++++++++++++++++++++++++++++ t/044-req-body.t | 33 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 451cd315..7013b7f2 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -10,6 +10,7 @@ static int ngx_http_lua_ngx_req_read_body(lua_State *L); static void ngx_http_lua_req_body_post_read(ngx_http_request_t *r); static int ngx_http_lua_ngx_req_discard_body(lua_State *L); +static int ngx_http_lua_ngx_req_get_body_data(lua_State *L); void @@ -20,6 +21,9 @@ ngx_http_lua_inject_req_body_api(lua_State *L) lua_pushcfunction(L, ngx_http_lua_ngx_req_discard_body); lua_setfield(L, -2, "discard_body"); + + lua_pushcfunction(L, ngx_http_lua_ngx_req_get_body_data); + lua_setfield(L, -2, "get_body_data"); } @@ -116,6 +120,43 @@ ngx_http_lua_req_body_post_read(ngx_http_request_t *r) } +static int +ngx_http_lua_ngx_req_get_body_data(lua_State *L) +{ + ngx_http_request_t *r; + int n; + size_t len; + + n = lua_gettop(L); + + if (n != 0) { + return luaL_error(L, "expecting 0 arguments but seen %d", n); + } + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r->request_body == NULL + || r->request_body->temp_file + || r->request_body->bufs == NULL) + { + lua_pushnil(L); + return 1; + } + + len = r->request_body->bufs->buf->last - r->request_body->bufs->buf->pos; + + if (len == 0) { + lua_pushnil(L); + return 1; + } + + lua_pushlstring(L, (char *) r->request_body->bufs->buf->pos, len); + return 1; +} + + static int ngx_http_lua_ngx_req_discard_body(lua_State *L) { diff --git a/t/044-req-body.t b/t/044-req-body.t index 8bf09d5e..6c4d832d 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -191,3 +191,36 @@ qr/400 Bad Request/] --- error_code eval [200, ''] + + +=== TEST 9: read buffered body and retrieve the data +--- config + location = /test { + content_by_lua ' + ngx.req.read_body() + ngx.say(ngx.req.get_body_data()) + '; + } +--- request +POST /test +hello, world +--- response_body +hello, world + + + +=== TEST 10: read buffered body to file and call get_body_data +--- config + client_body_in_file_only on; + location = /test { + content_by_lua ' + ngx.req.read_body() + ngx.say(ngx.req.get_body_data()) + '; + } +--- request +POST /test +hello, world +--- response_body +nil + From b0474ca7503c6f448b244f637a88217bcadada4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Tue, 18 Oct 2011 20:15:43 +0800 Subject: [PATCH 22/46] implemented new function ngx.req.get_body_file(). --- src/ngx_http_lua_req_body.c | 45 +++++++++++++++++++++++++++++++------ t/044-req-body.t | 34 +++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 7013b7f2..4d8d84ce 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -11,6 +11,7 @@ static int ngx_http_lua_ngx_req_read_body(lua_State *L); static void ngx_http_lua_req_body_post_read(ngx_http_request_t *r); static int ngx_http_lua_ngx_req_discard_body(lua_State *L); static int ngx_http_lua_ngx_req_get_body_data(lua_State *L); +static int ngx_http_lua_ngx_req_get_body_file(lua_State *L); void @@ -24,6 +25,9 @@ ngx_http_lua_inject_req_body_api(lua_State *L) lua_pushcfunction(L, ngx_http_lua_ngx_req_get_body_data); lua_setfield(L, -2, "get_body_data"); + + lua_pushcfunction(L, ngx_http_lua_ngx_req_get_body_file); + lua_setfield(L, -2, "get_body_file"); } @@ -120,6 +124,33 @@ ngx_http_lua_req_body_post_read(ngx_http_request_t *r) } +static int +ngx_http_lua_ngx_req_discard_body(lua_State *L) +{ + ngx_http_request_t *r; + ngx_int_t rc; + int n; + + n = lua_gettop(L); + + if (n != 0) { + return luaL_error(L, "expecting 0 arguments but seen %d", n); + } + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + rc = ngx_http_discard_request_body(r); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return luaL_error(L, "failed to discard request body"); + } + + return 0; +} + + static int ngx_http_lua_ngx_req_get_body_data(lua_State *L) { @@ -158,10 +189,9 @@ ngx_http_lua_ngx_req_get_body_data(lua_State *L) static int -ngx_http_lua_ngx_req_discard_body(lua_State *L) +ngx_http_lua_ngx_req_get_body_file(lua_State *L) { ngx_http_request_t *r; - ngx_int_t rc; int n; n = lua_gettop(L); @@ -174,12 +204,13 @@ ngx_http_lua_ngx_req_discard_body(lua_State *L) r = lua_touserdata(L, -1); lua_pop(L, 1); - rc = ngx_http_discard_request_body(r); - - if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return luaL_error(L, "failed to discard request body"); + if (r->request_body == NULL || r->request_body->temp_file == NULL) { + lua_pushnil(L); + return 1; } - return 0; + lua_pushlstring(L, (char *) r->request_body->temp_file->file.name.data, + r->request_body->temp_file->file.name.len); + return 1; } diff --git a/t/044-req-body.t b/t/044-req-body.t index 6c4d832d..ff27b558 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -11,7 +11,7 @@ repeat_each(2); plan tests => repeat_each() * (blocks() * 2 + 4); #no_diff(); -#no_long_string(); +no_long_string(); #master_on(); #workers(2); run_tests(); @@ -224,3 +224,35 @@ hello, world --- response_body nil + + +=== TEST 11: read buffered body to file and call get_body_file +--- config + client_body_in_file_only on; + location = /test { + content_by_lua ' + ngx.req.read_body() + ngx.say(ngx.req.get_body_file()) + '; + } +--- request +POST /test +hello, world +--- response_body_like: client_body_temp/ + + + +=== TEST 12: read buffered body to memory and retrieve the file +--- config + location = /test { + content_by_lua ' + ngx.req.read_body() + ngx.say(ngx.req.get_body_file()) + '; + } +--- request +POST /test +hello, world +--- response_body +nil + From b6c7dd053d9425e2a6e7b15bdff263bf1fd81cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Wed, 19 Oct 2011 11:58:18 +0800 Subject: [PATCH 23/46] bugfix: fixed hanging issues when using ngx.exec() within rewrite_by_lua and access_by_lua. thanks Nginx User for reporting it. --- src/ngx_http_lua_accessby.c | 6 ++-- src/ngx_http_lua_rewriteby.c | 12 ++++++-- src/ngx_http_lua_util.c | 21 ++++++------- t/017-exec.t | 32 +++++++++++++++++-- t/023-rewrite/exec.t | 60 ++++++++++++++++++++++++++++++++++-- t/024-access/exec.t | 28 +++++++++++++++++ 6 files changed, 139 insertions(+), 20 deletions(-) diff --git a/src/ngx_http_lua_accessby.c b/src/ngx_http_lua_accessby.c index 00e6acc4..d9c0194c 100644 --- a/src/ngx_http_lua_accessby.c +++ b/src/ngx_http_lua_accessby.c @@ -136,7 +136,8 @@ ngx_http_lua_access_handler_file(ngx_http_request_t *r) } if (rc == NGX_DONE) { - return NGX_HTTP_OK; + ngx_http_finalize_request(r, NGX_DONE); + return NGX_DONE; } if (rc >= NGX_HTTP_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) { @@ -312,7 +313,8 @@ ngx_http_lua_access_handler_inline(ngx_http_request_t *r) } if (rc == NGX_DONE) { - return NGX_HTTP_OK; + ngx_http_finalize_request(r, NGX_DONE); + return NGX_DONE; } if (rc >= NGX_HTTP_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) { diff --git a/src/ngx_http_lua_rewriteby.c b/src/ngx_http_lua_rewriteby.c index 1a9842f3..86245d13 100644 --- a/src/ngx_http_lua_rewriteby.c +++ b/src/ngx_http_lua_rewriteby.c @@ -1,6 +1,9 @@ /* vim:set ft=c ts=4 sw=4 et fdm=marker: */ +#ifndef DDEBUG #define DDEBUG 0 +#endif +#include "ddebug.h" #include #include "ngx_http_lua_rewriteby.h" @@ -134,7 +137,8 @@ ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r) } if (rc == NGX_DONE) { - return NGX_OK; + ngx_http_finalize_request(r, NGX_DONE); + return NGX_DONE; } if (rc >= NGX_HTTP_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) { @@ -272,7 +276,7 @@ ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r) ngx_http_lua_loc_conf_t *llcf; char *err; - dd("HERE"); + dd("rewrite by lua inline"); llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); @@ -308,7 +312,9 @@ ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r) } if (rc == NGX_DONE) { - return NGX_OK; + dd("XXX NGX_DONE returned"); + ngx_http_finalize_request(r, NGX_DONE); + return NGX_DONE; } if (rc >= NGX_HTTP_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) { diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 3b271bdd..3b9b979d 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1019,11 +1019,8 @@ run: } if (rc == NGX_DONE) { - if (ctx->entered_content_phase) { - ngx_http_finalize_request(r, rc); - } - - return NGX_OK; + ngx_http_finalize_request(r, rc); + return NGX_DONE; } dd("entered content phase: %d", (int) ctx->entered_content_phase); @@ -1532,14 +1529,15 @@ ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, return rc; } - if (! ctx->entered_content_phase && - r != r->connection->data) - { +#if 0 + if (!ctx->entered_content_phase) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ r->main->count--; + dd("XXX decrement main count: c:%d", (int) r->main->count); } +#endif return NGX_DONE; } @@ -1562,14 +1560,15 @@ ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, dd("XXYY HERE %d\n", (int) r->main->count); - if (! ctx->entered_content_phase && - r != r->connection->data) - { +#if 0 + if (!ctx->entered_content_phase && r != r->connection->data && r->postponed) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ + dd("XXX decrement main count"); r->main->count--; } +#endif return NGX_DONE; } diff --git a/t/017-exec.t b/t/017-exec.t index a9fd925f..84862e72 100644 --- a/t/017-exec.t +++ b/t/017-exec.t @@ -3,7 +3,7 @@ use lib 'lib'; use Test::Nginx::Socket; -repeat_each(2); +#repeat_each(2); plan tests => blocks() * repeat_each() * 2; @@ -440,7 +440,7 @@ hello -=== TEST 20: reset ctx +=== TEST 20: exec + rewrite + named locations --- config location @proxy { rewrite_by_lua return; @@ -518,3 +518,31 @@ hello hello hello + + +=== TEST 23: content_by_lua + ngx.exec + subrequest capture +--- config + location /main { + rewrite_by_lua ' + res = ngx.location.capture("/test_loc"); + ngx.print("hello, ", res.body) + '; + content_by_lua return; + } + location /test_loc { + content_by_lua ' + ngx.exec("@proxy") + '; + } + location @proxy { + #echo proxy; + proxy_pass http://127.0.0.1:$server_port/foo; + } + location /foo { + echo bah; + } +--- request + GET /main +--- response_body +hello, bah + diff --git a/t/023-rewrite/exec.t b/t/023-rewrite/exec.t index 98fca25c..9c13eae3 100644 --- a/t/023-rewrite/exec.t +++ b/t/023-rewrite/exec.t @@ -249,8 +249,8 @@ hello } location /p{ #content_by_lua ' - #local res = ngx.location.capture("/sub") - #ngx.print(res.body) + #local res = ngx.location.capture("/sub") + #ngx.print(res.body) #'; echo_location /sub; } @@ -322,3 +322,59 @@ hello --- response_body hello + + +=== TEST 15: rewrite_by_lua + ngx.exec + subrequest capture +--- config + location /main { + rewrite_by_lua ' + res = ngx.location.capture("/test_loc"); + ngx.print("hello, ", res.body) + '; + content_by_lua return; + } + location /test_loc { + rewrite_by_lua ' + ngx.exec("@proxy") + '; + } + location @proxy { + #echo proxy; + proxy_pass http://127.0.0.1:$server_port/foo; + } + location /foo { + echo bah; + } +--- request + GET /main +--- response_body +hello, bah + + +=== TEST 15: rewrite_by_lua_file + ngx.exec + subrequest capture +--- config + location /main { + rewrite_by_lua ' + res = ngx.location.capture("/test_loc"); + ngx.print("hello, ", res.body) + '; + content_by_lua return; + } + location /test_loc { + rewrite_by_lua_file html/jump.lua; + } + location @proxy { + #echo proxy; + proxy_pass http://127.0.0.1:$server_port/foo; + } + location /foo { + echo bah; + } +--- user_files +>>> jump.lua +ngx.exec("@proxy") +--- request + GET /main +--- response_body +hello, bah + diff --git a/t/024-access/exec.t b/t/024-access/exec.t index ddc72780..b07e88bd 100644 --- a/t/024-access/exec.t +++ b/t/024-access/exec.t @@ -323,3 +323,31 @@ hello --- response_body hello + + +=== TEST 15: access_by_lua + ngx.exec + subrequest capture +--- config + location /main { + access_by_lua ' + res = ngx.location.capture("/test_loc"); + ngx.print("hello, ", res.body) + '; + content_by_lua return; + } + location /test_loc { + rewrite_by_lua ' + ngx.exec("@proxy") + '; + } + location @proxy { + #echo proxy; + proxy_pass http://127.0.0.1:$server_port/foo; + } + location /foo { + echo bah; + } +--- request + GET /main +--- response_body +hello, bah + From 2c9a564a4d45dd6b5cf111c8c098d78342b7c834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Wed, 19 Oct 2011 12:14:34 +0800 Subject: [PATCH 24/46] refactored the code for rewrite_by_lua and rewrite_by_lua_file to reduce code duplication. --- src/ngx_http_lua_rewriteby.c | 258 ++++++++++++++++------------------- src/ngx_http_lua_rewriteby.h | 7 +- 2 files changed, 121 insertions(+), 144 deletions(-) diff --git a/src/ngx_http_lua_rewriteby.c b/src/ngx_http_lua_rewriteby.c index 86245d13..859cf19a 100644 --- a/src/ngx_http_lua_rewriteby.c +++ b/src/ngx_http_lua_rewriteby.c @@ -12,141 +12,8 @@ #include "ngx_http_lua_cache.h" -ngx_int_t -ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r) -{ - int cc_ref; - lua_State *cc; - ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; - - /* {{{ new coroutine to handle request */ - cc = ngx_http_lua_new_thread(r, L, &cc_ref); - - if (cc == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "lua: failed to create new coroutine to handle request"); - - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* move code closure to new coroutine */ - lua_xmove(L, cc, 1); - - /* set closure's env table to new coroutine's globals table */ - lua_pushvalue(cc, LUA_GLOBALSINDEX); - lua_setfenv(cc, -2); - - /* save reference of code to ease forcing stopping */ - lua_pushvalue(cc, -1); - lua_setglobal(cc, GLOBALS_SYMBOL_RUNCODE); - - /* save nginx request in coroutine globals table */ - lua_pushlightuserdata(cc, r); - lua_setglobal(cc, GLOBALS_SYMBOL_REQUEST); - /* }}} */ - - /* {{{ initialize request context */ - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - dd("ctx = %p", ctx); - - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_lua_reset_ctx(r, L, ctx); - - ctx->entered_rewrite_phase = 1; - - ctx->cc = cc; - ctx->cc_ref = cc_ref; - - /* }}} */ - - /* {{{ register request cleanup hooks */ - if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); - if (cln == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - cln->handler = ngx_http_lua_request_cleanup; - cln->data = r; - ctx->cleanup = &cln->handler; - } - /* }}} */ - - return ngx_http_lua_run_thread(L, r, ctx, 0); -} - - -ngx_int_t -ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r) -{ - lua_State *L; - ngx_int_t rc; - u_char *script_path; - ngx_http_lua_main_conf_t *lmcf; - ngx_http_lua_loc_conf_t *llcf; - char *err; - ngx_str_t eval_src; - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (ngx_http_complex_value(r, &llcf->rewrite_src, &eval_src) != NGX_OK) { - return NGX_ERROR; - } - - script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, - eval_src.len); - - if (script_path == NULL) { - return NGX_ERROR; - } - - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); - L = lmcf->lua; - - /* load Lua script file (w/ cache) sp = 1 */ - rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->rewrite_src_key, - &err, llcf->enable_code_cache ? 1 : 0); - - if (rc != NGX_OK) { - if (err == NULL) { - err = "unknown error"; - } - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "Failed to load Lua inlined code: %s", err); - - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* make sure we have a valid code chunk */ - assert(lua_isfunction(L, -1)); - - rc = ngx_http_lua_rewrite_by_chunk(L, r); - - if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - if (rc == NGX_AGAIN) { - return NGX_DONE; - } - - if (rc == NGX_DONE) { - ngx_http_finalize_request(r, NGX_DONE); - return NGX_DONE; - } - - if (rc >= NGX_HTTP_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - return NGX_DECLINED; -} +static ngx_int_t ngx_http_lua_rewrite_by_chunk(lua_State *L, + ngx_http_request_t *r); ngx_int_t @@ -299,9 +166,123 @@ ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } - rc = ngx_http_lua_rewrite_by_chunk(L, r); + return ngx_http_lua_rewrite_by_chunk(L, r); +} - dd("rewrite by chunk returns %d", (int) rc); + +ngx_int_t +ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r) +{ + lua_State *L; + ngx_int_t rc; + u_char *script_path; + ngx_http_lua_main_conf_t *lmcf; + ngx_http_lua_loc_conf_t *llcf; + char *err; + ngx_str_t eval_src; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (ngx_http_complex_value(r, &llcf->rewrite_src, &eval_src) != NGX_OK) { + return NGX_ERROR; + } + + script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, + eval_src.len); + + if (script_path == NULL) { + return NGX_ERROR; + } + + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + L = lmcf->lua; + + /* load Lua script file (w/ cache) sp = 1 */ + rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->rewrite_src_key, + &err, llcf->enable_code_cache ? 1 : 0); + + if (rc != NGX_OK) { + if (err == NULL) { + err = "unknown error"; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "Failed to load Lua file code: %s", err); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return ngx_http_lua_rewrite_by_chunk(L, r); +} + + +static ngx_int_t +ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r) +{ + int cc_ref; + lua_State *cc; + ngx_http_lua_ctx_t *ctx; + ngx_http_cleanup_t *cln; + ngx_int_t rc; + + /* {{{ new coroutine to handle request */ + cc = ngx_http_lua_new_thread(r, L, &cc_ref); + + if (cc == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine to handle request"); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* move code closure to new coroutine */ + lua_xmove(L, cc, 1); + + /* set closure's env table to new coroutine's globals table */ + lua_pushvalue(cc, LUA_GLOBALSINDEX); + lua_setfenv(cc, -2); + + /* save reference of code to ease forcing stopping */ + lua_pushvalue(cc, -1); + lua_setglobal(cc, GLOBALS_SYMBOL_RUNCODE); + + /* save nginx request in coroutine globals table */ + lua_pushlightuserdata(cc, r); + lua_setglobal(cc, GLOBALS_SYMBOL_REQUEST); + /* }}} */ + + /* {{{ initialize request context */ + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_lua_reset_ctx(r, L, ctx); + + ctx->entered_rewrite_phase = 1; + + ctx->cc = cc; + ctx->cc_ref = cc_ref; + + /* }}} */ + + /* {{{ register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_http_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cln->handler = ngx_http_lua_request_cleanup; + cln->data = r; + ctx->cleanup = &cln->handler; + } + /* }}} */ + + rc = ngx_http_lua_run_thread(L, r, ctx, 0); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; @@ -312,7 +293,6 @@ ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r) } if (rc == NGX_DONE) { - dd("XXX NGX_DONE returned"); ngx_http_finalize_request(r, NGX_DONE); return NGX_DONE; } @@ -321,8 +301,6 @@ ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r) return rc; } - dd("returning declined..."); - return NGX_DECLINED; } diff --git a/src/ngx_http_lua_rewriteby.h b/src/ngx_http_lua_rewriteby.h index 8e09b8c4..e87059c0 100644 --- a/src/ngx_http_lua_rewriteby.h +++ b/src/ngx_http_lua_rewriteby.h @@ -1,17 +1,16 @@ /* vim:set ft=c ts=4 sw=4 et fdm=marker: */ -#ifndef NGX_HTTP_LUA_REWRITE_BY_H__ -#define NGX_HTTP_LUA_REWRITE_BY_H__ +#ifndef NGX_HTTP_LUA_REWRITEBY_H +#define NGX_HTTP_LUA_REWRITEBY_H #include "ngx_http_lua_common.h" -ngx_int_t ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r); ngx_int_t ngx_http_lua_rewrite_handler(ngx_http_request_t *r); ngx_int_t ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r); ngx_int_t ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r); -#endif +#endif /* NGX_HTTP_LUA_REWRITEBY_H */ From 982cb581a93c665e7766f96eb8954d6b0e3a8582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Wed, 19 Oct 2011 12:23:18 +0800 Subject: [PATCH 25/46] refactored the code for access_by_lua and access_by_lua_file to reduce code duplication. --- src/ngx_http_lua_accessby.c | 261 +++++++++++++++++------------------- src/ngx_http_lua_accessby.h | 1 - 2 files changed, 122 insertions(+), 140 deletions(-) diff --git a/src/ngx_http_lua_accessby.c b/src/ngx_http_lua_accessby.c index d9c0194c..35978369 100644 --- a/src/ngx_http_lua_accessby.c +++ b/src/ngx_http_lua_accessby.c @@ -9,143 +9,7 @@ #include "ngx_http_lua_cache.h" -ngx_int_t -ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r) -{ - int cc_ref; - lua_State *cc; - ngx_http_lua_ctx_t *ctx; - ngx_http_cleanup_t *cln; - - /* {{{ new coroutine to handle request */ - cc = ngx_http_lua_new_thread(r, L, &cc_ref); - - if (cc == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "lua: failed to create new coroutine " - "to handle request"); - - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* move code closure to new coroutine */ - lua_xmove(L, cc, 1); - - /* set closure's env table to new coroutine's globals table */ - lua_pushvalue(cc, LUA_GLOBALSINDEX); - lua_setfenv(cc, -2); - - /* save reference of code to ease forcing stopping */ - lua_pushvalue(cc, -1); - lua_setglobal(cc, GLOBALS_SYMBOL_RUNCODE); - - /* save nginx request in coroutine globals table */ - lua_pushlightuserdata(cc, r); - lua_setglobal(cc, GLOBALS_SYMBOL_REQUEST); - /* }}} */ - - /* {{{ initialize request context */ - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - dd("ctx = %p", ctx); - - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_lua_reset_ctx(r, L, ctx); - - ctx->entered_access_phase = 1; - - ctx->cc = cc; - ctx->cc_ref = cc_ref; - - /* }}} */ - - /* {{{ register request cleanup hooks */ - if (ctx->cleanup == NULL) { - cln = ngx_http_cleanup_add(r, 0); - if (cln == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - cln->handler = ngx_http_lua_request_cleanup; - cln->data = r; - ctx->cleanup = &cln->handler; - } - /* }}} */ - - return ngx_http_lua_run_thread(L, r, ctx, 0); -} - - -ngx_int_t -ngx_http_lua_access_handler_file(ngx_http_request_t *r) -{ - lua_State *L; - ngx_int_t rc; - u_char *script_path; - ngx_http_lua_main_conf_t *lmcf; - ngx_http_lua_loc_conf_t *llcf; - char *err; - ngx_str_t eval_src; - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - /* Eval nginx variables in code path string first */ - if (ngx_http_complex_value(r, &llcf->access_src, &eval_src) != NGX_OK) { - return NGX_ERROR; - } - - script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, - eval_src.len); - - if (script_path == NULL) { - return NGX_ERROR; - } - - lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); - L = lmcf->lua; - - /* load Lua script file (w/ cache) sp = 1 */ - rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->access_src_key, - &err, llcf->enable_code_cache ? 1 : 0); - - if (rc != NGX_OK) { - if (err == NULL) { - err = "unknown error"; - } - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "Failed to load Lua inlined code: %s", err); - - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* make sure we have a valid code chunk */ - assert(lua_isfunction(L, -1)); - - rc = ngx_http_lua_access_by_chunk(L, r); - - if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - if (rc == NGX_AGAIN) { - return NGX_DONE; - } - - if (rc == NGX_DONE) { - ngx_http_finalize_request(r, NGX_DONE); - return NGX_DONE; - } - - if (rc >= NGX_HTTP_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - return NGX_DECLINED; -} +static ngx_int_t ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r); ngx_int_t @@ -300,9 +164,128 @@ ngx_http_lua_access_handler_inline(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } - rc = ngx_http_lua_access_by_chunk(L, r); + return ngx_http_lua_access_by_chunk(L, r); +} - dd("access by chunk returns %d", (int) rc); + +ngx_int_t +ngx_http_lua_access_handler_file(ngx_http_request_t *r) +{ + lua_State *L; + ngx_int_t rc; + u_char *script_path; + ngx_http_lua_main_conf_t *lmcf; + ngx_http_lua_loc_conf_t *llcf; + char *err; + ngx_str_t eval_src; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + /* Eval nginx variables in code path string first */ + if (ngx_http_complex_value(r, &llcf->access_src, &eval_src) != NGX_OK) { + return NGX_ERROR; + } + + script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, + eval_src.len); + + if (script_path == NULL) { + return NGX_ERROR; + } + + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + L = lmcf->lua; + + /* load Lua script file (w/ cache) sp = 1 */ + rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->access_src_key, + &err, llcf->enable_code_cache ? 1 : 0); + + if (rc != NGX_OK) { + if (err == NULL) { + err = "unknown error"; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "Failed to load Lua inlined code: %s", err); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* make sure we have a valid code chunk */ + assert(lua_isfunction(L, -1)); + + return ngx_http_lua_access_by_chunk(L, r); +} + + +static ngx_int_t +ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r) +{ + int cc_ref; + lua_State *cc; + ngx_http_lua_ctx_t *ctx; + ngx_http_cleanup_t *cln; + ngx_int_t rc; + + /* {{{ new coroutine to handle request */ + cc = ngx_http_lua_new_thread(r, L, &cc_ref); + + if (cc == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine " + "to handle request"); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* move code closure to new coroutine */ + lua_xmove(L, cc, 1); + + /* set closure's env table to new coroutine's globals table */ + lua_pushvalue(cc, LUA_GLOBALSINDEX); + lua_setfenv(cc, -2); + + /* save reference of code to ease forcing stopping */ + lua_pushvalue(cc, -1); + lua_setglobal(cc, GLOBALS_SYMBOL_RUNCODE); + + /* save nginx request in coroutine globals table */ + lua_pushlightuserdata(cc, r); + lua_setglobal(cc, GLOBALS_SYMBOL_REQUEST); + /* }}} */ + + /* {{{ initialize request context */ + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_lua_reset_ctx(r, L, ctx); + + ctx->entered_access_phase = 1; + + ctx->cc = cc; + ctx->cc_ref = cc_ref; + + /* }}} */ + + /* {{{ register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_http_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cln->handler = ngx_http_lua_request_cleanup; + cln->data = r; + ctx->cleanup = &cln->handler; + } + /* }}} */ + + rc = ngx_http_lua_run_thread(L, r, ctx, 0); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; diff --git a/src/ngx_http_lua_accessby.h b/src/ngx_http_lua_accessby.h index ac5b00ad..6a47eead 100644 --- a/src/ngx_http_lua_accessby.h +++ b/src/ngx_http_lua_accessby.h @@ -7,7 +7,6 @@ #include "ngx_http_lua_common.h" -ngx_int_t ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r); ngx_int_t ngx_http_lua_access_handler(ngx_http_request_t *r); ngx_int_t ngx_http_lua_access_handler_inline(ngx_http_request_t *r); ngx_int_t ngx_http_lua_access_handler_file(ngx_http_request_t *r); From 569e3511c565ee3dd80aaa24c3f9786fd235182c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Wed, 19 Oct 2011 15:21:13 +0800 Subject: [PATCH 26/46] added (passing) tests for ngx.location.capture + ngx.redirect under http pipelining. --- src/ngx_http_lua_accessby.h | 6 +++--- t/022-redirect.t | 28 ++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/ngx_http_lua_accessby.h b/src/ngx_http_lua_accessby.h index 6a47eead..ed20920b 100644 --- a/src/ngx_http_lua_accessby.h +++ b/src/ngx_http_lua_accessby.h @@ -1,7 +1,7 @@ /* vim:set ft=c ts=4 sw=4 et fdm=marker: */ -#ifndef NGX_HTTP_LUA_ACCESS_BY_H__ -#define NGX_HTTP_LUA_ACCESS_BY_H__ +#ifndef NGX_HTTP_LUA_ACCESSBY_H +#define NGX_HTTP_LUA_ACCESSBY_H #include "ngx_http_lua_common.h" @@ -12,5 +12,5 @@ ngx_int_t ngx_http_lua_access_handler_inline(ngx_http_request_t *r); ngx_int_t ngx_http_lua_access_handler_file(ngx_http_request_t *r); -#endif +#endif /* NGX_HTTP_LUA_ACCESSBY_H */ diff --git a/t/022-redirect.t b/t/022-redirect.t index 57691d6e..0ad320de 100644 --- a/t/022-redirect.t +++ b/t/022-redirect.t @@ -7,10 +7,10 @@ use Test::Nginx::Socket; #master_process_enabled(1); #log_level('warn'); -repeat_each(2); +#repeat_each(2); #repeat_each(1); -plan tests => blocks() * repeat_each() * 3; +plan tests => repeat_each() * (blocks() * 3 + 1); #no_diff(); #no_long_string(); @@ -145,3 +145,27 @@ Location: http://www.taobao.com/foo?bar=3 --- response_body_like: 302 Found --- error_code: 302 + +=== TEST 6: location.capture + ngx.redirect +--- config + location /echo { + echo hello, world; + } + location /proxy { + proxy_pass http://127.0.0.1:$TEST_NGINX_CLIENT_PORT/echo; + } + location /read { + content_by_lua ' + ngx.location.capture("/proxy") + ngx.location.capture("/proxy") + ngx.redirect("/echo") + ngx.exit(403) + '; + } +--- pipelined_requests eval +["GET /read/1", "GET /read/2"] +--- error_code eval +[302, 302] +--- response_body eval +[qr/302 Found/, qr/302 Found/] + From a0a9fb49a2a125b0786def17979427e953efbc65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Wed, 19 Oct 2011 16:53:14 +0800 Subject: [PATCH 27/46] documented the new APIs: ngx.req.read_body(), ngx.req.discard_body(), ngx.req.get_body_data(), and ngx.req.get_body_file(); also improved the coding style and the test suite a bit. --- README | 130 +++++++++++++++++++++++++++++++++--- README.markdown | 94 ++++++++++++++++++++++++-- doc/HttpLuaModule.wiki | 90 +++++++++++++++++++++++-- src/ngx_http_lua_accessby.c | 3 +- src/ngx_http_lua_util.c | 2 +- t/014-bugs.t | 2 +- t/017-exec.t | 2 +- t/023-rewrite/exec.t | 2 +- 8 files changed, 299 insertions(+), 26 deletions(-) diff --git a/README b/README index 0dd8584d..ac83a2ff 100644 --- a/README +++ b/README @@ -8,8 +8,8 @@ Status This module is under active development and is already production ready. Version - This document describes ngx_lua v0.3.1rc14 - () released on 16 + This document describes ngx_lua v0.3.1rc17 + () released on 19 October 2011. Synopsis @@ -681,6 +681,10 @@ Directives be read until the content handler's Lua code is about to run (i.e., the request body will be read at the content phase). + You're recommended to use the ngx.req.read_body function and + ngx.req.discard_body for finer control over the request body reading + process though. + The same applies to access_by_lua and access_by_lua_file. Nginx API for Lua @@ -1300,6 +1304,110 @@ Nginx API for Lua removed. So ngx.req.set_header("X-Foo", nil) is equivalent to ngx.req.clear_header("X-Foo") + ngx.req.read_body + syntax: *ngx.req.read_body()* + + context: *rewrite_by_lua*, access_by_lua*, content_by_lua** + + Read the client request body synchronously but still non-blocking. + + If the request body is already read previously by turning on + lua_need_request_body or by using other modules, then this function is a + no-op and returns immediately. + + In case of errors, like connection errors while reading the data, this + method will throw out a Lua exception *or* terminate the current request + with the 500 status code immediately. + + You can later either retrieve the request body data via + ngx.req.get_body_data or retrieve the temporary file name for the body + data cached to disk via ngx.req.get_body_file, depending on + + 1. whether the current request body has already exceeding your + client_body_buffer_size, + + 2. and whether you have turned on client_body_in_file_only. + + In case that you do not want to read the request body and the current + request may have a request body, then it's crucial to use the + ngx.req.discard_body function to explicitly discard the request body, or + you'll break HTTP 1.1 keepalive and HTTP 1.1 pipelining. + + This function was first introduced in the "v0.3.1rc17" release. + + ngx.req.discard_body + syntax: *ngx.req.discard_body()* + + context: *rewrite_by_lua*, access_by_lua*, content_by_lua** + + Explicitly discard the request body, i.e., read the data on the + connection and throw it away immediately. Please note that, simply + ignoring request body is not the right way to discard it, you need to + call this function, or you'll break things under HTTP 1.1 keepalive or + HTTP 1.1 pipelining. + + If the request body has already been read, this function does nothing + and returns immediately. + + This function was first introduced in the "v0.3.1rc17" release. + + See also ngx.req.read_body. + + ngx.req.get_body_data + syntax: *ngx.req.get_body_data()* + + context: *rewrite_by_lua*, access_by_lua*, content_by_lua** + + Retrieves the in-memory request body data. + + Returns "nil" if + + 1. the request body has not been read, + + 2. the request body has been read into disk temporary files, + + 3. or the request body has zero size. + + If the request body has not been read yet, call ngx.req.read_body first + (or turned on lua_need_request_body to force this module to read the + request body automatically, but this is not recommended). + + If the request body has been read into disk files, try calling the + ngx.req.get_body_file function instead. + + In case that you want to enforce in-memory request bodies, try setting + client_body_buffer_size to the same size value in client_max_body_size. + + Note that calling this function instead of using "ngx.var.request_body" + or "ngx.var.echo_request-body" is more efficient because it can save one + dynamic memory allocation and one data copy. + + This function was first introduced in the "v0.3.1rc17" release. + + See also ngx.req.get_body_file. + + ngx.req.get_body_file + syntax: *ngx.req.get_body_file()* + + context: *rewrite_by_lua*, access_by_lua*, content_by_lua** + + Retrieves the file name for the in-file request body data. Returns "nil" + if the request body has not been read or has been read into memory. + + If the request body has not been read yet, call ngx.req.read_body first + (or turned on lua_need_request_body to force this module to read the + request body automatically, but this is not recommended). + + If the request body has been read into memory, try calling the + ngx.req.get_body_data function instead. + + In case that you want to enforce in-file request bodies, try turning on + client_body_in_file_only. + + This function was first introduced in the "v0.3.1rc17" release. + + See also ngx.req.get_body_data. + ngx.req.clear_header syntax: *ngx.req.clear_header(header_name)* @@ -2243,19 +2351,24 @@ TODO * expose nginx's shared memory facility to the Lua land. + * add support for multi-value arguments to [[#ngx.req.set_uri_args]] + if its "args" argument is a Lua table. + Future Plan - * Add the "lua_require" directive to load module into main thread's + * add the "lua_require" directive to load module into main thread's globals. - * Add the "cosocket" mechamism that will emulate a common set of Lua + * add the "cosocket" mechamism that will emulate a common set of Lua socket API that will give you totally transparently non-blocking capability out of the box by means of a completely new upstream layer atop the nginx event model and no nginx subrequest overheads. - * Add Lua code automatic time slicing support by yielding and resuming + * add Lua code automatic time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks. - * Make set_by_lua using the same mechanism as content_by_lua. + * make set_by_lua using the same mechanism as content_by_lua. + + * add coroutine API back to the Lua land. Known Issues * As ngx_lua's predefined Nginx I/O APIs use coroutine @@ -2270,9 +2383,8 @@ Known Issues * Because the standard Lua 5.1 interpreter's VM is not fully resumable, the methods ngx.location.capture, - [[#ngx.location.capture_multi|ngx.location.capture_multi], - ngx.redirect, ngx.exec, and ngx.exit cannot be used within the - context of a Lua pcall() + ngx.location.capture_multi, ngx.redirect, ngx.exec, and ngx.exit + cannot be used within the context of a Lua pcall() () or xpcall() () when the standard Lua 5.1 interpreter is used; you'll get the error "attempt diff --git a/README.markdown b/README.markdown index 87d45244..ec45b9c2 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc14](https://github.com/chaoslawful/lua-nginx-module/tags) released on 16 October 2011. +This document describes ngx_lua [v0.3.1rc17](https://github.com/chaoslawful/lua-nginx-module/tags) released on 19 October 2011. Synopsis ======== @@ -679,6 +679,8 @@ the request body won't be read until the content handler's Lua code is about to run (i.e., the request body will be read at the content phase). +You're recommended to use the [ngx.req.read_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body) function and [ngx.req.discard_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.discard_body) for finer control over the request body reading process though. + The same applies to [access_by_lua](http://wiki.nginx.org/HttpLuaModule#access_by_lua) and [access_by_lua_file](http://wiki.nginx.org/HttpLuaModule#access_by_lua_file). Nginx API for Lua @@ -1480,6 +1482,84 @@ is equivalent to ngx.req.clear_header("X-Foo") +ngx.req.read_body +----------------- +**syntax:** *ngx.req.read_body()* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** + +Read the client request body synchronously but still non-blocking. + +If the request body is already read previously by turning on [lua_need_request_body](http://wiki.nginx.org/HttpLuaModule#lua_need_request_body) or by using other modules, then this function is a no-op and returns immediately. + +In case of errors, like connection errors while reading the data, this method will throw out a Lua exception *or* terminate the current request with the 500 status code immediately. + +You can later either retrieve the request body data via [ngx.req.get_body_data](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_data) or retrieve the temporary file name for the body data cached to disk via [ngx.req.get_body_file](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_file), depending on + +1. whether the current request body has already exceeding your [client_body_buffer_size](http://wiki.nginx.org/HttpCoreModule#client_body_buffer_size), +1. and whether you have turned on [client_body_in_file_only](http://wiki.nginx.org/HttpCoreModule#client_body_in_file_only). + +In case that you do not want to read the request body and the current request may have a request body, then it's crucial to use the [ngx.req.discard_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.discard_body) function to explicitly discard the request body, or you'll break HTTP 1.1 keepalive and HTTP 1.1 pipelining. + +This function was first introduced in the `v0.3.1rc17` release. + +ngx.req.discard_body +-------------------- +**syntax:** *ngx.req.discard_body()* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** + +Explicitly discard the request body, i.e., read the data on the connection and throw it away immediately. Please note that, simply ignoring request body is not the right way to discard it, you need to call this function, or you'll break things under HTTP 1.1 keepalive or HTTP 1.1 pipelining. + +If the request body has already been read, this function does nothing and returns immediately. + +This function was first introduced in the `v0.3.1rc17` release. + +See also [ngx.req.read_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body). + +ngx.req.get_body_data +--------------------- +**syntax:** *ngx.req.get_body_data()* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** + +Retrieves the in-memory request body data. + +Returns `nil` if +1. the request body has not been read, +1. the request body has been read into disk temporary files, +1. or the request body has zero size. + +If the request body has not been read yet, call [ngx.req.read_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body) first (or turned on [lua_need_request_body](http://wiki.nginx.org/HttpLuaModule#lua_need_request_body) to force this module to read the request body automatically, but this is not recommended). + +If the request body has been read into disk files, try calling the [ngx.req.get_body_file](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_file) function instead. + +In case that you want to enforce in-memory request bodies, try setting [client_body_buffer_size](http://wiki.nginx.org/HttpCoreModule#client_body_buffer_size) to the same size value in [client_max_body_size](http://wiki.nginx.org/HttpCoreModule#client_max_body_size). + +Note that calling this function instead of using `ngx.var.request_body` or `ngx.var.echo_request-body` is more efficient because it can save one dynamic memory allocation and one data copy. + +This function was first introduced in the `v0.3.1rc17` release. + +See also [ngx.req.get_body_file](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_file). + +ngx.req.get_body_file +--------------------- +**syntax:** *ngx.req.get_body_file()* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** + +Retrieves the file name for the in-file request body data. Returns `nil` if the request body has not been read or has been read into memory. + +If the request body has not been read yet, call [ngx.req.read_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body) first (or turned on [lua_need_request_body](http://wiki.nginx.org/HttpLuaModule#lua_need_request_body) to force this module to read the request body automatically, but this is not recommended). + +If the request body has been read into memory, try calling the [ngx.req.get_body_data](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_data) function instead. + +In case that you want to enforce in-file request bodies, try turning on [client_body_in_file_only](http://wiki.nginx.org/HttpCoreModule#client_body_in_file_only). + +This function was first introduced in the `v0.3.1rc17` release. + +See also [ngx.req.get_body_data](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_data). + ngx.req.clear_header -------------------- **syntax:** *ngx.req.clear_header(header_name)* @@ -2330,20 +2410,22 @@ TODO * add options to [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) and [ngx.location.capture_multi](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi) in order to share and copy a particular set of nginx variables with subrequests, specified by the user. * add an option to [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) and [ngx.location.capture_multi](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi) so as to specify the [ngx.ctx](http://wiki.nginx.org/HttpLuaModule#ngx.ctx) table for subrequests. * expose nginx's shared memory facility to the Lua land. +* add support for multi-value arguments to [[#ngx.req.set_uri_args]] if its `args` argument is a Lua table. Future Plan =========== -* Add the `lua_require` directive to load module into main thread's globals. -* Add the "cosocket" mechamism that will emulate a common set of Lua socket API that will give you totally transparently non-blocking capability out of the box by means of a completely new upstream layer atop the nginx event model and no nginx subrequest overheads. -* Add Lua code automatic time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks. -* Make set_by_lua using the same mechanism as content_by_lua. +* add the `lua_require` directive to load module into main thread's globals. +* add the "cosocket" mechamism that will emulate a common set of Lua socket API that will give you totally transparently non-blocking capability out of the box by means of a completely new upstream layer atop the nginx event model and no nginx subrequest overheads. +* add Lua code automatic time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks. +* make set_by_lua using the same mechanism as content_by_lua. +* add coroutine API back to the Lua land. Known Issues ============ * As ngx_lua's predefined Nginx I/O APIs use coroutine yielding/resuming mechanism, the user code should not call any Lua modules that use coroutine API to prevent obfuscating the predefined Nginx APIs like [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) (actually coroutine modules have been masked off in [content_by_lua](http://wiki.nginx.org/HttpLuaModule#content_by_lua) directives and others). This limitation is a little crucial, but don't worry, we're working on an alternative coroutine implementation that can fit into the Nginx event model. When it is done, the user code will be able to use the Lua coroutine mechanism freely as in standard Lua again! -* Because the standard Lua 5.1 interpreter's VM is not fully resumable, the methods [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture), [[#ngx.location.capture_multi|ngx.location.capture_multi], [ngx.redirect](http://wiki.nginx.org/HttpLuaModule#ngx.redirect), [ngx.exec](http://wiki.nginx.org/HttpLuaModule#ngx.exec), and [ngx.exit](http://wiki.nginx.org/HttpLuaModule#ngx.exit) cannot be used within the context of a Lua [pcall()](http://www.lua.org/manual/5.1/manual.html#pdf-pcall) or [xpcall()](http://www.lua.org/manual/5.1/manual.html#pdf-xpcall) when the standard Lua 5.1 interpreter is used; you'll get the error `attempt to yield across metamethod/C-call boundary`. To fix this, please use LuaJIT 2.0 instead, because LuaJIT 2.0 supports a fully resume-able VM. +* Because the standard Lua 5.1 interpreter's VM is not fully resumable, the methods [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture), [ngx.location.capture_multi](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi), [ngx.redirect](http://wiki.nginx.org/HttpLuaModule#ngx.redirect), [ngx.exec](http://wiki.nginx.org/HttpLuaModule#ngx.exec), and [ngx.exit](http://wiki.nginx.org/HttpLuaModule#ngx.exit) cannot be used within the context of a Lua [pcall()](http://www.lua.org/manual/5.1/manual.html#pdf-pcall) or [xpcall()](http://www.lua.org/manual/5.1/manual.html#pdf-xpcall) when the standard Lua 5.1 interpreter is used; you'll get the error `attempt to yield across metamethod/C-call boundary`. To fix this, please use LuaJIT 2.0 instead, because LuaJIT 2.0 supports a fully resume-able VM. * The [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) and [ngx.location.capture_multi](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi) Lua methods cannot capture locations configured by [HttpEchoModule](http://wiki.nginx.org/HttpEchoModule)'s [echo_location](http://wiki.nginx.org/HttpEchoModule#echo_location), [echo_location_async](http://wiki.nginx.org/HttpEchoModule#echo_location_async), [echo_subrequest](http://wiki.nginx.org/HttpEchoModule#echo_subrequest), or [echo_subrequest_async](http://wiki.nginx.org/HttpEchoModule#echo_subrequest_async) directives. This won't be fixed in the future due to technical problems. * **WATCH OUT: Globals WON'T persist between requests**, because of the one-coroutine-per-request isolation design. Especially watch yourself when using `require()` to import modules, and use this form: diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 490c5d14..5e4e6735 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc14] released on 16 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc17] released on 19 October 2011. = Synopsis = @@ -658,6 +658,8 @@ the request body won't be read until the content handler's Lua code is about to run (i.e., the request body will be read at the content phase). +You're recommended to use the [[#ngx.req.read_body|ngx.req.read_body]] function and [[#ngx.req.discard_body|ngx.req.discard_body]] for finer control over the request body reading process though. + The same applies to [[#access_by_lua|access_by_lua]] and [[#access_by_lua_file|access_by_lua_file]]. = Nginx API for Lua = @@ -1440,6 +1442,80 @@ is equivalent to ngx.req.clear_header("X-Foo") +== ngx.req.read_body == +'''syntax:''' ''ngx.req.read_body()'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' + +Read the client request body synchronously but still non-blocking. + +If the request body is already read previously by turning on [[#lua_need_request_body|lua_need_request_body]] or by using other modules, then this function is a no-op and returns immediately. + +In case of errors, like connection errors while reading the data, this method will throw out a Lua exception ''or'' terminate the current request with the 500 status code immediately. + +You can later either retrieve the request body data via [[#ngx.req.get_body_data|ngx.req.get_body_data]] or retrieve the temporary file name for the body data cached to disk via [[#ngx.req.get_body_file|ngx.req.get_body_file]], depending on + +# whether the current request body has already exceeding your [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]], +# and whether you have turned on [[HttpCoreModule#client_body_in_file_only|client_body_in_file_only]]. + +In case that you do not want to read the request body and the current request may have a request body, then it's crucial to use the [[#ngx.req.discard_body|ngx.req.discard_body]] function to explicitly discard the request body, or you'll break HTTP 1.1 keepalive and HTTP 1.1 pipelining. + +This function was first introduced in the v0.3.1rc17 release. + +== ngx.req.discard_body == +'''syntax:''' ''ngx.req.discard_body()'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' + +Explicitly discard the request body, i.e., read the data on the connection and throw it away immediately. Please note that, simply ignoring request body is not the right way to discard it, you need to call this function, or you'll break things under HTTP 1.1 keepalive or HTTP 1.1 pipelining. + +If the request body has already been read, this function does nothing and returns immediately. + +This function was first introduced in the v0.3.1rc17 release. + +See also [[#ngx.req.read_body|ngx.req.read_body]]. + +== ngx.req.get_body_data == +'''syntax:''' ''ngx.req.get_body_data()'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' + +Retrieves the in-memory request body data. + +Returns nil if +# the request body has not been read, +# the request body has been read into disk temporary files, +# or the request body has zero size. + +If the request body has not been read yet, call [[#ngx.req.read_body|ngx.req.read_body]] first (or turned on [[#lua_need_request_body|lua_need_request_body]] to force this module to read the request body automatically, but this is not recommended). + +If the request body has been read into disk files, try calling the [[#ngx.req.get_body_file|ngx.req.get_body_file]] function instead. + +In case that you want to enforce in-memory request bodies, try setting [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]] to the same size value in [[HttpCoreModule#client_max_body_size|client_max_body_size]]. + +Note that calling this function instead of using ngx.var.request_body or ngx.var.echo_request-body is more efficient because it can save one dynamic memory allocation and one data copy. + +This function was first introduced in the v0.3.1rc17 release. + +See also [[#ngx.req.get_body_file|ngx.req.get_body_file]]. + +== ngx.req.get_body_file == +'''syntax:''' ''ngx.req.get_body_file()'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' + +Retrieves the file name for the in-file request body data. Returns nil if the request body has not been read or has been read into memory. + +If the request body has not been read yet, call [[#ngx.req.read_body|ngx.req.read_body]] first (or turned on [[#lua_need_request_body|lua_need_request_body]] to force this module to read the request body automatically, but this is not recommended). + +If the request body has been read into memory, try calling the [[#ngx.req.get_body_data|ngx.req.get_body_data]] function instead. + +In case that you want to enforce in-file request bodies, try turning on [[HttpCoreModule#client_body_in_file_only|client_body_in_file_only]]. + +This function was first introduced in the v0.3.1rc17 release. + +See also [[#ngx.req.get_body_data|ngx.req.get_body_data]]. + == ngx.req.clear_header == '''syntax:''' ''ngx.req.clear_header(header_name)'' @@ -2251,18 +2327,20 @@ filtering chain affects a lot. The correct configure adding order is: * add options to [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] in order to share and copy a particular set of nginx variables with subrequests, specified by the user. * add an option to [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] so as to specify the [[#ngx.ctx|ngx.ctx]] table for subrequests. * expose nginx's shared memory facility to the Lua land. +* add support for multi-value arguments to [[#ngx.req.set_uri_args]] if its args argument is a Lua table. = Future Plan = -* Add the lua_require directive to load module into main thread's globals. -* Add the "cosocket" mechamism that will emulate a common set of Lua socket API that will give you totally transparently non-blocking capability out of the box by means of a completely new upstream layer atop the nginx event model and no nginx subrequest overheads. -* Add Lua code automatic time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks. -* Make set_by_lua using the same mechanism as content_by_lua. +* add the lua_require directive to load module into main thread's globals. +* add the "cosocket" mechamism that will emulate a common set of Lua socket API that will give you totally transparently non-blocking capability out of the box by means of a completely new upstream layer atop the nginx event model and no nginx subrequest overheads. +* add Lua code automatic time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks. +* make set_by_lua using the same mechanism as content_by_lua. +* add coroutine API back to the Lua land. = Known Issues = * As ngx_lua's predefined Nginx I/O APIs use coroutine yielding/resuming mechanism, the user code should not call any Lua modules that use coroutine API to prevent obfuscating the predefined Nginx APIs like [[#ngx.location.capture|ngx.location.capture]] (actually coroutine modules have been masked off in [[#content_by_lua|content_by_lua]] directives and others). This limitation is a little crucial, but don't worry, we're working on an alternative coroutine implementation that can fit into the Nginx event model. When it is done, the user code will be able to use the Lua coroutine mechanism freely as in standard Lua again! -* Because the standard Lua 5.1 interpreter's VM is not fully resumable, the methods [[#ngx.location.capture|ngx.location.capture]], [[#ngx.location.capture_multi|ngx.location.capture_multi], [[#ngx.redirect|ngx.redirect]], [[#ngx.exec|ngx.exec]], and [[#ngx.exit|ngx.exit]] cannot be used within the context of a Lua [http://www.lua.org/manual/5.1/manual.html#pdf-pcall pcall()] or [http://www.lua.org/manual/5.1/manual.html#pdf-xpcall xpcall()] when the standard Lua 5.1 interpreter is used; you'll get the error attempt to yield across metamethod/C-call boundary. To fix this, please use LuaJIT 2.0 instead, because LuaJIT 2.0 supports a fully resume-able VM. +* Because the standard Lua 5.1 interpreter's VM is not fully resumable, the methods [[#ngx.location.capture|ngx.location.capture]], [[#ngx.location.capture_multi|ngx.location.capture_multi]], [[#ngx.redirect|ngx.redirect]], [[#ngx.exec|ngx.exec]], and [[#ngx.exit|ngx.exit]] cannot be used within the context of a Lua [http://www.lua.org/manual/5.1/manual.html#pdf-pcall pcall()] or [http://www.lua.org/manual/5.1/manual.html#pdf-xpcall xpcall()] when the standard Lua 5.1 interpreter is used; you'll get the error attempt to yield across metamethod/C-call boundary. To fix this, please use LuaJIT 2.0 instead, because LuaJIT 2.0 supports a fully resume-able VM. * The [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] Lua methods cannot capture locations configured by [[HttpEchoModule]]'s [[HttpEchoModule#echo_location|echo_location]], [[HttpEchoModule#echo_location_async|echo_location_async]], [[HttpEchoModule#echo_subrequest|echo_subrequest]], or [[HttpEchoModule#echo_subrequest_async|echo_subrequest_async]] directives. This won't be fixed in the future due to technical problems. * '''WATCH OUT: Globals WON'T persist between requests''', because of the one-coroutine-per-request isolation design. Especially watch yourself when using require() to import modules, and use this form: diff --git a/src/ngx_http_lua_accessby.c b/src/ngx_http_lua_accessby.c index 35978369..77bae611 100644 --- a/src/ngx_http_lua_accessby.c +++ b/src/ngx_http_lua_accessby.c @@ -9,7 +9,8 @@ #include "ngx_http_lua_cache.h" -static ngx_int_t ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r); +static ngx_int_t ngx_http_lua_access_by_chunk(lua_State *L, + ngx_http_request_t *r); ngx_int_t diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 3b9b979d..3d276b7e 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1561,7 +1561,7 @@ ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, dd("XXYY HERE %d\n", (int) r->main->count); #if 0 - if (!ctx->entered_content_phase && r != r->connection->data && r->postponed) { + if (!ctx->entered_content_phase) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ diff --git a/t/014-bugs.t b/t/014-bugs.t index f260c28f..6f237ff9 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -8,7 +8,7 @@ use Test::Nginx::Socket; log_level('debug'); #repeat_each(120); -#repeat_each(3); +repeat_each(3); plan tests => repeat_each() * (blocks() * 2 + 2); diff --git a/t/017-exec.t b/t/017-exec.t index 84862e72..1b5a9a48 100644 --- a/t/017-exec.t +++ b/t/017-exec.t @@ -3,7 +3,7 @@ use lib 'lib'; use Test::Nginx::Socket; -#repeat_each(2); +repeat_each(2); plan tests => blocks() * repeat_each() * 2; diff --git a/t/023-rewrite/exec.t b/t/023-rewrite/exec.t index 9c13eae3..8ec303fb 100644 --- a/t/023-rewrite/exec.t +++ b/t/023-rewrite/exec.t @@ -3,7 +3,7 @@ use lib 'lib'; use Test::Nginx::Socket; -#repeat_each(2); +repeat_each(2); #repeat_each(1); plan tests => blocks() * repeat_each() * 2; From 1122cbd49d85e245e86d1b961e83dfe02392c960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Wed, 19 Oct 2011 17:07:47 +0800 Subject: [PATCH 28/46] fixed a test cause because Test::Nginx fixed a bug in its Content-Length handling. --- t/016-resp-header.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/016-resp-header.t b/t/016-resp-header.t index 6d1d6454..7f8b8f97 100644 --- a/t/016-resp-header.t +++ b/t/016-resp-header.t @@ -64,8 +64,8 @@ Content-Type: text/html GET /read --- response_headers Content-Length: 3 ---- response_body -Hello +--- response_body chop +Hel From ea81bb2f01344fd75814ac4a3408f8170b0dff5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Thu, 20 Oct 2011 12:02:56 +0800 Subject: [PATCH 29/46] implemented new function ngx.req.set_body_data() to override the current request body with in-memory data. --- src/ngx_http_lua_req_body.c | 132 ++++++++++++++++++++++++++++++++++++ t/022-redirect.t | 3 +- t/023-rewrite/exec.t | 3 +- t/044-req-body.t | 68 ++++++++++++++++++- 4 files changed, 202 insertions(+), 4 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 4d8d84ce..888ca533 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -12,6 +12,8 @@ static void ngx_http_lua_req_body_post_read(ngx_http_request_t *r); static int ngx_http_lua_ngx_req_discard_body(lua_State *L); static int ngx_http_lua_ngx_req_get_body_data(lua_State *L); static int ngx_http_lua_ngx_req_get_body_file(lua_State *L); +static int ngx_http_lua_ngx_req_set_body_data(lua_State *L); +static void ngx_http_lua_pool_cleanup_file(ngx_pool_t *p, ngx_fd_t fd); void @@ -28,6 +30,9 @@ ngx_http_lua_inject_req_body_api(lua_State *L) lua_pushcfunction(L, ngx_http_lua_ngx_req_get_body_file); lua_setfield(L, -2, "get_body_file"); + + lua_pushcfunction(L, ngx_http_lua_ngx_req_set_body_data); + lua_setfield(L, -2, "set_body_data"); } @@ -214,3 +219,130 @@ ngx_http_lua_ngx_req_get_body_file(lua_State *L) return 1; } + +static int +ngx_http_lua_ngx_req_set_body_data(lua_State *L) +{ + ngx_http_request_t *r; + int n; + ngx_http_request_body_t *rb; + ngx_chain_t *cl; + ngx_temp_file_t *tf; + ngx_buf_t *b; + ngx_str_t body; +#if 1 + ngx_int_t rc; +#endif + + n = lua_gettop(L); + + if (n != 1) { + return luaL_error(L, "expecting 1 arguments but seen %d", n); + } + + body.data = (u_char *) luaL_checklstring(L, 1, &body.len); + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r->request_body == NULL) { + +#if 1 + rc = ngx_http_discard_request_body(r); + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return luaL_error(L, "failed to discard request body"); + } +#endif + + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + if (rb == NULL) { + return luaL_error(L, "out of memory"); + } + + r->request_body = rb; + + } else { + rb = r->request_body; + } + + tf = rb->temp_file; + + if (tf) { + if (tf->file.fd != NGX_INVALID_FILE) { + + dd("cleaning temp file %.*s", (int) tf->file.name.len, + tf->file.name.data); + + ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd); + tf->file.fd = NGX_INVALID_FILE; + + dd("temp file cleaned: %.*s", (int) tf->file.name.len, + tf->file.name.data); + } + + rb->temp_file = NULL; + } + + if (rb->bufs) { + for (cl = rb->bufs; cl; cl = cl->next) { + if (cl->buf->temporary) { + ngx_pfree(r->pool, cl->buf->start); + } + } + + rb->bufs->next = NULL; + + b = rb->bufs->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + b->temporary = 1; + b->start = ngx_palloc(r->pool, body.len); + if (b->start == NULL) { + return luaL_error(L, "out of memory"); + } + b->end = b->start + body.len; + + b->pos = b->start; + b->last = ngx_copy(b->pos, body.data, body.len); + + } else { + + rb->bufs = ngx_alloc_chain_link(r->pool); + if (rb->bufs == NULL) { + return luaL_error(L, "out of memory"); + } + + b = ngx_create_temp_buf(r->pool, body.len); + b->last = ngx_copy(b->pos, body.data, body.len); + + rb->bufs->buf = b; + rb->bufs->next = NULL; + } + + return 0; +} + + +static void +ngx_http_lua_pool_cleanup_file(ngx_pool_t *p, ngx_fd_t fd) +{ + ngx_pool_cleanup_t *c; + ngx_pool_cleanup_file_t *cf; + + for (c = p->cleanup; c; c = c->next) { + if (c->handler == ngx_pool_cleanup_file + || c->handler == ngx_pool_delete_file) + { + + cf = c->data; + + if (cf->fd == fd) { + c->handler(cf); + c->handler = NULL; + return; + } + } + } +} + diff --git a/t/022-redirect.t b/t/022-redirect.t index 0ad320de..44478c7e 100644 --- a/t/022-redirect.t +++ b/t/022-redirect.t @@ -146,7 +146,8 @@ Location: http://www.taobao.com/foo?bar=3 --- error_code: 302 -=== TEST 6: location.capture + ngx.redirect + +=== TEST 8: location.capture + ngx.redirect --- config location /echo { echo hello, world; diff --git a/t/023-rewrite/exec.t b/t/023-rewrite/exec.t index 8ec303fb..0c84122c 100644 --- a/t/023-rewrite/exec.t +++ b/t/023-rewrite/exec.t @@ -351,7 +351,8 @@ hello hello, bah -=== TEST 15: rewrite_by_lua_file + ngx.exec + subrequest capture + +=== TEST 16: rewrite_by_lua_file + ngx.exec + subrequest capture --- config location /main { rewrite_by_lua ' diff --git a/t/044-req-body.t b/t/044-req-body.t index ff27b558..77dc1766 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -8,7 +8,7 @@ use Test::Nginx::Socket; repeat_each(2); -plan tests => repeat_each() * (blocks() * 2 + 4); +plan tests => repeat_each() * (blocks() * 2 + 6); #no_diff(); no_long_string(); @@ -178,7 +178,6 @@ hiya, world"] ngx.req.read_body() ngx.say("body: ", ngx.var.request_body) '; - } --- pipelined_requests eval ["POST /foo @@ -256,3 +255,68 @@ hello, world --- response_body nil + + +=== TEST 13: read buffered body to memory and reset it +--- config + location = /test { + content_by_lua ' + ngx.req.read_body() + ngx.req.set_body_data("hiya, dear") + ngx.say(ngx.req.get_body_data()) + ngx.say(ngx.var.request_body) + ngx.say(ngx.var.echo_request_body) + '; + } +--- request +POST /test +hello, world +--- response_body +hiya, dear +hiya, dear +hiya, dear + + + +=== TEST 14: read body to file and then override it with data in memory +--- config + client_body_in_file_only on; + + location = /test { + content_by_lua ' + ngx.req.read_body() + ngx.req.set_body_data("hello, baby") + ngx.say(ngx.req.get_body_data()) + ngx.say(ngx.var.request_body) + '; + } +--- request +POST /test +yeah +--- response_body +hello, baby +hello, baby + + + +=== TEST 15: do not read the current request body but replace it with our own +--- config + client_body_in_file_only on; + + location = /test { + content_by_lua ' + ngx.req.set_body_data("hello, baby") + ngx.say(ngx.req.get_body_data()) + ngx.say(ngx.var.request_body) + '; + } +--- pipelined_requests eval +["POST /test\nyeah", "POST /test\nblah"] +--- response_body eval +["hello, baby +hello, baby +", +"hello, baby +hello, baby +"] + From 2fbea05b93a9c6de8dd76977b6b9d42851a90638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Thu, 20 Oct 2011 22:37:26 +0800 Subject: [PATCH 30/46] initial cut on the ngx.req.set_body_file() API. --- src/ngx_http_lua_req_body.c | 255 +++++++++++++++++++++++++++++++++++- src/ngx_http_lua_util.c | 108 +++++++++++++++ src/ngx_http_lua_util.h | 49 +++++-- t/016-resp-header.t | 4 +- t/034-match.t | 20 +++ t/044-req-body.t | 252 ++++++++++++++++++++++++++++++++++- 6 files changed, 668 insertions(+), 20 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 888ca533..d26f5b11 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -5,6 +5,7 @@ #include "ngx_http_lua_req_body.h" #include "ngx_http_lua_util.h" +#include "ngx_http_lua_headers_in.h" static int ngx_http_lua_ngx_req_read_body(lua_State *L); @@ -14,6 +15,7 @@ static int ngx_http_lua_ngx_req_get_body_data(lua_State *L); static int ngx_http_lua_ngx_req_get_body_file(lua_State *L); static int ngx_http_lua_ngx_req_set_body_data(lua_State *L); static void ngx_http_lua_pool_cleanup_file(ngx_pool_t *p, ngx_fd_t fd); +static int ngx_http_lua_ngx_req_set_body_file(lua_State *L); void @@ -33,6 +35,9 @@ ngx_http_lua_inject_req_body_api(lua_State *L) lua_pushcfunction(L, ngx_http_lua_ngx_req_set_body_data); lua_setfield(L, -2, "set_body_data"); + + lua_pushcfunction(L, ngx_http_lua_ngx_req_set_body_file); + lua_setfield(L, -2, "set_body_file"); } @@ -58,7 +63,7 @@ ngx_http_lua_ngx_req_read_body(lua_State *L) r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; -#if 0 +#if 1 if (r->request_body_in_file_only) { r->request_body_file_log_level = 0; } @@ -214,6 +219,16 @@ ngx_http_lua_ngx_req_get_body_file(lua_State *L) return 1; } + dd("XXX file directio: %u, f:%u, m:%u, t:%u, end - pos %d, size %d", + r->request_body->temp_file->file.directio, + r->request_body->bufs->buf->in_file, + r->request_body->bufs->buf->memory, + r->request_body->bufs->buf->temporary, + (int) (r->request_body->bufs->buf->end - + r->request_body->bufs->buf->pos), + (int) ngx_buf_size(r->request_body->bufs->buf) + ); + lua_pushlstring(L, (char *) r->request_body->temp_file->file.name.data, r->request_body->temp_file->file.name.len); return 1; @@ -288,6 +303,7 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) for (cl = rb->bufs; cl; cl = cl->next) { if (cl->buf->temporary) { ngx_pfree(r->pool, cl->buf->start); + cl->buf->temporary = 0; } } @@ -312,12 +328,13 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) if (rb->bufs == NULL) { return luaL_error(L, "out of memory"); } + rb->bufs->next = NULL; b = ngx_create_temp_buf(r->pool, body.len); b->last = ngx_copy(b->pos, body.data, body.len); rb->bufs->buf = b; - rb->bufs->next = NULL; + rb->buf = b; } return 0; @@ -346,3 +363,237 @@ ngx_http_lua_pool_cleanup_file(ngx_pool_t *p, ngx_fd_t fd) } } + +static int +ngx_http_lua_ngx_req_set_body_file(lua_State *L) +{ + ngx_http_request_t *r; + int n; + ngx_http_request_body_t *rb; + ngx_chain_t *cl; + ngx_temp_file_t *tf; + ngx_buf_t *b; + ngx_str_t name; + ngx_http_core_loc_conf_t *clcf; + ngx_int_t rc; + int clean; + ngx_open_file_info_t of; + ngx_str_t key, value; + ngx_pool_cleanup_t *cln; + ngx_pool_cleanup_file_t *clnf; + ngx_err_t err; + + n = lua_gettop(L); + + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 or 2 arguments but seen %d", n); + } + + name.data = (u_char *) luaL_checklstring(L, 1, &name.len); + + if (n == 2) { + luaL_checktype(L, 2, LUA_TBOOLEAN); + clean = lua_toboolean(L, 2); + + } else { + clean = 0; + } + + dd("clean: %d", (int) clean); + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r->request_body == NULL) { + +#if 1 + rc = ngx_http_discard_request_body(r); + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return luaL_error(L, "failed to discard request body"); + } +#endif + + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + if (rb == NULL) { + return luaL_error(L, "out of memory"); + } + + r->request_body = rb; + + } else { + rb = r->request_body; + } + + /* clean up existing r->request_body->bufs (if any) */ + + if (rb->bufs) { + dd("XXX reusing buf"); + + for (cl = rb->bufs; cl; cl = cl->next) { + if (cl->buf->temporary) { + ngx_pfree(r->pool, cl->buf->start); + cl->buf->temporary = 0; + } + } + + rb->bufs->next = NULL; + b = rb->bufs->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + } else { + + dd("XXX creating new buf"); + + rb->bufs = ngx_alloc_chain_link(r->pool); + if (rb->bufs == NULL) { + return luaL_error(L, "out of memory"); + } + rb->bufs->next = NULL; + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return luaL_error(L, "out of memory"); + } + + rb->bufs->buf = b; + rb->buf = b; + } + + b->last_in_chain = 1; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + /* just make r->request_body->temp_file a bare stub */ + + tf = rb->temp_file; + + if (tf) { + if (tf->file.fd != NGX_INVALID_FILE) { + + dd("cleaning temp file %.*s", (int) tf->file.name.len, + tf->file.name.data); + + ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd); + + ngx_memzero(tf, sizeof(ngx_temp_file_t)); + + tf->file.fd = NGX_INVALID_FILE; + + dd("temp file cleaned: %.*s", (int) tf->file.name.len, + tf->file.name.data); + } + + } else { + + tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); + if (tf == NULL) { + return luaL_error(L, "out of memory"); + } + + tf->file.fd = NGX_INVALID_FILE; + rb->temp_file = tf; + } + + /* read the file info and construct an in-file buf */ + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + if (ngx_http_lua_open_and_stat_file(name.data, &of, r->connection->log) + != NGX_OK) + { + return luaL_error(L, "%s \"%s\" failed", of.failed, name.data); + } + + dd("XXX new body file fd: %d", of.fd); + + tf->file.fd = of.fd; + tf->file.name = name; + tf->file.log = r->connection->log; + + /* FIXME we should not always set directio here */ + tf->file.directio = 1; + + if (of.size == 0) { + if (clean) { + if (ngx_delete_file(name.data) == NGX_FILE_ERROR) { + err = ngx_errno; + + if (err != NGX_ENOENT) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, + ngx_delete_file_n " \"%s\" failed", + name.data); + } + } + } + + if (ngx_close_file(of.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name.data); + } + + r->request_body->bufs = NULL; + r->request_body->buf = NULL; + + goto set_header; + } + + /* register file cleanup hook */ + + cln = ngx_pool_cleanup_add(r->pool, + sizeof(ngx_pool_cleanup_file_t)); + + if (cln == NULL) { + return luaL_error(L, "out of memory"); + } + + cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file; + clnf = cln->data; + + clnf->fd = of.fd; + clnf->name = name.data; + clnf->log = r->pool->log; + + b->file = &tf->file; + if (b->file == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + dd("XXX file size: %d", (int) of.size); + + b->file_pos = 0; + b->file_last = of.size; + + b->in_file = 1; + + dd("buf file: %p, f:%u", b->file, b->in_file); + +set_header: + /* override input header Content-Length */ + + value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN); + if (value.data == NULL) { + return luaL_error(L, "out of memory"); + } + + value.len = ngx_sprintf(value.data, "%O", of.size) - value.data; + + if (r->headers_in.content_length) { + r->headers_in.content_length_n = of.size; + r->headers_in.content_length->value.data = value.data; + r->headers_in.content_length->value.len = value.len; + + } else { + ngx_str_set(&key, "Content-Length"); + + rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */); + if (rc != NGX_OK) { + return luaL_error(L, "failed to reset the Content-Length " + "input header"); + } + } + + return 0; +} + diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 3d276b7e..a4137dce 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -1727,3 +1727,111 @@ ngx_http_lua_handle_rewrite_jump(lua_State *L, ngx_http_request_t *r, return NGX_OK; } + +ngx_int_t +ngx_http_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, + ngx_log_t *log) +{ + ngx_fd_t fd; + ngx_file_info_t fi; + + if (of->fd != NGX_INVALID_FILE) { + + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; + goto failed; + } + + if (of->uniq == ngx_file_uniq(&fi)) { + goto done; + } + + } else if (of->test_dir) { + + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; + goto failed; + } + + if (ngx_is_dir(&fi)) { + goto done; + } + } + + if (!of->log) { + + /* + * Use non-blocking open() not to hang on FIFO files, etc. + * This flag has no effect on a regular files. + */ + + fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + } else { + fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + } + + if (fd == NGX_INVALID_FILE) { + of->failed = ngx_open_file_n; + goto failed; + } + + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + return NGX_ERROR; + } + + if (ngx_is_dir(&fi)) { + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + } else { + of->fd = fd; + + if (of->directio <= ngx_file_size(&fi)) { + if (ngx_directio_on(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_directio_on_n " \"%s\" failed", name); + + } else { + of->is_directio = 1; + } + } + } + +done: + + of->uniq = ngx_file_uniq(&fi); + of->mtime = ngx_file_mtime(&fi); + of->size = ngx_file_size(&fi); + of->fs_size = ngx_file_fs_size(&fi); + of->is_dir = ngx_is_dir(&fi); + of->is_file = ngx_is_file(&fi); + of->is_link = ngx_is_link(&fi); + of->is_exec = ngx_is_exec(&fi); + + return NGX_OK; + +failed: + + of->fd = NGX_INVALID_FILE; + of->err = ngx_errno; + + return NGX_ERROR; +} + diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index 02aa0f67..c8748e07 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -18,40 +18,65 @@ lua_State * ngx_http_lua_new_state(ngx_conf_t *cf, - ngx_http_lua_main_conf_t *lmcf); + ngx_http_lua_main_conf_t *lmcf); + lua_State * ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *l, - int *ref); + int *ref); + void ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *l, int ref, - int force_quit); + int force_quit); + ngx_int_t ngx_http_lua_has_inline_var(ngx_str_t *s); + u_char * ngx_http_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len); + ngx_int_t ngx_http_lua_send_header_if_needed(ngx_http_request_t *r, - ngx_http_lua_ctx_t *ctx); + ngx_http_lua_ctx_t *ctx); + ngx_int_t ngx_http_lua_send_chain_link(ngx_http_request_t *r, - ngx_http_lua_ctx_t *ctx, ngx_chain_t *cl); + ngx_http_lua_ctx_t *ctx, ngx_chain_t *cl); + void ngx_http_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in); + ngx_int_t ngx_http_lua_add_copy_chain(ngx_http_request_t *r, - ngx_http_lua_ctx_t *ctx, ngx_chain_t **chain, ngx_chain_t *in); + ngx_http_lua_ctx_t *ctx, ngx_chain_t **chain, ngx_chain_t *in); + void ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L, - ngx_http_lua_ctx_t *ctx); + ngx_http_lua_ctx_t *ctx); + void ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r); + void ngx_http_lua_request_cleanup(void *data); + ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, - ngx_http_lua_ctx_t *ctx, int nret); + ngx_http_lua_ctx_t *ctx, int nret); + ngx_int_t ngx_http_lua_wev_handler(ngx_http_request_t *r); + u_char * ngx_http_lua_digest_hex(u_char *dest, const u_char *buf, - int buf_len); + int buf_len); + void ngx_http_lua_dump_postponed(ngx_http_request_t *r); + ngx_int_t ngx_http_lua_flush_postponed_outputs(ngx_http_request_t *r); + void ngx_http_lua_set_multi_value_table(lua_State *L, int index); + void ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size, - ngx_uint_t type); + ngx_uint_t type); + uintptr_t ngx_http_lua_escape_uri(u_char *dst, u_char *src, - size_t size, ngx_uint_t type); + size_t size, ngx_uint_t type); + void ngx_http_lua_inject_req_api(lua_State *L); + void ngx_http_lua_inject_req_api_no_io(lua_State *L); + void ngx_http_lua_process_args_option(ngx_http_request_t *r, - lua_State *L, int table, ngx_str_t *args); + lua_State *L, int table, ngx_str_t *args); + +ngx_int_t ngx_http_lua_open_and_stat_file(u_char *name, + ngx_open_file_info_t *of, ngx_log_t *log); #endif /* NGX_HTTP_LUA_UTIL_H */ diff --git a/t/016-resp-header.t b/t/016-resp-header.t index 7f8b8f97..6fc5255b 100644 --- a/t/016-resp-header.t +++ b/t/016-resp-header.t @@ -115,7 +115,7 @@ Hello --- request GET /read --- raw_response_headers_like chomp -X-Foo: a\r\n.*?X-Foo: bc$ +X-Foo: a\r\n.*?X-Foo: bc\r\n --- response_body Hello @@ -186,7 +186,7 @@ Hello --- request GET /read --- raw_response_headers_like chomp -X-Foo: a\r\n.*?X-Foo: abc$ +X-Foo: a\r\n.*?X-Foo: abc\r\n --- response_body Hello diff --git a/t/034-match.t b/t/034-match.t index fccda6c8..f4a2a756 100644 --- a/t/034-match.t +++ b/t/034-match.t @@ -547,3 +547,23 @@ not matched! bar baz + + +=== TEST 27: escaping sequences +--- config + location /re { + content_by_lua_file html/a.lua; + } +--- user_files +>>> a.lua +m = ngx.re.match("hello, 1234", "(\\\s+)") +if m then + ngx.say("[", m[0], "]") +else + ngx.say("not matched!") +end +--- request + GET /re +--- response_body +[ ] + diff --git a/t/044-req-body.t b/t/044-req-body.t index 77dc1766..22b4d409 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -6,9 +6,9 @@ use Test::Nginx::Socket; #master_process_enabled(1); #log_level('warn'); -repeat_each(2); +#repeat_each(2); -plan tests => repeat_each() * (blocks() * 2 + 6); +plan tests => repeat_each() * (blocks() * 2 + 16); #no_diff(); no_long_string(); @@ -257,7 +257,7 @@ nil -=== TEST 13: read buffered body to memory and reset it +=== TEST 13: read buffered body to memory and reset it with data in memory --- config location = /test { content_by_lua ' @@ -299,7 +299,7 @@ hello, baby -=== TEST 15: do not read the current request body but replace it with our own +=== TEST 15: do not read the current request body but replace it with our own in memory --- config client_body_in_file_only on; @@ -320,3 +320,247 @@ hello, baby hello, baby "] + + +=== TEST 16: read buffered body to file and reset it to a new file +--- config + client_body_in_file_only on; + + location = /test { + set $old ''; + set $new ''; + rewrite_by_lua ' + ngx.req.read_body() + ngx.var.old = ngx.req.get_body_file() + ngx.req.set_body_file(ngx.var.realpath_root .. "/a.txt") + ngx.var.new = ngx.req.get_body_file() + '; + #echo_request_body; + proxy_pass http://127.0.0.1:$server_port/echo; + #proxy_pass http://127.0.0.1:7890/echo; + add_header X-Old $old; + add_header X-New $new; + } + location /echo { + echo_read_request_body; + echo_request_body; + } +--- request +POST /test +hello, world +--- user_files +>>> a.txt +Will you change this world? +--- raw_response_headers_like +X-Old: \S+/client_body_temp/\d+\r +.*?X-New: \S+/html/a\.txt\r +--- response_body +Will you change this world? + + + +=== TEST 17: read buffered body to file and reset it to a new file +--- config + client_body_in_file_only on; + + location = /test { + set $old ''; + set $new ''; + rewrite_by_lua ' + ngx.req.read_body() + ngx.var.old = ngx.req.get_body_file() or "" + ngx.req.set_body_file(ngx.var.realpath_root .. "/a.txt") + ngx.var.new = ngx.req.get_body_file() + '; + #echo_request_body; + proxy_pass http://127.0.0.1:$server_port/echo; + #proxy_pass http://127.0.0.1:7890/echo; + add_header X-Old $old; + add_header X-New $new; + } + location /echo { + echo_read_request_body; + echo_request_body; + } +--- request +POST /test +hello, world! +--- user_files +>>> a.txt +Will you change this world? +--- raw_response_headers_like +X-Old: \S+/client_body_temp/\d+\r +.*?X-New: \S+/html/a\.txt\r +--- response_body +Will you change this world? + + + +=== TEST 18: read buffered body to file and reset it to a new file (auto-clean) +--- config + client_body_in_file_only on; + + location = /test { + set $old ''; + set $new ''; + content_by_lua ' + ngx.req.read_body() + ngx.var.old = ngx.req.get_body_file() + local a_file = ngx.var.realpath_root .. "/a.txt" + ngx.req.set_body_file(a_file, true) + local b_file = ngx.var.realpath_root .. "/b.txt" + ngx.req.set_body_file(b_file, true) + ngx.say("a.txt exists: ", io.open(a_file) and "yes" or "no") + ngx.say("b.txt exists: ", io.open(b_file) and "yes" or "no") + '; + } + location /echo { + echo_read_request_body; + echo_request_body; + } +--- request +POST /test +hello, world +--- user_files +>>> a.txt +Will you change this world? +>>> b.txt +Sure I will! +--- response_body +a.txt exists: no +b.txt exists: yes + + + +=== TEST 19: read buffered body to memoary and reset it to a new file (auto-clean) +--- config + client_body_in_file_only off; + + location = /test { + set $old ''; + set $new ''; + rewrite_by_lua ' + ngx.req.read_body() + local a_file = ngx.var.realpath_root .. "/a.txt" + ngx.req.set_body_file(a_file, true) + '; + echo_request_body; + } + location /echo { + echo_read_request_body; + echo_request_body; + } +--- pipelined_requests eval +["POST /test +hello, world", +"POST /test +hey, you"] +--- user_files +>>> a.txt +Will you change this world? +--- response_body eval +["Will you change this world?\n", +qr/500 Internal Server Error/] +--- error_code eval +[200, 500] + + + +=== TEST 20: read buffered body to memoary and reset it to a new file (no auto-clean) +--- config + client_body_in_file_only off; + + location = /test { + set $old ''; + set $new ''; + rewrite_by_lua ' + ngx.req.read_body() + local a_file = ngx.var.realpath_root .. "/a.txt" + ngx.req.set_body_file(a_file, false) + '; + echo_request_body; + } + location /echo { + echo_read_request_body; + echo_request_body; + } +--- pipelined_requests eval +["POST /test +hello, world", +"POST /test +hey, you"] +--- user_files +>>> a.txt +Will you change this world? +--- response_body eval +["Will you change this world?\n", +"Will you change this world?\n"] +--- error_code eval +[200, 200] + + + +=== TEST 21: no request body and reset it to a new file (auto-clean) +--- config + client_body_in_file_only off; + + location = /test { + set $old ''; + set $new ''; + rewrite_by_lua ' + local a_file = ngx.var.realpath_root .. "/a.txt" + ngx.req.set_body_file(a_file, false) + '; + echo_request_body; + } + location /echo { + echo_read_request_body; + echo_request_body; + } +--- pipelined_requests eval +["POST /test +hello, world", +"POST /test +hey, you"] +--- user_files +>>> a.txt +Will you change this world? +--- response_body eval +["Will you change this world?\n", +"Will you change this world?\n"] +--- error_code eval +[200, 200] +--- ONLY + + +=== TEST 22: no request body and reset it to a new file (no auto-clean) +--- config + client_body_in_file_only off; + + location = /test { + set $old ''; + set $new ''; + rewrite_by_lua ' + local a_file = ngx.var.realpath_root .. "/a.txt" + ngx.req.set_body_file(a_file, true) + '; + echo_request_body; + } + location /echo { + echo_read_request_body; + echo_request_body; + } +--- pipelined_requests eval +["POST /test +hello, world", +"POST /test +hey, you"] +--- user_files +>>> a.txt +Will you change this world? +--- response_body eval +["Will you change this world?\n", +qr/500 Internal Server Error/] +--- error_code eval +[200, 500] + From 893f7e72a5d77243ed20256d5d1f5c30d6bdebba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Fri, 21 Oct 2011 14:51:14 +0800 Subject: [PATCH 31/46] now we also override Content-Length for ngx.req.set_body_data(). --- src/ngx_http_lua_req_body.c | 27 ++++++++++++++++++++++++++- t/034-match.t | 27 ++++++++++++++++++++++++++- t/044-req-body.t | 23 ++++++++++++++++++++++- 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index d26f5b11..d7e9b614 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -244,7 +244,7 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) ngx_chain_t *cl; ngx_temp_file_t *tf; ngx_buf_t *b; - ngx_str_t body; + ngx_str_t body, key, value; #if 1 ngx_int_t rc; #endif @@ -337,6 +337,31 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) rb->buf = b; } + /* override input header Content-Length */ + + value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN); + if (value.data == NULL) { + return luaL_error(L, "out of memory"); + } + + value.len = ngx_sprintf(value.data, "%O", body.len) - value.data; + + if (r->headers_in.content_length) { + r->headers_in.content_length_n = body.len; + r->headers_in.content_length->value.data = value.data; + r->headers_in.content_length->value.len = value.len; + + } else { + + ngx_str_set(&key, "Content-Length"); + + rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */); + if (rc != NGX_OK) { + return luaL_error(L, "failed to reset the Content-Length " + "input header"); + } + } + return 0; } diff --git a/t/034-match.t b/t/034-match.t index f4a2a756..d6022f0c 100644 --- a/t/034-match.t +++ b/t/034-match.t @@ -12,7 +12,7 @@ repeat_each(2); plan tests => repeat_each() * (blocks() * 2); #no_diff(); -#no_long_string(); +no_long_string(); run_tests(); __DATA__ @@ -567,3 +567,28 @@ end --- response_body [ ] + + +=== TEST 28: escaping sequences +--- config + location /re { + access_by_lua_file html/a.lua; + content_by_lua return; + } +--- user_files +>>> a.lua +local uri = "2" +local regex = '(?:>[\\w\\s]*)'; +ngx.say("regex: ", regex) +m = ngx.re.match(uri, regex, "oi") +if m then + ngx.say("[", m[0], "]") +else + ngx.say("not matched!") +end +--- request + GET /re +--- response_body +[ ] +--- ONLY + diff --git a/t/044-req-body.t b/t/044-req-body.t index 22b4d409..9a332183 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -530,7 +530,7 @@ Will you change this world? "Will you change this world?\n"] --- error_code eval [200, 200] ---- ONLY + === TEST 22: no request body and reset it to a new file (no auto-clean) @@ -564,3 +564,24 @@ qr/500 Internal Server Error/] --- error_code eval [200, 500] + + +=== TEST 23: read buffered body to memory and reset it with data in memory + proxy +--- config + location = /test { + rewrite_by_lua ' + ngx.req.read_body() + ngx.req.set_body_data("hiya, dear dear friend!") + '; + proxy_pass http://127.0.0.1:$server_port/echo; + } + location = /echo { + echo_read_request_body; + echo_request_body; + } +--- request +POST /test +hello, world +--- response_body chomp +hiya, dear dear friend! + From 5c36f6e8ca862be26df398ddbe81fc27ef75eec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Fri, 21 Oct 2011 19:12:31 +0800 Subject: [PATCH 32/46] now we complete the work for ngx.req.set_body_file(), ngx.req.set_body_data(), and ngx.req.discard_body(). --- src/ngx_http_lua_common.h | 1 - src/ngx_http_lua_contentby.c | 2 - src/ngx_http_lua_req_body.c | 34 +++++++++-- src/ngx_http_lua_util.c | 2 - t/034-match.t | 4 +- t/044-req-body.t | 115 ++++++++++++++++++++++++++++++++++- 6 files changed, 146 insertions(+), 12 deletions(-) diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index fa9ae233..863538d8 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -181,7 +181,6 @@ typedef struct { /* whether it has run post_subrequest */ unsigned run_post_subrequest:1; unsigned req_header_cached:1; - } ngx_http_lua_ctx_t; diff --git a/src/ngx_http_lua_contentby.c b/src/ngx_http_lua_contentby.c index 145165f0..4dee512a 100644 --- a/src/ngx_http_lua_contentby.c +++ b/src/ngx_http_lua_contentby.c @@ -180,8 +180,6 @@ ngx_http_lua_content_phase_post_read(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; - r->read_event_handler = ngx_http_request_empty_handler; - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); ctx->read_body_done = 1; diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index d7e9b614..ccb61c13 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -74,6 +74,10 @@ ngx_http_lua_ngx_req_read_body(lua_State *L) return luaL_error(L, "request context is null"); } + if (r->discard_body || r->content_length_n == 0) { + return luaL_error(L, "body already discarded"); + } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua start to read buffered request body"); @@ -112,8 +116,6 @@ ngx_http_lua_req_body_post_read(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua req body post read"); - r->read_event_handler = ngx_http_block_reading; - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); ctx->req_read_body_done = 1; @@ -299,6 +301,23 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) rb->temp_file = NULL; } + if (body.len == 0) { + if (rb->bufs) { + for (cl = rb->bufs; cl; cl = cl->next) { + if (cl->buf->temporary) { + ngx_pfree(r->pool, cl->buf->start); + cl->buf->temporary = 0; + } + } + } + + rb->bufs = NULL; + rb->buf = NULL; + + dd("request body is set to empty string"); + goto set_header; + } + if (rb->bufs) { for (cl = rb->bufs; cl; cl = cl->next) { if (cl->buf->temporary) { @@ -337,6 +356,8 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) rb->buf = b; } +set_header: + /* override input header Content-Length */ value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN); @@ -346,8 +367,12 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) value.len = ngx_sprintf(value.data, "%O", body.len) - value.data; + dd("setting request Content-Length to %.*s (%d)", + (int) value.len, value.data, (int) body.len); + + r->headers_in.content_length_n = body.len; + if (r->headers_in.content_length) { - r->headers_in.content_length_n = body.len; r->headers_in.content_length->value.data = value.data; r->headers_in.content_length->value.len = value.len; @@ -604,8 +629,9 @@ set_header: value.len = ngx_sprintf(value.data, "%O", of.size) - value.data; + r->headers_in.content_length_n = of.size; + if (r->headers_in.content_length) { - r->headers_in.content_length_n = of.size; r->headers_in.content_length->value.data = value.data; r->headers_in.content_length->value.len = value.len; diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index a4137dce..7269fe55 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -655,8 +655,6 @@ ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua post read for rewrite/access phases"); - r->read_event_handler = ngx_http_block_reading; - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); ctx->read_body_done = 1; diff --git a/t/034-match.t b/t/034-match.t index d6022f0c..3cbb4a4a 100644 --- a/t/034-match.t +++ b/t/034-match.t @@ -589,6 +589,6 @@ end --- request GET /re --- response_body -[ ] ---- ONLY +regex: (?:>[\w\s]*) +[>2] diff --git a/t/044-req-body.t b/t/044-req-body.t index 9a332183..57756b81 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -8,7 +8,7 @@ use Test::Nginx::Socket; #repeat_each(2); -plan tests => repeat_each() * (blocks() * 2 + 16); +plan tests => repeat_each() * (blocks() * 2 + 24); #no_diff(); no_long_string(); @@ -308,8 +308,12 @@ hello, baby ngx.req.set_body_data("hello, baby") ngx.say(ngx.req.get_body_data()) ngx.say(ngx.var.request_body) + -- ngx.location.capture("/sleep") '; } + location = /sleep { + echo_sleep 0.5; + } --- pipelined_requests eval ["POST /test\nyeah", "POST /test\nblah"] --- response_body eval @@ -585,3 +589,112 @@ hello, world --- response_body chomp hiya, dear dear friend! + + +=== TEST 24: discard request body and reset it to a new file (no auto-clean) +--- config + client_body_in_file_only off; + + location = /test { + set $old ''; + set $new ''; + rewrite_by_lua ' + ngx.req.discard_body() + local a_file = ngx.var.realpath_root .. "/a.txt" + ngx.req.set_body_file(a_file, true) + '; + echo_request_body; + } + location /echo { + echo_read_request_body; + echo_request_body; + } +--- pipelined_requests eval +["POST /test +hello, world", +"POST /test +hey, you"] +--- user_files +>>> a.txt +Will you change this world? +--- response_body eval +["Will you change this world?\n", +qr/500 Internal Server Error/] +--- error_code eval +[200, 500] + + + +=== TEST 25: discard body and then read +--- config + location = /test { + content_by_lua ' + ngx.req.discard_body() + local rc, err = pcall(ngx.req.read_body) + ngx.say(err) + ngx.say(ngx.req.get_body_data()) + '; + } +--- pipelined_requests eval +["POST /test +hello, world", +"POST /test +hello, world"] +--- response_body eval +["body already discarded +nil +", +"body already discarded +nil +"] + + + +=== TEST 26: set empty request body in memory +--- config + location = /test { + rewrite_by_lua ' + ngx.req.set_body_data("") + '; + proxy_pass http://127.0.0.1:$server_port/echo; + } + location = /echo { + content_by_lua ' + ngx.req.read_body() + ngx.say("body: [", ngx.req.get_body_data(), "]") + '; + } +--- pipelined_requests eval +["POST /test +hello, world", +"POST /test +hello, world"] +--- response_body eval +["body: [nil]\n","body: [nil]\n"] + + + +=== TEST 27: set empty request body in file +--- config + location = /test { + rewrite_by_lua ' + ngx.req.set_body_file(ngx.var.realpath_root .. "/a.txt") + '; + proxy_pass http://127.0.0.1:$server_port/echo; + } + location = /echo { + content_by_lua ' + ngx.req.read_body() + ngx.say("body: [", ngx.req.get_body_data(), "]") + '; + } +--- user_files +>>> a.txt +--- pipelined_requests eval +["POST /test +hello, world", +"POST /test +hello, world"] +--- response_body eval +["body: [nil]\n","body: [nil]\n"] + From ad0300696a5547fd077d3343620923632682ddfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Fri, 21 Oct 2011 20:37:59 +0800 Subject: [PATCH 33/46] fixed compatibility with standard nginx. --- src/ngx_http_lua_req_body.c | 4 ++++ util/build2.sh | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index ccb61c13..356738e9 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -74,7 +74,11 @@ ngx_http_lua_ngx_req_read_body(lua_State *L) return luaL_error(L, "request context is null"); } +#ifdef NGINX_ALLOW_REQUEST_BODY_UPDATING if (r->discard_body || r->content_length_n == 0) { +#else + if (r->discard_body) { +#endif return luaL_error(L, "body already discarded"); } diff --git a/util/build2.sh b/util/build2.sh index e841dd28..32bd7f42 100755 --- a/util/build2.sh +++ b/util/build2.sh @@ -15,7 +15,7 @@ force=$2 ngx-build $force $version \ --add-module=$root/../ndk-nginx-module \ --add-module=$root/../set-misc-nginx-module \ - --with-cc-opt=$'-O3' \ + --with-cc-opt=$'-O3 -DNGINX_ALLOW_REQUEST_BODY_UPDATING' \ --with-ld-opt="-Wl,-rpath,/opt/drizzle/lib:/usr/local/lib:/home/lz/lib:/usr/local/openresty/luajit/lib" \ --without-mail_pop3_module \ --without-mail_imap_module \ From 5cef7124ff67799a85f9f345ada46400a90bac43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Fri, 21 Oct 2011 21:57:30 +0800 Subject: [PATCH 34/46] no longer use the NGINX_ALLOW_REQUEST_BODY_UPDATING macro and r->content_length_n. now calling ngx.req.read_body() after ngx.req.discard_body() is a no-op. --- src/ngx_http_lua_req_body.c | 8 -------- t/044-req-body.t | 12 +++--------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 356738e9..bcc1c02b 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -74,14 +74,6 @@ ngx_http_lua_ngx_req_read_body(lua_State *L) return luaL_error(L, "request context is null"); } -#ifdef NGINX_ALLOW_REQUEST_BODY_UPDATING - if (r->discard_body || r->content_length_n == 0) { -#else - if (r->discard_body) { -#endif - return luaL_error(L, "body already discarded"); - } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua start to read buffered request body"); diff --git a/t/044-req-body.t b/t/044-req-body.t index 57756b81..8f5a8f32 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -630,9 +630,8 @@ qr/500 Internal Server Error/] location = /test { content_by_lua ' ngx.req.discard_body() - local rc, err = pcall(ngx.req.read_body) - ngx.say(err) - ngx.say(ngx.req.get_body_data()) + ngx.req.read_body() + ngx.print(ngx.req.get_body_data()) '; } --- pipelined_requests eval @@ -641,12 +640,7 @@ hello, world", "POST /test hello, world"] --- response_body eval -["body already discarded -nil -", -"body already discarded -nil -"] +["nil","nil"] From 8eef13e10b8b92f57824a0fa4a090b9486b11db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Fri, 21 Oct 2011 22:18:02 +0800 Subject: [PATCH 35/46] updated docs to reflect recent changes regarding the request body API. --- README | 89 +++++++++++++++++++++++++++++++++++++++--- README.markdown | 56 +++++++++++++++++++++++--- doc/HttpLuaModule.wiki | 54 ++++++++++++++++++++++--- 3 files changed, 183 insertions(+), 16 deletions(-) diff --git a/README b/README index ac83a2ff..a4ce52b1 100644 --- a/README +++ b/README @@ -8,8 +8,8 @@ Status This module is under active development and is already production ready. Version - This document describes ngx_lua v0.3.1rc17 - () released on 19 + This document describes ngx_lua v0.3.1rc18 + () released on 21 October 2011. Synopsis @@ -1309,12 +1309,16 @@ Nginx API for Lua context: *rewrite_by_lua*, access_by_lua*, content_by_lua** - Read the client request body synchronously but still non-blocking. + Read the client request body synchronously but still non-blockingly. If the request body is already read previously by turning on lua_need_request_body or by using other modules, then this function is a no-op and returns immediately. + If the request body has already been explicitly discarded, either by + this module's ngx.req.discard_body or other modules, this function is a + no-op and returns immediately. + In case of errors, like connection errors while reading the data, this method will throw out a Lua exception *or* terminate the current request with the 500 status code immediately. @@ -1323,7 +1327,7 @@ Nginx API for Lua ngx.req.get_body_data or retrieve the temporary file name for the body data cached to disk via ngx.req.get_body_file, depending on - 1. whether the current request body has already exceeding your + 1. whether the current request body is already exceeding your client_body_buffer_size, 2. and whether you have turned on client_body_in_file_only. @@ -1346,6 +1350,8 @@ Nginx API for Lua call this function, or you'll break things under HTTP 1.1 keepalive or HTTP 1.1 pipelining. + This function is an asynchronous call and returns immediately. + If the request body has already been read, this function does nothing and returns immediately. @@ -1354,7 +1360,7 @@ Nginx API for Lua See also ngx.req.read_body. ngx.req.get_body_data - syntax: *ngx.req.get_body_data()* + syntax: *data = ngx.req.get_body_data()* context: *rewrite_by_lua*, access_by_lua*, content_by_lua** @@ -1387,13 +1393,17 @@ Nginx API for Lua See also ngx.req.get_body_file. ngx.req.get_body_file - syntax: *ngx.req.get_body_file()* + syntax: *file_name = ngx.req.get_body_file()* context: *rewrite_by_lua*, access_by_lua*, content_by_lua** Retrieves the file name for the in-file request body data. Returns "nil" if the request body has not been read or has been read into memory. + The returned file is read only and is usually cleaned up automatically + by Nginx's memory pool. It should not be modified, renamed, or removed + by your own Lua code. + If the request body has not been read yet, call ngx.req.read_body first (or turned on lua_need_request_body to force this module to read the request body automatically, but this is not recommended). @@ -1408,6 +1418,73 @@ Nginx API for Lua See also ngx.req.get_body_data. + ngx.req.set_body_data + syntax: *ngx.req.set_body_data(data)* + + context: *rewrite_by_lua*, access_by_lua*, content_by_lua** + + Set the current request's request body using the in-memory data + specified by the "data" argument. + + If the current request's request body has not been read, then it will be + properly discarded. When the current request's request body has been + read into memory or buffered into a disk file, then the old request + body's memory will be freed or the disk file will be cleaned up + immediately, respectively. + + This function requires patching the Nginx core to function properly + because the Nginx core does not allow modifying request bodies by the + current design. Here is a patch for Nginx 1.0.8: + nginx-1.0.8-allow_request_body_updating.patch + (), and this patch should be applied + cleanly to other releases of Nginx as well. + + If you're using ngx_openresty () 1.0.8.17+, then + you've already had this patch applied. + + This function was first introduced in the "v0.3.1rc18" release. + + See also ngx.req.set_body_file. + + ngx.req.set_body_file + syntax: *ngx.req.set_body_file(file_name, auto_clean?)* + + context: *rewrite_by_lua*, access_by_lua*, content_by_lua** + + Set the current request's request body using the in-file data specified + by the "file_name" argument. + + If the optional "auto_clean" argument is given a "true" value, then this + file will be automatically removed at request completion or the next + time this function or ngx.req.set_body_data are called in the same + request. The "auto_clean" is default to "false". + + You must ensure that the file specified by the "file_name" argument + exists and is readable by an Nginx worker process by setting its + permission properly. Otherwise a Lua exception will be thrown. + + If the current request's request body has not been read, then it will be + properly discarded. When the current request's request body has been + read into memory or buffered into a disk file, then the old request + body's memory will be freed or the disk file will be cleaned up + immediately, respectively. + + This function requires patching the Nginx core to function properly + because the Nginx core does not allow modifying request bodies by the + current design. Here is a patch for Nginx 1.0.8: + nginx-1.0.8-allow_request_body_updating.patch + (), and this patch should be applied + cleanly to other releases of Nginx as well. + + If you're using ngx_openresty () 1.0.8.17+, then + you've already had this patch applied. + + This function was first introduced in the "v0.3.1rc18" release. + + See also ngx.req.set_body_data. + ngx.req.clear_header syntax: *ngx.req.clear_header(header_name)* diff --git a/README.markdown b/README.markdown index ec45b9c2..a2429d4c 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc17](https://github.com/chaoslawful/lua-nginx-module/tags) released on 19 October 2011. +This document describes ngx_lua [v0.3.1rc18](https://github.com/chaoslawful/lua-nginx-module/tags) released on 21 October 2011. Synopsis ======== @@ -1488,15 +1488,17 @@ ngx.req.read_body **context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** -Read the client request body synchronously but still non-blocking. +Read the client request body synchronously but still non-blockingly. If the request body is already read previously by turning on [lua_need_request_body](http://wiki.nginx.org/HttpLuaModule#lua_need_request_body) or by using other modules, then this function is a no-op and returns immediately. +If the request body has already been explicitly discarded, either by this module's [ngx.req.discard_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.discard_body) or other modules, this function is a no-op and returns immediately. + In case of errors, like connection errors while reading the data, this method will throw out a Lua exception *or* terminate the current request with the 500 status code immediately. You can later either retrieve the request body data via [ngx.req.get_body_data](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_data) or retrieve the temporary file name for the body data cached to disk via [ngx.req.get_body_file](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_file), depending on -1. whether the current request body has already exceeding your [client_body_buffer_size](http://wiki.nginx.org/HttpCoreModule#client_body_buffer_size), +1. whether the current request body is already exceeding your [client_body_buffer_size](http://wiki.nginx.org/HttpCoreModule#client_body_buffer_size), 1. and whether you have turned on [client_body_in_file_only](http://wiki.nginx.org/HttpCoreModule#client_body_in_file_only). In case that you do not want to read the request body and the current request may have a request body, then it's crucial to use the [ngx.req.discard_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.discard_body) function to explicitly discard the request body, or you'll break HTTP 1.1 keepalive and HTTP 1.1 pipelining. @@ -1511,6 +1513,8 @@ ngx.req.discard_body Explicitly discard the request body, i.e., read the data on the connection and throw it away immediately. Please note that, simply ignoring request body is not the right way to discard it, you need to call this function, or you'll break things under HTTP 1.1 keepalive or HTTP 1.1 pipelining. +This function is an asynchronous call and returns immediately. + If the request body has already been read, this function does nothing and returns immediately. This function was first introduced in the `v0.3.1rc17` release. @@ -1519,7 +1523,7 @@ See also [ngx.req.read_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.read_bo ngx.req.get_body_data --------------------- -**syntax:** *ngx.req.get_body_data()* +**syntax:** *data = ngx.req.get_body_data()* **context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** @@ -1544,12 +1548,14 @@ See also [ngx.req.get_body_file](http://wiki.nginx.org/HttpLuaModule#ngx.req.get ngx.req.get_body_file --------------------- -**syntax:** *ngx.req.get_body_file()* +**syntax:** *file_name = ngx.req.get_body_file()* **context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** Retrieves the file name for the in-file request body data. Returns `nil` if the request body has not been read or has been read into memory. +The returned file is read only and is usually cleaned up automatically by Nginx's memory pool. It should not be modified, renamed, or removed by your own Lua code. + If the request body has not been read yet, call [ngx.req.read_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body) first (or turned on [lua_need_request_body](http://wiki.nginx.org/HttpLuaModule#lua_need_request_body) to force this module to read the request body automatically, but this is not recommended). If the request body has been read into memory, try calling the [ngx.req.get_body_data](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_data) function instead. @@ -1560,6 +1566,46 @@ This function was first introduced in the `v0.3.1rc17` release. See also [ngx.req.get_body_data](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_data). +ngx.req.set_body_data +--------------------- +**syntax:** *ngx.req.set_body_data(data)* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** + +Set the current request's request body using the in-memory data specified by the `data` argument. + +If the current request's request body has not been read, then it will be properly discarded. When the current request's request body has been read into memory or buffered into a disk file, then the old request body's memory will be freed or the disk file will be cleaned up immediately, respectively. + +This function requires patching the Nginx core to function properly because the Nginx core does not allow modifying request bodies by the current design. Here is a patch for Nginx 1.0.8: [nginx-1.0.8-allow_request_body_updating.patch](https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.8-allow_request_body_updating.patch), and this patch should be applied cleanly to other releases of Nginx as well. + +If you're using [ngx_openresty](http://openresty.org/) 1.0.8.17+, then you've already had this patch applied. + +This function was first introduced in the `v0.3.1rc18` release. + +See also [ngx.req.set_body_file](http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_file). + +ngx.req.set_body_file +--------------------- +**syntax:** *ngx.req.set_body_file(file_name, auto_clean?)* + +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** + +Set the current request's request body using the in-file data specified by the `file_name` argument. + +If the optional `auto_clean` argument is given a `true` value, then this file will be automatically removed at request completion or the next time this function or [ngx.req.set_body_data](http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_data) are called in the same request. The `auto_clean` is default to `false`. + +You must ensure that the file specified by the `file_name` argument exists and is readable by an Nginx worker process by setting its permission properly. Otherwise a Lua exception will be thrown. + +If the current request's request body has not been read, then it will be properly discarded. When the current request's request body has been read into memory or buffered into a disk file, then the old request body's memory will be freed or the disk file will be cleaned up immediately, respectively. + +This function requires patching the Nginx core to function properly because the Nginx core does not allow modifying request bodies by the current design. Here is a patch for Nginx 1.0.8: [nginx-1.0.8-allow_request_body_updating.patch](https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.8-allow_request_body_updating.patch), and this patch should be applied cleanly to other releases of Nginx as well. + +If you're using [ngx_openresty](http://openresty.org/) 1.0.8.17+, then you've already had this patch applied. + +This function was first introduced in the `v0.3.1rc18` release. + +See also [ngx.req.set_body_data](http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_data). + ngx.req.clear_header -------------------- **syntax:** *ngx.req.clear_header(header_name)* diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 5e4e6735..0b466652 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc17] released on 19 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc18] released on 21 October 2011. = Synopsis = @@ -1447,15 +1447,17 @@ is equivalent to '''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' -Read the client request body synchronously but still non-blocking. +Read the client request body synchronously but still non-blockingly. If the request body is already read previously by turning on [[#lua_need_request_body|lua_need_request_body]] or by using other modules, then this function is a no-op and returns immediately. +If the request body has already been explicitly discarded, either by this module's [[#ngx.req.discard_body|ngx.req.discard_body]] or other modules, this function is a no-op and returns immediately. + In case of errors, like connection errors while reading the data, this method will throw out a Lua exception ''or'' terminate the current request with the 500 status code immediately. You can later either retrieve the request body data via [[#ngx.req.get_body_data|ngx.req.get_body_data]] or retrieve the temporary file name for the body data cached to disk via [[#ngx.req.get_body_file|ngx.req.get_body_file]], depending on -# whether the current request body has already exceeding your [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]], +# whether the current request body is already exceeding your [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]], # and whether you have turned on [[HttpCoreModule#client_body_in_file_only|client_body_in_file_only]]. In case that you do not want to read the request body and the current request may have a request body, then it's crucial to use the [[#ngx.req.discard_body|ngx.req.discard_body]] function to explicitly discard the request body, or you'll break HTTP 1.1 keepalive and HTTP 1.1 pipelining. @@ -1469,6 +1471,8 @@ This function was first introduced in the v0.3.1rc17 release. Explicitly discard the request body, i.e., read the data on the connection and throw it away immediately. Please note that, simply ignoring request body is not the right way to discard it, you need to call this function, or you'll break things under HTTP 1.1 keepalive or HTTP 1.1 pipelining. +This function is an asynchronous call and returns immediately. + If the request body has already been read, this function does nothing and returns immediately. This function was first introduced in the v0.3.1rc17 release. @@ -1476,7 +1480,7 @@ This function was first introduced in the v0.3.1rc17 release. See also [[#ngx.req.read_body|ngx.req.read_body]]. == ngx.req.get_body_data == -'''syntax:''' ''ngx.req.get_body_data()'' +'''syntax:''' ''data = ngx.req.get_body_data()'' '''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' @@ -1500,12 +1504,14 @@ This function was first introduced in the v0.3.1rc17 release. See also [[#ngx.req.get_body_file|ngx.req.get_body_file]]. == ngx.req.get_body_file == -'''syntax:''' ''ngx.req.get_body_file()'' +'''syntax:''' ''file_name = ngx.req.get_body_file()'' '''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' Retrieves the file name for the in-file request body data. Returns nil if the request body has not been read or has been read into memory. +The returned file is read only and is usually cleaned up automatically by Nginx's memory pool. It should not be modified, renamed, or removed by your own Lua code. + If the request body has not been read yet, call [[#ngx.req.read_body|ngx.req.read_body]] first (or turned on [[#lua_need_request_body|lua_need_request_body]] to force this module to read the request body automatically, but this is not recommended). If the request body has been read into memory, try calling the [[#ngx.req.get_body_data|ngx.req.get_body_data]] function instead. @@ -1516,6 +1522,44 @@ This function was first introduced in the v0.3.1rc17 release. See also [[#ngx.req.get_body_data|ngx.req.get_body_data]]. +== ngx.req.set_body_data == +'''syntax:''' ''ngx.req.set_body_data(data)'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' + +Set the current request's request body using the in-memory data specified by the data argument. + +If the current request's request body has not been read, then it will be properly discarded. When the current request's request body has been read into memory or buffered into a disk file, then the old request body's memory will be freed or the disk file will be cleaned up immediately, respectively. + +This function requires patching the Nginx core to function properly because the Nginx core does not allow modifying request bodies by the current design. Here is a patch for Nginx 1.0.8: [https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.8-allow_request_body_updating.patch nginx-1.0.8-allow_request_body_updating.patch], and this patch should be applied cleanly to other releases of Nginx as well. + +If you're using [http://openresty.org/ ngx_openresty] 1.0.8.17+, then you've already had this patch applied. + +This function was first introduced in the v0.3.1rc18 release. + +See also [[#ngx.req.set_body_file|ngx.req.set_body_file]]. + +== ngx.req.set_body_file == +'''syntax:''' ''ngx.req.set_body_file(file_name, auto_clean?)'' + +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' + +Set the current request's request body using the in-file data specified by the file_name argument. + +If the optional auto_clean argument is given a true value, then this file will be automatically removed at request completion or the next time this function or [[#ngx.req.set_body_data|ngx.req.set_body_data]] are called in the same request. The auto_clean is default to false. + +You must ensure that the file specified by the file_name argument exists and is readable by an Nginx worker process by setting its permission properly. Otherwise a Lua exception will be thrown. + +If the current request's request body has not been read, then it will be properly discarded. When the current request's request body has been read into memory or buffered into a disk file, then the old request body's memory will be freed or the disk file will be cleaned up immediately, respectively. + +This function requires patching the Nginx core to function properly because the Nginx core does not allow modifying request bodies by the current design. Here is a patch for Nginx 1.0.8: [https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.8-allow_request_body_updating.patch nginx-1.0.8-allow_request_body_updating.patch], and this patch should be applied cleanly to other releases of Nginx as well. + +If you're using [http://openresty.org/ ngx_openresty] 1.0.8.17+, then you've already had this patch applied. + +This function was first introduced in the v0.3.1rc18 release. + +See also [[#ngx.req.set_body_data|ngx.req.set_body_data]]. + == ngx.req.clear_header == '''syntax:''' ''ngx.req.clear_header(header_name)'' From 0cb010bb3f614c104fc3f055d832f9d1f732461d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Fri, 21 Oct 2011 23:36:52 +0800 Subject: [PATCH 36/46] bugfix: lua_need_request_body should not skip requests with methods other than POST and PUT. thanks Nginx User. --- src/ngx_http_lua_accessby.c | 5 +---- src/ngx_http_lua_contentby.c | 5 +---- src/ngx_http_lua_rewriteby.c | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/ngx_http_lua_accessby.c b/src/ngx_http_lua_accessby.c index 77bae611..d29a793a 100644 --- a/src/ngx_http_lua_accessby.c +++ b/src/ngx_http_lua_accessby.c @@ -107,10 +107,7 @@ ngx_http_lua_access_handler(ngx_http_request_t *r) return rc; } - if (llcf->force_read_body && - ! ctx->read_body_done && - ((r->method & NGX_HTTP_POST) || (r->method & NGX_HTTP_PUT))) - { + if (llcf->force_read_body && !ctx->read_body_done) { r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; diff --git a/src/ngx_http_lua_contentby.c b/src/ngx_http_lua_contentby.c index 4dee512a..d9546e96 100644 --- a/src/ngx_http_lua_contentby.c +++ b/src/ngx_http_lua_contentby.c @@ -143,10 +143,7 @@ ngx_http_lua_content_handler(ngx_http_request_t *r) return rc; } - if (llcf->force_read_body && - ! ctx->read_body_done && - ((r->method & NGX_HTTP_POST) || (r->method & NGX_HTTP_PUT))) - { + if (llcf->force_read_body && !ctx->read_body_done) { r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; diff --git a/src/ngx_http_lua_rewriteby.c b/src/ngx_http_lua_rewriteby.c index 859cf19a..1945d83e 100644 --- a/src/ngx_http_lua_rewriteby.c +++ b/src/ngx_http_lua_rewriteby.c @@ -108,10 +108,7 @@ ngx_http_lua_rewrite_handler(ngx_http_request_t *r) return rc; } - if (llcf->force_read_body && - ! ctx->read_body_done && - ((r->method & NGX_HTTP_POST) || (r->method & NGX_HTTP_PUT))) - { + if (llcf->force_read_body && !ctx->read_body_done) { r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; From f03ac7ca368029ab748a46da9b4eda7d1692a6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sat, 22 Oct 2011 09:54:58 +0800 Subject: [PATCH 37/46] fixed -Wset-but-not-used warning issues caught by gcc 4.6.0. --- src/ngx_http_lua_req_body.c | 3 --- t/014-bugs.t | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index bcc1c02b..e0c6d4da 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -420,7 +420,6 @@ ngx_http_lua_ngx_req_set_body_file(lua_State *L) ngx_temp_file_t *tf; ngx_buf_t *b; ngx_str_t name; - ngx_http_core_loc_conf_t *clcf; ngx_int_t rc; int clean; ngx_open_file_info_t of; @@ -509,8 +508,6 @@ ngx_http_lua_ngx_req_set_body_file(lua_State *L) b->last_in_chain = 1; - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - /* just make r->request_body->temp_file a bare stub */ tf = rb->temp_file; diff --git a/t/014-bugs.t b/t/014-bugs.t index 6f237ff9..58238863 100644 --- a/t/014-bugs.t +++ b/t/014-bugs.t @@ -502,3 +502,23 @@ nil hello --- SKIP + + +=== TEST 24: set special variables +--- config + location /main { + content_by_lua ' + dofile(ngx.var.realpath_root .. "/a.lua") + '; + } + location /echo { + echo hi; + } +--- request + GET /main +--- user_files +>>> a.lua +ngx.location.capture("/echo") +--- response_body +--- SKIP + From 739825a7c4339425fd61e897b8a027b04a8ccd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sat, 22 Oct 2011 11:33:56 +0800 Subject: [PATCH 38/46] no longer set the NGINX_ALLOW_REQUEST_BODY_UPDATING macro in developer build script. --- util/build2.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/build2.sh b/util/build2.sh index 32bd7f42..e841dd28 100755 --- a/util/build2.sh +++ b/util/build2.sh @@ -15,7 +15,7 @@ force=$2 ngx-build $force $version \ --add-module=$root/../ndk-nginx-module \ --add-module=$root/../set-misc-nginx-module \ - --with-cc-opt=$'-O3 -DNGINX_ALLOW_REQUEST_BODY_UPDATING' \ + --with-cc-opt=$'-O3' \ --with-ld-opt="-Wl,-rpath,/opt/drizzle/lib:/usr/local/lib:/home/lz/lib:/usr/local/openresty/luajit/lib" \ --without-mail_pop3_module \ --without-mail_imap_module \ From 0a8a11b99489f4bd077bc0c5e1fa7d0be03261bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sat, 22 Oct 2011 11:40:46 +0800 Subject: [PATCH 39/46] put the debug log outputs to the right place. --- src/ngx_http_lua_capturefilter.c | 15 +++------------ src/ngx_http_lua_headerfilterby.c | 6 +++--- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/ngx_http_lua_capturefilter.c b/src/ngx_http_lua_capturefilter.c index 9ee06517..19c41f4b 100644 --- a/src/ngx_http_lua_capturefilter.c +++ b/src/ngx_http_lua_capturefilter.c @@ -39,9 +39,6 @@ ngx_http_lua_capture_header_filter(ngx_http_request_t *r) ngx_http_lua_ctx_t *old_ctx; ngx_http_lua_ctx_t *ctx; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua capture header filter, uri \"%V\"", &r->uri); - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("old ctx: %p", ctx); @@ -74,7 +71,7 @@ ngx_http_lua_capture_header_filter(ngx_http_request_t *r) if (ctx && ctx->capture) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua capturing request body"); + "lua capturing response body"); /* force subrequest response body buffer in memory */ r->filter_need_in_memory = 1; @@ -93,9 +90,6 @@ ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ngx_http_lua_ctx_t *ctx; ngx_http_lua_ctx_t *pr_ctx; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua capture body filter, uri \"%V\"", &r->uri); - if (in == NULL) { return ngx_http_lua_next_body_filter(r, NULL); } @@ -108,9 +102,6 @@ ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in) return ngx_http_lua_next_body_filter(r, in); } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua body filter"); - if (ctx->run_post_subrequest) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua body filter skipped because post subrequest already run"); @@ -129,8 +120,8 @@ ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in) return NGX_ERROR; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua body filter capturing output"); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua capture body filter capturing response body, uri \"%V\"", &r->uri); rc = ngx_http_lua_add_copy_chain(r, pr_ctx, &ctx->body, in); diff --git a/src/ngx_http_lua_headerfilterby.c b/src/ngx_http_lua_headerfilterby.c index bc880f5c..63d629db 100644 --- a/src/ngx_http_lua_headerfilterby.c +++ b/src/ngx_http_lua_headerfilterby.c @@ -244,9 +244,6 @@ ngx_http_lua_header_filter(ngx_http_request_t *r) ngx_http_lua_ctx_t *ctx; ngx_int_t rc; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua header filter, uri \"%V\"", &r->uri); - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->header_filter_handler == NULL) { @@ -254,6 +251,9 @@ ngx_http_lua_header_filter(ngx_http_request_t *r) return ngx_http_next_header_filter(r); } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua header filter for header_filter_by_lua , uri \"%V\"", &r->uri); + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); From 58d6bc91e2e71ae30d2ac9608a99081dba415cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sat, 22 Oct 2011 11:56:15 +0800 Subject: [PATCH 40/46] fixed reading size_t as an off_t in ngx.req.set_body_data() which may lead to memory issues on 32-bit systems. --- src/ngx_http_lua_req_body.c | 4 ++-- valgrind.suppress | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index e0c6d4da..ae710bc6 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -356,12 +356,12 @@ set_header: /* override input header Content-Length */ - value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN); + value.data = ngx_palloc(r->pool, NGX_SIZE_T_LEN); if (value.data == NULL) { return luaL_error(L, "out of memory"); } - value.len = ngx_sprintf(value.data, "%O", body.len) - value.data; + value.len = ngx_sprintf(value.data, "%uz", body.len) - value.data; dd("setting request Content-Length to %.*s (%d)", (int) value.len, value.data, (int) body.len); diff --git a/valgrind.suppress b/valgrind.suppress index ecef7262..06591937 100644 --- a/valgrind.suppress +++ b/valgrind.suppress @@ -1,3 +1,22 @@ +{ + + Memcheck:Addr4 + fun:lj_str_new + fun:lua_setfield +} +{ + + Memcheck:Addr4 + fun:lj_str_new + fun:lua_getfield +} +{ + + Memcheck:Leak + fun:malloc + fun:ngx_alloc + fun:(below main) +} { Memcheck:Param From b25d06b326a0ebb063088c79923e9634ccf7fd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Sat, 22 Oct 2011 13:04:30 +0800 Subject: [PATCH 41/46] improved the README file's formatting. --- README | 511 ++++++++++++++++++++++--------- README.markdown | 121 +++++++- doc/HttpLuaModule.wiki | 121 +++++++- src/ngx_http_lua_capturefilter.c | 3 +- 4 files changed, 592 insertions(+), 164 deletions(-) diff --git a/README b/README index a4ce52b1..f08836e1 100644 --- a/README +++ b/README @@ -8,8 +8,8 @@ Status This module is under active development and is already production ready. Version - This document describes ngx_lua v0.3.1rc18 - () released on 21 + This document describes ngx_lua v0.3.1rc19 + () released on 22 October 2011. Synopsis @@ -300,8 +300,10 @@ Directives Note that set_by_lua can only output a value to a single Nginx variable at a time. But a work-around is also available by means of the - ngx.var.VARIABLE interface, for example, location /foo { set $diff ''; # - we have to predefine the $diff variable here + ngx.var.VARIABLE interface, for example, + + location /foo { + set $diff ''; # we have to predefine the $diff variable here set_by_lua $sum ' local a = 32 @@ -317,9 +319,11 @@ Directives This directive can be freely mixed with all the directives of [[HttpRewriteModule]], [[HttpSetMiscModule]], and [[HttpArrayVarModule]]. All of these directives will run in exactly the - same order that they are written in the config file. For example, set - $foo 32; set_by_lua $bar 'tonumber(ngx.var.foo) + 1'; set $baz "bar: - $bar"; # $baz == "bar: 33" + same order that they are written in the config file. For example, + + set $foo 32; + set_by_lua $bar 'tonumber(ngx.var.foo) + 1'; + set $baz "bar: $bar"; # $baz == "bar: 33" This directive requires the ngx_devel_kit () module. @@ -637,8 +641,12 @@ Directives ngx.location.capture_multi) Here's a small example of overriding a response header (or adding if it - does not exist) in our Lua header filter: location / { proxy_pass - http://mybackend; header_filter_by_lua 'ngx.header.Foo = "blah"'; } + does not exist) in our Lua header filter: + + location / { + proxy_pass http://mybackend; + header_filter_by_lua 'ngx.header.Foo = "blah"'; + } This directive was first introduced in the "v0.2.1rc20" release. @@ -757,10 +765,17 @@ Nginx API for Lua context: *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua** value = ngx.var.some_nginx_variable_name ngx.var.some_nginx_variable_name = value Note that you can only write to - nginx variables that are already defined. For example: location /foo { - set $my_var ''; # this line is required to create $my_var at config time - content_by_lua ' ngx.var.my_var = 123; ... '; } That is, nginx variables - cannot be created on-the-fly. + nginx variables that are already defined. For example: + + location /foo { + set $my_var ''; # this line is required to create $my_var at config time + content_by_lua ' + ngx.var.my_var = 123; + ... + '; + } + + That is, nginx variables cannot be created on-the-fly. Some special nginx variables like $args and $limit_rate can be assigned a value, some are not, like $arg_PARAMETER. @@ -777,8 +792,10 @@ Nginx API for Lua ngx.ERROR (-1) ngx.AGAIN (-2) ngx.DONE (-4) - They take the same values of C, C, C, C, and etc. But now - only L only take two of these values, i.e., C and C. + + They take the same values of "NGX_OK", "NGX_AGAIN", "NGX_DONE", + "NGX_ERROR", and etc. But now only ngx.exit only take two of these + values, i.e., "NGX_OK" and "NGX_ERROR". HTTP method constants context: *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, @@ -795,21 +812,38 @@ Nginx API for Lua HTTP status constants context: *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, - header_filter_by_lua** value = ngx.HTTP_OK (200) value = - ngx.HTTP_CREATED (201) value = ngx.HTTP_SPECIAL_RESPONSE (300) value = - ngx.HTTP_MOVED_PERMANENTLY (301) value = ngx.HTTP_MOVED_TEMPORARILY - (302) value = ngx.HTTP_SEE_OTHER (303) value = ngx.HTTP_NOT_MODIFIED - (304) value = ngx.HTTP_BAD_REQUEST (400) value = ngx.HTTP_UNAUTHORIZED - (401) value = ngx.HTTP_FORBIDDEN (403) value = ngx.HTTP_NOT_FOUND (404) - value = ngx.HTTP_NOT_ALLOWED (405) value = ngx.HTTP_GONE (410) value = - ngx.HTTP_INTERNAL_SERVER_ERROR (500) value = - ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) value = - ngx.HTTP_SERVICE_UNAVAILABLE (503) + header_filter_by_lua** + + value = ngx.HTTP_OK (200) + value = ngx.HTTP_CREATED (201) + value = ngx.HTTP_SPECIAL_RESPONSE (300) + value = ngx.HTTP_MOVED_PERMANENTLY (301) + value = ngx.HTTP_MOVED_TEMPORARILY (302) + value = ngx.HTTP_SEE_OTHER (303) + value = ngx.HTTP_NOT_MODIFIED (304) + value = ngx.HTTP_BAD_REQUEST (400) + value = ngx.HTTP_UNAUTHORIZED (401) + value = ngx.HTTP_FORBIDDEN (403) + value = ngx.HTTP_NOT_FOUND (404) + value = ngx.HTTP_NOT_ALLOWED (405) + value = ngx.HTTP_GONE (410) + value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) + value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) + value = ngx.HTTP_SERVICE_UNAVAILABLE (503) Nginx log level constants context: *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, - header_filter_by_lua** ngx.STDERR ngx.EMERG ngx.ALERT ngx.CRIT ngx.ERR - ngx.WARN ngx.NOTICE ngx.INFO ngx.DEBUG + header_filter_by_lua** + + ngx.STDERR + ngx.EMERG + ngx.ALERT + ngx.CRIT + ngx.ERR + ngx.WARN + ngx.NOTICE + ngx.INFO + ngx.DEBUG These constants are usually used by the ngx.log method. @@ -822,8 +856,11 @@ Nginx API for Lua Emit args concatenated to nginx's "error.log" file, with log level "ngx.NOTICE" and prefix "lua print: ". - It's equivalent to ngx.log(ngx.NOTICE, 'lua print: ', a, b, ...) Lua - "nil" arguments are accepted and result in literal "nil", and Lua + It's equivalent to + + ngx.log(ngx.NOTICE, 'lua print: ', a, b, ...) + + Lua "nil" arguments are accepted and result in literal "nil", and Lua booleans result in "true" or "false". ngx.ctx @@ -834,16 +871,38 @@ Nginx API for Lua programmers. This table has a liftime identical to the current request (just like - Nginx variables). Consider the following example, location /test { - rewrite_by_lua ' ngx.say("foo = ", ngx.ctx.foo) ngx.ctx.foo = 76 '; - access_by_lua ' ngx.ctx.foo = ngx.ctx.foo + 3 '; content_by_lua ' - ngx.say(ngx.ctx.foo) '; } Then "GET /test" will yield the output foo = - nil 79 That is, the "ngx.ctx.foo" entry persists across the rewrite, - access, and content phases of a request. + Nginx variables). Consider the following example, + + location /test { + rewrite_by_lua ' + ngx.say("foo = ", ngx.ctx.foo) + ngx.ctx.foo = 76 + '; + access_by_lua ' + ngx.ctx.foo = ngx.ctx.foo + 3 + '; + content_by_lua ' + ngx.say(ngx.ctx.foo) + '; + } + + Then "GET /test" will yield the output + + foo = nil + 79 + + That is, the "ngx.ctx.foo" entry persists across the rewrite, access, + and content phases of a request. Also, every request has its own copy, include subrequests, for example: - location /sub { content_by_lua ' ngx.say("sub pre: ", ngx.ctx.blah) - ngx.ctx.blah = 32 ngx.say("sub post: ", ngx.ctx.blah) '; } + + location /sub { + content_by_lua ' + ngx.say("sub pre: ", ngx.ctx.blah) + ngx.ctx.blah = 32 + ngx.say("sub post: ", ngx.ctx.blah) + '; + } location /main { content_by_lua ' @@ -854,16 +913,27 @@ Nginx API for Lua ngx.say("main post: ", ngx.ctx.blah) '; } - Then C will give the output + + Then "GET /main" will give the output + main pre: 73 sub pre: nil sub post: 32 main post: 73 - We can see that modification of the C entry in the subrequest does not affect the one in its parent request. They do have two separate versions of C per se. + + We can see that modification of the "ngx.ctx.blah" entry in the + subrequest does not affect the one in its parent request. They do have + two separate versions of "ngx.ctx.blah" per se. Internal redirection will destroy the original request's "ngx.ctx" data (if any) and the new request will have an emptied "ngx.ctx" table. For - instance, location /new { content_by_lua ' ngx.say(ngx.ctx.foo) '; } + instance, + + location /new { + content_by_lua ' + ngx.say(ngx.ctx.foo) + '; + } location /orig { content_by_lua ' @@ -871,16 +941,21 @@ Nginx API for Lua ngx.exec("/new") '; } - Then C will give you + + Then "GET /orig" will give you + nil - rather than the original C<"hello"> value. + + rather than the original "hello" value. Arbitrary data values can be inserted into this "matic" table, including Lua closures and nested tables. You can also register your own meta methods with it. Overriding "ngx.ctx" with a new Lua table is also supported, for - example, ngx.ctx = { foo = 32, bar = 54 } + example, + + ngx.ctx = { foo = 32, bar = 54 } ngx.location.capture syntax: *res = ngx.location.capture(uri, options?)* @@ -903,30 +978,46 @@ Nginx API for Lua Subrequests are completely different from HTTP 301/302 redirection (via ngx.redirect) and internal redirection (via ngx.exec). - Here's a basic example: res = ngx.location.capture(uri) Returns a Lua - table with three slots ("res.status", "res.header", and "res.body"). + Here's a basic example: + + res = ngx.location.capture(uri) + + Returns a Lua table with three slots ("res.status", "res.header", and + "res.body"). "res.header" holds all the response headers of the subrequest and it is a normal Lua table. For multi-value response headers, the value is a Lua (array) table that holds all the values in the order that they appear. For instance, if the subrequest response headers contains the following - lines: Set-Cookie: a=3 Set-Cookie: foo=bar Set-Cookie: baz=blah Then - "res.header["Set-Cookie"]" will be evaluted to the table value "{"a=3", - "foo=bar", "baz=blah"}". + lines: - URI query strings can be concatenated to URI itself, for instance, res = - ngx.location.capture('/foo/bar?a=3&b=4') Named locations like @foo are - not allowed due to a limitation in the nginx core. Use normal locations - combined with the "internal" directive to prepare internal-only - locations. + Set-Cookie: a=3 + Set-Cookie: foo=bar + Set-Cookie: baz=blah + + Then "res.header["Set-Cookie"]" will be evaluted to the table value + "{"a=3", "foo=bar", "baz=blah"}". + + URI query strings can be concatenated to URI itself, for instance, + + res = ngx.location.capture('/foo/bar?a=3&b=4') + + Named locations like @foo are not allowed due to a limitation in the + nginx core. Use normal locations combined with the "internal" directive + to prepare internal-only locations. An optional option table can be fed as the second argument, which support various options like "method", "body", "args", and "share_all_vars". Issuing a POST subrequest, for example, can be done as - follows res = ngx.location.capture( '/foo/bar', { method = - ngx.HTTP_POST, body = 'hello, world' } ) See HTTP method constants - methods other than POST. The "method" option is "ngx.HTTP_GET" by - default. + follows + + res = ngx.location.capture( + '/foo/bar', + { method = ngx.HTTP_POST, body = 'hello, world' } + ) + + See HTTP method constants methods other than POST. The "method" option + is "ngx.HTTP_GET" by default. The "share_all_vars" option can control whether to share nginx variables among the current request and the new subrequest. If this option is set @@ -937,16 +1028,27 @@ Nginx API for Lua special care. So, by default, the option is set to "false". The "args" option can specify extra url arguments, for instance, - ngx.location.capture('/foo?a=1', { args = { b = 3, c = ':' } } ) is - equivalent to ngx.location.capture('/foo?a=1&b=3&c=%3a') that is, this - method will automatically escape argument keys and values according to - URI rules and concatenating them together into a complete query string. - Because it's all done in hand-written C, it should be faster than your - own Lua code. + + ngx.location.capture('/foo?a=1', + { args = { b = 3, c = ':' } } + ) + + is equivalent to + + ngx.location.capture('/foo?a=1&b=3&c=%3a') + + that is, this method will automatically escape argument keys and values + according to URI rules and concatenating them together into a complete + query string. Because it's all done in hand-written C, it should be + faster than your own Lua code. The "args" option can also take plain query string: - ngx.location.capture('/foo?a=1', { args = 'b=3&c=%3a' } } ) This is - functionally identical to the previous examples. + + ngx.location.capture('/foo?a=1', + { args = 'b=3&c=%3a' } } + ) + + This is functionally identical to the previous examples. Note that, by default, subrequests issued by ngx.location.capture inherit all the request headers of the current request. This may have @@ -967,9 +1069,13 @@ Nginx API for Lua running in parallel. This function issue several parallel subrequests specified by the input - table, and returns their results in the same order. For example, res1, - res2, res3 = ngx.location.capture_multi{ { "/foo", { args = "a=3&b=4" } - }, { "/bar" }, { "/baz", { method = ngx.HTTP_POST, body = "hello" } }, } + table, and returns their results in the same order. For example, + + res1, res2, res3 = ngx.location.capture_multi{ + { "/foo", { args = "a=3&b=4" } }, + { "/bar" }, + { "/baz", { method = ngx.HTTP_POST, body = "hello" } }, + } if res1.status == ngx.HTTP_OK then ... @@ -978,14 +1084,20 @@ Nginx API for Lua if res2.body == "BLAH" then ... end - This function will not return until all the subrequests terminate. - The total latency is the longest latency of the subrequests, instead of their sum. + + This function will not return until all the subrequests terminate. The + total latency is the longest latency of the subrequests, instead of + their sum. When you don't know inadvance how many subrequests you want to issue, - you can use Lua tables for both requests and responses. For instance, -- - construct the requests table local reqs = {} table.insert(reqs, { - "/mysql" }) table.insert(reqs, { "/postgres" }) table.insert(reqs, { - "/redis" }) table.insert(reqs, { "/memcached" }) + you can use Lua tables for both requests and responses. For instance, + + -- construct the requests table + local reqs = {} + table.insert(reqs, { "/mysql" }) + table.insert(reqs, { "/postgres" }) + table.insert(reqs, { "/redis" }) + table.insert(reqs, { "/memcached" }) -- issue all the requests at once and wait until they all return local resps = { ngx.location.capture_multi(reqs) } @@ -994,8 +1106,11 @@ Nginx API for Lua for i, resp in ipairs(resps) do -- process the response table "resp" end - The L function is just a special form - of this function. Logically speaking, the L can be implemented like this + + The ngx.location.capture function is just a special form of this + function. Logically speaking, the ngx.location.capture can be + implemented like this + ngx.location.capture = function (uri, args) return ngx.location.capture_multi({ {uri, args} }) @@ -1006,8 +1121,10 @@ Nginx API for Lua header_filter_by_lua** Read and write the current request's response status. This should be - called before sending out the response headers. ngx.status = - ngx.HTTP_CREATED status = ngx.status + called before sending out the response headers. + + ngx.status = ngx.HTTP_CREATED + status = ngx.status ngx.header.HEADER syntax: *ngx.header.HEADER = VALUE* @@ -1212,28 +1329,62 @@ Nginx API for Lua Returns a Lua table holds all of the current request's request URL query arguments. - Here's an example, location = /test { content_by_lua ' local args = - ngx.req.get_uri_args() for key, val in pairs(args) do if type(val) == - "table" then ngx.say(key, ": ", table.concat(val, ", ")) else - ngx.say(key, ": ", val) end end '; } Then "GET - /test?foo=bar&bar=baz&bar=blah" will yield the response body foo: bar - bar: baz, blah Multiple occurrences of an argument key will result in a - table value holding all of the values for that key in order. + Here's an example, + + location = /test { + content_by_lua ' + local args = ngx.req.get_uri_args() + for key, val in pairs(args) do + if type(val) == "table" then + ngx.say(key, ": ", table.concat(val, ", ")) + else + ngx.say(key, ": ", val) + end + end + '; + } + + Then "GET /test?foo=bar&bar=baz&bar=blah" will yield the response body + + foo: bar + bar: baz, blah + + Multiple occurrences of an argument key will result in a table value + holding all of the values for that key in order. Keys and values will be automatically unescaped according to URI escaping rules. For example, in the above settings, "GET - /test?a%20b=1%61+2" will yield the output a b: 1a 2 Arguments without - the "=" parts are treated as boolean arguments. For example, "GET - /test?foo&bar" will yield the outputs foo: true bar: true That is, they - will take Lua boolean values "true". However, they're different from - arguments taking empty string values. For example, "GET /test?foo=&bar=" - will give something like foo: bar: Empty key arguments are discarded, - for instance, "GET /test?=hello&=world" will yield empty outputs. + /test?a%20b=1%61+2" will yield the output + + a b: 1a 2 + + Arguments without the "=" parts are treated as boolean arguments. + For example, "GET /test?foo&bar" will yield the outputs + + foo: true + bar: true + + That is, they will take Lua boolean values "true". However, they're + different from arguments taking empty string values. For example, "GET + /test?foo=&bar=" will give something like + + foo: + bar: + + Empty key arguments are discarded, for instance, "GET + /test?=hello&=world" will yield empty outputs. Updating query arguments via the nginx variable $args (or "ngx.var.args" - in Lua) at runtime are also supported: ngx.var.args = "a=3&b=42" local - args = ngx.req.get_uri_args() Here the "args" table will always look - like {a = 3, b = 42} regardless of the actual request query string. + in Lua) at runtime are also supported: + + ngx.var.args = "a=3&b=42" + local args = ngx.req.get_uri_args() + + Here the "args" table will always look like + + {a = 3, b = 42} + + regardless of the actual request query string. ngx.req.get_post_args syntax: *ngx.req.get_post_args()* @@ -1242,30 +1393,63 @@ Nginx API for Lua header_filter_by_lua** Returns a Lua table holds all of the current request's POST query - arguments. It's required to turn on the lua_need_request_body directive, - or a Lua exception will be thrown. + arguments. It's required to read the request body first by calling + ngx.req.read_body or to turn on the lua_need_request_body directive, or + a Lua exception will be thrown. - Here's an example, location = /test { lua_need_request_body on; - content_by_lua ' local args = ngx.req.get_post_args() for key, val in - pairs(args) do if type(val) == "table" then ngx.say(key, ": ", - table.concat(val, ", ")) else ngx.say(key, ": ", val) end end '; } Then - # Post request with the body 'foo=bar&bar=baz&bar=blah' $ curl --data - 'foo=bar&bar=baz&bar=blah' localhost/test will yield the response body - like foo: bar bar: baz, blah Multiple occurrences of an argument key - will result in a table value holding all of the values for that key in - order. + Here's an example, + + location = /test { + content_by_lua ' + ngx.req.read_body() + for key, val in pairs(args) do + if type(val) == "table" then + ngx.say(key, ": ", table.concat(val, ", ")) + else + ngx.say(key, ": ", val) + end + end + '; + } + + Then + + # Post request with the body 'foo=bar&bar=baz&bar=blah' + $ curl --data 'foo=bar&bar=baz&bar=blah' localhost/test + + will yield the response body like + + foo: bar + bar: baz, blah + + Multiple occurrences of an argument key will result in a table value + holding all of the values for that key in order. Keys and values will be automatically unescaped according to URI - escaping rules. For example, in the above settings, # POST request with - body 'a%20b=1%61+2' $ curl -d 'a%20b=1%61+2' localhost/test will yield - the output a b: 1a 2 Arguments without the "=" parts are treated - as boolean arguments. For example, "GET /test?foo&bar" will yield the - outputs foo: true bar: true That is, they will take Lua boolean values - "true". However, they're different from arguments taking empty string - values. For example, "POST /test" with request body "foo=&bar=" will - give something like foo: bar: Empty key arguments are discarded, for - instance, "POST /test" with body "=hello&=world" will yield empty - outputs. + escaping rules. For example, in the above settings, + + # POST request with body 'a%20b=1%61+2' + $ curl -d 'a%20b=1%61+2' localhost/test + + will yield the output + + a b: 1a 2 + + Arguments without the "=" parts are treated as boolean arguments. + For example, "GET /test?foo&bar" will yield the outputs + + foo: true + bar: true + + That is, they will take Lua boolean values "true". However, they're + different from arguments taking empty string values. For example, "POST + /test" with request body "foo=&bar=" will give something like + + foo: + bar: + + Empty key arguments are discarded, for instance, "POST /test" with body + "=hello&=world" will yield empty outputs. ngx.req.get_headers syntax: *headers = ngx.req.get_headers()* @@ -1275,14 +1459,30 @@ Nginx API for Lua Returns a Lua table holds all of the current request's request headers. - Here's an example, local h = ngx.req.get_headers() for k, v in pairs(h) - do ... end To read an individual header: ngx.say("Host: ", - ngx.req.get_headers()["Host"]) For multiple instances of request headers - like Foo: foo Foo: bar Foo: baz the value of - "ngx.req.get_headers()["Foo"]" will be a Lua (array) table like this: - {"foo", "bar", "baz"} Another way to read individual request headers is - to use "ngx.var.http_HEADER", that is, nginx's standard $http_HEADER - variables. + Here's an example, + + local h = ngx.req.get_headers() + for k, v in pairs(h) do + ... + end + + To read an individual header: + + ngx.say("Host: ", ngx.req.get_headers()["Host"]) + + For multiple instances of request headers like + + Foo: foo + Foo: bar + Foo: baz + + the value of "ngx.req.get_headers()["Foo"]" will be a Lua (array) table + like this: + + {"foo", "bar", "baz"} + + Another way to read individual request headers is to use + "ngx.var.http_HEADER", that is, nginx's standard $http_HEADER variables. ngx.req.set_header syntax: *ngx.req.set_header(header_name, header_value)* @@ -1295,14 +1495,28 @@ Nginx API for Lua request's subrequests will be affected. Here's an example of setting the "Content-Length" header: - ngx.req.set_header("Content-Type", "text/css") The "header_value" can - take an array list of values, for example, ngx.req.set_header("Foo", - {"a", "abc"}) will produce two new request headers: Foo: a Foo: abc and - old "Foo" headers will be overridden if there's any. + + ngx.req.set_header("Content-Type", "text/css") + + The "header_value" can take an array list of values, for example, + + ngx.req.set_header("Foo", {"a", "abc"}) + + will produce two new request headers: + + Foo: a + Foo: abc + + and old "Foo" headers will be overridden if there's any. When the "header_value" argument is "nil", the request header will be - removed. So ngx.req.set_header("X-Foo", nil) is equivalent to - ngx.req.clear_header("X-Foo") + removed. So + + ngx.req.set_header("X-Foo", nil) + + is equivalent to + + ngx.req.clear_header("X-Foo") ngx.req.read_body syntax: *ngx.req.read_body()* @@ -1337,6 +1551,11 @@ Nginx API for Lua ngx.req.discard_body function to explicitly discard the request body, or you'll break HTTP 1.1 keepalive and HTTP 1.1 pipelining. + Here's a small example: + + ngx.req.read_body() + local args = ngx.req.get_post_args() + This function was first introduced in the "v0.3.1rc17" release. ngx.req.discard_body @@ -1841,8 +2060,10 @@ Nginx API for Lua Returns a formated string can be used as the cookie expiration time. The parameter "sec" is the timestamp in seconds (like those returned from - ngx.time). ngx.say(ngx.cookie_time(1290079655)) -- yields "Thu, - 18-Nov-10 11:27:35 GMT" + ngx.time). + + ngx.say(ngx.cookie_time(1290079655)) + -- yields "Thu, 18-Nov-10 11:27:35 GMT" ngx.http_time syntax: *str = ngx.http_time(sec)* @@ -1853,8 +2074,9 @@ Nginx API for Lua Returns a formated string can be used as the http header time (for example, being used in "Last-Modified" header). The parameter "sec" is the timestamp in seconds (like those returned from ngx.time). - ngx.say(ngx.http_time(1290079655)) -- yields "Thu, 18 Nov 10 11:27:35 - GMT" + + ngx.say(ngx.http_time(1290079655)) + -- yields "Thu, 18 Nov 10 11:27:35 GMT" ngx.parse_http_time syntax: *sec = ngx.parse_http_time(str)* @@ -1863,9 +2085,12 @@ Nginx API for Lua header_filter_by_lua** Parse the http time string (as returned by ngx.http_time) into seconds. - Returns the seconds or "nil" if the input string is in bad forms. local - time = ngx.parse_http_time("Thu, 18 Nov 10 11:27:35 GMT") if time == nil - then ... end + Returns the seconds or "nil" if the input string is in bad forms. + + local time = ngx.parse_http_time("Thu, 18 Nov 10 11:27:35 GMT") + if time == nil then + ... + end ngx.is_subrequest syntax: *value = ngx.is_subrequest* @@ -2280,9 +2505,9 @@ Installation example, the version 1.0.5 (see nginx compatibility), and then build the source with this module: - $ wget 'http://nginx.org/download/nginx-1.0.5.tar.gz' - $ tar -xzvf nginx-1.0.5.tar.gz - $ cd nginx-1.0.5/ + wget 'http://nginx.org/download/nginx-1.0.5.tar.gz' + tar -xzvf nginx-1.0.5.tar.gz + cd nginx-1.0.5/ # tell nginx's build system where to find lua: export LUA_LIB=/path/to/lua/lib @@ -2293,12 +2518,12 @@ Installation # export LUAJIT_INC=/path/to/luajit/include/luajit-2.0 # Here we assume you would install you nginx under /opt/nginx/. - $ ./configure --prefix=/opt/nginx \ + ./configure --prefix=/opt/nginx \ --add-module=/path/to/ngx_devel_kit \ --add-module=/path/to/lua-nginx-module - $ make -j2 - $ make install + make -j2 + make install Compatibility The following versions of Nginx should work with this module: diff --git a/README.markdown b/README.markdown index a2429d4c..9da8e5bb 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc18](https://github.com/chaoslawful/lua-nginx-module/tags) released on 21 October 2011. +This document describes ngx_lua [v0.3.1rc19](https://github.com/chaoslawful/lua-nginx-module/tags) released on 22 October 2011. Synopsis ======== @@ -310,6 +310,7 @@ Note that [set_by_lua](http://wiki.nginx.org/HttpLuaModule#set_by_lua) can only a time. But a work-around is also available by means of the [ngx.var.VARIABLE](http://wiki.nginx.org/HttpLuaModule#ngx.var.VARIABLE) interface, for example, + location /foo { set $diff ''; # we have to predefine the $diff variable here @@ -327,6 +328,7 @@ for example, This directive can be freely mixed with all the directives of [HttpRewriteModule](http://wiki.nginx.org/HttpRewriteModule), [HttpSetMiscModule](http://wiki.nginx.org/HttpSetMiscModule), and [HttpArrayVarModule](http://wiki.nginx.org/HttpArrayVarModule). All of these directives will run in exactly the same order that they are written in the config file. For example, + set $foo 32; set_by_lua $bar 'tonumber(ngx.var.foo) + 1'; set $baz "bar: $bar"; # $baz == "bar: 33" @@ -633,6 +635,7 @@ Use Lua defined in `` to define an output header filter. For now Here's a small example of overriding a response header (or adding if it does not exist) in our Lua header filter: + location / { proxy_pass http://mybackend; header_filter_by_lua 'ngx.header.Foo = "blah"'; @@ -757,6 +760,7 @@ ngx.var.VARIABLE Note that you can only write to nginx variables that are already defined. For example: + location /foo { set $my_var ''; # this line is required to create $my_var at config time content_by_lua ' @@ -765,6 +769,7 @@ For example: '; } + That is, nginx variables cannot be created on-the-fly. Some special nginx variables like `$args` and `$limit_rate` can be assigned a value, @@ -783,6 +788,7 @@ Core constants ngx.AGAIN (-2) ngx.DONE (-4) + They take the same values of `NGX_OK`, `NGX_AGAIN`, `NGX_DONE`, `NGX_ERROR`, and etc. But now only [ngx.exit](http://wiki.nginx.org/HttpLuaModule#ngx.exit) only take two of these values, i.e., `NGX_OK` and `NGX_ERROR`. @@ -804,6 +810,7 @@ HTTP status constants --------------------- **context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua** + value = ngx.HTTP_OK (200) value = ngx.HTTP_CREATED (201) value = ngx.HTTP_SPECIAL_RESPONSE (300) @@ -826,6 +833,7 @@ Nginx log level constants ------------------------- **context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua** + ngx.STDERR ngx.EMERG ngx.ALERT @@ -849,8 +857,10 @@ Emit args concatenated to nginx's `error.log` file, with log level `ngx.NOTICE` It's equivalent to + ngx.log(ngx.NOTICE, 'lua print: ', a, b, ...) + Lua `nil` arguments are accepted and result in literal `"nil"`, and Lua booleans result in `"true"` or `"false"`. ngx.ctx @@ -861,6 +871,7 @@ This table can be used to store per-request context data for Lua programmers. This table has a liftime identical to the current request (just like Nginx variables). Consider the following example, + location /test { rewrite_by_lua ' ngx.say("foo = ", ngx.ctx.foo) @@ -874,15 +885,19 @@ This table has a liftime identical to the current request (just like Nginx varia '; } + Then `GET /test` will yield the output + foo = nil 79 + That is, the `ngx.ctx.foo` entry persists across the rewrite, access, and content phases of a request. Also, every request has its own copy, include subrequests, for example: + location /sub { content_by_lua ' ngx.say("sub pre: ", ngx.ctx.blah) @@ -901,17 +916,21 @@ Also, every request has its own copy, include subrequests, for example: '; } + Then `GET /main` will give the output + main pre: 73 sub pre: nil sub post: 32 main post: 73 + We can see that modification of the `ngx.ctx.blah` entry in the subrequest does not affect the one in its parent request. They do have two separate versions of `ngx.ctx.blah` per se. Internal redirection will destroy the original request's `ngx.ctx` data (if any) and the new request will have an emptied `ngx.ctx` table. For instance, + location /new { content_by_lua ' ngx.say(ngx.ctx.foo) @@ -925,16 +944,20 @@ Internal redirection will destroy the original request's `ngx.ctx` data (if any) '; } + Then `GET /orig` will give you + nil + rather than the original `"hello"` value. Arbitrary data values can be inserted into this "matic" table, including Lua closures and nested tables. You can also register your own meta methods with it. Overriding `ngx.ctx` with a new Lua table is also supported, for example, + ngx.ctx = { foo = 32, bar = 54 } @@ -955,8 +978,10 @@ Subrequests are completely different from HTTP 301/302 redirection (via [ngx.red Here's a basic example: + res = ngx.location.capture(uri) + Returns a Lua table with three slots (`res.status`, `res.header`, and `res.body`). `res.header` holds all the response headers of the @@ -965,17 +990,21 @@ the value is a Lua (array) table that holds all the values in the order that they appear. For instance, if the subrequest response headers contains the following lines: + Set-Cookie: a=3 Set-Cookie: foo=bar Set-Cookie: baz=blah + Then `res.header["Set-Cookie"]` will be evaluted to the table value `{"a=3", "foo=bar", "baz=blah"}`. URI query strings can be concatenated to URI itself, for instance, + res = ngx.location.capture('/foo/bar?a=3&b=4') + Named locations like `@foo` are not allowed due to a limitation in the nginx core. Use normal locations combined with the `internal` directive to prepare internal-only locations. @@ -986,11 +1015,13 @@ argument, which support various options like Issuing a POST subrequest, for example, can be done as follows + res = ngx.location.capture( '/foo/bar', { method = ngx.HTTP_POST, body = 'hello, world' } ) + See HTTP method constants methods other than POST. The `method` option is `ngx.HTTP_GET` by default. @@ -1004,24 +1035,30 @@ care. So, by default, the option is set to `false`. The `args` option can specify extra url arguments, for instance, + ngx.location.capture('/foo?a=1', { args = { b = 3, c = ':' } } ) + is equivalent to + ngx.location.capture('/foo?a=1&b=3&c=%3a') + that is, this method will automatically escape argument keys and values according to URI rules and concatenating them together into a complete query string. Because it's all done in hand-written C, it should be faster than your own Lua code. The `args` option can also take plain query string: + ngx.location.capture('/foo?a=1', { args = 'b=3&c=%3a' } } ) + This is functionally identical to the previous examples. Note that, by default, subrequests issued by [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) inherit all the @@ -1041,6 +1078,7 @@ Just like [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.locatio This function issue several parallel subrequests specified by the input table, and returns their results in the same order. For example, + res1, res2, res3 = ngx.location.capture_multi{ { "/foo", { args = "a=3&b=4" } }, { "/bar" }, @@ -1055,12 +1093,14 @@ This function issue several parallel subrequests specified by the input table, a ... end + This function will not return until all the subrequests terminate. The total latency is the longest latency of the subrequests, instead of their sum. When you don't know inadvance how many subrequests you want to issue, you can use Lua tables for both requests and responses. For instance, + -- construct the requests table local reqs = {} table.insert(reqs, { "/mysql" }) @@ -1076,9 +1116,11 @@ you can use Lua tables for both requests and responses. For instance, -- process the response table "resp" end + The [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) function is just a special form of this function. Logically speaking, the [ngx.location.capture](http://wiki.nginx.org/HttpLuaModule#ngx.location.capture) can be implemented like this + ngx.location.capture = function (uri, args) return ngx.location.capture_multi({ {uri, args} }) @@ -1092,6 +1134,7 @@ ngx.status Read and write the current request's response status. This should be called before sending out the response headers. + ngx.status = ngx.HTTP_CREATED status = ngx.status @@ -1313,6 +1356,7 @@ Returns a Lua table holds all of the current request's request URL query argumen Here's an example, + location = /test { content_by_lua ' local args = ngx.req.get_uri_args() @@ -1326,38 +1370,51 @@ Here's an example, '; } + Then `GET /test?foo=bar&bar=baz&bar=blah` will yield the response body + foo: bar bar: baz, blah + Multiple occurrences of an argument key will result in a table value holding all of the values for that key in order. Keys and values will be automatically unescaped according to URI escaping rules. For example, in the above settings, `GET /test?a%20b=1%61+2` will yield the output + a b: 1a 2 + Arguments without the `=` parts are treated as boolean arguments. For example, `GET /test?foo&bar` will yield the outputs + foo: true bar: true + That is, they will take Lua boolean values `true`. However, they're different from arguments taking empty string values. For example, `GET /test?foo=&bar=` will give something like + foo: bar: + Empty key arguments are discarded, for instance, `GET /test?=hello&=world` will yield empty outputs. Updating query arguments via the nginx variable `$args` (or `ngx.var.args` in Lua) at runtime are also supported: + ngx.var.args = "a=3&b=42" local args = ngx.req.get_uri_args() + Here the `args` table will always look like + {a = 3, b = 42} + regardless of the actual request query string. ngx.req.get_post_args @@ -1366,14 +1423,14 @@ ngx.req.get_post_args **context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua** -Returns a Lua table holds all of the current request's POST query arguments. It's required to turn on the [lua_need_request_body](http://wiki.nginx.org/HttpLuaModule#lua_need_request_body) directive, or a Lua exception will be thrown. +Returns a Lua table holds all of the current request's POST query arguments. It's required to read the request body first by calling [ngx.req.read_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body) or to turn on the [lua_need_request_body](http://wiki.nginx.org/HttpLuaModule#lua_need_request_body) directive, or a Lua exception will be thrown. Here's an example, + location = /test { - lua_need_request_body on; content_by_lua ' - local args = ngx.req.get_post_args() + ngx.req.read_body() for key, val in pairs(args) do if type(val) == "table" then ngx.say(key, ": ", table.concat(val, ", ")) @@ -1384,37 +1441,50 @@ Here's an example, '; } + Then + # Post request with the body 'foo=bar&bar=baz&bar=blah' $ curl --data 'foo=bar&bar=baz&bar=blah' localhost/test + will yield the response body like + foo: bar bar: baz, blah + Multiple occurrences of an argument key will result in a table value holding all of the values for that key in order. Keys and values will be automatically unescaped according to URI escaping rules. For example, in the above settings, + # POST request with body 'a%20b=1%61+2' $ curl -d 'a%20b=1%61+2' localhost/test + will yield the output + a b: 1a 2 + Arguments without the `=` parts are treated as boolean arguments. For example, `GET /test?foo&bar` will yield the outputs + foo: true bar: true + That is, they will take Lua boolean values `true`. However, they're different from arguments taking empty string values. For example, `POST /test` with request body `foo=&bar=` will give something like + foo: bar: + Empty key arguments are discarded, for instance, `POST /test` with body `=hello&=world` will yield empty outputs. ngx.req.get_headers @@ -1427,25 +1497,33 @@ Returns a Lua table holds all of the current request's request headers. Here's an example, + local h = ngx.req.get_headers() for k, v in pairs(h) do ... end + To read an individual header: + ngx.say("Host: ", ngx.req.get_headers()["Host"]) + For multiple instances of request headers like + Foo: foo Foo: bar Foo: baz + the value of `ngx.req.get_headers()["Foo"]` will be a Lua (array) table like this: + {"foo", "bar", "baz"} + Another way to read individual request headers is to use `ngx.var.http_HEADER`, that is, nginx's standard [$http_HEADER](http://wiki.nginx.org/HttpCoreModule#.24http_HEADER) variables. ngx.req.set_header @@ -1459,26 +1537,35 @@ None of the current request's subrequests will be affected. Here's an example of setting the `Content-Length` header: + ngx.req.set_header("Content-Type", "text/css") + The `header_value` can take an array list of values, for example, + ngx.req.set_header("Foo", {"a", "abc"}) + will produce two new request headers: + Foo: a Foo: abc + and old `Foo` headers will be overridden if there's any. When the `header_value` argument is `nil`, the request header will be removed. So + ngx.req.set_header("X-Foo", nil) + is equivalent to + ngx.req.clear_header("X-Foo") @@ -1503,6 +1590,13 @@ You can later either retrieve the request body data via [ngx.req.get_body_data]( In case that you do not want to read the request body and the current request may have a request body, then it's crucial to use the [ngx.req.discard_body](http://wiki.nginx.org/HttpLuaModule#ngx.req.discard_body) function to explicitly discard the request body, or you'll break HTTP 1.1 keepalive and HTTP 1.1 pipelining. +Here's a small example: + + + ngx.req.read_body() + local args = ngx.req.get_post_args() + + This function was first introduced in the `v0.3.1rc17` release. ngx.req.discard_body @@ -1962,9 +2056,11 @@ ngx.cookie_time Returns a formated string can be used as the cookie expiration time. The parameter `sec` is the timestamp in seconds (like those returned from [ngx.time](http://wiki.nginx.org/HttpLuaModule#ngx.time)). + ngx.say(ngx.cookie_time(1290079655)) -- yields "Thu, 18-Nov-10 11:27:35 GMT" + ngx.http_time ------------- **syntax:** *str = ngx.http_time(sec)* @@ -1973,9 +2069,11 @@ ngx.http_time Returns a formated string can be used as the http header time (for example, being used in `Last-Modified` header). The parameter `sec` is the timestamp in seconds (like those returned from [ngx.time](http://wiki.nginx.org/HttpLuaModule#ngx.time)). + ngx.say(ngx.http_time(1290079655)) -- yields "Thu, 18 Nov 10 11:27:35 GMT" + ngx.parse_http_time ------------------- **syntax:** *sec = ngx.parse_http_time(str)* @@ -1984,11 +2082,13 @@ ngx.parse_http_time Parse the http time string (as returned by [ngx.http_time](http://wiki.nginx.org/HttpLuaModule#ngx.http_time)) into seconds. Returns the seconds or `nil` if the input string is in bad forms. + local time = ngx.parse_http_time("Thu, 18 Nov 10 11:27:35 GMT") if time == nil then ... end + ngx.is_subrequest ----------------- **syntax:** *value = ngx.is_subrequest* @@ -2354,9 +2454,10 @@ Alternatively, you can compile this module with nginx core's source by hand: 1. Download the latest version of the release tarball of this module from lua-nginx-module [file list](http://github.com/chaoslawful/lua-nginx-module/tags). 1. Grab the nginx source code from [nginx.org](http://nginx.org/), for example, the version 1.0.5 (see nginx compatibility), and then build the source with this module: - $ wget 'http://nginx.org/download/nginx-1.0.5.tar.gz' - $ tar -xzvf nginx-1.0.5.tar.gz - $ cd nginx-1.0.5/ + + wget 'http://nginx.org/download/nginx-1.0.5.tar.gz' + tar -xzvf nginx-1.0.5.tar.gz + cd nginx-1.0.5/ # tell nginx's build system where to find lua: export LUA_LIB=/path/to/lua/lib @@ -2367,12 +2468,12 @@ Alternatively, you can compile this module with nginx core's source by hand: # export LUAJIT_INC=/path/to/luajit/include/luajit-2.0 # Here we assume you would install you nginx under /opt/nginx/. - $ ./configure --prefix=/opt/nginx \ + ./configure --prefix=/opt/nginx \ --add-module=/path/to/ngx_devel_kit \ --add-module=/path/to/lua-nginx-module - $ make -j2 - $ make install + make -j2 + make install Compatibility diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 0b466652..40f6bb26 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc18] released on 21 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc19] released on 22 October 2011. = Synopsis = @@ -298,6 +298,7 @@ anything that may be blocked or time-consuming. Note that [[#set_by_lua|set_by_lua]] can only output a value to a single Nginx variable at a time. But a work-around is also available by means of the [[#ngx.var.VARIABLE|ngx.var.VARIABLE]] interface, for example, + location /foo { set $diff ''; # we have to predefine the $diff variable here @@ -315,6 +316,7 @@ for example, This directive can be freely mixed with all the directives of [[HttpRewriteModule]], [[HttpSetMiscModule]], and [[HttpArrayVarModule]]. All of these directives will run in exactly the same order that they are written in the config file. For example, + set $foo 32; set_by_lua $bar 'tonumber(ngx.var.foo) + 1'; @@ -613,6 +615,7 @@ Use Lua defined in to define an output header filt * Subrequest APIs (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]]) Here's a small example of overriding a response header (or adding if it does not exist) in our Lua header filter: + location / { proxy_pass http://mybackend; @@ -732,6 +735,7 @@ that outputs 88, the sum of 32 and 56. Note that you can only write to nginx variables that are already defined. For example: + location /foo { set $my_var ''; # this line is required to create $my_var at config time @@ -741,6 +745,7 @@ For example: '; } + That is, nginx variables cannot be created on-the-fly. Some special nginx variables like $args and $limit_rate can be assigned a value, @@ -758,6 +763,7 @@ interface as well, by writing ngx.var[1], ngx.var[2], ngx.AGAIN (-2) ngx.DONE (-4) + They take the same values of NGX_OK, NGX_AGAIN, NGX_DONE, NGX_ERROR, and etc. But now only [[#ngx.exit|ngx.exit]] only take two of these values, i.e., NGX_OK and NGX_ERROR. @@ -776,6 +782,7 @@ These constants are usually used in [[#ngx.location.capture|ngx.location.catpure == HTTP status constants == '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' + value = ngx.HTTP_OK (200) value = ngx.HTTP_CREATED (201) @@ -797,6 +804,7 @@ These constants are usually used in [[#ngx.location.capture|ngx.location.catpure == Nginx log level constants == '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' + ngx.STDERR ngx.EMERG @@ -819,9 +827,11 @@ These constants are usually used by the [[#ngx.log|ngx.log]] method. Emit args concatenated to nginx's error.log file, with log level ngx.NOTICE and prefix lua print: . It's equivalent to + ngx.log(ngx.NOTICE, 'lua print: ', a, b, ...) + Lua nil arguments are accepted and result in literal "nil", and Lua booleans result in "true" or "false". == ngx.ctx == @@ -830,6 +840,7 @@ Lua nil arguments are accepted and result in literal "nil" location /test { rewrite_by_lua ' @@ -844,14 +855,18 @@ This table has a liftime identical to the current request (just like Nginx varia '; } + Then GET /test will yield the output + foo = nil 79 + That is, the ngx.ctx.foo entry persists across the rewrite, access, and content phases of a request. Also, every request has its own copy, include subrequests, for example: + location /sub { content_by_lua ' @@ -871,16 +886,20 @@ Also, every request has its own copy, include subrequests, for example: '; } + Then GET /main will give the output + main pre: 73 sub pre: nil sub post: 32 main post: 73 + We can see that modification of the ngx.ctx.blah entry in the subrequest does not affect the one in its parent request. They do have two separate versions of ngx.ctx.blah per se. Internal redirection will destroy the original request's ngx.ctx data (if any) and the new request will have an emptied ngx.ctx table. For instance, + location /new { content_by_lua ' @@ -895,15 +914,19 @@ Internal redirection will destroy the original request's ngx.ctx da '; } + Then GET /orig will give you + nil + rather than the original "hello" value. Arbitrary data values can be inserted into this "matic" table, including Lua closures and nested tables. You can also register your own meta methods with it. Overriding ngx.ctx with a new Lua table is also supported, for example, + ngx.ctx = { foo = 32, bar = 54 } @@ -923,9 +946,11 @@ Also note that subrequests just mimic the HTTP interface but there's ''no'' extr Subrequests are completely different from HTTP 301/302 redirection (via [[#ngx.redirect|ngx.redirect]]) and internal redirection (via [[#ngx.exec|ngx.exec]]). Here's a basic example: + res = ngx.location.capture(uri) + Returns a Lua table with three slots (res.status, res.header, and res.body). res.header holds all the response headers of the @@ -933,18 +958,22 @@ subrequest and it is a normal Lua table. For multi-value response headers, the value is a Lua (array) table that holds all the values in the order that they appear. For instance, if the subrequest response headers contains the following lines: + Set-Cookie: a=3 Set-Cookie: foo=bar Set-Cookie: baz=blah + Then res.header["Set-Cookie"] will be evaluted to the table value {"a=3", "foo=bar", "baz=blah"}. URI query strings can be concatenated to URI itself, for instance, + res = ngx.location.capture('/foo/bar?a=3&b=4') + Named locations like @foo are not allowed due to a limitation in the nginx core. Use normal locations combined with the internal directive to prepare internal-only locations. @@ -954,12 +983,14 @@ argument, which support various options like method, body, args, and share_all_vars. Issuing a POST subrequest, for example, can be done as follows + res = ngx.location.capture( '/foo/bar', { method = ngx.HTTP_POST, body = 'hello, world' } ) + See HTTP method constants methods other than POST. The method option is ngx.HTTP_GET by default. @@ -972,25 +1003,31 @@ and lead to confusing issues, use it with special care. So, by default, the option is set to false. The args option can specify extra url arguments, for instance, + ngx.location.capture('/foo?a=1', { args = { b = 3, c = ':' } } ) + is equivalent to + ngx.location.capture('/foo?a=1&b=3&c=%3a') + that is, this method will automatically escape argument keys and values according to URI rules and concatenating them together into a complete query string. Because it's all done in hand-written C, it should be faster than your own Lua code. The args option can also take plain query string: + ngx.location.capture('/foo?a=1', { args = 'b=3&c=%3a' } } ) + This is functionally identical to the previous examples. Note that, by default, subrequests issued by [[#ngx.location.capture|ngx.location.capture]] inherit all the @@ -1008,6 +1045,7 @@ in gzip'd responses that your Lua code is not able to handle properly. So always Just like [[#ngx.location.capture|ngx.location.capture]], but supports multiple subrequests running in parallel. This function issue several parallel subrequests specified by the input table, and returns their results in the same order. For example, + res1, res2, res3 = ngx.location.capture_multi{ { "/foo", { args = "a=3&b=4" } }, @@ -1023,11 +1061,13 @@ This function issue several parallel subrequests specified by the input table, a ... end + This function will not return until all the subrequests terminate. The total latency is the longest latency of the subrequests, instead of their sum. When you don't know inadvance how many subrequests you want to issue, you can use Lua tables for both requests and responses. For instance, + -- construct the requests table local reqs = {} @@ -1044,8 +1084,10 @@ you can use Lua tables for both requests and responses. For instance, -- process the response table "resp" end + The [[#ngx.location.capture|ngx.location.capture]] function is just a special form of this function. Logically speaking, the [[#ngx.location.capture|ngx.location.capture]] can be implemented like this + ngx.location.capture = function (uri, args) @@ -1058,6 +1100,7 @@ of this function. Logically speaking, the [[#ngx.location.capture|ngx.location.c Read and write the current request's response status. This should be called before sending out the response headers. + ngx.status = ngx.HTTP_CREATED status = ngx.status @@ -1275,6 +1318,7 @@ See also [[#ngx.req.set_uri|ngx.req.set_uri]]. Returns a Lua table holds all of the current request's request URL query arguments. Here's an example, + location = /test { content_by_lua ' @@ -1289,38 +1333,51 @@ Here's an example, '; } + Then GET /test?foo=bar&bar=baz&bar=blah will yield the response body + foo: bar bar: baz, blah + Multiple occurrences of an argument key will result in a table value holding all of the values for that key in order. Keys and values will be automatically unescaped according to URI escaping rules. For example, in the above settings, GET /test?a%20b=1%61+2 will yield the output + a b: 1a 2 + Arguments without the = parts are treated as boolean arguments. For example, GET /test?foo&bar will yield the outputs + foo: true bar: true + That is, they will take Lua boolean values true. However, they're different from arguments taking empty string values. For example, GET /test?foo=&bar= will give something like + foo: bar: + Empty key arguments are discarded, for instance, GET /test?=hello&=world will yield empty outputs. Updating query arguments via the nginx variable $args (or ngx.var.args in Lua) at runtime are also supported: + ngx.var.args = "a=3&b=42" local args = ngx.req.get_uri_args() + Here the args table will always look like + {a = 3, b = 42} + regardless of the actual request query string. == ngx.req.get_post_args == @@ -1328,14 +1385,14 @@ regardless of the actual request query string. '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' -Returns a Lua table holds all of the current request's POST query arguments. It's required to turn on the [[#lua_need_request_body|lua_need_request_body]] directive, or a Lua exception will be thrown. +Returns a Lua table holds all of the current request's POST query arguments. It's required to read the request body first by calling [[#ngx.req.read_body|ngx.req.read_body]] or to turn on the [[#lua_need_request_body|lua_need_request_body]] directive, or a Lua exception will be thrown. Here's an example, + location = /test { - lua_need_request_body on; content_by_lua ' - local args = ngx.req.get_post_args() + ngx.req.read_body() for key, val in pairs(args) do if type(val) == "table" then ngx.say(key, ": ", table.concat(val, ", ")) @@ -1346,37 +1403,50 @@ Here's an example, '; } + Then + # Post request with the body 'foo=bar&bar=baz&bar=blah' $ curl --data 'foo=bar&bar=baz&bar=blah' localhost/test + will yield the response body like + foo: bar bar: baz, blah + Multiple occurrences of an argument key will result in a table value holding all of the values for that key in order. Keys and values will be automatically unescaped according to URI escaping rules. For example, in the above settings, + # POST request with body 'a%20b=1%61+2' $ curl -d 'a%20b=1%61+2' localhost/test + will yield the output + a b: 1a 2 + Arguments without the = parts are treated as boolean arguments. For example, GET /test?foo&bar will yield the outputs + foo: true bar: true + That is, they will take Lua boolean values true. However, they're different from arguments taking empty string values. For example, POST /test with request body foo=&bar= will give something like + foo: bar: + Empty key arguments are discarded, for instance, POST /test with body =hello&=world will yield empty outputs. == ngx.req.get_headers == @@ -1387,26 +1457,34 @@ Empty key arguments are discarded, for instance, POST /test with bo Returns a Lua table holds all of the current request's request headers. Here's an example, + local h = ngx.req.get_headers() for k, v in pairs(h) do ... end + To read an individual header: + ngx.say("Host: ", ngx.req.get_headers()["Host"]) + For multiple instances of request headers like + Foo: foo Foo: bar Foo: baz + the value of ngx.req.get_headers()["Foo"] will be a Lua (array) table like this: + {"foo", "bar", "baz"} + Another way to read individual request headers is to use ngx.var.http_HEADER, that is, nginx's standard [[HttpCoreModule#$http_HEADER|$http_HEADER]] variables. == ngx.req.set_header == @@ -1418,26 +1496,35 @@ Set the current request's request header named header_name to value None of the current request's subrequests will be affected. Here's an example of setting the Content-Length header: + ngx.req.set_header("Content-Type", "text/css") + The header_value can take an array list of values, for example, + ngx.req.set_header("Foo", {"a", "abc"}) + will produce two new request headers: + Foo: a Foo: abc + and old Foo headers will be overridden if there's any. When the header_value argument is nil, the request header will be removed. So + ngx.req.set_header("X-Foo", nil) + is equivalent to + ngx.req.clear_header("X-Foo") @@ -1462,6 +1549,13 @@ You can later either retrieve the request body data via [[#ngx.req.get_body_data In case that you do not want to read the request body and the current request may have a request body, then it's crucial to use the [[#ngx.req.discard_body|ngx.req.discard_body]] function to explicitly discard the request body, or you'll break HTTP 1.1 keepalive and HTTP 1.1 pipelining. +Here's a small example: + + + ngx.req.read_body() + local args = ngx.req.get_post_args() + + This function was first introduced in the v0.3.1rc17 release. == ngx.req.discard_body == @@ -1893,32 +1987,38 @@ This is the UTC time. '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' Returns a formated string can be used as the cookie expiration time. The parameter sec is the timestamp in seconds (like those returned from [[#ngx.time|ngx.time]]). + ngx.say(ngx.cookie_time(1290079655)) -- yields "Thu, 18-Nov-10 11:27:35 GMT" + == ngx.http_time == '''syntax:''' ''str = ngx.http_time(sec)'' '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' Returns a formated string can be used as the http header time (for example, being used in Last-Modified header). The parameter sec is the timestamp in seconds (like those returned from [[#ngx.time|ngx.time]]). + ngx.say(ngx.http_time(1290079655)) -- yields "Thu, 18 Nov 10 11:27:35 GMT" + == ngx.parse_http_time == '''syntax:''' ''sec = ngx.parse_http_time(str)'' '''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*'' Parse the http time string (as returned by [[#ngx.http_time|ngx.http_time]]) into seconds. Returns the seconds or nil if the input string is in bad forms. + local time = ngx.parse_http_time("Thu, 18 Nov 10 11:27:35 GMT") if time == nil then ... end + == ngx.is_subrequest == '''syntax:''' ''value = ngx.is_subrequest'' @@ -2273,10 +2373,11 @@ Alternatively, you can compile this module with nginx core's source by hand: # Download the latest version of the release tarball of the ngx_devel_kit (NDK) module from lua-nginx-module [http://github.com/simpl/ngx_devel_kit/tags file list]. # Download the latest version of the release tarball of this module from lua-nginx-module [http://github.com/chaoslawful/lua-nginx-module/tags file list]. # Grab the nginx source code from [http://nginx.org/ nginx.org], for example, the version 1.0.5 (see nginx compatibility), and then build the source with this module: + - $ wget 'http://nginx.org/download/nginx-1.0.5.tar.gz' - $ tar -xzvf nginx-1.0.5.tar.gz - $ cd nginx-1.0.5/ + wget 'http://nginx.org/download/nginx-1.0.5.tar.gz' + tar -xzvf nginx-1.0.5.tar.gz + cd nginx-1.0.5/ # tell nginx's build system where to find lua: export LUA_LIB=/path/to/lua/lib @@ -2287,12 +2388,12 @@ Alternatively, you can compile this module with nginx core's source by hand: # export LUAJIT_INC=/path/to/luajit/include/luajit-2.0 # Here we assume you would install you nginx under /opt/nginx/. - $ ./configure --prefix=/opt/nginx \ + ./configure --prefix=/opt/nginx \ --add-module=/path/to/ngx_devel_kit \ --add-module=/path/to/lua-nginx-module - $ make -j2 - $ make install + make -j2 + make install = Compatibility = diff --git a/src/ngx_http_lua_capturefilter.c b/src/ngx_http_lua_capturefilter.c index 19c41f4b..04817969 100644 --- a/src/ngx_http_lua_capturefilter.c +++ b/src/ngx_http_lua_capturefilter.c @@ -121,7 +121,8 @@ ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua capture body filter capturing response body, uri \"%V\"", &r->uri); + "lua capture body filter capturing response body, uri \"%V\"", + &r->uri); rc = ngx_http_lua_add_copy_chain(r, pr_ctx, &ctx->body, in); From a79cc7500756b1e7c7b304bc6c34b8e7947ce944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Mon, 24 Oct 2011 10:40:26 +0800 Subject: [PATCH 42/46] added a small test case. --- t/044-req-body.t | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/t/044-req-body.t b/t/044-req-body.t index 8f5a8f32..9c22010c 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -692,3 +692,36 @@ hello, world"] --- response_body eval ["body: [nil]\n","body: [nil]\n"] + + +=== TEST 28: read and set body +--- config + location /test { + lua_need_request_body on; + access_by_lua_file html/myscript.lua; + echo_request_body; + } +--- user_files +>>> myscript.lua + local data, data2 = ngx.req.get_post_args(), {} + for k, v in pairs(data) do + if type(v) == "table" then + for i, val in ipairs(v) do + local s = ngx.escape_uri(string.upper(k)) .. '=' + .. ngx.escape_uri(string.upper(val)) + table.insert(data2, s) + end + else + local s = ngx.escape_uri(string.upper(k)) .. '=' + .. ngx.escape_uri(string.upper(v)) + table.insert(data2, s) + end + end + ngx.req.set_body_data(table.concat(data2, "&")) +--- request +POST /test +a=1&a=2&b=hello&c=world +--- response_body +B=HELLO&A=1&A=2&C=WORLD +--- SKIP + From 61b9d83fe52e34d85fa41f49e5721f6fc4e14479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Mon, 24 Oct 2011 11:23:24 +0800 Subject: [PATCH 43/46] now we allow setting ngx.var.VARIABLE to nil. --- src/ngx_http_lua_variable.c | 78 +++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/src/ngx_http_lua_variable.c b/src/ngx_http_lua_variable.c index c771649d..8def4f5d 100644 --- a/src/ngx_http_lua_variable.c +++ b/src/ngx_http_lua_variable.c @@ -138,6 +138,8 @@ ngx_http_lua_var_set(lua_State *L) ngx_str_t name; ngx_uint_t hash; ngx_http_request_t *r; + int value_type; + const char *msg; lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(L, -1); @@ -167,15 +169,35 @@ ngx_http_lua_var_set(lua_State *L) /* we read the variable new value */ - p = (u_char *) luaL_checklstring(L, 3, &len); + value_type = lua_type(L, 3); + switch (value_type) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = (u_char *) luaL_checklstring(L, 3, &len); - val = ngx_palloc(r->pool, len); - if (val == NULL) { - return luaL_error(L, "memory allocation erorr"); + val = ngx_palloc(r->pool, len); + if (val == NULL) { + return luaL_error(L, "memory allocation erorr"); + } + + ngx_memcpy(val, p, len); + + break; + + case LUA_TNIL: + /* undef the variable */ + + val = NULL; + len = 0; + + break; + + default: + msg = lua_pushfstring(L, "string, number, or nil expected, " + "but got %s", lua_typename(L, value_type)); + return luaL_argerror(L, 1, msg); } - ngx_memcpy(val, p, len); - /* we fetch the variable itself */ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); @@ -188,17 +210,29 @@ ngx_http_lua_var_set(lua_State *L) } if (v->set_handler) { + + dd("set variables with set_handler"); + vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)); if (vv == NULL) { return luaL_error(L, "out of memory"); } - vv->valid = 1; - vv->not_found = 0; - vv->no_cacheable = 0; + if (value_type == LUA_TNIL) { + vv->valid = 0; + vv->not_found = 1; + vv->no_cacheable = 0; + vv->data = NULL; + vv->len = 0; - vv->data = val; - vv->len = len; + } else { + vv->valid = 1; + vv->not_found = 0; + vv->no_cacheable = 0; + + vv->data = val; + vv->len = len; + } v->set_handler(r, vv, v->data); @@ -208,12 +242,24 @@ ngx_http_lua_var_set(lua_State *L) if (v->flags & NGX_HTTP_VAR_INDEXED) { vv = &r->variables[v->index]; - vv->valid = 1; - vv->not_found = 0; - vv->no_cacheable = 0; + dd("set indexed variable"); - vv->data = val; - vv->len = len; + if (value_type == LUA_TNIL) { + vv->valid = 0; + vv->not_found = 1; + vv->no_cacheable = 0; + + vv->data = NULL; + vv->len = 0; + + } else { + vv->valid = 1; + vv->not_found = 0; + vv->no_cacheable = 0; + + vv->data = val; + vv->len = len; + } return 0; } From 3638fd09cc19facf79711a797fc4468cd073fb9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Mon, 24 Oct 2011 11:56:59 +0800 Subject: [PATCH 44/46] bugfix: no longer free request body buffers that are not allocated by ourselves. --- src/ngx_http_lua_common.h | 4 +-- src/ngx_http_lua_conf.c | 1 + src/ngx_http_lua_req_body.c | 44 ++++++++++++++++++++---- t/044-req-body.t | 68 +++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 8 deletions(-) diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 863538d8..e2c203d5 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -73,6 +73,8 @@ typedef struct { typedef struct { + ngx_buf_tag_t tag; + ngx_flag_t force_read_body; /* whether force request body to be read */ @@ -109,8 +111,6 @@ typedef struct { u_char *header_filter_src_key; /* cached key for header_filter_src */ - - } ngx_http_lua_loc_conf_t; diff --git a/src/ngx_http_lua_conf.c b/src/ngx_http_lua_conf.c index 241621e2..05a8385f 100644 --- a/src/ngx_http_lua_conf.c +++ b/src/ngx_http_lua_conf.c @@ -86,6 +86,7 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) conf->force_read_body = NGX_CONF_UNSET; conf->enable_code_cache = NGX_CONF_UNSET; + conf->tag = (ngx_buf_tag_t) &ngx_http_lua_module; return conf; } diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index ae710bc6..e0ed04d7 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -1,5 +1,5 @@ #ifndef DDEBUG -#define DDEBUG 0 +#define DDEBUG 1 #endif #include "ddebug.h" @@ -239,13 +239,14 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; - ngx_chain_t *cl; ngx_temp_file_t *tf; ngx_buf_t *b; ngx_str_t body, key, value; #if 1 ngx_int_t rc; #endif + ngx_chain_t *cl; + ngx_http_lua_loc_conf_t *llcf; n = lua_gettop(L); @@ -298,10 +299,19 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) } if (body.len == 0) { + if (rb->bufs) { + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + for (cl = rb->bufs; cl; cl = cl->next) { - if (cl->buf->temporary) { + if (cl->buf->tag == llcf->tag && cl->buf->temporary) { + + dd("free old request body buffer: size:%d", + (int) ngx_buf_size(cl->buf)); + ngx_pfree(r->pool, cl->buf->start); + cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } @@ -314,10 +324,17 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) goto set_header; } + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (rb->bufs) { + for (cl = rb->bufs; cl; cl = cl->next) { - if (cl->buf->temporary) { + if (cl->buf->tag == llcf->tag && cl->buf->temporary) { + dd("free old request body buffer: size:%d", + (int) ngx_buf_size(cl->buf)); + ngx_pfree(r->pool, cl->buf->start); + cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } @@ -327,7 +344,10 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) b = rb->bufs->buf; ngx_memzero(b, sizeof(ngx_buf_t)); + b->temporary = 1; + b->tag = llcf->tag; + b->start = ngx_palloc(r->pool, body.len); if (b->start == NULL) { return luaL_error(L, "out of memory"); @@ -346,6 +366,7 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) rb->bufs->next = NULL; b = ngx_create_temp_buf(r->pool, body.len); + b->tag = llcf->tag; b->last = ngx_copy(b->pos, body.data, body.len); rb->bufs->buf = b; @@ -416,7 +437,6 @@ ngx_http_lua_ngx_req_set_body_file(lua_State *L) ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; - ngx_chain_t *cl; ngx_temp_file_t *tf; ngx_buf_t *b; ngx_str_t name; @@ -427,6 +447,8 @@ ngx_http_lua_ngx_req_set_body_file(lua_State *L) ngx_pool_cleanup_t *cln; ngx_pool_cleanup_file_t *clnf; ngx_err_t err; + ngx_chain_t *cl; + ngx_http_lua_loc_conf_t *llcf; n = lua_gettop(L); @@ -472,12 +494,18 @@ ngx_http_lua_ngx_req_set_body_file(lua_State *L) /* clean up existing r->request_body->bufs (if any) */ + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (rb->bufs) { dd("XXX reusing buf"); for (cl = rb->bufs; cl; cl = cl->next) { - if (cl->buf->temporary) { + if (cl->buf->tag == llcf->tag && cl->buf->temporary) { + dd("free old request body buffer: size:%d", + (int) ngx_buf_size(cl->buf)); + ngx_pfree(r->pool, cl->buf->start); + cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } @@ -487,6 +515,8 @@ ngx_http_lua_ngx_req_set_body_file(lua_State *L) ngx_memzero(b, sizeof(ngx_buf_t)); + b->tag = llcf->tag; + } else { dd("XXX creating new buf"); @@ -502,6 +532,8 @@ ngx_http_lua_ngx_req_set_body_file(lua_State *L) return luaL_error(L, "out of memory"); } + b->tag = llcf->tag; + rb->bufs->buf = b; rb->buf = b; } diff --git a/t/044-req-body.t b/t/044-req-body.t index 9c22010c..b3815ece 100644 --- a/t/044-req-body.t +++ b/t/044-req-body.t @@ -725,3 +725,71 @@ a=1&a=2&b=hello&c=world B=HELLO&A=1&A=2&C=WORLD --- SKIP + + +=== TEST 29: read buffered body to memory and reset it with data in memory + proxy twice +--- config + location = /test { + rewrite_by_lua ' + ngx.req.read_body() + ngx.req.set_body_data("hiya, dear dear friend!") + ngx.req.set_body_data("howdy, my dear little sister!") + '; + proxy_pass http://127.0.0.1:$server_port/echo; + } + location = /echo { + echo_read_request_body; + echo_request_body; + } +--- request +POST /test +hello, world +--- response_body chomp +howdy, my dear little sister! + + + +=== TEST 30: read buffered body to memory and reset it with data in memory and then reset it to file +--- config + location = /test { + rewrite_by_lua ' + ngx.req.read_body() + ngx.req.set_body_data("hiya, dear dear friend!") + ngx.req.set_body_file(ngx.var.realpath_root .. "/a.txt") + '; + proxy_pass http://127.0.0.1:$server_port/echo; + } + location = /echo { + echo_read_request_body; + echo_request_body; + } +--- user_files +>>> a.txt +howdy, my dear little sister! +--- request +POST /test +hello, world +--- response_body +howdy, my dear little sister! + + + +=== TEST 31: read buffered body to memory and reset it with empty string + proxy twice +--- config + location = /test { + rewrite_by_lua ' + ngx.req.read_body() + ngx.req.set_body_data("hiya, dear dear friend!") + ngx.req.set_body_data("") + '; + proxy_pass http://127.0.0.1:$server_port/echo; + } + location = /echo { + echo_read_request_body; + echo_request_body; + } +--- request +POST /test +hello, world +--- response_body chomp + From d3e601fc93bbc7b073da47672b8ee9fca6497de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Mon, 24 Oct 2011 12:02:29 +0800 Subject: [PATCH 45/46] added tests for setting ngx.var.VAR to nil values. --- t/045-ngx-var.t | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 t/045-ngx-var.t diff --git a/t/045-ngx-var.t b/t/045-ngx-var.t new file mode 100644 index 00000000..668531a6 --- /dev/null +++ b/t/045-ngx-var.t @@ -0,0 +1,53 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use lib 'lib'; +use Test::Nginx::Socket; + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2); + +#no_diff(); +no_long_string(); +#master_on(); +#workers(2); +run_tests(); + +__DATA__ + +=== TEST 1: set indexed variables to nil +--- config + location = /test { + set $var 32; + content_by_lua ' + ngx.say("old: ", ngx.var.var) + ngx.var.var = nil + ngx.say("new: ", ngx.var.var) + '; + } +--- request +GET /test +--- response_body +old: 32 +new: nil + + + +=== TEST 2: set variables with set_handler to nil +--- config + location = /test { + content_by_lua ' + ngx.say("old: ", ngx.var.args) + ngx.var.args = nil + ngx.say("new: ", ngx.var.args) + '; + } +--- request +GET /test?hello=world +--- response_body +old: hello=world +new: nil + From 5973268d3a626d391b2fb3731a793117cfe5b48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agentzh=20=28=E7=AB=A0=E4=BA=A6=E6=98=A5=29?= Date: Mon, 24 Oct 2011 12:04:01 +0800 Subject: [PATCH 46/46] updated docs and turned off ddebug. --- README | 10 ++++++---- README.markdown | 6 +++--- doc/HttpLuaModule.wiki | 6 +++--- src/ngx_http_lua_req_body.c | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README b/README index f08836e1..55213439 100644 --- a/README +++ b/README @@ -8,8 +8,8 @@ Status This module is under active development and is already production ready. Version - This document describes ngx_lua v0.3.1rc19 - () released on 22 + This document describes ngx_lua v0.3.1rc20 + () released on 24 October 2011. Synopsis @@ -1583,9 +1583,11 @@ Nginx API for Lua context: *rewrite_by_lua*, access_by_lua*, content_by_lua** - Retrieves the in-memory request body data. + Retrieves the in-memory request body data. It returns a Lua string + rather than a Lua table holding all the parsed query arguments. If you + want the latter, use ngx.req.get_post_args instead. - Returns "nil" if + This function returns "nil" if 1. the request body has not been read, diff --git a/README.markdown b/README.markdown index 9da8e5bb..b650de9f 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,7 @@ This module is under active development and is already production ready. Version ======= -This document describes ngx_lua [v0.3.1rc19](https://github.com/chaoslawful/lua-nginx-module/tags) released on 22 October 2011. +This document describes ngx_lua [v0.3.1rc20](https://github.com/chaoslawful/lua-nginx-module/tags) released on 24 October 2011. Synopsis ======== @@ -1621,9 +1621,9 @@ ngx.req.get_body_data **context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** -Retrieves the in-memory request body data. +Retrieves the in-memory request body data. It returns a Lua string rather than a Lua table holding all the parsed query arguments. If you want the latter, use [ngx.req.get_post_args](http://wiki.nginx.org/HttpLuaModule#ngx.req.get_post_args) instead. -Returns `nil` if +This function returns `nil` if 1. the request body has not been read, 1. the request body has been read into disk temporary files, 1. or the request body has zero size. diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 40f6bb26..25ad089d 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -10,7 +10,7 @@ This module is under active development and is already production ready. = Version = -This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc19] released on 22 October 2011. +This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.3.1rc20] released on 24 October 2011. = Synopsis = @@ -1578,9 +1578,9 @@ See also [[#ngx.req.read_body|ngx.req.read_body]]. '''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' -Retrieves the in-memory request body data. +Retrieves the in-memory request body data. It returns a Lua string rather than a Lua table holding all the parsed query arguments. If you want the latter, use [[#ngx.req.get_post_args|ngx.req.get_post_args]] instead. -Returns nil if +This function returns nil if # the request body has not been read, # the request body has been read into disk temporary files, # or the request body has zero size. diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index e0ed04d7..0fdcbd21 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -1,5 +1,5 @@ #ifndef DDEBUG -#define DDEBUG 1 +#define DDEBUG 0 #endif #include "ddebug.h"