예제 #1
0
void *
remove_from_queue(Queue *q)
{
  void *data = NULL;
  Cell *remove_cell;

  TSMutexLock(q->mutex);
  if (q->nb_elem > 0) {
    remove_cell = q->head;
    TSAssert(remove_cell->magic == MAGIC_ALIVE);

    data = remove_cell->ptr_data;
    q->head = remove_cell->ptr_prev;
    if (q->head == NULL) {
      TSAssert(q->nb_elem == 1);
      q->tail = NULL;
    } else {
      TSAssert(q->head->magic == MAGIC_ALIVE);
      q->head->ptr_next = NULL;
    }

    remove_cell->magic = MAGIC_DEAD;
    TSfree(remove_cell);
    q->nb_elem--;
  }
  TSMutexUnlock(q->mutex);
  return data;
}
예제 #2
0
void
add_to_queue(Queue *q, void *data)
{
  Cell *new_cell;
  int n;

  if (data != NULL) {
    TSMutexLock(q->mutex);
    /* Init the new cell */
    new_cell = TSmalloc(sizeof(Cell));
    new_cell->magic = MAGIC_ALIVE;
    new_cell->ptr_data = data;
    new_cell->ptr_next = q->tail;
    new_cell->ptr_prev = NULL;

    /* Add this new cell to the queue */
    if (q->tail == NULL) {
      TSAssert(q->head == NULL);
      TSAssert(q->nb_elem == 0);
      q->tail = new_cell;
      q->head = new_cell;
    } else {
      TSAssert(q->tail->magic == MAGIC_ALIVE);
      q->tail->ptr_prev = new_cell;
      q->tail = new_cell;
    }
    n = q->nb_elem++;
    TSMutexUnlock(q->mutex);

    if (n > MAX_JOBS_ALARM) {
      TSError("[thread_pool] Warning:Too many jobs in plugin thread pool queue (%d). Maximum allowed is %d", n, MAX_JOBS_ALARM);
    }
  }
}
예제 #3
0
/*-------------------------------------------------------------------------
  wake_up_streams
  Send an event to the upstream vconnection to either
    - ask for more data
    - let it know we're done
  Reenable the downstream vconnection
  Input:
    contp      continuation for the current transaction
  Output :
  Return Value:
   0 if failure
   1 if success
  -------------------------------------------------------------------------*/
static int
wake_up_streams(TSCont contp)
{
  TSVIO input_vio;
  ContData *data;
  int ntodo;

  data = TSContDataGet(contp);
  TSAssert(data->magic == MAGIC_ALIVE);

  input_vio = TSVConnWriteVIOGet(contp);
  ntodo = TSVIONTodoGet(input_vio);

  if (ntodo > 0) {
    TSVIOReenable(data->output_vio);
    TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio);
  } else {
    TSDebug(DBG_TAG, "Total bytes produced by transform = %d", data->transform_bytes);
    TSVIONBytesSet(data->output_vio, data->transform_bytes);
    TSVIOReenable(data->output_vio);
    TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio);
  }

  return 1;
}
예제 #4
0
/*-------------------------------------------------------------------------
  cont_data_destroy
  Deallocate ContData structure associated to a transaction

  Input:
    data   structure to deallocate
  Output:
  Return Value:
    none
  -------------------------------------------------------------------------*/
static void
cont_data_destroy(ContData * data)
{
  TSDebug(DBG_TAG, "Destroying continuation data");
  if (data) {
    TSAssert(data->magic == MAGIC_ALIVE);
    if (data->output_reader) {
      TSIOBufferReaderFree(data->output_reader);
      data->output_reader = NULL;
    }
    if (data->output_buffer) {
      TSIOBufferDestroy(data->output_buffer);
      data->output_buffer = NULL;
    }
    if (data->psi_reader) {
      TSIOBufferReaderFree(data->psi_reader);
      data->psi_reader = NULL;
    }
    if (data->psi_buffer) {
      TSIOBufferDestroy(data->psi_buffer);
      data->psi_buffer = NULL;
    }
    data->magic = MAGIC_DEAD;
    TSfree(data);
  }
}
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;
}
/* handle_hook
 * Fires on TS_EVENT_HTTP_READ_REQUEST_HDR events, gets the effectiveUrl
 * finds the host, gets the generation ID, gen_id, for the host
 * and runs TSCacheUrlSet to change the cache key for the read
 */
