Compliance: delete Warnings that have warning-date different from Date. Added HttpReply::removeStaleWarnings() method that iterates over all Warning headers and removes stale warning-values. If a reply has no valid Date header, all warning-values with warning-date are removed. Also, we remove warning-value if we failed to parse warning-date. removeStaleWarnings() is called from processReplyHeader(), after reply headers are parsed. Co-Advisor test cases: test_case/rfc2616/date-accept-fmt-warn-asctime-rfc1123 test_case/rfc2616/date-accept-fmt-warn-asctime-rfc850 test_case/rfc2616/date-accept-fmt-warn-rfc1123-asctime test_case/rfc2616/date-accept-fmt-warn-rfc1123-rfc850 test_case/rfc2616/date-accept-fmt-warn-rfc850-asctime test_case/rfc2616/date-accept-fmt-warn-rfc850-rfc1123 === modified file 'src/HttpReply.cc' --- src/HttpReply.cc 2010-08-24 04:18:51 +0000 +++ src/HttpReply.cc 2010-08-31 22:45:11 +0000 @@ -613,20 +613,105 @@ HttpReply::clone() const rep->hdrCacheInit(); rep->hdr_sz = hdr_sz; rep->http_ver = http_ver; rep->pstate = pstate; rep->body_pipe = body_pipe; rep->protocol = protocol; // keep_alive is handled in hdrCacheInit() return rep; } bool HttpReply::inheritProperties(const HttpMsg *aMsg) { const HttpReply *aRep = dynamic_cast(aMsg); if (!aRep) return false; keep_alive = aRep->keep_alive; return true; } + +void HttpReply::removeStaleWarnings() +{ + if (header.has(HDR_WARNING)) { + HttpHeaderPos headerPos = HttpHeaderInitPos; + bool deletedAll = true; + while (HttpHeaderEntry *const e = header.getEntry(&headerPos)) { + if (e->id == HDR_WARNING) { + if (removeStaleWarningValues(*e)) { + int ignore = 0; + header.delAt(headerPos, ignore); + } else if (deletedAll) + deletedAll = false; + } + } + if (deletedAll) + CBIT_CLR(header.mask, HDR_WARNING); + } +} + +/** + * Remove warning-values with warn-date different from Date value from + * a single header entry. Returns true if all warning-values are + * invalid and the header entry should be removed. + */ +bool HttpReply::removeStaleWarningValues(HttpHeaderEntry &e) +{ + String newValue; + bool skipped = false; + const char *item = 0; + int len = 0; + const char *pos = 0; + const char *prevItemEnd = 0; + while (strListGetItem(&e.value, ',', &item, &len, &pos)) { + bool keep = true; + // Does warning-value have warn-date (which contains quoted date)? + // We scan backwards, looking for two quoted strings. + // warning-value = warn-code SP warn-agent SP warn-text [SP warn-date] + const char *p = item + len - 1; + + while (p >= item && xisspace(*p)) --p; // skip whitespace + + // warning-value MUST end with quote + if (p >= item && *p == '"') { + const char *const warnDateEnd = p; + --p; + while (p >= item && *p != '"') --p; // find the next quote + + const char *warnDate = p + 1; + --p; + while (p >= item && xisspace(*p)) --p; // skip whitespace + + if (p >= item && *p == '"' && warnDate - p > 2) { + // found warn-text + // const_cast is safe because e.value is not const + *const_cast(warnDateEnd) = '\0'; + const time_t time = parse_rfc1123(warnDate); + *const_cast(warnDateEnd) = '"'; + keep = (time > 0 && time == date); // keep valid and matching date + } + } + + if (!keep) { + if (!skipped) { + // skipping for the first time, copy all previous warning-values + if (prevItemEnd) + newValue = e.value.substr(0, prevItemEnd - e.value.termedBuf()); + skipped = true; + } + } else if (skipped) { + newValue.append(", "); + newValue.append(item, len); + } + + prevItemEnd = item + len; + } + + if (skipped) { + if (newValue.undefined()) + return true; + e.value = newValue; + } + + return false; +} === modified file 'src/HttpReply.h' --- src/HttpReply.h 2010-02-06 06:32:11 +0000 +++ src/HttpReply.h 2010-08-31 22:27:30 +0000 @@ -121,55 +121,60 @@ public: /** Checks whether received body exceeds known maximum size. * Requires a prior call to calcMaxBodySize(). */ bool receivedBodyTooLarge(HttpRequest&, int64_t receivedBodySize); /** Checks whether expected body exceeds known maximum size. * Requires a prior call to calcMaxBodySize(). */ bool expectedBodyTooLarge(HttpRequest& request); int validatorsMatch (HttpReply const *other) const; void packHeadersInto(Packer * p) const; /** Clone this reply. * Could be done as a copy-contructor but we do not want to accidently copy a HttpReply.. */ HttpReply *clone() const; + /// Remove Warnings with warn-date different from Date value + void removeStaleWarnings(); + private: /** initialize */ void init(); void clean(); void hdrCacheClean(); void packInto(Packer * p); /* ez-routines */ /** \return construct 304 reply and pack it into a MemBuf */ MemBuf *packed304Reply(); /* header manipulation */ time_t hdrExpirationTime(); /** Calculates and stores maximum body size if needed. * Used by receivedBodyTooLarge() and expectedBodyTooLarge(). */ void calcMaxBodySize(HttpRequest& request); + bool removeStaleWarningValues(HttpHeaderEntry &e); + mutable int64_t bodySizeMax; /**< cached result of calcMaxBodySize */ protected: virtual void packFirstLineInto(Packer * p, bool) const; virtual bool parseFirstLine(const char *start, const char *end); virtual void hdrCacheInit(); }; MEMPROXY_CLASS_INLINE(HttpReply); #endif /* SQUID_HTTPREPLY_H */ === modified file 'src/http.cc' --- src/http.cc 2010-08-24 04:18:51 +0000 +++ src/http.cc 2010-08-31 22:27:41 +0000 @@ -714,40 +714,42 @@ HttpStateData::processReplyHeader() // TODO: pass to the client anyway? } #endif delete newrep; debugs(11, 2, HERE << "1xx headers consume " << header_bytes_read << " bytes header."); header_bytes_read = 0; if (reply_bytes_read > 0) debugs(11, 2, HERE << "1xx headers consume " << reply_bytes_read << " bytes reply."); reply_bytes_read = 0; ctx_exit(ctx); processReplyHeader(); return; } flags.chunked = 0; if (newrep->sline.protocol == PROTO_HTTP && newrep->header.chunked()) { flags.chunked = 1; httpChunkDecoder = new ChunkedCodingParser; } + newrep->removeStaleWarnings(); + if (!peerSupportsConnectionPinning()) orig_request->flags.connection_auth_disabled = 1; HttpReply *vrep = setVirginReply(newrep); flags.headers_parsed = 1; keepaliveAccounting(vrep); checkDateSkew(vrep); processSurrogateControl (vrep); /** \todo IF the reply is a 1.0 reply, AND it has a Connection: Header * Parse the header and remove all referenced headers */ orig_request->hier.peer_reply_status = newrep->sline.status; ctx_exit(ctx); }