Author: Christos Tsantilas Add request_header_add option This patch: This patch add request_header_add, a new ACL-driven squid.conf option that allow addition of HTTP request header fields before the request is sent to the next HTTP hop (a peer proxy or an origin server): request_header_add acl1 [acl2] where: * Field-name is a token specifying an HTTP header name. * Field-value is either a constant token or a quoted string containing %macros. The following field-value macros are supported: %LOGIN, %SRC, %DST, %user_cert_subject, %user_ca, %% * One or more Squid ACLs may be specified to restrict header insertion to matching requests. The request_header_add option supports fast ACLs only. This is a Measurement Factory project. === modified file 'src/ConfigParser.cc' --- src/ConfigParser.cc 2012-01-20 18:55:04 +0000 +++ src/ConfigParser.cc 2012-06-29 17:00:49 +0000 @@ -99,79 +99,83 @@ t += strspn(buf, w_space); t2 = t + strcspn(t, w_space); t3 = t2 + strspn(t2, w_space); while (*t3 && *t3 != '#') { t2 = t3 + strcspn(t3, w_space); t3 = t2 + strspn(t2, w_space); } *t2 = '\0'; } /* skip comments */ /* skip blank lines */ } while ( *t == '#' || !*t ); return t; } void -ConfigParser::ParseQuotedString(char **var) +ConfigParser::ParseQuotedString(char **var, bool *wasQuoted) { String sVar; - ParseQuotedString(&sVar); + ParseQuotedString(&sVar, wasQuoted); *var = xstrdup(sVar.termedBuf()); } void -ConfigParser::ParseQuotedString(String *var) +ConfigParser::ParseQuotedString(String *var, bool *wasQuoted) { // Get all of the remaining string char *token = strtok(NULL, ""); if (token == NULL) self_destruct(); if (*token != '"') { token = strtok(token, w_space); var->reset(token); + if (wasQuoted) + *wasQuoted = false; return; } char *s = token + 1; /* scan until the end of the quoted string, unescaping " and \ */ while (*s && *s != '"') { if (*s == '\\') { const char * next = s+1; // may point to 0 memmove(s, next, strlen(next) + 1); } s++; } if (*s != '"') { debugs(3, DBG_CRITICAL, "ParseQuotedString: missing '\"' at the end of quoted string" ); self_destruct(); } strtok(s-1, "\""); /*Reset the strtok to point after the " */ *s = '\0'; var->reset(token+1); + if (wasQuoted) + *wasQuoted = true; } const char * ConfigParser::QuoteString(String &var) { static String quotedStr; const char *s = var.termedBuf(); bool needQuote = false; for (const char *l = s; !needQuote && *l != '\0'; l++ ) needQuote = !isalnum(*l); if (!needQuote) return s; quotedStr.clean(); quotedStr.append('"'); for (; *s != '\0'; s++) { if (*s == '"' || *s == '\\') quotedStr.append('\\'); === modified file 'src/ConfigParser.h' --- src/ConfigParser.h 2012-01-20 18:55:04 +0000 +++ src/ConfigParser.h 2012-06-29 17:00:49 +0000 @@ -50,30 +50,35 @@ /** * A configuration file Parser. Instances of this class track * parsing state and perform tokenisation. Syntax is currently * taken care of outside this class. * * One reason for this class is to allow testing of configuration * using modules without linking cache_cf.o in - because that drags * in all of squid by reference. Instead the tokeniser only is * brought in. */ class ConfigParser { public: void destruct(); static void ParseUShort(unsigned short *var); static void ParseBool(bool *var); static void ParseString(char **var); static void ParseString(String *var); - static void ParseQuotedString(char **var); - static void ParseQuotedString(String *var); + /// Parse an unquoted token (no spaces) or a "quoted string" that + /// may include spaces. In some contexts, quotes strings may also + /// include macros. Quoted strings may escape any character with + /// a backslash (\), which is currently only useful for inner + /// quotes. TODO: support quoted strings anywhere a token is accepted. + static void ParseQuotedString(char **var, bool *wasQuoted = NULL); + static void ParseQuotedString(String *var, bool *wasQuoted = NULL); static const char *QuoteString(String &var); static void ParseWordList(wordlist **list); static char * strtokFile(); }; extern int parseConfigFile(const char *file_name); #endif /* SQUID_CONFIGPARSER_H */ === modified file 'src/HttpHeaderTools.cc' --- src/HttpHeaderTools.cc 2012-06-29 16:36:58 +0000 +++ src/HttpHeaderTools.cc 2012-06-29 16:41:34 +0000 @@ -18,47 +18,57 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (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-old.h" #include "acl/FilledChecklist.h" #include "acl/Gadgets.h" +#include "client_side.h" +#include "comm/Connection.h" #include "compat/strtoll.h" +#include "fde.h" #include "HttpHdrContRange.h" #include "HttpHeader.h" #include "HttpHeaderTools.h" #include "HttpRequest.h" #include "MemBuf.h" +#if USE_SSL +#include "ssl/support.h" +#endif #include "Store.h" +#include +#if HAVE_STRING +#include +#endif static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs); HttpHeaderFieldInfo * httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count) { int i; HttpHeaderFieldInfo *table = NULL; assert(attrs && count); /* allocate space */ table = new HttpHeaderFieldInfo[count]; for (i = 0; i < count; ++i) { const http_hdr_type id = attrs[i].id; HttpHeaderFieldInfo *info = table + id; /* sanity checks */ assert(id >= 0 && id < count); assert(attrs[i].name); @@ -600,20 +610,143 @@ // a custom header if (e.id == HDR_OTHER) { // does it have an ACL list configured? // Optimize: use a name type that we do not need to convert to here const ManglersByName::const_iterator i = custom.find(e.name.termedBuf()); if (i != custom.end()) return &i->second; } // Next-to-last resort: "Other" rules match any custom header if (e.id == HDR_OTHER && known[HDR_OTHER].access_list) return &known[HDR_OTHER]; // Last resort: "All" rules match any header if (all.access_list) return &all; return NULL; } +#if USE_AUTH +static +const char * +httpHdrFmt_login(HttpRequest *request) +{ + if (request->auth_user_request != NULL) + return request->auth_user_request->username(); + + return NULL; +} +#endif + +static +const char * +httpHdrFmt_src(HttpRequest *request) +{ + static char buf[256]; + /*Maybe we want to use indirect_client in some cases: + return request->indirect_client_addr.NtoA(buf, sizeof(buf)); + Do we need a new configuration parameter: *uses_indirect_client + */ + return request->client_addr.NtoA(buf, sizeof(buf)); +} + +static +const char * +httpHdrFmt_dst(HttpRequest *request) +{ + return request->GetHost(); +} + +#if USE_SSL +static +const char * +httpHdrFmt_user_cert_subject(HttpRequest *request) +{ + ConnStateData *conn = request->clientConnectionManager.get(); + if (conn && conn->clientConnection != NULL) { + SSL *ssl = fd_table[conn->clientConnection->fd].ssl; + return sslGetUserAttribute(ssl, "DN"); + } + return NULL; +} + +static +const char * +httpHdrFmt_user_ca(HttpRequest *request) +{ + const ConnStateData *conn = request->clientConnectionManager.get(); + if (conn && conn->clientConnection != NULL) { + SSL *ssl = fd_table[conn->clientConnection->fd].ssl; + return sslGetCAAttribute(ssl, "DN"); + } + return NULL; +} +#endif + +static +const char * +httpHdrFormat(const std::string &hdrValueWithMacros, HttpRequest *request) +{ + /// header value formatting macro details + struct FmtMacro { + const std::string code; ///< macro name + const char *(*fn)(HttpRequest *); ///< computes macro substitution + }; + static const FmtMacro fmtMacros[] = { +#if USE_AUTH + {"LOGIN", httpHdrFmt_login}, +#endif + {"SRC", httpHdrFmt_src}, + {"DST", httpHdrFmt_dst}, +#if USE_SSL + {"user_cert_subject", httpHdrFmt_user_cert_subject}, + {"user_ca", httpHdrFmt_user_ca}, +#endif + {"", NULL} + }; + + static std::string buf; + buf.clear(); + const char *s = hdrValueWithMacros.c_str(); + while (const char *macroName = strchr(s, '%')) { + buf.append(s, macroName - s); // left of macro + ++macroName; // skip '%' + int macroNameLen = 0; + char const *macroValue = NULL; + for (int i = 0; fmtMacros[i].fn; ++i) { + const FmtMacro &fmt = fmtMacros[i]; + macroNameLen = fmt.code.length(); + if (fmt.code.compare(0, macroNameLen, macroName, macroNameLen) == 0) { + macroValue = fmt.fn(request); + break; + } + macroNameLen = 0; // positive value would indicate a match + } + if (macroNameLen) { // we found a known macro + buf.append(macroValue ? macroValue : ""); + } else if (*macroName == '%') { // a "%%" escape sequence + buf.append("%"); + macroNameLen = 1; + } else { + buf.append("%"); // unknown macro -- a misconfiguration + } + s = macroName + macroNameLen; // skip processed macro + } + buf.append(s); // leftovers if any + return buf.c_str(); +} + +void +httpHdrAdd(HttpHeader *heads, HttpRequest *request, HeaderWithAclList &headersAdd) +{ + ACLFilledChecklist checklist(NULL, request, NULL); + for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) { + if (!hwa->aclList || checklist.fastCheck(hwa->aclList) == ACCESS_ALLOWED) { + const char *fieldValue = hwa->quoted ? httpHdrFormat(hwa->fieldValue, request) : hwa->fieldValue.c_str(); + HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, hwa->fieldName.c_str(), + fieldValue); + heads->addEntry(e); + } + } +} === modified file 'src/HttpHeaderTools.h' --- src/HttpHeaderTools.h 2012-06-29 16:36:58 +0000 +++ src/HttpHeaderTools.h 2012-06-29 17:10:47 +0000 @@ -1,30 +1,36 @@ #ifndef SQUID_HTTPHEADERTOOLS_H #define SQUID_HTTPHEADERTOOLS_H +#if HAVE_LIST +#include +#endif #if HAVE_MAP #include #endif #if HAVE_STRING #include #endif +class HeaderWithAcl; +typedef std::list HeaderWithAclList; + class acl_access; struct _header_mangler { acl_access *access_list; char *replacement; }; typedef struct _header_mangler header_mangler; class StoreEntry; /// A collection of header_mangler objects for a given message kind. class HeaderManglers { public: HeaderManglers(); ~HeaderManglers(); /// returns a header mangler for field e or nil if none was specified const header_mangler *find(const HttpHeaderEntry &e) const; /// returns a mangler for the named header (known or custom) header_mangler *track(const char *name); @@ -38,21 +44,32 @@ void dumpReplacement(StoreEntry *entry, const char *optionName) const; private: /// a name:mangler map; optimize: use unordered map or some such typedef std::map ManglersByName; /// one mangler for each known header header_mangler known[HDR_ENUM_END]; /// one mangler for each custom header ManglersByName custom; /// configured if some mangling ACL applies to all header names header_mangler all; private: /* not implemented */ HeaderManglers(const HeaderManglers &); HeaderManglers &operator =(const HeaderManglers &); }; + +class ACLList; +class HeaderWithAcl { +public: + HeaderWithAcl() : fieldId (HDR_BAD_HDR), quoted(false), aclList(NULL) {} + http_hdr_type fieldId; + std::string fieldName; + std::string fieldValue; + bool quoted; + ACLList *aclList; +}; #endif === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2012-06-29 16:36:58 +0000 +++ src/cache_cf.cc 2012-06-29 16:53:07 +0000 @@ -73,40 +73,44 @@ #include "mgr/Registration.h" #include "Parsing.h" #include "rfc1738.h" #if SQUID_SNMP #include "snmp.h" #endif #include "Store.h" #include "StoreFileSystem.h" #include "SwapDir.h" #include "wordlist.h" #include "ipc/Kids.h" #if HAVE_GLOB_H #include #endif #if HAVE_LIMITS_H #include #endif +#if HAVE_LIST +#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(); @@ -160,40 +164,43 @@ static void parseBytesLine(size_t * bptr, const char *units); #if USE_SSL static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value); #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_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 **); static void parse_b_size_t(size_t * var); static void parse_b_int64_t(int64_t * var); @@ -4297,20 +4304,72 @@ cfg->oldest_service_failure = (m * d); } static void dump_icap_service_failure_limit(StoreEntry *entry, const char *name, const Adaptation::Icap::Config &cfg) { storeAppendPrintf(entry, "%s %d", name, cfg.service_failure_limit); if (cfg.oldest_service_failure > 0) { storeAppendPrintf(entry, " in %d seconds", (int)cfg.oldest_service_failure); } storeAppendPrintf(entry, "\n"); } static void free_icap_service_failure_limit(Adaptation::Icap::Config *cfg) { cfg->oldest_service_failure = 0; cfg->service_failure_limit = 0; } #endif + +static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers) +{ + if (!headers) + return; + + for (HeaderWithAclList::iterator hwa = headers->begin(); hwa != headers->end(); ++hwa) { + storeAppendPrintf(entry, "%s ", hwa->fieldName.c_str()); + storeAppendPrintf(entry, "%s ", hwa->fieldValue.c_str()); + if (hwa->aclList) + dump_acl_list(entry, hwa->aclList); + storeAppendPrintf(entry, "\n"); + } +} + +static void parse_HeaderWithAclList(HeaderWithAclList **headers) +{ + char *fn; + if (!*headers) { + *headers = new HeaderWithAclList; + } + if ((fn = strtok(NULL, w_space)) == NULL) { + self_destruct(); + return; + } + HeaderWithAcl hwa; + hwa.fieldName = fn; + hwa.fieldId = httpHeaderIdByNameDef(fn, strlen(fn)); + if (hwa.fieldId == HDR_BAD_HDR) + hwa.fieldId = HDR_OTHER; + + String buf; + bool wasQuoted; + ConfigParser::ParseQuotedString(&buf, &wasQuoted); + hwa.fieldValue = buf.termedBuf(); + hwa.quoted = wasQuoted; + aclParseAclList(LegacyParser, &hwa.aclList); + (*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); + } + delete *header; + *header = NULL; +} === modified file 'src/cf.data.depend' --- src/cf.data.depend 2012-04-25 05:29:20 +0000 +++ src/cf.data.depend 2012-06-29 16:54:08 +0000 @@ -14,40 +14,41 @@ cachedir cache_replacement_policy cachemgrpasswd ConfigAclTos CpuAffinityMap 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 memcachemode obsolete onoff peer peer_access cache_peer acl PortCfg === modified file 'src/cf.data.pre' --- src/cf.data.pre 2012-06-29 16:36:58 +0000 +++ src/cf.data.pre 2012-06-29 17:08:50 +0000 @@ -4407,44 +4407,44 @@ more configurable. A list of ACLs for each header name allows removal of specific header fields under specific conditions. This option only applies to outgoing HTTP request headers (i.e., headers sent by Squid to the next HTTP hop such as a cache peer or an origin server). The option has no effect during cache hit detection. The equivalent adaptation vectoring point in ICAP terminology is post-cache REQMOD. The option is applied to individual outgoing request header fields. For each request header field F, Squid uses the first qualifying sets of request_header_access rules: 1. Rules with header_name equal to F's name. 2. Rules with header_name 'Other', provided F's name is not on the hard-coded list of commonly used HTTP header names. 3. Rules with header_name 'All'. Within that qualifying rule set, rule ACLs are checked as usual. If ACLs of an "allow" rule match, the header field is allowed to - go through as is. If ACLs of a "deny" rule match, the header is - removed and request_header_replace is then checked to identify - if the removed header has a replacement. If no rules within the - set have matching ACLs, the header field is left as is. + go through as is. If ACLs of a "deny" rule match, the header field + is either removed or replaced, depending on whether there is a + corresponding request_header_replace option. If no rules within + the set have matching ACLs, the header field is left as is. For example, to achieve the same behavior as the old 'http_anonymizer standard' option, you should use: request_header_access From deny all request_header_access Referer deny all request_header_access Server deny all request_header_access User-Agent deny all request_header_access WWW-Authenticate deny all request_header_access Link deny all Or, to reproduce the old 'http_anonymizer paranoid' feature you should use: request_header_access Allow allow all request_header_access Authorization allow all request_header_access WWW-Authenticate allow all request_header_access Proxy-Authorization allow all request_header_access Proxy-Authenticate allow all request_header_access Cache-Control allow all @@ -4562,40 +4562,91 @@ DOC_END NAME: reply_header_replace IFDEF: USE_HTTP_VIOLATIONS TYPE: http_header_replace LOC: Config.reply_header_access DEFAULT: none DOC_START Usage: reply_header_replace header_name message Example: reply_header_replace Server Foo/1.0 This option allows you to change the contents of headers denied with reply_header_access above, by replacing them with some fixed string. This only applies to reply headers, not request headers. By default, headers are removed if denied. DOC_END +NAME: request_header_add +TYPE: HeaderWithAclList +LOC: Config.request_header_add +DEFAULT: none +DOC_START + Usage: request_header_add field-name field-value acl1 [acl2] ... + Example: request_header_add X-Client-CA "CA=%user_ca" all + + This option adds header fields to outgoing HTTP requests (i.e., + request headers sent by Squid to the next HTTP hop such as a + cache peer or an origin server). The option has no effect during + cache hit detection. The equivalent adaptation vectoring point + in ICAP terminology is post-cache REQMOD. + + Field-name is a token specifying an HTTP header name. If a + standard HTTP header name is used, Squid does not check whether + the new header conflicts with any existing headers or violates + HTTP rules. If the request to be modified already contains a + field with the same name, the old field is preserved but the + header field values are not merged. + + Field-value is either a token or a quoted string. If quoted + string format is used, then the surrounding quotes are removed + while escape sequences and %macros are processed. The following + field-value macros are supported: + + %LOGIN: Authenticated user login name or an empty string if + no authentication used. + + %SRC: Client IP address + + %DST: Requested host + + %user_cert_subject: The subject line of the received client + certificate or an empty string if Squid received no client + certificate or received an invalid/malformed certificate. + + %user_ca: The name of the SSL certificate issuer of the + received client certificate or an empty string if Squid + received no client certificate or received an invalid or + malformed certificate. + + %%: A single percent symbol (%). + + One or more Squid ACLs may be specified to restrict header + injection to matching requests. As always in squid.conf, all + ACLs in an option ACL list must be satisfied for the insertion + to happen. The request_header_add option supports fast ACLs + only. +DOC_END + NAME: relaxed_header_parser COMMENT: on|off|warn TYPE: tristate LOC: Config.onoff.relaxed_header_parser DEFAULT: on DOC_START In the default "on" setting Squid accepts certain forms of non-compliant HTTP messages where it is unambiguous what the sending application intended even if the message is not correctly formatted. The messages is then normalized to the correct form when forwarded by Squid. If set to "warn" then a warning will be emitted in cache.log each time such HTTP error is encountered. If set to "off" then such HTTP errors will cause the request or response to be rejected. DOC_END COMMENT_START === modified file 'src/http.cc' --- src/http.cc 2012-06-16 15:03:46 +0000 +++ src/http.cc 2012-06-29 16:57:43 +0000 @@ -71,40 +71,42 @@ #include "StatCounters.h" #include "Store.h" #define SQUID_ENTER_THROWING_CODE() try { #define SQUID_EXIT_THROWING_CODE(status) \ status = true; \ } \ catch (const std::exception &e) { \ debugs (11, 1, "Exception error:" << e.what()); \ status = false; \ } CBDATA_CLASS_INIT(HttpStateData); static const char *const crlf = "\r\n"; static void httpMaybeRemovePublic(StoreEntry *, http_status); static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request, HttpHeader * hdr_out, const int we_do_ranges, const http_state_flags); +//Declared in HttpHeaderTools.cc +void httpHdrAdd(HttpHeader *heads, HttpRequest *request, HeaderWithAclList &headers_add); HttpStateData::HttpStateData(FwdState *theFwdState) : AsyncJob("HttpStateData"), ServerStateData(theFwdState), lastChunk(0), header_bytes_read(0), reply_bytes_read(0), body_bytes_truncated(0), httpChunkDecoder(NULL) { debugs(11,5,HERE << "HttpStateData " << this << " created"); ignoreCacheControl = false; surrogateNoStore = false; serverConnection = fwd->serverConnection(); readBuf = new MemBuf; readBuf->init(16*1024, 256*1024); // reset peer response time stats for %hier.peer_http_request_sent.tv_sec = 0; request->hier.peer_http_request_sent.tv_usec = 0; if (fwd->serverConnection() != NULL) _peer = cbdataReference(fwd->serverConnection()->getPeer()); /* might be NULL */ if (_peer) { @@ -1768,40 +1770,43 @@ if (flags.keepalive) { hdr_out->putStr(HDR_CONNECTION, "keep-alive"); } /* append Front-End-Https */ if (flags.front_end_https) { if (flags.front_end_https == 1 || request->protocol == AnyP::PROTO_HTTPS) hdr_out->putStr(HDR_FRONT_END_HTTPS, "On"); } if (flags.chunked_request) { // Do not just copy the original value so that if the client-side // starts decode other encodings, this code may remain valid. hdr_out->putStr(HDR_TRANSFER_ENCODING, "chunked"); } /* Now mangle the headers. */ if (Config2.onoff.mangle_request_headers) httpHdrMangleList(hdr_out, request, ROR_REQUEST); + if (Config.request_header_add && !Config.request_header_add->empty()) + httpHdrAdd(hdr_out, request, *Config.request_header_add); + strConnection.clean(); } /** * Decides whether a particular header may be cloned from the received Clients request * to our outgoing fetch request. */ void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request, HttpHeader * hdr_out, const int we_do_ranges, const http_state_flags flags) { debugs(11, 5, "httpBuildRequestHeader: " << e->name << ": " << e->value ); switch (e->id) { /** \par RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid should not pass on. */ case HDR_PROXY_AUTHORIZATION: /** \par Proxy-Authorization: * Only pass on proxy authentication to peers for which * authentication forwarding is explicitly enabled === modified file 'src/structs.h' --- src/structs.h 2012-06-29 16:36:58 +0000 +++ src/structs.h 2012-06-29 16:58:28 +0000 @@ -557,40 +557,42 @@ } dns, udp, tcp; } comm_incoming; 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; 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