static int
handle_hook(TSCont *contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp = (TSHttpTxn)edata;
  char *url = NULL, *host = NULL;
  int url_length;
  int gen_id;
  int ok = 1;

  switch (event) {
  case TS_EVENT_HTTP_READ_REQUEST_HDR:
    TSDebug(PLUGIN_NAME, "handling TS_EVENT_HTTP_READ_REQUEST_HDR");

    if (ok) {
      url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_length);
      if (!url) {
        TSError("[%s] could not retrieve request url", PLUGIN_NAME);
        ok = 0;
      }
    }

    if (ok) {
      get_genid_host(&host, url);
      if (!host) {
        TSError("[%s] could not retrieve request host", PLUGIN_NAME);
        ok = 0;
      }
    }

    if (ok) {
      TSDebug(PLUGIN_NAME, "From url (%s) discovered host (%s)", url, host);
      if ((gen_id = get_genid(host)) != 0) {
        if (TSHttpTxnConfigIntSet(txnp, TS_CONFIG_HTTP_CACHE_GENERATION, gen_id) != TS_SUCCESS) {
          TSDebug(PLUGIN_NAME, "Error, unable to modify cache url");
          TSError("[%s] Unable to set cache generation for %s to %d", PLUGIN_NAME, url, gen_id);
          ok = 0;
        }
      }
    }

    /* Clean up */
    if (url) {
      TSfree(url);
    }
    if (host) {
      TSfree(host);
    }
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
    break;

  default:
    TSAssert(!"Unexpected event");
    ok = 0;
    break;
  }

  return ok;
}
예제 #7
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;
}
예제 #8
0
static int handle_hook(TSCont contp, TSEvent event, void *edata) {
    TSHttpTxn txnp = (TSHttpTxn) edata;
    pr_list *prl;
    int ok = 1;

    prl = (pr_list *)TSContDataGet(contp);

    switch (event) {
        case TS_EVENT_HTTP_READ_REQUEST_HDR:
            ok = rewrite_cacheurl(prl, txnp);
            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
            break;
        default:
            TSAssert(!"Unexpected event");
            ok = 0;
            break;
    }

    return ok;
}
예제 #9
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;
}
예제 #10
0
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;
}
예제 #11
0
/*-------------------------------------------------------------------------
  transform_handler
  Handler for all events received during the transformation process

  Input:
    contp      continuation for the current transaction
    event      event received
    data       pointer on optional data
  Output :
  Return Value:
  -------------------------------------------------------------------------*/
static int
transform_handler(TSCont contp, TSEvent event, void *edata)
{
  TSVIO input_vio;
  ContData *data;
  int state, retval;

  /* This section will be called by both TS internal
     and the thread. Protect it with a mutex to avoid
     concurrent calls. */

  /* Handle TryLock result */
  if (TSMutexLockTry(TSContMutexGet(contp)) != TS_SUCCESS) {
    TSCont c = TSContCreate(trylock_handler, NULL);
    TryLockData *d = TSmalloc(sizeof(TryLockData));

    d->contp = contp;
    d->event = event;
    TSContDataSet(c, d);
    TSContSchedule(c, 10, TS_THREAD_POOL_DEFAULT);
    return 1;
  }

  data = TSContDataGet(contp);
  TSAssert(data->magic == MAGIC_ALIVE);

  state = data->state;

  /* Check to see if the transformation has been closed */
  retval = TSVConnClosedGet(contp);
  if (retval) {
    /* If the thread is still executing its job, we don't want to destroy
       the continuation right away as the thread will call us back
       on this continuation. */
    if (state == STATE_READ_PSI) {
      TSContSchedule(contp, 10, TS_THREAD_POOL_DEFAULT);
    } else {
      TSMutexUnlock(TSContMutexGet(contp));
      cont_data_destroy(TSContDataGet(contp));
      TSContDestroy(contp);
      return 1;
    }
  } else {
    switch (event) {
    case TS_EVENT_ERROR:
      input_vio = TSVConnWriteVIOGet(contp);
      TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio);
      break;

    case TS_EVENT_VCONN_WRITE_COMPLETE:
      TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);
      break;

    case TS_EVENT_VCONN_WRITE_READY:
      /* downstream vconnection is done reading data we've write into it.
         let's read some more data from upstream if we're in read state. */
      if (state == STATE_READ_DATA) {
        handle_transform(contp);
      }
      break;

    case TS_EVENT_IMMEDIATE:
      if (state == STATE_READ_DATA) {
        /* upstream vconnection signals some more data ready to be read
           let's try to transform some more data */
        handle_transform(contp);
      } else if (state == STATE_DUMP_PSI) {
        /* The thread scheduled an event on our continuation to let us
           know it has completed its job
           Let's dump the include content to the output vconnection */
        dump_psi(contp);
        wake_up_streams(contp);
      }
      break;

    default:
      TSAssert(!"Unexpected event");
      break;
    }
  }

  TSMutexUnlock(TSContMutexGet(contp));
  return 1;
}
예제 #12
0
/*-------------------------------------------------------------------------
  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;
}
예제 #13
0
/*-------------------------------------------------------------------------
  psi_include
  Read file to include. Copy its content into an iobuffer.

  This is the function doing blocking calls and called by the plugin's threads

  Input:
    data      continuation for the current transaction
  Output :
    data->psi_buffer  contains the file content
    data->psi_sucess  0 if include failed, 1 if success
  Return Value:
    0  if failure
    1  if success
  -------------------------------------------------------------------------*/
