ngx_int_t ngx_http_lua_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc) { ngx_http_request_t *pr; ngx_http_lua_ctx_t *pr_ctx; ngx_http_lua_ctx_t *ctx = data; size_t len; ngx_str_t *body_str; u_char *p; ngx_chain_t *cl; if (ctx->run_post_subrequest) { return rc; } ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run post subrequest handler: rc:%d, waiting:%d, done:%d", rc, ctx->waiting, ctx->done); ctx->run_post_subrequest = 1; #if 0 ngx_http_lua_dump_postponed(r); #endif pr = r->parent; pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_lua_module); if (pr_ctx == NULL) { return NGX_ERROR; } pr_ctx->waiting--; if (pr_ctx->waiting == 0) { pr_ctx->done = 1; } if (pr_ctx->entered_content_phase) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua restoring write event handler"); pr->write_event_handler = ngx_http_lua_content_wev_handler; } dd("status rc = %d", (int) rc); dd("status headers_out.status = %d", (int) r->headers_out.status); dd("uri: %.*s", (int) r->uri.len, r->uri.data); /* capture subrequest response status */ if (rc == NGX_ERROR) { pr_ctx->sr_statuses[ctx->index] = NGX_HTTP_INTERNAL_SERVER_ERROR; } else if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { dd("HERE"); pr_ctx->sr_statuses[ctx->index] = rc; } else { dd("THERE"); pr_ctx->sr_statuses[ctx->index] = r->headers_out.status; } if (pr_ctx->sr_statuses[ctx->index] == 0) { pr_ctx->sr_statuses[ctx->index] = NGX_HTTP_OK; } dd("pr_ctx status: %d", (int) pr_ctx->sr_statuses[ctx->index]); /* copy subrequest response headers */ pr_ctx->sr_headers[ctx->index] = &r->headers_out; /* copy subrequest response body */ body_str = &pr_ctx->sr_bodies[ctx->index]; len = 0; for (cl = ctx->body; cl; cl = cl->next) { /* ignore all non-memory buffers */ len += cl->buf->last - cl->buf->pos; } body_str->len = len; if (len == 0) { body_str->data = NULL; } else { p = ngx_palloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } body_str->data = p; /* copy from and then free the data buffers */ for (cl = ctx->body; cl; cl = cl->next) { p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); cl->buf->last = cl->buf->pos; #if 0 dd("free body chain link buf ASAP"); ngx_pfree(r->pool, cl->buf->start); #endif } } if (ctx->body) { #if defined(nginx_version) && nginx_version >= 1001004 ngx_chain_update_chains(r->pool, #else ngx_chain_update_chains( #endif &pr_ctx->free_bufs, &pr_ctx->busy_bufs, &ctx->body, (ngx_buf_tag_t) &ngx_http_lua_module); dd("free bufs: %p", pr_ctx->free_bufs); }
ngx_int_t 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; lua_State *cc; ngx_str_t *body_str; ngx_http_headers_out_t *sr_headers; ngx_list_part_t *part; ngx_table_elt_t *header; ngx_uint_t i, index; dd("wev handler %.*s %.*s a:%d, postponed:%p", (int) r->uri.len, r->uri.data, (int) ngx_cached_err_log_time.len, ngx_cached_err_log_time.data, r == r->connection->data, r->postponed); #if 0 ngx_http_lua_dump_postponed(r); #endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { goto error; } dd("ctx = %p", ctx); dd("request done: %d", (int) r->done); dd("cleanup done: %p", ctx->cleanup); if (ctx->cleanup == NULL) { /* already done */ dd("cleanup is null: %.*s", (int) r->uri.len, r->uri.data); if (ctx->entered_content_phase) { ngx_http_finalize_request(r, ngx_http_lua_flush_postponed_outputs(r)); } return NGX_DONE; } dd("waiting: %d, done: %d", (int) ctx->waiting, ctx->done); if (ctx->waiting && ! ctx->done) { dd("%.*s waiting and not done", (int) r->uri.len, r->uri.data); #if 0 ngx_http_lua_dump_postponed(r); #endif if (r == r->connection->data && r->postponed) { if (r->postponed->request) { r->connection->data = r->postponed->request; #if defined(nginx_version) && nginx_version >= 8012 ngx_http_post_request(r->postponed->request, NULL); #else ngx_http_post_request(r->postponed->request); #endif } else { ngx_http_lua_flush_postponed_outputs(r); } } return NGX_DONE; } ctx->done = 0; dd("nsubreqs: %d", (int) ctx->nsubreqs); for (index = 0; index < ctx->nsubreqs; index++) { dd("summary: reqs %d, subquery %d, waiting %d, req %.*s", (int) ctx->nsubreqs, (int) index, (int) ctx->waiting, (int) r->uri.len, r->uri.data); cc = ctx->cc; /* {{{ construct ret value */ lua_newtable(cc); /* copy captured status */ lua_pushinteger(cc, ctx->sr_statuses[index]); lua_setfield(cc, -2, "status"); /* copy captured body */ body_str = &ctx->sr_bodies[index]; lua_pushlstring(cc, (char *) body_str->data, body_str->len); lua_setfield(cc, -2, "body"); if (body_str->data) { dd("free body buffer ASAP"); ngx_pfree(r->pool, body_str->data); } /* copy captured headers */ lua_newtable(cc); /* res.header */ sr_headers = ctx->sr_headers[index]; if (sr_headers->content_length == NULL && sr_headers->content_length_n >= 0) { lua_pushliteral(cc, "Content-Length"); /* header key */ lua_pushnumber(cc, sr_headers->content_length_n); /* head key value */ lua_rawset(cc, -3); /* head */ } if (sr_headers->content_type.len) { lua_pushliteral(cc, "Content-Type"); /* header key */ lua_pushlstring(cc, (char *) sr_headers->content_type.data, sr_headers->content_type.len); /* head key value */ lua_rawset(cc, -3); /* head */ } dd("saving subrequest response headers"); part = &sr_headers->headers.part; header = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } dd("checking sr header %.*s", (int) header[i].key.len, header[i].key.data); #if 1 if (header[i].hash == 0) { continue; } #endif dd("pushing sr header %.*s", (int) header[i].key.len, header[i].key.data); lua_pushlstring(cc, (char *) header[i].key.data, header[i].key.len); /* header key */ lua_pushvalue(cc, -1); /* stack: table key key */ /* check if header already exists */ lua_rawget(cc, -3); /* stack: table key value */ if (lua_isnil(cc, -1)) { lua_pop(cc, 1); /* stack: table key */ lua_pushlstring(cc, (char *) header[i].value.data, header[i].value.len); /* stack: table key value */ lua_rawset(cc, -3); /* stack: table */ } else { if (! lua_istable(cc, -1)) { /* already inserted one value */ lua_createtable(cc, 4, 0); /* stack: table key value table */ lua_insert(cc, -2); /* stack: table key table value */ lua_rawseti(cc, -2, 1); /* stack: table key table */ lua_pushlstring(cc, (char *) header[i].value.data, header[i].value.len); /* stack: table key table value */ lua_rawseti(cc, -2, lua_objlen(cc, -2) + 1); /* stack: table key table */ lua_rawset(cc, -3); /* stack: table */ } else { lua_pushlstring(cc, (char *) header[i].value.data, header[i].value.len); /* stack: table key table value */ lua_rawseti(cc, -2, lua_objlen(cc, -2) + 1); /* stack: table key table */ lua_pop(cc, 2); /* stack: table */ } } } lua_setfield(cc, -2, "header"); /* }}} */ } dd("free sr_statues/headers/bodies memory ASAP"); #if 1 ngx_pfree(r->pool, ctx->sr_statuses); ctx->sr_statuses = NULL; ctx->sr_headers = NULL; ctx->sr_bodies = NULL; #endif 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); dd("already run thread for %.*s...", (int) r->uri.len, r->uri.data); if (rc == NGX_AGAIN || rc == NGX_DONE) { return NGX_DONE; } dd("entered content phase: %d", (int) ctx->entered_content_phase); if (ctx->entered_content_phase) { ngx_http_finalize_request(r, rc); } if (rc == NGX_OK) { return NGX_DECLINED; } return rc; error: if (ctx->entered_content_phase) { ngx_http_finalize_request(r, ctx->headers_sent ? NGX_ERROR: NGX_HTTP_INTERNAL_SERVER_ERROR); } 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; /* 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; }