------------------------------------------------------------ revno: 12418 revision-id: squid3@treenet.co.nz-20121129103722-026c5061p2pxzkge parent: squid3@treenet.co.nz-20121129103627-ljhpd8ewbjc7nirk author: Alex Rousskov committer: Amos Jeffries branch nick: 3.3 timestamp: Thu 2012-11-29 03:37:22 -0700 message: Make it possible to match empty header field values using req_header and rep_header. Warning: Some req_header and rep_header ACLs that were [accidentally] not matching empty headers (e.g., "^$" or ".*") will now start matching them. A new HttpHeader::getByNameIfPresent() method is added to be able to detect presence of empty header fields while ACLHTTPHeaderData::match() is adjusted to convert undefined String values into empty c-strings ("") for ACLRegexData::match() to work. Prior to these changes, when trying to match an empty header value with a regex like "^$", ACLHTTPHeaderData::match() would return false because: * HttpHeader::getStrOrList() and getByName() return an undefined String. * String::termedBuf() returns NULL for undefined Strings; and * ACLRegexData::match() always fails on NULL c-strings. ------------------------------------------------------------ # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: squid3@treenet.co.nz-20121129103722-026c5061p2pxzkge # target_branch: http://bzr.squid-cache.org/bzr/squid3/3.3 # testament_sha1: ea725c4dd319d85a17c9e6c0c2e25ef560c53e0b # timestamp: 2012-11-29 10:40:23 +0000 # source_branch: http://bzr.squid-cache.org/bzr/squid3/3.3 # base_revision_id: squid3@treenet.co.nz-20121129103627-\ # ljhpd8ewbjc7nirk # # Begin patch === modified file 'src/HttpHeader.cc' --- src/HttpHeader.cc 2012-09-20 16:36:22 +0000 +++ src/HttpHeader.cc 2012-11-29 10:37:22 +0000 @@ -1020,11 +1020,20 @@ } /* - * Returns the value of the specified header. + * Returns the value of the specified header and/or an undefined String. */ String HttpHeader::getByName(const char *name) const { + String result; + // ignore presence: return undefined string if an empty header is present + (void)getByNameIfPresent(name, result); + return result; +} + +bool +HttpHeader::getByNameIfPresent(const char *name, String &result) const +{ http_hdr_type id; HttpHeaderPos pos = HttpHeaderInitPos; HttpHeaderEntry *e; @@ -1034,19 +1043,23 @@ /* First try the quick path */ id = httpHeaderIdByNameDef(name, strlen(name)); - if (id != -1) - return getStrOrList(id); - - String result; + if (id != -1) { + if (!has(id)) + return false; + result = getStrOrList(id); + return true; + } /* Sorry, an unknown header name. Do linear search */ + bool found = false; while ((e = getEntry(&pos))) { if (e->id == HDR_OTHER && e->name.caseCmp(name) == 0) { + found = true; strListAdd(&result, e->value.termedBuf(), ','); } } - return result; + return found; } /* === modified file 'src/HttpHeader.h' --- src/HttpHeader.h 2012-09-25 16:38:36 +0000 +++ src/HttpHeader.h 2012-11-29 10:37:22 +0000 @@ -246,6 +246,8 @@ bool getList(http_hdr_type id, String *s) const; String getStrOrList(http_hdr_type id) const; String getByName(const char *name) const; + /// sets value and returns true iff a [possibly empty] named field is there + bool getByNameIfPresent(const char *name, String &value) const; String getByNameListMember(const char *name, const char *member, const char separator) const; String getListMember(http_hdr_type id, const char *member, const char separator) const; int has(http_hdr_type id) const; === modified file 'src/acl/HttpHeaderData.cc' --- src/acl/HttpHeaderData.cc 2012-08-31 16:57:39 +0000 +++ src/acl/HttpHeaderData.cc 2012-11-29 10:37:22 +0000 @@ -65,9 +65,23 @@ debugs(28, 3, "aclHeaderData::match: checking '" << hdrName << "'"); - String value = hdrId != HDR_BAD_HDR ? hdr->getStrOrList(hdrId) : hdr->getByName(hdrName.termedBuf()); + String value; + if (hdrId != HDR_BAD_HDR) { + if (!hdr->has(hdrId)) + return false; + value = hdr->getStrOrList(hdrId); + } else { + if (!hdr->getByNameIfPresent(hdrName.termedBuf(), value)) + return false; + } - return regex_rule->match(value.termedBuf()); + // By now, we know the header is present, but: + // HttpHeader::get*() return an undefined String for empty header values; + // String::termedBuf() returns NULL for undefined Strings; and + // ACLRegexData::match() always fails on NULL strings. + // This makes it possible to detect an empty header value using regex: + const char *cvalue = value.defined() ? value.termedBuf() : ""; + return regex_rule->match(cvalue); } wordlist *