This is an automated email from the ASF dual-hosted git repository. gancho pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push: new f7e3cf7 cachekey: running as global plugin f7e3cf7 is described below commit f7e3cf7a92b7deb1bfafd6bf16a3c0a9dac3d37c Author: Gancho Tenev <gan...@apache.org> AuthorDate: Thu Jul 5 08:21:35 2018 -0700 cachekey: running as global plugin The plugin can now run as a global plugin (a single global instance configured using `plugin.config`) or as per-remap plugin (a separate instance configured per remap rule in `remap.config`). If both global and per-remap instance are used the per-remap configuration would take precedence (per-remap configuration would be applied and the global configuration ignored). --- doc/admin-guide/plugins/cachekey.en.rst | 40 +++++++++ plugins/cachekey/cachekey.cc | 131 ++++++++++++++++++++++----- plugins/cachekey/cachekey.h | 20 +++-- plugins/cachekey/configs.cc | 13 ++- plugins/cachekey/configs.h | 3 +- plugins/cachekey/plugin.cc | 153 +++++++++++++++++++------------- 6 files changed, 264 insertions(+), 96 deletions(-) diff --git a/doc/admin-guide/plugins/cachekey.en.rst b/doc/admin-guide/plugins/cachekey.en.rst index bc126ca..958cdbe 100644 --- a/doc/admin-guide/plugins/cachekey.en.rst +++ b/doc/admin-guide/plugins/cachekey.en.rst @@ -177,6 +177,46 @@ Cache key elements separator * ``--separator=<string>`` - the cache key is constructed by extracting elements from HTTP URI and headers or by using the UA classifiers and they are appended during the key construction and separated by ``/`` (by default). This options allows to override the default separator to any string (including an empty string). +How to run the plugin +===================== + +The plugin can run as a global plugin (a single global instance configured using `plugin.config`) or as per-remap plugin (a separate instance configured per remap rule in `remap.config`). + +Global instance +^^^^^^^^^^^^^^^ + +:: + + $ cat plugin.config + cachekey.so \ + --include-params=a,b,c \ + --sort-params=true + + +Per-remap instance +^^^^^^^^^^^^^^^^^^ + +:: + + $cat remap.config + map http://www.example.com http://www.origin.com \ + @plugin=cachekey.so \ + @pparam=--include-params=a,b,c \ + @pparam=--sort-params=true + + +If both global and per-remap instance are used the per-remap configuration would take precedence (per-remap configuration would be applied and the global configuration ignored). + +Because of the ATS core (remap) and the CacheKey plugin implementation there is a slight difference between the global and the per-remap functionality when ``--uri-type=remap`` is used. + +* The global instance always uses the URI **after** remap (at ``TS_HTTP_POST_REMAP_HOOK``). + +* The per-remap instance uses the URI **during** remap (after ``TS_HTTP_PRE_REMAP_HOOK`` and before ``TS_HTTP_POST_REMAP_HOOK``) which leads to a different URI to be used depending on plugin order in the remap rule. + + * If CacheKey plugin is the first plugin in the remap rule the URI used will be practically the same as the pristine URI. + * If the CacheKey plugin is the last plugin in the remap rule (which is right before ``TS_HTTP_POST_REMAP_HOOK``) the behavior will be simillar to the global instnance. + + Detailed examples and troubleshooting ===================================== diff --git a/plugins/cachekey/cachekey.cc b/plugins/cachekey/cachekey.cc index 2a0b140..a31d628 100644 --- a/plugins/cachekey/cachekey.cc +++ b/plugins/cachekey/cachekey.cc @@ -172,17 +172,98 @@ classifyUserAgent(const Classifier &c, TSMBuffer buf, TSMLoc hdrs, String &class return matched; } +static String +getUri(TSMBuffer buf, TSMLoc url) +{ + String uri; + int uriLen; + const char *uriPtr = TSUrlStringGet(buf, url, &uriLen); + if (nullptr != uriPtr && 0 != uriLen) { + uri.assign(uriPtr, uriLen); + TSfree((void *)uriPtr); + } else { + CacheKeyError("failed to get URI"); + } + return uri; +} + /** * @brief Constructor setting up the cache key prefix, initializing request info. * @param txn transaction handle. - * @param buf marshal buffer - * @param url URI handle - * @param hdrs headers handle + * @param separator cache key elements separator + * @param uriType type of the URI used to create the cachekey ("remap" or "pristine") + * @param rri remap request info */ -CacheKey::CacheKey(TSHttpTxn txn, TSMBuffer buf, TSMLoc url, TSMLoc hdrs, String separator) - : _txn(txn), _buf(buf), _url(url), _hdrs(hdrs), _separator(separator) +CacheKey::CacheKey(TSHttpTxn txn, String separator, CacheKeyUriType uriType, TSRemapRequestInfo *rri) + : _txn(txn), _separator(separator), _uriType(uriType) { _key.reserve(512); + + _remap = (nullptr != rri); + + /* Get the URI and header to base the cachekey on. + * @TODO it might make sense to add more supported URI types */ + + if (_remap) { + CacheKeyDebug("setting cache key from a remap plugin"); + if (PRISTINE == _uriType) { + if (TS_SUCCESS != TSHttpTxnPristineUrlGet(_txn, &_buf, &_url)) { + /* Failing here is unlikely. No action seems the only reasonable thing to do from within this plug-in */ + CacheKeyError("failed to get pristine URI handle"); + return; + } + CacheKeyDebug("using pristine uri '%s'", getUri(_buf, _url).c_str()); + } else { + _buf = rri->requestBufp; + _url = rri->requestUrl; + CacheKeyDebug("using remap uri '%s'", getUri(_buf, _url).c_str()); + } + _hdrs = rri->requestHdrp; + } else { + CacheKeyDebug("setting cache key from a global plugin"); + if (TS_SUCCESS != TSHttpTxnClientReqGet(_txn, &_buf, &_hdrs)) { + /* Failing here is unlikely. No action seems the only reasonable thing to do from within this plug-in */ + CacheKeyError("failed to get client request handle"); + return; + } + + if (PRISTINE == _uriType) { + if (TS_SUCCESS != TSHttpTxnPristineUrlGet(_txn, &_buf, &_url)) { + TSHandleMLocRelease(_buf, TS_NULL_MLOC, _hdrs); + CacheKeyError("failed to get pristine URI handle"); + return; + } + CacheKeyDebug("using pristine uri '%s'", getUri(_buf, _url).c_str()); + } else { + if (TS_SUCCESS != TSHttpHdrUrlGet(_buf, _hdrs, &_url)) { + TSHandleMLocRelease(_buf, TS_NULL_MLOC, _hdrs); + CacheKeyError("failed to get URI handle"); + return; + } + CacheKeyDebug("using post-remap uri '%s','", getUri(_buf, _url).c_str()); + } + } + _valid = true; /* success, we got all necessary elements - URI, headers, etc. */ +} + +CacheKey::~CacheKey() +{ + if (_valid) { + /* free resources only if valid, if not valid it is assumed nothing was allocated or was freed */ + if (_remap) { + /* _buf and _hdrs are assigned from remap info - no need to release here. */ + if (PRISTINE == _uriType) { + if (TS_SUCCESS != TSHandleMLocRelease(_buf, TS_NULL_MLOC, _url)) { + CacheKeyError("failed to release pristine URI handle"); + } + } + } else { + if (TS_SUCCESS != TSHandleMLocRelease(_buf, TS_NULL_MLOC, _hdrs) && + TS_SUCCESS != TSHandleMLocRelease(_buf, TS_NULL_MLOC, _url)) { + CacheKeyError("failed to release URI and headers handle"); + } + } + } } /** @@ -230,23 +311,9 @@ CacheKey::append(const char *s, unsigned n) ::appendEncoded(_key, s, n); } -static String -getUri(TSMBuffer buf, TSMLoc url) -{ - String uri; - int uriLen; - const char *uriPtr = TSUrlStringGet(buf, url, &uriLen); - if (nullptr != uriPtr && 0 != uriLen) { - uri.assign(uriPtr, uriLen); - TSfree((void *)uriPtr); - } else { - CacheKeyError("failed to get URI"); - } - return uri; -} - /** - * @brief Append to the cache key a custom prefix, capture from hots:port, capture from URI or default to host:port part of the URI. + * @brief Append to the cache key a custom prefix, capture from hots:port, capture from URI or default to host:port part of the + * URI. * @note This is the only cache key component from the key which is always available. * @param prefix if not empty string will append the static prefix to the cache key. * @param prefixCapture if not empty will append regex capture/replacement from the host:port. @@ -592,6 +659,24 @@ CacheKey::appendUaClass(Classifier &classifier) bool CacheKey::finalize() const { - CacheKeyDebug("finalizing cache key '%s'", _key.c_str()); - return TSCacheUrlSet(_txn, &(_key[0]), _key.size()) == TS_SUCCESS; + bool res = true; + CacheKeyDebug("finalizing cache key '%s' from a %s plugin", _key.c_str(), (_remap ? "remap" : "global")); + if (TS_SUCCESS != TSCacheUrlSet(_txn, &(_key[0]), _key.size())) { + int len; + char *url = TSHttpTxnEffectiveUrlStringGet(_txn, &len); + if (nullptr != url) { + if (_remap) { + /* Remap instance. Always runs first by design (before TS_HTTP_POST_REMAP_HOOK) */ + CacheKeyError("failed to set cache key for url %.*s", len, url); + } else { + /* Global instance. We would fail and get here if a per-remap instance has already set the cache key + * (currently TSCacheUrlSet() can be called only once successfully). Don't error, just debug. + * @todo avoid the consecutive attempts and error only on unexpected failures. */ + CacheKeyDebug("failed to set cache key for url %.*s", len, url); + } + TSfree(url); + } + res = false; + } + return res; } diff --git a/plugins/cachekey/cachekey.h b/plugins/cachekey/cachekey.h index 3a24631..6116bba 100644 --- a/plugins/cachekey/cachekey.h +++ b/plugins/cachekey/cachekey.h @@ -23,6 +23,8 @@ #pragma once +#include "ts/ts.h" +#include "ts/remap.h" #include "common.h" #include "configs.h" @@ -48,7 +50,8 @@ class CacheKey { public: - CacheKey(TSHttpTxn txn, TSMBuffer buf, TSMLoc url, TSMLoc hdrs, String separator); + CacheKey(TSHttpTxn txn, String separator, CacheKeyUriType urlType, TSRemapRequestInfo *rri = nullptr); + ~CacheKey(); void append(unsigned number); void append(const String &); @@ -71,11 +74,14 @@ private: CacheKey(); // disallow /* Information from the request */ - TSHttpTxn _txn; /**< @brief transaction handle */ - TSMBuffer _buf; /**< @brief marshal buffer */ - TSMLoc _url; /**< @brief URI handle */ - TSMLoc _hdrs; /**< @brief headers handle */ + TSHttpTxn _txn; /**< @brief transaction handle */ + TSMBuffer _buf; /**< @brief marshal buffer */ + TSMLoc _url; /**< @brief URI handle */ + TSMLoc _hdrs; /**< @brief headers handle */ + bool _valid = false; /**< @brief shows if the constructor discovered the input correctly */ + bool _remap = false; /**< @brief shows if the input URI was from remap info */ - String _key; /**< @brief cache key */ - String _separator; /**< @brief a separator used to separate the cache key elements extracted from the URI */ + String _key; /**< @brief cache key */ + String _separator; /**< @brief a separator used to separate the cache key elements extracted from the URI */ + CacheKeyUriType _uriType; /**< @brief the URI type used as a cachekey base: pristine, remap, etc. */ }; diff --git a/plugins/cachekey/configs.cc b/plugins/cachekey/configs.cc index dff01b5..16cf27a 100644 --- a/plugins/cachekey/configs.cc +++ b/plugins/cachekey/configs.cc @@ -321,9 +321,11 @@ Configs::loadClassifiers(const String &args, bool blacklist) * @brief initializes plugin configuration. * @param argc number of plugin parameters * @param argv plugin parameters + * @param perRemapConfig boolean showing if this is per-remap config (vs global config). + * */ bool -Configs::init(int argc, char *argv[]) +Configs::init(int argc, const char *argv[], bool perRemapConfig) { static const struct option longopt[] = { {const_cast<char *>("exclude-params"), optional_argument, nullptr, 'a'}, @@ -351,9 +353,12 @@ Configs::init(int argc, char *argv[]) bool status = true; - /* argv contains the "to" and "from" URLs. Skip the first so that the second one poses as the program name. */ - argc--; - argv++; + /* For remap.config: argv contains the "to" and "from" URLs. Skip the first so that the second one poses as the program name. + * For plugin.config: argv contains the plugin shared object name. Don't skip any */ + if (perRemapConfig) { + argc--; + argv++; + } for (;;) { int opt; diff --git a/plugins/cachekey/configs.h b/plugins/cachekey/configs.h index db54da3..b4890d5 100644 --- a/plugins/cachekey/configs.h +++ b/plugins/cachekey/configs.h @@ -131,8 +131,9 @@ public: * @brief initializes plugin configuration. * @param argc number of plugin parameters * @param argv plugin parameters + * @param perRemapConfig boolean showing if this is per-remap config (vs global config). */ - bool init(int argc, char *argv[]); + bool init(int argc, const char *argv[], bool perRemapConfig); /** * @brief provides means for post-processing of the plugin parameters to finalize the configuration or to "cache" some of the diff --git a/plugins/cachekey/plugin.cc b/plugins/cachekey/plugin.cc index c86e65b..cf3d539 100644 --- a/plugins/cachekey/plugin.cc +++ b/plugins/cachekey/plugin.cc @@ -26,6 +26,93 @@ #include "cachekey.h" #include "common.h" +/* Configuration only used by the global plugin instance. */ +Configs *globalConfig = nullptr; + +/** + * @brief Set the cache key called by both global and remap instances. + * + * @param txn transaction handle + * @param config cachekey configuration + */ +static void +setCacheKey(TSHttpTxn txn, Configs *config, TSRemapRequestInfo *rri = nullptr) +{ + /* Initial cache key facility from the requested URL. */ + CacheKey cachekey(txn, config->getSeparator(), config->getUriType(), rri); + + /* Append custom prefix or the host:port */ + if (!config->prefixToBeRemoved()) { + cachekey.appendPrefix(config->_prefix, config->_prefixCapture, config->_prefixCaptureUri); + } + /* Classify User-Agent and append the class name to the cache key if matched. */ + cachekey.appendUaClass(config->_classifier); + + /* Capture from User-Agent header. */ + cachekey.appendUaCaptures(config->_uaCapture); + + /* Append headers to the cache key. */ + cachekey.appendHeaders(config->_headers); + + /* Append cookies to the cache key. */ + cachekey.appendCookies(config->_cookies); + + /* Append the path to the cache key. */ + if (!config->pathToBeRemoved()) { + cachekey.appendPath(config->_pathCapture, config->_pathCaptureUri); + } + /* Append query parameters to the cache key. */ + cachekey.appendQuery(config->_query); + + /* Set the cache key */ + cachekey.finalize(); +} + +static int +contSetCachekey(TSCont contp, TSEvent event, void *edata) +{ + TSHttpTxn txn = static_cast<TSHttpTxn>(edata); + + setCacheKey(txn, globalConfig); + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + return 0; +} + +/** + * @brief Global plugin instance initialization. + * + * Processes the configuration and initializes a global plugin instance. + * @param argc plugin arguments number + * @param argv plugin arguments + */ +void +TSPluginInit(int argc, const char *argv[]) +{ + TSPluginRegistrationInfo info; + + info.plugin_name = PLUGIN_NAME; + info.vendor_name = "Apache Software Foundation"; + info.support_email = "d...@trafficserver.apache.org"; + + if (TSPluginRegister(&info) != TS_SUCCESS) { + CacheKeyError("global plugin registration failed"); + } + + globalConfig = new Configs(); + if (nullptr != globalConfig && globalConfig->init(argc, argv, /* perRemapConfig */ false)) { + TSCont cont = TSContCreate(contSetCachekey, nullptr); + TSHttpHookAdd(TS_HTTP_POST_REMAP_HOOK, cont); + + CacheKeyDebug("global plugin initialized"); + } else { + globalConfig = nullptr; + delete globalConfig; + + CacheKeyError("failed to initialize global plugin"); + } +} + /** * @brief Plugin initialization. * @param apiInfo remap interface info pointer @@ -54,14 +141,16 @@ TSReturnCode TSRemapNewInstance(int argc, char *argv[], void **instance, char *errBuf, int errBufSize) { Configs *config = new Configs(); - if (nullptr != config && config->init(argc, argv)) { + if (nullptr != config && config->init(argc, const_cast<const char **>(argv), /* perRemapConfig */ true)) { *instance = config; } else { - CacheKeyError("failed to initialize the " PLUGIN_NAME " plugin"); + CacheKeyError("failed to initialize the remap plugin"); *instance = nullptr; delete config; return TS_ERROR; } + + CacheKeyDebug("remap plugin initialized"); return TS_SUCCESS; } @@ -91,65 +180,7 @@ TSRemapDoRemap(void *instance, TSHttpTxn txn, TSRemapRequestInfo *rri) Configs *config = (Configs *)instance; if (nullptr != config) { - TSMBuffer bufp; - TSMLoc urlLoc; - TSMLoc hdrLoc; - - /* Get the URI and header to base the cachekey on. - * @TODO it might make sense to add more supported URI types */ - if (PRISTINE == config->getUriType()) { - if (TS_SUCCESS != TSHttpTxnPristineUrlGet(txn, &bufp, &urlLoc)) { - /* Failing here is unlikely. No action seems the only reasonable thing to do from within this plug-in */ - CacheKeyError("failed to get pristine URI handle"); - return TSREMAP_NO_REMAP; - } - } else { - bufp = rri->requestBufp; - urlLoc = rri->requestUrl; - } - hdrLoc = rri->requestHdrp; - - /* Initial cache key facility from the requested URL. */ - CacheKey cachekey(txn, bufp, urlLoc, hdrLoc, config->getSeparator()); - - /* Append custom prefix or the host:port */ - if (!config->prefixToBeRemoved()) { - cachekey.appendPrefix(config->_prefix, config->_prefixCapture, config->_prefixCaptureUri); - } - /* Classify User-Agent and append the class name to the cache key if matched. */ - cachekey.appendUaClass(config->_classifier); - - /* Capture from User-Agent header. */ - cachekey.appendUaCaptures(config->_uaCapture); - - /* Append headers to the cache key. */ - cachekey.appendHeaders(config->_headers); - - /* Append cookies to the cache key. */ - cachekey.appendCookies(config->_cookies); - - /* Append the path to the cache key. */ - if (!config->pathToBeRemoved()) { - cachekey.appendPath(config->_pathCapture, config->_pathCaptureUri); - } - /* Append query parameters to the cache key. */ - cachekey.appendQuery(config->_query); - - /* Set the cache key */ - if (!cachekey.finalize()) { - char *url; - int len; - - url = TSHttpTxnEffectiveUrlStringGet(txn, &len); - CacheKeyError("failed to set cache key for url %.*s", len, url); - TSfree(url); - } - - if (PRISTINE == config->getUriType()) { - if (TS_SUCCESS != TSHandleMLocRelease(bufp, TS_NULL_MLOC, urlLoc)) { - CacheKeyError("failed to release pristine URI handle"); - } - } + setCacheKey(txn, config, rri); } return TSREMAP_NO_REMAP;