static int ts_http_fetcher_verify_cl(http_fetcher *fch, int code) { int64_t need, left; if (fch->resp_already >= fch->resp_cl) return BODY_COMPLETE; left = TSIOBufferReaderAvail(fch->flow_reader); if (fch->resp_already + left >= fch->resp_cl) { need = fch->resp_cl - fch->resp_already; } else { need = left; } TSIOBufferCopy(fch->body_buffer, fch->flow_reader, need, 0); TSIOBufferReaderConsume(fch->flow_reader, need); fch->resp_already += left; if (fch->resp_already >= fch->resp_cl) return BODY_COMPLETE; else if (code == BODY_COMPLETE) return BODY_ERROR; return BODY_READY; }
static char * request_body_get(TSHttpTxn txnp, int *len) { char *ret = NULL; TSIOBufferReader post_buffer_reader = TSHttpTxnPostBufferReaderGet(txnp); int64_t read_avail = TSIOBufferReaderAvail(post_buffer_reader); if (read_avail == 0) { TSIOBufferReaderFree(post_buffer_reader); return NULL; } ret = (char *)TSmalloc(sizeof(char) * read_avail); int64_t consumed = 0; int64_t data_len = 0; const char *char_data = NULL; TSIOBufferBlock block = TSIOBufferReaderStart(post_buffer_reader); while (block != NULL) { char_data = TSIOBufferBlockReadStart(block, post_buffer_reader, &data_len); memcpy(ret + consumed, char_data, data_len); consumed += data_len; block = TSIOBufferBlockNext(block); } TSIOBufferReaderFree(post_buffer_reader); *len = (int)consumed; return ret; }
void ts_http_fetcher_launch(http_fetcher *fch) { int64_t hdr_len; // post body , content-length TSIOBufferWrite(fch->req_buffer, "\r\n", sizeof("\r\n")-1); hdr_len = TSIOBufferReaderAvail(fch->req_reader); fch->http_vc = TSHttpConnect(&(fch->aip)); fch->hdr_buffer = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_8K); fch->hdr_reader = TSIOBufferReaderAlloc(fch->hdr_buffer); fch->hdr_bufp = TSMBufferCreate(); fch->hdr_loc = TSHttpHdrCreate(fch->hdr_bufp); fch->resp_buffer = TSIOBufferCreate(); fch->resp_reader = TSIOBufferReaderAlloc(fch->resp_buffer); fch->http_parser = TSHttpParserCreate(); fch->fetch_contp = TSContCreate(ts_http_fetch_handler, fch->mutexp); TSContDataSet(fch->fetch_contp, fch); fch->read_vio = TSVConnRead(fch->http_vc, fch->fetch_contp, fch->resp_buffer, INT64_MAX); if (fch->method == TS_FETCH_METHOD_POST && fch->post_cl >= 0) { fch->write_vio = TSVConnWrite(fch->http_vc, fch->fetch_contp, fch->req_reader, hdr_len + fch->post_cl); } else { fch->write_vio = TSVConnWrite(fch->http_vc, fch->fetch_contp, fch->req_reader, hdr_len); } fch->launched = 1; }
static int handle_output(TSCont contp, JCrusherData * data) { const char *output; int64_t written_bytes; /* Check to see if we need to initiate the output operation. */ TSDebug("jcrusher", "Start of handle_output()"); if (!data->downstream_vio) { TSVConn output_conn; /* Get the json_object as string and write it into buffer */ output = json_object_to_json_string_ext(data->json_obj, JSON_C_TO_STRING_PLAIN); written_bytes = TSIOBufferWrite(data->downstream_buffer, output, (int64_t)(strlen(output))); TSDebug("jcrusher", "handle_output - Just write %" PRId64 " bytes to ouput", written_bytes); /* Get the output connection where we'll write data to. */ output_conn = TSTransformOutputVConnGet(contp); data->downstream_vio = TSVConnWrite(output_conn, contp, data->downstream_reader, TSIOBufferReaderAvail(data->downstream_reader)); TSAssert(data->downstream_vio); } TSDebug("jcrusher", "End of handle_output()"); return 1; }
static int transform_bypass(TSCont contp, TransformData *data) { data->state = STATE_BYPASS; if (data->server_vc) { TSVConnAbort(data->server_vc, 1); data->server_vc = NULL; data->server_vio = NULL; } if (data->output_buf) { TSIOBufferDestroy(data->output_buf); data->output_buf = NULL; data->output_reader = NULL; } TSIOBufferReaderConsume(data->input_reader, sizeof(int)); data->output_vc = TSTransformOutputVConnGet((TSVConn)contp); if (data->output_vc == NULL) { TSError("[%s] TSTransformOutputVConnGet returns NULL", PLUGIN_NAME); } else { data->output_vio = TSVConnWrite(data->output_vc, contp, data->input_reader, TSIOBufferReaderAvail(data->input_reader)); if (data->output_vio == NULL) { TSError("[%s] TSVConnWrite returns NULL", PLUGIN_NAME); } } return 1; }
// This serves to consume all the data that arrives. If it's not consumed the tunnel gets stalled // and the transaction doesn't complete. Other things could be done with the data, accessible via // the IO buffer @a reader, such as writing it to disk to make an externally accessible copy. static int client_reader(TSCont contp, TSEvent event, void *edata) { SinkData *data = TSContDataGet(contp); // If we got closed, we're done. if (TSVConnClosedGet(contp)) { TSfree(data); TSContDestroy(contp); return 0; } TSVIO input_vio = TSVConnWriteVIOGet(contp); if (!data) { data = TSmalloc(sizeof(SinkData)); data->total = 0; TSContDataSet(contp, data); } switch (event) { case TS_EVENT_ERROR: TSDebug(PLUGIN_NAME, "Error event"); TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio); break; case TS_EVENT_VCONN_READ_COMPLETE: TSDebug(PLUGIN_NAME, "READ_COMPLETE"); break; case TS_EVENT_VCONN_READ_READY: case TS_EVENT_IMMEDIATE: TSDebug(PLUGIN_NAME, "Data event - %s", event == TS_EVENT_IMMEDIATE ? "IMMEDIATE" : "READ_READY"); // Look for data and if we find any, consume. if (TSVIOBufferGet(input_vio)) { TSIOBufferReader reader = TSVIOReaderGet(input_vio); int64_t n = TSIOBufferReaderAvail(reader); if (n > 0) { TSIOBufferReaderConsume(reader, n); TSVIONDoneSet(input_vio, TSVIONDoneGet(input_vio) + n); data->total += n; // internal accounting so we can print the value at the end. TSDebug(PLUGIN_NAME, "Consumed %" PRId64 " bytes", n); } if (TSVIONTodoGet(input_vio) > 0) { // signal that we can accept more data. TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); } else { TSDebug(PLUGIN_NAME, "send WRITE_COMPLETE"); TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); } } else { // buffer gone, we're done. TSDebug(PLUGIN_NAME, "upstream buffer disappeared - %" PRId64 " bytes", data->total); } break; default: TSDebug(PLUGIN_NAME, "unhandled event %d", event); break; } return 0; }
static int ts_lua_flush(lua_State * L) { int64_t avail; ts_lua_http_intercept_ctx *ictx; ictx = ts_lua_get_http_intercept_ctx(L); avail = TSIOBufferReaderAvail(ictx->output.reader); if (avail > 0) { ictx->to_flush = TSVIONDoneGet(ictx->output.vio) + TSIOBufferReaderAvail(ictx->output.reader); TSVIOReenable(ictx->output.vio); return lua_yield(L, 0); } return 0; }
static int transform_connect(TSCont contp, TransformData *data) { TSAction action; int content_length; struct sockaddr_in ip_addr; data->state = STATE_CONNECT; content_length = TSIOBufferReaderAvail(data->input_reader); if (content_length >= 0) { data->content_length = content_length; data->content_length = htonl(data->content_length); /* Prepend the content length to the buffer. * If we decide to not send the content to the transforming * server then we need to make sure and skip input_reader * over the content length. */ { TSIOBuffer temp; TSIOBufferReader tempReader; temp = TSIOBufferCreate(); tempReader = TSIOBufferReaderAlloc(temp); TSIOBufferWrite(temp, (const char *)&content_length, sizeof(int)); TSIOBufferCopy(temp, data->input_reader, content_length, 0); TSIOBufferReaderFree(data->input_reader); TSIOBufferDestroy(data->input_buf); data->input_buf = temp; data->input_reader = tempReader; } } else { TSError("[%s] TSIOBufferReaderAvail returns TS_ERROR", PLUGIN_NAME); return 0; } /* TODO: This only supports IPv4, probably should be changed at some point, but it's an example ... */ memset(&ip_addr, 0, sizeof(ip_addr)); ip_addr.sin_family = AF_INET; ip_addr.sin_addr.s_addr = server_ip; /* Should be in network byte order */ ip_addr.sin_port = server_port; TSDebug(PLUGIN_NAME, "net connect."); action = TSNetConnect(contp, (struct sockaddr const *)&ip_addr); if (!TSActionDone(action)) { data->pending_action = action; } return 0; }
static int ts_http_fetcher_transfer(http_fetcher *fch, int code) { int64_t left; left = TSIOBufferReaderAvail(fch->flow_reader); TSIOBufferCopy(fch->body_buffer, fch->flow_reader, left, 0); TSIOBufferReaderConsume(fch->flow_reader, left); return code; }
void ts_http_fetcher_consume_resp_body(http_fetcher *fch, int64_t len) { if (!fch->header_done) return; TSIOBufferReaderConsume(fch->body_reader, len); if (!fch->deleted && !fch->action && TSIOBufferReaderAvail(fch->body_reader) < TS_FETCH_MARK_BODY_LOW_WATER) { fch->action = TSContSchedule(fch->fetch_contp, 0, TS_THREAD_POOL_DEFAULT); } }
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 int transform_write(TSCont contp, TransformData *data) { int content_length; data->state = STATE_WRITE; content_length = TSIOBufferReaderAvail(data->input_reader); if (content_length >= 0) { data->server_vio = TSVConnWrite(data->server_vc, contp, TSIOBufferReaderClone(data->input_reader), content_length); } else { TSError("[%s] TSIOBufferReaderAvail returns TS_ERROR", PLUGIN_NAME); } return 0; }
static int handle_output(TSCont contp, MyData *data) { /* Check to see if we need to initiate the output operation. */ if (!data->output_vio) { TSVConn output_conn; /* Get the output connection where we'll write data to. */ output_conn = TSTransformOutputVConnGet(contp); data->output_vio = TSVConnWrite(output_conn, contp, data->output_reader, TSIOBufferReaderAvail(data->output_reader)); TSAssert(data->output_vio); } return 1; }
static int ts_lua_http_intercept_process_write(TSEvent event, ts_lua_http_intercept_ctx * ictx) { switch (event) { case TS_EVENT_VCONN_WRITE_READY: if (TSIOBufferReaderAvail(ictx->output.reader)) TSVIOReenable(ictx->output.vio); break; case TS_EVENT_VCONN_WRITE_COMPLETE: ictx->send_complete = 1; break; case TS_EVENT_ERROR: default: return -1; } return 0; }
static int ts_lua_http_intercept_process_write(TSEvent event, ts_lua_http_intercept_ctx *ictx) { int64_t done, avail; switch (event) { case TS_EVENT_VCONN_WRITE_READY: avail = TSIOBufferReaderAvail(ictx->output.reader); if (ictx->all_ready) { TSVIOReenable(ictx->output.vio); } else if (ictx->to_flush > 0) { // ts.flush() done = TSVIONDoneGet(ictx->output.vio); if (ictx->to_flush > done) { TSVIOReenable(ictx->output.vio); } else { // we had flush all the data we want ictx->to_flush = 0; ts_lua_flush_launch(ictx); // wake up } } else if (avail > 0) { TSVIOReenable(ictx->output.vio); } break; case TS_EVENT_VCONN_WRITE_COMPLETE: ictx->send_complete = 1; break; case TS_EVENT_ERROR: default: return -1; } return 0; }
static int ts_lua_http_intercept_process_read(TSEvent event, ts_lua_http_intercept_ctx *ictx) { int64_t avail = TSIOBufferReaderAvail(ictx->input.reader); TSIOBufferReaderConsume(ictx->input.reader, avail); switch (event) { case TS_EVENT_VCONN_READ_READY: TSVConnShutdown(ictx->net_vc, 1, 0); case TS_EVENT_VCONN_READ_COMPLETE: case TS_EVENT_VCONN_EOS: ictx->recv_complete = 1; break; default: return -1; } return 0; }
int ts_http_fetcher_process_write(http_fetcher *fch, TSEvent event) { switch (event) { case TS_EVENT_VCONN_WRITE_READY: if (TSIOBufferReaderAvail(fch->req_reader) > 0) TSVIOReenable(fch->write_vio); break; case TS_EVENT_VCONN_WRITE_COMPLETE: break; case TS_EVENT_ERROR: default: return ts_http_fetcher_callback_sm(fch, TS_EVENT_FETCH_ERROR); } return 0; }
static int transform_read_status_event(TSCont contp, TransformData * data, TSEvent event, void *edata) { switch (event) { case TS_EVENT_ERROR: case TS_EVENT_VCONN_EOS: return transform_bypass(contp, data); case TS_EVENT_VCONN_READ_COMPLETE: if (TSIOBufferReaderAvail(data->output_reader) == sizeof(int)) { TSIOBufferBlock blk; char *buf; void *buf_ptr; int64_t avail; int64_t read_nbytes = sizeof(int); int64_t read_ndone = 0; buf_ptr = &data->content_length; while (read_nbytes > 0) { blk = TSIOBufferReaderStart(data->output_reader); buf = (char *) TSIOBufferBlockReadStart(blk, data->output_reader, &avail); read_ndone = (avail >= read_nbytes) ? read_nbytes : avail; memcpy(buf_ptr, buf, read_ndone); if (read_ndone > 0) { TSIOBufferReaderConsume(data->output_reader, read_ndone); read_nbytes -= read_ndone; /* move ptr frwd by read_ndone bytes */ buf_ptr = (char *) buf_ptr + read_ndone; } } //data->content_length = ntohl(data->content_length); return transform_read(contp, data); } return transform_bypass(contp, data); default: break; } return 0; }
static int ts_lua_http_intercept_run_coroutine(ts_lua_http_intercept_ctx * ictx, int n) { int ret; int64_t avail; int64_t done; lua_State *L; L = ictx->lua; ret = lua_resume(L, n); switch (ret) { case 0: // finished avail = TSIOBufferReaderAvail(ictx->output.reader); done = TSVIONDoneGet(ictx->output.vio); TSVIONBytesSet(ictx->output.vio, avail + done); ictx->all_ready = 1; if (avail) { TSVIOReenable(ictx->output.vio); } else { ictx->send_complete = 1; } break; case 1: // yield break; default: // error fprintf(stderr, "lua_resume failed: %s\n", lua_tostring(L, -1)); return -1; } return 0; }
/*------------------------------------------------------------------------- dump_psi Dump the psi_output to the downstream vconnection. Input: contp continuation for the current transaction Output : Return Value: 0 if failure 1 if success -------------------------------------------------------------------------*/ static int dump_psi(TSCont contp) { ContData *data; int psi_output_len; /* TODO: This is odd, do we need to get the input_vio, but never use it ?? */ #if 0 TSVIO input_vio; input_vio = TSVConnWriteVIOGet(contp); #endif data = TSContDataGet(contp); TSAssert(data->magic == MAGIC_ALIVE); /* If script exec succeded, copy its output to the downstream vconn */ if (data->psi_success == 1) { psi_output_len = TSIOBufferReaderAvail(data->psi_reader); if (psi_output_len > 0) { data->transform_bytes += psi_output_len; TSDebug(DBG_TAG, "Inserting %d bytes from include file", psi_output_len); /* TODO: Should we check the return value of TSIOBufferCopy() ? */ TSIOBufferCopy(TSVIOBufferGet(data->output_vio), data->psi_reader, psi_output_len, 0); /* Consume all the output data */ TSIOBufferReaderConsume(data->psi_reader, psi_output_len); /* Reenable the output connection so it can read the data we've produced. */ TSVIOReenable(data->output_vio); } } /* Change state to finish up reading upstream data */ data->state = STATE_READ_DATA; return 0; }
int ts_chunked_process(TSIOBufferReader readerp, TSIOBuffer bufp, int end) { char hex[32]; int n; int64_t avail; avail = TSIOBufferReaderAvail(readerp); if (avail) { n = snprintf(hex, sizeof(hex), "%llx\r\n", (long long)avail); TSIOBufferWrite(bufp, hex, n); TSIOBufferCopy(bufp, readerp, avail, 0); TSIOBufferWrite(bufp, "\r\n", sizeof("\r\n") - 1); TSIOBufferReaderConsume(readerp, avail); } if (end) TSIOBufferWrite(bufp, "0\r\n\r\n", sizeof("0\r\n\r\n") - 1); return end; }
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); }
static int ts_dechunk_transform_handler(TSCont contp, ts_dechunk_transform_ctx *tc) { TSVConn output_conn; TSVIO input_vio; TSIOBufferReader input_reader; int64_t towrite, upstream_done, avail; int ret, eos; output_conn = TSTransformOutputVConnGet(contp); input_vio = TSVConnWriteVIOGet(contp); input_reader = TSVIOReaderGet(input_vio); if (!tc->output_buffer) { tc->output_buffer = TSIOBufferCreate(); tc->output_reader = TSIOBufferReaderAlloc(tc->output_buffer); tc->output_vio = TSVConnWrite(output_conn, contp, tc->output_reader, INT64_MAX); } if (!TSVIOBufferGet(input_vio)) { TSVIONBytesSet(tc->output_vio, tc->total); TSVIOReenable(tc->output_vio); return 1; } towrite = TSVIONTodoGet(input_vio); upstream_done = TSVIONDoneGet(input_vio); avail = TSIOBufferReaderAvail(input_reader); if (towrite > avail) { towrite = avail; eos = 0; } else { eos = 1; } ret = ts_dechunk_process(tc->info, input_reader, tc->output_buffer, eos); if (ret < 0) { tc->total = TSVIONDoneGet(tc->output_vio) + TSIOBufferReaderAvail(tc->output_reader); TSVIONBytesSet(tc->output_vio, tc->total); TSVIOReenable(tc->output_vio); TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio); return ret; } if (!ret) { TSVIOReenable(tc->output_vio); TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); } else { tc->total = TSVIONDoneGet(tc->output_vio) + TSIOBufferReaderAvail(tc->output_reader); TSVIONBytesSet(tc->output_vio, tc->total); TSVIOReenable(tc->output_vio); TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); } return 1; }
/** * Process data from ATS. * * Process data from one of the ATS events. * * @param[in,out] contp - the continuation * @param[in,out] ibd - the filter descriptor */ static void process_data(TSCont contp, ibd_ctx *ibd) { int64_t ntodo; int64_t navail; TSIOBufferReader input_reader, output_reader; TSIOBufferBlock block; const char *buf; int64_t nbytes; ib_status_t rc; tsib_filter_ctx *fctx = ibd->data; tsib_txn_ctx *txndata = TSContDataGet(contp); TSVIO input_vio = TSVConnWriteVIOGet(contp); TSIOBuffer in_buf = TSVIOBufferGet(input_vio); /* Test whether we're going into an errordoc */ if (HTTP_CODE(txndata->status)) { /* We're going to an error document, * so we discard all this data */ ib_log_debug2_tx(txndata->tx, "Status is %d, discarding", txndata->status); ibd->data->buffering = IOBUF_DISCARD; } /* Test for EOS */ if (in_buf == NULL) { if (fctx->output_buffer != NULL) { /* flush anything we have buffered. This is final! */ rc = flush_data(fctx, -1, 1); switch(rc) { case IB_OK: break; case IB_EBADVAL: ib_log_error_tx(txndata->tx, "Bad/Inconsistent stream edit(s) ignored."); break; default: /* Can't happen unless a new status is introduced */ ib_log_error_tx(txndata->tx, "Unhandled return value %d", rc); break; } } else { /* I guess NULL input may mean something other than EOS. * This appears to be possible when * processing an HTTP error from the backend. */ ib_log_debug2_tx(txndata->tx, "Filter input was null. No filtering."); /* RNS-1268: seems we may have to go through all the motions * of creating and enabling an output_vio with no data. */ fctx->output_buffer = TSIOBufferCreate(); ib_mm_register_cleanup(txndata->tx->mm, (ib_mm_cleanup_fn_t) TSIOBufferDestroy, (void*) fctx->output_buffer); output_reader = TSIOBufferReaderAlloc(fctx->output_buffer); fctx->output_vio = TSVConnWrite(TSTransformOutputVConnGet(contp), contp, output_reader, 0); TSVIOReenable(fctx->output_vio); } return; } /* Test for first time, and initialise. */ if (!fctx->output_buffer) { // FIXME - What to choose here and why? int64_t output_vio_sz = TSVIONBytesGet(input_vio); // NOTE: Using INT64_MAX asserts on 4.2.2: InkAPI.cc:6261: failed assert `sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS` //int64_t output_vio_sz = INT64_MAX; // NOTE: Does it matter that this is only INT32_MAX as in the examples? // int64_t output_vio_sz = INT32_MAX; //int64_t output_vio_sz = fctx->have_edits // ? INT64_MAX // : TSVIONBytesGet(input_vio); fctx->output_buffer = TSIOBufferCreate(); ib_mm_register_cleanup(txndata->tx->mm, (ib_mm_cleanup_fn_t) TSIOBufferDestroy, (void*) fctx->output_buffer); // FIXME - Where is TSIOBufferReaderFree()? output_reader = TSIOBufferReaderAlloc(fctx->output_buffer); fctx->output_vio = TSVConnWrite(TSTransformOutputVConnGet(contp), contp, output_reader, output_vio_sz); fctx->buffer = TSIOBufferCreate(); ib_mm_register_cleanup(txndata->tx->mm, (ib_mm_cleanup_fn_t) TSIOBufferDestroy, (void*) fctx->buffer); // FIXME - Where is TSIOBufferReaderFree()? fctx->reader = TSIOBufferReaderAlloc(fctx->buffer); /* Get the buffering config */ if (!HTTP_CODE(txndata->status)) { buffer_init(ibd, txndata->tx); } } /* Get any unprocessed bytes. */ ntodo = TSVIONTodoGet(input_vio); /* Test for EOS */ if (ntodo == 0) { ib_log_debug2_tx(txndata->tx, "ntodo zero before consuming data"); flush_data(fctx, -1, 1); /* 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); return; } /* OK, there's some input awaiting our attention */ input_reader = TSVIOReaderGet(input_vio); while (navail = TSIOBufferReaderAvail(input_reader), navail > 0) { block = TSIOBufferReaderStart(input_reader); buf = TSIOBufferBlockReadStart(block, input_reader, &nbytes); rc = (*ibd->ibd->ib_notify_body)(txndata->tx->ib, txndata->tx, buf, nbytes); if (rc != IB_OK) { ib_log_error_tx(txndata->tx, "Error %d notifying body data.", rc); } rc = buffer_data_chunk(fctx, input_reader, nbytes); switch (rc) { case IB_EAGAIN: case IB_OK: break; case IB_EBADVAL: ib_log_error_tx(txndata->tx, "Bad/Inconsistent stream edit(s) ignored."); break; default: /* Can't happen unless a new status is introduced */ ib_log_error_tx(txndata->tx, "Unhandled return value %d", rc); break; } TSIOBufferReaderConsume(input_reader, nbytes); TSVIONDoneSet(input_vio, TSVIONDoneGet(input_vio) + nbytes); } ntodo = TSVIONTodoGet(input_vio); if (ntodo == 0) { ib_log_debug2_tx(txndata->tx, "ntodo zero after consuming data"); flush_data(fctx, -1, 1); /* 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); } else { /* 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); } }
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); }
int ts_http_fetcher_filter_body(http_fetcher *fch, int ev) { int ret; int64_t resp_avail, body_avail, wavail, need; if (fch->action) { TSActionCancel(fch->action); fch->action = NULL; } need = 0; resp_avail = TSIOBufferReaderAvail(fch->resp_reader); if (ev != BODY_COMPLETE) { body_avail = TSIOBufferReaderAvail(fch->body_reader); wavail = TS_FETCH_MARK_BODY_HIGH_WATER - body_avail; if (wavail <= 0) return TS_FETCH_EVENT_BODY_QUIET; need = resp_avail > wavail ? wavail : resp_avail; TSIOBufferCopy(fch->flow_buffer, fch->resp_reader, need, 0); TSIOBufferReaderConsume(fch->resp_reader, need); if (TSIOBufferReaderAvail(fch->resp_reader) < TS_FETCH_MARK_RESP_LOW_WATER) TSVIOReenable(fch->read_vio); } else { TSIOBufferCopy(fch->flow_buffer, fch->resp_reader, resp_avail, 0); TSIOBufferReaderConsume(fch->resp_reader, resp_avail); need = resp_avail; } if (fch->chunked) { ret = ts_http_fetcher_verify_chunked(fch, ev); } else if (fch->resp_cl >= 0) { ret = ts_http_fetcher_verify_cl(fch, ev); } else { ret = ts_http_fetcher_transfer(fch, ev); } switch (ret) { case BODY_READY: if (need == 0) return TS_FETCH_EVENT_BODY_QUIET; return TS_FETCH_EVENT_BODY_READY; case BODY_COMPLETE: fch->body_done = 1; return TS_FETCH_EVENT_BODY_COMPLETE; default: break; } fch->error = 1; return TS_FETCH_EVENT_ERROR; }
static void handle_transform(TSCont contp) { TSVConn output_conn; TSIOBuffer buf_test; TSVIO input_vio; MyData *data; int64_t towrite; int64_t avail; TSDebug("null-transform", "Entering handle_transform()"); /* 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); /* Get our data structure for this operation. The private data * structure contains the output VIO and output buffer. If the * private data structure pointer is NULL, then we'll create it * and initialize its internals. */ data = TSContDataGet(contp); if (!data) { data = my_data_alloc(); data->output_buffer = TSIOBufferCreate(); data->output_reader = TSIOBufferReaderAlloc(data->output_buffer); TSDebug("null-transform", "\tWriting %" PRId64 " bytes on VConn", TSVIONBytesGet(input_vio)); // data->output_vio = TSVConnWrite(output_conn, contp, data->output_reader, INT32_MAX); data->output_vio = TSVConnWrite(output_conn, contp, data->output_reader, INT64_MAX); // data->output_vio = TSVConnWrite(output_conn, contp, data->output_reader, TSVIONBytesGet(input_vio)); TSContDataSet(contp, data); } /* We also check to see if the input VIO's buffer is non-NULL. A * NULL buffer indicates that the write operation has been * shutdown and that the upstream continuation does not want us to send any * more WRITE_READY or WRITE_COMPLETE events. For this simplistic * transformation that means we're done. In a more complex * transformation we might have to finish writing the transformed * data to our output connection. */ buf_test = TSVIOBufferGet(input_vio); if (!buf_test) { TSVIONBytesSet(data->output_vio, TSVIONDoneGet(input_vio)); TSVIOReenable(data->output_vio); 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("null-transform", "\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. */ avail = TSIOBufferReaderAvail(TSVIOReaderGet(input_vio)); TSDebug("null-transform", "\tavail is %" PRId64 "", avail); if (towrite > avail) { towrite = avail; } if (towrite > 0) { /* Copy the data from the read buffer to the output buffer. */ TSIOBufferCopy(TSVIOBufferGet(data->output_vio), TSVIOReaderGet(input_vio), towrite, 0); /* Tell the read buffer that we have read the data and are no * longer interested in it. */ TSIOBufferReaderConsume(TSVIOReaderGet(input_vio), towrite); /* Modify the input VIO to reflect how much data we've * completed. */ TSVIONDoneSet(input_vio, TSVIONDoneGet(input_vio) + towrite); } } /* 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(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(data->output_vio, TSVIONDoneGet(input_vio)); TSVIOReenable(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 handle_buffering(TSCont contp, MyData *data) { TSVIO write_vio; int towrite; int avail; /* 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. */ write_vio = TSVConnWriteVIOGet(contp); /* Create the output buffer and its associated reader */ if (!data->output_buffer) { data->output_buffer = TSIOBufferCreate(); TSAssert(data->output_buffer); data->output_reader = TSIOBufferReaderAlloc(data->output_buffer); TSAssert(data->output_reader); } /* We also check to see if the write VIO's buffer is non-NULL. A NULL buffer indicates that the write operation has been shutdown and that the continuation does not want us to send any more WRITE_READY or WRITE_COMPLETE events. For this buffered transformation that means we're done buffering data. */ if (!TSVIOBufferGet(write_vio)) { data->state = STATE_OUTPUT_DATA; return 0; } /* Determine how much data we have left to read. For this bnull transform plugin this is also the amount of data we have left to write to the output connection. */ towrite = TSVIONTodoGet(write_vio); if (towrite > 0) { /* The amount of data left to read needs to be truncated by the amount of data actually in the read buffer. */ avail = TSIOBufferReaderAvail(TSVIOReaderGet(write_vio)); if (towrite > avail) { towrite = avail; } if (towrite > 0) { /* Copy the data from the read buffer to the input buffer. */ TSIOBufferCopy(data->output_buffer, TSVIOReaderGet(write_vio), towrite, 0); /* Tell the read buffer that we have read the data and are no longer interested in it. */ TSIOBufferReaderConsume(TSVIOReaderGet(write_vio), towrite); /* Modify the write VIO to reflect how much data we've completed. */ TSVIONDoneSet(write_vio, TSVIONDoneGet(write_vio) + towrite); } } /* Now we check the write VIO to see if there is data left to read. */ if (TSVIONTodoGet(write_vio) > 0) { if (towrite > 0) { /* Call back the write VIO continuation to let it know that we are ready for more data. */ TSContCall(TSVIOContGet(write_vio), TS_EVENT_VCONN_WRITE_READY, write_vio); } } else { data->state = STATE_OUTPUT_DATA; /* Call back the write VIO continuation to let it know that we have completed the write operation. */ TSContCall(TSVIOContGet(write_vio), TS_EVENT_VCONN_WRITE_COMPLETE, write_vio); } return 1; }
/*------------------------------------------------------------------------- handle_transform Get data from upstream vconn. Parse it. Include file if include tags found. Copy data to downstream vconn. Wake up upstream to get more data. Input: contp continuation for the current transaction Output : Return Value: 0 if failure 1 if success -------------------------------------------------------------------------*/ static int handle_transform(TSCont contp) { TSVConn output_conn; TSVIO input_vio; ContData *data; TSIOBufferReader input_reader; int toread, avail, psi, toconsume, towrite; /* Get the output (downstream) vconnection where we'll write data to. */ output_conn = TSTransformOutputVConnGet(contp); /* Get upstream vio */ input_vio = TSVConnWriteVIOGet(contp); data = TSContDataGet(contp); TSAssert(data->magic == MAGIC_ALIVE); if (!data->output_buffer) { data->output_buffer = TSIOBufferCreate(); data->output_reader = TSIOBufferReaderAlloc(data->output_buffer); /* INT64_MAX because we don't know yet how much bytes we'll produce */ data->output_vio = TSVConnWrite(output_conn, contp, data->output_reader, INT64_MAX); } /* If the input VIO's buffer is NULL, the transformation is over */ if (!TSVIOBufferGet(input_vio)) { TSDebug(DBG_TAG, "input_vio NULL, terminating transformation"); TSVIONBytesSet(data->output_vio, data->transform_bytes); TSVIOReenable(data->output_vio); return 1; } /* Determine how much data we have left to read. */ toread = TSVIONTodoGet(input_vio); if (toread > 0) { input_reader = TSVIOReaderGet(input_vio); avail = TSIOBufferReaderAvail(input_reader); /* There are some data available for reading. Let's parse it */ if (avail > 0) { /* No need to parse data if there are too few bytes left to contain an include command... */ if (toread > (PSI_START_TAG_LEN + PSI_END_TAG_LEN)) { psi = parse_data(contp, input_reader, avail, &toconsume, &towrite); } else { towrite = avail; toconsume = avail; psi = 0; } if (towrite > 0) { /* Update the total size of the doc so far */ data->transform_bytes += towrite; /* Copy the data from the read buffer to the output buffer. */ /* TODO: Should we check the return value of TSIOBufferCopy() ? */ TSIOBufferCopy(TSVIOBufferGet(data->output_vio), TSVIOReaderGet(input_vio), towrite, 0); /* Reenable the output connection so it can read the data we've produced. */ TSVIOReenable(data->output_vio); } if (toconsume > 0) { /* Consume data we've processed an we are no longer interested in */ TSIOBufferReaderConsume(input_reader, toconsume); /* Modify the input VIO to reflect how much data we've completed. */ TSVIONDoneSet(input_vio, TSVIONDoneGet(input_vio) + toconsume); } /* Did we find a psi filename to execute in the data ? */ if (psi) { Job *new_job; /* Add a request to include a file into the jobs queue.. */ /* We'll be called back once it's done with an EVENT_IMMEDIATE */ TSDebug(DBG_TAG, "Psi filename extracted. Adding an include job to thread queue."); data->state = STATE_READ_PSI; /* Create a new job request and add it to the queue */ new_job = job_create(contp, &psi_include, NULL); add_to_queue(&job_queue, new_job); /* Signal to the threads there is a new job */ thread_signal_job(); return 1; } } } /* Wake up upstream and downstream vconnections */ wake_up_streams(contp); return 1; }
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; }