A long series of commits sprinkled Squid code with special treatment of
virgin response buffer space equal to one or two bytes:
* 2005 commit
2afaba07 adds a 2-byte hack to HttpStateData. The hack
avoids stalling transactions by never asking
StoreEntry::delayAwareRead() to read 1 byte.
* 2006 commit
253cacc adds the same 2-byte hack to Ftp::Client.
* 2007 commit
478cfe99 adds a 2-byte hack to
Icap::ModXact::virginConsume() so that ICAP transactions do not stall
when HttpStateData hack prevents new virgin bytes from being read into
a BodyPipe buffer that has just one or two space bytes left. The ICAP
hack was probably one-byte more aggressive than necessary...
* 2011 commit
0ad2b63b adds +1 byte hacks to ServerStateData and
ClientHttpRequest to work around the same StoreEntry::delayAwareRead()
problem leading to excessive buffering with slow clients.
* A followup 2012 commit
4dc2b072 adjusts StoreEntry::delayAwareRead()
to allow 1-byte reads, making _all_ of the above hacks unnecessary.
However, that commit only removed +1 byte hacks added in 2011, missing
HttpStateData and ICAP hacks added in 2005, 2006 and 2007, probably
because that 2012 work focused on fixing the bug introduced by 2011
commit (that was triggered by slow client-Squid communication).
We now remove the remaining known hacks that worked around
StoreEntry::delayAwareRead() inability to read 1 byte (i.e. the first
three hacks on the above list).
These changes also happen to fix transaction stalls with read_ahead_gap
set to 1 and probably some other rare use cases: Transactions could
stall because inBuf.spaceSize() may be 1 for an emptied inBuf, leading
to maybeMakeSpaceAvailable() returning zero due to `read_size < 2` being
true. That zero result prevents HttpStateData from making progress by
reading new virgin response bytes. Mistreatment of "emptied inBuf" cases
was exposed by 2023 commit
50c5af88 changes. More work is needed to
address other, more prominent "emptied inBuf" cases.
BodyPipe &bp = *virgin.body_pipe;
const bool wantToPostpone = isRepeatable || canStartBypass || protectGroupBypass;
- // Why > 2? HttpState does not use the last bytes in the buffer
- // because Client::delayRead() is arguably broken. See
- // HttpStateData::maybeReadVirginBody for more details.
- if (wantToPostpone && bp.buf().spaceSize() > 2) {
+ if (wantToPostpone && bp.buf().spaceSize() > 0) {
// Postponing may increase memory footprint and slow the HTTP side
// down. Not postponing may increase the number of ICAP errors
// if the ICAP service fails. We may also use "potential" space to
debugs(9, 9, "FTP may read up to " << read_sz << " bytes");
- if (read_sz < 2) // see http.cc
+ if (!read_sz)
return;
data.read_pending = true;
// how much we want to read
const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), maxReadSize);
- if (read_size < 2) {
+ if (!read_size) {
debugs(11, 7, "will not read up to " << read_size << " into buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
return 0;
}