Пример #1
0
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;
}
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;
}
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);
  }
}
Пример #4
0
/*-------------------------------------------------------------------------
  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;
  }
}
Пример #5
0
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;
}
Пример #6
0
static void process_hdr(ib_txn_ctx *data, TSHttpTxn txnp,
                        ironbee_direction *ibd)
{
  ib_conndata_t icdata;
  int rv;
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  TSIOBuffer iobufp;
  TSIOBufferReader readerp;
  TSIOBufferBlock blockp;
  int64_t len;

  TSDebug("ironbee", "process %s headers\n", ibd->word);

  icdata.ib = ironbee;
  icdata.mp = data->ssn->iconn->mp;
  icdata.conn = data->ssn->iconn;

  /* before the HTTP headers comes the request line / response code */
  rv = (*ibd->hdr_get)(txnp, &bufp, &hdr_loc);
  if (rv) {
    TSError ("couldn't retrieve %s header: %d\n", ibd->word, rv);
    return;
  }

  /* Get the data into an IOBuffer so we can access them! */
  //iobufp = TSIOBufferSizedCreate(...);
  iobufp = TSIOBufferCreate();
  TSHttpHdrPrint(bufp, hdr_loc, iobufp);

  readerp = TSIOBufferReaderAlloc(iobufp);
  blockp = TSIOBufferReaderStart(readerp);

  len = TSIOBufferBlockReadAvail(blockp, readerp);
  icdata.data = (void*)TSIOBufferBlockReadStart(blockp, readerp, &len);
  icdata.dlen = icdata.dalloc = len;

  (*ibd->ib_notify)(ironbee, &icdata);

  TSIOBufferDestroy(iobufp);
  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
}
Пример #7
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;
}
Пример #8
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;
    }
}
Пример #9
0
/**
 * 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);
    }
}
Пример #10
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 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;
}
Пример #12
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
handle_buffering(TSCont contp, JCrusherData * data)
{
  TSVIO upstream_vio;
  TSIOBuffer upstream_buffer;
  int64_t toread;
  int64_t avail;

  TSDebug("jcrusher", "Start of handle_buffering()");
  /* 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. */
  upstream_vio = TSVConnWriteVIOGet(contp);

  /* Create the output buffer and its associated reader */
  if (!data->downstream_buffer) {
    data->downstream_buffer = TSIOBufferCreate();
    TSAssert(data->downstream_buffer);
    data->downstream_reader = TSIOBufferReaderAlloc(data->downstream_buffer);
    TSAssert(data->downstream_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. */

  upstream_buffer = TSVIOBufferGet(upstream_vio);
  if (NULL == upstream_buffer) {
    data->state = STATE_OUTPUT_DATA;
    TSDebug("jcrusher", "handle_buffering - upstream_buffer is NULL");
    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. */

  toread = TSVIONTodoGet(upstream_vio);
  TSDebug("jcrusher", "handle_buffering - toread is %" PRId64, toread);
  if (toread > 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(upstream_vio));
    if (toread > avail) {
      toread = avail;
    }
    TSDebug("jcrusher", "handle_buffering - toread is %" PRId64, toread);
    TSDebug("jcrusher", "handle_buffering - avail is %" PRId64, avail);

    TSIOBufferReader upstream_reader = TSVIOReaderGet(upstream_vio);
    TSIOBufferBlock upstream_blk = TSIOBufferReaderStart(upstream_reader);
    const char *input = TSIOBufferBlockReadStart(upstream_blk, upstream_reader, &toread);
    TSDebug("jcrusher", "handle_buffering - just read [%d] bytes from buffer", (int)strlen(input));

    TSDebug("jcrusher", "handle_buffering - parse json input");
    data->json_obj = json_tokener_parse_ex(data->json_tok, input, strlen(input));
    if (json_tokener_success == (data->json_err = json_tokener_get_error(data->json_tok))) {
      TSDebug("jcrusher", "handle_buffering - got json_tokener_success");
      data->state = STATE_OUTPUT_DATA;
      /* Call back the write VIO continuation to let it know that we
         have completed the write operation. */
      TSContCall(TSVIOContGet(upstream_vio), TS_EVENT_VCONN_WRITE_COMPLETE, upstream_vio);
      return 1;
    }
    TSDebug("jcrusher", "handle_buffering - got json_tokener_continue");

    /* Tell the read buffer that we have read the data and are no
       longer interested in it. */
    TSIOBufferReaderConsume(TSVIOReaderGet(upstream_vio), toread);

    /* Modify the upstream VIO to reflect how much data we've
       completed. */
    TSVIONDoneSet(upstream_vio, TSVIONDoneGet(upstream_vio) + toread);

    /* Now we check the upstream VIO to see if there is data left to read. */
    /* Call back the upstream VIO continuation to let it know that we
       are ready for more data. */
    TSContCall(TSVIOContGet(upstream_vio), TS_EVENT_VCONN_WRITE_READY, upstream_vio);
  } else {
    TSDebug("jcrusher", "handle_buffering - seems we read all");
    data->state = STATE_OUTPUT_DATA;
    /* Call back the write VIO continuation to let it know that we
       have completed the write operation. */
    TSContCall(TSVIOContGet(upstream_vio), TS_EVENT_VCONN_WRITE_COMPLETE, upstream_vio);
  }

  TSDebug("jcrusher", "handle_buffering - End");
  return 1;
}
Пример #14
0
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);
  }
}
Пример #15
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;
    }
}
Пример #16
0
/*-------------------------------------------------------------------------
  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;
  }
}
Пример #17
0
/**
 * 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);
    }
}