------------------------------------------------------------ revno: 12213 revision-id: chtsanti@users.sourceforge.net-20120717142506-qpc2wusc5akd08ro parent: chtsanti@users.sourceforge.net-20120717141124-a2vli93uao6jb6xy committer: Christos Tsantilas branch nick: trunk timestamp: Tue 2012-07-17 17:25:06 +0300 message: Add request_header_add option 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. In theory, all of the logformat codes can be used as %macros. However, unlike logging the transaction may not yet have enough information to expand a macro when the new header value is needed. The macro will be expanded into a single dash ('-') in such cases. Not all macros have been tested. * One or more Squid ACLs may be specified to restrict header insertion to matching requests. The request_header_add option supports fast ACLs only. - Add the %ssl::>cert_subject and %ssl::>cert_issuer logformating codes which prints the Subject field and Issuer field of the received client SSL certificate or a dash ('-'). This is a Measurement Factory project. ------------------------------------------------------------ # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: chtsanti@users.sourceforge.net-20120717142506-\ # qpc2wusc5akd08ro # target_branch: http://bzr.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: 833ea5de1ac532519aef573c94d1a550e93c87c5 # timestamp: 2012-07-17 14:52:29 +0000 # source_branch: http://bzr.squid-cache.org/bzr/squid3/trunk/ # base_revision_id: chtsanti@users.sourceforge.net-20120717141124-\ # a2vli93uao6jb6xy # # Begin patch === modified file 'src/AccessLogEntry.h' --- src/AccessLogEntry.h 2012-07-17 14:11:24 +0000 +++ src/AccessLogEntry.h 2012-07-17 14:25:06 +0000 @@ -41,6 +41,9 @@ #include "adaptation/icap/Elements.h" #endif #include "RefCount.h" +#if USE_SSL +#include "ssl/gadgets.h" +#endif /* forward decls */ class HttpReply; @@ -158,6 +161,7 @@ #if USE_SSL const char *ssluser; + Ssl::X509_Pointer sslClientCert; ///< cert received from the client #endif AnyP::PortCfg *port; === modified file 'src/ConfigParser.cc' --- src/ConfigParser.cc 2012-01-20 18:55:04 +0000 +++ src/ConfigParser.cc 2012-07-17 14:25:06 +0000 @@ -116,15 +116,15 @@ } 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, ""); @@ -134,8 +134,11 @@ if (*token != '"') { token = strtok(token, w_space); var->reset(token); + if (wasQuoted) + *wasQuoted = false; return; - } + } else if (wasQuoted) + *wasQuoted = true; char *s = token + 1; /* scan until the end of the quoted string, unescaping " and \ */ === modified file 'src/ConfigParser.h' --- src/ConfigParser.h 2012-01-20 18:55:04 +0000 +++ src/ConfigParser.h 2012-07-17 14:25:06 +0000 @@ -67,8 +67,13 @@ 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(); === modified file 'src/HttpHeaderTools.cc' --- src/HttpHeaderTools.cc 2012-07-06 00:22:24 +0000 +++ src/HttpHeaderTools.cc 2012-07-17 14:25:06 +0000 @@ -35,13 +35,24 @@ #include "squid-old.h" #include "acl/FilledChecklist.h" #include "acl/Gadgets.h" +#include "client_side.h" +#include "client_side_request.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); @@ -616,3 +627,31 @@ return NULL; } +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 = NULL; + MemBuf mb; + if (hwa->quoted) { + if (request->al != NULL) { + mb.init(); + hwa->valueFormat->assemble(mb, request->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); + } + } +} === modified file 'src/HttpHeaderTools.h' --- src/HttpHeaderTools.h 2012-07-06 00:22:24 +0000 +++ src/HttpHeaderTools.h 2012-07-17 14:25:06 +0000 @@ -1,6 +1,11 @@ #ifndef SQUID_HTTPHEADERTOOLS_H #define SQUID_HTTPHEADERTOOLS_H +#include "format/Format.h" + +#if HAVE_LIST +#include +#endif #if HAVE_MAP #include #endif @@ -8,6 +13,9 @@ #include #endif +class HeaderWithAcl; +typedef std::list HeaderWithAclList; + class acl_access; struct _header_mangler { acl_access *access_list; @@ -56,4 +64,29 @@ HeaderManglers(const HeaderManglers &); HeaderManglers &operator =(const HeaderManglers &); }; + +class ACLList; +class HeaderWithAcl +{ +public: + HeaderWithAcl() : aclList(NULL), fieldId (HDR_BAD_HDR), quoted(false) {} + + /// 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; +}; #endif === modified file 'src/HttpRequest.cc' --- src/HttpRequest.cc 2012-02-03 04:07:36 +0000 +++ src/HttpRequest.cc 2012-07-17 14:25:06 +0000 @@ -35,6 +35,7 @@ */ #include "squid-old.h" +#include "AccessLogEntry.h" #include "DnsLookupDetails.h" #include "HttpRequest.h" #include "HttpHdrCc.h" @@ -262,6 +263,8 @@ // main property is which connection the request was received on (if any) clientConnectionManager = aReq->clientConnectionManager; + + al = aReq->al; return true; } === modified file 'src/HttpRequest.h' --- src/HttpRequest.h 2011-12-30 01:24:57 +0000 +++ src/HttpRequest.h 2012-07-17 14:25:06 +0000 @@ -55,6 +55,8 @@ class HttpHdrRange; class DnsLookupDetails; +class AccessLogEntry; +typedef RefCount AccessLogEntryPointer; class HttpRequest: public HttpMsg { @@ -239,6 +241,12 @@ */ CbcPointer clientConnectionManager; + /** + * The AccessLogEntry for the current ClientHttpRequest/Server HttpRequest + * pair, if known; + */ + AccessLogEntryPointer al; + int64_t getRangeOffsetLimit(); /* the result of this function gets cached in rangeOffsetLimit */ private: === modified file 'src/Makefile.am' --- src/Makefile.am 2012-07-05 21:35:27 +0000 +++ src/Makefile.am 2012-07-17 14:25:06 +0000 @@ -1098,6 +1098,7 @@ tests/stub_debug.cc \ tests/stub_errorpage.cc \ tests/stub_HelperChildConfig.cc \ + tests/stub_libformat.cc \ StatCounters.h \ StatCounters.cc \ StatHist.h \ @@ -1195,6 +1196,7 @@ tests/stub_errorpage.cc \ tests/stub_fd.cc \ tests/stub_HttpRequest.cc \ + tests/stub_libformat.cc \ tests/stub_MemObject.cc \ tests/stub_MemStore.cc \ tests/stub_mime.cc \ @@ -1529,6 +1531,7 @@ tests/stub_internal.cc \ tests/stub_ipc.cc \ tests/stub_ipcache.cc \ + tests/stub_libformat.cc \ tests/stub_libicmp.cc \ tests/stub_MemStore.cc \ tests/stub_mime.cc \ @@ -2457,6 +2460,7 @@ tests/stub_helper.cc \ tests/stub_HelperChildConfig.cc \ tests/stub_http.cc \ + tests/stub_libformat.cc \ HttpBody.h \ HttpBody.cc \ tests/stub_HttpReply.cc \ @@ -2592,6 +2596,7 @@ tests/stub_Port.cc \ tests/stub_UdsOp.cc \ tests/stub_internal.cc \ + tests/stub_libformat.cc \ tests/stub_store_rebuild.cc \ tests/stub_store_stats.cc \ fd.cc \ @@ -2795,6 +2800,7 @@ tests/stub_icp.cc \ tests/stub_ipc.cc \ tests/stub_ipcache.cc \ + tests/stub_libformat.cc \ tests/stub_libicmp.cc \ tests/stub_MemStore.cc \ tests/stub_mime.cc \ === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2012-07-05 14:38:28 +0000 +++ src/cache_cf.cc 2012-07-17 14:25:06 +0000 @@ -90,6 +90,10 @@ #include #endif +#if HAVE_LIST +#include +#endif + #if USE_SSL #include "ssl/gadgets.h" #endif @@ -177,6 +181,9 @@ 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); @@ -4314,3 +4321,68 @@ } #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; + if (hwa.quoted) { + Format::Format *nlf = new ::Format::Format("hdrWithAcl"); + if (!nlf->parse(hwa.fieldValue.c_str())) { + self_destruct(); + return; + } + hwa.valueFormat = nlf; + } + 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); + + if (hwa->valueFormat) { + delete hwa->valueFormat; + hwa->valueFormat = NULL; + } + } + 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-07-17 14:25:06 +0000 @@ -31,6 +31,7 @@ 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 === modified file 'src/cf.data.pre' --- src/cf.data.pre 2012-07-05 14:38:28 +0000 +++ src/cf.data.pre 2012-07-17 14:25:06 +0000 @@ -3129,6 +3129,20 @@ service name in curly braces to record response time(s) specific to that service. For example: %{my_service}adapt::sum_trs + If SSL is enabled, the following formating codes become available: + + %ssl::>cert_subject The Subject field of the received client + SSL certificate or a dash ('-') if Squid has + received an invalid/malformed certificate or + no certificate at all. Consider encoding the + logged value because Subject often has spaces. + + %ssl::>cert_issuer The Issuer field of the received client + SSL certificate or a dash ('-') if Squid has + received an invalid/malformed certificate or + no certificate at all. Consider encoding the + logged value because Issuer often has spaces. + The default formats available (which do not need re-defining) are: logformat squid %ts.%03tu %6tr %>a %Ss/%03>Hs %cert_issuer" 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. + + In theory, all of the logformat codes can be used as %macros. + However, unlike logging (which happens at the very end of + transaction lifetime), the transaction may not yet have enough + information to expand a macro when the new header value is needed. + And some information may already be available to Squid but not yet + committed where the macro expansion code can access it (report + such instances!). The macro will be expanded into a single dash + ('-') in such cases. Not all macros have been tested. + + 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 === modified file 'src/client_side.cc' --- src/client_side.cc 2012-07-17 14:11:24 +0000 +++ src/client_side.cc 2012-07-17 14:25:06 +0000 @@ -2533,6 +2533,7 @@ } request->clientConnectionManager = conn; + request->al = http->al; request->flags.accelerated = http->flags.accel; request->flags.sslBumped = conn->switchedToHttps(); === modified file 'src/client_side_request.cc' --- src/client_side_request.cc 2012-07-17 14:11:24 +0000 +++ src/client_side_request.cc 2012-07-17 14:25:06 +0000 @@ -178,6 +178,12 @@ setConn(aConn); al = new AccessLogEntry; al->tcpClient = clientConnection = aConn->clientConnection; +#if USE_SSL + if (aConn->clientConnection != NULL && aConn->clientConnection->isOpen()) { + if (SSL *ssl = fd_table[aConn->clientConnection->fd].ssl) + al->cache.sslClientCert.reset(SSL_get_peer_certificate(ssl)); + } +#endif dlinkAdd(this, &active, &ClientActiveRequests); #if USE_ADAPTATION request_satisfaction_mode = false; === modified file 'src/format/ByteCode.h' --- src/format/ByteCode.h 2011-11-18 07:48:25 +0000 +++ src/format/ByteCode.h 2012-07-17 14:25:06 +0000 @@ -190,6 +190,11 @@ LFT_ICAP_STATUS_CODE, #endif +#if USE_SSL + LFT_SSL_USER_CERT_SUBJECT, + LFT_SSL_USER_CERT_ISSUER, +#endif + LFT_PERCENT /* special string cases for escaped chars */ } ByteCode_t; === modified file 'src/format/Format.cc' --- src/format/Format.cc 2012-07-17 14:11:24 +0000 +++ src/format/Format.cc 2012-07-17 14:25:06 +0000 @@ -3,6 +3,7 @@ #include "comm/Connection.h" #include "err_detail_type.h" #include "errorpage.h" +#include "fde.h" #include "format/Format.h" #include "format/Quoting.h" #include "format/Token.h" @@ -43,9 +44,9 @@ } bool -Format::Format::parse(char *def) +Format::Format::parse(const char *def) { - char *cur, *eos; + const char *cur, *eos; Token *new_lt, *last_lt; enum Quoting quote = LOG_QUOTE_NONE; @@ -290,7 +291,7 @@ } void -Format::Format::assemble(MemBuf &mb, const AccessLogEntryPointer &al, int logSequenceNumber) const +Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const { char tmp[1024]; String sb; @@ -1004,6 +1005,26 @@ dooff = 1; break; +#if USE_SSL + 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_PERCENT: out = "%"; === modified file 'src/format/Format.h' --- src/format/Format.h 2012-07-17 14:11:24 +0000 +++ src/format/Format.h 2012-07-17 14:25:06 +0000 @@ -35,7 +35,7 @@ /* First off, let's tokenize, we'll optimize in a second pass. * A token can either be a %-prefixed sequence (usually a dynamic * token but it can be an escaped sequence), or a string. */ - bool parse(char *def); + bool parse(const char *def); /// assemble the state information into a formatted line. void assemble(MemBuf &mb, const AccessLogEntryPointer &al, int logSequenceNumber) const; === modified file 'src/format/Token.cc' --- src/format/Token.cc 2012-05-12 03:21:00 +0000 +++ src/format/Token.cc 2012-07-17 14:25:06 +0000 @@ -186,6 +186,14 @@ }; #endif +#if USE_SSL +// SSL (ssl::) tokens +static TokenTableEntry TokenTableSsl[] = { + {">cert_subject", LFT_SSL_USER_CERT_SUBJECT}, + {">cert_issuer", LFT_SSL_USER_CERT_ISSUER}, + {NULL, LFT_NONE} +}; +#endif } // namespace Format /// Register all components custom format tokens @@ -202,13 +210,15 @@ TheConfig.registerTokens(String("icap"),::Format::TokenTableIcap); #endif - // TODO tokens for OpenSSL errors in "ssl::" +#if USE_SSL + TheConfig.registerTokens(String("ssl"),::Format::TokenTableSsl); +#endif } /// Scans a token table to see if the next token exists there /// returns a pointer to next unparsed byte and updates type member if found -char * -Format::Token::scanForToken(TokenTableEntry const table[], char *cur) +const char * +Format::Token::scanForToken(TokenTableEntry const table[], const char *cur) { for (TokenTableEntry const *lte = table; lte->configTag != NULL; lte++) { debugs(46, 8, HERE << "compare tokens '" << lte->configTag << "' with '" << cur << "'"); @@ -227,9 +237,9 @@ * def is for sure null-terminated */ int -Format::Token::parse(char *def, Quoting *quoting) +Format::Token::parse(const char *def, Quoting *quoting) { - char *cur = def; + const char *cur = def; int l; @@ -318,11 +328,16 @@ cur++; } - if (xisdigit(*cur)) - widthMin = strtol(cur, &cur, 10); + char *endp; + if (xisdigit(*cur)) { + widthMin = strtol(cur, &endp, 10); + cur = endp; + } - if (*cur == '.' && xisdigit(*(++cur))) - widthMax = strtol(cur, &cur, 10); + if (*cur == '.' && xisdigit(*(++cur))) { + widthMax = strtol(cur, &endp, 10); + cur = endp; + } if (*cur == '{') { char *cp; === modified file 'src/format/Token.h' --- src/format/Token.h 2011-11-18 07:48:25 +0000 +++ src/format/Token.h 2012-07-17 14:25:06 +0000 @@ -48,7 +48,7 @@ * and fills in this item with the token information. * def is for sure null-terminated. */ - int parse(char *def, enum Quoting *quote); + int parse(const char *def, enum Quoting *quote); ByteCode_t type; const char *label; @@ -72,7 +72,7 @@ Token *next; /* todo: move from linked list to array */ private: - char *scanForToken(TokenTableEntry const table[], char *cur); + const char *scanForToken(TokenTableEntry const table[], const char *cur); }; extern const char *log_tags[]; === modified file 'src/http.cc' --- src/http.cc 2012-07-10 23:35:14 +0000 +++ src/http.cc 2012-07-17 14:25:06 +0000 @@ -88,6 +88,8 @@ 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), @@ -1782,6 +1784,9 @@ 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(); } === modified file 'src/structs.h' --- src/structs.h 2012-07-05 14:38:28 +0000 +++ src/structs.h 2012-07-17 14:25:06 +0000 @@ -574,6 +574,8 @@ 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 === modified file 'src/tests/Stub.list' --- src/tests/Stub.list 2012-03-19 04:39:36 +0000 +++ src/tests/Stub.list 2012-07-17 14:25:06 +0000 @@ -30,6 +30,7 @@ tests/stub_ipc_TypedMsgHdr.cc \ tests/stub_ipcache.cc \ tests/stub_libcomm.cc \ + tests/stub_libformat.cc \ tests/stub_libicmp.cc \ tests/stub_main_cc.cc \ tests/stub_mem.cc \ === modified file 'src/tests/stub_HttpRequest.cc' --- src/tests/stub_HttpRequest.cc 2012-01-20 18:55:04 +0000 +++ src/tests/stub_HttpRequest.cc 2012-07-17 14:25:06 +0000 @@ -1,4 +1,5 @@ #include "squid.h" +#include "AccessLogEntry.h" #include "HttpRequest.h" #define STUB_API "HttpRequest.cc"