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