예제 #1
0
/*-------------------------------------------------------------------------
  getNextValue

  Extract commas separated items from a string "elem1, elem2, ..., elemN"
  Returns pointer and size of the next item.
  -------------------------------------------------------------------------*/
char *
getNextValue(const char *list, char **offset)
{
  char *begin;
  char *end;
  char *ret;
  int len;

  if (list == NULL) {
    return NULL;
  }

  /* start after the last return item */
  begin = (char *) *offset;

  /* skip blanks before the item */
  while ((*begin == ' ') && (*begin != '\0')) {
    begin++;
  }

  if (*begin == '\0') {
    return NULL;
  }

  end = begin;
  len = 0;

  while ((*end != ',') && (*end != ' ') && (*end != '\0')) {
    end++;
    len++;
  }

  /* Make offset point after the item last character */
  if (*end != '\0') {
    *offset = (char *) (end + 1);
  } else {
    *offset = end;
  }

  ret = INKmalloc(len + 1);
  memcpy(ret, begin, len);
  ret[len] = '\0';

  return ret;
}
예제 #2
0
HdrInfo_T *
initHdr()
{
  HdrInfo_T *tmpHdr = NULL;

  tmpHdr = (HdrInfo_T *) INKmalloc(sizeof(HdrInfo_T));

  tmpHdr->httpType = INK_HTTP_TYPE_UNKNOWN;
  tmpHdr->hdrLength = 0;
  tmpHdr->httpVersion = 0;

  tmpHdr->httpMethod = NULL;
  tmpHdr->hostName = NULL;

  tmpHdr->hdrReason = NULL;

  return tmpHdr;
}
예제 #3
0
int
get_client_req(INKHttpTxn txnp, char **p_url_line, char **p_client_req, int *neg_test_val, int *pin_val)
{
  INKMBuffer bufp;
  INKReturnCode ret_code;
  INKMLoc hdr_loc;
  INKMLoc url_loc;
  INKMLoc neg_loc;
  INKMLoc pin_loc;

  INKIOBuffer output_buffer;
  INKIOBufferReader reader;
  int total_avail;

  INKIOBufferBlock block;
  const char *block_start;
  int block_avail;

  char *output_string;
  char *url_str;
  int url_len;
  int output_len;

  LOG_SET_FUNCTION_NAME("get_client_req");

  *p_url_line = NULL;
  *p_client_req = NULL;
  *neg_test_val = 0;
  *pin_val = 0;

#ifdef DEBUG
  if (INKHttpTxnClientReqGet(NULL, &bufp, &hdr_loc) != 0) {
    LOG_ERROR_NEG("INKHttpTxnClientReqGet(null, buf, hdr_loc)");
  }

  if (INKHttpTxnClientReqGet(txnp, NULL, &hdr_loc) != 0) {
    LOG_ERROR_NEG("INKHttpTxnClientReqGet(txnp, null, hdr_loc)");
  }

  if (INKHttpTxnClientReqGet(txnp, &bufp, NULL) != 0) {
    LOG_ERROR_NEG("INKHttpTxnClientReqGet(txnp, buf, null)");
  }
#endif

  if (!INKHttpTxnClientReqGet(txnp, &bufp, &hdr_loc)) {
    LOG_ERROR_AND_RETURN("INKHttpTxnClientReqGet");
  }

  if ((url_loc = INKHttpHdrUrlGet(bufp, hdr_loc)) == INK_ERROR_PTR) {
    INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
    LOG_ERROR_AND_RETURN("INKHttpHdrUrlGet");
  }
#ifdef DEBUG
  if (INKUrlStringGet(NULL, url_loc, &url_len) != INK_ERROR_PTR) {
    LOG_ERROR_NEG("INKUrlStringGet(NULL, url_loc, &int)");
  }

  if (INKUrlStringGet(bufp, NULL, &url_len) != INK_ERROR_PTR) {
    LOG_ERROR_NEG("INKUrlStringGet(bufp, NULL, &int)");
  }
#endif

  if ((url_str = INKUrlStringGet(bufp, url_loc, &url_len)) == INK_ERROR_PTR) {
    INKHandleMLocRelease(bufp, hdr_loc, url_loc);
    INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
    LOG_ERROR_AND_RETURN("INKUrlStringGet");
  }

  if (INKHandleMLocRelease(bufp, hdr_loc, url_loc) == INK_ERROR) {
    INKfree(url_str);
    INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
    LOG_ERROR_AND_RETURN("INKHandleMLocRelease");
  }

  url_str[url_len] = '\0';
  *p_url_line = url_str;

  if ((output_buffer = INKIOBufferCreate()) == INK_ERROR_PTR) {
    INKfree(url_str);
    INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
    LOG_ERROR_AND_RETURN("INKIOBufferCreate");
  }
#ifdef DEBUG
  if (INKIOBufferReaderAlloc(NULL) != INK_ERROR_PTR) {
    LOG_ERROR_NEG("INKIOBufferReaderAlloc(NULL)");
  }
#endif

  if ((reader = INKIOBufferReaderAlloc(output_buffer)) == INK_ERROR_PTR) {
    INKfree(url_str);
    INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
    INKIOBufferDestroy(output_buffer);
    LOG_ERROR_AND_RETURN("INKIOBufferReaderAlloc");
  }

  /* This will print  just MIMEFields and not
     the http request line */
  if (INKMimeHdrPrint(bufp, hdr_loc, output_buffer) == INK_ERROR) {
    INKfree(url_str);
    INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
    INKIOBufferDestroy(output_buffer);
    LOG_ERROR_AND_RETURN("INKMimeHdrPrint");
  }

  if ((neg_loc = INKMimeHdrFieldFind(bufp, hdr_loc, "CacheTester-HostNameSet", -1)) == INK_ERROR_PTR) {
    INKfree(url_str);
    INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
    INKIOBufferDestroy(output_buffer);
    LOG_ERROR_AND_RETURN("INKMimeHdrFieldFind");
  }

  if (neg_loc != NULL) {

    ret_code = INKMimeHdrFieldValueIntGet(bufp, hdr_loc, neg_loc, 0, neg_test_val);
    if (ret_code == INK_ERROR) {
      INKfree(url_str);
      INKHandleMLocRelease(bufp, hdr_loc, neg_loc);
      INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
      INKIOBufferDestroy(output_buffer);
      LOG_ERROR_AND_RETURN("INKMimeHdrFieldValueIntGet");
    }

    if (INKHandleMLocRelease(bufp, hdr_loc, neg_loc) == INK_ERROR) {
      INKfree(url_str);
      INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
      INKIOBufferDestroy(output_buffer);
      LOG_ERROR_AND_RETURN("INKHandleMLocRelease");
    }
  }

  if ((pin_loc = INKMimeHdrFieldFind(bufp, hdr_loc, "CacheTester-Pin", -1)) == INK_ERROR_PTR) {
    INKfree(url_str);
    INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
    INKIOBufferDestroy(output_buffer);
    LOG_ERROR_AND_RETURN("INKMimeHdrFieldFind");
  }

  if (pin_loc != NULL) {

    ret_code = INKMimeHdrFieldValueIntGet(bufp, hdr_loc, pin_loc, 0, pin_val);
    if (ret_code == INK_ERROR) {
      INKfree(url_str);
      INKHandleMLocRelease(bufp, hdr_loc, pin_loc);
      INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
      INKIOBufferDestroy(output_buffer);
      LOG_ERROR_AND_RETURN("INKMimeHdrFieldValueIntGet");
    }

    if (INKHandleMLocRelease(bufp, hdr_loc, pin_loc) == INK_ERROR) {
      INKfree(url_str);
      INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
      INKIOBufferDestroy(output_buffer);
      LOG_ERROR_AND_RETURN("INKHandleMLocRelease");
    }
  }

  if (INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc) == INK_ERROR) {
    INKfree(url_str);
    INKIOBufferDestroy(output_buffer);
    LOG_ERROR_AND_RETURN("INKHandleMLocRelease");
  }

  /* 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 */
  if ((total_avail = INKIOBufferReaderAvail(reader)) == INK_ERROR) {
    INKfree(url_str);
    INKIOBufferDestroy(output_buffer);
    LOG_ERROR_AND_RETURN("INKIOBufferReaderAvail");
  }
