/*------------------------------------------------------------------------- TSPluginInit Function called at plugin init time Input: argc number of args argv list vof args Output : Return Value: -------------------------------------------------------------------------*/ void TSPluginInit(int argc, const char *argv[]) { TSPluginRegistrationInfo info; int i; TSReturnCode retval; info.plugin_name = "psi"; info.vendor_name = "Apache"; info.support_email = ""; if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) { TSError("Plugin registration failed.\n"); } if (!check_ts_version()) { TSError("Plugin requires Traffic Server 3.0 or later\n"); return; } /* Initialize the psi directory = <plugin_path>/include */ sprintf(psi_directory, "%s/%s", TSPluginDirGet(), PSI_PATH); /* create an TSTextLogObject to log any psi include */ retval = TSTextLogObjectCreate("psi", TS_LOG_MODE_ADD_TIMESTAMP, &log); if (retval == TS_ERROR) { TSError("Failed creating log for psi plugin"); log = NULL; } /* Create working threads */ thread_init(); init_queue(&job_queue); for (i = 0; i < NB_THREADS; i++) { char *thread_name = (char *) TSmalloc(64); sprintf(thread_name, "Thread[%d]", i); if (!TSThreadCreate((TSThreadFunc) thread_loop, thread_name)) { TSError("[TSPluginInit] Error while creating threads"); return; } } TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, TSContCreate(read_response_handler, TSMutexCreate())); TSDebug(DBG_TAG, "Plugin started"); }
static void handle_txn_start(TSCont contp ATS_UNUSED, TSHttpTxn txnp) { TSCont txn_contp; cdata *cd; txn_contp = TSContCreate((TSEventFunc)blacklist_plugin, TSMutexCreate()); /* create the data that'll be associated with the continuation */ cd = (cdata *)TSmalloc(sizeof(cdata)); TSContDataSet(txn_contp, cd); cd->txnp = txnp; TSHttpTxnHookAdd(txnp, TS_HTTP_OS_DNS_HOOK, txn_contp); TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); }
static void handle_response(TSHttpTxn txnp, TSCont contp ATS_UNUSED) { TSMBuffer bufp; TSMLoc hdr_loc; TSMLoc url_loc; char *url_str; char *buf; int url_length; if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) { TSError("[%s] Couldn't retrieve client response header", PLUGIN_NAME); goto done; } TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_FORBIDDEN); TSHttpHdrReasonSet(bufp, hdr_loc, TSHttpHdrReasonLookup(TS_HTTP_STATUS_FORBIDDEN), strlen(TSHttpHdrReasonLookup(TS_HTTP_STATUS_FORBIDDEN))); if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) { TSError("[%s] Couldn't retrieve client request header", PLUGIN_NAME); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); 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; } buf = (char *)TSmalloc(4096); url_str = TSUrlStringGet(bufp, url_loc, &url_length); sprintf(buf, "You are forbidden from accessing \"%s\"\n", url_str); TSfree(url_str); TSHandleMLocRelease(bufp, hdr_loc, url_loc); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); TSHttpTxnErrorBodySet(txnp, buf, strlen(buf), NULL); done: TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); }
static config_holder_t* new_config_holder(const char* path) { char default_config_file[1024]; config_holder_t* config_holder = TSmalloc(sizeof(config_holder_t)); config_holder->config_path = 0; config_holder->config = 0; config_holder->last_load = 0; // TSmalloc(32); // if(path) { config_holder->config_path = nstr(path); } else { /* Default config file of plugins/cacheurl.config */ // sprintf(default_config_file, "%s/astats.config", TSPluginDirGet()); sprintf(default_config_file, "%s/"DEFAULT_CONFIG_NAME, TSConfigDirGet()); config_holder->config_path = nstr(default_config_file); } load_config_file(config_holder); return config_holder; }
static int ts_lua_sleep(lua_State * L) { int sec; TSAction action; TSCont contp; ts_lua_http_intercept_item *node; ts_lua_http_intercept_ctx *ictx; ictx = ts_lua_get_http_intercept_ctx(L); sec = luaL_checknumber(L, 1); contp = TSContCreate(ts_lua_sleep_handler, TSContMutexGet(ictx->contp)); action = TSContSchedule(contp, sec * 1000, TS_THREAD_POOL_DEFAULT); node = (ts_lua_http_intercept_item *) TSmalloc(sizeof(ts_lua_http_intercept_item)); TS_LUA_ADD_INTERCEPT_ITEM(ictx, node, contp, ts_lua_sleep_cleanup, action); TSContDataSet(contp, node); return lua_yield(L, 0); }
TSReturnCode TSRemapNewInstance(int argc, char **argv, void **ih, char *errbuf, int errbuf_size) { int i; char *ptr; secure_link_info *sli; // squash unused variable warnings ... (void)errbuf; (void)errbuf_size; sli = (secure_link_info *)TSmalloc(sizeof(secure_link_info)); sli->secret = NULL; sli->strict = 0; for(i = 2; i < argc; i++) { if((ptr = strchr(argv[i], ':')) != NULL) { *ptr++ = '\0'; if(strcmp(argv[i], "secret") == 0) { if(sli->secret != NULL) { TSfree(sli->secret); } sli->secret = TSstrdup(ptr); } else if(strcmp(argv[i], "policy") == 0) { sli->strict = !strcasecmp(ptr, "strict"); } else { TSDebug(PLUGIN_NAME, "Unknown parameter [%s]", argv[i]); } } else { TSError("Invalid parameter [%s]", argv[i]); } } if(sli->secret == NULL) { sli->secret = TSstrdup(""); } *ih = (void *)sli; return TS_SUCCESS; }
static char * gen_header(char *status_str, char *mime, int *header_len) { TSHttpStatus status; char * buf = NULL; status = atoi(status_str); if (status > 0 && status < 999) { const char* status_reason; int len = sizeof(HEADER_TEMPLATE) + 3 + 1; status_reason = TSHttpHdrReasonLookup(status); len += strlen(status_reason); len += strlen(mime); buf = TSmalloc(len); *header_len = snprintf(buf, len, HEADER_TEMPLATE, status, status_reason, mime); } else { *header_len = 0; } return buf; }
ts_lua_http_ctx * ts_lua_create_http_ctx(ts_lua_main_ctx *main_ctx, ts_lua_instance_conf *conf) { 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)); http_ctx->lua = lua_newthread(L); l = http_ctx->lua; 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); http_ctx->ref = luaL_ref(L, LUA_REGISTRYINDEX); http_ctx->mctx = main_ctx; ts_lua_set_http_ctx(http_ctx->lua, http_ctx); ts_lua_create_context_table(http_ctx->lua); return http_ctx; }
static RequestInfo * create_request_info(TSHttpTxn txn) { RequestInfo *req_info; char *url; int url_len; TSMBuffer buf; TSMLoc loc; const struct sockaddr *sa; req_info = (RequestInfo *)TSmalloc(sizeof(RequestInfo)); memset(req_info, 0, sizeof(RequestInfo)); url = TSHttpTxnEffectiveUrlStringGet(txn, &url_len); req_info->effective_url = TSstrndup(url, url_len); TSfree(url); TSHttpTxnClientReqGet(txn, &buf, &loc); req_info->buf = TSMBufferCreate(); TSHttpHdrClone(req_info->buf, buf, loc, &(req_info->http_hdr_loc)); TSHandleMLocRelease(buf, TS_NULL_MLOC, loc); sa = TSHttpTxnClientAddrGet(txn); switch (sa->sa_family) { case AF_INET: memcpy(&req_info->client_addr.sin, sa, sizeof(struct sockaddr_in)); break; case AF_INET6: memcpy(&req_info->client_addr.sin6, sa, sizeof(struct sockaddr_in6)); break; default: break; } return req_info; }
/*------------------------------------------------------------------------- cont_data_alloc Allocate and initialize a ContData structure associated to a transaction Input: Output: Return Value: Pointer on a new allocated ContData structure -------------------------------------------------------------------------*/ static ContData * cont_data_alloc() { ContData *data; data = (ContData *) TSmalloc(sizeof(ContData)); data->magic = MAGIC_ALIVE; data->output_vio = NULL; data->output_buffer = NULL; data->output_reader = NULL; data->psi_buffer = NULL; data->psi_reader = NULL; data->psi_filename[0] = '\0'; data->psi_filename_len = 0; data->psi_success = 0; data->parse_state = PARSE_SEARCH; data->state = STATE_READ_DATA; data->transform_bytes = 0; return data; }
static void process_data(TSCont contp, ibd_ctx* ibd) { TSVConn output_conn; TSIOBuffer buf_test; TSVIO input_vio; ib_txn_ctx *data; int64_t towrite; int64_t avail; int first_time = 0; char *bufp = NULL; TSDebug("ironbee", "Entering process_data()"); /* Get the output (downstream) vconnection where we'll write data to. */ output_conn = TSTransformOutputVConnGet(contp); /* Get the write VIO for the write operation that was performed on * ourself. This VIO contains the buffer that we are to read from * as well as the continuation we are to call when the buffer is * empty. This is the input VIO (the write VIO for the upstream * vconnection). */ input_vio = TSVConnWriteVIOGet(contp); data = TSContDataGet(contp); if (!ibd->data->output_buffer) { first_time = 1; ibd->data->output_buffer = TSIOBufferCreate(); ibd->data->output_reader = TSIOBufferReaderAlloc(ibd->data->output_buffer); TSDebug("ironbee", "\tWriting %d bytes on VConn", TSVIONBytesGet(input_vio)); ibd->data->output_vio = TSVConnWrite(output_conn, contp, ibd->data->output_reader, INT64_MAX); } if (ibd->data->buf) { /* this is the second call to us, and we have data buffered. * Feed buffered data to ironbee */ ib_conndata_t icdata; icdata.ib = ironbee; icdata.mp = data->ssn->iconn->mp; icdata.conn = data->ssn->iconn; icdata.dalloc = ibd->data->buflen; icdata.dlen = ibd->data->buflen; icdata.data = (uint8_t *)ibd->data->buf; (*ibd->ibd->ib_notify)(ironbee, &icdata); TSfree(ibd->data->buf); ibd->data->buf = NULL; ibd->data->buflen = 0; } /* test for input data */ buf_test = TSVIOBufferGet(input_vio); if (!buf_test) { TSDebug("ironbee", "No more data, finishing"); TSVIONBytesSet(ibd->data->output_vio, TSVIONDoneGet(input_vio)); TSVIOReenable(ibd->data->output_vio); /* FIXME - is this right here - can conn data be kept across reqs? */ ibd->data->output_buffer = NULL; ibd->data->output_reader = NULL; ibd->data->output_vio = NULL; return; } /* Determine how much data we have left to read. For this null * transform plugin this is also the amount of data we have left * to write to the output connection. */ towrite = TSVIONTodoGet(input_vio); TSDebug("ironbee", "\ttoWrite is %" PRId64 "", towrite); if (towrite > 0) { /* The amount of data left to read needs to be truncated by * the amount of data actually in the read buffer. */ /* first time through, we have to buffer the data until * after the headers have been sent. Ugh! */ if (first_time) { bufp = ibd->data->buf = TSmalloc(towrite); ibd->data->buflen = towrite; } avail = TSIOBufferReaderAvail(TSVIOReaderGet(input_vio)); TSDebug("ironbee", "\tavail is %" PRId64 "", avail); if (towrite > avail) { towrite = avail; } if (towrite > 0) { int btowrite = towrite; /* Copy the data from the read buffer to the output buffer. */ TSIOBufferCopy(TSVIOBufferGet(ibd->data->output_vio), TSVIOReaderGet(input_vio), towrite, 0); /* feed the data to ironbee, and consume them */ while (btowrite > 0) { ib_conndata_t icdata; int64_t ilength; TSIOBufferReader input_reader = TSVIOReaderGet(input_vio); TSIOBufferBlock blkp = TSIOBufferReaderStart(input_reader); const char *ibuf = TSIOBufferBlockReadStart(blkp, input_reader, &ilength); /* feed it to ironbee or to buffer */ if (first_time) { memcpy(bufp, ibuf, ilength); bufp += ilength; } else { icdata.ib = ironbee; icdata.mp = data->ssn->iconn->mp; icdata.conn = data->ssn->iconn; icdata.dalloc = ilength; icdata.dlen = ilength; icdata.data = (uint8_t *)ibuf; (*ibd->ibd->ib_notify)(ironbee, &icdata); } //"response", TSHttpTxnClientRespGet, ib_state_notify_conn_data_out // ib_state_notify_conn_data_out(ironbee, &icdata); /* and mark it as all consumed */ btowrite -= ilength; TSIOBufferReaderConsume(input_reader, ilength); TSVIONDoneSet(input_vio, TSVIONDoneGet(input_vio) + ilength); } } } /* Now we check the input VIO to see if there is data left to * read. */ if (TSVIONTodoGet(input_vio) > 0) { if (towrite > 0) { /* If there is data left to read, then we reenable the output * connection by reenabling the output VIO. This will wake up * the output connection and allow it to consume data from the * output buffer. */ TSVIOReenable(ibd->data->output_vio); /* Call back the input VIO continuation to let it know that we * are ready for more data. */ TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); } } else { /* If there is no data left to read, then we modify the output * VIO to reflect how much data the output connection should * expect. This allows the output connection to know when it * is done reading. We then reenable the output connection so * that it can consume the data we just gave it. */ TSVIONBytesSet(ibd->data->output_vio, TSVIONDoneGet(input_vio)); TSVIOReenable(ibd->data->output_vio); /* Call back the input VIO continuation to let it know that we * have completed the write operation. */ TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); } }
static int ts_lua_add_hook(lua_State *L) { int type; int entry; TSVConn connp; ts_lua_http_ctx *http_ctx; ts_lua_transform_ctx *transform_ctx; http_ctx = ts_lua_get_http_ctx(L); entry = lua_tointeger(L, 1); // get hook id type = lua_type(L, 2); if (type != LUA_TFUNCTION) return 0; switch (entry) { case TS_LUA_HOOK_CACHE_LOOKUP_COMPLETE: TSHttpTxnHookAdd(http_ctx->txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, http_ctx->main_contp); lua_pushvalue(L, 2); lua_setglobal(L, TS_LUA_FUNCTION_CACHE_LOOKUP_COMPLETE); break; case TS_LUA_HOOK_SEND_REQUEST_HDR: TSHttpTxnHookAdd(http_ctx->txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, http_ctx->main_contp); lua_pushvalue(L, 2); lua_setglobal(L, TS_LUA_FUNCTION_SEND_REQUEST); break; case TS_LUA_HOOK_READ_RESPONSE_HDR: TSHttpTxnHookAdd(http_ctx->txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, http_ctx->main_contp); lua_pushvalue(L, 2); lua_setglobal(L, TS_LUA_FUNCTION_READ_RESPONSE); break; case TS_LUA_HOOK_SEND_RESPONSE_HDR: TSHttpTxnHookAdd(http_ctx->txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, http_ctx->main_contp); lua_pushvalue(L, 2); lua_setglobal(L, TS_LUA_FUNCTION_SEND_RESPONSE); break; case TS_LUA_REQUEST_TRANSFORM: case TS_LUA_RESPONSE_TRANSFORM: transform_ctx = (ts_lua_transform_ctx*)TSmalloc(sizeof(ts_lua_transform_ctx)); memset(transform_ctx, 0, sizeof(ts_lua_transform_ctx)); transform_ctx->hctx = http_ctx; connp = TSTransformCreate(ts_lua_transform_entry, http_ctx->txnp); TSContDataSet(connp, transform_ctx); if (entry == TS_LUA_REQUEST_TRANSFORM) { TSHttpTxnHookAdd(http_ctx->txnp, TS_HTTP_REQUEST_TRANSFORM_HOOK, connp); } else { TSHttpTxnHookAdd(http_ctx->txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp); } lua_pushlightuserdata(L, transform_ctx); lua_pushvalue(L, 2); lua_rawset(L, LUA_GLOBALSINDEX); break; default: break; } return 0; }
static int ironbee_plugin(TSCont contp, TSEvent event, void *edata) { TSVConn connp; TSCont mycont; TSHttpTxn txnp = (TSHttpTxn) edata; TSHttpSsn ssnp = (TSHttpSsn) edata; ib_txn_ctx *txndata; ib_ssn_ctx *ssndata; TSDebug("ironbee", "Entering ironbee_plugin with %d", event); switch (event) { /* CONNECTION */ case TS_EVENT_HTTP_SSN_START: /* start of connection */ /* But we can't initialise conn stuff here, because there's * no API to get the connection stuff required by ironbee * at this point. So instead, intercept the first TXN * * what we can and must do: create a new contp whose * lifetime is our ssn */ mycont = TSContCreate(ironbee_plugin, NULL); TSHttpSsnHookAdd (ssnp, TS_HTTP_TXN_START_HOOK, mycont); TSContDataSet(mycont, NULL); TSHttpSsnHookAdd (ssnp, TS_HTTP_SSN_CLOSE_HOOK, mycont); TSHttpSsnReenable (ssnp, TS_EVENT_HTTP_CONTINUE); break; case TS_EVENT_HTTP_TXN_START: /* start of Request */ /* First req on a connection, we set up conn stuff */ ssndata = TSContDataGet(contp); if (ssndata == NULL) { ib_conn_t *iconn = NULL; ib_status_t rc; rc = ib_conn_create(ironbee, &iconn, contp); if (rc != IB_OK) { TSError("ironbee", "ib_conn_create: %d\n", rc); return rc; // FIXME - figure out what to do } ssndata = TSmalloc(sizeof(ib_ssn_ctx)); memset(ssndata, 0, sizeof(ib_ssn_ctx)); ssndata->iconn = iconn; ssndata->txnp = txnp; TSContDataSet(contp, ssndata); ib_state_notify_conn_opened(ironbee, iconn); } /* create a txn cont (request ctx) */ mycont = TSContCreate(ironbee_plugin, NULL); txndata = TSmalloc(sizeof(ib_txn_ctx)); memset(txndata, 0, sizeof(ib_txn_ctx)); txndata->ssn = ssndata; txndata->txnp = txnp; TSContDataSet(mycont, txndata); /* With both of these, SSN_CLOSE gets called first. * I must be misunderstanding SSN * So hook it all to TXN */ TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, mycont); /* Hook to process responses */ TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, mycont); /* Hook to process requests */ TSHttpTxnHookAdd(txnp, TS_HTTP_READ_REQUEST_HDR_HOOK, mycont); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); break; /* HTTP RESPONSE */ case TS_EVENT_HTTP_READ_RESPONSE_HDR: txndata = TSContDataGet(contp); /* hook to examine output headers */ /* Not sure why we can't do it right now, but it seems headers * are not yet available. * Can we use another case switch in this function? */ TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp); /* hook an output filter to watch data */ connp = TSTransformCreate(out_data_event, txnp); TSContDataSet(connp, txndata); TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); break; /* hook for processing response headers */ case TS_EVENT_HTTP_SEND_RESPONSE_HDR: txndata = TSContDataGet(contp); process_hdr(txndata, txnp, &ironbee_direction_resp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); break; /* HTTP REQUEST */ case TS_EVENT_HTTP_READ_REQUEST_HDR: txndata = TSContDataGet(contp); /* hook to examine output headers */ /* Not sure why we can't do it right now, but it seems headers * are not yet available. * Can we use another case switch in this function? */ //TSHttpTxnHookAdd(txnp, TS_HTTP_OS_DNS_HOOK, contp); TSHttpTxnHookAdd(txnp, TS_HTTP_PRE_REMAP_HOOK, contp); /* hook an input filter to watch data */ connp = TSTransformCreate(in_data_event, txnp); TSContDataSet(connp, txndata); TSHttpTxnHookAdd(txnp, TS_HTTP_REQUEST_TRANSFORM_HOOK, connp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); break; /* hook for processing incoming request/headers */ case TS_EVENT_HTTP_PRE_REMAP: txndata = TSContDataGet(contp); process_hdr(txndata, txnp, &ironbee_direction_req); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); break; /* CLEANUP EVENTS */ case TS_EVENT_HTTP_TXN_CLOSE: TSDebug("ironbee", "TXN Close: %x\n", contp); ib_txn_ctx_destroy(TSContDataGet(contp)); TSContDataSet(contp, NULL); TSContDestroy(contp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); break; case TS_EVENT_HTTP_SSN_CLOSE: TSDebug("ironbee", "SSN Close: %x\n", contp); ib_ssn_ctx_destroy(TSContDataGet(contp)); TSContDestroy(contp); TSHttpSsnReenable(ssnp, TS_EVENT_HTTP_CONTINUE); break; /* if we get here we've got a bug */ default: TSError("BUG: unhandled event %d in ironbee_plugin\n", event); break; } return 0; }
static void handle_dns(TSHttpTxn txnp, TSCont contp ATS_UNUSED) { TSMBuffer bufp; TSMLoc hdr_loc; TSIOBuffer output_buffer; TSIOBufferReader reader; int total_avail; TSIOBufferBlock block; const char *block_start; int64_t block_avail; char *output_string; int64_t output_len; if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) { TSDebug(PLUGIN_NAME, "couldn't retrieve client request header"); TSError("[%s] Couldn't retrieve client request header", PLUGIN_NAME); goto done; } output_buffer = TSIOBufferCreate(); reader = TSIOBufferReaderAlloc(output_buffer); /* This will print just MIMEFields and not the http request line */ TSDebug(PLUGIN_NAME, "Printing the hdrs ... "); TSMimeHdrPrint(bufp, hdr_loc, output_buffer); if (TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc) == TS_ERROR) { TSDebug(PLUGIN_NAME, "non-fatal: error releasing MLoc"); TSError("[%s] non-fatal: Couldn't release MLoc", PLUGIN_NAME); } /* Find out how the big the complete header is by seeing the total bytes in the buffer. We need to look at the buffer rather than the first block to see the size of the entire header */ total_avail = TSIOBufferReaderAvail(reader); /* Allocate the string with an extra byte for the string terminator */ output_string = (char *)TSmalloc(total_avail + 1); output_len = 0; /* We need to loop over all the buffer blocks to make sure we get the complete header since the header can be in multiple blocks */ block = TSIOBufferReaderStart(reader); while (block) { block_start = TSIOBufferBlockReadStart(block, reader, &block_avail); /* We'll get a block pointer back even if there is no data left to read so check for this condition and break out of the loop. A block with no data to read means we've exhausted buffer of data since if there was more data on a later block in the chain, this block would have been skipped over */ if (block_avail == 0) { break; } memcpy(output_string + output_len, block_start, block_avail); output_len += block_avail; /* Consume the data so that we get to the next block */ TSIOBufferReaderConsume(reader, block_avail); /* Get the next block now that we've consumed the data off the last block */ block = TSIOBufferReaderStart(reader); } /* Terminate the string */ output_string[output_len] = '\0'; output_len++; /* Free up the TSIOBuffer that we used to print out the header */ TSIOBufferReaderFree(reader); TSIOBufferDestroy(output_buffer); /* Although I'd never do this a production plugin, printf the header so that we can see it's all there */ TSDebug(PLUGIN_NAME, "%s", output_string); TSfree(output_string); done: TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); }
static char * nstrl(const char *s, int len) { char *mys = (char *)TSmalloc(len + 1); memcpy(mys, s, len); mys[len] = 0; return mys; }
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_cache_remove(lua_State *L) { const char *keystr, *optstr; size_t key_len, opt_len; int n; TSCont contp; TSCacheKey key; TSAction action; ts_lua_cont_info *ci; ts_lua_async_item *ai; ts_lua_cache_info *info; ci = ts_lua_get_cont_info(L); if (ci == NULL) return 0; n = lua_gettop(L); if (n < 1) { return luaL_error(L, "'ts.cache_remove' requires parameter"); } /* keystr */ if (!lua_isstring(L, 1)) { return luaL_error(L, "'ts.cache_remove' first param is not string"); } keystr = luaL_checklstring(L, 1, &key_len); optstr = NULL; opt_len = 0; /* option */ if (n >= 2 && lua_isstring(L, 2)) { optstr = luaL_checklstring(L, 2, &opt_len);; } key = ts_lua_cache_key_create(keystr, key_len, optstr, opt_len); if (key == NULL) { return luaL_error(L, "'ts.cache_remove' failed"); } info = (ts_lua_cache_info*)TSmalloc(sizeof(ts_lua_cache_info)); memset(info, 0, sizeof(ts_lua_cache_info)); info->cache_key = key; contp = TSContCreate(ts_lua_cache_remove_handler, ci->mutex); ai = ts_lua_async_create_item(contp, ts_lua_cache_cleanup, info, ci); TSContDataSet(contp, ai); action = TSCacheRemove(contp, key); if (!TSActionDone(action)) { info->cache_action = action; return lua_yield(L, 0); } else { // action done ts_lua_release_cache_info(info, 1); ai->data = NULL; return 0; } }
static int astats_origin(TSCont cont, TSEvent event, void *edata) { TSCont icontp; stats_state *my_state; config_t* config; TSHttpTxn txnp = (TSHttpTxn) edata; TSMBuffer reqp; TSMLoc hdr_loc = NULL, url_loc = NULL; TSEvent reenable = TS_EVENT_HTTP_CONTINUE; config = get_config(cont); TSDebug(PLUGIN_TAG, "in the read stuff"); if (TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc) != TS_SUCCESS) goto cleanup; if (TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc) != TS_SUCCESS) goto cleanup; int path_len = 0; const char* path = TSUrlPathGet(reqp,url_loc,&path_len); TSDebug(PLUGIN_TAG,"Path: %.*s",path_len,path); TSDebug(PLUGIN_TAG,"Path: %.*s",path_len,path); if (!(path_len == config->stats_path_len && !memcmp(path, config->stats_path, config->stats_path_len))) { // TSDebug(PLUGIN_TAG, "not right path: %.*s",path_len,path); goto notforme; } const struct sockaddr *addr = TSHttpTxnClientAddrGet(txnp); if(!is_ip_allowed(config, addr)) { TSDebug(PLUGIN_TAG, "not right ip"); goto notforme; } // TSDebug(PLUGIN_TAG,"Path...: %.*s",path_len,path); int query_len; char *query = (char*)TSUrlHttpQueryGet(reqp,url_loc,&query_len); TSDebug(PLUGIN_TAG,"query: %.*s",query_len,query); TSSkipRemappingSet(txnp,1); //not strictly necessary, but speed is everything these days /* This is us -- register our intercept */ TSDebug(PLUGIN_TAG, "Intercepting request"); icontp = TSContCreate(stats_dostuff, TSMutexCreate()); my_state = (stats_state *) TSmalloc(sizeof(*my_state)); memset(my_state, 0, sizeof(*my_state)); my_state->recordTypes = config->recordTypes; if (query_len) { my_state->query = nstrl(query, query_len); TSDebug(PLUGIN_TAG,"new query: %s", my_state->query); stats_fillState(my_state, my_state->query, query_len); } TSContDataSet(icontp, my_state); TSHttpTxnIntercept(icontp, txnp); goto cleanup; notforme: cleanup: #if (TS_VERSION_NUMBER < 2001005) if (path) TSHandleStringRelease(reqp, url_loc, path); #endif if (url_loc) TSHandleMLocRelease(reqp, hdr_loc, url_loc); if (hdr_loc) TSHandleMLocRelease(reqp, TS_NULL_MLOC, hdr_loc); TSHttpTxnReenable(txnp, reenable); return 0; }
static int ts_lua_shared_dict_set(lua_State *L) { int n; int64_t nkey, nval; const char *key, *val; size_t key_len, val_len; int ktype, vtype; void *hKey; int isNew; Tcl_HashEntry *hPtr; ts_lua_shared_dict_item *item, *old_item; ts_lua_shared_dict *dct; n = lua_gettop(L); if (n != 3) { return luaL_error(L, "invalid param for xx:set(key, value)"); } dct = (ts_lua_shared_dict*)lua_touserdata(L, 1); if (dct == NULL) { return luaL_error(L, "userdata is required for xx:set()"); } if (dct->quota && dct->used > dct->quota) { lua_pushnumber(L, 0); lua_pushlstring(L, "no memory", sizeof("no memory") - 1); return 2; } ktype = lua_type(L, 2); vtype = lua_type(L, 3); switch (ktype) { case LUA_TNUMBER: if (dct->flags & TS_LUA_SHDICT_FLAG_INTKEY) { nkey = luaL_checknumber(L, 2); key_len = sizeof(long); hKey = (void*)nkey; } else { key = luaL_checklstring(L, 2, &key_len); hKey = (void*)key; } break; case LUA_TSTRING: if (dct->flags & TS_LUA_SHDICT_FLAG_INTKEY) { return luaL_error(L, "key for xx:set() should be number"); } key = luaL_checklstring(L, 2, &key_len); hKey = (void*)key; break; default: return luaL_error(L, "invalid key type: %s for xx:set()", lua_typename(L, ktype)); } if (key_len > TS_LUA_SHDICT_MAX_KEY_LENGTH) return luaL_error(L, "key length is too long: %d", (int)key_len); val_len = 0; switch (vtype) { case LUA_TNUMBER: nval = lua_tonumber(L, 3); break; case LUA_TSTRING: val = lua_tolstring(L, 3, &val_len); break; case LUA_TBOOLEAN: nval = lua_toboolean(L, 3); break; case LUA_TNIL: nval = 0; break; default: return luaL_error(L, "invalid val type: %s for xx:set()", lua_typename(L, vtype)); } if (val_len > TS_LUA_SHDICT_MAX_VAL_LENGTH) return luaL_error(L, "val length is too long: %d", (int)val_len); item = (ts_lua_shared_dict_item*)TSmalloc(sizeof(ts_lua_shared_dict_item) + val_len); item->vtype = vtype; item->ksize = key_len; item->vsize = val_len; if (vtype == LUA_TSTRING) { item->v.s = (char*)(((char*)item) + sizeof(ts_lua_shared_dict_item)); memcpy(item->v.s, val, val_len); } else { item->v.n = nval; } isNew = 0; old_item = NULL; TSMutexLock(dct->map.mutexp); hPtr = Tcl_CreateHashEntry(&(dct->map.t), hKey, &isNew); if (isNew) { Tcl_SetHashValue(hPtr, item); dct->used += sizeof(ts_lua_shared_dict_item) + item->ksize + item->vsize; } else { old_item = (ts_lua_shared_dict_item*)Tcl_GetHashValue(hPtr); Tcl_SetHashValue(hPtr, item); dct->used += (int)(item->ksize + item->vsize) - (int)(old_item->ksize + old_item->vsize); } TSMutexUnlock(dct->map.mutexp); if (!isNew && old_item) { TSfree(old_item); } lua_pushnumber(L, 1); lua_pushnil(L); return 2; }
static inline void * ts_malloc(size_t s) { return TSmalloc(s); }
static CachedHeaderInfo * get_cached_header_info(TSHttpTxn txn) { CachedHeaderInfo *chi; TSMBuffer cr_buf; TSMLoc cr_hdr_loc, cr_date_loc, cr_cache_control_loc, cr_cache_control_dup_loc; int cr_cache_control_count, val_len, i; char *value, *ptr; chi = (CachedHeaderInfo *)TSmalloc(sizeof(CachedHeaderInfo)); memset(chi, 0, sizeof(CachedHeaderInfo)); if (TSHttpTxnCachedRespGet(txn, &cr_buf, &cr_hdr_loc) == TS_SUCCESS) { cr_date_loc = TSMimeHdrFieldFind(cr_buf, cr_hdr_loc, TS_MIME_FIELD_DATE, TS_MIME_LEN_DATE); if (cr_date_loc != TS_NULL_MLOC) { TSDebug(PLUGIN_NAME, "Found a date"); chi->date = TSMimeHdrFieldValueDateGet(cr_buf, cr_hdr_loc, cr_date_loc); TSHandleMLocRelease(cr_buf, cr_hdr_loc, cr_date_loc); } cr_cache_control_loc = TSMimeHdrFieldFind(cr_buf, cr_hdr_loc, TS_MIME_FIELD_CACHE_CONTROL, TS_MIME_LEN_CACHE_CONTROL); while (cr_cache_control_loc != TS_NULL_MLOC) { TSDebug(PLUGIN_NAME, "Found cache-control"); cr_cache_control_count = TSMimeHdrFieldValuesCount(cr_buf, cr_hdr_loc, cr_cache_control_loc); for (i = 0; i < cr_cache_control_count; i++) { value = (char *)TSMimeHdrFieldValueStringGet(cr_buf, cr_hdr_loc, cr_cache_control_loc, i, &val_len); ptr = value; if (strncmp(value, TS_HTTP_VALUE_MAX_AGE, TS_HTTP_LEN_MAX_AGE) == 0) { TSDebug(PLUGIN_NAME, "Found max-age"); ptr += TS_HTTP_LEN_MAX_AGE; if (*ptr == '=') { ptr++; chi->max_age = atol(ptr); } else { ptr = TSstrndup(value, TS_HTTP_LEN_MAX_AGE + 2); TSDebug(PLUGIN_NAME, "This is what I found: %s", ptr); TSfree(ptr); } } else if (strncmp(value, HTTP_VALUE_STALE_WHILE_REVALIDATE, strlen(HTTP_VALUE_STALE_WHILE_REVALIDATE)) == 0) { TSDebug(PLUGIN_NAME, "Found stale-while-revalidate"); ptr += strlen(HTTP_VALUE_STALE_WHILE_REVALIDATE); if (*ptr == '=') { ptr++; chi->stale_while_revalidate = atol(ptr); } } else if (strncmp(value, HTTP_VALUE_STALE_IF_ERROR, strlen(HTTP_VALUE_STALE_IF_ERROR)) == 0) { TSDebug(PLUGIN_NAME, "Found stale-on-error"); ptr += strlen(HTTP_VALUE_STALE_IF_ERROR); if (*ptr == '=') { ptr++; chi->stale_on_error = atol(ptr); } } else { TSDebug(PLUGIN_NAME, "Unknown field value"); } } cr_cache_control_dup_loc = TSMimeHdrFieldNextDup(cr_buf, cr_hdr_loc, cr_cache_control_loc); TSHandleMLocRelease(cr_buf, cr_hdr_loc, cr_cache_control_loc); cr_cache_control_loc = cr_cache_control_dup_loc; } TSHandleMLocRelease(cr_buf, TS_NULL_MLOC, cr_hdr_loc); } return chi; }
static char * nstr(const char *s) { char *mys = (char *)TSmalloc(strlen(s)+1); strcpy(mys, s); return mys; }
void TSPluginInit(int argc, const char *argv[]) { int ret = 0; ts_lua_g_main_ctx_array = TSmalloc(sizeof(ts_lua_main_ctx) * TS_LUA_MAX_STATE_COUNT); memset(ts_lua_g_main_ctx_array, 0, sizeof(ts_lua_main_ctx) * TS_LUA_MAX_STATE_COUNT); ret = ts_lua_create_vm(ts_lua_g_main_ctx_array, TS_LUA_MAX_STATE_COUNT); if (ret) { ts_lua_destroy_vm(ts_lua_g_main_ctx_array, TS_LUA_MAX_STATE_COUNT); TSfree(ts_lua_g_main_ctx_array); return; } if (argc < 2) { TSError("[%s] lua script file required !!", __FUNCTION__); return; } if (strlen(argv[1]) >= TS_LUA_MAX_SCRIPT_FNAME_LENGTH - 16) { TSError("[%s] lua script file name too long !!", __FUNCTION__); return; } ts_lua_instance_conf *conf = TSmalloc(sizeof(ts_lua_instance_conf)); if (!conf) { TSError("[%s] TSmalloc failed !!", __FUNCTION__); return; } memset(conf, 0, sizeof(ts_lua_instance_conf)); sprintf(conf->script, "%s", argv[1]); ts_lua_init_instance(conf); ret = ts_lua_add_module(conf, ts_lua_g_main_ctx_array, TS_LUA_MAX_STATE_COUNT, argc - 1, (char **) &argv[1]); if (ret != 0) { TSError("[%s] ts_lua_add_module failed", __FUNCTION__); return; } TSCont global_contp = TSContCreate(globalHookHandler, NULL); if (!global_contp) { TSError("[%s] could not create transaction start continuation", __FUNCTION__); return; } TSContDataSet(global_contp, conf); //adding hook based on whether the lua global function exists. ts_lua_main_ctx *main_ctx = &ts_lua_g_main_ctx_array[0]; ts_lua_http_ctx *http_ctx = ts_lua_create_http_ctx(main_ctx, conf); lua_State *l = http_ctx->lua; lua_getglobal(l, TS_LUA_FUNCTION_G_SEND_REQUEST); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_SEND_REQUEST_HDR_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "send_request_hdr_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_READ_RESPONSE); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "read_response_hdr_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_SEND_RESPONSE); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_SEND_RESPONSE_HDR_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "send_response_hdr_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_CACHE_LOOKUP_COMPLETE); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "cache_lookup_complete_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_READ_REQUEST); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "read_request_hdr_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_TXN_START); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "txn_start_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_PRE_REMAP); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_PRE_REMAP_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "pre_remap_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_POST_REMAP); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_POST_REMAP_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "post_remap_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_SELECT_ALT); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_SELECT_ALT_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "select_alt_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_OS_DNS); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_OS_DNS_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "os_dns_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_READ_CACHE); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_READ_CACHE_HDR_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "read_cache_hdr_hook added"); } lua_pop(l, 1); lua_getglobal(l, TS_LUA_FUNCTION_G_TXN_CLOSE); if (lua_type(l, -1) == LUA_TFUNCTION) { TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, global_contp); TSDebug(TS_LUA_DEBUG_TAG, "txn_close_hook added"); } lua_pop(l, 1); ts_lua_destroy_http_ctx(http_ctx); }
static void ironbee_plugin_txn_start(TSCont contp, TSHttpTxn txnp) { assert(contp != NULL); assert(txnp != NULL); /* start of Request */ /* First req on a connection, we set up conn stuff */ ib_status_t rc; ib_engine_t *ib = NULL; TSCont mycont; tsib_ssn_ctx *ssndata; tsib_txn_ctx *txndata; ssndata = TSContDataGet(contp); if (ssndata->iconn == NULL) { rc = tsib_manager_engine_acquire(&ib); if (rc == IB_DECLINED) { /* OK, this means the manager is disabled deliberately, * but otherwise all's well. So this TXN * gets processed without intervention from Ironbee * and is invisble when our SSN_CLOSE hook runs. */ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); TSDebug("ironbee", "Decline from engine manager"); return; } else if (rc != IB_OK) { TSError("[ironbee] Failed to acquire engine: %s", ib_status_to_string(rc)); goto noib_error; } if (ib != NULL) { rc = ib_conn_create(ib, &ssndata->iconn, contp); if (rc != IB_OK) { TSError("[ironbee] ib_conn_create: %s", ib_status_to_string(rc)); tsib_manager_engine_release(ib); goto noib_error; } /* In the normal case, release the engine when the * connection's memory pool is destroyed */ rc = ib_mm_register_cleanup(ssndata->iconn->mm, cleanup_ib_connection, ib); if (rc != IB_OK) { TSError("[ironbee] ib_mm_register_cleanup: %s", ib_status_to_string(rc)); tsib_manager_engine_release(ib); goto noib_error; } TSDebug("ironbee", "CONN CREATE: conn=%p", ssndata->iconn); ssndata->txnp = txnp; ssndata->txn_count = ssndata->closing = 0; rc = ironbee_conn_init(ssndata); if (rc != IB_OK) { TSError("[ironbee] ironbee_conn_init: %s", ib_status_to_string(rc)); goto noib_error; } TSContDataSet(contp, ssndata); TSDebug("ironbee", "ironbee_plugin: ib_state_notify_conn_opened()"); rc = ib_state_notify_conn_opened(ib, ssndata->iconn); if (rc != IB_OK) { TSError("[ironbee] Failed to notify connection opened: %s", ib_status_to_string(rc)); } } else { /* Use TSError where there's no ib or tx */ TSError("Ironbee: No ironbee engine!"); goto noib_error; } } /* create a txn cont (request ctx) and tx */ txndata = TSmalloc(sizeof(*txndata)); memset(txndata, 0, sizeof(*txndata)); txndata->ssn = ssndata; txndata->txnp = txnp; rc = ib_tx_create(&txndata->tx, ssndata->iconn, txndata); if (rc != IB_OK) { TSError("[ironbee] Failed to create tx: %d", rc); tsib_manager_engine_release(ib); TSfree(txndata); goto noib_error; } ++ssndata->txn_count; ib_log_debug_tx(txndata->tx, "TX CREATE: conn=%p tx=%p id=%s txn_count=%d", ssndata->iconn, txndata->tx, txndata->tx->id, txndata->ssn->txn_count); mycont = TSContCreate(ironbee_plugin, TSContMutexGet(contp)); TSContDataSet(mycont, txndata); TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, mycont); /* Hook to process responses */ TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, mycont); /* Hook to process requests */ TSHttpTxnHookAdd(txnp, TS_HTTP_PRE_REMAP_HOOK, mycont); /* Hook to process request headers when sent to the server. */ TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, mycont); /* Create continuations for input and output filtering * to give them txn lifetime. */ txndata->in_data_cont = TSTransformCreate(in_data_event, txnp); TSContDataSet(txndata->in_data_cont, txndata); txndata->out_data_cont = TSTransformCreate(out_data_event, txnp); TSContDataSet(txndata->out_data_cont, txndata); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); return; noib_error: /* NULL txndata signals this to SEND_RESPONSE */ TSContDataSet(contp, NULL); TSError("[ironbee] Internal error initialising for transaction"); TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp); /* FIXME: check this. * Purpose is to ensure contp doesn't leak, but may not be right */ TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, contp); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR); return; }
static bool load_config(plugin_state_t *pstate, invalidate_t **ilist) { FILE *fs; struct stat s; size_t path_len; char *path; char line[LINE_MAX]; time_t now; pcre *config_re; const char *errptr; int erroffset, ovector[OVECTOR_SIZE], rc; int ln = 0; invalidate_t *iptr, *i; if (pstate->config_file[0] != '/') { path_len = strlen(TSConfigDirGet()) + strlen(pstate->config_file) + 2; path = alloca(path_len); snprintf(path, path_len, "%s/%s", TSConfigDirGet(), pstate->config_file); } else path = pstate->config_file; if (stat(path, &s) < 0) { TSDebug(LOG_PREFIX, "Could not stat %s", path); return false; } if (s.st_mtime > pstate->last_load) { now = time(NULL); if (!(fs = fopen(path, "r"))) { TSDebug(LOG_PREFIX, "Could not open %s for reading", path); return false; } config_re = pcre_compile("^([^#].+?)\\s+(\\d+)\\s*$", 0, &errptr, &erroffset, NULL); while (fgets(line, LINE_MAX, fs) != NULL) { ln++; TSDebug(LOG_PREFIX, "Processing: %d %s", ln, line); rc = pcre_exec(config_re, NULL, line, strlen(line), 0, 0, ovector, OVECTOR_SIZE); if (rc == 3) { i = (invalidate_t *)TSmalloc(sizeof(invalidate_t)); init_invalidate_t(i); pcre_get_substring(line, ovector, rc, 1, &i->regex_text); i->epoch = now; i->expiry = atoi(line + ovector[4]); i->regex = pcre_compile(i->regex_text, 0, &errptr, &erroffset, NULL); if (i->expiry <= i->epoch) { TSDebug(LOG_PREFIX, "Rule is already expired!"); free_invalidate_t(i); } else if (i->regex == NULL) { TSDebug(LOG_PREFIX, "%s did not compile", i->regex_text); free_invalidate_t(i); } else { i->regex_extra = pcre_study(i->regex, 0, &errptr); if (!*ilist) { *ilist = i; TSDebug(LOG_PREFIX, "Created new list and Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry); } else { iptr = *ilist; while (1) { if (strcmp(i->regex_text, iptr->regex_text) == 0) { if (iptr->expiry != i->expiry) { TSDebug(LOG_PREFIX, "Updating duplicate %s", i->regex_text); iptr->epoch = i->epoch; iptr->expiry = i->expiry; } free_invalidate_t(i); i = NULL; break; } else if (!iptr->next) break; else iptr = iptr->next; } if (i) { iptr->next = i; TSDebug(LOG_PREFIX, "Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry); } } } } else TSDebug(LOG_PREFIX, "Skipping line %d", ln); } pcre_free(config_re); fclose(fs); pstate->last_load = s.st_mtime; return true; } else TSDebug(LOG_PREFIX, "File mod time is not newer: %d >= %d", (int)pstate->last_load, (int)s.st_mtime); return false; }
/*------------------------------------------------------------------------- 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 * hc_thread(void *data) { int fd = inotify_init(); HCDirEntry *dirs; int len; HCFileData *fl = NULL; char buffer[INOTIFY_BUFLEN]; struct timeval last_free, now; gettimeofday(&last_free, NULL); /* Setup watchers for the directories, these are a one time setup */ dirs = setup_watchers(fd); while (1) { int i = 0; /* First clean out anything old from the freelist */ gettimeofday(&now, NULL); if ((now.tv_sec - last_free.tv_sec) > FREELIST_TIMEOUT) { HCFileData *fdata = fl, *prev_fdata = fl; TSDebug(PLUGIN_NAME, "Checking the freelist"); memcpy(&last_free, &now, sizeof(struct timeval)); while(fdata) { if (fdata->remove > now.tv_sec) { if (prev_fdata) prev_fdata->_next = fdata->_next; fdata = fdata->_next; TSDebug(PLUGIN_NAME, "Cleaning up entry from frelist"); TSfree(fdata); } else { prev_fdata = fdata; fdata = fdata->_next; } } } /* Read the inotify events, blocking! */ len = read(fd, buffer, INOTIFY_BUFLEN); if (len >= 0) { while (i < len) { struct inotify_event *event = (struct inotify_event *)&buffer[i]; int wd = event->wd; HCFileInfo *finfo = g_config; while (finfo) { if ((wd == finfo->wd) || (wd == finfo->dir->wd && !strncmp(event->name, finfo->basename, event->len))) break; finfo = finfo->_next; } if (finfo) { HCFileData *new_data = TSmalloc(sizeof(HCFileData)); if (event->mask & (IN_CLOSE_WRITE)) { TSDebug(PLUGIN_NAME, "Modify file event (%d) on %s", event->mask, finfo->fname); } else if (event->mask & (IN_CREATE|IN_MOVED_TO)) { TSDebug(PLUGIN_NAME, "Create file event (%d) on %s", event->mask, finfo->fname); finfo->wd = inotify_add_watch(fd, finfo->fname, IN_DELETE_SELF|IN_CLOSE_WRITE|IN_ATTRIB); } else if (event->mask & (IN_DELETE_SELF|IN_MOVED_FROM)) { TSDebug(PLUGIN_NAME, "Delete file event (%d) on %s", event->mask, finfo->fname); finfo->wd = inotify_rm_watch(fd, finfo->wd); } memset(new_data, 0, sizeof(HCFileData)); reload_status_file(finfo, new_data); finfo->data->_next = fl; finfo->data->remove = now.tv_sec + FREELIST_TIMEOUT; fl = finfo->data; ink_atomic_swap_ptr(&(finfo->data), new_data); } i += sizeof(struct inotify_event) + event->len; } } } /* Cleanup, in case we later exit this thread ... */ while (dirs) { HCDirEntry *d = dirs; dirs = dirs->_next; TSfree(d); } return NULL; /* Yeah, that never happens */ }
static int ts_lua_cache_open(lua_State *L) { const char *keystr, *optstr; size_t key_len, opt_len; int operate, n; TSCont contp; TSCacheKey key; TSAction action; ts_lua_cont_info *ci; ts_lua_async_item *ai; ts_lua_cache_info *info; ci = ts_lua_get_cont_info(L); if (ci == NULL) return 0; n = lua_gettop(L); if (n < 2) { return luaL_error(L, "'ts.cache_open' requires parameter"); } /* keystr */ if (!lua_isstring(L, 1)) { return luaL_error(L, "'ts.cache_open' first param is not string"); } else if (!lua_isnumber(L, 2)) { return luaL_error(L, "'ts.cache_open' second param is not TS_LUA_CACHE_READ/TS_LUA_CACHE_WRITE"); } keystr = luaL_checklstring(L, 1, &key_len); operate = lua_tonumber(L, 2); if (operate != TS_LUA_CACHE_READ && operate != TS_LUA_CACHE_WRITE) { return luaL_error(L, "'ts.cache_open' second param is not TS_LUA_CACHE_READ/TS_LUA_CACHE_WRITE"); } optstr = NULL; opt_len = 0; /* option */ if (n >= 3 && lua_isstring(L, 3)) { optstr = luaL_checklstring(L, 3, &opt_len); } key = ts_lua_cache_key_create(keystr, key_len, optstr, opt_len); if (key == NULL) { return luaL_error(L, "'ts.cache_open' failed"); } info = (ts_lua_cache_info*)TSmalloc(sizeof(ts_lua_cache_info)); memset(info, 0, sizeof(ts_lua_cache_info)); info->cache_key = key; info->optype = operate; contp = TSContCreate(ts_lua_cache_main_handler, ci->mutex); ai = ts_lua_async_create_item(contp, ts_lua_cache_cleanup, info, ci); TSContDataSet(contp, ai); info->contp = contp; info->ioh.buffer = TSIOBufferCreate(); info->ioh.reader = TSIOBufferReaderAlloc(info->ioh.buffer); if (operate == TS_LUA_CACHE_READ) { info->reserved.buffer = TSIOBufferCreate(); info->reserved.reader = TSIOBufferReaderAlloc(info->reserved.buffer); info->current_handler = &ts_lua_cache_open_read; action = TSCacheRead(contp, key); } else { info->current_handler = &ts_lua_cache_open_write; action = TSCacheWrite(contp, key); } if (TSActionDone(action)) { // done return 1; } else { // undone info->cache_action = action; return lua_yield(L, 0); } }
static HCFileInfo* parse_configs(const char* fname) { FILE *fd; char buf[64*1024]; /* Way huge, but wth */ HCFileInfo *head_finfo = NULL, *finfo = NULL, *prev_finfo = NULL; if (NULL == (fd = fopen(fname, "r"))) return NULL; while (!feof(fd)) { char *str, *save; int state = 0; char *ok=NULL, *miss=NULL, *mime=NULL; prev_finfo = finfo; finfo = TSmalloc(sizeof(HCFileInfo)); memset(finfo, 0, sizeof(HCFileInfo)); if (NULL == head_finfo) head_finfo = finfo; if (prev_finfo) prev_finfo->_next = finfo; fread(buf, sizeof(buf), 1, fd); str = strtok_r(buf, "\t", &save); while (NULL != str) { if (strlen(str) > 0) { switch (state) { case 0: if ('/' == *str) ++str; strncpy(finfo->path, str, MAX_PATH_LEN - 1); finfo->p_len = strlen(finfo->path); break; case 1: strncpy(finfo->fname, str, MAX_FILENAME_LEN - 1); finfo->basename = strrchr(finfo->fname, '/'); if (finfo->basename) ++(finfo->basename); break; case 2: mime = str; break; case 3: ok = str; break; case 4: miss = str; break; } ++state; } str = strtok_r(NULL, "\t", &save); } finfo->ok = gen_header(ok, mime, &finfo->o_len); finfo->miss = gen_header(miss, mime, &finfo->m_len); finfo->data = TSmalloc(sizeof(HCFileData)); memset(finfo->data, 0, sizeof(HCFileData)); reload_status_file(finfo, finfo->data); } fclose(fd); return head_finfo; }
static int ts_lua_cache_read(lua_State *L) { int64_t length, n, avail; char *dst; ts_lua_cont_info *ci; ts_lua_cache_info *info; ci = ts_lua_get_cont_info(L); if (ci == NULL) return 0; n = lua_gettop(L); if (n < 1) { return luaL_error(L, "'ts.cache_read' requires parameter"); } /* length */ if (n >= 2) { if (!lua_isnumber(L, 2)) { return luaL_error(L, "'ts.cache_read' second param is not number"); } length = lua_tonumber(L, 2); if (length <= 0) { return luaL_error(L, "'ts.cache_read' second param is a not valid number"); } } else { length = INT64_MAX; } /* table */ if (!lua_istable(L, 1)) { return luaL_error(L, "'ts.cache_read' first param is not valid"); } lua_pushlstring(L, "info", sizeof("info") - 1); lua_gettable(L, 1); if (!lua_islightuserdata(L, -1)) { return luaL_error(L, "'ts.cache_read' first param is not valid"); } info = (ts_lua_cache_info*)lua_touserdata(L, -1); lua_pop(L, 1); if (info->hit == 0) { return luaL_error(L, "'ts.cache_read' is reading from unhit doc"); } if (info->optype != TS_LUA_CACHE_READ) { return luaL_error(L, "'ts.cache_read' is reading from invalid vc"); } if (info->eof || info->err) { lua_pushnil(L); return 1; } if (length != INT64_MAX) { info->need += length; } else { info->need = INT64_MAX; } info->current_handler = &ts_lua_cache_handle_read; avail = TSIOBufferReaderAvail(info->reserved.reader); if (avail + info->already >= info->need) { n = info->need - info->already; dst = (char*)TSmalloc(n); IOBufferReaderCopy(info->reserved.reader, dst, n); lua_pushlstring(L, (char*)dst, n); TSfree(dst); info->already = info->need; TSIOBufferReaderConsume(info->reserved.reader, n); return 1; } if (info->ioh.vio == NULL) { info->ioh.vio = TSVConnRead(info->cache_vc, info->contp, info->ioh.buffer, INT64_MAX); } else { TSVIOReenable(info->ioh.vio); } info->wait = 1; return lua_yield(L, 0); }