#include "anyp/PortCfg.h"
#include "comm/Connection.h"
+#include "HttpHeader.h"
#include "HttpVersion.h"
#include "HttpRequestMethod.h"
#include "HierarchyLogEntry.h"
#if ICAP_CLIENT
#include "adaptation/icap/Elements.h"
#endif
+#include "Notes.h"
#include "RefCount.h"
#if USE_SSL
#include "ssl/gadgets.h"
HttpReply *reply;
HttpRequest *request; //< virgin HTTP request
HttpRequest *adapted_request; //< HTTP request after adaptation and redirection
+ /// key:value pairs set by note and adaptation_meta
+ NotePairs notes;
#if ICAP_CLIENT
/** \brief This subclass holds log info for ICAP part of request
#if USE_SSL
hoErrorDetail,
#endif
+ hoNote,
hoEnd
} http_hdr_owner_type;
*/
#include "squid.h"
+#include "globals.h"
#include "acl/FilledChecklist.h"
#include "acl/Gadgets.h"
#include "client_side_request.h"
#include "client_side.h"
#include "comm/Connection.h"
#include "compat/strtoll.h"
+#include "ConfigParser.h"
#include "fde.h"
#include "HttpHdrContRange.h"
#include "HttpHeader.h"
multicast.cc \
neighbors.h \
neighbors.cc \
+ Notes.cc \
+ Notes.h \
Packer.cc \
Packer.h \
Parsing.cc \
tests_testHttpReply_SOURCES=\
cbdata.cc \
cbdata.h \
+ ConfigParser.cc \
ETag.cc \
fatal.h \
tests/stub_fatal.cc \
mime_header.cc \
neighbors.h \
neighbors.cc \
+ Notes.cc \
+ Notes.h \
Packer.cc \
Parsing.cc \
pconn.cc \
multicast.cc \
neighbors.h \
neighbors.cc \
+ Notes.cc \
+ Notes.h \
Packer.cc \
Parsing.cc \
pconn.cc \
multicast.cc \
neighbors.h \
neighbors.cc \
+ Notes.cc \
+ Notes.h \
Packer.cc \
Parsing.cc \
pconn.cc \
multicast.cc \
neighbors.h \
neighbors.cc \
+ Notes.cc \
+ Notes.h \
Packer.cc \
Parsing.cc \
peer_digest.cc \
mime_header.cc \
neighbors.h \
neighbors.cc \
+ Notes.cc \
+ Notes.h \
Packer.cc \
Parsing.cc \
pconn.cc \
mime_header.cc \
neighbors.h \
neighbors.cc \
+ Notes.cc \
+ Notes.h \
Packer.cc \
Parsing.cc \
pconn.cc \
#include "HttpHeaderTools.h"
#include "icmp/IcmpConfig.h"
#include "ip/Address.h"
+#include "Notes.h"
#include "RefCount.h"
#include "YesNoNone.h"
HeaderManglers *reply_header_access;
///request_header_add access list
HeaderWithAclList *request_header_add;
+ ///note
+ Notes notes;
char *coredump_dir;
char *chroot_dir;
#if USE_CACHE_DIGESTS
int Adaptation::Config::send_client_ip = false;
int Adaptation::Config::send_username = false;
int Adaptation::Config::use_indirect_client = true;
-Adaptation::Config::MetaHeaders Adaptation::Config::metaHeaders;
-
-Adaptation::Config::MetaHeader::Value::~Value()
-{
- aclDestroyAclList(&aclList);
-}
-
-Adaptation::Config::MetaHeader::Value::Pointer
-Adaptation::Config::MetaHeader::addValue(const String &value)
-{
- Value::Pointer v = new Value(value);
- values.push_back(v);
- return v;
-}
-
-const char *
-Adaptation::Config::MetaHeader::match(HttpRequest *request, HttpReply *reply)
-{
-
- typedef Values::iterator VLI;
- ACLFilledChecklist ch(NULL, request, NULL);
- if (reply)
- ch.reply = HTTPMSGLOCK(reply);
-
- for (VLI i = values.begin(); i != values.end(); ++i ) {
- const int ret= ch.fastCheck((*i)->aclList);
- debugs(93, 5, HERE << "Check for header name: " << name << ": " << (*i)->value
- <<", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
- if (ret == ACCESS_ALLOWED)
- return (*i)->value.termedBuf();
- }
- return NULL;
-}
-
-Adaptation::Config::MetaHeader::Pointer
-Adaptation::Config::addMetaHeader(const String &headerName)
-{
- typedef MetaHeaders::iterator AMLI;
- for (AMLI i = metaHeaders.begin(); i != metaHeaders.end(); ++i) {
- if ((*i)->name == headerName)
- return (*i);
- }
-
- MetaHeader::Pointer meta = new MetaHeader(headerName);
- metaHeaders.push_back(meta);
- return meta;
-}
+const char *metasBlacklist[] = {
+ "Methods",
+ "Service",
+ "ISTag",
+ "Encapsulated",
+ "Opt-body-type",
+ "Max-Connections",
+ "Options-TTL",
+ "Date",
+ "Service-ID",
+ "Allow",
+ "Preview",
+ "Transfer-Preview",
+ "Transfer-Ignore",
+ "Transfer-Complete",
+ NULL
+};
+Notes Adaptation::Config::metaHeaders("ICAP header", metasBlacklist);
Adaptation::ServiceConfig*
Adaptation::Config::newServiceConfig() const
DetachServices();
serviceConfigs.clean();
-
- FreeMetaHeader();
}
void
FinalizeEach(AllRules(), "message adaptation access rules");
}
-void
-Adaptation::Config::ParseMetaHeader(ConfigParser &parser)
-{
- String name, value;
- const char *warnFor[] = {
- "Methods",
- "Service",
- "ISTag",
- "Encapsulated",
- "Opt-body-type",
- "Max-Connections",
- "Options-TTL",
- "Date",
- "Service-ID",
- "Allow",
- "Preview",
- "Transfer-Preview",
- "Transfer-Ignore",
- "Transfer-Complete",
- NULL
- };
- ConfigParser::ParseString(&name);
- ConfigParser::ParseQuotedString(&value);
-
- // TODO: Find a way to move this check to ICAP
- for (int i = 0; warnFor[i] != NULL; ++i) {
- if (name.caseCmp(warnFor[i]) == 0) {
- fatalf("%s:%d: meta name \"%s\" is a reserved ICAP header name",
- cfg_filename, config_lineno, name.termedBuf());
- }
- }
-
- MetaHeader::Pointer meta = addMetaHeader(name);
- MetaHeader::Value::Pointer headValue = meta->addValue(value);
- aclParseAclList(parser, &headValue->aclList);
-}
-
-void
-Adaptation::Config::DumpMetaHeader(StoreEntry *entry, const char *name)
-{
- typedef MetaHeaders::iterator AMLI;
- for (AMLI m = metaHeaders.begin(); m != metaHeaders.end(); ++m) {
- typedef MetaHeader::Values::iterator VLI;
- for (VLI v =(*m)->values.begin(); v != (*m)->values.end(); ++v ) {
- storeAppendPrintf(entry, "%s " SQUIDSTRINGPH " %s",
- name, SQUIDSTRINGPRINT((*m)->name), ConfigParser::QuoteString((*v)->value));
- dump_acl_list(entry, (*v)->aclList);
- storeAppendPrintf(entry, "\n");
- }
- }
-}
-
-void
-Adaptation::Config::FreeMetaHeader()
-{
- metaHeaders.clean();
-}
-
void
Adaptation::Config::ParseServiceSet()
{
#include "base/AsyncCall.h"
#include "adaptation/forward.h"
#include "adaptation/Elements.h"
+#include "Notes.h"
#include "SquidString.h"
class acl_access;
static void ParseServiceSet(void);
static void ParseServiceChain(void);
- static void ParseMetaHeader(ConfigParser &parser);
- static void FreeMetaHeader();
- static void DumpMetaHeader(StoreEntry *, const char *);
static void ParseAccess(ConfigParser &parser);
static void FreeAccess(void);
time_t oldest_service_failure;
int service_revival_delay;
- /**
- * Used to store meta headers. The meta headers are custom
- * ICAP request headers or ECAP options used to pass custom
- * transaction-state related meta information to a service.
- */
- class MetaHeader: public RefCountable
- {
- public:
- typedef RefCount<MetaHeader> Pointer;
- /// Stores a value for the meta header.
- class Value: public RefCountable
- {
- public:
- typedef RefCount<Value> Pointer;
- String value; ///< a header value
- ACLList *aclList; ///< The access list used to determine if this value is valid for a request
- explicit Value(const String &aVal) : value(aVal), aclList(NULL) {}
- ~Value();
- };
- typedef Vector<Value::Pointer> Values;
-
- explicit MetaHeader(const String &aName): name(aName) {}
-
- /**
- * Adds a value to the meta header and returns a pointer to the
- * related Value object.
- */
- Value::Pointer addValue(const String &value);
-
- /**
- * Walks through the possible values list of the meta and selects
- * the first value which matches the given HttpRequest and HttpReply
- * or NULL if none matches.
- */
- const char *match(HttpRequest *request, HttpReply *reply);
- String name; ///< The meta header name
- Values values; ///< The possible values list for the meta header
- };
- typedef Vector<MetaHeader::Pointer> MetaHeaders;
- static MetaHeaders metaHeaders; ///< The list of configured meta headers
-
- /**
- * Adds a header to the meta headers list and returns a pointer to the
- * related metaHeaders object. If the header name already exists in list,
- * returns a pointer to the existing object.
- */
- static MetaHeader::Pointer addMetaHeader(const String &header);
+ static Notes metaHeaders; ///< The list of configured meta headers
typedef Vector<ServiceConfigPointer> ServiceConfigs;
ServiceConfigs serviceConfigs;
#include "adaptation/DynamicGroupCfg.h"
#include "Array.h"
#include "HttpHeader.h"
+#include "Notes.h"
#include "RefCount.h"
#include "SquidString.h"
HttpHeader lastMeta;
/// All REQMOD and RESPMOD meta headers merged. Last field wins conflicts.
HttpHeader allMeta;
+ /// key:value pairs set by adaptation_meta, to be added to
+ /// AccessLogEntry::notes when ALE becomes available
+ NotePairs metaHeaders;
/// sets future services for the Adaptation::AccessCheck to notice
void setFutureServices(const DynamicGroupCfg &services);
HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
if (name.known()) { // must check to avoid empty names matching unset cfg
- typedef Adaptation::Config::MetaHeaders::iterator ACAMLI;
+ typedef Notes::iterator ACAMLI;
for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) {
- if (name == (*i)->name.termedBuf()) {
+ if (name == (*i)->key.termedBuf()) {
if (const char *value = (*i)->match(request, reply))
return libecap::Area::FromTempString(value);
else
Must(request);
HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
- typedef Adaptation::Config::MetaHeaders::iterator ACAMLI;
+ typedef Notes::iterator ACAMLI;
for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) {
const char *v = (*i)->match(request, reply);
if (v) {
- const libecap::Name name((*i)->name.termedBuf());
+ const libecap::Name name((*i)->key.termedBuf());
const libecap::Area value = libecap::Area::FromTempString(v);
visitor.visit(name, value);
}
if (!theVirginRep.raw().body_pipe)
makingVb = opNever; // there is nothing to deliver
- const HttpRequest *request = dynamic_cast<const HttpRequest*> (theCauseRep ?
+ HttpRequest *request = dynamic_cast<HttpRequest*> (theCauseRep ?
theCauseRep->raw().header : theVirginRep.raw().header);
Must(request);
+
+ HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
+
Adaptation::History::Pointer ah = request->adaptLogHistory();
if (ah != NULL) {
// retrying=false because ecap never retries transactions
adaptHistoryId = ah->recordXactStart(service().cfg().key, current_time, false);
+ typedef Notes::iterator ACAMLI;
+ for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) {
+ const char *v = (*i)->match(request, reply);
+ if (v && !ah->metaHeaders.hasByNameListMember((*i)->key.termedBuf(), v, ',')) {
+ ah->metaHeaders.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->key.termedBuf(), v));
+ }
+ }
}
theMaster->start();
makeUsernameHeader(request, buf);
// Adaptation::Config::metaHeaders
- typedef Adaptation::Config::MetaHeaders::iterator ACAMLI;
+ typedef Notes::iterator ACAMLI;
for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) {
HttpRequest *r = virgin.cause ?
virgin.cause : dynamic_cast<HttpRequest*>(virgin.header);
HttpReply *reply = dynamic_cast<HttpReply*>(virgin.header);
- if (const char *value = (*i)->match(r, reply))
- buf.Printf("%s: %s\r\n", (*i)->name.termedBuf(), value);
+ if (const char *value = (*i)->match(r, reply)) {
+ buf.Printf("%s: %s\r\n", (*i)->key.termedBuf(), value);
+ Adaptation::History::Pointer ah = request->adaptHistory(false);
+ if (ah != NULL && !ah->metaHeaders.hasByNameListMember((*i)->key.termedBuf(), value, ','))
+ ah->metaHeaders.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->key.termedBuf(), value));
+ }
}
// fprintf(stderr, "%s\n", buf.content());
static void parse_adaptation_service_set_type();
static void parse_adaptation_service_chain_type();
static void parse_adaptation_access_type();
-static void parse_adaptation_meta_type(Adaptation::Config::MetaHeaders *);
-static void dump_adaptation_meta_type(StoreEntry *, const char *, Adaptation::Config::MetaHeaders &);
-static void free_adaptation_meta_type(Adaptation::Config::MetaHeaders *);
#endif
#if ICAP_CLIENT
static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers);
static void parse_HeaderWithAclList(HeaderWithAclList **header);
static void free_HeaderWithAclList(HeaderWithAclList **header);
+static void parse_note(Notes *);
+static void dump_note(StoreEntry *, const char *, Notes &);
+static void free_note(Notes *);
static void parse_denyinfo(AclDenyInfoList ** var);
static void dump_denyinfo(StoreEntry * entry, const char *name, AclDenyInfoList * var);
static void free_denyinfo(AclDenyInfoList ** var);
{
Adaptation::Config::ParseAccess(LegacyParser);
}
-
-static void
-parse_adaptation_meta_type(Adaptation::Config::MetaHeaders *)
-{
- Adaptation::Config::ParseMetaHeader(LegacyParser);
-}
-
-static void
-dump_adaptation_meta_type(StoreEntry *entry, const char *name, Adaptation::Config::MetaHeaders &)
-{
- Adaptation::Config::DumpMetaHeader(entry, name);
-}
-
-static void
-free_adaptation_meta_type(Adaptation::Config::MetaHeaders *)
-{
- // Nothing to do, it is released inside Adaptation::Config::freeService()
-}
#endif /* USE_ADAPTATION */
#if ICAP_CLIENT
delete *header;
*header = NULL;
}
+
+static void parse_note(Notes *notes)
+{
+ assert(notes);
+ notes->parse(LegacyParser);
+}
+
+static void dump_note(StoreEntry *entry, const char *name, Notes ¬es)
+{
+ notes.dump(entry, name);
+}
+
+static void free_note(Notes *notes)
+{
+ notes->clean();
+}
adaptation_access_type adaptation_service_set adaptation_service_chain acl icap_service icap_class
adaptation_service_set_type icap_service ecap_service
adaptation_service_chain_type icap_service ecap_service
-adaptation_meta_type acl
icap_access_type icap_class acl
icap_class_type icap_service
icap_service_type
logformat
YesNoNone
memcachemode
+note acl
obsolete
onoff
peer
err_code The ID of an error response served by Squid or
a similar internal error identifier.
err_detail Additional err_code-dependent error information.
+ note The meta header specified by the argument. Also
+ logs the adaptation meta headers set by the
+ adaptation_meta configuration parameter.
+ If no argument given all meta headers logged.
Connection related format codes:
only.
DOC_END
+NAME: note
+TYPE: note
+LOC: Config.notes
+DEFAULT: none
+DOC_START
+ This option used to log custom information about the master
+ transaction. For example, an admin may configure Squid to log
+ which "user group" the transaction belongs to, where "user group"
+ will be determined based on a set of ACLs and not [just]
+ authentication information.
+ Values of key/value pairs can be logged using %{key}note macros:
+
+ note key value acl ...
+ logformat myFormat ... %{key}note ...
+DOC_END
+
NAME: relaxed_header_parser
COMMENT: on|off|warn
TYPE: tristate
DOC_END
NAME: adaptation_meta
-TYPE: adaptation_meta_type
+TYPE: note
IFDEF: USE_ADAPTATION
LOC: Adaptation::Config::metaHeaders
DEFAULT: none
any character, which is currently only useful for escaping backslashes
and double quotes. For example,
"this string has one backslash (\\) and two \"quotes\""
+
+ Used adaptation_meta header values may be logged via %note
+ logformat code. If multiple adaptation_meta headers with the same name
+ are used during master transaction lifetime, the header values are
+ logged in the order they were used and duplicate values are ignored
+ (only the first repeated value will be logged).
DOC_END
NAME: icap_retry
packerToMemInit(&p, &mb);
ah->lastMeta.packInto(&p);
aLogEntry->adapt.last_meta = xstrdup(mb.buf);
+ aLogEntry->notes.append(&ah->metaHeaders);
}
#endif
#endif
+ /*Add meta headers*/
+ typedef Notes::iterator ACAMLI;
+ for (ACAMLI i = Config.notes.begin(); i != Config.notes.end(); ++i) {
+ if (const char *value = (*i)->match(request, al->reply)) {
+ al->notes.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->key.termedBuf(), value));
+ debugs(33, 3, HERE << (*i)->key.termedBuf() << " " << value);
+ }
+ }
+
ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.log, this);
if (al->reply)
LFT_SSL_USER_CERT_ISSUER,
#endif
+ LFT_NOTE,
LFT_PERCENT /* special string cases for escaped chars */
} ByteCode_t;
}
break;
#endif
+ case LFT_NOTE:
+ if (fmt->data.string) {
+ sb = al->notes.getByName(fmt->data.string);
+ out = sb.termedBuf();
+ quote = 1;
+ } else {
+ HttpHeaderPos pos = HttpHeaderInitPos;
+ while (const HttpHeaderEntry *e = al->notes.getEntry(&pos)) {
+ sb.append(e->name);
+ sb.append(": ");
+ sb.append(e->value);
+ sb.append("\r\n");
+ }
+ out = sb.termedBuf();
+ quote = 1;
+ }
+ break;
case LFT_PERCENT:
out = "%";
{">eui", LFT_CLIENT_EUI},
{"err_code", LFT_SQUID_ERROR },
{"err_detail", LFT_SQUID_ERROR_DETAIL },
+ {"note", LFT_NOTE },
{NULL, LFT_NONE} /* this must be last */
};
#include "squid.h"
#include "AccessLogEntry.h"
#include "acl/Checklist.h"
+#if USE_ADAPTATION
+#include "adaptation/Config.h"
+#endif
#include "CachePeer.h"
+#include "errorpage.h"
#include "err_detail_type.h"
#include "errorpage.h"
#include "errorpage.h"
curr_token->type == Format::LFT_ADAPTATION_ALL_XACT_TIMES ||
curr_token->type == Format::LFT_ADAPTATION_LAST_HEADER ||
curr_token->type == Format::LFT_ADAPTATION_LAST_HEADER_ELEM ||
- curr_token->type == Format::LFT_ADAPTATION_LAST_ALL_HEADERS) {
+ curr_token->type == Format::LFT_ADAPTATION_LAST_ALL_HEADERS||
+ (curr_token->type == Format::LFT_NOTE && !Adaptation::Config::metaHeaders.empty())) {
Log::TheConfig.hasAdaptToken = true;
}
#if ICAP_CLIENT
void parse_time_t(time_t * var) STUB
char * strtokFile(void) STUB_RETVAL(NULL)
void ConfigParser::ParseUShort(unsigned short *var) STUB
+void ConfigParser::ParseString(String*) STUB
void dump_acl_access(StoreEntry * entry, const char *name, acl_access * head) STUB
+void dump_acl_list(StoreEntry*, ACLList*) STUB
YesNoNone::operator void*() const { STUB_NOP; return NULL; }
}
#include "ConfigParser.h"
-void
-ConfigParser::destruct()
-{
-// CALLED as shutdown no-op
-// fatal("ConfigParser::destruct. Not implemented.");
-}
void
eventAdd(const char *name, EVH * func, void *arg, double when, int, bool cbdata)