You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2013/04/19 19:28:04 UTC

[1/7] git commit: TS-1053 Make combo_handler compiler.

Updated Branches:
  refs/heads/master bab12ba88 -> 21515f600


TS-1053 Make combo_handler compiler.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/7f7eddf5
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/7f7eddf5
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/7f7eddf5

Branch: refs/heads/master
Commit: 7f7eddf57065996df936a372681341ce7c637dd8
Parents: bab12ba
Author: Conan Wang <co...@gmail.com>
Authored: Fri Apr 19 11:04:13 2013 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Fri Apr 19 11:04:13 2013 -0600

----------------------------------------------------------------------
 .../experimental/combo_handler/combo_handler.cc    |  152 ++++++---------
 1 files changed, 55 insertions(+), 97 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7f7eddf5/plugins/experimental/combo_handler/combo_handler.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/combo_handler/combo_handler.cc b/plugins/experimental/combo_handler/combo_handler.cc
index 0650d94..50e1da6 100644
--- a/plugins/experimental/combo_handler/combo_handler.cc
+++ b/plugins/experimental/combo_handler/combo_handler.cc
@@ -27,8 +27,9 @@
 #include <arpa/inet.h>
 
 #include <ts/ts.h>
+#include <ts/experimental.h>
 
-#include <HttpDataFetcherImpl.h> 
+#include <HttpDataFetcherImpl.h>
 #include <gzip.h>
 #include <Utils.h>
 
@@ -56,13 +57,12 @@ typedef list<string> StringList;
 
 struct ClientRequest {
   TSHttpStatus status;
-  unsigned int client_ip;
-  int client_port;
+  const sockaddr *client_addr;
   StringList file_urls;
   bool gzip_accepted;
   string defaultBucket;	//default Bucket is set to l
   ClientRequest()
-    : status(TS_HTTP_STATUS_OK), client_ip(0), client_port(0), gzip_accepted(false), defaultBucket("l") { };
+    : status(TS_HTTP_STATUS_OK), client_addr(NULL), gzip_accepted(false), defaultBucket("l") { };
 };
 
 struct InterceptData {
@@ -132,7 +132,7 @@ InterceptData::init(TSVConn vconn)
   req_hdr_loc = TSHttpHdrCreate(req_hdr_bufp);
   TSHttpHdrTypeSet(req_hdr_bufp, req_hdr_loc, TS_HTTP_TYPE_REQUEST);
 
-  fetcher = new HttpDataFetcherImpl(contp, creq.client_ip, creq.client_port, "combohandler_fetcher");
+  fetcher = new HttpDataFetcherImpl(contp, creq.client_addr, "combohandler_fetcher");
 
   initialized = true;
   LOG_DEBUG("InterceptData initialized!");
@@ -208,15 +208,12 @@ TSPluginInit(int argc, const char *argv[])
   LOG_DEBUG("Signature key is [%s]", SIG_KEY_NAME.c_str());
 
   TSCont rrh_contp = TSContCreate(handleReadRequestHeader, NULL);
-  if (!rrh_contp || (rrh_contp == TS_ERROR_PTR)) {
+  if (!rrh_contp) {
     LOG_ERROR("Could not create read request header continuation");
     return;
   }
-  if (TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, rrh_contp) == TS_ERROR) {
-    LOG_ERROR("Error while registering to read request hook");
-    TSContDestroy(rrh_contp);
-    return;
-  }
+  TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, rrh_contp);
+
   Utils::init(&TSDebug, &TSError);
   LOG_DEBUG("Plugin started");
 }
@@ -232,28 +229,23 @@ handleReadRequestHeader(TSCont contp, TSEvent event, void *edata)
   TSMBuffer bufp;
   TSMLoc hdr_loc;
 
