diff --git a/README.markdown b/README.markdown index 6839efc6bd..7309633383 100644 --- a/README.markdown +++ b/README.markdown @@ -5460,7 +5460,7 @@ ngx.exit **syntax:** *ngx.exit(status)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** +**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua** When `status >= 200` (i.e., `ngx.HTTP_OK` and above), it will interrupt the execution of the current request and return status code to Nginx. @@ -5505,7 +5505,7 @@ Note that while this method accepts all [HTTP status constants](#http-status-con Also note that this method call terminates the processing of the current request and that it is recommended that a coding style that combines this method call with the `return` statement, i.e., `return ngx.exit(...)` be used to reinforce the fact that the request processing is being terminated. -When being used in the contexts of [header_filter_by_lua*](#header_filter_by_lua), [balancer_by_lua*](#balancer_by_lua_block), and +When being used in the contexts of [header_filter_by_lua*](#header_filter_by_lua), [body_filter_by_lua*](#body_filter_by_lua_block), [balancer_by_lua*](#balancer_by_lua_block), and [ssl_session_store_by_lua*](#ssl_session_store_by_lua_block), `ngx.exit()` is an asynchronous operation and will return immediately. This behavior may change in future and it is recommended that users always use `return` in combination as suggested above. diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 91409d2109..4129f7b4f9 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -4579,7 +4579,7 @@ Since v0.8.3 this function returns 1 on success, or re '''syntax:''' ''ngx.exit(status)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' +'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*'' When status >= 200 (i.e., ngx.HTTP_OK and above), it will interrupt the execution of the current request and return status code to Nginx. @@ -4621,7 +4621,7 @@ Note that while this method accepts all [[#HTTP status constants|HTTP status con Also note that this method call terminates the processing of the current request and that it is recommended that a coding style that combines this method call with the return statement, i.e., return ngx.exit(...) be used to reinforce the fact that the request processing is being terminated. -When being used in the contexts of [[#header_filter_by_lua|header_filter_by_lua*]], [[#balancer_by_lua_block|balancer_by_lua*]], and +When being used in the contexts of [[#header_filter_by_lua|header_filter_by_lua*]], [[#body_filter_by_lua_block|body_filter_by_lua*]], [[#balancer_by_lua_block|balancer_by_lua*]], and [[#ssl_session_store_by_lua_block|ssl_session_store_by_lua*]], ngx.exit() is an asynchronous operation and will return immediately. This behavior may change in future and it is recommended that users always use return in combination as suggested above. diff --git a/src/ngx_http_lua_bodyfilterby.c b/src/ngx_http_lua_bodyfilterby.c index 75608695e1..b9c493f084 100644 --- a/src/ngx_http_lua_bodyfilterby.c +++ b/src/ngx_http_lua_bodyfilterby.c @@ -82,12 +82,15 @@ ngx_int_t ngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; - u_char *err_msg; - size_t len; + ngx_int_t rc; + u_char *err_msg; + size_t len; #if (NGX_PCRE) - ngx_pool_t *old_pool; + ngx_pool_t *old_pool; #endif + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("initialize nginx context in Lua VM, code chunk at stack top sp = 1"); ngx_http_lua_body_filter_by_lua_env(L, r, in); @@ -110,6 +113,8 @@ ngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_http_lua_pcre_malloc_done(old_pool); #endif + dd("rc == %d", (int) rc); + if (rc != 0) { /* error occurred */ @@ -136,7 +141,10 @@ ngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r, lua_settop(L, 0); - if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + if (ctx->body_filter_aborted + || rc == NGX_ERROR + || rc >= NGX_HTTP_SPECIAL_RESPONSE) + { return NGX_ERROR; } @@ -166,15 +174,9 @@ ngx_http_lua_body_filter_inline(ngx_http_request_t *r, ngx_chain_t *in) return NGX_ERROR; } - rc = ngx_http_lua_body_filter_by_chunk(L, r, in); - - dd("body filter by chunk returns %d", (int) rc); - - if (rc != NGX_OK) { - return NGX_ERROR; - } + dd("calling body filter by chunk"); - return NGX_OK; + return ngx_http_lua_body_filter_by_chunk(L, r, in); } @@ -216,13 +218,7 @@ ngx_http_lua_body_filter_file(ngx_http_request_t *r, ngx_chain_t *in) /* make sure we have a valid code chunk */ ngx_http_lua_assert(lua_isfunction(L, -1)); - rc = ngx_http_lua_body_filter_by_chunk(L, r, in); - - if (rc != NGX_OK) { - return NGX_ERROR; - } - - return NGX_OK; + return ngx_http_lua_body_filter_by_chunk(L, r, in); } @@ -262,6 +258,12 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } } + if (ctx->body_filter_aborted) { + /* maybe there is a bug in the output body filter caller, other + * outputs will be intercepted here */ + return NGX_ERROR; + } + if (ctx->seen_last_in_filter) { for (/* void */; in; in = in->next) { dd("mark the buf as consumed: %d", (int) ngx_buf_size(in->buf)); diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 781a2454a1..7eb113127d 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -559,6 +559,8 @@ typedef struct ngx_http_lua_ctx_s { unsigned exited:1; + unsigned body_filter_aborted:1; + unsigned eof:1; /* 1: last_buf has been sent; 0: last_buf not sent yet */ diff --git a/src/ngx_http_lua_control.c b/src/ngx_http_lua_control.c index 3d92d88e23..d6f86061cb 100644 --- a/src/ngx_http_lua_control.c +++ b/src/ngx_http_lua_control.c @@ -365,6 +365,7 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER + | NGX_HTTP_LUA_CONTEXT_BODY_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE @@ -375,6 +376,19 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, return NGX_ERROR; } + if (ctx->context & NGX_HTTP_LUA_CONTEXT_BODY_FILTER) { + + if (status != NGX_ERROR && status != NGX_HTTP_CLOSE) { + *errlen = ngx_snprintf(err, *errlen, "attempt to exit with the " + "code not 444 or ngx.ERROR") + - err; + return NGX_ERROR; + } + + ctx->body_filter_aborted = 1; + return NGX_DONE; + } + if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)) diff --git a/t/082-body-filter.t b/t/082-body-filter.t index 5f765fa439..a84bc99a9b 100644 --- a/t/082-body-filter.t +++ b/t/082-body-filter.t @@ -10,7 +10,7 @@ log_level('debug'); repeat_each(2); -plan tests => repeat_each() * (blocks() * 3 + 11); +plan tests => repeat_each() * (blocks() * 3 + 8); #no_diff(); no_long_string(); @@ -838,3 +838,46 @@ GET /lua --- ignore_response --- error_log API disabled in the context of body_filter_by_lua* + + + +=== TEST 27: exit 444 +--- config + location = /t { + content_by_lua_block { + ngx.say("111") + ngx.say("222") + ngx.say("333") + } + + body_filter_by_lua_block { + ngx.exit(444) + } + } +--- request + GET /t +--- ignore_response +--- no_error_log +[error] +[alert] + + + +=== TEST 28: exit OK +--- config + location = /t { + content_by_lua_block { + ngx.say("111") + ngx.say("222") + ngx.say("333") + } + + body_filter_by_lua_block { + ngx.exit(200) + } + } +--- request + GET /t +--- ignore_response +--- error_log +attempt to exit with the code not 444 or ngx.ERROR