| /* |
| 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_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); |
| |
| 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 == NULL) |
| 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(); |
| } |
| |
| return 0; |
| } |
| |
| void |
| ts_lua_destroy_vm(ts_lua_main_ctx *arr, int n) |
| { |
| int i; |
| lua_State *L; |
| |
| for (i = 0; i < n; i++) { |
| L = arr[i].lua; |
| if (L) |
| lua_close(L); |
| } |
| |
| return; |
| } |
| |
| lua_State * |
| ts_lua_new_state() |
| { |
| lua_State *L; |
| |
| L = luaL_newstate(); |
| |
| if (L == NULL) { |
| return NULL; |
| } |
| |
| luaL_openlibs(L); |
| |
| ts_lua_init_registry(L); |
| |
| ts_lua_init_globals(L); |
| |
| return L; |
| } |
| |
| ts_lua_instance_conf * |
| ts_lua_script_registered(lua_State *L, char *script) |
| { |
| TSMgmtInt curr_time; |
| ts_lua_instance_conf *conf = NULL; |
| |
| TSDebug(TS_LUA_DEBUG_TAG, "[%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.node.config.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)) { |
| TSDebug(TS_LUA_DEBUG_TAG, "[%s] failed to get script time for [%s]", __FUNCTION__, script); |
| lua_pop(L, -1); |
| return NULL; |
| } 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)) { |
| TSDebug(TS_LUA_DEBUG_TAG, "[%s] failed to get script ptr for [%s]", __FUNCTION__, script); |
| lua_pop(L, -1); |
| return NULL; |
| } else { |
| conf = lua_touserdata(L, -1); |
| lua_pop(L, -1); |
| return conf; |
| } |
| } else { |
| TSDebug(TS_LUA_DEBUG_TAG, "[%s] script time not matching for [%s]", __FUNCTION__, script); |
| return NULL; |
| } |
| } |
| |
| } else { |
| TSError("[ts_lua][%s] failed to get node's reconfigure time while checking script registration", __FUNCTION__); |
| return NULL; |
| } |
| } |
| |
| void |
| ts_lua_script_register(lua_State *L, char *script, ts_lua_instance_conf *conf) |
| { |
| TSMgmtInt time; |
| |
| TSDebug(TS_LUA_DEBUG_TAG, "[%s] registering script [%s]", __FUNCTION__, script); |
| |
| // we recorded the script reconfigure_time and its conf pointer in registry |
| if (TS_SUCCESS == TSMgmtIntGet("proxy.node.config.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 */ |
| |
| lua_gc(L, LUA_GCCOLLECT, 0); |
| |
| 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); |
| 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 */ |
| |
| lua_gc(L, LUA_GCCOLLECT, 0); |
| |
| 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 */ |
| |
| lua_gc(L, LUA_GCCOLLECT, 0); |
| |
| 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_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 = 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 = 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 = TSmalloc(sizeof(ts_lua_http_ctx)); |
| memset(actx, 0, sizeof(ts_lua_http_ctx)); |
| |
| // 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); |
| |
| // 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; |
| |
| ts_lua_release_cont_info(ci); |
| TSfree(http_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 = 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 = TSmalloc(sizeof(ts_lua_http_ctx)); |
| memset(http_ctx, 0, sizeof(ts_lua_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; |
| |
| 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 == NULL) { |
| 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); |
| } |
| |
| 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 = 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 = TSmalloc(sizeof(ts_lua_http_intercept_ctx)); |
| memset(ictx, 0, sizeof(ts_lua_http_intercept_ctx)); |
| |
| 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 = 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 = (ts_lua_http_transform_ctx *)TSmalloc(sizeof(ts_lua_http_transform_ctx)); |
| memset(transform_ctx, 0, sizeof(ts_lua_http_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; |
| 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); |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| break; |
| |
| case TS_EVENT_HTTP_READ_RESPONSE_HDR: |
| |
| lua_getglobal(L, TS_LUA_FUNCTION_READ_RESPONSE); |
| |
| if (lua_type(L, -1) == LUA_TFUNCTION) { |
| ret = lua_resume(L, 0); |
| } |
| |
| break; |
| |
| case TS_EVENT_HTTP_SEND_RESPONSE_HDR: |
| |
| // client response can be changed within a transaction |
| // (e.g. due to the follow redirect feature). So, clearing the pointers |
| // to allow API(s) to fetch the pointers again when it re-enters the hook |
| if (http_ctx->client_response_hdrp != NULL) { |
| TSHandleMLocRelease(http_ctx->client_response_bufp, TS_NULL_MLOC, http_ctx->client_response_hdrp); |
| http_ctx->client_response_bufp = NULL; |
| http_ctx->client_response_hdrp = NULL; |
| } |
| |
| lua_getglobal(L, TS_LUA_FUNCTION_SEND_RESPONSE); |
| |
| if (lua_type(L, -1) == LUA_TFUNCTION) { |
| ret = lua_resume(L, 0); |
| } |
| |
| if (http_ctx->client_response_hdrp != NULL) { |
| TSHandleMLocRelease(http_ctx->client_response_bufp, TS_NULL_MLOC, http_ctx->client_response_hdrp); |
| http_ctx->client_response_bufp = NULL; |
| http_ctx->client_response_hdrp = NULL; |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| break; |
| |
| case TS_EVENT_HTTP_TXN_CLOSE: |
| 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; |
| } |
| |
| TSMutexUnlock(main_ctx->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; |
| } |