static int ngx_stream_lua_socket_udp(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; if (lua_gettop(L) != 0) { return luaL_error(L, "expecting zero arguments, but got %d", lua_gettop(L)); } s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); lua_createtable(L, 3 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, &ngx_stream_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_stream_lua_uthread_spawn(lua_State *L) { int n; ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_co_ctx_t *coctx = NULL; n = lua_gettop(L); s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_coroutine_create_helper(L, s, ctx, &coctx); /* anchor the newly created coroutine into the Lua registry */ lua_pushlightuserdata(L, &ngx_stream_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_pushvalue(L, -2); coctx->co_ref = luaL_ref(L, -2); lua_pop(L, 1); if (n > 1) { lua_replace(L, 1); lua_xmove(L, coctx->co, n - 1); } coctx->is_uthread = 1; ctx->uthreads++; coctx->co_status = NGX_STREAM_LUA_CO_RUNNING; ctx->co_op = NGX_STREAM_LUA_USER_THREAD_RESUME; ctx->cur_co_ctx->thread_spawn_yielded = 1; if (ngx_stream_lua_post_thread(s, ctx, ctx->cur_co_ctx) != NGX_OK) { return luaL_error(L, "no memory"); } coctx->parent_co_ctx = ctx->cur_co_ctx; ctx->cur_co_ctx = coctx; #if 0 /* TODO */ ngx_stream_lua_probe_user_thread_spawn(s, L, coctx->co); #endif dd("yielding with arg %s, top=%d, index-1:%s", luaL_typename(L, -1), (int) lua_gettop(L), luaL_typename(L, 1)); return lua_yield(L, 1); }
static int ngx_stream_lua_on_abort(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_co_ctx_t *coctx = NULL; ngx_stream_lua_srv_conf_t *lscf; s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_fake_session2(L, s, ctx); if (ctx->on_abort_co_ctx) { lua_pushnil(L); lua_pushliteral(L, "duplicate call"); return 2; } lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); if (!lscf->check_client_abort) { lua_pushnil(L); lua_pushliteral(L, "lua_check_client_abort is off"); return 2; } ngx_stream_lua_coroutine_create_helper(L, s, ctx, &coctx); lua_pushlightuserdata(L, &ngx_stream_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_pushvalue(L, -2); dd("on_wait thread 1: %p", lua_tothread(L, -1)); coctx->co_ref = luaL_ref(L, -2); lua_pop(L, 1); coctx->is_uthread = 1; ctx->on_abort_co_ctx = coctx; dd("on_wait thread 2: %p", coctx->co); coctx->co_status = NGX_STREAM_LUA_CO_SUSPENDED; coctx->parent_co_ctx = ctx->cur_co_ctx; lua_pushinteger(L, 1); return 1; }
/** * Send last_buf, terminate output stream * */ static int ngx_stream_lua_ngx_eof(lua_State *L) { #if 0 /* TODO */ ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_int_t rc; s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session object found"); } if (lua_gettop(L) != 0) { return luaL_error(L, "no argument is expected"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_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 session socket acquired"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT); ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua send eof"); rc = ngx_stream_lua_send_chain_link(s, ctx, NULL /* indicate last_buf */); dd("send chain: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_STREAM_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } #endif lua_pushinteger(L, 1); return 1; }
static int ngx_stream_lua_coroutine_yield(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_co_ctx_t *coctx; s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); coctx = ctx->cur_co_ctx; coctx->co_status = NGX_STREAM_LUA_CO_SUSPENDED; ctx->co_op = NGX_STREAM_LUA_USER_CORO_YIELD; if (!coctx->is_uthread && coctx->parent_co_ctx) { dd("set coroutine to running"); coctx->parent_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING; #if 0 /* TODO */ ngx_stream_lua_probe_user_coroutine_yield(s, coctx->parent_co_ctx->co, L); #endif } else { #if 0 /* TODO */ ngx_stream_lua_probe_user_coroutine_yield(s, NULL, L); #endif } /* yield and pass retvals to main thread, * and resume parent coroutine there */ return lua_yield(L, lua_gettop(L)); }
static int ngx_stream_lua_coroutine_create(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } return ngx_stream_lua_coroutine_create_helper(L, s, ctx, NULL); }
/** * Send last_buf, terminate output stream * */ static int ngx_stream_lua_ngx_eof(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session object found"); } if (lua_gettop(L) != 0) { return luaL_error(L, "no argument is expected"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } #if 0 if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw session socket acquired"); return 2; } #endif if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } ctx->eof = 1; ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT); ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua send eof"); lua_pushinteger(L, 1); return 1; }
static int ngx_stream_lua_socket_udp_close(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_socket_udp_upstream_t *u; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting 1 argument " "(including the object) but seen %d", lua_gettop(L)); } s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->udp_connection.connection == NULL) { lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->session != s) { return luaL_error(L, "bad session"); } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } ngx_stream_lua_socket_udp_finalize(s, u); lua_pushinteger(L, 1); return 1; }
static int ngx_stream_lua_coroutine_status(lua_State *L) { lua_State *co; /* new coroutine to be created */ const ngx_str_t *names; ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "coroutine expected"); s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); names = ngx_stream_lua_co_status_names; coctx = ngx_stream_lua_get_co_ctx(co, ctx); if (coctx == NULL) { lua_pushlstring(L, (const char *) names[NGX_STREAM_LUA_CO_DEAD].data, names[NGX_STREAM_LUA_CO_DEAD].len); return 1; } dd("co status: %d", coctx->co_status); lua_pushlstring(L, (const char *) names[coctx->co_status].data, names[coctx->co_status].len); return 1; }
static int ngx_stream_lua_ngx_exit(lua_State *L) { ngx_int_t rc; ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session object found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); rc = (ngx_int_t) luaL_checkinteger(L, 1); dd("setting exit code: %d", (int) rc); ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua exit with code %i", ctx->exit_code); dd("calling yield"); return lua_yield(L, 0); }
static int ngx_stream_lua_coroutine_resume(lua_State *L) { lua_State *co; ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_co_ctx_t *coctx; ngx_stream_lua_co_ctx_t *p_coctx; /* parent co ctx */ co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "coroutine expected"); s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); p_coctx = ctx->cur_co_ctx; if (p_coctx == NULL) { return luaL_error(L, "no parent co ctx found"); } coctx = ngx_stream_lua_get_co_ctx(co, ctx); if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } #if 0 /* TODO */ ngx_stream_lua_probe_user_coroutine_resume(s, L, co); #endif if (coctx->co_status != NGX_STREAM_LUA_CO_SUSPENDED) { dd("coroutine resume: %d", coctx->co_status); lua_pushboolean(L, 0); lua_pushfstring(L, "cannot resume %s coroutine", ngx_stream_lua_co_status_names[coctx->co_status].data); return 2; } p_coctx->co_status = NGX_STREAM_LUA_CO_NORMAL; coctx->parent_co_ctx = p_coctx; dd("set coroutine to running"); coctx->co_status = NGX_STREAM_LUA_CO_RUNNING; ctx->co_op = NGX_STREAM_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_stream_lua_uthread_kill(lua_State *L) { lua_State *sub_co; ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_co_ctx_t *coctx, *sub_coctx; s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); coctx = ctx->cur_co_ctx; sub_co = lua_tothread(L, 1); luaL_argcheck(L, sub_co, 1, "stream lua thread expected"); sub_coctx = ngx_stream_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; } switch (sub_coctx->co_status) { case NGX_STREAM_LUA_CO_ZOMBIE: ngx_stream_lua_del_thread(s, L, ctx, sub_coctx); ctx->uthreads--; lua_pushnil(L); lua_pushliteral(L, "already terminated"); return 2; case NGX_STREAM_LUA_CO_DEAD: lua_pushnil(L); lua_pushliteral(L, "already waited or killed"); return 2; default: ngx_stream_lua_cleanup_pending_operation(sub_coctx); ngx_stream_lua_del_thread(s, L, ctx, sub_coctx); ctx->uthreads--; lua_pushinteger(L, 1); return 1; } /* not reacheable */ }
/** * Force flush out response content * */ static int ngx_stream_lua_ngx_flush(lua_State *L) { #if 0 /* TODO */ ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_chain_t *cl; ngx_int_t rc; int n; unsigned wait = 0; ngx_event_t *wev; ngx_stream_core_loc_conf_t *clcf; ngx_stream_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); } s = ngx_stream_lua_get_session(L); if (n == 1 && s == s->main) { luaL_checktype(L, 1, LUA_TBOOLEAN); wait = lua_toboolean(L, 1); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_REWRITE | NGX_STREAM_LUA_CONTEXT_ACCESS | NGX_STREAM_LUA_CONTEXT_CONTENT); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw session socket acquired"); return 2; } coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } #if 1 if (!s->header_sent && !ctx->header_sent) { lua_pushnil(L); lua_pushliteral(L, "nothing to flush"); return 2; } #endif cl = ngx_stream_lua_get_flush_chain(s, ctx); if (cl == NULL) { return luaL_error(L, "no memory"); } rc = ngx_stream_lua_send_chain_link(s, ctx, cl); dd("send chain: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_STREAM_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, s->connection->buffered); wev = s->connection->write; if (wait && (s->connection->buffered & NGX_STREAM_LOWLEVEL_BUFFERED || wev->delayed)) { ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua flush requires waiting: buffered 0x%uxd, " "delayed:%d", (unsigned) s->connection->buffered, wev->delayed); coctx->flushing = 1; ctx->flushing_coros++; if (ctx->entered_content_phase) { /* mimic ngx_stream_set_write_handler */ s->write_event_handler = ngx_stream_lua_content_wev_handler; } else { s->write_event_handler = ngx_stream_core_run_phases; } clcf = ngx_stream_get_module_loc_conf(s, ngx_stream_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_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx); coctx->cleanup = ngx_stream_lua_flush_cleanup; coctx->data = s; return lua_yield(L, 0); } ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua flush asynchronously"); #endif lua_pushinteger(L, 1); return 1; }
static int ngx_stream_lua_socket_udp_setpeername(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_str_t host; int port; ngx_resolver_ctx_t *rctx, temp; int saved_top; int n; u_char *p; size_t len; ngx_url_t url; ngx_int_t rc; ngx_stream_lua_srv_conf_t *lscf; ngx_udp_connection_t *uc; int timeout; ngx_stream_lua_co_ctx_t *coctx; ngx_stream_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); } s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); luaL_checktype(L, 1, LUA_TTABLE); p = (u_char *) luaL_checklstring(L, 2, &len); host.data = ngx_palloc(s->connection->pool, len + 1); if (host.data == NULL) { return luaL_error(L, "no 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->session && u->session != s) { return luaL_error(L, "bad session"); } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } if (u->udp_connection.connection) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua udp socket reconnect without " "shutting down"); ngx_stream_lua_socket_udp_finalize(s, u); } ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua reuse socket upstream ctx"); } else { u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_udp_upstream_t)); if (u == NULL) { return luaL_error(L, "no memory"); } #if 1 lua_pushlightuserdata(L, &ngx_stream_lua_udp_udata_metatable_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); #endif lua_rawseti(L, 1, SOCKET_CTX_INDEX); } ngx_memzero(u, sizeof(ngx_stream_lua_socket_udp_upstream_t)); u->session = s; /* set the controlling session */ lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); u->conf = lscf; uc = &u->udp_connection; uc->log = *s->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 = (in_port_t) port; url.no_resolve = 1; if (ngx_parse_url(s->connection->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(s->connection->pool, sizeof(ngx_stream_lua_resolved_t)); if (u->resolved == NULL) { return luaL_error(L, "no memory"); } if (url.addrs && url.addrs[0].sockaddr) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream 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_stream_lua_socket_resolve_retval_handler(s, u, L); if (rc == NGX_AGAIN) { return lua_yield(L, 0); } return rc; } temp.name = host; rctx = ngx_resolve_start(lscf->resolver, &temp); if (rctx == NULL) { u->ft_type |= NGX_STREAM_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_STREAM_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushfstring(L, "no lua_resolver defined to resolve \"%s\"", host.data); return 2; } rctx->name = host; #if !defined(nginx_version) || nginx_version < 1005008 rctx->type = NGX_RESOLVE_A; #endif rctx->handler = ngx_stream_lua_socket_resolve_handler; rctx->data = u; rctx->timeout = lscf->resolver_timeout; u->co_ctx = ctx->cur_co_ctx; u->resolved->ctx = rctx; saved_top = lua_gettop(L); coctx = ctx->cur_co_ctx; ngx_stream_lua_cleanup_pending_operation(coctx); coctx->cleanup = ngx_stream_lua_udp_resolve_cleanup; if (ngx_resolve_name(rctx) != NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua udp socket fail to run resolver " "immediately"); u->ft_type |= NGX_STREAM_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_stream_lua_socket_resolve_retval_handler; coctx->data = u; ctx->write_event_handler = ngx_stream_lua_content_wev_handler; return lua_yield(L, 0); }
/** * Force flush out response content * */ static int ngx_stream_lua_ngx_flush(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; int n; unsigned wait = 0; ngx_event_t *wev; ngx_stream_lua_srv_conf_t *lscf; ngx_stream_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); } s = ngx_stream_lua_get_session(L); wait = 1; /* always wait */ ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT); #if 0 if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw session socket acquired"); return 2; } #endif coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } wev = s->connection->write; if (wait && (ctx->downstream_busy_bufs || wev->delayed)) { ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua flush requires waiting: busy bufs %p, " "delayed %d", ctx->downstream_busy_bufs, wev->delayed); coctx->flushing = 1; ctx->flushing_coros++; /* mimic ngx_stream_set_write_handler */ ctx->write_event_handler = ngx_stream_lua_content_wev_handler; lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); if (!wev->delayed) { ngx_add_timer(wev, lscf->send_timeout); } if (ngx_handle_write_event(wev, lscf->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_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx); coctx->cleanup = ngx_stream_lua_flush_cleanup; coctx->data = s; return lua_yield(L, 0); } ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua flush asynchronously"); lua_pushinteger(L, 1); return 1; }
static int ngx_stream_lua_ngx_echo(lua_State *L, unsigned newline) { ngx_stream_session_t *s; ngx_stream_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; s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session object found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw session socket acquired"); 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_stream_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_stream_lua_chain_get_free_buf(s->connection->log, s->connection->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_stream_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_STREAM, s->connection->log, 0, newline ? "stream lua say response" : "stream lua print response"); rc = ngx_stream_lua_send_chain_link(s, ctx, cl); if (rc == NGX_ERROR) { 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_stream_lua_socket_udp_send(lua_State *L) { ssize_t n; ngx_stream_session_t *s; u_char *p; size_t len; ngx_stream_lua_socket_udp_upstream_t *u; int type; const char *msg; ngx_str_t query; ngx_stream_lua_srv_conf_t *lscf; if (lua_gettop(L) != 2) { return luaL_error(L, "expecting 2 arguments (including the object), " "but got %d", lua_gettop(L)); } s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "session object not found"); } luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->udp_connection.connection == NULL) { lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); if (lscf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "attempt to send data on a closed socket: u:%p, c:%p", u, u ? u->udp_connection.connection : NULL); } lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->session != s) { return luaL_error(L, "bad session"); } if (u->ft_type) { u->ft_type = 0; } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } type = lua_type(L, 2); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, 2, &len); break; case LUA_TTABLE: len = ngx_stream_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); break; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "or array table expected, got %s", lua_typename(L, type)); return luaL_argerror(L, 2, msg); } query.data = lua_newuserdata(L, len); query.len = len; switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = (u_char *) lua_tolstring(L, 2, &len); ngx_memcpy(query.data, (u_char *) p, len); break; case LUA_TTABLE: (void) ngx_stream_lua_copy_str_in_table(L, 2, query.data); break; default: return luaL_error(L, "impossible to reach here"); } u->ft_type = 0; /* mimic ngx_stream_upstream_init_session here */ #if 1 u->waiting = 0; #endif dd("sending query %.*s", (int) query.len, query.data); n = ngx_send(u->udp_connection.connection, query.data, query.len); dd("ngx_send returns %d (query len %d)", (int) n, (int) query.len); if (n == NGX_ERROR || n == NGX_AGAIN) { u->socket_errno = ngx_socket_errno; return ngx_stream_lua_socket_error_retval_handler(s, u, L); } if (n != (ssize_t) query.len) { dd("not the while query was sent"); u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_PARTIALWRITE; return ngx_stream_lua_socket_error_retval_handler(s, u, L); } dd("n == len"); lua_pushinteger(L, 1); return 1; }
static int ngx_stream_lua_socket_udp_receive(lua_State *L) { ngx_stream_session_t *s; ngx_stream_lua_socket_udp_upstream_t *u; ngx_int_t rc; ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_co_ctx_t *coctx; size_t size; int nargs; ngx_stream_lua_srv_conf_t *lscf; nargs = lua_gettop(L); if (nargs != 1 && nargs != 2) { return luaL_error(L, "expecting 1 or 2 arguments " "(including the object), but got %d", nargs); } s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua udp socket calling receive() method"); luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->udp_connection.connection == NULL) { lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module); if (lscf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "attempt to receive data on a closed socket: u:%p, " "c:%p", u, u ? u->udp_connection.connection : NULL); } lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->session != s) { return luaL_error(L, "bad session"); } if (u->ft_type) { u->ft_type = 0; } #if 1 if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } #endif ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua udp socket read timeout: %M", u->read_timeout); size = (size_t) luaL_optnumber(L, 2, UDP_MAX_DATAGRAM_SIZE); size = ngx_min(size, UDP_MAX_DATAGRAM_SIZE); u->recv_buf_size = size; ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua udp socket receive buffer size: %uz", u->recv_buf_size); rc = ngx_stream_lua_socket_udp_read(s, u); if (rc == NGX_ERROR) { dd("read failed: %d", (int) u->ft_type); rc = ngx_stream_lua_socket_udp_receive_retval_handler(s, u, L); dd("udp receive retval returned: %d", (int) rc); return rc; } if (rc == NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream lua udp socket receive done in a single run"); return ngx_stream_lua_socket_udp_receive_retval_handler(s, u, L); } /* n == NGX_AGAIN */ u->read_event_handler = ngx_stream_lua_socket_udp_read_handler; ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } coctx = ctx->cur_co_ctx; ngx_stream_lua_cleanup_pending_operation(coctx); coctx->cleanup = ngx_stream_lua_udp_socket_cleanup; coctx->data = u; ctx->write_event_handler = ngx_stream_lua_content_wev_handler; u->co_ctx = coctx; u->waiting = 1; u->prepare_retvals = ngx_stream_lua_socket_udp_receive_retval_handler; return lua_yield(L, 0); }
static int ngx_stream_lua_uthread_wait(lua_State *L) { int i, nargs, nrets; lua_State *sub_co; ngx_stream_session_t *s; ngx_stream_lua_ctx_t *ctx; ngx_stream_lua_co_ctx_t *coctx, *sub_coctx; s = ngx_stream_lua_get_session(L); if (s == NULL) { return luaL_error(L, "no session found"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); if (ctx == NULL) { return luaL_error(L, "no session ctx found"); } ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_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, "stream lua thread expected"); sub_coctx = ngx_stream_lua_get_co_ctx(sub_co, ctx); if (sub_coctx == NULL) { return luaL_error(L, "no co ctx found"); } 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_STREAM_LUA_CO_ZOMBIE: ngx_stream_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_stream_lua_del_thread(s, L, ctx, sub_coctx); ctx->uthreads--; #endif return nrets; case NGX_STREAM_LUA_CO_DEAD: dd("uthread already waited: %p (parent %p)", sub_coctx, coctx); 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 or killed"); return 2; default: dd("uthread %p still alive, status: %d, parent %p", sub_coctx, sub_coctx->co_status, coctx); break; } #if 0 /* TODO */ ngx_stream_lua_probe_user_thread_wait(L, sub_coctx->co); #endif sub_coctx->waited_by_parent = 1; } return lua_yield(L, 0); }