static int ngx_http_lua_socket_udp(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; if (lua_gettop(L) != 0) { return luaL_error(L, "expecting zero arguments, but got %d", lua_gettop(L)); } 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 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); lua_createtable(L, 3 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, &ngx_http_lua_socket_udp_metatable_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); dd("top: %d", lua_gettop(L)); return 1; }
static int ngx_http_lua_ngx_sleep(lua_State *L) { int n; ngx_int_t delay; /* in msec */ ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000); if (delay < 0) { return luaL_error(L, "invalid sleep duration \"%d\"", delay); } 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; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } coctx->data = r; coctx->sleep.handler = ngx_http_lua_sleep_handler; coctx->sleep.data = coctx; coctx->sleep.log = r->connection->log; dd("adding timer with delay %lu ms, r:%.*s", (unsigned long) delay, (int) r->uri.len, r->uri.data); ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay); ngx_http_lua_cleanup_pending_operation(coctx); coctx->cleanup = ngx_http_lua_sleep_cleanup; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua ready to sleep for %d ms", delay); return lua_yield(L, 0); }
/** * Send last_buf, terminate output stream * */ static int ngx_http_lua_ngx_eof(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } if (lua_gettop(L) != 0) { return luaL_error(L, "no argument is expected"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua send eof"); rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); dd("send chain: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } lua_pushinteger(L, 1); return 1; }
int ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx) { lua_State *vm; /* the Lua VM */ lua_State *co; /* new coroutine to be created */ ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); 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); vm = ngx_http_lua_get_lua_vm(r, ctx); /* create new coroutine on root Lua state, so it always yields * to main Lua thread */ co = lua_newthread(vm); ngx_http_lua_probe_user_coroutine_create(r, L, co); coctx = ngx_http_lua_create_co_ctx(r, ctx); if (coctx == NULL) { return luaL_error(L, "out of memory"); } coctx->co = co; coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED; /* make new coroutine share globals of the parent coroutine. * NOTE: globals don't have to be separated! */ lua_pushvalue(L, LUA_GLOBALSINDEX); lua_xmove(L, co, 1); lua_replace(co, LUA_GLOBALSINDEX); lua_xmove(vm, L, 1); /* move coroutine from main thread to L */ lua_pushvalue(L, 1); /* copy entry function to top of L*/ lua_xmove(L, co, 1); /* move entry function from L to co */ if (pcoctx) { *pcoctx = coctx; } return 1; /* return new coroutine to Lua */ }
static int ngx_http_lua_coroutine_yield(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *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); ctx->co_op = NGX_HTTP_LUA_USER_CORO_YIELD; coctx = ctx->cur_co_ctx; coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED; if (coctx->parent_co_ctx) { dd("set coroutine to running"); coctx->parent_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING; ngx_http_lua_probe_user_coroutine_yield(r, coctx->parent_co_ctx->co, L); } else { ngx_http_lua_probe_user_coroutine_yield(r, NULL, L); } /* yield and pass retvals to main thread, * and resume parent coroutine there */ return lua_yield(L, lua_gettop(L)); }
static int ngx_http_lua_coroutine_yield(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *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 | NGX_HTTP_LUA_CONTEXT_SSL_CERT); coctx = ctx->cur_co_ctx; coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED; ctx->co_op = NGX_HTTP_LUA_USER_CORO_YIELD; if (!coctx->is_uthread && coctx->parent_co_ctx) { dd("set coroutine to running"); coctx->parent_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING; ngx_http_lua_probe_user_coroutine_yield(r, coctx->parent_co_ctx->co, L); } else { ngx_http_lua_probe_user_coroutine_yield(r, NULL, L); } /* yield and pass retvals to main thread, * and resume parent coroutine there */ return lua_yield(L, lua_gettop(L)); }
static int ngx_http_lua_coroutine_status(lua_State *L) { lua_State *co; /* new coroutine to be created */ ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "coroutine expected"); 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 | NGX_HTTP_LUA_CONTEXT_SSL_CERT); coctx = ngx_http_lua_get_co_ctx(co, ctx); if (coctx == NULL) { lua_pushlstring(L, (const char *) ngx_http_lua_co_status_names[NGX_HTTP_LUA_CO_DEAD].data, ngx_http_lua_co_status_names[NGX_HTTP_LUA_CO_DEAD].len); return 1; } dd("co status: %d", coctx->co_status); lua_pushlstring(L, (const char *) ngx_http_lua_co_status_names[coctx->co_status].data, ngx_http_lua_co_status_names[coctx->co_status].len); return 1; }
static int ngx_http_lua_coroutine_status(lua_State *L) { lua_State *co; /* new coroutine to be created */ ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "coroutine expected"); 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 = ngx_http_lua_get_co_ctx(co, ctx); if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } dd("co status: %d", coctx->co_status); lua_pushstring(L, ngx_http_lua_co_status_names[coctx->co_status]); return 1; }
/** * Send out headers * */ static int ngx_http_lua_ngx_send_headers(lua_State *L) { ngx_int_t rc; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; 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 ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); if (!r->header_sent && !ctx->header_sent) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua send headers"); rc = ngx_http_lua_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } } lua_pushinteger(L, 1); return 1; }
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 */ }
/** * Force flush out response content * */ static int ngx_http_lua_ngx_flush(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_chain_t *cl; ngx_int_t rc; int n; unsigned wait = 0; ngx_event_t *wev; ngx_http_core_loc_conf_t *clcf; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n > 1) { return luaL_error(L, "attempt to pass %d arguments, but accepted 0 " "or 1", n); } r = ngx_http_lua_get_req(L); if (n == 1 && r == r->main) { luaL_checktype(L, 1, LUA_TBOOLEAN); wait = lua_toboolean(L, 1); } 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); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } if (r->header_only) { lua_pushnil(L); lua_pushliteral(L, "header only"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } if (ctx->buffering) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua http 1.0 buffering makes ngx.flush() a no-op"); lua_pushnil(L); lua_pushliteral(L, "buffering"); return 2; } #if 1 if (!r->header_sent && !ctx->header_sent) { lua_pushnil(L); lua_pushliteral(L, "nothing to flush"); return 2; } #endif cl = ngx_http_lua_get_flush_chain(r, ctx); if (cl == NULL) { return luaL_error(L, "no memory"); } rc = ngx_http_lua_send_chain_link(r, ctx, cl); dd("send chain: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } dd("wait:%d, rc:%d, buffered:0x%x", wait, (int) rc, r->connection->buffered); wev = r->connection->write; if (wait && (r->connection->buffered & NGX_HTTP_LOWLEVEL_BUFFERED || wev->delayed)) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua flush requires waiting: buffered 0x%uxd, " "delayed:%d", (unsigned) r->connection->buffered, wev->delayed); coctx->flushing = 1; ctx->flushing_coros++; if (ctx->entered_content_phase) { /* mimic ngx_http_set_write_handler */ r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (!wev->delayed) { ngx_add_timer(wev, clcf->send_timeout); } if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { if (wev->timer_set) { wev->delayed = 0; ngx_del_timer(wev); } lua_pushnil(L); lua_pushliteral(L, "connection broken"); return 2; } ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); coctx->cleanup = ngx_http_lua_flush_cleanup; coctx->data = r; return lua_yield(L, 0); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua flush asynchronously"); lua_pushinteger(L, 1); return 1; }
static int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; const char *p; size_t len; size_t size; ngx_buf_t *b; ngx_chain_t *cl; ngx_int_t rc; int i; int nargs; int type; const char *msg; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object 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); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } if (r->header_only) { lua_pushnil(L); lua_pushliteral(L, "header only"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } nargs = lua_gettop(L); size = 0; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; break; case LUA_TNIL: size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TTABLE: size += ngx_http_lua_calc_strlen_in_table(L, i, i, 0 /* strict */); break; case LUA_TLIGHTUSERDATA: dd("userdata: %p", lua_touserdata(L, i)); if (lua_touserdata(L, i) == NULL) { size += sizeof("null") - 1; break; } continue; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "ngx.null, or array table expected, " "but got %s", lua_typename(L, type)); return luaL_argerror(L, i, msg); } } if (newline) { size += sizeof("\n") - 1; } if (size == 0) { /* do nothing for empty strings */ lua_pushinteger(L, 1); return 1; } cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size); if (cl == NULL) { return luaL_error(L, "no memory"); } b = cl->buf; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = lua_tolstring(L, i, &len); b->last = ngx_copy(b->last, (u_char *) p, len); break; case LUA_TNIL: *b->last++ = 'n'; *b->last++ = 'i'; *b->last++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { *b->last++ = 't'; *b->last++ = 'r'; *b->last++ = 'u'; *b->last++ = 'e'; } else { *b->last++ = 'f'; *b->last++ = 'a'; *b->last++ = 'l'; *b->last++ = 's'; *b->last++ = 'e'; } break; case LUA_TTABLE: b->last = ngx_http_lua_copy_str_in_table(L, i, b->last); break; case LUA_TLIGHTUSERDATA: *b->last++ = 'n'; *b->last++ = 'u'; *b->last++ = 'l'; *b->last++ = 'l'; break; default: return luaL_error(L, "impossible to reach here"); } } if (newline) { *b->last++ = '\n'; } #if 0 if (b->last != b->end) { return luaL_error(L, "buffer error: %p != %p", b->last, b->end); } #endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, newline ? "lua say response" : "lua print response"); rc = ngx_http_lua_send_chain_link(r, ctx, cl); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } dd("downstream write: %d, buf len: %d", (int) rc, (int) (b->last - b->pos)); lua_pushinteger(L, 1); return 1; }
static int ngx_http_lua_ngx_req_set_uri(lua_State *L) { ngx_http_request_t *r; size_t len; u_char *p; int n; int jump = 0; ngx_http_lua_ctx_t *ctx; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 argument but seen %d", n); } lua_pushlightuserdata(L, &ngx_http_lua_request_key); lua_rawget(L, LUA_GLOBALSINDEX); r = lua_touserdata(L, -1); lua_pop(L, 1); if (n == 2) { luaL_checktype(L, 2, LUA_TBOOLEAN); jump = lua_toboolean(L, 2); if (jump) { ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } dd("rewrite: %d, access: %d, content: %d", (int) ctx->entered_rewrite_phase, (int) ctx->entered_access_phase, (int) ctx->entered_content_phase); ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua set uri jump to \"%V\"", &r->uri); ngx_http_lua_check_if_abortable(L, ctx); } } p = (u_char *) luaL_checklstring(L, 1, &len); if (len == 0) { return luaL_error(L, "attempt to use zero-length uri"); } r->uri.data = ngx_palloc(r->pool, len); if (r->uri.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(r->uri.data, p, len); r->uri.len = len; r->internal = 1; r->valid_unparsed_uri = 0; ngx_http_set_exten(r); if (jump) { r->uri_changed = 1; return lua_yield(L, 0); } r->valid_location = 0; r->uri_changed = 0; return 0; }
static int ngx_http_lua_ngx_exit(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } 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 object 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); rc = (ngx_int_t) luaL_checkinteger(L, 1); if (ctx->no_abort && rc != NGX_ERROR && rc != NGX_HTTP_CLOSE && rc != NGX_HTTP_REQUEST_TIME_OUT && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST) { return luaL_error(L, "attempt to abort with pending subrequests"); } if (ctx->headers_sent && rc >= NGX_HTTP_SPECIAL_RESPONSE && rc != NGX_HTTP_REQUEST_TIME_OUT && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST && rc != NGX_HTTP_CLOSE) { if (rc != (ngx_int_t) r->headers_out.status) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to " "set status %i via ngx.exit after sending out the " "response status %ui", rc, r->headers_out.status); } rc = NGX_HTTP_OK; } ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exit with code %i", ctx->exit_code); dd("calling yield"); return lua_yield(L, 0); }
static int ngx_http_lua_ngx_location_capture_multi(lua_State *L) { ngx_http_request_t *r; ngx_http_request_t *sr; /* subrequest object */ ngx_http_post_subrequest_t *psr; ngx_http_lua_ctx_t *sr_ctx; ngx_http_lua_ctx_t *ctx; ngx_array_t *extra_vars; ngx_str_t uri; ngx_str_t args; ngx_str_t extra_args; ngx_uint_t flags; u_char *p; u_char *q; size_t len; size_t nargs; int rc; int n; ngx_uint_t method; ngx_http_request_body_t *body; int type; ngx_buf_t *b; unsigned vars_action; ngx_uint_t nsubreqs; ngx_uint_t index; size_t sr_statuses_len; size_t sr_headers_len; size_t sr_bodies_len; size_t sr_flags_len; unsigned custom_ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_post_subrequest_data_t *psr_data; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "only one argument is expected, but got %d", n); } luaL_checktype(L, 1, LUA_TTABLE); nsubreqs = lua_objlen(L, 1); if (nsubreqs == 0) { return luaL_error(L, "at least one subrequest should be specified"); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no 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; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua location capture, uri:\"%V\" c:%ud", &r->uri, r->main->count); sr_statuses_len = nsubreqs * sizeof(ngx_int_t); sr_headers_len = nsubreqs * sizeof(ngx_http_headers_out_t *); sr_bodies_len = nsubreqs * sizeof(ngx_str_t); sr_flags_len = nsubreqs * sizeof(uint8_t); p = ngx_pcalloc(r->pool, sr_statuses_len + sr_headers_len + sr_bodies_len + sr_flags_len); if (p == NULL) { return luaL_error(L, "out of memory"); } coctx->sr_statuses = (void *) p; p += sr_statuses_len; coctx->sr_headers = (void *) p; p += sr_headers_len; coctx->sr_bodies = (void *) p; p += sr_bodies_len; coctx->sr_flags = (void *) p; coctx->nsubreqs = nsubreqs; coctx->pending_subreqs = 0; extra_vars = NULL; for (index = 0; index < nsubreqs; index++) { coctx->pending_subreqs++; lua_rawgeti(L, 1, index + 1); if (lua_isnil(L, -1)) { return luaL_error(L, "only array-like tables are allowed"); } dd("queries query: top %d", lua_gettop(L)); if (lua_type(L, -1) != LUA_TTABLE) { return luaL_error(L, "the query argument %d is not a table, " "but a %s", index, lua_typename(L, lua_type(L, -1))); } nargs = lua_objlen(L, -1); if (nargs != 1 && nargs != 2) { return luaL_error(L, "query argument %d expecting one or " "two arguments", index); } lua_rawgeti(L, 2, 1); /* queries query uri */ dd("queries query uri: %d", lua_gettop(L)); dd("first arg in first query: %s", lua_typename(L, lua_type(L, -1))); body = NULL; extra_args.data = NULL; extra_args.len = 0; if (extra_vars != NULL) { /* flush out existing elements in the array */ extra_vars->nelts = 0; } vars_action = 0; custom_ctx = 0; if (nargs == 2) { /* check out the options table */ lua_rawgeti(L, 2, 2); /* queries query uri opts */ dd("queries query uri opts: %d", lua_gettop(L)); if (lua_type(L, 4) != LUA_TTABLE) { return luaL_error(L, "expecting table as the 2nd argument for " "subrequest %d, but got %s", index, luaL_typename(L, 4)); } dd("queries query uri opts: %d", lua_gettop(L)); /* check the args option */ lua_getfield(L, 4, "args"); type = lua_type(L, -1); switch (type) { case LUA_TTABLE: ngx_http_lua_process_args_option(r, L, -1, &extra_args); break; case LUA_TNIL: /* do nothing */ break; case LUA_TNUMBER: case LUA_TSTRING: extra_args.data = (u_char *) lua_tolstring(L, -1, &len); extra_args.len = len; break; default: return luaL_error(L, "Bad args option value"); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the vars option */ lua_getfield(L, 4, "vars"); switch (lua_type(L, -1)) { case LUA_TTABLE: ngx_http_lua_process_vars_option(r, L, -1, &extra_vars); dd("post process vars top: %d", lua_gettop(L)); break; case LUA_TNIL: /* do nothing */ break; default: return luaL_error(L, "Bad vars option value"); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the share_all_vars option */ lua_getfield(L, 4, "share_all_vars"); switch (lua_type(L, -1)) { case LUA_TNIL: /* do nothing */ break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { vars_action |= NGX_HTTP_LUA_SHARE_ALL_VARS; } break; default: return luaL_error(L, "Bad share_all_vars option value"); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the copy_all_vars option */ lua_getfield(L, 4, "copy_all_vars"); switch (lua_type(L, -1)) { case LUA_TNIL: /* do nothing */ break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { vars_action |= NGX_HTTP_LUA_COPY_ALL_VARS; } break; default: return luaL_error(L, "Bad copy_all_vars option value"); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the "method" option */ lua_getfield(L, 4, "method"); type = lua_type(L, -1); if (type == LUA_TNIL) { method = NGX_HTTP_GET; } else { if (type != LUA_TNUMBER) { return luaL_error(L, "Bad http request method"); } method = (ngx_uint_t) lua_tonumber(L, -1); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the "ctx" option */ lua_getfield(L, 4, "ctx"); type = lua_type(L, -1); if (type != LUA_TNIL) { if (type != LUA_TTABLE) { return luaL_error(L, "Bad ctx option value type %s, " "expected a Lua table", lua_typename(L, type)); } custom_ctx = 1; } else { lua_pop(L, 1); } dd("queries query uri opts ctx?: %d", lua_gettop(L)); /* check the "body" option */ lua_getfield(L, 4, "body"); type = lua_type(L, -1); if (type != LUA_TNIL) { if (type != LUA_TSTRING && type != LUA_TNUMBER) { return luaL_error(L, "Bad http request body"); } body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (body == NULL) { return luaL_error(L, "out of memory"); } q = (u_char *) lua_tolstring(L, -1, &len); dd("request body: [%.*s]", (int) len, q); if (len) { b = ngx_create_temp_buf(r->pool, len); if (b == NULL) { return luaL_error(L, "out of memory"); } b->last = ngx_copy(b->last, q, len); body->bufs = ngx_alloc_chain_link(r->pool); if (body->bufs == NULL) { return luaL_error(L, "out of memory"); } body->bufs->buf = b; body->bufs->next = NULL; body->buf = b; } } lua_pop(L, 1); /* pop the body */ /* stack: queries query uri opts ctx? */ lua_remove(L, 4); /* stack: queries query uri ctx? */ dd("queries query uri ctx?: %d", lua_gettop(L)); } else { method = NGX_HTTP_GET; } /* stack: queries query uri ctx? */ p = (u_char *) luaL_checklstring(L, 3, &len); uri.data = ngx_palloc(r->pool, len); if (uri.data == NULL) { return luaL_error(L, "memory allocation error"); } ngx_memcpy(uri.data, p, len); uri.len = len; args.data = NULL; args.len = 0; flags = 0; rc = ngx_http_parse_unsafe_uri(r, &uri, &args, &flags); if (rc != NGX_OK) { dd("rc = %d", (int) rc); return luaL_error(L, "unsafe uri in argument #1: %s", p); } if (args.len == 0) { if (extra_args.len) { p = ngx_palloc(r->pool, extra_args.len); if (p == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(p, extra_args.data, extra_args.len); args.data = p; args.len = extra_args.len; } } else if (extra_args.len) { /* concatenate the two parts of args together */ len = args.len + (sizeof("&") - 1) + extra_args.len; p = ngx_palloc(r->pool, len); if (p == NULL) { return luaL_error(L, "out of memory"); } q = ngx_copy(p, args.data, args.len); *q++ = '&'; ngx_memcpy(q, extra_args.data, extra_args.len); args.data = p; args.len = len; } p = ngx_pnalloc(r->pool, sizeof(ngx_http_post_subrequest_t) + sizeof(ngx_http_lua_ctx_t) + sizeof(ngx_http_lua_post_subrequest_data_t)); if (p == NULL) { return luaL_error(L, "out of memory"); } psr = (ngx_http_post_subrequest_t *) p; p += sizeof(ngx_http_post_subrequest_t); sr_ctx = (ngx_http_lua_ctx_t *) p; p += sizeof(ngx_http_lua_ctx_t); psr_data = (ngx_http_lua_post_subrequest_data_t *) p; ngx_memzero(sr_ctx, sizeof(ngx_http_lua_ctx_t)); /* set by ngx_memzero: * sr_ctx->run_post_subrequest = 0 * sr_ctx->free = NULL * sr_ctx->body = NULL */ ngx_http_lua_init_ctx(sr_ctx); sr_ctx->capture = 1; sr_ctx->index = index; sr_ctx->last_body = &sr_ctx->body; psr_data->ctx = sr_ctx; psr_data->pr_co_ctx = coctx; psr->handler = ngx_http_lua_post_subrequest; psr->data = psr_data; rc = ngx_http_lua_subrequest(r, &uri, &args, &sr, psr, 0); if (rc != NGX_OK) { return luaL_error(L, "failed to issue subrequest: %d", (int) rc); } ngx_http_set_ctx(sr, sr_ctx, ngx_http_lua_module); rc = ngx_http_lua_adjust_subrequest(sr, method, body, vars_action, extra_vars); if (rc != NGX_OK) { ngx_http_lua_cancel_subreq(sr); return luaL_error(L, "failed to adjust the subrequest: %d", (int) rc); } dd("queries query uri opts ctx? %d", lua_gettop(L)); /* stack: queries query uri ctx? */ if (custom_ctx) { ngx_http_lua_ngx_set_ctx_helper(L, sr, sr_ctx, -1); lua_pop(L, 3); } else { lua_pop(L, 2); } /* stack: queries */ } if (extra_vars) { ngx_array_destroy(extra_vars); } ctx->no_abort = 1; return lua_yield(L, 0); }
static int ngx_http_lua_coroutine_resume(lua_State *L) { lua_State *co; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_co_ctx_t *p_coctx; /* parent co ctx */ co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "coroutine expected"); 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); p_coctx = ctx->cur_co_ctx; if (p_coctx == NULL) { return luaL_error(L, "no parent co ctx found"); } coctx = ngx_http_lua_get_co_ctx(co, ctx); if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } ngx_http_lua_probe_user_coroutine_resume(r, L, co); if (coctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) { dd("coroutine resume: %d", coctx->co_status); lua_pushboolean(L, 0); lua_pushfstring(L, "cannot resume %s coroutine", ngx_http_lua_co_status_names[coctx->co_status]); return 2; } p_coctx->co_status = NGX_HTTP_LUA_CO_NORMAL; coctx->parent_co_ctx = p_coctx; dd("set coroutine to running"); coctx->co_status = NGX_HTTP_LUA_CO_RUNNING; ctx->co_op = NGX_HTTP_LUA_USER_CORO_RESUME; ctx->cur_co_ctx = coctx; /* yield and pass args to main thread, and resume target coroutine from * there */ return lua_yield(L, lua_gettop(L) - 1); }
static int ngx_http_lua_coroutine_create(lua_State *L) { lua_State *mt; /* the main thread */ lua_State *co; /* new coroutine to be created */ ngx_http_request_t *r; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); 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); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); mt = lmcf->lua; /* create new coroutine on root Lua state, so it always yields * to main Lua thread */ co = lua_newthread(mt); ngx_http_lua_probe_user_coroutine_create(r, L, co); coctx = ngx_http_lua_create_co_ctx(r, ctx); if (coctx == NULL) { return luaL_error(L, "out of memory"); } coctx->co = co; coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED; /* make new coroutine share globals of the parent coroutine. * NOTE: globals don't have to be separated! */ lua_pushvalue(L, LUA_GLOBALSINDEX); lua_xmove(L, co, 1); lua_replace(co, LUA_GLOBALSINDEX); lua_xmove(mt, L, 1); /* move coroutine from main thread to L */ lua_pushvalue(L, 1); /* copy entry function to top of L*/ lua_xmove(L, co, 1); /* move entry function from L to co */ return 1; /* return new coroutine to Lua */ }
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_ngx_exit(lua_State *L) { ngx_int_t rc; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object 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 | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER | NGX_HTTP_LUA_CONTEXT_SSL_CERT); rc = (ngx_int_t) luaL_checkinteger(L, 1); if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_CERT) { #if (NGX_HTTP_SSL) ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exit with code %i", rc); return lua_yield(L, 0); #else return luaL_error(L, "no SSL support"); #endif } if (ctx->no_abort && rc != NGX_ERROR && rc != NGX_HTTP_CLOSE && rc != NGX_HTTP_REQUEST_TIME_OUT && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST) { return luaL_error(L, "attempt to abort with pending subrequests"); } if ((r->header_sent || ctx->header_sent) && rc >= NGX_HTTP_SPECIAL_RESPONSE && rc != NGX_HTTP_REQUEST_TIME_OUT && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST && rc != NGX_HTTP_CLOSE) { if (rc != (ngx_int_t) r->headers_out.status) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to " "set status %i via ngx.exit after sending out the " "response status %ui", rc, r->headers_out.status); } rc = NGX_HTTP_OK; } dd("setting exit code: %d", (int) rc); ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exit with code %i", ctx->exit_code); if (ctx->context & (NGX_HTTP_LUA_CONTEXT_HEADER_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER)) { return 0; } dd("calling yield"); return lua_yield(L, 0); }
static int ngx_http_lua_ngx_redirect(lua_State *L) { ngx_http_lua_ctx_t *ctx; ngx_int_t rc; int n; u_char *p; u_char *uri; size_t len; ngx_table_elt_t *h; ngx_http_request_t *r; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting one or two arguments"); } p = (u_char *) luaL_checklstring(L, 1, &len); if (n == 2) { rc = (ngx_int_t) luaL_checknumber(L, 2); if (rc != NGX_HTTP_MOVED_TEMPORARILY && rc != NGX_HTTP_MOVED_PERMANENTLY && rc != NGX_HTTP_TEMPORARY_REDIRECT) { return luaL_error(L, "only ngx.HTTP_MOVED_TEMPORARILY, " "ngx.HTTP_MOVED_PERMANENTLY, and " "ngx.HTTP_TEMPORARY_REDIRECT are allowed"); } } else { rc = NGX_HTTP_MOVED_TEMPORARILY; } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object 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_check_if_abortable(L, ctx); if (r->header_sent || ctx->header_sent) { return luaL_error(L, "attempt to call ngx.redirect after sending out " "the headers"); } uri = ngx_palloc(r->pool, len); if (uri == NULL) { return luaL_error(L, "no memory"); } ngx_memcpy(uri, p, len); h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return luaL_error(L, "no memory"); } h->hash = ngx_http_lua_location_hash; #if 0 dd("location hash: %lu == %lu", (unsigned long) h->hash, (unsigned long) ngx_hash_key_lc((u_char *) "Location", sizeof("Location") - 1)); #endif h->value.len = len; h->value.data = uri; ngx_str_set(&h->key, "Location"); r->headers_out.status = rc; ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua redirect to \"%V\" with code %i", &h->value, ctx->exit_code); if (len && uri[0] != '/') { r->headers_out.location = h; } /* * we do not set r->headers_out.location here to avoid the handling * the local redirects without a host name by ngx_http_header_filter() */ return lua_yield(L, 0); }
int ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx) { lua_State *vm; /* the Lua VM */ lua_State *co; /* new coroutine to be created */ ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); 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 | NGX_HTTP_LUA_CONTEXT_SSL_CERT); vm = ngx_http_lua_get_lua_vm(r, ctx); /* create new coroutine on root Lua state, so it always yields * to main Lua thread */ co = lua_newthread(vm); ngx_http_lua_probe_user_coroutine_create(r, L, co); coctx = ngx_http_lua_get_co_ctx(co, ctx); if (coctx == NULL) { coctx = ngx_http_lua_create_co_ctx(r, ctx); if (coctx == NULL) { return luaL_error(L, "no memory"); } } else { ngx_memzero(coctx, sizeof(ngx_http_lua_co_ctx_t)); coctx->co_ref = LUA_NOREF; } coctx->co = co; coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED; /* make new coroutine share globals of the parent coroutine. * NOTE: globals don't have to be separated! */ ngx_http_lua_get_globals_table(L); lua_xmove(L, co, 1); ngx_http_lua_set_globals_table(co); lua_xmove(vm, L, 1); /* move coroutine from main thread to L */ lua_pushvalue(L, 1); /* copy entry function to top of L*/ lua_xmove(L, co, 1); /* move entry function from L to co */ if (pcoctx) { *pcoctx = coctx; } #ifdef NGX_LUA_USE_ASSERT coctx->co_top = 1; #endif return 1; /* return new coroutine to Lua */ }
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_socket_udp_setpeername(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_str_t host; int port; ngx_resolver_ctx_t *rctx, temp; ngx_http_core_loc_conf_t *clcf; int saved_top; int n; u_char *p; size_t len; ngx_url_t url; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; ngx_udp_connection_t *uc; int timeout; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_socket_udp_upstream_t *u; /* * TODO: we should probably accept an extra argument to setpeername() * to allow the user bind the datagram unix domain socket himself, * which is necessary for systems without autobind support. */ n = lua_gettop(L); if (n != 2 && n != 3) { return luaL_error(L, "ngx.socket.udp setpeername: expecting 2 or 3 " "arguments (including the object), but seen %d", n); } 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 ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); luaL_checktype(L, 1, LUA_TTABLE); p = (u_char *) luaL_checklstring(L, 2, &len); host.data = ngx_palloc(r->pool, len + 1); if (host.data == NULL) { return luaL_error(L, "out of memory"); } host.len = len; ngx_memcpy(host.data, p, len); host.data[len] = '\0'; if (n == 3) { port = luaL_checkinteger(L, 3); if (port < 0 || port > 65536) { lua_pushnil(L); lua_pushfstring(L, "bad port number: %d", port); return 2; } } else { /* n == 2 */ port = 0; } lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u) { if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } if (u->udp_connection.connection) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket reconnect without shutting down"); ngx_http_lua_socket_udp_finalize(r, u); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua reuse socket upstream ctx"); } else { u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_udp_upstream_t)); if (u == NULL) { return luaL_error(L, "out of memory"); } #if 1 lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ lua_pushcfunction(L, ngx_http_lua_socket_udp_upstream_destroy); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); #endif lua_rawseti(L, 1, SOCKET_CTX_INDEX); } ngx_memzero(u, sizeof(ngx_http_lua_socket_udp_upstream_t)); u->request = r; /* set the controlling request */ llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); u->conf = llcf; uc = &u->udp_connection; uc->log = *r->connection->log; dd("lua peer connection log: %p", &uc->log); lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); timeout = (ngx_int_t) lua_tointeger(L, -1); lua_pop(L, 1); if (timeout > 0) { u->read_timeout = (ngx_msec_t) timeout; } else { u->read_timeout = u->conf->read_timeout; } ngx_memzero(&url, sizeof(ngx_url_t)); url.url.len = host.len; url.url.data = host.data; url.default_port = port; url.no_resolve = 1; if (ngx_parse_url(r->pool, &url) != NGX_OK) { lua_pushnil(L); if (url.err) { lua_pushfstring(L, "failed to parse host name \"%s\": %s", host.data, url.err); } else { lua_pushfstring(L, "failed to parse host name \"%s\"", host.data); } return 2; } u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); if (u->resolved == NULL) { return luaL_error(L, "out of memory"); } if (url.addrs && url.addrs[0].sockaddr) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket network address given directly"); u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; u->resolved->naddrs = 1; u->resolved->host = url.addrs[0].name; } else { u->resolved->host = host; u->resolved->port = (in_port_t) port; } if (u->resolved->sockaddr) { rc = ngx_http_lua_socket_resolve_retval_handler(r, u, L); if (rc == NGX_AGAIN) { return lua_yield(L, 0); } return rc; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); temp.name = host; rctx = ngx_resolve_start(clcf->resolver, &temp); if (rctx == NULL) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushliteral(L, "failed to start the resolver"); return 2; } if (rctx == NGX_NO_RESOLVER) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); return 2; } rctx->name = host; rctx->type = NGX_RESOLVE_A; rctx->handler = ngx_http_lua_socket_resolve_handler; rctx->data = u; rctx->timeout = clcf->resolver_timeout; u->co_ctx = ctx->cur_co_ctx; u->resolved->ctx = rctx; saved_top = lua_gettop(L); coctx = ctx->cur_co_ctx; coctx->cleanup = ngx_http_lua_udp_resolve_cleanup; if (ngx_resolve_name(rctx) != NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket fail to run resolver immediately"); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; u->resolved->ctx = NULL; lua_pushnil(L); lua_pushfstring(L, "%s could not be resolved", host.data); return 2; } if (u->waiting == 1) { /* resolved and already connecting */ return lua_yield(L, 0); } n = lua_gettop(L) - saved_top; if (n) { /* errors occurred during resolving or connecting * or already connected */ return n; } /* still resolving */ u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_resolve_retval_handler; coctx->data = u; if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } return lua_yield(L, 0); }
static int ngx_http_lua_ngx_req_read_body(lua_State *L) { ngx_http_request_t *r; int n; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "expecting 0 arguments but seen %d", n); } 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, "request object not found"); } r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; #if 1 if (r->request_body_in_file_only) { r->request_body_file_log_level = 0; } #endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "request context is null"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua start to read buffered request body"); rc = ngx_http_read_client_request_body(r, ngx_http_lua_req_body_post_read); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return luaL_error(L, "failed to read request body"); } if (rc == NGX_AGAIN) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua read buffered request body requires I/O interruptions"); ctx->waiting_more_body = 1; ctx->req_read_body_done = 0; return lua_yield(L, 0); } /* rc == NGX_OK */ ctx->req_read_body_done = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua has read buffered request body in a single run"); return 0; }
static int ngx_http_lua_ngx_redirect(lua_State *L) { ngx_http_lua_ctx_t *ctx; ngx_int_t rc; int n; u_char *p; u_char *uri; size_t len; ngx_http_request_t *r; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting one or two arguments"); } p = (u_char *) luaL_checklstring(L, 1, &len); if (n == 2) { rc = (ngx_int_t) luaL_checknumber(L, 2); if (rc != NGX_HTTP_MOVED_TEMPORARILY && rc != NGX_HTTP_MOVED_PERMANENTLY) { return luaL_error(L, "only ngx.HTTP_MOVED_TEMPORARILY and " "ngx.HTTP_MOVED_PERMANENTLY are allowed"); } } else { rc = NGX_HTTP_MOVED_TEMPORARILY; } 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 object 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_check_if_abortable(L, ctx); if (ctx->headers_sent) { return luaL_error(L, "attempt to call ngx.redirect after sending out " "the headers"); } uri = ngx_palloc(r->pool, len); if (uri == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(uri, p, len); r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { return luaL_error(L, "out of memory"); } r->headers_out.location->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('l', 'o'), 'c'), 'a'), 't'), 'i'), 'o'), 'n'); #if 0 dd("location hash: %lu == %lu", (unsigned long) r->headers_out.location->hash, (unsigned long) ngx_hash_key_lc((u_char *) "Location", sizeof("Location") - 1)); #endif r->headers_out.location->value.len = len; r->headers_out.location->value.data = uri; ngx_str_set(&r->headers_out.location->key, "Location"); r->headers_out.status = rc; ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua redirect to \"%V\" with code %i", &r->headers_out.location->value, ctx->exit_code); return lua_yield(L, 0); }
static int ngx_http_lua_ngx_req_read_body(lua_State *L) { ngx_http_request_t *r; int n; ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "expecting 0 arguments but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; #if 1 if (r->request_body_in_file_only) { r->request_body_file_log_level = 0; } #endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no 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; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua start to read buffered request body"); rc = ngx_http_read_client_request_body(r, ngx_http_lua_req_body_post_read); #if (nginx_version < 1002006) || \ (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http read client request body returned error code %i, " "exitting now", rc); return lua_yield(L, 0); } #if (nginx_version >= 1002006 && nginx_version < 1003000) || \ nginx_version >= 1003009 r->main->count--; dd("decrement r->main->count: %d", (int) r->main->count); #endif if (rc == NGX_AGAIN) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua read buffered request body requires I/O " "interruptions"); ctx->waiting_more_body = 1; ctx->downstream = coctx; ngx_http_lua_cleanup_pending_operation(coctx); coctx->cleanup = ngx_http_lua_req_body_cleanup; coctx->data = r; return lua_yield(L, 0); } /* rc == NGX_OK */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua has read buffered request body in a single run"); return 0; }
static int ngx_http_lua_ngx_exec(lua_State *L) { int n; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_str_t uri; ngx_str_t args, user_args; ngx_uint_t flags; u_char *p; u_char *q; size_t len; const char *msg; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting one or two arguments, but got %d", n); } 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 object found"); } args.data = NULL; args.len = 0; /* read the 1st argument (uri) */ p = (u_char *) luaL_checklstring(L, 1, &len); if (len == 0) { return luaL_error(L, "The uri argument is empty"); } uri.data = ngx_palloc(r->pool, len); if (uri.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(uri.data, p, len); uri.len = len; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no 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_check_if_abortable(L, ctx); if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) { ctx->headers_sent = 1; return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (n == 2) { /* read the 2nd argument (args) */ dd("args type: %s", luaL_typename(L, 2)); switch (lua_type(L, 2)) { case LUA_TNUMBER: case LUA_TSTRING: p = (u_char *) lua_tolstring(L, 2, &len); user_args.data = ngx_palloc(r->pool, len); if (user_args.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(user_args.data, p, len); user_args.len = len; break; case LUA_TTABLE: ngx_http_lua_process_args_option(r, L, 2, &user_args); dd("user_args: %.*s", (int) user_args.len, user_args.data); break; case LUA_TNIL: user_args.data = NULL; user_args.len = 0; break; default: msg = lua_pushfstring(L, "string, number, or table expected, " "but got %s", luaL_typename(L, 2)); return luaL_argerror(L, 2, msg); } } else { user_args.data = NULL; user_args.len = 0; } if (user_args.len) { if (args.len == 0) { args = user_args; } else { p = ngx_palloc(r->pool, args.len + user_args.len + 1); if (p == NULL) { return luaL_error(L, "out of memory"); } q = ngx_copy(p, args.data, args.len); *q++ = '&'; ngx_memcpy(q, user_args.data, user_args.len); args.data = p; args.len += user_args.len + 1; } } if (ctx->headers_sent) { return luaL_error(L, "attempt to call ngx.exec after " "sending out response headers"); } ctx->exec_uri = uri; ctx->exec_args = args; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exec \"%V?%V\"", &ctx->exec_uri, &ctx->exec_args); return lua_yield(L, 0); }
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); }