plugins/lua/ts_lua_util.cc (942 lines of code) (raw):

/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "ts_lua_util.h" #include "ts_lua_remap.h" #include "ts_lua_constant.h" #include "ts_lua_client_request.h" #include "ts_lua_server_request.h" #include "ts_lua_server_response.h" #include "ts_lua_client_response.h" #include "ts_lua_cached_response.h" #include "ts_lua_context.h" #include "ts_lua_hook.h" #include "ts_lua_vconn.h" #include "ts_lua_http.h" #include "ts_lua_misc.h" #include "ts_lua_log.h" #include "ts_lua_crypto.h" #include "ts_lua_mgmt.h" #include "ts_lua_package.h" #include "ts_lua_stat.h" #include "ts_lua_fetch.h" #include "ts_lua_http_intercept.h" static lua_State *ts_lua_new_state(); static void ts_lua_init_registry(lua_State *L); static void ts_lua_init_globals(lua_State *L); static void ts_lua_inject_ts_api(lua_State *L); static ts_lua_ctx_stats *ts_lua_create_ctx_stats(); static void ts_lua_destroy_ctx_stats(ts_lua_ctx_stats *stats); static void ts_lua_update_server_response_hdrp(ts_lua_http_ctx *http_ctx); /** Update http_ctx->server_response_hdrp if there. This is required in the beginning of toughing response, because holding old pointer could be freed by core. */ void ts_lua_update_server_response_hdrp(ts_lua_http_ctx *http_ctx) { if (http_ctx->server_response_hdrp) { if (TSHttpTxnServerRespGet(http_ctx->txnp, &http_ctx->server_response_bufp, &http_ctx->server_response_hdrp) != TS_SUCCESS) { TSError("[ts_lua][%s] failed to get server response", __FUNCTION__); return; } } } void ts_lua_clear_http_ctx(ts_lua_http_ctx *http_ctx) { if (http_ctx->rri == nullptr) { if (http_ctx->client_request_url != nullptr) { TSHandleMLocRelease(http_ctx->client_request_bufp, http_ctx->client_request_hdrp, http_ctx->client_request_url); http_ctx->client_request_url = nullptr; } if (http_ctx->client_request_bufp != nullptr) { TSHandleMLocRelease(http_ctx->client_request_bufp, TS_NULL_MLOC, http_ctx->client_request_hdrp); http_ctx->client_request_bufp = nullptr; http_ctx->client_request_hdrp = nullptr; } } if (http_ctx->server_request_url != nullptr) { TSHandleMLocRelease(http_ctx->server_request_bufp, http_ctx->server_request_hdrp, http_ctx->server_request_url); http_ctx->server_request_url = nullptr; } if (http_ctx->server_request_hdrp != nullptr) { TSHandleMLocRelease(http_ctx->server_request_bufp, TS_NULL_MLOC, http_ctx->server_request_hdrp); http_ctx->server_request_bufp = nullptr; http_ctx->server_request_hdrp = nullptr; } if (http_ctx->server_response_bufp != nullptr) { TSHandleMLocRelease(http_ctx->server_response_bufp, TS_NULL_MLOC, http_ctx->server_response_hdrp); http_ctx->server_response_bufp = nullptr; http_ctx->server_response_hdrp = nullptr; } if (http_ctx->client_response_hdrp != nullptr) { TSHandleMLocRelease(http_ctx->client_response_bufp, TS_NULL_MLOC, http_ctx->client_response_hdrp); http_ctx->client_response_bufp = nullptr; http_ctx->client_response_hdrp = nullptr; } if (http_ctx->cached_response_bufp != nullptr) { TSMimeHdrDestroy(http_ctx->cached_response_bufp, http_ctx->cached_response_hdrp); TSHandleMLocRelease(http_ctx->cached_response_bufp, TS_NULL_MLOC, http_ctx->cached_response_hdrp); TSMBufferDestroy(http_ctx->cached_response_bufp); http_ctx->cached_response_bufp = nullptr; http_ctx->cached_response_hdrp = nullptr; } } int ts_lua_create_vm(ts_lua_main_ctx *arr, int n) { int i; lua_State *L; for (i = 0; i < n; i++) { L = ts_lua_new_state(); if (L == nullptr) return -1; lua_pushvalue(L, LUA_GLOBALSINDEX); arr[i].gref = luaL_ref(L, LUA_REGISTRYINDEX); /* L[REG][gref] = L[GLOBAL] */ arr[i].lua = L; arr[i].mutexp = TSMutexCreate(); arr[i].stats = ts_lua_create_ctx_stats(); } return 0; } void ts_lua_destroy_vm(ts_lua_main_ctx *arr, int n) { int i; lua_State *L; TSMutex mutexp; ts_lua_ctx_stats *stats; for (i = 0; i < n; i++) { L = arr[i].lua; if (L) { lua_close(L); arr[i].lua = nullptr; } mutexp = arr[i].mutexp; if (mutexp) { TSMutexDestroy(mutexp); arr[i].mutexp = nullptr; } stats = arr[i].stats; if (stats) { ts_lua_destroy_ctx_stats(stats); arr[i].stats = nullptr; } } return; } lua_State * ts_lua_new_state() { lua_State *L; L = luaL_newstate(); if (L == nullptr) { return nullptr; } luaL_openlibs(L); ts_lua_init_registry(L); ts_lua_init_globals(L); return L; } ts_lua_ctx_stats * ts_lua_create_ctx_stats() { auto stats = TSRalloc<ts_lua_ctx_stats>(); memset(stats, 0, sizeof(*stats)); stats->mutexp = TSMutexCreate(); return stats; } void ts_lua_destroy_ctx_stats(ts_lua_ctx_stats *stats) { if (stats) { TSMutexDestroy(stats->mutexp); stats->mutexp = nullptr; TSfree(stats); } } ts_lua_instance_conf * ts_lua_script_registered(lua_State *L, char *script) { TSMgmtInt curr_time; ts_lua_instance_conf *conf = nullptr; Dbg(dbg_ctl, "[%s] checking if script [%s] is registered", __FUNCTION__, script); // first check the reconfigure_time for the script. if it is not found, then it is new // if it matches the current reconfigure_time, then it is loaded already // And we return the conf pointer of it. Otherwise it can be loaded again. if (TS_SUCCESS == TSMgmtIntGet("proxy.process.proxy.reconfigure_time", &curr_time)) { lua_pushliteral(L, "__scriptTime"); lua_pushstring(L, script); lua_concat(L, 2); lua_rawget(L, LUA_REGISTRYINDEX); if (lua_isnil(L, -1)) { Dbg(dbg_ctl, "[%s] failed to get script time for [%s]", __FUNCTION__, script); lua_pop(L, -1); return nullptr; } else { int time = lua_tonumber(L, -1); lua_pop(L, -1); if (time == curr_time) { lua_pushliteral(L, "__scriptPtr"); lua_pushstring(L, script); lua_concat(L, 2); lua_rawget(L, LUA_REGISTRYINDEX); if (lua_isnil(L, -1)) { Dbg(dbg_ctl, "[%s] failed to get script ptr for [%s]", __FUNCTION__, script); lua_pop(L, -1); return nullptr; } else { conf = static_cast<decltype(conf)>(lua_touserdata(L, -1)); lua_pop(L, -1); return conf; } } else { Dbg(dbg_ctl, "[%s] script time not matching for [%s]", __FUNCTION__, script); return nullptr; } } } else { TSError("[ts_lua][%s] failed to get node's reconfigure time while checking script registration", __FUNCTION__); return nullptr; } } void ts_lua_script_register(lua_State *L, char *script, ts_lua_instance_conf *conf) { TSMgmtInt time; Dbg(dbg_ctl, "[%s] registering script [%s]", __FUNCTION__, script); // we recorded the script reconfigure_time and its conf pointer in registry if (TS_SUCCESS == TSMgmtIntGet("proxy.process.proxy.reconfigure_time", &time)) { lua_pushliteral(L, "__scriptTime"); lua_pushstring(L, script); lua_concat(L, 2); lua_pushnumber(L, time); lua_rawset(L, LUA_REGISTRYINDEX); lua_pushliteral(L, "__scriptPtr"); lua_pushstring(L, script); lua_concat(L, 2); lua_pushlightuserdata(L, conf); lua_rawset(L, LUA_REGISTRYINDEX); } else { TSError("[ts_lua][%s] failed to get node's reconfigure time while registering script", __FUNCTION__); } } int ts_lua_add_module(ts_lua_instance_conf *conf, ts_lua_main_ctx *arr, int n, int argc, char *argv[], char *errbuf, int errbuf_size) { int i, ret; int t; lua_State *L; for (i = 0; i < n; i++) { conf->_first = (i == 0) ? 1 : 0; conf->_last = (i == n - 1) ? 1 : 0; TSMutexLock(arr[i].mutexp); L = arr[i].lua; lua_newtable(L); /* new TB1 */ lua_pushvalue(L, -1); /* new TB2 */ lua_setfield(L, -2, "_G"); /* TB1[_G] = TB2 empty table, we can change _G to xx */ lua_newtable(L); /* new TB3 */ lua_rawgeti(L, LUA_REGISTRYINDEX, arr[i].gref); /* push L[GLOBAL] */ lua_setfield(L, -2, "__index"); /* TB3[__index] = L[GLOBAL] which has ts.xxx api */ lua_setmetatable(L, -2); /* TB1[META] = TB3 */ lua_replace(L, LUA_GLOBALSINDEX); /* L[GLOBAL] = TB1 */ ts_lua_set_instance_conf(L, conf); if (conf->content) { if (luaL_loadstring(L, conf->content)) { snprintf(errbuf, errbuf_size, "[%s] luaL_loadstring failed: %s", __FUNCTION__, lua_tostring(L, -1)); lua_pop(L, 1); TSMutexUnlock(arr[i].mutexp); return -1; } } else if (strlen(conf->script)) { if (luaL_loadfile(L, conf->script)) { snprintf(errbuf, errbuf_size, "[%s] luaL_loadfile %s failed: %s", __FUNCTION__, conf->script, lua_tostring(L, -1)); lua_pop(L, 1); TSMutexUnlock(arr[i].mutexp); return -1; } } if (lua_pcall(L, 0, 0, 0)) { snprintf(errbuf, errbuf_size, "[%s] lua_pcall %s failed: %s", __FUNCTION__, conf->script, lua_tostring(L, -1)); lua_pop(L, 1); TSMutexUnlock(arr[i].mutexp); return -1; } /* call "__init__", to parse parameters */ lua_getglobal(L, "__init__"); if (lua_type(L, -1) == LUA_TFUNCTION) { // specifying that the file has an __init__ function conf->init_func = 1; lua_newtable(L); for (t = 0; t < argc; t++) { lua_pushnumber(L, t); lua_pushstring(L, argv[t]); lua_rawset(L, -3); } if (lua_pcall(L, 1, 1, 0)) { snprintf(errbuf, errbuf_size, "[%s] lua_pcall %s failed: %s", __FUNCTION__, conf->script, lua_tostring(L, -1)); lua_pop(L, 1); TSMutexUnlock(arr[i].mutexp); return -1; } ret = lua_tonumber(L, -1); lua_pop(L, 1); if (ret) { TSMutexUnlock(arr[i].mutexp); return -1; /* script parse error */ } } else { lua_pop(L, 1); /* pop nil */ } lua_pushlightuserdata(L, conf); lua_pushvalue(L, LUA_GLOBALSINDEX); lua_rawset(L, LUA_REGISTRYINDEX); /* L[REG][conf] = L[GLOBAL] */ lua_newtable(L); lua_replace(L, LUA_GLOBALSINDEX); /* L[GLOBAL] = EMPTY */ if (conf->ljgc > 0) { Dbg(dbg_ctl, "ljgc = %d, running LuaJIT Garbage Collector...", conf->ljgc); lua_gc(L, LUA_GCCOLLECT, 0); } else { Dbg(dbg_ctl, "ljgc = %d, NOT running LuaJIT Garbage Collector...", conf->ljgc); } TSMutexUnlock(arr[i].mutexp); } return 0; } int ts_lua_del_module(ts_lua_instance_conf *conf, ts_lua_main_ctx *arr, int n) { int i; lua_State *L; for (i = 0; i < n; i++) { TSMutexLock(arr[i].mutexp); L = arr[i].lua; /* call "__clean__", to clean resources */ lua_pushlightuserdata(L, conf); lua_rawget(L, LUA_REGISTRYINDEX); lua_replace(L, LUA_GLOBALSINDEX); /* L[GLOBAL] = L[REG][conf] */ lua_getglobal(L, "__clean__"); /* get __clean__ function */ if (lua_type(L, -1) == LUA_TFUNCTION) { if (lua_pcall(L, 0, 0, 0)) { TSError("[ts_lua][%s] lua_pcall %s failed: %s", __FUNCTION__, conf->script, lua_tostring(L, -1)); } } else { lua_pop(L, 1); /* pop nil */ } lua_pushlightuserdata(L, conf); if (conf->ref_count > 1) { Dbg(dbg_ctl, "Reference Count = %d , NOT clearing registry...", conf->ref_count); lua_pushvalue(L, LUA_GLOBALSINDEX); lua_rawset(L, LUA_REGISTRYINDEX); /* L[REG][conf] = L[GLOBAL] */ } else { Dbg(dbg_ctl, "Reference Count = %d , clearing registry...", conf->ref_count); lua_pushnil(L); lua_rawset(L, LUA_REGISTRYINDEX); /* L[REG][conf] = nil */ } lua_newtable(L); lua_replace(L, LUA_GLOBALSINDEX); /* L[GLOBAL] = EMPTY */ if (conf->ljgc > 0) { Dbg(dbg_ctl, "ljgc = %d, running LuaJIT Garbage Collector...", conf->ljgc); lua_gc(L, LUA_GCCOLLECT, 0); } else { Dbg(dbg_ctl, "ljgc = %d, NOT running LuaJIT Garbage Collector...", conf->ljgc); } TSMutexUnlock(arr[i].mutexp); } return 0; } int ts_lua_reload_module(ts_lua_instance_conf *conf, ts_lua_main_ctx *arr, int n) { int i; lua_State *L; for (i = 0; i < n; i++) { TSMutexLock(arr[i].mutexp); L = arr[i].lua; /* call "__reload__", to clean resources if necessary */ lua_pushlightuserdata(L, conf); lua_rawget(L, LUA_REGISTRYINDEX); lua_replace(L, LUA_GLOBALSINDEX); /* L[GLOBAL] = L[REG][conf] */ lua_getglobal(L, "__reload__"); /* get __clean__ function */ if (lua_type(L, -1) == LUA_TFUNCTION) { if (lua_pcall(L, 0, 0, 0)) { TSError("[ts_lua][%s] lua_pcall %s failed: %s", __FUNCTION__, conf->script, lua_tostring(L, -1)); } } else { lua_pop(L, 1); /* pop nil */ } // new global context lua_newtable(L); /* new TB1 */ lua_pushvalue(L, -1); /* new TB2 */ lua_setfield(L, -2, "_G"); /* TB1[_G] = TB2 empty table, we can change _G to xx */ lua_newtable(L); /* new TB3 */ lua_rawgeti(L, LUA_REGISTRYINDEX, arr[i].gref); /* push L[GLOBAL] */ lua_setfield(L, -2, "__index"); /* TB3[__index] = L[GLOBAL] which has ts.xxx api */ lua_setmetatable(L, -2); /* TB1[META] = TB3 */ lua_replace(L, LUA_GLOBALSINDEX); /* L[GLOBAL] = TB1 */ ts_lua_set_instance_conf(L, conf); if (strlen(conf->script)) { if (luaL_loadfile(L, conf->script)) { TSError("[ts_lua][%s] luaL_loadfile %s failed: %s", __FUNCTION__, conf->script, lua_tostring(L, -1)); } else { if (lua_pcall(L, 0, 0, 0)) { TSError("[ts_lua][%s] lua_pcall %s failed: %s", __FUNCTION__, conf->script, lua_tostring(L, -1)); } } } lua_pushlightuserdata(L, conf); lua_pushvalue(L, LUA_GLOBALSINDEX); lua_rawset(L, LUA_REGISTRYINDEX); /* L[REG][conf] = L[GLOBAL] */ lua_newtable(L); lua_replace(L, LUA_GLOBALSINDEX); /* L[GLOBAL] = EMPTY */ if (conf->ljgc > 0) { Dbg(dbg_ctl, "ljgc = %d, running LuaJIT Garbage Collector...", conf->ljgc); lua_gc(L, LUA_GCCOLLECT, 0); } else { Dbg(dbg_ctl, "ljgc = %d, NOT running LuaJIT Garbage Collector...", conf->ljgc); } TSMutexUnlock(arr[i].mutexp); } return 0; } int ts_lua_init_instance(ts_lua_instance_conf *conf ATS_UNUSED) { return 0; } int ts_lua_del_instance(ts_lua_instance_conf *conf ATS_UNUSED) { return 0; } static void ts_lua_init_registry(lua_State *L ATS_UNUSED) { return; } static void ts_lua_init_globals(lua_State *L) { ts_lua_inject_ts_api(L); } static void ts_lua_inject_ts_api(lua_State *L) { lua_newtable(L); ts_lua_inject_remap_api(L); ts_lua_inject_constant_api(L); ts_lua_inject_client_request_api(L); ts_lua_inject_server_request_api(L); ts_lua_inject_server_response_api(L); ts_lua_inject_client_response_api(L); ts_lua_inject_cached_response_api(L); ts_lua_inject_log_api(L); ts_lua_inject_context_api(L); ts_lua_inject_hook_api(L); ts_lua_inject_vconn_api(L); ts_lua_inject_http_api(L); ts_lua_inject_intercept_api(L); ts_lua_inject_misc_api(L); ts_lua_inject_crypto_api(L); ts_lua_inject_mgmt_api(L); ts_lua_inject_package_api(L); ts_lua_inject_stat_api(L); ts_lua_inject_fetch_api(L); lua_getglobal(L, "package"); lua_getfield(L, -1, "loaded"); lua_pushvalue(L, -3); lua_setfield(L, -2, "ts"); lua_pop(L, 2); lua_setglobal(L, "ts"); } void ts_lua_set_instance_conf(lua_State *L, ts_lua_instance_conf *conf) { lua_pushliteral(L, "__ts_instance_conf"); lua_pushlightuserdata(L, conf); lua_rawset(L, LUA_GLOBALSINDEX); } ts_lua_instance_conf * ts_lua_get_instance_conf(lua_State *L) { ts_lua_instance_conf *conf; lua_pushliteral(L, "__ts_instance_conf"); lua_rawget(L, LUA_GLOBALSINDEX); conf = static_cast<decltype(conf)>(lua_touserdata(L, -1)); lua_pop(L, 1); // pop the conf out return conf; } void ts_lua_set_cont_info(lua_State *L, ts_lua_cont_info *ci) { lua_pushliteral(L, "__ts_cont_info"); lua_pushlightuserdata(L, ci); lua_rawset(L, LUA_GLOBALSINDEX); } ts_lua_cont_info * ts_lua_get_cont_info(lua_State *L) { ts_lua_cont_info *ci; lua_pushliteral(L, "__ts_cont_info"); lua_rawget(L, LUA_GLOBALSINDEX); ci = static_cast<decltype(ci)>(lua_touserdata(L, -1)); lua_pop(L, 1); // pop the coroutine out return ci; } ts_lua_http_ctx * ts_lua_create_async_ctx(lua_State *L, ts_lua_cont_info *hci, int n) { int i; lua_State *l; ts_lua_coroutine *crt; ts_lua_http_ctx *actx; actx = TSRalloc<ts_lua_http_ctx>(); memset(actx, 0, sizeof(*actx)); // create lua_thread l = lua_newthread(L); // init the coroutine crt = &actx->cinfo.routine; crt->mctx = hci->routine.mctx; crt->lua = l; crt->ref = luaL_ref(L, LUA_REGISTRYINDEX); // update thread stats ts_lua_main_ctx *const main_ctx = crt->mctx; ts_lua_ctx_stats *const stats = main_ctx->stats; TSMutexLock(stats->mutexp); ++stats->threads; if (stats->threads_max < stats->threads) { stats->threads_max = stats->threads; } TSMutexUnlock(stats->mutexp); // replace the param; start with 2 because first two params are not needed for (i = 2; i < n; i++) { lua_pushvalue(L, i + 1); } lua_xmove(L, l, n - 2); return actx; } void ts_lua_destroy_async_ctx(ts_lua_http_ctx *http_ctx) { ts_lua_cont_info *ci; ci = &http_ctx->cinfo; // update thread stats ts_lua_main_ctx *const main_ctx = ci->routine.mctx; ts_lua_ctx_stats *const stats = main_ctx->stats; TSMutexLock(stats->mutexp); --stats->threads; TSMutexUnlock(stats->mutexp); ts_lua_release_cont_info(ci); TSfree(http_ctx); } void ts_lua_set_vconn_ctx(lua_State *L, ts_lua_vconn_ctx *ctx) { lua_pushliteral(L, "__ts_vconn_ctx"); lua_pushlightuserdata(L, ctx); lua_rawset(L, LUA_GLOBALSINDEX); } ts_lua_vconn_ctx * ts_lua_get_vconn_ctx(lua_State *L) { ts_lua_vconn_ctx *ctx; lua_pushliteral(L, "__ts_vconn_ctx"); lua_rawget(L, LUA_GLOBALSINDEX); ctx = static_cast<decltype(ctx)>(lua_touserdata(L, -1)); lua_pop(L, 1); // pop the ctx out return ctx; } ts_lua_vconn_ctx * ts_lua_create_vconn_ctx(ts_lua_main_ctx *main_ctx, ts_lua_instance_conf *conf) { ts_lua_vconn_ctx *vconn_ctx; vconn_ctx = TSRalloc<ts_lua_vconn_ctx>(); memset(vconn_ctx, 0, sizeof(*vconn_ctx)); lua_State *L = main_ctx->lua; lua_State *l = lua_newthread(L); lua_pushlightuserdata(L, conf); lua_rawget(L, LUA_REGISTRYINDEX); /* new globals table for coroutine */ lua_newtable(l); lua_pushvalue(l, -1); lua_setfield(l, -2, "_G"); lua_newtable(l); lua_xmove(L, l, 1); lua_setfield(l, -2, "__index"); lua_setmetatable(l, -2); lua_replace(l, LUA_GLOBALSINDEX); // init coroutine vconn_ctx->ref = luaL_ref(L, LUA_REGISTRYINDEX); vconn_ctx->lua = l; vconn_ctx->mctx = main_ctx; // update thread stats ts_lua_ctx_stats *const stats = main_ctx->stats; TSMutexLock(stats->mutexp); ++stats->threads; if (stats->threads_max < stats->threads) { stats->threads_max = stats->threads; } TSMutexUnlock(stats->mutexp); vconn_ctx->instance_conf = conf; ts_lua_set_vconn_ctx(l, vconn_ctx); ts_lua_create_context_table(l); return vconn_ctx; } void ts_lua_destroy_vconn_ctx(ts_lua_vconn_ctx *vconn_ctx) { // update thread stats ts_lua_main_ctx *const main_ctx = vconn_ctx->mctx; ts_lua_ctx_stats *const stats = main_ctx->stats; TSMutexLock(stats->mutexp); --stats->threads; TSMutexUnlock(stats->mutexp); if (vconn_ctx->lua) { luaL_unref(vconn_ctx->lua, LUA_REGISTRYINDEX, vconn_ctx->ref); } TSfree(vconn_ctx); } void ts_lua_set_http_ctx(lua_State *L, ts_lua_http_ctx *ctx) { lua_pushliteral(L, "__ts_http_ctx"); lua_pushlightuserdata(L, ctx); lua_rawset(L, LUA_GLOBALSINDEX); } ts_lua_http_ctx * ts_lua_get_http_ctx(lua_State *L) { ts_lua_http_ctx *ctx; lua_pushliteral(L, "__ts_http_ctx"); lua_rawget(L, LUA_GLOBALSINDEX); ctx = static_cast<decltype(ctx)>(lua_touserdata(L, -1)); lua_pop(L, 1); // pop the ctx out return ctx; } ts_lua_http_ctx * ts_lua_create_http_ctx(ts_lua_main_ctx *main_ctx, ts_lua_instance_conf *conf) { ts_lua_coroutine *crt; ts_lua_http_ctx *http_ctx; lua_State *L; lua_State *l; L = main_ctx->lua; http_ctx = TSRalloc<ts_lua_http_ctx>(); memset(http_ctx, 0, sizeof(*http_ctx)); // create coroutine for http_ctx crt = &http_ctx->cinfo.routine; l = lua_newthread(L); lua_pushlightuserdata(L, conf); lua_rawget(L, LUA_REGISTRYINDEX); /* new globals table for coroutine */ lua_newtable(l); lua_pushvalue(l, -1); lua_setfield(l, -2, "_G"); lua_newtable(l); lua_xmove(L, l, 1); lua_setfield(l, -2, "__index"); lua_setmetatable(l, -2); lua_replace(l, LUA_GLOBALSINDEX); // init coroutine crt->ref = luaL_ref(L, LUA_REGISTRYINDEX); crt->lua = l; crt->mctx = main_ctx; // update thread stats ts_lua_ctx_stats *const stats = main_ctx->stats; TSMutexLock(stats->mutexp); ++stats->threads; if (stats->threads_max < stats->threads) { stats->threads_max = stats->threads; } TSMutexUnlock(stats->mutexp); http_ctx->instance_conf = conf; ts_lua_set_http_ctx(l, http_ctx); ts_lua_create_context_table(l); return http_ctx; } void ts_lua_destroy_http_ctx(ts_lua_http_ctx *http_ctx) { ts_lua_cont_info *ci; ci = &http_ctx->cinfo; if (http_ctx->rri == nullptr) { if (http_ctx->client_request_url) { TSHandleMLocRelease(http_ctx->client_request_bufp, http_ctx->client_request_hdrp, http_ctx->client_request_url); } if (http_ctx->client_request_bufp) { TSHandleMLocRelease(http_ctx->client_request_bufp, TS_NULL_MLOC, http_ctx->client_request_hdrp); } } if (http_ctx->server_request_url) { TSHandleMLocRelease(http_ctx->server_request_bufp, http_ctx->server_request_hdrp, http_ctx->server_request_url); } if (http_ctx->server_request_bufp) { TSHandleMLocRelease(http_ctx->server_request_bufp, TS_NULL_MLOC, http_ctx->server_request_hdrp); } if (http_ctx->server_response_bufp) { TSHandleMLocRelease(http_ctx->server_response_bufp, TS_NULL_MLOC, http_ctx->server_response_hdrp); } if (http_ctx->client_response_bufp) { TSHandleMLocRelease(http_ctx->client_response_bufp, TS_NULL_MLOC, http_ctx->client_response_hdrp); } if (http_ctx->cached_response_bufp) { TSMimeHdrDestroy(http_ctx->cached_response_bufp, http_ctx->cached_response_hdrp); TSHandleMLocRelease(http_ctx->cached_response_bufp, TS_NULL_MLOC, http_ctx->cached_response_hdrp); TSMBufferDestroy(http_ctx->cached_response_bufp); } // update thread stats ts_lua_main_ctx *const main_ctx = ci->routine.mctx; ts_lua_ctx_stats *const stats = main_ctx->stats; TSMutexLock(stats->mutexp); --stats->threads; TSMutexUnlock(stats->mutexp); ts_lua_release_cont_info(ci); TSfree(http_ctx); } void ts_lua_set_http_intercept_ctx(lua_State *L, ts_lua_http_intercept_ctx *ictx) { lua_pushliteral(L, "__ts_http_intercept_ctx"); lua_pushlightuserdata(L, ictx); lua_rawset(L, LUA_GLOBALSINDEX); } ts_lua_http_intercept_ctx * ts_lua_get_http_intercept_ctx(lua_State *L) { ts_lua_http_intercept_ctx *ictx; lua_pushliteral(L, "__ts_http_intercept_ctx"); lua_rawget(L, LUA_GLOBALSINDEX); ictx = static_cast<decltype(ictx)>(lua_touserdata(L, -1)); lua_pop(L, 1); // pop the ictx out return ictx; } ts_lua_http_intercept_ctx * ts_lua_create_http_intercept_ctx(lua_State *L, ts_lua_http_ctx *http_ctx, int n) { int i; lua_State *l; ts_lua_cont_info *hci; ts_lua_coroutine *crt; ts_lua_http_intercept_ctx *ictx; hci = &http_ctx->cinfo; ictx = TSRalloc<ts_lua_http_intercept_ctx>(); memset(ictx, 0, sizeof(*ictx)); ictx->hctx = http_ctx; // create lua_thread l = lua_newthread(L); // init the coroutine crt = &ictx->cinfo.routine; crt->mctx = hci->routine.mctx; crt->lua = l; crt->ref = luaL_ref(L, LUA_REGISTRYINDEX); // Todo: replace the global, context table for crt->lua // replicate the param for (i = 0; i < n; i++) { lua_pushvalue(L, i + 1); } lua_xmove(L, l, n); // move the intercept function and params to the new lua_thread ts_lua_set_http_intercept_ctx(l, ictx); return ictx; } void ts_lua_destroy_http_intercept_ctx(ts_lua_http_intercept_ctx *ictx) { ts_lua_cont_info *ci; ci = &ictx->cinfo; if (ictx->net_vc) { TSVConnClose(ictx->net_vc); } TS_LUA_RELEASE_IO_HANDLE((&ictx->input)); TS_LUA_RELEASE_IO_HANDLE((&ictx->output)); ts_lua_release_cont_info(ci); TSfree(ictx); } void ts_lua_set_http_transform_ctx(lua_State *L, ts_lua_http_transform_ctx *tctx) { lua_pushliteral(L, "__ts_http_transform_ctx"); lua_pushlightuserdata(L, tctx); lua_rawset(L, LUA_GLOBALSINDEX); } ts_lua_http_transform_ctx * ts_lua_get_http_transform_ctx(lua_State *L) { ts_lua_http_transform_ctx *tctx; lua_pushliteral(L, "__ts_http_transform_ctx"); lua_rawget(L, LUA_GLOBALSINDEX); tctx = static_cast<decltype(tctx)>(lua_touserdata(L, -1)); lua_pop(L, 1); // pop the ictx out return tctx; } ts_lua_http_transform_ctx * ts_lua_create_http_transform_ctx(ts_lua_http_ctx *http_ctx, TSVConn connp) { lua_State *L; ts_lua_cont_info *hci; ts_lua_cont_info *ci; ts_lua_coroutine *crt; ts_lua_http_transform_ctx *transform_ctx; hci = &http_ctx->cinfo; L = hci->routine.lua; transform_ctx = TSRalloc<ts_lua_http_transform_ctx>(); memset(transform_ctx, 0, sizeof(*transform_ctx)); transform_ctx->hctx = http_ctx; TSContDataSet(connp, transform_ctx); ci = &transform_ctx->cinfo; ci->contp = connp; ci->mutex = TSContMutexGet((TSCont)http_ctx->txnp); crt = &ci->routine; crt->mctx = hci->routine.mctx; crt->lua = lua_newthread(L); crt->ref = luaL_ref(L, LUA_REGISTRYINDEX); ts_lua_set_http_transform_ctx(crt->lua, transform_ctx); lua_pushlightuserdata(L, transform_ctx); lua_pushvalue(L, 2); lua_rawset(L, LUA_GLOBALSINDEX); // L[GLOBAL][transform_ctx] = transform handler return transform_ctx; } void ts_lua_destroy_http_transform_ctx(ts_lua_http_transform_ctx *transform_ctx) { ts_lua_cont_info *ci; if (!transform_ctx) { return; } ci = &transform_ctx->cinfo; TS_LUA_RELEASE_IO_HANDLE((&transform_ctx->output)); TS_LUA_RELEASE_IO_HANDLE((&transform_ctx->reserved)); ts_lua_release_cont_info(ci); TSfree(transform_ctx); } int ts_lua_http_cont_handler(TSCont contp, TSEvent ev, void *edata) { TSHttpTxn txnp; TSMBuffer bufp; TSMLoc hdr_loc; TSMLoc url_loc; int event, ret, rc, n, t; lua_State *L; ts_lua_http_ctx *http_ctx; ts_lua_main_ctx *main_ctx; ts_lua_cont_info *ci; ts_lua_coroutine *crt; event = (int)ev; http_ctx = (ts_lua_http_ctx *)TSContDataGet(contp); ci = &http_ctx->cinfo; crt = &ci->routine; main_ctx = crt->mctx; L = crt->lua; txnp = http_ctx->txnp; rc = ret = 0; TSMutexLock(main_ctx->mutexp); if (!http_ctx->client_request_bufp) { if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) { http_ctx->client_request_bufp = bufp; http_ctx->client_request_hdrp = hdr_loc; if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { http_ctx->client_request_url = url_loc; } } } if (!http_ctx->client_request_hdrp) { TSMutexUnlock(main_ctx->mutexp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); return 0; } ts_lua_set_cont_info(L, ci); switch (event) { case TS_EVENT_HTTP_POST_REMAP: lua_getglobal(L, TS_LUA_FUNCTION_POST_REMAP); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE: lua_getglobal(L, TS_LUA_FUNCTION_CACHE_LOOKUP_COMPLETE); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_SEND_REQUEST_HDR: lua_getglobal(L, TS_LUA_FUNCTION_SEND_REQUEST); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_READ_RESPONSE_HDR: ts_lua_update_server_response_hdrp(http_ctx); lua_getglobal(L, TS_LUA_FUNCTION_READ_RESPONSE); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_SEND_RESPONSE_HDR: lua_getglobal(L, TS_LUA_FUNCTION_SEND_RESPONSE); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_READ_REQUEST_HDR: lua_getglobal(L, TS_LUA_FUNCTION_READ_REQUEST); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_TXN_START: lua_getglobal(L, TS_LUA_FUNCTION_TXN_START); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_PRE_REMAP: lua_getglobal(L, TS_LUA_FUNCTION_PRE_REMAP); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_OS_DNS: lua_getglobal(L, TS_LUA_FUNCTION_OS_DNS); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_READ_CACHE_HDR: lua_getglobal(L, TS_LUA_FUNCTION_READ_CACHE); if (lua_type(L, -1) == LUA_TFUNCTION) { ret = lua_resume(L, 0); } ts_lua_clear_http_ctx(http_ctx); break; case TS_EVENT_HTTP_TXN_CLOSE: ts_lua_update_server_response_hdrp(http_ctx); lua_getglobal(L, TS_LUA_FUNCTION_TXN_CLOSE); if (lua_type(L, -1) == LUA_TFUNCTION) { if (lua_pcall(L, 0, 1, 0)) { TSError("[ts_lua][%s] lua_pcall failed: %s", __FUNCTION__, lua_tostring(L, -1)); } } ts_lua_destroy_http_ctx(http_ctx); break; case TS_LUA_EVENT_COROUTINE_CONT: n = (intptr_t)edata; ret = lua_resume(L, n); default: break; } switch (ret) { case 0: // coroutine succeed t = lua_gettop(L); if (t > 0) { rc = lua_tointeger(L, -1); lua_pop(L, 1); } break; case LUA_YIELD: // coroutine yield rc = 1; break; default: // coroutine failed TSError("[ts_lua][%s] lua_resume failed: %s", __FUNCTION__, lua_tostring(L, -1)); rc = -1; lua_pop(L, 1); break; } // current memory in use by this state int const gc_kb = lua_getgccount(L); TSMutexUnlock(main_ctx->mutexp); // collect state memory stats ts_lua_ctx_stats *const stats = main_ctx->stats; TSMutexLock(stats->mutexp); if (gc_kb != stats->gc_kb) { stats->gc_kb = gc_kb; if (stats->gc_kb_max < stats->gc_kb) { stats->gc_kb_max = stats->gc_kb; } } TSMutexUnlock(stats->mutexp); if (rc == 0) { TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); } else if (rc < 0) { TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR); } else { // wait for async } return 0; } namespace ts_lua_ns { DbgCtl dbg_ctl{TS_LUA_DEBUG_TAG}; }