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.
+}
+