}
virtual bool isAccepting() const;
virtual size_t bytesWanted(Range<size_t> const aRange, bool ignoreDelayPool = false) const;
+ /// flags [truncated or too big] entry with ENTRY_BAD_LENGTH and releases it
+ void lengthWentBad(const char *reason);
virtual void complete();
virtual store_client_t storeClientType() const;
virtual char const *getSerialisedMetaData();
void
ClientSocketContext::noteSentBodyBytes(size_t bytes)
{
+ debugs(33, 7, bytes << " body bytes");
+
http->out.offset += bytes;
if (!http->request->range)
return STREAM_FAILED;
}
+ if (EBIT_TEST(http->storeEntry()->flags, ENTRY_BAD_LENGTH)) {
+ debugs(88, 5, "clientReplyStatus: truncated response body");
+ return STREAM_UNPLANNED_COMPLETE;
+ }
+
if (!done) {
debugs(88, 5, "clientReplyStatus: closing, !done, but read 0 bytes");
return STREAM_FAILED;
// premature end of the adapted response body
void Client::handleAdaptedBodyProducerAborted()
{
+ if (abortOnBadEntry("entry went bad while waiting for the now-aborted adapted body"))
+ return;
+
+ Must(adaptedBodySource != nullptr);
+ if (!adaptedBodySource->exhausted()) {
+ debugs(11,5, "waiting to consume the remainder of the aborted adapted body");
+ return; // resumeBodyStorage() should eventually consume the rest
+ }
+
stopConsumingFrom(adaptedBodySource);
- handleAdaptationAborted();
+
+ if (handledEarlyAdaptationAbort())
+ return;
+
+ entry->lengthWentBad("body adaptation aborted");
+ handleAdaptationCompleted(); // the user should get a truncated response
}
// common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
return;
// TODO: bypass if possible
+ if (!handledEarlyAdaptationAbort())
+ abortTransaction("adaptation failure with a filled entry");
+}
+/// If the store entry is still empty, fully handles adaptation abort, returning
+/// true. Otherwise just updates the request error detail and returns false.
+bool
+Client::handledEarlyAdaptationAbort()
+{
if (entry->isEmpty()) {
- debugs(11,9, HERE << "creating ICAP error entry after ICAP failure");
+ debugs(11,8, "adaptation failure with an empty entry: " << *entry);
ErrorState *err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, request);
err->detailError(ERR_DETAIL_ICAP_RESPMOD_EARLY);
fwd->fail(err);
fwd->dontRetry(true);
- } else if (request) { // update logged info directly
- request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_ICAP_RESPMOD_LATE);
+ abortTransaction("adaptation failure with an empty entry");
+ return true; // handled
}
- abortTransaction("ICAP failure");
+ if (request) // update logged info directly
+ request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_ICAP_RESPMOD_LATE);
+
+ return false; // the caller must handle
}
// adaptation service wants us to deny HTTP client access to this response
void handleAdaptationCompleted();
void handleAdaptationBlocked(const Adaptation::Answer &answer);
void handleAdaptationAborted(bool bypassable = false);
+ bool handledEarlyAdaptationAbort();
/// called by StoreEntry when it has more buffer space available
void resumeBodyStorage();
#include "profiler/Profiler.h"
#include "servers/Http1Server.h"
#include "SquidConfig.h"
+#include "Store.h"
CBDATA_NAMESPACED_CLASS_INIT(Http1, Server);
// the last-chunk if there was no error, ignoring responseFinishedOrFailed.
const bool mustSendLastChunk = http->request->flags.chunkedReply &&
!http->request->flags.streamError &&
+ !EBIT_TEST(http->storeEntry()->flags, ENTRY_BAD_LENGTH) &&
!context->startOfOutput();
const bool responseFinishedOrFailed = !rep &&
!receivedData.data &&
store_check_cachable_hist.yes.Default);
}
+void
+StoreEntry::lengthWentBad(const char *reason)
+{
+ debugs(20, 3, "because " << reason << ": " << *this);
+ EBIT_SET(flags, ENTRY_BAD_LENGTH);
+ releaseRequest();
+}
+
void
StoreEntry::complete()
{
assert(mem_status == NOT_IN_MEMORY);
- if (!validLength()) {
- EBIT_SET(flags, ENTRY_BAD_LENGTH);
- releaseRequest();
- }
+ if (!EBIT_TEST(flags, ENTRY_BAD_LENGTH) && !validLength())
+ lengthWentBad("!validLength() in complete()");
#if USE_CACHE_DIGESTS
if (mem_obj->request)