void add_to_queue(Queue *q, void *data) { Cell *new_cell; int n; if (data != NULL) { TSMutexLock(q->mutex); /* Init the new cell */ new_cell = TSmalloc(sizeof(Cell)); new_cell->magic = MAGIC_ALIVE; new_cell->ptr_data = data; new_cell->ptr_next = q->tail; new_cell->ptr_prev = NULL; /* Add this new cell to the queue */ if (q->tail == NULL) { TSAssert(q->head == NULL); TSAssert(q->nb_elem == 0); q->tail = new_cell; q->head = new_cell; } else { TSAssert(q->tail->magic == MAGIC_ALIVE); q->tail->ptr_prev = new_cell; q->tail = new_cell; } n = q->nb_elem++; TSMutexUnlock(q->mutex); if (n > MAX_JOBS_ALARM) { TSError("[thread_pool] Warning:Too many jobs in plugin thread pool queue (%d). Maximum allowed is %d", n, MAX_JOBS_ALARM); } } }
TSRemapStatus TSRemapDoRemap(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; 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->client_request_bufp = rri->requestBufp; http_ctx->client_request_hdrp = rri->requestHdrp; http_ctx->client_request_url = rri->requestUrl; http_ctx->rri = rri; 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); // push do_remap function on the stack, and no async operation should exist here. lua_getglobal(L, TS_LUA_FUNCTION_REMAP); if (lua_pcall(L, 0, 1, 0) != 0) { ee("lua_pcall failed: %s", lua_tostring(L, -1)); ret = TSREMAP_NO_REMAP; } else { ret = lua_tointeger(L, -1); } lua_pop(L, 1); // pop the result if (http_ctx->hooks > 0) { TSMutexUnlock(main_ctx->mutexp); TSHttpTxnHookAdd(rh, TS_HTTP_TXN_CLOSE_HOOK, contp); } else { ts_lua_destroy_http_ctx(http_ctx); TSMutexUnlock(main_ctx->mutexp); } return ret; }
int ts_http_fetcher_callback_sm(http_fetcher *fch, TSEvent event) { if (fch->deleted && !fch->ref) { ts_http_fetcher_release(fch); return -1; } if (event == TS_FETCH_EVENT_BODY_QUIET) return 0; fch->ref++; if (fch->flags & TS_FETCH_FLAG_USE_NEW_LOCK) TSMutexLock(TSContMutexGet(fch->contp)); TSContCall(fch->contp, event, fch); if (fch->flags & TS_FETCH_FLAG_USE_NEW_LOCK) TSMutexUnlock(TSContMutexGet(fch->contp)); fch->ref--; if (fch->deleted && !fch->ref) { ts_http_fetcher_release(fch); return -1; } return 0; }
void * remove_from_queue(Queue *q) { void *data = NULL; Cell *remove_cell; TSMutexLock(q->mutex); if (q->nb_elem > 0) { remove_cell = q->head; TSAssert(remove_cell->magic == MAGIC_ALIVE); data = remove_cell->ptr_data; q->head = remove_cell->ptr_prev; if (q->head == NULL) { TSAssert(q->nb_elem == 1); q->tail = NULL; } else { TSAssert(q->head->magic == MAGIC_ALIVE); q->head->ptr_next = NULL; } remove_cell->magic = MAGIC_DEAD; TSfree(remove_cell); q->nb_elem--; } TSMutexUnlock(q->mutex); return data; }
static void free_request_state(StateInfo *state) { #if defined(DEBUG) int verify = 1; #else int verify = TSIsDebugTagSet(PLUGIN_NAME); #endif // Verify that the effective URL of this state object has been removed before we delete the state. if (verify) { void *ptr; TSMutexLock(state->plugin_config->troot_mutex); ptr = tfind(state->req_info->effective_url, &(state->plugin_config->troot), xstrcmp); TSMutexUnlock(state->plugin_config->troot_mutex); if (ptr) { TSReleaseAssert(ptr != state->req_info->effective_url); } } if (state->resp_info) { free_response_info(state->resp_info); } free_request_info(state->req_info); TSfree(state); }
int get_nbelem_queue(Queue *q) { int nb; TSMutexLock(q->mutex); nb = q->nb_elem; TSMutexUnlock(q->mutex); return nb; }
TSRemapStatus TSRemapDoRemap(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_instance_conf *instance_conf; 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->client_request_bufp = rri->requestBufp; http_ctx->client_request_hdrp = rri->requestHdrp; http_ctx->client_request_url = rri->requestUrl; http_ctx->remap = 1; l = http_ctx->lua; lua_getglobal(l, TS_LUA_FUNCTION_REMAP); if (lua_type(l, -1) != LUA_TFUNCTION) { TSMutexUnlock(main_ctx->mutexp); return TSREMAP_NO_REMAP; } contp = TSContCreate(ts_lua_http_cont_handler, NULL); TSContDataSet(contp, http_ctx); http_ctx->main_contp = contp; if (lua_pcall(l, 0, 1, 0) != 0) { fprintf(stderr, "lua_pcall failed: %s\n", lua_tostring(l, -1)); } ret = lua_tointeger(l, -1); lua_pop(l, 1); TSHttpTxnHookAdd(rh, TS_HTTP_TXN_CLOSE_HOOK, contp); TSMutexUnlock(main_ctx->mutexp); return ret; }
static int ts_lua_http_intercept_handler(TSCont contp, TSEvent event, void *edata) { int ret, n; TSMutex mtxp; ts_lua_http_intercept_ctx *ictx; ictx = (ts_lua_http_intercept_ctx *) TSContDataGet(contp); mtxp = NULL; if (edata == ictx->input.vio) { ret = ts_lua_http_intercept_process_read(event, ictx); } else if (edata == ictx->output.vio) { ret = ts_lua_http_intercept_process_write(event, ictx); } else { mtxp = ictx->mctx->mutexp; n = (int64_t) edata & 0xFFFF; TSMutexLock(mtxp); ret = ts_lua_http_intercept_run_coroutine(ictx, n); } if (ret || (ictx->send_complete && ictx->recv_complete)) { TSContDestroy(contp); if (!mtxp) { mtxp = ictx->mctx->mutexp; TSMutexLock(mtxp); } ts_lua_destroy_http_intercept_ctx(ictx); } if (mtxp) TSMutexUnlock(mtxp); return 0; }
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_cache_handle_write(ts_lua_cont_info *ci, ts_lua_cache_info *info, TSEvent event, void *edata) { lua_State *L; TSMutex mtx; int64_t avail, done, n; n = 0; switch (event) { case TS_EVENT_VCONN_WRITE_READY: done = TSVIONDoneGet(info->ioh.vio); if (done < info->need) { TSVIOReenable(info->ioh.vio); } else { n = info->need - info->already; avail = TSIOBufferReaderAvail(info->ioh.reader); TSIOBufferReaderConsume(info->ioh.reader, avail); } break; default: info->err = 1; break; } if (info->wait && (n > 0 || info->err)) { // resume L = ci->routine.lua; mtx = ci->routine.mctx->mutexp; TSMutexLock(mtx); if (n > 0) { lua_pushnumber(L, n); info->already = info->need; } else { lua_pushnumber(L, 0); } info->wait = 0; TSContCall(ci->contp, TS_LUA_EVENT_COROUTINE_CONT, (void*)1); TSMutexUnlock(mtx); } return 0; }
static void stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex create_mutex) { int stat_id = -1; ENTRY search, *result = NULL; static __thread struct hsearch_data stat_cache; static __thread bool hash_init = false; if (unlikely(!hash_init)) { hcreate_r(TS_MAX_API_STATS << 1, &stat_cache); hash_init = true; TSDebug(DEBUG_TAG, "stat cache hash init"); } search.key = name; search.data = 0; hsearch_r(search, FIND, &result, &stat_cache); if (unlikely(result == NULL)) { // This is an unlikely path because we most likely have the stat cached // so this mutex won't be much overhead and it fixes a race condition // in the RecCore. Hopefully this can be removed in the future. TSMutexLock(create_mutex); if (TS_ERROR == TSStatFindName((const char *)name, &stat_id)) { stat_id = TSStatCreate((const char *)name, TS_RECORDDATATYPE_INT, persist_type, TS_STAT_SYNC_SUM); if (stat_id == TS_ERROR) { TSDebug(DEBUG_TAG, "Error creating stat_name: %s", name); } else { TSDebug(DEBUG_TAG, "Created stat_name: %s stat_id: %d", name, stat_id); } } TSMutexUnlock(create_mutex); if (stat_id >= 0) { search.key = TSstrdup(name); search.data = (void *)((intptr_t)stat_id); hsearch_r(search, ENTER, &result, &stat_cache); TSDebug(DEBUG_TAG, "Cached stat_name: %s stat_id: %d", name, stat_id); } } else { stat_id = (int)((intptr_t)result->data); } if (likely(stat_id >= 0)) { TSStatIntIncrement(stat_id, amount); } else { TSDebug(DEBUG_TAG, "stat error! stat_name: %s stat_id: %d", name, stat_id); } }
void RateLimiter::Release(int counter_index, const char * key, uint64_t amount) { TSReleaseAssert(!pthread_rwlock_rdlock(&rwlock_keymap_)); std::map<const char *,LimiterState *>::iterator it = keymap_.find(key); TSReleaseAssert(!pthread_rwlock_unlock(&rwlock_keymap_)); TSReleaseAssert( it != keymap_.end() ); LimiterState * state = it->second; TSMutexLock(update_mutex_); state->set_taken(counter_index, state->taken(counter_index) - amount); dbg("released amount, currently taken %f", state->taken(counter_index)); TSMutexUnlock(update_mutex_); }
static int config_handler(TSCont cont, TSEvent event, void *edata) { plugin_state_t *pstate; invalidate_t *i, *iptr; TSCont free_cont; bool updated; TSMutex mutex; mutex = TSContMutexGet(cont); TSMutexLock(mutex); TSDebug(LOG_PREFIX, "In config Handler"); pstate = (plugin_state_t *)TSContDataGet(cont); i = copy_config(pstate->invalidate_list); updated = prune_config(&i); updated = load_config(pstate, &i) || updated; if (updated) { list_config(pstate, i); iptr = __sync_val_compare_and_swap(&(pstate->invalidate_list), pstate->invalidate_list, i); if (iptr) { free_cont = TSContCreate(free_handler, TSMutexCreate()); TSContDataSet(free_cont, (void *)iptr); TSContScheduleOnPool(free_cont, FREE_TMOUT, TS_THREAD_POOL_TASK); } } else { TSDebug(LOG_PREFIX, "No Changes"); if (i) { free_invalidate_t_list(i); } } TSMutexUnlock(mutex); // Don't reschedule for TS_EVENT_MGMT_UPDATE if (event == TS_EVENT_TIMEOUT) { TSContScheduleOnPool(cont, CONFIG_TMOUT, TS_THREAD_POOL_TASK); } return 0; }
static int ts_lua_cache_open_write(ts_lua_cont_info *ci, ts_lua_cache_info *info, TSEvent event, void *edata) { lua_State *L; TSMutex mtx; TSVConn vc; L = ci->routine.lua; mtx = ci->routine.mctx->mutexp; switch (event) { case TS_EVENT_CACHE_OPEN_WRITE: vc = (TSVConn)edata; info->cache_vc = vc; break; default: // error info->err = 1; break; } TSMutexLock(mtx); // result table lua_newtable(L); lua_pushlstring(L, "info", sizeof("info") - 1); lua_pushlightuserdata(L, info); lua_rawset(L, -3); if (info->cache_action) { info->cache_action = NULL; TSContCall(ci->contp, TS_LUA_EVENT_COROUTINE_CONT, (void*)1); } else { // return to the ts.cache_open synchronized } TSMutexUnlock(mtx); return 0; }
static int ts_lua_shared_dict_get_keys(lua_State *L) { int n, max; ts_lua_shared_dict *dct; ts_lua_shared_dict_keys_ctx ctx; n = lua_gettop(L); if (n < 1) { return luaL_error(L, "invalid param for xx:get_keys()"); } dct = (ts_lua_shared_dict*)lua_touserdata(L, 1); if (dct == NULL) { return luaL_error(L, "userdata is required for xx:get_keys()"); } if (n == 2) { max = lua_tonumber(L, 2); } else { max = 0; } ctx.l = L; ctx.dct = dct; ctx.max = max; ctx.n = 0; lua_newtable(L); if (!(dct->flags & TS_LUA_SHDICT_FLAG_STATIC)) TSMutexLock(dct->map.mutexp); ts_lua_hash_table_iterate(&dct->map.t, dump_entry_key, &ctx); if (!(dct->flags & TS_LUA_SHDICT_FLAG_STATIC)) TSMutexUnlock(dct->map.mutexp); return 1; }
/* This is where we start the PURGE events, setting up the transaction to fail, and bump the generation ID, and finally save the state. */ static void update_purge_state(PurgeInstance *purge) { FILE *file; TSMutexLock(purge->lock); ++purge->gen_id; TSDebug(PLUGIN_NAME, "Bumping the Generation ID to %" PRId64 " for %s", purge->gen_id, purge->id); if ((file = fopen(purge->state_file, "w"))) { TSDebug(PLUGIN_NAME, "\tsaving state to %s", purge->state_file); fprintf(file, "%" PRId64 "", purge->gen_id); fclose(file); } else { TSError("[%s] Unable to save state to file %s: errno=%d", PLUGIN_NAME, purge->state_file, errno); } TSMutexUnlock(purge->lock); }
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("[%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_pushnil(L); lua_rawset(L, LUA_REGISTRYINDEX); /* L[REG][conf] = nil */ lua_newtable(L); lua_replace(L, LUA_GLOBALSINDEX); /* L[GLOBAL] = EMPTY */ TSMutexUnlock(arr[i].mutexp); } return 0; }
uint64_t RateLimiter::GetMaxUnits(uint64_t amount,LimiterState * state) { timeval timev; gettimeofday(&timev,NULL); time_t t = time(NULL); struct tm * p = localtime(&t); int counter_index = p->tm_hour; LimiterEntry * limiter_entry = counters_[counter_index]; if (!state) { return limiter_entry->max_rate(); } TSMutexLock(update_mutex_); timeval elapsed; timeval stime = state->time(counter_index); timersub(&timev, &stime, &elapsed); float elapsed_ms = (elapsed.tv_sec * 1000.0f) + ( elapsed.tv_usec/1000.0f ); float rate_timeslice = 1.0f - (limiter_entry->milliseconds() - elapsed_ms) / limiter_entry->milliseconds(); if (rate_timeslice<0) rate_timeslice=0; float replenishment = rate_timeslice * limiter_entry->max_rate(); float newallowance = state->allowance(counter_index) + replenishment; newallowance = newallowance > limiter_entry->max_rate() ? limiter_entry->max_rate() : newallowance; int rv = amount; if (amount > newallowance) { amount = rv = newallowance; } newallowance -= amount; if (newallowance >= 0.0f ) { state->set_allowance(counter_index, newallowance); state->set_time(counter_index, timev); } TSMutexUnlock(update_mutex_); return rv; }
static void ts_lua_http_intercept_process(ts_lua_http_ctx * http_ctx, TSVConn conn) { TSCont contp; lua_State *l; TSMutex mtxp; ts_lua_http_intercept_ctx *ictx; mtxp = http_ctx->mctx->mutexp; TSMutexLock(mtxp); ictx = ts_lua_create_http_intercept_ctx(http_ctx); contp = TSContCreate(ts_lua_http_intercept_handler, TSMutexCreate()); TSContDataSet(contp, ictx); ictx->contp = contp; ictx->net_vc = conn; l = ictx->lua; // set up read. ts_lua_http_intercept_setup_read(ictx); // set up write. ts_lua_http_intercept_setup_write(ictx); // invoke function here if (http_ctx->intercept_type == TS_LUA_TYPE_HTTP_INTERCEPT) { lua_getglobal(l, TS_LUA_FUNCTION_HTTP_INTERCEPT); } else { lua_getglobal(l, TS_LUA_FUNCTION_HTTP_SERVER_INTERCEPT); } ts_lua_http_intercept_run_coroutine(ictx, 0); TSMutexUnlock(mtxp); }
/* This is where we start the PURGE events, setting up the transactino to fail, and bump the generation ID, and finally save the state. */ static int on_http_cache_lookup_complete(TSHttpTxn txnp, TSCont contp, PurgeInstance *purge) { FILE *file; TSMutexLock(purge->lock); ++purge->gen_id; TSDebug(PLUGIN_NAME, "Bumping the Generation ID to %" PRId64 " for %s", purge->gen_id, purge->id); if ((file = fopen(purge->state_file, "w"))) { TSDebug(PLUGIN_NAME, "\tsaving state to %s", purge->state_file); fprintf(file, "%" PRId64 "", purge->gen_id); fclose(file); } else { TSError("[%s] Unable to save state to file %s: errno=%d", PLUGIN_NAME, purge->state_file, errno); } TSMutexUnlock(purge->lock); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR); return TS_SUCCESS; }
int ts_http_fetcher_callback_sm(http_fetcher *fch, TSEvent event) { if (fch->deleted && !fch->ref) { ts_http_fetcher_release(fch); return -1; } if (event == TS_EVENT_FETCH_BODY_QUIET || fch->stopped) { return 0; } else if (event != TS_EVENT_FETCH_HEADER_DONE && event != TS_EVENT_FETCH_BODY_READY && event != TS_EVENT_FETCH_BODY_QUIET) { fch->stopped = 1; } fch->ref++; if (fch->flags & TS_FLAG_FETCH_USE_NEW_LOCK) TSMutexLock(TSContMutexGet(fch->contp)); TSContCall(fch->contp, event, fch); if (fch->flags & TS_FLAG_FETCH_USE_NEW_LOCK) TSMutexUnlock(TSContMutexGet(fch->contp)); fch->ref--; if (fch->deleted && !fch->ref) { ts_http_fetcher_release(fch); return -1; } return 0; }
static int ts_lua_shared_dict_get(lua_State *L) { int n, type; int64_t nkey; const char *key; void *val; size_t key_len; void *hKey; ts_lua_shared_dict *dct; ts_lua_shared_dict_item *item; n = lua_gettop(L); if (n != 2) { return luaL_error(L, "invalid param for xx:get(key)"); } dct = (ts_lua_shared_dict*)lua_touserdata(L, 1); if (dct == NULL) { return luaL_error(L, "userdata is required for xx:get()"); } type = lua_type(L, 2); if (dct->flags & TS_LUA_SHDICT_FLAG_INTKEY) { if (type != LUA_TNUMBER) { return luaL_error(L, "key for xx:get() is not number."); } nkey = lua_tonumber(L, 2); hKey = (void*)nkey; } else if (type != LUA_TSTRING) { return luaL_error(L, "key for xx:get() is not string."); } else { key = lua_tolstring(L, 2, &key_len); hKey = (void*)key; } if (!(dct->flags & TS_LUA_SHDICT_FLAG_STATIC)) TSMutexLock(dct->map.mutexp); if (ts_lua_hash_table_lookup(&dct->map.t, hKey, &val)) { // found item = (ts_lua_shared_dict_item*)val; switch (item->vtype) { case LUA_TNUMBER: lua_pushnumber(L, item->v.n); break; case LUA_TBOOLEAN: lua_pushboolean(L, item->v.n); break; case LUA_TSTRING: lua_pushlstring(L, item->v.s, item->vsize); break; case LUA_TNIL: default: lua_pushnil(L); } } else { // not found lua_pushnil(L); } if (!(dct->flags & TS_LUA_SHDICT_FLAG_STATIC)) TSMutexUnlock(dct->map.mutexp); return 1; }
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 ts_lua_cache_handle_read(ts_lua_cont_info *ci, ts_lua_cache_info *info, TSEvent event, void *edata) { lua_State *L; TSMutex mtx; char *dst; int64_t avail, n; n = 0; switch (event) { case TS_EVENT_VCONN_READ_READY: avail = TSIOBufferReaderAvail(info->ioh.reader); if (avail > 0) { TSIOBufferCopy(info->reserved.buffer, info->ioh.reader, avail, 0); TSIOBufferReaderConsume(info->ioh.reader, avail); } avail = TSIOBufferReaderAvail(info->reserved.reader); if (avail + info->already >= info->need) { n = info->need - info->already; } else { TSVIOReenable(info->ioh.vio); } break; case TS_EVENT_VCONN_READ_COMPLETE: case TS_EVENT_VCONN_EOS: avail = TSIOBufferReaderAvail(info->ioh.reader); if (avail > 0) { TSIOBufferCopy(info->reserved.buffer, info->ioh.reader, avail, 0); TSIOBufferReaderConsume(info->ioh.reader, avail); } n = TSIOBufferReaderAvail(info->reserved.reader); info->eof = 1; break; default: // error info->err = 1; break; } if (info->wait && (n > 0 || info->eof || info->err)) { // resume L = ci->routine.lua; mtx = ci->routine.mctx->mutexp; TSMutexLock(mtx); if (n > 0) { dst = TSmalloc(n); IOBufferReaderCopy(info->reserved.reader, dst, n); lua_pushlstring(L, (char*)dst, n); TSfree(dst); info->already += n; TSIOBufferReaderConsume(info->reserved.reader, n); } else { lua_pushnil(L); } info->wait = 0; TSContCall(ci->contp, TS_LUA_EVENT_COROUTINE_CONT, (void*)1); TSMutexUnlock(mtx); } return 0; }
static int ts_lua_shared_dict_del(lua_State *L) { int n, type; int64_t nkey; const char *key; size_t key_len; void *hKey; Tcl_HashEntry *entry; ts_lua_shared_dict_item *item; ts_lua_shared_dict *dct; n = lua_gettop(L); if (n != 2) { return luaL_error(L, "invalid param for xx:del(key)"); } dct = (ts_lua_shared_dict*)lua_touserdata(L, 1); if (dct == NULL) { return luaL_error(L, "userdata is required for xx:del()"); } type = lua_type(L, 2); if (dct->flags & TS_LUA_SHDICT_FLAG_INTKEY) { if (type != LUA_TNUMBER) { return luaL_error(L, "key for xx:del() is not number."); } nkey = lua_tonumber(L, 2); hKey = (void*)nkey; } else if (type != LUA_TSTRING) { return luaL_error(L, "key for xx:del() is not string."); } else { key = lua_tolstring(L, 2, &key_len); hKey = (void*)key; } item = NULL; TSMutexLock(dct->map.mutexp); entry = ts_lua_hash_table_lookup_entry(&dct->map.t, hKey); if (entry) { item = ts_lua_hash_table_entry_value(&dct->map.t, entry); Tcl_DeleteHashEntry(entry); dct->used -= item->ksize + item->vsize + sizeof(ts_lua_shared_dict_item); } TSMutexUnlock(dct->map.mutexp); if (item) { TSfree(item); } return 0; }
static int ts_lua_transform_handler(TSCont contp, ts_lua_transform_ctx *transform_ctx) { TSVConn output_conn; TSVIO input_vio; TSIOBufferReader input_reader; TSIOBufferBlock blk; int64_t towrite, blk_len, upstream_done, avail, left; const char *start; const char *res; size_t res_len; int ret, eos; lua_State *L; TSMutex mtxp; L = transform_ctx->hctx->lua; mtxp = transform_ctx->hctx->mctx->mutexp; output_conn = TSTransformOutputVConnGet(contp); input_vio = TSVConnWriteVIOGet(contp); 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->output_vio = TSVConnWrite(output_conn, contp, transform_ctx->output_reader, INT64_MAX); } if (!TSVIOBufferGet(input_vio)) { TSVIONBytesSet(transform_ctx->output_vio, transform_ctx->total); TSVIOReenable(transform_ctx->output_vio); return 1; } if (transform_ctx->eos) { return 1; } left = towrite = TSVIONTodoGet(input_vio); upstream_done = TSVIONDoneGet(input_vio); avail = TSIOBufferReaderAvail(input_reader); eos = 0; if (left <= avail) eos = 1; if (towrite > avail) towrite = avail; TSMutexLock(mtxp); blk = TSIOBufferReaderStart(input_reader); do { start = TSIOBufferBlockReadStart(blk, input_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; } else { lua_pushlstring(L, start, (size_t)towrite); towrite = 0; } if (!towrite && eos) { lua_pushinteger(L, 1); /* second param, not finish */ } else { lua_pushinteger(L, 0); /* second param, not finish */ } if (lua_pcall(L, 2, 2, 0)) { fprintf(stderr, "lua_pcall failed: %s\n", lua_tostring(L, -1)); } ret = lua_tointeger(L, -1); /* 0 is not finished, 1 is finished */ res = lua_tolstring(L, -2, &res_len); if (res && res_len) { TSIOBufferWrite(transform_ctx->output_buffer, res, res_len); transform_ctx->total += res_len; } lua_pop(L, 2); if (ret || (eos && !towrite)) { // EOS eos = 1; break; } blk = TSIOBufferBlockNext(blk); } while (blk && towrite > 0); TSMutexUnlock(mtxp); TSIOBufferReaderConsume(input_reader, avail); TSVIONDoneSet(input_vio, upstream_done + avail); if (eos) { transform_ctx->eos = 1; TSVIONBytesSet(transform_ctx->output_vio, transform_ctx->total); TSVIOReenable(transform_ctx->output_vio); TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); } else { TSVIOReenable(transform_ctx->output_vio); TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); } return 1; }
TSRemapStatus TSRemapDoRemap(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_instance_conf *instance_conf; 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->client_request_bufp = rri->requestBufp; http_ctx->client_request_hdrp = rri->requestHdrp; http_ctx->client_request_url = rri->requestUrl; http_ctx->remap = 1; http_ctx->has_hook = 0; contp = TSContCreate(ts_lua_http_cont_handler, NULL); TSContDataSet(contp, http_ctx); http_ctx->main_contp = contp; l = http_ctx->lua; lua_getglobal(l, TS_LUA_FUNCTION_REMAP); if (lua_type(l, -1) != LUA_TFUNCTION) { TSMutexUnlock(main_ctx->mutexp); return TSREMAP_NO_REMAP; } if (lua_pcall(l, 0, 1, 0) != 0) { TSError("lua_pcall failed: %s", lua_tostring(l, -1)); } 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); TSContDestroy(contp); } TSMutexUnlock(main_ctx->mutexp); return ret; }
static int ts_lua_cache_open_read(ts_lua_cont_info *ci, ts_lua_cache_info *info, TSEvent event, void *edata) { lua_State *L; TSMutex mtx; TSVConn vc; int64_t sz; L = ci->routine.lua; mtx = ci->routine.mctx->mutexp; TSMutexLock(mtx); // result table lua_newtable(L); switch (event) { case TS_EVENT_CACHE_OPEN_READ: vc = (TSVConn)edata; sz = TSVConnCacheObjectSizeGet(vc); info->cache_vc = vc; info->hit = 1; info->sz = sz; lua_pushlstring(L, "hit", sizeof("hit") - 1); lua_pushboolean(L, 1); lua_rawset(L, -3); lua_pushlstring(L, "size", sizeof("size") - 1); lua_pushnumber(L, sz); lua_rawset(L, -3); break; case TS_EVENT_CACHE_OPEN_READ_FAILED: // miss default: // error lua_pushlstring(L, "hit", sizeof("hit") - 1); lua_pushboolean(L, 0); lua_rawset(L, -3); lua_pushlstring(L, "size", sizeof("size") - 1); lua_pushnumber(L, -1); lua_rawset(L, -3); if (event != TS_EVENT_CACHE_OPEN_READ_FAILED) { info->err = 1; } break; } lua_pushlstring(L, "info", sizeof("info") - 1); lua_pushlightuserdata(L, info); lua_rawset(L, -3); if (info->cache_action) { info->cache_action = NULL; TSContCall(ci->contp, TS_LUA_EVENT_COROUTINE_CONT, (void*)1); } else { // return to the ts.cache_open synchronized } TSMutexUnlock(mtx); 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_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->remap = 0; 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) { TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); return 0; } txn_contp = TSContCreate(ts_lua_http_cont_handler, NULL); TSContDataSet(txn_contp, http_ctx); http_ctx->main_contp = txn_contp; l = http_ctx->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: 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: TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); return 0; break; } if (lua_type(l, -1) != LUA_TFUNCTION) { TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); lua_pop(l, 1); return 0; } if (lua_pcall(l, 0, 1, 0) != 0) { TSError("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); TSContDestroy(txn_contp); } TSMutexUnlock(main_ctx->mutexp); if(ret) { TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR); } else { TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); } return 0; }
/*------------------------------------------------------------------------- psi_include Read file to include. Copy its content into an iobuffer. This is the function doing blocking calls and called by the plugin's threads Input: data continuation for the current transaction Output : data->psi_buffer contains the file content data->psi_sucess 0 if include failed, 1 if success Return Value: 0 if failure 1 if success -------------------------------------------------------------------------*/ static int psi_include(TSCont contp, void *edata) { #define BUFFER_SIZE 1024 ContData *data; TSFile filep; char buf[BUFFER_SIZE]; char inc_file[PSI_PATH_MAX_SIZE + PSI_FILENAME_MAX_SIZE]; /* We manipulate plugin continuation data from a separate thread. Grab mutex to avoid concurrent access */ TSMutexLock(TSContMutexGet(contp)); data = TSContDataGet(contp); TSAssert(data->magic == MAGIC_ALIVE); if (!data->psi_buffer) { data->psi_buffer = TSIOBufferCreate(); data->psi_reader = TSIOBufferReaderAlloc(data->psi_buffer); } /* For security reason, we do not allow to include files that are not in the directory <plugin_path>/include. Also include file cannot contain any path. */ sprintf(inc_file, "%s/%s", psi_directory, _basename(data->psi_filename)); /* Read the include file and copy content into iobuffer */ if ((filep = TSfopen(inc_file, "r")) != NULL) { TSDebug(DBG_TAG, "Reading include file %s", inc_file); while (TSfgets(filep, buf, BUFFER_SIZE) != NULL) { TSIOBufferBlock block; int64_t len, avail, ndone, ntodo, towrite; char *ptr_block; len = strlen(buf); ndone = 0; ntodo = len; while (ntodo > 0) { /* TSIOBufferStart allocates more blocks if required */ block = TSIOBufferStart(data->psi_buffer); ptr_block = TSIOBufferBlockWriteStart(block, &avail); towrite = MIN(ntodo, avail); memcpy(ptr_block, buf + ndone, towrite); TSIOBufferProduce(data->psi_buffer, towrite); ntodo -= towrite; ndone += towrite; } } TSfclose(filep); data->psi_success = 1; if (log) { TSTextLogObjectWrite(log, "Successfully included file: %s", inc_file); } } else { data->psi_success = 0; if (log) { TSTextLogObjectWrite(log, "Failed to include file: %s", inc_file); } } /* Change state and schedule an event EVENT_IMMEDIATE on the plugin continuation to let it know we're done. */ /* Note: if the blocking call was not in the transformation state (i.e. in TS_HTTP_READ_REQUEST_HDR, TS_HTTP_OS_DNS and so on...) we could use TSHttpTxnReenable to wake up the transaction instead of sending an event. */ TSContSchedule(contp, 0, TS_THREAD_POOL_DEFAULT); data->psi_success = 0; data->state = STATE_READ_DATA; TSMutexUnlock(TSContMutexGet(contp)); return 0; }