Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: server rewrite code #2

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ env:
- TEST_NGINX_SLEEP=0.006
jobs:
- NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.0l OPENSSL_PATCH_VER=1.1.0d
- NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.1m OPENSSL_PATCH_VER=1.1.1f
- NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.1n OPENSSL_PATCH_VER=1.1.1f

services:
- memcached
Expand Down
126 changes: 123 additions & 3 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,6 @@ TODO
* add `ignore_resp_headers`, `ignore_resp_body`, and `ignore_resp` options to [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) methods, to allow micro performance tuning on the user side.
* add automatic Lua code time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks.
* add `stat` mode similar to [mod_lua](https://httpd.apache.org/docs/trunk/mod/mod_lua.html).
* cosocket: add client SSL certificate support.

[Back to TOC](#table-of-contents)

Expand Down Expand Up @@ -1132,6 +1131,8 @@ Directives
* [content_by_lua](#content_by_lua)
* [content_by_lua_block](#content_by_lua_block)
* [content_by_lua_file](#content_by_lua_file)
* [server_rewrite_by_lua_block](#server_rewrite_by_lua_block)
* [server_rewrite_by_lua_file](#server_rewrite_by_lua_file)
* [rewrite_by_lua](#rewrite_by_lua)
* [rewrite_by_lua_block](#rewrite_by_lua_block)
* [rewrite_by_lua_file](#rewrite_by_lua_file)
Expand Down Expand Up @@ -1910,6 +1911,97 @@ But be very careful about malicious user inputs and always carefully validate or

[Back to TOC](#directives)

server_rewrite_by_lua_block
---------------------------

**syntax:** *server_rewrite_by_lua_block { lua-script }*

**context:** *http, server*

**phase:** *server rewrite*

Acts as a server rewrite phase handler and executes Lua code string specified in `{ lua-script }` for every request.
The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox).

```nginx

server {
...

server_rewrite_by_lua_block {
ngx.ctx.a = "server_rewrite_by_lua_block in http"
}

location /lua {
content_by_lua_block {
ngx.say(ngx.ctx.a)
ngx.log(ngx.INFO, ngx.ctx.a)
}
}
}
```

Just as any other rewrite phase handlers, [server_rewrite_by_lua_block](#server_rewrite_by_lua_block) also runs in subrequests.

```nginx

server {
server_rewrite_by_lua_block {
ngx.log(ngx.INFO, "is_subrequest:", ngx.is_subrequest)
}

location /lua {
content_by_lua_block {
local res = ngx.location.capture("/sub")
ngx.print(res.body)
}
}

location /sub {
content_by_lua_block {
ngx.say("OK")
}
}
}
```

Note that when calling `ngx.exit(ngx.OK)` within a [server_rewrite_by_lua_block](#server_rewrite_by_lua_block) handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [server_rewrite_by_lua_block](#server_rewrite_by_lua_block) handler, call [ngx.exit](#ngxexit) 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.


```nginx

server_rewrite_by_lua_block {
ngx.exit(503)
}

location /bar {
...
# never exec
}
```


[Back to TOC](#directives)

server_rewrite_by_lua_file
--------------------------

**syntax:** *server_rewrite_by_lua_file &lt;path-to-lua-script-file&gt;*

**context:** *http, server*

**phase:** *server rewrite*

Equivalent to [server_rewrite_by_lua_block](#server_rewrite_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.10.22` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.

Nginx variables can be used in the `<path-to-lua-script-file>` string to provide flexibility. This however carries some risks and is not ordinarily recommended.

When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server.

When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid reloading Nginx.

[Back to TOC](#directives)

rewrite_by_lua
--------------

Expand Down Expand Up @@ -3594,6 +3686,7 @@ Nginx API for Lua
* [ngx.socket.stream](#ngxsocketstream)
* [ngx.socket.tcp](#ngxsockettcp)
* [tcpsock:connect](#tcpsockconnect)
* [tcpsock:setclientcert](#tcpsocksetclientcert)
* [tcpsock:sslhandshake](#tcpsocksslhandshake)
* [tcpsock:send](#tcpsocksend)
* [tcpsock:receive](#tcpsockreceive)
Expand Down Expand Up @@ -4903,11 +4996,12 @@ See also [ngx.req.set_uri](#ngxreqset_uri).
ngx.req.get_uri_args
--------------------

**syntax:** *args, err = ngx.req.get_uri_args(max_args?)*
**syntax:** *args, err = ngx.req.get_uri_args(max_args?, tab?)*

**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, balancer_by_lua&#42;*

Returns a Lua table holding all the current request URL query arguments.
Returns a Lua table holding all the current request URL query arguments. An optional `tab` argument
can be used to reuse the table returned by this method.

```nginx

Expand Down Expand Up @@ -7564,6 +7658,7 @@ ngx.socket.tcp
Creates and returns a TCP or stream-oriented unix domain socket object (also known as one type of the "cosocket" objects). The following methods are supported on this object:

* [connect](#tcpsockconnect)
* [setclientcert](#tcpsocksetclientcert)
* [sslhandshake](#tcpsocksslhandshake)
* [send](#tcpsocksend)
* [receive](#tcpsockreceive)
Expand Down Expand Up @@ -7723,6 +7818,31 @@ This method was first introduced in the `v0.5.0rc1` release.

[Back to TOC](#nginx-api-for-lua)

tcpsock:setclientcert
--------------------

**syntax:** *ok, err = tcpsock:setclientcert(cert, pkey)*

**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*

Set client certificate chain and corresponding private key to the TCP socket object.
The certificate chain and private key provided will be used later by the [tcpsock:sslhandshake](#tcpsocksslhandshake) method.

* `cert` specify a client certificate chain cdata object that will be used while handshaking with
remote server. These objects can be created using [ngx.ssl.parse\_pem\_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert)
function provided by lua-resty-core. Note that specifying the `cert` option requires
corresponding `pkey` be provided too. See below.
* `pkey` specify a private key corresponds to the `cert` option above.
These objects can be created using [ngx.ssl.parse\_pem\_priv\_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_priv_key)
function provided by lua-resty-core.

If both of `cert` and `pkey` are `nil`, this method will clear any existing client certificate and private key
that was previously set on the cosocket object.

This method was first introduced in the `v0.10.22` release.

[Back to TOC](#nginx-api-for-lua)

tcpsock:sslhandshake
--------------------

Expand Down
2 changes: 2 additions & 0 deletions config
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ HTTP_LUA_SRCS=" \
$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_contentby.c \
$ngx_addon_dir/src/ngx_http_lua_server_rewriteby.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 \
Expand Down Expand Up @@ -325,6 +326,7 @@ HTTP_LUA_DEPS=" \
$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_contentby.h \
$ngx_addon_dir/src/ngx_http_lua_server_rewriteby.c \
$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 \
Expand Down
87 changes: 85 additions & 2 deletions doc/HttpLuaModule.wiki
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,88 @@ Nginx variables are supported in the file path for dynamic dispatch, for example

But be very careful about malicious user inputs and always carefully validate or filter out the user-supplied path components.

== server_rewrite_by_lua_block ==

'''syntax:''' ''server_rewrite_by_lua_block { lua-script }''

'''context:''' ''http, server''

'''phase:''' ''server rewrite''

Acts as a server rewrite phase handler and executes Lua code string specified in <code>{ lua-script }</code> for every request.
The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox).

<geshi lang="nginx">
server {
...

server_rewrite_by_lua_block {
ngx.ctx.a = "server_rewrite_by_lua_block in http"
}

location /lua {
content_by_lua_block {
ngx.say(ngx.ctx.a)
ngx.log(ngx.INFO, ngx.ctx.a)
}
}
}
</geshi>

Just as any other rewrite phase handlers, [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]] also runs in subrequests.

<geshi lang="nginx">
server {
server_rewrite_by_lua_block {
ngx.log(ngx.INFO, "is_subrequest:", ngx.is_subrequest)
}

location /lua {
content_by_lua_block {
local res = ngx.location.capture("/sub")
ngx.print(res.body)
}
}

location /sub {
content_by_lua_block {
ngx.say("OK")
}
}
}
</geshi>

Note that when calling <code>ngx.exit(ngx.OK)</code> within a [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]] handler, the Nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]] handler, call [[#ngx.exit|ngx.exit]] with status >= 200 (<code>ngx.HTTP_OK</code>) and status < 300 (<code>ngx.HTTP_SPECIAL_RESPONSE</code>) for successful quits and <code>ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)</code> (or its friends) for failures.


<geshi lang="nginx">
server_rewrite_by_lua_block {
ngx.exit(503)
}

location /bar {
...
# never exec
}
</geshi>


== server_rewrite_by_lua_file ==

'''syntax:''' ''server_rewrite_by_lua_file <path-to-lua-script-file>''

'''context:''' ''http, server''

'''phase:''' ''server rewrite''

Equivalent to [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.10.22</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.

Nginx variables can be used in the <code><path-to-lua-script-file></code> string to provide flexibility. This however carries some risks and is not ordinarily recommended.

When a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.

When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [[#lua_code_cache|lua_code_cache]] <code>off</code> in <code>nginx.conf</code> to avoid reloading Nginx.

== rewrite_by_lua ==

'''syntax:''' ''rewrite_by_lua <lua-script-str>''
Expand Down Expand Up @@ -4101,11 +4183,12 @@ See also [[#ngx.req.set_uri|ngx.req.set_uri]].

== ngx.req.get_uri_args ==

'''syntax:''' ''args, err = ngx.req.get_uri_args(max_args?)''
'''syntax:''' ''args, err = ngx.req.get_uri_args(max_args?, tab?)''

'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*''

Returns a Lua table holding all the current request URL query arguments.
Returns a Lua table holding all the current request URL query arguments. An optional <code>tab</code> argument
can be used to reuse the table returned by this method.

<geshi lang="nginx">
location = /test {
Expand Down
12 changes: 11 additions & 1 deletion src/ngx_http_lua_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ typedef struct {
#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000
#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x2000
#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO 0x4000
#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE 0x8000


#define NGX_HTTP_LUA_FFI_NO_REQ_CTX -100
Expand Down Expand Up @@ -307,6 +308,7 @@ struct ngx_http_lua_main_conf_s {
unsigned requires_log:1;
unsigned requires_shm:1;
unsigned requires_capture_log:1;
unsigned requires_server_rewrite:1;
};


Expand Down Expand Up @@ -336,6 +338,12 @@ union ngx_http_lua_srv_conf_u {
u_char *ssl_client_hello_src_key;
u_char *ssl_client_hello_chunkname;
int ssl_client_hello_src_ref;

ngx_http_lua_srv_conf_handler_pt server_rewrite_handler;
ngx_http_complex_value_t server_rewrite_src;
u_char *server_rewrite_src_key;
u_char *server_rewrite_chunkname;
int server_rewrite_src_ref;
} srv;
#endif

Expand Down Expand Up @@ -378,6 +386,8 @@ typedef struct {

ngx_http_output_body_filter_pt body_filter_handler;



u_char *rewrite_chunkname;
ngx_http_complex_value_t rewrite_src; /* rewrite_by_lua
inline script/script
Expand Down Expand Up @@ -631,7 +641,7 @@ typedef struct ngx_http_lua_ctx_s {
response headers */
unsigned mime_set:1; /* whether the user has set Content-Type
response header */

unsigned entered_server_rewrite_phase:1;
unsigned entered_rewrite_phase:1;
unsigned entered_access_phase:1;
unsigned entered_content_phase:1;
Expand Down
3 changes: 3 additions & 0 deletions src/ngx_http_lua_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ ngx_http_lua_ngx_exec(lua_State *L)
}

ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT);

Expand Down Expand Up @@ -232,6 +233,7 @@ ngx_http_lua_ngx_redirect(lua_State *L)
}

ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT);

Expand Down Expand Up @@ -373,6 +375,7 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err,
}

if (ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
| NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE
| NGX_HTTP_LUA_CONTEXT_ACCESS
| NGX_HTTP_LUA_CONTEXT_CONTENT
| NGX_HTTP_LUA_CONTEXT_TIMER
Expand Down
Loading