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; }
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_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 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 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; }
/* When the handle is called, the net_vc is returned. */ static int accept_handler(TSCont contp, TSEvent event, void *edata) { TSCont txn_sm; TSMutex pmutex; switch (event) { case TS_EVENT_NET_ACCEPT: /* Create a new mutex for the TxnSM, which is going to handle the incoming request. */ pmutex = (TSMutex)TSMutexCreate(); txn_sm = (TSCont)TxnSMCreate(pmutex, (TSVConn)edata, server_port); /* This is no reason for not grabbing the lock. So skip the routine which handle LockTry failure case. */ TSMutexLockTry(pmutex); // TODO: why should it not check if we got the lock?? TSContCall(txn_sm, 0, NULL); TSMutexUnlock(pmutex); break; default: /* Something wrong with the network, if there are any pending NetAccept, cancel them. */ if (pending_action && !TSActionDone(pending_action)) TSActionCancel(pending_action); TSContDestroy(contp); break; } return TS_EVENT_NONE; }
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; }
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); } } }
int get_nbelem_queue(Queue *q) { int nb; TSMutexLock(q->mutex); nb = q->nb_elem; TSMutexUnlock(q->mutex); return nb; }
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 void read_blacklist(TSCont contp) { char blacklist_file[1024]; TSFile file; sprintf(blacklist_file, "%s/blacklist.txt", TSPluginDirGet()); file = TSfopen(blacklist_file, "r"); nsites = 0; /* If the Mutext lock is not successful try again in RETRY_TIME */ if (TSMutexLockTry(sites_mutex) != TS_SUCCESS) { if (file != NULL) { TSfclose(file); } TSContSchedule(contp, RETRY_TIME, TS_THREAD_POOL_DEFAULT); return; } if (file != NULL) { char buffer[1024]; while (TSfgets(file, buffer, sizeof(buffer) - 1) != NULL && nsites < MAX_NSITES) { char *eol; if ((eol = strstr(buffer, "\r\n")) != NULL) { /* To handle newlines on Windows */ *eol = '\0'; } else if ((eol = strchr(buffer, '\n')) != NULL) { *eol = '\0'; } else { /* Not a valid line, skip it */ continue; } if (sites[nsites] != NULL) { TSfree(sites[nsites]); } sites[nsites] = TSstrdup(buffer); nsites++; } TSfclose(file); } else { TSError("[%s] Unable to open %s", PLUGIN_NAME, blacklist_file); TSError("[%s] All sites will be allowed", PLUGIN_NAME); } TSMutexUnlock(sites_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_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; }
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; }
/* 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 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 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; }
/*------------------------------------------------------------------------- transform_handler Handler for all events received during the transformation process Input: contp continuation for the current transaction event event received data pointer on optional data Output : Return Value: -------------------------------------------------------------------------*/ static int transform_handler(TSCont contp, TSEvent event, void *edata) { TSVIO input_vio; ContData *data; int state, retval; /* This section will be called by both TS internal and the thread. Protect it with a mutex to avoid concurrent calls. */ /* Handle TryLock result */ if (TSMutexLockTry(TSContMutexGet(contp)) != TS_SUCCESS) { TSCont c = TSContCreate(trylock_handler, NULL); TryLockData *d = TSmalloc(sizeof(TryLockData)); d->contp = contp; d->event = event; TSContDataSet(c, d); TSContSchedule(c, 10, TS_THREAD_POOL_DEFAULT); return 1; } data = TSContDataGet(contp); TSAssert(data->magic == MAGIC_ALIVE); state = data->state; /* Check to see if the transformation has been closed */ retval = TSVConnClosedGet(contp); if (retval) { /* If the thread is still executing its job, we don't want to destroy the continuation right away as the thread will call us back on this continuation. */ if (state == STATE_READ_PSI) { TSContSchedule(contp, 10, TS_THREAD_POOL_DEFAULT); } else { TSMutexUnlock(TSContMutexGet(contp)); cont_data_destroy(TSContDataGet(contp)); TSContDestroy(contp); return 1; } } else { switch (event) { case TS_EVENT_ERROR: input_vio = TSVConnWriteVIOGet(contp); TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio); break; case TS_EVENT_VCONN_WRITE_COMPLETE: TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1); break; case TS_EVENT_VCONN_WRITE_READY: /* downstream vconnection is done reading data we've write into it. let's read some more data from upstream if we're in read state. */ if (state == STATE_READ_DATA) { handle_transform(contp); } break; case TS_EVENT_IMMEDIATE: if (state == STATE_READ_DATA) { /* upstream vconnection signals some more data ready to be read let's try to transform some more data */ handle_transform(contp); } else if (state == STATE_DUMP_PSI) { /* The thread scheduled an event on our continuation to let us know it has completed its job Let's dump the include content to the output vconnection */ dump_psi(contp); wake_up_streams(contp); } break; default: TSAssert(!"Unexpected event"); break; } } TSMutexUnlock(TSContMutexGet(contp)); return 1; }
static void handle_dns(TSHttpTxn txnp, TSCont contp) { TSMBuffer bufp; TSMLoc hdr_loc; TSMLoc url_loc; const char *host; int i; int host_length; if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) { TSError("[%s] Couldn't retrieve client request header", PLUGIN_NAME); goto done; } if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) { TSError("[%s] Couldn't retrieve request url", PLUGIN_NAME); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); goto done; } host = TSUrlHostGet(bufp, url_loc, &host_length); if (!host) { TSError("[%s] Couldn't retrieve request hostname", PLUGIN_NAME); TSHandleMLocRelease(bufp, hdr_loc, url_loc); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); goto done; } /* We need to lock the sites_mutex as that is the mutex that is protecting the global list of all blacklisted sites. */ if (TSMutexLockTry(sites_mutex) != TS_SUCCESS) { TSDebug(PLUGIN_NAME, "Unable to get lock. Will retry after some time"); TSHandleMLocRelease(bufp, hdr_loc, url_loc); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); TSContSchedule(contp, RETRY_TIME, TS_THREAD_POOL_DEFAULT); return; } for (i = 0; i < nsites; i++) { if (strncmp(host, sites[i], host_length) == 0) { if (log) { TSTextLogObjectWrite(log, "blacklisting site: %s", sites[i]); } else { TSDebug(PLUGIN_NAME, "blacklisting site: %s", sites[i]); } TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp); TSHandleMLocRelease(bufp, hdr_loc, url_loc); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR); TSMutexUnlock(sites_mutex); return; } } TSMutexUnlock(sites_mutex); TSHandleMLocRelease(bufp, hdr_loc, url_loc); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); done: TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); }
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 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; }
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; }
/*------------------------------------------------------------------------- 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; }
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; }