-  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc)) {
-    TSMLoc url_loc = TSHttpHdrUrlGet(bufp, hdr_loc);
-    if (url_loc && (url_loc != TS_ERROR_PTR)) {
+  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) {
+    TSMLoc url_loc;
+    if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) {
       if (isComboHandlerRequest(bufp, hdr_loc, url_loc)) {
         TSCont contp = TSContCreate(handleServerEvent, TSMutexCreate());
-        if (!contp || (contp == TS_ERROR_PTR)) {
+        if (!contp) {
           LOG_ERROR("[%s] Could not create intercept request", __FUNCTION__);
           reenable_to_event = TS_EVENT_HTTP_ERROR;
         } else {
-          if (TSHttpTxnServerIntercept(contp, txnp) == TS_SUCCESS) {
-            InterceptData *int_data = new InterceptData(contp);
-            TSContDataSet(contp, int_data);
-            // todo: check if these two cacheable sets are required
-            TSHttpTxnSetReqCacheableSet(txnp);
-            TSHttpTxnSetRespCacheableSet(txnp);
-            getClientRequest(txnp, bufp, hdr_loc, url_loc, int_data->creq);
-            LOG_DEBUG("Setup server intercept to handle client request");
-          } else {
-            TSContDestroy(contp);
-            LOG_ERROR("Could not setup server intercept");
-            reenable_to_event = TS_EVENT_HTTP_ERROR;
-          }
+          TSHttpTxnServerIntercept(contp, txnp);
+          InterceptData *int_data = new InterceptData(contp);
+          TSContDataSet(contp, int_data);
+          // todo: check if these two cacheable sets are required
+          TSHttpTxnReqCacheableSet(txnp, 1);
+          TSHttpTxnRespCacheableSet(txnp, 1);
+          getClientRequest(txnp, bufp, hdr_loc, url_loc, int_data->creq);
+          LOG_DEBUG("Setup server intercept to handle client request");
         }
       }
       TSHandleMLocRelease(bufp, hdr_loc, url_loc);
@@ -276,7 +268,7 @@ isComboHandlerRequest(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc)
   bool retval = false;
   const char *method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
 
-  if (method == TS_ERROR_PTR) {
+  if (!method) {
     LOG_ERROR("Could not obtain method!", __FUNCTION__);
   } else {
     if ((method_len != TS_HTTP_LEN_GET) || (strncasecmp(method, TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET) != 0)) {
@@ -284,19 +276,17 @@ isComboHandlerRequest(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc)
     } else {
       retval = true;
     }
-    TSHandleStringRelease(bufp, hdr_loc, method);
 
     if (retval) {
       int path_len;
       const char *path = TSUrlPathGet(bufp, url_loc, &path_len);
-      if (path == TS_ERROR_PTR) {
+      if (!path) {
         LOG_ERROR("Could not get path from request URL");
         retval = false;
       } else {
         retval = (path_len == COMBO_HANDLER_PATH_SIZE) &&
           (strncasecmp(path, COMBO_HANDLER_PATH.c_str(), COMBO_HANDLER_PATH_SIZE) == 0);
         LOG_DEBUG("Path [%.*s] is %s combo handler path", path_len, path, (retval ? "a" : "not a"));
-        TSHandleStringRelease(bufp, hdr_loc, path);
       }
     }
   }
@@ -312,20 +302,20 @@ getDefaultBucket(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_obj, ClientRequest &
   int host_len = 0;
   bool defaultBucketFound = false;
 
-  field_loc=TSMimeHdrFieldFind(bufp, hdr_obj, TS_MIME_FIELD_HOST, -1);
-  if (field_loc == TS_ERROR_PTR) {
+  field_loc = TSMimeHdrFieldFind(bufp, hdr_obj, TS_MIME_FIELD_HOST, -1);
+  if (field_loc == TS_NULL_MLOC) {
     LOG_ERROR("Host field not found.");
     return false;
   }
 
-  host=TSMimeHdrFieldValueGet (bufp, hdr_obj, field_loc, 0, &host_len);
+  host = TSMimeHdrFieldValueStringGet(bufp, hdr_obj, field_loc, 0, &host_len);
   if (!host || host_len <= 0) {
     LOG_ERROR("Error Extracting Host Header");
     TSHandleMLocRelease (bufp, hdr_obj, field_loc);
     return false;
   }
 
-  LOG_DEBUG("host: %s", host);
+  LOG_DEBUG("host: %.*s", host_len, host);
   for(int i = 0 ; i < host_len; i++)
     {
       if (host[i] == '.')
@@ -337,7 +327,6 @@ getDefaultBucket(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_obj, ClientRequest &
     }
 
   TSHandleMLocRelease (bufp, hdr_obj, field_loc);
-  TSHandleStringRelease(bufp, field_loc, host);
 
   LOG_DEBUG("defaultBucket: %s", creq.defaultBucket.data());
   return defaultBucketFound;
@@ -349,23 +338,16 @@ getClientRequest(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc,
   int query_len;
   const char *query = TSUrlHttpQueryGet(bufp, url_loc, &query_len);
 
-  if (query == TS_ERROR_PTR) {
+  if (!query) {
     LOG_ERROR("Could not get query from request URL");
   } else {
     if (!getDefaultBucket(txnp, bufp, hdr_loc, creq))
       {
         LOG_ERROR("failed getting Default Bucket for the request");
-        TSHandleStringRelease(bufp, url_loc, query);
         return;
       }
     parseQueryParameters(query, query_len, creq);
-    TSHandleStringRelease(bufp, url_loc, query);
-    creq.client_ip = ntohl(TSHttpTxnClientIPGet(txnp));
-    if (TSHttpTxnClientRemotePortGet(txnp, &creq.client_port) != TS_SUCCESS) {
-      creq.client_port = 0;
-    } else {
-      creq.client_port = ntohs(static_cast<uint16_t>(creq.client_port));
-    }
+    creq.client_addr = TSHttpTxnClientAddrGet(txnp);
     checkGzipAcceptance(bufp, hdr_loc, creq);
   }
 }
@@ -399,15 +381,15 @@ parseQueryParameters(const char *query, int query_len, ClientRequest &creq)
               // TODO - really verify the signature
               LOG_DEBUG("Verified signature successfully");
               sig_verified = true;
-            } else {
+            }
+            if (!sig_verified) {
               LOG_DEBUG("Signature [%.*s] on query [%.*s] is invalid", param_len - 4, param + 4,
                         param_start_pos, query);
             }
+          } else {
+            LOG_DEBUG("Verification not configured; ignoring signature...");
           }
-        } else {
-          LOG_DEBUG("Verification not configured; ignoring signature...");
-        }
-        break; // nothing useful after the signature
+          break; // nothing useful after the signature
       }
       if ((param_len >= 2) && (param[0] == 'p') && (param[1] == '=')) {
         common_prefix_size = param_len - 2;
@@ -484,17 +466,17 @@ checkGzipAcceptance(TSMBuffer bufp, TSMLoc hdr_loc, ClientRequest &creq)
   creq.gzip_accepted = false;
   TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_ACCEPT_ENCODING,
                                           TS_MIME_LEN_ACCEPT_ENCODING);
-  if ((field_loc != TS_ERROR_PTR) && field_loc) {
+  if (field_loc != TS_NULL_MLOC) {
     const char *value;
     int value_len;
     int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
 
     for (int i = 0; i < n_values; ++i) {
-      if (TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value, &value_len) == TS_SUCCESS) {
+      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
+      if (value) {
         if ((value_len == TS_HTTP_LEN_GZIP) && (strncasecmp(value, TS_HTTP_VALUE_GZIP, value_len) == 0)) {
           creq.gzip_accepted = true;
         }
-        TSHandleStringRelease(bufp, hdr_loc, value);
       } else {
         LOG_DEBUG("Error while getting value # %d of header [%.*s]", i, TS_MIME_LEN_ACCEPT_ENCODING,
                   TS_MIME_FIELD_ACCEPT_ENCODING);
@@ -622,7 +604,7 @@ readInterceptRequest(InterceptData &int_data)
   
   int consumed = 0;
   if (avail > 0) {
-    int data_len;
+    int64_t data_len;
     const char *data;
     TSIOBufferBlock block = TSIOBufferReaderStart(int_data.input.reader);
     while (block != NULL) {
@@ -634,24 +616,14 @@ readInterceptRequest(InterceptData &int_data)
       }
       consumed += data_len;
       block = TSIOBufferBlockNext(block);
-      if (block == TS_ERROR_PTR) {
-        LOG_ERROR("Error while getting block from ioreader");
-        return false;
-      }
     }
   }
   LOG_DEBUG("Consumed %d bytes from input vio", consumed);
   
-  if (TSIOBufferReaderConsume(int_data.input.reader, consumed) == TS_ERROR) {
-    LOG_ERROR("Error while consuming data from input vio");
-    return false;
-  }
+  TSIOBufferReaderConsume(int_data.input.reader, consumed);
   
   // Modify the input VIO to reflect how much data we've completed.
-  if (TSVIONDoneSet(int_data.input.vio, TSVIONDoneGet(int_data.input.vio) + consumed) == TS_ERROR) {
-    LOG_ERROR("Error while setting ndone on input vio");
-    return false;
-  }
+  TSVIONDoneSet(int_data.input.vio, TSVIONDoneGet(int_data.input.vio) + consumed);
 
   if (!int_data.read_complete) {
     LOG_DEBUG("Re-enabling input VIO as request header not completely read yet");
@@ -719,15 +691,9 @@ writeResponse(InterceptData &int_data)
   }
     
   LOG_DEBUG("Wrote reply of size %d", n_bytes_written);
-  if (TSVIONBytesSet(int_data.output.vio, n_bytes_written) == TS_ERROR) {
-    LOG_ERROR("Error while setting nbytes to %d on output vio", n_bytes_written);
-    return false;
-  }
+  TSVIONBytesSet(int_data.output.vio, n_bytes_written);
   
-  if (TSVIOReenable(int_data.output.vio) == TS_ERROR) {
-    LOG_ERROR("Error while reenabling output VIO");
-    return false;
-  }
+  TSVIOReenable(int_data.output.vio);
   return true;
 }
 
@@ -750,20 +716,16 @@ prepareResponse(InterceptData &int_data, ByteBlockList &body_blocks, string &res
         }
         field_loc = TSMimeHdrFieldFind(resp_data.bufp, resp_data.hdr_loc, TS_MIME_FIELD_EXPIRES,
                                         TS_MIME_LEN_EXPIRES);
-        if (field_loc && (field_loc != TS_ERROR_PTR)) {
+        if (field_loc != TS_NULL_MLOC) {
           time_t curr_field_expires_time;
           int n_values = TSMimeHdrFieldValuesCount(resp_data.bufp, resp_data.hdr_loc, field_loc);
           if ((n_values != TS_ERROR) && (n_values > 0)) {
-            if (TSMimeHdrFieldValueDateGet(resp_data.bufp, resp_data.hdr_loc, field_loc,
-                                            &curr_field_expires_time) == TS_SUCCESS) {
-              if (!got_expires_time) {
-                expires_time = curr_field_expires_time;
-                got_expires_time = true;
-              } else if (curr_field_expires_time < expires_time) {
-                expires_time = curr_field_expires_time;
-              }
-            } else {
-              LOG_DEBUG("Error while getting date value");
+            curr_field_expires_time = TSMimeHdrFieldValueDateGet(resp_data.bufp, resp_data.hdr_loc, field_loc);
+            if (!got_expires_time) {
+              expires_time = curr_field_expires_time;
+              got_expires_time = true;
+            } else if (curr_field_expires_time < expires_time) {
+              expires_time = curr_field_expires_time;
             }
           }
           TSHandleMLocRelease(resp_data.bufp, resp_data.hdr_loc, field_loc);
@@ -806,24 +768,20 @@ getContentType(TSMBuffer bufp, TSMLoc hdr_loc, string &resp_header_fields)
   bool retval = false;
   TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_TYPE,
                                           TS_MIME_LEN_CONTENT_TYPE);
-  if (field_loc && (field_loc != TS_ERROR_PTR)) {
+  if (field_loc != TS_NULL_MLOC) {
     bool values_added = false;
     const char *value;
     int value_len;
     int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
     for (int i = 0; i < n_values; ++i) {
-      if (TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value, &value_len) == TS_SUCCESS) {
-        if (!values_added) {
-          resp_header_fields.append("Content-Type: ");
-          values_added = true;
-        } else {
-          resp_header_fields.append(", ");
-        }
-        resp_header_fields.append(value, value_len);
-        TSHandleStringRelease(bufp, hdr_loc, value);
+      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
+      if (!values_added) {
+        resp_header_fields.append("Content-Type: ");
+        values_added = true;
       } else {
-        LOG_DEBUG("Error while getting Content-Type value #%d", i);
+        resp_header_fields.append(", ");
       }
+      resp_header_fields.append(value, value_len);
     }
     TSHandleMLocRelease(bufp, hdr_loc, field_loc);
     if (values_added) {


[4/7] git commit: TS-1053 Move combo_handler to ESI, also change plugin.cc to esi.cc

Posted by zw...@apache.org.
TS-1053 Move combo_handler to ESI, also change plugin.cc to esi.cc


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/ee04a10d
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/ee04a10d
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/ee04a10d

Branch: refs/heads/master
Commit: ee04a10dc592065abf534d408b2685e4c588a19a
Parents: 7f7eddf
Author: Leif Hedstrom <zw...@apache.org>
Authored: Fri Apr 19 11:07:16 2013 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Fri Apr 19 11:07:16 2013 -0600

----------------------------------------------------------------------
 .../experimental/combo_handler/combo_handler.cc    |  841 -------
 plugins/experimental/esi/Makefile.am               |   11 +-
 plugins/experimental/esi/combo_handler.cc          |  842 +++++++
 plugins/experimental/esi/esi.cc                    | 1806 +++++++++++++++
 plugins/experimental/esi/plugin.cc                 | 1806 ---------------
 5 files changed, 2657 insertions(+), 2649 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee04a10d/plugins/experimental/combo_handler/combo_handler.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/combo_handler/combo_handler.cc b/plugins/experimental/combo_handler/combo_handler.cc
deleted file mode 100644
index 50e1da6..0000000
--- a/plugins/experimental/combo_handler/combo_handler.cc
+++ /dev/null
@@ -1,841 +0,0 @@
-/** @file
-
-    ATS plugin to do combo handling.
-
-    @section license License
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-*/
-
-#include <list>
-#include <string>
-#include <time.h>
-#include <arpa/inet.h>
-
-#include <ts/ts.h>
-#include <ts/experimental.h>
-
-#include <HttpDataFetcherImpl.h>
-#include <gzip.h>
-#include <Utils.h>
-
-using namespace std;
-using namespace EsiLib;
-
-#define DEBUG_TAG "combo_handler"
-
-static string SIG_KEY_NAME;
-
-#define DEFAULT_COMBO_HANDLER_PATH "admin/v1/combo"
-static string COMBO_HANDLER_PATH;
-static int COMBO_HANDLER_PATH_SIZE;
-
-#define LOG_ERROR(fmt, args...) do {                                    \
-    TSError("[%s:%d] [%s] ERROR: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
-    TSDebug(DEBUG_TAG, "[%s:%d] [%s] ERROR: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
-  } while (0)
-
-#define LOG_DEBUG(fmt, args...) do {                                    \
-    TSDebug(DEBUG_TAG, "[%s:%d] [%s] DEBUG: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
-  } while (0)
-
-typedef list<string> StringList;
-
-struct ClientRequest {
-  TSHttpStatus status;
-  const sockaddr *client_addr;
-  StringList file_urls;
-  bool gzip_accepted;
-  string defaultBucket;	//default Bucket is set to l
-  ClientRequest()
-    : status(TS_HTTP_STATUS_OK), client_addr(NULL), gzip_accepted(false), defaultBucket("l") { };
-};
-
-struct InterceptData {
-  TSVConn net_vc;
-  TSCont contp;
-
-  struct IoHandle {
-    TSVIO vio;
-    TSIOBuffer buffer;
-    TSIOBufferReader reader;
-
-    IoHandle()
-      : vio(0), buffer(0), reader(0) { };
-
-    ~IoHandle() {
-      if (reader) {
-        TSIOBufferReaderFree(reader);
-      }
-      if (buffer) {
-        TSIOBufferDestroy(buffer);
-      }
-    };
-  };
-
-  IoHandle input;
-  IoHandle output;
-  TSHttpParser http_parser;
-
-  string body;
-  TSMBuffer req_hdr_bufp;
-  TSMLoc req_hdr_loc;
-  bool req_hdr_parsed;
-  bool initialized;
-  ClientRequest creq;
-  HttpDataFetcherImpl *fetcher;
-  bool read_complete;
-  bool write_complete;
-  string gzipped_data;
-  
-  InterceptData(TSCont cont) 
-    : net_vc(0), contp(cont), input(), output(), req_hdr_bufp(0), req_hdr_loc(0), req_hdr_parsed(false),
-      initialized(false), fetcher(0), read_complete(false), write_complete(false) {
-    http_parser = TSHttpParserCreate();
-  }
-
-  bool init(TSVConn vconn);
-  void setupWrite();
-
-  ~InterceptData();
-};
-
-bool
-InterceptData::init(TSVConn vconn)
-{
-  if (initialized) {
-    LOG_ERROR("InterceptData already initialized!");
-    return false;
-  }
-  
-  net_vc = vconn;
-
-  input.buffer = TSIOBufferCreate();
-  input.reader = TSIOBufferReaderAlloc(input.buffer);
-  input.vio = TSVConnRead(net_vc, contp, input.buffer, INT_MAX);
-
-  req_hdr_bufp = TSMBufferCreate();
-  req_hdr_loc = TSHttpHdrCreate(req_hdr_bufp);
-  TSHttpHdrTypeSet(req_hdr_bufp, req_hdr_loc, TS_HTTP_TYPE_REQUEST);
-
-  fetcher = new HttpDataFetcherImpl(contp, creq.client_addr, "combohandler_fetcher");
-
-  initialized = true;
-  LOG_DEBUG("InterceptData initialized!");
-  return true;
-}
-
-void
-InterceptData::setupWrite()
-{
-  TSAssert(output.buffer == 0);
-  output.buffer = TSIOBufferCreate();
-  output.reader = TSIOBufferReaderAlloc(output.buffer);
-  output.vio = TSVConnWrite(net_vc, contp, output.reader, INT_MAX);
-}
-
-InterceptData::~InterceptData()
-{
-  if (req_hdr_loc) {
-    TSHandleMLocRelease(req_hdr_bufp, TS_NULL_MLOC, req_hdr_loc);
-  }
-  if (req_hdr_bufp) {
-    TSMBufferDestroy(req_hdr_bufp);
-  }
-  if (fetcher) {
-    delete fetcher;
-  }
-  TSHttpParserDestroy(http_parser); 
-  if (net_vc) {
-    TSVConnClose(net_vc);
-  }
-}
-
-// forward declarations
-static int handleReadRequestHeader(TSCont contp, TSEvent event, void *edata);
-static bool isComboHandlerRequest(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc);
-static void getClientRequest(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc,
-                             ClientRequest &creq);
-static void parseQueryParameters(const char *query, int query_len, ClientRequest &creq);
-static void checkGzipAcceptance(TSMBuffer bufp, TSMLoc hdr_loc, ClientRequest &creq);
-static int handleServerEvent(TSCont contp, TSEvent event, void *edata);
-static bool initRequestProcessing(InterceptData &int_data, void *edata, bool &write_response);
-static bool readInterceptRequest(InterceptData &int_data);
-static bool writeResponse(InterceptData &int_data);
-static bool writeErrorResponse(InterceptData &int_data, int &n_bytes_written);
-static bool writeStandardHeaderFields(InterceptData &int_data, int &n_bytes_written);
-static void prepareResponse(InterceptData &int_data, ByteBlockList &body_blocks, string &resp_header_fields);
-static bool getContentType(TSMBuffer bufp, TSMLoc hdr_loc, string &resp_header_fields);
-static bool getDefaultBucket(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_obj, ClientRequest &creq);
-
-
-void
-TSPluginInit(int argc, const char *argv[])
-{
-  if ((argc > 1) && (strcmp(argv[1], "-") != 0)) {
-    COMBO_HANDLER_PATH =  argv[1];
-    if (COMBO_HANDLER_PATH == "/") {
-      COMBO_HANDLER_PATH.clear();
-    } else {
-      if (COMBO_HANDLER_PATH[0] == '/') {
-        COMBO_HANDLER_PATH.erase(0, 1);
-      }
-      if (COMBO_HANDLER_PATH[COMBO_HANDLER_PATH.size() - 1] == '/') {
-        COMBO_HANDLER_PATH.erase(COMBO_HANDLER_PATH.size() - 1, 1);
-      }
-    }
-  } else {
-    COMBO_HANDLER_PATH = DEFAULT_COMBO_HANDLER_PATH;
-  }
-  COMBO_HANDLER_PATH_SIZE = static_cast<int>(COMBO_HANDLER_PATH.size());
-  LOG_DEBUG("Combo handler path is [%s]", COMBO_HANDLER_PATH.c_str());
-
-  SIG_KEY_NAME = ((argc > 2) && (strcmp(argv[2], "-") != 0)) ? argv[2] : "";
-  LOG_DEBUG("Signature key is [%s]", SIG_KEY_NAME.c_str());
-
-  TSCont rrh_contp = TSContCreate(handleReadRequestHeader, NULL);
-  if (!rrh_contp) {
-    LOG_ERROR("Could not create read request header continuation");
-    return;
-  }
-  TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, rrh_contp);
-
-  Utils::init(&TSDebug, &TSError);
-  LOG_DEBUG("Plugin started");
-}
-
-static int
-handleReadRequestHeader(TSCont contp, TSEvent event, void *edata)
-{
-  TSAssert(event == TS_EVENT_HTTP_READ_REQUEST_HDR);
-
-  LOG_DEBUG("handling read request header event...");
-  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
-  TSEvent reenable_to_event = TS_EVENT_HTTP_CONTINUE;
-  TSMBuffer bufp;
-  TSMLoc hdr_loc;
-
-  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) {
-    TSMLoc url_loc;
-    if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) {
-      if (isComboHandlerRequest(bufp, hdr_loc, url_loc)) {
-        TSCont contp = TSContCreate(handleServerEvent, TSMutexCreate());
-        if (!contp) {
-          LOG_ERROR("[%s] Could not create intercept request", __FUNCTION__);
-          reenable_to_event = TS_EVENT_HTTP_ERROR;
-        } else {
-          TSHttpTxnServerIntercept(contp, txnp);
-          InterceptData *int_data = new InterceptData(contp);
-          TSContDataSet(contp, int_data);
-          // todo: check if these two cacheable sets are required
-          TSHttpTxnReqCacheableSet(txnp, 1);
-          TSHttpTxnRespCacheableSet(txnp, 1);
-          getClientRequest(txnp, bufp, hdr_loc, url_loc, int_data->creq);
-          LOG_DEBUG("Setup server intercept to handle client request");
-        }
-      }
-      TSHandleMLocRelease(bufp, hdr_loc, url_loc);
-    } else {
-      LOG_ERROR("Could not get request URL");
-    }
-    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-  } else {
-    LOG_ERROR("Could not get client request");
-  }
-
-  TSHttpTxnReenable(txnp, reenable_to_event);
-  return 1;
-}
-
-static bool
-isComboHandlerRequest(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc)
-{
-  int method_len;
-  bool retval = false;
-  const char *method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
-
-  if (!method) {
-    LOG_ERROR("Could not obtain method!", __FUNCTION__);
-  } else {
-    if ((method_len != TS_HTTP_LEN_GET) || (strncasecmp(method, TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET) != 0)) {
-      LOG_DEBUG("Unsupported method [%.*s]", method_len, method);
-    } else {
-      retval = true;
-    }
-
-    if (retval) {
-      int path_len;
-      const char *path = TSUrlPathGet(bufp, url_loc, &path_len);
-      if (!path) {
-        LOG_ERROR("Could not get path from request URL");
-        retval = false;
-      } else {
-        retval = (path_len == COMBO_HANDLER_PATH_SIZE) &&
-          (strncasecmp(path, COMBO_HANDLER_PATH.c_str(), COMBO_HANDLER_PATH_SIZE) == 0);
-        LOG_DEBUG("Path [%.*s] is %s combo handler path", path_len, path, (retval ? "a" : "not a"));
-      }
-    }
-  }
-  return retval;
-}
-
-static bool
-getDefaultBucket(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_obj, ClientRequest &creq)
-{
-  LOG_DEBUG("In getDefaultBucket");
-  TSMLoc field_loc;
-  const char* host;
-  int host_len = 0;
-  bool defaultBucketFound = false;
-
-  field_loc = TSMimeHdrFieldFind(bufp, hdr_obj, TS_MIME_FIELD_HOST, -1);
-  if (field_loc == TS_NULL_MLOC) {
-    LOG_ERROR("Host field not found.");
-    return false;
-  }
-
-  host = TSMimeHdrFieldValueStringGet(bufp, hdr_obj, field_loc, 0, &host_len);
-  if (!host || host_len <= 0) {
-    LOG_ERROR("Error Extracting Host Header");
-    TSHandleMLocRelease (bufp, hdr_obj, field_loc);
-    return false;
-  }
-
-  LOG_DEBUG("host: %.*s", host_len, host);
-  for(int i = 0 ; i < host_len; i++)
-    {
-      if (host[i] == '.')
-        {
-          creq.defaultBucket = string(host, i);
-          defaultBucketFound = true;
-          break;
-        }
-    }
-
-  TSHandleMLocRelease (bufp, hdr_obj, field_loc);
-
-  LOG_DEBUG("defaultBucket: %s", creq.defaultBucket.data());
-  return defaultBucketFound;
-}
-
-static void
-getClientRequest(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc, ClientRequest &creq)
-{
-  int query_len;
-  const char *query = TSUrlHttpQueryGet(bufp, url_loc, &query_len);
-
-  if (!query) {
-    LOG_ERROR("Could not get query from request URL");
-  } else {
-    if (!getDefaultBucket(txnp, bufp, hdr_loc, creq))
-      {
-        LOG_ERROR("failed getting Default Bucket for the request");
-        return;
-      }
-    parseQueryParameters(query, query_len, creq);
-    creq.client_addr = TSHttpTxnClientAddrGet(txnp);
-    checkGzipAcceptance(bufp, hdr_loc, creq);
-  }
-}
-
-static void
-parseQueryParameters(const char *query, int query_len, ClientRequest &creq)
-{
-  creq.status = TS_HTTP_STATUS_OK;
-  int param_start_pos = 0;
-  bool sig_verified = false;
-  int colon_pos = -1;
-  string file_url("http://localhost/");
-  size_t file_base_url_size = file_url.size();
-  const char *common_prefix = 0;
-  int common_prefix_size = 0;
-  const char *common_prefix_path = 0;
-  int common_prefix_path_size = 0;
-
-  for (int i = 0; i <= query_len; ++i) {
-    if ((i == query_len) || (query[i] == '&') || (query[i] == '?')) {
-      int param_len = i - param_start_pos;
-      if (param_len) {
-        const char *param = query + param_start_pos;
-        if ((param_len >= 4) && (strncmp(param, "sig=", 4) == 0)) {
-          if (SIG_KEY_NAME.size()) {
-            if (!param_start_pos) {
-              LOG_DEBUG("Signature cannot be the first parameter in query [%.*s]", query_len, query);
-            } else if (param_len == 4) {
-              LOG_DEBUG("Signature empty in query [%.*s]", query_len, query);
-            } else {
-              // TODO - really verify the signature
-              LOG_DEBUG("Verified signature successfully");
-              sig_verified = true;
-            }
-            if (!sig_verified) {
-              LOG_DEBUG("Signature [%.*s] on query [%.*s] is invalid", param_len - 4, param + 4,
-                        param_start_pos, query);
-            }
-          } else {
-            LOG_DEBUG("Verification not configured; ignoring signature...");
-          }
-          break; // nothing useful after the signature
-      }
-      if ((param_len >= 2) && (param[0] == 'p') && (param[1] == '=')) {
-        common_prefix_size = param_len - 2;
-        common_prefix_path_size = 0;
-        if (common_prefix_size) {
-          common_prefix = param + 2;
-          for (int i = 0; i < common_prefix_size; ++i) {
-            if (common_prefix[i] == ':') {
-              common_prefix_path = common_prefix;
-              common_prefix_path_size = i;
-              ++i; // go beyond the ':'
-              common_prefix += i;
-              common_prefix_size -= i;
-              break;
-            }
-          }
-        }
-        LOG_DEBUG("Common prefix is [%.*s], common prefix path is [%.*s]", common_prefix_size, common_prefix,
-                  common_prefix_path_size, common_prefix_path);
-      }
-      else {
-        if (common_prefix_path_size) {
-          if (colon_pos >= param_start_pos) { // we have a colon in this param as well?
-            LOG_ERROR("Ambiguous 'bucket': [%.*s] specified in common prefix and [%.*s] specified in "
-                      "current parameter [%.*s]", common_prefix_path_size, common_prefix_path,
-                      colon_pos - param_start_pos, param, param_len, param);
-            creq.file_urls.clear();
-            break;
-          }
-          file_url.append(common_prefix_path, common_prefix_path_size);
-        }
-        else if (colon_pos >= param_start_pos) { // we have a colon
-          if ((colon_pos == param_start_pos) || (colon_pos == (i - 1))) {
-            LOG_ERROR("Colon-separated path [%.*s] has empty part(s)", param_len, param);
-            creq.file_urls.clear();
-            break;
-          }
-          file_url.append(param, colon_pos - param_start_pos); // appending pre ':' part first
-            
-          // modify these to point to the "actual" file path
-          param_start_pos = colon_pos + 1;
-          param_len = i - param_start_pos;
-          param = query + param_start_pos;
-        } else {
-          file_url += creq.defaultBucket; // default path
-        }
-        file_url += '/';
-        if (common_prefix_size) {
-          file_url.append(common_prefix, common_prefix_size);
-        }
-        file_url.append(param, param_len);
-        creq.file_urls.push_back(file_url);
-        LOG_DEBUG("Added file path [%s]", file_url.c_str());
-        file_url.resize(file_base_url_size);
-      }
-    }
-    param_start_pos = i + 1;
-  } else if (query[i] == ':') {
-    colon_pos = i;
-  }
-}
-if (!creq.file_urls.size()) {
-  creq.status = TS_HTTP_STATUS_BAD_REQUEST;
- } else if (SIG_KEY_NAME.size() && !sig_verified) {
-  LOG_DEBUG("Invalid/empty signature found; Need valid signature");
-  creq.status = TS_HTTP_STATUS_FORBIDDEN;
-  creq.file_urls.clear();
- }
-}
-
-static void
-checkGzipAcceptance(TSMBuffer bufp, TSMLoc hdr_loc, ClientRequest &creq)
-{
-  creq.gzip_accepted = false;
-  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_ACCEPT_ENCODING,
-                                          TS_MIME_LEN_ACCEPT_ENCODING);
-  if (field_loc != TS_NULL_MLOC) {
-    const char *value;
-    int value_len;
-    int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
-
-    for (int i = 0; i < n_values; ++i) {
-      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
-      if (value) {
-        if ((value_len == TS_HTTP_LEN_GZIP) && (strncasecmp(value, TS_HTTP_VALUE_GZIP, value_len) == 0)) {
-          creq.gzip_accepted = true;
-        }
-      } else {
-        LOG_DEBUG("Error while getting value # %d of header [%.*s]", i, TS_MIME_LEN_ACCEPT_ENCODING,
-                  TS_MIME_FIELD_ACCEPT_ENCODING);
-      }
-      if (creq.gzip_accepted) {
-        break;
-      }
-    }
-    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-  }
-  LOG_DEBUG("Client %s gzip encoding", (creq.gzip_accepted ? "accepts" : "does not accept"));
-}
-
-static int
-handleServerEvent(TSCont contp, TSEvent event, void *edata)
-{
-  InterceptData *int_data = static_cast<InterceptData *>(TSContDataGet(contp));
-  bool write_response = false;
-
-  switch (event) {
-  case TS_EVENT_NET_ACCEPT_FAILED:
-    LOG_DEBUG("Received net accept failed event; going to abort continuation");
-    int_data->read_complete = int_data->write_complete = true;
-    break;
-
-  case TS_EVENT_NET_ACCEPT:
-    LOG_DEBUG("Received net accept event");
-    if (!initRequestProcessing(*int_data, edata, write_response)) {
-      LOG_ERROR("Could not initialize request processing");
-      return 0;
-    }
-    break;
-
-  case TS_EVENT_VCONN_READ_READY:
-    LOG_DEBUG("Received read ready event");
-    if (!readInterceptRequest(*int_data)) {
-      LOG_ERROR("Error while reading from input vio");
-      return 0;
-    }
-    break;
-
-  case TS_EVENT_VCONN_READ_COMPLETE:
-  case TS_EVENT_VCONN_EOS:
-    LOG_DEBUG("Received read complete/eos event %d", event);
-    int_data->read_complete = true;
-    break;
-
-  case TS_EVENT_VCONN_WRITE_READY:
-    LOG_DEBUG("Received write ready event");
-    break;
-
-  case TS_EVENT_VCONN_WRITE_COMPLETE:
-    LOG_DEBUG("Received write complete event");
-    int_data->write_complete = true;
-    break;
-
-  case TS_EVENT_ERROR:
-    LOG_ERROR("Received error event!");
-    break;
-
-  default:
-    if (int_data->fetcher && int_data->fetcher->isFetchEvent(event)) {
-      if (!int_data->fetcher->handleFetchEvent(event, edata)) {
-        LOG_ERROR("Couldn't handle fetch request event %d", event);
-      }
-      write_response = int_data->fetcher->isFetchComplete();
-    } else {
-      LOG_DEBUG("Unexpected event %d", event);
-    }
-    break;
-  }
-
-  if (write_response) {
-    if (!writeResponse(*int_data)) {
-      LOG_ERROR("Couldn't write response");
-      int_data->write_complete = true;
-    } else {
-      LOG_DEBUG("Wrote response successfully");
-    }
-  }
-
-  if (int_data->read_complete && int_data->write_complete) {
-    LOG_DEBUG("Completed request processing. Shutting down...");
-    delete int_data;
-    TSContDestroy(contp);
-  }
-
-  return 1;
-}
-
-static bool
-initRequestProcessing(InterceptData &int_data, void *edata, bool &write_response)
-{
-  TSAssert(int_data.initialized == false);
-  if (!int_data.init(static_cast<TSVConn>(edata))) {
-    LOG_ERROR("Could not initialize intercept data!");
-    return false;
-  }
-
-  if (int_data.creq.status == TS_HTTP_STATUS_OK) {
-    for (StringList::iterator iter = int_data.creq.file_urls.begin();
-         iter != int_data.creq.file_urls.end(); ++iter) {
-      if (!int_data.fetcher->addFetchRequest(*iter)) {
-        LOG_ERROR("Couldn't add fetch request for URL [%s]", iter->c_str());
-      } else {
-        LOG_DEBUG("Added fetch request for URL [%s]", iter->c_str());
-      }
-    }
-  } else {
-    LOG_DEBUG("Client request status [%d] not ok; Not fetching URLs", int_data.creq.status);
-    write_response = true;
-  }
-  return true;
-}
-
-static bool
-readInterceptRequest(InterceptData &int_data)
-{
-  TSAssert(!int_data.read_complete);
-  int avail = TSIOBufferReaderAvail(int_data.input.reader);
-  if (avail == TS_ERROR) {
-    LOG_ERROR("Error while getting number of bytes available");
-    return false;
-  }
-  
-  int consumed = 0;
-  if (avail > 0) {
-    int64_t data_len;
-    const char *data;
-    TSIOBufferBlock block = TSIOBufferReaderStart(int_data.input.reader);
-    while (block != NULL) {
-      data = TSIOBufferBlockReadStart(block, int_data.input.reader, &data_len);
-      const char *endptr = data + data_len;
-      if (TSHttpHdrParseReq(int_data.http_parser, int_data.req_hdr_bufp, int_data.req_hdr_loc,
-                             &data, endptr) == TS_PARSE_DONE) {
-        int_data.read_complete = true;
-      }
-      consumed += data_len;
-      block = TSIOBufferBlockNext(block);
-    }
-  }
-  LOG_DEBUG("Consumed %d bytes from input vio", consumed);
-  
-  TSIOBufferReaderConsume(int_data.input.reader, consumed);
-  
-  // Modify the input VIO to reflect how much data we've completed.
-  TSVIONDoneSet(int_data.input.vio, TSVIONDoneGet(int_data.input.vio) + consumed);
-
-  if (!int_data.read_complete) {
-    LOG_DEBUG("Re-enabling input VIO as request header not completely read yet");
-    TSVIOReenable(int_data.input.vio);
-  }
-  return true;
-}
-
-static const string OK_REPLY_LINE("HTTP/1.0 200 OK\r\n");
-static const string BAD_REQUEST_RESPONSE("HTTP/1.0 400 Bad Request\r\n\r\n");
-static const string ERROR_REPLY_RESPONSE("HTTP/1.0 500 Internal Server Error\r\n\r\n");
-static const string FORBIDDEN_RESPONSE("HTTP/1.0 403 Forbidden\r\n\r\n");
-static const char GZIP_ENCODING_FIELD[] = { "Content-Encoding: gzip\r\n" };
-static const int GZIP_ENCODING_FIELD_SIZE = sizeof(GZIP_ENCODING_FIELD) - 1;
-
-static bool
-writeResponse(InterceptData &int_data)
-{
-  int_data.setupWrite();
-  
-  ByteBlockList body_blocks;
-  string resp_header_fields;
-  prepareResponse(int_data, body_blocks, resp_header_fields);
-
-  int n_bytes_written = 0;
-  if (int_data.creq.status != TS_HTTP_STATUS_OK) {
-    if (!writeErrorResponse(int_data, n_bytes_written)) {
-      LOG_ERROR("Couldn't write response error");
-      return false;
-    }
-  } else {
-    n_bytes_written = OK_REPLY_LINE.size();
-    if (TSIOBufferWrite(int_data.output.buffer, OK_REPLY_LINE.data(), n_bytes_written) == TS_ERROR) {
-      LOG_ERROR("Error while writing reply line");
-      return false;
-    }
-    
-    if (!writeStandardHeaderFields(int_data, n_bytes_written)) {
-      LOG_ERROR("Could not write standard header fields");
-      return false;
-    }
-    
-    if (resp_header_fields.size()) {
-      if (TSIOBufferWrite(int_data.output.buffer, resp_header_fields.data(),
-                           resp_header_fields.size()) == TS_ERROR) {
-        LOG_ERROR("Error while writing additional response header fields");
-        return false;
-      }
-      n_bytes_written += resp_header_fields.size();
-    }
-    
-    if (TSIOBufferWrite(int_data.output.buffer, "\r\n", 2) == TS_ERROR) {
-      LOG_ERROR("Error while writing header terminator");
-      return false;
-    }
-    n_bytes_written += 2;
-    
-    for (ByteBlockList::iterator iter = body_blocks.begin(); iter != body_blocks.end(); ++iter) {
-      if (TSIOBufferWrite(int_data.output.buffer, iter->data, iter->data_len) == TS_ERROR) {
-        LOG_ERROR("Error while writing content");
-        return false;
-      }
-      n_bytes_written += iter->data_len;
-    }
-  }
-    
-  LOG_DEBUG("Wrote reply of size %d", n_bytes_written);
-  TSVIONBytesSet(int_data.output.vio, n_bytes_written);
-  
-  TSVIOReenable(int_data.output.vio);
-  return true;
-}
-
-static void
-prepareResponse(InterceptData &int_data, ByteBlockList &body_blocks, string &resp_header_fields)
-{
-  bool got_content_type = false;
-
-  if (int_data.creq.status == TS_HTTP_STATUS_OK) {
-    HttpDataFetcherImpl::ResponseData resp_data;
-    TSMLoc field_loc;
-    time_t expires_time;
-    bool got_expires_time = false;
-    for (StringList::iterator iter = int_data.creq.file_urls.begin(); iter != int_data.creq.file_urls.end();
-         ++iter) {
-      if (int_data.fetcher->getData(*iter, resp_data)) {
-        body_blocks.push_back(ByteBlock(resp_data.content, resp_data.content_len));
-        if (!got_content_type) {
-          got_content_type = getContentType(resp_data.bufp, resp_data.hdr_loc, resp_header_fields);
-        }
-        field_loc = TSMimeHdrFieldFind(resp_data.bufp, resp_data.hdr_loc, TS_MIME_FIELD_EXPIRES,
-                                        TS_MIME_LEN_EXPIRES);
-        if (field_loc != TS_NULL_MLOC) {
-          time_t curr_field_expires_time;
-          int n_values = TSMimeHdrFieldValuesCount(resp_data.bufp, resp_data.hdr_loc, field_loc);
-          if ((n_values != TS_ERROR) && (n_values > 0)) {
-            curr_field_expires_time = TSMimeHdrFieldValueDateGet(resp_data.bufp, resp_data.hdr_loc, field_loc);
-            if (!got_expires_time) {
-              expires_time = curr_field_expires_time;
-              got_expires_time = true;
-            } else if (curr_field_expires_time < expires_time) {
-              expires_time = curr_field_expires_time;
-            }
-          }
-          TSHandleMLocRelease(resp_data.bufp, resp_data.hdr_loc, field_loc);
-        }
-      } else {
-        LOG_ERROR("Could not get content for requested URL [%s]", iter->c_str());
-        int_data.creq.status = TS_HTTP_STATUS_BAD_REQUEST;
-        break;
-      }
-    }
-    if (int_data.creq.status == TS_HTTP_STATUS_OK) {
-      if (got_expires_time) {
-        if (expires_time <= 0) {
-          resp_header_fields.append("Expires: 0\r\n");
-        } else {
-          char line_buf[128];
-          int line_size = strftime(line_buf, 128, "Expires: %a, %d %b %Y %T GMT\r\n", gmtime(&expires_time));
-          resp_header_fields.append(line_buf, line_size);
-        }
-      }
-      LOG_DEBUG("Prepared response header field\n%s", resp_header_fields.c_str());
-    }
-  }
-
-  if ((int_data.creq.status == TS_HTTP_STATUS_OK) && int_data.creq.gzip_accepted) {
-    if (!gzip(body_blocks, int_data.gzipped_data)) {
-      LOG_ERROR("Could not gzip content!");
-      int_data.creq.status = TS_HTTP_STATUS_INTERNAL_SERVER_ERROR;
-    } else {
-      body_blocks.clear();
-      body_blocks.push_back(ByteBlock(int_data.gzipped_data.data(), int_data.gzipped_data.size()));
-      resp_header_fields.append(GZIP_ENCODING_FIELD, GZIP_ENCODING_FIELD_SIZE);
-    }
-  }
-}
-
-static bool
-getContentType(TSMBuffer bufp, TSMLoc hdr_loc, string &resp_header_fields)
-{
-  bool retval = false;
-  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_TYPE,
-                                          TS_MIME_LEN_CONTENT_TYPE);
-  if (field_loc != TS_NULL_MLOC) {
-    bool values_added = false;
-    const char *value;
-    int value_len;
-    int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
-    for (int i = 0; i < n_values; ++i) {
-      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
-      if (!values_added) {
-        resp_header_fields.append("Content-Type: ");
-        values_added = true;
-      } else {
-        resp_header_fields.append(", ");
-      }
-      resp_header_fields.append(value, value_len);
-    }
-    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-    if (values_added) {
-      resp_header_fields.append("\r\n");
-      retval = true;
-    }
-  }
-  return retval;
-}
-
-static const char INVARIANT_FIELD_LINES[] = { "Vary: Accept-Encoding\r\n"
-                                              "Cache-Control: max-age=315360000\r\n" };
-static const char INVARIANT_FIELD_LINES_SIZE = sizeof(INVARIANT_FIELD_LINES) - 1;
-
-static bool
-writeStandardHeaderFields(InterceptData &int_data, int &n_bytes_written)
-{
-  if (TSIOBufferWrite(int_data.output.buffer, INVARIANT_FIELD_LINES,
-                       INVARIANT_FIELD_LINES_SIZE) == TS_ERROR) {
-    LOG_ERROR("Error while writing invariant fields");
-    return false;
-  }
-  n_bytes_written += INVARIANT_FIELD_LINES_SIZE;
-  time_t time_now = static_cast<time_t>(TShrtime() / 1000000000); // it returns nanoseconds!
-  char last_modified_line[128];
-  int last_modified_line_size = strftime(last_modified_line, 128, "Last-Modified: %a, %d %b %Y %T GMT\r\n",
-                                         gmtime(&time_now));
-  if (TSIOBufferWrite(int_data.output.buffer, last_modified_line, last_modified_line_size) == TS_ERROR) {
-    LOG_ERROR("Error while writing last-modified fields");
-    return false;
-  }
-  n_bytes_written += last_modified_line_size;
-  return true;
-}
-
-static bool
-writeErrorResponse(InterceptData &int_data, int &n_bytes_written)
-{
-  const string *response;
-  switch (int_data.creq.status) {
-  case TS_HTTP_STATUS_BAD_REQUEST:
-    response = &BAD_REQUEST_RESPONSE;
-    break;
-  case TS_HTTP_STATUS_FORBIDDEN:
-    response = &FORBIDDEN_RESPONSE;
-    break;
-  default:
-    response = &ERROR_REPLY_RESPONSE;
-    break;
-  } 
-  if (TSIOBufferWrite(int_data.output.buffer, response->data(), response->size()) == TS_ERROR) {
-    LOG_ERROR("Error while writing error response");
-    return false;
-  }
-  n_bytes_written += response->size();
-  return true;
-}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee04a10d/plugins/experimental/esi/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/Makefile.am b/plugins/experimental/esi/Makefile.am
index 39e2af8..e9398da 100644
--- a/plugins/experimental/esi/Makefile.am
+++ b/plugins/experimental/esi/Makefile.am
@@ -25,7 +25,7 @@ AM_CXXFLAGS = \
   -I$(top_srcdir)/proxy/api \
   -Wno-deprecated
 
-pkglib_LTLIBRARIES = esi.la
+pkglib_LTLIBRARIES = esi.la combo_handler.la
 lib_LTLIBRARIES = libesi.la libtest.la
 
 check_PROGRAMS = docnode_test parser_test processor_test utils_test vars_test
@@ -57,13 +57,20 @@ libtest_la_SOURCES = \
 	lib/gzip.cc
 
 esi_la_SOURCES =  \
+	esi.cc \
 	fetcher/HttpDataFetcherImpl.cc \
-	plugin.cc \
 	serverIntercept.cc
 
+combo_handler_la_SOURCES = \
+	combo_handler.cc \
+	fetcher/HttpDataFetcherImpl.cc
+
 esi_la_LIBADD = libesi.la
 esi_la_LDFLAGS = -module -avoid-version -shared
 
+combo_handler_la_LIBADD = libesi.la
+combo_handler_la_LDFLAGS = -module -avoid-version -shared
+
 docnode_test_SOURCES = test/docnode_test.cc test/print_funcs.cc
 docnode_test_LDADD = libesi.la  @LIBDL@ -lz -lpthread
 parser_test_SOURCES = test/parser_test.cc test/print_funcs.cc

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee04a10d/plugins/experimental/esi/combo_handler.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/combo_handler.cc b/plugins/experimental/esi/combo_handler.cc
new file mode 100644
index 0000000..19e17e9
--- /dev/null
+++ b/plugins/experimental/esi/combo_handler.cc
@@ -0,0 +1,842 @@
+/** @file
+
+    ATS plugin to do combo handling.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#include <list>
+#include <string>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include "ts/ts.h"
+#include "ts/experimental.h"
+
+#include "HttpDataFetcherImpl.h"
+#include "gzip.h"
+#include "Utils.h"
+
+
+using namespace std;
+using namespace EsiLib;
+
+#define DEBUG_TAG "combo_handler"
+
+static string SIG_KEY_NAME;
+
+#define DEFAULT_COMBO_HANDLER_PATH "admin/v1/combo"
+static string COMBO_HANDLER_PATH;
+static int COMBO_HANDLER_PATH_SIZE;
+
+#define LOG_ERROR(fmt, args...) do {                                    \
+    TSError("[%s:%d] [%s] ERROR: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
+    TSDebug(DEBUG_TAG, "[%s:%d] [%s] ERROR: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
+  } while (0)
+
+#define LOG_DEBUG(fmt, args...) do {                                    \
+    TSDebug(DEBUG_TAG, "[%s:%d] [%s] DEBUG: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
+  } while (0)
+
+typedef list<string> StringList;
+
+struct ClientRequest {
+  TSHttpStatus status;
+  const sockaddr *client_addr;
+  StringList file_urls;
+  bool gzip_accepted;
+  string defaultBucket;	//default Bucket is set to l
+  ClientRequest()
+    : status(TS_HTTP_STATUS_OK), client_addr(NULL), gzip_accepted(false), defaultBucket("l") { };
+};
+
+struct InterceptData {
+  TSVConn net_vc;
+  TSCont contp;
+
+  struct IoHandle {
+    TSVIO vio;
+    TSIOBuffer buffer;
+    TSIOBufferReader reader;
+
+    IoHandle()
+      : vio(0), buffer(0), reader(0) { };
+
+    ~IoHandle() {
+      if (reader) {
+        TSIOBufferReaderFree(reader);
+      }
+      if (buffer) {
+        TSIOBufferDestroy(buffer);
+      }
+    };
+  };
+
+  IoHandle input;
+  IoHandle output;
+  TSHttpParser http_parser;
+
+  string body;
+  TSMBuffer req_hdr_bufp;
+  TSMLoc req_hdr_loc;
+  bool req_hdr_parsed;
+  bool initialized;
+  ClientRequest creq;
+  HttpDataFetcherImpl *fetcher;
+  bool read_complete;
+  bool write_complete;
+  string gzipped_data;
+  
+  InterceptData(TSCont cont) 
+    : net_vc(0), contp(cont), input(), output(), req_hdr_bufp(0), req_hdr_loc(0), req_hdr_parsed(false),
+      initialized(false), fetcher(0), read_complete(false), write_complete(false) {
+    http_parser = TSHttpParserCreate();
+  }
+
+  bool init(TSVConn vconn);
+  void setupWrite();
+
+  ~InterceptData();
+};
+
+bool
+InterceptData::init(TSVConn vconn)
+{
+  if (initialized) {
+    LOG_ERROR("InterceptData already initialized!");
+    return false;
+  }
+  
+  net_vc = vconn;
+
+  input.buffer = TSIOBufferCreate();
+  input.reader = TSIOBufferReaderAlloc(input.buffer);
+  input.vio = TSVConnRead(net_vc, contp, input.buffer, INT64_MAX);
+
+  req_hdr_bufp = TSMBufferCreate();
+  req_hdr_loc = TSHttpHdrCreate(req_hdr_bufp);
+  TSHttpHdrTypeSet(req_hdr_bufp, req_hdr_loc, TS_HTTP_TYPE_REQUEST);
+
+  fetcher = new HttpDataFetcherImpl(contp, creq.client_addr, "combohandler_fetcher");
+
+  initialized = true;
+  LOG_DEBUG("InterceptData initialized!");
+  return true;
+}
+
+void
+InterceptData::setupWrite()
+{
+  TSAssert(output.buffer == 0);
+  output.buffer = TSIOBufferCreate();
+  output.reader = TSIOBufferReaderAlloc(output.buffer);
+  output.vio = TSVConnWrite(net_vc, contp, output.reader, INT64_MAX);
+}
+
+InterceptData::~InterceptData()
+{
+  if (req_hdr_loc) {
+    TSHandleMLocRelease(req_hdr_bufp, TS_NULL_MLOC, req_hdr_loc);
+  }
+  if (req_hdr_bufp) {
+    TSMBufferDestroy(req_hdr_bufp);
+  }
+  if (fetcher) {
+    delete fetcher;
+  }
+  TSHttpParserDestroy(http_parser); 
+  if (net_vc) {
+    TSVConnClose(net_vc);
+  }
+}
+
+// forward declarations
+static int handleReadRequestHeader(TSCont contp, TSEvent event, void *edata);
+static bool isComboHandlerRequest(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc);
+static void getClientRequest(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc,
+                             ClientRequest &creq);
+static void parseQueryParameters(const char *query, int query_len, ClientRequest &creq);
+static void checkGzipAcceptance(TSMBuffer bufp, TSMLoc hdr_loc, ClientRequest &creq);
+static int handleServerEvent(TSCont contp, TSEvent event, void *edata);
+static bool initRequestProcessing(InterceptData &int_data, void *edata, bool &write_response);
+static bool readInterceptRequest(InterceptData &int_data);
+static bool writeResponse(InterceptData &int_data);
+static bool writeErrorResponse(InterceptData &int_data, int &n_bytes_written);
+static bool writeStandardHeaderFields(InterceptData &int_data, int &n_bytes_written);
+static void prepareResponse(InterceptData &int_data, ByteBlockList &body_blocks, string &resp_header_fields);
+static bool getContentType(TSMBuffer bufp, TSMLoc hdr_loc, string &resp_header_fields);
+static bool getDefaultBucket(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_obj, ClientRequest &creq);
+
+
+void
+TSPluginInit(int argc, const char *argv[])
+{
+  if ((argc > 1) && (strcmp(argv[1], "-") != 0)) {
+    COMBO_HANDLER_PATH =  argv[1];
+    if (COMBO_HANDLER_PATH == "/") {
+      COMBO_HANDLER_PATH.clear();
+    } else {
+      if (COMBO_HANDLER_PATH[0] == '/') {
+        COMBO_HANDLER_PATH.erase(0, 1);
+      }
+      if (COMBO_HANDLER_PATH[COMBO_HANDLER_PATH.size() - 1] == '/') {
+        COMBO_HANDLER_PATH.erase(COMBO_HANDLER_PATH.size() - 1, 1);
+      }
+    }
+  } else {
+    COMBO_HANDLER_PATH = DEFAULT_COMBO_HANDLER_PATH;
+  }
+  COMBO_HANDLER_PATH_SIZE = static_cast<int>(COMBO_HANDLER_PATH.size());
+  LOG_DEBUG("Combo handler path is [%s]", COMBO_HANDLER_PATH.c_str());
+
+  SIG_KEY_NAME = ((argc > 2) && (strcmp(argv[2], "-") != 0)) ? argv[2] : "";
+  LOG_DEBUG("Signature key is [%s]", SIG_KEY_NAME.c_str());
+
+  TSCont rrh_contp = TSContCreate(handleReadRequestHeader, NULL);
+  if (!rrh_contp) {
+    LOG_ERROR("Could not create read request header continuation");
+    return;
+  }
+  TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, rrh_contp);
+
+  Utils::init(&TSDebug, &TSError);
+  LOG_DEBUG("Plugin started");
+}
+
+static int
+handleReadRequestHeader(TSCont contp, TSEvent event, void *edata)
+{
+  TSAssert(event == TS_EVENT_HTTP_READ_REQUEST_HDR);
+
+  LOG_DEBUG("handling read request header event...");
+  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
+  TSEvent reenable_to_event = TS_EVENT_HTTP_CONTINUE;
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+
+  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) {
+    TSMLoc url_loc;
+    if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) {
+      if (isComboHandlerRequest(bufp, hdr_loc, url_loc)) {
+        TSCont contp = TSContCreate(handleServerEvent, TSMutexCreate());
+        if (!contp) {
+          LOG_ERROR("[%s] Could not create intercept request", __FUNCTION__);
+          reenable_to_event = TS_EVENT_HTTP_ERROR;
+        } else {
+          TSHttpTxnServerIntercept(contp, txnp);
+          InterceptData *int_data = new InterceptData(contp);
+          TSContDataSet(contp, int_data);
+          // todo: check if these two cacheable sets are required
+          TSHttpTxnReqCacheableSet(txnp, 1);
+          TSHttpTxnRespCacheableSet(txnp, 1);
+          getClientRequest(txnp, bufp, hdr_loc, url_loc, int_data->creq);
+          LOG_DEBUG("Setup server intercept to handle client request");
+        }
+      }
+      TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+    } else {
+      LOG_ERROR("Could not get request URL");
+    }
+    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+  } else {
+    LOG_ERROR("Could not get client request");
+  }
+
+  TSHttpTxnReenable(txnp, reenable_to_event);
+  return 1;
+}
+
+static bool
+isComboHandlerRequest(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc)
+{
+  int method_len;
+  bool retval = false;
+  const char *method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
+
+  if (!method) {
+    LOG_ERROR("Could not obtain method!");
+  } else {
+    if ((method_len != TS_HTTP_LEN_GET) || (strncasecmp(method, TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET) != 0)) {
+      LOG_DEBUG("Unsupported method [%.*s]", method_len, method);
+    } else {
+      retval = true;
+    }
+
+    if (retval) {
+      int path_len;
+      const char *path = TSUrlPathGet(bufp, url_loc, &path_len);
+      if (!path) {
+        LOG_ERROR("Could not get path from request URL");
+        retval = false;
+      } else {
+        retval = (path_len == COMBO_HANDLER_PATH_SIZE) &&
+          (strncasecmp(path, COMBO_HANDLER_PATH.c_str(), COMBO_HANDLER_PATH_SIZE) == 0);
+        LOG_DEBUG("Path [%.*s] is %s combo handler path", path_len, path, (retval ? "a" : "not a"));
+      }
+    }
+  }
+  return retval;
+}
+
+static bool
+getDefaultBucket(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_obj, ClientRequest &creq)
+{
+  LOG_DEBUG("In getDefaultBucket");
+  TSMLoc field_loc;
+  const char* host;
+  int host_len = 0;
+  bool defaultBucketFound = false;
+
+  field_loc = TSMimeHdrFieldFind(bufp, hdr_obj, TS_MIME_FIELD_HOST, -1);
+  if (field_loc == TS_NULL_MLOC) {
+    LOG_ERROR("Host field not found.");
+    return false;
+  }
+
+  host = TSMimeHdrFieldValueStringGet(bufp, hdr_obj, field_loc, 0, &host_len);
+  if (!host || host_len <= 0) {
+    LOG_ERROR("Error Extracting Host Header");
+    TSHandleMLocRelease (bufp, hdr_obj, field_loc);
+    return false;
+  }
+
+  LOG_DEBUG("host: %.*s", host_len, host);
+  for(int i = 0 ; i < host_len; i++)
+    {
+      if (host[i] == '.')
+        {
+          creq.defaultBucket = string(host, i);
+          defaultBucketFound = true;
+          break;
+        }
+    }
+
+  TSHandleMLocRelease (bufp, hdr_obj, field_loc);
+
+  LOG_DEBUG("defaultBucket: %s", creq.defaultBucket.data());
+  return defaultBucketFound;
+}
+
+static void
+getClientRequest(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc, ClientRequest &creq)
+{
+  int query_len;
+  const char *query = TSUrlHttpQueryGet(bufp, url_loc, &query_len);
+
+  if (!query) {
+    LOG_ERROR("Could not get query from request URL");
+  } else {
+    if (!getDefaultBucket(txnp, bufp, hdr_loc, creq))
+      {
+        LOG_ERROR("failed getting Default Bucket for the request");
+        return;
+      }
+    parseQueryParameters(query, query_len, creq);
+    creq.client_addr = TSHttpTxnClientAddrGet(txnp);
+    checkGzipAcceptance(bufp, hdr_loc, creq);
+  }
+}
+
+static void
+parseQueryParameters(const char *query, int query_len, ClientRequest &creq)
+{
+  creq.status = TS_HTTP_STATUS_OK;
+  int param_start_pos = 0;
+  bool sig_verified = false;
+  int colon_pos = -1;
+  string file_url("http://localhost/");
+  size_t file_base_url_size = file_url.size();
+  const char *common_prefix = 0;
+  int common_prefix_size = 0;
+  const char *common_prefix_path = 0;
+  int common_prefix_path_size = 0;
+
+  for (int i = 0; i <= query_len; ++i) {
+    if ((i == query_len) || (query[i] == '&') || (query[i] == '?')) {
+      int param_len = i - param_start_pos;
+      if (param_len) {
+        const char *param = query + param_start_pos;
+        if ((param_len >= 4) && (strncmp(param, "sig=", 4) == 0)) {
+          if (SIG_KEY_NAME.size()) {
+            if (!param_start_pos) {
+              LOG_DEBUG("Signature cannot be the first parameter in query [%.*s]", query_len, query);
+            } else if (param_len == 4) {
+              LOG_DEBUG("Signature empty in query [%.*s]", query_len, query);
+            } else {
+              // TODO - really verify the signature
+              LOG_DEBUG("Verified signature successfully");
+              sig_verified = true;
+            }
+            if (!sig_verified) {
+              LOG_DEBUG("Signature [%.*s] on query [%.*s] is invalid", param_len - 4, param + 4,
+                        param_start_pos, query);
+            }
+          } else {
+            LOG_DEBUG("Verification not configured; ignoring signature...");
+          }
+          break; // nothing useful after the signature
+      }
+      if ((param_len >= 2) && (param[0] == 'p') && (param[1] == '=')) {
+        common_prefix_size = param_len - 2;
+        common_prefix_path_size = 0;
+        if (common_prefix_size) {
+          common_prefix = param + 2;
+          for (int i = 0; i < common_prefix_size; ++i) {
+            if (common_prefix[i] == ':') {
+              common_prefix_path = common_prefix;
+              common_prefix_path_size = i;
+              ++i; // go beyond the ':'
+              common_prefix += i;
+              common_prefix_size -= i;
+              break;
+            }
+          }
+        }
+        LOG_DEBUG("Common prefix is [%.*s], common prefix path is [%.*s]", common_prefix_size, common_prefix,
+                  common_prefix_path_size, common_prefix_path);
+      }
+      else {
+        if (common_prefix_path_size) {
+          if (colon_pos >= param_start_pos) { // we have a colon in this param as well?
+            LOG_ERROR("Ambiguous 'bucket': [%.*s] specified in common prefix and [%.*s] specified in "
+                      "current parameter [%.*s]", common_prefix_path_size, common_prefix_path,
+                      colon_pos - param_start_pos, param, param_len, param);
+            creq.file_urls.clear();
+            break;
+          }
+          file_url.append(common_prefix_path, common_prefix_path_size);
+        }
+        else if (colon_pos >= param_start_pos) { // we have a colon
+          if ((colon_pos == param_start_pos) || (colon_pos == (i - 1))) {
+            LOG_ERROR("Colon-separated path [%.*s] has empty part(s)", param_len, param);
+            creq.file_urls.clear();
+            break;
+          }
+          file_url.append(param, colon_pos - param_start_pos); // appending pre ':' part first
+            
+          // modify these to point to the "actual" file path
+          param_start_pos = colon_pos + 1;
+          param_len = i - param_start_pos;
+          param = query + param_start_pos;
+        } else {
+          file_url += creq.defaultBucket; // default path
+        }
+        file_url += '/';
+        if (common_prefix_size) {
+          file_url.append(common_prefix, common_prefix_size);
+        }
+        file_url.append(param, param_len);
+        creq.file_urls.push_back(file_url);
+        LOG_DEBUG("Added file path [%s]", file_url.c_str());
+        file_url.resize(file_base_url_size);
+      }
+    }
+    param_start_pos = i + 1;
+  } else if (query[i] == ':') {
+    colon_pos = i;
+  }
+}
+if (!creq.file_urls.size()) {
+  creq.status = TS_HTTP_STATUS_BAD_REQUEST;
+ } else if (SIG_KEY_NAME.size() && !sig_verified) {
+  LOG_DEBUG("Invalid/empty signature found; Need valid signature");
+  creq.status = TS_HTTP_STATUS_FORBIDDEN;
+  creq.file_urls.clear();
+ }
+}
+
+static void
+checkGzipAcceptance(TSMBuffer bufp, TSMLoc hdr_loc, ClientRequest &creq)
+{
+  creq.gzip_accepted = false;
+  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_ACCEPT_ENCODING,
+                                          TS_MIME_LEN_ACCEPT_ENCODING);
+  if (field_loc != TS_NULL_MLOC) {
+    const char *value;
+    int value_len;
+    int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
+
+    for (int i = 0; i < n_values; ++i) {
+      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
+      if (value) {
+        if ((value_len == TS_HTTP_LEN_GZIP) && (strncasecmp(value, TS_HTTP_VALUE_GZIP, value_len) == 0)) {
+          creq.gzip_accepted = true;
+        }
+      } else {
+        LOG_DEBUG("Error while getting value # %d of header [%.*s]", i, TS_MIME_LEN_ACCEPT_ENCODING,
+                  TS_MIME_FIELD_ACCEPT_ENCODING);
+      }
+      if (creq.gzip_accepted) {
+        break;
+      }
+    }
+    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+  }
+  LOG_DEBUG("Client %s gzip encoding", (creq.gzip_accepted ? "accepts" : "does not accept"));
+}
+
+static int
+handleServerEvent(TSCont contp, TSEvent event, void *edata)
+{
+  InterceptData *int_data = static_cast<InterceptData *>(TSContDataGet(contp));
+  bool write_response = false;
+
+  switch (event) {
+  case TS_EVENT_NET_ACCEPT_FAILED:
+    LOG_DEBUG("Received net accept failed event; going to abort continuation");
+    int_data->read_complete = int_data->write_complete = true;
+    break;
+
+  case TS_EVENT_NET_ACCEPT:
+    LOG_DEBUG("Received net accept event");
+    if (!initRequestProcessing(*int_data, edata, write_response)) {
+      LOG_ERROR("Could not initialize request processing");
+      return 0;
+    }
+    break;
+
+  case TS_EVENT_VCONN_READ_READY:
+    LOG_DEBUG("Received read ready event");
+    if (!readInterceptRequest(*int_data)) {
+      LOG_ERROR("Error while reading from input vio");
+      return 0;
+    }
+    break;
+
+  case TS_EVENT_VCONN_READ_COMPLETE:
+  case TS_EVENT_VCONN_EOS:
+    LOG_DEBUG("Received read complete/eos event %d", event);
+    int_data->read_complete = true;
+    break;
+
+  case TS_EVENT_VCONN_WRITE_READY:
+    LOG_DEBUG("Received write ready event");
+    break;
+
+  case TS_EVENT_VCONN_WRITE_COMPLETE:
+    LOG_DEBUG("Received write complete event");
+    int_data->write_complete = true;
+    break;
+
+  case TS_EVENT_ERROR:
+    LOG_ERROR("Received error event!");
+    break;
+
+  default:
+    if (int_data->fetcher && int_data->fetcher->isFetchEvent(event)) {
+      if (!int_data->fetcher->handleFetchEvent(event, edata)) {
+        LOG_ERROR("Couldn't handle fetch request event %d", event);
+      }
+      write_response = int_data->fetcher->isFetchComplete();
+    } else {
+      LOG_DEBUG("Unexpected event %d", event);
+    }
+    break;
+  }
+
+  if (write_response) {
+    if (!writeResponse(*int_data)) {
+      LOG_ERROR("Couldn't write response");
+      int_data->write_complete = true;
+    } else {
+      LOG_DEBUG("Wrote response successfully");
+    }
+  }
+
+  if (int_data->read_complete && int_data->write_complete) {
+    LOG_DEBUG("Completed request processing. Shutting down...");
+    delete int_data;
+    TSContDestroy(contp);
+  }
+
+  return 1;
+}
+
+static bool
+initRequestProcessing(InterceptData &int_data, void *edata, bool &write_response)
+{
+  TSAssert(int_data.initialized == false);
+  if (!int_data.init(static_cast<TSVConn>(edata))) {
+    LOG_ERROR("Could not initialize intercept data!");
+    return false;
+  }
+
+  if (int_data.creq.status == TS_HTTP_STATUS_OK) {
+    for (StringList::iterator iter = int_data.creq.file_urls.begin();
+         iter != int_data.creq.file_urls.end(); ++iter) {
+      if (!int_data.fetcher->addFetchRequest(*iter)) {
+        LOG_ERROR("Couldn't add fetch request for URL [%s]", iter->c_str());
+      } else {
+        LOG_DEBUG("Added fetch request for URL [%s]", iter->c_str());
+      }
+    }
+  } else {
+    LOG_DEBUG("Client request status [%d] not ok; Not fetching URLs", int_data.creq.status);
+    write_response = true;
+  }
+  return true;
+}
+
+static bool
+readInterceptRequest(InterceptData &int_data)
+{
+  TSAssert(!int_data.read_complete);
+  int avail = TSIOBufferReaderAvail(int_data.input.reader);
+  if (avail == TS_ERROR) {
+    LOG_ERROR("Error while getting number of bytes available");
+    return false;
+  }
+  
+  int consumed = 0;
+  if (avail > 0) {
+    int64_t data_len;
+    const char *data;
+    TSIOBufferBlock block = TSIOBufferReaderStart(int_data.input.reader);
+    while (block != NULL) {
+      data = TSIOBufferBlockReadStart(block, int_data.input.reader, &data_len);
+      const char *endptr = data + data_len;
+      if (TSHttpHdrParseReq(int_data.http_parser, int_data.req_hdr_bufp, int_data.req_hdr_loc,
+                             &data, endptr) == TS_PARSE_DONE) {
+        int_data.read_complete = true;
+      }
+      consumed += data_len;
+      block = TSIOBufferBlockNext(block);
+    }
+  }
+  LOG_DEBUG("Consumed %d bytes from input vio", consumed);
+  
+  TSIOBufferReaderConsume(int_data.input.reader, consumed);
+  
+  // Modify the input VIO to reflect how much data we've completed.
+  TSVIONDoneSet(int_data.input.vio, TSVIONDoneGet(int_data.input.vio) + consumed);
+
+  if (!int_data.read_complete) {
+    LOG_DEBUG("Re-enabling input VIO as request header not completely read yet");
+    TSVIOReenable(int_data.input.vio);
+  }
+  return true;
+}
+
+static const string OK_REPLY_LINE("HTTP/1.0 200 OK\r\n");
+static const string BAD_REQUEST_RESPONSE("HTTP/1.0 400 Bad Request\r\n\r\n");
+static const string ERROR_REPLY_RESPONSE("HTTP/1.0 500 Internal Server Error\r\n\r\n");
+static const string FORBIDDEN_RESPONSE("HTTP/1.0 403 Forbidden\r\n\r\n");
+static const char GZIP_ENCODING_FIELD[] = { "Content-Encoding: gzip\r\n" };
+static const int GZIP_ENCODING_FIELD_SIZE = sizeof(GZIP_ENCODING_FIELD) - 1;
+
+static bool
+writeResponse(InterceptData &int_data)
+{
+  int_data.setupWrite();
+  
+  ByteBlockList body_blocks;
+  string resp_header_fields;
+  prepareResponse(int_data, body_blocks, resp_header_fields);
+
+  int n_bytes_written = 0;
+  if (int_data.creq.status != TS_HTTP_STATUS_OK) {
+    if (!writeErrorResponse(int_data, n_bytes_written)) {
+      LOG_ERROR("Couldn't write response error");
+      return false;
+    }
+  } else {
+    n_bytes_written = OK_REPLY_LINE.size();
+    if (TSIOBufferWrite(int_data.output.buffer, OK_REPLY_LINE.data(), n_bytes_written) == TS_ERROR) {
+      LOG_ERROR("Error while writing reply line");
+      return false;
+    }
+    
+    if (!writeStandardHeaderFields(int_data, n_bytes_written)) {
+      LOG_ERROR("Could not write standard header fields");
+      return false;
+    }
+    
+    if (resp_header_fields.size()) {
+      if (TSIOBufferWrite(int_data.output.buffer, resp_header_fields.data(),
+                           resp_header_fields.size()) == TS_ERROR) {
+        LOG_ERROR("Error while writing additional response header fields");
+        return false;
+      }
+      n_bytes_written += resp_header_fields.size();
+    }
+    
+    if (TSIOBufferWrite(int_data.output.buffer, "\r\n", 2) == TS_ERROR) {
+      LOG_ERROR("Error while writing header terminator");
+      return false;
+    }
+    n_bytes_written += 2;
+    
+    for (ByteBlockList::iterator iter = body_blocks.begin(); iter != body_blocks.end(); ++iter) {
+      if (TSIOBufferWrite(int_data.output.buffer, iter->data, iter->data_len) == TS_ERROR) {
+        LOG_ERROR("Error while writing content");
+        return false;
+      }
+      n_bytes_written += iter->data_len;
+    }
+  }
+    
+  LOG_DEBUG("Wrote reply of size %d", n_bytes_written);
+  TSVIONBytesSet(int_data.output.vio, n_bytes_written);
+  
+  TSVIOReenable(int_data.output.vio);
+  return true;
+}
+
+static void
+prepareResponse(InterceptData &int_data, ByteBlockList &body_blocks, string &resp_header_fields)
+{
+  bool got_content_type = false;
+
+  if (int_data.creq.status == TS_HTTP_STATUS_OK) {
+    HttpDataFetcherImpl::ResponseData resp_data;
+    TSMLoc field_loc;
+    time_t expires_time;
+    bool got_expires_time = false;
+    for (StringList::iterator iter = int_data.creq.file_urls.begin(); iter != int_data.creq.file_urls.end();
+         ++iter) {
+      if (int_data.fetcher->getData(*iter, resp_data)) {
+        body_blocks.push_back(ByteBlock(resp_data.content, resp_data.content_len));
+        if (!got_content_type) {
+          got_content_type = getContentType(resp_data.bufp, resp_data.hdr_loc, resp_header_fields);
+        }
+        field_loc = TSMimeHdrFieldFind(resp_data.bufp, resp_data.hdr_loc, TS_MIME_FIELD_EXPIRES,
+                                        TS_MIME_LEN_EXPIRES);
+        if (field_loc != TS_NULL_MLOC) {
+          time_t curr_field_expires_time;
+          int n_values = TSMimeHdrFieldValuesCount(resp_data.bufp, resp_data.hdr_loc, field_loc);
+          if ((n_values != TS_ERROR) && (n_values > 0)) {
+            curr_field_expires_time = TSMimeHdrFieldValueDateGet(resp_data.bufp, resp_data.hdr_loc, field_loc);
+            if (!got_expires_time) {
+              expires_time = curr_field_expires_time;
+              got_expires_time = true;
+            } else if (curr_field_expires_time < expires_time) {
+              expires_time = curr_field_expires_time;
+            }
+          }
+          TSHandleMLocRelease(resp_data.bufp, resp_data.hdr_loc, field_loc);
+        }
+      } else {
+        LOG_ERROR("Could not get content for requested URL [%s]", iter->c_str());
+        int_data.creq.status = TS_HTTP_STATUS_BAD_REQUEST;
+        break;
+      }
+    }
+    if (int_data.creq.status == TS_HTTP_STATUS_OK) {
+      if (got_expires_time) {
+        if (expires_time <= 0) {
+          resp_header_fields.append("Expires: 0\r\n");
+        } else {
+          char line_buf[128];
+          int line_size = strftime(line_buf, 128, "Expires: %a, %d %b %Y %T GMT\r\n", gmtime(&expires_time));
+          resp_header_fields.append(line_buf, line_size);
+        }
+      }
+      LOG_DEBUG("Prepared response header field\n%s", resp_header_fields.c_str());
+    }
+  }
+
+  if ((int_data.creq.status == TS_HTTP_STATUS_OK) && int_data.creq.gzip_accepted) {
+    if (!gzip(body_blocks, int_data.gzipped_data)) {
+      LOG_ERROR("Could not gzip content!");
+      int_data.creq.status = TS_HTTP_STATUS_INTERNAL_SERVER_ERROR;
+    } else {
+      body_blocks.clear();
+      body_blocks.push_back(ByteBlock(int_data.gzipped_data.data(), int_data.gzipped_data.size()));
+      resp_header_fields.append(GZIP_ENCODING_FIELD, GZIP_ENCODING_FIELD_SIZE);
+    }
+  }
+}
+
+static bool
+getContentType(TSMBuffer bufp, TSMLoc hdr_loc, string &resp_header_fields)
+{
+  bool retval = false;
+  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_TYPE,
+                                          TS_MIME_LEN_CONTENT_TYPE);
+  if (field_loc != TS_NULL_MLOC) {
+    bool values_added = false;
+    const char *value;
+    int value_len;
+    int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
+    for (int i = 0; i < n_values; ++i) {
+      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
+      if (!values_added) {
+        resp_header_fields.append("Content-Type: ");
+        values_added = true;
+      } else {
+        resp_header_fields.append(", ");
+      }
+      resp_header_fields.append(value, value_len);
+    }
+    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+    if (values_added) {
+      resp_header_fields.append("\r\n");
+      retval = true;
+    }
+  }
+  return retval;
+}
+
+static const char INVARIANT_FIELD_LINES[] = { "Vary: Accept-Encoding\r\n"
+                                              "Cache-Control: max-age=315360000\r\n" };
+static const char INVARIANT_FIELD_LINES_SIZE = sizeof(INVARIANT_FIELD_LINES) - 1;
+
+static bool
+writeStandardHeaderFields(InterceptData &int_data, int &n_bytes_written)
+{
+  if (TSIOBufferWrite(int_data.output.buffer, INVARIANT_FIELD_LINES,
+                       INVARIANT_FIELD_LINES_SIZE) == TS_ERROR) {
+    LOG_ERROR("Error while writing invariant fields");
+    return false;
+  }
+  n_bytes_written += INVARIANT_FIELD_LINES_SIZE;
+  time_t time_now = static_cast<time_t>(TShrtime() / 1000000000); // it returns nanoseconds!
+  char last_modified_line[128];
+  int last_modified_line_size = strftime(last_modified_line, 128, "Last-Modified: %a, %d %b %Y %T GMT\r\n",
+                                         gmtime(&time_now));
+  if (TSIOBufferWrite(int_data.output.buffer, last_modified_line, last_modified_line_size) == TS_ERROR) {
+    LOG_ERROR("Error while writing last-modified fields");
+    return false;
+  }
+  n_bytes_written += last_modified_line_size;
+  return true;
+}
+
+static bool
+writeErrorResponse(InterceptData &int_data, int &n_bytes_written)
+{
+  const string *response;
+  switch (int_data.creq.status) {
+  case TS_HTTP_STATUS_BAD_REQUEST:
+    response = &BAD_REQUEST_RESPONSE;
+    break;
+  case TS_HTTP_STATUS_FORBIDDEN:
+    response = &FORBIDDEN_RESPONSE;
+    break;
+  default:
+    response = &ERROR_REPLY_RESPONSE;
+    break;
+  } 
+  if (TSIOBufferWrite(int_data.output.buffer, response->data(), response->size()) == TS_ERROR) {
+    LOG_ERROR("Error while writing error response");
+    return false;
+  }
+  n_bytes_written += response->size();
+  return true;
+}


[7/7] git commit: TS-1053 Move the README for combo handler

Posted by zw...@apache.org.
TS-1053 Move the README for combo handler


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/21515f60
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/21515f60
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/21515f60

Branch: refs/heads/master
Commit: 21515f600a33fd4b4e1e28bd88b0b69854180c5b
Parents: 0012eee
Author: Leif Hedstrom <zw...@apache.org>
Authored: Fri Apr 19 11:25:37 2013 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Fri Apr 19 11:25:37 2013 -0600

----------------------------------------------------------------------
 plugins/experimental/combo_handler/README |   84 ------------------------
 plugins/experimental/esi/README.combo     |   84 ++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/21515f60/plugins/experimental/combo_handler/README
----------------------------------------------------------------------
diff --git a/plugins/experimental/combo_handler/README b/plugins/experimental/combo_handler/README
deleted file mode 100644
index ea64bbd..0000000
--- a/plugins/experimental/combo_handler/README
+++ /dev/null
@@ -1,84 +0,0 @@
-Combohandler
---------------------
-
-This plugin provides that functionality (and more) with the same
-interface but with these differences in configuration:
-
-The arguments in the plugin.config line in order represent
-
-1) The path that should triggers combo handler (defaults to
-   "admin/v1/combo")
-
-2) The name of the key used for signature verification (disabled
-   by default)
-
-A "-" can be supplied as a value for any of these arguments to request
-default value be applied. 
-
-Also, just like the original combohandler, this plugin generates URLs
-of the form 'http://localhost/<dir>/<file-path>'. <dir> here defaults
-to 'l' unless specified by the file path in the query parameter using
-a colon. For example:
-
-http://combo.com/admin/v1/combo?filepath1&dir1:filepath2&filepath3
-
-Will result in these three pages being fetched:
-
-http://localhost/l/filepath1
-http://localhost/dir1/filepath2
-http://localhost/l/filepath3
-
-Remap rules have to be specified to map the above URLs to desired
-content servers.
-
-From 1.1.0, the plugin also supports a prefix parameter. Common parts
-of successive file paths can be extracted and specified separately
-using a 'p' query parameter. Successive file path parameters are
-appended to this prefix to create complete file paths. The prefix will
-remain active until changed or cleared (set to an empty string). For
-example, the query
-
-"/file1&p=/path1/&file2&file3&p=&/file4&p=/dir:path2/&file5&file6"
-
-results in these file paths being "reconstructed":
-
-/file1
-/path1/file2
-/path1/file3
-/file4
-/dir:path2/file5
-/dir:path2/file6
-
-Version 1.1.2
-- Use the Bucket visited(instead of 'l' as the default) as the nickname if nickname is not passed.
-
-Version 1.1.1
--------------
-- Using yts_http_fetcher_impl package instead of yts_esi_lib
-
-Version 1.1.0
--------------
-- Support for 'p=' prefix parameters
-
-Version 1.0.4
--------------
-- Checking vconn before closing (triggered on cache hits)
-
-Version 1.0.3
--------------
-- Defaulting to '/l' as the path if no colon prefix path
-  is specified
-
-Version 1.0.2
--------------
-- Using localhost as content server (relying on remap rules)
-  and generalizing the colon prefix paths
-
-Version 1.0.1
--------------
-- Using d.yimg.com for "d:" prefixed file paths
-
-Version 1.0.0
--------------
-- Initial version
-

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/21515f60/plugins/experimental/esi/README.combo
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/README.combo b/plugins/experimental/esi/README.combo
new file mode 100644
index 0000000..ea64bbd
--- /dev/null
+++ b/plugins/experimental/esi/README.combo
@@ -0,0 +1,84 @@
+Combohandler
+--------------------
+
+This plugin provides that functionality (and more) with the same
+interface but with these differences in configuration:
+
+The arguments in the plugin.config line in order represent
+
+1) The path that should triggers combo handler (defaults to
+   "admin/v1/combo")
+
+2) The name of the key used for signature verification (disabled
+   by default)
+
+A "-" can be supplied as a value for any of these arguments to request
+default value be applied. 
+
+Also, just like the original combohandler, this plugin generates URLs
+of the form 'http://localhost/<dir>/<file-path>'. <dir> here defaults
+to 'l' unless specified by the file path in the query parameter using
+a colon. For example:
+
+http://combo.com/admin/v1/combo?filepath1&dir1:filepath2&filepath3
+
+Will result in these three pages being fetched:
+
+http://localhost/l/filepath1
+http://localhost/dir1/filepath2
+http://localhost/l/filepath3
+
+Remap rules have to be specified to map the above URLs to desired
+content servers.
+
+From 1.1.0, the plugin also supports a prefix parameter. Common parts
+of successive file paths can be extracted and specified separately
+using a 'p' query parameter. Successive file path parameters are
+appended to this prefix to create complete file paths. The prefix will
+remain active until changed or cleared (set to an empty string). For
+example, the query
+
+"/file1&p=/path1/&file2&file3&p=&/file4&p=/dir:path2/&file5&file6"
+
+results in these file paths being "reconstructed":
+
+/file1
+/path1/file2
+/path1/file3
+/file4
+/dir:path2/file5
+/dir:path2/file6
+
+Version 1.1.2
+- Use the Bucket visited(instead of 'l' as the default) as the nickname if nickname is not passed.
+
+Version 1.1.1
+-------------
+- Using yts_http_fetcher_impl package instead of yts_esi_lib
+
+Version 1.1.0
+-------------
+- Support for 'p=' prefix parameters
+
+Version 1.0.4
+-------------
+- Checking vconn before closing (triggered on cache hits)
+
+Version 1.0.3
+-------------
+- Defaulting to '/l' as the path if no colon prefix path
+  is specified
+
+Version 1.0.2
+-------------
+- Using localhost as content server (relying on remap rules)
+  and generalizing the colon prefix paths
+
+Version 1.0.1
+-------------
+- Using d.yimg.com for "d:" prefixed file paths
+
+Version 1.0.0
+-------------
+- Initial version
+


[5/7] git commit: Added TS-1053.

Posted by zw...@apache.org.
Added TS-1053.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/27fb7b7a
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/27fb7b7a
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/27fb7b7a

Branch: refs/heads/master
Commit: 27fb7b7a1d720b86a418c820a216c0ffe31411ae
Parents: ee04a10
Author: Leif Hedstrom <zw...@apache.org>
Authored: Fri Apr 19 11:09:34 2013 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Fri Apr 19 11:09:34 2013 -0600

----------------------------------------------------------------------
 CHANGES |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/27fb7b7a/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 2f09827..3351e2b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,8 @@
   Changes with Apache Traffic Server 3.3.3
 
 
+  *) [TS-1053] Make combo_handler compiler. Author: Conan Wang.
+
   *) [TS-1792] Cleanup extremely verbvose debug text on Vary headers.
 
   *) [TS-1819] Guarantee hwloc initialization.


[6/7] git commit: TS-1053 Cleaning this up, since I was mucking around in it.

Posted by zw...@apache.org.
TS-1053 Cleaning this up, since I was mucking around in it.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/0012eee3
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/0012eee3
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/0012eee3

Branch: refs/heads/master
Commit: 0012eee3b43f736737062e10267f423a156c35d5
Parents: 27fb7b7
Author: Leif Hedstrom <zw...@apache.org>
Authored: Fri Apr 19 11:16:37 2013 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Fri Apr 19 11:16:37 2013 -0600

----------------------------------------------------------------------
 .../esi/fetcher/HttpDataFetcherImpl.cc             |   64 ++++++++-------
 1 files changed, 36 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/0012eee3/plugins/experimental/esi/fetcher/HttpDataFetcherImpl.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/fetcher/HttpDataFetcherImpl.cc b/plugins/experimental/esi/fetcher/HttpDataFetcherImpl.cc
index a76633a..157257e 100644
--- a/plugins/experimental/esi/fetcher/HttpDataFetcherImpl.cc
+++ b/plugins/experimental/esi/fetcher/HttpDataFetcherImpl.cc
@@ -32,7 +32,8 @@ using namespace EsiLib;
 
 const int HttpDataFetcherImpl::FETCH_EVENT_ID_BASE = 10000;
 
-inline void HttpDataFetcherImpl::_release(RequestData &req_data) {
+inline void HttpDataFetcherImpl::_release(RequestData &req_data)
+{
   if (req_data.bufp) {
     if (req_data.hdr_loc) {
       TSHandleMLocRelease(req_data.bufp, TS_NULL_MLOC, req_data.hdr_loc);
@@ -46,18 +47,21 @@ inline void HttpDataFetcherImpl::_release(RequestData &req_data) {
 HttpDataFetcherImpl::HttpDataFetcherImpl(TSCont contp,sockaddr const* client_addr,
                                          const char *debug_tag)
   : _contp(contp), _n_pending_requests(0), _curr_event_id_base(FETCH_EVENT_ID_BASE),
-    _headers_str(""),_client_addr(client_addr) {
+    _headers_str(""),_client_addr(client_addr)
+{
   _http_parser = TSHttpParserCreate();
   snprintf(_debug_tag, sizeof(_debug_tag), "%s", debug_tag);
 }
 
-HttpDataFetcherImpl::~HttpDataFetcherImpl() { 
+HttpDataFetcherImpl::~HttpDataFetcherImpl()
+{ 
   clear(); 
   TSHttpParserDestroy(_http_parser); 
 }
 
 bool
-HttpDataFetcherImpl::addFetchRequest(const string &url, FetchedDataProcessor *callback_obj /* = 0 */) {
+HttpDataFetcherImpl::addFetchRequest(const string &url, FetchedDataProcessor *callback_obj /* = 0 */)
+{
   // do we already have a request for this?
   std::pair<UrlToContentMap::iterator, bool> insert_result = 
     _pages.insert(UrlToContentMap::value_type(url, RequestData()));
@@ -85,8 +89,7 @@ HttpDataFetcherImpl::addFetchRequest(const string &url, FetchedDataProcessor *ca
     }
   }
 
-  sprintf(http_req, "GET %s HTTP/1.0\r\n"
-      "%s\r\n", url.c_str(), _headers_str.c_str());
+  sprintf(http_req, "GET %s HTTP/1.0\r\n%s\r\n", url.c_str(), _headers_str.c_str());
 
   TSFetchEvent event_ids;
   event_ids.success_event_id = _curr_event_id_base;
@@ -94,21 +97,20 @@ HttpDataFetcherImpl::addFetchRequest(const string &url, FetchedDataProcessor *ca
   event_ids.timeout_event_id = _curr_event_id_base + 2;
   _curr_event_id_base += 3;
 
-  TSFetchUrl(http_req, length, _client_addr, _contp, AFTER_BODY,
-                  event_ids);
+  TSFetchUrl(http_req, length, _client_addr, _contp, AFTER_BODY, event_ids);
   if (http_req != buff) {
     free(http_req);
   }
   
-  TSDebug(_debug_tag, "[%s] Successfully added fetch request for URL [%s]",
-           __FUNCTION__, url.data());
+  TSDebug(_debug_tag, "[%s] Successfully added fetch request for URL [%s]", __FUNCTION__, url.data());
   _page_entry_lookup.push_back(insert_result.first);
   ++_n_pending_requests;
   return true;
 }
 
 bool
-HttpDataFetcherImpl::_isFetchEvent(TSEvent event, int &base_event_id) const {
+HttpDataFetcherImpl::_isFetchEvent(TSEvent event, int &base_event_id) const
+{
   base_event_id = _getBaseEventId(event);
   if ((base_event_id < 0) || (base_event_id >= static_cast<int>(_page_entry_lookup.size()))) {
     TSDebug(_debug_tag, "[%s] Event id %d not within fetch event id range [%d, %ld)",
@@ -120,7 +122,8 @@ HttpDataFetcherImpl::_isFetchEvent(TSEvent event, int &base_event_id) const {
 }
 
 bool
-HttpDataFetcherImpl::handleFetchEvent(TSEvent event, void *edata) {
+HttpDataFetcherImpl::handleFetchEvent(TSEvent event, void *edata)
+{
   int base_event_id;
   if (!_isFetchEvent(event, base_event_id)) {
     TSError("[%s] Event %d is not a fetch event", __FUNCTION__, event);
@@ -142,8 +145,7 @@ HttpDataFetcherImpl::handleFetchEvent(TSEvent event, void *edata) {
 
   int event_id = (static_cast<int>(event) - FETCH_EVENT_ID_BASE) % 3;
   if (event_id != 0) { // failure or timeout
-    TSError("[%s] Received failure/timeout event id %d for request [%s]",
-             __FUNCTION__, event_id, req_str.data());
+    TSError("[%s] Received failure/timeout event id %d for request [%s]", __FUNCTION__, event_id, req_str.data());
     return true;
   }
 
@@ -209,16 +211,20 @@ HttpDataFetcherImpl::handleFetchEvent(TSEvent event, void *edata) {
 
 bool
 HttpDataFetcherImpl::_checkHeaderValue(TSMBuffer bufp, TSMLoc hdr_loc, const char *name, int name_len,
-                 const char *exp_value, int exp_value_len, bool prefix) const {
+                 const char *exp_value, int exp_value_len, bool prefix) const
+{
   TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, name, name_len);
   if (!field_loc) {
     return false;
   }
+
   bool retval = false;
+
   if (exp_value && exp_value_len) {
     const char *value;
     int value_len;
     int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
+
     for (int i = 0; i < n_values; ++i) {
       value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
       if ( NULL != value || value_len ) {
@@ -246,8 +252,10 @@ HttpDataFetcherImpl::_checkHeaderValue(TSMBuffer bufp, TSMLoc hdr_loc, const cha
 }
 
 bool
-HttpDataFetcherImpl::getData(const string &url, ResponseData &resp_data) const {
+HttpDataFetcherImpl::getData(const string &url, ResponseData &resp_data) const
+{
   UrlToContentMap::const_iterator iter = _pages.find(url);
+
   if (iter == _pages.end()) {
     TSError("Content being requested for unregistered URL [%s]", url.data());
     return false;
@@ -272,7 +280,8 @@ HttpDataFetcherImpl::getData(const string &url, ResponseData &resp_data) const {
 }
 
 void
-HttpDataFetcherImpl::clear() {
+HttpDataFetcherImpl::clear()
+{
   for (UrlToContentMap::iterator iter = _pages.begin(); iter != _pages.end(); ++iter) {
     _release(iter->second);
   }
@@ -284,7 +293,8 @@ HttpDataFetcherImpl::clear() {
 }
 
 DataStatus
-HttpDataFetcherImpl::getRequestStatus(const string &url) const {
+HttpDataFetcherImpl::getRequestStatus(const string &url) const
+{
   UrlToContentMap::const_iterator iter = _pages.find(url);
   if (iter == _pages.end()) {
     TSError("Status being requested for unregistered URL [%s]", url.data());
@@ -303,28 +313,25 @@ HttpDataFetcherImpl::getRequestStatus(const string &url) const {
 }
 
 void
-HttpDataFetcherImpl::useHeader(const HttpHeader &header) {
+HttpDataFetcherImpl::useHeader(const HttpHeader &header)
+{
   // request data body would not be passed to async request and so we should not pass on the content length
-  if (Utils::areEqual(header.name, header.name_len,
-                      TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH)) {
+  if (Utils::areEqual(header.name, header.name_len, TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH)) {
     return;
   }
 
   // should not support partial request for async request
-  if (Utils::areEqual(header.name, header.name_len,
-                      TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE)) {
+  if (Utils::areEqual(header.name, header.name_len, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE)) {
     return;
   }
 
   // should not support keep-alive for async requests
-  if (Utils::areEqual(header.name, header.name_len,
-              TS_MIME_FIELD_CONNECTION, TS_MIME_LEN_CONNECTION)) {
+  if (Utils::areEqual(header.name, header.name_len, TS_MIME_FIELD_CONNECTION, TS_MIME_LEN_CONNECTION)) {
       return;
   }
 
   // should not support keep-alive for async requests
-  if (Utils::areEqual(header.name, header.name_len,
-              TS_MIME_FIELD_PROXY_CONNECTION, TS_MIME_LEN_PROXY_CONNECTION)) {
+  if (Utils::areEqual(header.name, header.name_len, TS_MIME_FIELD_PROXY_CONNECTION, TS_MIME_LEN_PROXY_CONNECTION)) {
       return;
   }
 
@@ -335,7 +342,8 @@ HttpDataFetcherImpl::useHeader(const HttpHeader &header) {
 }
 
 void
-HttpDataFetcherImpl::useHeaders(const HttpHeaderList &headers) {
+HttpDataFetcherImpl::useHeaders(const HttpHeaderList &headers)
+{
   for (HttpHeaderList::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) {
     useHeader(*iter);
   }


[2/7] TS-1053 Move combo_handler to ESI, also change plugin.cc to esi.cc

Posted by zw...@apache.org.
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee04a10d/plugins/experimental/esi/plugin.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/plugin.cc b/plugins/experimental/esi/plugin.cc
deleted file mode 100644
index e7fd960..0000000
--- a/plugins/experimental/esi/plugin.cc
+++ /dev/null
@@ -1,1806 +0,0 @@
-/** @file
-
-  A brief file description
-
-  @section license License
-
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
- */
-#define __STDC_FORMAT_MACROS
-#define __STDC_LIMIT_MACROS
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <limits.h>
-#include <string.h>
-#include <string>
-#include <list>
-#include <arpa/inet.h>
-#include <pthread.h>
-#include <getopt.h>
-#include "ts/ts.h"
-#include "ts/experimental.h"
-#include <ts/remap.h>
-
-#include "lib/Utils.h"
-#include "lib/gzip.h"
-#include "EsiGzip.h"
-#include "EsiProcessor.h"
-#include "HttpDataFetcher.h"
-#include "HandlerManager.h"
-#include "serverIntercept.h"
-#include "Stats.h"
-#include "HttpDataFetcherImpl.h"
-#include "FailureInfo.h"
-using std::string;
-using std::list;
-using namespace EsiLib;
-using namespace Stats;
-
-struct OptionInfo
-{
-  bool packed_node_support;
-  bool private_response;
-  bool disable_gzip_output;
-  bool first_byte_flush;
-};
-
-static HandlerManager *gHandlerManager = NULL;
-
-#define DEBUG_TAG "plugin_esi"
-#define PROCESSOR_DEBUG_TAG "plugin_esi_processor"
-#define GZIP_DEBUG_TAG "plugin_esi_gzip"
-#define PARSER_DEBUG_TAG "plugin_esi_parser"
-#define FETCHER_DEBUG_TAG "plugin_esi_fetcher"
-#define VARS_DEBUG_TAG "plugin_esi_vars"
-#define HANDLER_MGR_DEBUG_TAG "plugin_esi_handler_mgr"
-#define EXPR_DEBUG_TAG VARS_DEBUG_TAG
-
-#define MIME_FIELD_XESI "X-Esi"
-#define MIME_FIELD_XESI_LEN 5
-
-#define HTTP_VALUE_PRIVATE_EXPIRES "-1"
-#define HTTP_VALUE_PRIVATE_CC      "max-age=0, private"
-
-enum DataType { DATA_TYPE_RAW_ESI = 0, DATA_TYPE_GZIPPED_ESI = 1, DATA_TYPE_PACKED_ESI = 2 };
-static const char *DATA_TYPE_NAMES_[] = {
-  "RAW_ESI",
-  "GZIPPED_ESI",
-  "PACKED_ESI"
-};
-
-static const char *HEADER_MASK_PREFIX = "Mask-";
-static const int HEADER_MASK_PREFIX_SIZE = 5;
-
-struct ContData
-{
-  enum STATE { READING_ESI_DOC, FETCHING_DATA, PROCESSING_COMPLETE };
-  STATE curr_state;
-  TSVIO input_vio;
-  TSIOBufferReader input_reader;
-  TSVIO output_vio;
-  TSIOBuffer output_buffer;
-  TSIOBufferReader output_reader;
-  Variables *esi_vars;
-  HttpDataFetcherImpl *data_fetcher;
-  EsiProcessor *esi_proc;
-  EsiGzip *esi_gzip;
-  TSCont contp;
-  TSHttpTxn txnp;
-  const struct OptionInfo *option_info;
-  char *request_url;
-  sockaddr const* client_addr;
-  DataType input_type;
-  string packed_node_list;
-  string gzipped_data;
-  char debug_tag[32];
-  bool gzip_output;
-  bool initialized;
-  bool xform_closed;
-  bool intercept_header;
-  bool cache_txn;
-  bool head_only;
-
-  bool os_response_cacheable;
-  list<string> post_headers;
-
-  ContData(TSCont contptr, TSHttpTxn tx)
-    : curr_state(READING_ESI_DOC), input_vio(NULL), output_vio(NULL),
-      output_buffer(NULL), output_reader(NULL),
-      esi_vars(NULL), data_fetcher(NULL), esi_proc(NULL), esi_gzip(NULL), 
-      contp(contptr), txnp(tx), request_url(NULL),
-      input_type(DATA_TYPE_RAW_ESI), packed_node_list(""),
-      gzipped_data(""), gzip_output(false),
-      initialized(false), xform_closed(false),
-      intercept_header(false), cache_txn(false), head_only(false)
-      , os_response_cacheable(true)
-  {
-    client_addr = TSHttpTxnClientAddrGet(txnp);
-    *debug_tag = '\0';
-  }
-
-  void fillPostHeader(TSMBuffer bufp, TSMLoc hdr_loc);
-
-  void getClientState();
-
-  void getServerState();
-
-  void checkXformStatus();
-
-  bool init();
-
-  ~ContData();
-};
-
-class TSStatSystem : public StatSystem {
-public:
-  void create(int handle) {
-        g_stat_indices[handle]=TSStatCreate(Stats::STAT_NAMES[handle], TS_RECORDDATATYPE_INT, TS_STAT_PERSISTENT, TS_STAT_SYNC_COUNT);
-  }
-//  void increment(int handle, TSMgmtInt step = 1) {
-  void increment(int handle, int step = 1) {
-    TSStatIntIncrement(g_stat_indices[handle], step);
-  }
-};
-
-
-static const char *
-createDebugTag(const char *prefix, TSCont contp, string &dest)
-{
-  char buf[1024];
-  snprintf(buf, 1024, "%s_%p", prefix, contp);
-  dest.assign(buf);
-  return dest.c_str();
-}
-
-static bool
-checkHeaderValue(TSMBuffer bufp, TSMLoc hdr_loc, const char *name, int name_len,
-                 const char *exp_value = 0, int exp_value_len = 0, bool prefix = false); // forward decl
-
-static bool
-checkForCacheHeader(const char *name, int name_len, const char *value, int value_len, bool &cacheable);
-
-void
-ContData::checkXformStatus() {
-  if (!xform_closed) {
-    int retval = TSVConnClosedGet(contp);
-    if ((retval == TS_ERROR) || retval) {
-      if (retval == TS_ERROR) {
-        TSDebug(debug_tag, "[%s] Error while getting close status of transformation at state %d",
-                 __FUNCTION__, curr_state);
-      } else {
-        TSDebug(debug_tag, "[%s] Vconn closed", __FUNCTION__);
-      }
-      xform_closed = true;
-    }
-  }
-}
-
-bool
-ContData::init()
-{
-  if (initialized) {
-    TSError("[%s] ContData already initialized!", __FUNCTION__);
-    return false;
-  }
-
-  string tmp_tag;
-  createDebugTag(DEBUG_TAG, contp, tmp_tag);
-  memcpy(debug_tag, tmp_tag.c_str(), tmp_tag.length() + 1);
-
-  checkXformStatus();
-
-  bool retval = false;
-
-  if (!xform_closed) {
-    // Get upstream VIO
-    input_vio = TSVConnWriteVIOGet(contp);
-    if (!input_vio) {
-      TSError("[%s] Error while getting input vio", __FUNCTION__);
-      goto lReturn;
-    }
-    input_reader = TSVIOReaderGet(input_vio);
-
-    // get downstream VIO
-    TSVConn output_conn;
-    output_conn = TSTransformOutputVConnGet(contp);
-    if(!output_conn) {
-      TSError("[%s] Error while getting transform VC", __FUNCTION__);
-      goto lReturn;
-    }
-    output_buffer = TSIOBufferCreate();
-    output_reader = TSIOBufferReaderAlloc(output_buffer);
-
-    // we don't know how much data we are going to write, so INT_MAX
-    output_vio = TSVConnWrite(output_conn, contp, output_reader, INT64_MAX);
-
-    string fetcher_tag, vars_tag, expr_tag, proc_tag, gzip_tag;
-    if (!data_fetcher) {
-      data_fetcher = new HttpDataFetcherImpl(contp, client_addr,
-                                             createDebugTag(FETCHER_DEBUG_TAG, contp, fetcher_tag));
-    }
-    if (!esi_vars) {
-      esi_vars = new Variables(createDebugTag(VARS_DEBUG_TAG, contp, vars_tag), &TSDebug, &TSError);
-    }
-
-    esi_proc = new EsiProcessor(createDebugTag(PROCESSOR_DEBUG_TAG, contp, proc_tag),
-                                createDebugTag(PARSER_DEBUG_TAG, contp, fetcher_tag),
-                                createDebugTag(EXPR_DEBUG_TAG, contp, expr_tag),
-                                &TSDebug, &TSError, *data_fetcher, *esi_vars, *gHandlerManager);
-    
-    esi_gzip = new EsiGzip(createDebugTag(GZIP_DEBUG_TAG, contp, gzip_tag), &TSDebug, &TSError);
-
-    TSDebug(debug_tag, "[%s] Set input data type to [%s]", __FUNCTION__,
-             DATA_TYPE_NAMES_[input_type]);
-
-    retval = true;
-  } else {
-    TSDebug(debug_tag, "[%s] Transformation closed during initialization; Returning false",
-             __FUNCTION__);
-  }
-
-lReturn:
-  initialized = true;
-  return retval;
-}
-
-void
-ContData::getClientState() {
-  TSMBuffer req_bufp;
-  TSMLoc req_hdr_loc;
-  if (TSHttpTxnClientReqGet(txnp, &req_bufp, &req_hdr_loc) != TS_SUCCESS) {
-    TSError("[%s] Error while retrieving client request", __FUNCTION__);
-    return;
-  }
-
-  if (!esi_vars) {
-    string vars_tag;
-    esi_vars = new Variables(createDebugTag(VARS_DEBUG_TAG, contp, vars_tag), &TSDebug, &TSError);
-  }
-  if (!data_fetcher) {
-    string fetcher_tag;
-    data_fetcher = new HttpDataFetcherImpl(contp, client_addr,
-                                           createDebugTag(FETCHER_DEBUG_TAG, contp, fetcher_tag));
-  }
-  if (req_bufp && req_hdr_loc) {
-    TSMBuffer bufp;
-    TSMLoc url_loc;
-    if(TSHttpTxnPristineUrlGet(txnp, &bufp, &url_loc) != TS_SUCCESS) {
-        TSError("[%s] Error while retrieving hdr url", __FUNCTION__);
-        return;
-    }
-    if (url_loc) {
-      if (request_url) {
-        TSfree(request_url);
-      }
-      int length;
-      request_url = TSUrlStringGet(bufp, url_loc, &length);
-      TSDebug(DEBUG_TAG, "[%s] Got request URL [%s]", __FUNCTION__, request_url ? request_url : "(null)");
-      int query_len;
-      const char *query = TSUrlHttpQueryGet(bufp, url_loc, &query_len);
-      if (query) {
-        esi_vars->populate(query, query_len);
-      }
-      TSHandleMLocRelease(bufp, req_hdr_loc, url_loc);
-    }
-    TSMLoc field_loc = TSMimeHdrFieldGet(req_bufp, req_hdr_loc, 0);
-    while (field_loc) {
-      TSMLoc next_field_loc;
-      const char *name;
-      int name_len;
-
-      name = TSMimeHdrFieldNameGet(req_bufp, req_hdr_loc, field_loc, &name_len);
-      if (name) {
-        int n_values;
-        n_values = TSMimeHdrFieldValuesCount(req_bufp, req_hdr_loc, field_loc);
-        if (n_values && (n_values != TS_ERROR)) {
-          const char *value = NULL;
-          int value_len = 0;
-          if (n_values == 1) {
-              value = TSMimeHdrFieldValueStringGet(req_bufp, req_hdr_loc, field_loc, 0, &value_len);
-
-              if ( NULL != value || value_len ) {
-                if (Utils::areEqual(name, name_len, TS_MIME_FIELD_ACCEPT_ENCODING,
-                      TS_MIME_LEN_ACCEPT_ENCODING) &&
-                    Utils::areEqual(value, value_len, TS_HTTP_VALUE_GZIP,
-                      TS_HTTP_LEN_GZIP)) {
-                  gzip_output = true;
-                }
-              }
-          } else {
-            for (int i = 0; i < n_values; ++i) {
-              value = TSMimeHdrFieldValueStringGet(req_bufp, req_hdr_loc, field_loc, i, &value_len);
-              if ( NULL != value || value_len ) {
-                if (Utils::areEqual(name, name_len, TS_MIME_FIELD_ACCEPT_ENCODING,
-                      TS_MIME_LEN_ACCEPT_ENCODING) &&
-                    Utils::areEqual(value, value_len, TS_HTTP_VALUE_GZIP,
-                      TS_HTTP_LEN_GZIP)) {
-                  gzip_output = true;
-                }
-              }
-            }
-
-            value = TSMimeHdrFieldValueStringGet(req_bufp, req_hdr_loc, field_loc, -1, &value_len);
-          }
-
-          if (value != NULL) {
-            HttpHeader header(name, name_len, value, value_len);
-            data_fetcher->useHeader(header);
-            esi_vars->populate(header);
-          }
-        }
-      }
-
-      next_field_loc = TSMimeHdrFieldNext(req_bufp, req_hdr_loc, field_loc);
-      TSHandleMLocRelease(req_bufp, req_hdr_loc, field_loc);
-      field_loc = next_field_loc;
-    }
-  }
-
-  if (gzip_output) {
-    if (option_info->disable_gzip_output) {
-      TSDebug(DEBUG_TAG, "[%s] disable gzip output", __FUNCTION__);
-      gzip_output = false;
-    } else {
-      TSDebug(DEBUG_TAG, "[%s] Client accepts gzip encoding; will compress output", __FUNCTION__);
-    }
-  }
-
-  TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_hdr_loc);
-}
-
-void
-ContData::fillPostHeader(TSMBuffer bufp, TSMLoc hdr_loc) {
-  int n_mime_headers = TSMimeHdrFieldsCount(bufp, hdr_loc);
-  TSMLoc field_loc;
-  const char *name, *value;
-  int name_len, value_len;
-  string header;
-  for (int i = 0; i < n_mime_headers; ++i) {
-    field_loc = TSMimeHdrFieldGet(bufp, hdr_loc, i);
-    if (!field_loc) {
-      TSDebug(DEBUG_TAG, "[%s] Error while obtaining header field #%d", __FUNCTION__, i);
-      continue;
-    }
-    name = TSMimeHdrFieldNameGet(bufp, hdr_loc, field_loc, &name_len);
-    if (name) {
-      if (Utils::areEqual(name, name_len, TS_MIME_FIELD_TRANSFER_ENCODING, TS_MIME_LEN_TRANSFER_ENCODING)) {
-        TSDebug(DEBUG_TAG, "[%s] Not retaining transfer encoding header", __FUNCTION__);
-      } else if (Utils::areEqual(name, name_len, MIME_FIELD_XESI, MIME_FIELD_XESI_LEN)) {
-        TSDebug(DEBUG_TAG, "[%s] Not retaining 'X-Esi' header", __FUNCTION__);
-      } else if (Utils::areEqual(name, name_len, TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH)) {
-        TSDebug(DEBUG_TAG, "[%s] Not retaining 'Content-length' header", __FUNCTION__);
-      }  else {
-        header.assign(name, name_len);
-        header.append(": ");
-        int n_field_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
-        for (int j = 0; j < n_field_values; ++j) {
-          value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, j, &value_len);
-          if ( NULL == value || !value_len ) {
-            TSDebug(DEBUG_TAG, "[%s] Error while getting value #%d of header [%.*s]",
-                     __FUNCTION__, j, name_len, name);
-          } else {
-            if (Utils::areEqual(name, name_len, TS_MIME_FIELD_VARY, TS_MIME_LEN_VARY) &&
-                Utils::areEqual(value, value_len, TS_MIME_FIELD_ACCEPT_ENCODING,
-                                TS_MIME_LEN_ACCEPT_ENCODING)) {
-              TSDebug(DEBUG_TAG, "[%s] Not retaining 'vary: accept-encoding' header", __FUNCTION__);
-            } else if (Utils::areEqual(name, name_len, TS_MIME_FIELD_CONTENT_ENCODING,
-                                       TS_MIME_LEN_CONTENT_ENCODING) &&
-                       Utils::areEqual(value, value_len, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP)) {
-              TSDebug(DEBUG_TAG, "[%s] Not retaining 'content-encoding: gzip' header", __FUNCTION__);
-            } else {
-              if (header[header.size() - 2] != ':') {
-                header.append(", ");
-              }
-              header.append(value, value_len);
-              checkForCacheHeader(name, name_len, value, value_len,
-                                  os_response_cacheable);
-              if (!os_response_cacheable) {
-                TSDebug(DEBUG_TAG, "[%s] Header [%.*s] with value [%.*s] is a no-cache header",
-                         __FUNCTION__, name_len, name, value_len, value);
-                break;
-              }
-            }
-          } // end if got value string
-        } // end value iteration
-
-        if (static_cast<int>(header.size()) > (name_len + 2 /* for ': ' */ )) {
-          header.append("\r\n");
-          post_headers.push_back(header);
-        }
-      } // end if processable header
-    } // end if got header name
-    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-    if (!os_response_cacheable) {
-      post_headers.clear();
-      break;
-    }
-  } // end header iteration
-}
-
-void
-ContData::getServerState() {
-  TSMBuffer bufp;
-  TSMLoc hdr_loc;
-
-  if (cache_txn) {
-    if (intercept_header) {
-      input_type = DATA_TYPE_PACKED_ESI;
-      return;
-    } else if (TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
-      TSError("[%s] Could not get server response; set input type to RAW_ESI", __FUNCTION__);
-      input_type = DATA_TYPE_RAW_ESI;
-      return;
-    }
-  } else if (TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
-    TSError("[%s] Could not get server response; set input type to RAW_ESI", __FUNCTION__);
-    input_type = DATA_TYPE_RAW_ESI;
-    return;
-  }
-
-  if (checkHeaderValue(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_ENCODING,
-                       TS_MIME_LEN_CONTENT_ENCODING, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP)) {
-    input_type = DATA_TYPE_GZIPPED_ESI;
-  } else {
-    input_type = DATA_TYPE_RAW_ESI;
-  }
-
-  if (option_info->packed_node_support && !cache_txn && !head_only) {
-    fillPostHeader(bufp, hdr_loc);
-  }
-
-  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-}
-
-ContData::~ContData()
-{
-  TSDebug(debug_tag, "[%s] Destroying continuation data", __FUNCTION__);
-  if (output_reader) {
-    TSIOBufferReaderFree(output_reader);
-  }
-  if (output_buffer) {
-    TSIOBufferDestroy(output_buffer);
-  }
-  if (request_url) {
-    TSfree(request_url);
-  }
-  if (esi_vars) {
-    delete esi_vars;
-  }
-  if (data_fetcher) {
-    delete data_fetcher;
-  }
-  if (esi_proc) {
-    delete esi_proc;
-  }
-  if(esi_gzip) {
-    delete esi_gzip;
-  }
-}
-
-static int removeCacheHandler(TSCont contp, TSEvent event, void *edata) {
-    //TSDebug(DEBUG_TAG, "[%s] event: %d", __FUNCTION__, (int)event);
-    TSContDestroy(contp);
-    //just ignore cache remove message
-    return 0;
-}
-
-static bool removeCacheKey(TSHttpTxn txnp) {
-  TSMBuffer req_bufp;
-  TSMLoc req_hdr_loc;
-  TSMLoc url_loc = NULL;
-  TSCont contp = NULL;
-  TSCacheKey cacheKey = NULL;
-  bool result = false;
-
-  if (TSHttpTxnClientReqGet(txnp, &req_bufp, &req_hdr_loc) != TS_SUCCESS) {
-    TSError("[%s] Error while retrieving client request", __FUNCTION__);
-    return false;
-  }
-
-  do {
-    if(TSHttpTxnPristineUrlGet(txnp, &req_bufp, &url_loc) != TS_SUCCESS) {
-      TSError("[%s] Error while retrieving hdr url", __FUNCTION__);
-      break;
-    }
-
-    contp = TSContCreate(removeCacheHandler, NULL);
-    if (contp == NULL) {
-      TSError("[%s] Could not create continuation", __FUNCTION__);
-      break;
-    }
-
-    cacheKey = TSCacheKeyCreate();
-    if (cacheKey == NULL) {
-      TSError("[%s] TSCacheKeyCreate fail", __FUNCTION__);
-      break;
-    }
-
-    if (TSCacheKeyDigestFromUrlSet(cacheKey, url_loc) != TS_SUCCESS) {
-      TSError("[%s] TSCacheKeyDigestFromUrlSet fail", __FUNCTION__);
-      break;
-    }
-
-    TSCacheRemove(contp, cacheKey);
-    result = true;
-    TSError("[%s] TSCacheRemoved", __FUNCTION__);
-  } while (0);
-
-  if (cacheKey != NULL) {
-      TSCacheKeyDestroy(cacheKey);
-  }
-  if (!result) {
-    if (contp != NULL) {
-      TSContDestroy(contp);
-    }
-  }
-
-  TSHandleMLocRelease(req_bufp, req_hdr_loc, url_loc);
-  if (url_loc != NULL) {
-    TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_hdr_loc);
-  }
-
-  return result;
-}
-
-static void
-cacheNodeList(ContData *cont_data) {
-  if (TSHttpTxnAborted(cont_data->txnp) == TS_SUCCESS) {
-    TSDebug(cont_data->debug_tag, "[%s] Not caching node list as txn has been aborted", __FUNCTION__);
-    return;
-  }
-  string post_request("");
-  post_request.append(TS_HTTP_METHOD_POST);
-  post_request.append(" ");
-  post_request.append(cont_data->request_url);
-  post_request.append(" HTTP/1.0\r\n");
-  post_request.append(SERVER_INTERCEPT_HEADER);
-  post_request.append(": cache=1\r\n");
-  for (list<string>::iterator list_iter = cont_data->post_headers.begin();
-       list_iter != cont_data->post_headers.end(); ++list_iter) {
-    post_request.append(ECHO_HEADER_PREFIX);
-
-  //TSDebug(cont_data->debug_tag, "[%s] header == %s", __FUNCTION__, list_iter->c_str());
-    if (((int)list_iter->length() > HEADER_MASK_PREFIX_SIZE) &&
-            (strncmp(list_iter->c_str(), HEADER_MASK_PREFIX, HEADER_MASK_PREFIX_SIZE) == 0)) {
-      post_request.append(list_iter->c_str() + HEADER_MASK_PREFIX_SIZE, list_iter->length() - HEADER_MASK_PREFIX_SIZE);
-    } else {
-      post_request.append(*list_iter);
-    }
-  }
-  post_request.append(TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
-  post_request.append(": ");
-  post_request.append(TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP);
-  post_request.append("\r\n");
-
-  string body("");
-  cont_data->esi_proc->packNodeList(body, false);
-  char buf[64];
-  snprintf(buf, 64, "%s: %d\r\n\r\n", TS_MIME_FIELD_CONTENT_LENGTH, (int)body.size());
-
-  post_request.append(buf);
-  post_request.append(body);
-
-  //TSError("[%s] DO caching node list size=%d", __FUNCTION__, (int)body.size());
-  //TSDebug(cont_data->debug_tag, "[%s] caching node list size=%d", __FUNCTION__, (int)body.size());
-
-  TSFetchEvent event_ids = {0};
-  TSFetchUrl(post_request.data(), post_request.size(), cont_data->client_addr,
-                  cont_data->contp, NO_CALLBACK, event_ids);
-}
-
-static int
-transformData(TSCont contp)
-{
-  ContData *cont_data;
-  int64_t toread, consumed = 0, avail;
-  bool input_vio_buf_null = false;
-  bool process_input_complete = false;
-
-  // Get the output (downstream) vconnection where we'll write data to.
-  cont_data = static_cast<ContData *>(TSContDataGet(contp));
-
-  // If the input VIO's buffer is NULL, we need to terminate the transformation
-  if (!TSVIOBufferGet(cont_data->input_vio)) {
-    input_vio_buf_null = true;
-    if (cont_data->curr_state == ContData::PROCESSING_COMPLETE) {
-      TSDebug(cont_data->debug_tag, "[%s] input_vio NULL, marking transformation to be terminated",
-               __FUNCTION__);
-      return 1;
-    } else if (cont_data->curr_state == ContData::READING_ESI_DOC) {
-      TSDebug(cont_data->debug_tag, "[%s] input_vio NULL while in read state. Assuming end of input",
-               __FUNCTION__);
-      process_input_complete = true;
-    } else {
-      if (!cont_data->data_fetcher->isFetchComplete()) {
-        TSDebug(cont_data->debug_tag,
-                 "[%s] input_vio NULL, but data needs to be fetched. Returning control", __FUNCTION__);
-        if(!cont_data->option_info->first_byte_flush) {
-          return 1;
-        }
-      } else {
-        TSDebug(cont_data->debug_tag,
-                 "[%s] input_vio NULL, but processing needs to (and can) be completed", __FUNCTION__);
-      }
-    }
-  }
-
-  if (!process_input_complete && (cont_data->curr_state == ContData::READING_ESI_DOC)) {
-    // Determine how much data we have left to read.
-    toread = TSVIONTodoGet(cont_data->input_vio);
-    TSDebug(cont_data->debug_tag, "[%s] upstream VC has %" PRId64" bytes available to read",
-             __FUNCTION__, toread);
-
-    if (toread > 0) {
-      avail = TSIOBufferReaderAvail(cont_data->input_reader);
-      if (avail == TS_ERROR) {
-        TSError("[%s] Error while getting number of bytes available", __FUNCTION__);
-        return 0;
-      }
-
-      // There are some data available for reading. Let's parse it
-      if (avail > 0) {
-        int64_t data_len;
-        const char *data;
-        TSIOBufferBlock block = TSIOBufferReaderStart(cont_data->input_reader);
-        // Now start extraction
-        while (block != NULL) {
-          data = TSIOBufferBlockReadStart(block, cont_data->input_reader, &data_len);
-          if (cont_data->input_type == DATA_TYPE_RAW_ESI) {
-            cont_data->esi_proc->addParseData(data, data_len);
-          } else if (cont_data->input_type == DATA_TYPE_GZIPPED_ESI) {
-            cont_data->gzipped_data.append(data, data_len);
-          } else {
-            cont_data->packed_node_list.append(data, data_len);
-          }
-          TSDebug(cont_data->debug_tag,
-                   "[%s] Added chunk of %" PRId64" bytes starting with [%.10s] to parse list",
-                   __FUNCTION__, data_len, (data_len ? data : "(null)"));
-          consumed += data_len;
-
-          block = TSIOBufferBlockNext(block);
-        }
-      }
-      TSDebug(cont_data->debug_tag, "[%s] Consumed %" PRId64" bytes from upstream VC",
-               __FUNCTION__, consumed);
-
-      TSIOBufferReaderConsume(cont_data->input_reader, consumed);
-
-      // Modify the input VIO to reflect how much data we've completed.
-      TSVIONDoneSet(cont_data->input_vio, TSVIONDoneGet(cont_data->input_vio) + consumed);
-
-      toread = TSVIONTodoGet(cont_data->input_vio); // set this for the test after this if block
-    }
-
-    if (toread > 0) { // testing this again because it might have changed in previous if block
-      // let upstream know we are ready to read new data
-      TSContCall(TSVIOContGet(cont_data->input_vio), TS_EVENT_VCONN_WRITE_READY, cont_data->input_vio);
-    } else {
-      // we have consumed everything that there was to read
-      process_input_complete = true;
-    }
-  }
-  if (process_input_complete) {
-    TSDebug(cont_data->debug_tag, "[%s] Completed reading input...", __FUNCTION__);
-    if (cont_data->input_type == DATA_TYPE_PACKED_ESI) {
-      TSDebug(DEBUG_TAG, "[%s] Going to use packed node list of size %d",
-               __FUNCTION__, (int) cont_data->packed_node_list.size());
-      if (cont_data->esi_proc->usePackedNodeList(cont_data->packed_node_list) == EsiProcessor::UNPACK_FAILURE) {
-        removeCacheKey(cont_data->txnp);
-
-        cont_data->input_type = DATA_TYPE_RAW_ESI;
-        cont_data->esi_proc->start();
-        cont_data->esi_proc->addParseData(cont_data->packed_node_list.data(), cont_data->packed_node_list.size());
-      }
-    }
-
-    if (cont_data->input_type != DATA_TYPE_PACKED_ESI) {
-      if (cont_data->input_type == DATA_TYPE_GZIPPED_ESI) {
-        BufferList buf_list;
-        if (gunzip(cont_data->gzipped_data.data(), cont_data->gzipped_data.size(), buf_list)) {
-          for (BufferList::iterator iter = buf_list.begin(); iter != buf_list.end(); ++iter) {
-            cont_data->esi_proc->addParseData(iter->data(), iter->size());
-          }
-        } else {
-          TSError("[%s] Error while gunzipping data", __FUNCTION__);
-        }
-      }
-      if (cont_data->esi_proc->completeParse()) {
-        if (cont_data->option_info->packed_node_support && cont_data->os_response_cacheable
-            && !cont_data->cache_txn && !cont_data->head_only)
-        {
-          cacheNodeList(cont_data);
-        }
-      }
-    }
-
-    cont_data->curr_state = ContData::FETCHING_DATA;
-    if (!input_vio_buf_null) {
-      TSContCall(TSVIOContGet(cont_data->input_vio), TS_EVENT_VCONN_WRITE_COMPLETE,
-                  cont_data->input_vio);
-    }
-  }
-
-  if ((cont_data->curr_state == ContData::FETCHING_DATA) &&
-      (!cont_data->option_info->first_byte_flush)) { // retest as state may have changed in previous block
-    if (cont_data->data_fetcher->isFetchComplete()) {
-      TSDebug(cont_data->debug_tag, "[%s] data ready; going to process doc", __FUNCTION__);
-      const char *out_data;
-      int out_data_len;
-      EsiProcessor::ReturnCode retval = cont_data->esi_proc->process(out_data, out_data_len);
-      TSDebug(cont_data->debug_tag, "[%s] data length: %d, retval: %d", __FUNCTION__, out_data_len, retval);
-      if (retval == EsiProcessor::NEED_MORE_DATA) {
-        TSDebug(cont_data->debug_tag, "[%s] ESI processor needs more data; "
-                 "will wait for all data to be fetched", __FUNCTION__);
-        return 1;
-      }
-      cont_data->curr_state = ContData::PROCESSING_COMPLETE;
-      if (retval == EsiProcessor::SUCCESS) {
-        TSDebug(cont_data->debug_tag,
-                 "[%s] ESI processor output document of size %d starting with [%.10s]",
-                 __FUNCTION__, out_data_len, (out_data_len ? out_data : "(null)"));
-      } else {
-        TSError("[%s] ESI processor failed to process document; will return empty document", __FUNCTION__);
-        out_data = "";
-        out_data_len = 0;
-      }
-
-      // make sure transformation has not been prematurely terminated
-      if (!cont_data->xform_closed) {
-        string cdata;
-        if (cont_data->gzip_output) {
-          if (!gzip(out_data, out_data_len, cdata)) {
-            TSError("[%s] Error while gzipping content", __FUNCTION__);
-            out_data_len = 0;
-            out_data = "";
-          } else {
-            TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes",
-                     __FUNCTION__, out_data_len, (int) cdata.size());
-            out_data_len = cdata.size();
-            out_data = cdata.data();
-          }
-        }
-
-        // Get downstream VIO
-        TSVConn output_conn;
-        output_conn = TSTransformOutputVConnGet(contp);
-        if (!output_conn) {
-          TSError("[%s] Error while getting transform VC", __FUNCTION__);
-          return 0;
-        }
-
-        TSVIO output_vio;
-        output_vio = TSVConnWrite(output_conn, contp, cont_data->output_reader, out_data_len);
-
-        if (TSIOBufferWrite(TSVIOBufferGet(output_vio), out_data, out_data_len) == TS_ERROR) {
-          TSError("[%s] Error while writing bytes to downstream VC", __FUNCTION__);
-          return 0;
-        }
-
-        TSVIONBytesSet(output_vio, out_data_len);
-
-        // Reenable the output connection so it can read the data we've produced.
-        TSVIOReenable(output_vio);
-      }
-    } else {
-      TSDebug(cont_data->debug_tag, "[%s] Data not available yet; cannot process document",
-               __FUNCTION__);
-    }
-  }
-
-  if ((cont_data->curr_state == ContData::FETCHING_DATA) &&
-      (cont_data->option_info->first_byte_flush)) { // retest as state may have changed in previous block
-    TSDebug(cont_data->debug_tag, "[%s] trying to process doc", __FUNCTION__);
-    string out_data;
-    string cdata;
-    int overall_len;
-    EsiProcessor::ReturnCode retval = cont_data->esi_proc->flush(out_data, overall_len);
-
-    if (cont_data->data_fetcher->isFetchComplete()) {
-      TSDebug(cont_data->debug_tag, "[%s] data ready; last process() will have finished the entire processing", __FUNCTION__);
-      cont_data->curr_state = ContData::PROCESSING_COMPLETE;
-    }
-
-    if (retval == EsiProcessor::SUCCESS) {
-      TSDebug(cont_data->debug_tag,
-                 "[%s] ESI processor output document of size %d starting with [%.10s]",
-                 __FUNCTION__, (int) out_data.size(), (out_data.size() ? out_data.data() : "(null)"));
-    } else {
-      TSError("[%s] ESI processor failed to process document; will return empty document", __FUNCTION__);
-      out_data.assign("");
-        
-      if(!cont_data->xform_closed) {
-        TSVIONBytesSet(cont_data->output_vio, 0);
-        TSVIOReenable(cont_data->output_vio);
-      }
-    }
-
-    // make sure transformation has not been prematurely terminated
-    if (!cont_data->xform_closed && out_data.size() > 0) {
-      if (cont_data->gzip_output) {
-        if (!cont_data->esi_gzip->stream_encode(out_data, cdata)) {
-          TSError("[%s] Error while gzipping content", __FUNCTION__);
-        } else {
-          TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes",
-                     __FUNCTION__, (int) out_data.size(), (int) cdata.size());
-        }
-      }
-
-      if (cont_data->gzip_output) {
-        if (TSIOBufferWrite(TSVIOBufferGet(cont_data->output_vio), cdata.data(), cdata.size()) == TS_ERROR) {
-          TSError("[%s] Error while writing bytes to downstream VC", __FUNCTION__);
-          return 0;
-        }
-      } else {
-        if (TSIOBufferWrite(TSVIOBufferGet(cont_data->output_vio), out_data.data(), out_data.size()) == TS_ERROR) {
-          TSError("[%s] Error while writing bytes to downstream VC", __FUNCTION__);
-          return 0;
-        }
-      }
-    }
-    if(!cont_data->xform_closed) {     
-      // should not set any fixed length
-      if(cont_data->curr_state == ContData::PROCESSING_COMPLETE) {
-        if(cont_data->gzip_output) {
-          string cdata;
-          int downstream_length;
-          if(!cont_data->esi_gzip->stream_finish(cdata, downstream_length)) {
-            TSError("[%s] Error while finishing gzip", __FUNCTION__);
-            return 0;  
-          } else {   
-            if (TSIOBufferWrite(TSVIOBufferGet(cont_data->output_vio), cdata.data(), cdata.size()) == TS_ERROR) {
-              TSError("[%s] Error while writing bytes to downstream VC", __FUNCTION__);
-              return 0;
-            }
-            TSDebug(cont_data->debug_tag,
-                 "[%s] ESI processed overall/gzip: %d",
-                 __FUNCTION__, downstream_length );
-            TSVIONBytesSet(cont_data->output_vio, downstream_length);          
-          }
-        } else {
-          TSDebug(cont_data->debug_tag,
-                 "[%s] ESI processed overall: %d",
-                 __FUNCTION__, overall_len );
-          TSVIONBytesSet(cont_data->output_vio, overall_len);
-        }
-      } 
-   
-      // Reenable the output connection so it can read the data we've produced.
-      TSVIOReenable(cont_data->output_vio);
-    }
-  }
-
-  return 1;
-}
-
-static int
-transformHandler(TSCont contp, TSEvent event, void *edata)
-{
-  TSVIO input_vio;
-  ContData *cont_data;
-  cont_data = static_cast<ContData *>(TSContDataGet(contp));
-
-  // we need these later, but declaring now avoid compiler warning w.r.t. goto
-  bool process_event = true;
-  const char *cont_debug_tag;
-  bool shutdown, is_fetch_event;
-
-  if (!cont_data->initialized) {
-    if (!cont_data->init()) {
-      TSError("[%s] Could not initialize continuation data; shutting down transformation", __FUNCTION__);
-      goto lShutdown;
-    }
-    TSDebug(cont_data->debug_tag, "[%s] initialized continuation data", __FUNCTION__);
-  }
-
-  cont_debug_tag = cont_data->debug_tag; // just a handy reference
-
-  cont_data->checkXformStatus();
-
-  is_fetch_event = cont_data->data_fetcher->isFetchEvent(event);
-
-
-  if (cont_data->xform_closed) {
-    TSDebug(cont_debug_tag, "[%s] Transformation closed. Post-processing...", __FUNCTION__);
-    if (cont_data->curr_state == ContData::PROCESSING_COMPLETE) {
-      TSDebug(cont_debug_tag, "[%s] Processing is complete, not processing current event %d",
-               __FUNCTION__, event);
-      process_event = false;
-    } else if (cont_data->curr_state == ContData::READING_ESI_DOC) {
-      TSDebug(cont_debug_tag, "[%s] Parsing is incomplete, will force end of input",
-               __FUNCTION__);
-      cont_data->curr_state = ContData::FETCHING_DATA;
-    }
-    if (cont_data->curr_state == ContData::FETCHING_DATA) { // retest as it may be modified in prev. if block
-      if (cont_data->data_fetcher->isFetchComplete()) {
-        TSDebug(cont_debug_tag,
-                 "[%s] Requested data has been fetched; will skip event and marking processing as complete ",
-                 __FUNCTION__);
-        cont_data->curr_state = ContData::PROCESSING_COMPLETE;
-        process_event = false;
-      } else {
-        if (is_fetch_event) {
-          TSDebug(cont_debug_tag, "[%s] Going to process received data",
-                   __FUNCTION__);
-        } else {
-          // transformation is over, but data hasn't been fetched;
-          // let's wait for data to be fetched - we will be called
-          // by Fetch API and go through this loop again
-          TSDebug(cont_debug_tag, "[%s] Ignoring event %d; Will wait for pending data",
-                   __FUNCTION__, event);
-          process_event = false;
-        }
-      }
-    }
-  }
-
-  if (process_event) {
-    switch (event) {
-    case TS_EVENT_ERROR:
-      // doubt: what is this code doing?
-      input_vio = TSVConnWriteVIOGet(contp);
-      if (!input_vio) {
-        TSError("[%s] Error while getting upstream vio", __FUNCTION__);
-      } else {
-        TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio);
-      }
-      // FetchSM also might send this; let's just output whatever we have
-      cont_data->curr_state = ContData::FETCHING_DATA;
-      transformData(contp);
-      break;
-
-    case TS_EVENT_VCONN_WRITE_READY:
-      TSDebug(cont_debug_tag, "[%s] WRITE_READY", __FUNCTION__);
-      if(!cont_data->option_info->first_byte_flush) {
-        TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);
-      }
-      break;
-
-    case TS_EVENT_VCONN_WRITE_COMPLETE:
-      TSDebug(cont_debug_tag, "[%s] shutting down transformation", __FUNCTION__);
-      TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);
-      break;
-
-    case TS_EVENT_IMMEDIATE:
-      TSDebug(cont_debug_tag, "[%s] handling TS_EVENT_IMMEDIATE...", __FUNCTION__);
-      transformData(contp);
-      break;
-
-    default:
-      if (is_fetch_event) {
-        TSDebug(cont_debug_tag, "[%s] Handling fetch event %d...", __FUNCTION__, event);
-        if (cont_data->data_fetcher->handleFetchEvent(event, edata)) {
-          if (cont_data->curr_state == ContData::FETCHING_DATA) {
-            // there's a small chance that fetcher is ready even before
-            // parsing is complete; hence we need to check the state too
-            if(cont_data->option_info->first_byte_flush ||
-               cont_data->data_fetcher->isFetchComplete()){
-              TSDebug(cont_debug_tag, "[%s] fetcher is ready with data, going into process stage",
-                     __FUNCTION__);
-              transformData(contp);
-            }
-          }
-        } else {
-          TSError("[%s] Could not handle fetch event!", __FUNCTION__);
-        }
-      } else {
-        TSAssert(!"Unexpected event");
-      }
-      break;
-    }
-  }
-
-  TSDebug(cont_data->debug_tag, "[%s] transformHandler, event: %d, curr_state: %d", __FUNCTION__, (int)event, (int)cont_data->curr_state);
-
-  shutdown = (cont_data->xform_closed && (cont_data->curr_state == ContData::PROCESSING_COMPLETE));
-  if (shutdown) {
-    if (process_event && is_fetch_event) {
-      // we need to return control to the fetch API to give up it's
-      // lock on our continuation which will fail if we destroy
-      // ourselves right now
-      TSDebug(cont_debug_tag, "[%s] Deferring shutdown as data event was just processed", __FUNCTION__);
-      TSContSchedule(contp, 10, TS_THREAD_POOL_TASK);
-    } else {
-      goto lShutdown;
-    }
-  }
-
-  return 1;
-
-lShutdown:
-  TSDebug(cont_data->debug_tag, "[%s] transformation closed; cleaning up data...", __FUNCTION__);
-  delete cont_data;
-  TSContDestroy(contp);
-  return 1;
-}
-
-struct RespHdrModData {
-  bool cache_txn;
-  bool gzip_encoding;
-  bool head_only;
-  const struct OptionInfo *option_info;
-};
-
-static void
-addMimeHeaderField(TSMBuffer bufp, TSMLoc hdr_loc, const char *name, int name_len,
-                   const char *value, int value_len) {
-  TSMLoc field_loc = (TSMLoc)NULL;
-  TSMimeHdrFieldCreate(bufp, hdr_loc, &field_loc);
-  if (!field_loc) {
-    TSError("[%s] Error while creating mime field", __FUNCTION__);
-  } else {
-    if (TSMimeHdrFieldNameSet(bufp, hdr_loc, field_loc, name, name_len) != TS_SUCCESS) {
-      TSError("[%s] Error while setting name [%.*s] for MIME header field", __FUNCTION__, name_len, name);
-    } else {
-      if (TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, field_loc, 0, value, value_len) != TS_SUCCESS) {
-        TSError("[%s] Error while inserting value [%.*s] string to MIME field [%.*s]", __FUNCTION__,
-                 value_len, value, name_len, name);
-      } else {
-        if (TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc) != TS_SUCCESS) {
-          TSError("[%s] Error while appending MIME field with name [%.*s] and value [%.*s]", __FUNCTION__,
-                   name_len, name, value_len, value);
-        }
-      }
-    }
-    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-  }
-}
-
-static int
-modifyResponseHeader(TSCont contp, TSEvent event, void *edata) {
-  int retval = 0;
-  RespHdrModData *mod_data = static_cast<RespHdrModData *>(TSContDataGet(contp));
-  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
-  if (event != TS_EVENT_HTTP_SEND_RESPONSE_HDR) {
-    TSError("[%s] Unexpected event (%d)", __FUNCTION__, event);
-    goto lReturn;
-  }
-  TSMBuffer bufp;
-  TSMLoc hdr_loc;
-  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) {
-    int n_mime_headers = TSMimeHdrFieldsCount(bufp, hdr_loc);
-    TSMLoc field_loc;
-    const char *name, *value;
-    int name_len, value_len;
-    bool have_content_length = false;
-    for (int i = 0; i < n_mime_headers; ++i) {
-      field_loc = TSMimeHdrFieldGet(bufp, hdr_loc, i);
-      if (!field_loc) {
-        TSDebug(DEBUG_TAG, "[%s] Error while obtaining header field #%d", __FUNCTION__, i);
-        continue;
-      }
-      name = TSMimeHdrFieldNameGet(bufp, hdr_loc, field_loc, &name_len);
-      if (name) {
-        bool destroy_header = false;
-
-        if (Utils::areEqual(name, name_len, SERVER_INTERCEPT_HEADER, SERVER_INTERCEPT_HEADER_LEN)) {
-          destroy_header = true;
-        } else if (Utils::areEqual(name, name_len, TS_MIME_FIELD_AGE, TS_MIME_LEN_AGE)) {
-          destroy_header = true;
-        } else if (Utils::areEqual(name, name_len, MIME_FIELD_XESI, MIME_FIELD_XESI_LEN)) {
-          destroy_header = true;
-        } else if ((name_len > HEADER_MASK_PREFIX_SIZE) &&
-                   (strncmp(name, HEADER_MASK_PREFIX, HEADER_MASK_PREFIX_SIZE) == 0))
-         {
-          destroy_header = true;
-        } else if (mod_data->option_info->private_response && (Utils::areEqual(name,
-                name_len, TS_MIME_FIELD_CACHE_CONTROL, TS_MIME_LEN_CACHE_CONTROL)
-              || Utils::areEqual(name, name_len, TS_MIME_FIELD_EXPIRES,
-                TS_MIME_LEN_EXPIRES)))
-        {
-          destroy_header = true;
-        } else if (Utils::areEqual(name, name_len, TS_MIME_FIELD_CONTENT_LENGTH,
-              TS_MIME_LEN_CONTENT_LENGTH))
-        {
-          have_content_length = true;
-          if (mod_data->head_only) {
-            destroy_header = true;
-            TSError("[%s] remove Content-Length", __FUNCTION__);
-          }
-        } else {
-          int n_field_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
-          for (int j = 0; j < n_field_values; ++j) {
-            value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, j, &value_len);
-            if ( NULL == value || !value_len ) {
-              TSDebug(DEBUG_TAG, "[%s] Error while getting value #%d of header [%.*s]",
-                       __FUNCTION__, j, name_len, name);
-            } else {
-              if (!mod_data->option_info->packed_node_support || mod_data->cache_txn) {
-                bool response_cacheable, is_cache_header;
-                is_cache_header = checkForCacheHeader(name, name_len, value, value_len, response_cacheable);
-                if (is_cache_header && response_cacheable) {
-                  destroy_header = true;
-                }
-              }
-            } // if got valid value for header
-          } // end for
-        }
-        if (destroy_header) {
-          TSDebug(DEBUG_TAG, "[%s] Removing header with name [%.*s]", __FUNCTION__, name_len, name);
-          TSMimeHdrFieldDestroy(bufp, hdr_loc, field_loc);
-          --n_mime_headers;
-          --i;
-        }
-      }
-      TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-    }
-    if (mod_data->gzip_encoding &&
-        !checkHeaderValue(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_ENCODING,
-          TS_MIME_LEN_CONTENT_ENCODING, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP))
-    {
-      addMimeHeaderField(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_ENCODING,
-          TS_MIME_LEN_CONTENT_ENCODING, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP);
-    }
-
-    if (!have_content_length) {
-      TSError("[%s] no Content-Length!", __FUNCTION__);
-    }
-
-    if (mod_data->option_info->packed_node_support && mod_data->cache_txn) {
-      addMimeHeaderField(bufp, hdr_loc, TS_MIME_FIELD_VARY,
-          TS_MIME_LEN_VARY, TS_MIME_FIELD_ACCEPT_ENCODING,
-          TS_MIME_LEN_ACCEPT_ENCODING);
-    }
-
-    if (mod_data->option_info->private_response) {
-      addMimeHeaderField(bufp, hdr_loc, TS_MIME_FIELD_EXPIRES,
-          TS_MIME_LEN_EXPIRES, HTTP_VALUE_PRIVATE_EXPIRES,
-          sizeof(HTTP_VALUE_PRIVATE_EXPIRES) - 1);
-      addMimeHeaderField(bufp, hdr_loc, TS_MIME_FIELD_CACHE_CONTROL,
-          TS_MIME_LEN_CACHE_CONTROL, HTTP_VALUE_PRIVATE_CC,
-          sizeof(HTTP_VALUE_PRIVATE_CC) - 1);
-    }
-
-    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-    TSDebug(DEBUG_TAG, "[%s] Inspected client-bound headers", __FUNCTION__);
-    retval = 1;
-  } else {
-    TSError("[%s] Error while getting response from txn", __FUNCTION__);
-  }
-
-lReturn:
-  delete mod_data;
-  TSContDestroy(contp);
-  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
-  return retval;
-}
-
-static bool
-checkHeaderValue(TSMBuffer bufp, TSMLoc hdr_loc, const char *name, int name_len,
-                 const char *exp_value, int exp_value_len, bool prefix) {
-  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, name, name_len);
-  if (!field_loc) {
-    return false;
-  }
-  bool retval = false;
-  if (exp_value && exp_value_len) {
-    const char *value;
-    int value_len;
-    int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
-    for (int i = 0; i < n_values; ++i) {
-      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
-      if ( NULL != value || value_len ) {
-        if (prefix) {
-          if ((value_len >= exp_value_len) &&
-              (strncasecmp(value, exp_value, exp_value_len) == 0)) {
-            retval = true;
-          }
-        } else if (Utils::areEqual(value, value_len, exp_value, exp_value_len)) {
-          retval = true;
-        }
-      } else {
-        TSDebug(DEBUG_TAG, "[%s] Error while getting value # %d of header [%.*s]", __FUNCTION__,
-                 i, name_len, name);
-      }
-      if (retval) {
-        break;
-      }
-    }
-  } else { // only presence required
-    retval = true;
-  }
-  TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-  return retval;
-}
-
-static void
-maskOsCacheHeaders(TSHttpTxn txnp) {
-  TSMBuffer bufp;
-  TSMLoc hdr_loc;
-  if (TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
-    TSError("[%s] Couldn't get server response from txn", __FUNCTION__);
-    return;
-  }
-  int n_mime_headers = TSMimeHdrFieldsCount(bufp, hdr_loc);
-  TSMLoc field_loc;
-  const char *name, *value;
-  int name_len, value_len, n_field_values;
-  bool os_response_cacheable, is_cache_header, mask_header;
-  string masked_name;
-  os_response_cacheable = true;
-  for (int i = 0; i < n_mime_headers; ++i) {
-    field_loc = TSMimeHdrFieldGet(bufp, hdr_loc, i);
-    if (!field_loc) {
-      TSDebug(DEBUG_TAG, "[%s] Error while obtaining header field #%d", __FUNCTION__, i);
-      continue;
-    }
-    name = TSMimeHdrFieldNameGet(bufp, hdr_loc, field_loc, &name_len);
-    if (name) {
-      mask_header = is_cache_header = false;
-      n_field_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
-      for (int j = 0; j < n_field_values; ++j) {
-        value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, j, &value_len);
-        if ( NULL == value || !value_len ) {
-          TSDebug(DEBUG_TAG, "[%s] Error while getting value #%d of header [%.*s]",
-                   __FUNCTION__, j, name_len, name);
-        } else {
-          is_cache_header = checkForCacheHeader(name, name_len, value, value_len, os_response_cacheable);
-          if (!os_response_cacheable) {
-            break;
-          }
-          if (is_cache_header) {
-            TSDebug(DEBUG_TAG, "[%s] Masking OS cache header [%.*s] with value [%.*s]. ",
-                     __FUNCTION__, name_len, name, value_len, value);
-            mask_header = true;
-          }
-        } // end if got value string
-      } // end value iteration
-      if (mask_header) {
-        masked_name.assign(HEADER_MASK_PREFIX);
-        masked_name.append(name, name_len);
-        if (TSMimeHdrFieldNameSet(bufp, hdr_loc, field_loc, masked_name.data(),
-                                   masked_name.size()) != TS_SUCCESS) {
-          TSError("[%s] Couldn't rename header [%.*s]", __FUNCTION__, name_len, name);
-        }
-      }
-    } // end if got header name
-    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-    if (!os_response_cacheable) {
-      break;
-    }
-  } // end header iteration
-  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-}
-
-static bool
-isTxnTransformable(TSHttpTxn txnp, bool is_cache_txn, bool * intercept_header, bool * head_only) {
-  //  We are only interested in transforming "200 OK" responses with a
-  //  Content-Type: text/ header and with X-Esi header
-
-  TSMBuffer bufp;
-  TSMLoc hdr_loc;
-  TSHttpStatus resp_status;
-  TSReturnCode header_obtained;
-  bool retval = false;
-
-  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
-    TSError("[%s] Couldn't get txn header", __FUNCTION__);
-    return false;
-  }
-
-  int method_len;
-  const char *method;
-  method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
-  if (method == NULL) {
-    TSError("[%s] Couldn't get method", __FUNCTION__);
-    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-    return false;
-  }
-
-  if (method_len >= TS_HTTP_LEN_HEAD && memcmp(method, TS_HTTP_METHOD_HEAD, TS_HTTP_LEN_HEAD) == 0) {
-    *head_only = true;
-  }
-  else if (!(((method_len >= TS_HTTP_LEN_POST && memcmp(method, TS_HTTP_METHOD_POST, TS_HTTP_LEN_POST) == 0)) ||
-        ((method_len >= TS_HTTP_LEN_GET && memcmp(method, TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET) == 0))))
-  {
-    TSDebug(DEBUG_TAG, "[%s] method %.*s will be ignored", __FUNCTION__, method_len, method);
-    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-    return false;
-  }
-  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-
-  header_obtained = is_cache_txn ? TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc) :
-    TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc);
-  if (header_obtained != TS_SUCCESS) {
-    TSError("[%s] Couldn't get txn header", __FUNCTION__);
-    return false;
-  }
-
-  do {
-    *intercept_header = checkHeaderValue(bufp, hdr_loc, SERVER_INTERCEPT_HEADER, SERVER_INTERCEPT_HEADER_LEN);
-    if (*intercept_header) {
-      if (is_cache_txn) {
-        TSDebug(DEBUG_TAG, "[%s] Packed ESI document found in cache; will process", __FUNCTION__);
-        retval = true;
-      } else {
-        TSDebug(DEBUG_TAG, "[%s] Found Intercept header in server response; document not processable",
-            __FUNCTION__);
-      }
-      break; // found internal header; no other detection required
-    }
-
-    // allow response with specific status code to be transformable
-    resp_status = TSHttpHdrStatusGet(bufp, hdr_loc);
-    if (static_cast<int>(resp_status) == static_cast<int>(TS_ERROR)) {
-      TSError("[%s] Error while getting http status", __FUNCTION__);
-      break;
-    }
-    if (resp_status != TS_HTTP_STATUS_OK) {
-      TSDebug(DEBUG_TAG, "[%s] Not handling non-OK response status %d", __FUNCTION__, resp_status);
-      break;
-    }
-
-    if (!checkHeaderValue(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_TYPE, TS_MIME_LEN_CONTENT_TYPE,
-          "text/", 5, true)) {
-      TSDebug(DEBUG_TAG, "[%s] Not text content", __FUNCTION__);
-      break;
-    }
-    if (!checkHeaderValue(bufp, hdr_loc, MIME_FIELD_XESI, MIME_FIELD_XESI_LEN)) {
-      TSDebug(DEBUG_TAG, "[%s] ESI header [%s] not found", __FUNCTION__, MIME_FIELD_XESI);
-      break;
-    }
-
-    retval = true;
-  } while (0);
-
-  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-  return retval;
-}
-
-static bool
-isCacheObjTransformable(TSHttpTxn txnp, bool * intercept_header, bool * head_only) {
-  int obj_status;
-  if (TSHttpTxnCacheLookupStatusGet(txnp, &obj_status) == TS_ERROR) {
-    TSError("[%s] Couldn't get cache status of object", __FUNCTION__);
-    return false;
-  }
-  if ((obj_status == TS_CACHE_LOOKUP_HIT_FRESH) || (obj_status == TS_CACHE_LOOKUP_HIT_STALE)) {
-    /*
-    time_t respTime;
-    if (TSHttpTxnCachedRespTimeGet(txnp, &respTime) == TS_SUCCESS) {
-      TSError("[%s] RespTime; %d", __FUNCTION__, (int)respTime);
-    }
-    */
-
-    TSDebug(DEBUG_TAG, "[%s] doc found in cache, will add transformation", __FUNCTION__);
-    return isTxnTransformable(txnp, true, intercept_header, head_only);
-  }
-  TSDebug(DEBUG_TAG, "[%s] cache object's status is %d; not transformable",
-           __FUNCTION__, obj_status);
-  return false;
-}
-
-static bool
-isInterceptRequest(TSHttpTxn txnp) {
-  if (TSHttpIsInternalRequest(txnp) != TS_SUCCESS) {
-    TSDebug(DEBUG_TAG, "[%s] Skipping external request", __FUNCTION__);
-    return false;
-  }
-
-  TSMBuffer bufp;
-  TSMLoc hdr_loc;
-  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
-    TSError("[%s] Could not get client request", __FUNCTION__);
-    return false;
-  }
-
-  bool valid_request = false;
-  bool retval = false;
-  int method_len;
-  const char *method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
-  if (!method) {
-    TSError("[%s] Could not obtain method!", __FUNCTION__);
-  } else {
-    if ((method_len != TS_HTTP_LEN_POST) ||
-        (strncasecmp(method, TS_HTTP_METHOD_POST, TS_HTTP_LEN_POST))) {
-      TSDebug(DEBUG_TAG, "[%s] Method [%.*s] invalid, [%s] expected", __FUNCTION__, method_len, method,
-               TS_HTTP_METHOD_POST);
-    } else {
-      TSDebug(DEBUG_TAG, "[%s] Valid server intercept method found", __FUNCTION__);
-      valid_request = true;
-    }
-  }
-
-  if (valid_request) {
-    retval = checkHeaderValue(bufp, hdr_loc, SERVER_INTERCEPT_HEADER, SERVER_INTERCEPT_HEADER_LEN);
-  }
-  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
-  return retval;
-}
-
-static bool
-checkForCacheHeader(const char *name, int name_len, const char *value, int value_len, bool &cacheable) {
-  cacheable = true;
-  if (Utils::areEqual(name, name_len, TS_MIME_FIELD_EXPIRES, TS_MIME_LEN_EXPIRES)) {
-    if ((value_len == 1) && (*value == '0')) {
-      cacheable = false;
-    }else if (Utils::areEqual(value, value_len, "-1",2)) {
-      cacheable = false;
-    }
-    return true;
-  }
-  if (Utils::areEqual(name, name_len, TS_MIME_FIELD_CACHE_CONTROL, TS_MIME_LEN_CACHE_CONTROL)) {
-    if (Utils::areEqual(value, value_len, TS_HTTP_VALUE_PRIVATE, TS_HTTP_LEN_PRIVATE)) {
-      cacheable = false;
-    }
-    return true;
-  }
-  return false;
-}
-
-static bool
-addSendResponseHeaderHook(TSHttpTxn txnp, const ContData * src_cont_data) {
-  TSCont contp = TSContCreate(modifyResponseHeader, NULL);
-  if (!contp) {
-    TSError("[%s] Could not create continuation", __FUNCTION__);
-    return false;
-  }
-  TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
-  RespHdrModData *cont_data = new RespHdrModData();
-  cont_data->option_info = src_cont_data->option_info;
-  cont_data->cache_txn = src_cont_data->cache_txn;
-  cont_data->head_only = src_cont_data->head_only;
-  cont_data->gzip_encoding = src_cont_data->gzip_output;
-  TSContDataSet(contp, cont_data);
-  return true;
-}
-
-static bool
-addTransform(TSHttpTxn txnp, const bool processing_os_response,
-    const bool intercept_header, const bool head_only,
-    const struct OptionInfo *pOptionInfo)
-{
-  TSCont contp = 0;
-  ContData *cont_data = 0;
-
-  contp = TSTransformCreate(transformHandler, txnp);
-  if (!contp) {
-    TSError("[%s] Error while creating a new transformation", __FUNCTION__);
-    goto lFail;
-  }
-
-  cont_data = new ContData(contp, txnp);
-  TSContDataSet(contp, cont_data);
-
-  cont_data->option_info = pOptionInfo;
-  cont_data->cache_txn = !processing_os_response;
-  cont_data->intercept_header = intercept_header;
-  cont_data->head_only = head_only;
-  cont_data->getClientState();
-  cont_data->getServerState();
-
-  if (cont_data->cache_txn) {
-    if (cont_data->option_info->packed_node_support) {
-      if (cont_data->input_type != DATA_TYPE_PACKED_ESI) {
-        removeCacheKey(txnp);
-      }
-    } else {
-      if (cont_data->input_type == DATA_TYPE_PACKED_ESI) {
-        removeCacheKey(txnp);
-      }
-    }
-  }
-
-  TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, contp);
-
-  if (!addSendResponseHeaderHook(txnp, cont_data)) {
-    TSError("[%s] Couldn't add send response header hook", __FUNCTION__);
-    goto lFail;
-  }
-
-  TSHttpTxnTransformedRespCache(txnp, 0);
-  if (cont_data->option_info->packed_node_support) {
-    TSHttpTxnUntransformedRespCache(txnp, 0);
-  } else {
-    TSHttpTxnUntransformedRespCache(txnp, 1);
-  }
-
-  TSDebug(DEBUG_TAG, "[%s] Added transformation (0x%p)", __FUNCTION__, contp);
-  return true;
-
-lFail:
-  if (contp) {
-    TSContDestroy(contp);
-  }
-  if (cont_data) {
-    delete cont_data;
-  }
-  return false;
-}
-
-pthread_key_t threadKey = 0;
-static int
-globalHookHandler(TSCont contp, TSEvent event, void *edata) {
-  TSHttpTxn txnp = (TSHttpTxn) edata;
-  bool intercept_header = false;
-  bool head_only = false;
-  bool intercept_req = isInterceptRequest(txnp);
-  struct OptionInfo *pOptionInfo = (struct OptionInfo *)TSContDataGet(contp);
-
-  switch (event) {
-  case TS_EVENT_HTTP_READ_REQUEST_HDR:
-    TSDebug(DEBUG_TAG, "[%s] handling read request header event...", __FUNCTION__);
-    if (intercept_req) {
-      if (!setupServerIntercept(txnp)) {
-        TSError("[%s] Could not setup server intercept", __FUNCTION__);
-      } else {
-        TSDebug(DEBUG_TAG, "[%s] Setup server intercept", __FUNCTION__);
-      }
-    } else {
-      TSDebug(DEBUG_TAG, "[%s] Not setting up intercept", __FUNCTION__);
-    }
-    break;
-
-  case TS_EVENT_HTTP_READ_RESPONSE_HDR:
-  case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
-    if (!intercept_req) {
-      if (event == TS_EVENT_HTTP_READ_RESPONSE_HDR) {
-        bool mask_cache_headers = false;
-        TSDebug(DEBUG_TAG, "[%s] handling read response header event...", __FUNCTION__);
-        if (isCacheObjTransformable(txnp, &intercept_header, &head_only)) {
-          // transformable cache object will definitely have a
-          // transformation already as cache_lookup_complete would
-          // have been processed before this
-          TSDebug(DEBUG_TAG, "[%s] xform should already have been added on cache lookup. Not adding now",
-                   __FUNCTION__);
-          mask_cache_headers = true;
-        } else if (isTxnTransformable(txnp, false, &intercept_header, &head_only)) {
-          addTransform(txnp, true, intercept_header, head_only, pOptionInfo);
-          Stats::increment(Stats::N_OS_DOCS);
-          mask_cache_headers = true;
-        }
-        if (pOptionInfo->packed_node_support && mask_cache_headers) {
-          // we'll 'mask' OS cache headers so that traffic server will
-          // not try to cache this. We cannot outright delete them
-          // because we need them in our POST request; hence the 'masking'
-          maskOsCacheHeaders(txnp);
-        }
-      } else {
-        TSDebug(DEBUG_TAG, "[%s] handling cache lookup complete event...", __FUNCTION__);
-        if (isCacheObjTransformable(txnp, &intercept_header, &head_only)) {
-          // we make the assumption above that a transformable cache
-          // object would already have a tranformation. We should revisit
-          // that assumption in case we change the statement below
-          addTransform(txnp, false, intercept_header, head_only, pOptionInfo);
-          Stats::increment(Stats::N_CACHE_DOCS);
-        }
-      }
-    }
-    break;
-
-  default:
-    TSDebug(DEBUG_TAG, "[%s] Don't know how to handle event type %d", __FUNCTION__, event);
-    break;
-  }
-
-  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
-  return 0;
-}
-
-static void
-loadHandlerConf(const char *file_name, Utils::KeyValueMap &handler_conf) {
-  std::list<string> conf_lines;
-  TSFile conf_file = TSfopen(file_name, "r");
-  if (conf_file != NULL) {
-    char buf[1024];
-    while (TSfgets(conf_file, buf, sizeof(buf) - 1) != NULL) {
-      conf_lines.push_back(string(buf));
-    }
-    TSfclose(conf_file);
-    Utils::parseKeyValueConfig(conf_lines, handler_conf);
-    TSDebug(DEBUG_TAG, "[%s] Loaded handler conf file [%s]", __FUNCTION__, file_name);
-  } else {
-    TSError("[%s] Failed to open handler config file [%s]", __FUNCTION__, file_name);
-  }
-}
-
-static int esiPluginInit(int argc, const char *argv[], struct OptionInfo *pOptionInfo) {
-  static TSStatSystem *statSystem = NULL;
-
-  if (statSystem == NULL) {
-    statSystem = new TSStatSystem();
-    Utils::init(&TSDebug, &TSError);
-    Stats::init(statSystem);
-  }
-
-  if (gHandlerManager == NULL) {
-    gHandlerManager = new HandlerManager(HANDLER_MGR_DEBUG_TAG, &TSDebug, &TSError);
-  }
-
-  memset(pOptionInfo, 0, sizeof(struct OptionInfo));
-  if (argc > 1) {
-    int c;
-    static const struct option longopts[] = {
-      { const_cast<char *>("packed-node-support"), no_argument, NULL, 'n' },
-      { const_cast<char *>("private-response"), no_argument, NULL, 'p' },
-      { const_cast<char *>("disable-gzip-output"), no_argument, NULL, 'z' },
-      { const_cast<char *>("first-byte-flush"), no_argument, NULL, 'b' },
-      { const_cast<char *>("handler-filename"), required_argument, NULL, 'f' },
-      { NULL, 0, NULL, 0 }
-    };
-
-    optarg = NULL;
-    optind = opterr = optopt = 0;
-    int longindex = 0;
-    while ((c = getopt_long(argc, (char * const*) argv, "npzbf:", longopts, &longindex)) != -1) {
-      switch (c) {
-        case 'n':
-          pOptionInfo->packed_node_support = true;
-          break;
-        case 'p':
-          pOptionInfo->private_response = true;
-          break;
-        case 'z':
-          pOptionInfo->disable_gzip_output = true;
-          break;
-        case 'b':
-          pOptionInfo->first_byte_flush = true;
-          break;
-        case 'f':
-          {
-            Utils::KeyValueMap handler_conf;
-            loadHandlerConf(optarg, handler_conf);
-            gHandlerManager->loadObjects(handler_conf);
-            break;
-          }
-        default:
-          break;
-      }
-    }
-  }
-
-  int result = 0;
-  bool bKeySet;
-  if (threadKey == 0) {
-    bKeySet = true;
-    if ((result=pthread_key_create(&threadKey, NULL)) != 0) {
-      TSError("[%s] Could not create key", __FUNCTION__);
-      TSDebug(DEBUG_TAG, "[%s] Could not create key", __FUNCTION__);
-    }
-  }
-  else {
-    bKeySet = false;
-  }
-
-  if (result == 0) {
-    TSDebug(DEBUG_TAG, "[%s] Plugin started%s, " \
-        "packed-node-support: %d, private-response: %d, " \
-        "disable-gzip-output: %d, first-byte-flush: %d ", __FUNCTION__, bKeySet ? " and key is set" : "",
-        pOptionInfo->packed_node_support, pOptionInfo->private_response,
-        pOptionInfo->disable_gzip_output, pOptionInfo->first_byte_flush);
-  }
-
-  return result;
-}
-
-void
-TSPluginInit(int argc, const char *argv[]) {
-  struct OptionInfo *pOptionInfo = (struct OptionInfo *)TSmalloc(sizeof(struct OptionInfo));
-  if (pOptionInfo == NULL) {
-    TSError("[%s] malloc %d bytes fail", __FUNCTION__, (int)sizeof(struct OptionInfo));
-    return;
-  }
-  if (esiPluginInit(argc, argv, pOptionInfo) != 0) {
-    return;
-  }
-
-  TSCont global_contp = TSContCreate(globalHookHandler, NULL);
-  if (!global_contp) {
-    TSError("[%s] Could not create global continuation", __FUNCTION__);
-    return;
-  }
-  TSContDataSet(global_contp, pOptionInfo);
-
-  TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, global_contp);
-  TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, global_contp);
-  TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, global_contp);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Initialize the plugin as a remap plugin.
-//
-TSReturnCode
-TSRemapInit(TSRemapInterface* api_info, char *errbuf, int errbuf_size)
-{
-  if (!api_info) {
-    snprintf(errbuf, errbuf_size, "[TSRemapInit] - Invalid TSRemapInterface argument");
-    TSError("[TSRemapInit] - Invalid TSRemapInterface argument");
-    return TS_ERROR;
-  }
-
-  if (api_info->size < sizeof(TSRemapInterface)) {
-    snprintf(errbuf, errbuf_size, "[TSRemapInit] - Incorrect size of TSRemapInterface structure");
-    TSError("[TSRemapInit] - Incorrect size of TSRemapInterface structure");
-    return TS_ERROR;
-  }
-
-  TSDebug(DEBUG_TAG, "esi remap plugin is successfully initialized");
-  return TS_SUCCESS;
-}
-
-TSReturnCode
-TSRemapNewInstance(int argc, char* argv[], void** ih, char* errbuf, int errbuf_size)
-{
-  if (argc < 2) {
-    snprintf(errbuf, errbuf_size, "Unable to create remap instance, " \
-        "argc: %d < 2", argc);
-    TSError("Unable to create remap instance! argc: %d < 2", argc);
-    return TS_ERROR;
-  }
-
-  int index = 0;
-  const char *new_argv[argc];
-
-  new_argv[index++] = "esi.so";
-  for (int i=2; i<argc; i++) {
-    new_argv[index++] = argv[i];
-  }
-  new_argv[index] = NULL;
-
-  struct OptionInfo *pOptionInfo = (struct OptionInfo *)TSmalloc(sizeof(struct OptionInfo));
-  if (pOptionInfo == NULL) {
-    snprintf(errbuf, errbuf_size, "malloc %d bytes fail", (int)sizeof(struct OptionInfo));
-    TSError("[%s] malloc %d bytes fail", __FUNCTION__, (int)sizeof(struct OptionInfo));
-    return TS_ERROR;
-  }
-  if (esiPluginInit(index, new_argv, pOptionInfo) != 0) {
-    snprintf(errbuf, errbuf_size, "esiPluginInit fail!");
-    return TS_ERROR;
-  }
-  TSCont contp = TSContCreate(globalHookHandler, NULL);
-  TSContDataSet(contp, pOptionInfo);
-  *ih = static_cast<void*>(contp);
-
-  return TS_SUCCESS;
-}
-
-void
-TSRemapDeleteInstance(void* ih)
-{
-  TSCont contp = static_cast<TSCont>(ih);
-  if (contp != NULL) {
-    TSContDestroy(contp);
-  }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Main entry point when used as a remap plugin.
-//
-TSRemapStatus
-TSRemapDoRemap(void* ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
-{
-  if (NULL != ih) {
-    TSCont contp = static_cast<TSCont>(ih);
-    TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
-    TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp);
-
-    if (isInterceptRequest(txnp)) {
-      if (!setupServerIntercept(txnp)) {
-        TSError("[%s] Could not setup server intercept", __FUNCTION__);
-      } else {
-        TSDebug(DEBUG_TAG, "[%s] Setup server intercept", __FUNCTION__);
-      }
-    } else {
-      TSDebug(DEBUG_TAG, "[%s] Not setting up intercept", __FUNCTION__);
-    }
-  }
-
-  return TSREMAP_NO_REMAP; // This plugin never rewrites anything.
-}
-


[3/7] TS-1053 Move combo_handler to ESI, also change plugin.cc to esi.cc

Posted by zw...@apache.org.
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee04a10d/plugins/experimental/esi/esi.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/esi.cc b/plugins/experimental/esi/esi.cc
new file mode 100644
index 0000000..e7fd960
--- /dev/null
+++ b/plugins/experimental/esi/esi.cc
@@ -0,0 +1,1806 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+#define __STDC_FORMAT_MACROS
+#define __STDC_LIMIT_MACROS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <string>
+#include <list>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <getopt.h>
+#include "ts/ts.h"
+#include "ts/experimental.h"
+#include <ts/remap.h>
+
+#include "lib/Utils.h"
+#include "lib/gzip.h"
+#include "EsiGzip.h"
+#include "EsiProcessor.h"
+#include "HttpDataFetcher.h"
+#include "HandlerManager.h"
+#include "serverIntercept.h"
+#include "Stats.h"
+#include "HttpDataFetcherImpl.h"
+#include "FailureInfo.h"
+using std::string;
+using std::list;
+using namespace EsiLib;
+using namespace Stats;
+
+struct OptionInfo
+{
+  bool packed_node_support;
+  bool private_response;
+  bool disable_gzip_output;
+  bool first_byte_flush;
+};
+
+static HandlerManager *gHandlerManager = NULL;
+
+#define DEBUG_TAG "plugin_esi"
+#define PROCESSOR_DEBUG_TAG "plugin_esi_processor"
+#define GZIP_DEBUG_TAG "plugin_esi_gzip"
+#define PARSER_DEBUG_TAG "plugin_esi_parser"
+#define FETCHER_DEBUG_TAG "plugin_esi_fetcher"
+#define VARS_DEBUG_TAG "plugin_esi_vars"
+#define HANDLER_MGR_DEBUG_TAG "plugin_esi_handler_mgr"
+#define EXPR_DEBUG_TAG VARS_DEBUG_TAG
+
+#define MIME_FIELD_XESI "X-Esi"
+#define MIME_FIELD_XESI_LEN 5
+
+#define HTTP_VALUE_PRIVATE_EXPIRES "-1"
+#define HTTP_VALUE_PRIVATE_CC      "max-age=0, private"
+
+enum DataType { DATA_TYPE_RAW_ESI = 0, DATA_TYPE_GZIPPED_ESI = 1, DATA_TYPE_PACKED_ESI = 2 };
+static const char *DATA_TYPE_NAMES_[] = {
+  "RAW_ESI",
+  "GZIPPED_ESI",
+  "PACKED_ESI"
+};
+
+static const char *HEADER_MASK_PREFIX = "Mask-";
+static const int HEADER_MASK_PREFIX_SIZE = 5;
+
+struct ContData
+{
+  enum STATE { READING_ESI_DOC, FETCHING_DATA, PROCESSING_COMPLETE };
+  STATE curr_state;
+  TSVIO input_vio;
+  TSIOBufferReader input_reader;
+  TSVIO output_vio;
+  TSIOBuffer output_buffer;
+  TSIOBufferReader output_reader;
+  Variables *esi_vars;
+  HttpDataFetcherImpl *data_fetcher;
+  EsiProcessor *esi_proc;
+  EsiGzip *esi_gzip;
+  TSCont contp;
+  TSHttpTxn txnp;
+  const struct OptionInfo *option_info;
+  char *request_url;
+  sockaddr const* client_addr;
+  DataType input_type;
+  string packed_node_list;
+  string gzipped_data;
+  char debug_tag[32];
+  bool gzip_output;
+  bool initialized;
+  bool xform_closed;
+  bool intercept_header;
+  bool cache_txn;
+  bool head_only;
+
+  bool os_response_cacheable;
+  list<string> post_headers;
+
+  ContData(TSCont contptr, TSHttpTxn tx)
+    : curr_state(READING_ESI_DOC), input_vio(NULL), output_vio(NULL),
+      output_buffer(NULL), output_reader(NULL),
+      esi_vars(NULL), data_fetcher(NULL), esi_proc(NULL), esi_gzip(NULL), 
+      contp(contptr), txnp(tx), request_url(NULL),
+      input_type(DATA_TYPE_RAW_ESI), packed_node_list(""),
+      gzipped_data(""), gzip_output(false),
+      initialized(false), xform_closed(false),
+      intercept_header(false), cache_txn(false), head_only(false)
+      , os_response_cacheable(true)
+  {
+    client_addr = TSHttpTxnClientAddrGet(txnp);
+    *debug_tag = '\0';
+  }
+
+  void fillPostHeader(TSMBuffer bufp, TSMLoc hdr_loc);
+
+  void getClientState();
+
+  void getServerState();
+
+  void checkXformStatus();
+
+  bool init();
+
+  ~ContData();
+};
+
+class TSStatSystem : public StatSystem {
+public:
+  void create(int handle) {
+        g_stat_indices[handle]=TSStatCreate(Stats::STAT_NAMES[handle], TS_RECORDDATATYPE_INT, TS_STAT_PERSISTENT, TS_STAT_SYNC_COUNT);
+  }
+//  void increment(int handle, TSMgmtInt step = 1) {
+  void increment(int handle, int step = 1) {
+    TSStatIntIncrement(g_stat_indices[handle], step);
+  }
+};
+
+
+static const char *
+createDebugTag(const char *prefix, TSCont contp, string &dest)
+{
+  char buf[1024];
+  snprintf(buf, 1024, "%s_%p", prefix, contp);
+  dest.assign(buf);
+  return dest.c_str();
+}
+
+static bool
+checkHeaderValue(TSMBuffer bufp, TSMLoc hdr_loc, const char *name, int name_len,
+                 const char *exp_value = 0, int exp_value_len = 0, bool prefix = false); // forward decl
+
+static bool
+checkForCacheHeader(const char *name, int name_len, const char *value, int value_len, bool &cacheable);
+
+void
+ContData::checkXformStatus() {
+  if (!xform_closed) {
+    int retval = TSVConnClosedGet(contp);
+    if ((retval == TS_ERROR) || retval) {
+      if (retval == TS_ERROR) {
+        TSDebug(debug_tag, "[%s] Error while getting close status of transformation at state %d",
+                 __FUNCTION__, curr_state);
+      } else {
+        TSDebug(debug_tag, "[%s] Vconn closed", __FUNCTION__);
+      }
+      xform_closed = true;
+    }
+  }
+}
+
+bool
+ContData::init()
+{
+  if (initialized) {
+    TSError("[%s] ContData already initialized!", __FUNCTION__);
+    return false;
+  }
+
+  string tmp_tag;
+  createDebugTag(DEBUG_TAG, contp, tmp_tag);
+  memcpy(debug_tag, tmp_tag.c_str(), tmp_tag.length() + 1);
+
+  checkXformStatus();
+
+  bool retval = false;
+
+  if (!xform_closed) {
+    // Get upstream VIO
+    input_vio = TSVConnWriteVIOGet(contp);
+    if (!input_vio) {
+      TSError("[%s] Error while getting input vio", __FUNCTION__);
+      goto lReturn;
+    }
+    input_reader = TSVIOReaderGet(input_vio);
+
+    // get downstream VIO
+    TSVConn output_conn;
+    output_conn = TSTransformOutputVConnGet(contp);
+    if(!output_conn) {
+      TSError("[%s] Error while getting transform VC", __FUNCTION__);
+      goto lReturn;
+    }
+    output_buffer = TSIOBufferCreate();
+    output_reader = TSIOBufferReaderAlloc(output_buffer);
+
+    // we don't know how much data we are going to write, so INT_MAX
+    output_vio = TSVConnWrite(output_conn, contp, output_reader, INT64_MAX);
+
+    string fetcher_tag, vars_tag, expr_tag, proc_tag, gzip_tag;
+    if (!data_fetcher) {
+      data_fetcher = new HttpDataFetcherImpl(contp, client_addr,
+                                             createDebugTag(FETCHER_DEBUG_TAG, contp, fetcher_tag));
+    }
+    if (!esi_vars) {
+      esi_vars = new Variables(createDebugTag(VARS_DEBUG_TAG, contp, vars_tag), &TSDebug, &TSError);
+    }
+
+    esi_proc = new EsiProcessor(createDebugTag(PROCESSOR_DEBUG_TAG, contp, proc_tag),
+                                createDebugTag(PARSER_DEBUG_TAG, contp, fetcher_tag),
+                                createDebugTag(EXPR_DEBUG_TAG, contp, expr_tag),
+                                &TSDebug, &TSError, *data_fetcher, *esi_vars, *gHandlerManager);
+    
+    esi_gzip = new EsiGzip(createDebugTag(GZIP_DEBUG_TAG, contp, gzip_tag), &TSDebug, &TSError);
+
+    TSDebug(debug_tag, "[%s] Set input data type to [%s]", __FUNCTION__,
+             DATA_TYPE_NAMES_[input_type]);
+
+    retval = true;
+  } else {
+    TSDebug(debug_tag, "[%s] Transformation closed during initialization; Returning false",
+             __FUNCTION__);
+  }
+
+lReturn:
+  initialized = true;
+  return retval;
+}
+
+void
+ContData::getClientState() {
+  TSMBuffer req_bufp;
+  TSMLoc req_hdr_loc;
+  if (TSHttpTxnClientReqGet(txnp, &req_bufp, &req_hdr_loc) != TS_SUCCESS) {
+    TSError("[%s] Error while retrieving client request", __FUNCTION__);
+    return;
+  }
+
+  if (!esi_vars) {
+    string vars_tag;
+    esi_vars = new Variables(createDebugTag(VARS_DEBUG_TAG, contp, vars_tag), &TSDebug, &TSError);
+  }
+  if (!data_fetcher) {
+    string fetcher_tag;
+    data_fetcher = new HttpDataFetcherImpl(contp, client_addr,
+                                           createDebugTag(FETCHER_DEBUG_TAG, contp, fetcher_tag));
+  }
+  if (req_bufp && req_hdr_loc) {
+    TSMBuffer bufp;
+    TSMLoc url_loc;
+    if(TSHttpTxnPristineUrlGet(txnp, &bufp, &url_loc) != TS_SUCCESS) {
+        TSError("[%s] Error while retrieving hdr url", __FUNCTION__);
+        return;
+    }
+    if (url_loc) {
+      if (request_url) {
+        TSfree(request_url);
+      }
+      int length;
+      request_url = TSUrlStringGet(bufp, url_loc, &length);
+      TSDebug(DEBUG_TAG, "[%s] Got request URL [%s]", __FUNCTION__, request_url ? request_url : "(null)");
+      int query_len;
+      const char *query = TSUrlHttpQueryGet(bufp, url_loc, &query_len);
+      if (query) {
+        esi_vars->populate(query, query_len);
+      }
+      TSHandleMLocRelease(bufp, req_hdr_loc, url_loc);
+    }
+    TSMLoc field_loc = TSMimeHdrFieldGet(req_bufp, req_hdr_loc, 0);
+    while (field_loc) {
+      TSMLoc next_field_loc;
+      const char *name;
+      int name_len;
+
+      name = TSMimeHdrFieldNameGet(req_bufp, req_hdr_loc, field_loc, &name_len);
+      if (name) {
+        int n_values;
+        n_values = TSMimeHdrFieldValuesCount(req_bufp, req_hdr_loc, field_loc);
+        if (n_values && (n_values != TS_ERROR)) {
+          const char *value = NULL;
+          int value_len = 0;
+          if (n_values == 1) {
+              value = TSMimeHdrFieldValueStringGet(req_bufp, req_hdr_loc, field_loc, 0, &value_len);
+
+              if ( NULL != value || value_len ) {
+                if (Utils::areEqual(name, name_len, TS_MIME_FIELD_ACCEPT_ENCODING,
+                      TS_MIME_LEN_ACCEPT_ENCODING) &&
+                    Utils::areEqual(value, value_len, TS_HTTP_VALUE_GZIP,
+                      TS_HTTP_LEN_GZIP)) {
+                  gzip_output = true;
+                }
+              }
+          } else {
+            for (int i = 0; i < n_values; ++i) {
+              value = TSMimeHdrFieldValueStringGet(req_bufp, req_hdr_loc, field_loc, i, &value_len);
+              if ( NULL != value || value_len ) {
+                if (Utils::areEqual(name, name_len, TS_MIME_FIELD_ACCEPT_ENCODING,
+                      TS_MIME_LEN_ACCEPT_ENCODING) &&
+                    Utils::areEqual(value, value_len, TS_HTTP_VALUE_GZIP,
+                      TS_HTTP_LEN_GZIP)) {
+                  gzip_output = true;
+                }
+              }
+            }
+
+            value = TSMimeHdrFieldValueStringGet(req_bufp, req_hdr_loc, field_loc, -1, &value_len);
+          }
+
+          if (value != NULL) {
+            HttpHeader header(name, name_len, value, value_len);
+            data_fetcher->useHeader(header);
+            esi_vars->populate(header);
+          }
+        }
+      }
+
+      next_field_loc = TSMimeHdrFieldNext(req_bufp, req_hdr_loc, field_loc);
+      TSHandleMLocRelease(req_bufp, req_hdr_loc, field_loc);
+      field_loc = next_field_loc;
+    }
+  }
+
+  if (gzip_output) {
+    if (option_info->disable_gzip_output) {
+      TSDebug(DEBUG_TAG, "[%s] disable gzip output", __FUNCTION__);
+      gzip_output = false;
+    } else {
+      TSDebug(DEBUG_TAG, "[%s] Client accepts gzip encoding; will compress output", __FUNCTION__);
+    }
+  }
+
+  TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_hdr_loc);
+}
+
+void
+ContData::fillPostHeader(TSMBuffer bufp, TSMLoc hdr_loc) {
+  int n_mime_headers = TSMimeHdrFieldsCount(bufp, hdr_loc);
+  TSMLoc field_loc;
+  const char *name, *value;
+  int name_len, value_len;
+  string header;
+  for (int i = 0; i < n_mime_headers; ++i) {
+    field_loc = TSMimeHdrFieldGet(bufp, hdr_loc, i);
+    if (!field_loc) {
+      TSDebug(DEBUG_TAG, "[%s] Error while obtaining header field #%d", __FUNCTION__, i);
+      continue;
+    }
+    name = TSMimeHdrFieldNameGet(bufp, hdr_loc, field_loc, &name_len);
+    if (name) {
+      if (Utils::areEqual(name, name_len, TS_MIME_FIELD_TRANSFER_ENCODING, TS_MIME_LEN_TRANSFER_ENCODING)) {
+        TSDebug(DEBUG_TAG, "[%s] Not retaining transfer encoding header", __FUNCTION__);
+      } else if (Utils::areEqual(name, name_len, MIME_FIELD_XESI, MIME_FIELD_XESI_LEN)) {
+        TSDebug(DEBUG_TAG, "[%s] Not retaining 'X-Esi' header", __FUNCTION__);
+      } else if (Utils::areEqual(name, name_len, TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH)) {
+        TSDebug(DEBUG_TAG, "[%s] Not retaining 'Content-length' header", __FUNCTION__);
+      }  else {
+        header.assign(name, name_len);
+        header.append(": ");
+        int n_field_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
+        for (int j = 0; j < n_field_values; ++j) {
+          value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, j, &value_len);
+          if ( NULL == value || !value_len ) {
+            TSDebug(DEBUG_TAG, "[%s] Error while getting value #%d of header [%.*s]",
+                     __FUNCTION__, j, name_len, name);
+          } else {
+            if (Utils::areEqual(name, name_len, TS_MIME_FIELD_VARY, TS_MIME_LEN_VARY) &&
+                Utils::areEqual(value, value_len, TS_MIME_FIELD_ACCEPT_ENCODING,
+                                TS_MIME_LEN_ACCEPT_ENCODING)) {
+              TSDebug(DEBUG_TAG, "[%s] Not retaining 'vary: accept-encoding' header", __FUNCTION__);
+            } else if (Utils::areEqual(name, name_len, TS_MIME_FIELD_CONTENT_ENCODING,
+                                       TS_MIME_LEN_CONTENT_ENCODING) &&
+                       Utils::areEqual(value, value_len, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP)) {
+              TSDebug(DEBUG_TAG, "[%s] Not retaining 'content-encoding: gzip' header", __FUNCTION__);
+            } else {
+              if (header[header.size() - 2] != ':') {
+                header.append(", ");
+              }
+              header.append(value, value_len);
+              checkForCacheHeader(name, name_len, value, value_len,
+                                  os_response_cacheable);
+              if (!os_response_cacheable) {
+                TSDebug(DEBUG_TAG, "[%s] Header [%.*s] with value [%.*s] is a no-cache header",
+                         __FUNCTION__, name_len, name, value_len, value);
+                break;
+              }
+            }
+          } // end if got value string
+        } // end value iteration
+
+        if (static_cast<int>(header.size()) > (name_len + 2 /* for ': ' */ )) {
+          header.append("\r\n");
+          post_headers.push_back(header);
+        }
+      } // end if processable header
+    } // end if got header name
+    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+    if (!os_response_cacheable) {
+      post_headers.clear();
+      break;
+    }
+  } // end header iteration
+}
+
+void
+ContData::getServerState() {
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+
+  if (cache_txn) {
+    if (intercept_header) {
+      input_type = DATA_TYPE_PACKED_ESI;
+      return;
+    } else if (TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+      TSError("[%s] Could not get server response; set input type to RAW_ESI", __FUNCTION__);
+      input_type = DATA_TYPE_RAW_ESI;
+      return;
+    }
+  } else if (TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+    TSError("[%s] Could not get server response; set input type to RAW_ESI", __FUNCTION__);
+    input_type = DATA_TYPE_RAW_ESI;
+    return;
+  }
+
+  if (checkHeaderValue(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_ENCODING,
+                       TS_MIME_LEN_CONTENT_ENCODING, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP)) {
+    input_type = DATA_TYPE_GZIPPED_ESI;
+  } else {
+    input_type = DATA_TYPE_RAW_ESI;
+  }
+
+  if (option_info->packed_node_support && !cache_txn && !head_only) {
+    fillPostHeader(bufp, hdr_loc);
+  }
+
+  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+}
+
+ContData::~ContData()
+{
+  TSDebug(debug_tag, "[%s] Destroying continuation data", __FUNCTION__);
+  if (output_reader) {
+    TSIOBufferReaderFree(output_reader);
+  }
+  if (output_buffer) {
+    TSIOBufferDestroy(output_buffer);
+  }
+  if (request_url) {
+    TSfree(request_url);
+  }
+  if (esi_vars) {
+    delete esi_vars;
+  }
+  if (data_fetcher) {
+    delete data_fetcher;
+  }
+  if (esi_proc) {
+    delete esi_proc;
+  }
+  if(esi_gzip) {
+    delete esi_gzip;
+  }
+}
+
+static int removeCacheHandler(TSCont contp, TSEvent event, void *edata) {
+    //TSDebug(DEBUG_TAG, "[%s] event: %d", __FUNCTION__, (int)event);
+    TSContDestroy(contp);
+    //just ignore cache remove message
+    return 0;
+}
+
+static bool removeCacheKey(TSHttpTxn txnp) {
+  TSMBuffer req_bufp;
+  TSMLoc req_hdr_loc;
+  TSMLoc url_loc = NULL;
+  TSCont contp = NULL;
+  TSCacheKey cacheKey = NULL;
+  bool result = false;
+
+  if (TSHttpTxnClientReqGet(txnp, &req_bufp, &req_hdr_loc) != TS_SUCCESS) {
+    TSError("[%s] Error while retrieving client request", __FUNCTION__);
+    return false;
+  }
+
+  do {
+    if(TSHttpTxnPristineUrlGet(txnp, &req_bufp, &url_loc) != TS_SUCCESS) {
+      TSError("[%s] Error while retrieving hdr url", __FUNCTION__);
+      break;
+    }
+
+    contp = TSContCreate(removeCacheHandler, NULL);
+    if (contp == NULL) {
+      TSError("[%s] Could not create continuation", __FUNCTION__);
+      break;
+    }
+
+    cacheKey = TSCacheKeyCreate();
+    if (cacheKey == NULL) {
+      TSError("[%s] TSCacheKeyCreate fail", __FUNCTION__);
+      break;
+    }
+
+    if (TSCacheKeyDigestFromUrlSet(cacheKey, url_loc) != TS_SUCCESS) {
+      TSError("[%s] TSCacheKeyDigestFromUrlSet fail", __FUNCTION__);
+      break;
+    }
+
+    TSCacheRemove(contp, cacheKey);
+    result = true;
+    TSError("[%s] TSCacheRemoved", __FUNCTION__);
+  } while (0);
+
+  if (cacheKey != NULL) {
+      TSCacheKeyDestroy(cacheKey);
+  }
+  if (!result) {
+    if (contp != NULL) {
+      TSContDestroy(contp);
+    }
+  }
+
+  TSHandleMLocRelease(req_bufp, req_hdr_loc, url_loc);
+  if (url_loc != NULL) {
+    TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_hdr_loc);
+  }
+
+  return result;
+}
+
+static void
+cacheNodeList(ContData *cont_data) {
+  if (TSHttpTxnAborted(cont_data->txnp) == TS_SUCCESS) {
+    TSDebug(cont_data->debug_tag, "[%s] Not caching node list as txn has been aborted", __FUNCTION__);
+    return;
+  }
+  string post_request("");
+  post_request.append(TS_HTTP_METHOD_POST);
+  post_request.append(" ");
+  post_request.append(cont_data->request_url);
+  post_request.append(" HTTP/1.0\r\n");
+  post_request.append(SERVER_INTERCEPT_HEADER);
+  post_request.append(": cache=1\r\n");
+  for (list<string>::iterator list_iter = cont_data->post_headers.begin();
+       list_iter != cont_data->post_headers.end(); ++list_iter) {
+    post_request.append(ECHO_HEADER_PREFIX);
+
+  //TSDebug(cont_data->debug_tag, "[%s] header == %s", __FUNCTION__, list_iter->c_str());
+    if (((int)list_iter->length() > HEADER_MASK_PREFIX_SIZE) &&
+            (strncmp(list_iter->c_str(), HEADER_MASK_PREFIX, HEADER_MASK_PREFIX_SIZE) == 0)) {
+      post_request.append(list_iter->c_str() + HEADER_MASK_PREFIX_SIZE, list_iter->length() - HEADER_MASK_PREFIX_SIZE);
+    } else {
+      post_request.append(*list_iter);
+    }
+  }
+  post_request.append(TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
+  post_request.append(": ");
+  post_request.append(TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP);
+  post_request.append("\r\n");
+
+  string body("");
+  cont_data->esi_proc->packNodeList(body, false);
+  char buf[64];
+  snprintf(buf, 64, "%s: %d\r\n\r\n", TS_MIME_FIELD_CONTENT_LENGTH, (int)body.size());
+
+  post_request.append(buf);
+  post_request.append(body);
+
+  //TSError("[%s] DO caching node list size=%d", __FUNCTION__, (int)body.size());
+  //TSDebug(cont_data->debug_tag, "[%s] caching node list size=%d", __FUNCTION__, (int)body.size());
+
+  TSFetchEvent event_ids = {0};
+  TSFetchUrl(post_request.data(), post_request.size(), cont_data->client_addr,
+                  cont_data->contp, NO_CALLBACK, event_ids);
+}
+
+static int
+transformData(TSCont contp)
+{
+  ContData *cont_data;
+  int64_t toread, consumed = 0, avail;
+  bool input_vio_buf_null = false;
+  bool process_input_complete = false;
+
+  // Get the output (downstream) vconnection where we'll write data to.
+  cont_data = static_cast<ContData *>(TSContDataGet(contp));
+
+  // If the input VIO's buffer is NULL, we need to terminate the transformation
+  if (!TSVIOBufferGet(cont_data->input_vio)) {
+    input_vio_buf_null = true;
+    if (cont_data->curr_state == ContData::PROCESSING_COMPLETE) {
+      TSDebug(cont_data->debug_tag, "[%s] input_vio NULL, marking transformation to be terminated",
+               __FUNCTION__);
+      return 1;
+    } else if (cont_data->curr_state == ContData::READING_ESI_DOC) {
+      TSDebug(cont_data->debug_tag, "[%s] input_vio NULL while in read state. Assuming end of input",
+               __FUNCTION__);
+      process_input_complete = true;
+    } else {
+      if (!cont_data->data_fetcher->isFetchComplete()) {
+        TSDebug(cont_data->debug_tag,
+                 "[%s] input_vio NULL, but data needs to be fetched. Returning control", __FUNCTION__);
+        if(!cont_data->option_info->first_byte_flush) {
+          return 1;
+        }
+      } else {
+        TSDebug(cont_data->debug_tag,
+                 "[%s] input_vio NULL, but processing needs to (and can) be completed", __FUNCTION__);
+      }
+    }
+  }
+
+  if (!process_input_complete && (cont_data->curr_state == ContData::READING_ESI_DOC)) {
+    // Determine how much data we have left to read.
+    toread = TSVIONTodoGet(cont_data->input_vio);
+    TSDebug(cont_data->debug_tag, "[%s] upstream VC has %" PRId64" bytes available to read",
+             __FUNCTION__, toread);
+
+    if (toread > 0) {
+      avail = TSIOBufferReaderAvail(cont_data->input_reader);
+      if (avail == TS_ERROR) {
+        TSError("[%s] Error while getting number of bytes available", __FUNCTION__);
+        return 0;
+      }
+
+      // There are some data available for reading. Let's parse it
+      if (avail > 0) {
+        int64_t data_len;
+        const char *data;
+        TSIOBufferBlock block = TSIOBufferReaderStart(cont_data->input_reader);
+        // Now start extraction
+        while (block != NULL) {
+          data = TSIOBufferBlockReadStart(block, cont_data->input_reader, &data_len);
+          if (cont_data->input_type == DATA_TYPE_RAW_ESI) {
+            cont_data->esi_proc->addParseData(data, data_len);
+          } else if (cont_data->input_type == DATA_TYPE_GZIPPED_ESI) {
+            cont_data->gzipped_data.append(data, data_len);
+          } else {
+            cont_data->packed_node_list.append(data, data_len);
+          }
+          TSDebug(cont_data->debug_tag,
+                   "[%s] Added chunk of %" PRId64" bytes starting with [%.10s] to parse list",
+                   __FUNCTION__, data_len, (data_len ? data : "(null)"));
+          consumed += data_len;
+
+          block = TSIOBufferBlockNext(block);
+        }
+      }
+      TSDebug(cont_data->debug_tag, "[%s] Consumed %" PRId64" bytes from upstream VC",
+               __FUNCTION__, consumed);
+
+      TSIOBufferReaderConsume(cont_data->input_reader, consumed);
+
+      // Modify the input VIO to reflect how much data we've completed.
+      TSVIONDoneSet(cont_data->input_vio, TSVIONDoneGet(cont_data->input_vio) + consumed);
+
+      toread = TSVIONTodoGet(cont_data->input_vio); // set this for the test after this if block
+    }
+
+    if (toread > 0) { // testing this again because it might have changed in previous if block
+      // let upstream know we are ready to read new data
+      TSContCall(TSVIOContGet(cont_data->input_vio), TS_EVENT_VCONN_WRITE_READY, cont_data->input_vio);
+    } else {
+      // we have consumed everything that there was to read
+      process_input_complete = true;
+    }
+  }
+  if (process_input_complete) {
+    TSDebug(cont_data->debug_tag, "[%s] Completed reading input...", __FUNCTION__);
+    if (cont_data->input_type == DATA_TYPE_PACKED_ESI) {
+      TSDebug(DEBUG_TAG, "[%s] Going to use packed node list of size %d",
+               __FUNCTION__, (int) cont_data->packed_node_list.size());
+      if (cont_data->esi_proc->usePackedNodeList(cont_data->packed_node_list) == EsiProcessor::UNPACK_FAILURE) {
+        removeCacheKey(cont_data->txnp);
+
+        cont_data->input_type = DATA_TYPE_RAW_ESI;
+        cont_data->esi_proc->start();
+        cont_data->esi_proc->addParseData(cont_data->packed_node_list.data(), cont_data->packed_node_list.size());
+      }
+    }
+
+    if (cont_data->input_type != DATA_TYPE_PACKED_ESI) {
+      if (cont_data->input_type == DATA_TYPE_GZIPPED_ESI) {
+        BufferList buf_list;
+        if (gunzip(cont_data->gzipped_data.data(), cont_data->gzipped_data.size(), buf_list)) {
+          for (BufferList::iterator iter = buf_list.begin(); iter != buf_list.end(); ++iter) {
+            cont_data->esi_proc->addParseData(iter->data(), iter->size());
+          }
+        } else {
+          TSError("[%s] Error while gunzipping data", __FUNCTION__);
+        }
+      }
+      if (cont_data->esi_proc->completeParse()) {
+        if (cont_data->option_info->packed_node_support && cont_data->os_response_cacheable
+            && !cont_data->cache_txn && !cont_data->head_only)
+        {
+          cacheNodeList(cont_data);
+        }
+      }
+    }
+
+    cont_data->curr_state = ContData::FETCHING_DATA;
+    if (!input_vio_buf_null) {
+      TSContCall(TSVIOContGet(cont_data->input_vio), TS_EVENT_VCONN_WRITE_COMPLETE,
+                  cont_data->input_vio);
+    }
+  }
+
+  if ((cont_data->curr_state == ContData::FETCHING_DATA) &&
+      (!cont_data->option_info->first_byte_flush)) { // retest as state may have changed in previous block
+    if (cont_data->data_fetcher->isFetchComplete()) {
+      TSDebug(cont_data->debug_tag, "[%s] data ready; going to process doc", __FUNCTION__);
+      const char *out_data;
+      int out_data_len;
+      EsiProcessor::ReturnCode retval = cont_data->esi_proc->process(out_data, out_data_len);
+      TSDebug(cont_data->debug_tag, "[%s] data length: %d, retval: %d", __FUNCTION__, out_data_len, retval);
+      if (retval == EsiProcessor::NEED_MORE_DATA) {
+        TSDebug(cont_data->debug_tag, "[%s] ESI processor needs more data; "
+                 "will wait for all data to be fetched", __FUNCTION__);
+        return 1;
+      }
+      cont_data->curr_state = ContData::PROCESSING_COMPLETE;
+      if (retval == EsiProcessor::SUCCESS) {
+        TSDebug(cont_data->debug_tag,
+                 "[%s] ESI processor output document of size %d starting with [%.10s]",
+                 __FUNCTION__, out_data_len, (out_data_len ? out_data : "(null)"));
+      } else {
+        TSError("[%s] ESI processor failed to process document; will return empty document", __FUNCTION__);
+        out_data = "";
+        out_data_len = 0;
+      }
+
+      // make sure transformation has not been prematurely terminated
+      if (!cont_data->xform_closed) {
+        string cdata;
+        if (cont_data->gzip_output) {
+          if (!gzip(out_data, out_data_len, cdata)) {
+            TSError("[%s] Error while gzipping content", __FUNCTION__);
+            out_data_len = 0;
+            out_data = "";
+          } else {
+            TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes",
+                     __FUNCTION__, out_data_len, (int) cdata.size());
+            out_data_len = cdata.size();
+            out_data = cdata.data();
+          }
+        }
+
+        // Get downstream VIO
+        TSVConn output_conn;
+        output_conn = TSTransformOutputVConnGet(contp);
+        if (!output_conn) {
+          TSError("[%s] Error while getting transform VC", __FUNCTION__);
+          return 0;
+        }
+
+        TSVIO output_vio;
+        output_vio = TSVConnWrite(output_conn, contp, cont_data->output_reader, out_data_len);
+
+        if (TSIOBufferWrite(TSVIOBufferGet(output_vio), out_data, out_data_len) == TS_ERROR) {
+          TSError("[%s] Error while writing bytes to downstream VC", __FUNCTION__);
+          return 0;
+        }
+
+        TSVIONBytesSet(output_vio, out_data_len);
+
+        // Reenable the output connection so it can read the data we've produced.
+        TSVIOReenable(output_vio);
+      }
+    } else {
+      TSDebug(cont_data->debug_tag, "[%s] Data not available yet; cannot process document",
+               __FUNCTION__);
+    }
+  }
+
+  if ((cont_data->curr_state == ContData::FETCHING_DATA) &&
+      (cont_data->option_info->first_byte_flush)) { // retest as state may have changed in previous block
+    TSDebug(cont_data->debug_tag, "[%s] trying to process doc", __FUNCTION__);
+    string out_data;
+    string cdata;
+    int overall_len;
+    EsiProcessor::ReturnCode retval = cont_data->esi_proc->flush(out_data, overall_len);
+
+    if (cont_data->data_fetcher->isFetchComplete()) {
+      TSDebug(cont_data->debug_tag, "[%s] data ready; last process() will have finished the entire processing", __FUNCTION__);
+      cont_data->curr_state = ContData::PROCESSING_COMPLETE;
+    }
+
+    if (retval == EsiProcessor::SUCCESS) {
+      TSDebug(cont_data->debug_tag,
+                 "[%s] ESI processor output document of size %d starting with [%.10s]",
+                 __FUNCTION__, (int) out_data.size(), (out_data.size() ? out_data.data() : "(null)"));
+    } else {
+      TSError("[%s] ESI processor failed to process document; will return empty document", __FUNCTION__);
+      out_data.assign("");
+        
+      if(!cont_data->xform_closed) {
+        TSVIONBytesSet(cont_data->output_vio, 0);
+        TSVIOReenable(cont_data->output_vio);
+      }
+    }
+
+    // make sure transformation has not been prematurely terminated
+    if (!cont_data->xform_closed && out_data.size() > 0) {
+      if (cont_data->gzip_output) {
+        if (!cont_data->esi_gzip->stream_encode(out_data, cdata)) {
+          TSError("[%s] Error while gzipping content", __FUNCTION__);
+        } else {
+          TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes",
+                     __FUNCTION__, (int) out_data.size(), (int) cdata.size());
+        }
+      }
+
+      if (cont_data->gzip_output) {
+        if (TSIOBufferWrite(TSVIOBufferGet(cont_data->output_vio), cdata.data(), cdata.size()) == TS_ERROR) {
+          TSError("[%s] Error while writing bytes to downstream VC", __FUNCTION__);
+          return 0;
+        }
+      } else {
+        if (TSIOBufferWrite(TSVIOBufferGet(cont_data->output_vio), out_data.data(), out_data.size()) == TS_ERROR) {
+          TSError("[%s] Error while writing bytes to downstream VC", __FUNCTION__);
+          return 0;
+        }
+      }
+    }
+    if(!cont_data->xform_closed) {     
+      // should not set any fixed length
+      if(cont_data->curr_state == ContData::PROCESSING_COMPLETE) {
+        if(cont_data->gzip_output) {
+          string cdata;
+          int downstream_length;
+          if(!cont_data->esi_gzip->stream_finish(cdata, downstream_length)) {
+            TSError("[%s] Error while finishing gzip", __FUNCTION__);
+            return 0;  
+          } else {   
+            if (TSIOBufferWrite(TSVIOBufferGet(cont_data->output_vio), cdata.data(), cdata.size()) == TS_ERROR) {
+              TSError("[%s] Error while writing bytes to downstream VC", __FUNCTION__);
+              return 0;
+            }
+            TSDebug(cont_data->debug_tag,
+                 "[%s] ESI processed overall/gzip: %d",
+                 __FUNCTION__, downstream_length );
+            TSVIONBytesSet(cont_data->output_vio, downstream_length);          
+          }
+        } else {
+          TSDebug(cont_data->debug_tag,
+                 "[%s] ESI processed overall: %d",
+                 __FUNCTION__, overall_len );
+          TSVIONBytesSet(cont_data->output_vio, overall_len);
+        }
+      } 
+   
+      // Reenable the output connection so it can read the data we've produced.
+      TSVIOReenable(cont_data->output_vio);
+    }
+  }
+
+  return 1;
+}
+
+static int
+transformHandler(TSCont contp, TSEvent event, void *edata)
+{
+  TSVIO input_vio;
+  ContData *cont_data;
+  cont_data = static_cast<ContData *>(TSContDataGet(contp));
+
+  // we need these later, but declaring now avoid compiler warning w.r.t. goto
+  bool process_event = true;
+  const char *cont_debug_tag;
+  bool shutdown, is_fetch_event;
+
+  if (!cont_data->initialized) {
+    if (!cont_data->init()) {
+      TSError("[%s] Could not initialize continuation data; shutting down transformation", __FUNCTION__);
+      goto lShutdown;
+    }
+    TSDebug(cont_data->debug_tag, "[%s] initialized continuation data", __FUNCTION__);
+  }
+
+  cont_debug_tag = cont_data->debug_tag; // just a handy reference
+
+  cont_data->checkXformStatus();
+
+  is_fetch_event = cont_data->data_fetcher->isFetchEvent(event);
+
+
+  if (cont_data->xform_closed) {
+    TSDebug(cont_debug_tag, "[%s] Transformation closed. Post-processing...", __FUNCTION__);
+    if (cont_data->curr_state == ContData::PROCESSING_COMPLETE) {
+      TSDebug(cont_debug_tag, "[%s] Processing is complete, not processing current event %d",
+               __FUNCTION__, event);
+      process_event = false;
+    } else if (cont_data->curr_state == ContData::READING_ESI_DOC) {
+      TSDebug(cont_debug_tag, "[%s] Parsing is incomplete, will force end of input",
+               __FUNCTION__);
+      cont_data->curr_state = ContData::FETCHING_DATA;
+    }
+    if (cont_data->curr_state == ContData::FETCHING_DATA) { // retest as it may be modified in prev. if block
+      if (cont_data->data_fetcher->isFetchComplete()) {
+        TSDebug(cont_debug_tag,
+                 "[%s] Requested data has been fetched; will skip event and marking processing as complete ",
+                 __FUNCTION__);
+        cont_data->curr_state = ContData::PROCESSING_COMPLETE;
+        process_event = false;
+      } else {
+        if (is_fetch_event) {
+          TSDebug(cont_debug_tag, "[%s] Going to process received data",
+                   __FUNCTION__);
+        } else {
+          // transformation is over, but data hasn't been fetched;
+          // let's wait for data to be fetched - we will be called
+          // by Fetch API and go through this loop again
+          TSDebug(cont_debug_tag, "[%s] Ignoring event %d; Will wait for pending data",
+                   __FUNCTION__, event);
+          process_event = false;
+        }
+      }
+    }
+  }
+
+  if (process_event) {
+    switch (event) {
+    case TS_EVENT_ERROR:
+      // doubt: what is this code doing?
+      input_vio = TSVConnWriteVIOGet(contp);
+      if (!input_vio) {
+        TSError("[%s] Error while getting upstream vio", __FUNCTION__);
+      } else {
+        TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio);
+      }
+      // FetchSM also might send this; let's just output whatever we have
+      cont_data->curr_state = ContData::FETCHING_DATA;
+      transformData(contp);
+      break;
+
+    case TS_EVENT_VCONN_WRITE_READY:
+      TSDebug(cont_debug_tag, "[%s] WRITE_READY", __FUNCTION__);
+      if(!cont_data->option_info->first_byte_flush) {
+        TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);
+      }
+      break;
+
+    case TS_EVENT_VCONN_WRITE_COMPLETE:
+      TSDebug(cont_debug_tag, "[%s] shutting down transformation", __FUNCTION__);
+      TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);
+      break;
+
+    case TS_EVENT_IMMEDIATE:
+      TSDebug(cont_debug_tag, "[%s] handling TS_EVENT_IMMEDIATE...", __FUNCTION__);
+      transformData(contp);
+      break;
+
+    default:
+      if (is_fetch_event) {
+        TSDebug(cont_debug_tag, "[%s] Handling fetch event %d...", __FUNCTION__, event);
+        if (cont_data->data_fetcher->handleFetchEvent(event, edata)) {
+          if (cont_data->curr_state == ContData::FETCHING_DATA) {
+            // there's a small chance that fetcher is ready even before
+            // parsing is complete; hence we need to check the state too
+            if(cont_data->option_info->first_byte_flush ||
+               cont_data->data_fetcher->isFetchComplete()){
+              TSDebug(cont_debug_tag, "[%s] fetcher is ready with data, going into process stage",
+                     __FUNCTION__);
+              transformData(contp);
+            }
+          }
+        } else {
+          TSError("[%s] Could not handle fetch event!", __FUNCTION__);
+        }
+      } else {
+        TSAssert(!"Unexpected event");
+      }
+      break;
+    }
+  }
+
+  TSDebug(cont_data->debug_tag, "[%s] transformHandler, event: %d, curr_state: %d", __FUNCTION__, (int)event, (int)cont_data->curr_state);
+
+  shutdown = (cont_data->xform_closed && (cont_data->curr_state == ContData::PROCESSING_COMPLETE));
+  if (shutdown) {
+    if (process_event && is_fetch_event) {
+      // we need to return control to the fetch API to give up it's
+      // lock on our continuation which will fail if we destroy
+      // ourselves right now
+      TSDebug(cont_debug_tag, "[%s] Deferring shutdown as data event was just processed", __FUNCTION__);
+      TSContSchedule(contp, 10, TS_THREAD_POOL_TASK);
+    } else {
+      goto lShutdown;
+    }
+  }
+
+  return 1;
+
+lShutdown:
+  TSDebug(cont_data->debug_tag, "[%s] transformation closed; cleaning up data...", __FUNCTION__);
+  delete cont_data;
+  TSContDestroy(contp);
+  return 1;
+}
+
+struct RespHdrModData {
+  bool cache_txn;
+  bool gzip_encoding;
+  bool head_only;
+  const struct OptionInfo *option_info;
+};
+
+static void
+addMimeHeaderField(TSMBuffer bufp, TSMLoc hdr_loc, const char *name, int name_len,
+                   const char *value, int value_len) {
+  TSMLoc field_loc = (TSMLoc)NULL;
+  TSMimeHdrFieldCreate(bufp, hdr_loc, &field_loc);
+  if (!field_loc) {
+    TSError("[%s] Error while creating mime field", __FUNCTION__);
+  } else {
+    if (TSMimeHdrFieldNameSet(bufp, hdr_loc, field_loc, name, name_len) != TS_SUCCESS) {
+      TSError("[%s] Error while setting name [%.*s] for MIME header field", __FUNCTION__, name_len, name);
+    } else {
+      if (TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, field_loc, 0, value, value_len) != TS_SUCCESS) {
+        TSError("[%s] Error while inserting value [%.*s] string to MIME field [%.*s]", __FUNCTION__,
+                 value_len, value, name_len, name);
+      } else {
+        if (TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc) != TS_SUCCESS) {
+          TSError("[%s] Error while appending MIME field with name [%.*s] and value [%.*s]", __FUNCTION__,
+                   name_len, name, value_len, value);
+        }
+      }
+    }
+    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+  }
+}
+
+static int
+modifyResponseHeader(TSCont contp, TSEvent event, void *edata) {
+  int retval = 0;
+  RespHdrModData *mod_data = static_cast<RespHdrModData *>(TSContDataGet(contp));
+  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
+  if (event != TS_EVENT_HTTP_SEND_RESPONSE_HDR) {
+    TSError("[%s] Unexpected event (%d)", __FUNCTION__, event);
+    goto lReturn;
+  }
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) {
+    int n_mime_headers = TSMimeHdrFieldsCount(bufp, hdr_loc);
+    TSMLoc field_loc;
+    const char *name, *value;
+    int name_len, value_len;
+    bool have_content_length = false;
+    for (int i = 0; i < n_mime_headers; ++i) {
+      field_loc = TSMimeHdrFieldGet(bufp, hdr_loc, i);
+      if (!field_loc) {
+        TSDebug(DEBUG_TAG, "[%s] Error while obtaining header field #%d", __FUNCTION__, i);
+        continue;
+      }
+      name = TSMimeHdrFieldNameGet(bufp, hdr_loc, field_loc, &name_len);
+      if (name) {
+        bool destroy_header = false;
+
+        if (Utils::areEqual(name, name_len, SERVER_INTERCEPT_HEADER, SERVER_INTERCEPT_HEADER_LEN)) {
+          destroy_header = true;
+        } else if (Utils::areEqual(name, name_len, TS_MIME_FIELD_AGE, TS_MIME_LEN_AGE)) {
+          destroy_header = true;
+        } else if (Utils::areEqual(name, name_len, MIME_FIELD_XESI, MIME_FIELD_XESI_LEN)) {
+          destroy_header = true;
+        } else if ((name_len > HEADER_MASK_PREFIX_SIZE) &&
+                   (strncmp(name, HEADER_MASK_PREFIX, HEADER_MASK_PREFIX_SIZE) == 0))
+         {
+          destroy_header = true;
+        } else if (mod_data->option_info->private_response && (Utils::areEqual(name,
+                name_len, TS_MIME_FIELD_CACHE_CONTROL, TS_MIME_LEN_CACHE_CONTROL)
+              || Utils::areEqual(name, name_len, TS_MIME_FIELD_EXPIRES,
+                TS_MIME_LEN_EXPIRES)))
+        {
+          destroy_header = true;
+        } else if (Utils::areEqual(name, name_len, TS_MIME_FIELD_CONTENT_LENGTH,
+              TS_MIME_LEN_CONTENT_LENGTH))
+        {
+          have_content_length = true;
+          if (mod_data->head_only) {
+            destroy_header = true;
+            TSError("[%s] remove Content-Length", __FUNCTION__);
+          }
+        } else {
+          int n_field_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
+          for (int j = 0; j < n_field_values; ++j) {
+            value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, j, &value_len);
+            if ( NULL == value || !value_len ) {
+              TSDebug(DEBUG_TAG, "[%s] Error while getting value #%d of header [%.*s]",
+                       __FUNCTION__, j, name_len, name);
+            } else {
+              if (!mod_data->option_info->packed_node_support || mod_data->cache_txn) {
+                bool response_cacheable, is_cache_header;
+                is_cache_header = checkForCacheHeader(name, name_len, value, value_len, response_cacheable);
+                if (is_cache_header && response_cacheable) {
+                  destroy_header = true;
+                }
+              }
+            } // if got valid value for header
+          } // end for
+        }
+        if (destroy_header) {
+          TSDebug(DEBUG_TAG, "[%s] Removing header with name [%.*s]", __FUNCTION__, name_len, name);
+          TSMimeHdrFieldDestroy(bufp, hdr_loc, field_loc);
+          --n_mime_headers;
+          --i;
+        }
+      }
+      TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+    }
+    if (mod_data->gzip_encoding &&
+        !checkHeaderValue(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_ENCODING,
+          TS_MIME_LEN_CONTENT_ENCODING, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP))
+    {
+      addMimeHeaderField(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_ENCODING,
+          TS_MIME_LEN_CONTENT_ENCODING, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP);
+    }
+
+    if (!have_content_length) {
+      TSError("[%s] no Content-Length!", __FUNCTION__);
+    }
+
+    if (mod_data->option_info->packed_node_support && mod_data->cache_txn) {
+      addMimeHeaderField(bufp, hdr_loc, TS_MIME_FIELD_VARY,
+          TS_MIME_LEN_VARY, TS_MIME_FIELD_ACCEPT_ENCODING,
+          TS_MIME_LEN_ACCEPT_ENCODING);
+    }
+
+    if (mod_data->option_info->private_response) {
+      addMimeHeaderField(bufp, hdr_loc, TS_MIME_FIELD_EXPIRES,
+          TS_MIME_LEN_EXPIRES, HTTP_VALUE_PRIVATE_EXPIRES,
+          sizeof(HTTP_VALUE_PRIVATE_EXPIRES) - 1);
+      addMimeHeaderField(bufp, hdr_loc, TS_MIME_FIELD_CACHE_CONTROL,
+          TS_MIME_LEN_CACHE_CONTROL, HTTP_VALUE_PRIVATE_CC,
+          sizeof(HTTP_VALUE_PRIVATE_CC) - 1);
+    }
+
+    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+    TSDebug(DEBUG_TAG, "[%s] Inspected client-bound headers", __FUNCTION__);
+    retval = 1;
+  } else {
+    TSError("[%s] Error while getting response from txn", __FUNCTION__);
+  }
+
+lReturn:
+  delete mod_data;
+  TSContDestroy(contp);
+  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+  return retval;
+}
+
+static bool
+checkHeaderValue(TSMBuffer bufp, TSMLoc hdr_loc, const char *name, int name_len,
+                 const char *exp_value, int exp_value_len, bool prefix) {
+  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, name, name_len);
+  if (!field_loc) {
+    return false;
+  }
+  bool retval = false;
+  if (exp_value && exp_value_len) {
+    const char *value;
+    int value_len;
+    int n_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
+    for (int i = 0; i < n_values; ++i) {
+      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, i, &value_len);
+      if ( NULL != value || value_len ) {
+        if (prefix) {
+          if ((value_len >= exp_value_len) &&
+              (strncasecmp(value, exp_value, exp_value_len) == 0)) {
+            retval = true;
+          }
+        } else if (Utils::areEqual(value, value_len, exp_value, exp_value_len)) {
+          retval = true;
+        }
+      } else {
+        TSDebug(DEBUG_TAG, "[%s] Error while getting value # %d of header [%.*s]", __FUNCTION__,
+                 i, name_len, name);
+      }
+      if (retval) {
+        break;
+      }
+    }
+  } else { // only presence required
+    retval = true;
+  }
+  TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+  return retval;
+}
+
+static void
+maskOsCacheHeaders(TSHttpTxn txnp) {
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+  if (TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+    TSError("[%s] Couldn't get server response from txn", __FUNCTION__);
+    return;
+  }
+  int n_mime_headers = TSMimeHdrFieldsCount(bufp, hdr_loc);
+  TSMLoc field_loc;
+  const char *name, *value;
+  int name_len, value_len, n_field_values;
+  bool os_response_cacheable, is_cache_header, mask_header;
+  string masked_name;
+  os_response_cacheable = true;
+  for (int i = 0; i < n_mime_headers; ++i) {
+    field_loc = TSMimeHdrFieldGet(bufp, hdr_loc, i);
+    if (!field_loc) {
+      TSDebug(DEBUG_TAG, "[%s] Error while obtaining header field #%d", __FUNCTION__, i);
+      continue;
+    }
+    name = TSMimeHdrFieldNameGet(bufp, hdr_loc, field_loc, &name_len);
+    if (name) {
+      mask_header = is_cache_header = false;
+      n_field_values = TSMimeHdrFieldValuesCount(bufp, hdr_loc, field_loc);
+      for (int j = 0; j < n_field_values; ++j) {
+        value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, j, &value_len);
+        if ( NULL == value || !value_len ) {
+          TSDebug(DEBUG_TAG, "[%s] Error while getting value #%d of header [%.*s]",
+                   __FUNCTION__, j, name_len, name);
+        } else {
+          is_cache_header = checkForCacheHeader(name, name_len, value, value_len, os_response_cacheable);
+          if (!os_response_cacheable) {
+            break;
+          }
+          if (is_cache_header) {
+            TSDebug(DEBUG_TAG, "[%s] Masking OS cache header [%.*s] with value [%.*s]. ",
+                     __FUNCTION__, name_len, name, value_len, value);
+            mask_header = true;
+          }
+        } // end if got value string
+      } // end value iteration
+      if (mask_header) {
+        masked_name.assign(HEADER_MASK_PREFIX);
+        masked_name.append(name, name_len);
+        if (TSMimeHdrFieldNameSet(bufp, hdr_loc, field_loc, masked_name.data(),
+                                   masked_name.size()) != TS_SUCCESS) {
+          TSError("[%s] Couldn't rename header [%.*s]", __FUNCTION__, name_len, name);
+        }
+      }
+    } // end if got header name
+    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+    if (!os_response_cacheable) {
+      break;
+    }
+  } // end header iteration
+  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+}
+
+static bool
+isTxnTransformable(TSHttpTxn txnp, bool is_cache_txn, bool * intercept_header, bool * head_only) {
+  //  We are only interested in transforming "200 OK" responses with a
+  //  Content-Type: text/ header and with X-Esi header
+
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+  TSHttpStatus resp_status;
+  TSReturnCode header_obtained;
+  bool retval = false;
+
+  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+    TSError("[%s] Couldn't get txn header", __FUNCTION__);
+    return false;
+  }
+
+  int method_len;
+  const char *method;
+  method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
+  if (method == NULL) {
+    TSError("[%s] Couldn't get method", __FUNCTION__);
+    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+    return false;
+  }
+
+  if (method_len >= TS_HTTP_LEN_HEAD && memcmp(method, TS_HTTP_METHOD_HEAD, TS_HTTP_LEN_HEAD) == 0) {
+    *head_only = true;
+  }
+  else if (!(((method_len >= TS_HTTP_LEN_POST && memcmp(method, TS_HTTP_METHOD_POST, TS_HTTP_LEN_POST) == 0)) ||
+        ((method_len >= TS_HTTP_LEN_GET && memcmp(method, TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET) == 0))))
+  {
+    TSDebug(DEBUG_TAG, "[%s] method %.*s will be ignored", __FUNCTION__, method_len, method);
+    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+    return false;
+  }
+  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+
+  header_obtained = is_cache_txn ? TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc) :
+    TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc);
+  if (header_obtained != TS_SUCCESS) {
+    TSError("[%s] Couldn't get txn header", __FUNCTION__);
+    return false;
+  }
+
+  do {
+    *intercept_header = checkHeaderValue(bufp, hdr_loc, SERVER_INTERCEPT_HEADER, SERVER_INTERCEPT_HEADER_LEN);
+    if (*intercept_header) {
+      if (is_cache_txn) {
+        TSDebug(DEBUG_TAG, "[%s] Packed ESI document found in cache; will process", __FUNCTION__);
+        retval = true;
+      } else {
+        TSDebug(DEBUG_TAG, "[%s] Found Intercept header in server response; document not processable",
+            __FUNCTION__);
+      }
+      break; // found internal header; no other detection required
+    }
+
+    // allow response with specific status code to be transformable
+    resp_status = TSHttpHdrStatusGet(bufp, hdr_loc);
+    if (static_cast<int>(resp_status) == static_cast<int>(TS_ERROR)) {
+      TSError("[%s] Error while getting http status", __FUNCTION__);
+      break;
+    }
+    if (resp_status != TS_HTTP_STATUS_OK) {
+      TSDebug(DEBUG_TAG, "[%s] Not handling non-OK response status %d", __FUNCTION__, resp_status);
+      break;
+    }
+
+    if (!checkHeaderValue(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_TYPE, TS_MIME_LEN_CONTENT_TYPE,
+          "text/", 5, true)) {
+      TSDebug(DEBUG_TAG, "[%s] Not text content", __FUNCTION__);
+      break;
+    }
+    if (!checkHeaderValue(bufp, hdr_loc, MIME_FIELD_XESI, MIME_FIELD_XESI_LEN)) {
+      TSDebug(DEBUG_TAG, "[%s] ESI header [%s] not found", __FUNCTION__, MIME_FIELD_XESI);
+      break;
+    }
+
+    retval = true;
+  } while (0);
+
+  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+  return retval;
+}
+
+static bool
+isCacheObjTransformable(TSHttpTxn txnp, bool * intercept_header, bool * head_only) {
+  int obj_status;
+  if (TSHttpTxnCacheLookupStatusGet(txnp, &obj_status) == TS_ERROR) {
+    TSError("[%s] Couldn't get cache status of object", __FUNCTION__);
+    return false;
+  }
+  if ((obj_status == TS_CACHE_LOOKUP_HIT_FRESH) || (obj_status == TS_CACHE_LOOKUP_HIT_STALE)) {
+    /*
+    time_t respTime;
+    if (TSHttpTxnCachedRespTimeGet(txnp, &respTime) == TS_SUCCESS) {
+      TSError("[%s] RespTime; %d", __FUNCTION__, (int)respTime);
+    }
+    */
+
+    TSDebug(DEBUG_TAG, "[%s] doc found in cache, will add transformation", __FUNCTION__);
+    return isTxnTransformable(txnp, true, intercept_header, head_only);
+  }
+  TSDebug(DEBUG_TAG, "[%s] cache object's status is %d; not transformable",
+           __FUNCTION__, obj_status);
+  return false;
+}
+
+static bool
+isInterceptRequest(TSHttpTxn txnp) {
+  if (TSHttpIsInternalRequest(txnp) != TS_SUCCESS) {
+    TSDebug(DEBUG_TAG, "[%s] Skipping external request", __FUNCTION__);
+    return false;
+  }
+
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+    TSError("[%s] Could not get client request", __FUNCTION__);
+    return false;
+  }
+
+  bool valid_request = false;
+  bool retval = false;
+  int method_len;
+  const char *method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
+  if (!method) {
+    TSError("[%s] Could not obtain method!", __FUNCTION__);
+  } else {
+    if ((method_len != TS_HTTP_LEN_POST) ||
+        (strncasecmp(method, TS_HTTP_METHOD_POST, TS_HTTP_LEN_POST))) {
+      TSDebug(DEBUG_TAG, "[%s] Method [%.*s] invalid, [%s] expected", __FUNCTION__, method_len, method,
+               TS_HTTP_METHOD_POST);
+    } else {
+      TSDebug(DEBUG_TAG, "[%s] Valid server intercept method found", __FUNCTION__);
+      valid_request = true;
+    }
+  }
+
+  if (valid_request) {
+    retval = checkHeaderValue(bufp, hdr_loc, SERVER_INTERCEPT_HEADER, SERVER_INTERCEPT_HEADER_LEN);
+  }
+  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+  return retval;
+}
+
+static bool
+checkForCacheHeader(const char *name, int name_len, const char *value, int value_len, bool &cacheable) {
+  cacheable = true;
+  if (Utils::areEqual(name, name_len, TS_MIME_FIELD_EXPIRES, TS_MIME_LEN_EXPIRES)) {
+    if ((value_len == 1) && (*value == '0')) {
+      cacheable = false;
+    }else if (Utils::areEqual(value, value_len, "-1",2)) {
+      cacheable = false;
+    }
+    return true;
+  }
+  if (Utils::areEqual(name, name_len, TS_MIME_FIELD_CACHE_CONTROL, TS_MIME_LEN_CACHE_CONTROL)) {
+    if (Utils::areEqual(value, value_len, TS_HTTP_VALUE_PRIVATE, TS_HTTP_LEN_PRIVATE)) {
+      cacheable = false;
+    }
+    return true;
+  }
+  return false;
+}
+
+static bool
+addSendResponseHeaderHook(TSHttpTxn txnp, const ContData * src_cont_data) {
+  TSCont contp = TSContCreate(modifyResponseHeader, NULL);
+  if (!contp) {
+    TSError("[%s] Could not create continuation", __FUNCTION__);
+    return false;
+  }
+  TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
+  RespHdrModData *cont_data = new RespHdrModData();
+  cont_data->option_info = src_cont_data->option_info;
+  cont_data->cache_txn = src_cont_data->cache_txn;
+  cont_data->head_only = src_cont_data->head_only;
+  cont_data->gzip_encoding = src_cont_data->gzip_output;
+  TSContDataSet(contp, cont_data);
+  return true;
+}
+
+static bool
+addTransform(TSHttpTxn txnp, const bool processing_os_response,
+    const bool intercept_header, const bool head_only,
+    const struct OptionInfo *pOptionInfo)
+{
+  TSCont contp = 0;
+  ContData *cont_data = 0;
+
+  contp = TSTransformCreate(transformHandler, txnp);
+  if (!contp) {
+    TSError("[%s] Error while creating a new transformation", __FUNCTION__);
+    goto lFail;
+  }
+
+  cont_data = new ContData(contp, txnp);
+  TSContDataSet(contp, cont_data);
+
+  cont_data->option_info = pOptionInfo;
+  cont_data->cache_txn = !processing_os_response;
+  cont_data->intercept_header = intercept_header;
+  cont_data->head_only = head_only;
+  cont_data->getClientState();
+  cont_data->getServerState();
+
+  if (cont_data->cache_txn) {
+    if (cont_data->option_info->packed_node_support) {
+      if (cont_data->input_type != DATA_TYPE_PACKED_ESI) {
+        removeCacheKey(txnp);
+      }
+    } else {
+      if (cont_data->input_type == DATA_TYPE_PACKED_ESI) {
+        removeCacheKey(txnp);
+      }
+    }
+  }
+
+  TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, contp);
+
+  if (!addSendResponseHeaderHook(txnp, cont_data)) {
+    TSError("[%s] Couldn't add send response header hook", __FUNCTION__);
+    goto lFail;
+  }
+
+  TSHttpTxnTransformedRespCache(txnp, 0);
+  if (cont_data->option_info->packed_node_support) {
+    TSHttpTxnUntransformedRespCache(txnp, 0);
+  } else {
+    TSHttpTxnUntransformedRespCache(txnp, 1);
+  }
+
+  TSDebug(DEBUG_TAG, "[%s] Added transformation (0x%p)", __FUNCTION__, contp);
+  return true;
+
+lFail:
+  if (contp) {
+    TSContDestroy(contp);
+  }
+  if (cont_data) {
+    delete cont_data;
+  }
+  return false;
+}
+
+pthread_key_t threadKey = 0;
+static int
+globalHookHandler(TSCont contp, TSEvent event, void *edata) {
+  TSHttpTxn txnp = (TSHttpTxn) edata;
+  bool intercept_header = false;
+  bool head_only = false;
+  bool intercept_req = isInterceptRequest(txnp);
+  struct OptionInfo *pOptionInfo = (struct OptionInfo *)TSContDataGet(contp);
+
+  switch (event) {
+  case TS_EVENT_HTTP_READ_REQUEST_HDR:
+    TSDebug(DEBUG_TAG, "[%s] handling read request header event...", __FUNCTION__);
+    if (intercept_req) {
+      if (!setupServerIntercept(txnp)) {
+        TSError("[%s] Could not setup server intercept", __FUNCTION__);
+      } else {
+        TSDebug(DEBUG_TAG, "[%s] Setup server intercept", __FUNCTION__);
+      }
+    } else {
+      TSDebug(DEBUG_TAG, "[%s] Not setting up intercept", __FUNCTION__);
+    }
+    break;
+
+  case TS_EVENT_HTTP_READ_RESPONSE_HDR:
+  case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
+    if (!intercept_req) {
+      if (event == TS_EVENT_HTTP_READ_RESPONSE_HDR) {
+        bool mask_cache_headers = false;
+        TSDebug(DEBUG_TAG, "[%s] handling read response header event...", __FUNCTION__);
+        if (isCacheObjTransformable(txnp, &intercept_header, &head_only)) {
+          // transformable cache object will definitely have a
+          // transformation already as cache_lookup_complete would
+          // have been processed before this
+          TSDebug(DEBUG_TAG, "[%s] xform should already have been added on cache lookup. Not adding now",
+                   __FUNCTION__);
+          mask_cache_headers = true;
+        } else if (isTxnTransformable(txnp, false, &intercept_header, &head_only)) {
+          addTransform(txnp, true, intercept_header, head_only, pOptionInfo);
+          Stats::increment(Stats::N_OS_DOCS);
+          mask_cache_headers = true;
+        }
+        if (pOptionInfo->packed_node_support && mask_cache_headers) {
+          // we'll 'mask' OS cache headers so that traffic server will
+          // not try to cache this. We cannot outright delete them
+          // because we need them in our POST request; hence the 'masking'
+          maskOsCacheHeaders(txnp);
+        }
+      } else {
+        TSDebug(DEBUG_TAG, "[%s] handling cache lookup complete event...", __FUNCTION__);
+        if (isCacheObjTransformable(txnp, &intercept_header, &head_only)) {
+          // we make the assumption above that a transformable cache
+          // object would already have a tranformation. We should revisit
+          // that assumption in case we change the statement below
+          addTransform(txnp, false, intercept_header, head_only, pOptionInfo);
+          Stats::increment(Stats::N_CACHE_DOCS);
+        }
+      }
+    }
+    break;
+
+  default:
+    TSDebug(DEBUG_TAG, "[%s] Don't know how to handle event type %d", __FUNCTION__, event);
+    break;
+  }
+
+  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+  return 0;
+}
+
+static void
+loadHandlerConf(const char *file_name, Utils::KeyValueMap &handler_conf) {
+  std::list<string> conf_lines;
+  TSFile conf_file = TSfopen(file_name, "r");
+  if (conf_file != NULL) {
+    char buf[1024];
+    while (TSfgets(conf_file, buf, sizeof(buf) - 1) != NULL) {
+      conf_lines.push_back(string(buf));
+    }
+    TSfclose(conf_file);
+    Utils::parseKeyValueConfig(conf_lines, handler_conf);
+    TSDebug(DEBUG_TAG, "[%s] Loaded handler conf file [%s]", __FUNCTION__, file_name);
+  } else {
+    TSError("[%s] Failed to open handler config file [%s]", __FUNCTION__, file_name);
+  }
+}
+
+static int esiPluginInit(int argc, const char *argv[], struct OptionInfo *pOptionInfo) {
+  static TSStatSystem *statSystem = NULL;
+
+  if (statSystem == NULL) {
+    statSystem = new TSStatSystem();
+    Utils::init(&TSDebug, &TSError);
+    Stats::init(statSystem);
+  }
+
+  if (gHandlerManager == NULL) {
+    gHandlerManager = new HandlerManager(HANDLER_MGR_DEBUG_TAG, &TSDebug, &TSError);
+  }
+
+  memset(pOptionInfo, 0, sizeof(struct OptionInfo));
+  if (argc > 1) {
+    int c;
+    static const struct option longopts[] = {
+      { const_cast<char *>("packed-node-support"), no_argument, NULL, 'n' },
+      { const_cast<char *>("private-response"), no_argument, NULL, 'p' },
+      { const_cast<char *>("disable-gzip-output"), no_argument, NULL, 'z' },
+      { const_cast<char *>("first-byte-flush"), no_argument, NULL, 'b' },
+      { const_cast<char *>("handler-filename"), required_argument, NULL, 'f' },
+      { NULL, 0, NULL, 0 }
+    };
+
+    optarg = NULL;
+    optind = opterr = optopt = 0;
+    int longindex = 0;
+    while ((c = getopt_long(argc, (char * const*) argv, "npzbf:", longopts, &longindex)) != -1) {
+      switch (c) {
+        case 'n':
+          pOptionInfo->packed_node_support = true;
+          break;
+        case 'p':
+          pOptionInfo->private_response = true;
+          break;
+        case 'z':
+          pOptionInfo->disable_gzip_output = true;
+          break;
+        case 'b':
+          pOptionInfo->first_byte_flush = true;
+          break;
+        case 'f':
+          {
+            Utils::KeyValueMap handler_conf;
+            loadHandlerConf(optarg, handler_conf);
+            gHandlerManager->loadObjects(handler_conf);
+            break;
+          }
+        default:
+          break;
+      }
+    }
+  }
+
+  int result = 0;
+  bool bKeySet;
+  if (threadKey == 0) {
+    bKeySet = true;
+    if ((result=pthread_key_create(&threadKey, NULL)) != 0) {
+      TSError("[%s] Could not create key", __FUNCTION__);
+      TSDebug(DEBUG_TAG, "[%s] Could not create key", __FUNCTION__);
+    }
+  }
+  else {
+    bKeySet = false;
+  }
+
+  if (result == 0) {
+    TSDebug(DEBUG_TAG, "[%s] Plugin started%s, " \
+        "packed-node-support: %d, private-response: %d, " \
+        "disable-gzip-output: %d, first-byte-flush: %d ", __FUNCTION__, bKeySet ? " and key is set" : "",
+        pOptionInfo->packed_node_support, pOptionInfo->private_response,
+        pOptionInfo->disable_gzip_output, pOptionInfo->first_byte_flush);
+  }
+
+  return result;
+}
+
+void
+TSPluginInit(int argc, const char *argv[]) {
+  struct OptionInfo *pOptionInfo = (struct OptionInfo *)TSmalloc(sizeof(struct OptionInfo));
+  if (pOptionInfo == NULL) {
+    TSError("[%s] malloc %d bytes fail", __FUNCTION__, (int)sizeof(struct OptionInfo));
+    return;
+  }
+  if (esiPluginInit(argc, argv, pOptionInfo) != 0) {
+    return;
+  }
+
+  TSCont global_contp = TSContCreate(globalHookHandler, NULL);
+  if (!global_contp) {
+    TSError("[%s] Could not create global continuation", __FUNCTION__);
+    return;
+  }
+  TSContDataSet(global_contp, pOptionInfo);
+
+  TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, global_contp);
+  TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, global_contp);
+  TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, global_contp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Initialize the plugin as a remap plugin.
+//
+TSReturnCode
+TSRemapInit(TSRemapInterface* api_info, char *errbuf, int errbuf_size)
+{
+  if (!api_info) {
+    snprintf(errbuf, errbuf_size, "[TSRemapInit] - Invalid TSRemapInterface argument");
+    TSError("[TSRemapInit] - Invalid TSRemapInterface argument");
+    return TS_ERROR;
+  }
+
+  if (api_info->size < sizeof(TSRemapInterface)) {
+    snprintf(errbuf, errbuf_size, "[TSRemapInit] - Incorrect size of TSRemapInterface structure");
+    TSError("[TSRemapInit] - Incorrect size of TSRemapInterface structure");
+    return TS_ERROR;
+  }
+
+  TSDebug(DEBUG_TAG, "esi remap plugin is successfully initialized");
+  return TS_SUCCESS;
+}
+
+TSReturnCode
+TSRemapNewInstance(int argc, char* argv[], void** ih, char* errbuf, int errbuf_size)
+{
+  if (argc < 2) {
+    snprintf(errbuf, errbuf_size, "Unable to create remap instance, " \
+        "argc: %d < 2", argc);
+    TSError("Unable to create remap instance! argc: %d < 2", argc);
+    return TS_ERROR;
+  }
+
+  int index = 0;
+  const char *new_argv[argc];
+
+  new_argv[index++] = "esi.so";
+  for (int i=2; i<argc; i++) {
+    new_argv[index++] = argv[i];
+  }
+  new_argv[index] = NULL;
+
+  struct OptionInfo *pOptionInfo = (struct OptionInfo *)TSmalloc(sizeof(struct OptionInfo));
+  if (pOptionInfo == NULL) {
+    snprintf(errbuf, errbuf_size, "malloc %d bytes fail", (int)sizeof(struct OptionInfo));
+    TSError("[%s] malloc %d bytes fail", __FUNCTION__, (int)sizeof(struct OptionInfo));
+    return TS_ERROR;
+  }
+  if (esiPluginInit(index, new_argv, pOptionInfo) != 0) {
+    snprintf(errbuf, errbuf_size, "esiPluginInit fail!");
+    return TS_ERROR;
+  }
+  TSCont contp = TSContCreate(globalHookHandler, NULL);
+  TSContDataSet(contp, pOptionInfo);
+  *ih = static_cast<void*>(contp);
+
+  return TS_SUCCESS;
+}
+
+void
+TSRemapDeleteInstance(void* ih)
+{
+  TSCont contp = static_cast<TSCont>(ih);
+  if (contp != NULL) {
+    TSContDestroy(contp);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Main entry point when used as a remap plugin.
+//
+TSRemapStatus
+TSRemapDoRemap(void* ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
+{
+  if (NULL != ih) {
+    TSCont contp = static_cast<TSCont>(ih);
+    TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
+    TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp);
+
+    if (isInterceptRequest(txnp)) {
+      if (!setupServerIntercept(txnp)) {
+        TSError("[%s] Could not setup server intercept", __FUNCTION__);
+      } else {
+        TSDebug(DEBUG_TAG, "[%s] Setup server intercept", __FUNCTION__);
+      }
+    } else {
+      TSDebug(DEBUG_TAG, "[%s] Not setting up intercept", __FUNCTION__);
+    }
+  }
+
+  return TSREMAP_NO_REMAP; // This plugin never rewrites anything.
+}
+