diff --git a/src/HttpRequest.cc b/src/HttpRequest.cc index ed324263157..f4ede747224 100644 --- a/src/HttpRequest.cc +++ b/src/HttpRequest.cc @@ -28,6 +28,7 @@ #include "HttpRequest.h" #include "log/Config.h" #include "MemBuf.h" +#include "sbuf/List.h" #include "sbuf/StringConvert.h" #include "SquidConfig.h" #include "Store.h" @@ -754,16 +755,19 @@ HttpRequest::notes() } void -UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &helperNotes) -{ - // Tag client connection if the helper responded with clt_conn_tag=tag. - const char *cltTag = "clt_conn_tag"; - if (const char *connTag = helperNotes.findFirst(cltTag)) { - if (csd) { - csd->notes()->remove(cltTag); - csd->notes()->add(cltTag, connTag); +UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &helperNotes, const SBufList &connTags) +{ + // Tag client connection if the helper responded with clt_conn_tag=tag + // OR, if the admin configured the annotation to be copied to the client connection + if (csd) { + for (const auto ¬e : helperNotes) { + if (IsMember(connTags, note->name())) { + csd->notes()->remove(note->name()); + csd->notes()->add(note->name(), note->value()); + } } } + request.notes()->replaceOrAdd(&helperNotes); } diff --git a/src/HttpRequest.h b/src/HttpRequest.h index bd783782c50..8e60da78679 100644 --- a/src/HttpRequest.h +++ b/src/HttpRequest.h @@ -277,7 +277,7 @@ class ConnStateData; /** * Updates ConnStateData ids and HttpRequest notes from helpers received notes. */ -void UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const ¬es); +void UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const ¬es , const SBufList &clientConnectionTags); /// \returns listening/*_port address used by the client connection (or nil) /// nil parameter(s) indicate missing caller information and are handled safely diff --git a/src/Notes.h b/src/Notes.h index 37b5c476b8f..8f605308ec7 100644 --- a/src/Notes.h +++ b/src/Notes.h @@ -205,6 +205,7 @@ class NotePairs: public RefCountable }; typedef std::vector Entries; ///< The key/value pair entries typedef std::vector Names; + typedef Entries::const_iterator const_iterator; ///< iterates over the notes list NotePairs() {} NotePairs &operator=(NotePairs const &) = delete; @@ -265,6 +266,11 @@ class NotePairs: public RefCountable /// pair to multiple single-token pairs; returns existing entries otherwise. const Entries &expandListEntries(const CharacterSet *delimiters) const; + /// points to the first argument + const_iterator begin() const { return entries.cbegin(); } + /// points to the end of list + const_iterator end() const { return entries.cend(); } + private: Entries entries; ///< The key/value pair entries }; diff --git a/src/auth/UserRequest.cc b/src/auth/UserRequest.cc index c9a9b113a2b..bba8b5583a9 100644 --- a/src/auth/UserRequest.cc +++ b/src/auth/UserRequest.cc @@ -258,7 +258,8 @@ authTryGetUser(Auth::UserRequest::Pointer auth_user_request, ConnStateData * con // workaround by using anything already set in HttpRequest // OR use new and rely on a later Sync copying these to AccessLogEntry - UpdateRequestNotes(conn, *request, res->user()->notes); + auto schemeCfg = Auth::SchemeConfig::Find(res->scheme()->type()); // TODO: refactor to avoid this + UpdateRequestNotes(conn, *request, res->user()->notes, schemeCfg->authenticateChildren.clientConnectionTags); } return res; diff --git a/src/client_side_request.cc b/src/client_side_request.cc index 507f53d32ba..20a520ff72e 100644 --- a/src/client_side_request.cc +++ b/src/client_side_request.cc @@ -1019,7 +1019,7 @@ ClientRequestContext::clientRedirectDone(const Helper::Reply &reply) if (http->al) http->al->syncNotes(old_request); - UpdateRequestNotes(http->getConn(), *old_request, reply.notes); + UpdateRequestNotes(http->getConn(), *old_request, reply.notes, Config.redirectChildren.clientConnectionTags); switch (reply.result) { case Helper::TimedOut: @@ -1135,7 +1135,7 @@ ClientRequestContext::clientStoreIdDone(const Helper::Reply &reply) if (http->al) http->al->syncNotes(old_request); - UpdateRequestNotes(http->getConn(), *old_request, reply.notes); + UpdateRequestNotes(http->getConn(), *old_request, reply.notes, Config.storeIdChildren.clientConnectionTags); switch (reply.result) { case Helper::Unknown: diff --git a/src/external_acl.cc b/src/external_acl.cc index cfa1adff061..ab11c1cb5d6 100644 --- a/src/external_acl.cc +++ b/src/external_acl.cc @@ -583,7 +583,7 @@ copyResultsFromEntry(const HttpRequest::Pointer &req, const ExternalACLEntryPoin req->extacl_message = entry->message; // attach the helper kv-pair to the transaction - UpdateRequestNotes(req->clientConnectionManager.get(), *req, entry->notes); + UpdateRequestNotes(req->clientConnectionManager.get(), *req, entry->notes, entry->def->children.clientConnectionTags); } } diff --git a/src/helper/ChildConfig.cc b/src/helper/ChildConfig.cc index 8c40215cd8b..0b31d584272 100644 --- a/src/helper/ChildConfig.cc +++ b/src/helper/ChildConfig.cc @@ -7,13 +7,17 @@ */ #include "squid.h" +#include "base/CharacterSet.h" +#include "base/PackableStream.h" #include "cache_cf.h" #include "ConfigParser.h" #include "debug/Stream.h" #include "globals.h" #include "helper/ChildConfig.h" #include "Parsing.h" -#include "sbuf/SBuf.h" +#include "parser/Tokenizer.h" +#include "sbuf/List.h" +#include "Store.h" #include @@ -72,7 +76,7 @@ Helper::ChildConfig::needNew() const void Helper::ChildConfig::parseConfig() { - char const *token = ConfigParser::NextToken(); + char *token = ConfigParser::NextToken(); if (!token) { self_destruct(); @@ -89,22 +93,23 @@ Helper::ChildConfig::parseConfig() } /* Parse extension options */ - for (; (token = ConfigParser::NextToken()) ;) { - if (strncmp(token, "startup=", 8) == 0) { - n_startup = xatoui(token + 8); - } else if (strncmp(token, "idle=", 5) == 0) { - n_idle = xatoui(token + 5); + char *value; + while (ConfigParser::NextKvPair(token, value)) { + if (strncmp(token, "startup", 7) == 0) { + n_startup = xatoui(value); + } else if (strncmp(token, "idle", 4) == 0) { + n_idle = xatoui(value); if (n_idle < 1) { debugs(0, DBG_CRITICAL, "WARNING: OVERRIDE: Using idle=0 for helpers causes request failures. Overriding to use idle=1 instead."); n_idle = 1; } - } else if (strncmp(token, "concurrency=", 12) == 0) { - concurrency = xatoui(token + 12); - } else if (strncmp(token, "queue-size=", 11) == 0) { - queue_size = xatoui(token + 11); + } else if (strncmp(token, "concurrency", 11) == 0) { + concurrency = xatoui(value); + } else if (strncmp(token, "queue-size", 10) == 0) { + queue_size = xatoui(value); defaultQueueSize = false; - } else if (strncmp(token, "on-persistent-overload=", 23) == 0) { - const SBuf action(token + 23); + } else if (strncmp(token, "on-persistent-overload", 22) == 0) { + const SBuf action(value); if (action.cmp("ERR") == 0) onPersistentOverload = actErr; else if (action.cmp("die") == 0) @@ -114,8 +119,10 @@ Helper::ChildConfig::parseConfig() self_destruct(); return; } - } else if (strncmp(token, "reservation-timeout=", 20) == 0) - reservationTimeout = xatoui(token + 20); + } else if (strncmp(token, "reservation-timeout", 19) == 0) + reservationTimeout = xatoui(value); + else if (strncmp(token, "connection-notes", 16) == 0) + parseNotesList(SBuf(value)); else { debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Undefined option: " << token << "."); self_destruct(); @@ -139,3 +146,56 @@ Helper::ChildConfig::parseConfig() queue_size = 2 * n_max; } +/// parses comma-separated list of key names to be +/// treated like clt_conn_tag +void +Helper::ChildConfig::parseNotesList(const SBuf &buf) +{ + ::Parser::Tokenizer tok(buf); + + static const CharacterSet delims("comma", ","); + SBuf item; + while (tok.token(item, delims)) { + static const SBuf wsp(" "); + item.trim(wsp); + if (!item.isEmpty()) + clientConnectionTags.emplace_back(item); + } +} + +void +Helper::ChildConfig::printConfig(StoreEntry *e, const char *directive) +{ + PackableStream os(*e); + os << "\n" << directive << " " << n_max; + + if (n_startup != 0) + os << " startup=" << n_startup; + + if (n_idle != 0) + os << " idle=" << n_idle; + + if (concurrency != 0) + os << " concurrency=" << concurrency; + + if (!defaultQueueSize) + os << " queue-size=" << queue_size; + + switch (onPersistentOverload) { + case actErr: + os << " on-persistent-overload=ERR"; + break; + case actDie: // defaults not printed + break; + } + + if (reservationTimeout != 64) + os << " reservation-timeout=" << reservationTimeout; + + static const SBuf comma(","); + auto cnotes = JoinContainerToSBuf(clientConnectionTags.begin(), clientConnectionTags.end(), comma); + if (cnotes.cmp("clt_conn_tags") != 0) + os << " connection-notes=\"" << cnotes << '"'; + + os << "\n"; +} diff --git a/src/helper/ChildConfig.h b/src/helper/ChildConfig.h index b73959db541..2a2169a63d2 100644 --- a/src/helper/ChildConfig.h +++ b/src/helper/ChildConfig.h @@ -9,6 +9,8 @@ #ifndef SQUID_SRC_HELPER_CHILDCONFIG_H #define SQUID_SRC_HELPER_CHILDCONFIG_H +#include "sbuf/SBuf.h" + namespace Helper { @@ -34,6 +36,9 @@ class ChildConfig int needNew() const; void parseConfig(); + /// produce squid.conf syntax for mgr:config report + void printConfig(StoreEntry *, const char *directive); + /** * Update an existing set of details with new start/max/idle/concurrent limits. * This is for parsing new child settings into an object incrementally then updating @@ -107,13 +112,20 @@ class ChildConfig /// older stateful helper server reservations may be forgotten time_t reservationTimeout = 64; // reservation-timeout + + /// List of kv-pair keys to set as annotations of the client TCP connection. + /// Default: clt_conn_tag + SBufList clientConnectionTags = { SBuf("clt_conn_tag") }; + +private: + void parseNotesList(const SBuf &); }; } // namespace Helper /* Legacy parser interface */ #define parse_HelperChildConfig(c) (c)->parseConfig() -#define dump_HelperChildConfig(e,n,c) storeAppendPrintf((e), "\n%s %d startup=%d idle=%d concurrency=%d\n", (n), (c).n_max, (c).n_startup, (c).n_idle, (c).concurrency) +#define dump_HelperChildConfig(e,n,c) (c).printConfig((e),(n)) #define free_HelperChildConfig(dummy) // NO. #endif /* SQUID_SRC_HELPER_CHILDCONFIG_H */