MemStore.cc(506) copyFromShm: entry 1284 copied ...
FATAL: assertion failed: MemObject.cc:123: "!updatedReply_"
The code loading a response from the shared memory cache incorrectly
assumed that the being-loaded response could not have been updated by an
HTTP 304 (Not Modified) reply and called adjustableBaseReply() that is
banned for updated responses. The goal of that call was to determine
whether the cached response header has been parsed. That determination
can be made without using a method that is banned for updated responses.
StoreClient and clientReplyContext had very similar checks that used the
right/safe approach because their current code did not need an immediate
access to an "adjustable" response. We have updated all external
psParsed checks to reduce chances that this bug resurfaces.
" from " << extra.page << '+' << prefixSize);
// parse headers if needed; they might span multiple slices!
- auto &reply = e.mem().adjustableBaseReply();
- if (reply.pstate != Http::Message::psParsed) {
+ if (!e.hasParsedReplyHeader()) {
httpHeaderParsingBuffer.append(sliceBuf.data, sliceBuf.length);
+ auto &reply = e.mem().adjustableBaseReply();
if (reply.parseTerminatedPrefix(httpHeaderParsingBuffer.c_str(), httpHeaderParsingBuffer.length()))
httpHeaderParsingBuffer = SBuf(); // we do not need these bytes anymore
}
debugs(20, 5, "mem-loaded all " << e.mem_obj->endOffset() << '/' <<
anchor.basics.swap_file_sz << " bytes of " << e);
- if (e.mem().adjustableBaseReply().pstate != Http::Message::psParsed)
+ if (!e.hasParsedReplyHeader())
throw TextException(ToSBuf("truncated mem-cached headers; accumulated: ", httpHeaderParsingBuffer.length()), Here());
// from StoreEntry::complete()
/// \see MemObject::freshestReply()
const HttpReply *hasFreshestReply() const { return mem_obj ? &mem_obj->freshestReply() : nullptr; }
+ /// whether this entry has access to [deserialized] [HTTP] response headers
+ bool hasParsedReplyHeader() const;
+
void write(StoreIOBuffer);
/** Check if the Store entry is empty
assert(mem != nullptr);
assert(http->request != nullptr);
- if (mem->baseReply().pstate != Http::Message::psParsed)
+ if (!http->storeEntry()->hasParsedReplyHeader())
/* haven't found end of headers yet */
return 0;
return mem_obj->mostBytesWanted(aRange.end, ignoreDelayPools);
}
+bool
+StoreEntry::hasParsedReplyHeader() const
+{
+ if (mem_obj) {
+ const auto &reply = mem_obj->baseReply();
+ if (reply.pstate == Http::Message::psParsed) {
+ debugs(20, 7, reply.hdr_sz);
+ return true;
+ }
+ }
+ return false;
+}
+
bool
StoreEntry::checkDeferRead(int) const
{
if (!answeredOnce()) {
// All on-disk responses have HTTP headers. First disk body read(s)
// include HTTP headers that we must parse (if needed) and skip.
- const auto haveHttpHeaders = entry->mem_obj->baseReply().pstate == Http::Message::psParsed;
+ const auto haveHttpHeaders = entry->hasParsedReplyHeader();
if (!haveHttpHeaders && !parseHttpHeadersFromDisk())
return;
skipHttpHeadersFromDisk();