static int
psi_include(TSCont contp, void *edata)
{
#define BUFFER_SIZE 1024
  ContData *data;
  TSFile filep;
  char buf[BUFFER_SIZE];
  char inc_file[PSI_PATH_MAX_SIZE + PSI_FILENAME_MAX_SIZE];

  /* We manipulate plugin continuation data from a separate thread.
     Grab mutex to avoid concurrent access */
  TSMutexLock(TSContMutexGet(contp));
  data = TSContDataGet(contp);
  TSAssert(data->magic == MAGIC_ALIVE);

  if (!data->psi_buffer) {
    data->psi_buffer = TSIOBufferCreate();
    data->psi_reader = TSIOBufferReaderAlloc(data->psi_buffer);
  }

  /* For security reason, we do not allow to include files that are
     not in the directory <plugin_path>/include.
     Also include file cannot contain any path. */
  sprintf(inc_file, "%s/%s", psi_directory, _basename(data->psi_filename));

  /* Read the include file and copy content into iobuffer */
  if ((filep = TSfopen(inc_file, "r")) != NULL) {
    TSDebug(DBG_TAG, "Reading include file %s", inc_file);

    while (TSfgets(filep, buf, BUFFER_SIZE) != NULL) {
      TSIOBufferBlock block;
      int64_t len, avail, ndone, ntodo, towrite;
      char *ptr_block;

      len = strlen(buf);
      ndone = 0;
      ntodo = len;
      while (ntodo > 0) {
        /* TSIOBufferStart allocates more blocks if required */
        block = TSIOBufferStart(data->psi_buffer);
        ptr_block = TSIOBufferBlockWriteStart(block, &avail);
        towrite = MIN(ntodo, avail);

        memcpy(ptr_block, buf + ndone, towrite);
        TSIOBufferProduce(data->psi_buffer, towrite);
        ntodo -= towrite;
        ndone += towrite;
      }
    }
    TSfclose(filep);
    data->psi_success = 1;
    if (log) {
      TSTextLogObjectWrite(log, "Successfully included file: %s", inc_file);
    }
  } else {
    data->psi_success = 0;
    if (log) {
      TSTextLogObjectWrite(log, "Failed to include file: %s", inc_file);
    }
  }

  /* Change state and schedule an event EVENT_IMMEDIATE on the plugin continuation
     to let it know we're done. */

  /* Note: if the blocking call was not in the transformation state (i.e. in
     TS_HTTP_READ_REQUEST_HDR, TS_HTTP_OS_DNS and so on...) we could
     use TSHttpTxnReenable to wake up the transaction instead of sending an event. */

  TSContSchedule(contp, 0, TS_THREAD_POOL_DEFAULT);
  data->psi_success = 0;
  data->state = STATE_READ_DATA;
  TSMutexUnlock(TSContMutexGet(contp));

  return 0;
}
예제 #14
0
/*-------------------------------------------------------------------------
  parse_data
  Search for psi filename in the data.

  Input:
    contp   continuation for the current transaction
    reader  reader on the iobuffer that contains data
    avail   amount of data available in the iobuffer
  Output:
    towrite    amount of data in the iobuffer that can be written
               to the downstream vconnection
    toconsume  amount of data in the iobuffer to consume
  Return Value:
    0   if no psi filename found
    1  if a psi filename was found
  -------------------------------------------------------------------------*/
static int
parse_data(TSCont contp, TSIOBufferReader input_reader, int avail, int *toconsume, int *towrite)
{
  ContData *data;
  int nparse = 0;
  int status;

  data = TSContDataGet(contp);
  TSAssert(data->magic == MAGIC_ALIVE);

  if (data->parse_state == PARSE_SEARCH) {

    /* Search for the start pattern */
    status = strsearch_ioreader(input_reader, PSI_START_TAG, &nparse);
    switch (status) {
    case STR_FAIL:
      /* We didn't found the pattern */
      *toconsume = avail;
      *towrite = avail;
      data->parse_state = PARSE_SEARCH;
      return 0;
    case STR_PARTIAL:
      /* We need to read some more data */
      *toconsume = nparse;
      *towrite = nparse;
      data->parse_state = PARSE_SEARCH;
      return 0;
    case STR_SUCCESS:
      /* We found the start_pattern, let's go ahead */
      data->psi_filename_len = 0;
      data->psi_filename[0] = '\0';
      data->parse_state = PARSE_EXTRACT;
      break;
    default:
      TSAssert(!"strsearch_ioreader returned unexpected status");
    }
  }


  /* And now let's extract the filename */
  status = strextract_ioreader(input_reader, nparse + PSI_START_TAG_LEN,
                               PSI_END_TAG, data->psi_filename, &data->psi_filename_len);
  switch (status) {
  case STR_FAIL:
    /* We couldn't extract a valid filename */
    *toconsume = nparse;
    *towrite = nparse;
    data->parse_state = PARSE_SEARCH;
    return 0;
  case STR_PARTIAL:
    /* We need to read some more data */
    *toconsume = nparse;
    *towrite = nparse;
    data->parse_state = PARSE_EXTRACT;
    return 0;
  case STR_SUCCESS:
    /* We got a valid filename */
    *toconsume = nparse + PSI_START_TAG_LEN + data->psi_filename_len + PSI_END_TAG_LEN;
    *towrite = nparse;
    data->parse_state = PARSE_SEARCH;
    return 1;
  default:
    TSAssert(!"strextract_ioreader returned bad status");
  }

  return 0;
}
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;
}