From 0e2e7b3e4c06d2abb0f778b97244dadbd005f4c4 Mon Sep 17 00:00:00 2001 From: Arcadiy Ivanov Date: Mon, 9 Nov 2020 03:36:18 -0500 Subject: [PATCH] Add access_by_lua(_block/_file) and access_by_lua_no_postpone Some rudimentary tests Requested cleanup of `#if 1` Try fixing tests --- README.md | 5 +- config | 2 + src/ngx_stream_lua_accessby.c | 320 ++++++++++++++++++++++++++++++++ src/ngx_stream_lua_accessby.h | 22 +++ src/ngx_stream_lua_common.h | 21 ++- src/ngx_stream_lua_control.c | 3 +- src/ngx_stream_lua_coroutine.c | 4 + src/ngx_stream_lua_directive.c | 114 +++++++++++- src/ngx_stream_lua_directive.h | 4 + src/ngx_stream_lua_module.c | 55 +++++- src/ngx_stream_lua_output.c | 9 +- src/ngx_stream_lua_phase.c | 4 + src/ngx_stream_lua_semaphore.c | 3 +- src/ngx_stream_lua_sleep.c | 3 +- src/ngx_stream_lua_socket_tcp.c | 10 +- src/ngx_stream_lua_socket_udp.c | 7 +- src/ngx_stream_lua_uthread.c | 6 +- src/ngx_stream_lua_util.c | 3 +- src/ngx_stream_lua_util.h | 1 + t/024-access/sanity.t | 171 +++++++++++++++++ t/089-phase.t | 5 +- t/132-lua-blocks.t | 18 +- 22 files changed, 754 insertions(+), 36 deletions(-) create mode 100644 src/ngx_stream_lua_accessby.c create mode 100644 src/ngx_stream_lua_accessby.h create mode 100644 t/024-access/sanity.t diff --git a/README.md b/README.md index 27e997b2..b98aae7b 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,9 @@ behavior. * [init_worker_by_lua_file](https://github.com/openresty/lua-nginx-module#init_worker_by_lua_file) * [preread_by_lua_block](#preread_by_lua_block) * [preread_by_lua_file](#preread_by_lua_file) +* [access_by_lua](https://github.com/openresty/lua-nginx-module#access_by_lua) +* [access_by_lua_block](https://github.com/openresty/lua-nginx-module#access_by_lua_block) +* [access_by_lua_file](https://github.com/openresty/lua-nginx-module#access_by_lua_file) * [content_by_lua_block](https://github.com/openresty/lua-nginx-module#content_by_lua_block) * [content_by_lua_file](https://github.com/openresty/lua-nginx-module#content_by_lua_file) * [balancer_by_lua_block](https://github.com/openresty/lua-nginx-module#balancer_by_lua_block) @@ -170,6 +173,7 @@ behavior. * [lua_add_variable](#lua_add_variable) * [lua_capture_error_log](https://github.com/openresty/lua-nginx-module#lua_capture_error_log) * [preread_by_lua_no_postpone](#preread_by_lua_no_postpone) +* [access_by_lua_no_postpone](https://github.com/openresty/lua-nginx-module#access_by_lua_no_postpone) The [send_timeout](https://nginx.org/r/send_timeout) directive in the Nginx "http" subsystem is missing in the "stream" subsystem. As such, @@ -527,7 +531,6 @@ output to be completely flushed out (to the system socket send buffers). TODO ==== -* Add new directives `access_by_lua_block` and `access_by_lua_file`. * Add `lua_postpone_output` to emulate the [postpone_output](https://nginx.org/r/postpone_output) directive. [Back to TOC](#table-of-contents) diff --git a/config b/config index e984f965..104764c8 100644 --- a/config +++ b/config @@ -280,6 +280,7 @@ STREAM_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_stream_lua_ssl_certby.c \ $ngx_addon_dir/src/ngx_stream_lua_log_ringbuf.c \ $ngx_addon_dir/src/ngx_stream_lua_input_filters.c \ + $ngx_addon_dir/src/ngx_stream_lua_accessby.c \ " STREAM_LUA_DEPS=" \ @@ -324,6 +325,7 @@ STREAM_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_stream_lua_ssl_certby.h \ $ngx_addon_dir/src/ngx_stream_lua_log_ringbuf.h \ $ngx_addon_dir/src/ngx_stream_lua_input_filters.h \ + $ngx_addon_dir/src/ngx_stream_lua_accessby.h \ " # ---------------------------------------- diff --git a/src/ngx_stream_lua_accessby.c b/src/ngx_stream_lua_accessby.c new file mode 100644 index 00000000..7ee45d41 --- /dev/null +++ b/src/ngx_stream_lua_accessby.c @@ -0,0 +1,320 @@ + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + * Copyright (C) Arcadiy Ivanov (arcivanov) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include +#include "ngx_stream_lua_accessby.h" +#include "ngx_stream_lua_util.h" +#include "ngx_stream_lua_exception.h" +#include "ngx_stream_lua_cache.h" + + +static ngx_int_t ngx_stream_lua_access_by_chunk(lua_State *L, + ngx_stream_lua_request_t *r); + + +ngx_int_t +ngx_stream_lua_access_handler(ngx_stream_session_t *s) +{ + ngx_int_t rc; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_srv_conf_t *lscf; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_request_t *r; + + dd("entered"); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "lua access handler"); + + lmcf = ngx_stream_get_module_main_conf(s, ngx_stream_lua_module); + + if (!lmcf->postponed_to_access_phase_end) { + ngx_stream_phase_handler_t tmp, *ph, *cur_ph, *last_ph; + ngx_stream_core_main_conf_t *cmcf; + lmcf->postponed_to_access_phase_end = 1; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + ph = cmcf->phase_engine.handlers; + cur_ph = &ph[s->phase_handler]; + /* we should skip the post_access phase handler here too */ + last_ph = &ph[cur_ph->next - 2]; + + dd("ph cur: %d, ph next: %d", (int) s->phase_handler, + (int) (cur_ph->next - 2)); + + if (cur_ph < last_ph) { + dd("swapping the contents of cur_ph and last_ph..."); + + tmp = *cur_ph; + + memmove(cur_ph, cur_ph + 1, + (last_ph - cur_ph) * sizeof (ngx_stream_phase_handler_t)); + + *last_ph = tmp; + + s->phase_handler--; /* redo the current ph */ + + return NGX_DECLINED; + } + } + + lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); + + if (lscf->access_handler == NULL) { + dd("no access handler found"); + return NGX_DECLINED; + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + ctx = ngx_stream_lua_create_ctx(s); + if (ctx == NULL) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + } + + r = ctx->request; + + dd("entered? %d", (int) ctx->entered_access_phase); + + if (ctx->entered_access_phase) { + dd("calling wev handler"); + rc = ctx->resume_handler(r); + dd("wev handler returns %d", (int) rc); + + if (rc == NGX_ERROR || rc == NGX_DONE || rc == NGX_OK || rc > NGX_OK) { + return rc; + } + + return NGX_DECLINED; + } + + dd("calling access handler"); + return lscf->access_handler(r); +} + + +ngx_int_t +ngx_stream_lua_access_handler_inline(ngx_stream_lua_request_t *r) +{ + ngx_int_t rc; + lua_State *L; + ngx_stream_lua_srv_conf_t *lscf; + + dd("entered"); + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + L = ngx_stream_lua_get_lua_vm(r, NULL); + + /* load Lua inline script (w/ cache) sp = 1 */ + rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L, + lscf->access_src.value.data, + lscf->access_src.value.len, + lscf->access_src_key, + (const char *) lscf->access_chunkname); + + if (rc != NGX_OK) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + return ngx_stream_lua_access_by_chunk(L, r); +} + + +ngx_int_t +ngx_stream_lua_access_handler_file(ngx_stream_lua_request_t *r) +{ + u_char *script_path; + ngx_int_t rc; + ngx_str_t eval_src; + lua_State *L; + ngx_stream_lua_srv_conf_t *lscf; + + dd("entered"); + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + /* Eval nginx variables in code path string first */ + if (ngx_stream_complex_value(r->session, &lscf->access_src, &eval_src) != NGX_OK) { + return NGX_ERROR; + } + + script_path = ngx_stream_lua_rebase_path(r->pool, eval_src.data, + eval_src.len); + + if (script_path == NULL) { + return NGX_ERROR; + } + + L = ngx_stream_lua_get_lua_vm(r, NULL); + + /* load Lua script file (w/ cache) sp = 1 */ + rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, script_path, + lscf->access_src_key); + if (rc != NGX_OK) { + if (rc < NGX_STREAM_SPECIAL_RESPONSE) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_stream_lua_assert(lua_isfunction(L, -1)); + + return ngx_stream_lua_access_by_chunk(L, r); +} + + +static ngx_int_t +ngx_stream_lua_access_by_chunk(lua_State *L, ngx_stream_lua_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_event_t *rev; + ngx_connection_t *c; + ngx_stream_lua_ctx_t *ctx; + ngx_stream_lua_cleanup_t *cln; + + ngx_stream_lua_srv_conf_t *lscf; + + /* {{{ new coroutine to handle request */ + co = ngx_stream_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine " + "to handle request"); + + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_stream_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_stream_lua_set_req(co, r); + + /* {{{ initialize request context */ + ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); + + dd("ctx = %p", ctx); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_stream_lua_reset_ctx(r, L, ctx); + + ctx->entered_access_phase = 1; + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + // ngx_stream_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx); + + /* }}} */ + + /* {{{ register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_stream_lua_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + cln->handler = ngx_stream_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + /* }}} */ + + ctx->context = NGX_STREAM_LUA_CONTEXT_ACCESS; + + lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module); + + if (lscf->check_client_abort) { + r->read_event_handler = ngx_stream_lua_rd_check_broken_connection; + + rev = r->connection->read; + + if (!rev->active) { + if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + } else { + r->read_event_handler = ngx_stream_lua_block_reading; + } + + rc = ngx_stream_lua_run_thread(L, r, ctx, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "access run thread returned %d", (int) rc); + + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + + c = r->connection; + + if (rc == NGX_AGAIN) { + rc = ngx_stream_lua_run_posted_threads(c, L, r, ctx, 0); + + if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) { + return rc; + } + + if (rc != NGX_OK) { + return NGX_DECLINED; + } + + } else if (rc == NGX_DONE) { + ngx_stream_lua_finalize_request(r, NGX_DONE); + + rc = ngx_stream_lua_run_posted_threads(c, L, r, ctx, 0); + + if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) { + return rc; + } + + if (rc != NGX_OK) { + return NGX_DECLINED; + } + } + + if (rc == NGX_OK) { + return NGX_OK; + } + + return NGX_DECLINED; +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ \ No newline at end of file diff --git a/src/ngx_stream_lua_accessby.h b/src/ngx_stream_lua_accessby.h new file mode 100644 index 00000000..0ac2d0a2 --- /dev/null +++ b/src/ngx_stream_lua_accessby.h @@ -0,0 +1,22 @@ + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_STREAM_LUA_ACCESSBY_H_INCLUDED_ +#define _NGX_STREAM_LUA_ACCESSBY_H_INCLUDED_ + + +#include "ngx_stream_lua_common.h" + + +ngx_int_t ngx_stream_lua_access_handler(ngx_stream_session_t *s); +ngx_int_t ngx_stream_lua_access_handler_inline(ngx_stream_lua_request_t *r); +ngx_int_t ngx_stream_lua_access_handler_file(ngx_stream_lua_request_t *r); + + +#endif /* _NGX_STREAM_LUA_ACCESSBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ \ No newline at end of file diff --git a/src/ngx_stream_lua_common.h b/src/ngx_stream_lua_common.h index 8d8cf48c..d8cfaec1 100644 --- a/src/ngx_stream_lua_common.h +++ b/src/ngx_stream_lua_common.h @@ -132,6 +132,7 @@ #define NGX_STREAM_LUA_CONTEXT_PREREAD 0x0020 #define NGX_STREAM_LUA_CONTEXT_SSL_CERT 0x0040 #define NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO 0x0080 +#define NGX_STREAM_LUA_CONTEXT_ACCESS 0x0100 #define NGX_STREAM_LUA_FFI_NO_REQ_CTX -100 @@ -207,6 +208,7 @@ struct ngx_stream_lua_main_conf_s { ngx_array_t *preload_hooks; /* of ngx_stream_lua_preload_hook_t */ ngx_flag_t postponed_to_preread_phase_end; + ngx_flag_t postponed_to_access_phase_end; ngx_stream_lua_main_conf_handler_pt init_handler; ngx_str_t init_src; @@ -233,7 +235,8 @@ struct ngx_stream_lua_main_conf_s { ngx_flag_t set_sa_restart; unsigned requires_preread:1; - + unsigned requires_capture_filter:1; + unsigned requires_access:1; unsigned requires_log:1; unsigned requires_shm:1; unsigned requires_capture_log:1; @@ -269,16 +272,24 @@ struct ngx_stream_lua_srv_conf_s { code cache */ ngx_stream_lua_handler_pt preread_handler; - + ngx_stream_lua_handler_pt access_handler; ngx_stream_lua_handler_pt content_handler; ngx_stream_lua_handler_pt log_handler; u_char *preread_chunkname; - ngx_stream_complex_value_t preread_src; /* access_by_lua + ngx_stream_complex_value_t preread_src; /* preread_by_lua + inline script/script + file path */ + + u_char *preread_src_key; /* cached key for preread_src */ + + u_char *access_chunkname; + ngx_stream_complex_value_t access_src; /* access_by_lua inline script/script file path */ - u_char *preread_src_key; /* cached key for access_src */ + u_char *access_src_key; /* cached key for access_src */ + u_char *content_chunkname; @@ -492,7 +503,7 @@ typedef struct ngx_stream_lua_ctx_s { response headers */ unsigned entered_preread_phase:1; - + unsigned entered_access_phase:1; unsigned entered_content_phase:1; unsigned buffering:1; /* HTTP 1.0 response body buffering flag */ diff --git a/src/ngx_stream_lua_control.c b/src/ngx_stream_lua_control.c index 86dda5d1..894b6370 100644 --- a/src/ngx_stream_lua_control.c +++ b/src/ngx_stream_lua_control.c @@ -118,7 +118,8 @@ ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err, | NGX_STREAM_LUA_CONTEXT_BALANCER | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_PREREAD, + | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_ACCESS, err, errlen) != NGX_OK) { return NGX_ERROR; diff --git a/src/ngx_stream_lua_coroutine.c b/src/ngx_stream_lua_coroutine.c index 27b552f1..e51f6dc2 100644 --- a/src/ngx_stream_lua_coroutine.c +++ b/src/ngx_stream_lua_coroutine.c @@ -129,6 +129,7 @@ ngx_stream_lua_coroutine_create_helper(lua_State *L, | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_ACCESS ); vm = ngx_stream_lua_get_lua_vm(r, ctx); @@ -210,6 +211,7 @@ ngx_stream_lua_coroutine_resume(lua_State *L) | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_ACCESS ); p_coctx = ctx->cur_co_ctx; @@ -271,6 +273,7 @@ ngx_stream_lua_coroutine_yield(lua_State *L) | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_ACCESS ); coctx = ctx->cur_co_ctx; @@ -431,6 +434,7 @@ ngx_stream_lua_coroutine_status(lua_State *L) | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_ACCESS ); coctx = ngx_stream_lua_get_co_ctx(co, ctx); diff --git a/src/ngx_stream_lua_directive.c b/src/ngx_stream_lua_directive.c index 5580106f..7896a842 100644 --- a/src/ngx_stream_lua_directive.c +++ b/src/ngx_stream_lua_directive.c @@ -34,7 +34,7 @@ #include "api/ngx_stream_lua_api.h" #include "ngx_stream_lua_prereadby.h" - +#include "ngx_stream_lua_accessby.h" typedef struct ngx_stream_lua_block_parser_ctx_s ngx_stream_lua_block_parser_ctx_t; @@ -233,12 +233,124 @@ ngx_stream_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } +char * +ngx_stream_lua_access_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_stream_lua_access_by_lua; + cf->handler_conf = conf; + + rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_stream_lua_access_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + size_t chunkname_len; + u_char *p, *chunkname; + ngx_str_t *value; + ngx_stream_lua_main_conf_t *lmcf; + ngx_stream_lua_srv_conf_t *lscf = conf; + + ngx_stream_compile_complex_value_t ccv; + + dd("enter"); + + /* must specify a content handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->access_handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + dd("value[0]: %.*s", (int) value[0].len, value[0].data); + dd("value[1]: %.*s", (int) value[1].len, value[1].data); + + if (value[1].len == 0) { + /* Oops...Invalid location conf */ + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "invalid location config: no runnable Lua code"); + + return NGX_CONF_ERROR; + } + + if (cmd->post == ngx_stream_lua_access_handler_inline) { + chunkname = ngx_stream_lua_gen_chunk_name(cf, "access_by_lua", + sizeof("access_by_lua") - 1, + &chunkname_len); + if (chunkname == NULL) { + return NGX_CONF_ERROR; + } + + lscf->access_chunkname = chunkname; + + dd("chunkname: %s", chunkname); + + /* Don't eval nginx variables for inline lua code */ + lscf->access_src.value = value[1]; + p = ngx_palloc(cf->pool, + chunkname_len + NGX_STREAM_LUA_INLINE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + lscf->access_src_key = p; + p = ngx_copy(p, chunkname, chunkname_len); + p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, + NGX_STREAM_LUA_INLINE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } else { + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &lscf->access_src; + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (lscf->access_src.lengths == NULL) { + /* no variable found */ + p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->access_src_key = p; + + p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, + NGX_STREAM_LUA_FILE_TAG_LEN); + p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + } + + lscf->access_handler = (ngx_stream_lua_handler_pt) cmd->post; + + lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module); + + lmcf->requires_access = 1; + + return NGX_CONF_OK; +} char * ngx_stream_lua_preread_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, diff --git a/src/ngx_stream_lua_directive.h b/src/ngx_stream_lua_directive.h index 6b36bb13..3fce6925 100644 --- a/src/ngx_stream_lua_directive.h +++ b/src/ngx_stream_lua_directive.h @@ -54,6 +54,10 @@ ngx_stream_lua_preread_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_stream_lua_preread_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_stream_lua_access_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_lua_access_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); char * ngx_stream_lua_add_variable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); diff --git a/src/ngx_stream_lua_module.c b/src/ngx_stream_lua_module.c index 47bb33ce..ed296de3 100644 --- a/src/ngx_stream_lua_module.c +++ b/src/ngx_stream_lua_module.c @@ -33,7 +33,7 @@ #include "ngx_stream_lua_prereadby.h" - +#include "ngx_stream_lua_accessby.h" static void *ngx_stream_lua_create_main_conf(ngx_conf_t *cf); static char *ngx_stream_lua_init_main_conf(ngx_conf_t *cf, void *conf); @@ -237,6 +237,36 @@ static ngx_command_t ngx_stream_lua_cmds[] = { 0, (void *) ngx_stream_lua_preread_handler_inline }, + /* access_by_lua "" */ + { ngx_string("access_by_lua"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_access_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_access_handler_inline }, + + /* access_by_lua_block { } */ + { ngx_string("access_by_lua_block"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_lua_access_by_lua_block, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_access_handler_inline }, + + /* access_by_lua_file rel/or/abs/path/to/script */ + { ngx_string("access_by_lua_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_lua_access_by_lua, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + (void *) ngx_stream_lua_access_handler_file }, + + { ngx_string("access_by_lua_no_postpone"), + NGX_STREAM_MAIN_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_lua_main_conf_t, postponed_to_access_phase_end), + NULL }, /* content_by_lua "" */ { ngx_string("content_by_lua"), @@ -529,6 +559,21 @@ ngx_stream_lua_init(ngx_conf_t *cf) lmcf->postponed_to_preread_phase_end = 0; } + dd("requires access: %d", (int) lmcf->requires_access); + + if (lmcf->requires_access) { + h = ngx_array_push(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_lua_access_handler; + } + + if (lmcf->postponed_to_access_phase_end == NGX_CONF_UNSET) { + lmcf->postponed_to_access_phase_end = 0; + } + dd("requires log: %d", (int) lmcf->requires_log); if (lmcf->requires_log) { @@ -704,6 +749,7 @@ ngx_stream_lua_create_main_conf(ngx_conf_t *cf) #endif lmcf->postponed_to_preread_phase_end = NGX_CONF_UNSET; + lmcf->postponed_to_access_phase_end = NGX_CONF_UNSET; lmcf->set_sa_restart = NGX_CONF_UNSET; @@ -973,6 +1019,13 @@ ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) conf->preread_chunkname = prev->preread_chunkname; } + if (conf->access_src.value.len == 0) { + conf->access_src = prev->access_src; + conf->access_handler = prev->access_handler; + conf->access_src_key = prev->access_src_key; + conf->access_chunkname = prev->access_chunkname; + } + return NGX_CONF_OK; } diff --git a/src/ngx_stream_lua_output.c b/src/ngx_stream_lua_output.c index cfb33cb1..9bdeaffb 100644 --- a/src/ngx_stream_lua_output.c +++ b/src/ngx_stream_lua_output.c @@ -76,7 +76,8 @@ ngx_stream_lua_ngx_echo(lua_State *L, unsigned newline) } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT - | NGX_STREAM_LUA_CONTEXT_PREREAD); + | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_ACCESS); if (ctx->eof) { @@ -485,7 +486,8 @@ ngx_stream_lua_ngx_flush(lua_State *L) } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT - | NGX_STREAM_LUA_CONTEXT_PREREAD); + | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_ACCESS); coctx = ctx->cur_co_ctx; @@ -608,7 +610,8 @@ ngx_stream_lua_ngx_eof(lua_State *L) } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT - | NGX_STREAM_LUA_CONTEXT_PREREAD); + | NGX_STREAM_LUA_CONTEXT_PREREAD + | NGX_STREAM_LUA_CONTEXT_ACCESS); ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, "lua send eof"); diff --git a/src/ngx_stream_lua_phase.c b/src/ngx_stream_lua_phase.c index db1f7658..3fb6d68f 100644 --- a/src/ngx_stream_lua_phase.c +++ b/src/ngx_stream_lua_phase.c @@ -62,6 +62,10 @@ ngx_stream_lua_ngx_get_phase(lua_State *L) lua_pushliteral(L, "preread"); break; + case NGX_STREAM_LUA_CONTEXT_ACCESS: + lua_pushliteral(L, "access"); + break; + case NGX_STREAM_LUA_CONTEXT_CONTENT: lua_pushliteral(L, "content"); break; diff --git a/src/ngx_stream_lua_semaphore.c b/src/ngx_stream_lua_semaphore.c index 9bef4243..c609889a 100644 --- a/src/ngx_stream_lua_semaphore.c +++ b/src/ngx_stream_lua_semaphore.c @@ -391,7 +391,8 @@ ngx_stream_lua_ffi_sema_wait(ngx_stream_lua_request_t *r, | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_TIMER, + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_ACCESS, err, errlen); if (rc != NGX_OK) { diff --git a/src/ngx_stream_lua_sleep.c b/src/ngx_stream_lua_sleep.c index 0fd2171c..386bd96c 100644 --- a/src/ngx_stream_lua_sleep.c +++ b/src/ngx_stream_lua_sleep.c @@ -66,7 +66,8 @@ ngx_stream_lua_ngx_sleep(lua_State *L) | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_TIMER); + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_ACCESS); coctx = ctx->cur_co_ctx; if (coctx == NULL) { diff --git a/src/ngx_stream_lua_socket_tcp.c b/src/ngx_stream_lua_socket_tcp.c index e87bc6fc..4cdf6239 100644 --- a/src/ngx_stream_lua_socket_tcp.c +++ b/src/ngx_stream_lua_socket_tcp.c @@ -447,7 +447,8 @@ ngx_stream_lua_socket_tcp(lua_State *L) | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_TIMER); + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_ACCESS); lua_createtable(L, 5 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( @@ -899,11 +900,11 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT - | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_TIMER); + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_ACCESS); luaL_checktype(L, 1, LUA_TTABLE); @@ -5074,7 +5075,8 @@ ngx_stream_lua_req_socket_tcp(lua_State *L) } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT - |NGX_STREAM_LUA_CONTEXT_PREREAD); + | NGX_STREAM_LUA_CONTEXT_ACCESS + | NGX_STREAM_LUA_CONTEXT_PREREAD); c = r->connection; diff --git a/src/ngx_stream_lua_socket_udp.c b/src/ngx_stream_lua_socket_udp.c index befcd0f1..809791de 100644 --- a/src/ngx_stream_lua_socket_udp.c +++ b/src/ngx_stream_lua_socket_udp.c @@ -191,7 +191,8 @@ ngx_stream_lua_socket_udp(lua_State *L) | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_TIMER); + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_ACCESS); lua_createtable(L, 3 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( @@ -254,7 +255,8 @@ ngx_stream_lua_socket_udp_setpeername(lua_State *L) | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_TIMER); + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_ACCESS); luaL_checktype(L, 1, LUA_TTABLE); @@ -1730,6 +1732,7 @@ ngx_stream_lua_req_socket_udp(lua_State *L) } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_ACCESS |NGX_STREAM_LUA_CONTEXT_PREREAD); c = r->connection; diff --git a/src/ngx_stream_lua_uthread.c b/src/ngx_stream_lua_uthread.c index 991df56a..c98e8bb2 100644 --- a/src/ngx_stream_lua_uthread.c +++ b/src/ngx_stream_lua_uthread.c @@ -138,7 +138,8 @@ ngx_stream_lua_uthread_wait(lua_State *L) | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_TIMER); + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_ACCESS); coctx = ctx->cur_co_ctx; @@ -238,7 +239,8 @@ ngx_stream_lua_uthread_kill(lua_State *L) | NGX_STREAM_LUA_CONTEXT_PREREAD | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_STREAM_LUA_CONTEXT_SSL_CERT - | NGX_STREAM_LUA_CONTEXT_TIMER); + | NGX_STREAM_LUA_CONTEXT_TIMER + | NGX_STREAM_LUA_CONTEXT_ACCESS); coctx = ctx->cur_co_ctx; diff --git a/src/ngx_stream_lua_util.c b/src/ngx_stream_lua_util.c index 58061b49..f3207f22 100644 --- a/src/ngx_stream_lua_util.c +++ b/src/ngx_stream_lua_util.c @@ -681,7 +681,8 @@ ngx_stream_lua_reset_ctx(ngx_stream_lua_request_t *r, lua_State *L, ctx->entry_co_ctx.co_ref = LUA_NOREF; - + ctx->entered_preread_phase = 0; + ctx->entered_access_phase = 0; ctx->entered_content_phase = 0; ctx->exit_code = 0; diff --git a/src/ngx_stream_lua_util.h b/src/ngx_stream_lua_util.h index 85cd6ab4..302c8cac 100644 --- a/src/ngx_stream_lua_util.h +++ b/src/ngx_stream_lua_util.h @@ -81,6 +81,7 @@ extern char ngx_stream_lua_headers_metatable_key; : (c) == NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ "ssl_client_hello_by_lua*" \ : (c) == NGX_STREAM_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ + : (c) == NGX_STREAM_LUA_CONTEXT_ACCESS ? "access_by_lua*" \ : "(unknown)") diff --git a/t/024-access/sanity.t b/t/024-access/sanity.t new file mode 100644 index 00000000..1c88b8f4 --- /dev/null +++ b/t/024-access/sanity.t @@ -0,0 +1,171 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; +#worker_connections(1014); +#no_nginx_manager(); +#log_level('debug'); +#master_on(); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2); + +#no_diff(); +#no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: basic print +--- stream_server_config + access_by_lua_block { ngx.print("Hello, Lua!\n") } + + content_by_lua return; + #content_by_lua 'ngx.say("Hi")'; +--- stream_response +Hello, Lua! + + + +=== TEST 2: basic say +--- stream_server_config + access_by_lua_block { + ngx.say("Hello, Lua!") + ngx.say("Yay! ", 123); + } + + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +Hello, Lua! +Yay! 123 + + + +=== TEST 3: no ngx.echo +--- stream_server_config + access_by_lua_block { ngx.echo("Hello, Lua!\\n") } + content_by_lua_block { ngx.exit(ngx.OK) } +--- error_log +attempt to call field 'echo' (a nil value) + + + +=== TEST 4: variable +--- stream_server_config + # NOTE: the newline escape sequence must be double-escaped, as nginx config + # parser will unescape first! + access_by_lua_block { v = ngx.var["remote_addr"] ngx.print("remote_addr: ", v, "\n") } + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +remote_addr: 127.0.0.1 + + + +=== TEST 5: variable (file) +--- stream_server_config + access_by_lua_file html/test.lua; + content_by_lua_block { ngx.exit(ngx.OK) } +--- user_files +>>> test.lua +v = ngx.var["remote_addr"] +ngx.print("remote_addr: ", v, "\n") +--- stream_response +remote_addr: 127.0.0.1 + + + +=== TEST 6: nil is "nil" +--- stream_server_config + access_by_lua_block { ngx.say(nil) } + + content_by_lua return; +--- stream_response +nil + + + +=== TEST 7: write boolean +--- stream_server_config + access_by_lua_block { ngx.say(true, " ", false) } + + content_by_lua return; +--- stream_response +true false + + + +=== TEST 8: nginx quote sql string 1 +--- stream_server_config + access_by_lua_block { ngx.say(ngx.quote_sql_str('hello\n\r\'"\\')) } + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +'hello\n\r\'\"\\' + + + +=== TEST 9: nginx quote sql string 2 +--- stream_server_config + access_by_lua_block { ngx.say(ngx.quote_sql_str("hello\n\r'\"\\")) } + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +'hello\n\r\'\"\\' + + + +=== TEST 10: use dollar +--- stream_server_config + access_by_lua_block { + local s = "hello 112"; + ngx.say(string.find(s, "%d+$")); + } + + content_by_lua_block { ngx.exit(ngx.OK) } +--- stream_response +79 + + + +=== TEST 11: short circuit +--- stream_server_config + access_by_lua_block { + ngx.say("Hi") + ngx.eof() + ngx.exit(ngx.OK) + } + + content_by_lua_block { + print("HERE") + ngx.print("BAD") + } +--- stream_response +Hi + + + +=== TEST 12: nginx vars in script path +--- stream_server_config + access_by_lua_file html/$remote_addr.lua; + + content_by_lua_block { + print("HERE") + ngx.print("BAD") + } +--- user_files +>>> 127.0.0.1.lua +ngx.say("Hi") +ngx.eof() +ngx.exit(ngx.OK) +--- stream_response +Hi + + + +=== TEST 14: Lua file does not exist +--- stream_server_config + access_by_lua_file html/test2.lua; + return here; +--- user_files +>>> test.lua +v = ngx.var["remote_addr"] +ngx.print("remote_addr: ", v, "\n") +--- error_log eval +qr/failed to load external Lua file ".*?\btest2\.lua": cannot open .*? No such file or directory/ diff --git a/t/089-phase.t b/t/089-phase.t index c00773e1..39e96b85 100644 --- a/t/089-phase.t +++ b/t/089-phase.t @@ -28,13 +28,14 @@ init === TEST 2: get_phase in access_by_lua -TODO ---- SKIP --- stream_server_config access_by_lua_block { ngx.say(ngx.get_phase()) ngx.exit(200) } + content_by_lua_block { + return; + } --- stream_response access diff --git a/t/132-lua-blocks.t b/t/132-lua-blocks.t index eeba068a..973b56ce 100644 --- a/t/132-lua-blocks.t +++ b/t/132-lua-blocks.t @@ -9,7 +9,7 @@ use Test::Nginx::Socket::Lua::Stream; repeat_each(2); #repeat_each(1); -plan tests => repeat_each() * ((blocks() * 3) + 1); +plan tests => repeat_each() * ((blocks() * 3) + 3); #no_diff(); no_long_string(); @@ -207,13 +207,12 @@ close: 1 nil glob = glob .. ", init worker }here{" } --- stream_server_config -#access_by_lua_block { - #local s = ngx.var.a - #s = s .. '}access{\n' - #ngx.var.a = s - #} + access_by_lua_block { + local s = '}access{\n' + ngx.ctx.a = s + } content_by_lua_block { - s = [[}content{]] + local s = ngx.ctx.a .. [[}content{]] ngx.ctx.a = s ngx.say(s) ngx.say("glob: ", glob) @@ -224,6 +223,7 @@ close: 1 nil --- config --- stream_response +}access{ }content{ glob: init by lua }here{, init worker }here{ @@ -369,8 +369,6 @@ done === TEST 17: double quotes in long brackets -TODO ---- SKIP --- stream_server_config access_by_lua_block { print([[Hey, it is "!]]) } content_by_lua_block { ngx.say([["]]) } @@ -385,8 +383,6 @@ Hey, it is "! === TEST 18: single quotes in long brackets -TODO ---- SKIP --- stream_server_config access_by_lua_block { print([[Hey, it is '!]]) } content_by_lua_block { ngx.say([[']]) }