static int ts_lua_schedule_handler(TSCont contp, TSEvent ev, void *edata) { lua_State *L; ts_lua_cont_info *ci; ts_lua_coroutine *crt; int event, n, ret; ts_lua_http_ctx *actx; ts_lua_main_ctx *main_ctx; event = (int)ev; TSDebug(TS_LUA_DEBUG_TAG, "getting actx and other info"); actx = (ts_lua_http_ctx *)TSContDataGet(contp); TSDebug(TS_LUA_DEBUG_TAG, "getting http_Ctx"); ci = &actx->cinfo; crt = &ci->routine; main_ctx = crt->mctx; L = crt->lua; ret = 0; TSMutexLock(main_ctx->mutexp); ts_lua_set_cont_info(L, ci); if (event == TS_LUA_EVENT_COROUTINE_CONT) { TSDebug(TS_LUA_DEBUG_TAG, "event is coroutine_cont"); n = (intptr_t)edata; ret = lua_resume(L, n); } else { TSDebug(TS_LUA_DEBUG_TAG, "event is not coroutine_cont"); n = lua_gettop(L); ret = lua_resume(L, n - 1); } if (ret == LUA_YIELD) { TSMutexUnlock(main_ctx->mutexp); goto done; } if (ret != 0) { TSError("[ts_lua] lua_resume failed: %s", lua_tostring(L, -1)); } lua_pop(L, lua_gettop(L)); TSMutexUnlock(main_ctx->mutexp); ts_lua_destroy_async_ctx(actx); done: return 0; }
static int ts_lua_transform_handler(TSCont contp, ts_lua_http_transform_ctx *transform_ctx, TSEvent event, int n) { TSVConn output_conn; TSVIO input_vio; TSIOBufferReader input_reader; TSIOBufferBlock blk; int64_t toread, towrite, blk_len, upstream_done, input_avail, input_wm_bytes, l; const char *start; const char *res; size_t res_len; int ret, eos, write_down, rc, top, empty_input; ts_lua_coroutine *crt; ts_lua_cont_info *ci; lua_State *L; TSMutex mtxp; ci = &transform_ctx->cinfo; crt = &ci->routine; mtxp = crt->mctx->mutexp; L = crt->lua; output_conn = TSTransformOutputVConnGet(contp); input_vio = TSVConnWriteVIOGet(contp); empty_input = 0; if (!TSVIOBufferGet(input_vio)) { if (transform_ctx->output.vio) { TSDebug(TS_LUA_DEBUG_TAG, "[%s] reenabling output VIO after input VIO does not exist", __FUNCTION__); TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total); TSVIOReenable(transform_ctx->output.vio); return 0; } else { TSDebug(TS_LUA_DEBUG_TAG, "[%s] no input VIO and output VIO", __FUNCTION__); empty_input = 1; } } else { // input VIO exists input_wm_bytes = TSIOBufferWaterMarkGet(TSVIOBufferGet(input_vio)); if (transform_ctx->upstream_watermark_bytes >= 0 && transform_ctx->upstream_watermark_bytes != input_wm_bytes) { TSDebug(TS_LUA_DEBUG_TAG, "[%s] Setting input_vio watermark to %" PRId64 " bytes", __FUNCTION__, transform_ctx->upstream_watermark_bytes); TSIOBufferWaterMarkSet(TSVIOBufferGet(input_vio), transform_ctx->upstream_watermark_bytes); } } if (empty_input == 0) { input_reader = TSVIOReaderGet(input_vio); } if (!transform_ctx->output.buffer) { transform_ctx->output.buffer = TSIOBufferCreate(); transform_ctx->output.reader = TSIOBufferReaderAlloc(transform_ctx->output.buffer); transform_ctx->reserved.buffer = TSIOBufferCreate(); transform_ctx->reserved.reader = TSIOBufferReaderAlloc(transform_ctx->reserved.buffer); if (empty_input == 0) { transform_ctx->upstream_bytes = TSVIONBytesGet(input_vio); } else { transform_ctx->upstream_bytes = 0; } transform_ctx->downstream_bytes = INT64_MAX; } if (empty_input == 0) { input_avail = TSIOBufferReaderAvail(input_reader); upstream_done = TSVIONDoneGet(input_vio); toread = TSVIONTodoGet(input_vio); if (toread <= input_avail) { // upstream finished eos = 1; } else { eos = 0; } } else { input_avail = 0; toread = 0; eos = 1; } if (input_avail > 0) { // move to the reserved.buffer TSIOBufferCopy(transform_ctx->reserved.buffer, input_reader, input_avail, 0); // reset input TSIOBufferReaderConsume(input_reader, input_avail); TSVIONDoneSet(input_vio, upstream_done + input_avail); } write_down = 0; if (empty_input == 0) { towrite = TSIOBufferReaderAvail(transform_ctx->reserved.reader); } else { towrite = 0; } TSMutexLock(mtxp); ts_lua_set_cont_info(L, ci); do { if (event == TS_LUA_EVENT_COROUTINE_CONT) { event = 0; goto launch; } else { n = 2; } if (towrite == 0 && empty_input == 0) { break; } if (empty_input == 0) { blk = TSIOBufferReaderStart(transform_ctx->reserved.reader); start = TSIOBufferBlockReadStart(blk, transform_ctx->reserved.reader, &blk_len); lua_pushlightuserdata(L, transform_ctx); lua_rawget(L, LUA_GLOBALSINDEX); /* push function */ if (towrite > blk_len) { lua_pushlstring(L, start, (size_t)blk_len); towrite -= blk_len; TSIOBufferReaderConsume(transform_ctx->reserved.reader, blk_len); } else { lua_pushlstring(L, start, (size_t)towrite); TSIOBufferReaderConsume(transform_ctx->reserved.reader, towrite); towrite = 0; } if (!towrite && eos) { lua_pushinteger(L, 1); /* second param, data finished */ } else { lua_pushinteger(L, 0); /* second param, data not finish */ } } else { lua_pushlightuserdata(L, transform_ctx); lua_rawget(L, LUA_GLOBALSINDEX); /* push function */ lua_pushlstring(L, "", 0); lua_pushinteger(L, 1); /* second param, data finished */ } launch: rc = lua_resume(L, n); top = lua_gettop(L); switch (rc) { case LUA_YIELD: // coroutine yield TSMutexUnlock(mtxp); return 0; case 0: // coroutine success if (top == 2) { ret = lua_tointeger(L, -1); /* 0 is not finished, 1 is finished */ res = lua_tolstring(L, -2, &res_len); } else { // what hells code are you writing ? ret = 0; res = NULL; res_len = 0; } break; default: // coroutine failed TSError("[ts_lua] lua_resume failed: %s", lua_tostring(L, -1)); ret = 1; res = NULL; res_len = 0; break; } if (res && res_len > 0) { if (!transform_ctx->output.vio) { l = transform_ctx->downstream_bytes; if (ret) { l = res_len; } transform_ctx->output.vio = TSVConnWrite(output_conn, contp, transform_ctx->output.reader, l); // HttpSM go on } TSIOBufferWrite(transform_ctx->output.buffer, res, res_len); transform_ctx->total += res_len; write_down = 1; } lua_pop(L, top); if (ret || (eos && !towrite)) { // EOS eos = 1; break; } } while (towrite > 0); TSMutexUnlock(mtxp); if (eos && !transform_ctx->output.vio) { transform_ctx->output.vio = TSVConnWrite(output_conn, contp, transform_ctx->output.reader, 0); } if (write_down || eos) { TSVIOReenable(transform_ctx->output.vio); } if (toread > input_avail) { // upstream not finished. if (eos) { TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total); if (empty_input == 0) { TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_EOS, input_vio); } } else { if (empty_input == 0) { TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); } } } else { // upstream is finished. TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total); if (empty_input == 0) { TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); } } return 0; }
static int globalHookHandler(TSCont contp, TSEvent event ATS_UNUSED, void *edata) { TSHttpTxn txnp = (TSHttpTxn)edata; TSMBuffer bufp; TSMLoc hdr_loc; TSMLoc url_loc; int ret; uint64_t req_id; TSCont txn_contp; lua_State *l; ts_lua_main_ctx *main_ctx; ts_lua_http_ctx *http_ctx; ts_lua_cont_info *ci; ts_lua_instance_conf *conf = (ts_lua_instance_conf *)TSContDataGet(contp); req_id = __sync_fetch_and_add(&ts_lua_g_http_next_id, 1); main_ctx = &ts_lua_g_main_ctx_array[req_id % TS_LUA_MAX_STATE_COUNT]; TSDebug(TS_LUA_DEBUG_TAG, "[%s] req_id: %" PRId64, __FUNCTION__, req_id); TSMutexLock(main_ctx->mutexp); http_ctx = ts_lua_create_http_ctx(main_ctx, conf); http_ctx->txnp = txnp; http_ctx->rri = NULL; http_ctx->has_hook = 0; 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) { ts_lua_destroy_http_ctx(http_ctx); TSMutexUnlock(main_ctx->mutexp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); return 0; } txn_contp = TSContCreate(ts_lua_http_cont_handler, NULL); TSContDataSet(txn_contp, http_ctx); ci = &http_ctx->cinfo; ci->contp = txn_contp; ci->mutex = TSContMutexGet((TSCont)txnp); l = ci->routine.lua; switch (event) { case TS_EVENT_HTTP_READ_REQUEST_HDR: lua_getglobal(l, TS_LUA_FUNCTION_G_READ_REQUEST); break; case TS_EVENT_HTTP_SEND_REQUEST_HDR: lua_getglobal(l, TS_LUA_FUNCTION_G_SEND_REQUEST); break; case TS_EVENT_HTTP_READ_RESPONSE_HDR: lua_getglobal(l, TS_LUA_FUNCTION_G_READ_RESPONSE); 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_hdrp = NULL; } lua_getglobal(l, TS_LUA_FUNCTION_G_SEND_RESPONSE); break; case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE: lua_getglobal(l, TS_LUA_FUNCTION_G_CACHE_LOOKUP_COMPLETE); break; case TS_EVENT_HTTP_TXN_START: lua_getglobal(l, TS_LUA_FUNCTION_G_TXN_START); break; case TS_EVENT_HTTP_PRE_REMAP: lua_getglobal(l, TS_LUA_FUNCTION_G_PRE_REMAP); break; case TS_EVENT_HTTP_POST_REMAP: lua_getglobal(l, TS_LUA_FUNCTION_G_POST_REMAP); break; case TS_EVENT_HTTP_SELECT_ALT: lua_getglobal(l, TS_LUA_FUNCTION_G_SELECT_ALT); break; case TS_EVENT_HTTP_OS_DNS: lua_getglobal(l, TS_LUA_FUNCTION_G_OS_DNS); break; case TS_EVENT_HTTP_READ_CACHE_HDR: lua_getglobal(l, TS_LUA_FUNCTION_G_READ_CACHE); break; case TS_EVENT_HTTP_TXN_CLOSE: lua_getglobal(l, TS_LUA_FUNCTION_G_TXN_CLOSE); break; default: ts_lua_destroy_http_ctx(http_ctx); TSMutexUnlock(main_ctx->mutexp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); return 0; } if (lua_type(l, -1) != LUA_TFUNCTION) { lua_pop(l, 1); ts_lua_destroy_http_ctx(http_ctx); TSMutexUnlock(main_ctx->mutexp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); return 0; } ts_lua_set_cont_info(l, NULL); if (lua_pcall(l, 0, 1, 0) != 0) { TSError("[ts_lua] lua_pcall failed: %s", lua_tostring(l, -1)); } ret = lua_tointeger(l, -1); lua_pop(l, 1); if (http_ctx->has_hook) { // add a hook to release resources for context TSDebug(TS_LUA_DEBUG_TAG, "[%s] has txn hook -> adding txn close hook handler to release resources", __FUNCTION__); TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp); } else { TSDebug(TS_LUA_DEBUG_TAG, "[%s] no txn hook -> release resources now", __FUNCTION__); ts_lua_destroy_http_ctx(http_ctx); } TSMutexUnlock(main_ctx->mutexp); if (ret) { TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR); } else { TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); } return 0; }
static TSRemapStatus ts_lua_remap_plugin_init(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) { int ret; uint64_t req_id; TSCont contp; lua_State *L; ts_lua_main_ctx *main_ctx; ts_lua_http_ctx *http_ctx; ts_lua_cont_info *ci; ts_lua_instance_conf *instance_conf; int remap = (rri == NULL ? 0 : 1); instance_conf = (ts_lua_instance_conf *)ih; req_id = __sync_fetch_and_add(&ts_lua_http_next_id, 1); main_ctx = &ts_lua_main_ctx_array[req_id % TS_LUA_MAX_STATE_COUNT]; TSMutexLock(main_ctx->mutexp); http_ctx = ts_lua_create_http_ctx(main_ctx, instance_conf); http_ctx->txnp = rh; http_ctx->has_hook = 0; http_ctx->rri = rri; if (rri != NULL) { http_ctx->client_request_bufp = rri->requestBufp; http_ctx->client_request_hdrp = rri->requestHdrp; http_ctx->client_request_url = rri->requestUrl; } ci = &http_ctx->cinfo; L = ci->routine.lua; contp = TSContCreate(ts_lua_http_cont_handler, NULL); TSContDataSet(contp, http_ctx); ci->contp = contp; ci->mutex = TSContMutexGet((TSCont)rh); lua_getglobal(L, (remap ? TS_LUA_FUNCTION_REMAP : TS_LUA_FUNCTION_OS_RESPONSE)); if (lua_type(L, -1) != LUA_TFUNCTION) { TSMutexUnlock(main_ctx->mutexp); return TSREMAP_NO_REMAP; } ts_lua_set_cont_info(L, NULL); if (lua_pcall(L, 0, 1, 0) != 0) { TSError("[ts_lua] lua_pcall failed: %s", lua_tostring(L, -1)); ret = TSREMAP_NO_REMAP; } else { ret = lua_tointeger(L, -1); } lua_pop(L, 1); if (http_ctx->has_hook) { TSDebug(TS_LUA_DEBUG_TAG, "[%s] has txn hook -> adding txn close hook handler to release resources", __FUNCTION__); TSHttpTxnHookAdd(rh, TS_HTTP_TXN_CLOSE_HOOK, contp); } else { TSDebug(TS_LUA_DEBUG_TAG, "[%s] no txn hook -> release resources now", __FUNCTION__); ts_lua_destroy_http_ctx(http_ctx); } TSMutexUnlock(main_ctx->mutexp); return ret; }