#ifdef DEBUG
  if (INKIOBufferReaderAvail(NULL) != INK_ERROR) {
    LOG_ERROR_NEG("INKIOBufferReaderAvail(NULL)");
  }
#endif

  /* Allocate the string with an extra byte for the string
     terminator */
  output_string = (char *) INKmalloc(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 = INKIOBufferReaderStart(reader);

  if (block == INK_ERROR_PTR) {
    INKfree(url_str);
    INKfree(output_string);
    LOG_ERROR_AND_RETURN("INKIOBufferReaderStart");
  }
#ifdef DEBUG
  if (INKIOBufferReaderStart(NULL) != INK_ERROR_PTR) {
    LOG_ERROR_NEG("INKIOBufferReaderStart(NULL)");
  }

  if (INKIOBufferBlockReadStart(NULL, reader, &block_avail) != INK_ERROR_PTR) {
    LOG_ERROR_NEG("INKIOBufferBlockReadStart(null, reader, &int)");
  }

  if (INKIOBufferBlockReadStart(block, NULL, &block_avail) != INK_ERROR_PTR) {
    LOG_ERROR_NEG("INKIOBufferBlockReadStart(block, null, &int)");
  }
#endif

  while (block) {

    block_start = INKIOBufferBlockReadStart(block, reader, &block_avail);

    if (block_start == INK_ERROR_PTR) {
      INKfree(url_str);
      INKfree(output_string);
      LOG_ERROR_AND_RETURN("INKIOBufferBlockReadStart");
    }

    /* FC paranoia: make sure we don't copy more bytes than buffer size can handle */
    if ((output_len + block_avail) > total_avail) {
      INKfree(url_str);
      INKfree(output_string);
      LOG_ERROR_AND_RETURN("More bytes than expected in IOBuffer");
    }

    /* 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 */
    if (INKIOBufferReaderConsume(reader, block_avail) == INK_ERROR) {
      INKfree(url_str);
      INKfree(output_string);
      LOG_ERROR_AND_RETURN("INKIOBufferReaderConsume");
    }
#ifdef DEBUG
    if (INKIOBufferReaderConsume(NULL, block_avail) != INK_ERROR) {
      LOG_ERROR_NEG("INKIOBufferReaderConsume(null, int)");
    }

    if (INKIOBufferReaderConsume(reader, -1) != INK_ERROR) {
      LOG_ERROR_NEG("INKIOBufferReaderConsume(reader, -1)");
    }
#endif

    /* Get the next block now that we've consumed the
       data off the last block */
    if ((block = INKIOBufferReaderStart(reader)) == INK_ERROR_PTR) {
      INKfree(url_str);
      INKfree(output_string);
      LOG_ERROR_AND_RETURN("INKIOBufferReaderStart");
    }
  }

  /* Terminate the string */
  output_string[output_len] = '\0';
  /*output_len++; */

  /* Free up the INKIOBuffer that we used to print out the header */
  if (INKIOBufferReaderFree(reader) == INK_ERROR) {
    INKfree(url_str);
    INKfree(output_string);
    LOG_ERROR_AND_RETURN("INKIOBufferReaderFree");
  }
#ifdef DEBUG
  if (INKIOBufferReaderFree(NULL) != INK_ERROR) {
    LOG_ERROR_NEG("INKIOBufferReaderFree(NULL)");
  }
#endif

  if (INKIOBufferDestroy(output_buffer) == INK_ERROR) {
    INKfree(url_str);
    INKfree(output_string);
    LOG_ERROR_AND_RETURN("INKIOBufferDestroy");
  }

  *p_client_req = output_string;
  return 1;
}
예제 #4
0
void
cache_exercise(INKHttpTxn txnp, char *url, int pin_val, int hostname_set, INKCont cache_handler_cont)
{
  INKCacheKey cache_key;
  CACHE_URL_DATA *url_data;
  int cache_ready;
  char *pchar;
  char hostname[MAX_URL_LEN];

  LOG_SET_FUNCTION_NAME("cache_exercise");

  pchar = strstr(url, "://");
  if (pchar == NULL) {
    pchar = url;
  } else {
    pchar += 3;
  }

  strncpy(hostname, pchar, MAX_URL_LEN - 1);

  pchar = strstr(hostname, "/");
  if (pchar != NULL) {
    *pchar = '\0';
  }

  if (INKCacheReady(&cache_ready) == INK_ERROR) {
    LOG_ERROR_AND_REENABLE("INKCacheReady");
    return;
  }
#ifdef DEBUG
  /*INKDebug(DEBUG_TAG, "Starting Negative Test for INKCacheReady"); */
  if (INKCacheReady(NULL) != INK_ERROR) {
    LOG_ERROR_NEG("INKCacheReady(NULL)");
  }
  /*INKDebug(DEBUG_TAG, "Done Negative Test for INKCacheReady"); */
#endif

  if (cache_ready == 0) {
    INKDebug(DEBUG_TAG, "%s: ERROR!! Cache Not Ready\n", PLUGIN_NAME);
    insert_in_response(txnp, "MISS");
    INKHttpTxnReenable(txnp, INK_EVENT_HTTP_CONTINUE);
    return;
  }

  if (INKCacheKeyCreate(&cache_key) == INK_ERROR) {
    LOG_ERROR_AND_REENABLE("INKCacheKeyCreate");
    return;
  }
#ifdef DEBUG
  /*INKDebug(DEBUG_TAG, "Starting Negative Test for INKCacheKeyCreate"); */
  if (INKCacheKeyCreate(NULL) != INK_ERROR) {
    LOG_ERROR_NEG("INKCacheKeyCreate(NULL)");
  }
  /*INKDebug(DEBUG_TAG, "Done Negative Test for INKCacheKeyCreate"); */
#endif

#ifdef DEBUG
  /*INKDebug(DEBUG_TAG, "Starting Negative Test for INKCacheKeyDigestSet"); */
  if (INKCacheKeyDigestSet(NULL, (unsigned char *) url, strlen(url)) != INK_ERROR) {
    LOG_ERROR_NEG("INKCacheKeyDigestSet(NULL, string, len)");
  }

  if (INKCacheKeyDigestSet(cache_key, NULL, strlen(url)) != INK_ERROR) {
    LOG_ERROR_NEG("INKCacheKeyDigestSet(cache_key, NULL, len)");
  }

  if (INKCacheKeyDigestSet(cache_key, (unsigned char *) url, -1) != INK_ERROR) {
    LOG_ERROR_NEG("INKCacheKeyDigestSet(cache_key, string, -1)");
  }
  /*INKDebug(DEBUG_TAG, "Done Negative Test for INKCacheKeyDigestSet"); */
#endif

  if (INKCacheKeyDigestSet(cache_key, (unsigned char *) url, strlen(url)) == INK_ERROR) {
    INKCacheKeyDestroy(cache_key);
    LOG_ERROR_AND_REENABLE("INKCacheKeyDigestSet");
    return;
  }

  url_data = INKmalloc(sizeof(CACHE_URL_DATA));
  if (url_data == NULL) {
    INKCacheKeyDestroy(cache_key);
    LOG_ERROR_AND_REENABLE("INKmalloc");
    return;
  }

  url_data->magic = MAGIC_ALIVE;
  url_data->url = url;
  url_data->url_len = strlen(url);
  url_data->key = cache_key;
  url_data->pin_time = pin_val;
  url_data->write_again_after_remove = 0;
  url_data->txnp = txnp;

  url_data->bufp = INKIOBufferCreate();
  if (url_data->bufp == INK_ERROR_PTR) {
    INKCacheKeyDestroy(cache_key);
    INKfree(url_data);
    LOG_ERROR_AND_REENABLE("INKIOBufferCreate");
    return;
  }

  if (INKContDataSet(cache_handler_cont, url_data) == INK_ERROR) {
    INKCacheKeyDestroy(cache_key);
    INKfree(url_data);
    LOG_ERROR_AND_REENABLE("INKContDataSet");
    return;
  }
#ifdef DEBUG
  /*INKDebug(DEBUG_TAG, "Starting Negative Test for INKCacheKeyHostNameSet"); */
  if (INKCacheKeyHostNameSet(NULL, (unsigned char *) hostname, strlen(hostname)) != INK_ERROR) {
    LOG_ERROR_NEG("INKCacheKeyHostNameSet(NULL, string, len)");
  }

  if (INKCacheKeyHostNameSet(url_data->key, NULL, strlen(hostname)) != INK_ERROR) {
    LOG_ERROR_NEG("INKCacheKeyHostNameSet(cache_key, NULL, len)");
  }

  if (INKCacheKeyHostNameSet(url_data->key, (unsigned char *) hostname, -1) != INK_ERROR) {
    LOG_ERROR_NEG("INKCacheKeyHostNameSet(cache_key, string, -1)");
  }
  /*INKDebug(DEBUG_TAG, "Done Negative Test for INKCacheKeyHostNameSet"); */
#endif

  if (hostname_set > 0) {
    INKDebug(DEBUG_TAG, "HostName set for cache_key to %s", hostname);
    if (INKCacheKeyHostNameSet(url_data->key, (unsigned char *) hostname, strlen(hostname)) == INK_ERROR) {
      INKCacheKeyDestroy(cache_key);
      INKfree(url_data);
      LOG_ERROR_AND_REENABLE("INKCacheKeyHostNameSet");
      return;
    }
  }

  /* try to read from the cache */
  if (INKCacheRead(cache_handler_cont, cache_key) == INK_ERROR_PTR) {
    INKCacheKeyDestroy(cache_key);
    INKfree(url_data);
    LOG_ERROR_AND_REENABLE("INKCacheRead");
    return;
  }
#ifdef DEBUG
  /*INKDebug(DEBUG_TAG, "Starting Negative Test for INKCacheRead"); */
  if (INKCacheRead(cache_handler_cont, NULL) != INK_ERROR_PTR) {
    LOG_ERROR_NEG("INKCacheRead(cache_handler_cont, NULL)");
  }

  if (INKCacheRead(NULL, cache_key) != INK_ERROR_PTR) {
    LOG_ERROR_NEG("INKCacheRead(NULL, cache_key)");
  }
  /*INKDebug(DEBUG_TAG, "Done Negative Test for INKCacheRead"); */
#endif

  return;
}
예제 #5
0
static void
handle_dns(INKHttpTxn txnp, INKCont contp)
{
  INKMBuffer bufp;
  INKMLoc hdr_loc;

  INKIOBuffer output_buffer;
  INKIOBufferReader reader;
  int total_avail;

  INKIOBufferBlock block;
  const char *block_start;
  int block_avail;

  char *output_string;
  int output_len;

  if (!INKHttpTxnClientReqGet(txnp, &bufp, &hdr_loc)) {
    INKDebug(DEBUG_TAG, "couldn't retrieve client request header");
    INKError("couldn't retrieve client request header\n");
    goto done;
  }

  output_buffer = INKIOBufferCreate();

  /* INKIOBufferCreate may return an error pointer */
  if ((void *) output_buffer == INK_ERROR_PTR) {
    INKDebug(DEBUG_TAG, "couldn't allocate IOBuffer");
    INKError("couldn't allocate IOBuffer\n");
    goto done;
  }

  reader = INKIOBufferReaderAlloc(output_buffer);

  /* INKIOBufferReaderAlloc may return an error pointer */
  if ((void *) reader == INK_ERROR_PTR) {
    INKDebug(DEBUG_TAG, "couldn't allocate IOBufferReader");
    INKError("couldn't allocate IOBufferReader\n");
    goto done;
  }

  /* This will print  just MIMEFields and not
     the http request line */
  INKDebug(DEBUG_TAG, "Printing the hdrs ... ");
  if (INKMimeHdrPrint(bufp, hdr_loc, output_buffer) == INK_ERROR) {
    INKDebug(DEBUG_TAG, "non-fatal: error printing mime-hdrs");
    INKError("non-fatal: error printing mime-hdrs\n");
  }

  if (INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc) == INK_ERROR) {
    INKDebug(DEBUG_TAG, "non-fatal: error releasing MLoc");
    INKError("non-fatal: error releasing MLoc\n");
  }

  /* 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 = INKIOBufferReaderAvail(reader);

  /* INKIOBufferReaderAvail may send an INK_ERROR */
  if ((INKReturnCode) total_avail == INK_ERROR) {
    INKDebug(DEBUG_TAG, "couldn't get available byte-count from IO-read-buffer");
    INKError("couldn't get available byte-count from IO-read-buffer\n");
    goto done;
  }

  /* Allocate the string with an extra byte for the string
     terminator */
  output_string = (char *) INKmalloc(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 = INKIOBufferReaderStart(reader);

  /* INKIOBufferReaderStart may return an error pointer */
  if (block == INK_ERROR_PTR) {
    INKDebug(DEBUG_TAG, "couldn't get from IOBufferBlock");
    INKError("couldn't get from IOBufferBlock\n");
    goto done;
  }

  while (block) {

    block_start = INKIOBufferBlockReadStart(block, reader, &block_avail);

    /* INKIOBufferBlockReadStart may return an error pointer */
    if (block_start == INK_ERROR_PTR) {
      INKDebug(DEBUG_TAG, "couldn't read from IOBuffer");
      INKError("couldn't read from IOBuffer\n");
      goto done;
    }

    /* 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 */
    if (INKIOBufferReaderConsume(reader, block_avail) == INK_ERROR) {
      INKDebug(DEBUG_TAG, "error consuming data from the ReaderBlock");
      INKError("error consuming data from the ReaderBlock\n");
    }

    /* Get the next block now that we've consumed the
       data off the last block */
    block = INKIOBufferReaderStart(reader);

    /* INKIOBufferReaderStart may return an error pointer */
    if (block == INK_ERROR_PTR) {
      INKDebug(DEBUG_TAG, "couldn't get from IOBufferBlock");
      INKError("couldn't get from IOBufferBlock\n");
      goto done;
    }
  }

  /* Terminate the string */
  output_string[output_len] = '\0';
  output_len++;

  /* Free up the INKIOBuffer that we used to print out the header */
  if (INKIOBufferReaderFree(reader) != INK_SUCCESS) {
    INKDebug(DEBUG_TAG, "non-fatal: error releasing IOBufferReader");
    INKError("non-fatal: error releasing IOBufferReader\n");
  }

  if (INKIOBufferDestroy(output_buffer) != INK_SUCCESS) {
    INKDebug(DEBUG_TAG, "non-fatal: error destroying IOBuffer");
    INKError("non-fatal: error destroying IOBuffer\n");
  }

  /* Although I'd never do this a production plugin, printf
     the header so that we can see it's all there */
  INKDebug("debug-output-header", "%s", output_string);

  INKfree(output_string);

done:
  INKHttpTxnReenable(txnp, INK_EVENT_HTTP_CONTINUE);
}