static void ngx_http_lua_request_cleanup(void *data) { ngx_http_request_t *r = data; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_ctx_t *ctx; lua_State *L; dd("(lua-request-cleanup) force request coroutine quit"); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); L = lmcf->lua; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); /* force coroutine handling the request quit */ if (ctx == NULL) { return; } if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } lua_getfield(L, LUA_REGISTRYINDEX, NGX_LUA_CORT_REF); lua_rawgeti(L, -1, ctx->cc_ref); if (lua_isthread(L, -1)) { /* coroutine not finished yet, force quit */ ngx_http_lua_del_thread(r, L, ctx->cc_ref, 1); #if 0 ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); #endif } lua_pop(L, 2); }
void ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx) { if (ctx->cc_ref != LUA_NOREF) { ngx_http_lua_del_thread(r, L, ctx->cc_ref, 0); ctx->cc_ref = LUA_NOREF; } ctx->waiting = 0; ctx->done = 0; ctx->entered_rewrite_phase = 0; ctx->entered_access_phase = 0; ctx->exit_code = 0; ctx->exited = 0; ctx->exec_uri.data = NULL; ctx->exec_uri.len = 0; ctx->sr_statuses = NULL; ctx->sr_headers = NULL; ctx->sr_bodies = NULL; }
ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int nret) { int rv; int cc_ref; lua_State *cc; const char *err, *msg; ngx_int_t rc; /* set Lua VM panic handler */ lua_atpanic(L, ngx_http_lua_atpanic); dd("ctx = %p", ctx); NGX_LUA_EXCEPTION_TRY { cc = ctx->cc; cc_ref = ctx->cc_ref; /* run code */ rv = lua_resume(cc, nret); dd("lua resume returns %d", (int) rv); switch (rv) { case LUA_YIELD: /* yielded, let event handler do the rest job */ /* FIXME: add io cmd dispatcher here */ dd("lua coroutine yielded"); #if 0 ngx_http_lua_dump_postponed(r); #endif lua_settop(cc, 0); return NGX_AGAIN; case 0: dd("normal end %.*s", (int) r->uri.len, r->uri.data); #if 0 ngx_http_lua_dump_postponed(r); #endif ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { dd("cleaning up cleanup"); *ctx->cleanup = NULL; ctx->cleanup = NULL; } if (ctx->entered_content_phase) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } return NGX_OK; case LUA_ERRRUN: err = "runtime error"; break; case LUA_ERRSYNTAX: err = "syntax error"; break; case LUA_ERRMEM: err = "memory allocation error"; break; case LUA_ERRERR: err = "error handler error"; break; default: err = "unknown error"; break; } if (lua_isstring(cc, -1)) { dd("user custom error msg"); msg = lua_tostring(cc, -1); } else { if (lua_isnil(cc, -1)) { if (ctx->exited) { dd("run here...exiting... %d", (int) ctx->exit_code); ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } if (ctx->exit_code == NGX_OK) { if (ctx->entered_content_phase) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } } return ctx->exit_code; } if (ctx->exec_uri.len) { ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } if (ctx->exec_uri.data[0] == '@') { if (ctx->exec_args.len > 0) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "query strings %V ignored when exec'ing " "named location %V", &ctx->exec_args, &ctx->exec_uri); } #if defined(nginx_version) && nginx_version >= 8011 /* ngx_http_named_location always increments * r->main->count, which is not we want for * non-content phases */ if (! ctx->entered_content_phase) { r->main->count--; } #endif return ngx_http_named_location(r, &ctx->exec_uri); } dd("internal redirect to %.*s", (int) ctx->exec_uri.len, ctx->exec_uri.data); #if defined(nginx_version) && nginx_version >= 8011 /* ngx_http_internal_redirect always increments * r->main->count, which is not we want for * non-content phases */ if (! ctx->entered_content_phase) { r->main->count--; } #endif return ngx_http_internal_redirect(r, &ctx->exec_uri, &ctx->exec_args); } } msg = "unknown reason"; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "content_by_lua aborted: %s: %s", err, msg); ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } dd("headers sent? %d", ctx->headers_sent ? 1 : 0); return ctx->headers_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; } NGX_LUA_EXCEPTION_CATCH { dd("nginx execution restored"); } return NGX_ERROR; }
ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int nret) { int rv; int cc_ref; lua_State *cc; const char *err, *msg; ngx_int_t rc; /* set Lua VM panic handler */ lua_atpanic(L, ngx_http_lua_atpanic); dd("ctx = %p", ctx); NGX_LUA_EXCEPTION_TRY { cc = ctx->cc; cc_ref = ctx->cc_ref; /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_init(r->pool); /* run code */ rv = lua_resume(cc, nret); /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_done(); dd("lua resume returns %d", (int) rv); switch (rv) { case LUA_YIELD: /* yielded, let event handler do the rest job */ /* FIXME: add io cmd dispatcher here */ dd("lua coroutine yielded"); #if 0 ngx_http_lua_dump_postponed(r); #endif lua_settop(cc, 0); return NGX_AGAIN; case 0: dd("normal end %.*s", (int) r->uri.len, r->uri.data); #if 0 ngx_http_lua_dump_postponed(r); #endif ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { dd("cleaning up cleanup"); *ctx->cleanup = NULL; ctx->cleanup = NULL; } if (ctx->entered_content_phase) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } return NGX_OK; case LUA_ERRRUN: err = "runtime error"; break; case LUA_ERRSYNTAX: err = "syntax error"; break; case LUA_ERRMEM: err = "memory allocation error"; break; case LUA_ERRERR: err = "error handler error"; break; default: err = "unknown error"; break; } if (lua_isstring(cc, -1)) { dd("user custom error msg"); msg = lua_tostring(cc, -1); } else { if (lua_isnil(cc, -1)) { if (ctx->exited) { dd("run here...exiting... %d", (int) ctx->exit_code); ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } if ((ctx->exit_code == NGX_OK && ctx->entered_content_phase) || (ctx->exit_code >= NGX_HTTP_OK && ctx->exit_code < NGX_HTTP_SPECIAL_RESPONSE)) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } return ctx->exit_code; } if (ctx->exec_uri.len) { ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } if (ctx->exec_uri.data[0] == '@') { if (ctx->exec_args.len > 0) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "query strings %V ignored when exec'ing " "named location %V", &ctx->exec_args, &ctx->exec_uri); } r->write_event_handler = ngx_http_request_empty_handler; rc = ngx_http_named_location(r, &ctx->exec_uri); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (! ctx->entered_content_phase && r != r->connection->data) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ r->main->count--; } return NGX_DONE; } dd("internal redirect to %.*s", (int) ctx->exec_uri.len, ctx->exec_uri.data); /* resume the write event handler */ r->write_event_handler = ngx_http_request_empty_handler; rc = ngx_http_internal_redirect(r, &ctx->exec_uri, &ctx->exec_args); dd("internal redirect returned %d when in content phase? " "%d", (int) rc, ctx->entered_content_phase); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } dd("XXYY HERE %d\n", (int) r->main->count); if (! ctx->entered_content_phase && r != r->connection->data) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ r->main->count--; } return NGX_DONE; } } msg = "unknown reason"; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua handler aborted: %s: %s", err, msg); ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } dd("headers sent? %d", ctx->headers_sent ? 1 : 0); return ctx->headers_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; } NGX_LUA_EXCEPTION_CATCH { dd("nginx execution restored"); } return NGX_ERROR; }
static int ngx_http_lua_uthread_wait(lua_State *L) { int i, nargs, nrets; lua_State *sub_co; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx, *sub_coctx; lua_pushlightuserdata(L, &ngx_http_lua_request_key); lua_rawget(L, LUA_GLOBALSINDEX); r = lua_touserdata(L, -1); lua_pop(L, 1); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); coctx = ctx->cur_co_ctx; nargs = lua_gettop(L); for (i = 1; i <= nargs; i++) { sub_co = lua_tothread(L, i); luaL_argcheck(L, sub_co, i, "lua thread expected"); sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx); if (sub_coctx == NULL) { return luaL_error(L, "no co ctx found for the ngx.thread " "instance given"); } if (!sub_coctx->is_uthread) { return luaL_error(L, "attempt to wait on a coroutine that is " "not a user thread"); } if (sub_coctx->parent_co_ctx != coctx) { return luaL_error(L, "only the parent coroutine can wait on the " "thread"); } switch (sub_coctx->co_status) { case NGX_HTTP_LUA_CO_ZOMBIE: ngx_http_lua_probe_info("found zombie child"); nrets = lua_gettop(sub_coctx->co); dd("child retval count: %d, %s: %s", n, luaL_typename(sub_coctx->co, -1), lua_tostring(sub_coctx->co, -1)); if (nrets) { lua_xmove(sub_coctx->co, L, nrets); } #if 1 ngx_http_lua_del_thread(r, L, ctx, sub_coctx); ctx->uthreads--; #endif return nrets; default: /* still alive */ break; } sub_coctx->waited_by_parent = 1; } return lua_yield(L, 0); }
static int ngx_http_lua_uthread_kill(lua_State *L) { int i, nargs; int killed; lua_State *sub_co; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx, *sub_coctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); coctx = ctx->cur_co_ctx; nargs = lua_gettop(L); killed = 0; for (i = 1; i <= nargs; i++) { sub_co = lua_tothread(L, i); luaL_argcheck(L, sub_co, i, "lua thread expected"); sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx); if (sub_coctx == NULL || sub_coctx->co_status == NGX_HTTP_LUA_CO_DEAD) { /* XXX The thread is dead, consider it a success and continue. */ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "attempt to kill an already dead thread"); killed++; continue; } if (!sub_coctx->is_uthread) { return luaL_error(L, "attempt to kill a coroutine that is " "not a user thread"); } if (sub_coctx->parent_co_ctx != coctx) { return luaL_error(L, "only the parent coroutine can kill the " "thread"); } if (sub_coctx->nsubreqs) { /* XXX Don't kill threads with pending subrequests. */ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to kill thread with pending subrequests"); continue; } if (sub_coctx->cleanup) { sub_coctx->cleanup(sub_coctx); sub_coctx->cleanup = NULL; } if (ngx_http_lua_del_thread(r, L, ctx, sub_coctx) == NGX_OK) { killed++; ctx->uthreads--; } } lua_pushinteger(L, killed); return 1; }
static int ngx_http_lua_uthread_wait(lua_State *L) { int i, nargs, nrets; lua_State *sub_co; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx, *sub_coctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); coctx = ctx->cur_co_ctx; nargs = lua_gettop(L); for (i = 1; i <= nargs; i++) { sub_co = lua_tothread(L, i); luaL_argcheck(L, sub_co, i, "lua thread expected"); sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx); if (sub_coctx == NULL) { goto found_dead; } if (!sub_coctx->is_uthread) { return luaL_error(L, "attempt to wait on a coroutine that is " "not a user thread"); } if (sub_coctx->parent_co_ctx != coctx) { return luaL_error(L, "only the parent coroutine can wait on the " "thread"); } switch (sub_coctx->co_status) { case NGX_HTTP_LUA_CO_ZOMBIE: ngx_http_lua_probe_info("found zombie child"); nrets = lua_gettop(sub_coctx->co); dd("child retval count: %d, %s: %s", (int) nrets, luaL_typename(sub_coctx->co, -1), lua_tostring(sub_coctx->co, -1)); if (nrets) { lua_xmove(sub_coctx->co, L, nrets); } #if 1 ngx_http_lua_del_thread(r, L, ctx, sub_coctx); ctx->uthreads--; #endif return nrets; case NGX_HTTP_LUA_CO_DEAD: dd("uthread already waited: %p (parent %p)", sub_coctx, coctx); found_dead: if (i < nargs) { /* just ignore it if it is not the last one */ continue; } /* being the last one */ lua_pushnil(L); lua_pushliteral(L, "already waited"); return 2; default: dd("uthread %p still alive, status: %d, parent %p", sub_coctx, sub_coctx->co_status, coctx); break; } ngx_http_lua_probe_user_thread_wait(L, sub_coctx->co); sub_coctx->waited_by_parent = 1; } return lua_yield(L, 0); }
static int ngx_http_lua_uthread_kill(lua_State *L) { lua_State *sub_co; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx, *sub_coctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); coctx = ctx->cur_co_ctx; sub_co = lua_tothread(L, 1); luaL_argcheck(L, sub_co, 1, "lua thread expected"); sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx); if (sub_coctx == NULL) { return luaL_error(L, "no co ctx found"); } if (!sub_coctx->is_uthread) { lua_pushnil(L); lua_pushliteral(L, "not user thread"); return 2; } if (sub_coctx->parent_co_ctx != coctx) { lua_pushnil(L); lua_pushliteral(L, "killer not parent"); return 2; } if (sub_coctx->pending_subreqs > 0) { lua_pushnil(L); lua_pushliteral(L, "pending subrequests"); return 2; } switch (sub_coctx->co_status) { case NGX_HTTP_LUA_CO_ZOMBIE: ngx_http_lua_del_thread(r, L, ctx, sub_coctx); ctx->uthreads--; lua_pushnil(L); lua_pushliteral(L, "already terminated"); return 2; case NGX_HTTP_LUA_CO_DEAD: lua_pushnil(L); lua_pushliteral(L, "already waited or killed"); return 2; default: ngx_http_lua_cleanup_pending_operation(sub_coctx); ngx_http_lua_del_thread(r, L, ctx, sub_coctx); ctx->uthreads--; lua_pushinteger(L, 1); return 1; } /* not reacheable */ }
static ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int nret) { int rc; int cc_ref; lua_State *cc; const char *err, *msg; /* set Lua VM panic handler */ lua_atpanic(L, ngx_http_lua_atpanic); NGX_LUA_EXCEPTION_TRY { cc = ctx->cc; cc_ref = ctx->cc_ref; /* run code */ rc = lua_resume(cc, nret); dd("lua resume returns %d", (int) rc); switch (rc) { case LUA_YIELD: /* yielded, let event handler do the rest job */ /* FIXME: add io cmd dispatcher here */ lua_settop(cc, 0); return NGX_AGAIN; break; case 0: /* normal end */ ngx_http_lua_del_thread(r, L, cc_ref, 0); if (ctx->cleanup) { dd("cleaning up cleanup"); *ctx->cleanup = NULL; ctx->cleanup = NULL; } ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); return NGX_OK; break; case LUA_ERRRUN: err = "runtime error"; break; case LUA_ERRSYNTAX: err = "syntax error"; break; case LUA_ERRMEM: err = "memory allocation error"; break; case LUA_ERRERR: err = "error handler error"; break; default: err = "unknown error"; break; } if (lua_isstring(cc, -1)) { dd("user custom error msg"); msg = lua_tostring(cc, -1); } else { if (lua_isnil(cc, -1) && ctx->error_rc != 0) { dd("run here...throwing..."); ngx_http_lua_del_thread(r, L, cc_ref, 0); if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } return ctx->error_rc; } msg = "unknown reason"; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "content_by_lua prematurely ended: %s: %s", err, msg); ngx_http_lua_del_thread(r, L, cc_ref, 0); if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } dd("headers sent? %d", ctx->headers_sent ? 1 : 0); return ctx->headers_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; } NGX_LUA_EXCEPTION_CATCH { dd("NginX execution restored"); } return NGX_ERROR; }