diff --git a/plugins/experimental/esi/README.combo b/plugins/experimental/esi/README.combo
index ea64bbd37f5..db0abd0ad2f 100644
--- a/plugins/experimental/esi/README.combo
+++ b/plugins/experimental/esi/README.combo
@@ -10,23 +10,23 @@ The arguments in the plugin.config line in order represent
"admin/v1/combo")
2) The name of the key used for signature verification (disabled
- by default)
+ by default) [verification not implemented yet]
A "-" can be supplied as a value for any of these arguments to request
-default value be applied.
+default value be applied.
Also, just like the original combohandler, this plugin generates URLs
of the form 'http://localhost/
/'. here defaults
-to 'l' unless specified by the file path in the query parameter using
+to "Host" header 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/combo.com/filepath1
http://localhost/dir1/filepath2
-http://localhost/l/filepath3
+http://localhost/combo.com/filepath3
Remap rules have to be specified to map the above URLs to desired
content servers.
@@ -49,6 +49,25 @@ results in these file paths being "reconstructed":
/dir:path2/file5
/dir:path2/file6
+Config sample
+------
+ [plugin.config]
+ combo_handler.so admin/v1/combo
+
+ [remap.config]
+ map http://combo.com http://os.combo.com @plugin=combo_handler.so
+ map http://localhost/combo.com http://os.combo.com
+ map http://localhost/dir1 http://os1.combo.com
+
+ (note that if pristine_host_hdr is enabled in records.config, the Host header
+ of request to your combo origin server is 'localhost')
+
+Version 1.2.0
+- ported to ATS-3.3.3
+- per-map enabled (disabled for all map by default)
+- use full Host header as default bucket
+- limit sub-file's max count(30) and querystring's max length(3000)
+
Version 1.1.2
- Use the Bucket visited(instead of 'l' as the default) as the nickname if nickname is not passed.
diff --git a/plugins/experimental/esi/combo_handler.cc b/plugins/experimental/esi/combo_handler.cc
index 3891d2f5936..2d14bb2fac7 100644
--- a/plugins/experimental/esi/combo_handler.cc
+++ b/plugins/experimental/esi/combo_handler.cc
@@ -28,6 +28,7 @@
#include "ts/ts.h"
#include "ts/experimental.h"
+#include "ts/remap.h"
#include "ink_defs.h"
#include "HttpDataFetcherImpl.h"
@@ -39,6 +40,10 @@ using namespace EsiLib;
#define DEBUG_TAG "combo_handler"
+#define MAX_FILE_COUNT 30
+#define MAX_QUERY_LENGTH 3000
+
+int arg_idx;
static string SIG_KEY_NAME;
#define DEFAULT_COMBO_HANDLER_PATH "admin/v1/combo"
@@ -61,7 +66,7 @@ struct ClientRequest {
const sockaddr *client_addr;
StringList file_urls;
bool gzip_accepted;
- string defaultBucket; //default Bucket is set to l
+ string defaultBucket; // default Bucket will be set to HOST header
ClientRequest()
: status(TS_HTTP_STATUS_OK), client_addr(NULL), gzip_accepted(false), defaultBucket("l") { };
};
@@ -217,19 +222,54 @@ TSPluginInit(int argc, const char *argv[])
LOG_ERROR("Could not create read request header continuation");
return;
}
- TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, rrh_contp);
+
+ TSHttpHookAdd(TS_HTTP_OS_DNS_HOOK, rrh_contp);
+
+ if (TSHttpArgIndexReserve(DEBUG_TAG, "will save plugin-enable flag here",
+ &arg_idx) != TS_SUCCESS) {
+ LOG_ERROR("failed to reserve private data slot");
+ return;
+ } else {
+ LOG_DEBUG("arg_idx: %d", arg_idx);
+ }
Utils::init(&TSDebug, &TSError);
LOG_DEBUG("Plugin started");
}
+/*
+ Handle TS_EVENT_HTTP_OS_DNS event after TS_EVENT_HTTP_POST_REMAP and
+ TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE in order to make this plugin
+ "per-remap configurable", that is, we enable combo for specific channels,
+ and disable for other channels.
+
+ In yahoo's original code, this function handle TS_EVENT_HTTP_READ_REQUEST_HDR.
+ Because TS_EVENT_HTTP_READ_REQUEST_HDR is before TSRemapDoRemap, we can not
+ read "plugin_enable" flag in the READ_REQUEST_HDR event.
+*/
static int
handleReadRequestHeader(TSCont /* contp ATS_UNUSED */, TSEvent event, void *edata)
{
- TSAssert(event == TS_EVENT_HTTP_READ_REQUEST_HDR);
-
- LOG_DEBUG("handling read request header event...");
TSHttpTxn txnp = static_cast(edata);
+
+ if (event != TS_EVENT_HTTP_OS_DNS) {
+ LOG_ERROR("unknown event for this plugin %d", event);
+ return 0;
+ }
+
+ int *plugin_enable;
+ plugin_enable = (int *)TSHttpTxnArgGet(txnp, arg_idx);
+ if (plugin_enable && *plugin_enable == 1) {
+ LOG_DEBUG("combo is enabled for this channel");
+ } else {
+ LOG_DEBUG("combo is disabled for this channel");
+ TSfree(plugin_enable);
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+ return 0;
+ }
+ TSfree(plugin_enable);
+
+ LOG_DEBUG("handling TS_EVENT_HTTP_OS_DNS event...");
TSEvent reenable_to_event = TS_EVENT_HTTP_CONTINUE;
TSMBuffer bufp;
TSMLoc hdr_loc;
@@ -321,6 +361,10 @@ getDefaultBucket(TSHttpTxn /* txnp ATS_UNUSED */, TSMBuffer bufp, TSMLoc hdr_obj
}
LOG_DEBUG("host: %.*s", host_len, host);
+ creq.defaultBucket = string(host, host_len);
+ defaultBucketFound = true;
+
+ /*
for(int i = 0 ; i < host_len; i++)
{
if (host[i] == '.')
@@ -330,6 +374,7 @@ getDefaultBucket(TSHttpTxn /* txnp ATS_UNUSED */, TSMBuffer bufp, TSMLoc hdr_obj
break;
}
}
+ */
TSHandleMLocRelease (bufp, hdr_obj, field_loc);
@@ -345,12 +390,19 @@ getClientRequest(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc url_loc,
if (!query) {
LOG_ERROR("Could not get query from request URL");
+ creq.status = TS_HTTP_STATUS_BAD_REQUEST;
+ return;
} else {
if (!getDefaultBucket(txnp, bufp, hdr_loc, creq))
{
LOG_ERROR("failed getting Default Bucket for the request");
return;
}
+ if (query_len > MAX_QUERY_LENGTH) {
+ creq.status = TS_HTTP_STATUS_BAD_REQUEST;
+ LOG_ERROR("querystring too long");
+ return;
+ }
parseQueryParameters(query, query_len, creq);
creq.client_addr = TSHttpTxnClientAddrGet(txnp);
checkGzipAcceptance(bufp, hdr_loc, creq);
@@ -372,7 +424,7 @@ parseQueryParameters(const char *query, int query_len, ClientRequest &creq)
int common_prefix_path_size = 0;
for (int i = 0; i <= query_len; ++i) {
- if ((i == query_len) || (query[i] == '&') || (query[i] == '?')) {
+ if ((i == query_len) || (query[i] == '&')) {
int param_len = i - param_start_pos;
if (param_len) {
const char *param = query + param_start_pos;
@@ -463,6 +515,13 @@ if (!creq.file_urls.size()) {
creq.status = TS_HTTP_STATUS_FORBIDDEN;
creq.file_urls.clear();
}
+
+if (creq.file_urls.size() > MAX_FILE_COUNT) {
+ creq.status = TS_HTTP_STATUS_BAD_REQUEST;
+ LOG_ERROR("too many files in url");
+ creq.file_urls.clear();
+}
+
}
static void
@@ -844,3 +903,59 @@ writeErrorResponse(InterceptData &int_data, int &n_bytes_written)
n_bytes_written += response->size();
return true;
}
+
+TSRemapStatus
+TSRemapDoRemap(void* ih, TSHttpTxn rh, TSRemapRequestInfo* rri)
+{
+ int *plugin_enable;
+
+ plugin_enable = static_cast(TSmalloc(sizeof(int)));
+ *plugin_enable = 1;
+
+ TSHttpTxnArgSet(rh, arg_idx, plugin_enable); /* Save for later hooks */
+
+ return TSREMAP_NO_REMAP; /* Continue with next remap plugin in chain */
+}
+
+/*
+ Initialize the plugin as a remap plugin.
+*/
+TSReturnCode
+TSRemapInit(TSRemapInterface* api_info, char *errbuf, int errbuf_size)
+{
+ if (!api_info) {
+ strncpy(errbuf, "[TSRemapInit] - Invalid TSRemapInterface argument",
+ errbuf_size - 1);
+ return TS_ERROR;
+ }
+
+ if (api_info->size < sizeof(TSRemapInterface)) {
+ strncpy(errbuf,
+ "[TSRemapInit] - Incorrect size of TSRemapInterface structure",
+ errbuf_size - 1);
+ return TS_ERROR;
+ }
+
+ TSDebug(DEBUG_TAG, "%s plugin's remap part is initialized", DEBUG_TAG);
+
+ return TS_SUCCESS;
+}
+
+TSReturnCode
+TSRemapNewInstance(int argc, char* argv[], void** ih, char* errbuf,
+ int errbuf_size)
+{
+ *ih = NULL;
+
+ TSDebug(DEBUG_TAG, "%s Remap Instance for '%s' created",
+ DEBUG_TAG, argv[0]);
+
+ return TS_SUCCESS;
+}
+
+
+void
+TSRemapDeleteInstance(void* ih)
+{
+ return;
+}