void ts_http_fetcher_add_header(http_fetcher *fch, const char *name, int name_len, const char *value, int value_len) { if (name_len == TS_MIME_LEN_CONTENT_LENGTH && memcmp(name, TS_MIME_FIELD_CONTENT_LENGTH, name_len) == 0) { // for POST fch->post_cl = atoll(value); } TSIOBufferWrite(fch->req_buffer, name, name_len); TSIOBufferWrite(fch->req_buffer, ": ", sizeof(": ")-1); TSIOBufferWrite(fch->req_buffer, value, value_len); TSIOBufferWrite(fch->req_buffer, "\r\n", sizeof("\r\n")-1); }
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 ts_lua_http_intercept_run_coroutine(ts_lua_http_intercept_ctx * ictx) { int ret; const char *res; size_t res_len; lua_State *L; L = ictx->lua; ret = lua_resume(L, 0); switch (ret) { case 0: // finished res = lua_tolstring(L, -1, &res_len); ts_lua_http_intercept_setup_write(ictx); TSIOBufferWrite(ictx->output.buffer, res, res_len); TSVIONBytesSet(ictx->output.vio, res_len); break; case 1: // yield break; default: // error fprintf(stderr, "lua_resume failed: %s\n", lua_tostring(L, -1)); return -1; } return 0; }
void ts_http_fetcher_append_data(http_fetcher *fch, const char *data, int len) { TSIOBufferWrite(fch->req_buffer, data, len); if (fch->launched) TSVIOReenable(fch->write_vio); }
static int stats_add_data_to_resp_buffer(const char *s, stats_state *my_state) { int s_len = strlen(s); TSIOBufferWrite(my_state->resp_buffer, s, s_len); return s_len; }
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; }
void ts_http_fetcher_append_data(http_fetcher *fch, const char *data, int len) { if (len <= 0 || fch->body_done) return; TSIOBufferWrite(fch->req_buffer, data, len); if (fch->launched) TSVIOReenable(fch->write_vio); }
void ts_http_fetcher_init_common(http_fetcher *fch, int method, const char *uri, int uri_len) { char buf[2048]; int n; fch->method = method; n = sprintf(buf, "%s %.*s HTTP/1.1\r\n", http_method_str[method], uri_len,uri); TSIOBufferWrite(fch->req_buffer, buf, n); }
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; }
void ts_http_fetcher_init(http_fetcher *fch, const char *method, int method_len, const char *uri, int uri_len) { char buf[2048]; int n; if (uri_len >= sizeof(buf) - 64) uri_len = sizeof(buf) - 64; if (method_len == TS_HTTP_LEN_GET && !strncasecmp(method, TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET)) { fch->method = TS_FETCH_HTTP_METHOD_GET; n = sprintf(buf, "GET %.*s HTTP/1.1\r\n", uri_len, uri); } else if (method_len == TS_HTTP_LEN_POST && !strncasecmp(method, TS_HTTP_METHOD_POST, TS_HTTP_LEN_POST)) { fch->method = TS_FETCH_HTTP_METHOD_POST; n = sprintf(buf, "POST %.*s HTTP/1.1\r\n", uri_len, uri); } else if (method_len == TS_HTTP_LEN_CONNECT && !strncasecmp(method, TS_HTTP_METHOD_CONNECT, TS_HTTP_LEN_CONNECT)) { fch->method = TS_FETCH_HTTP_METHOD_CONNECT; n = sprintf(buf, "CONNECT %.*s HTTP/1.1\r\n", uri_len, uri); } else if (method_len == TS_HTTP_LEN_DELETE && !strncasecmp(method, TS_HTTP_METHOD_DELETE, TS_HTTP_LEN_DELETE)) { fch->method = TS_FETCH_HTTP_METHOD_DELETE; n = sprintf(buf, "DELETE %.*s HTTP/1.1\r\n", uri_len, uri); } else if (method_len == TS_HTTP_LEN_HEAD && !strncasecmp(method, TS_HTTP_METHOD_HEAD, TS_HTTP_LEN_HEAD)) { fch->method = TS_FETCH_HTTP_METHOD_HEAD; n = sprintf(buf, "HEAD %.*s HTTP/1.1\r\n", uri_len, uri); } else if (method_len == TS_HTTP_LEN_PURGE && !strncasecmp(method, TS_HTTP_METHOD_PURGE, TS_HTTP_LEN_PURGE)) { fch->method = TS_FETCH_HTTP_METHOD_PURGE; n = sprintf(buf, "PURGE %.*s HTTP/1.1\r\n", uri_len, uri); } else if (method_len == TS_HTTP_LEN_PUT && !strncasecmp(method, TS_HTTP_METHOD_PUT, TS_HTTP_LEN_PUT)) { fch->method = TS_FETCH_HTTP_METHOD_PUT; n = sprintf(buf, "PUT %.*s HTTP/1.1\r\n", uri_len, uri); } else { if (method_len >= 16) method_len = 16; n = sprintf(buf, "%.*s %.*s HTTP/1.1\r\n", method_len, method, uri_len, uri); } TSIOBufferWrite(fch->req_buffer, buf, n); }
static int ts_lua_say(lua_State * L) { const char *data; size_t len; ts_lua_http_intercept_ctx *ictx; ictx = ts_lua_get_http_intercept_ctx(L); data = luaL_checklstring(L, 1, &len); if (len > 0) { TSIOBufferWrite(ictx->output.buffer, data, len); TSVIOReenable(ictx->output.vio); } return 0; }
static int add_file_to_resp(AcmeState *my_state) { if (-1 == my_state->fd) { return add_data_to_resp("\r\n", 2, my_state); } else { int ret = 0, len; char buf[8192]; while (1) { len = read(my_state->fd, buf, sizeof(buf)); if ((0 == len) || ((-1 == len) && (errno != EAGAIN) && (errno != EINTR))) { break; } else { TSIOBufferWrite(my_state->resp_buffer, buf, len); ret += len; } } close(my_state->fd); my_state->fd = -1; return ret; } }
/* Add data to the output */ inline static int add_data_to_resp(const char *buf, int len, HCState *my_state) { TSIOBufferWrite(my_state->resp_buffer, buf, len); return len; }
static int ts_lua_transform_handler(TSCont contp, ts_lua_http_transform_ctx *transform_ctx, TSEvent event, int n) { TSVConn output_conn; TSVIO input_vio; TSIOBufferReader input_reader; TSIOBufferBlock blk; int64_t toread, towrite, blk_len, upstream_done, input_avail, input_wm_bytes, l; const char *start; const char *res; size_t res_len; int ret, eos, write_down, rc, top, empty_input; ts_lua_coroutine *crt; ts_lua_cont_info *ci; lua_State *L; TSMutex mtxp; ci = &transform_ctx->cinfo; crt = &ci->routine; mtxp = crt->mctx->mutexp; L = crt->lua; output_conn = TSTransformOutputVConnGet(contp); input_vio = TSVConnWriteVIOGet(contp); empty_input = 0; if (!TSVIOBufferGet(input_vio)) { if (transform_ctx->output.vio) { TSDebug(TS_LUA_DEBUG_TAG, "[%s] reenabling output VIO after input VIO does not exist", __FUNCTION__); TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total); TSVIOReenable(transform_ctx->output.vio); return 0; } else { TSDebug(TS_LUA_DEBUG_TAG, "[%s] no input VIO and output VIO", __FUNCTION__); empty_input = 1; } } else { // input VIO exists input_wm_bytes = TSIOBufferWaterMarkGet(TSVIOBufferGet(input_vio)); if (transform_ctx->upstream_watermark_bytes >= 0 && transform_ctx->upstream_watermark_bytes != input_wm_bytes) { TSDebug(TS_LUA_DEBUG_TAG, "[%s] Setting input_vio watermark to %" PRId64 " bytes", __FUNCTION__, transform_ctx->upstream_watermark_bytes); TSIOBufferWaterMarkSet(TSVIOBufferGet(input_vio), transform_ctx->upstream_watermark_bytes); } } if (empty_input == 0) { input_reader = TSVIOReaderGet(input_vio); } if (!transform_ctx->output.buffer) { transform_ctx->output.buffer = TSIOBufferCreate(); transform_ctx->output.reader = TSIOBufferReaderAlloc(transform_ctx->output.buffer); transform_ctx->reserved.buffer = TSIOBufferCreate(); transform_ctx->reserved.reader = TSIOBufferReaderAlloc(transform_ctx->reserved.buffer); if (empty_input == 0) { transform_ctx->upstream_bytes = TSVIONBytesGet(input_vio); } else { transform_ctx->upstream_bytes = 0; } transform_ctx->downstream_bytes = INT64_MAX; } if (empty_input == 0) { input_avail = TSIOBufferReaderAvail(input_reader); upstream_done = TSVIONDoneGet(input_vio); toread = TSVIONTodoGet(input_vio); if (toread <= input_avail) { // upstream finished eos = 1; } else { eos = 0; } } else { input_avail = 0; toread = 0; eos = 1; } if (input_avail > 0) { // move to the reserved.buffer TSIOBufferCopy(transform_ctx->reserved.buffer, input_reader, input_avail, 0); // reset input TSIOBufferReaderConsume(input_reader, input_avail); TSVIONDoneSet(input_vio, upstream_done + input_avail); } write_down = 0; if (empty_input == 0) { towrite = TSIOBufferReaderAvail(transform_ctx->reserved.reader); } else { towrite = 0; } TSMutexLock(mtxp); ts_lua_set_cont_info(L, ci); do { if (event == TS_LUA_EVENT_COROUTINE_CONT) { event = 0; goto launch; } else { n = 2; } if (towrite == 0 && empty_input == 0) { break; } if (empty_input == 0) { blk = TSIOBufferReaderStart(transform_ctx->reserved.reader); start = TSIOBufferBlockReadStart(blk, transform_ctx->reserved.reader, &blk_len); lua_pushlightuserdata(L, transform_ctx); lua_rawget(L, LUA_GLOBALSINDEX); /* push function */ if (towrite > blk_len) { lua_pushlstring(L, start, (size_t)blk_len); towrite -= blk_len; TSIOBufferReaderConsume(transform_ctx->reserved.reader, blk_len); } else { lua_pushlstring(L, start, (size_t)towrite); TSIOBufferReaderConsume(transform_ctx->reserved.reader, towrite); towrite = 0; } if (!towrite && eos) { lua_pushinteger(L, 1); /* second param, data finished */ } else { lua_pushinteger(L, 0); /* second param, data not finish */ } } else { lua_pushlightuserdata(L, transform_ctx); lua_rawget(L, LUA_GLOBALSINDEX); /* push function */ lua_pushlstring(L, "", 0); lua_pushinteger(L, 1); /* second param, data finished */ } launch: rc = lua_resume(L, n); top = lua_gettop(L); switch (rc) { case LUA_YIELD: // coroutine yield TSMutexUnlock(mtxp); return 0; case 0: // coroutine success if (top == 2) { ret = lua_tointeger(L, -1); /* 0 is not finished, 1 is finished */ res = lua_tolstring(L, -2, &res_len); } else { // what hells code are you writing ? ret = 0; res = NULL; res_len = 0; } break; default: // coroutine failed TSError("[ts_lua] lua_resume failed: %s", lua_tostring(L, -1)); ret = 1; res = NULL; res_len = 0; break; } if (res && res_len > 0) { if (!transform_ctx->output.vio) { l = transform_ctx->downstream_bytes; if (ret) { l = res_len; } transform_ctx->output.vio = TSVConnWrite(output_conn, contp, transform_ctx->output.reader, l); // HttpSM go on } TSIOBufferWrite(transform_ctx->output.buffer, res, res_len); transform_ctx->total += res_len; write_down = 1; } lua_pop(L, top); if (ret || (eos && !towrite)) { // EOS eos = 1; break; } } while (towrite > 0); TSMutexUnlock(mtxp); if (eos && !transform_ctx->output.vio) { transform_ctx->output.vio = TSVConnWrite(output_conn, contp, transform_ctx->output.reader, 0); } if (write_down || eos) { TSVIOReenable(transform_ctx->output.vio); } if (toread > input_avail) { // upstream not finished. if (eos) { TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total); if (empty_input == 0) { TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_EOS, input_vio); } } else { if (empty_input == 0) { TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); } } } else { // upstream is finished. TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total); if (empty_input == 0) { TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); } } return 0; }
static int ts_lua_transform_handler(TSCont contp, ts_lua_transform_ctx *transform_ctx) { TSVConn output_conn; TSVIO input_vio; TSIOBufferReader input_reader; TSIOBufferBlock blk; int64_t towrite, blk_len, upstream_done, avail, left; const char *start; const char *res; size_t res_len; int ret, eos; lua_State *L; TSMutex mtxp; L = transform_ctx->hctx->lua; mtxp = transform_ctx->hctx->mctx->mutexp; output_conn = TSTransformOutputVConnGet(contp); input_vio = TSVConnWriteVIOGet(contp); input_reader = TSVIOReaderGet(input_vio); if (!transform_ctx->output_buffer) { transform_ctx->output_buffer = TSIOBufferCreate(); transform_ctx->output_reader = TSIOBufferReaderAlloc(transform_ctx->output_buffer); transform_ctx->output_vio = TSVConnWrite(output_conn, contp, transform_ctx->output_reader, INT64_MAX); } if (!TSVIOBufferGet(input_vio)) { TSVIONBytesSet(transform_ctx->output_vio, transform_ctx->total); TSVIOReenable(transform_ctx->output_vio); return 1; } if (transform_ctx->eos) { return 1; } left = towrite = TSVIONTodoGet(input_vio); upstream_done = TSVIONDoneGet(input_vio); avail = TSIOBufferReaderAvail(input_reader); eos = 0; if (left <= avail) eos = 1; if (towrite > avail) towrite = avail; TSMutexLock(mtxp); blk = TSIOBufferReaderStart(input_reader); do { start = TSIOBufferBlockReadStart(blk, input_reader, &blk_len); lua_pushlightuserdata(L, transform_ctx); lua_rawget(L, LUA_GLOBALSINDEX); /* push function */ if (towrite > blk_len) { lua_pushlstring(L, start, (size_t)blk_len); towrite -= blk_len; } else { lua_pushlstring(L, start, (size_t)towrite); towrite = 0; } if (!towrite && eos) { lua_pushinteger(L, 1); /* second param, not finish */ } else { lua_pushinteger(L, 0); /* second param, not finish */ } if (lua_pcall(L, 2, 2, 0)) { fprintf(stderr, "lua_pcall failed: %s\n", lua_tostring(L, -1)); } ret = lua_tointeger(L, -1); /* 0 is not finished, 1 is finished */ res = lua_tolstring(L, -2, &res_len); if (res && res_len) { TSIOBufferWrite(transform_ctx->output_buffer, res, res_len); transform_ctx->total += res_len; } lua_pop(L, 2); if (ret || (eos && !towrite)) { // EOS eos = 1; break; } blk = TSIOBufferBlockNext(blk); } while (blk && towrite > 0); TSMutexUnlock(mtxp); TSIOBufferReaderConsume(input_reader, avail); TSVIONDoneSet(input_vio, upstream_done + avail); if (eos) { transform_ctx->eos = 1; TSVIONBytesSet(transform_ctx->output_vio, transform_ctx->total); TSVIOReenable(transform_ctx->output_vio); TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); } else { TSVIOReenable(transform_ctx->output_vio); TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); } return 1; }
static int ts_lua_cache_write(lua_State *L) { int64_t length, n; const char *data; size_t dlen; 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 < 2) { return luaL_error(L, "'ts.cache_write' requires parameter"); } /* data */ if (!lua_isstring(L, 2)) { return luaL_error(L, "'ts.cache_write' second param is not string"); } data = luaL_checklstring(L, 2, &dlen); /* length */ length = dlen; if (n >= 3) { if (!lua_isnumber(L, 3)) { return luaL_error(L, "'ts.cache_write' third param is not number"); } length = lua_tonumber(L, 3); if (length <= 0) { return luaL_error(L, "'ts.cache_write' third param is a not valid number"); } if (length > dlen) length = dlen; } /* table */ if (!lua_istable(L, 1)) { return luaL_error(L, "'ts.cache_write' 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_write' first param is not valid"); } info = (ts_lua_cache_info*)lua_touserdata(L, -1); lua_pop(L, 1); if (info->optype != TS_LUA_CACHE_WRITE) { return luaL_error(L, "'ts.cache_write' is writing to invalid vc"); } if (info->err) { lua_pushnil(L); return 1; } info->need += length; info->current_handler = &ts_lua_cache_handle_write; TSIOBufferWrite(info->ioh.buffer, data, length); if (info->ioh.vio == NULL) { info->ioh.vio = TSVConnWrite(info->cache_vc, info->contp, info->ioh.reader, INT64_MAX); } else { TSVIOReenable(info->ioh.vio); } info->wait = 1; return lua_yield(L, 0); }
/** * Function to flush buffered data and apply edits in-stream * * @param[in] fctx - the filter data * @param[in] nbytes - number of bytes to flush (-1 to flush all data) * @param[in] last - final flush indicator (no more data to come) * @return success or error status */ static ib_status_t flush_data(tsib_filter_ctx *fctx, int64_t nbytes, int last) { /* copy logic from mod_range_filter. * * * It's a push logic, so that'll be range_filter's output filter * * Note: We're not buffering anything here. We only see data * when they're flushed from the buffer! */ ib_status_t rc = IB_OK; int nedits, i; size_t n, start; if (nbytes == -1) { /* just output all we have */ nbytes = fctx->buffered; } if ((fctx->edits != NULL) && (fctx->edits->len > 0)) { /* Sort to reverse order, so we can pop elements simply by * decrementing len */ nedits = fctx->edits->len/sizeof(edit_t); qsort(fctx->edits->data, nedits, sizeof(edit_t), qcompare); for (i = nedits-1; i >= 0; --i) { edit_t *edit = &((edit_t*) fctx->edits->data)[i]; /* sanity-check that edit is in range */ if (edit->start < fctx->bytes_done) { /* Edit applies to data already gone. This probably means * someone fed us overlapping edits */ rc = IB_EBADVAL; /* Abandon this edit. Continue loop (next edit may be fine) */ fctx->edits->len -= sizeof(edit_t); continue; } else if (edit->start + edit->bytes > fctx->bytes_done + nbytes) { /* Edit goes beyond data we're dealing with. * So leave it for next time. * This could affect buffering behaviour, but in a good cause * If this is out-of-range then so are other edits, * but they'll be in range when we have more data. * * Best we can do now is to flush data before this edit. * by setting nbytes. * * Exception: if it's the last call, this edit is out-of-range * so we just abandon it. */ if (!last) { nbytes = edit->start - fctx->bytes_done; rc = IB_EAGAIN; break; } else { fctx->edits->len -= sizeof(edit_t); rc = IB_EBADVAL; continue; } } /* copy data up to start-of-edit */ start = edit->start - fctx->bytes_done; while (start > 0) { n = TSIOBufferCopy(fctx->output_buffer, fctx->reader, start, 0); assert (n > 0); // FIXME - handle error TSIOBufferReaderConsume(fctx->reader, n); fctx->buffered -= n; fctx->bytes_done += n; nbytes -= n; start -= n; } /* Discard anything that's being deleted */ TSIOBufferReaderConsume(fctx->reader, edit->bytes); nbytes -= edit->bytes; fctx->buffered -= edit->bytes; fctx->bytes_done += edit->bytes; /* Insert replacement string */ n = TSIOBufferWrite(fctx->output_buffer, edit->repl, edit->repl_len); assert(n == edit->repl_len); // FIXME (if this ever happens)! /* Record change to data size */ fctx->offs += edit->repl_len - edit->bytes; /* We're done with this edit. */ fctx->edits->len -= sizeof(edit_t); } } /* There's no (more) editing to do, so we can just move data to output * using TS native refcounted pointer ops */ while (nbytes > 0) { n = TSIOBufferCopy(fctx->output_buffer, fctx->reader, nbytes, 0); assert (n > 0); // FIXME - handle error TSIOBufferReaderConsume(fctx->reader, n); fctx->buffered -= n; fctx->bytes_done += n; nbytes -= n; } if (last) { // TODO - Do we need to add this to what is there vs just set it as we are now? /* Now we can tell downstream exactly how much data it has */ TSVIONBytesSet(fctx->output_vio, fctx->bytes_done + fctx->offs); } TSVIOReenable(fctx->output_vio); return rc; }