return 0; // parsed nothing, need more data
}
+size_t
+HttpReply::prefixLen() const
+{
+ return sline.packedLength() + header.len + 2;
+}
+
void
HttpReply::configureContentLengthInterpreter(Http::ContentLengthInterpreter &interpreter)
{
/// \retval 0 implies that more data is needed to parse the response prefix
size_t parseTerminatedPrefix(const char *, size_t);
+ /// approximate size of a "status-line CRLF headers CRLF" sequence
+ /// \sa HttpRequest::prefixLen()
+ size_t prefixLen() const;
+
private:
/** initialize */
void init();
// origin replied 304
if (status == Http::scNotModified) {
- http->updateLoggingTags(LOG_TCP_REFRESH_UNMODIFIED);
- http->request->flags.staleIfHit = false; // old_entry is no longer stale
-
// TODO: The update may not be instantaneous. Should we wait for its
// completion to avoid spawning too much client-disassociated work?
- Store::Root().updateOnNotModified(old_entry, *http->storeEntry());
+ if (!Store::Root().updateOnNotModified(old_entry, *http->storeEntry())) {
+ old_entry->release(true);
+ restoreState();
+ http->updateLoggingTags(LOG_TCP_MISS);
+ processMiss();
+ return;
+ }
+
+ http->updateLoggingTags(LOG_TCP_REFRESH_UNMODIFIED);
+ http->request->flags.staleIfHit = false; // old_entry is no longer stale
// if client sent IMS
if (http->request->flags.ims && !old_entry->modifiedSince(http->request->ims, http->request->imslen)) {
return reason_ ? reason_ : Http::StatusCodeString(status());
}
+size_t
+Http::StatusLine::packedLength() const
+{
+ // Keep in sync with packInto(). TODO: Refactor to avoid code duplication.
+
+ auto packedStatus = status();
+ auto packedReason = reason();
+
+ if (packedStatus == scNone) {
+ packedStatus = scInternalServerError;
+ packedReason = StatusCodeString(packedStatus);
+ }
+
+ // "ICY %3d %s\r\n"
+ if (version.protocol == AnyP::PROTO_ICY) {
+ return
+ + 3 // ICY
+ + 1 // SP
+ + 3 // %3d (packedStatus)
+ + 1 // SP
+ + strlen(packedReason) // %s
+ + 2; // CRLF
+ }
+
+ // "HTTP/%d.%d %3d %s\r\n"
+ return
+ + 4 // HTTP
+ + 1 // "/"
+ + 3 // %d.%d (version.major and version.minor)
+ + 1 // SP
+ + 3 // %3d (packedStatus)
+ + 1 // SP
+ + strlen(packedReason) // %s
+ + 2; // CRLF
+}
+
void
Http::StatusLine::packInto(Packable * p) const
{
+ // Keep in sync with packedLength().
+
assert(p);
auto packedStatus = status();
/// retrieve the reason string for this status line
const char *reason() const;
+ /// expected size of packInto() output
+ size_t packedLength() const;
+
/// pack fields into a Packable object
void packInto(Packable *) const;
assert(fetch->old_entry->mem_obj->request);
- Store::Root().updateOnNotModified(fetch->old_entry, *fetch->entry);
+ if (!Store::Root().updateOnNotModified(fetch->old_entry, *fetch->entry)) {
+ peerDigestFetchAbort(fetch, buf, "header update failure after a 304 response");
+ return -1;
+ }
/* get rid of 304 reply */
storeUnregister(fetch->sc, fetch->entry, fetch);
#include "mgr/StoreIoAction.h"
#include "repl_modules.h"
#include "RequestFlags.h"
+#include "sbuf/Stream.h"
#include "SquidConfig.h"
#include "StatCounters.h"
#include "stmem.h"
// update reply before calling timestampsSet() below
const auto &oldReply = mem_obj->freshestReply();
const auto updatedReply = oldReply.recreateOnNotModified(e304.mem_obj->baseReply());
- if (updatedReply) // HTTP 304 brought in new information
+ if (updatedReply) { // HTTP 304 brought in new information
+ if (updatedReply->prefixLen() > Config.maxReplyHeaderSize) {
+ throw TextException(ToSBuf("cannot update the cached response because its updated ",
+ updatedReply->prefixLen(), "-byte header would exceed ",
+ Config.maxReplyHeaderSize, "-byte reply_header_max_size"), Here());
+ }
mem_obj->updateReply(*updatedReply);
+ }
// else continue to use the previous update, if any
if (!timestampsSet() && !updatedReply)
// and keep the entry in store_table for its on-disk data
}
-void
+bool
Store::Controller::updateOnNotModified(StoreEntry *old, StoreEntry &e304)
{
Must(old);
// sake, it is best to detect and skip such repeated update calls.
if (e304.mem_obj->appliedUpdates) {
debugs(20, 5, "ignored repeated update of " << *old << " with " << e304);
- return;
+ return true;
}
e304.mem_obj->appliedUpdates = true;
- if (!old->updateOnNotModified(e304)) {
- debugs(20, 5, "updated nothing in " << *old << " with " << e304);
- return;
+ try {
+ if (!old->updateOnNotModified(e304)) {
+ debugs(20, 5, "updated nothing in " << *old << " with " << e304);
+ return true;
+ }
+ } catch (...) {
+ debugs(20, DBG_IMPORTANT, "ERROR: Failed to update a cached response: " << CurrentException);
+ return false;
}
if (sharedMemStore && old->mem_status == IN_MEMORY && !EBIT_TEST(old->flags, ENTRY_SPECIAL))
if (old->swap_dirn > -1)
swapDir->updateHeaders(old);
+
+ return true;
}
bool
void memoryOut(StoreEntry &, const bool preserveSwappable);
/// using a 304 response, update the old entry (metadata and reply headers)
- void updateOnNotModified(StoreEntry *old, StoreEntry &e304);
+ /// \returns whether the old entry can be used (and is considered fresh)
+ bool updateOnNotModified(StoreEntry *old, StoreEntry &e304);
/// tries to make the entry available for collapsing future requests
bool allowCollapsing(StoreEntry *, const RequestFlags &, const HttpRequestMethod &);
int HttpReply::httpMsgParseError() STUB_RETVAL(0)
bool HttpReply::expectingBody(const HttpRequestMethod&, int64_t&) const STUB_RETVAL(false)
size_t HttpReply::parseTerminatedPrefix(const char *, size_t) STUB_RETVAL(0)
+size_t HttpReply::prefixLen() const STUB_RETVAL(0)
bool HttpReply::parseFirstLine(const char *, const char *) STUB_RETVAL(false)
void HttpReply::hdrCacheInit() STUB
HttpReply * HttpReply::clone() const STUB_RETVAL(nullptr)
void StatusLine::clean() STUB
void StatusLine::set(const AnyP::ProtocolVersion &, Http::StatusCode, const char *) STUB
const char *StatusLine::reason() const STUB_RETVAL(nullptr)
+size_t StatusLine::packedLength() const STUB_RETVAL(0)
void StatusLine::packInto(Packable *) const STUB
bool StatusLine::parse(const String &, const char *, const char *) STUB_RETVAL(false)
}
void Controller::handleIdleEntry(StoreEntry &) STUB
void Controller::freeMemorySpace(const int) STUB
void Controller::memoryOut(StoreEntry &, const bool) STUB
-void Controller::updateOnNotModified(StoreEntry *, StoreEntry &) STUB
+bool Controller::updateOnNotModified(StoreEntry *, StoreEntry &) STUB
bool Controller::allowCollapsing(StoreEntry *, const RequestFlags &, const HttpRequestMethod &) STUB_RETVAL(false)
void Controller::addReading(StoreEntry *, const cache_key *) STUB
void Controller::addWriting(StoreEntry *, const cache_key *) STUB