{
public:
LogTags(LogTags_ot t) : oldType(t) {assert(oldType < LOG_TYPE_MAX);}
+ // XXX: this operator does not reset flags
+ // TODO: either replace with a category-only setter or remove
LogTags &operator =(const LogTags_ot &t) {assert(t < LOG_TYPE_MAX); oldType = t; return *this;}
/// compute the status access.log field
/// determine if the log tag code indicates a cache HIT
bool isTcpHit() const;
- /// error states terminating the transaction
- struct Errors {
- Errors() : timedout(false), aborted(false) {}
+ /// Things that may happen to a transaction while it is being
+ /// processed according to its LOG_* category. Logged as _SUFFIX(es).
+ /// Unlike LOG_* categories, these flags may not be mutually exclusive.
+ class Errors {
+ public:
+ Errors() : ignored(false), timedout(false), aborted(false) {}
- bool timedout; ///< tag: TIMEDOUT - terminated due to a lifetime or I/O timeout
- bool aborted; ///< tag: ABORTED - other abnormal termination (e.g., I/O error)
+ bool ignored; ///< _IGNORED: the response was not used for anything
+ bool timedout; ///< _TIMEDOUT: terminated due to a lifetime or I/O timeout
+ bool aborted; ///< _ABORTED: other abnormal termination (e.g., I/O error)
} err;
private:
if (deleting)
return;
- debugs(88, 3, "handleIMSReply: " << http->storeEntry()->url() << ", " << (long unsigned) result.length << " bytes" );
+ debugs(88, 3, http->storeEntry()->url() << ", " << (long unsigned) result.length << " bytes");
if (http->storeEntry() == NULL)
return;
// request to origin was aborted
if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) {
- debugs(88, 3, "handleIMSReply: request to origin aborted '" << http->storeEntry()->url() << "', sending old entry to client" );
+ debugs(88, 3, "request to origin aborted '" << http->storeEntry()->url() << "', sending old entry to client");
http->logType = LOG_TCP_REFRESH_FAIL_OLD;
sendClientOldEntry();
}
if (http->request->flags.ims && !old_entry->modifiedSince(http->request->ims, http->request->imslen)) {
// forward the 304 from origin
- debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and forwarding 304 to client");
+ debugs(88, 3, "origin replied 304, revalidating existing entry and forwarding 304 to client");
sendClientUpstreamResponse();
} else {
// send existing entry, it's still valid
- debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and sending " <<
+ debugs(88, 3, "origin replied 304, revalidating existing entry and sending " <<
old_rep->sline.status() << " to client");
sendClientOldEntry();
}
// origin replied with a non-error code
else if (status > Http::scNone && status < Http::scInternalServerError) {
- // forward response from origin
- http->logType = LOG_TCP_REFRESH_MODIFIED;
- debugs(88, 3, "handleIMSReply: origin replied " << status << ", replacing existing entry and forwarding to client");
+ const HttpReply *new_rep = http->storeEntry()->getReply();
+ // RFC 7234 section 4: a cache MUST use the most recent response
+ // (as determined by the Date header field)
+ if (new_rep->olderThan(old_rep)) {
+ http->logType.err.ignored = true;
+ debugs(88, 3, "origin replied " << status <<
+ " but with an older date header, sending old entry (" <<
+ old_rep->sline.status() << ") to client");
+ sendClientOldEntry();
+ } else {
+ http->logType = LOG_TCP_REFRESH_MODIFIED;
+ debugs(88, 3, "origin replied " << status <<
+ ", replacing existing entry and forwarding to client");
- if (collapsedRevalidation)
- http->storeEntry()->clearPublicKeyScope();
+ if (collapsedRevalidation)
+ http->storeEntry()->clearPublicKeyScope();
- sendClientUpstreamResponse();
+ sendClientUpstreamResponse();
+ }
}
// origin replied with an error
else if (http->request->flags.failOnValidationError) {
http->logType = LOG_TCP_REFRESH_FAIL_ERR;
- debugs(88, 3, "handleIMSReply: origin replied with error " << status <<
+ debugs(88, 3, "origin replied with error " << status <<
", forwarding to client due to fail_on_validation_err");
sendClientUpstreamResponse();
} else {
// ignore and let client have old entry
http->logType = LOG_TCP_REFRESH_FAIL_OLD;
- debugs(88, 3, "handleIMSReply: origin replied with error " <<
+ debugs(88, 3, "origin replied with error " <<
status << ", sending old entry (" << old_rep->sline.status() << ") to client");
sendClientOldEntry();
}
lastChunk(0),
httpChunkDecoder(NULL),
payloadSeen(0),
- payloadTruncated(0)
+ payloadTruncated(0),
+ sawDateGoBack(false)
{
debugs(11,5,HERE << "HttpStateData " << this << " created");
ignoreCacheControl = false;
mustStop("HttpStateData::httpTimeout");
}
+static StoreEntry *
+findPreviouslyCachedEntry(StoreEntry *newEntry) {
+ assert(newEntry->mem_obj);
+ return newEntry->mem_obj->request ?
+ storeGetPublicByRequest(newEntry->mem_obj->request) :
+ storeGetPublic(newEntry->mem_obj->storeId(), newEntry->mem_obj->method);
+}
+
/// Remove an existing public store entry if the incoming response (to be
/// stored in a currently private entry) is going to invalidate it.
static void
{
int remove = 0;
int forbidden = 0;
- StoreEntry *pe;
// If the incoming response already goes into a public entry, then there is
// nothing to remove. This protects ready-for-collapsing entries as well.
if (!remove && !forbidden)
return;
- assert(e->mem_obj);
-
- if (e->mem_obj->request)
- pe = storeGetPublicByRequest(e->mem_obj->request);
- else
- pe = storeGetPublic(e->mem_obj->storeId(), e->mem_obj->method);
+ StoreEntry *pe = findPreviouslyCachedEntry(e);
if (pe != NULL) {
assert(e != pe);
return 0;
}
+ // RFC 7234 section 4: a cache MUST use the most recent response
+ // (as determined by the Date header field)
+ if (sawDateGoBack) {
+ debugs(22, 3, "NO because " << *entry << " has an older date header.");
+ return 0;
+ }
+
// Check for Surrogate/1.0 protocol conditions
// NP: reverse-proxy traffic our parent server has instructed us never to cache
if (surrogateNoStore) {
/* Check if object is cacheable or not based on reply code */
debugs(11, 3, "HTTP CODE: " << rep->sline.status());
- if (neighbors_do_private_keys)
+ if (const StoreEntry *oldEntry = findPreviouslyCachedEntry(entry))
+ sawDateGoBack = rep->olderThan(oldEntry->getReply());
+
+ if (neighbors_do_private_keys && !sawDateGoBack)
httpMaybeRemovePublic(entry, rep->sline.status());
bool varyFailure = false;