static void parse_response(StateInfo *state) { TSIOBufferBlock block; TSParseResult pr = TS_PARSE_CONT; int64_t avail; char *start; block = TSIOBufferReaderStart(state->resp_io_buf_reader); while ((pr == TS_PARSE_CONT) && (block != NULL)) { start = (char *)TSIOBufferBlockReadStart(block, state->resp_io_buf_reader, &avail); if (avail > 0) { pr = TSHttpHdrParseResp(state->resp_info->parser, state->resp_info->buf, state->resp_info->http_hdr_loc, (const char **)&start, (const char *)(start + avail)); } block = TSIOBufferBlockNext(block); } if (pr != TS_PARSE_CONT) { state->resp_info->status = TSHttpHdrStatusGet(state->resp_info->buf, state->resp_info->http_hdr_loc); state->resp_info->parsed = true; TSDebug(PLUGIN_NAME, "HTTP Status: %d", state->resp_info->status); } }
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; }
int64_t IOBufferReaderCopy(TSIOBufferReader readerp, void *buf, int64_t length) { int64_t avail, need, n; const char *start; TSIOBufferBlock blk; n = 0; blk = TSIOBufferReaderStart(readerp); while (blk) { start = TSIOBufferBlockReadStart(blk, readerp, &avail); need = length < avail ? length : avail; if (need > 0) { memcpy((char*)buf + n, start, need); length -= need; n += need; } if (length == 0) break; blk = TSIOBufferBlockNext(blk); } return n; }
/*------------------------------------------------------------------------- strsearch_ioreader Looks for string pattern in an iobuffer Input: reader reader on a iobuffer pattern string to look for (nul terminated) Output: nparse number of chars scanned, excluding the matching pattern Return Value: STR_SUCCESS if pattern found STR_PARTIAL if pattern found partially STR_FAIL if pattern not found -------------------------------------------------------------------------*/ static StrOperationResult strsearch_ioreader(TSIOBufferReader reader, const char *pattern, int *nparse) { int index = 0; TSIOBufferBlock block = TSIOBufferReaderStart(reader); int slen = strlen(pattern); if (slen <= 0) { return STR_FAIL; } *nparse = 0; /* Loop thru each block while we've not yet found the pattern */ while ((block != NULL) && (index < slen)) { int64_t blocklen; const char *blockptr = TSIOBufferBlockReadStart(block, reader, &blocklen); const char *ptr; for (ptr = blockptr; ptr < blockptr + blocklen; ptr++) { (*nparse)++; if (*ptr == pattern[index]) { index++; if (index == slen) { break; } } else { index = 0; } } /* Parse next block */ block = TSIOBufferBlockNext(block); } *nparse -= index; /* Adjust nparse so it doesn't include matching chars */ if (index == slen) { TSDebug(DBG_TAG, "strfind: match for %s at position %d", pattern, *nparse); return STR_SUCCESS; } else if (index > 0) { TSDebug(DBG_TAG, "strfind: partial match for %s at position %d", pattern, *nparse); return STR_PARTIAL; } else { TSDebug(DBG_TAG, "strfind no match for %s", pattern); return STR_FAIL; } }
int ts_http_fetcher_parse_header(http_fetcher *fch) { int64_t avail, used; const char *start, *guard, *end; int ret; TSIOBufferBlock blk; blk = TSIOBufferReaderStart(fch->resp_reader); while (blk) { guard = start = TSIOBufferBlockReadStart(blk, fch->resp_reader, &avail); end = start + avail; ret = TSHttpHdrParseResp(fch->http_parser, fch->hdr_bufp, fch->hdr_loc, &start, end); switch (ret) { case TS_PARSE_ERROR: return -1; case TS_PARSE_DONE: used = start - guard; TSIOBufferCopy(fch->hdr_buffer, fch->resp_reader, used, 0); TSIOBufferReaderConsume(fch->resp_reader, used); ts_http_fetcher_extract(fch); // ts_http_fetcher_setup_filter(fch); fch->header_done = 1; return 0; default: TSIOBufferCopy(fch->hdr_buffer, fch->resp_reader, avail, 0); TSIOBufferReaderConsume(fch->resp_reader, avail); break; } blk = TSIOBufferBlockNext(blk); } return 0; }
int ts_dechunk_process(ts_dechunk_info *info, TSIOBufferReader readerp, TSIOBuffer bufp, int end) { int n; int64_t avail, need; int64_t blk_len; const char *start; const char *cur; TSIOBufferBlock blk, next_blk; blk = TSIOBufferReaderStart(readerp); while (blk) { next_blk = TSIOBufferBlockNext(blk); start = TSIOBufferBlockReadStart(blk, readerp, &blk_len); avail = blk_len; if (avail) { do { cur = start + blk_len - avail; switch (info->state) { case TS_DECHUNK_WAIT_LENGTH: n = ts_dechunk_extract_frag_len(info, cur, avail); if (n < 0) return -1; avail -= n; if (!info->dechunk_enabled) TSIOBufferCopy(bufp, readerp, n, 0); TSIOBufferReaderConsume(readerp, n); break; case TS_DECHUNK_WAIT_RETURN: n = ts_dechunk_extract_return(info, cur, avail, 0); avail -= n; if (!info->dechunk_enabled) TSIOBufferCopy(bufp, readerp, n, 0); TSIOBufferReaderConsume(readerp, n); break; case TS_DECHUNK_WAIT_DATA: if (info->frag_len + avail <= info->frag_total) { TSIOBufferCopy(bufp, readerp, avail, 0); TSIOBufferReaderConsume(readerp, avail); info->frag_len += avail; avail = 0; break; } else { need = info->frag_total - info->frag_len; if (need) { TSIOBufferCopy(bufp, readerp, need, 0); TSIOBufferReaderConsume(readerp, need); info->frag_len += need; avail -= need; } info->cr = 0; info->state = TS_DECHUNK_WAIT_RETURN_END; } break; case TS_DECHUNK_WAIT_RETURN_END: n = ts_dechunk_extract_return(info, cur, avail, 1); avail -= n; if (!info->dechunk_enabled) TSIOBufferCopy(bufp, readerp, n, 0); TSIOBufferReaderConsume(readerp, n); break; case TS_DECHUNK_DATA_DONE: if (!info->dechunk_enabled) TSIOBufferCopy(bufp, readerp, avail, 0); TSIOBufferReaderConsume(readerp, avail); avail = 0; info->done = 1; break; default: break; } } while (avail > 0); } if (info->done) break; blk = next_blk; } if (info->done) { return 1; } else if (end) { return -1; } else { return 0; } }
static int ts_http_fetcher_verify_chunked(http_fetcher *fch, int code) { int n; int64_t avail, need; int64_t blk_len; const char *start; const char *cur; TSIOBufferBlock blk, next_blk; chunked_info *ci = &fch->cinfo; blk = TSIOBufferReaderStart(fch->flow_reader); while (blk) { next_blk = TSIOBufferBlockNext(blk); start = TSIOBufferBlockReadStart(blk, fch->flow_reader, &blk_len); avail = blk_len; if (avail) { do { cur = start + blk_len - avail; switch (ci->state) { case CHUNK_WAIT_LENGTH: n = chunked_extract_frag_len(ci, cur, avail); if (n < 0) return BODY_ERROR; avail -= n; if (!ci->dechunk_enabled) TSIOBufferCopy(fch->body_buffer, fch->flow_reader, n, 0); TSIOBufferReaderConsume(fch->flow_reader, n); break; case CHUNK_WAIT_RETURN: n = chunked_extract_return(ci, cur, avail, 0); avail -= n; if (!ci->dechunk_enabled) TSIOBufferCopy(fch->body_buffer, fch->flow_reader, n, 0); TSIOBufferReaderConsume(fch->flow_reader, n); break; case CHUNK_WAIT_DATA: if (ci->frag_len + avail <= ci->frag_total) { TSIOBufferCopy(fch->body_buffer, fch->flow_reader, avail, 0); TSIOBufferReaderConsume(fch->flow_reader, avail); ci->frag_len += avail; avail = 0; break; } else { need = ci->frag_total - ci->frag_len; if (need) { TSIOBufferCopy(fch->body_buffer, fch->flow_reader, need, 0); TSIOBufferReaderConsume(fch->flow_reader, need); ci->frag_len += need; avail -= need; } ci->cr = 0; ci->state = CHUNK_WAIT_RETURN_END; } break; case CHUNK_WAIT_RETURN_END: n = chunked_extract_return(ci, cur, avail, 1); avail -= n; if (!ci->dechunk_enabled) TSIOBufferCopy(fch->body_buffer, fch->flow_reader, n, 0); TSIOBufferReaderConsume(fch->flow_reader, n); break; case CHUNK_DATA_DONE: if (!ci->dechunk_enabled) TSIOBufferCopy(fch->body_buffer, fch->flow_reader, avail, 0); TSIOBufferReaderConsume(fch->flow_reader, avail); avail = 0; ci->done = 1; break; default: break; } } while (avail > 0); } if (ci->done) break; blk = next_blk; } if (ci->done) { return BODY_COMPLETE; } else if (code == BODY_COMPLETE) { return BODY_ERROR; } else { return BODY_READY; } }
/*------------------------------------------------------------------------- strextract_ioreader Extract a string from an iobuffer. Start reading at position offset in iobuffer and extract until the string end_pattern is found. Input: reader reader on a iobuffer offset position to start reading end_pattern the termination string (nul terminated) Output: buffer if success, contains the extracted string, nul terminated buflen if success, contains the buffer length (excluding null char). Return Value: STR_SUCCESS if extraction successful STR_PARTIAL if extraction not yet completed STR_FAIL if extraction failed -------------------------------------------------------------------------*/ static int strextract_ioreader(TSIOBufferReader reader, int offset, const char *end_pattern, char *buffer, int *buflen) { int buf_idx = 0; int p_idx = 0; int nbytes_so_far = 0; int plen = strlen(end_pattern); const char *ptr; TSIOBufferBlock block = TSIOBufferReaderStart(reader); if (plen <= 0) { return STR_FAIL; } /* Now start extraction */ while ((block != NULL) && (p_idx < plen) && (buf_idx < PSI_FILENAME_MAX_SIZE)) { int64_t blocklen; const char *blockptr = TSIOBufferBlockReadStart(block, reader, &blocklen); for (ptr = blockptr; ptr < blockptr + blocklen; ptr++, nbytes_so_far++) { if (nbytes_so_far >= offset) { /* Add a new character to the filename */ buffer[buf_idx++] = *ptr; /* If we have reach the end of the filename, we're done */ if (end_pattern[p_idx] == *ptr) { p_idx++; if (p_idx == plen) { break; } } else { p_idx = 0; } /* The filename is too long, something is fishy... let's abort extraction */ if (buf_idx >= PSI_FILENAME_MAX_SIZE) { break; } } } block = TSIOBufferBlockNext(block); } /* Error, could not read end of filename */ if (buf_idx >= PSI_FILENAME_MAX_SIZE) { TSDebug(DBG_TAG, "strextract: filename too long"); *buflen = 0; return STR_FAIL; } /* Full Match */ if (p_idx == plen) { /* Nul terminate the filename, remove the end_pattern copied into the buffer */ *buflen = buf_idx - plen; buffer[*buflen] = '\0'; TSDebug(DBG_TAG, "strextract: filename = |%s|", buffer); return STR_SUCCESS; } /* End of filename not yet reached we need to read some more data */ else { TSDebug(DBG_TAG, "strextract: partially extracted filename"); *buflen = buf_idx - p_idx; return STR_PARTIAL; } }
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; }
/** * 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; ib_filter_ctx *fctx = ibd->data; ib_txn_ctx *data = TSContDataGet(contp); TSVIO input_vio = TSVConnWriteVIOGet(contp); TSIOBuffer in_buf = TSVIOBufferGet(input_vio); /* Test whether we're going into an errordoc */ if (IB_HTTP_CODE(data->status)) { /* We're going to an error document, * so we discard all this data */ TSDebug("ironbee", "Status is %d, discarding", data->status); ibd->data->buffering = IOBUF_DISCARD; } /* Test for EOS */ if (in_buf == NULL) { /* flush anything we have buffered. This is final! */ flush_data(fctx, -1, 1); return; } ntodo = TSVIONTodoGet(input_vio); /* Test for first time, and initialise. */ if (!fctx->output_buffer) { fctx->output_buffer = TSIOBufferCreate(); ib_mm_register_cleanup(data->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, TSVIONBytesGet(input_vio)); fctx->buffer = TSIOBufferCreate(); ib_mm_register_cleanup(data->tx->mm, (ib_mm_cleanup_fn_t) TSIOBufferDestroy, (void*) fctx->buffer); fctx->reader = TSIOBufferReaderAlloc(fctx->buffer); /* Get the buffering config */ if (!IB_HTTP_CODE(data->status)) { buffer_init(ibd, data->tx); } /* Do we still have to delay feeding the first data to Ironbee * to keep the IB events in their proper order? * * Appears maybe not, so let's do nothing until it shows signs of breakage. */ #if BUFFER_FIRST /* First time through we can only buffer data until headers are sent. */ fctx->first_time = 1; input_reader = TSVIOReaderGet(input_vio); fctx->buffered = TSIOBufferCopy(fctx->buffer, TSVIOReaderGet(input_vio), ntodo, 0); TSIOBufferReaderConsume(input_reader, fctx->buffered); /* Do we need to request more input or just continue? */ TSVIONDoneSet(input_vio, fctx->buffu`ered + fctx->bytes_done); TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); return; #endif } /* second time through we have to feed already-buffered data through * ironbee while retaining it in buffer. Regardless of what else happens. */ #if BUFFER_FIRST if (fctx->first_time) { fctx->first_time = 0; for (block = TSIOBufferStart(fctx->buffer); block != NULL; block = TSIOBufferBlockNext(block)) { //nbytes = TSIOBufferBlockDataSizeGet(block); /* FIXME - do this without a reader ? */ buf = TSIOBufferBlockReadStart(block, fctx->reader, &nbytes); //rc = examine_data_chunk(ibd->ibd, data->tx, buf, nbytes); rc = (*ibd->ibd->ib_notify_body)(data->tx->ib, data->tx, buf, nbytes); if (rc != IB_OK) { // FIXME ??? } } } #endif /* Test for EOS */ if (ntodo == 0) { TSDebug("[ironbee]", "ntodo zero before consuming data"); /* 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)(data->tx->ib, data->tx, buf, nbytes); if (rc != IB_OK) { // FIXME ??? } rc = buffer_data_chunk(fctx, input_reader, nbytes); if (rc != IB_OK) { // FIXME ??? } TSIOBufferReaderConsume(input_reader, nbytes); TSVIONDoneSet(input_vio, TSVIONDoneGet(input_vio) + nbytes); } ntodo = TSVIONTodoGet(input_vio); if (ntodo == 0) { TSDebug("[ironbee]", "ntodo zero after consuming data"); /* 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); } }