From cdc7088febe9837266e64fa73a0f4e0937245b6c Mon Sep 17 00:00:00 2001 From: Miles Libbey Date: Tue, 7 Jul 2026 20:53:14 -0700 Subject: [PATCH] prefetch: admit --fetch-query requests only when the key is present With --fetch-query configured, the front-end/first-pass gate set handleFetch=true whenever the query key was *configured*, not when the request carried it. So every request whose path matched no fetch-path-pattern was admitted anyway, ran the pattern replace, failed, and logged ERROR "failed to process the pattern" -- while scheduling no prefetch at all (BgFetch runs only on the success path). Admit only when the request's query actually contains the key, matched as a "=" parameter rather than a substring (which could hit another parameter's name or value). The same parameter test is now used by the hasValidQuery branch selector and the query-branch loop as well. --- plugins/prefetch/plugin.cc | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/plugins/prefetch/plugin.cc b/plugins/prefetch/plugin.cc index b935e7928db..a472bea0d50 100644 --- a/plugins/prefetch/plugin.cc +++ b/plugins/prefetch/plugin.cc @@ -348,6 +348,31 @@ getPristineUrlQuery(TSHttpTxn txnp) return pristineQuery; } +/** + * @brief Whether a single query parameter is the configured "=..." parameter. + */ +static bool +isQueryKeyParam(const String ¶m, const String &key) +{ + return param.size() > key.size() && param.compare(0, key.size(), key) == 0 && param[key.size()] == '='; +} + +/** + * @brief Whether the query string contains the configured "=..." parameter. + */ +static bool +hasQueryKeyParam(const String &query, const String &key) +{ + std::istringstream qs(query); + String param; + while (getline(qs, param, '&')) { + if (isQueryKeyParam(param, key)) { + return true; + } + } + return false; +} + static constexpr StringView CmcdHeader{"Cmcd-Request"}; static constexpr StringView CmcdNorFieldPrefix{"nor="}; static constexpr StringView CmcdNrrFieldPrefix{"nrr="}; @@ -590,9 +615,9 @@ contHandleFetch(const TSCont contp, TSEvent event, void *edata) const String currentQuery = getPristineUrlQuery(txnp); bool hasValidQuery = false; - // If there is a --fetch-query defined in the config, and that string is found in the querystring, assume it is - // valid, and prefer the --fetch-query over the --fetch-path-pattern(s). - if (!config.getQueryKeyName().empty() && currentQuery.find(config.getQueryKeyName()) != String::npos) { + // If there is a --fetch-query defined in the config, and that parameter is present in the querystring, assume it + // is valid, and prefer the --fetch-query over the --fetch-path-pattern(s). + if (!config.getQueryKeyName().empty() && hasQueryKeyParam(currentQuery, config.getQueryKeyName())) { PrefetchDebug("Setting hasValidQuery to true"); hasValidQuery = true; } @@ -663,7 +688,7 @@ contHandleFetch(const TSCont contp, TSEvent event, void *edata) String param; while (getline(cStringStream, param, '&')) { - if (param.find(config.getQueryKeyName()) != 0) { + if (!isQueryKeyParam(param, config.getQueryKeyName())) { continue; } if (config.getFetchCount() < done++) { @@ -851,8 +876,8 @@ TSRemapDoRemap(void *instance, TSHttpTxn txnp, TSRemapRequestInfo *rri) PrefetchDebug("failed to get path to (pre)match"); } - String queryKey = config.getQueryKeyName(); - if (!queryKey.empty()) { + const String &queryKey = config.getQueryKeyName(); + if (!handleFetch && !queryKey.empty() && hasQueryKeyParam(getPristineUrlQuery(txnp), queryKey)) { PrefetchDebug("handling for query-key: %s", queryKey.c_str()); handleFetch = true; }