meta_header option This patch adds meta_header option to squid.conf. It is similar to adaptation_meta but is applied after all adaptation and before logging. Values of named meta headers can be logged using %{name}meta_header macros. meta_header name value acl ... logformat myFormat ... %{name}meta_header ... This option may be initially 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. From user point of view, adaptation_header sets/implies meta_header (i.e., setting adaptation_header is sufficient to be able to log it using %meta_header) but the meta_header option itself (if any) is evaluated later, so it has no effect on ICAP headers. This is a Measurement Factory project === modified file 'src/AccessLogEntry.h' --- src/AccessLogEntry.h 2012-08-28 13:00:30 +0000 +++ src/AccessLogEntry.h 2012-09-07 15:04:17 +0000 @@ -39,41 +39,41 @@ #include "HttpRequestMethod.h" #if ICAP_CLIENT #include "adaptation/icap/Elements.h" #endif #include "RefCount.h" #if USE_SSL #include "ssl/gadgets.h" #endif /* forward decls */ class HttpReply; class HttpRequest; class AccessLogEntry: public RefCountable { public: typedef RefCount Pointer; AccessLogEntry() : url(NULL), tcpClient(), reply(NULL), request(NULL), - adapted_request(NULL) {} + adapted_request(NULL), metaHeaders(hoMeta) {} ~AccessLogEntry(); /// Fetch the client IP log string into the given buffer. /// Knows about several alternate locations of the IP /// including indirect forwarded-for IP if configured to log that void getLogClientIp(char *buf, size_t bufsz) const; const char *url; /// TCP/IP level details about the client connection Comm::ConnectionPointer tcpClient; // TCP/IP level details about the server or peer connection // are stored in hier.tcpServer /** \brief This subclass holds log info for HTTP protocol * \todo Inner class declarations should be moved outside * \todo details of HTTP held in the parent class need moving into here. */ class HttpDetails { @@ -210,40 +210,42 @@ /// image of the last ICAP response header or eCAP meta received char *last_meta; } adapt; #endif // Why is this a sub-class and not a set of real "private:" fields? // It looks like its duplicating HTTPRequestMethod anyway! // TODO: shuffle this to the relevant protocol section OR replace with request->method class Private { public: Private() : method_str(NULL) {} const char *method_str; } _private; HierarchyLogEntry hier; HttpReply *reply; HttpRequest *request; //< virgin HTTP request HttpRequest *adapted_request; //< HTTP request after adaptation and redirection + /// key:value pairs set by meta_header (and adaptation_meta) + HttpHeader metaHeaders; #if ICAP_CLIENT /** \brief This subclass holds log info for ICAP part of request * \todo Inner class declarations should be moved outside */ class IcapLogEntry { public: IcapLogEntry():bodyBytesRead(-1),request(NULL),reply(NULL),outcome(Adaptation::Icap::xoUnknown),trTime(0),ioTime(0),resStatus(HTTP_STATUS_NONE) {} Ip::Address hostAddr; ///< ICAP server IP address String serviceName; ///< ICAP service name String reqUri; ///< ICAP Request-URI Adaptation::Icap::ICAP::Method reqMethod; ///< ICAP request method int64_t bytesSent; ///< number of bytes sent to ICAP server so far int64_t bytesRead; ///< number of bytes read from ICAP server so far /** * number of ICAP body bytes read from ICAP server or -1 for no encapsulated * message data in ICAP reply (eg 204 responses) */ === modified file 'src/HttpHeader.h' --- src/HttpHeader.h 2012-09-03 09:02:20 +0000 +++ src/HttpHeader.h 2012-09-19 15:40:18 +0000 @@ -158,40 +158,41 @@ ftDate_1123, ftETag, ftPCc, ftPContRange, ftPRange, ftPSc, ftDate_1123_or_ETag } field_type; /** Possible owners of http header */ typedef enum { hoNone =0, #if USE_HTCP hoHtcpReply, #endif hoRequest, hoReply, #if USE_SSL hoErrorDetail, #endif + hoMeta, hoEnd } http_hdr_owner_type; struct _HttpHeaderFieldAttrs { const char *name; http_hdr_type id; field_type type; }; /** Iteration for headers; use HttpHeaderPos as opaque type, do not interpret */ typedef ssize_t HttpHeaderPos; /* use this and only this to initialize HttpHeaderPos */ #define HttpHeaderInitPos (-1) class HttpHeaderEntry { public: HttpHeaderEntry(http_hdr_type id, const char *name, const char *value); === modified file 'src/HttpHeaderTools.cc' --- src/HttpHeaderTools.cc 2012-09-03 09:02:20 +0000 +++ src/HttpHeaderTools.cc 2012-09-19 15:40:18 +0000 @@ -20,40 +20,41 @@ * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.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" #include "HttpHeaderTools.h" #include "HttpRequest.h" #include "MemBuf.h" #include "Store.h" #include "StrList.h" #if USE_SSL #include "ssl/support.h" #endif #include #include #if HAVE_ERRNO_H #include #endif static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs); @@ -518,20 +519,110 @@ MemBuf mb; if (hwa->quoted) { if (al != NULL) { mb.init(); hwa->valueFormat->assemble(mb, al, 0); fieldValue = mb.content(); } } else { fieldValue = hwa->fieldValue.c_str(); } if (!fieldValue || fieldValue[0] == '\0') fieldValue = "-"; HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, hwa->fieldName.c_str(), fieldValue); heads->addEntry(e); } } } + +MetaHeader::Value::~Value() +{ + aclDestroyAclList(&aclList); +} + +MetaHeader::Value::Pointer +MetaHeader::addValue(const String &value) +{ + Value::Pointer v = new Value(value); + values.push_back(v); + return v; +} + +const char * +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; +} + +MetaHeader::Pointer +MetaHeaders::add(const String &headerName) +{ + typedef MetaHeaders::Headers::iterator AMLI; + for (AMLI i = headers.begin(); i != headers.end(); ++i) { + if ((*i)->name == headerName) + return (*i); + } + + MetaHeader::Pointer meta = new MetaHeader(headerName); + headers.push_back(meta); + return meta; +} + +MetaHeader::Pointer +MetaHeaders::parse(ConfigParser &parser) +{ + String name, value; + ConfigParser::ParseString(&name); + ConfigParser::ParseQuotedString(&value); + MetaHeader::Pointer meta = add(name); + MetaHeader::Value::Pointer headValue = meta->addValue(value); + aclParseAclList(parser, &headValue->aclList); + + if (blacklisted) { + for (int i = 0; blacklisted[i] != NULL; ++i) { + if (meta->name.caseCmp(blacklisted[i]) == 0) { + fatalf("%s:%d: meta name \"%s\" is a reserved %s name", + cfg_filename, config_lineno, meta->name.termedBuf(), + descr ? descr : ""); + } + } + } + + return meta; +} + +void +MetaHeaders::dump(StoreEntry *entry, const char *name) +{ + typedef MetaHeaders::Headers::iterator AMLI; + for (AMLI m = headers.begin(); m != headers.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 +MetaHeaders::clean() +{ + headers.clean(); +} === modified file 'src/HttpHeaderTools.h' --- src/HttpHeaderTools.h 2012-09-03 09:02:20 +0000 +++ src/HttpHeaderTools.h 2012-09-19 15:40:18 +0000 @@ -75,40 +75,125 @@ /// HTTP header field name std::string fieldName; /// HTTP header field value, possibly with macros std::string fieldValue; /// when the header field should be added (always if nil) ACLList *aclList; /// compiled HTTP header field value (no macros) Format::Format *valueFormat; /// internal ID for "known" headers or HDR_OTHER http_hdr_type fieldId; /// whether fieldValue may contain macros bool quoted; }; +class HttpRequest; +class HttpReply; + +/** + * Used to store meta headers. The meta headers are custom + * HTTP or ICAP request headers or ECAP options used to pass + * custom transaction-state related meta information to squid + * internal subsystems or to addaptation services. + */ +class MetaHeader: public RefCountable +{ +public: + typedef RefCount Pointer; + /// Stores a value for the meta header. + class Value: public RefCountable + { + public: + typedef RefCount 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 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 +}; + +class ConfigParser; +/** + * Used to store a meta headers list. + */ +class MetaHeaders { +public: + typedef Vector Headers; + typedef Headers::iterator iterator; ///< iterates over the meta headers list + + MetaHeaders(const char *aDescr, const char **metasBlacklist): descr(aDescr), blacklisted(metasBlacklist) {} + MetaHeaders(): descr(NULL), blacklisted(NULL) {} + ~MetaHeaders() { headers.clean(); } + /** + * Parse a meta headers line and returns a pointer to the + * parsed MetaHeader object. + */ + MetaHeader::Pointer parse(ConfigParser &parser); + /** + * Dump the heta headers list to the given StoreEntry object. + */ + void dump(StoreEntry *entry, const char *name); + void clean(); /// clean the meta headers list + + /// points to the first argument + iterator begin() { return headers.begin(); } + /// points to the end of list + iterator end() { return headers.end(); } + /// return true if the meta headers list is empty + bool empty() { return headers.empty(); } + + Headers headers; ///< The MetaHeader::Pointer objects array list + const char *descr; ///< A short description for MetaHeaders list + const char **blacklisted; ///< Null terminated list of blacklisted meta names +private: + /** + * Adds a header to the meta headers list and returns a pointer to the + * related MetaHeader object. If the header name already exists in list, + * returns a pointer to the existing object. + */ + MetaHeader::Pointer add(const String &headerName); +}; + extern int httpHeaderParseOffset(const char *start, int64_t * off); class HttpHeaderFieldInfo; class String; class HttpHeader; class HttpRequest; extern HttpHeaderFieldInfo *httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count); extern void httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * info, int count); extern http_hdr_type httpHeaderIdByName(const char *name, size_t name_len, const HttpHeaderFieldInfo * attrs, int end); extern http_hdr_type httpHeaderIdByNameDef(const char *name, int name_len); extern const char *httpHeaderNameById(int id); extern int httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive); extern int httpHeaderParseInt(const char *start, int *val); extern void httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...) PRINTF_FORMAT_ARG3; extern const char *getStringPrefix(const char *str, const char *end); extern void httpHdrMangleList(HttpHeader *, HttpRequest *, int req_or_rep); === modified file 'src/adaptation/Config.cc' --- src/adaptation/Config.cc 2012-09-01 14:38:36 +0000 +++ src/adaptation/Config.cc 2012-09-19 15:40:18 +0000 @@ -33,87 +33,58 @@ #include "acl/Gadgets.h" #include "adaptation/AccessRule.h" #include "adaptation/Config.h" #include "adaptation/History.h" #include "adaptation/Service.h" #include "adaptation/ServiceGroups.h" #include "Array.h" #include "ConfigParser.h" #include "globals.h" #include "HttpReply.h" #include "HttpRequest.h" #include "Store.h" #include "structs.h" bool Adaptation::Config::Enabled = false; char *Adaptation::Config::masterx_shared_name = NULL; int Adaptation::Config::service_iteration_limit = 16; 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 +}; +MetaHeaders Adaptation::Config::metaHeaders("ICAP header", metasBlacklist); Adaptation::ServiceConfig* Adaptation::Config::newServiceConfig() const { return new ServiceConfig(); } void Adaptation::Config::removeService(const String& service) { removeRule(service); const Groups& groups = AllGroups(); for (unsigned int i = 0; i < groups.size(); ) { const ServiceGroupPointer group = groups[i]; const ServiceGroup::Store& services = group->services; typedef ServiceGroup::Store::const_iterator SGSI; for (SGSI it = services.begin(); it != services.end(); ++it) { if (*it == service) { group->removedServices.push_back(service); group->services.prune(service); @@ -164,42 +135,40 @@ void Adaptation::Config::parseService() { ServiceConfigPointer cfg = newServiceConfig(); if (!cfg->parse()) { fatalf("%s:%d: malformed adaptation service configuration", cfg_filename, config_lineno); } serviceConfigs.push_back(cfg); } void Adaptation::Config::freeService() { FreeAccess(); FreeServiceGroups(); DetachServices(); serviceConfigs.clean(); - - FreeMetaHeader(); } void Adaptation::Config::dumpService(StoreEntry *entry, const char *name) const { typedef Services::iterator SCI; for (SCI i = AllServices().begin(); i != AllServices().end(); ++i) { const ServiceConfig &cfg = (*i)->cfg(); storeAppendPrintf(entry, "%s " SQUIDSTRINGPH "_%s %s %d " SQUIDSTRINGPH "\n", name, SQUIDSTRINGPRINT(cfg.key), cfg.methodStr(), cfg.vectPointStr(), cfg.bypass, SQUIDSTRINGPRINT(cfg.uri)); } } bool Adaptation::Config::finalize() { if (!onoff) { @@ -240,98 +209,40 @@ { typedef typename Collection::iterator CI; for (CI i = collection.begin(); i != collection.end(); ++i) (*i)->finalize(); debugs(93,2, HERE << "Initialized " << collection.size() << ' ' << label); } void Adaptation::Config::Finalize(bool enabled) { Enabled = enabled; debugs(93, DBG_IMPORTANT, "Adaptation support is " << (Enabled ? "on" : "off.")); FinalizeEach(AllServices(), "message adaptation services"); FinalizeEach(AllGroups(), "message adaptation service groups"); 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() { Adaptation::Config::ParseServiceGroup(new ServiceSet); } void Adaptation::Config::ParseServiceChain() { Adaptation::Config::ParseServiceGroup(new ServiceChain); } void Adaptation::Config::ParseServiceGroup(ServiceGroupPointer g) { assert(g != NULL); g->parse(); AllGroups().push_back(g); } void === modified file 'src/adaptation/Config.h' --- src/adaptation/Config.h 2012-08-14 11:53:07 +0000 +++ src/adaptation/Config.h 2012-09-04 16:31:35 +0000 @@ -1,120 +1,72 @@ #ifndef SQUID_ADAPTATION__CONFIG_H #define SQUID_ADAPTATION__CONFIG_H #include "event.h" #include "acl/Gadgets.h" #include "base/AsyncCall.h" #include "adaptation/forward.h" #include "adaptation/Elements.h" +#include "HttpHeaderTools.h" #include "SquidString.h" class acl_access; class ConfigParser; class HttpRequest; class HttpReply; namespace Adaptation { class Config { public: static void Finalize(bool enable); 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); static void DumpAccess(StoreEntry *, const char *); friend class AccessCheck; public: static bool Enabled; // true if at least one adaptation mechanism is // these are global squid.conf options, documented elsewhere static char *masterx_shared_name; // global TODO: do we need TheConfig? static int service_iteration_limit; static int send_client_ip; static int send_username; static int use_indirect_client; // Options below are accessed via Icap::TheConfig or Ecap::TheConfig // TODO: move ICAP-specific options to Icap::Config and add TheConfig int onoff; int service_failure_limit; 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 Pointer; - /// Stores a value for the meta header. - class Value: public RefCountable - { - public: - typedef RefCount 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 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 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); - typedef Vector ServiceConfigs; ServiceConfigs serviceConfigs; Config(); virtual ~Config(); void parseService(void); void freeService(void); void dumpService(StoreEntry *, const char *) const; ServicePointer findService(const String&); /** * Creates and starts the adaptation services. In the case the adaptation * mechanism is disabled then removes any reference to the services from * access rules and service groups, and returns false. * \return true if the services are ready and running, false otherwise */ virtual bool finalize(); protected: === modified file 'src/adaptation/History.cc' --- src/adaptation/History.cc 2012-09-03 08:58:40 +0000 +++ src/adaptation/History.cc 2012-09-19 15:40:18 +0000 @@ -18,40 +18,41 @@ start(current_time), theRptm(-1), retried(false) { } void Adaptation::History::Entry::stop() { // theRptm may already be set if the access log entry has already been made (void)rptm(); // will cache result in theRptm if not set already } int Adaptation::History::Entry::rptm() { if (theRptm < 0) theRptm = tvSubMsec(start, current_time); return theRptm; } Adaptation::History::History(): lastMeta(hoReply), allMeta(hoReply), + metaHeaders(hoMeta), theNextServices(TheNullServices) { } int Adaptation::History::recordXactStart(const String &serviceId, const timeval &when, bool retrying) { // the history will be empty on retries if it was enabled after the failure if (retrying && !theEntries.empty()) theEntries.back().retried = true; theEntries.push_back(Adaptation::History::Entry(serviceId, when)); return theEntries.size() - 1; // record position becomes history ID } void Adaptation::History::recordXactFinish(int hid) { Must(0 <= hid && hid < static_cast(theEntries.size())); theEntries[hid].stop(); } === modified file 'src/adaptation/History.h' --- src/adaptation/History.h 2012-08-28 13:00:30 +0000 +++ src/adaptation/History.h 2012-09-10 14:50:56 +0000 @@ -33,40 +33,43 @@ /// sets or resets a cross-transactional database record void updateXxRecord(const char *name, const String &value); /// returns true and fills the record fields iff there is a db record bool getXxRecord(String &name, String &value) const; /// sets or resets next services for the Adaptation::Iterator to notice void updateNextServices(const String &services); /// returns true, fills the value, and resets iff next services were set bool extractNextServices(String &value); /// store the last meta header fields received from the adaptation service void recordMeta(const HttpHeader *lm); public: /// Last received meta header (REQMOD or RESPMOD, whichever comes last). 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::metaHeaders when ALE becomes available + HttpHeader metaHeaders; /// sets future services for the Adaptation::AccessCheck to notice void setFutureServices(const DynamicGroupCfg &services); /// returns true, fills the value, and resets iff future services were set bool extractFutureServices(DynamicGroupCfg &services); private: /// single Xaction stats (i.e., a historical record entry) class Entry { public: Entry(const String &serviceId, const timeval &when); Entry(); // required by Vector<> void stop(); ///< updates stats on transaction end int rptm(); ///< returns response time [msec], calculates it if needed String service; ///< adaptation service ID timeval start; ///< when the xaction was started === modified file 'src/adaptation/ecap/XactionRep.cc' --- src/adaptation/ecap/XactionRep.cc 2012-08-28 13:00:30 +0000 +++ src/adaptation/ecap/XactionRep.cc 2012-09-07 17:06:15 +0000 @@ -160,88 +160,98 @@ if (name.known()) { // must check to avoid empty names matching unset cfg Adaptation::History::Pointer ah = request->adaptHistory(false); if (ah != NULL) { String name, value; if (ah->getXxRecord(name, value)) return libecap::Area::FromTempBuffer(value.rawBuf(), value.size()); } } return libecap::Area(); } const libecap::Area Adaptation::Ecap::XactionRep::metaValue(const libecap::Name &name) const { HttpRequest *request = dynamic_cast(theCauseRep ? theCauseRep->raw().header : theVirginRep.raw().header); Must(request); HttpReply *reply = dynamic_cast(theVirginRep.raw().header); if (name.known()) { // must check to avoid empty names matching unset cfg - typedef Adaptation::Config::MetaHeaders::iterator ACAMLI; + typedef MetaHeaders::iterator ACAMLI; for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) { if (name == (*i)->name.termedBuf()) { if (const char *value = (*i)->match(request, reply)) return libecap::Area::FromTempString(value); else return libecap::Area(); } } } return libecap::Area(); } void Adaptation::Ecap::XactionRep::visitEachMetaHeader(libecap::NamedValueVisitor &visitor) const { HttpRequest *request = dynamic_cast(theCauseRep ? theCauseRep->raw().header : theVirginRep.raw().header); Must(request); HttpReply *reply = dynamic_cast(theVirginRep.raw().header); - typedef Adaptation::Config::MetaHeaders::iterator ACAMLI; + typedef MetaHeaders::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::Area value = libecap::Area::FromTempString(v); visitor.visit(name, value); } } } void Adaptation::Ecap::XactionRep::start() { Must(theMaster); if (!theVirginRep.raw().body_pipe) makingVb = opNever; // there is nothing to deliver - const HttpRequest *request = dynamic_cast (theCauseRep ? + HttpRequest *request = dynamic_cast (theCauseRep ? theCauseRep->raw().header : theVirginRep.raw().header); Must(request); + + HttpReply *reply = dynamic_cast(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 MetaHeaders::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)->name.termedBuf(), v, ',')) { + ah->metaHeaders.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->name.termedBuf(), v)); + } + } } theMaster->start(); } void Adaptation::Ecap::XactionRep::swanSong() { // clear body_pipes, if any // this code does not maintain proxying* and canAccessVb states; should it? if (theAnswerRep != NULL) { BodyPipe::Pointer body_pipe = answer().body_pipe; if (body_pipe != NULL) { Must(body_pipe->stillProducing(this)); stopProducingFor(body_pipe, false); } } BodyPipe::Pointer &body_pipe = theVirginRep.raw().body_pipe; === modified file 'src/adaptation/icap/ModXact.cc' --- src/adaptation/icap/ModXact.cc 2012-08-31 16:57:39 +0000 +++ src/adaptation/icap/ModXact.cc 2012-09-19 15:40:18 +0000 @@ -1403,50 +1403,54 @@ } makeAllowHeader(buf); if (TheConfig.send_client_ip && request) { Ip::Address client_addr; #if FOLLOW_X_FORWARDED_FOR if (TheConfig.use_indirect_client) { client_addr = request->indirect_client_addr; } else #endif client_addr = request->client_addr; if (!client_addr.IsAnyAddr() && !client_addr.IsNoAddr()) buf.Printf("X-Client-IP: %s\r\n", client_addr.NtoA(ntoabuf,MAX_IPSTRLEN)); } if (TheConfig.send_username && request) makeUsernameHeader(request, buf); // Adaptation::Config::metaHeaders - typedef Adaptation::Config::MetaHeaders::iterator ACAMLI; + typedef MetaHeaders::iterator ACAMLI; for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) { HttpRequest *r = virgin.cause ? virgin.cause : dynamic_cast(virgin.header); Must(r); HttpReply *reply = dynamic_cast(virgin.header); - if (const char *value = (*i)->match(r, reply)) + if (const char *value = (*i)->match(r, reply)) { buf.Printf("%s: %s\r\n", (*i)->name.termedBuf(), value); + Adaptation::History::Pointer ah = request->adaptHistory(false); + if (ah != NULL && !ah->metaHeaders.hasByNameListMember((*i)->name.termedBuf(), value, ',')) + ah->metaHeaders.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->name.termedBuf(), value)); + } } // fprintf(stderr, "%s\n", buf.content()); buf.append(ICAP::crlf, 2); // terminate ICAP header // fill icapRequest for logging Must(icapRequest->parseCharBuf(buf.content(), buf.contentSize())); // start ICAP request body with encapsulated HTTP headers buf.append(httpBuf.content(), httpBuf.contentSize()); httpBuf.clean(); } // decides which Allow values to write and updates the request buffer void Adaptation::Icap::ModXact::makeAllowHeader(MemBuf &buf) { const bool allow204in = preview.enabled(); // TODO: add shouldAllow204in() const bool allow204out = state.allowedPostview204 = shouldAllow204(); === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2012-09-01 14:38:36 +0000 +++ src/cache_cf.cc 2012-09-19 15:40:18 +0000 @@ -106,43 +106,40 @@ #include #endif #if HAVE_PWD_H #include #endif #if HAVE_GRP_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if USE_SSL #include "ssl/gadgets.h" #endif #if USE_ADAPTATION 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 parse_icap_service_type(Adaptation::Icap::Config *); static void dump_icap_service_type(StoreEntry *, const char *, const Adaptation::Icap::Config &); static void free_icap_service_type(Adaptation::Icap::Config *); static void parse_icap_class_type(); static void parse_icap_access_type(); static void parse_icap_service_failure_limit(Adaptation::Icap::Config *); static void dump_icap_service_failure_limit(StoreEntry *, const char *, const Adaptation::Icap::Config &); static void free_icap_service_failure_limit(Adaptation::Icap::Config *); #endif #if USE_ECAP static void parse_ecap_service_type(Adaptation::Ecap::Config *); static void dump_ecap_service_type(StoreEntry *, const char *, const Adaptation::Ecap::Config &); static void free_ecap_service_type(Adaptation::Ecap::Config *); #endif @@ -191,40 +188,43 @@ #endif #if !USE_DNSHELPER static void parseBytesLineSigned(ssize_t * bptr, const char *units); #endif static size_t parseBytesUnits(const char *unit); static void free_all(void); void requirePathnameExists(const char *name, const char *path); static OBJH dump_config; #if USE_HTTP_VIOLATIONS static void free_HeaderManglers(HeaderManglers **pm); static void dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers); static void parse_http_header_access(HeaderManglers **manglers); #define free_http_header_access free_HeaderManglers static void dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers); static void parse_http_header_replace(HeaderManglers **manglers); #define free_http_header_replace free_HeaderManglers #endif 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_meta_header(MetaHeaders *); +static void dump_meta_header(StoreEntry *, const char *, MetaHeaders &); +static void free_meta_header(MetaHeaders *); static void parse_denyinfo(acl_deny_info_list ** var); static void dump_denyinfo(StoreEntry * entry, const char *name, acl_deny_info_list * var); static void free_denyinfo(acl_deny_info_list ** var); #if USE_WCCPv2 static void parse_IpAddress_list(Ip::Address_list **); static void dump_IpAddress_list(StoreEntry *, const char *, const Ip::Address_list *); static void free_IpAddress_list(Ip::Address_list **); #if CURRENTLY_UNUSED static int check_null_IpAddress_list(const Ip::Address_list *); #endif /* CURRENTLY_UNUSED */ #endif /* USE_WCCPv2 */ static void parsePortCfg(AnyP::PortCfg **, const char *protocol); #define parse_PortCfg(l) parsePortCfg((l), token) static void dump_PortCfg(StoreEntry *, const char *, const AnyP::PortCfg *); static void free_PortCfg(AnyP::PortCfg **); #if USE_SSL static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign); @@ -4212,58 +4212,40 @@ #if USE_ADAPTATION static void parse_adaptation_service_set_type() { Adaptation::Config::ParseServiceSet(); } static void parse_adaptation_service_chain_type() { Adaptation::Config::ParseServiceChain(); } static void parse_adaptation_access_type() { 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 static void parse_icap_service_type(Adaptation::Icap::Config * cfg) { cfg->parseService(); } static void free_icap_service_type(Adaptation::Icap::Config * cfg) { cfg->freeService(); } static void dump_icap_service_type(StoreEntry * entry, const char *name, const Adaptation::Icap::Config &cfg) { cfg.dumpService(entry, name); @@ -4661,20 +4643,36 @@ (*headers)->push_back(hwa); } static void free_HeaderWithAclList(HeaderWithAclList **header) { if (!(*header)) return; for (HeaderWithAclList::iterator hwa = (*header)->begin(); hwa != (*header)->end(); ++hwa) { if (hwa->aclList) aclDestroyAclList(&hwa->aclList); if (hwa->valueFormat) { delete hwa->valueFormat; hwa->valueFormat = NULL; } } delete *header; *header = NULL; } + +static void parse_meta_header(MetaHeaders *metaHeaders) +{ + assert(metaHeaders); + metaHeaders->parse(LegacyParser); +} + +static void dump_meta_header(StoreEntry *entry, const char *name, MetaHeaders &metaHeaders) +{ + metaHeaders.dump(entry, name); +} + +static void free_meta_header(MetaHeaders *metaHeaders) +{ + metaHeaders->clean(); +} === modified file 'src/cf.data.depend' --- src/cf.data.depend 2012-07-18 16:21:47 +0000 +++ src/cf.data.depend 2012-09-07 14:52:34 +0000 @@ -18,51 +18,51 @@ debug delay_pool_access acl delay_class delay_pool_class delay_pools delay_pool_count delay_pool_rates delay_class client_delay_pool_access acl client_delay_pool_count client_delay_pool_rates denyinfo acl eol externalAclHelper auth_param HelperChildConfig hostdomain cache_peer hostdomaintype cache_peer http_header_access acl http_header_replace HeaderWithAclList acl 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 icap_service_failure_limit ecap_service_type int kb_int64_t kb_size_t logformat YesNoNone +meta_header acl memcachemode obsolete onoff peer peer_access cache_peer acl PortCfg QosConfig refreshpattern removalpolicy size_t IpAddress_list string string time_msec time_t tristate uri_whitespace u_short wccp2_method wccp2_amethod === modified file 'src/cf.data.pre' --- src/cf.data.pre 2012-08-30 15:36:03 +0000 +++ src/cf.data.pre 2012-09-10 21:15:26 +0000 @@ -3256,40 +3256,44 @@ [ output in squid text log format as used by log_mime_hdrs # output in URL quoted format ' output as-is - left aligned width minimum and/or maximum field width: [width_min][.width_max] When minimum starts with 0, the field is zero-padded. String values exceeding maximum width are truncated. {arg} argument such as header name etc Format codes: % a literal % character sn Unique sequence number per log line entry 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. + meta_header 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: >a Client source IP address >A Client FQDN >p Client source port >eui Client source EUI (MAC address, EUI-48 or EUI-64 identifier) >la Local IP address the client connected to >lp Local port number the client connected to la Local listening IP address the client connection was connected to. lp Local listening port number the client connection was connected to. headers.adapted_request = xstrdup(mb.buf); // the virgin request is saved to aLogEntry->request if (aLogEntry->request) { packerClean(&p); mb.reset(); packerToMemInit(&p, &mb); aLogEntry->request->header.packInto(&p); aLogEntry->headers.request = xstrdup(mb.buf); } #if USE_ADAPTATION const Adaptation::History::Pointer ah = request->adaptLogHistory(); if (ah != NULL) { packerClean(&p); mb.reset(); packerToMemInit(&p, &mb); ah->lastMeta.packInto(&p); aLogEntry->adapt.last_meta = xstrdup(mb.buf); + aLogEntry->metaHeaders.append(&ah->metaHeaders); } #endif packerClean(&p); mb.clean(); } #if ICAP_CLIENT const Adaptation::Icap::History::Pointer ih = request->icapHistory(); if (ih != NULL) aLogEntry->icap.processingTime = ih->processingTime(); #endif aLogEntry->http.method = request->method; aLogEntry->http.version = request->http_ver; aLogEntry->hier = request->hier; if (request->content_length > 0) // negative when no body or unknown length aLogEntry->cache.requestSize += request->content_length; aLogEntry->cache.extuser = request->extacl_user.termedBuf(); @@ -673,40 +674,49 @@ al->cache.code = logType; al->cache.msec = tvSubMsec(start_time, current_time); if (request) prepareLogWithRequestDetails(request, al); if (getConn() != NULL && getConn()->clientConnection != NULL && getConn()->clientConnection->rfc931[0]) al->cache.rfc931 = getConn()->clientConnection->rfc931; #if USE_SSL && 0 /* This is broken. Fails if the connection has been closed. Needs * to snarf the ssl details some place earlier.. */ if (getConn() != NULL) al->cache.ssluser = sslGetUserEmail(fd_table[getConn()->fd].ssl); #endif + /*Add meta headers*/ + typedef MetaHeaders::iterator ACAMLI; + for (ACAMLI i = Config.metaHeaders.begin(); i != Config.metaHeaders.end(); ++i) { + if (const char *value = (*i)->match(request, al->reply)) { + al->metaHeaders.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->name.termedBuf(), value)); + debugs(33, 3, HERE << (*i)->name.termedBuf() << " " << value); + } + } + ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.log, this); if (al->reply) checklist->reply = HTTPMSGLOCK(al->reply); if (!Config.accessList.log || checklist->fastCheck() == ACCESS_ALLOWED) { if (request) al->adapted_request = HTTPMSGLOCK(request); accessLogLog(al, checklist); updateCounters(); if (getConn() != NULL && getConn()->clientConnection != NULL) clientdbUpdate(getConn()->clientConnection->remote, logType, AnyP::PROTO_HTTP, out.size); } delete checklist; } void ClientHttpRequest::freeResources() === modified file 'src/format/ByteCode.h' --- src/format/ByteCode.h 2012-07-18 16:21:47 +0000 +++ src/format/ByteCode.h 2012-09-07 16:58:08 +0000 @@ -179,37 +179,38 @@ LFT_ICAP_REQ_HEADER, LFT_ICAP_REQ_HEADER_ELEM, LFT_ICAP_REQ_ALL_HEADERS, LFT_ICAP_REP_HEADER, LFT_ICAP_REP_HEADER_ELEM, LFT_ICAP_REP_ALL_HEADERS, LFT_ICAP_TR_RESPONSE_TIME, LFT_ICAP_IO_TIME, LFT_ICAP_OUTCOME, LFT_ICAP_STATUS_CODE, #endif #if USE_SSL LFT_SSL_BUMP_MODE, LFT_SSL_USER_CERT_SUBJECT, LFT_SSL_USER_CERT_ISSUER, #endif + LFT_META_HEADER, LFT_PERCENT /* special string cases for escaped chars */ } ByteCode_t; /// Quoting style for a format output. enum Quoting { LOG_QUOTE_NONE = 0, LOG_QUOTE_QUOTES, LOG_QUOTE_MIMEBLOB, LOG_QUOTE_URL, LOG_QUOTE_RAW }; extern const char *log_tags[]; } // namespace Format #endif /* _SQUID_FMT_BYTECODE_H */ === modified file 'src/format/Format.cc' --- src/format/Format.cc 2012-08-31 16:57:39 +0000 +++ src/format/Format.cc 2012-09-19 15:40:18 +0000 @@ -1023,40 +1023,57 @@ } case LFT_SSL_USER_CERT_SUBJECT: if (X509 *cert = al->cache.sslClientCert.get()) { if (X509_NAME *subject = X509_get_subject_name(cert)) { X509_NAME_oneline(subject, tmp, sizeof(tmp)); out = tmp; } } break; case LFT_SSL_USER_CERT_ISSUER: if (X509 *cert = al->cache.sslClientCert.get()) { if (X509_NAME *issuer = X509_get_issuer_name(cert)) { X509_NAME_oneline(issuer, tmp, sizeof(tmp)); out = tmp; } } break; #endif + case LFT_META_HEADER: + if (fmt->data.string) { + sb = al->metaHeaders.getByName(fmt->data.string); + out = sb.termedBuf(); + quote = 1; + } else { + HttpHeaderPos pos = HttpHeaderInitPos; + while (const HttpHeaderEntry *e = al->metaHeaders.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 = "%"; break; } if (dooff) { snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff); out = tmp; } else if (doint) { snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint); out = tmp; } if (out && *out) { if (quote || fmt->quote != LOG_QUOTE_NONE) { char *newout = NULL; int newfree = 0; === modified file 'src/format/Token.cc' --- src/format/Token.cc 2012-08-06 17:41:08 +0000 +++ src/format/Token.cc 2012-09-07 16:57:49 +0000 @@ -131,40 +131,41 @@ {"2 byte tokens static TokenTableEntry TokenTableMisc[] = { {">eui", LFT_CLIENT_EUI}, {"err_code", LFT_SQUID_ERROR }, {"err_detail", LFT_SQUID_ERROR_DETAIL }, + {"meta_header", LFT_META_HEADER }, {NULL, LFT_NONE} /* this must be last */ }; #if USE_ADAPTATION static TokenTableEntry TokenTableAdapt[] = { {"all_trs", LFT_ADAPTATION_ALL_XACT_TIMES}, {"sum_trs", LFT_ADAPTATION_SUM_XACT_TIMES}, {"next) { if (log->type == Log::Format::CLF_NONE) continue; log->logfile = logfileOpen(log->filename, MAX_URL << 2, 1); LogfileStatus = LOG_ENABLE; #if USE_ADAPTATION for (Format::Token * curr_token = (log->logFormat?log->logFormat->format:NULL); curr_token; curr_token = curr_token->next) { if (curr_token->type == Format::LFT_ADAPTATION_SUM_XACT_TIMES || 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_META_HEADER && !Adaptation::Config::metaHeaders.empty())) { Log::TheConfig.hasAdaptToken = true; } #if ICAP_CLIENT if (curr_token->type == Format::LFT_ICAP_TOTAL_TIME) { Log::TheConfig.hasIcapToken = true; } #endif } #endif } #if HEADERS_LOG headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512); assert(NULL != headerslog); #endif #if MULTICAST_MISS_STREAM === modified file 'src/structs.h' --- src/structs.h 2012-09-03 09:02:20 +0000 +++ src/structs.h 2012-09-19 15:40:18 +0000 @@ -563,40 +563,42 @@ int max_open_disk_fds; int uri_whitespace; acl_size_t *rangeOffsetLimit; #if MULTICAST_MISS_STREAM struct { Ip::Address addr; int ttl; unsigned short port; char *encode_key; } mcast_miss; #endif /// request_header_access and request_header_replace HeaderManglers *request_header_access; /// reply_header_access and reply_header_replace HeaderManglers *reply_header_access; ///request_header_add access list HeaderWithAclList *request_header_add; + ///meta_header + MetaHeaders metaHeaders; char *coredump_dir; char *chroot_dir; #if USE_CACHE_DIGESTS struct { int bits_per_entry; time_t rebuild_period; time_t rewrite_period; size_t swapout_chunk_size; int rebuild_chunk_percentage; } digest; #endif #if USE_SSL struct { int unclean_shutdown; char *ssl_engine; } SSL; #endif