diff --git a/.gitignore b/.gitignore index 4d9a0ed..31deb83 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/squid-4.15.tar.xz +squid-6.10.tar.xz diff --git a/.squid.metadata b/.squid.metadata deleted file mode 100644 index 30ac1f5..0000000 --- a/.squid.metadata +++ /dev/null @@ -1 +0,0 @@ -60bda34ba39657e2d870c8c1d2acece8a69c3075 SOURCES/squid-4.15.tar.xz diff --git a/SOURCES/squid-4.11-active-ftp.patch b/SOURCES/squid-4.11-active-ftp.patch deleted file mode 100644 index 00a9b56..0000000 --- a/SOURCES/squid-4.11-active-ftp.patch +++ /dev/null @@ -1,127 +0,0 @@ -diff --git a/src/clients/FtpClient.cc b/src/clients/FtpClient.cc -index b665bcf..d287e55 100644 ---- a/src/clients/FtpClient.cc -+++ b/src/clients/FtpClient.cc -@@ -778,7 +778,8 @@ Ftp::Client::connectDataChannel() - bool - Ftp::Client::openListenSocket() - { -- return false; -+ debugs(9, 3, HERE); -+ return false; - } - - /// creates a data channel Comm close callback -diff --git a/src/clients/FtpClient.h b/src/clients/FtpClient.h -index a76a5a0..218d696 100644 ---- a/src/clients/FtpClient.h -+++ b/src/clients/FtpClient.h -@@ -118,7 +118,7 @@ public: - bool sendPort(); - bool sendPassive(); - void connectDataChannel(); -- bool openListenSocket(); -+ virtual bool openListenSocket(); - void switchTimeoutToDataChannel(); - - CtrlChannel ctrl; ///< FTP control channel state -diff --git a/src/clients/FtpGateway.cc b/src/clients/FtpGateway.cc -index 411bce9..31d3e36 100644 ---- a/src/clients/FtpGateway.cc -+++ b/src/clients/FtpGateway.cc -@@ -87,6 +87,13 @@ struct GatewayFlags { - class Gateway; - typedef void (StateMethod)(Ftp::Gateway *); - -+} // namespace FTP -+ -+static void ftpOpenListenSocket(Ftp::Gateway * ftpState, int fallback); -+ -+namespace Ftp -+{ -+ - /// FTP Gateway: An FTP client that takes an HTTP request with an ftp:// URI, - /// converts it into one or more FTP commands, and then - /// converts one or more FTP responses into the final HTTP response. -@@ -137,7 +144,11 @@ public: - - /// create a data channel acceptor and start listening. - void listenForDataChannel(const Comm::ConnectionPointer &conn); -- -+ virtual bool openListenSocket() { -+ debugs(9, 3, HERE); -+ ftpOpenListenSocket(this, 0); -+ return Comm::IsConnOpen(data.conn); -+ } - int checkAuth(const HttpHeader * req_hdr); - void checkUrlpath(); - void buildTitleUrl(); -@@ -1787,6 +1798,7 @@ ftpOpenListenSocket(Ftp::Gateway * ftpState, int fallback) - } - - ftpState->listenForDataChannel(temp); -+ ftpState->data.listenConn = temp; - } - - static void -@@ -1822,13 +1834,19 @@ ftpSendPORT(Ftp::Gateway * ftpState) - // pull out the internal IP address bytes to send in PORT command... - // source them from the listen_conn->local - -+ struct sockaddr_in addr; -+ socklen_t addrlen = sizeof(addr); -+ getsockname(ftpState->data.listenConn->fd, (struct sockaddr *) &addr, &addrlen); -+ unsigned char port_high = ntohs(addr.sin_port) >> 8; -+ unsigned char port_low = ntohs(addr.sin_port) & 0xff; -+ - struct addrinfo *AI = NULL; - ftpState->data.listenConn->local.getAddrInfo(AI, AF_INET); - unsigned char *addrptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_addr; -- unsigned char *portptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_port; -+ // unsigned char *portptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_port; - snprintf(cbuf, CTRL_BUFLEN, "PORT %d,%d,%d,%d,%d,%d\r\n", - addrptr[0], addrptr[1], addrptr[2], addrptr[3], -- portptr[0], portptr[1]); -+ port_high, port_low); - ftpState->writeCommand(cbuf); - ftpState->state = Ftp::Client::SENT_PORT; - -@@ -1881,14 +1899,27 @@ ftpSendEPRT(Ftp::Gateway * ftpState) - return; - } - -+ -+ unsigned int port; -+ struct sockaddr_storage addr; -+ socklen_t addrlen = sizeof(addr); -+ getsockname(ftpState->data.listenConn->fd, (struct sockaddr *) &addr, &addrlen); -+ if (addr.ss_family == AF_INET) { -+ struct sockaddr_in *addr4 = (struct sockaddr_in*) &addr; -+ port = ntohs( addr4->sin_port ); -+ } else { -+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &addr; -+ port = ntohs( addr6->sin6_port ); -+ } -+ - char buf[MAX_IPSTRLEN]; - - /* RFC 2428 defines EPRT as IPv6 equivalent to IPv4 PORT command. */ - /* Which can be used by EITHER protocol. */ -- snprintf(cbuf, CTRL_BUFLEN, "EPRT |%d|%s|%d|\r\n", -+ snprintf(cbuf, CTRL_BUFLEN, "EPRT |%d|%s|%u|\r\n", - ( ftpState->data.listenConn->local.isIPv6() ? 2 : 1 ), - ftpState->data.listenConn->local.toStr(buf,MAX_IPSTRLEN), -- ftpState->data.listenConn->local.port() ); -+ port); - - ftpState->writeCommand(cbuf); - ftpState->state = Ftp::Client::SENT_EPRT; -@@ -1907,7 +1938,7 @@ ftpReadEPRT(Ftp::Gateway * ftpState) - ftpSendPORT(ftpState); - return; - } -- -+ ftpState->ctrl.message = NULL; - ftpRestOrList(ftpState); - } - diff --git a/SOURCES/squid-4.11-convert-ipv4.patch b/SOURCES/squid-4.11-convert-ipv4.patch deleted file mode 100644 index 5198f35..0000000 --- a/SOURCES/squid-4.11-convert-ipv4.patch +++ /dev/null @@ -1,143 +0,0 @@ -From 771908d313ee9c255adfb5e4fdba4d6797c18409 Mon Sep 17 00:00:00 2001 -From: Amos Jeffries -Date: Thu, 7 Mar 2019 13:50:38 +0000 -Subject: [PATCH] Bug 4928: Cannot convert non-IPv4 to IPv4 (#379) - -... when reaching client_ip_max_connections - -The client_ip_max_connections limit is checked before the TCP dst-IP is located for the newly received TCP connection. This leaves Squid unable to fetch the NFMARK or similar -details later on (they do not exist for [::]). - -Move client_ip_max_connections test later in the TCP accept process to ensure dst-IP is known when the error is produced. ---- - src/comm/TcpAcceptor.cc | 82 ++++++++++++++++++++--------------------- - 1 file changed, 39 insertions(+), 43 deletions(-) - -diff --git a/src/comm/TcpAcceptor.cc b/src/comm/TcpAcceptor.cc -index d4b576d..936aa30 100644 ---- a/src/comm/TcpAcceptor.cc -+++ b/src/comm/TcpAcceptor.cc -@@ -282,7 +282,16 @@ Comm::TcpAcceptor::acceptOne() - ConnectionPointer newConnDetails = new Connection(); - const Comm::Flag flag = oldAccept(newConnDetails); - -- if (flag == Comm::COMM_ERROR) { -+ /* Check for errors */ -+ if (!newConnDetails->isOpen()) { -+ -+ if (flag == Comm::NOMESSAGE) { -+ /* register interest again */ -+ debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub); -+ SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0); -+ return; -+ } -+ - // A non-recoverable error; notify the caller */ - debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub); - if (intendedForUserConnections()) -@@ -292,16 +301,12 @@ Comm::TcpAcceptor::acceptOne() - return; - } - -- if (flag == Comm::NOMESSAGE) { -- /* register interest again */ -- debugs(5, 5, "try later: " << conn << " handler Subscription: " << theCallSub); -- } else { -- debugs(5, 5, "Listener: " << conn << -- " accepted new connection " << newConnDetails << -- " handler Subscription: " << theCallSub); -- notify(flag, newConnDetails); -- } -+ newConnDetails->nfmark = Ip::Qos::getNfmarkFromConnection(newConnDetails, Ip::Qos::dirAccepted); - -+ debugs(5, 5, HERE << "Listener: " << conn << -+ " accepted new connection " << newConnDetails << -+ " handler Subscription: " << theCallSub); -+ notify(flag, newConnDetails); - SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0); - } - -@@ -341,8 +346,8 @@ Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer & - * - * \retval Comm::OK success. details parameter filled. - * \retval Comm::NOMESSAGE attempted accept() but nothing useful came in. -- * Or this client has too many connections already. - * \retval Comm::COMM_ERROR an outright failure occurred. -+ * Or this client has too many connections already. - */ - Comm::Flag - Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details) -@@ -383,6 +388,15 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details) - - details->remote = *gai; - -+ if ( Config.client_ip_max_connections >= 0) { -+ if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) { -+ debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections."); -+ Ip::Address::FreeAddr(gai); -+ PROF_stop(comm_accept); -+ return Comm::COMM_ERROR; -+ } -+ } -+ - // lookup the local-end details of this new connection - Ip::Address::InitAddr(gai); - details->local.setEmpty(); -@@ -396,6 +410,23 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details) - details->local = *gai; - Ip::Address::FreeAddr(gai); - -+ /* fdstat update */ -+ fdd_table[sock].close_file = NULL; -+ fdd_table[sock].close_line = 0; -+ -+ fde *F = &fd_table[sock]; -+ details->remote.toStr(F->ipaddr,MAX_IPSTRLEN); -+ F->remote_port = details->remote.port(); -+ F->local_addr = details->local; -+ F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET; -+ -+ // set socket flags -+ commSetCloseOnExec(sock); -+ commSetNonBlocking(sock); -+ -+ /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */ -+ F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet? -+ - // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses - if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) { - debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details); -@@ -414,33 +445,6 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details) - } - #endif - -- details->nfmark = Ip::Qos::getNfmarkFromConnection(details, Ip::Qos::dirAccepted); -- -- if (Config.client_ip_max_connections >= 0) { -- if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) { -- debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections."); -- PROF_stop(comm_accept); -- return Comm::NOMESSAGE; -- } -- } -- -- /* fdstat update */ -- fdd_table[sock].close_file = NULL; -- fdd_table[sock].close_line = 0; -- -- fde *F = &fd_table[sock]; -- details->remote.toStr(F->ipaddr,MAX_IPSTRLEN); -- F->remote_port = details->remote.port(); -- F->local_addr = details->local; -- F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET; -- -- // set socket flags -- commSetCloseOnExec(sock); -- commSetNonBlocking(sock); -- -- /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */ -- F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet? -- - PROF_stop(comm_accept); - return Comm::OK; - } diff --git a/SOURCES/squid-4.11-include-guards.patch b/SOURCES/squid-4.11-include-guards.patch deleted file mode 100644 index fb96c57..0000000 --- a/SOURCES/squid-4.11-include-guards.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff --git a/compat/os/linux.h b/compat/os/linux.h -index 0ff05c6..d51389b 100644 ---- a/compat/os/linux.h -+++ b/compat/os/linux.h -@@ -44,6 +44,36 @@ - #include - #endif - -+/* -+ * Netfilter header madness. (see Bug 4323) -+ * -+ * Netfilter have a history of defining their own versions of network protocol -+ * primitives without sufficient protection against the POSIX defines which are -+ * aways present in Linux. -+ * -+ * netinet/in.h must be included before any other sys header in order to properly -+ * activate include guards in the kernel maintainers added -+ * to workaround it. -+ */ -+#if HAVE_NETINET_IN_H -+#include -+#endif -+ -+/* -+ * Netfilter header madness. (see Bug 4323) -+ * -+ * Netfilter have a history of defining their own versions of network protocol -+ * primitives without sufficient protection against the POSIX defines which are -+ * aways present in Linux. -+ * -+ * netinet/in.h must be included before any other sys header in order to properly -+ * activate include guards in the kernel maintainers added -+ * to workaround it. -+ */ -+#if HAVE_NETINET_IN_H -+#include -+#endif -+ - /* - * sys/capability.h is only needed in Linux apparently. - * diff --git a/SOURCES/squid-4.11-large-acl.patch b/SOURCES/squid-4.11-large-acl.patch deleted file mode 100644 index 8aacf38..0000000 --- a/SOURCES/squid-4.11-large-acl.patch +++ /dev/null @@ -1,178 +0,0 @@ -diff --git a/src/acl/RegexData.cc b/src/acl/RegexData.cc -index 01a4c12..b5c1679 100644 ---- a/src/acl/RegexData.cc -+++ b/src/acl/RegexData.cc -@@ -22,6 +22,7 @@ - #include "ConfigParser.h" - #include "Debug.h" - #include "sbuf/List.h" -+#include "sbuf/Algorithms.h" - - ACLRegexData::~ACLRegexData() - { -@@ -129,6 +130,18 @@ compileRE(std::list &curlist, const char * RE, int flags) - return true; - } - -+static bool -+compileRE(std::list &curlist, const SBufList &RE, int flags) -+{ -+ if (RE.empty()) -+ return curlist.empty(); // XXX: old code did this. It looks wrong. -+ SBuf regexp; -+ static const SBuf openparen("("), closeparen(")"), separator(")|("); -+ JoinContainerIntoSBuf(regexp, RE.begin(), RE.end(), separator, openparen, -+ closeparen); -+ return compileRE(curlist, regexp.c_str(), flags); -+} -+ - /** Compose and compile one large RE from a set of (small) REs. - * The ultimate goal is to have only one RE per ACL so that match() is - * called only once per ACL. -@@ -137,16 +150,11 @@ static int - compileOptimisedREs(std::list &curlist, const SBufList &sl) - { - std::list newlist; -- int numREs = 0; -+ SBufList accumulatedRE; -+ int numREs = 0, reSize = 0; - int flags = REG_EXTENDED | REG_NOSUB; -- int largeREindex = 0; -- char largeRE[BUFSIZ]; -- *largeRE = 0; - - for (const SBuf & configurationLineWord : sl) { -- int RElen; -- RElen = configurationLineWord.length(); -- - static const SBuf minus_i("-i"); - static const SBuf plus_i("+i"); - if (configurationLineWord == minus_i) { -@@ -155,10 +163,11 @@ compileOptimisedREs(std::list &curlist, const SBufList &sl) - debugs(28, 2, "optimisation of -i ... -i" ); - } else { - debugs(28, 2, "-i" ); -- if (!compileRE(newlist, largeRE, flags)) -+ if (!compileRE(newlist, accumulatedRE, flags)) - return 0; - flags |= REG_ICASE; -- largeRE[largeREindex=0] = '\0'; -+ accumulatedRE.clear(); -+ reSize = 0; - } - } else if (configurationLineWord == plus_i) { - if ((flags & REG_ICASE) == 0) { -@@ -166,37 +175,34 @@ compileOptimisedREs(std::list &curlist, const SBufList &sl) - debugs(28, 2, "optimisation of +i ... +i"); - } else { - debugs(28, 2, "+i"); -- if (!compileRE(newlist, largeRE, flags)) -+ if (!compileRE(newlist, accumulatedRE, flags)) - return 0; - flags &= ~REG_ICASE; -- largeRE[largeREindex=0] = '\0'; -+ accumulatedRE.clear(); -+ reSize = 0; - } -- } else if (RElen + largeREindex + 3 < BUFSIZ-1) { -+ } else if (reSize < 1024) { - debugs(28, 2, "adding RE '" << configurationLineWord << "'"); -- if (largeREindex > 0) { -- largeRE[largeREindex] = '|'; -- ++largeREindex; -- } -- largeRE[largeREindex] = '('; -- ++largeREindex; -- configurationLineWord.copy(largeRE+largeREindex, BUFSIZ-largeREindex); -- largeREindex += configurationLineWord.length(); -- largeRE[largeREindex] = ')'; -- ++largeREindex; -- largeRE[largeREindex] = '\0'; -+ accumulatedRE.push_back(configurationLineWord); - ++numREs; -+ reSize += configurationLineWord.length(); - } else { - debugs(28, 2, "buffer full, generating new optimised RE..." ); -- if (!compileRE(newlist, largeRE, flags)) -+ accumulatedRE.push_back(configurationLineWord); -+ if (!compileRE(newlist, accumulatedRE, flags)) - return 0; -- largeRE[largeREindex=0] = '\0'; -+ accumulatedRE.clear(); -+ reSize = 0; - continue; /* do the loop again to add the RE to largeRE */ - } - } - -- if (!compileRE(newlist, largeRE, flags)) -+ if (!compileRE(newlist, accumulatedRE, flags)) - return 0; - -+ accumulatedRE.clear(); -+ reSize = 0; -+ - /* all was successful, so put the new list at the tail */ - curlist.splice(curlist.end(), newlist); - -diff --git a/src/sbuf/Algorithms.h b/src/sbuf/Algorithms.h -index 21ee889..338e9c0 100644 ---- a/src/sbuf/Algorithms.h -+++ b/src/sbuf/Algorithms.h -@@ -81,6 +81,57 @@ SBufContainerJoin(const Container &items, const SBuf& separator) - return rv; - } - -+/** Join container of SBufs and append to supplied target -+ * -+ * append to the target SBuf all elements in the [begin,end) range from -+ * an iterable container, prefixed by prefix, separated by separator and -+ * followed by suffix. Prefix and suffix are added also in case of empty -+ * iterable -+ * -+ * \return the modified dest -+ */ -+template -+SBuf& -+JoinContainerIntoSBuf(SBuf &dest, const ContainerIterator &begin, -+ const ContainerIterator &end, const SBuf& separator, -+ const SBuf& prefix = SBuf(), const SBuf& suffix = SBuf()) -+{ -+ if (begin == end) { -+ dest.append(prefix).append(suffix); -+ return dest; -+ } -+ -+ // optimization: pre-calculate needed storage -+ const SBuf::size_type totalContainerSize = -+ std::accumulate(begin, end, 0, SBufAddLength(separator)) + -+ dest.length() + prefix.length() + suffix.length(); -+ SBufReservationRequirements req; -+ req.minSpace = totalContainerSize; -+ dest.reserve(req); -+ -+ auto i = begin; -+ dest.append(prefix); -+ dest.append(*i); -+ ++i; -+ for (; i != end; ++i) -+ dest.append(separator).append(*i); -+ dest.append(suffix); -+ return dest; -+} -+ -+ -+/// convenience wrapper of JoinContainerIntoSBuf with no caller-supplied SBuf -+template -+SBuf -+JoinContainerToSBuf(const ContainerIterator &begin, -+ const ContainerIterator &end, const SBuf& separator, -+ const SBuf& prefix = SBuf(), const SBuf& suffix = SBuf()) -+{ -+ SBuf rv; -+ return JoinContainerIntoSBuf(rv, begin, end, separator, prefix, suffix); -+} -+ -+ - namespace std { - /// default hash functor to support std::unordered_map - template <> diff --git a/SOURCES/squid-4.15-CVE-2021-28116.patch b/SOURCES/squid-4.15-CVE-2021-28116.patch deleted file mode 100644 index 116a520..0000000 --- a/SOURCES/squid-4.15-CVE-2021-28116.patch +++ /dev/null @@ -1,424 +0,0 @@ -commit b003a0da7865caa25b5d1e70c79329b32409b02a (HEAD -> refs/heads/v4, refs/remotes/origin/v4) -Author: Amos Jeffries -Date: 2021-09-24 21:53:11 +0000 - - WCCP: Validate packets better (#899) - - Update WCCP to support exception based error handling for - parsing and processing we are moving Squid to for protocol - handling. - - Update the main WCCPv2 parsing checks to throw meaningful - exceptions when detected. - -diff --git a/src/wccp2.cc b/src/wccp2.cc -index ee592449c..6ef469e91 100644 ---- a/src/wccp2.cc -+++ b/src/wccp2.cc -@@ -1108,6 +1108,59 @@ wccp2ConnectionClose(void) - * Functions for handling the requests. - */ - -+/// Checks that the given area section ends inside the given (whole) area. -+/// \param error the message to throw when the section does not fit -+static void -+CheckSectionLength(const void *sectionStart, const size_t sectionLength, const void *wholeStart, const size_t wholeSize, const char *error) -+{ -+ assert(sectionStart); -+ assert(wholeStart); -+ -+ const auto wholeEnd = static_cast(wholeStart) + wholeSize; -+ assert(sectionStart >= wholeStart && "we never go backwards"); -+ assert(sectionStart <= wholeEnd && "we never go beyond our whole (but zero-sized fields are OK)"); -+ static_assert(sizeof(wccp2_i_see_you_t) <= PTRDIFF_MAX, "paranoid: no UB when subtracting in-whole pointers"); -+ // subtraction safe due to the three assertions above -+ const auto remainderDiff = wholeEnd - static_cast(sectionStart); -+ -+ // casting safe due to the assertions above (and size_t definition) -+ assert(remainderDiff >= 0); -+ const auto remainderSize = static_cast(remainderDiff); -+ -+ if (sectionLength <= remainderSize) -+ return; -+ -+ throw TextException(error, Here()); -+} -+ -+/// Checks that the area contains at least dataLength bytes after the header. -+/// The size of the field header itself is not included in dataLength. -+/// \returns the total field size -- the field header and field data combined -+template -+static size_t -+CheckFieldDataLength(const FieldHeader *header, const size_t dataLength, const void *areaStart, const size_t areaSize, const char *error) -+{ -+ assert(header); -+ const auto dataStart = reinterpret_cast(header) + sizeof(header); -+ CheckSectionLength(dataStart, dataLength, areaStart, areaSize, error); -+ return sizeof(header) + dataLength; // no overflow after CheckSectionLength() -+} -+ -+/// Positions the given field at a given start within a given packet area. -+/// The Field type determines the correct field size (used for bounds checking). -+/// \param field the field pointer the function should set -+/// \param areaStart the start of a packet (sub)structure containing the field -+/// \param areaSize the size of the packet (sub)structure starting at areaStart -+/// \param fieldStart the start of a field within the given area -+/// \param error the message to throw when the field does not fit the area -+template -+static void -+SetField(Field *&field, const void *fieldStart, const void *areaStart, const size_t areaSize, const char *error) -+{ -+ CheckSectionLength(fieldStart, sizeof(Field), areaStart, areaSize, error); -+ field = static_cast(const_cast(fieldStart)); -+} -+ - /* - * Accept the UDP packet - */ -@@ -1124,8 +1177,6 @@ wccp2HandleUdp(int sock, void *) - - /* These structs form the parts of the packet */ - -- struct wccp2_item_header_t *header = NULL; -- - struct wccp2_security_none_t *security_info = NULL; - - struct wccp2_service_info_t *service_info = NULL; -@@ -1141,14 +1192,13 @@ wccp2HandleUdp(int sock, void *) - struct wccp2_cache_identity_info_t *cache_identity = NULL; - - struct wccp2_capability_info_header_t *router_capability_header = NULL; -+ char *router_capability_data_start = nullptr; - - struct wccp2_capability_element_t *router_capability_element; - - struct sockaddr_in from; - - struct in_addr cache_address; -- int len, found; -- short int data_length, offset; - uint32_t tmp; - char *ptr; - int num_caches; -@@ -1161,20 +1211,18 @@ wccp2HandleUdp(int sock, void *) - Ip::Address from_tmp; - from_tmp.setIPv4(); - -- len = comm_udp_recvfrom(sock, -- &wccp2_i_see_you, -- WCCP_RESPONSE_SIZE, -- 0, -- from_tmp); -+ const auto lenOrError = comm_udp_recvfrom(sock, &wccp2_i_see_you, WCCP_RESPONSE_SIZE, 0, from_tmp); - -- if (len < 0) -+ if (lenOrError < 0) - return; -+ const auto len = static_cast(lenOrError); - -- if (ntohs(wccp2_i_see_you.version) != WCCP2_VERSION) -- return; -- -- if (ntohl(wccp2_i_see_you.type) != WCCP2_I_SEE_YOU) -- return; -+ try { -+ // TODO: Remove wccp2_i_see_you.data and use a buffer to read messages. -+ const auto message_header_size = sizeof(wccp2_i_see_you) - sizeof(wccp2_i_see_you.data); -+ Must2(len >= message_header_size, "incomplete WCCP message header"); -+ Must2(ntohs(wccp2_i_see_you.version) == WCCP2_VERSION, "WCCP version unsupported"); -+ Must2(ntohl(wccp2_i_see_you.type) == WCCP2_I_SEE_YOU, "WCCP packet type unsupported"); - - /* FIXME INET6 : drop conversion boundary */ - from_tmp.getSockAddr(from); -@@ -1182,73 +1230,60 @@ wccp2HandleUdp(int sock, void *) - debugs(80, 3, "Incoming WCCPv2 I_SEE_YOU length " << ntohs(wccp2_i_see_you.length) << "."); - - /* Record the total data length */ -- data_length = ntohs(wccp2_i_see_you.length); -+ const auto data_length = ntohs(wccp2_i_see_you.length); -+ Must2(data_length <= len - message_header_size, -+ "malformed packet claiming it's bigger than received data"); - -- offset = 0; -- -- if (data_length > len) { -- debugs(80, DBG_IMPORTANT, "ERROR: Malformed WCCPv2 packet claiming it's bigger than received data"); -- return; -- } -+ size_t offset = 0; - - /* Go through the data structure */ -- while (data_length > offset) { -+ while (offset + sizeof(struct wccp2_item_header_t) <= data_length) { - - char *data = wccp2_i_see_you.data; - -- header = (struct wccp2_item_header_t *) &data[offset]; -+ const auto itemHeader = reinterpret_cast(&data[offset]); -+ const auto itemSize = CheckFieldDataLength(itemHeader, ntohs(itemHeader->length), -+ data, data_length, "truncated record"); -+ // XXX: Check "The specified length must be a multiple of 4 octets" -+ // requirement to avoid unaligned memory reads after the first item. - -- switch (ntohs(header->type)) { -+ switch (ntohs(itemHeader->type)) { - - case WCCP2_SECURITY_INFO: -- -- if (security_info != NULL) { -- debugs(80, DBG_IMPORTANT, "Duplicate security definition"); -- return; -- } -- -- security_info = (struct wccp2_security_none_t *) &wccp2_i_see_you.data[offset]; -+ Must2(!security_info, "duplicate security definition"); -+ SetField(security_info, itemHeader, itemHeader, itemSize, -+ "security definition truncated"); - break; - - case WCCP2_SERVICE_INFO: -- -- if (service_info != NULL) { -- debugs(80, DBG_IMPORTANT, "Duplicate service_info definition"); -- return; -- } -- -- service_info = (struct wccp2_service_info_t *) &wccp2_i_see_you.data[offset]; -+ Must2(!service_info, "duplicate service_info definition"); -+ SetField(service_info, itemHeader, itemHeader, itemSize, -+ "service_info definition truncated"); - break; - - case WCCP2_ROUTER_ID_INFO: -- -- if (router_identity_info != NULL) { -- debugs(80, DBG_IMPORTANT, "Duplicate router_identity_info definition"); -- return; -- } -- -- router_identity_info = (struct router_identity_info_t *) &wccp2_i_see_you.data[offset]; -+ Must2(!router_identity_info, "duplicate router_identity_info definition"); -+ SetField(router_identity_info, itemHeader, itemHeader, itemSize, -+ "router_identity_info definition truncated"); - break; - - case WCCP2_RTR_VIEW_INFO: -- -- if (router_view_header != NULL) { -- debugs(80, DBG_IMPORTANT, "Duplicate router_view definition"); -- return; -- } -- -- router_view_header = (struct router_view_t *) &wccp2_i_see_you.data[offset]; -+ Must2(!router_view_header, "duplicate router_view definition"); -+ SetField(router_view_header, itemHeader, itemHeader, itemSize, -+ "router_view definition truncated"); - break; - -- case WCCP2_CAPABILITY_INFO: -- -- if (router_capability_header != NULL) { -- debugs(80, DBG_IMPORTANT, "Duplicate router_capability definition"); -- return; -- } -+ case WCCP2_CAPABILITY_INFO: { -+ Must2(!router_capability_header, "duplicate router_capability definition"); -+ SetField(router_capability_header, itemHeader, itemHeader, itemSize, -+ "router_capability definition truncated"); - -- router_capability_header = (struct wccp2_capability_info_header_t *) &wccp2_i_see_you.data[offset]; -+ CheckFieldDataLength(router_capability_header, ntohs(router_capability_header->capability_info_length), -+ itemHeader, itemSize, "capability info truncated"); -+ router_capability_data_start = reinterpret_cast(router_capability_header) + -+ sizeof(*router_capability_header); - break; -+ } - - /* Nothing to do for the types below */ - -@@ -1257,22 +1292,17 @@ wccp2HandleUdp(int sock, void *) - break; - - default: -- debugs(80, DBG_IMPORTANT, "Unknown record type in WCCPv2 Packet (" << ntohs(header->type) << ")."); -+ debugs(80, DBG_IMPORTANT, "Unknown record type in WCCPv2 Packet (" << ntohs(itemHeader->type) << ")."); - } - -- offset += sizeof(struct wccp2_item_header_t); -- offset += ntohs(header->length); -- -- if (offset > data_length) { -- debugs(80, DBG_IMPORTANT, "Error: WCCPv2 packet tried to tell us there is data beyond the end of the packet"); -- return; -- } -+ offset += itemSize; -+ assert(offset <= data_length && "CheckFieldDataLength(itemHeader...) established that"); - } - -- if ((security_info == NULL) || (service_info == NULL) || (router_identity_info == NULL) || (router_view_header == NULL)) { -- debugs(80, DBG_IMPORTANT, "Incomplete WCCPv2 Packet"); -- return; -- } -+ Must2(security_info, "packet missing security definition"); -+ Must2(service_info, "packet missing service_info definition"); -+ Must2(router_identity_info, "packet missing router_identity_info definition"); -+ Must2(router_view_header, "packet missing router_view definition"); - - debugs(80, 5, "Complete packet received"); - -@@ -1308,10 +1338,7 @@ wccp2HandleUdp(int sock, void *) - break; - } - -- if (router_list_ptr->next == NULL) { -- debugs(80, DBG_IMPORTANT, "WCCPv2 Packet received from unknown router"); -- return; -- } -+ Must2(router_list_ptr->next, "packet received from unknown router"); - - /* Set the router id */ - router_list_ptr->info->router_address = router_identity_info->router_id_element.router_address; -@@ -1331,11 +1358,20 @@ wccp2HandleUdp(int sock, void *) - } - } else { - -- char *end = ((char *) router_capability_header) + sizeof(*router_capability_header) + ntohs(router_capability_header->capability_info_length) - sizeof(struct wccp2_capability_info_header_t); -- -- router_capability_element = (struct wccp2_capability_element_t *) (((char *) router_capability_header) + sizeof(*router_capability_header)); -- -- while ((char *) router_capability_element <= end) { -+ const auto router_capability_data_length = ntohs(router_capability_header->capability_info_length); -+ assert(router_capability_data_start); -+ const auto router_capability_data_end = router_capability_data_start + -+ router_capability_data_length; -+ for (auto router_capability_data_current = router_capability_data_start; -+ router_capability_data_current < router_capability_data_end;) { -+ -+ SetField(router_capability_element, router_capability_data_current, -+ router_capability_data_start, router_capability_data_length, -+ "capability element header truncated"); -+ const auto elementSize = CheckFieldDataLength( -+ router_capability_element, ntohs(router_capability_element->capability_length), -+ router_capability_data_start, router_capability_data_length, -+ "capability element truncated"); - - switch (ntohs(router_capability_element->capability_type)) { - -@@ -1377,7 +1413,7 @@ wccp2HandleUdp(int sock, void *) - debugs(80, DBG_IMPORTANT, "Unknown capability type in WCCPv2 Packet (" << ntohs(router_capability_element->capability_type) << ")."); - } - -- router_capability_element = (struct wccp2_capability_element_t *) (((char *) router_capability_element) + sizeof(struct wccp2_item_header_t) + ntohs(router_capability_element->capability_length)); -+ router_capability_data_current += elementSize; - } - } - -@@ -1396,23 +1432,34 @@ wccp2HandleUdp(int sock, void *) - num_caches = 0; - - /* Check to see if we're the master cache and update the cache list */ -- found = 0; -+ bool found = false; - service_list_ptr->lowest_ip = 1; - cache_list_ptr = &router_list_ptr->cache_list_head; - - /* to find the list of caches, we start at the end of the router view header */ - - ptr = (char *) (router_view_header) + sizeof(struct router_view_t); -+ const auto router_view_size = sizeof(struct router_view_t) + -+ ntohs(router_view_header->header.length); - - /* Then we read the number of routers */ -- memcpy(&tmp, ptr, sizeof(tmp)); -+ const uint32_t *routerCountRaw = nullptr; -+ SetField(routerCountRaw, ptr, router_view_header, router_view_size, -+ "malformed packet (truncated router view info w/o number of routers)"); - - /* skip the number plus all the ip's */ -- -- ptr += sizeof(tmp) + (ntohl(tmp) * sizeof(struct in_addr)); -+ ptr += sizeof(*routerCountRaw); -+ const auto ipCount = ntohl(*routerCountRaw); -+ const auto ipsSize = ipCount * sizeof(struct in_addr); // we check for unsigned overflow below -+ Must2(ipsSize / sizeof(struct in_addr) != ipCount, "huge IP address count"); -+ CheckSectionLength(ptr, ipsSize, router_view_header, router_view_size, "invalid IP address count"); -+ ptr += ipsSize; - - /* Then read the number of caches */ -- memcpy(&tmp, ptr, sizeof(tmp)); -+ const uint32_t *cacheCountRaw = nullptr; -+ SetField(cacheCountRaw, ptr, router_view_header, router_view_size, -+ "malformed packet (truncated router view info w/o cache count)"); -+ memcpy(&tmp, cacheCountRaw, sizeof(tmp)); // TODO: Replace tmp with cacheCount - ptr += sizeof(tmp); - - if (ntohl(tmp) != 0) { -@@ -1426,7 +1473,8 @@ wccp2HandleUdp(int sock, void *) - - case WCCP2_ASSIGNMENT_METHOD_HASH: - -- cache_identity = (struct wccp2_cache_identity_info_t *) ptr; -+ SetField(cache_identity, ptr, router_view_header, router_view_size, -+ "malformed packet (truncated router view info cache w/o assignment hash)"); - - ptr += sizeof(struct wccp2_cache_identity_info_t); - -@@ -1437,13 +1485,15 @@ wccp2HandleUdp(int sock, void *) - - case WCCP2_ASSIGNMENT_METHOD_MASK: - -- cache_mask_info = (struct cache_mask_info_t *) ptr; -+ SetField(cache_mask_info, ptr, router_view_header, router_view_size, -+ "malformed packet (truncated router view info cache w/o assignment mask)"); - - /* The mask assignment has an undocumented variable length entry here */ - - if (ntohl(cache_mask_info->num1) == 3) { - -- cache_mask_identity = (struct wccp2_cache_mask_identity_info_t *) ptr; -+ SetField(cache_mask_identity, ptr, router_view_header, router_view_size, -+ "malformed packet (truncated router view info cache w/o assignment mask identity)"); - - ptr += sizeof(struct wccp2_cache_mask_identity_info_t); - -@@ -1474,10 +1524,7 @@ wccp2HandleUdp(int sock, void *) - debugs (80, 5, "checking cache list: (" << std::hex << cache_address.s_addr << ":" << router_list_ptr->local_ip.s_addr << ")"); - - /* Check to see if it's the master, or us */ -- -- if (cache_address.s_addr == router_list_ptr->local_ip.s_addr) { -- found = 1; -- } -+ found = found || (cache_address.s_addr == router_list_ptr->local_ip.s_addr); - - if (cache_address.s_addr < router_list_ptr->local_ip.s_addr) { - service_list_ptr->lowest_ip = 0; -@@ -1494,7 +1541,7 @@ wccp2HandleUdp(int sock, void *) - cache_list_ptr->next = NULL; - - service_list_ptr->lowest_ip = 1; -- found = 1; -+ found = true; - num_caches = 1; - } - -@@ -1502,7 +1549,7 @@ wccp2HandleUdp(int sock, void *) - - router_list_ptr->num_caches = htonl(num_caches); - -- if ((found == 1) && (service_list_ptr->lowest_ip == 1)) { -+ if (found && (service_list_ptr->lowest_ip == 1)) { - if (ntohl(router_view_header->change_number) != router_list_ptr->member_change) { - debugs(80, 4, "Change detected - queueing up new assignment"); - router_list_ptr->member_change = ntohl(router_view_header->change_number); -@@ -1515,6 +1562,10 @@ wccp2HandleUdp(int sock, void *) - eventDelete(wccp2AssignBuckets, NULL); - debugs(80, 5, "I am not the lowest ip cache - not assigning buckets"); - } -+ -+ } catch (...) { -+ debugs(80, DBG_IMPORTANT, "ERROR: Ignoring WCCPv2 message: " << CurrentException); -+ } - } - - static void diff --git a/SOURCES/squid-4.15-CVE-2021-46784.patch b/SOURCES/squid-4.15-CVE-2021-46784.patch deleted file mode 100644 index 3c3da59..0000000 --- a/SOURCES/squid-4.15-CVE-2021-46784.patch +++ /dev/null @@ -1,129 +0,0 @@ -From 780c4ea1b4c9d2fb41f6962aa6ed73ae57f74b2b Mon Sep 17 00:00:00 2001 -From: Joshua Rogers -Date: Mon, 18 Apr 2022 13:42:36 +0000 -Subject: [PATCH] Improve handling of Gopher responses (#1022) - ---- - src/gopher.cc | 45 ++++++++++++++++++++------------------------- - 1 file changed, 20 insertions(+), 25 deletions(-) - -diff --git a/src/gopher.cc b/src/gopher.cc -index 169b0e18299..6187da18bcd 100644 ---- a/src/gopher.cc -+++ b/src/gopher.cc -@@ -371,7 +371,6 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len) - char *lpos = NULL; - char *tline = NULL; - LOCAL_ARRAY(char, line, TEMP_BUF_SIZE); -- LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE); - char *name = NULL; - char *selector = NULL; - char *host = NULL; -@@ -381,7 +380,6 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len) - char gtype; - StoreEntry *entry = NULL; - -- memset(tmpbuf, '\0', TEMP_BUF_SIZE); - memset(line, '\0', TEMP_BUF_SIZE); - - entry = gopherState->entry; -@@ -416,7 +414,7 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len) - return; - } - -- String outbuf; -+ SBuf outbuf; - - if (!gopherState->HTML_header_added) { - if (gopherState->conversion == GopherStateData::HTML_CSO_RESULT) -@@ -583,34 +581,34 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len) - break; - } - -- memset(tmpbuf, '\0', TEMP_BUF_SIZE); -- - if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) { - if (strlen(escaped_selector) != 0) -- snprintf(tmpbuf, TEMP_BUF_SIZE, " %s\n", -- icon_url, escaped_selector, rfc1738_escape_part(host), -- *port ? ":" : "", port, html_quote(name)); -+ outbuf.appendf(" %s\n", -+ icon_url, escaped_selector, rfc1738_escape_part(host), -+ *port ? ":" : "", port, html_quote(name)); - else -- snprintf(tmpbuf, TEMP_BUF_SIZE, " %s\n", -- icon_url, rfc1738_escape_part(host), *port ? ":" : "", -- port, html_quote(name)); -+ outbuf.appendf(" %s\n", -+ icon_url, rfc1738_escape_part(host), *port ? ":" : "", -+ port, html_quote(name)); - - } else if (gtype == GOPHER_INFO) { -- snprintf(tmpbuf, TEMP_BUF_SIZE, "\t%s\n", html_quote(name)); -+ outbuf.appendf("\t%s\n", html_quote(name)); - } else { - if (strncmp(selector, "GET /", 5) == 0) { - /* WWW link */ -- snprintf(tmpbuf, TEMP_BUF_SIZE, " %s\n", -- icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name)); -+ outbuf.appendf(" %s\n", -+ icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name)); -+ } else if (gtype == GOPHER_WWW) { -+ outbuf.appendf(" %s\n", -+ icon_url, rfc1738_escape_unescaped(selector), html_quote(name)); - } else { - /* Standard link */ -- snprintf(tmpbuf, TEMP_BUF_SIZE, " %s\n", -- icon_url, host, gtype, escaped_selector, html_quote(name)); -+ outbuf.appendf(" %s\n", -+ icon_url, host, gtype, escaped_selector, html_quote(name)); - } - } - - safe_free(escaped_selector); -- outbuf.append(tmpbuf); - } else { - memset(line, '\0', TEMP_BUF_SIZE); - continue; -@@ -643,13 +641,12 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len) - break; - - if (gopherState->cso_recno != recno) { -- snprintf(tmpbuf, TEMP_BUF_SIZE, "

Record# %d
%s

\n
", recno, html_quote(result));
-+                    outbuf.appendf("

Record# %d
%s

\n
", recno, html_quote(result));
-                     gopherState->cso_recno = recno;
-                 } else {
--                    snprintf(tmpbuf, TEMP_BUF_SIZE, "%s\n", html_quote(result));
-+                    outbuf.appendf("%s\n", html_quote(result));
-                 }
- 
--                outbuf.append(tmpbuf);
-                 break;
-             } else {
-                 int code;
-@@ -677,8 +674,7 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
- 
-                 case 502: { /* Too Many Matches */
-                     /* Print the message the server returns */
--                    snprintf(tmpbuf, TEMP_BUF_SIZE, "

%s

\n
", html_quote(result));
--                    outbuf.append(tmpbuf);
-+                    outbuf.appendf("

%s

\n
", html_quote(result));
-                     break;
-                 }
- 
-@@ -694,13 +690,12 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
- 
-     }               /* while loop */
- 
--    if (outbuf.size() > 0) {
--        entry->append(outbuf.rawBuf(), outbuf.size());
-+    if (outbuf.length() > 0) {
-+        entry->append(outbuf.rawContent(), outbuf.length());
-         /* now let start sending stuff to client */
-         entry->flush();
-     }
- 
--    outbuf.clean();
-     return;
- }
- 
\ No newline at end of file
diff --git a/SOURCES/squid-4.15-CVE-2022-41318.patch b/SOURCES/squid-4.15-CVE-2022-41318.patch
deleted file mode 100644
index cb303ad..0000000
--- a/SOURCES/squid-4.15-CVE-2022-41318.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-commit 4031c6c2b004190fdffbc19dab7cd0305a2025b7 (refs/remotes/origin/v4, refs/remotes/github/v4, refs/heads/v4)
-Author: Amos Jeffries 
-Date:   2022-08-09 23:34:54 +0000
-
-    Bug 3193 pt2: NTLM decoder truncating strings (#1114)
-    
-    The initial bug fix overlooked large 'offset' causing integer
-    wrap to extract a too-short length string.
-    
-    Improve debugs and checks sequence to clarify cases and ensure
-    that all are handled correctly.
-
-diff --git a/lib/ntlmauth/ntlmauth.cc b/lib/ntlmauth/ntlmauth.cc
-index 5d9637290..f00fd51f8 100644
---- a/lib/ntlmauth/ntlmauth.cc
-+++ b/lib/ntlmauth/ntlmauth.cc
-@@ -107,10 +107,19 @@ ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr
-     int32_t o = le32toh(str->offset);
-     // debug("ntlm_fetch_string(plength=%d,l=%d,o=%d)\n",packet_size,l,o);
- 
--    if (l < 0 || l > NTLM_MAX_FIELD_LENGTH || o + l > packet_size || o == 0) {
--        debug("ntlm_fetch_string: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
-+    if (l < 0 || l > NTLM_MAX_FIELD_LENGTH) {
-+        debug("ntlm_fetch_string: insane string length (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
-         return rv;
-     }
-+    else if (o <= 0 || o > packet_size) {
-+        debug("ntlm_fetch_string: insane string offset (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
-+        return rv;
-+    }
-+    else if (l > packet_size - o) {
-+        debug("ntlm_fetch_string: truncated string data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
-+        return rv;
-+    }
-+
-     rv.str = (char *)packet + o;
-     rv.l = 0;
-     if ((flags & NTLM_NEGOTIATE_ASCII) == 0) {
diff --git a/SOURCES/squid-4.15-CVE-2023-46724.patch b/SOURCES/squid-4.15-CVE-2023-46724.patch
deleted file mode 100644
index 58b8651..0000000
--- a/SOURCES/squid-4.15-CVE-2023-46724.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-diff --git a/src/anyp/Uri.cc b/src/anyp/Uri.cc
-index 20b9bf1..81ebb18 100644
---- a/src/anyp/Uri.cc
-+++ b/src/anyp/Uri.cc
-@@ -173,6 +173,10 @@ urlInitialize(void)
-     assert(0 == matchDomainName("*.foo.com", ".foo.com", mdnHonorWildcards));
-     assert(0 != matchDomainName("*.foo.com", "foo.com", mdnHonorWildcards));
- 
-+    assert(0 != matchDomainName("foo.com", ""));
-+    assert(0 != matchDomainName("foo.com", "", mdnHonorWildcards));
-+    assert(0 != matchDomainName("foo.com", "", mdnRejectSubsubDomains));
-+
-     /* more cases? */
- }
- 
-@@ -756,6 +760,8 @@ matchDomainName(const char *h, const char *d, MatchDomainNameFlags flags)
-         return -1;
- 
-     dl = strlen(d);
-+    if (dl == 0)
-+        return 1;
- 
-     /*
-      * Start at the ends of the two strings and work towards the
diff --git a/SOURCES/squid-4.15-CVE-2023-46728.patch b/SOURCES/squid-4.15-CVE-2023-46728.patch
deleted file mode 100644
index 980f372..0000000
--- a/SOURCES/squid-4.15-CVE-2023-46728.patch
+++ /dev/null
@@ -1,1673 +0,0 @@
-commit 0cf1b78cacfdb278107ae352022ced143635b528
-Author: Luboš Uhliarik 
-Date:   Wed Dec 6 20:04:56 2023 +0100
-
-    Remove gopher support
-
-diff --git a/doc/debug-sections.txt b/doc/debug-sections.txt
-index 8b8b25f..50bd122 100644
---- a/doc/debug-sections.txt
-+++ b/doc/debug-sections.txt
-@@ -27,7 +27,6 @@ section 06    Disk I/O Routines
- section 07    Multicast
- section 08    Swap File Bitmap
- section 09    File Transfer Protocol (FTP)
--section 10    Gopher
- section 11    Hypertext Transfer Protocol (HTTP)
- section 12    Internet Cache Protocol (ICP)
- section 13    High Level Memory Pool Management
-diff --git a/errors/en/ERR_UNSUP_REQ b/errors/en/ERR_UNSUP_REQ
-index 352399d..e208043 100644
---- a/errors/en/ERR_UNSUP_REQ
-+++ b/errors/en/ERR_UNSUP_REQ
-@@ -24,7 +24,7 @@ body
- 

Unsupported Request Method and Protocol

- - --

Squid does not support all request methods for all access protocols. For example, you can not POST a Gopher request.

-+

Squid does not support all request methods for all access protocols.

- -

Your cache administrator is %w.

-
-diff --git a/errors/errorpage.css b/errors/errorpage.css -index 38ba434..facee93 100644 ---- a/errors/errorpage.css -+++ b/errors/errorpage.css -@@ -73,7 +73,7 @@ p { - pre { - } - --/* special event: FTP / Gopher directory listing */ -+/* special event: FTP directory listing */ - #dirmsg { - font-family: courier, monospace; - color: black; -diff --git a/errors/templates/ERR_UNSUP_REQ b/errors/templates/ERR_UNSUP_REQ -index e880392..196887d 100644 ---- a/errors/templates/ERR_UNSUP_REQ -+++ b/errors/templates/ERR_UNSUP_REQ -@@ -24,7 +24,7 @@ body -

Unsupported Request Method and Protocol

- - --

Squid does not support all request methods for all access protocols. For example, you can not POST a Gopher request.

-+

Squid does not support all request methods for all access protocols.

- -

Your cache administrator is %w.

-
-diff --git a/src/FwdState.cc b/src/FwdState.cc -index 41a1679..5363572 100644 ---- a/src/FwdState.cc -+++ b/src/FwdState.cc -@@ -28,7 +28,6 @@ - #include "fde.h" - #include "FwdState.h" - #include "globals.h" --#include "gopher.h" - #include "hier_code.h" - #include "http.h" - #include "http/Stream.h" -@@ -1007,10 +1006,6 @@ FwdState::dispatch() - httpStart(this); - break; - -- case AnyP::PROTO_GOPHER: -- gopherStart(this); -- break; -- - case AnyP::PROTO_FTP: - if (request->flags.ftpNative) - Ftp::StartRelay(this); -diff --git a/src/HttpMsg.h b/src/HttpMsg.h -index 2bf799f..06ef081 100644 ---- a/src/HttpMsg.h -+++ b/src/HttpMsg.h -@@ -38,7 +38,6 @@ public: - srcFtp = 1 << (16 + 1), ///< ftp_port or FTP server - srcIcap = 1 << (16 + 2), ///< traditional ICAP service without encryption - srcEcap = 1 << (16 + 3), ///< eCAP service that uses insecure libraries/daemons -- srcGopher = 1 << (16 + 14), ///< Gopher server - srcWhois = 1 << (16 + 15), ///< Whois server - srcUnsafe = 0xFFFF0000, ///< Unsafe sources mask - srcSafe = 0x0000FFFF ///< Safe sources mask -diff --git a/src/HttpRequest.cc b/src/HttpRequest.cc -index 0c11f5a..38b9307 100644 ---- a/src/HttpRequest.cc -+++ b/src/HttpRequest.cc -@@ -18,7 +18,6 @@ - #include "Downloader.h" - #include "err_detail_type.h" - #include "globals.h" --#include "gopher.h" - #include "http.h" - #include "http/one/RequestParser.h" - #include "http/Stream.h" -@@ -556,11 +555,6 @@ HttpRequest::maybeCacheable() - return false; - break; - -- case AnyP::PROTO_GOPHER: -- if (!gopherCachable(this)) -- return false; -- break; -- - case AnyP::PROTO_CACHE_OBJECT: - return false; - -diff --git a/src/IoStats.h b/src/IoStats.h -index e04deef..0b69d41 100644 ---- a/src/IoStats.h -+++ b/src/IoStats.h -@@ -22,7 +22,7 @@ public: - int writes; - int write_hist[histSize]; - } -- Http, Ftp, Gopher; -+ Http, Ftp; - }; - - #endif /* SQUID_IOSTATS_H_ */ -diff --git a/src/Makefile.am b/src/Makefile.am -index 7189757..cbce754 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -306,8 +306,6 @@ squid_SOURCES = \ - FwdState.h \ - Generic.h \ - globals.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - helper.h \ - hier_code.h \ -@@ -1260,8 +1258,6 @@ tests_testCacheManager_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - hier_code.h \ - helper.cc \ - $(HTCPSOURCE) \ -@@ -1679,8 +1675,6 @@ tests_testEvent_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -1915,8 +1909,6 @@ tests_testEventLoop_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -2146,8 +2138,6 @@ tests_test_http_range_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -2462,8 +2452,6 @@ tests_testHttpRequest_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -3308,8 +3296,6 @@ tests_testURL_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -diff --git a/src/Makefile.in b/src/Makefile.in -index 53ac16d..d46f49c 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -263,7 +263,7 @@ am__squid_SOURCES_DIST = AclRegs.cc AuthReg.cc AccessLogEntry.cc \ - ExternalACL.h ExternalACLEntry.cc ExternalACLEntry.h \ - FadingCounter.h FadingCounter.cc fatal.h fatal.cc fd.h fd.cc \ - fde.cc fde.h FileMap.h filemap.cc fqdncache.h fqdncache.cc \ -- FwdState.cc FwdState.h Generic.h globals.h gopher.h gopher.cc \ -+ FwdState.cc FwdState.h Generic.h globals.h \ - helper.cc helper.h hier_code.h HierarchyLogEntry.h htcp.cc \ - htcp.h http.cc http.h HttpHeaderFieldStat.h HttpHdrCc.h \ - HttpHdrCc.cc HttpHdrCc.cci HttpHdrRange.cc HttpHdrSc.cc \ -@@ -352,7 +352,7 @@ am_squid_OBJECTS = $(am__objects_1) AccessLogEntry.$(OBJEXT) \ - EventLoop.$(OBJEXT) external_acl.$(OBJEXT) \ - ExternalACLEntry.$(OBJEXT) FadingCounter.$(OBJEXT) \ - fatal.$(OBJEXT) fd.$(OBJEXT) fde.$(OBJEXT) filemap.$(OBJEXT) \ -- fqdncache.$(OBJEXT) FwdState.$(OBJEXT) gopher.$(OBJEXT) \ -+ fqdncache.$(OBJEXT) FwdState.$(OBJEXT) \ - helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ - HttpHdrCc.$(OBJEXT) HttpHdrRange.$(OBJEXT) HttpHdrSc.$(OBJEXT) \ - HttpHdrScTarget.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ -@@ -539,7 +539,7 @@ am__tests_testCacheManager_SOURCES_DIST = AccessLogEntry.cc debug.cc \ - tests/stub_ETag.cc event.cc external_acl.cc \ - ExternalACLEntry.cc fatal.h tests/stub_fatal.cc fd.h fd.cc \ - fde.cc FileMap.h filemap.cc fqdncache.h fqdncache.cc \ -- FwdState.cc FwdState.h gopher.h gopher.cc hier_code.h \ -+ FwdState.cc FwdState.h hier_code.h \ - helper.cc htcp.cc htcp.h http.cc HttpBody.h HttpBody.cc \ - HttpHeader.h HttpHeader.cc HttpHeaderFieldInfo.h \ - HttpHeaderTools.h HttpHeaderTools.cc HttpHeaderFieldStat.h \ -@@ -594,7 +594,7 @@ am_tests_testCacheManager_OBJECTS = AccessLogEntry.$(OBJEXT) \ - event.$(OBJEXT) external_acl.$(OBJEXT) \ - ExternalACLEntry.$(OBJEXT) tests/stub_fatal.$(OBJEXT) \ - fd.$(OBJEXT) fde.$(OBJEXT) filemap.$(OBJEXT) \ -- fqdncache.$(OBJEXT) FwdState.$(OBJEXT) gopher.$(OBJEXT) \ -+ fqdncache.$(OBJEXT) FwdState.$(OBJEXT) \ - helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ - HttpBody.$(OBJEXT) HttpHeader.$(OBJEXT) \ - HttpHeaderTools.$(OBJEXT) HttpHdrCc.$(OBJEXT) \ -@@ -838,7 +838,7 @@ am__tests_testEvent_SOURCES_DIST = AccessLogEntry.cc BodyPipe.cc \ - EventLoop.h EventLoop.cc external_acl.cc ExternalACLEntry.cc \ - FadingCounter.cc fatal.h tests/stub_fatal.cc fd.h fd.cc fde.cc \ - FileMap.h filemap.cc fqdncache.h fqdncache.cc FwdState.cc \ -- FwdState.h gopher.h gopher.cc helper.cc hier_code.h htcp.cc \ -+ FwdState.h helper.cc hier_code.h htcp.cc \ - htcp.h http.cc HttpBody.h HttpBody.cc \ - tests/stub_HttpControlMsg.cc HttpHeader.h HttpHeader.cc \ - HttpHeaderFieldInfo.h HttpHeaderTools.h HttpHeaderTools.cc \ -@@ -891,7 +891,7 @@ am_tests_testEvent_OBJECTS = AccessLogEntry.$(OBJEXT) \ - external_acl.$(OBJEXT) ExternalACLEntry.$(OBJEXT) \ - FadingCounter.$(OBJEXT) tests/stub_fatal.$(OBJEXT) \ - fd.$(OBJEXT) fde.$(OBJEXT) filemap.$(OBJEXT) \ -- fqdncache.$(OBJEXT) FwdState.$(OBJEXT) gopher.$(OBJEXT) \ -+ fqdncache.$(OBJEXT) FwdState.$(OBJEXT) \ - helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ - HttpBody.$(OBJEXT) tests/stub_HttpControlMsg.$(OBJEXT) \ - HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ -@@ -975,8 +975,8 @@ am__tests_testEventLoop_SOURCES_DIST = AccessLogEntry.cc BodyPipe.cc \ - tests/stub_ETag.cc EventLoop.h EventLoop.cc event.cc \ - external_acl.cc ExternalACLEntry.cc FadingCounter.cc fatal.h \ - tests/stub_fatal.cc fd.h fd.cc fde.cc FileMap.h filemap.cc \ -- fqdncache.h fqdncache.cc FwdState.cc FwdState.h gopher.h \ -- gopher.cc helper.cc hier_code.h htcp.cc htcp.h http.cc \ -+ fqdncache.h fqdncache.cc FwdState.cc FwdState.h \ -+ helper.cc hier_code.h htcp.cc htcp.h http.cc \ - HttpBody.h HttpBody.cc tests/stub_HttpControlMsg.cc \ - HttpHeader.h HttpHeader.cc HttpHeaderFieldInfo.h \ - HttpHeaderTools.h HttpHeaderTools.cc HttpHeaderFieldStat.h \ -@@ -1029,7 +1029,7 @@ am_tests_testEventLoop_OBJECTS = AccessLogEntry.$(OBJEXT) \ - external_acl.$(OBJEXT) ExternalACLEntry.$(OBJEXT) \ - FadingCounter.$(OBJEXT) tests/stub_fatal.$(OBJEXT) \ - fd.$(OBJEXT) fde.$(OBJEXT) filemap.$(OBJEXT) \ -- fqdncache.$(OBJEXT) FwdState.$(OBJEXT) gopher.$(OBJEXT) \ -+ fqdncache.$(OBJEXT) FwdState.$(OBJEXT) \ - helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ - HttpBody.$(OBJEXT) tests/stub_HttpControlMsg.$(OBJEXT) \ - HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ -@@ -1187,7 +1187,7 @@ am__tests_testHttpRequest_SOURCES_DIST = AccessLogEntry.cc \ - fs_io.cc dlink.h dlink.cc dns_internal.cc errorpage.cc \ - tests/stub_ETag.cc external_acl.cc ExternalACLEntry.cc fatal.h \ - tests/stub_fatal.cc fd.h fd.cc fde.cc fqdncache.h fqdncache.cc \ -- FwdState.cc FwdState.h gopher.h gopher.cc helper.cc \ -+ FwdState.cc FwdState.h helper.cc \ - hier_code.h htcp.cc htcp.h http.cc HttpBody.h HttpBody.cc \ - tests/stub_HttpControlMsg.cc HttpHeader.h HttpHeader.cc \ - HttpHeaderFieldInfo.h HttpHeaderTools.h HttpHeaderTools.cc \ -@@ -1243,7 +1243,7 @@ am_tests_testHttpRequest_OBJECTS = AccessLogEntry.$(OBJEXT) \ - $(am__objects_4) errorpage.$(OBJEXT) tests/stub_ETag.$(OBJEXT) \ - external_acl.$(OBJEXT) ExternalACLEntry.$(OBJEXT) \ - tests/stub_fatal.$(OBJEXT) fd.$(OBJEXT) fde.$(OBJEXT) \ -- fqdncache.$(OBJEXT) FwdState.$(OBJEXT) gopher.$(OBJEXT) \ -+ fqdncache.$(OBJEXT) FwdState.$(OBJEXT) \ - helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ - HttpBody.$(OBJEXT) tests/stub_HttpControlMsg.$(OBJEXT) \ - HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ -@@ -1670,8 +1670,8 @@ am__tests_testURL_SOURCES_DIST = AccessLogEntry.cc BodyPipe.cc \ - fs_io.cc dlink.h dlink.cc dns_internal.cc errorpage.cc ETag.cc \ - event.cc external_acl.cc ExternalACLEntry.cc fatal.h \ - tests/stub_fatal.cc fd.h fd.cc fde.cc FileMap.h filemap.cc \ -- fqdncache.h fqdncache.cc FwdState.cc FwdState.h gopher.h \ -- gopher.cc helper.cc hier_code.h htcp.cc htcp.h http.cc \ -+ fqdncache.h fqdncache.cc FwdState.cc FwdState.h \ -+ helper.cc hier_code.h htcp.cc htcp.h http.cc \ - HttpBody.h HttpBody.cc tests/stub_HttpControlMsg.cc \ - HttpHeaderFieldStat.h HttpHdrCc.h HttpHdrCc.cc HttpHdrCc.cci \ - HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \ -@@ -1725,7 +1725,7 @@ am_tests_testURL_OBJECTS = AccessLogEntry.$(OBJEXT) BodyPipe.$(OBJEXT) \ - event.$(OBJEXT) external_acl.$(OBJEXT) \ - ExternalACLEntry.$(OBJEXT) tests/stub_fatal.$(OBJEXT) \ - fd.$(OBJEXT) fde.$(OBJEXT) filemap.$(OBJEXT) \ -- fqdncache.$(OBJEXT) FwdState.$(OBJEXT) gopher.$(OBJEXT) \ -+ fqdncache.$(OBJEXT) FwdState.$(OBJEXT) \ - helper.$(OBJEXT) $(am__objects_5) http.$(OBJEXT) \ - HttpBody.$(OBJEXT) tests/stub_HttpControlMsg.$(OBJEXT) \ - HttpHdrCc.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ -@@ -1925,8 +1925,8 @@ am__tests_test_http_range_SOURCES_DIST = AccessLogEntry.cc BodyPipe.cc \ - dns_internal.cc errorpage.cc tests/stub_ETag.cc event.cc \ - FadingCounter.cc fatal.h tests/stub_libauth.cc \ - tests/stub_fatal.cc fd.h fd.cc fde.cc FileMap.h filemap.cc \ -- fqdncache.h fqdncache.cc FwdState.cc FwdState.h gopher.h \ -- gopher.cc helper.cc hier_code.h htcp.cc htcp.h http.cc \ -+ fqdncache.h fqdncache.cc FwdState.cc FwdState.h \ -+ helper.cc hier_code.h htcp.cc htcp.h http.cc \ - HttpBody.h HttpBody.cc tests/stub_HttpControlMsg.cc \ - HttpHeaderFieldStat.h HttpHdrCc.h HttpHdrCc.cc HttpHdrCc.cci \ - HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \ -@@ -1979,7 +1979,7 @@ am_tests_test_http_range_OBJECTS = AccessLogEntry.$(OBJEXT) \ - FadingCounter.$(OBJEXT) tests/stub_libauth.$(OBJEXT) \ - tests/stub_fatal.$(OBJEXT) fd.$(OBJEXT) fde.$(OBJEXT) \ - filemap.$(OBJEXT) fqdncache.$(OBJEXT) FwdState.$(OBJEXT) \ -- gopher.$(OBJEXT) helper.$(OBJEXT) $(am__objects_5) \ -+ helper.$(OBJEXT) $(am__objects_5) \ - http.$(OBJEXT) HttpBody.$(OBJEXT) \ - tests/stub_HttpControlMsg.$(OBJEXT) HttpHdrCc.$(OBJEXT) \ - HttpHdrContRange.$(OBJEXT) HttpHdrRange.$(OBJEXT) \ -@@ -2131,7 +2131,7 @@ am__depfiles_remade = ./$(DEPDIR)/AccessLogEntry.Po \ - ./$(DEPDIR)/external_acl.Po ./$(DEPDIR)/fatal.Po \ - ./$(DEPDIR)/fd.Po ./$(DEPDIR)/fde.Po ./$(DEPDIR)/filemap.Po \ - ./$(DEPDIR)/fqdncache.Po ./$(DEPDIR)/fs_io.Po \ -- ./$(DEPDIR)/globals.Po ./$(DEPDIR)/gopher.Po \ -+ ./$(DEPDIR)/globals.Po \ - ./$(DEPDIR)/helper.Po ./$(DEPDIR)/hier_code.Po \ - ./$(DEPDIR)/htcp.Po ./$(DEPDIR)/http.Po \ - ./$(DEPDIR)/icp_opcode.Po ./$(DEPDIR)/icp_v2.Po \ -@@ -3046,7 +3046,7 @@ squid_SOURCES = $(ACL_REGISTRATION_SOURCES) AccessLogEntry.cc \ - ExternalACL.h ExternalACLEntry.cc ExternalACLEntry.h \ - FadingCounter.h FadingCounter.cc fatal.h fatal.cc fd.h fd.cc \ - fde.cc fde.h FileMap.h filemap.cc fqdncache.h fqdncache.cc \ -- FwdState.cc FwdState.h Generic.h globals.h gopher.h gopher.cc \ -+ FwdState.cc FwdState.h Generic.h globals.h \ - helper.cc helper.h hier_code.h HierarchyLogEntry.h \ - $(HTCPSOURCE) http.cc http.h HttpHeaderFieldStat.h HttpHdrCc.h \ - HttpHdrCc.cc HttpHdrCc.cci HttpHdrRange.cc HttpHdrSc.cc \ -@@ -3711,8 +3711,6 @@ tests_testCacheManager_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - hier_code.h \ - helper.cc \ - $(HTCPSOURCE) \ -@@ -4137,8 +4135,6 @@ tests_testEvent_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -4374,8 +4370,6 @@ tests_testEventLoop_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -4607,8 +4601,6 @@ tests_test_http_range_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -4927,8 +4919,6 @@ tests_testHttpRequest_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -5780,8 +5770,6 @@ tests_testURL_SOURCES = \ - fqdncache.cc \ - FwdState.cc \ - FwdState.h \ -- gopher.h \ -- gopher.cc \ - helper.cc \ - hier_code.h \ - $(HTCPSOURCE) \ -@@ -6826,7 +6814,6 @@ distclean-compile: - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fqdncache.Po@am__quote@ # am--include-marker - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs_io.Po@am__quote@ # am--include-marker - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globals.Po@am__quote@ # am--include-marker --@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gopher.Po@am__quote@ # am--include-marker - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helper.Po@am__quote@ # am--include-marker - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hier_code.Po@am__quote@ # am--include-marker - @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/htcp.Po@am__quote@ # am--include-marker -@@ -7808,7 +7795,6 @@ distclean: distclean-recursive - -rm -f ./$(DEPDIR)/fqdncache.Po - -rm -f ./$(DEPDIR)/fs_io.Po - -rm -f ./$(DEPDIR)/globals.Po -- -rm -f ./$(DEPDIR)/gopher.Po - -rm -f ./$(DEPDIR)/helper.Po - -rm -f ./$(DEPDIR)/hier_code.Po - -rm -f ./$(DEPDIR)/htcp.Po -@@ -8133,7 +8119,6 @@ maintainer-clean: maintainer-clean-recursive - -rm -f ./$(DEPDIR)/fqdncache.Po - -rm -f ./$(DEPDIR)/fs_io.Po - -rm -f ./$(DEPDIR)/globals.Po -- -rm -f ./$(DEPDIR)/gopher.Po - -rm -f ./$(DEPDIR)/helper.Po - -rm -f ./$(DEPDIR)/hier_code.Po - -rm -f ./$(DEPDIR)/htcp.Po -diff --git a/src/adaptation/ecap/Host.cc b/src/adaptation/ecap/Host.cc -index 33fbb5a..5f17f86 100644 ---- a/src/adaptation/ecap/Host.cc -+++ b/src/adaptation/ecap/Host.cc -@@ -49,7 +49,6 @@ Adaptation::Ecap::Host::Host() - libecap::protocolHttp.assignHostId(AnyP::PROTO_HTTP); - libecap::protocolHttps.assignHostId(AnyP::PROTO_HTTPS); - libecap::protocolFtp.assignHostId(AnyP::PROTO_FTP); -- libecap::protocolGopher.assignHostId(AnyP::PROTO_GOPHER); - libecap::protocolWais.assignHostId(AnyP::PROTO_WAIS); - libecap::protocolUrn.assignHostId(AnyP::PROTO_URN); - libecap::protocolWhois.assignHostId(AnyP::PROTO_WHOIS); -diff --git a/src/adaptation/ecap/MessageRep.cc b/src/adaptation/ecap/MessageRep.cc -index a2779e7..94595b6 100644 ---- a/src/adaptation/ecap/MessageRep.cc -+++ b/src/adaptation/ecap/MessageRep.cc -@@ -140,8 +140,6 @@ Adaptation::Ecap::FirstLineRep::protocol() const - return libecap::protocolHttps; - case AnyP::PROTO_FTP: - return libecap::protocolFtp; -- case AnyP::PROTO_GOPHER: -- return libecap::protocolGopher; - case AnyP::PROTO_WAIS: - return libecap::protocolWais; - case AnyP::PROTO_WHOIS: -diff --git a/src/anyp/ProtocolType.h b/src/anyp/ProtocolType.h -index 66f7bc2..ef3ab25 100644 ---- a/src/anyp/ProtocolType.h -+++ b/src/anyp/ProtocolType.h -@@ -27,7 +27,6 @@ typedef enum { - PROTO_HTTPS, - PROTO_COAP, - PROTO_COAPS, -- PROTO_GOPHER, - PROTO_WAIS, - PROTO_CACHE_OBJECT, - PROTO_ICP, -diff --git a/src/anyp/Uri.cc b/src/anyp/Uri.cc -index ced3181..b0b60cf 100644 ---- a/src/anyp/Uri.cc -+++ b/src/anyp/Uri.cc -@@ -885,8 +885,6 @@ urlCheckRequest(const HttpRequest * r) - if (r->method == Http::METHOD_PUT) - rc = 1; - -- case AnyP::PROTO_GOPHER: -- - case AnyP::PROTO_WAIS: - - case AnyP::PROTO_WHOIS: -diff --git a/src/anyp/UriScheme.cc b/src/anyp/UriScheme.cc -index bac5435..f96c73f 100644 ---- a/src/anyp/UriScheme.cc -+++ b/src/anyp/UriScheme.cc -@@ -87,9 +87,6 @@ AnyP::UriScheme::defaultPort() const - // Assuming IANA policy of allocating same port for base and TLS protocol versions will occur. - return 5683; - -- case AnyP::PROTO_GOPHER: -- return 70; -- - case AnyP::PROTO_WAIS: - return 210; - -diff --git a/src/cf.data.pre b/src/cf.data.pre -index b5519b2..bc2ddcd 100644 ---- a/src/cf.data.pre -+++ b/src/cf.data.pre -@@ -1513,7 +1513,6 @@ acl SSL_ports port 443 - acl Safe_ports port 80 # http - acl Safe_ports port 21 # ftp - acl Safe_ports port 443 # https --acl Safe_ports port 70 # gopher - acl Safe_ports port 210 # wais - acl Safe_ports port 1025-65535 # unregistered ports - acl Safe_ports port 280 # http-mgmt -@@ -4563,7 +4562,7 @@ DOC_START - [http::]url.getScheme() == AnyP::PROTO_HTTP) - return method.respMaybeCacheable(); - -- if (request->url.getScheme() == AnyP::PROTO_GOPHER) -- return gopherCachable(request); -- - if (request->url.getScheme() == AnyP::PROTO_CACHE_OBJECT) - return 0; - -diff --git a/src/err_type.h b/src/err_type.h -index 742fc5a..dbb4527 100644 ---- a/src/err_type.h -+++ b/src/err_type.h -@@ -65,7 +65,7 @@ typedef enum { - ERR_GATEWAY_FAILURE, - - /* Special Cases */ -- ERR_DIR_LISTING, /* Display of remote directory (FTP, Gopher) */ -+ ERR_DIR_LISTING, /* Display of remote directory (FTP) */ - ERR_SQUID_SIGNATURE, /* not really an error */ - ERR_SHUTTING_DOWN, - ERR_PROTOCOL_UNKNOWN, -diff --git a/src/gopher.cc b/src/gopher.cc -deleted file mode 100644 -index 6187da1..0000000 ---- a/src/gopher.cc -+++ /dev/null -@@ -1,977 +0,0 @@ --/* -- * Copyright (C) 1996-2021 The Squid Software Foundation and contributors -- * -- * Squid software is distributed under GPLv2+ license and includes -- * contributions from numerous individuals and organizations. -- * Please see the COPYING and CONTRIBUTORS files for details. -- */ -- --/* DEBUG: section 10 Gopher */ -- --#include "squid.h" --#include "comm.h" --#include "comm/Read.h" --#include "comm/Write.h" --#include "errorpage.h" --#include "fd.h" --#include "FwdState.h" --#include "globals.h" --#include "html_quote.h" --#include "HttpReply.h" --#include "HttpRequest.h" --#include "MemBuf.h" --#include "mime.h" --#include "parser/Tokenizer.h" --#include "rfc1738.h" --#include "SquidConfig.h" --#include "SquidTime.h" --#include "StatCounters.h" --#include "Store.h" --#include "tools.h" -- --#if USE_DELAY_POOLS --#include "DelayPools.h" --#include "MemObject.h" --#endif -- --/* gopher type code from rfc. Anawat. */ --#define GOPHER_FILE '0' --#define GOPHER_DIRECTORY '1' --#define GOPHER_CSO '2' --#define GOPHER_ERROR '3' --#define GOPHER_MACBINHEX '4' --#define GOPHER_DOSBIN '5' --#define GOPHER_UUENCODED '6' --#define GOPHER_INDEX '7' --#define GOPHER_TELNET '8' --#define GOPHER_BIN '9' --#define GOPHER_REDUNT '+' --#define GOPHER_3270 'T' --#define GOPHER_GIF 'g' --#define GOPHER_IMAGE 'I' -- --#define GOPHER_HTML 'h' --#define GOPHER_INFO 'i' -- --/// W3 address --#define GOPHER_WWW 'w' --#define GOPHER_SOUND 's' -- --#define GOPHER_PLUS_IMAGE ':' --#define GOPHER_PLUS_MOVIE ';' --#define GOPHER_PLUS_SOUND '<' -- --#define GOPHER_PORT 70 -- --#define TAB '\t' -- --// TODO CODE: should this be a protocol-specific thing? --#define TEMP_BUF_SIZE 4096 -- --#define MAX_CSO_RESULT 1024 -- --/** -- * Gopher Gateway Internals -- * -- * Gopher is somewhat complex and gross because it must convert from -- * the Gopher protocol to HTTP. -- */ --class GopherStateData --{ -- CBDATA_CLASS(GopherStateData); -- --public: -- GopherStateData(FwdState *aFwd) : -- entry(aFwd->entry), -- conversion(NORMAL), -- HTML_header_added(0), -- HTML_pre(0), -- type_id(GOPHER_FILE /* '0' */), -- cso_recno(0), -- len(0), -- buf(NULL), -- fwd(aFwd) -- { -- *request = 0; -- buf = (char *)memAllocate(MEM_4K_BUF); -- entry->lock("gopherState"); -- *replybuf = 0; -- } -- ~GopherStateData() {if(buf) swanSong();} -- -- /* AsyncJob API emulated */ -- void deleteThis(const char *aReason); -- void swanSong(); -- --public: -- StoreEntry *entry; -- enum { -- NORMAL, -- HTML_DIR, -- HTML_INDEX_RESULT, -- HTML_CSO_RESULT, -- HTML_INDEX_PAGE, -- HTML_CSO_PAGE -- } conversion; -- int HTML_header_added; -- int HTML_pre; -- char type_id; -- char request[MAX_URL]; -- int cso_recno; -- int len; -- char *buf; /* pts to a 4k page */ -- Comm::ConnectionPointer serverConn; -- FwdState::Pointer fwd; -- HttpReply::Pointer reply_; -- char replybuf[BUFSIZ]; --}; -- --CBDATA_CLASS_INIT(GopherStateData); -- --static CLCB gopherStateFree; --static void gopherMimeCreate(GopherStateData *); --static void gopher_request_parse(const HttpRequest * req, -- char *type_id, -- char *request); --static void gopherEndHTML(GopherStateData *); --static void gopherToHTML(GopherStateData *, char *inbuf, int len); --static CTCB gopherTimeout; --static IOCB gopherReadReply; --static IOCB gopherSendComplete; --static PF gopherSendRequest; -- --static char def_gopher_bin[] = "www/unknown"; -- --static char def_gopher_text[] = "text/plain"; -- --static void --gopherStateFree(const CommCloseCbParams ¶ms) --{ -- GopherStateData *gopherState = (GopherStateData *)params.data; -- -- if (gopherState == NULL) -- return; -- -- gopherState->deleteThis("gopherStateFree"); --} -- --void --GopherStateData::deleteThis(const char *) --{ -- swanSong(); -- delete this; --} -- --void --GopherStateData::swanSong() --{ -- if (entry) -- entry->unlock("gopherState"); -- -- if (buf) { -- memFree(buf, MEM_4K_BUF); -- buf = nullptr; -- } --} -- --/** -- * Create MIME Header for Gopher Data -- */ --static void --gopherMimeCreate(GopherStateData * gopherState) --{ -- StoreEntry *entry = gopherState->entry; -- const char *mime_type = NULL; -- const char *mime_enc = NULL; -- -- switch (gopherState->type_id) { -- -- case GOPHER_DIRECTORY: -- -- case GOPHER_INDEX: -- -- case GOPHER_HTML: -- -- case GOPHER_WWW: -- -- case GOPHER_CSO: -- mime_type = "text/html"; -- break; -- -- case GOPHER_GIF: -- -- case GOPHER_IMAGE: -- -- case GOPHER_PLUS_IMAGE: -- mime_type = "image/gif"; -- break; -- -- case GOPHER_SOUND: -- -- case GOPHER_PLUS_SOUND: -- mime_type = "audio/basic"; -- break; -- -- case GOPHER_PLUS_MOVIE: -- mime_type = "video/mpeg"; -- break; -- -- case GOPHER_MACBINHEX: -- -- case GOPHER_DOSBIN: -- -- case GOPHER_UUENCODED: -- -- case GOPHER_BIN: -- /* Rightnow We have no idea what it is. */ -- mime_enc = mimeGetContentEncoding(gopherState->request); -- mime_type = mimeGetContentType(gopherState->request); -- if (!mime_type) -- mime_type = def_gopher_bin; -- break; -- -- case GOPHER_FILE: -- -- default: -- mime_enc = mimeGetContentEncoding(gopherState->request); -- mime_type = mimeGetContentType(gopherState->request); -- if (!mime_type) -- mime_type = def_gopher_text; -- break; -- } -- -- assert(entry->isEmpty()); -- -- HttpReply *reply = new HttpReply; -- entry->buffer(); -- reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, -1, -1, -2); -- if (mime_enc) -- reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc); -- -- entry->replaceHttpReply(reply); -- gopherState->reply_ = reply; --} -- --/** -- * Parse a gopher request into components. By Anawat. -- */ --static void --gopher_request_parse(const HttpRequest * req, char *type_id, char *request) --{ -- ::Parser::Tokenizer tok(req->url.path()); -- -- if (request) -- *request = 0; -- -- tok.skip('/'); // ignore failures? path could be ab-empty -- -- if (tok.atEnd()) { -- *type_id = GOPHER_DIRECTORY; -- return; -- } -- -- static const CharacterSet anyByte("UTF-8",0x00, 0xFF); -- -- SBuf typeId; -- (void)tok.prefix(typeId, anyByte, 1); // never fails since !atEnd() -- *type_id = typeId[0]; -- -- if (request) { -- SBufToCstring(request, tok.remaining().substr(0, MAX_URL-1)); -- /* convert %xx to char */ -- rfc1738_unescape(request); -- } --} -- --/** -- * Parse the request to determine whether it is cachable. -- * -- * \param req Request data. -- * \retval 0 Not cachable. -- * \retval 1 Cachable. -- */ --int --gopherCachable(const HttpRequest * req) --{ -- int cachable = 1; -- char type_id; -- /* parse to see type */ -- gopher_request_parse(req, -- &type_id, -- NULL); -- -- switch (type_id) { -- -- case GOPHER_INDEX: -- -- case GOPHER_CSO: -- -- case GOPHER_TELNET: -- -- case GOPHER_3270: -- cachable = 0; -- break; -- -- default: -- cachable = 1; -- } -- -- return cachable; --} -- --static void --gopherHTMLHeader(StoreEntry * e, const char *title, const char *substring) --{ -- storeAppendPrintf(e, "\n"); -- storeAppendPrintf(e, ""); -- storeAppendPrintf(e, title, substring); -- storeAppendPrintf(e, ""); -- storeAppendPrintf(e, "\n"); -- storeAppendPrintf(e, "\n

"); -- storeAppendPrintf(e, title, substring); -- storeAppendPrintf(e, "

\n"); --} -- --static void --gopherHTMLFooter(StoreEntry * e) --{ -- storeAppendPrintf(e, "
\n"); -- storeAppendPrintf(e, "
\n"); -- storeAppendPrintf(e, "Generated %s by %s (%s)\n", -- mkrfc1123(squid_curtime), -- getMyHostname(), -- visible_appname_string); -- storeAppendPrintf(e, "
\n"); --} -- --static void --gopherEndHTML(GopherStateData * gopherState) --{ -- StoreEntry *e = gopherState->entry; -- -- if (!gopherState->HTML_header_added) { -- gopherHTMLHeader(e, "Server Return Nothing", NULL); -- storeAppendPrintf(e, "

The Gopher query resulted in a blank response

"); -- } else if (gopherState->HTML_pre) { -- storeAppendPrintf(e, "
\n"); -- } -- -- gopherHTMLFooter(e); --} -- --/** -- * Convert Gopher to HTML. -- * -- * Borrow part of code from libwww2 came with Mosaic distribution. -- */ --static void --gopherToHTML(GopherStateData * gopherState, char *inbuf, int len) --{ -- char *pos = inbuf; -- char *lpos = NULL; -- char *tline = NULL; -- LOCAL_ARRAY(char, line, TEMP_BUF_SIZE); -- char *name = NULL; -- char *selector = NULL; -- char *host = NULL; -- char *port = NULL; -- char *escaped_selector = NULL; -- const char *icon_url = NULL; -- char gtype; -- StoreEntry *entry = NULL; -- -- memset(line, '\0', TEMP_BUF_SIZE); -- -- entry = gopherState->entry; -- -- if (gopherState->conversion == GopherStateData::HTML_INDEX_PAGE) { -- char *html_url = html_quote(entry->url()); -- gopherHTMLHeader(entry, "Gopher Index %s", html_url); -- storeAppendPrintf(entry, -- "

This is a searchable Gopher index. Use the search\n" -- "function of your browser to enter search terms.\n" -- "\n"); -- gopherHTMLFooter(entry); -- /* now let start sending stuff to client */ -- entry->flush(); -- gopherState->HTML_header_added = 1; -- -- return; -- } -- -- if (gopherState->conversion == GopherStateData::HTML_CSO_PAGE) { -- char *html_url = html_quote(entry->url()); -- gopherHTMLHeader(entry, "CSO Search of %s", html_url); -- storeAppendPrintf(entry, -- "

A CSO database usually contains a phonebook or\n" -- "directory. Use the search function of your browser to enter\n" -- "search terms.

\n"); -- gopherHTMLFooter(entry); -- /* now let start sending stuff to client */ -- entry->flush(); -- gopherState->HTML_header_added = 1; -- -- return; -- } -- -- SBuf outbuf; -- -- if (!gopherState->HTML_header_added) { -- if (gopherState->conversion == GopherStateData::HTML_CSO_RESULT) -- gopherHTMLHeader(entry, "CSO Search Result", NULL); -- else -- gopherHTMLHeader(entry, "Gopher Menu", NULL); -- -- outbuf.append ("
");
--
--        gopherState->HTML_header_added = 1;
--
--        gopherState->HTML_pre = 1;
--    }
--
--    while (pos < inbuf + len) {
--        int llen;
--        int left = len - (pos - inbuf);
--        lpos = (char *)memchr(pos, '\n', left);
--        if (lpos) {
--            ++lpos;             /* Next line is after \n */
--            llen = lpos - pos;
--        } else {
--            llen = left;
--        }
--        if (gopherState->len + llen >= TEMP_BUF_SIZE) {
--            debugs(10, DBG_IMPORTANT, "GopherHTML: Buffer overflow. Lost some data on URL: " << entry->url()  );
--            llen = TEMP_BUF_SIZE - gopherState->len - 1;
--        }
--        if (!lpos) {
--            /* there is no complete line in inbuf */
--            /* copy it to temp buffer */
--            /* note: llen is adjusted above */
--            memcpy(gopherState->buf + gopherState->len, pos, llen);
--            gopherState->len += llen;
--            break;
--        }
--        if (gopherState->len != 0) {
--            /* there is something left from last tx. */
--            memcpy(line, gopherState->buf, gopherState->len);
--            memcpy(line + gopherState->len, pos, llen);
--            llen += gopherState->len;
--            gopherState->len = 0;
--        } else {
--            memcpy(line, pos, llen);
--        }
--        line[llen + 1] = '\0';
--        /* move input to next line */
--        pos = lpos;
--
--        /* at this point. We should have one line in buffer to process */
--
--        if (*line == '.') {
--            /* skip it */
--            memset(line, '\0', TEMP_BUF_SIZE);
--            continue;
--        }
--
--        switch (gopherState->conversion) {
--
--        case GopherStateData::HTML_INDEX_RESULT:
--
--        case GopherStateData::HTML_DIR: {
--            tline = line;
--            gtype = *tline;
--            ++tline;
--            name = tline;
--            selector = strchr(tline, TAB);
--
--            if (selector) {
--                *selector = '\0';
--                ++selector;
--                host = strchr(selector, TAB);
--
--                if (host) {
--                    *host = '\0';
--                    ++host;
--                    port = strchr(host, TAB);
--
--                    if (port) {
--                        char *junk;
--                        port[0] = ':';
--                        junk = strchr(host, TAB);
--
--                        if (junk)
--                            *junk++ = 0;    /* Chop port */
--                        else {
--                            junk = strchr(host, '\r');
--
--                            if (junk)
--                                *junk++ = 0;    /* Chop port */
--                            else {
--                                junk = strchr(host, '\n');
--
--                                if (junk)
--                                    *junk++ = 0;    /* Chop port */
--                            }
--                        }
--
--                        if ((port[1] == '0') && (!port[2]))
--                            port[0] = 0;    /* 0 means none */
--                    }
--
--                    /* escape a selector here */
--                    escaped_selector = xstrdup(rfc1738_escape_part(selector));
--
--                    switch (gtype) {
--
--                    case GOPHER_DIRECTORY:
--                        icon_url = mimeGetIconURL("internal-menu");
--                        break;
--
--                    case GOPHER_HTML:
--
--                    case GOPHER_FILE:
--                        icon_url = mimeGetIconURL("internal-text");
--                        break;
--
--                    case GOPHER_INDEX:
--
--                    case GOPHER_CSO:
--                        icon_url = mimeGetIconURL("internal-index");
--                        break;
--
--                    case GOPHER_IMAGE:
--
--                    case GOPHER_GIF:
--
--                    case GOPHER_PLUS_IMAGE:
--                        icon_url = mimeGetIconURL("internal-image");
--                        break;
--
--                    case GOPHER_SOUND:
--
--                    case GOPHER_PLUS_SOUND:
--                        icon_url = mimeGetIconURL("internal-sound");
--                        break;
--
--                    case GOPHER_PLUS_MOVIE:
--                        icon_url = mimeGetIconURL("internal-movie");
--                        break;
--
--                    case GOPHER_TELNET:
--
--                    case GOPHER_3270:
--                        icon_url = mimeGetIconURL("internal-telnet");
--                        break;
--
--                    case GOPHER_BIN:
--
--                    case GOPHER_MACBINHEX:
--
--                    case GOPHER_DOSBIN:
--
--                    case GOPHER_UUENCODED:
--                        icon_url = mimeGetIconURL("internal-binary");
--                        break;
--
--                    case GOPHER_INFO:
--                        icon_url = NULL;
--                        break;
--
--                    default:
--                        icon_url = mimeGetIconURL("internal-unknown");
--                        break;
--                    }
--
--                    if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) {
--                        if (strlen(escaped_selector) != 0)
--                            outbuf.appendf(" %s\n",
--                                           icon_url, escaped_selector, rfc1738_escape_part(host),
--                                           *port ? ":" : "", port, html_quote(name));
--                        else
--                            outbuf.appendf(" %s\n",
--                                           icon_url, rfc1738_escape_part(host), *port ? ":" : "",
--                                           port, html_quote(name));
--
--                    } else if (gtype == GOPHER_INFO) {
--                        outbuf.appendf("\t%s\n", html_quote(name));
--                    } else {
--                        if (strncmp(selector, "GET /", 5) == 0) {
--                            /* WWW link */
--                            outbuf.appendf(" %s\n",
--                                           icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name));
--                        } else if (gtype == GOPHER_WWW) {
--                            outbuf.appendf(" %s\n",
--                                           icon_url, rfc1738_escape_unescaped(selector), html_quote(name));
--                        } else {
--                            /* Standard link */
--                            outbuf.appendf(" %s\n",
--                                           icon_url, host, gtype, escaped_selector, html_quote(name));
--                        }
--                    }
--
--                    safe_free(escaped_selector);
--                } else {
--                    memset(line, '\0', TEMP_BUF_SIZE);
--                    continue;
--                }
--            } else {
--                memset(line, '\0', TEMP_BUF_SIZE);
--                continue;
--            }
--
--            break;
--            }           /* HTML_DIR, HTML_INDEX_RESULT */
--
--        case GopherStateData::HTML_CSO_RESULT: {
--            if (line[0] == '-') {
--                int code, recno;
--                char *s_code, *s_recno, *result;
--
--                s_code = strtok(line + 1, ":\n");
--                s_recno = strtok(NULL, ":\n");
--                result = strtok(NULL, "\n");
--
--                if (!result)
--                    break;
--
--                code = atoi(s_code);
--
--                recno = atoi(s_recno);
--
--                if (code != 200)
--                    break;
--
--                if (gopherState->cso_recno != recno) {
--                    outbuf.appendf("

Record# %d
%s

\n
", recno, html_quote(result));
--                    gopherState->cso_recno = recno;
--                } else {
--                    outbuf.appendf("%s\n", html_quote(result));
--                }
--
--                break;
--            } else {
--                int code;
--                char *s_code, *result;
--
--                s_code = strtok(line, ":");
--                result = strtok(NULL, "\n");
--
--                if (!result)
--                    break;
--
--                code = atoi(s_code);
--
--                switch (code) {
--
--                case 200: {
--                    /* OK */
--                    /* Do nothing here */
--                    break;
--                }
--
--                case 102:   /* Number of matches */
--
--                case 501:   /* No Match */
--
--                case 502: { /* Too Many Matches */
--                    /* Print the message the server returns */
--                    outbuf.appendf("

%s

\n
", html_quote(result));
--                    break;
--                }
--
--                }
--            }
--
--            }           /* HTML_CSO_RESULT */
--
--        default:
--            break;      /* do nothing */
--
--        }           /* switch */
--
--    }               /* while loop */
--
--    if (outbuf.length() > 0) {
--        entry->append(outbuf.rawContent(), outbuf.length());
--        /* now let start sending stuff to client */
--        entry->flush();
--    }
--
--    return;
--}
--
--static void
--gopherTimeout(const CommTimeoutCbParams &io)
--{
--    GopherStateData *gopherState = static_cast(io.data);
--    debugs(10, 4, HERE << io.conn << ": '" << gopherState->entry->url() << "'" );
--
--    gopherState->fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, gopherState->fwd->request));
--
--    if (Comm::IsConnOpen(io.conn))
--        io.conn->close();
--}
--
--/**
-- * This will be called when data is ready to be read from fd.
-- * Read until error or connection closed.
-- */
--static void
--gopherReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
--{
--    GopherStateData *gopherState = (GopherStateData *)data;
--    StoreEntry *entry = gopherState->entry;
--    int clen;
--    int bin;
--    size_t read_sz = BUFSIZ;
--#if USE_DELAY_POOLS
--    DelayId delayId = entry->mem_obj->mostBytesAllowed();
--#endif
--
--    /* Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us */
--
--    if (flag == Comm::ERR_CLOSING) {
--        return;
--    }
--
--    assert(buf == gopherState->replybuf);
--
--    // XXX: Should update delayId, statCounter, etc. before bailing
--    if (!entry->isAccepting()) {
--        debugs(10, 3, "terminating due to bad " << *entry);
--        // TODO: Do not abuse connection for triggering cleanup.
--        gopherState->serverConn->close();
--        return;
--    }
--
--#if USE_DELAY_POOLS
--    read_sz = delayId.bytesWanted(1, read_sz);
--#endif
--
--    /* leave one space for \0 in gopherToHTML */
--
--    if (flag == Comm::OK && len > 0) {
--#if USE_DELAY_POOLS
--        delayId.bytesIn(len);
--#endif
--
--        statCounter.server.all.kbytes_in += len;
--        statCounter.server.other.kbytes_in += len;
--    }
--
--    debugs(10, 5, HERE << conn << " read len=" << len);
--
--    if (flag == Comm::OK && len > 0) {
--        AsyncCall::Pointer nil;
--        commSetConnTimeout(conn, Config.Timeout.read, nil);
--        ++IOStats.Gopher.reads;
--
--        for (clen = len - 1, bin = 0; clen; ++bin)
--            clen >>= 1;
--
--        ++IOStats.Gopher.read_hist[bin];
--
--        HttpRequest *req = gopherState->fwd->request;
--        if (req->hier.bodyBytesRead < 0) {
--            req->hier.bodyBytesRead = 0;
--            // first bytes read, update Reply flags:
--            gopherState->reply_->sources |= HttpMsg::srcGopher;
--        }
--
--        req->hier.bodyBytesRead += len;
--    }
--
--    if (flag != Comm::OK) {
--        debugs(50, DBG_IMPORTANT, MYNAME << "error reading: " << xstrerr(xerrno));
--
--        if (ignoreErrno(xerrno)) {
--            AsyncCall::Pointer call = commCbCall(5,4, "gopherReadReply",
--                                                 CommIoCbPtrFun(gopherReadReply, gopherState));
--            comm_read(conn, buf, read_sz, call);
--        } else {
--            ErrorState *err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, gopherState->fwd->request);
--            err->xerrno = xerrno;
--            gopherState->fwd->fail(err);
--            gopherState->serverConn->close();
--        }
--    } else if (len == 0 && entry->isEmpty()) {
--        gopherState->fwd->fail(new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, gopherState->fwd->request));
--        gopherState->serverConn->close();
--    } else if (len == 0) {
--        /* Connection closed; retrieval done. */
--        /* flush the rest of data in temp buf if there is one. */
--
--        if (gopherState->conversion != GopherStateData::NORMAL)
--            gopherEndHTML(gopherState);
--
--        entry->timestampsSet();
--        entry->flush();
--        gopherState->fwd->complete();
--        gopherState->serverConn->close();
--    } else {
--        if (gopherState->conversion != GopherStateData::NORMAL) {
--            gopherToHTML(gopherState, buf, len);
--        } else {
--            entry->append(buf, len);
--        }
--        AsyncCall::Pointer call = commCbCall(5,4, "gopherReadReply",
--                                             CommIoCbPtrFun(gopherReadReply, gopherState));
--        comm_read(conn, buf, read_sz, call);
--    }
--}
--
--/**
-- * This will be called when request write is complete. Schedule read of reply.
-- */
--static void
--gopherSendComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int xerrno, void *data)
--{
--    GopherStateData *gopherState = (GopherStateData *) data;
--    StoreEntry *entry = gopherState->entry;
--    debugs(10, 5, HERE << conn << " size: " << size << " errflag: " << errflag);
--
--    if (size > 0) {
--        fd_bytes(conn->fd, size, FD_WRITE);
--        statCounter.server.all.kbytes_out += size;
--        statCounter.server.other.kbytes_out += size;
--    }
--
--    if (!entry->isAccepting()) {
--        debugs(10, 3, "terminating due to bad " << *entry);
--        // TODO: Do not abuse connection for triggering cleanup.
--        gopherState->serverConn->close();
--        return;
--    }
--
--    if (errflag) {
--        ErrorState *err;
--        err = new ErrorState(ERR_WRITE_ERROR, Http::scServiceUnavailable, gopherState->fwd->request);
--        err->xerrno = xerrno;
--        err->port = gopherState->fwd->request->url.port();
--        err->url = xstrdup(entry->url());
--        gopherState->fwd->fail(err);
--        gopherState->serverConn->close();
--        return;
--    }
--
--    /*
--     * OK. We successfully reach remote site.  Start MIME typing
--     * stuff.  Do it anyway even though request is not HTML type.
--     */
--    entry->buffer();
--
--    gopherMimeCreate(gopherState);
--
--    switch (gopherState->type_id) {
--
--    case GOPHER_DIRECTORY:
--        /* we got to convert it first */
--        gopherState->conversion = GopherStateData::HTML_DIR;
--        gopherState->HTML_header_added = 0;
--        break;
--
--    case GOPHER_INDEX:
--        /* we got to convert it first */
--        gopherState->conversion = GopherStateData::HTML_INDEX_RESULT;
--        gopherState->HTML_header_added = 0;
--        break;
--
--    case GOPHER_CSO:
--        /* we got to convert it first */
--        gopherState->conversion = GopherStateData::HTML_CSO_RESULT;
--        gopherState->cso_recno = 0;
--        gopherState->HTML_header_added = 0;
--        break;
--
--    default:
--        gopherState->conversion = GopherStateData::NORMAL;
--        entry->flush();
--    }
--
--    /* Schedule read reply. */
--    AsyncCall::Pointer call =  commCbCall(5,5, "gopherReadReply",
--                                          CommIoCbPtrFun(gopherReadReply, gopherState));
--    entry->delayAwareRead(conn, gopherState->replybuf, BUFSIZ, call);
--}
--
--/**
-- * This will be called when connect completes. Write request.
-- */
--static void
--gopherSendRequest(int, void *data)
--{
--    GopherStateData *gopherState = (GopherStateData *)data;
--    MemBuf mb;
--    mb.init();
--
--    if (gopherState->type_id == GOPHER_CSO) {
--        const char *t = strchr(gopherState->request, '?');
--
--        if (t)
--            ++t;        /* skip the ? */
--        else
--            t = "";
--
--        mb.appendf("query %s\r\nquit", t);
--    } else {
--        if (gopherState->type_id == GOPHER_INDEX) {
--            if (char *t = strchr(gopherState->request, '?'))
--                *t = '\t';
--        }
--        mb.append(gopherState->request, strlen(gopherState->request));
--    }
--    mb.append("\r\n", 2);
--
--    debugs(10, 5, gopherState->serverConn);
--    AsyncCall::Pointer call = commCbCall(5,5, "gopherSendComplete",
--                                         CommIoCbPtrFun(gopherSendComplete, gopherState));
--    Comm::Write(gopherState->serverConn, &mb, call);
--
--    if (!gopherState->entry->makePublic())
--        gopherState->entry->makePrivate(true);
--}
--
--void
--gopherStart(FwdState * fwd)
--{
--    GopherStateData *gopherState = new GopherStateData(fwd);
--
--    debugs(10, 3, gopherState->entry->url());
--
--    ++ statCounter.server.all.requests;
--
--    ++ statCounter.server.other.requests;
--
--    /* Parse url. */
--    gopher_request_parse(fwd->request,
--                         &gopherState->type_id, gopherState->request);
--
--    comm_add_close_handler(fwd->serverConnection()->fd, gopherStateFree, gopherState);
--
--    if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
--            && (strchr(gopherState->request, '?') == NULL)) {
--        /* Index URL without query word */
--        /* We have to generate search page back to client. No need for connection */
--        gopherMimeCreate(gopherState);
--
--        if (gopherState->type_id == GOPHER_INDEX) {
--            gopherState->conversion = GopherStateData::HTML_INDEX_PAGE;
--        } else {
--            if (gopherState->type_id == GOPHER_CSO) {
--                gopherState->conversion = GopherStateData::HTML_CSO_PAGE;
--            } else {
--                gopherState->conversion = GopherStateData::HTML_INDEX_PAGE;
--            }
--        }
--
--        gopherToHTML(gopherState, (char *) NULL, 0);
--        fwd->complete();
--        return;
--    }
--
--    gopherState->serverConn = fwd->serverConnection();
--    gopherSendRequest(fwd->serverConnection()->fd, gopherState);
--    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "gopherTimeout",
--                                     CommTimeoutCbPtrFun(gopherTimeout, gopherState));
--    commSetConnTimeout(fwd->serverConnection(), Config.Timeout.read, timeoutCall);
--}
--
-diff --git a/src/gopher.h b/src/gopher.h
-deleted file mode 100644
-index 1d73bac..0000000
---- a/src/gopher.h
-+++ /dev/null
-@@ -1,29 +0,0 @@
--/*
-- * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
-- *
-- * Squid software is distributed under GPLv2+ license and includes
-- * contributions from numerous individuals and organizations.
-- * Please see the COPYING and CONTRIBUTORS files for details.
-- */
--
--/* DEBUG: section 10    Gopher */
--
--#ifndef SQUID_GOPHER_H_
--#define SQUID_GOPHER_H_
--
--class FwdState;
--class HttpRequest;
--
--/**
-- \defgroup ServerProtocolGopherAPI Server-Side Gopher API
-- \ingroup ServerProtocol
-- */
--
--/// \ingroup ServerProtocolGopherAPI
--void gopherStart(FwdState *);
--
--/// \ingroup ServerProtocolGopherAPI
--int gopherCachable(const HttpRequest *);
--
--#endif /* SQUID_GOPHER_H_ */
--
-diff --git a/src/mgr/IoAction.cc b/src/mgr/IoAction.cc
-index 149f2c4..e48a2e0 100644
---- a/src/mgr/IoAction.cc
-+++ b/src/mgr/IoAction.cc
-@@ -35,9 +35,6 @@ Mgr::IoActionData::operator += (const IoActionData& stats)
-     ftp_reads += stats.ftp_reads;
-     for (int i = 0; i < IoStats::histSize; ++i)
-         ftp_read_hist[i] += stats.ftp_read_hist[i];
--    gopher_reads += stats.gopher_reads;
--    for (int i = 0; i < IoStats::histSize; ++i)
--        gopher_read_hist[i] += stats.gopher_read_hist[i];
- 
-     return *this;
- }
-diff --git a/src/mgr/IoAction.h b/src/mgr/IoAction.h
-index 32de089..f11ade7 100644
---- a/src/mgr/IoAction.h
-+++ b/src/mgr/IoAction.h
-@@ -27,10 +27,8 @@ public:
- public:
-     double http_reads;
-     double ftp_reads;
--    double gopher_reads;
-     double http_read_hist[IoStats::histSize];
-     double ftp_read_hist[IoStats::histSize];
--    double gopher_read_hist[IoStats::histSize];
- };
- 
- /// implement aggregated 'io' action
-diff --git a/src/squid.8.in b/src/squid.8.in
-index 11135c3..bfffd91 100644
---- a/src/squid.8.in
-+++ b/src/squid.8.in
-@@ -25,7 +25,7 @@ command\-line
- .PP
- .B squid
- is a high\-performance proxy caching server for web clients,
--supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.
-+supporting FTP, ICAP, ICP, HTCP and HTTP data objects.
- Unlike traditional caching software, 
- Squid handles all requests in a single, non-blocking process.
- .PP
-diff --git a/src/stat.cc b/src/stat.cc
-index 8a59be4..4ed2c57 100644
---- a/src/stat.cc
-+++ b/src/stat.cc
-@@ -206,12 +206,6 @@ GetIoStats(Mgr::IoActionData& stats)
-     for (i = 0; i < IoStats::histSize; ++i) {
-         stats.ftp_read_hist[i] = IOStats.Ftp.read_hist[i];
-     }
--
--    stats.gopher_reads = IOStats.Gopher.reads;
--
--    for (i = 0; i < IoStats::histSize; ++i) {
--        stats.gopher_read_hist[i] = IOStats.Gopher.read_hist[i];
--    }
- }
- 
- void
-@@ -244,19 +238,6 @@ DumpIoStats(Mgr::IoActionData& stats, StoreEntry* sentry)
-                           Math::doublePercent(stats.ftp_read_hist[i], stats.ftp_reads));
-     }
- 
--    storeAppendPrintf(sentry, "\n");
--    storeAppendPrintf(sentry, "Gopher I/O\n");
--    storeAppendPrintf(sentry, "number of reads: %.0f\n", stats.gopher_reads);
--    storeAppendPrintf(sentry, "Read Histogram:\n");
--
--    for (i = 0; i < IoStats::histSize; ++i) {
--        storeAppendPrintf(sentry, "%5d-%5d: %9.0f %2.0f%%\n",
--                          i ? (1 << (i - 1)) + 1 : 1,
--                          1 << i,
--                          stats.gopher_read_hist[i],
--                          Math::doublePercent(stats.gopher_read_hist[i], stats.gopher_reads));
--    }
--
-     storeAppendPrintf(sentry, "\n");
- }
- 
-diff --git a/test-suite/squidconf/regressions-3.4.0.1 b/test-suite/squidconf/regressions-3.4.0.1
-index 41a441b..85f0a64 100644
---- a/test-suite/squidconf/regressions-3.4.0.1
-+++ b/test-suite/squidconf/regressions-3.4.0.1
-@@ -44,6 +44,5 @@ refresh_pattern -i \.(gif|png|jpg|jpeg|ico)$ 40320 75% 86400
- refresh_pattern -i \.(iso|avi|wav|mp3|mpeg|swf|flv|x-flv)$ 1440 40% 40320
- 
- refresh_pattern ^ftp:           1440    20%     10080
--refresh_pattern ^gopher:        1440    0%      1440
- refresh_pattern -i (/cgi-bin/|\?)       0       0%      0
- refresh_pattern .       0       20%     4320
diff --git a/SOURCES/squid-4.15-CVE-2023-46846.patch b/SOURCES/squid-4.15-CVE-2023-46846.patch
deleted file mode 100644
index 5738703..0000000
--- a/SOURCES/squid-4.15-CVE-2023-46846.patch
+++ /dev/null
@@ -1,1281 +0,0 @@
-diff --git a/src/adaptation/icap/ModXact.cc b/src/adaptation/icap/ModXact.cc
-index 2db0a68..370f077 100644
---- a/src/adaptation/icap/ModXact.cc
-+++ b/src/adaptation/icap/ModXact.cc
-@@ -25,12 +25,13 @@
- #include "comm.h"
- #include "comm/Connection.h"
- #include "err_detail_type.h"
--#include "http/one/TeChunkedParser.h"
- #include "HttpHeaderTools.h"
- #include "HttpMsg.h"
- #include "HttpReply.h"
- #include "HttpRequest.h"
- #include "MasterXaction.h"
-+#include "parser/Tokenizer.h"
-+#include "sbuf/Stream.h"
- #include "SquidTime.h"
- 
- // flow and terminology:
-@@ -44,6 +45,8 @@ CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, ModXactLauncher);
- 
- static const size_t TheBackupLimit = BodyPipe::MaxCapacity;
- 
-+const SBuf Adaptation::Icap::ChunkExtensionValueParser::UseOriginalBodyName("use-original-body");
-+
- Adaptation::Icap::ModXact::State::State()
- {
-     memset(this, 0, sizeof(*this));
-@@ -1108,6 +1111,7 @@ void Adaptation::Icap::ModXact::decideOnParsingBody()
-         state.parsing = State::psBody;
-         replyHttpBodySize = 0;
-         bodyParser = new Http1::TeChunkedParser;
-+        bodyParser->parseExtensionValuesWith(&extensionParser);
-         makeAdaptedBodyPipe("adapted response from the ICAP server");
-         Must(state.sending == State::sendingAdapted);
-     } else {
-@@ -1142,9 +1146,8 @@ void Adaptation::Icap::ModXact::parseBody()
-     }
- 
-     if (parsed) {
--        if (state.readyForUob && bodyParser->useOriginBody >= 0) {
--            prepPartialBodyEchoing(
--                static_cast(bodyParser->useOriginBody));
-+        if (state.readyForUob && extensionParser.sawUseOriginalBody()) {
-+            prepPartialBodyEchoing(extensionParser.useOriginalBody());
-             stopParsing();
-             return;
-         }
-@@ -2014,3 +2017,14 @@ void Adaptation::Icap::ModXactLauncher::updateHistory(bool doStart)
-     }
- }
- 
-+void
-+Adaptation::Icap::ChunkExtensionValueParser::parse(Tokenizer &tok, const SBuf &extName)
-+{
-+    if (extName == UseOriginalBodyName) {
-+        useOriginalBody_ = tok.udec64("use-original-body");
-+        assert(useOriginalBody_ >= 0);
-+    } else {
-+        Ignore(tok, extName);
-+    }
-+}
-+
-diff --git a/src/adaptation/icap/ModXact.h b/src/adaptation/icap/ModXact.h
-index f7afa69..fb4dec0 100644
---- a/src/adaptation/icap/ModXact.h
-+++ b/src/adaptation/icap/ModXact.h
-@@ -15,6 +15,7 @@
- #include "adaptation/icap/Xaction.h"
- #include "BodyPipe.h"
- #include "http/one/forward.h"
-+#include "http/one/TeChunkedParser.h"
- 
- /*
-  * ICAPModXact implements ICAP REQMOD and RESPMOD transaction using
-@@ -105,6 +106,23 @@ private:
-     enum State { stDisabled, stWriting, stIeof, stDone } theState;
- };
- 
-+/// handles ICAP-specific chunk extensions supported by Squid
-+class ChunkExtensionValueParser: public Http1::ChunkExtensionValueParser
-+{
-+public:
-+    /* Http1::ChunkExtensionValueParser API */
-+    virtual void parse(Tokenizer &tok, const SBuf &extName) override;
-+
-+    bool sawUseOriginalBody() const { return useOriginalBody_ >= 0; }
-+    uint64_t useOriginalBody() const { assert(sawUseOriginalBody()); return static_cast(useOriginalBody_); }
-+
-+private:
-+    static const SBuf UseOriginalBodyName;
-+
-+    /// the value of the parsed use-original-body chunk extension (or -1)
-+    int64_t useOriginalBody_ = -1;
-+};
-+
- class ModXact: public Xaction, public BodyProducer, public BodyConsumer
- {
-     CBDATA_CLASS(ModXact);
-@@ -270,6 +288,8 @@ private:
- 
-     int adaptHistoryId; ///< adaptation history slot reservation
- 
-+    ChunkExtensionValueParser extensionParser;
-+
-     class State
-     {
- 
-diff --git a/src/http/one/Parser.cc b/src/http/one/Parser.cc
-index 0c86733..affe0b1 100644
---- a/src/http/one/Parser.cc
-+++ b/src/http/one/Parser.cc
-@@ -7,10 +7,11 @@
-  */
- 
- #include "squid.h"
-+#include "base/CharacterSet.h"
- #include "Debug.h"
- #include "http/one/Parser.h"
--#include "http/one/Tokenizer.h"
- #include "mime_header.h"
-+#include "parser/Tokenizer.h"
- #include "SquidConfig.h"
- 
- /// RFC 7230 section 2.6 - 7 magic octets
-@@ -61,20 +62,19 @@ Http::One::Parser::DelimiterCharacters()
-            RelaxedDelimiterCharacters() : CharacterSet::SP;
- }
- 
--bool
--Http::One::Parser::skipLineTerminator(Http1::Tokenizer &tok) const
-+void
-+Http::One::Parser::skipLineTerminator(Tokenizer &tok) const
- {
-     if (tok.skip(Http1::CrLf()))
--        return true;
-+        return;
- 
-     if (Config.onoff.relaxed_header_parser && tok.skipOne(CharacterSet::LF))
--        return true;
-+        return;
- 
-     if (tok.atEnd() || (tok.remaining().length() == 1 && tok.remaining().at(0) == '\r'))
--        return false; // need more data
-+        throw InsufficientInput();
- 
-     throw TexcHere("garbage instead of CRLF line terminator");
--    return false; // unreachable, but make naive compilers happy
- }
- 
- /// all characters except the LF line terminator
-@@ -102,7 +102,7 @@ LineCharacters()
- void
- Http::One::Parser::cleanMimePrefix()
- {
--    Http1::Tokenizer tok(mimeHeaderBlock_);
-+    Tokenizer tok(mimeHeaderBlock_);
-     while (tok.skipOne(RelaxedDelimiterCharacters())) {
-         (void)tok.skipAll(LineCharacters()); // optional line content
-         // LF terminator is required.
-@@ -137,7 +137,7 @@ Http::One::Parser::cleanMimePrefix()
- void
- Http::One::Parser::unfoldMime()
- {
--    Http1::Tokenizer tok(mimeHeaderBlock_);
-+    Tokenizer tok(mimeHeaderBlock_);
-     const auto szLimit = mimeHeaderBlock_.length();
-     mimeHeaderBlock_.clear();
-     // prevent the mime sender being able to make append() realloc/grow multiple times.
-@@ -228,7 +228,7 @@ Http::One::Parser::getHostHeaderField()
-     debugs(25, 5, "looking for " << name);
- 
-     // while we can find more LF in the SBuf
--    Http1::Tokenizer tok(mimeHeaderBlock_);
-+    Tokenizer tok(mimeHeaderBlock_);
-     SBuf p;
- 
-     while (tok.prefix(p, LineCharacters())) {
-@@ -250,7 +250,7 @@ Http::One::Parser::getHostHeaderField()
-         p.consume(namelen + 1);
- 
-         // TODO: optimize SBuf::trim to take CharacterSet directly
--        Http1::Tokenizer t(p);
-+        Tokenizer t(p);
-         t.skipAll(CharacterSet::WSP);
-         p = t.remaining();
- 
-@@ -278,10 +278,15 @@ Http::One::ErrorLevel()
- }
- 
- // BWS = *( SP / HTAB ) ; WhitespaceCharacters() may relax this RFC 7230 rule
--bool
--Http::One::ParseBws(Tokenizer &tok)
-+void
-+Http::One::ParseBws(Parser::Tokenizer &tok)
- {
--    if (const auto count = tok.skipAll(Parser::WhitespaceCharacters())) {
-+    const auto count = tok.skipAll(Parser::WhitespaceCharacters());
-+
-+    if (tok.atEnd())
-+        throw InsufficientInput(); // even if count is positive
-+
-+    if (count) {
-         // Generating BWS is a MUST-level violation so warn about it as needed.
-         debugs(33, ErrorLevel(), "found " << count << " BWS octets");
-         // RFC 7230 says we MUST parse BWS, so we fall through even if
-@@ -289,6 +294,6 @@ Http::One::ParseBws(Tokenizer &tok)
-     }
-     // else we successfully "parsed" an empty BWS sequence
- 
--    return true;
-+    // success: no more BWS characters expected
- }
- 
-diff --git a/src/http/one/Parser.h b/src/http/one/Parser.h
-index 58a5cae..40e281b 100644
---- a/src/http/one/Parser.h
-+++ b/src/http/one/Parser.h
-@@ -12,6 +12,7 @@
- #include "anyp/ProtocolVersion.h"
- #include "http/one/forward.h"
- #include "http/StatusCode.h"
-+#include "parser/forward.h"
- #include "sbuf/SBuf.h"
- 
- namespace Http {
-@@ -40,6 +41,7 @@ class Parser : public RefCountable
- {
- public:
-     typedef SBuf::size_type size_type;
-+    typedef ::Parser::Tokenizer Tokenizer;
- 
-     Parser() : parseStatusCode(Http::scNone), parsingStage_(HTTP_PARSE_NONE), hackExpectsMime_(false) {}
-     virtual ~Parser() {}
-@@ -118,11 +120,11 @@ protected:
-      * detect and skip the CRLF or (if tolerant) LF line terminator
-      * consume from the tokenizer.
-      *
--     * throws if non-terminator is detected.
-+     * \throws exception on bad or InsuffientInput.
-      * \retval true only if line terminator found.
-      * \retval false incomplete or missing line terminator, need more data.
-      */
--    bool skipLineTerminator(Http1::Tokenizer &tok) const;
-+    void skipLineTerminator(Tokenizer &) const;
- 
-     /**
-      * Scan to find the mime headers block for current message.
-@@ -159,8 +161,8 @@ private:
- };
- 
- /// skips and, if needed, warns about RFC 7230 BWS ("bad" whitespace)
--/// \returns true (always; unlike all the skip*() functions)
--bool ParseBws(Tokenizer &tok);
-+/// \throws InsufficientInput when the end of BWS cannot be confirmed
-+void ParseBws(Parser::Tokenizer &);
- 
- /// the right debugs() level for logging HTTP violation messages
- int ErrorLevel();
-diff --git a/src/http/one/RequestParser.cc b/src/http/one/RequestParser.cc
-index a325f7d..0f13c92 100644
---- a/src/http/one/RequestParser.cc
-+++ b/src/http/one/RequestParser.cc
-@@ -9,8 +9,8 @@
- #include "squid.h"
- #include "Debug.h"
- #include "http/one/RequestParser.h"
--#include "http/one/Tokenizer.h"
- #include "http/ProtocolVersion.h"
-+#include "parser/Tokenizer.h"
- #include "profiler/Profiler.h"
- #include "SquidConfig.h"
- 
-@@ -64,7 +64,7 @@ Http::One::RequestParser::skipGarbageLines()
-  *  RFC 7230 section 2.6, 3.1 and 3.5
-  */
- bool
--Http::One::RequestParser::parseMethodField(Http1::Tokenizer &tok)
-+Http::One::RequestParser::parseMethodField(Tokenizer &tok)
- {
-     // method field is a sequence of TCHAR.
-     // Limit to 32 characters to prevent overly long sequences of non-HTTP
-@@ -145,7 +145,7 @@ Http::One::RequestParser::RequestTargetCharacters()
- }
- 
- bool
--Http::One::RequestParser::parseUriField(Http1::Tokenizer &tok)
-+Http::One::RequestParser::parseUriField(Tokenizer &tok)
- {
-     /* Arbitrary 64KB URI upper length limit.
-      *
-@@ -178,7 +178,7 @@ Http::One::RequestParser::parseUriField(Http1::Tokenizer &tok)
- }
- 
- bool
--Http::One::RequestParser::parseHttpVersionField(Http1::Tokenizer &tok)
-+Http::One::RequestParser::parseHttpVersionField(Tokenizer &tok)
- {
-     static const SBuf http1p0("HTTP/1.0");
-     static const SBuf http1p1("HTTP/1.1");
-@@ -253,7 +253,7 @@ Http::One::RequestParser::skipDelimiter(const size_t count, const char *where)
- 
- /// Parse CRs at the end of request-line, just before the terminating LF.
- bool
--Http::One::RequestParser::skipTrailingCrs(Http1::Tokenizer &tok)
-+Http::One::RequestParser::skipTrailingCrs(Tokenizer &tok)
- {
-     if (Config.onoff.relaxed_header_parser) {
-         (void)tok.skipAllTrailing(CharacterSet::CR); // optional; multiple OK
-@@ -289,12 +289,12 @@ Http::One::RequestParser::parseRequestFirstLine()
-     // Earlier, skipGarbageLines() took care of any leading LFs (if allowed).
-     // Now, the request line has to end at the first LF.
-     static const CharacterSet lineChars = CharacterSet::LF.complement("notLF");
--    ::Parser::Tokenizer lineTok(buf_);
-+    Tokenizer lineTok(buf_);
-     if (!lineTok.prefix(line, lineChars) || !lineTok.skip('\n')) {
-         if (buf_.length() >= Config.maxRequestHeaderSize) {
-             /* who should we blame for our failure to parse this line? */
- 
--            Http1::Tokenizer methodTok(buf_);
-+            Tokenizer methodTok(buf_);
-             if (!parseMethodField(methodTok))
-                 return -1; // blame a bad method (or its delimiter)
- 
-@@ -308,7 +308,7 @@ Http::One::RequestParser::parseRequestFirstLine()
-         return 0;
-     }
- 
--    Http1::Tokenizer tok(line);
-+    Tokenizer tok(line);
- 
-     if (!parseMethodField(tok))
-         return -1;
-diff --git a/src/http/one/RequestParser.h b/src/http/one/RequestParser.h
-index 7086548..26697cd 100644
---- a/src/http/one/RequestParser.h
-+++ b/src/http/one/RequestParser.h
-@@ -54,11 +54,11 @@ private:
-     bool doParse(const SBuf &aBuf);
- 
-     /* all these return false and set parseStatusCode on parsing failures */
--    bool parseMethodField(Http1::Tokenizer &);
--    bool parseUriField(Http1::Tokenizer &);
--    bool parseHttpVersionField(Http1::Tokenizer &);
-+    bool parseMethodField(Tokenizer &);
-+    bool parseUriField(Tokenizer &);
-+    bool parseHttpVersionField(Tokenizer &);
-     bool skipDelimiter(const size_t count, const char *where);
--    bool skipTrailingCrs(Http1::Tokenizer &tok);
-+    bool skipTrailingCrs(Tokenizer &tok);
- 
-     bool http0() const {return !msgProtocol_.major;}
-     static const CharacterSet &RequestTargetCharacters();
-diff --git a/src/http/one/ResponseParser.cc b/src/http/one/ResponseParser.cc
-index 24af849..65baf09 100644
---- a/src/http/one/ResponseParser.cc
-+++ b/src/http/one/ResponseParser.cc
-@@ -9,8 +9,8 @@
- #include "squid.h"
- #include "Debug.h"
- #include "http/one/ResponseParser.h"
--#include "http/one/Tokenizer.h"
- #include "http/ProtocolVersion.h"
-+#include "parser/Tokenizer.h"
- #include "profiler/Profiler.h"
- #include "SquidConfig.h"
- 
-@@ -47,7 +47,7 @@ Http::One::ResponseParser::firstLineSize() const
- // NP: we found the protocol version and consumed it already.
- // just need the status code and reason phrase
- int
--Http::One::ResponseParser::parseResponseStatusAndReason(Http1::Tokenizer &tok, const CharacterSet &WspDelim)
-+Http::One::ResponseParser::parseResponseStatusAndReason(Tokenizer &tok, const CharacterSet &WspDelim)
- {
-     if (!completedStatus_) {
-         debugs(74, 9, "seek status-code in: " << tok.remaining().substr(0,10) << "...");
-@@ -87,14 +87,13 @@ Http::One::ResponseParser::parseResponseStatusAndReason(Http1::Tokenizer &tok, c
-     static const CharacterSet phraseChars = CharacterSet::WSP + CharacterSet::VCHAR + CharacterSet::OBSTEXT;
-     (void)tok.prefix(reasonPhrase_, phraseChars); // optional, no error if missing
-     try {
--        if (skipLineTerminator(tok)) {
--            debugs(74, DBG_DATA, "parse remaining buf={length=" << tok.remaining().length() << ", data='" << tok.remaining() << "'}");
--            buf_ = tok.remaining(); // resume checkpoint
--            return 1;
--        }
-+        skipLineTerminator(tok);
-+        buf_ = tok.remaining(); // resume checkpoint
-+        debugs(74, DBG_DATA, Raw("leftovers", buf_.rawContent(), buf_.length()));
-+        return 1;
-+    } catch (const InsufficientInput &) {
-         reasonPhrase_.clear();
-         return 0; // need more to be sure we have it all
--
-     } catch (const std::exception &ex) {
-         debugs(74, 6, "invalid status-line: " << ex.what());
-     }
-@@ -119,7 +118,7 @@ Http::One::ResponseParser::parseResponseStatusAndReason(Http1::Tokenizer &tok, c
- int
- Http::One::ResponseParser::parseResponseFirstLine()
- {
--    Http1::Tokenizer tok(buf_);
-+    Tokenizer tok(buf_);
- 
-     const CharacterSet &WspDelim = DelimiterCharacters();
- 
-diff --git a/src/http/one/ResponseParser.h b/src/http/one/ResponseParser.h
-index 15db4a0..cf13b4d 100644
---- a/src/http/one/ResponseParser.h
-+++ b/src/http/one/ResponseParser.h
-@@ -43,7 +43,7 @@ public:
- 
- private:
-     int parseResponseFirstLine();
--    int parseResponseStatusAndReason(Http1::Tokenizer&, const CharacterSet &);
-+    int parseResponseStatusAndReason(Tokenizer&, const CharacterSet &);
- 
-     /// magic prefix for identifying ICY response messages
-     static const SBuf IcyMagic;
-diff --git a/src/http/one/TeChunkedParser.cc b/src/http/one/TeChunkedParser.cc
-index 754086e..6d2f8ea 100644
---- a/src/http/one/TeChunkedParser.cc
-+++ b/src/http/one/TeChunkedParser.cc
-@@ -13,10 +13,13 @@
- #include "http/one/Tokenizer.h"
- #include "http/ProtocolVersion.h"
- #include "MemBuf.h"
-+#include "parser/Tokenizer.h"
- #include "Parsing.h"
-+#include "sbuf/Stream.h"
- #include "SquidConfig.h"
- 
--Http::One::TeChunkedParser::TeChunkedParser()
-+Http::One::TeChunkedParser::TeChunkedParser():
-+    customExtensionValueParser(nullptr)
- {
-     // chunked encoding only exists in HTTP/1.1
-     Http1::Parser::msgProtocol_ = Http::ProtocolVersion(1,1);
-@@ -31,7 +34,11 @@ Http::One::TeChunkedParser::clear()
-     buf_.clear();
-     theChunkSize = theLeftBodySize = 0;
-     theOut = NULL;
--    useOriginBody = -1;
-+    // XXX: We do not reset customExtensionValueParser here. Based on the
-+    // clear() API description, we must, but it makes little sense and could
-+    // break method callers if they appear because some of them may forget to
-+    // reset customExtensionValueParser. TODO: Remove Http1::Parser as our
-+    // parent class and this unnecessary method with it.
- }
- 
- bool
-@@ -49,14 +56,14 @@ Http::One::TeChunkedParser::parse(const SBuf &aBuf)
-     if (parsingStage_ == Http1::HTTP_PARSE_NONE)
-         parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ;
- 
--    Http1::Tokenizer tok(buf_);
-+    Tokenizer tok(buf_);
- 
-     // loop for as many chunks as we can
-     // use do-while instead of while so that we can incrementally
-     // restart in the middle of a chunk/frame
-     do {
- 
--        if (parsingStage_ == Http1::HTTP_PARSE_CHUNK_EXT && !parseChunkExtension(tok, theChunkSize))
-+        if (parsingStage_ == Http1::HTTP_PARSE_CHUNK_EXT && !parseChunkMetadataSuffix(tok))
-             return false;
- 
-         if (parsingStage_ == Http1::HTTP_PARSE_CHUNK && !parseChunkBody(tok))
-@@ -80,7 +87,7 @@ Http::One::TeChunkedParser::needsMoreSpace() const
- 
- /// RFC 7230 section 4.1 chunk-size
- bool
--Http::One::TeChunkedParser::parseChunkSize(Http1::Tokenizer &tok)
-+Http::One::TeChunkedParser::parseChunkSize(Tokenizer &tok)
- {
-     Must(theChunkSize <= 0); // Should(), really
- 
-@@ -104,66 +111,75 @@ Http::One::TeChunkedParser::parseChunkSize(Http1::Tokenizer &tok)
-     return false; // should not be reachable
- }
- 
--/**
-- * Parses chunk metadata suffix, looking for interesting extensions and/or
-- * getting to the line terminator. RFC 7230 section 4.1.1 and its Errata #4667:
-- *
-- *   chunk-ext = *( BWS  ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
-- *   chunk-ext-name = token
-- *   chunk-ext-val  = token / quoted-string
-- *
-- * ICAP 'use-original-body=N' extension is supported.
-- */
-+/// Parses "[chunk-ext] CRLF" from RFC 7230 section 4.1.1:
-+///   chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
-+///   last-chunk = 1*"0" [ chunk-ext ] CRLF
- bool
--Http::One::TeChunkedParser::parseChunkExtension(Http1::Tokenizer &tok, bool skipKnown)
-+Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
- {
--    SBuf ext;
--    SBuf value;
--    while (
--        ParseBws(tok) && // Bug 4492: IBM_HTTP_Server sends SP after chunk-size
--        tok.skip(';') &&
--        ParseBws(tok) && // Bug 4492: ICAP servers send SP before chunk-ext-name
--        tok.prefix(ext, CharacterSet::TCHAR)) { // chunk-ext-name
--
--        // whole value part is optional. if no '=' expect next chunk-ext
--        if (ParseBws(tok) && tok.skip('=') && ParseBws(tok)) {
--
--            if (!skipKnown) {
--                if (ext.cmp("use-original-body",17) == 0 && tok.int64(useOriginBody, 10)) {
--                    debugs(94, 3, "Found chunk extension " << ext << "=" << useOriginBody);
--                    buf_ = tok.remaining(); // parse checkpoint
--                    continue;
--                }
--            }
--
--            debugs(94, 5, "skipping unknown chunk extension " << ext);
--
--            // unknown might have a value token or quoted-string
--            if (tok.quotedStringOrToken(value) && !tok.atEnd()) {
--                buf_ = tok.remaining(); // parse checkpoint
--                continue;
--            }
--
--            // otherwise need more data OR corrupt syntax
--            break;
--        }
--
--        if (!tok.atEnd())
--            buf_ = tok.remaining(); // parse checkpoint (unless there might be more token name)
--    }
--
--    if (skipLineTerminator(tok)) {
--        buf_ = tok.remaining(); // checkpoint
--        // non-0 chunk means data, 0-size means optional Trailer follows
-+    // Code becomes much simpler when incremental parsing functions throw on
-+    // bad or insufficient input, like in the code below. TODO: Expand up.
-+    try {
-+        parseChunkExtensions(tok); // a possibly empty chunk-ext list
-+        skipLineTerminator(tok);
-+        buf_ = tok.remaining();
-         parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME;
-         return true;
-+    } catch (const InsufficientInput &) {
-+        tok.reset(buf_); // backtrack to the last commit point
-+        return false;
-     }
-+    // other exceptions bubble up to kill message parsing
-+}
- 
--    return false;
-+/// Parses the chunk-ext list (RFC 7230 section 4.1.1 and its Errata #4667):
-+/// chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
-+void
-+Http::One::TeChunkedParser::parseChunkExtensions(Tokenizer &tok)
-+{
-+    do {
-+        ParseBws(tok); // Bug 4492: IBM_HTTP_Server sends SP after chunk-size
-+
-+        if (!tok.skip(';'))
-+            return; // reached the end of extensions (if any)
-+
-+        parseOneChunkExtension(tok);
-+        buf_ = tok.remaining(); // got one extension
-+    } while (true);
-+}
-+
-+void
-+Http::One::ChunkExtensionValueParser::Ignore(Tokenizer &tok, const SBuf &extName)
-+{
-+    const auto ignoredValue = tokenOrQuotedString(tok);
-+    debugs(94, 5, extName << " with value " << ignoredValue);
-+}
-+
-+/// Parses a single chunk-ext list element:
-+/// chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
-+void
-+Http::One::TeChunkedParser::parseOneChunkExtension(Tokenizer &tok)
-+{
-+    ParseBws(tok); // Bug 4492: ICAP servers send SP before chunk-ext-name
-+
-+    const auto extName = tok.prefix("chunk-ext-name", CharacterSet::TCHAR);
-+
-+    ParseBws(tok);
-+
-+    if (!tok.skip('='))
-+        return; // parsed a valueless chunk-ext
-+
-+    ParseBws(tok);
-+
-+    // optimization: the only currently supported extension needs last-chunk
-+    if (!theChunkSize && customExtensionValueParser)
-+        customExtensionValueParser->parse(tok, extName);
-+    else
-+        ChunkExtensionValueParser::Ignore(tok, extName);
- }
- 
- bool
--Http::One::TeChunkedParser::parseChunkBody(Http1::Tokenizer &tok)
-+Http::One::TeChunkedParser::parseChunkBody(Tokenizer &tok)
- {
-     if (theLeftBodySize > 0) {
-         buf_ = tok.remaining(); // sync buffers before buf_ use
-@@ -188,17 +204,20 @@ Http::One::TeChunkedParser::parseChunkBody(Http1::Tokenizer &tok)
- }
- 
- bool
--Http::One::TeChunkedParser::parseChunkEnd(Http1::Tokenizer &tok)
-+Http::One::TeChunkedParser::parseChunkEnd(Tokenizer &tok)
- {
-     Must(theLeftBodySize == 0); // Should(), really
- 
--    if (skipLineTerminator(tok)) {
-+    try {
-+        skipLineTerminator(tok);
-         buf_ = tok.remaining(); // parse checkpoint
-         theChunkSize = 0; // done with the current chunk
-         parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ;
-         return true;
-     }
--
--    return false;
-+    catch (const InsufficientInput &) {
-+        return false;
-+    }
-+    // other exceptions bubble up to kill message parsing
- }
- 
-diff --git a/src/http/one/TeChunkedParser.h b/src/http/one/TeChunkedParser.h
-index 1b0319e..2ca8988 100644
---- a/src/http/one/TeChunkedParser.h
-+++ b/src/http/one/TeChunkedParser.h
-@@ -18,6 +18,26 @@ namespace Http
- namespace One
- {
- 
-+using ::Parser::InsufficientInput;
-+
-+// TODO: Move this class into http/one/ChunkExtensionValueParser.*
-+/// A customizable parser of a single chunk extension value (chunk-ext-val).
-+/// From RFC 7230 section 4.1.1 and its Errata #4667:
-+/// chunk-ext = *( BWS  ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
-+/// chunk-ext-name = token
-+/// chunk-ext-val  = token / quoted-string
-+class ChunkExtensionValueParser
-+{
-+public:
-+    typedef ::Parser::Tokenizer Tokenizer;
-+
-+    /// extracts and ignores the value of a named extension
-+    static void Ignore(Tokenizer &tok, const SBuf &extName);
-+
-+    /// extracts and then interprets (or ignores) the extension value
-+    virtual void parse(Tokenizer &tok, const SBuf &extName) = 0;
-+};
-+
- /**
-  * An incremental parser for chunked transfer coding
-  * defined in RFC 7230 section 4.1.
-@@ -25,7 +45,7 @@ namespace One
-  *
-  * The parser shovels content bytes from the raw
-  * input buffer into the content output buffer, both caller-supplied.
-- * Ignores chunk extensions except for ICAP's ieof.
-+ * Chunk extensions like use-original-body are handled via parseExtensionValuesWith().
-  * Trailers are available via mimeHeader() if wanted.
-  */
- class TeChunkedParser : public Http1::Parser
-@@ -37,6 +57,10 @@ public:
-     /// set the buffer to be used to store decoded chunk data
-     void setPayloadBuffer(MemBuf *parsedContent) {theOut = parsedContent;}
- 
-+    /// Instead of ignoring all chunk extension values, give the supplied
-+    /// parser a chance to handle them. Only applied to last-chunk (for now).
-+    void parseExtensionValuesWith(ChunkExtensionValueParser *parser) { customExtensionValueParser = parser; }
-+
-     bool needsMoreSpace() const;
- 
-     /* Http1::Parser API */
-@@ -45,17 +69,20 @@ public:
-     virtual Parser::size_type firstLineSize() const {return 0;} // has no meaning with multiple chunks
- 
- private:
--    bool parseChunkSize(Http1::Tokenizer &tok);
--    bool parseChunkExtension(Http1::Tokenizer &tok, bool skipKnown);
--    bool parseChunkBody(Http1::Tokenizer &tok);
--    bool parseChunkEnd(Http1::Tokenizer &tok);
-+    bool parseChunkSize(Tokenizer &tok);
-+    bool parseChunkMetadataSuffix(Tokenizer &);
-+    void parseChunkExtensions(Tokenizer &);
-+    void parseOneChunkExtension(Tokenizer &);
-+    bool parseChunkBody(Tokenizer &tok);
-+    bool parseChunkEnd(Tokenizer &tok);
- 
-     MemBuf *theOut;
-     uint64_t theChunkSize;
-     uint64_t theLeftBodySize;
- 
--public:
--    int64_t useOriginBody;
-+    /// An optional plugin for parsing and interpreting custom chunk-ext-val.
-+    /// This "visitor" object is owned by our creator.
-+    ChunkExtensionValueParser *customExtensionValueParser;
- };
- 
- } // namespace One
-diff --git a/src/http/one/Tokenizer.cc b/src/http/one/Tokenizer.cc
-index 804b8e1..3a6bef3 100644
---- a/src/http/one/Tokenizer.cc
-+++ b/src/http/one/Tokenizer.cc
-@@ -8,35 +8,18 @@
- 
- #include "squid.h"
- #include "Debug.h"
-+#include "http/one/Parser.h"
- #include "http/one/Tokenizer.h"
--
--bool
--Http::One::Tokenizer::quotedString(SBuf &returnedToken, const bool http1p0)
--{
--    checkpoint();
--
--    if (!skip('"'))
--        return false;
--
--    return qdText(returnedToken, http1p0);
--}
--
--bool
--Http::One::Tokenizer::quotedStringOrToken(SBuf &returnedToken, const bool http1p0)
-+#include "parser/Tokenizer.h"
-+#include "sbuf/Stream.h"
-+
-+/// Extracts quoted-string after the caller removes the initial '"'.
-+/// \param http1p0 whether to prohibit \-escaped characters in quoted strings
-+/// \throws InsufficientInput when input can be a token _prefix_
-+/// \returns extracted quoted string (without quotes and with chars unescaped)
-+static SBuf
-+parseQuotedStringSuffix(Parser::Tokenizer &tok, const bool http1p0)
- {
--    checkpoint();
--
--    if (!skip('"'))
--        return prefix(returnedToken, CharacterSet::TCHAR);
--
--    return qdText(returnedToken, http1p0);
--}
--
--bool
--Http::One::Tokenizer::qdText(SBuf &returnedToken, const bool http1p0)
--{
--    // the initial DQUOTE has been skipped by the caller
--
-     /*
-      * RFC 1945 - defines qdtext:
-      *   inclusive of LWS (which includes CR and LF)
-@@ -61,12 +44,17 @@ Http::One::Tokenizer::qdText(SBuf &returnedToken, const bool http1p0)
-     // best we can do is a conditional reference since http1p0 value may change per-client
-     const CharacterSet &tokenChars = (http1p0 ? qdtext1p0 : qdtext1p1);
- 
--    for (;;) {
--        SBuf::size_type prefixLen = buf().findFirstNotOf(tokenChars);
--        returnedToken.append(consume(prefixLen));
-+    SBuf parsedToken;
-+
-+    while (!tok.atEnd()) {
-+        SBuf qdText;
-+        if (tok.prefix(qdText, tokenChars))
-+            parsedToken.append(qdText);
-+
-+        if (!http1p0 && tok.skip('\\')) { // HTTP/1.1 allows quoted-pair, HTTP/1.0 does not
-+            if (tok.atEnd())
-+                break;
- 
--        // HTTP/1.1 allows quoted-pair, HTTP/1.0 does not
--        if (!http1p0 && skip('\\')) {
-             /* RFC 7230 section 3.2.6
-              *
-              * The backslash octet ("\") can be used as a single-octet quoting
-@@ -78,32 +66,42 @@ Http::One::Tokenizer::qdText(SBuf &returnedToken, const bool http1p0)
-              */
-             static const CharacterSet qPairChars = CharacterSet::HTAB + CharacterSet::SP + CharacterSet::VCHAR + CharacterSet::OBSTEXT;
-             SBuf escaped;
--            if (!prefix(escaped, qPairChars, 1)) {
--                returnedToken.clear();
--                restoreLastCheckpoint();
--                return false;
--            }
--            returnedToken.append(escaped);
-+            if (!tok.prefix(escaped, qPairChars, 1))
-+                throw TexcHere("invalid escaped character in quoted-pair");
-+
-+            parsedToken.append(escaped);
-             continue;
-+        }
- 
--        } else if (skip('"')) {
--            break; // done
-+        if (tok.skip('"'))
-+            return parsedToken; // may be empty
- 
--        } else if (atEnd()) {
--            // need more data
--            returnedToken.clear();
--            restoreLastCheckpoint();
--            return false;
--        }
-+        if (tok.atEnd())
-+            break;
- 
--        // else, we have an error
--        debugs(24, 8, "invalid bytes for set " << tokenChars.name);
--        returnedToken.clear();
--        restoreLastCheckpoint();
--        return false;
-+        throw TexcHere(ToSBuf("invalid bytes for set ", tokenChars.name));
-     }
- 
--    // found the whole string
--    return true;
-+    throw Http::One::InsufficientInput();
-+}
-+
-+SBuf
-+Http::One::tokenOrQuotedString(Parser::Tokenizer &tok, const bool http1p0)
-+{
-+    if (tok.skip('"'))
-+        return parseQuotedStringSuffix(tok, http1p0);
-+
-+    if (tok.atEnd())
-+        throw InsufficientInput();
-+
-+    SBuf parsedToken;
-+    if (!tok.prefix(parsedToken, CharacterSet::TCHAR))
-+        throw TexcHere("invalid input while expecting an HTTP token");
-+
-+    if (tok.atEnd())
-+        throw InsufficientInput();
-+
-+    // got the complete token
-+    return parsedToken;
- }
- 
-diff --git a/src/http/one/Tokenizer.h b/src/http/one/Tokenizer.h
-index 658875f..2d40574 100644
---- a/src/http/one/Tokenizer.h
-+++ b/src/http/one/Tokenizer.h
-@@ -9,68 +9,47 @@
- #ifndef SQUID_SRC_HTTP_ONE_TOKENIZER_H
- #define SQUID_SRC_HTTP_ONE_TOKENIZER_H
- 
--#include "parser/Tokenizer.h"
-+#include "parser/forward.h"
-+#include "sbuf/forward.h"
- 
- namespace Http {
- namespace One {
- 
- /**
-- * Lexical processor extended to tokenize HTTP/1.x syntax.
-+ * Extracts either an HTTP/1 token or quoted-string while dealing with
-+ * possibly incomplete input typical for incremental text parsers.
-+ * Unescapes escaped characters in HTTP/1.1 quoted strings.
-  *
-- * \see ::Parser::Tokenizer for more detail
-+ * \param http1p0 whether to prohibit \-escaped characters in quoted strings
-+ * \throws InsufficientInput as appropriate, including on unterminated tokens
-+ * \returns extracted token or quoted string (without quotes)
-+ *
-+ * Governed by:
-+ *  - RFC 1945 section 2.1
-+ *  "
-+ *    A string of text is parsed as a single word if it is quoted using
-+ *    double-quote marks.
-+ *
-+ *        quoted-string  = ( <"> *(qdtext) <"> )
-+ *
-+ *        qdtext         =  and CTLs,
-+ *                         but including LWS>
-+ *
-+ *    Single-character quoting using the backslash ("\") character is not
-+ *    permitted in HTTP/1.0.
-+ *  "
-+ *
-+ *  - RFC 7230 section 3.2.6
-+ *  "
-+ *    A string of text is parsed as a single value if it is quoted using
-+ *    double-quote marks.
-+ *
-+ *    quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE
-+ *    qdtext         = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
-+ *    obs-text       = %x80-FF
-+ *  "
-  */
--class Tokenizer : public ::Parser::Tokenizer
--{
--public:
--    Tokenizer(SBuf &s) : ::Parser::Tokenizer(s), savedStats_(0) {}
--
--    /**
--     * Attempt to parse a quoted-string lexical construct.
--     *
--     * Governed by:
--     *  - RFC 1945 section 2.1
--     *  "
--     *    A string of text is parsed as a single word if it is quoted using
--     *    double-quote marks.
--     *
--     *        quoted-string  = ( <"> *(qdtext) <"> )
--     *
--     *        qdtext         =  and CTLs,
--     *                         but including LWS>
--     *
--     *    Single-character quoting using the backslash ("\") character is not
--     *    permitted in HTTP/1.0.
--     *  "
--     *
--     *  - RFC 7230 section 3.2.6
--     *  "
--     *    A string of text is parsed as a single value if it is quoted using
--     *    double-quote marks.
--     *
--     *    quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE
--     *    qdtext         = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
--     *    obs-text       = %x80-FF
--     *  "
--     *
--     * \param escaped HTTP/1.0 does not permit \-escaped characters
--     */
--    bool quotedString(SBuf &value, const bool http1p0 = false);
--
--    /**
--     * Attempt to parse a (token / quoted-string ) lexical construct.
--     */
--    bool quotedStringOrToken(SBuf &value, const bool http1p0 = false);
--
--private:
--    /// parse the internal component of a quote-string, and terminal DQUOTE
--    bool qdText(SBuf &value, const bool http1p0);
--
--    void checkpoint() { savedCheckpoint_ = buf(); savedStats_ = parsedSize(); }
--    void restoreLastCheckpoint() { undoParse(savedCheckpoint_, savedStats_); }
--
--    SBuf savedCheckpoint_;
--    SBuf::size_type savedStats_;
--};
-+SBuf tokenOrQuotedString(Parser::Tokenizer &tok, const bool http1p0 = false);
- 
- } // namespace One
- } // namespace Http
-diff --git a/src/http/one/forward.h b/src/http/one/forward.h
-index c90dc34..2b4ad28 100644
---- a/src/http/one/forward.h
-+++ b/src/http/one/forward.h
-@@ -10,6 +10,7 @@
- #define SQUID_SRC_HTTP_ONE_FORWARD_H
- 
- #include "base/RefCount.h"
-+#include "parser/forward.h"
- #include "sbuf/forward.h"
- 
- namespace Http {
-@@ -31,6 +32,8 @@ typedef RefCount ResponseParserPointer;
- /// CRLF textual representation
- const SBuf &CrLf();
- 
-+using ::Parser::InsufficientInput;
-+
- } // namespace One
- } // namespace Http
- 
-diff --git a/src/parser/BinaryTokenizer.h b/src/parser/BinaryTokenizer.h
-index acebd4d..24042d4 100644
---- a/src/parser/BinaryTokenizer.h
-+++ b/src/parser/BinaryTokenizer.h
-@@ -9,6 +9,7 @@
- #ifndef SQUID_SRC_PARSER_BINARYTOKENIZER_H
- #define SQUID_SRC_PARSER_BINARYTOKENIZER_H
- 
-+#include "parser/forward.h"
- #include "sbuf/SBuf.h"
- 
- namespace Parser
-@@ -44,7 +45,7 @@ public:
- class BinaryTokenizer
- {
- public:
--    class InsufficientInput {}; // thrown when a method runs out of data
-+    typedef ::Parser::InsufficientInput InsufficientInput;
-     typedef uint64_t size_type; // enough for the largest supported offset
- 
-     BinaryTokenizer();
-diff --git a/src/parser/Makefile.am b/src/parser/Makefile.am
-index af2b759..0daa5a8 100644
---- a/src/parser/Makefile.am
-+++ b/src/parser/Makefile.am
-@@ -13,6 +13,7 @@ noinst_LTLIBRARIES = libparser.la
- libparser_la_SOURCES = \
- 	BinaryTokenizer.h \
- 	BinaryTokenizer.cc \
-+	forward.h \
- 	Tokenizer.h \
- 	Tokenizer.cc
- 
-diff --git a/src/parser/Tokenizer.cc b/src/parser/Tokenizer.cc
-index 7e73e04..68f4aec 100644
---- a/src/parser/Tokenizer.cc
-+++ b/src/parser/Tokenizer.cc
-@@ -10,7 +10,9 @@
- 
- #include "squid.h"
- #include "Debug.h"
-+#include "parser/forward.h"
- #include "parser/Tokenizer.h"
-+#include "sbuf/Stream.h"
- 
- #include 
- #if HAVE_CTYPE_H
-@@ -96,6 +98,23 @@ Parser::Tokenizer::prefix(SBuf &returnedToken, const CharacterSet &tokenChars, c
-     return true;
- }
- 
-+SBuf
-+Parser::Tokenizer::prefix(const char *description, const CharacterSet &tokenChars, const SBuf::size_type limit)
-+{
-+    if (atEnd())
-+        throw InsufficientInput();
-+
-+    SBuf result;
-+
-+    if (!prefix(result, tokenChars, limit))
-+        throw TexcHere(ToSBuf("cannot parse ", description));
-+
-+    if (atEnd())
-+        throw InsufficientInput();
-+
-+    return result;
-+}
-+
- bool
- Parser::Tokenizer::suffix(SBuf &returnedToken, const CharacterSet &tokenChars, const SBuf::size_type limit)
- {
-@@ -283,3 +302,24 @@ Parser::Tokenizer::int64(int64_t & result, int base, bool allowSign, const SBuf:
-     return success(s - range.rawContent());
- }
- 
-+int64_t
-+Parser::Tokenizer::udec64(const char *description, const SBuf::size_type limit)
-+{
-+    if (atEnd())
-+        throw InsufficientInput();
-+
-+    int64_t result = 0;
-+
-+    // Since we only support unsigned decimals, a parsing failure with a
-+    // non-empty input always implies invalid/malformed input (or a buggy
-+    // limit=0 caller). TODO: Support signed and non-decimal integers by
-+    // refactoring int64() to detect insufficient input.
-+    if (!int64(result, 10, false, limit))
-+        throw TexcHere(ToSBuf("cannot parse ", description));
-+
-+    if (atEnd())
-+        throw InsufficientInput(); // more digits may be coming
-+
-+    return result;
-+}
-+
-diff --git a/src/parser/Tokenizer.h b/src/parser/Tokenizer.h
-index 54414be..03a8388 100644
---- a/src/parser/Tokenizer.h
-+++ b/src/parser/Tokenizer.h
-@@ -143,6 +143,19 @@ public:
-      */
-     bool int64(int64_t &result, int base = 0, bool allowSign = true, SBuf::size_type limit = SBuf::npos);
- 
-+    /*
-+     * The methods below mimic their counterparts documented above, but they
-+     * throw on errors, including InsufficientInput. The field description
-+     * parameter is used for error reporting and debugging.
-+     */
-+
-+    /// prefix() wrapper but throws InsufficientInput if input contains
-+    /// nothing but the prefix (i.e. if the prefix is not "terminated")
-+    SBuf prefix(const char *description, const CharacterSet &tokenChars, SBuf::size_type limit = SBuf::npos);
-+
-+    /// int64() wrapper but limited to unsigned decimal integers (for now)
-+    int64_t udec64(const char *description, SBuf::size_type limit = SBuf::npos);
-+
- protected:
-     SBuf consume(const SBuf::size_type n);
-     SBuf::size_type success(const SBuf::size_type n);
-diff --git a/src/parser/forward.h b/src/parser/forward.h
-new file mode 100644
-index 0000000..5a95b7a
---- /dev/null
-+++ b/src/parser/forward.h
-@@ -0,0 +1,22 @@
-+/*
-+ * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
-+ *
-+ * Squid software is distributed under GPLv2+ license and includes
-+ * contributions from numerous individuals and organizations.
-+ * Please see the COPYING and CONTRIBUTORS files for details.
-+ */
-+
-+#ifndef SQUID_PARSER_FORWARD_H
-+#define SQUID_PARSER_FORWARD_H
-+
-+namespace Parser {
-+class Tokenizer;
-+class BinaryTokenizer;
-+
-+// TODO: Move this declaration (to parser/Elements.h) if we need more like it.
-+/// thrown by modern "incremental" parsers when they need more data
-+class InsufficientInput {};
-+} // namespace Parser
-+
-+#endif /* SQUID_PARSER_FORWARD_H */
-+
-diff --git a/src/http/one/Parser.cc b/src/http/one/Parser.cc
-index affe0b1..05591fe 100644
---- a/src/http/one/Parser.cc
-+++ b/src/http/one/Parser.cc
-@@ -65,16 +65,10 @@ Http::One::Parser::DelimiterCharacters()
- void
- Http::One::Parser::skipLineTerminator(Tokenizer &tok) const
- {
--    if (tok.skip(Http1::CrLf()))
--        return;
--
-     if (Config.onoff.relaxed_header_parser && tok.skipOne(CharacterSet::LF))
-         return;
- 
--    if (tok.atEnd() || (tok.remaining().length() == 1 && tok.remaining().at(0) == '\r'))
--        throw InsufficientInput();
--
--    throw TexcHere("garbage instead of CRLF line terminator");
-+    tok.skipRequired("line-terminating CRLF", Http1::CrLf());
- }
- 
- /// all characters except the LF line terminator
-diff --git a/src/http/one/Parser.h b/src/http/one/Parser.h
-index 40e281b..9a2a4ad 100644
---- a/src/http/one/Parser.h
-+++ b/src/http/one/Parser.h
-@@ -120,9 +120,7 @@ protected:
-      * detect and skip the CRLF or (if tolerant) LF line terminator
-      * consume from the tokenizer.
-      *
--     * \throws exception on bad or InsuffientInput.
--     * \retval true only if line terminator found.
--     * \retval false incomplete or missing line terminator, need more data.
-+     * \throws exception on bad or InsufficientInput
-      */
-     void skipLineTerminator(Tokenizer &) const;
- 
-diff --git a/src/http/one/TeChunkedParser.cc b/src/http/one/TeChunkedParser.cc
-index 6d2f8ea..3bff6c7 100644
---- a/src/http/one/TeChunkedParser.cc
-+++ b/src/http/one/TeChunkedParser.cc
-@@ -91,6 +91,11 @@ Http::One::TeChunkedParser::parseChunkSize(Tokenizer &tok)
- {
-     Must(theChunkSize <= 0); // Should(), really
- 
-+    static const SBuf bannedHexPrefixLower("0x");
-+    static const SBuf bannedHexPrefixUpper("0X");
-+    if (tok.skip(bannedHexPrefixLower) || tok.skip(bannedHexPrefixUpper))
-+        throw TextException("chunk starts with 0x", Here());
-+
-     int64_t size = -1;
-     if (tok.int64(size, 16, false) && !tok.atEnd()) {
-         if (size < 0)
-@@ -121,7 +126,7 @@ Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
-     // bad or insufficient input, like in the code below. TODO: Expand up.
-     try {
-         parseChunkExtensions(tok); // a possibly empty chunk-ext list
--        skipLineTerminator(tok);
-+        tok.skipRequired("CRLF after [chunk-ext]", Http1::CrLf());
-         buf_ = tok.remaining();
-         parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME;
-         return true;
-@@ -132,12 +137,14 @@ Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
-     // other exceptions bubble up to kill message parsing
- }
- 
--/// Parses the chunk-ext list (RFC 7230 section 4.1.1 and its Errata #4667):
-+/// Parses the chunk-ext list (RFC 9112 section 7.1.1:
- /// chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
- void
--Http::One::TeChunkedParser::parseChunkExtensions(Tokenizer &tok)
-+Http::One::TeChunkedParser::parseChunkExtensions(Tokenizer &callerTok)
- {
-     do {
-+        auto tok = callerTok;
-+
-         ParseBws(tok); // Bug 4492: IBM_HTTP_Server sends SP after chunk-size
- 
-         if (!tok.skip(';'))
-@@ -145,6 +152,7 @@ Http::One::TeChunkedParser::parseChunkExtensions(Tokenizer &tok)
- 
-         parseOneChunkExtension(tok);
-         buf_ = tok.remaining(); // got one extension
-+        callerTok = tok;
-     } while (true);
- }
- 
-@@ -158,11 +166,14 @@ Http::One::ChunkExtensionValueParser::Ignore(Tokenizer &tok, const SBuf &extName
- /// Parses a single chunk-ext list element:
- /// chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
- void
--Http::One::TeChunkedParser::parseOneChunkExtension(Tokenizer &tok)
-+Http::One::TeChunkedParser::parseOneChunkExtension(Tokenizer &callerTok)
- {
-+    auto tok = callerTok;
-+
-     ParseBws(tok); // Bug 4492: ICAP servers send SP before chunk-ext-name
- 
-     const auto extName = tok.prefix("chunk-ext-name", CharacterSet::TCHAR);
-+    callerTok = tok; // in case we determine that this is a valueless chunk-ext
- 
-     ParseBws(tok);
- 
-@@ -176,6 +187,8 @@ Http::One::TeChunkedParser::parseOneChunkExtension(Tokenizer &tok)
-         customExtensionValueParser->parse(tok, extName);
-     else
-         ChunkExtensionValueParser::Ignore(tok, extName);
-+
-+    callerTok = tok;
- }
- 
- bool
-@@ -209,7 +222,7 @@ Http::One::TeChunkedParser::parseChunkEnd(Tokenizer &tok)
-     Must(theLeftBodySize == 0); // Should(), really
- 
-     try {
--        skipLineTerminator(tok);
-+        tok.skipRequired("chunk CRLF", Http1::CrLf());
-         buf_ = tok.remaining(); // parse checkpoint
-         theChunkSize = 0; // done with the current chunk
-         parsingStage_ = Http1::HTTP_PARSE_CHUNK_SZ;
-diff --git a/src/parser/Tokenizer.cc b/src/parser/Tokenizer.cc
-index 68f4aec..8516869 100644
---- a/src/parser/Tokenizer.cc
-+++ b/src/parser/Tokenizer.cc
-@@ -147,6 +147,18 @@ Parser::Tokenizer::skipAll(const CharacterSet &tokenChars)
-     return success(prefixLen);
- }
- 
-+void
-+Parser::Tokenizer::skipRequired(const char *description, const SBuf &tokenToSkip)
-+{
-+    if (skip(tokenToSkip) || tokenToSkip.isEmpty())
-+        return;
-+
-+    if (tokenToSkip.startsWith(buf_))
-+        throw InsufficientInput();
-+
-+    throw TextException(ToSBuf("cannot skip ", description), Here());
-+}
-+
- bool
- Parser::Tokenizer::skipOne(const CharacterSet &chars)
- {
-diff --git a/src/parser/Tokenizer.h b/src/parser/Tokenizer.h
-index 03a8388..78ab9e7 100644
---- a/src/parser/Tokenizer.h
-+++ b/src/parser/Tokenizer.h
-@@ -115,6 +115,13 @@ public:
-      */
-     SBuf::size_type skipAll(const CharacterSet &discardables);
- 
-+    /** skips a given character sequence (string);
-+     * does nothing if the sequence is empty
-+     *
-+     * \throws exception on mismatching prefix or InsufficientInput
-+     */
-+    void skipRequired(const char *description, const SBuf &tokenToSkip);
-+
-     /** Removes a single trailing character from the set.
-      *
-      * \return whether a character was removed
diff --git a/SOURCES/squid-4.15-CVE-2023-46847.patch b/SOURCES/squid-4.15-CVE-2023-46847.patch
deleted file mode 100644
index c268517..0000000
--- a/SOURCES/squid-4.15-CVE-2023-46847.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-diff --git a/src/auth/digest/Config.cc b/src/auth/digest/Config.cc
-index 6a9736f..0a883fa 100644
---- a/src/auth/digest/Config.cc
-+++ b/src/auth/digest/Config.cc
-@@ -847,11 +847,15 @@ Auth::Digest::Config::decode(char const *proxy_auth, const char *aRequestRealm)
-             break;
- 
-         case DIGEST_NC:
--            if (value.size() != 8) {
-+            if (value.size() == 8) {
-+                // for historical reasons, the nc value MUST be exactly 8 bytes
-+                static_assert(sizeof(digest_request->nc) == 8 + 1, "bad nc buffer size");
-+                xstrncpy(digest_request->nc, value.rawBuf(), value.size() + 1);
-+                debugs(29, 9, "Found noncecount '" << digest_request->nc << "'");
-+            } else {
-                 debugs(29, 9, "Invalid nc '" << value << "' in '" << temp << "'");
-+                digest_request->nc[0] = 0;
-             }
--            xstrncpy(digest_request->nc, value.rawBuf(), value.size() + 1);
--            debugs(29, 9, "Found noncecount '" << digest_request->nc << "'");
-             break;
- 
-         case DIGEST_CNONCE:
diff --git a/SOURCES/squid-4.15-CVE-2023-49285.patch b/SOURCES/squid-4.15-CVE-2023-49285.patch
deleted file mode 100644
index f6351e4..0000000
--- a/SOURCES/squid-4.15-CVE-2023-49285.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-commit 77b3fb4df0f126784d5fd4967c28ed40eb8d521b
-Author: Alex Rousskov 
-Date:   Wed Oct 25 19:41:45 2023 +0000
-
-    RFC 1123: Fix date parsing (#1538)
-    
-    The bug was discovered and detailed by Joshua Rogers at
-    https://megamansec.github.io/Squid-Security-Audit/datetime-overflow.html
-    where it was filed as "1-Byte Buffer OverRead in RFC 1123 date/time
-    Handling".
-
-diff --git a/lib/rfc1123.c b/lib/rfc1123.c
-index e5bf9a4d7..cb484cc00 100644
---- a/lib/rfc1123.c
-+++ b/lib/rfc1123.c
-@@ -50,7 +50,13 @@ make_month(const char *s)
-     char month[3];
- 
-     month[0] = xtoupper(*s);
-+    if (!month[0])
-+        return -1; // protects *(s + 1) below
-+
-     month[1] = xtolower(*(s + 1));
-+    if (!month[1])
-+        return -1; // protects *(s + 2) below
-+
-     month[2] = xtolower(*(s + 2));
- 
-     for (i = 0; i < 12; i++)
-
diff --git a/SOURCES/squid-4.15-CVE-2023-49286.patch b/SOURCES/squid-4.15-CVE-2023-49286.patch
deleted file mode 100644
index 28f5beb..0000000
--- a/SOURCES/squid-4.15-CVE-2023-49286.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-diff --git a/src/ipc.cc b/src/ipc.cc
-index 42e11e6..a68e623 100644
---- a/src/ipc.cc
-+++ b/src/ipc.cc
-@@ -19,6 +19,11 @@
- #include "SquidConfig.h"
- #include "SquidIpc.h"
- #include "tools.h"
-+#include 
-+
-+#if HAVE_UNISTD_H
-+#include 
-+#endif
- 
- static const char *hello_string = "hi there\n";
- #ifndef HELLO_BUF_SZ
-@@ -365,6 +370,22 @@ ipcCreate(int type, const char *prog, const char *const args[], const char *name
-     }
- 
-     PutEnvironment();
-+
-+    // A dup(2) wrapper that reports and exits the process on errors. The
-+    // exiting logic is only suitable for this child process context.
-+    const auto dupOrExit = [prog,name](const int oldFd) {
-+        const auto newFd = dup(oldFd);
-+        if (newFd < 0) {
-+            const auto savedErrno = errno;
-+            debugs(54, DBG_CRITICAL, "ERROR: Helper process initialization failure: " << name <<
-+                   Debug::Extra << "helper (CHILD) PID: " << getpid() <<
-+                   Debug::Extra << "helper program name: " << prog <<
-+                   Debug::Extra << "dup(2) system call error for FD " << oldFd << ": " << xstrerr(savedErrno));
-+            _exit(EXIT_FAILURE);
-+        }
-+        return newFd;
-+    };
-+
-     /*
-      * This double-dup stuff avoids problems when one of
-      *  crfd, cwfd, or debug_log are in the rage 0-2.
-@@ -372,17 +393,16 @@ ipcCreate(int type, const char *prog, const char *const args[], const char *name
- 
-     do {
-         /* First make sure 0-2 is occupied by something. Gets cleaned up later */
--        x = dup(crfd);
--        assert(x > -1);
--    } while (x < 3 && x > -1);
-+        x = dupOrExit(crfd);
-+    } while (x < 3);
- 
-     close(x);
- 
--    t1 = dup(crfd);
-+    t1 = dupOrExit(crfd);
- 
--    t2 = dup(cwfd);
-+    t2 = dupOrExit(cwfd);
- 
--    t3 = dup(fileno(debug_log));
-+    t3 = dupOrExit(fileno(debug_log));
- 
-     assert(t1 > 2 && t2 > 2 && t3 > 2);
- 
diff --git a/SOURCES/squid-4.15-CVE-2023-50269.patch b/SOURCES/squid-4.15-CVE-2023-50269.patch
deleted file mode 100644
index 06ea82c..0000000
--- a/SOURCES/squid-4.15-CVE-2023-50269.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-diff --git a/src/ClientRequestContext.h b/src/ClientRequestContext.h
-index fe2edf6..47aa935 100644
---- a/src/ClientRequestContext.h
-+++ b/src/ClientRequestContext.h
-@@ -81,6 +81,10 @@ public:
- #endif
-     ErrorState *error; ///< saved error page for centralized/delayed processing
-     bool readNextRequest; ///< whether Squid should read after error handling
-+
-+#if FOLLOW_X_FORWARDED_FOR
-+    size_t currentXffHopNumber = 0; ///< number of X-Forwarded-For header values processed so far
-+#endif
- };
- 
- #endif /* SQUID_CLIENTREQUESTCONTEXT_H */
-diff --git a/src/client_side_request.cc b/src/client_side_request.cc
-index 1c6ff62..b758f6f 100644
---- a/src/client_side_request.cc
-+++ b/src/client_side_request.cc
-@@ -78,6 +78,11 @@
- static const char *const crlf = "\r\n";
- 
- #if FOLLOW_X_FORWARDED_FOR
-+
-+#if !defined(SQUID_X_FORWARDED_FOR_HOP_MAX)
-+#define SQUID_X_FORWARDED_FOR_HOP_MAX 64
-+#endif
-+
- static void clientFollowXForwardedForCheck(allow_t answer, void *data);
- #endif /* FOLLOW_X_FORWARDED_FOR */
- 
-@@ -485,8 +490,16 @@ clientFollowXForwardedForCheck(allow_t answer, void *data)
-                 /* override the default src_addr tested if we have to go deeper than one level into XFF */
-                 Filled(calloutContext->acl_checklist)->src_addr = request->indirect_client_addr;
-             }
--            calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
--            return;
-+            if (++calloutContext->currentXffHopNumber < SQUID_X_FORWARDED_FOR_HOP_MAX) {
-+                calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
-+                return;
-+            }
-+            const auto headerName = Http::HeaderLookupTable.lookup(Http::HdrType::X_FORWARDED_FOR).name;
-+            debugs(28, DBG_CRITICAL, "ERROR: Ignoring trailing " << headerName << " addresses" <<
-+                   Debug::Extra << "addresses allowed by follow_x_forwarded_for: " << calloutContext->currentXffHopNumber <<
-+                   Debug::Extra << "last/accepted address: " << request->indirect_client_addr <<
-+                   Debug::Extra << "ignored trailing addresses: " << request->x_forwarded_for_iterator);
-+            // fall through to resume clientAccessCheck() processing
-         }
-     }
- 
diff --git a/SOURCES/squid-4.15-CVE-2023-5824.patch b/SOURCES/squid-4.15-CVE-2023-5824.patch
deleted file mode 100644
index 4395c71..0000000
--- a/SOURCES/squid-4.15-CVE-2023-5824.patch
+++ /dev/null
@@ -1,4352 +0,0 @@
-commit bf9a9ec5329bde6acc26797d1fa7a7a165fec01f
-Author: Tomas Korbar 
-Date:   Tue Nov 21 13:21:43 2023 +0100
-
-    Fix CVE-2023-5824 (#1335) (#1561) (#1562)
-    Supply ALE with HttpReply before checking http_reply_access (#398)
-    Replace adjustable base reply - downstream change neccessary for
-    backport
-
-diff --git a/src/AccessLogEntry.cc b/src/AccessLogEntry.cc
-index 1956c9b..4f1e73e 100644
---- a/src/AccessLogEntry.cc
-+++ b/src/AccessLogEntry.cc
-@@ -10,6 +10,7 @@
- #include "AccessLogEntry.h"
- #include "HttpReply.h"
- #include "HttpRequest.h"
-+#include "MemBuf.h"
- #include "SquidConfig.h"
- 
- #if USE_OPENSSL
-@@ -89,6 +90,8 @@ AccessLogEntry::getExtUser() const
-     return nullptr;
- }
- 
-+AccessLogEntry::AccessLogEntry() {}
-+
- AccessLogEntry::~AccessLogEntry()
- {
-     safe_free(headers.request);
-@@ -97,14 +100,11 @@ AccessLogEntry::~AccessLogEntry()
-     safe_free(adapt.last_meta);
- #endif
- 
--    safe_free(headers.reply);
--
-     safe_free(headers.adapted_request);
-     HTTPMSGUNLOCK(adapted_request);
- 
-     safe_free(lastAclName);
- 
--    HTTPMSGUNLOCK(reply);
-     HTTPMSGUNLOCK(request);
- #if ICAP_CLIENT
-     HTTPMSGUNLOCK(icap.reply);
-@@ -124,3 +124,10 @@ AccessLogEntry::effectiveVirginUrl() const
-     return nullptr;
- }
- 
-+void
-+AccessLogEntry::packReplyHeaders(MemBuf &mb) const
-+{
-+    if (reply)
-+        reply->packHeadersUsingFastPacker(mb);
-+}
-+
-diff --git a/src/AccessLogEntry.h b/src/AccessLogEntry.h
-index 1f29e61..f1d2ecc 100644
---- a/src/AccessLogEntry.h
-+++ b/src/AccessLogEntry.h
-@@ -40,13 +40,7 @@ class AccessLogEntry: public RefCountable
- public:
-     typedef RefCount Pointer;
- 
--    AccessLogEntry() :
--        url(nullptr),
--        lastAclName(nullptr),
--        reply(nullptr),
--        request(nullptr),
--        adapted_request(nullptr)
--    {}
-+    AccessLogEntry();
-     ~AccessLogEntry();
- 
-     /// Fetch the client IP log string into the given buffer.
-@@ -63,6 +57,9 @@ public:
-     /// Fetch the transaction method string (ICP opcode, HTCP opcode or HTTP method)
-     SBuf getLogMethod() const;
- 
-+    /// dump all reply headers (for sending or risky logging)
-+    void packReplyHeaders(MemBuf &mb) const;
-+
-     SBuf url;
- 
-     /// TCP/IP level details about the client connection
-@@ -187,14 +184,12 @@ public:
- 
-     public:
-         Headers() : request(NULL),
--            adapted_request(NULL),
--            reply(NULL) {}
-+            adapted_request(NULL)
-+            {}
- 
-         char *request; //< virgin HTTP request headers
- 
-         char *adapted_request; //< HTTP request headers after adaptation and redirection
--
--        char *reply;
-     } headers;
- 
- #if USE_ADAPTATION
-@@ -212,13 +207,13 @@ public:
-     } adapt;
- #endif
- 
--    const char *lastAclName; ///< string for external_acl_type %ACL format code
-+    const char *lastAclName = nullptr; ///< string for external_acl_type %ACL format code
-     SBuf lastAclData; ///< string for external_acl_type %DATA format code
- 
-     HierarchyLogEntry hier;
--    HttpReply *reply;
--    HttpRequest *request; //< virgin HTTP request
--    HttpRequest *adapted_request; //< HTTP request after adaptation and redirection
-+    HttpReplyPointer reply;
-+    HttpRequest *request = nullptr; //< virgin HTTP request
-+    HttpRequest *adapted_request = nullptr; //< HTTP request after adaptation and redirection
- 
-     /// key:value pairs set by squid.conf note directive and
-     /// key=value pairs returned from URL rewrite/redirect helper
-diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
-index 8dcc7e3..21206a9 100644
---- a/src/HttpHeader.cc
-+++ b/src/HttpHeader.cc
-@@ -9,6 +9,7 @@
- /* DEBUG: section 55    HTTP Header */
- 
- #include "squid.h"
-+#include "base/Assure.h"
- #include "base/EnumIterator.h"
- #include "base64.h"
- #include "globals.h"
-diff --git a/src/HttpHeaderTools.cc b/src/HttpHeaderTools.cc
-index f1e45a4..1337b8d 100644
---- a/src/HttpHeaderTools.cc
-+++ b/src/HttpHeaderTools.cc
-@@ -479,7 +479,7 @@ httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer
- 
-     checklist.al = al;
-     if (al && al->reply) {
--        checklist.reply = al->reply;
-+        checklist.reply = al->reply.getRaw();
-         HTTPMSGLOCK(checklist.reply);
-     }
- 
-diff --git a/src/HttpReply.cc b/src/HttpReply.cc
-index 6feb262..e74960b 100644
---- a/src/HttpReply.cc
-+++ b/src/HttpReply.cc
-@@ -20,7 +20,9 @@
- #include "HttpReply.h"
- #include "HttpRequest.h"
- #include "MemBuf.h"
-+#include "sbuf/Stream.h"
- #include "SquidConfig.h"
-+#include "SquidMath.h"
- #include "SquidTime.h"
- #include "Store.h"
- #include "StrList.h"
-@@ -524,6 +526,38 @@ HttpReply::expectedBodyTooLarge(HttpRequest& request)
-     return expectedSize > bodySizeMax;
- }
- 
-+size_t
-+HttpReply::parseTerminatedPrefix(const char * const terminatedBuf, const size_t bufSize)
-+{
-+    auto error = Http::scNone;
-+    const bool eof = false; // TODO: Remove after removing atEnd from HttpHeader::parse()
-+    if (parse(terminatedBuf, bufSize, eof, &error)) {
-+        debugs(58, 7, "success after accumulating " << bufSize << " bytes and parsing " << hdr_sz);
-+        Assure(pstate == psParsed);
-+        Assure(hdr_sz > 0);
-+        Assure(!Less(bufSize, hdr_sz)); // cannot parse more bytes than we have
-+        return hdr_sz; // success
-+    }
-+
-+    Assure(pstate != psParsed);
-+    hdr_sz = 0;
-+
-+    if (error) {
-+        throw TextException(ToSBuf("failed to parse HTTP headers",
-+                                   Debug::Extra, "parser error code: ", error,
-+                                   Debug::Extra, "accumulated unparsed bytes: ", bufSize,
-+                                   Debug::Extra, "reply_header_max_size: ", Config.maxReplyHeaderSize),
-+                            Here());
-+    }
-+
-+    debugs(58, 3, "need more bytes after accumulating " << bufSize << " out of " << Config.maxReplyHeaderSize);
-+
-+    // the parse() call above enforces Config.maxReplyHeaderSize limit
-+    // XXX: Make this a strict comparison after fixing Http::Message::parse() enforcement
-+    Assure(bufSize <= Config.maxReplyHeaderSize);
-+    return 0; // parsed nothing, need more data
-+}
-+
- void
- HttpReply::calcMaxBodySize(HttpRequest& request) const
- {
-diff --git a/src/HttpReply.h b/src/HttpReply.h
-index 6c90e20..4301cfd 100644
---- a/src/HttpReply.h
-+++ b/src/HttpReply.h
-@@ -121,6 +121,13 @@ public:
-     /// \returns false if any information is missing
-     bool olderThan(const HttpReply *them) const;
- 
-+    /// Parses response status line and headers at the start of the given
-+    /// NUL-terminated buffer of the given size. Respects reply_header_max_size.
-+    /// Assures pstate becomes Http::Message::psParsed on (and only on) success.
-+    /// \returns the number of bytes in a successfully parsed prefix (or zero)
-+    /// \retval 0 implies that more data is needed to parse the response prefix
-+    size_t parseTerminatedPrefix(const char *, size_t);
-+
- private:
-     /** initialize */
-     void init();
-diff --git a/src/MemObject.cc b/src/MemObject.cc
-index df7791f..650d3fd 100644
---- a/src/MemObject.cc
-+++ b/src/MemObject.cc
-@@ -196,8 +196,8 @@ struct LowestMemReader : public unary_function {
-     LowestMemReader(int64_t seed):current(seed) {}
- 
-     void operator() (store_client const &x) {
--        if (x.memReaderHasLowerOffset(current))
--            current = x.copyInto.offset;
-+        if (x.getType() == STORE_MEM_CLIENT)
-+            current = std::min(current, x.discardableHttpEnd());
-     }
- 
-     int64_t current;
-@@ -369,6 +369,12 @@ MemObject::policyLowestOffsetToKeep(bool swap) const
-      */
-     int64_t lowest_offset = lowestMemReaderOffset();
- 
-+    // XXX: Remove the last (Config.onoff.memory_cache_first-based) condition
-+    // and update keepForLocalMemoryCache() accordingly. The caller wants to
-+    // remove all local memory that is safe to remove. Honoring caching
-+    // preferences is its responsibility. Our responsibility is safety. The
-+    // situation was different when ff4b33f added that condition -- there was no
-+    // keepInLocalMemory/keepForLocalMemoryCache() call guard back then.
-     if (endOffset() < lowest_offset ||
-             endOffset() - inmem_lo > (int64_t)Config.Store.maxInMemObjSize ||
-             (swap && !Config.onoff.memory_cache_first))
-@@ -492,7 +498,7 @@ MemObject::mostBytesAllowed() const
- 
- #endif
- 
--        j = sc->delayId.bytesWanted(0, sc->copyInto.length);
-+        j = sc->bytesWanted();
- 
-         if (j > jmax) {
-             jmax = j;
-diff --git a/src/MemObject.h b/src/MemObject.h
-index 711966d..9f4add0 100644
---- a/src/MemObject.h
-+++ b/src/MemObject.h
-@@ -56,9 +56,23 @@ public:
- 
-     void write(const StoreIOBuffer &buf);
-     void unlinkRequest();
-+
-+    /// HTTP response before 304 (Not Modified) updates
-+    /// starts "empty"; modified via replaceBaseReply() or adjustableBaseReply()
-+    HttpReply &baseReply() const { return *_reply; }
-+
-     HttpReply const *getReply() const;
-     void replaceHttpReply(HttpReply *newrep);
-     void stat (MemBuf * mb) const;
-+
-+    /// The offset of the last memory-stored HTTP response byte plus one.
-+    /// * HTTP response headers (if any) are stored at offset zero.
-+    /// * HTTP response body byte[n] usually has offset (hdr_sz + n), where
-+    ///   hdr_sz is the size of stored HTTP response headers (zero if none); and
-+    ///   n is the corresponding byte offset in the whole resource body.
-+    ///   However, some 206 (Partial Content) response bodies are stored (and
-+    ///   retrieved) as regular 200 response bodies, disregarding offsets of
-+    ///   their body parts. \sa HttpStateData::decideIfWeDoRanges().
-     int64_t endOffset () const;
-     void markEndOfReplyHeaders(); ///< sets _reply->hdr_sz to endOffset()
-     /// negative if unknown; otherwise, expected object_sz, expected endOffset
-diff --git a/src/MemStore.cc b/src/MemStore.cc
-index a4a6ab2..6762c4f 100644
---- a/src/MemStore.cc
-+++ b/src/MemStore.cc
-@@ -17,6 +17,8 @@
- #include "MemObject.h"
- #include "MemStore.h"
- #include "mime_header.h"
-+#include "sbuf/SBuf.h"
-+#include "sbuf/Stream.h"
- #include "SquidConfig.h"
- #include "SquidMath.h"
- #include "StoreStats.h"
-@@ -316,19 +318,25 @@ MemStore::get(const cache_key *key)
-     // create a brand new store entry and initialize it with stored info
-     StoreEntry *e = new StoreEntry();
- 
--    // XXX: We do not know the URLs yet, only the key, but we need to parse and
--    // store the response for the Root().find() callers to be happy because they
--    // expect IN_MEMORY entries to already have the response headers and body.
--    e->createMemObject();
--
--    anchorEntry(*e, index, *slot);
--
--    const bool copied = copyFromShm(*e, index, *slot);
--
--    if (copied)
--        return e;
-+    try {
-+        // XXX: We do not know the URLs yet, only the key, but we need to parse and
-+        // store the response for the Root().find() callers to be happy because they
-+        // expect IN_MEMORY entries to already have the response headers and body.
-+        e->createMemObject();
-+
-+        anchorEntry(*e, index, *slot);
-+
-+        // TODO: make copyFromShm() throw on all failures, simplifying this code
-+        if (copyFromShm(*e, index, *slot))
-+            return e;
-+        debugs(20, 3, "failed for " << *e);
-+    } catch (...) {
-+        // see store_client::parseHttpHeadersFromDisk() for problems this may log
-+        debugs(20, DBG_IMPORTANT, "ERROR: Cannot load a cache hit from shared memory" <<
-+               Debug::Extra << "exception: " << CurrentException <<
-+               Debug::Extra << "cache_mem entry: " << *e);
-+    }
- 
--    debugs(20, 3, "failed for " << *e);
-     map->freeEntry(index); // do not let others into the same trap
-     destroyStoreEntry(static_cast(e));
-     return NULL;
-@@ -473,6 +481,8 @@ MemStore::copyFromShm(StoreEntry &e, const sfileno index, const Ipc::StoreMapAnc
-     Ipc::StoreMapSliceId sid = anchor.start; // optimize: remember the last sid
-     bool wasEof = anchor.complete() && sid < 0;
-     int64_t sliceOffset = 0;
-+
-+    SBuf httpHeaderParsingBuffer;
-     while (sid >= 0) {
-         const Ipc::StoreMapSlice &slice = map->readableSlice(index, sid);
-         // slice state may change during copying; take snapshots now
-@@ -495,10 +505,18 @@ MemStore::copyFromShm(StoreEntry &e, const sfileno index, const Ipc::StoreMapAnc
-             const StoreIOBuffer sliceBuf(wasSize - prefixSize,
-                                          e.mem_obj->endOffset(),
-                                          page + prefixSize);
--            if (!copyFromShmSlice(e, sliceBuf, wasEof))
--                return false;
-+
-+            copyFromShmSlice(e, sliceBuf);
-             debugs(20, 8, "entry " << index << " copied slice " << sid <<
-                    " from " << extra.page << '+' << prefixSize);
-+
-+            // parse headers if needed; they might span multiple slices!
-+            auto &reply = e.mem().baseReply();
-+            if (reply.pstate != psParsed) {
-+                httpHeaderParsingBuffer.append(sliceBuf.data, sliceBuf.length);
-+                if (reply.parseTerminatedPrefix(httpHeaderParsingBuffer.c_str(), httpHeaderParsingBuffer.length()))
-+                    httpHeaderParsingBuffer = SBuf(); // we do not need these bytes anymore
-+            }
-         }
-         // else skip a [possibly incomplete] slice that we copied earlier
- 
-@@ -524,6 +542,9 @@ MemStore::copyFromShm(StoreEntry &e, const sfileno index, const Ipc::StoreMapAnc
-     debugs(20, 5, "mem-loaded all " << e.mem_obj->endOffset() << '/' <<
-            anchor.basics.swap_file_sz << " bytes of " << e);
- 
-+    if (e.mem().baseReply().pstate != psParsed)
-+        throw TextException(ToSBuf("truncated mem-cached headers; accumulated: ", httpHeaderParsingBuffer.length()), Here());
-+
-     // from StoreEntry::complete()
-     e.mem_obj->object_sz = e.mem_obj->endOffset();
-     e.store_status = STORE_OK;
-@@ -539,32 +560,11 @@ MemStore::copyFromShm(StoreEntry &e, const sfileno index, const Ipc::StoreMapAnc
- }
- 
- /// imports one shared memory slice into local memory
--bool
--MemStore::copyFromShmSlice(StoreEntry &e, const StoreIOBuffer &buf, bool eof)
-+void
-+MemStore::copyFromShmSlice(StoreEntry &e, const StoreIOBuffer &buf)
- {
-     debugs(20, 7, "buf: " << buf.offset << " + " << buf.length);
- 
--    // from store_client::readBody()
--    // parse headers if needed; they might span multiple slices!
--    HttpReply *rep = (HttpReply *)e.getReply();
--    if (rep->pstate < psParsed) {
--        // XXX: have to copy because httpMsgParseStep() requires 0-termination
--        MemBuf mb;
--        mb.init(buf.length+1, buf.length+1);
--        mb.append(buf.data, buf.length);
--        mb.terminate();
--        const int result = rep->httpMsgParseStep(mb.buf, buf.length, eof);
--        if (result > 0) {
--            assert(rep->pstate == psParsed);
--        } else if (result < 0) {
--            debugs(20, DBG_IMPORTANT, "Corrupted mem-cached headers: " << e);
--            return false;
--        } else { // more slices are needed
--            assert(!eof);
--        }
--    }
--    debugs(20, 7, "rep pstate: " << rep->pstate);
--
-     // local memory stores both headers and body so copy regardless of pstate
-     const int64_t offBefore = e.mem_obj->endOffset();
-     assert(e.mem_obj->data_hdr.write(buf)); // from MemObject::write()
-@@ -572,7 +572,6 @@ MemStore::copyFromShmSlice(StoreEntry &e, const StoreIOBuffer &buf, bool eof)
-     // expect to write the entire buf because StoreEntry::write() never fails
-     assert(offAfter >= 0 && offBefore <= offAfter &&
-            static_cast(offAfter - offBefore) == buf.length);
--    return true;
- }
- 
- /// whether we should cache the entry
-diff --git a/src/MemStore.h b/src/MemStore.h
-index 516da3c..31a2015 100644
---- a/src/MemStore.h
-+++ b/src/MemStore.h
-@@ -76,7 +76,7 @@ protected:
-     void copyToShm(StoreEntry &e);
-     void copyToShmSlice(StoreEntry &e, Ipc::StoreMapAnchor &anchor, Ipc::StoreMap::Slice &slice);
-     bool copyFromShm(StoreEntry &e, const sfileno index, const Ipc::StoreMapAnchor &anchor);
--    bool copyFromShmSlice(StoreEntry &e, const StoreIOBuffer &buf, bool eof);
-+    void copyFromShmSlice(StoreEntry &, const StoreIOBuffer &);
- 
-     void updateHeadersOrThrow(Ipc::StoreMapUpdate &update);
- 
-diff --git a/src/SquidMath.h b/src/SquidMath.h
-index c70acd1..bfca0cc 100644
---- a/src/SquidMath.h
-+++ b/src/SquidMath.h
-@@ -9,6 +9,11 @@
- #ifndef _SQUID_SRC_SQUIDMATH_H
- #define _SQUID_SRC_SQUIDMATH_H
- 
-+#include 
-+#include 
-+
-+// TODO: Move to src/base/Math.h and drop the Math namespace
-+
- /* Math functions we define locally for Squid */
- namespace Math
- {
-@@ -21,5 +26,165 @@ double doubleAverage(const double, const double, int, const int);
- 
- } // namespace Math
- 
-+// If Sum() performance becomes important, consider using GCC and clang
-+// built-ins like __builtin_add_overflow() instead of manual overflow checks.
-+
-+/// detects a pair of unsigned types
-+/// reduces code duplication in declarations further below
-+template 
-+using AllUnsigned = typename std::conditional<
-+                    std::is_unsigned::value && std::is_unsigned::value,
-+                    std::true_type,
-+                    std::false_type
-+                    >::type;
-+
-+// TODO: Replace with std::cmp_less() after migrating to C++20.
-+/// whether integer a is less than integer b, with correct overflow handling
-+template 
-+constexpr bool
-+Less(const A a, const B b) {
-+    // The casts below make standard C++ integer conversions explicit. They
-+    // quell compiler warnings about signed/unsigned comparison. The first two
-+    // lines exclude different-sign a and b, making the casts/comparison safe.
-+    using AB = typename std::common_type::type;
-+    return
-+        (a >= 0 && b < 0) ? false :
-+        (a < 0 && b >= 0) ? true :
-+        /* (a >= 0) == (b >= 0) */ static_cast(a) < static_cast(b);
-+}
-+
-+/// ensure that T is supported by NaturalSum() and friends
-+template
-+constexpr void
-+AssertNaturalType()
-+{
-+    static_assert(std::numeric_limits::is_bounded, "std::numeric_limits::max() is meaningful");
-+    static_assert(std::numeric_limits::is_exact, "no silent loss of precision");
-+    static_assert(!std::is_enum::value, "no silent creation of non-enumerated values");
-+}
-+
-+// TODO: Investigate whether this optimization can be expanded to [signed] types
-+// A and B when std::numeric_limits::is_modulo is true.
-+/// This IncreaseSumInternal() overload is optimized for speed.
-+/// \returns a non-overflowing sum of the two unsigned arguments (or nothing)
-+/// \prec both argument types are unsigned
-+template ::value, int> = 0>
-+std::pair
-+IncreaseSumInternal(const A a, const B b) {
-+    // paranoid: AllUnsigned precondition established that already
-+    static_assert(std::is_unsigned::value, "AllUnsigned dispatch worked for A");
-+    static_assert(std::is_unsigned::value, "AllUnsigned dispatch worked for B");
-+
-+    AssertNaturalType();
-+    AssertNaturalType();
-+    AssertNaturalType();
-+
-+    // we should only be called by IncreaseSum(); it forces integer promotion
-+    static_assert(std::is_same::value, "a will not be promoted");
-+    static_assert(std::is_same::value, "b will not be promoted");
-+    // and without integer promotions, a sum of unsigned integers is unsigned
-+    static_assert(std::is_unsigned::value, "a+b is unsigned");
-+
-+    // with integer promotions ruled out, a or b can only undergo integer
-+    // conversion to the higher rank type (A or B, we do not know which)
-+    using AB = typename std::common_type::type;
-+    static_assert(std::is_same::value || std::is_same::value, "no unexpected conversions");
-+    static_assert(std::is_same::value, "lossless assignment");
-+    const AB sum = a + b;
-+
-+    static_assert(std::numeric_limits::is_modulo, "we can detect overflows");
-+    // 1. modulo math: overflowed sum is smaller than any of its operands
-+    // 2. the sum may overflow S (i.e. the return base type)
-+    // We do not need Less() here because we compare promoted unsigned types.
-+    return (sum >= a && sum <= std::numeric_limits::max()) ?
-+           std::make_pair(sum, true) : std::make_pair(S(), false);
-+}
-+
-+/// This IncreaseSumInternal() overload supports a larger variety of types.
-+/// \returns a non-overflowing sum of the two arguments (or nothing)
-+/// \returns nothing if at least one of the arguments is negative
-+/// \prec at least one of the argument types is signed
-+template ::value, int> = 0>
-+std::pair constexpr
-+IncreaseSumInternal(const A a, const B b) {
-+    AssertNaturalType();
-+    AssertNaturalType();
-+    AssertNaturalType();
-+
-+    // we should only be called by IncreaseSum() that does integer promotion
-+    static_assert(std::is_same::value, "a will not be promoted");
-+    static_assert(std::is_same::value, "b will not be promoted");
-+
-+    return
-+        // We could support a non-under/overflowing sum of negative numbers, but
-+        // our callers use negative values specially (e.g., for do-not-use or
-+        // do-not-limit settings) and are not supposed to do math with them.
-+        (a < 0 || b < 0) ? std::make_pair(S(), false) :
-+        // To avoid undefined behavior of signed overflow, we must not compute
-+        // the raw a+b sum if it may overflow. When A is not B, a or b undergoes
-+        // (safe for non-negatives) integer conversion in these expressions, so
-+        // we do not know the resulting a+b type AB and its maximum. We must
-+        // also detect subsequent casting-to-S overflows.
-+        // Overflow condition: (a + b > maxAB) or (a + b > maxS).
-+        // A is an integer promotion of S, so maxS <= maxA <= maxAB.
-+        // Since maxS <= maxAB, it is sufficient to just check: a + b > maxS,
-+        // which is the same as the overflow-safe condition here: maxS - a < b.
-+        // Finally, (maxS - a) cannot overflow because a is not negative and
-+        // cannot underflow because a is a promotion of s: 0 <= a <= maxS.
-+        Less(std::numeric_limits::max() - a, b) ? std::make_pair(S(), false) :
-+        std::make_pair(S(a + b), true);
-+}
-+
-+/// argument pack expansion termination for IncreaseSum()
-+template 
-+std::pair
-+IncreaseSum(const S s, const T t)
-+{
-+    // Force (always safe) integer promotions now, to give std::enable_if_t<>
-+    // promoted types instead of entering IncreaseSumInternal(s,t)
-+    // but getting a _signed_ promoted value of s or t in s + t.
-+    return IncreaseSumInternal(+s, +t);
-+}
-+
-+/// \returns a non-overflowing sum of the arguments (or nothing)
-+template 
-+std::pair
-+IncreaseSum(const S sum, const T t, const Args... args) {
-+    const auto head = IncreaseSum(sum, t);
-+    if (head.second) {
-+        return IncreaseSum(head.first, args...);
-+    } else {
-+        // std::optional() triggers bogus -Wmaybe-uninitialized warnings in GCC v10.3
-+        return std::make_pair(S(), false);
-+    }
-+}
-+
-+/// \returns an exact, non-overflowing sum of the arguments (or nothing)
-+template 
-+std::pair
-+NaturalSum(const Args... args) {
-+    return IncreaseSum(0, args...);
-+}
-+
-+/// Safely resets the given variable to NaturalSum() of the given arguments.
-+/// If the sum overflows, resets to variable's maximum possible value.
-+/// \returns the new variable value (like an assignment operator would)
-+template 
-+S
-+SetToNaturalSumOrMax(S &var, const Args... args)
-+{
-+    var = NaturalSum(args...).value_or(std::numeric_limits::max());
-+    return var;
-+}
-+
-+/// converts a given non-negative integer into an integer of a given type
-+/// without loss of information or undefined behavior
-+template 
-+Result
-+NaturalCast(const Source s)
-+{
-+    return NaturalSum(s).value();
-+}
-+
- #endif /* _SQUID_SRC_SQUIDMATH_H */
- 
-diff --git a/src/Store.h b/src/Store.h
-index 3eb6b84..2475fe0 100644
---- a/src/Store.h
-+++ b/src/Store.h
-@@ -49,6 +49,9 @@ public:
-     StoreEntry();
-     virtual ~StoreEntry();
- 
-+    MemObject &mem() { assert(mem_obj); return *mem_obj; }
-+    const MemObject &mem() const { assert(mem_obj); return *mem_obj; }
-+
-     virtual HttpReply const *getReply() const;
-     virtual void write (StoreIOBuffer);
- 
-diff --git a/src/StoreClient.h b/src/StoreClient.h
-index 65472d8..942f9fc 100644
---- a/src/StoreClient.h
-+++ b/src/StoreClient.h
-@@ -9,11 +9,13 @@
- #ifndef SQUID_STORECLIENT_H
- #define SQUID_STORECLIENT_H
- 
-+#include "base/AsyncCall.h"
- #include "dlink.h"
-+#include "store/ParsingBuffer.h"
- #include "StoreIOBuffer.h"
- #include "StoreIOState.h"
- 
--typedef void STCB(void *, StoreIOBuffer);   /* store callback */
-+using STCB = void (void *, StoreIOBuffer);   /* store callback */
- 
- class StoreEntry;
- 
-@@ -39,17 +41,34 @@ class store_client
- public:
-     store_client(StoreEntry *);
-     ~store_client();
--    bool memReaderHasLowerOffset(int64_t) const;
-+
-+    /// the client will not use HTTP response bytes with lower offsets (if any)
-+    auto discardableHttpEnd() const { return discardableHttpEnd_; }
-+
-     int getType() const;
--    void fail();
--    void callback(ssize_t len, bool error = false);
-+
-+    /// React to the end of reading the response from disk. There will be no
-+    /// more readHeader() and readBody() callbacks for the current storeRead()
-+    /// swapin after this notification.
-+    void noteSwapInDone(bool error);
-+
-     void doCopy (StoreEntry *e);
-     void readHeader(const char *buf, ssize_t len);
-     void readBody(const char *buf, ssize_t len);
-+
-+    /// Request StoreIOBuffer-described response data via an asynchronous STCB
-+    /// callback. At most one outstanding request is allowed per store_client.
-     void copy(StoreEntry *, StoreIOBuffer, STCB *, void *);
-+
-     void dumpStats(MemBuf * output, int clientNumber) const;
- 
--    int64_t cmp_offset;
-+    // TODO: When STCB gets a dedicated Answer type, move this info there.
-+    /// Whether the last successful storeClientCopy() answer was known to
-+    /// contain the last body bytes of the HTTP response
-+    /// \retval true requesting bytes at higher offsets is futile
-+    /// \sa STCB
-+    bool atEof() const { return atEof_; }
-+
- #if STORE_CLIENT_LIST_DEBUG
- 
-     void *owner;
-@@ -59,33 +78,86 @@ public:
-     StoreIOState::Pointer swapin_sio;
- 
-     struct {
-+        /// whether we are expecting a response to be swapped in from disk
-+        /// (i.e. whether async storeRead() is currently in progress)
-+        // TODO: a better name reflecting the 'in' scope of the flag
-         bool disk_io_pending;
-+
-+        /// whether the store_client::doCopy()-initiated STCB sequence is
-+        /// currently in progress
-         bool store_copying;
--        bool copy_event_pending;
-     } flags;
- 
- #if USE_DELAY_POOLS
-     DelayId delayId;
-+
-+    /// The maximum number of bytes the Store client can read/copy next without
-+    /// overflowing its buffer and without violating delay pool limits. Store
-+    /// I/O is not rate-limited, but we assume that the same number of bytes may
-+    /// be read from the Squid-to-server connection that may be rate-limited.
-+    int bytesWanted() const;
-+
-     void setDelayId(DelayId delay_id);
- #endif
- 
-     dlink_node node;
--    /* Below here is private - do no alter outside storeClient calls */
--    StoreIOBuffer copyInto;
- 
- private:
--    bool moreToSend() const;
-+    bool moreToRead() const;
-+    bool canReadFromMemory() const;
-+    bool answeredOnce() const { return answers >= 1; }
-+    bool sendingHttpHeaders() const;
-+    int64_t nextHttpReadOffset() const;
- 
-     void fileRead();
-     void scheduleDiskRead();
--    void scheduleMemRead();
-+    void readFromMemory();
-     void scheduleRead();
-     bool startSwapin();
-     bool unpackHeader(char const *buf, ssize_t len);
-+    void handleBodyFromDisk();
-+    void maybeWriteFromDiskToMemory(const StoreIOBuffer &);
-+
-+    bool parseHttpHeadersFromDisk();
-+    bool tryParsingHttpHeaders();
-+    void skipHttpHeadersFromDisk();
-+
-+    void fail();
-+    void callback(ssize_t);
-+    void noteCopiedBytes(size_t);
-+    void noteNews();
-+    void finishCallback();
-+    static void FinishCallback(store_client *);
- 
-     int type;
-     bool object_ok;
- 
-+    /// \copydoc atEof()
-+    bool atEof_;
-+
-+    /// Storage and metadata associated with the current copy() request. Ought
-+    /// to be ignored when not answering a copy() request.
-+    /// * copyInto.offset is the requested HTTP response body offset;
-+    /// * copyInto.data is the client-owned, client-provided result buffer;
-+    /// * copyInto.length is the size of the .data result buffer;
-+    /// * copyInto.flags are unused by this class.
-+    StoreIOBuffer copyInto;
-+
-+    // TODO: Convert to uint64_t after fixing mem_hdr::endOffset() and friends.
-+    /// \copydoc discardableHttpEnd()
-+    int64_t discardableHttpEnd_ = 0;
-+
-+    /// the total number of finishCallback() calls
-+    uint64_t answers;
-+
-+    /// Accumulates raw bytes read from Store while answering the current copy()
-+    /// request. Buffer contents depends on the source and parsing stage; it may
-+    /// hold (parts of) swap metadata, HTTP response headers, and/or HTTP
-+    /// response body bytes.
-+    std::pair parsingBuffer = std::make_pair(Store::ParsingBuffer(), false);
-+
-+    StoreIOBuffer lastDiskRead; ///< buffer used for the last storeRead() call
-+
-     /* Until we finish stuffing code into store_client */
- 
- public:
-@@ -97,6 +169,7 @@ public:
-         bool pending() const;
-         STCB *callback_handler;
-         void *callback_data;
-+        AsyncCall::Pointer notifier;
-     } _callback;
- };
- 
-diff --git a/src/StoreIOBuffer.h b/src/StoreIOBuffer.h
-index 009aafe..ad1c491 100644
---- a/src/StoreIOBuffer.h
-+++ b/src/StoreIOBuffer.h
-@@ -43,6 +43,9 @@ public:
-         return Range(offset, offset + length);
-     }
- 
-+    /// convenience method for changing the offset of a being-configured buffer
-+    StoreIOBuffer &positionAt(const int64_t newOffset) { offset = newOffset; return *this; }
-+
-     void dump() const {
-         if (fwrite(data, length, 1, stderr)) {}
-         if (fwrite("\n", 1, 1, stderr)) {}
-diff --git a/src/acl/Asn.cc b/src/acl/Asn.cc
-index 94ec862..07353d6 100644
---- a/src/acl/Asn.cc
-+++ b/src/acl/Asn.cc
-@@ -16,20 +16,22 @@
- #include "acl/DestinationIp.h"
- #include "acl/SourceAsn.h"
- #include "acl/Strategised.h"
-+#include "base/CharacterSet.h"
- #include "FwdState.h"
- #include "HttpReply.h"
- #include "HttpRequest.h"
- #include "ipcache.h"
- #include "MasterXaction.h"
- #include "mgr/Registration.h"
-+#include "parser/Tokenizer.h"
- #include "radix.h"
- #include "RequestFlags.h"
-+#include "sbuf/SBuf.h"
- #include "SquidConfig.h"
- #include "Store.h"
- #include "StoreClient.h"
- 
- #define WHOIS_PORT 43
--#define AS_REQBUF_SZ    4096
- 
- /* BEGIN of definitions for radix tree entries */
- 
-@@ -70,33 +72,18 @@ class ASState
-     CBDATA_CLASS(ASState);
- 
- public:
--    ASState();
-+    ASState() = default;
-     ~ASState();
- 
-     StoreEntry *entry;
-     store_client *sc;
-     HttpRequest::Pointer request;
-     int as_number;
--    int64_t offset;
--    int reqofs;
--    char reqbuf[AS_REQBUF_SZ];
--    bool dataRead;
-+    Store::ParsingBuffer parsingBuffer;
- };
- 
- CBDATA_CLASS_INIT(ASState);
- 
--ASState::ASState() :
--    entry(NULL),
--    sc(NULL),
--    request(NULL),
--    as_number(0),
--    offset(0),
--    reqofs(0),
--    dataRead(false)
--{
--    memset(reqbuf, 0, AS_REQBUF_SZ);
--}
--
- ASState::~ASState()
- {
-     debugs(53, 3, entry->url());
-@@ -112,7 +99,7 @@ struct rtentry_t {
-     m_ADDR e_mask;
- };
- 
--static int asnAddNet(char *, int);
-+static int asnAddNet(const SBuf &, int);
- 
- static void asnCacheStart(int as);
- 
-@@ -256,8 +243,7 @@ asnCacheStart(int as)
-     }
- 
-     asState->entry = e;
--    StoreIOBuffer readBuffer (AS_REQBUF_SZ, asState->offset, asState->reqbuf);
--    storeClientCopy(asState->sc, e, readBuffer, asHandleReply, asState);
-+    storeClientCopy(asState->sc, e, asState->parsingBuffer.makeInitialSpace(), asHandleReply, asState);
- }
- 
- static void
-@@ -265,13 +251,8 @@ asHandleReply(void *data, StoreIOBuffer result)
- {
-     ASState *asState = (ASState *)data;
-     StoreEntry *e = asState->entry;
--    char *s;
--    char *t;
--    char *buf = asState->reqbuf;
--    int leftoversz = -1;
- 
--    debugs(53, 3, "asHandleReply: Called with size=" << (unsigned int)result.length);
--    debugs(53, 3, "asHandleReply: buffer='" << buf << "'");
-+    debugs(53, 3, result << " for " << asState->as_number << " with " << *e);
- 
-     /* First figure out whether we should abort the request */
- 
-@@ -280,11 +261,7 @@ asHandleReply(void *data, StoreIOBuffer result)
-         return;
-     }
- 
--    if (result.length == 0 && asState->dataRead) {
--        debugs(53, 3, "asHandleReply: Done: " << e->url());
--        delete asState;
--        return;
--    } else if (result.flags.error) {
-+    if (result.flags.error) {
-         debugs(53, DBG_IMPORTANT, "asHandleReply: Called with Error set and size=" << (unsigned int) result.length);
-         delete asState;
-         return;
-@@ -294,117 +271,85 @@ asHandleReply(void *data, StoreIOBuffer result)
-         return;
-     }
- 
--    /*
--     * Next, attempt to parse our request
--     * Remembering that the actual buffer size is retsize + reqofs!
--     */
--    s = buf;
-+    asState->parsingBuffer.appended(result.data, result.length);
-+    Parser::Tokenizer tok(SBuf(asState->parsingBuffer.content().data, asState->parsingBuffer.contentSize()));
-+    SBuf address;
-+    // Word delimiters in WHOIS ASN replies. RFC 3912 mentions SP, CR, and LF.
-+    // Others are added to mimic an earlier isspace()-based implementation.
-+    static const auto WhoisSpaces = CharacterSet("ASCII_spaces", " \f\r\n\t\v");
-+    while (tok.token(address, WhoisSpaces)) {
-+        (void)asnAddNet(address, asState->as_number);
-+    }
-+    asState->parsingBuffer.consume(tok.parsedSize());
-+    const auto leftoverBytes = asState->parsingBuffer.contentSize();
- 
--    while ((size_t)(s - buf) < result.length + asState->reqofs && *s != '\0') {
--        while (*s && xisspace(*s))
--            ++s;
-+    if (asState->sc->atEof()) {
-+        if (leftoverBytes)
-+            debugs(53, 2, "WHOIS: Discarding the last " << leftoverBytes << " received bytes of a truncated AS response");
-+        delete asState;
-+        return;
-+    }
- 
--        for (t = s; *t; ++t) {
--            if (xisspace(*t))
--                break;
--        }
-+    if (asState->sc->atEof()) {
-+        if (leftoverBytes)
-+            debugs(53, 2, "WHOIS: Discarding the last " << leftoverBytes << " received bytes of a truncated AS response");
-+        delete asState;
-+        return;
-+    }
- 
--        if (*t == '\0') {
--            /* oof, word should continue on next block */
--            break;
--        }
-+    const auto remainingSpace = asState->parsingBuffer.space().positionAt(result.offset + result.length);
- 
--        *t = '\0';
--        debugs(53, 3, "asHandleReply: AS# " << s << " (" << asState->as_number << ")");
--        asnAddNet(s, asState->as_number);
--        s = t + 1;
--        asState->dataRead = true;
-+    if (!remainingSpace.length) {
-+        Assure(leftoverBytes);
-+        debugs(53, DBG_IMPORTANT, "WARNING: Ignoring the tail of a WHOIS AS response" <<
-+               " with an unparsable section of " << leftoverBytes <<
-+               " bytes ending at offset " << remainingSpace.offset);
-+        delete asState;
-+        return;
-     }
- 
--    /*
--     * Next, grab the end of the 'valid data' in the buffer, and figure
--     * out how much data is left in our buffer, which we need to keep
--     * around for the next request
--     */
--    leftoversz = (asState->reqofs + result.length) - (s - buf);
--
--    assert(leftoversz >= 0);
--
--    /*
--     * Next, copy the left over data, from s to s + leftoversz to the
--     * beginning of the buffer
--     */
--    memmove(buf, s, leftoversz);
--
--    /*
--     * Next, update our offset and reqofs, and kick off a copy if required
--     */
--    asState->offset += result.length;
--
--    asState->reqofs = leftoversz;
--
--    debugs(53, 3, "asState->offset = " << asState->offset);
--
--    if (e->store_status == STORE_PENDING) {
--        debugs(53, 3, "asHandleReply: store_status == STORE_PENDING: " << e->url()  );
--        StoreIOBuffer tempBuffer (AS_REQBUF_SZ - asState->reqofs,
--                                  asState->offset,
--                                  asState->reqbuf + asState->reqofs);
--        storeClientCopy(asState->sc,
--                        e,
--                        tempBuffer,
--                        asHandleReply,
--                        asState);
--    } else {
--        StoreIOBuffer tempBuffer;
--        debugs(53, 3, "asHandleReply: store complete, but data received " << e->url()  );
--        tempBuffer.offset = asState->offset;
--        tempBuffer.length = AS_REQBUF_SZ - asState->reqofs;
--        tempBuffer.data = asState->reqbuf + asState->reqofs;
--        storeClientCopy(asState->sc,
--                        e,
--                        tempBuffer,
--                        asHandleReply,
--                        asState);
--    }
-+    const decltype(StoreIOBuffer::offset) stillReasonableOffset = 100000; // an arbitrary limit in bytes
-+    if (remainingSpace.offset > stillReasonableOffset) {
-+        // stop suspicious accumulation of parsed addresses and/or work
-+        debugs(53, DBG_IMPORTANT, "WARNING: Ignoring the tail of a suspiciously large WHOIS AS response" <<
-+               " exceeding " << stillReasonableOffset << " bytes");
-+        delete asState;
-+        return;
-+     }
-+
-+    storeClientCopy(asState->sc, e, remainingSpace, asHandleReply, asState);
- }
- 
- /**
-  * add a network (addr, mask) to the radix tree, with matching AS number
-  */
- static int
--asnAddNet(char *as_string, int as_number)
-+asnAddNet(const SBuf &addressAndMask, const int as_number)
- {
-     struct squid_radix_node *rn;
-     CbDataList **Tail = NULL;
-     CbDataList *q = NULL;
-     as_info *asinfo = NULL;
- 
--    Ip::Address mask;
--    Ip::Address addr;
--    char *t;
--    int bitl;
--
--    t = strchr(as_string, '/');
--
--    if (t == NULL) {
-+    static const CharacterSet NonSlashSet = CharacterSet("slash", "/").complement("non-slash");
-+    Parser::Tokenizer tok(addressAndMask);
-+    SBuf addressToken;
-+    if (!(tok.prefix(addressToken, NonSlashSet) && tok.skip('/'))) {
-         debugs(53, 3, "asnAddNet: failed, invalid response from whois server.");
-         return 0;
-     }
- 
--    *t = '\0';
--    addr = as_string;
--    bitl = atoi(t + 1);
--
--    if (bitl < 0)
--        bitl = 0;
-+    const Ip::Address addr = addressToken.c_str();
- 
-     // INET6 TODO : find a better way of identifying the base IPA family for mask than this.
--    t = strchr(as_string, '.');
-+    const auto addrFamily = (addressToken.find('.') != SBuf::npos) ? AF_INET : AF_INET6;
- 
-     // generate Netbits Format Mask
-+    Ip::Address mask;
-     mask.setNoAddr();
--    mask.applyMask(bitl, (t!=NULL?AF_INET:AF_INET6) );
-+    int64_t bitl = 0;
-+    if (tok.int64(bitl, 10, false))
-+        mask.applyMask(bitl, addrFamily);
- 
-     debugs(53, 3, "asnAddNet: called for " << addr << "/" << mask );
- 
-diff --git a/src/acl/FilledChecklist.cc b/src/acl/FilledChecklist.cc
-index 9826c24..33eeb67 100644
---- a/src/acl/FilledChecklist.cc
-+++ b/src/acl/FilledChecklist.cc
-@@ -116,7 +116,6 @@ ACLFilledChecklist::verifyAle() const
-     if (reply && !al->reply) {
-         showDebugWarning("HttpReply object");
-         al->reply = reply;
--        HTTPMSGLOCK(al->reply);
-     }
- 
- #if USE_IDENT
-diff --git a/src/adaptation/icap/ModXact.cc b/src/adaptation/icap/ModXact.cc
-index 370f077..2bcc917 100644
---- a/src/adaptation/icap/ModXact.cc
-+++ b/src/adaptation/icap/ModXact.cc
-@@ -1292,11 +1292,8 @@ void Adaptation::Icap::ModXact::finalizeLogInfo()
-     al.adapted_request = adapted_request_;
-     HTTPMSGLOCK(al.adapted_request);
- 
--    if (adapted_reply_) {
--        al.reply = adapted_reply_;
--        HTTPMSGLOCK(al.reply);
--    } else
--        al.reply = NULL;
-+    // XXX: This reply (and other ALE members!) may have been needed earlier.
-+    al.reply = adapted_reply_;
- 
-     if (h->rfc931.size())
-         al.cache.rfc931 = h->rfc931.termedBuf();
-@@ -1331,12 +1328,6 @@ void Adaptation::Icap::ModXact::finalizeLogInfo()
-         if (replyHttpBodySize >= 0)
-             al.cache.highOffset = replyHttpBodySize;
-         //don't set al.cache.objectSize because it hasn't exist yet
--
--        MemBuf mb;
--        mb.init();
--        adapted_reply_->header.packInto(&mb);
--        al.headers.reply = xstrdup(mb.buf);
--        mb.clean();
-     }
-     prepareLogWithRequestDetails(adapted_request_, alep);
-     Xaction::finalizeLogInfo();
-diff --git a/src/adaptation/icap/icap_log.cc b/src/adaptation/icap/icap_log.cc
-index ecc4baf..6bb5a6d 100644
---- a/src/adaptation/icap/icap_log.cc
-+++ b/src/adaptation/icap/icap_log.cc
-@@ -62,7 +62,7 @@ void icapLogLog(AccessLogEntry::Pointer &al)
-     if (IcapLogfileStatus == LOG_ENABLE) {
-         ACLFilledChecklist checklist(NULL, al->adapted_request, NULL);
-         if (al->reply) {
--            checklist.reply = al->reply;
-+            checklist.reply = al->reply.getRaw();
-             HTTPMSGLOCK(checklist.reply);
-         }
-         accessLogLogTo(Config.Log.icaplogs, al, &checklist);
-diff --git a/src/base/Assure.cc b/src/base/Assure.cc
-new file mode 100644
-index 0000000..cb69fc5
---- /dev/null
-+++ b/src/base/Assure.cc
-@@ -0,0 +1,24 @@
-+/*
-+ * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
-+ *
-+ * Squid software is distributed under GPLv2+ license and includes
-+ * contributions from numerous individuals and organizations.
-+ * Please see the COPYING and CONTRIBUTORS files for details.
-+ */
-+
-+#include "squid.h"
-+#include "base/Assure.h"
-+#include "base/TextException.h"
-+#include "sbuf/Stream.h"
-+
-+[[ noreturn ]] void
-+ReportAndThrow_(const int debugLevel, const char *description, const SourceLocation &location)
-+{
-+    const TextException ex(description, location);
-+    const auto label = debugLevel <= DBG_IMPORTANT ? "ERROR: Squid BUG: " : "";
-+    // TODO: Consider also printing the number of BUGs reported so far. It would
-+    // require GC, but we could even print the number of same-location reports.
-+    debugs(0, debugLevel, label << ex);
-+    throw ex;
-+}
-+
-diff --git a/src/base/Assure.h b/src/base/Assure.h
-new file mode 100644
-index 0000000..bb571d2
---- /dev/null
-+++ b/src/base/Assure.h
-@@ -0,0 +1,52 @@
-+/*
-+ * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
-+ *
-+ * Squid software is distributed under GPLv2+ license and includes
-+ * contributions from numerous individuals and organizations.
-+ * Please see the COPYING and CONTRIBUTORS files for details.
-+ */
-+
-+#ifndef SQUID_SRC_BASE_ASSURE_H
-+#define SQUID_SRC_BASE_ASSURE_H
-+
-+#include "base/Here.h"
-+
-+/// Reports the description (at the given debugging level) and throws
-+/// the corresponding exception. Reduces compiled code size of Assure() and
-+/// Must() callers. Do not call directly; use Assure() instead.
-+/// \param description explains the condition (i.e. what MUST happen)
-+[[ noreturn ]] void ReportAndThrow_(int debugLevel, const char *description, const SourceLocation &);
-+
-+/// Calls ReportAndThrow() if needed. Reduces caller code duplication.
-+/// Do not call directly; use Assure() instead.
-+/// \param description c-string explaining the condition (i.e. what MUST happen)
-+#define Assure_(debugLevel, condition, description, location) \
-+    while (!(condition)) \
-+        ReportAndThrow_((debugLevel), (description), (location))
-+
-+#if !defined(NDEBUG)
-+
-+/// Like assert() but throws an exception instead of aborting the process. Use
-+/// this macro to detect code logic mistakes (i.e. bugs) where aborting the
-+/// current AsyncJob or a similar task is unlikely to jeopardize Squid service
-+/// integrity. For example, this macro is _not_ appropriate for detecting bugs
-+/// that indicate a dangerous global state corruption which may go unnoticed by
-+/// other jobs after the current job or task is aborted.
-+#define Assure(condition) \
-+        Assure2((condition), #condition)
-+
-+/// Like Assure() but allows the caller to customize the exception message.
-+/// \param description string literal describing the condition (i.e. what MUST happen)
-+#define Assure2(condition, description) \
-+        Assure_(0, (condition), ("assurance failed: " description), Here())
-+
-+#else
-+
-+/* do-nothing implementations for NDEBUG builds */
-+#define Assure(condition) ((void)0)
-+#define Assure2(condition, description) ((void)0)
-+
-+#endif /* NDEBUG */
-+
-+#endif /* SQUID_SRC_BASE_ASSURE_H */
-+
-diff --git a/src/base/Makefile.am b/src/base/Makefile.am
-index 9b0f4cf..d5f4c01 100644
---- a/src/base/Makefile.am
-+++ b/src/base/Makefile.am
-@@ -11,6 +11,8 @@ include $(top_srcdir)/src/TestHeaders.am
- noinst_LTLIBRARIES = libbase.la
- 
- libbase_la_SOURCES = \
-+	Assure.cc \
-+	Assure.h \
- 	AsyncCall.cc \
- 	AsyncCall.h \
- 	AsyncCallQueue.cc \
-diff --git a/src/base/Makefile.in b/src/base/Makefile.in
-index 90a4f5b..6a83aa4 100644
---- a/src/base/Makefile.in
-+++ b/src/base/Makefile.in
-@@ -163,7 +163,7 @@ CONFIG_CLEAN_FILES =
- CONFIG_CLEAN_VPATH_FILES =
- LTLIBRARIES = $(noinst_LTLIBRARIES)
- libbase_la_LIBADD =
--am_libbase_la_OBJECTS = AsyncCall.lo AsyncCallQueue.lo AsyncJob.lo \
-+am_libbase_la_OBJECTS = Assure.lo AsyncCall.lo AsyncCallQueue.lo AsyncJob.lo \
- 	CharacterSet.lo File.lo Here.lo RegexPattern.lo \
- 	RunnersRegistry.lo TextException.lo
- libbase_la_OBJECTS = $(am_libbase_la_OBJECTS)
-@@ -186,7 +186,7 @@ am__v_at_1 =
- DEFAULT_INCLUDES = 
- depcomp = $(SHELL) $(top_srcdir)/cfgaux/depcomp
- am__maybe_remake_depfiles = depfiles
--am__depfiles_remade = ./$(DEPDIR)/AsyncCall.Plo \
-+am__depfiles_remade = ./$(DEPDIR)/Assure.Plo ./$(DEPDIR)/AsyncCall.Plo \
- 	./$(DEPDIR)/AsyncCallQueue.Plo ./$(DEPDIR)/AsyncJob.Plo \
- 	./$(DEPDIR)/CharacterSet.Plo ./$(DEPDIR)/File.Plo \
- 	./$(DEPDIR)/Here.Plo ./$(DEPDIR)/RegexPattern.Plo \
-@@ -729,6 +729,8 @@ COMPAT_LIB = $(top_builddir)/compat/libcompatsquid.la $(LIBPROFILER)
- subst_perlshell = sed -e 's,[@]PERL[@],$(PERL),g' <$(srcdir)/$@.pl.in >$@ || ($(RM) -f $@ ; exit 1)
- noinst_LTLIBRARIES = libbase.la
- libbase_la_SOURCES = \
-+	Assure.cc \
-+	Assure.h \
- 	AsyncCall.cc \
- 	AsyncCall.h \
- 	AsyncCallQueue.cc \
-@@ -827,6 +829,7 @@ mostlyclean-compile:
- distclean-compile:
- 	-rm -f *.tab.c
- 
-+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Assure.Plo@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AsyncCall.Plo@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AsyncCallQueue.Plo@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AsyncJob.Plo@am__quote@ # am--include-marker
-@@ -1167,7 +1170,8 @@ clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
- 	clean-noinstLTLIBRARIES mostlyclean-am
- 
- distclean: distclean-am
--		-rm -f ./$(DEPDIR)/AsyncCall.Plo
-+		-rm -f ./$(DEPDIR)/Assure.Plo
-+	-rm -f ./$(DEPDIR)/AsyncCall.Plo
- 	-rm -f ./$(DEPDIR)/AsyncCallQueue.Plo
- 	-rm -f ./$(DEPDIR)/AsyncJob.Plo
- 	-rm -f ./$(DEPDIR)/CharacterSet.Plo
-@@ -1221,7 +1225,8 @@ install-ps-am:
- installcheck-am:
- 
- maintainer-clean: maintainer-clean-am
--		-rm -f ./$(DEPDIR)/AsyncCall.Plo
-+		-rm -f ./$(DEPDIR)/Assure.Plo
-+	-rm -f ./$(DEPDIR)/AsyncCall.Plo
- 	-rm -f ./$(DEPDIR)/AsyncCallQueue.Plo
- 	-rm -f ./$(DEPDIR)/AsyncJob.Plo
- 	-rm -f ./$(DEPDIR)/CharacterSet.Plo
-diff --git a/src/base/TextException.cc b/src/base/TextException.cc
-index 5cfeb26..f895ae9 100644
---- a/src/base/TextException.cc
-+++ b/src/base/TextException.cc
-@@ -58,6 +58,13 @@ TextException::what() const throw()
-     return result.what();
- }
- 
-+std::ostream &
-+operator <<(std::ostream &os, const TextException &ex)
-+{
-+    ex.print(os);
-+    return os;
-+}
-+
- std::ostream &
- CurrentException(std::ostream &os)
- {
-diff --git a/src/base/TextException.h b/src/base/TextException.h
-index 6a79536..1f9ca11 100644
---- a/src/base/TextException.h
-+++ b/src/base/TextException.h
-@@ -9,6 +9,7 @@
- #ifndef SQUID__TEXTEXCEPTION_H
- #define SQUID__TEXTEXCEPTION_H
- 
-+#include "base/Assure.h"
- #include "base/Here.h"
- 
- #include 
-@@ -51,11 +52,12 @@ public:
- /// prints active (i.e., thrown but not yet handled) exception
- std::ostream &CurrentException(std::ostream &);
- 
-+/// efficiently prints TextException
-+std::ostream &operator <<(std::ostream &, const TextException &);
-+
- /// legacy convenience macro; it is not difficult to type Here() now
- #define TexcHere(msg) TextException((msg), Here())
- 
--/// Like assert() but throws an exception instead of aborting the process
--/// and allows the caller to specify a custom exception message.
- #define Must2(condition, message) \
-     do { \
-         if (!(condition)) { \
-@@ -65,8 +67,13 @@ std::ostream &CurrentException(std::ostream &);
-         } \
-     } while (/*CONSTCOND*/ false)
- 
-+/// Like assert() but throws an exception instead of aborting the process
-+/// and allows the caller to specify a custom exception message.
-+#define Must3(condition, description, location) \
-+    Assure_(3, (condition), ("check failed: " description), (location))
-+
- /// Like assert() but throws an exception instead of aborting the process.
--#define Must(condition) Must2((condition), "check failed: " #condition)
-+#define Must(condition) Must3((condition), #condition, Here())
- 
- /// Reports and swallows all exceptions to prevent compiler warnings and runtime
- /// errors related to throwing class destructors. Should be used for most dtors.
-diff --git a/src/clientStream.cc b/src/clientStream.cc
-index 04d89c0..bd5dd09 100644
---- a/src/clientStream.cc
-+++ b/src/clientStream.cc
-@@ -154,8 +154,7 @@ clientStreamCallback(clientStreamNode * thisObject, ClientHttpRequest * http,
-     assert(thisObject && http && thisObject->node.next);
-     next = thisObject->next();
- 
--    debugs(87, 3, "clientStreamCallback: Calling " << next->callback << " with cbdata " <<
--           next->data.getRaw() << " from node " << thisObject);
-+    debugs(87, 3, thisObject << " gives " << next->data << ' ' << replyBuffer);
-     next->callback(next, http, rep, replyBuffer);
- }
- 
-diff --git a/src/client_side.cc b/src/client_side.cc
-index ab393e4..c46a845 100644
---- a/src/client_side.cc
-+++ b/src/client_side.cc
-@@ -429,7 +429,7 @@ ClientHttpRequest::logRequest()
-         // The al->notes and request->notes must point to the same object.
-         (void)SyncNotes(*al, *request);
-         for (auto i = Config.notes.begin(); i != Config.notes.end(); ++i) {
--            if (const char *value = (*i)->match(request, al->reply, al)) {
-+            if (const char *value = (*i)->match(request, al->reply.getRaw(), al)) {
-                 NotePairs ¬es = SyncNotes(*al, *request);
-                 notes.add((*i)->key.termedBuf(), value);
-                 debugs(33, 3, (*i)->key.termedBuf() << " " << value);
-@@ -439,7 +439,7 @@ ClientHttpRequest::logRequest()
- 
-     ACLFilledChecklist checklist(NULL, request, NULL);
-     if (al->reply) {
--        checklist.reply = al->reply;
-+        checklist.reply = al->reply.getRaw();
-         HTTPMSGLOCK(checklist.reply);
-     }
- 
-@@ -457,7 +457,7 @@ ClientHttpRequest::logRequest()
-         ACLFilledChecklist statsCheck(Config.accessList.stats_collection, request, NULL);
-         statsCheck.al = al;
-         if (al->reply) {
--            statsCheck.reply = al->reply;
-+            statsCheck.reply = al->reply.getRaw();
-             HTTPMSGLOCK(statsCheck.reply);
-         }
-         updatePerformanceCounters = statsCheck.fastCheck().allowed();
-@@ -3844,6 +3844,11 @@ ConnStateData::finishDechunkingRequest(bool withSuccess)
- void
- ConnStateData::sendControlMsg(HttpControlMsg msg)
- {
-+    if (const auto context = pipeline.front()) {
-+        if (context->http)
-+            context->http->al->reply = msg.reply;
-+    }
-+
-     if (!isOpen()) {
-         debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
-         return;
-diff --git a/src/client_side_reply.cc b/src/client_side_reply.cc
-index c919af4..fea5ecb 100644
---- a/src/client_side_reply.cc
-+++ b/src/client_side_reply.cc
-@@ -34,6 +34,7 @@
- #include "RequestFlags.h"
- #include "SquidConfig.h"
- #include "SquidTime.h"
-+#include "SquidMath.h"
- #include "Store.h"
- #include "StrList.h"
- #include "tools.h"
-@@ -76,11 +77,7 @@ clientReplyContext::clientReplyContext(ClientHttpRequest *clientContext) :
-     purgeStatus(Http::scNone),
-     lookingforstore(0),
-     http(cbdataReference(clientContext)),
--    headers_sz(0),
-     sc(NULL),
--    old_reqsize(0),
--    reqsize(0),
--    reqofs(0),
- #if USE_CACHE_DIGESTS
-     lookup_type(NULL),
- #endif
-@@ -166,8 +163,6 @@ void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry, const char *rea
- #if USE_DELAY_POOLS
-     sc->setDelayId(DelayId::DelayClient(http));
- #endif
--    reqofs = 0;
--    reqsize = 0;
-     if (http->request)
-         http->request->ignoreRange(reason);
-     flags.storelogiccomplete = 1;
-@@ -206,13 +201,10 @@ clientReplyContext::saveState()
-     old_sc = sc;
-     old_lastmod = http->request->lastmod;
-     old_etag = http->request->etag;
--    old_reqsize = reqsize;
--    tempBuffer.offset = reqofs;
-+
-     /* Prevent accessing the now saved entries */
-     http->storeEntry(NULL);
-     sc = NULL;
--    reqsize = 0;
--    reqofs = 0;
- }
- 
- void
-@@ -223,8 +215,6 @@ clientReplyContext::restoreState()
-     removeClientStoreReference(&sc, http);
-     http->storeEntry(old_entry);
-     sc = old_sc;
--    reqsize = old_reqsize;
--    reqofs = tempBuffer.offset;
-     http->request->lastmod = old_lastmod;
-     http->request->etag = old_etag;
-     /* Prevent accessed the old saved entries */
-@@ -232,7 +222,7 @@ clientReplyContext::restoreState()
-     old_sc = NULL;
-     old_lastmod = -1;
-     old_etag.clean();
--    old_reqsize = 0;
-+
-     tempBuffer.offset = 0;
- }
- 
-@@ -250,18 +240,27 @@ clientReplyContext::getNextNode() const
-     return (clientStreamNode *)ourNode->node.next->data;
- }
- 
--/* This function is wrong - the client parameters don't include the
-- * header offset
-- */
-+/// Request HTTP response headers from Store, to be sent to the given recipient.
-+/// That recipient also gets zero, some, or all HTTP response body bytes (into
-+/// next()->readBuffer).
- void
--clientReplyContext::triggerInitialStoreRead()
-+clientReplyContext::triggerInitialStoreRead(STCB recipient)
- {
--    /* when confident, 0 becomes reqofs, and then this factors into
--     * startSendProcess
--     */
--    assert(reqofs == 0);
-+    Assure(recipient != HandleIMSReply);
-+    lastStreamBufferedBytes = StoreIOBuffer(); // storeClientCopy(next()->readBuffer) invalidates
-     StoreIOBuffer localTempBuffer (next()->readBuffer.length, 0, next()->readBuffer.data);
--    storeClientCopy(sc, http->storeEntry(), localTempBuffer, SendMoreData, this);
-+    ::storeClientCopy(sc, http->storeEntry(), localTempBuffer, recipient, this);
-+}
-+
-+/// Request HTTP response body bytes from Store into next()->readBuffer. This
-+/// method requests body bytes at readerBuffer.offset and, hence, it should only
-+/// be called after we triggerInitialStoreRead() and get the requested HTTP
-+/// response headers (using zero offset).
-+void
-+clientReplyContext::requestMoreBodyFromStore()
-+{
-+    lastStreamBufferedBytes = StoreIOBuffer(); // storeClientCopy(next()->readBuffer) invalidates
-+    ::storeClientCopy(sc, http->storeEntry(), next()->readBuffer, SendMoreData, this);
- }
- 
- /* there is an expired entry in the store.
-@@ -358,30 +357,23 @@ clientReplyContext::processExpired()
-     {
-         /* start counting the length from 0 */
-         StoreIOBuffer localTempBuffer(HTTP_REQBUF_SZ, 0, tempbuf);
--        storeClientCopy(sc, entry, localTempBuffer, HandleIMSReply, this);
-+        // keep lastStreamBufferedBytes: tempbuf is not a Client Stream buffer
-+        ::storeClientCopy(sc, entry, localTempBuffer, HandleIMSReply, this);
-     }
- }
- 
- void
--clientReplyContext::sendClientUpstreamResponse()
-+clientReplyContext::sendClientUpstreamResponse(const StoreIOBuffer &upstreamResponse)
- {
--    StoreIOBuffer tempresult;
-     removeStoreReference(&old_sc, &old_entry);
- 
-     if (collapsedRevalidation)
-         http->storeEntry()->clearPublicKeyScope();
- 
-     /* here the data to send is the data we just received */
--    tempBuffer.offset = 0;
--    old_reqsize = 0;
--    /* sendMoreData tracks the offset as well.
--     * Force it back to zero */
--    reqofs = 0;
-     assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
--    /* TODO: provide sendMoreData with the ready parsed reply */
--    tempresult.length = reqsize;
--    tempresult.data = tempbuf;
--    sendMoreData(tempresult);
-+
-+    sendMoreData(upstreamResponse);
- }
- 
- void
-@@ -398,11 +390,9 @@ clientReplyContext::sendClientOldEntry()
-     restoreState();
-     /* here the data to send is in the next nodes buffers already */
-     assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
--    /* sendMoreData tracks the offset as well.
--     * Force it back to zero */
--    reqofs = 0;
--    StoreIOBuffer tempresult (reqsize, reqofs, next()->readBuffer.data);
--    sendMoreData(tempresult);
-+    Assure(matchesStreamBodyBuffer(lastStreamBufferedBytes));
-+    Assure(!lastStreamBufferedBytes.offset);
-+    sendMoreData(lastStreamBufferedBytes);
- }
- 
- /* This is the workhorse of the HandleIMSReply callback.
-@@ -416,11 +406,11 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
-     if (deleting)
-         return;
- 
--    debugs(88, 3, http->storeEntry()->url() << ", " << (long unsigned) result.length << " bytes");
--
-     if (http->storeEntry() == NULL)
-         return;
- 
-+    debugs(88, 3, http->storeEntry()->url() << " got " << result);
-+
-     if (result.flags.error && !EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
-         return;
- 
-@@ -433,9 +423,6 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
-         return;
-     }
- 
--    /* update size of the request */
--    reqsize = result.length + reqofs;
--
-     const Http::StatusCode status = http->storeEntry()->getReply()->sline.status();
- 
-     // request to origin was aborted
-@@ -460,7 +447,7 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
-         if (http->request->flags.ims && !old_entry->modifiedSince(http->request->ims, http->request->imslen)) {
-             // forward the 304 from origin
-             debugs(88, 3, "origin replied 304, revalidating existing entry and forwarding 304 to client");
--            sendClientUpstreamResponse();
-+            sendClientUpstreamResponse(result);
-         } else {
-             // send existing entry, it's still valid
-             debugs(88, 3, "origin replied 304, revalidating existing entry and sending " <<
-@@ -484,7 +471,7 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
-             http->logType = LOG_TCP_REFRESH_MODIFIED;
-             debugs(88, 3, "origin replied " << status <<
-                    ", replacing existing entry and forwarding to client");
--            sendClientUpstreamResponse();
-+            sendClientUpstreamResponse(result);
-         }
-     }
- 
-@@ -493,7 +480,7 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
-         http->logType = LOG_TCP_REFRESH_FAIL_ERR;
-         debugs(88, 3, "origin replied with error " << status <<
-                ", forwarding to client due to fail_on_validation_err");
--        sendClientUpstreamResponse();
-+        sendClientUpstreamResponse(result);
-     } else {
-         // ignore and let client have old entry
-         http->logType = LOG_TCP_REFRESH_FAIL_OLD;
-@@ -506,13 +493,7 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
- SQUIDCEXTERN CSR clientGetMoreData;
- SQUIDCEXTERN CSD clientReplyDetach;
- 
--/**
-- * clientReplyContext::cacheHit Should only be called until the HTTP reply headers
-- * have been parsed.  Normally this should be a single call, but
-- * it might take more than one.  As soon as we have the headers,
-- * we hand off to clientSendMoreData, processExpired, or
-- * processMiss.
-- */
-+/// \copydoc clientReplyContext::cacheHit()
- void
- clientReplyContext::CacheHit(void *data, StoreIOBuffer result)
- {
-@@ -520,11 +501,11 @@ clientReplyContext::CacheHit(void *data, StoreIOBuffer result)
-     context->cacheHit(result);
- }
- 
--/**
-- * Process a possible cache HIT.
-- */
-+/// Processes HTTP response headers received from Store on a suspected cache hit
-+/// path. May be called several times (e.g., a Vary marker object hit followed
-+/// by the corresponding variant hit).
- void
--clientReplyContext::cacheHit(StoreIOBuffer result)
-+clientReplyContext::cacheHit(const StoreIOBuffer result)
- {
-     /** Ignore if the HIT object is being deleted. */
-     if (deleting) {
-@@ -536,7 +517,7 @@ clientReplyContext::cacheHit(StoreIOBuffer result)
- 
-     HttpRequest *r = http->request;
- 
--    debugs(88, 3, "clientCacheHit: " << http->uri << ", " << result.length << " bytes");
-+    debugs(88, 3, http->uri << " got " << result);
- 
-     if (http->storeEntry() == NULL) {
-         debugs(88, 3, "clientCacheHit: request aborted");
-@@ -560,20 +541,7 @@ clientReplyContext::cacheHit(StoreIOBuffer result)
-         return;
-     }
- 
--    if (result.length == 0) {
--        debugs(88, 5, "store IO buffer has no content. MISS");
--        /* the store couldn't get enough data from the file for us to id the
--         * object
--         */
--        /* treat as a miss */
--        http->logType = LOG_TCP_MISS;
--        processMiss();
--        return;
--    }
--
-     assert(!EBIT_TEST(e->flags, ENTRY_ABORTED));
--    /* update size of the request */
--    reqsize = result.length + reqofs;
- 
-     /*
-      * Got the headers, now grok them
-@@ -587,6 +555,8 @@ clientReplyContext::cacheHit(StoreIOBuffer result)
-         return;
-     }
- 
-+    noteStreamBufferredBytes(result);
-+
-     switch (varyEvaluateMatch(e, r)) {
- 
-     case VARY_NONE:
-@@ -687,7 +657,7 @@ clientReplyContext::cacheHit(StoreIOBuffer result)
-         return;
-     } else if (r->conditional()) {
-         debugs(88, 5, "conditional HIT");
--        if (processConditional(result))
-+        if (processConditional())
-             return;
-     }
- 
-@@ -806,7 +776,7 @@ clientReplyContext::processOnlyIfCachedMiss()
- 
- /// process conditional request from client
- bool
--clientReplyContext::processConditional(StoreIOBuffer &result)
-+clientReplyContext::processConditional()
- {
-     StoreEntry *const e = http->storeEntry();
- 
-@@ -984,16 +954,7 @@ clientReplyContext::purgeFoundObject(StoreEntry *entry)
- 
-     http->logType = LOG_TCP_HIT;
- 
--    reqofs = 0;
--
--    localTempBuffer.offset = http->out.offset;
--
--    localTempBuffer.length = next()->readBuffer.length;
--
--    localTempBuffer.data = next()->readBuffer.data;
--
--    storeClientCopy(sc, http->storeEntry(),
--                    localTempBuffer, CacheHit, this);
-+    triggerInitialStoreRead(CacheHit);
- }
- 
- void
-@@ -1111,16 +1072,10 @@ clientReplyContext::purgeDoPurgeHead(StoreEntry *newEntry)
- }
- 
- void
--clientReplyContext::traceReply(clientStreamNode * node)
-+clientReplyContext::traceReply()
- {
--    clientStreamNode *nextNode = (clientStreamNode *)node->node.next->data;
--    StoreIOBuffer localTempBuffer;
-     createStoreEntry(http->request->method, RequestFlags());
--    localTempBuffer.offset = nextNode->readBuffer.offset + headers_sz;
--    localTempBuffer.length = nextNode->readBuffer.length;
--    localTempBuffer.data = nextNode->readBuffer.data;
--    storeClientCopy(sc, http->storeEntry(),
--                    localTempBuffer, SendMoreData, this);
-+    triggerInitialStoreRead();
-     http->storeEntry()->releaseRequest();
-     http->storeEntry()->buffer();
-     HttpReply *rep = new HttpReply;
-@@ -1169,16 +1124,16 @@ int
- clientReplyContext::storeOKTransferDone() const
- {
-     assert(http->storeEntry()->objectLen() >= 0);
-+    const auto headers_sz = http->storeEntry()->mem().baseReply().hdr_sz;
-     assert(http->storeEntry()->objectLen() >= headers_sz);
--    if (http->out.offset >= http->storeEntry()->objectLen() - headers_sz) {
--        debugs(88,3,HERE << "storeOKTransferDone " <<
--               " out.offset=" << http->out.offset <<
--               " objectLen()=" << http->storeEntry()->objectLen() <<
--               " headers_sz=" << headers_sz);
--        return 1;
--    }
- 
--    return 0;
-+    const auto done = http->out.offset >= http->storeEntry()->objectLen() - headers_sz;
-+    const auto debugLevel = done ? 3 : 5;
-+    debugs(88, debugLevel, done <<
-+           " out.offset=" << http->out.offset <<
-+           " objectLen()=" << http->storeEntry()->objectLen() <<
-+           " headers_sz=" << headers_sz);
-+    return done ? 1 : 0;
- }
- 
- int
-@@ -1190,10 +1145,9 @@ clientReplyContext::storeNotOKTransferDone() const
-     MemObject *mem = http->storeEntry()->mem_obj;
-     assert(mem != NULL);
-     assert(http->request != NULL);
--    /* mem->reply was wrong because it uses the UPSTREAM header length!!! */
--    HttpReply const *curReply = mem->getReply();
-+    const auto expectedBodySize = mem->baseReply().content_length;
- 
--    if (headers_sz == 0)
-+    if (mem->baseReply().pstate != psParsed)
-         /* haven't found end of headers yet */
-         return 0;
- 
-@@ -1202,19 +1156,14 @@ clientReplyContext::storeNotOKTransferDone() const
-      * If we are sending a body and we don't have a content-length,
-      * then we must wait for the object to become STORE_OK.
-      */
--    if (curReply->content_length < 0)
--        return 0;
--
--    uint64_t expectedLength = curReply->content_length + http->out.headers_sz;
--
--    if (http->out.size < expectedLength)
-+    if (expectedBodySize < 0)
-         return 0;
--    else {
--        debugs(88,3,HERE << "storeNotOKTransferDone " <<
--               " out.size=" << http->out.size <<
--               " expectedLength=" << expectedLength);
--        return 1;
--    }
-+    const auto done = http->out.offset >= expectedBodySize;
-+    const auto debugLevel = done ? 3 : 5;
-+    debugs(88, debugLevel, done <<
-+           " out.offset=" << http->out.offset <<
-+           " expectedBodySize=" << expectedBodySize);
-+    return done ? 1 : 0;
- }
- 
- /* A write has completed, what is the next status based on the
-@@ -1632,6 +1581,8 @@ clientReplyContext::cloneReply()
-     reply = http->storeEntry()->getReply()->clone();
-     HTTPMSGLOCK(reply);
- 
-+    http->al->reply = reply;
-+
-     if (reply->sline.protocol == AnyP::PROTO_HTTP) {
-         /* RFC 2616 requires us to advertise our version (but only on real HTTP traffic) */
-         reply->sline.version = Http::ProtocolVersion();
-@@ -1778,20 +1729,12 @@ clientGetMoreData(clientStreamNode * aNode, ClientHttpRequest * http)
-     assert (context);
-     assert(context->http == http);
- 
--    clientStreamNode *next = ( clientStreamNode *)aNode->node.next->data;
--
-     if (!context->ourNode)
-         context->ourNode = aNode;
- 
-     /* no cbdatareference, this is only used once, and safely */
-     if (context->flags.storelogiccomplete) {
--        StoreIOBuffer tempBuffer;
--        tempBuffer.offset = next->readBuffer.offset + context->headers_sz;
--        tempBuffer.length = next->readBuffer.length;
--        tempBuffer.data = next->readBuffer.data;
--
--        storeClientCopy(context->sc, http->storeEntry(),
--                        tempBuffer, clientReplyContext::SendMoreData, context);
-+        context->requestMoreBodyFromStore();
-         return;
-     }
- 
-@@ -1804,7 +1747,7 @@ clientGetMoreData(clientStreamNode * aNode, ClientHttpRequest * http)
- 
-     if (context->http->request->method == Http::METHOD_TRACE) {
-         if (context->http->request->header.getInt64(Http::HdrType::MAX_FORWARDS) == 0) {
--            context->traceReply(aNode);
-+            context->traceReply();
-             return;
-         }
- 
-@@ -1834,7 +1777,6 @@ clientReplyContext::doGetMoreData()
- #endif
- 
-         assert(http->logType.oldType == LOG_TCP_HIT);
--        reqofs = 0;
-         /* guarantee nothing has been sent yet! */
-         assert(http->out.size == 0);
-         assert(http->out.offset == 0);
-@@ -1849,10 +1791,7 @@ clientReplyContext::doGetMoreData()
-             }
-         }
- 
--        localTempBuffer.offset = reqofs;
--        localTempBuffer.length = getNextNode()->readBuffer.length;
--        localTempBuffer.data = getNextNode()->readBuffer.data;
--        storeClientCopy(sc, http->storeEntry(), localTempBuffer, CacheHit, this);
-+        triggerInitialStoreRead(CacheHit);
-     } else {
-         /* MISS CASE, http->logType is already set! */
-         processMiss();
-@@ -1887,12 +1826,11 @@ clientReplyContext::makeThisHead()
- }
- 
- bool
--clientReplyContext::errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess)const
-+clientReplyContext::errorInStream(const StoreIOBuffer &result) const
- {
-     return /* aborted request */
-         (http->storeEntry() && EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) ||
--        /* Upstream read error */ (result.flags.error) ||
--        /* Upstream EOF */ (sizeToProcess == 0);
-+        /* Upstream read error */ (result.flags.error);
- }
- 
- void
-@@ -1913,24 +1851,17 @@ clientReplyContext::sendStreamError(StoreIOBuffer const &result)
- }
- 
- void
--clientReplyContext::pushStreamData(StoreIOBuffer const &result, char *source)
-+clientReplyContext::pushStreamData(const StoreIOBuffer &result)
- {
--    StoreIOBuffer localTempBuffer;
--
-     if (result.length == 0) {
-         debugs(88, 5, "clientReplyContext::pushStreamData: marking request as complete due to 0 length store result");
-         flags.complete = 1;
-     }
- 
--    assert(result.offset - headers_sz == next()->readBuffer.offset);
--    localTempBuffer.offset = result.offset - headers_sz;
--    localTempBuffer.length = result.length;
--
--    if (localTempBuffer.length)
--        localTempBuffer.data = source;
-+    assert(!result.length || result.offset == next()->readBuffer.offset);
- 
-     clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
--                         localTempBuffer);
-+                         result);
- }
- 
- clientStreamNode *
-@@ -2022,7 +1953,6 @@ clientReplyContext::processReplyAccess ()
-     if (http->logType.oldType == LOG_TCP_DENIED ||
-             http->logType.oldType == LOG_TCP_DENIED_REPLY ||
-             alwaysAllowResponse(reply->sline.status())) {
--        headers_sz = reply->hdr_sz;
-         processReplyAccessResult(ACCESS_ALLOWED);
-         return;
-     }
-@@ -2033,8 +1963,6 @@ clientReplyContext::processReplyAccess ()
-         return;
-     }
- 
--    headers_sz = reply->hdr_sz;
--
-     /** check for absent access controls (permit by default) */
-     if (!Config.accessList.reply) {
-         processReplyAccessResult(ACCESS_ALLOWED);
-@@ -2091,11 +2019,9 @@ clientReplyContext::processReplyAccessResult(const allow_t &accessAllowed)
-     /* Ok, the reply is allowed, */
-     http->loggingEntry(http->storeEntry());
- 
--    ssize_t body_size = reqofs - reply->hdr_sz;
--    if (body_size < 0) {
--        reqofs = reply->hdr_sz;
--        body_size = 0;
--    }
-+    Assure(matchesStreamBodyBuffer(lastStreamBufferedBytes));
-+    Assure(!lastStreamBufferedBytes.offset);
-+    auto body_size = lastStreamBufferedBytes.length; // may be zero
- 
-     debugs(88, 3, "clientReplyContext::sendMoreData: Appending " <<
-            (int) body_size << " bytes after " << reply->hdr_sz <<
-@@ -2123,19 +2049,27 @@ clientReplyContext::processReplyAccessResult(const allow_t &accessAllowed)
-     assert (!flags.headersSent);
-     flags.headersSent = true;
- 
-+    // next()->readBuffer.offset may be positive for Range requests, but our
-+    // localTempBuffer initialization code assumes that next()->readBuffer.data
-+    // points to the response body at offset 0 because the first
-+    // storeClientCopy() request always has offset 0 (i.e. our first Store
-+    // request ignores next()->readBuffer.offset).
-+    //
-+    // XXX: We cannot fully check that assumption: readBuffer.offset field is
-+    // often out of sync with the buffer content, and if some buggy code updates
-+    // the buffer while we were waiting for the processReplyAccessResult()
-+    // callback, we may not notice.
-+
-     StoreIOBuffer localTempBuffer;
--    char *buf = next()->readBuffer.data;
--    char *body_buf = buf + reply->hdr_sz;
-+    const auto body_buf = next()->readBuffer.data;
- 
-     //Server side may disable ranges under some circumstances.
- 
-     if ((!http->request->range))
-         next()->readBuffer.offset = 0;
- 
--    body_buf -= next()->readBuffer.offset;
--
--    if (next()->readBuffer.offset != 0) {
--        if (next()->readBuffer.offset > body_size) {
-+    if (next()->readBuffer.offset > 0) {
-+        if (Less(body_size, next()->readBuffer.offset)) {
-             /* Can't use any of the body we received. send nothing */
-             localTempBuffer.length = 0;
-             localTempBuffer.data = NULL;
-@@ -2148,7 +2082,6 @@ clientReplyContext::processReplyAccessResult(const allow_t &accessAllowed)
-         localTempBuffer.data = body_buf;
-     }
- 
--    /* TODO??: move the data in the buffer back by the request header size */
-     clientStreamCallback((clientStreamNode *)http->client_stream.head->data,
-                          http, reply, localTempBuffer);
- 
-@@ -2161,6 +2094,8 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
-     if (deleting)
-         return;
- 
-+    debugs(88, 5, http->uri << " got " << result);
-+
-     StoreEntry *entry = http->storeEntry();
- 
-     if (ConnStateData * conn = http->getConn()) {
-@@ -2173,7 +2108,9 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
-             return;
-         }
- 
--        if (reqofs==0 && !http->logType.isTcpHit()) {
-+        if (!flags.headersSent && !http->logType.isTcpHit()) {
-+            // We get here twice if processReplyAccessResult() calls startError().
-+            // TODO: Revise when we check/change QoS markings to reduce syscalls.
-             if (Ip::Qos::TheConfig.isHitTosActive()) {
-                 Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
-             }
-@@ -2187,21 +2124,9 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
-                " out.offset=" << http->out.offset);
-     }
- 
--    char *buf = next()->readBuffer.data;
--
--    if (buf != result.data) {
--        /* we've got to copy some data */
--        assert(result.length <= next()->readBuffer.length);
--        memcpy(buf, result.data, result.length);
--    }
--
-     /* We've got the final data to start pushing... */
-     flags.storelogiccomplete = 1;
- 
--    reqofs += result.length;
--
--    assert(reqofs <= HTTP_REQBUF_SZ || flags.headersSent);
--
-     assert(http->request != NULL);
- 
-     /* ESI TODO: remove this assert once everything is stable */
-@@ -2210,20 +2135,25 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
- 
-     makeThisHead();
- 
--    debugs(88, 5, "clientReplyContext::sendMoreData: " << http->uri << ", " <<
--           reqofs << " bytes (" << result.length <<
--           " new bytes)");
--
--    /* update size of the request */
--    reqsize = reqofs;
--
--    if (errorInStream(result, reqofs)) {
-+    if (errorInStream(result)) {
-         sendStreamError(result);
-         return;
-     }
- 
-+    if (!matchesStreamBodyBuffer(result)) {
-+        // Subsequent processing expects response body bytes to be at the start
-+        // of our Client Stream buffer. When given something else (e.g., bytes
-+        // in our tempbuf), we copy and adjust to meet those expectations.
-+        const auto &ourClientStreamsBuffer = next()->readBuffer;
-+        assert(result.length <= ourClientStreamsBuffer.length);
-+        memcpy(ourClientStreamsBuffer.data, result.data, result.length);
-+        result.data = ourClientStreamsBuffer.data;
-+    }
-+
-+    noteStreamBufferredBytes(result);
-+
-     if (flags.headersSent) {
--        pushStreamData (result, buf);
-+        pushStreamData(result);
-         return;
-     }
- 
-@@ -2234,23 +2164,38 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
-         sc->setDelayId(DelayId::DelayClient(http,reply));
- #endif
- 
--    /* handle headers */
-+    holdingBuffer = result;
-+    processReplyAccess();
-+    return;
-+}
-+
-+/// Whether the given body area describes the start of our Client Stream buffer.
-+/// An empty area does.
-+bool
-+clientReplyContext::matchesStreamBodyBuffer(const StoreIOBuffer &their) const
-+{
-+    // the answer is undefined for errors; they are not really "body buffers"
-+    Assure(!their.flags.error);
- 
--    if (Config.onoff.log_mime_hdrs) {
--        size_t k;
-+    if (!their.length)
-+        return true; // an empty body area always matches our body area
- 
--        if ((k = headersEnd(buf, reqofs))) {
--            safe_free(http->al->headers.reply);
--            http->al->headers.reply = (char *)xcalloc(k + 1, 1);
--            xstrncpy(http->al->headers.reply, buf, k);
--        }
-+    if (their.data != next()->readBuffer.data) {
-+        debugs(88, 7, "no: " << their << " vs. " << next()->readBuffer);
-+        return false;
-     }
- 
--    holdingBuffer = result;
--    processReplyAccess();
--    return;
-+    return true;
-+}
-+
-+void
-+clientReplyContext::noteStreamBufferredBytes(const StoreIOBuffer &result)
-+{
-+    Assure(matchesStreamBodyBuffer(result));
-+    lastStreamBufferedBytes = result; // may be unchanged and/or zero-length
- }
- 
-+
- /* Using this breaks the client layering just a little!
-  */
- void
-@@ -2289,13 +2234,6 @@ clientReplyContext::createStoreEntry(const HttpRequestMethod& m, RequestFlags re
-     sc->setDelayId(DelayId::DelayClient(http));
- #endif
- 
--    reqofs = 0;
--
--    reqsize = 0;
--
--    /* I don't think this is actually needed! -- adrian */
--    /* http->reqbuf = http->norm_reqbuf; */
--    //    assert(http->reqbuf == http->norm_reqbuf);
-     /* The next line is illegal because we don't know if the client stream
-      * buffers have been set up
-      */
-diff --git a/src/client_side_reply.h b/src/client_side_reply.h
-index dddab1a..bf705a4 100644
---- a/src/client_side_reply.h
-+++ b/src/client_side_reply.h
-@@ -39,7 +39,6 @@ public:
-     void purgeFoundGet(StoreEntry *newEntry);
-     void purgeFoundHead(StoreEntry *newEntry);
-     void purgeFoundObject(StoreEntry *entry);
--    void sendClientUpstreamResponse();
-     void purgeDoPurgeGet(StoreEntry *entry);
-     void purgeDoPurgeHead(StoreEntry *entry);
-     void doGetMoreData();
-@@ -67,7 +66,7 @@ public:
-     void processExpired();
-     clientStream_status_t replyStatus();
-     void processMiss();
--    void traceReply(clientStreamNode * node);
-+    void traceReply();
-     const char *storeId() const { return (http->store_id.size() > 0 ? http->store_id.termedBuf() : http->uri); }
- 
-     Http::StatusCode purgeStatus;
-@@ -77,13 +76,14 @@ public:
-     virtual void created (StoreEntry *newEntry);
- 
-     ClientHttpRequest *http;
--    int headers_sz;
-     store_client *sc;       /* The store_client we're using */
-     StoreIOBuffer tempBuffer;   /* For use in validating requests via IMS */
-     int old_reqsize;        /* ... again, for the buffer */
--    size_t reqsize;
--    size_t reqofs;
--    char tempbuf[HTTP_REQBUF_SZ];   ///< a temporary buffer if we need working storage
-+    /// Buffer dedicated to receiving storeClientCopy() responses to generated
-+    /// revalidation requests. These requests cannot use next()->readBuffer
-+    /// because the latter keeps the contents of the stale HTTP response during
-+    /// revalidation. sendClientOldEntry() uses that contents.
-+    char tempbuf[HTTP_REQBUF_SZ];
- #if USE_CACHE_DIGESTS
- 
-     const char *lookup_type;    /* temporary hack: storeGet() result: HIT/MISS/NONE */
-@@ -101,9 +101,10 @@ public:
- private:
-     clientStreamNode *getNextNode() const;
-     void makeThisHead();
--    bool errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess)const ;
-+    bool errorInStream(const StoreIOBuffer &result) const;
-+    bool matchesStreamBodyBuffer(const StoreIOBuffer &) const;
-     void sendStreamError(StoreIOBuffer const &result);
--    void pushStreamData(StoreIOBuffer const &result, char *source);
-+    void pushStreamData(const StoreIOBuffer &);
-     clientStreamNode * next() const;
-     StoreIOBuffer holdingBuffer;
-     HttpReply *reply;
-@@ -115,11 +116,13 @@ private:
-     bool alwaysAllowResponse(Http::StatusCode sline) const;
-     int checkTransferDone();
-     void processOnlyIfCachedMiss();
--    bool processConditional(StoreIOBuffer &result);
-+    bool processConditional();
-+    void noteStreamBufferredBytes(const StoreIOBuffer &);
-     void cacheHit(StoreIOBuffer result);
-     void handleIMSReply(StoreIOBuffer result);
-     void sendMoreData(StoreIOBuffer result);
--    void triggerInitialStoreRead();
-+    void triggerInitialStoreRead(STCB = SendMoreData);
-+    void requestMoreBodyFromStore();
-     void sendClientOldEntry();
-     void purgeAllCached();
-     void forgetHit();
-@@ -129,6 +132,13 @@ private:
-     void sendPreconditionFailedError();
-     void sendNotModified();
-     void sendNotModifiedOrPreconditionFailedError();
-+    void sendClientUpstreamResponse(const StoreIOBuffer &upstreamResponse);
-+
-+    /// Reduces a chance of an accidental direct storeClientCopy() call that
-+    /// (should but) forgets to invalidate our lastStreamBufferedBytes. This
-+    /// function is not defined; decltype() syntax prohibits "= delete", but
-+    /// function usage will trigger deprecation warnings and linking errors.
-+    static decltype(::storeClientCopy) storeClientCopy [[deprecated]];
- 
-     StoreEntry *old_entry;
-     /* ... for entry to be validated */
-@@ -145,6 +155,12 @@ private:
-     } CollapsedRevalidation;
- 
-     CollapsedRevalidation collapsedRevalidation;
-+
-+    /// HTTP response body bytes stored in our Client Stream buffer (if any)
-+    StoreIOBuffer lastStreamBufferedBytes;
-+
-+    // TODO: Remove after moving the meat of this function into a method.
-+    friend CSR clientGetMoreData;
- };
- 
- #endif /* SQUID_CLIENTSIDEREPLY_H */
-diff --git a/src/client_side_request.cc b/src/client_side_request.cc
-index ab08fd2..92da530 100644
---- a/src/client_side_request.cc
-+++ b/src/client_side_request.cc
-@@ -2045,6 +2045,8 @@ ClientHttpRequest::handleAdaptedHeader(HttpMsg *msg)
-         storeEntry()->replaceHttpReply(new_rep);
-         storeEntry()->timestampsSet();
- 
-+        al->reply = new_rep;
-+
-         if (!adaptedBodySource) // no body
-             storeEntry()->complete();
-         clientGetMoreData(node, this);
-diff --git a/src/clients/Client.cc b/src/clients/Client.cc
-index f5defbb..cada70e 100644
---- a/src/clients/Client.cc
-+++ b/src/clients/Client.cc
-@@ -136,6 +136,8 @@ Client::setVirginReply(HttpReply *rep)
-     assert(rep);
-     theVirginReply = rep;
-     HTTPMSGLOCK(theVirginReply);
-+    if (fwd->al)
-+        fwd->al->reply = theVirginReply;
-     return theVirginReply;
- }
- 
-@@ -155,6 +157,8 @@ Client::setFinalReply(HttpReply *rep)
-     assert(rep);
-     theFinalReply = rep;
-     HTTPMSGLOCK(theFinalReply);
-+    if (fwd->al)
-+        fwd->al->reply = theFinalReply;
- 
-     // give entry the reply because haveParsedReplyHeaders() expects it there
-     entry->replaceHttpReply(theFinalReply, false); // but do not write yet
-@@ -550,6 +554,7 @@ Client::blockCaching()
-         ACLFilledChecklist ch(acl, originalRequest(), NULL);
-         ch.reply = const_cast(entry->getReply()); // ACLFilledChecklist API bug
-         HTTPMSGLOCK(ch.reply);
-+        ch.al = fwd->al;
-         if (!ch.fastCheck().allowed()) { // when in doubt, block
-             debugs(20, 3, "store_miss prohibits caching");
-             return true;
-diff --git a/src/enums.h b/src/enums.h
-index 4a860d8..262d62c 100644
---- a/src/enums.h
-+++ b/src/enums.h
-@@ -203,7 +203,6 @@ enum {
- typedef enum {
-     DIGEST_READ_NONE,
-     DIGEST_READ_REPLY,
--    DIGEST_READ_HEADERS,
-     DIGEST_READ_CBLOCK,
-     DIGEST_READ_MASK,
-     DIGEST_READ_DONE
-diff --git a/src/format/Format.cc b/src/format/Format.cc
-index 3b6a44b..689bdf9 100644
---- a/src/format/Format.cc
-+++ b/src/format/Format.cc
-@@ -330,7 +330,7 @@ log_quoted_string(const char *str, char *out)
- static const HttpMsg *
- actualReplyHeader(const AccessLogEntry::Pointer &al)
- {
--    const HttpMsg *msg = al->reply;
-+    const HttpMsg *msg = al->reply.getRaw();
- #if ICAP_CLIENT
-     // al->icap.reqMethod is methodNone in access.log context
-     if (!msg && al->icap.reqMethod == Adaptation::methodReqmod)
-@@ -853,24 +853,35 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
-             } else
- #endif
-             {
-+                // just headers without start-line and CRLF
-+                // XXX: reconcile with 'headers.request;
-                 quote = 1;
-             }
-             break;
- 
-         case LFT_ADAPTED_REQUEST_ALL_HEADERS:
-+            // just headers without start-line and CRLF
-+            // XXX: reconcile with 'headers.adapted_request;
-             quote = 1;
-             break;
- 
--        case LFT_REPLY_ALL_HEADERS:
--            out = al->headers.reply;
-+        case LFT_REPLY_ALL_HEADERS: {
-+            MemBuf allHeaders;
-+            allHeaders.init();
-+            // status-line + headers + CRLF
-+            // XXX: reconcile with '>h' and '>ha'
-+            al->packReplyHeaders(allHeaders);
-+            sb.assign(allHeaders.content(), allHeaders.contentSize());
-+            out = sb.c_str();
- #if ICAP_CLIENT
-             if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
-                 out = al->headers.adapted_request;
- #endif
-             quote = 1;
--            break;
-+        }
-+        break;
- 
-         case LFT_USER_NAME:
- #if USE_AUTH
-diff --git a/src/http.cc b/src/http.cc
-index 017e492..877172d 100644
---- a/src/http.cc
-+++ b/src/http.cc
-@@ -775,6 +775,9 @@ HttpStateData::processReplyHeader()
- void
- HttpStateData::handle1xx(HttpReply *reply)
- {
-+    if (fwd->al)
-+        fwd->al->reply = reply;
-+
-     HttpReply::Pointer msg(reply); // will destroy reply if unused
- 
-     // one 1xx at a time: we must not be called while waiting for previous 1xx
-diff --git a/src/icmp/net_db.cc b/src/icmp/net_db.cc
-index 7dc42a2..52595f6 100644
---- a/src/icmp/net_db.cc
-+++ b/src/icmp/net_db.cc
-@@ -33,6 +33,7 @@
- #include "mgr/Registration.h"
- #include "mime_header.h"
- #include "neighbors.h"
-+#include "sbuf/SBuf.h"
- #include "SquidConfig.h"
- #include "SquidTime.h"
- #include "Store.h"
-@@ -49,8 +50,6 @@
- #include "ipcache.h"
- #include "StoreClient.h"
- 
--#define NETDB_REQBUF_SZ 4096
--
- typedef enum {
-     STATE_NONE,
-     STATE_HEADER,
-@@ -67,12 +66,8 @@ public:
-         e(NULL),
-         sc(NULL),
-         r(theReq),
--        used(0),
--        buf_sz(NETDB_REQBUF_SZ),
--        buf_ofs(0),
-         connstate(STATE_HEADER)
-     {
--        *buf = 0;
- 
-         assert(NULL != r);
-         HTTPMSGLOCK(r);
-@@ -92,10 +87,10 @@ public:
-     StoreEntry *e;
-     store_client *sc;
-     HttpRequest *r;
--    int64_t used;
--    size_t buf_sz;
--    char buf[NETDB_REQBUF_SZ];
--    int buf_ofs;
-+
-+    /// for receiving a NetDB reply body from Store and interpreting it
-+    Store::ParsingBuffer parsingBuffer;
-+
-     netdb_conn_state_t connstate;
- };
- 
-@@ -698,24 +693,19 @@ netdbExchangeHandleReply(void *data, StoreIOBuffer receivedData)
-     Ip::Address addr;
- 
-     netdbExchangeState *ex = (netdbExchangeState *)data;
--    int rec_sz = 0;
--    int o;
- 
-     struct in_addr line_addr;
-     double rtt;
-     double hops;
--    char *p;
-     int j;
-     HttpReply const *rep;
--    size_t hdr_sz;
-     int nused = 0;
--    int size;
--    int oldbufofs = ex->buf_ofs;
- 
--    rec_sz = 0;
-+    size_t rec_sz = 0; // received record size (TODO: make const)
-     rec_sz += 1 + sizeof(struct in_addr);
-     rec_sz += 1 + sizeof(int);
-     rec_sz += 1 + sizeof(int);
-+    Assure(rec_sz <= ex->parsingBuffer.capacity());
-     debugs(38, 3, "netdbExchangeHandleReply: " << receivedData.length << " read bytes");
- 
-     if (!cbdataReferenceValid(ex->p)) {
-@@ -726,64 +716,29 @@ netdbExchangeHandleReply(void *data, StoreIOBuffer receivedData)
- 
-     debugs(38, 3, "netdbExchangeHandleReply: for '" << ex->p->host << ":" << ex->p->http_port << "'");
- 
--    if (receivedData.length == 0 && !receivedData.flags.error) {
-+    if (receivedData.flags.error) {
-         debugs(38, 3, "netdbExchangeHandleReply: Done");
-         delete ex;
-         return;
-     }
- 
--    p = ex->buf;
--
--    /* Get the size of the buffer now */
--    size = ex->buf_ofs + receivedData.length;
--    debugs(38, 3, "netdbExchangeHandleReply: " << size << " bytes buf");
--
--    /* Check if we're still doing headers */
--
-     if (ex->connstate == STATE_HEADER) {
--
--        ex->buf_ofs += receivedData.length;
--
--        /* skip reply headers */
--
--        if ((hdr_sz = headersEnd(p, ex->buf_ofs))) {
--            debugs(38, 5, "netdbExchangeHandleReply: hdr_sz = " << hdr_sz);
--            rep = ex->e->getReply();
--            assert(rep->sline.status() != Http::scNone);
--            debugs(38, 3, "netdbExchangeHandleReply: reply status " << rep->sline.status());
--
--            if (rep->sline.status() != Http::scOkay) {
--                delete ex;
--                return;
--            }
--
--            assert((size_t)ex->buf_ofs >= hdr_sz);
--
--            /*
--             * Now, point p to the part of the buffer where the data
--             * starts, and update the size accordingly
--             */
--            assert(ex->used == 0);
--            ex->used = hdr_sz;
--            size = ex->buf_ofs - hdr_sz;
--            p += hdr_sz;
--
--            /* Finally, set the conn state mode to STATE_BODY */
--            ex->connstate = STATE_BODY;
--        } else {
--            StoreIOBuffer tempBuffer;
--            tempBuffer.offset = ex->buf_ofs;
--            tempBuffer.length = ex->buf_sz - ex->buf_ofs;
--            tempBuffer.data = ex->buf + ex->buf_ofs;
--            /* Have more headers .. */
--            storeClientCopy(ex->sc, ex->e, tempBuffer,
--                            netdbExchangeHandleReply, ex);
-+        const auto scode = ex->e->mem().baseReply().sline.status();
-+        assert(scode != Http::scNone);
-+        debugs(38, 3, "reply status " << scode);
-+        if (scode != Http::scOkay) {
-+            delete ex;
-             return;
--        }
-+         }
-+        ex->connstate = STATE_BODY;
-     }
- 
-     assert(ex->connstate == STATE_BODY);
- 
-+    ex->parsingBuffer.appended(receivedData.data, receivedData.length);
-+    auto p = ex->parsingBuffer.c_str(); // current parsing position
-+    auto size = ex->parsingBuffer.contentSize(); // bytes we still need to parse
-+
-     /* If we get here, we have some body to parse .. */
-     debugs(38, 5, "netdbExchangeHandleReply: start parsing loop, size = " << size);
- 
-@@ -792,6 +747,7 @@ netdbExchangeHandleReply(void *data, StoreIOBuffer receivedData)
-         addr.setAnyAddr();
-         hops = rtt = 0.0;
- 
-+        size_t o; // current record parsing offset
-         for (o = 0; o < rec_sz;) {
-             switch ((int) *(p + o)) {
- 
-@@ -829,8 +785,6 @@ netdbExchangeHandleReply(void *data, StoreIOBuffer receivedData)
- 
-         assert(o == rec_sz);
- 
--        ex->used += rec_sz;
--
-         size -= rec_sz;
- 
-         p += rec_sz;
-@@ -838,32 +792,8 @@ netdbExchangeHandleReply(void *data, StoreIOBuffer receivedData)
-         ++nused;
-     }
- 
--    /*
--     * Copy anything that is left over to the beginning of the buffer,
--     * and adjust buf_ofs accordingly
--     */
--
--    /*
--     * Evilly, size refers to the buf size left now,
--     * ex->buf_ofs is the original buffer size, so just copy that
--     * much data over
--     */
--    memmove(ex->buf, ex->buf + (ex->buf_ofs - size), size);
--
--    ex->buf_ofs = size;
--
--    /*
--     * And don't re-copy the remaining data ..
--     */
--    ex->used += size;
--
--    /*
--     * Now the tricky bit - size _included_ the leftover bit from the _last_
--     * storeClientCopy. We don't want to include that, or our offset will be wrong.
--     * So, don't count the size of the leftover buffer we began with.
--     * This can _disappear_ when we're not tracking offsets ..
--     */
--    ex->used -= oldbufofs;
-+    const auto parsedSize = ex->parsingBuffer.contentSize() - size;
-+    ex->parsingBuffer.consume(parsedSize);
- 
-     debugs(38, 3, "netdbExchangeHandleReply: size left over in this buffer: " << size << " bytes");
- 
-@@ -871,20 +801,26 @@ netdbExchangeHandleReply(void *data, StoreIOBuffer receivedData)
-            " entries, (x " << rec_sz << " bytes) == " << nused * rec_sz <<
-            " bytes total");
- 
--    debugs(38, 3, "netdbExchangeHandleReply: used " << ex->used);
--
-     if (EBIT_TEST(ex->e->flags, ENTRY_ABORTED)) {
-         debugs(38, 3, "netdbExchangeHandleReply: ENTRY_ABORTED");
-         delete ex;
--    } else if (ex->e->store_status == STORE_PENDING) {
--        StoreIOBuffer tempBuffer;
--        tempBuffer.offset = ex->used;
--        tempBuffer.length = ex->buf_sz - ex->buf_ofs;
--        tempBuffer.data = ex->buf + ex->buf_ofs;
--        debugs(38, 3, "netdbExchangeHandleReply: EOF not received");
--        storeClientCopy(ex->sc, ex->e, tempBuffer,
--                        netdbExchangeHandleReply, ex);
-+        return;
-     }
-+
-+    if (ex->sc->atEof()) {
-+        if (const auto leftoverBytes = ex->parsingBuffer.contentSize())
-+            debugs(38, 2, "discarding a partially received record due to Store EOF: " << leftoverBytes);
-+        delete ex;
-+        return;
-+    }
-+
-+    // TODO: To protect us from a broken peer sending an "infinite" stream of
-+    // new addresses, limit the cumulative number of received bytes or records?
-+
-+    const auto remainingSpace = ex->parsingBuffer.space().positionAt(receivedData.offset + receivedData.length);
-+    // rec_sz is at most buffer capacity, and we consume all fully loaded records
-+    Assure(remainingSpace.length);
-+    storeClientCopy(ex->sc, ex->e, remainingSpace, netdbExchangeHandleReply, ex);
- }
- 
- #endif /* USE_ICMP */
-@@ -1296,14 +1232,9 @@ netdbExchangeStart(void *data)
-     ex->e = storeCreateEntry(uri, uri, RequestFlags(), Http::METHOD_GET);
-     assert(NULL != ex->e);
- 
--    StoreIOBuffer tempBuffer;
--    tempBuffer.length = ex->buf_sz;
--    tempBuffer.data = ex->buf;
--
-     ex->sc = storeClientListAdd(ex->e, ex);
-+    storeClientCopy(ex->sc, ex->e, ex->parsingBuffer.makeInitialSpace(), netdbExchangeHandleReply, ex);
- 
--    storeClientCopy(ex->sc, ex->e, tempBuffer,
--                    netdbExchangeHandleReply, ex);
-     ex->r->flags.loopDetected = true;   /* cheat! -- force direct */
- 
-     // XXX: send as Proxy-Authenticate instead
-diff --git a/src/internal.cc b/src/internal.cc
-index 81d5175..3a04ce0 100644
---- a/src/internal.cc
-+++ b/src/internal.cc
-@@ -9,6 +9,7 @@
- /* DEBUG: section 76    Internal Squid Object handling */
- 
- #include "squid.h"
-+#include "base/Assure.h"
- #include "CacheManager.h"
- #include "comm/Connection.h"
- #include "errorpage.h"
-diff --git a/src/log/FormatHttpdCombined.cc b/src/log/FormatHttpdCombined.cc
-index 6639e88..70ea336 100644
---- a/src/log/FormatHttpdCombined.cc
-+++ b/src/log/FormatHttpdCombined.cc
-@@ -69,7 +69,10 @@ Log::Format::HttpdCombined(const AccessLogEntry::Pointer &al, Logfile * logfile)
- 
-     if (Config.onoff.log_mime_hdrs) {
-         char *ereq = ::Format::QuoteMimeBlob(al->headers.request);
--        char *erep = ::Format::QuoteMimeBlob(al->headers.reply);
-+        MemBuf mb;
-+        mb.init();
-+        al->packReplyHeaders(mb);
-+        auto erep = ::Format::QuoteMimeBlob(mb.content());
-         logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
-         safe_free(ereq);
-         safe_free(erep);
-diff --git a/src/log/FormatHttpdCommon.cc b/src/log/FormatHttpdCommon.cc
-index 1613d0e..9e933a0 100644
---- a/src/log/FormatHttpdCommon.cc
-+++ b/src/log/FormatHttpdCommon.cc
-@@ -54,7 +54,10 @@ Log::Format::HttpdCommon(const AccessLogEntry::Pointer &al, Logfile * logfile)
- 
-     if (Config.onoff.log_mime_hdrs) {
-         char *ereq = ::Format::QuoteMimeBlob(al->headers.request);
--        char *erep = ::Format::QuoteMimeBlob(al->headers.reply);
-+        MemBuf mb;
-+        mb.init();
-+        al->packReplyHeaders(mb);
-+        auto erep = ::Format::QuoteMimeBlob(mb.content());
-         logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
-         safe_free(ereq);
-         safe_free(erep);
-diff --git a/src/log/FormatSquidNative.cc b/src/log/FormatSquidNative.cc
-index 0ab97e4..23076b2 100644
---- a/src/log/FormatSquidNative.cc
-+++ b/src/log/FormatSquidNative.cc
-@@ -71,7 +71,10 @@ Log::Format::SquidNative(const AccessLogEntry::Pointer &al, Logfile * logfile)
- 
-     if (Config.onoff.log_mime_hdrs) {
-         char *ereq = ::Format::QuoteMimeBlob(al->headers.request);
--        char *erep = ::Format::QuoteMimeBlob(al->headers.reply);
-+        MemBuf mb;
-+        mb.init();
-+        al->packReplyHeaders(mb);
-+        auto erep = ::Format::QuoteMimeBlob(mb.content());
-         logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
-         safe_free(ereq);
-         safe_free(erep);
-diff --git a/src/peer_digest.cc b/src/peer_digest.cc
-index 7b6314d..8a66277 100644
---- a/src/peer_digest.cc
-+++ b/src/peer_digest.cc
-@@ -39,7 +39,6 @@ static EVH peerDigestCheck;
- static void peerDigestRequest(PeerDigest * pd);
- static STCB peerDigestHandleReply;
- static int peerDigestFetchReply(void *, char *, ssize_t);
--int peerDigestSwapInHeaders(void *, char *, ssize_t);
- int peerDigestSwapInCBlock(void *, char *, ssize_t);
- int peerDigestSwapInMask(void *, char *, ssize_t);
- static int peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name);
-@@ -374,6 +373,9 @@ peerDigestRequest(PeerDigest * pd)
-     fetch->sc = storeClientListAdd(e, fetch);
-     /* set lastmod to trigger IMS request if possible */
- 
-+    // TODO: Also check for fetch->pd->cd presence as a precondition for sending
-+    // IMS requests because peerDigestFetchReply() does not accept 304 responses
-+    // without an in-memory cache digest.
-     if (old_e)
-         e->lastModified(old_e->lastModified());
- 
-@@ -408,11 +410,16 @@ peerDigestHandleReply(void *data, StoreIOBuffer receivedData)
-     digest_read_state_t prevstate;
-     int newsize;
- 
--    assert(fetch->pd && receivedData.data);
-+    if (receivedData.flags.error) {
-+        peerDigestFetchAbort(fetch, fetch->buf, "failure loading digest reply from Store");
-+        return;
-+    }
-+
-+    assert(fetch->pd);
-     /* The existing code assumes that the received pointer is
-      * where we asked the data to be put
-      */
--    assert(fetch->buf + fetch->bufofs == receivedData.data);
-+    assert(!receivedData.data || fetch->buf + fetch->bufofs == receivedData.data);
- 
-     /* Update the buffer size */
-     fetch->bufofs += receivedData.length;
-@@ -444,10 +451,6 @@ peerDigestHandleReply(void *data, StoreIOBuffer receivedData)
-             retsize = peerDigestFetchReply(fetch, fetch->buf, fetch->bufofs);
-             break;
- 
--        case DIGEST_READ_HEADERS:
--            retsize = peerDigestSwapInHeaders(fetch, fetch->buf, fetch->bufofs);
--            break;
--
-         case DIGEST_READ_CBLOCK:
-             retsize = peerDigestSwapInCBlock(fetch, fetch->buf, fetch->bufofs);
-             break;
-@@ -487,7 +490,7 @@ peerDigestHandleReply(void *data, StoreIOBuffer receivedData)
-     // checking at the beginning of this function. However, in this case, we would have to require
-     // that the parser does not regard EOF as a special condition (it is true now but may change
-     // in the future).
--    if (!receivedData.length) { // EOF
-+    if (fetch->sc->atEof()) {
-         peerDigestFetchAbort(fetch, fetch->buf, "premature end of digest reply");
-         return;
-     }
-@@ -506,19 +509,12 @@ peerDigestHandleReply(void *data, StoreIOBuffer receivedData)
-     }
- }
- 
--/* wait for full http headers to be received then parse them */
--/*
-- * This routine handles parsing the reply line.
-- * If the reply line indicates an OK, the same data is thrown
-- * to SwapInHeaders(). If the reply line is a NOT_MODIFIED,
-- * we simply stop parsing.
-- */
-+/// handle HTTP response headers in the initial storeClientCopy() response
- static int
- peerDigestFetchReply(void *data, char *buf, ssize_t size)
- {
-     DigestFetchState *fetch = (DigestFetchState *)data;
-     PeerDigest *pd = fetch->pd;
--    size_t hdr_size;
-     assert(pd && buf);
-     assert(!fetch->offset);
- 
-@@ -527,7 +523,7 @@ peerDigestFetchReply(void *data, char *buf, ssize_t size)
-     if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply"))
-         return -1;
- 
--    if ((hdr_size = headersEnd(buf, size))) {
-+    {
-         HttpReply const *reply = fetch->entry->getReply();
-         assert(reply);
-         assert(reply->sline.status() != Http::scNone);
-@@ -563,6 +559,15 @@ peerDigestFetchReply(void *data, char *buf, ssize_t size)
-             /* preserve request -- we need its size to update counters */
-             /* requestUnlink(r); */
-             /* fetch->entry->mem_obj->request = NULL; */
-+
-+            if (!fetch->pd->cd) {
-+                peerDigestFetchAbort(fetch, buf, "304 without the old in-memory digest");
-+                return -1;
-+            }
-+
-+            // stay with the old in-memory digest
-+            peerDigestFetchStop(fetch, buf, "Not modified");
-+            fetch->state = DIGEST_READ_DONE;
-         } else if (status == Http::scOkay) {
-             /* get rid of old entry if any */
- 
-@@ -573,67 +578,12 @@ peerDigestFetchReply(void *data, char *buf, ssize_t size)
-                 fetch->old_entry->unlock("peerDigestFetchReply 200");
-                 fetch->old_entry = NULL;
-             }
-+            fetch->state = DIGEST_READ_CBLOCK;
-         } else {
-             /* some kind of a bug */
-             peerDigestFetchAbort(fetch, buf, reply->sline.reason());
-             return -1;      /* XXX -1 will abort stuff in ReadReply! */
-         }
--
--        /* must have a ready-to-use store entry if we got here */
--        /* can we stay with the old in-memory digest? */
--        if (status == Http::scNotModified && fetch->pd->cd) {
--            peerDigestFetchStop(fetch, buf, "Not modified");
--            fetch->state = DIGEST_READ_DONE;
--        } else {
--            fetch->state = DIGEST_READ_HEADERS;
--        }
--    } else {
--        /* need more data, do we have space? */
--
--        if (size >= SM_PAGE_SIZE)
--            peerDigestFetchAbort(fetch, buf, "reply header too big");
--    }
--
--    /* We don't want to actually ack that we've handled anything,
--     * otherwise SwapInHeaders() won't get the reply line .. */
--    return 0;
--}
--
--/* fetch headers from disk, pass on to SwapInCBlock */
--int
--peerDigestSwapInHeaders(void *data, char *buf, ssize_t size)
--{
--    DigestFetchState *fetch = (DigestFetchState *)data;
--    size_t hdr_size;
--
--    assert(fetch->state == DIGEST_READ_HEADERS);
--
--    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders"))
--        return -1;
--
--    assert(!fetch->offset);
--
--    if ((hdr_size = headersEnd(buf, size))) {
--        assert(fetch->entry->getReply());
--        assert(fetch->entry->getReply()->sline.status() != Http::scNone);
--
--        if (fetch->entry->getReply()->sline.status() != Http::scOkay) {
--            debugs(72, DBG_IMPORTANT, "peerDigestSwapInHeaders: " << fetch->pd->host <<
--                   " status " << fetch->entry->getReply()->sline.status() <<
--                   " got cached!");
--
--            peerDigestFetchAbort(fetch, buf, "internal status error");
--            return -1;
--        }
--
--        fetch->state = DIGEST_READ_CBLOCK;
--        return hdr_size;    /* Say how much data we read */
--    }
--
--    /* need more data, do we have space? */
--    if (size >= SM_PAGE_SIZE) {
--        peerDigestFetchAbort(fetch, buf, "stored header too big");
--        return -1;
-     }
- 
-     return 0;       /* We need to read more to parse .. */
-@@ -755,7 +705,7 @@ peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const
-     }
- 
-     /* continue checking (maybe-successful eof case) */
--    if (!reason && !size) {
-+    if (!reason && !size && fetch->state != DIGEST_READ_REPLY) {
-         if (!pd->cd)
-             reason = "null digest?!";
-         else if (fetch->mask_offset != pd->cd->mask_size)
-diff --git a/src/servers/FtpServer.cc b/src/servers/FtpServer.cc
-index fab26cf..d3faa8d 100644
---- a/src/servers/FtpServer.cc
-+++ b/src/servers/FtpServer.cc
-@@ -777,12 +777,6 @@ Ftp::Server::handleReply(HttpReply *reply, StoreIOBuffer data)
-     Http::StreamPointer context = pipeline.front();
-     assert(context != nullptr);
- 
--    if (context->http && context->http->al != NULL &&
--            !context->http->al->reply && reply) {
--        context->http->al->reply = reply;
--        HTTPMSGLOCK(context->http->al->reply);
--    }
--
-     static ReplyHandler handlers[] = {
-         NULL, // fssBegin
-         NULL, // fssConnected
-diff --git a/src/servers/Http1Server.cc b/src/servers/Http1Server.cc
-index 7514779..e76fb3e 100644
---- a/src/servers/Http1Server.cc
-+++ b/src/servers/Http1Server.cc
-@@ -310,9 +310,6 @@ Http::One::Server::handleReply(HttpReply *rep, StoreIOBuffer receivedData)
-     }
- 
-     assert(rep);
--    HTTPMSGUNLOCK(http->al->reply);
--    http->al->reply = rep;
--    HTTPMSGLOCK(http->al->reply);
-     context->sendStartOfMessage(rep, receivedData);
- }
- 
-diff --git a/src/stmem.cc b/src/stmem.cc
-index d117c15..b627005 100644
---- a/src/stmem.cc
-+++ b/src/stmem.cc
-@@ -95,8 +95,6 @@ mem_hdr::freeDataUpto(int64_t target_offset)
-             break;
-     }
- 
--    assert (lowestOffset () <= target_offset);
--
-     return lowestOffset ();
- }
- 
-diff --git a/src/store.cc b/src/store.cc
-index 1948447..b4c7f82 100644
---- a/src/store.cc
-+++ b/src/store.cc
-@@ -273,6 +273,8 @@ StoreEntry::storeClientType() const
- 
-     assert(mem_obj);
- 
-+    debugs(20, 7, *this << " inmem_lo=" << mem_obj->inmem_lo);
-+
-     if (mem_obj->inmem_lo)
-         return STORE_DISK_CLIENT;
- 
-@@ -300,6 +302,7 @@ StoreEntry::storeClientType() const
-                 return STORE_MEM_CLIENT;
-             }
-         }
-+        debugs(20, 7, "STORE_OK STORE_DISK_CLIENT");
-         return STORE_DISK_CLIENT;
-     }
- 
-@@ -319,10 +322,18 @@ StoreEntry::storeClientType() const
-     if (swap_status == SWAPOUT_NONE)
-         return STORE_MEM_CLIENT;
- 
-+    // TODO: The above "must make this a mem client" logic contradicts "Slight
-+    // weirdness" logic in store_client::doCopy() that converts hits to misses
-+    // on startSwapin() failures. We should probably attempt to open a swapin
-+    // file _here_ instead (and avoid STORE_DISK_CLIENT designation for clients
-+    // that fail to do so). That would also address a similar problem with Rock
-+    // store that does not yet support swapin during SWAPOUT_WRITING.
-+
-     /*
-      * otherwise, make subsequent clients read from disk so they
-      * can not delay the first, and vice-versa.
-      */
-+    debugs(20, 7, "STORE_PENDING STORE_DISK_CLIENT");
-     return STORE_DISK_CLIENT;
- }
- 
-diff --git a/src/store/Makefile.am b/src/store/Makefile.am
-index be177d8..ccfc2dd 100644
---- a/src/store/Makefile.am
-+++ b/src/store/Makefile.am
-@@ -23,4 +23,6 @@ libstore_la_SOURCES= \
- 	forward.h \
- 	LocalSearch.cc \
- 	LocalSearch.h \
-+	ParsingBuffer.cc \
-+	ParsingBuffer.h \
- 	Storage.h
-diff --git a/src/store/Makefile.in b/src/store/Makefile.in
-index bb4387d..1959c99 100644
---- a/src/store/Makefile.in
-+++ b/src/store/Makefile.in
-@@ -163,7 +163,7 @@ CONFIG_CLEAN_FILES =
- CONFIG_CLEAN_VPATH_FILES =
- LTLIBRARIES = $(noinst_LTLIBRARIES)
- libstore_la_LIBADD =
--am_libstore_la_OBJECTS = Controller.lo Disk.lo Disks.lo LocalSearch.lo
-+am_libstore_la_OBJECTS = Controller.lo Disk.lo Disks.lo LocalSearch.lo ParsingBuffer.lo
- libstore_la_OBJECTS = $(am_libstore_la_OBJECTS)
- AM_V_lt = $(am__v_lt_@AM_V@)
- am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
-@@ -185,7 +185,7 @@ DEFAULT_INCLUDES =
- depcomp = $(SHELL) $(top_srcdir)/cfgaux/depcomp
- am__maybe_remake_depfiles = depfiles
- am__depfiles_remade = ./$(DEPDIR)/Controller.Plo ./$(DEPDIR)/Disk.Plo \
--	./$(DEPDIR)/Disks.Plo ./$(DEPDIR)/LocalSearch.Plo
-+	./$(DEPDIR)/Disks.Plo ./$(DEPDIR)/LocalSearch.Plo ./$(DEPDIR)/ParsingBuffer.Plo
- am__mv = mv -f
- CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- 	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
-@@ -776,6 +776,8 @@ libstore_la_SOURCES = \
- 	forward.h \
- 	LocalSearch.cc \
- 	LocalSearch.h \
-+	ParsingBuffer.cc \
-+	ParsingBuffer.h \
- 	Storage.h
- 
- all: all-recursive
-@@ -846,6 +848,7 @@ distclean-compile:
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Disk.Plo@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Disks.Plo@am__quote@ # am--include-marker
- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LocalSearch.Plo@am__quote@ # am--include-marker
-+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParsingBuffer.Plo@am__quote@ # am--include-marker
- 
- $(am__depfiles_remade):
- 	@$(MKDIR_P) $(@D)
-@@ -1254,6 +1257,7 @@ distclean: distclean-recursive
- 	-rm -f ./$(DEPDIR)/Disk.Plo
- 	-rm -f ./$(DEPDIR)/Disks.Plo
- 	-rm -f ./$(DEPDIR)/LocalSearch.Plo
-+	-rm -f ./$(DEPDIR)/ParsingBuffer.Plo
- 	-rm -f Makefile
- distclean-am: clean-am distclean-compile distclean-generic \
- 	distclean-tags
-@@ -1303,6 +1307,7 @@ maintainer-clean: maintainer-clean-recursive
- 	-rm -f ./$(DEPDIR)/Disk.Plo
- 	-rm -f ./$(DEPDIR)/Disks.Plo
- 	-rm -f ./$(DEPDIR)/LocalSearch.Plo
-+	-rm -f ./$(DEPDIR)/ParsingBuffer.Plo
- 	-rm -f Makefile
- maintainer-clean-am: distclean-am maintainer-clean-generic
- 
-diff --git a/src/store/ParsingBuffer.cc b/src/store/ParsingBuffer.cc
-new file mode 100644
-index 0000000..ca6be72
---- /dev/null
-+++ b/src/store/ParsingBuffer.cc
-@@ -0,0 +1,199 @@
-+/*
-+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
-+ *
-+ * Squid software is distributed under GPLv2+ license and includes
-+ * contributions from numerous individuals and organizations.
-+ * Please see the COPYING and CONTRIBUTORS files for details.
-+ */
-+
-+#include "squid.h"
-+#include "sbuf/Stream.h"
-+#include "SquidMath.h"
-+#include "store/ParsingBuffer.h"
-+
-+#include 
-+
-+// Several Store::ParsingBuffer() methods use assert() because the corresponding
-+// failure means there is a good chance that somebody have already read from (or
-+// written to) the wrong memory location. Since this buffer is used for storing
-+// HTTP response bytes, such failures may corrupt traffic. No Assure() handling
-+// code can safely recover from such failures.
-+
-+Store::ParsingBuffer::ParsingBuffer(StoreIOBuffer &initialSpace):
-+    readerSuppliedMemory_(initialSpace)
-+{
-+}
-+
-+/// a read-only content start (or nil for some zero-size buffers)
-+const char *
-+Store::ParsingBuffer::memory() const
-+{
-+    return extraMemory_.second ? extraMemory_.first.rawContent() : readerSuppliedMemory_.data;
-+}
-+
-+size_t
-+Store::ParsingBuffer::capacity() const
-+{
-+    return extraMemory_.second ? (extraMemory_.first.length() + extraMemory_.first.spaceSize()) : readerSuppliedMemory_.length;
-+}
-+
-+size_t
-+Store::ParsingBuffer::contentSize() const
-+{
-+    return extraMemory_.second ? extraMemory_.first.length() : readerSuppliedMemoryContentSize_;
-+}
-+
-+void
-+Store::ParsingBuffer::appended(const char * const newBytes, const size_t newByteCount)
-+{
-+    // a positive newByteCount guarantees that, after the first assertion below
-+    // succeeds, the second assertion will not increment a nil memory() pointer
-+    if (!newByteCount)
-+        return;
-+
-+    // these checks order guarantees that memory() is not nil in the second assertion
-+    assert(newByteCount <= spaceSize()); // the new bytes end in our space
-+    assert(memory() + contentSize() == newBytes); // the new bytes start in our space
-+    // and now we know that newBytes is not nil either
-+
-+    if (extraMemory_.second)
-+        extraMemory_.first.rawAppendFinish(newBytes, newByteCount);
-+    else
-+        readerSuppliedMemoryContentSize_ = IncreaseSum(readerSuppliedMemoryContentSize_, newByteCount).first;
-+
-+    assert(contentSize() <= capacity()); // paranoid
-+}
-+
-+void
-+Store::ParsingBuffer::consume(const size_t parsedBytes)
-+{
-+    Assure(contentSize() >= parsedBytes); // more conservative than extraMemory_->consume()
-+    if (extraMemory_.second) {
-+        extraMemory_.first.consume(parsedBytes);
-+    } else {
-+        readerSuppliedMemoryContentSize_ -= parsedBytes;
-+        if (parsedBytes && readerSuppliedMemoryContentSize_)
-+            memmove(readerSuppliedMemory_.data, memory() + parsedBytes, readerSuppliedMemoryContentSize_);
-+    }
-+}
-+
-+StoreIOBuffer
-+Store::ParsingBuffer::space()
-+{
-+    const auto size = spaceSize();
-+    const auto start = extraMemory_.second ?
-+                       extraMemory_.first.rawAppendStart(size) :
-+                       (readerSuppliedMemory_.data + readerSuppliedMemoryContentSize_);
-+    return StoreIOBuffer(spaceSize(), 0, start);
-+}
-+
-+StoreIOBuffer
-+Store::ParsingBuffer::makeSpace(const size_t pageSize)
-+{
-+    growSpace(pageSize);
-+    auto result = space();
-+    Assure(result.length >= pageSize);
-+    result.length = pageSize;
-+    return result;
-+}
-+
-+StoreIOBuffer
-+Store::ParsingBuffer::content() const
-+{
-+    // This const_cast is a StoreIOBuffer API limitation: That class does not
-+    // support a "constant content view", even though it is used as such a view.
-+    return StoreIOBuffer(contentSize(), 0, const_cast(memory()));
-+}
-+
-+/// makes sure we have the requested number of bytes, allocates enough memory if needed
-+void
-+Store::ParsingBuffer::growSpace(const size_t minimumSpaceSize)
-+{
-+    const auto capacityIncreaseAttempt = IncreaseSum(contentSize(), minimumSpaceSize);
-+    if (!capacityIncreaseAttempt.second)
-+        throw TextException(ToSBuf("no support for a single memory block of ", contentSize(), '+', minimumSpaceSize, " bytes"), Here());
-+    const auto newCapacity = capacityIncreaseAttempt.first;
-+
-+    if (newCapacity <= capacity())
-+        return; // already have enough space; no reallocation is needed
-+
-+    debugs(90, 7, "growing to provide " << minimumSpaceSize << " in " << *this);
-+
-+    if (extraMemory_.second) {
-+        extraMemory_.first.reserveCapacity(newCapacity);
-+    } else {
-+        SBuf newStorage;
-+        newStorage.reserveCapacity(newCapacity);
-+        newStorage.append(readerSuppliedMemory_.data, readerSuppliedMemoryContentSize_);
-+        extraMemory_.first = std::move(newStorage);
-+        extraMemory_.second = true;
-+    }
-+    Assure(spaceSize() >= minimumSpaceSize);
-+}
-+
-+SBuf
-+Store::ParsingBuffer::toSBuf() const
-+{
-+    return extraMemory_.second ? extraMemory_.first : SBuf(content().data, content().length);
-+}
-+
-+size_t
-+Store::ParsingBuffer::spaceSize() const
-+{
-+    if (extraMemory_.second)
-+        return extraMemory_.first.spaceSize();
-+
-+    assert(readerSuppliedMemoryContentSize_ <= readerSuppliedMemory_.length);
-+    return readerSuppliedMemory_.length - readerSuppliedMemoryContentSize_;
-+}
-+
-+/// 0-terminates stored byte sequence, allocating more memory if needed, but
-+/// without increasing the number of stored content bytes
-+void
-+Store::ParsingBuffer::terminate()
-+{
-+    *makeSpace(1).data = 0;
-+}
-+
-+StoreIOBuffer
-+Store::ParsingBuffer::packBack()
-+{
-+    const auto bytesToPack = contentSize();
-+    // until our callers do not have to work around legacy code expectations
-+    Assure(bytesToPack);
-+
-+    // if we accumulated more bytes at some point, any extra metadata should
-+    // have been consume()d by now, allowing readerSuppliedMemory_.data reuse
-+    Assure(bytesToPack <= readerSuppliedMemory_.length);
-+
-+    auto result = readerSuppliedMemory_;
-+    result.length = bytesToPack;
-+    Assure(result.data);
-+
-+    if (!extraMemory_.second) {
-+        // no accumulated bytes copying because they are in readerSuppliedMemory_
-+        debugs(90, 7, "quickly exporting " << result.length << " bytes via " << readerSuppliedMemory_);
-+    } else {
-+        debugs(90, 7, "slowly exporting " << result.length << " bytes from " << extraMemory_.first.id << " back into " << readerSuppliedMemory_);
-+        memmove(result.data, extraMemory_.first.rawContent(), result.length);
-+    }
-+
-+    return result;
-+}
-+
-+void
-+Store::ParsingBuffer::print(std::ostream &os) const
-+{
-+    os << "size=" << contentSize();
-+
-+    if (extraMemory_.second) {
-+        os << " capacity=" << capacity();
-+        os << " extra=" << extraMemory_.first.id;
-+    }
-+
-+    // report readerSuppliedMemory_ (if any) even if we are no longer using it
-+    // for content storage; it affects packBack() and related parsing logic
-+    if (readerSuppliedMemory_.length)
-+        os << ' ' << readerSuppliedMemory_;
-+}
-+
-diff --git a/src/store/ParsingBuffer.h b/src/store/ParsingBuffer.h
-new file mode 100644
-index 0000000..b473ac6
---- /dev/null
-+++ b/src/store/ParsingBuffer.h
-@@ -0,0 +1,128 @@
-+/*
-+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
-+ *
-+ * Squid software is distributed under GPLv2+ license and includes
-+ * contributions from numerous individuals and organizations.
-+ * Please see the COPYING and CONTRIBUTORS files for details.
-+ */
-+
-+#ifndef SQUID_SRC_STORE_PARSINGBUFFER_H
-+#define SQUID_SRC_STORE_PARSINGBUFFER_H
-+
-+#include "sbuf/SBuf.h"
-+#include "StoreIOBuffer.h"
-+
-+#include 
-+
-+namespace Store
-+{
-+
-+/// A continuous buffer for efficient accumulation and NUL-termination of
-+/// Store-read bytes. The buffer accumulates two kinds of Store readers:
-+///
-+/// * Readers that do not have any external buffer to worry about but need to
-+///   accumulate, terminate, and/or consume buffered content read by Store.
-+///   These readers use the default constructor and then allocate the initial
-+///   buffer space for their first read (if any).
-+///
-+/// * Readers that supply their StoreIOBuffer at construction time. That buffer
-+///   is enough to handle the majority of use cases. However, the supplied
-+///   StoreIOBuffer capacity may be exceeded when parsing requires accumulating
-+///   multiple Store read results and/or NUL-termination of a full buffer.
-+///
-+/// This buffer seamlessly grows as needed, reducing memory over-allocation and,
-+/// in case of StoreIOBuffer-seeded construction, memory copies.
-+class ParsingBuffer
-+{
-+public:
-+    /// creates buffer without any space or content
-+    ParsingBuffer() = default;
-+
-+    /// seeds this buffer with the caller-supplied buffer space
-+    explicit ParsingBuffer(StoreIOBuffer &);
-+
-+    /// a NUL-terminated version of content(); same lifetime as content()
-+    const char *c_str() { terminate(); return memory(); }
-+
-+    /// export content() into SBuf, avoiding content copying when possible
-+    SBuf toSBuf() const;
-+
-+    /// the total number of append()ed bytes that were not consume()d
-+    size_t contentSize() const;
-+
-+    /// the number of bytes in the space() buffer
-+    size_t spaceSize() const;
-+
-+    /// the maximum number of bytes we can store without allocating more space
-+    size_t capacity() const;
-+
-+    /// Stored append()ed bytes that have not been consume()d. The returned
-+    /// buffer offset is set to zero; the caller is responsible for adjusting
-+    /// the offset if needed (TODO: Add/return a no-offset Mem::View instead).
-+    /// The returned buffer is invalidated by calling a non-constant method or
-+    /// by changing the StoreIOBuffer contents given to our constructor.
-+    StoreIOBuffer content() const;
-+
-+    /// A (possibly empty) buffer for reading the next byte(s). The returned
-+    /// buffer offset is set to zero; the caller is responsible for adjusting
-+    /// the offset if needed (TODO: Add/return a no-offset Mem::Area instead).
-+    /// The returned buffer is invalidated by calling a non-constant method or
-+    /// by changing the StoreIOBuffer contents given to our constructor.
-+    StoreIOBuffer space();
-+
-+    /// A buffer for reading the exact number of next byte(s). The method may
-+    /// allocate new memory and copy previously appended() bytes as needed.
-+    /// \param pageSize the exact number of bytes the caller wants to read
-+    /// \returns space() after any necessary allocations
-+    StoreIOBuffer makeSpace(size_t pageSize);
-+
-+    /// A buffer suitable for the first storeClientCopy() call. The method may
-+    /// allocate new memory and copy previously appended() bytes as needed.
-+    /// \returns space() after any necessary allocations
-+    /// \deprecated New clients should call makeSpace() with client-specific
-+    /// pageSize instead of this one-size-fits-all legacy method.
-+    StoreIOBuffer makeInitialSpace() { return makeSpace(4096); }
-+
-+    /// remember the new bytes received into the previously provided space()
-+    void appended(const char *, size_t);
-+
-+    /// get rid of previously appended() prefix of a given size
-+    void consume(size_t);
-+
-+    /// Returns stored content, reusing the StoreIOBuffer given at the
-+    /// construction time. Copying is avoided if we did not allocate extra
-+    /// memory since construction. Not meant for default-constructed buffers.
-+    /// \prec positive contentSize() (\sa store_client::finishCallback())
-+    StoreIOBuffer packBack();
-+
-+    /// summarizes object state (for debugging)
-+    void print(std::ostream &) const;
-+
-+private:
-+    const char *memory() const;
-+    void terminate();
-+    void growSpace(size_t);
-+
-+private:
-+    /// externally allocated buffer we were seeded with (or a zero-size one)
-+    StoreIOBuffer readerSuppliedMemory_;
-+
-+    /// append()ed to readerSuppliedMemory_ bytes that were not consume()d
-+    size_t readerSuppliedMemoryContentSize_ = 0;
-+
-+    /// our internal buffer that takes over readerSuppliedMemory_ when the
-+    /// latter becomes full and more memory is needed
-+    std::pair extraMemory_ = std::make_pair(SBuf(), false);
-+};
-+
-+inline std::ostream &
-+operator <<(std::ostream &os, const ParsingBuffer &b)
-+{
-+    b.print(os);
-+    return os;
-+}
-+
-+} // namespace Store
-+
-+#endif /* SQUID_SRC_STORE_PARSINGBUFFER_H */
-+
-diff --git a/src/store/forward.h b/src/store/forward.h
-index 1422a85..db5ee1c 100644
---- a/src/store/forward.h
-+++ b/src/store/forward.h
-@@ -46,6 +46,7 @@ class Disks;
- class Disk;
- class DiskConfig;
- class EntryGuard;
-+class ParsingBuffer;
- 
- typedef ::StoreEntry Entry;
- typedef ::MemStore Memory;
-diff --git a/src/store_client.cc b/src/store_client.cc
-index 1b54f04..a5f2440 100644
---- a/src/store_client.cc
-+++ b/src/store_client.cc
-@@ -9,6 +9,7 @@
- /* DEBUG: section 90    Storage Manager Client-Side Interface */
- 
- #include "squid.h"
-+#include "base/AsyncCbdataCalls.h"
- #include "event.h"
- #include "globals.h"
- #include "HttpReply.h"
-@@ -16,8 +17,10 @@
- #include "MemBuf.h"
- #include "MemObject.h"
- #include "mime_header.h"
-+#include "sbuf/Stream.h"
- #include "profiler/Profiler.h"
- #include "SquidConfig.h"
-+#include "SquidMath.h"
- #include "StatCounters.h"
- #include "Store.h"
- #include "store_swapin.h"
-@@ -39,17 +42,10 @@
- static StoreIOState::STRCB storeClientReadBody;
- static StoreIOState::STRCB storeClientReadHeader;
- static void storeClientCopy2(StoreEntry * e, store_client * sc);
--static EVH storeClientCopyEvent;
- static bool CheckQuickAbortIsReasonable(StoreEntry * entry);
- 
- CBDATA_CLASS_INIT(store_client);
- 
--bool
--store_client::memReaderHasLowerOffset(int64_t anOffset) const
--{
--    return getType() == STORE_MEM_CLIENT && copyInto.offset < anOffset;
--}
--
- int
- store_client::getType() const
- {
-@@ -105,25 +101,35 @@ storeClientListAdd(StoreEntry * e, void *data)
- }
- 
- void
--store_client::callback(ssize_t sz, bool error)
-+store_client::FinishCallback(store_client * const sc)
- {
--    size_t bSz = 0;
-+    sc->finishCallback();
-+}
- 
--    if (sz >= 0 && !error)
--        bSz = sz;
-+void
-+store_client::finishCallback()
-+{
-+    Assure(_callback.callback_handler);
-+    Assure(_callback.notifier);
- 
--    StoreIOBuffer result(bSz, 0 ,copyInto.data);
-+    // XXX: Some legacy code relies on zero-length buffers having nil data
-+    // pointers. Some other legacy code expects "correct" result.offset even
-+    // when there is no body to return. Accommodate all those expectations.
-+    auto result = StoreIOBuffer(0, copyInto.offset, nullptr);
-+    if (object_ok && parsingBuffer.second && parsingBuffer.first.contentSize())
-+        result = parsingBuffer.first.packBack();
-+    result.flags.error = object_ok ? 0 : 1;
- 
--    if (sz < 0 || error)
--        result.flags.error = 1;
-+    // no HTTP headers and no body bytes (but not because there was no space)
-+    atEof_ = !sendingHttpHeaders() && !result.length && copyInto.length;
-+
-+    parsingBuffer.second = false;
-+    ++answers;
- 
--    result.offset = cmp_offset;
--    assert(_callback.pending());
--    cmp_offset = copyInto.offset + bSz;
-     STCB *temphandler = _callback.callback_handler;
-     void *cbdata = _callback.callback_data;
--    _callback = Callback(NULL, NULL);
--    copyInto.data = NULL;
-+    _callback = Callback(nullptr, nullptr);
-+    copyInto.data = nullptr;
- 
-     if (cbdataReferenceValid(cbdata))
-         temphandler(cbdata, result);
-@@ -131,32 +137,18 @@ store_client::callback(ssize_t sz, bool error)
-     cbdataReferenceDone(cbdata);
- }
- 
--static void
--storeClientCopyEvent(void *data)
--{
--    store_client *sc = (store_client *)data;
--    debugs(90, 3, "storeClientCopyEvent: Running");
--    assert (sc->flags.copy_event_pending);
--    sc->flags.copy_event_pending = false;
--
--    if (!sc->_callback.pending())
--        return;
--
--    storeClientCopy2(sc->entry, sc);
--}
--
- store_client::store_client(StoreEntry *e) :
--    cmp_offset(0),
- #if STORE_CLIENT_LIST_DEBUG
-     owner(cbdataReference(data)),
- #endif
-     entry(e),
-     type(e->storeClientType()),
--    object_ok(true)
-+    object_ok(true),
-+    atEof_(false),
-+    answers(0)
- {
-     flags.disk_io_pending = false;
-     flags.store_copying = false;
--    flags.copy_event_pending = false;
-     ++ entry->refcount;
- 
-     if (getType() == STORE_DISK_CLIENT) {
-@@ -202,16 +194,33 @@ store_client::copy(StoreEntry * anEntry,
- #endif
- 
-     assert(!_callback.pending());
--#if ONLYCONTIGUOUSREQUESTS
--
--    assert(cmp_offset == copyRequest.offset);
--#endif
--    /* range requests will skip into the body */
--    cmp_offset = copyRequest.offset;
-     _callback = Callback (callback_fn, cbdataReference(data));
-     copyInto.data = copyRequest.data;
-     copyInto.length = copyRequest.length;
-     copyInto.offset = copyRequest.offset;
-+    Assure(copyInto.offset >= 0);
-+
-+    if (!copyInto.length) {
-+        // During the first storeClientCopy() call, a zero-size buffer means
-+        // that we will have to drop any HTTP response body bytes we read (with
-+        // the HTTP headers from disk). After that, it means we cannot return
-+        // anything to the caller at all.
-+        debugs(90, 2, "WARNING: zero-size storeClientCopy() buffer: " << copyInto);
-+        // keep going; moreToRead() should prevent any from-Store reading
-+    }
-+
-+    // Our nextHttpReadOffset() expects the first copy() call to have zero
-+    // offset. More complex code could handle a positive first offset, but it
-+    // would only be useful when reading responses from memory: We would not
-+    // _delay_ the response (to read the requested HTTP body bytes from disk)
-+    // when we already can respond with HTTP headers.
-+    Assure(!copyInto.offset || answeredOnce());
-+
-+    parsingBuffer.first = Store::ParsingBuffer(copyInto);
-+    parsingBuffer.second = true;
-+
-+    discardableHttpEnd_ = nextHttpReadOffset();
-+    debugs(90, 7, "discardableHttpEnd_=" << discardableHttpEnd_);
- 
-     static bool copying (false);
-     assert (!copying);
-@@ -239,50 +248,41 @@ store_client::copy(StoreEntry * anEntry,
-     // Add no code here. This object may no longer exist.
- }
- 
--/// Whether there is (or will be) more entry data for us.
-+/// Whether Store has (or possibly will have) more entry data for us.
- bool
--store_client::moreToSend() const
-+store_client::moreToRead() const
- {
-+    if (!copyInto.length)
-+        return false; // the client supplied a zero-size buffer
-+
-     if (entry->store_status == STORE_PENDING)
-         return true; // there may be more coming
- 
-     /* STORE_OK, including aborted entries: no more data is coming */
- 
--    const int64_t len = entry->objectLen();
-+    if (canReadFromMemory())
-+        return true; // memory has the first byte wanted by the client
- 
--    // If we do not know the entry length, then we have to open the swap file.
--    const bool canSwapIn = entry->hasDisk();
--    if (len < 0)
--        return canSwapIn;
-+    if (!entry->hasDisk())
-+        return false; // cannot read anything from disk either
- 
--    if (copyInto.offset >= len)
--        return false; // sent everything there is
-+    if (entry->objectLen() >= 0 && copyInto.offset >= entry->contentLen())
-+        return false; // the disk cannot have byte(s) wanted by the client
- 
--    if (canSwapIn)
--        return true; // if we lack prefix, we can swap it in
--
--    // If we cannot swap in, make sure we have what we want in RAM. Otherwise,
--    // scheduleRead calls scheduleDiskRead which asserts without a swap file.
--    const MemObject *mem = entry->mem_obj;
--    return mem &&
--           mem->inmem_lo <= copyInto.offset && copyInto.offset < mem->endOffset();
-+    // we cannot be sure until we swap in metadata and learn contentLen(),
-+    // but the disk may have the byte(s) wanted by the client
-+    return true;
- }
- 
- static void
- storeClientCopy2(StoreEntry * e, store_client * sc)
- {
-     /* reentrancy not allowed  - note this could lead to
--     * dropped events
-+     * dropped notifications about response data availability
-      */
- 
--    if (sc->flags.copy_event_pending) {
--        return;
--    }
--
-     if (sc->flags.store_copying) {
--        sc->flags.copy_event_pending = true;
--        debugs(90, 3, "storeClientCopy2: Queueing storeClientCopyEvent()");
--        eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0);
-+        debugs(90, 3, "prevented recursive copying for " << *e);
-         return;
-     }
- 
-@@ -295,39 +295,44 @@ storeClientCopy2(StoreEntry * e, store_client * sc)
-      * if the peer aborts, we want to give the client(s)
-      * everything we got before the abort condition occurred.
-      */
--    /* Warning: doCopy may indirectly free itself in callbacks,
--     * hence the lock to keep it active for the duration of
--     * this function
--     * XXX: Locking does not prevent calling sc destructor (it only prevents
--     * freeing sc memory) so sc may become invalid from C++ p.o.v.
--     */
--    CbcPointer tmpLock = sc;
--    assert (!sc->flags.store_copying);
-     sc->doCopy(e);
--    assert(!sc->flags.store_copying);
-+}
-+
-+/// Whether our answer, if sent right now, will announce the availability of
-+/// HTTP response headers (to the STCB callback) for the first time.
-+bool
-+store_client::sendingHttpHeaders() const
-+{
-+    return !answeredOnce() && entry->mem().baseReply().hdr_sz > 0;
- }
- 
- void
- store_client::doCopy(StoreEntry *anEntry)
- {
-+    Assure(_callback.pending());
-+    Assure(!flags.disk_io_pending);
-+    Assure(!flags.store_copying);
-+
-     assert (anEntry == entry);
-     flags.store_copying = true;
-     MemObject *mem = entry->mem_obj;
- 
--    debugs(33, 5, "store_client::doCopy: co: " <<
--           copyInto.offset << ", hi: " <<
--           mem->endOffset());
-+    debugs(33, 5, this << " into " << copyInto <<
-+           " hi: " << mem->endOffset() <<
-+           " objectLen: " << entry->objectLen() <<
-+           " past_answers: " << answers);
- 
--    if (!moreToSend()) {
-+    const auto sendHttpHeaders = sendingHttpHeaders();
-+
-+    if (!sendHttpHeaders && !moreToRead()) {
-         /* There is no more to send! */
-         debugs(33, 3, HERE << "There is no more to send!");
--        callback(0);
-+        noteNews();
-         flags.store_copying = false;
-         return;
-     }
- 
--    /* Check that we actually have data */
--    if (anEntry->store_status == STORE_PENDING && copyInto.offset >= mem->endOffset()) {
-+    if (!sendHttpHeaders && anEntry->store_status == STORE_PENDING && nextHttpReadOffset() >= mem->endOffset()) {
-         debugs(90, 3, "store_client::doCopy: Waiting for more");
-         flags.store_copying = false;
-         return;
-@@ -349,7 +354,24 @@ store_client::doCopy(StoreEntry *anEntry)
-         if (!startSwapin())
-             return; // failure
-     }
--    scheduleRead();
-+
-+    // send any immediately available body bytes even if we also sendHttpHeaders
-+    if (canReadFromMemory()) {
-+        readFromMemory();
-+        noteNews(); // will sendHttpHeaders (if needed) as well
-+        flags.store_copying = false;
-+        return;
-+    }
-+
-+    if (sendHttpHeaders) {
-+        debugs(33, 5, "just send HTTP headers: " << mem->baseReply().hdr_sz);
-+        noteNews();
-+        flags.store_copying = false;
-+        return;
-+    }
-+
-+    // no information that the client needs is available immediately
-+    scheduleDiskRead();
- }
- 
- /// opens the swapin "file" if possible; otherwise, fail()s and returns false
-@@ -383,14 +405,13 @@ store_client::startSwapin()
- }
- 
- void
--store_client::scheduleRead()
-+store_client::noteSwapInDone(const bool error)
- {
--    MemObject *mem = entry->mem_obj;
--
--    if (copyInto.offset >= mem->inmem_lo && copyInto.offset < mem->endOffset())
--        scheduleMemRead();
-+    Assure(_callback.pending());
-+    if (error)
-+        fail();
-     else
--        scheduleDiskRead();
-+        noteNews();
- }
- 
- void
-@@ -415,15 +436,44 @@ store_client::scheduleDiskRead()
-     flags.store_copying = false;
- }
- 
-+/// whether at least one byte wanted by the client is in memory
-+bool
-+store_client::canReadFromMemory() const
-+{
-+    const auto &mem = entry->mem();
-+    const auto memReadOffset = nextHttpReadOffset();
-+    return mem.inmem_lo <= memReadOffset && memReadOffset < mem.endOffset() &&
-+           parsingBuffer.first.spaceSize();
-+}
-+
-+/// The offset of the next stored HTTP response byte wanted by the client.
-+int64_t
-+store_client::nextHttpReadOffset() const
-+{
-+    Assure(parsingBuffer.second);
-+    const auto &mem = entry->mem();
-+    const auto hdr_sz = mem.baseReply().hdr_sz;
-+    // Certain SMP cache manager transactions do not store HTTP headers in
-+    // mem_hdr; they store just a kid-specific piece of the future report body.
-+    // In such cases, hdr_sz ought to be zero. In all other (known) cases,
-+    // mem_hdr contains HTTP response headers (positive hdr_sz if parsed)
-+    // followed by HTTP response body. This code math accommodates all cases.
-+    return NaturalSum(hdr_sz, copyInto.offset, parsingBuffer.first.contentSize()).first;
-+}
-+
-+/// Copies at least some of the requested body bytes from MemObject memory,
-+/// satisfying the copy() request.
-+/// \pre canReadFromMemory() is true
- void
--store_client::scheduleMemRead()
-+store_client::readFromMemory()
- {
--    /* What the client wants is in memory */
--    /* Old style */
--    debugs(90, 3, "store_client::doCopy: Copying normal from memory");
--    size_t sz = entry->mem_obj->data_hdr.copy(copyInto);
--    callback(sz);
--    flags.store_copying = false;
-+    Assure(parsingBuffer.second);
-+    const auto readInto = parsingBuffer.first.space().positionAt(nextHttpReadOffset());
-+
-+    debugs(90, 3, "copying HTTP body bytes from memory into " << readInto);
-+    const auto sz = entry->mem_obj->data_hdr.copy(readInto);
-+    Assure(sz > 0); // our canReadFromMemory() precondition guarantees that
-+    parsingBuffer.first.appended(readInto.data, sz);
- }
- 
- void
-@@ -435,65 +485,150 @@ store_client::fileRead()
-     assert(!flags.disk_io_pending);
-     flags.disk_io_pending = true;
- 
-+    // mem->swap_hdr_sz is zero here during initial read(s)
-+    const auto nextStoreReadOffset = NaturalSum(mem->swap_hdr_sz, nextHttpReadOffset()).first;
-+
-+    // XXX: If fileRead() is called when we do not yet know mem->swap_hdr_sz,
-+    // then we must start reading from disk offset zero to learn it: we cannot
-+    // compute correct HTTP response start offset on disk without it. However,
-+    // late startSwapin() calls imply that the assertion below might fail.
-+    Assure(mem->swap_hdr_sz > 0 || !nextStoreReadOffset);
-+
-+    // TODO: Remove this assertion. Introduced in 1998 commit 3157c72, it
-+    // assumes that swapped out memory is freed unconditionally, but we no
-+    // longer do that because trimMemory() path checks lowestMemReaderOffset().
-+    // It is also misplaced: We are not swapping out anything here and should
-+    // not care about any swapout invariants.
-     if (mem->swap_hdr_sz != 0)
-         if (entry->swappingOut())
--            assert(mem->swapout.sio->offset() > copyInto.offset + (int64_t)mem->swap_hdr_sz);
-+            assert(mem->swapout.sio->offset() > nextStoreReadOffset);
-+
-+    // XXX: We should let individual cache_dirs limit the read size instead, but
-+    // we cannot do that without more fixes and research because:
-+    // * larger reads corrupt responses when cache_dir uses SharedMemory::get();
-+    // * we do not know how to find all I/O code that assumes this limit;
-+    // * performance effects of larger disk reads may be negative somewhere.
-+    const decltype(StoreIOBuffer::length) maxReadSize = SM_PAGE_SIZE;
-+
-+    Assure(parsingBuffer.second);
-+    // also, do not read more than we can return (via a copyInto.length buffer)
-+    const auto readSize = std::min(copyInto.length, maxReadSize);
-+    lastDiskRead = parsingBuffer.first.makeSpace(readSize).positionAt(nextStoreReadOffset);
-+    debugs(90, 5, "into " << lastDiskRead);
- 
-     storeRead(swapin_sio,
--              copyInto.data,
--              copyInto.length,
--              copyInto.offset + mem->swap_hdr_sz,
-+              lastDiskRead.data,
-+              lastDiskRead.length,
-+              lastDiskRead.offset,
-               mem->swap_hdr_sz == 0 ? storeClientReadHeader
-               : storeClientReadBody,
-               this);
- }
- 
- void
--store_client::readBody(const char *, ssize_t len)
-+store_client::readBody(const char * const buf, const ssize_t lastIoResult)
- {
--    int parsed_header = 0;
--
--    // Don't assert disk_io_pending here.. may be called by read_header
-+    Assure(flags.disk_io_pending);
-     flags.disk_io_pending = false;
-     assert(_callback.pending());
--    debugs(90, 3, "storeClientReadBody: len " << len << "");
-+    Assure(parsingBuffer.second);
-+    debugs(90, 3, "got " << lastIoResult << " using " << parsingBuffer.first);
- 
--    if (len < 0)
-+    if (lastIoResult < 0)
-         return fail();
- 
--    if (copyInto.offset == 0 && len > 0 && entry->getReply()->sline.status() == Http::scNone) {
--        /* Our structure ! */
--        HttpReply *rep = (HttpReply *) entry->getReply(); // bypass const
-+    if (!lastIoResult) {
-+        if (answeredOnce())
-+            return noteNews();
- 
--        if (!rep->parseCharBuf(copyInto.data, headersEnd(copyInto.data, len))) {
--            debugs(90, DBG_CRITICAL, "Could not parse headers from on disk object");
--        } else {
--            parsed_header = 1;
--        }
-+        debugs(90, DBG_CRITICAL, "ERROR: Truncated HTTP headers in on-disk object");
-+        return fail();
-     }
- 
--    const HttpReply *rep = entry->getReply();
--    if (len > 0 && rep && entry->mem_obj->inmem_lo == 0 && entry->objectLen() <= (int64_t)Config.Store.maxInMemObjSize && Config.onoff.memory_cache_disk) {
--        storeGetMemSpace(len);
--        // The above may start to free our object so we need to check again
-+    assert(lastDiskRead.data == buf);
-+    lastDiskRead.length = lastIoResult;
-+
-+    parsingBuffer.first.appended(buf, lastIoResult);
-+
-+    // we know swap_hdr_sz by now and were reading beyond swap metadata because
-+    // readHead() would have been called otherwise (to read swap metadata)
-+    const auto swap_hdr_sz = entry->mem().swap_hdr_sz;
-+    Assure(swap_hdr_sz > 0);
-+    Assure(!Less(lastDiskRead.offset, swap_hdr_sz));
-+
-+    // Map lastDiskRead (i.e. the disk area we just read) to an HTTP reply part.
-+    // The bytes are the same, but disk and HTTP offsets differ by swap_hdr_sz.
-+    const auto httpOffset = lastDiskRead.offset - swap_hdr_sz;
-+    const auto httpPart = StoreIOBuffer(lastDiskRead).positionAt(httpOffset);
-+
-+    maybeWriteFromDiskToMemory(httpPart);
-+    handleBodyFromDisk();
-+}
-+
-+/// de-serializes HTTP response (partially) read from disk storage
-+void
-+store_client::handleBodyFromDisk()
-+{
-+    // We cannot de-serialize on-disk HTTP response without MemObject because
-+    // without MemObject::swap_hdr_sz we cannot know where that response starts.
-+    Assure(entry->mem_obj);
-+    Assure(entry->mem_obj->swap_hdr_sz > 0);
-+
-+    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 == psParsed;
-+        if (!haveHttpHeaders && !parseHttpHeadersFromDisk())
-+            return;
-+        skipHttpHeadersFromDisk();
-+    }
-+
-+    noteNews();
-+}
-+
-+/// Adds HTTP response data loaded from disk to the memory cache (if
-+/// needed/possible). The given part may contain portions of HTTP response
-+/// headers and/or HTTP response body.
-+void
-+store_client::maybeWriteFromDiskToMemory(const StoreIOBuffer &httpResponsePart)
-+{
-+    // XXX: Reject [memory-]uncachable/unshareable responses instead of assuming
-+    // that an HTTP response should be written to MemObject's data_hdr (and that
-+    // it may purge already cached entries) just because it "fits" and was
-+    // loaded from disk. For example, this response may already be marked for
-+    // release. The (complex) cachability decision(s) should be made outside
-+    // (and obeyed by) this low-level code.
-+    if (httpResponsePart.length && entry->mem_obj->inmem_lo == 0 && entry->objectLen() <= (int64_t)Config.Store.maxInMemObjSize && Config.onoff.memory_cache_disk) {
-+        storeGetMemSpace(httpResponsePart.length);
-+        // XXX: This "recheck" is not needed because storeGetMemSpace() cannot
-+        // purge mem_hdr bytes of a locked entry, and we do lock ours. And
-+        // inmem_lo offset itself should not be relevant to appending new bytes.
-+        //
-+        // recheck for the above call may purge entry's data from the memory cache
-         if (entry->mem_obj->inmem_lo == 0) {
--            /* Copy read data back into memory.
--             * copyInto.offset includes headers, which is what mem cache needs
--             */
--            int64_t mem_offset = entry->mem_obj->endOffset();
--            if ((copyInto.offset == mem_offset) || (parsed_header && mem_offset == rep->hdr_sz)) {
--                entry->mem_obj->write(StoreIOBuffer(len, copyInto.offset, copyInto.data));
--            }
-+            // XXX: This code assumes a non-shared memory cache.
-+            if (httpResponsePart.offset == entry->mem_obj->endOffset())
-+                entry->mem_obj->write(httpResponsePart);
-         }
-     }
--
--    callback(len);
- }
- 
- void
- store_client::fail()
- {
-+    debugs(90, 3, (object_ok ? "once" : "again"));
-+    if (!object_ok)
-+        return; // we failed earlier; nothing to do now
-+
-     object_ok = false;
-+
-+    noteNews();
-+}
-+
-+/// if necessary and possible, informs the Store reader about copy() result
-+void
-+store_client::noteNews()
-+{
-     /* synchronous open failures callback from the store,
-      * before startSwapin detects the failure.
-      * TODO: fix this inconsistent behaviour - probably by
-@@ -501,8 +636,20 @@ store_client::fail()
-      * not synchronous
-      */
- 
--    if (_callback.pending())
--        callback(0, true);
-+    if (!_callback.callback_handler) {
-+        debugs(90, 5, "client lost interest");
-+        return;
-+    }
-+
-+    if (_callback.notifier) {
-+        debugs(90, 5, "earlier news is being delivered by " << _callback.notifier);
-+        return;
-+    }
-+
-+    _callback.notifier = asyncCall(90, 4, "store_client::FinishCallback", cbdataDialer(store_client::FinishCallback, this));
-+    ScheduleCallHere(_callback.notifier);
-+
-+    Assure(!_callback.pending());
- }
- 
- static void
-@@ -573,38 +720,22 @@ store_client::readHeader(char const *buf, ssize_t len)
-     if (!object_ok)
-         return;
- 
-+    Assure(parsingBuffer.second);
-+    debugs(90, 3, "got " << len << " using " << parsingBuffer.first);
-+
-     if (len < 0)
-         return fail();
- 
-+    Assure(!parsingBuffer.first.contentSize());
-+    parsingBuffer.first.appended(buf, len);
-     if (!unpackHeader(buf, len)) {
-         fail();
-         return;
-     }
-+    parsingBuffer.first.consume(mem->swap_hdr_sz);
- 
--    /*
--     * If our last read got some data the client wants, then give
--     * it to them, otherwise schedule another read.
--     */
--    size_t body_sz = len - mem->swap_hdr_sz;
--
--    if (copyInto.offset < static_cast(body_sz)) {
--        /*
--         * we have (part of) what they want
--         */
--        size_t copy_sz = min(copyInto.length, body_sz);
--        debugs(90, 3, "storeClientReadHeader: copying " << copy_sz << " bytes of body");
--        memmove(copyInto.data, copyInto.data + mem->swap_hdr_sz, copy_sz);
--
--        readBody(copyInto.data, copy_sz);
--
--        return;
--    }
--
--    /*
--     * we don't have what the client wants, but at least we now
--     * know the swap header size.
--     */
--    fileRead();
-+    maybeWriteFromDiskToMemory(parsingBuffer.first.content());
-+    handleBodyFromDisk();
- }
- 
- int
-@@ -673,10 +804,12 @@ storeUnregister(store_client * sc, StoreEntry * e, void *data)
-         ++statCounter.swap.ins;
-     }
- 
--    if (sc->_callback.pending()) {
--        /* callback with ssize = -1 to indicate unexpected termination */
--        debugs(90, 3, "store_client for " << *e << " has a callback");
--        sc->fail();
-+    if (sc->_callback.callback_handler || sc->_callback.notifier) {
-+        debugs(90, 3, "forgetting store_client callback for " << *e);
-+        // Do not notify: Callers want to stop copying and forget about this
-+        // pending copy request. Some would mishandle a notification from here.
-+        if (sc->_callback.notifier)
-+            sc->_callback.notifier->cancel("storeUnregister");
-     }
- 
- #if STORE_CLIENT_LIST_DEBUG
-@@ -684,6 +817,8 @@ storeUnregister(store_client * sc, StoreEntry * e, void *data)
- 
- #endif
- 
-+    // XXX: We might be inside sc store_client method somewhere up the call
-+    // stack. TODO: Convert store_client to AsyncJob to make destruction async.
-     delete sc;
- 
-     assert(e->locked());
-@@ -740,6 +875,9 @@ StoreEntry::invokeHandlers()
- 
-         if (sc->flags.disk_io_pending)
-             continue;
-+        
-+        if (sc->flags.store_copying)
-+            continue;
- 
-         storeClientCopy2(this, sc);
-     }
-@@ -847,6 +985,63 @@ CheckQuickAbortIsReasonable(StoreEntry * entry)
-     return true;
- }
- 
-+/// parses HTTP header bytes loaded from disk
-+/// \returns false if fail() or scheduleDiskRead() has been called and, hence,
-+/// the caller should just quit without any further action
-+bool
-+store_client::parseHttpHeadersFromDisk()
-+{
-+    try {
-+        return tryParsingHttpHeaders();
-+    } catch (...) {
-+        // XXX: Our parser enforces Config.maxReplyHeaderSize limit, but our
-+        // packer does not. Since packing might increase header size, we may
-+        // cache a header that we cannot parse and get here. Same for MemStore.
-+        debugs(90, DBG_CRITICAL, "ERROR: Cannot parse on-disk HTTP headers" <<
-+               Debug::Extra << "exception: " << CurrentException <<
-+               Debug::Extra << "raw input size: " << parsingBuffer.first.contentSize() << " bytes" <<
-+               Debug::Extra << "current buffer capacity: " << parsingBuffer.first.capacity() << " bytes");
-+        fail();
-+        return false;
-+    }
-+}
-+
-+/// parseHttpHeadersFromDisk() helper
-+/// \copydoc parseHttpHeaders()
-+bool
-+store_client::tryParsingHttpHeaders()
-+{
-+    Assure(parsingBuffer.second);
-+    Assure(!copyInto.offset); // otherwise, parsingBuffer cannot have HTTP response headers
-+    auto &adjustableReply = entry->mem().baseReply();
-+    if (adjustableReply.parseTerminatedPrefix(parsingBuffer.first.c_str(), parsingBuffer.first.contentSize()))
-+        return true;
-+
-+    // TODO: Optimize by checking memory as well. For simplicity sake, we
-+    // continue on the disk-reading path, but readFromMemory() can give us the
-+    // missing header bytes immediately if a concurrent request put those bytes
-+    // into memory while we were waiting for our disk response.
-+    scheduleDiskRead();
-+    return false;
-+}
-+
-+/// skips HTTP header bytes previously loaded from disk
-+void
-+store_client::skipHttpHeadersFromDisk()
-+{
-+    const auto hdr_sz = entry->mem_obj->baseReply().hdr_sz;
-+    Assure(hdr_sz > 0); // all on-disk responses have HTTP headers
-+    if (Less(parsingBuffer.first.contentSize(), hdr_sz)) {
-+        debugs(90, 5, "discovered " << hdr_sz << "-byte HTTP headers in memory after reading some of them from disk: " << parsingBuffer.first);
-+        parsingBuffer.first.consume(parsingBuffer.first.contentSize()); // skip loaded HTTP header prefix
-+    } else {
-+        parsingBuffer.first.consume(hdr_sz); // skip loaded HTTP headers
-+        const auto httpBodyBytesAfterHeader = parsingBuffer.first.contentSize(); // may be zero
-+        Assure(httpBodyBytesAfterHeader <= copyInto.length);
-+        debugs(90, 5, "read HTTP body prefix: " << httpBodyBytesAfterHeader);
-+    }
-+}
-+
- void
- store_client::dumpStats(MemBuf * output, int clientNumber) const
- {
-@@ -864,8 +1059,8 @@ store_client::dumpStats(MemBuf * output, int clientNumber) const
-     if (flags.store_copying)
-         output->append(" store_copying", 14);
- 
--    if (flags.copy_event_pending)
--        output->append(" copy_event_pending", 19);
-+    if (_callback.notifier)
-+        output->append(" notifying", 10);
- 
-     output->append("\n",1);
- }
-@@ -873,12 +1068,19 @@ store_client::dumpStats(MemBuf * output, int clientNumber) const
- bool
- store_client::Callback::pending() const
- {
--    return callback_handler && callback_data;
-+    return callback_handler && !notifier;
- }
- 
- store_client::Callback::Callback(STCB *function, void *data) : callback_handler(function), callback_data (data) {}
- 
- #if USE_DELAY_POOLS
-+int
-+store_client::bytesWanted() const
-+{
-+    // TODO: To avoid using stale copyInto, return zero if !_callback.pending()?
-+    return delayId.bytesWanted(0, copyInto.length);
-+}
-+
- void
- store_client::setDelayId(DelayId delay_id)
- {
-diff --git a/src/store_swapin.cc b/src/store_swapin.cc
-index a05d7e3..cd32e94 100644
---- a/src/store_swapin.cc
-+++ b/src/store_swapin.cc
-@@ -56,7 +56,7 @@ storeSwapInFileClosed(void *data, int errflag, StoreIOState::Pointer)
- 
-     if (sc->_callback.pending()) {
-         assert (errflag <= 0);
--        sc->callback(0, errflag ? true : false);
-+        sc->noteSwapInDone(errflag);
-     }
- 
-     ++statCounter.swap.ins;
-diff --git a/src/tests/stub_HttpReply.cc b/src/tests/stub_HttpReply.cc
-index 8ca7f9e..5cde8e6 100644
---- a/src/tests/stub_HttpReply.cc
-+++ b/src/tests/stub_HttpReply.cc
-@@ -25,6 +25,7 @@ void httpBodyPackInto(const HttpBody *, Packable *) STUB
- bool HttpReply::sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *error) STUB_RETVAL(false)
- 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)
- bool HttpReply::parseFirstLine(const char *start, const char *end) STUB_RETVAL(false)
- void HttpReply::hdrCacheInit() STUB
- HttpReply * HttpReply::clone() const STUB_RETVAL(NULL)
-diff --git a/src/tests/stub_store_client.cc b/src/tests/stub_store_client.cc
-index 2a13874..debe24e 100644
---- a/src/tests/stub_store_client.cc
-+++ b/src/tests/stub_store_client.cc
-@@ -34,7 +34,12 @@ void storeLogOpen(void) STUB
- void storeDigestInit(void) STUB
- void storeRebuildStart(void) STUB
- void storeReplSetup(void) STUB
--bool store_client::memReaderHasLowerOffset(int64_t anOffset) const STUB_RETVAL(false)
- void store_client::dumpStats(MemBuf * output, int clientNumber) const STUB
- int store_client::getType() const STUB_RETVAL(0)
-+void store_client::noteSwapInDone(bool) STUB
-+#if USE_DELAY_POOLS
-+int store_client::bytesWanted() const STUB_RETVAL(0)
-+#endif
-+
-+
- 
-diff --git a/src/urn.cc b/src/urn.cc
-index 74453e1..6efdec1 100644
---- a/src/urn.cc
-+++ b/src/urn.cc
-@@ -26,8 +26,6 @@
- #include "tools.h"
- #include "urn.h"
- 
--#define URN_REQBUF_SZ   4096
--
- class UrnState : public StoreClient
- {
-     CBDATA_CLASS(UrnState);
-@@ -45,8 +43,8 @@ public:
-     HttpRequest::Pointer request;
-     HttpRequest::Pointer urlres_r;
- 
--    char reqbuf[URN_REQBUF_SZ] = { '\0' };
--    int reqofs = 0;
-+    /// for receiving a URN resolver reply body from Store and interpreting it
-+    Store::ParsingBuffer parsingBuffer;
- 
- private:
-     char *urlres;
-@@ -63,7 +61,7 @@ typedef struct {
- } url_entry;
- 
- static STCB urnHandleReply;
--static url_entry *urnParseReply(const char *inbuf, const HttpRequestMethod&);
-+static url_entry *urnParseReply(const SBuf &, const HttpRequestMethod &);
- static const char *const crlf = "\r\n";
- 
- CBDATA_CLASS_INIT(UrnState);
-@@ -183,13 +181,8 @@ UrnState::created(StoreEntry *newEntry)
-         sc = storeClientListAdd(urlres_e, this);
-     }
- 
--    reqofs = 0;
--    StoreIOBuffer tempBuffer;
--    tempBuffer.offset = reqofs;
--    tempBuffer.length = URN_REQBUF_SZ;
--    tempBuffer.data = reqbuf;
-     storeClientCopy(sc, urlres_e,
--                    tempBuffer,
-+                    parsingBuffer.makeInitialSpace(),
-                     urnHandleReply,
-                     this);
- }
-@@ -224,9 +217,6 @@ urnHandleReply(void *data, StoreIOBuffer result)
-     UrnState *urnState = static_cast(data);
-     StoreEntry *e = urnState->entry;
-     StoreEntry *urlres_e = urnState->urlres_e;
--    char *s = NULL;
--    size_t k;
--    HttpReply *rep;
-     url_entry *urls;
-     url_entry *u;
-     url_entry *min_u;
-@@ -234,10 +224,7 @@ urnHandleReply(void *data, StoreIOBuffer result)
-     ErrorState *err;
-     int i;
-     int urlcnt = 0;
--    char *buf = urnState->reqbuf;
--    StoreIOBuffer tempBuffer;
--
--    debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");
-+    debugs(52, 3, result << " with " << *e);
- 
-     if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
-         delete urnState;
-@@ -250,59 +237,39 @@ urnHandleReply(void *data, StoreIOBuffer result)
-         return;
-     }
- 
--    /* Update reqofs to point to where in the buffer we'd be */
--    urnState->reqofs += result.length;
--
--    /* Handle reqofs being bigger than normal */
--    if (urnState->reqofs >= URN_REQBUF_SZ) {
--        delete urnState;
--        return;
--    }
-+    urnState->parsingBuffer.appended(result.data, result.length);
- 
-     /* If we haven't received the entire object (urn), copy more */
--    if (urlres_e->store_status == STORE_PENDING) {
--        Must(result.length > 0); // zero length ought to imply STORE_OK
--        tempBuffer.offset = urnState->reqofs;
--        tempBuffer.length = URN_REQBUF_SZ - urnState->reqofs;
--        tempBuffer.data = urnState->reqbuf + urnState->reqofs;
-+    if (!urnState->sc->atEof()) {
-+        const auto bufferedBytes = urnState->parsingBuffer.contentSize();
-+        const auto remainingSpace = urnState->parsingBuffer.space().positionAt(bufferedBytes);
-+
-+        if (!remainingSpace.length) {
-+            debugs(52, 3, "ran out of buffer space after " << bufferedBytes << " bytes");
-+            // TODO: Here and in other error cases, send ERR_URN_RESOLVE to client.
-+            delete urnState;
-+            return;
-+        }
-+
-         storeClientCopy(urnState->sc, urlres_e,
--                        tempBuffer,
-+                        remainingSpace,
-                         urnHandleReply,
-                         urnState);
-         return;
-     }
- 
--    /* we know its STORE_OK */
--    k = headersEnd(buf, urnState->reqofs);
--
--    if (0 == k) {
--        debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url()  );
--        delete urnState;
--        return;
--    }
--
--    s = buf + k;
--    assert(urlres_e->getReply());
--    rep = new HttpReply;
--    rep->parseCharBuf(buf, k);
--    debugs(52, 3, "reply exists, code=" << rep->sline.status() << ".");
--
--    if (rep->sline.status() != Http::scOkay) {
-+    const auto &peerReply = urlres_e->mem().baseReply();
-+    debugs(52, 3, "got reply, code=" << peerReply.sline.status());
-+    if (peerReply.sline.status() != Http::scOkay) {
-         debugs(52, 3, "urnHandleReply: failed.");
-         err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw());
-         err->url = xstrdup(e->url());
-         errorAppendEntry(e, err);
--        delete rep;
-         delete urnState;
-         return;
-     }
- 
--    delete rep;
--
--    while (xisspace(*s))
--        ++s;
--
--    urls = urnParseReply(s, urnState->request->method);
-+    urls = urnParseReply(urnState->parsingBuffer.toSBuf(), urnState->request->method);
- 
-     if (!urls) {     /* unknown URN error */
-         debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
-@@ -350,7 +317,7 @@ urnHandleReply(void *data, StoreIOBuffer result)
-         "Generated by %s@%s\n"
-         "\n",
-         APP_FULLNAME, getMyHostname());
--    rep = new HttpReply;
-+    const auto rep = new HttpReply;
-     rep->setHeaders(Http::scFound, NULL, "text/html", mb->contentSize(), 0, squid_curtime);
- 
-     if (min_u) {
-@@ -372,9 +339,8 @@ urnHandleReply(void *data, StoreIOBuffer result)
- }
- 
- static url_entry *
--urnParseReply(const char *inbuf, const HttpRequestMethod& m)
-+urnParseReply(const SBuf &inBuf, const HttpRequestMethod &m)
- {
--    char *buf = xstrdup(inbuf);
-     char *token;
-     url_entry *list;
-     url_entry *old;
-@@ -383,6 +349,13 @@ urnParseReply(const char *inbuf, const HttpRequestMethod& m)
-     debugs(52, 3, "urnParseReply");
-     list = (url_entry *)xcalloc(n + 1, sizeof(*list));
- 
-+    // XXX: Switch to tokenizer-based parsing.
-+    const auto allocated = SBufToCstring(inBuf);
-+
-+    auto buf = allocated;
-+    while (xisspace(*buf))
-+        ++buf;
-+
-     for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) {
-         debugs(52, 3, "urnParseReply: got '" << token << "'");
- 
-@@ -418,7 +391,7 @@ urnParseReply(const char *inbuf, const HttpRequestMethod& m)
-     }
- 
-     debugs(52, 3, "urnParseReply: Found " << i << " URLs");
--    xfree(buf);
-+    xfree(allocated);
-     return list;
- }
- 
diff --git a/SOURCES/squid-4.15-CVE-2024-23638.patch b/SOURCES/squid-4.15-CVE-2024-23638.patch
deleted file mode 100644
index 51fc1f6..0000000
--- a/SOURCES/squid-4.15-CVE-2024-23638.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-commit 8fcff9c09824b18628f010d26a04247f6a6cbcb8
-Author: Alex Rousskov 
-Date:   Sun Nov 12 09:33:20 2023 +0000
-
-    Do not update StoreEntry expiration after errorAppendEntry() (#1580)
-    
-    errorAppendEntry() is responsible for setting entry expiration times,
-    which it does by calling StoreEntry::storeErrorResponse() that calls
-    StoreEntry::negativeCache().
-    
-    This change was triggered by a vulnerability report by Joshua Rogers at
-    https://megamansec.github.io/Squid-Security-Audit/cache-uaf.html where
-    it was filed as "Use-After-Free in Cache Manager Errors". The reported
-    "use after free" vulnerability was unknowingly addressed by 2022 commit
-    1fa761a that removed excessively long "reentrant" store_client calls
-    responsible for the disappearance of the properly locked StoreEntry in
-    this (and probably other) contexts.
-
-
-diff --git a/src/cache_manager.cc b/src/cache_manager.cc
-index 8055ece..fdcc9cf 100644
---- a/src/cache_manager.cc
-+++ b/src/cache_manager.cc
-@@ -323,7 +323,6 @@ CacheManager::Start(const Comm::ConnectionPointer &client, HttpRequest * request
-         const auto err = new ErrorState(ERR_INVALID_URL, Http::scNotFound, request);
-         err->url = xstrdup(entry->url());
-         errorAppendEntry(entry, err);
--        entry->expires = squid_curtime;
-         return;
-     }
- 
diff --git a/SOURCES/squid-4.15-CVE-2024-25111.patch b/SOURCES/squid-4.15-CVE-2024-25111.patch
deleted file mode 100644
index e8ea010..0000000
--- a/SOURCES/squid-4.15-CVE-2024-25111.patch
+++ /dev/null
@@ -1,193 +0,0 @@
-diff --git a/src/http.cc b/src/http.cc
-index b006300..023e411 100644
---- a/src/http.cc
-+++ b/src/http.cc
-@@ -52,6 +52,7 @@
- #include "rfc1738.h"
- #include "SquidConfig.h"
- #include "SquidTime.h"
-+#include "SquidMath.h"
- #include "StatCounters.h"
- #include "Store.h"
- #include "StrList.h"
-@@ -1150,18 +1151,26 @@ HttpStateData::readReply(const CommIoCbParams &io)
-      * Plus, it breaks our lame *HalfClosed() detection
-      */
- 
--    Must(maybeMakeSpaceAvailable(true));
--    CommIoCbParams rd(this); // will be expanded with ReadNow results
--    rd.conn = io.conn;
--    rd.size = entry->bytesWanted(Range(0, inBuf.spaceSize()));
-+    size_t moreDataPermission = 0;
-+    if ((!canBufferMoreReplyBytes(&moreDataPermission) || !moreDataPermission)) {
-+        abortTransaction("ready to read required data, but the read buffer is full and cannot be drained");
-+        return;
-+    }
-+
-+    const auto readSizeMax = maybeMakeSpaceAvailable(moreDataPermission);
-+    // TODO: Move this logic inside maybeMakeSpaceAvailable():
-+    const auto readSizeWanted = readSizeMax ? entry->bytesWanted(Range(0, readSizeMax)) : 0;
- 
--    if (rd.size <= 0) {
-+    if (readSizeWanted <= 0) {
-         assert(entry->mem_obj);
-         AsyncCall::Pointer nilCall;
-         entry->mem_obj->delayRead(DeferredRead(readDelayed, this, CommRead(io.conn, NULL, 0, nilCall)));
-         return;
-     }
- 
-+    CommIoCbParams rd(this); // will be expanded with ReadNow results
-+    rd.conn = io.conn;
-+    rd.size = readSizeWanted;
-     switch (Comm::ReadNow(rd, inBuf)) {
-     case Comm::INPROGRESS:
-         if (inBuf.isEmpty())
-@@ -1520,8 +1529,11 @@ HttpStateData::maybeReadVirginBody()
-     if (!Comm::IsConnOpen(serverConnection) || fd_table[serverConnection->fd].closing())
-         return;
- 
--    if (!maybeMakeSpaceAvailable(false))
-+    size_t moreDataPermission = 0;
-+    if ((!canBufferMoreReplyBytes(&moreDataPermission)) || !moreDataPermission) {
-+        abortTransaction("more response bytes required, but the read buffer is full and cannot be drained");
-         return;
-+    }
- 
-     // XXX: get rid of the do_next_read flag
-     // check for the proper reasons preventing read(2)
-@@ -1539,40 +1551,79 @@ HttpStateData::maybeReadVirginBody()
-     Comm::Read(serverConnection, call);
- }
- 
-+/// Desired inBuf capacity based on various capacity preferences/limits:
-+/// * a smaller buffer may not hold enough for look-ahead header/body parsers;
-+/// * a smaller buffer may result in inefficient tiny network reads;
-+/// * a bigger buffer may waste memory;
-+/// * a bigger buffer may exceed SBuf storage capabilities (SBuf::maxSize);
-+size_t
-+HttpStateData::calcReadBufferCapacityLimit() const
-+{
-+    if (!flags.headers_parsed)
-+        return Config.maxReplyHeaderSize;
-+
-+    // XXX: Our inBuf is not used to maintain the read-ahead gap, and using
-+    // Config.readAheadGap like this creates huge read buffers for large
-+    // read_ahead_gap values. TODO: Switch to using tcp_recv_bufsize as the
-+    // primary read buffer capacity factor.
-+    //
-+    // TODO: Cannot reuse throwing NaturalCast() here. Consider removing
-+    // .value() dereference in NaturalCast() or add/use NaturalCastOrMax().
-+    const auto configurationPreferences = NaturalSum(Config.readAheadGap).second ? NaturalSum(Config.readAheadGap).first : SBuf::maxSize;
-+
-+    // TODO: Honor TeChunkedParser look-ahead and trailer parsing requirements
-+    // (when explicit configurationPreferences are set too low).
-+
-+    return std::min(configurationPreferences, SBuf::maxSize);
-+}
-+
-+/// The maximum number of virgin reply bytes we may buffer before we violate
-+/// the currently configured response buffering limits.
-+/// \retval std::nullopt means that no more virgin response bytes can be read
-+/// \retval 0 means that more virgin response bytes may be read later
-+/// \retval >0 is the number of bytes that can be read now (subject to other constraints)
- bool
--HttpStateData::maybeMakeSpaceAvailable(bool doGrow)
-+HttpStateData::canBufferMoreReplyBytes(size_t *maxReadSize) const
- {
--    // how much we are allowed to buffer
--    const int limitBuffer = (flags.headers_parsed ? Config.readAheadGap : Config.maxReplyHeaderSize);
--
--    if (limitBuffer < 0 || inBuf.length() >= (SBuf::size_type)limitBuffer) {
--        // when buffer is at or over limit already
--        debugs(11, 7, "will not read up to " << limitBuffer << ". buffer has (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
--        debugs(11, DBG_DATA, "buffer has {" << inBuf << "}");
--        // Process next response from buffer
--        processReply();
--        return false;
-+#if USE_ADAPTATION
-+    // If we do not check this now, we may say the final "no" prematurely below
-+    // because inBuf.length() will decrease as adaptation drains buffered bytes.
-+    if (responseBodyBuffer) {
-+        debugs(11, 3, "yes, but waiting for adaptation to drain read buffer");
-+        *maxReadSize = 0; // yes, we may be able to buffer more (but later)
-+        return true;
-+    }
-+#endif
-+
-+    const auto maxCapacity = calcReadBufferCapacityLimit();
-+    if (inBuf.length() >= maxCapacity) {
-+        debugs(11, 3, "no, due to a full buffer: " << inBuf.length() << '/' << inBuf.spaceSize() << "; limit: " << maxCapacity);
-+        return false; // no, configuration prohibits buffering more
-     }
- 
-+    *maxReadSize = (maxCapacity - inBuf.length()); // positive
-+    debugs(11, 7, "yes, may read up to " << *maxReadSize << " into " << inBuf.length() << '/' << inBuf.spaceSize());
-+    return true; // yes, can read up to this many bytes (subject to other constraints)
-+}
-+
-+/// prepare read buffer for reading
-+/// \return the maximum number of bytes the caller should attempt to read
-+/// \retval 0 means that the caller should delay reading
-+size_t
-+HttpStateData::maybeMakeSpaceAvailable(const size_t maxReadSize)
-+{
-     // how much we want to read
--    const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), (limitBuffer - inBuf.length()));
-+    const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), maxReadSize);
- 
--    if (!read_size) {
-+    if (read_size < 2) {
-         debugs(11, 7, "will not read up to " << read_size << " into buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
--        return false;
-+        return 0;
-     }
- 
--    // just report whether we could grow or not, do not actually do it
--    if (doGrow)
--        return (read_size >= 2);
--
-     // we may need to grow the buffer
-     inBuf.reserveSpace(read_size);
--    debugs(11, 8, (!flags.do_next_read ? "will not" : "may") <<
--           " read up to " << read_size << " bytes info buf(" << inBuf.length() << "/" << inBuf.spaceSize() <<
--           ") from " << serverConnection);
--
--    return (inBuf.spaceSize() >= 2); // only read if there is 1+ bytes of space available
-+    debugs(11, 7, "may read up to " << read_size << " bytes info buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
-+    return read_size;
- }
- 
- /// called after writing the very last request byte (body, last-chunk, etc)
-diff --git a/src/http.h b/src/http.h
-index 8965b77..007d2e6 100644
---- a/src/http.h
-+++ b/src/http.h
-@@ -15,6 +15,8 @@
- #include "http/StateFlags.h"
- #include "sbuf/SBuf.h"
- 
-+#include 
-+
- class FwdState;
- class HttpHeader;
- 
-@@ -107,16 +109,9 @@ private:
- 
-     void abortTransaction(const char *reason) { abortAll(reason); } // abnormal termination
- 
--    /**
--     * determine if read buffer can have space made available
--     * for a read.
--     *
--     * \param grow  whether to actually expand the buffer
--     *
--     * \return whether the buffer can be grown to provide space
--     *         regardless of whether the grow actually happened.
--     */
--    bool maybeMakeSpaceAvailable(bool grow);
-+    size_t calcReadBufferCapacityLimit() const;
-+    bool canBufferMoreReplyBytes(size_t *maxReadSize) const;
-+    size_t maybeMakeSpaceAvailable(size_t maxReadSize);
- 
-     // consuming request body
-     virtual void handleMoreRequestBodyAvailable();
diff --git a/SOURCES/squid-4.15-CVE-2024-25617.patch b/SOURCES/squid-4.15-CVE-2024-25617.patch
deleted file mode 100644
index 86e391a..0000000
--- a/SOURCES/squid-4.15-CVE-2024-25617.patch
+++ /dev/null
@@ -1,105 +0,0 @@
-diff --git a/src/SquidString.h b/src/SquidString.h
-index a791885..b9aef38 100644
---- a/src/SquidString.h
-+++ b/src/SquidString.h
-@@ -114,7 +114,16 @@ private:
- 
-     size_type len_;  /* current length  */
- 
--    static const size_type SizeMax_ = 65535; ///< 64K limit protects some fixed-size buffers
-+    /// An earlier 64KB limit was meant to protect some fixed-size buffers, but
-+    /// (a) we do not know where those buffers are (or whether they still exist)
-+    /// (b) too many String users unknowingly exceeded that limit and asserted.
-+    /// We are now using a larger limit to reduce the number of (b) cases,
-+    /// especially cases where "compact" lists of items grow 50% in size when we
-+    /// convert them to canonical form. The new limit is selected to withstand
-+    /// concatenation and ~50% expansion of two HTTP headers limited by default
-+    /// request_header_max_size and reply_header_max_size settings.
-+    static const size_type SizeMax_ = 3*64*1024 - 1;
-+
-     /// returns true after increasing the first argument by extra if the sum does not exceed SizeMax_
-     static bool SafeAdd(size_type &base, size_type extra) { if (extra <= SizeMax_ && base <= SizeMax_ - extra) { base += extra; return true; } return false; }
- 
-diff --git a/src/cache_cf.cc b/src/cache_cf.cc
-index a9c1b7e..46f07bb 100644
---- a/src/cache_cf.cc
-+++ b/src/cache_cf.cc
-@@ -935,6 +935,18 @@ configDoConfigure(void)
-                (uint32_t)Config.maxRequestBufferSize, (uint32_t)Config.maxRequestHeaderSize);
-     }
- 
-+    // Warn about the dangers of exceeding String limits when manipulating HTTP
-+    // headers. Technically, we do not concatenate _requests_, so we could relax
-+    // their check, but we keep the two checks the same for simplicity sake.
-+    const auto safeRawHeaderValueSizeMax = (String::SizeMaxXXX()+1)/3;
-+    // TODO: static_assert(safeRawHeaderValueSizeMax >= 64*1024); // no WARNINGs for default settings
-+    if (Config.maxRequestHeaderSize > safeRawHeaderValueSizeMax)
-+        debugs(3, DBG_CRITICAL, "WARNING: Increasing request_header_max_size beyond " << safeRawHeaderValueSizeMax <<
-+               " bytes makes Squid more vulnerable to denial-of-service attacks; configured value: " << Config.maxRequestHeaderSize << " bytes");
-+    if (Config.maxReplyHeaderSize > safeRawHeaderValueSizeMax)
-+        debugs(3, DBG_CRITICAL, "WARNING: Increasing reply_header_max_size beyond " << safeRawHeaderValueSizeMax <<
-+               " bytes makes Squid more vulnerable to denial-of-service attacks; configured value: " << Config.maxReplyHeaderSize << " bytes");
-+
-     /*
-      * Disable client side request pipelining if client_persistent_connections OFF.
-      * Waste of resources queueing any pipelined requests when the first will close the connection.
-diff --git a/src/cf.data.pre b/src/cf.data.pre
-index bc2ddcd..d55b870 100644
---- a/src/cf.data.pre
-+++ b/src/cf.data.pre
-@@ -6196,11 +6196,14 @@ TYPE: b_size_t
- DEFAULT: 64 KB
- LOC: Config.maxRequestHeaderSize
- DOC_START
--	This specifies the maximum size for HTTP headers in a request.
--	Request headers are usually relatively small (about 512 bytes).
--	Placing a limit on the request header size will catch certain
--	bugs (for example with persistent connections) and possibly
--	buffer-overflow or denial-of-service attacks.
-+	This directives limits the header size of a received HTTP request
-+	(including request-line). Increasing this limit beyond its 64 KB default
-+	exposes certain old Squid code to various denial-of-service attacks. This
-+	limit also applies to received FTP commands.
-+
-+	This limit has no direct affect on Squid memory consumption.
-+
-+	Squid does not check this limit when sending requests.
- DOC_END
- 
- NAME: reply_header_max_size
-@@ -6209,11 +6212,14 @@ TYPE: b_size_t
- DEFAULT: 64 KB
- LOC: Config.maxReplyHeaderSize
- DOC_START
--	This specifies the maximum size for HTTP headers in a reply.
--	Reply headers are usually relatively small (about 512 bytes).
--	Placing a limit on the reply header size will catch certain
--	bugs (for example with persistent connections) and possibly
--	buffer-overflow or denial-of-service attacks.
-+	This directives limits the header size of a received HTTP response
-+	(including status-line). Increasing this limit beyond its 64 KB default
-+	exposes certain old Squid code to various denial-of-service attacks. This
-+	limit also applies to FTP command responses.
-+
-+	Squid also checks this limit when loading hit responses from disk cache.
-+
-+	Squid does not check this limit when sending responses.
- DOC_END
- 
- NAME: request_body_max_size
-diff --git a/src/http.cc b/src/http.cc
-index 877172d..b006300 100644
---- a/src/http.cc
-+++ b/src/http.cc
-@@ -1820,8 +1820,9 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request,
- 
-         String strFwd = hdr_in->getList(Http::HdrType::X_FORWARDED_FOR);
- 
--        // if we cannot double strFwd size, then it grew past 50% of the limit
--        if (!strFwd.canGrowBy(strFwd.size())) {
-+        // Detect unreasonably long header values. And paranoidly check String
-+        // limits: a String ought to accommodate two reasonable-length values.
-+        if (strFwd.size() > 32*1024 || !strFwd.canGrowBy(strFwd.size())) {
-             // There is probably a forwarding loop with Via detection disabled.
-             // If we do nothing, String will assert on overflow soon.
-             // TODO: Terminate all transactions with huge XFF?
diff --git a/SOURCES/squid-4.15-ftp-filename-extraction.patch b/SOURCES/squid-4.15-ftp-filename-extraction.patch
deleted file mode 100644
index cf1aeb3..0000000
--- a/SOURCES/squid-4.15-ftp-filename-extraction.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-diff --git a/src/clients/FtpGateway.cc b/src/clients/FtpGateway.cc
-index da9867f..e992638 100644
---- a/src/clients/FtpGateway.cc
-+++ b/src/clients/FtpGateway.cc
-@@ -1084,16 +1084,17 @@ Ftp::Gateway::checkAuth(const HttpHeader * req_hdr)
- void
- Ftp::Gateway::checkUrlpath()
- {
--    static SBuf str_type_eq("type=");
--    auto t = request->url.path().rfind(';');
--
--    if (t != SBuf::npos) {
--        auto filenameEnd = t-1;
--        if (request->url.path().substr(++t).cmp(str_type_eq, str_type_eq.length()) == 0) {
--            t += str_type_eq.length();
--            typecode = (char)xtoupper(request->url.path()[t]);
--            request->url.path(request->url.path().substr(0,filenameEnd));
--        }
-+    // If typecode was specified, extract it and leave just the filename in
-+    // url.path. Tolerate trailing garbage or missing typecode value. Roughly:
-+    // [filename] ;type=[typecode char] [trailing garbage]
-+    static const SBuf middle(";type=");
-+    const auto typeSpecStart = request->url.path().find(middle);
-+    if (typeSpecStart != SBuf::npos) {
-+        const auto fullPath = request->url.path();
-+        const auto typecodePos = typeSpecStart + middle.length();
-+        typecode = (typecodePos < fullPath.length()) ?
-+            static_cast(xtoupper(fullPath[typecodePos])) : '\0';
-+        request->url.path(fullPath.substr(0, typeSpecStart));
-     }
- 
-     int l = request->url.path().length();
diff --git a/SOURCES/squid-4.15-ignore-wsp-after-chunk-size.patch b/SOURCES/squid-4.15-ignore-wsp-after-chunk-size.patch
deleted file mode 100644
index ea4025f..0000000
--- a/SOURCES/squid-4.15-ignore-wsp-after-chunk-size.patch
+++ /dev/null
@@ -1,367 +0,0 @@
-From 8d0ee420a4d91ac7fd97316338f1e28b4b060cbf Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Lubo=C5=A1=20Uhliarik?= 
-Date: Thu, 10 Oct 2024 19:26:27 +0200
-Subject: [PATCH 1/6] Ignore whitespace chars after chunk-size
-
-Previously (before #1498 change), squid was accepting TE-chunked replies
-with whitespaces after chunk-size and missing chunk-ext data. After
-
-It turned out that replies with such whitespace chars are pretty
-common and other webservers which can act as forward proxies (e.g.
-nginx, httpd...) are accepting them.
-
-This change will allow to proxy chunked responses from origin server,
-which had whitespaces inbetween chunk-size and CRLF.
----
- src/http/one/TeChunkedParser.cc | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/http/one/TeChunkedParser.cc b/src/http/one/TeChunkedParser.cc
-index 9cce10fdc91..04753395e16 100644
---- a/src/http/one/TeChunkedParser.cc
-+++ b/src/http/one/TeChunkedParser.cc
-@@ -125,6 +125,7 @@ Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
-     // Code becomes much simpler when incremental parsing functions throw on
-     // bad or insufficient input, like in the code below. TODO: Expand up.
-     try {
-+        tok.skipAll(CharacterSet::WSP); // Some servers send SP/TAB after chunk-size
-         parseChunkExtensions(tok); // a possibly empty chunk-ext list
-         tok.skipRequired("CRLF after [chunk-ext]", Http1::CrLf());
-         buf_ = tok.remaining();
-
-From 9c8d35f899035fa06021ab3fe6919f892c2f0c6b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Lubo=C5=A1=20Uhliarik?= 
-Date: Fri, 11 Oct 2024 02:06:31 +0200
-Subject: [PATCH 2/6] Added new argument to Http::One::ParseBws()
-
-Depending on new wsp_only argument in ParseBws() it will be decided
-which set of whitespaces characters will be parsed. If wsp_only is set
-to true, only SP and HTAB chars will be parsed.
-
-Also optimized number of ParseBws calls.
----
- src/http/one/Parser.cc          |  4 ++--
- src/http/one/Parser.h           |  3 ++-
- src/http/one/TeChunkedParser.cc | 13 +++++++++----
- src/http/one/TeChunkedParser.h  |  2 +-
- 4 files changed, 14 insertions(+), 8 deletions(-)
-
-diff --git a/src/http/one/Parser.cc b/src/http/one/Parser.cc
-index b1908316a0b..01d7e3bc0e8 100644
---- a/src/http/one/Parser.cc
-+++ b/src/http/one/Parser.cc
-@@ -273,9 +273,9 @@ Http::One::ErrorLevel()
- 
- // BWS = *( SP / HTAB ) ; WhitespaceCharacters() may relax this RFC 7230 rule
- void
--Http::One::ParseBws(Parser::Tokenizer &tok)
-+Http::One::ParseBws(Parser::Tokenizer &tok, const bool wsp_only)
- {
--    const auto count = tok.skipAll(Parser::WhitespaceCharacters());
-+    const auto count = tok.skipAll(wsp_only ? CharacterSet::WSP : Parser::WhitespaceCharacters());
- 
-     if (tok.atEnd())
-         throw InsufficientInput(); // even if count is positive
-diff --git a/src/http/one/Parser.h b/src/http/one/Parser.h
-index d9a0ac8c273..08200371cd6 100644
---- a/src/http/one/Parser.h
-+++ b/src/http/one/Parser.h
-@@ -163,8 +163,9 @@ class Parser : public RefCountable
- };
- 
- /// skips and, if needed, warns about RFC 7230 BWS ("bad" whitespace)
-+/// \param wsp_only force skipping of whitespaces only, don't consider skipping relaxed delimeter chars
- /// \throws InsufficientInput when the end of BWS cannot be confirmed
--void ParseBws(Parser::Tokenizer &);
-+void ParseBws(Parser::Tokenizer &, const bool wsp_only = false);
- 
- /// the right debugs() level for logging HTTP violation messages
- int ErrorLevel();
-diff --git a/src/http/one/TeChunkedParser.cc b/src/http/one/TeChunkedParser.cc
-index 04753395e16..41e1e5ddaea 100644
---- a/src/http/one/TeChunkedParser.cc
-+++ b/src/http/one/TeChunkedParser.cc
-@@ -125,8 +125,11 @@ Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
-     // Code becomes much simpler when incremental parsing functions throw on
-     // bad or insufficient input, like in the code below. TODO: Expand up.
-     try {
--        tok.skipAll(CharacterSet::WSP); // Some servers send SP/TAB after chunk-size
--        parseChunkExtensions(tok); // a possibly empty chunk-ext list
-+        // A possibly empty chunk-ext list. If no chunk-ext has been found,
-+        // try to skip trailing BWS, because some servers send "chunk-size BWS CRLF".
-+        if (!parseChunkExtensions(tok))
-+            ParseBws(tok, true);
-+
-         tok.skipRequired("CRLF after [chunk-ext]", Http1::CrLf());
-         buf_ = tok.remaining();
-         parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME;
-@@ -140,20 +143,22 @@ Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
- 
- /// Parses the chunk-ext list (RFC 9112 section 7.1.1:
- /// chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
--void
-+bool
- Http::One::TeChunkedParser::parseChunkExtensions(Tokenizer &callerTok)
- {
-+    bool foundChunkExt = false;
-     do {
-         auto tok = callerTok;
- 
-         ParseBws(tok); // Bug 4492: IBM_HTTP_Server sends SP after chunk-size
- 
-         if (!tok.skip(';'))
--            return; // reached the end of extensions (if any)
-+            return foundChunkExt; // reached the end of extensions (if any)
- 
-         parseOneChunkExtension(tok);
-         buf_ = tok.remaining(); // got one extension
-         callerTok = tok;
-+        foundChunkExt = true;
-     } while (true);
- }
- 
-diff --git a/src/http/one/TeChunkedParser.h b/src/http/one/TeChunkedParser.h
-index 02eacd1bb89..8c5d4bb4cba 100644
---- a/src/http/one/TeChunkedParser.h
-+++ b/src/http/one/TeChunkedParser.h
-@@ -71,7 +71,7 @@ class TeChunkedParser : public Http1::Parser
- private:
-     bool parseChunkSize(Tokenizer &tok);
-     bool parseChunkMetadataSuffix(Tokenizer &);
--    void parseChunkExtensions(Tokenizer &);
-+    bool parseChunkExtensions(Tokenizer &);
-     void parseOneChunkExtension(Tokenizer &);
-     bool parseChunkBody(Tokenizer &tok);
-     bool parseChunkEnd(Tokenizer &tok);
-
-From 81e67f97f9c386bdd0bb4a5e182395c46adb70ad Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Lubo=C5=A1=20Uhliarik?= 
-Date: Fri, 11 Oct 2024 02:44:33 +0200
-Subject: [PATCH 3/6] Fix typo in Parser.h
-
----
- src/http/one/Parser.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/http/one/Parser.h b/src/http/one/Parser.h
-index 08200371cd6..3ef4c5f7752 100644
---- a/src/http/one/Parser.h
-+++ b/src/http/one/Parser.h
-@@ -163,7 +163,7 @@ class Parser : public RefCountable
- };
- 
- /// skips and, if needed, warns about RFC 7230 BWS ("bad" whitespace)
--/// \param wsp_only force skipping of whitespaces only, don't consider skipping relaxed delimeter chars
-+/// \param wsp_only force skipping of whitespaces only, don't consider skipping relaxed delimiter chars
- /// \throws InsufficientInput when the end of BWS cannot be confirmed
- void ParseBws(Parser::Tokenizer &, const bool wsp_only = false);
- 
-
-From a0d4fe1794e605f8299a5c118c758a807453f016 Mon Sep 17 00:00:00 2001
-From: Alex Rousskov 
-Date: Thu, 10 Oct 2024 22:39:42 -0400
-Subject: [PATCH 4/6] Bug 5449 is a regression of Bug 4492!
-
-Both bugs deal with "chunk-size SP+ CRLF" use cases. Bug 4492 had _two_
-spaces after chunk-size, which answers one of the PR review questions:
-Should we skip just one space? No, we should not.
-
-The lines moved around in many commits, but I believe this regression
-was introduced in commit 951013d0 because that commit stopped consuming
-partially parsed chunk-ext sequences. That consumption was wrong, but it
-had a positive side effect -- fixing Bug 4492...
----
- src/http/one/TeChunkedParser.cc | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/src/http/one/TeChunkedParser.cc b/src/http/one/TeChunkedParser.cc
-index 41e1e5ddaea..aa4a840fdcf 100644
---- a/src/http/one/TeChunkedParser.cc
-+++ b/src/http/one/TeChunkedParser.cc
-@@ -125,10 +125,10 @@ Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
-     // Code becomes much simpler when incremental parsing functions throw on
-     // bad or insufficient input, like in the code below. TODO: Expand up.
-     try {
--        // A possibly empty chunk-ext list. If no chunk-ext has been found,
--        // try to skip trailing BWS, because some servers send "chunk-size BWS CRLF".
--        if (!parseChunkExtensions(tok))
--            ParseBws(tok, true);
-+        // Bug 4492: IBM_HTTP_Server sends SP after chunk-size
-+        ParseBws(tok, true);
-+
-+        parseChunkExtensions(tok);
- 
-         tok.skipRequired("CRLF after [chunk-ext]", Http1::CrLf());
-         buf_ = tok.remaining();
-@@ -150,7 +150,7 @@ Http::One::TeChunkedParser::parseChunkExtensions(Tokenizer &callerTok)
-     do {
-         auto tok = callerTok;
- 
--        ParseBws(tok); // Bug 4492: IBM_HTTP_Server sends SP after chunk-size
-+        ParseBws(tok);
- 
-         if (!tok.skip(';'))
-             return foundChunkExt; // reached the end of extensions (if any)
-
-From f837f5ff61301a17008f16ce1fb793c2abf19786 Mon Sep 17 00:00:00 2001
-From: Alex Rousskov 
-Date: Thu, 10 Oct 2024 23:06:42 -0400
-Subject: [PATCH 5/6] fixup: Fewer conditionals/ifs and more explicit spelling
-
-... to draw code reader attention when something unusual is going on.
----
- src/http/one/Parser.cc          | 22 ++++++++++++++++++----
- src/http/one/Parser.h           | 10 ++++++++--
- src/http/one/TeChunkedParser.cc | 14 ++++++--------
- src/http/one/TeChunkedParser.h  |  2 +-
- 4 files changed, 33 insertions(+), 15 deletions(-)
-
-diff --git a/src/http/one/Parser.cc b/src/http/one/Parser.cc
-index 01d7e3bc0e8..d3937e5e96b 100644
---- a/src/http/one/Parser.cc
-+++ b/src/http/one/Parser.cc
-@@ -271,11 +271,12 @@ Http::One::ErrorLevel()
-     return Config.onoff.relaxed_header_parser < 0 ? DBG_IMPORTANT : 5;
- }
- 
--// BWS = *( SP / HTAB ) ; WhitespaceCharacters() may relax this RFC 7230 rule
--void
--Http::One::ParseBws(Parser::Tokenizer &tok, const bool wsp_only)
-+/// common part of ParseBws() and ParseStrctBws()
-+namespace Http::One {
-+static void
-+ParseBws_(Parser::Tokenizer &tok, const CharacterSet &bwsChars)
- {
--    const auto count = tok.skipAll(wsp_only ? CharacterSet::WSP : Parser::WhitespaceCharacters());
-+    const auto count = tok.skipAll(bwsChars);
- 
-     if (tok.atEnd())
-         throw InsufficientInput(); // even if count is positive
-@@ -290,4 +291,17 @@ Http::One::ParseBws(Parser::Tokenizer &tok, const bool wsp_only)
- 
-     // success: no more BWS characters expected
- }
-+} // namespace Http::One
-+
-+void
-+Http::One::ParseBws(Parser::Tokenizer &tok)
-+{
-+    ParseBws_(tok, CharacterSet::WSP);
-+}
-+
-+void
-+Http::One::ParseStrictBws(Parser::Tokenizer &tok)
-+{
-+    ParseBws_(tok, Parser::WhitespaceCharacters());
-+}
- 
-diff --git a/src/http/one/Parser.h b/src/http/one/Parser.h
-index 3ef4c5f7752..49e399de546 100644
---- a/src/http/one/Parser.h
-+++ b/src/http/one/Parser.h
-@@ -163,9 +163,15 @@ class Parser : public RefCountable
- };
- 
- /// skips and, if needed, warns about RFC 7230 BWS ("bad" whitespace)
--/// \param wsp_only force skipping of whitespaces only, don't consider skipping relaxed delimiter chars
- /// \throws InsufficientInput when the end of BWS cannot be confirmed
--void ParseBws(Parser::Tokenizer &, const bool wsp_only = false);
-+/// \sa WhitespaceCharacters() for the definition of BWS characters
-+/// \sa ParseStrictBws() that avoids WhitespaceCharacters() uncertainties
-+void ParseBws(Parser::Tokenizer &);
-+
-+/// Like ParseBws() but only skips CharacterSet::WSP characters. This variation
-+/// must be used if the next element may start with CR or any other character
-+/// from RelaxedDelimiterCharacters().
-+void ParseStrictBws(Parser::Tokenizer &);
- 
- /// the right debugs() level for logging HTTP violation messages
- int ErrorLevel();
-diff --git a/src/http/one/TeChunkedParser.cc b/src/http/one/TeChunkedParser.cc
-index aa4a840fdcf..859471b8c77 100644
---- a/src/http/one/TeChunkedParser.cc
-+++ b/src/http/one/TeChunkedParser.cc
-@@ -125,11 +125,11 @@ Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
-     // Code becomes much simpler when incremental parsing functions throw on
-     // bad or insufficient input, like in the code below. TODO: Expand up.
-     try {
--        // Bug 4492: IBM_HTTP_Server sends SP after chunk-size
--        ParseBws(tok, true);
--
--        parseChunkExtensions(tok);
-+        // Bug 4492: IBM_HTTP_Server sends SP after chunk-size.
-+        // No ParseBws() here because it may consume CR required further below.
-+        ParseStrictBws(tok);
- 
-+        parseChunkExtensions(tok); // a possibly empty chunk-ext list
-         tok.skipRequired("CRLF after [chunk-ext]", Http1::CrLf());
-         buf_ = tok.remaining();
-         parsingStage_ = theChunkSize ? Http1::HTTP_PARSE_CHUNK : Http1::HTTP_PARSE_MIME;
-@@ -143,22 +143,20 @@ Http::One::TeChunkedParser::parseChunkMetadataSuffix(Tokenizer &tok)
- 
- /// Parses the chunk-ext list (RFC 9112 section 7.1.1:
- /// chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
--bool
-+void
- Http::One::TeChunkedParser::parseChunkExtensions(Tokenizer &callerTok)
- {
--    bool foundChunkExt = false;
-     do {
-         auto tok = callerTok;
- 
-         ParseBws(tok);
- 
-         if (!tok.skip(';'))
--            return foundChunkExt; // reached the end of extensions (if any)
-+            return; // reached the end of extensions (if any)
- 
-         parseOneChunkExtension(tok);
-         buf_ = tok.remaining(); // got one extension
-         callerTok = tok;
--        foundChunkExt = true;
-     } while (true);
- }
- 
-diff --git a/src/http/one/TeChunkedParser.h b/src/http/one/TeChunkedParser.h
-index 8c5d4bb4cba..02eacd1bb89 100644
---- a/src/http/one/TeChunkedParser.h
-+++ b/src/http/one/TeChunkedParser.h
-@@ -71,7 +71,7 @@ class TeChunkedParser : public Http1::Parser
- private:
-     bool parseChunkSize(Tokenizer &tok);
-     bool parseChunkMetadataSuffix(Tokenizer &);
--    bool parseChunkExtensions(Tokenizer &);
-+    void parseChunkExtensions(Tokenizer &);
-     void parseOneChunkExtension(Tokenizer &);
-     bool parseChunkBody(Tokenizer &tok);
-     bool parseChunkEnd(Tokenizer &tok);
-
-From f79936a234e722adb2dd08f31cf6019d81ee712c Mon Sep 17 00:00:00 2001
-From: Alex Rousskov 
-Date: Thu, 10 Oct 2024 23:31:08 -0400
-Subject: [PATCH 6/6] fixup: Deadly typo
-
----
- src/http/one/Parser.cc | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/http/one/Parser.cc b/src/http/one/Parser.cc
-index d3937e5e96b..7403a9163a2 100644
---- a/src/http/one/Parser.cc
-+++ b/src/http/one/Parser.cc
-@@ -296,12 +296,12 @@ ParseBws_(Parser::Tokenizer &tok, const CharacterSet &bwsChars)
- void
- Http::One::ParseBws(Parser::Tokenizer &tok)
- {
--    ParseBws_(tok, CharacterSet::WSP);
-+    ParseBws_(tok, Parser::WhitespaceCharacters());
- }
- 
- void
- Http::One::ParseStrictBws(Parser::Tokenizer &tok)
- {
--    ParseBws_(tok, Parser::WhitespaceCharacters());
-+    ParseBws_(tok, CharacterSet::WSP);
- }
- 
-
diff --git a/SOURCES/squid-4.15-ip-bind-address-no-port.patch b/SOURCES/squid-4.15-ip-bind-address-no-port.patch
deleted file mode 100644
index 85844ae..0000000
--- a/SOURCES/squid-4.15-ip-bind-address-no-port.patch
+++ /dev/null
@@ -1,156 +0,0 @@
-commit c08948c8b831a2ba73c676b48aa11ba1b58cc542
-Author: Tomas Korbar 
-Date:   Thu Dec 8 11:03:08 2022 +0100
-
-    Backport adding IP_BIND_ADDRESS_NO_PORT flag to outgoing connections
-
-diff --git a/src/comm.cc b/src/comm.cc
-index 0d5f34d..6811b54 100644
---- a/src/comm.cc
-+++ b/src/comm.cc
-@@ -58,6 +58,7 @@
-  */
- 
- static IOCB commHalfClosedReader;
-+static int comm_openex(int sock_type, int proto, Ip::Address &, int flags, const char *note);
- static void comm_init_opened(const Comm::ConnectionPointer &conn, const char *note, struct addrinfo *AI);
- static int comm_apply_flags(int new_socket, Ip::Address &addr, int flags, struct addrinfo *AI);
- 
-@@ -75,6 +76,7 @@ static EVH commHalfClosedCheck;
- static void commPlanHalfClosedCheck();
- 
- static Comm::Flag commBind(int s, struct addrinfo &);
-+static void commSetBindAddressNoPort(int);
- static void commSetReuseAddr(int);
- static void commSetNoLinger(int);
- #ifdef TCP_NODELAY
-@@ -201,6 +203,22 @@ comm_local_port(int fd)
-     return F->local_addr.port();
- }
- 
-+/// sets the IP_BIND_ADDRESS_NO_PORT socket option to optimize ephemeral port
-+/// reuse by outgoing TCP connections that must bind(2) to a source IP address
-+static void
-+commSetBindAddressNoPort(const int fd)
-+{
-+#if defined(IP_BIND_ADDRESS_NO_PORT)
-+    int flag = 1;
-+    if (setsockopt(fd, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, reinterpret_cast(&flag), sizeof(flag)) < 0) {
-+        const auto savedErrno = errno;
-+        debugs(50, DBG_IMPORTANT, "ERROR: setsockopt(IP_BIND_ADDRESS_NO_PORT) failure: " << xstrerr(savedErrno));
-+    }
-+#else
-+    (void)fd;
-+#endif
-+}
-+
- static Comm::Flag
- commBind(int s, struct addrinfo &inaddr)
- {
-@@ -227,6 +245,10 @@ comm_open(int sock_type,
-           int flags,
-           const char *note)
- {
-+    // assume zero-port callers do not need to know the assigned port right away
-+    if (sock_type == SOCK_STREAM && addr.port() == 0 && ((flags & COMM_DOBIND) || !addr.isAnyAddr()))
-+        flags |= COMM_DOBIND_PORT_LATER;
-+
-     return comm_openex(sock_type, proto, addr, flags, note);
- }
- 
-@@ -328,7 +350,7 @@ comm_set_transparent(int fd)
-  * Create a socket. Default is blocking, stream (TCP) socket.  IO_TYPE
-  * is OR of flags specified in defines.h:COMM_*
-  */
--int
-+static int
- comm_openex(int sock_type,
-             int proto,
-             Ip::Address &addr,
-@@ -476,6 +498,9 @@ comm_apply_flags(int new_socket,
-         if ( addr.isNoAddr() )
-             debugs(5,0,"CRITICAL: Squid is attempting to bind() port " << addr << "!!");
- 
-+        if ((flags & COMM_DOBIND_PORT_LATER))
-+            commSetBindAddressNoPort(new_socket);
-+
-         if (commBind(new_socket, *AI) != Comm::OK) {
-             comm_close(new_socket);
-             return -1;
-diff --git a/src/comm.h b/src/comm.h
-index c963e1c..9ff201d 100644
---- a/src/comm.h
-+++ b/src/comm.h
-@@ -43,7 +43,6 @@ void comm_import_opened(const Comm::ConnectionPointer &, const char *note, struc
- 
- /**
-  * Open a port specially bound for listening or sending through a specific port.
-- * This is a wrapper providing IPv4/IPv6 failover around comm_openex().
-  * Please use for all listening sockets and bind() outbound sockets.
-  *
-  * It will open a socket bound for:
-@@ -59,7 +58,6 @@ void comm_import_opened(const Comm::ConnectionPointer &, const char *note, struc
- int comm_open_listener(int sock_type, int proto, Ip::Address &addr, int flags, const char *note);
- void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note);
- 
--int comm_openex(int, int, Ip::Address &, int, const char *);
- unsigned short comm_local_port(int fd);
- 
- int comm_udp_sendto(int sock, const Ip::Address &to, const void *buf, int buflen);
-diff --git a/src/comm/ConnOpener.cc b/src/comm/ConnOpener.cc
-index 25a30e4..2082214 100644
---- a/src/comm/ConnOpener.cc
-+++ b/src/comm/ConnOpener.cc
-@@ -263,7 +263,7 @@ Comm::ConnOpener::createFd()
-     if (callback_ == NULL || callback_->canceled())
-         return false;
- 
--    temporaryFd_ = comm_openex(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, host_);
-+    temporaryFd_ = comm_open(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, host_);
-     if (temporaryFd_ < 0) {
-         sendAnswer(Comm::ERR_CONNECT, 0, "Comm::ConnOpener::createFd");
-         return false;
-diff --git a/src/comm/Connection.h b/src/comm/Connection.h
-index 4f2f23a..1e32c22 100644
---- a/src/comm/Connection.h
-+++ b/src/comm/Connection.h
-@@ -47,6 +47,8 @@ namespace Comm
- #define COMM_DOBIND             0x08  // requires a bind()
- #define COMM_TRANSPARENT        0x10  // arrived via TPROXY
- #define COMM_INTERCEPTION       0x20  // arrived via NAT
-+/// Internal Comm optimization: Keep the source port unassigned until connect(2)
-+#define COMM_DOBIND_PORT_LATER 0x100
- 
- /**
-  * Store data about the physical and logical attributes of a connection.
-diff --git a/src/ipc.cc b/src/ipc.cc
-index e1d48fc..e92a27f 100644
---- a/src/ipc.cc
-+++ b/src/ipc.cc
-@@ -95,12 +95,12 @@ ipcCreate(int type, const char *prog, const char *const args[], const char *name
-     } else void(0)
- 
-     if (type == IPC_TCP_SOCKET) {
--        crfd = cwfd = comm_open(SOCK_STREAM,
-+        crfd = cwfd = comm_open_listener(SOCK_STREAM,
-                                 0,
-                                 local_addr,
-                                 COMM_NOCLOEXEC,
-                                 name);
--        prfd = pwfd = comm_open(SOCK_STREAM,
-+        prfd = pwfd = comm_open_listener(SOCK_STREAM,
-                                 0,          /* protocol */
-                                 local_addr,
-                                 0,          /* blocking */
-diff --git a/src/tests/stub_comm.cc b/src/tests/stub_comm.cc
-index 58f85e4..5381ab2 100644
---- a/src/tests/stub_comm.cc
-+++ b/src/tests/stub_comm.cc
-@@ -46,7 +46,6 @@ int comm_open_uds(int sock_type, int proto, struct sockaddr_un* addr, int flags)
- void comm_import_opened(const Comm::ConnectionPointer &, const char *note, struct addrinfo *AI) STUB
- int comm_open_listener(int sock_type, int proto, Ip::Address &addr, int flags, const char *note) STUB_RETVAL(-1)
- void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note) STUB
--int comm_openex(int, int, Ip::Address &, int, tos_t tos, nfmark_t nfmark, const char *) STUB_RETVAL(-1)
- unsigned short comm_local_port(int fd) STUB_RETVAL(0)
- int comm_udp_sendto(int sock, const Ip::Address &to, const void *buf, int buflen) STUB_RETVAL(-1)
- void commCallCloseHandlers(int fd) STUB
diff --git a/SOURCES/squid-4.15.tar.xz.asc b/SOURCES/squid-4.15.tar.xz.asc
deleted file mode 100644
index 7305eaa..0000000
--- a/SOURCES/squid-4.15.tar.xz.asc
+++ /dev/null
@@ -1,25 +0,0 @@
-File: squid-4.15.tar.xz
-Date: Mon 10 May 2021 10:50:22 UTC
-Size: 2454176
-MD5 : a593de9dc888dfeca4f1f7db2cd7d3b9
-SHA1: 60bda34ba39657e2d870c8c1d2acece8a69c3075
-Key : CD6DBF8EF3B17D3E 
-            B068 84ED B779 C89B 044E  64E3 CD6D BF8E F3B1 7D3E
-      keyring = http://www.squid-cache.org/pgp.asc
-      keyserver = pool.sks-keyservers.net
------BEGIN PGP SIGNATURE-----
-
-iQIzBAABCgAdFiEEsGiE7bd5yJsETmTjzW2/jvOxfT4FAmCZD/UACgkQzW2/jvOx
-fT6zZg/+N8JMIYpmVJ7jm4lF0Ub2kEHGTOrc+tnlA3LGnlMQuTm61+BYk58g0SKW
-96NbJ0cycW215Q34L+Y0tWuxEbIU01vIc3AA7rQd0LKy+fQU0OtBuhk5Vf4bKilW
-uHEVIQZs9HmY6bqC+kgtCf49tVZvR8FZYNuilg/68+i/pQdwaDDmVb+j2oF7w+y2
-dgkTFWtM5NTL6bqUVC0E7lLFPjzMefKfxkkpWFdV/VrAhU25jN24kpnjcfotQhdW
-LDFy5okduz3ljso9pBYJfLeMXM1FZPpceC91zj32x3tcUyrD3yIoXob58rEKvfe4
-RDXN4SuClsNe4UQ4oNoGIES9XtaYlOzPR1PlbqPUrdp1cDnhgLJ+1fkAixlMqCml
-wuI1VIKSEY+nvRzQzFHnXJK9otV8QwMF76AHaytO9y+X6JuZmu/CcV1pq61qY9qv
-t1/8z99wWSxpu17zthZgq64J225GF/hkBedaFlYoS5k5YUMDLPlRSCC0yPmb8JBF
-Cns5i/aq2PmOx2ZhQ2RQIF416J3HK8Galw8ytFOjnEcn4ux9yzKNjL38p4+PJJA0
-7GCMAqYYNjok3LSkGbiR7cPgbHnkqRfYbPFLMj4FtruoFlZ9L5MIU3oFvqA3ZR6l
-Az6LaKLsAYPUmukAOPUSIrqpKXZHc7hdBWkT+7RYA4qaoU+9oIo=
-=1Re1
------END PGP SIGNATURE-----
diff --git a/SOURCES/squid.sysconfig b/SOURCES/squid.sysconfig
deleted file mode 100644
index 3864bd8..0000000
--- a/SOURCES/squid.sysconfig
+++ /dev/null
@@ -1,9 +0,0 @@
-# default squid options
-SQUID_OPTS=""
-
-# Time to wait for Squid to shut down when asked. Should not be necessary
-# most of the time.
-SQUID_SHUTDOWN_TIMEOUT=100
-
-# default squid conf file
-SQUID_CONF="/etc/squid/squid.conf"
diff --git a/SOURCES/cache_swap.sh b/cache_swap.sh
similarity index 50%
rename from SOURCES/cache_swap.sh
rename to cache_swap.sh
index 5e94072..77d06ac 100644
--- a/SOURCES/cache_swap.sh
+++ b/cache_swap.sh
@@ -5,12 +5,17 @@ fi
 
 SQUID_CONF=${SQUID_CONF:-"/etc/squid/squid.conf"}
 
-CACHE_SWAP=`sed -e 's/#.*//g' $SQUID_CONF | \
-	grep cache_dir | awk '{ print $3 }'`
+CACHE_SWAP=`awk '/^[[:blank:]]*cache_dir/ { print $3 }' "$SQUID_CONF"`
 
+init_cache_dirs=0
 for adir in $CACHE_SWAP; do
 	if [ ! -d $adir/00 ]; then
 		echo -n "init_cache_dir $adir... "
-		squid -N -z -F -f $SQUID_CONF >> /var/log/squid/squid.out 2>&1
+		init_cache_dirs=1
 	fi
 done
+
+if [ $init_cache_dirs -ne 0 ]; then
+	echo ""
+	squid --foreground -z -f "$SQUID_CONF" >> /var/log/squid/squid.out 2>&1
+fi
diff --git a/SOURCES/perl-requires-squid.sh b/perl-requires-squid.sh
similarity index 100%
rename from SOURCES/perl-requires-squid.sh
rename to perl-requires-squid.sh
diff --git a/pgp.asc b/pgp.asc
new file mode 100644
index 0000000..d4b886e
--- /dev/null
+++ b/pgp.asc
@@ -0,0 +1,2067 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+81Pk
+=EMOU
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/sources b/sources
new file mode 100644
index 0000000..41f7492
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+SHA512 (squid-6.10.tar.xz) = c0b75c3d383b1cd234b30dd02e84e1c5655fc53f63b75704bf4bac9ee0b86ba27e4656116893aff8b95dea19ff1befabcbb9dab3875da52fcb65f1d30f0fe5a9
diff --git a/SOURCES/squid-4.11-config.patch b/squid-6.1-config.patch
similarity index 85%
rename from SOURCES/squid-4.11-config.patch
rename to squid-6.1-config.patch
index 9b0e2d8..9d2b192 100644
--- a/SOURCES/squid-4.11-config.patch
+++ b/squid-6.1-config.patch
@@ -1,8 +1,8 @@
 diff --git a/src/cf.data.pre b/src/cf.data.pre
-index 26ef576..30d5509 100644
+index 44aa34d..12225bc 100644
 --- a/src/cf.data.pre
 +++ b/src/cf.data.pre
-@@ -5006,7 +5006,7 @@ DOC_END
+@@ -5453,7 +5453,7 @@ DOC_END
  
  NAME: logfile_rotate
  TYPE: int
@@ -11,7 +11,7 @@ index 26ef576..30d5509 100644
  LOC: Config.Log.rotateNumber
  DOC_START
  	Specifies the default number of logfile rotations to make when you
-@@ -6857,11 +6857,11 @@ COMMENT_END
+@@ -7447,11 +7447,11 @@ COMMENT_END
  
  NAME: cache_mgr
  TYPE: string
diff --git a/SOURCES/squid-4.15-halfclosed.patch b/squid-6.1-crash-half-closed.patch
similarity index 79%
rename from SOURCES/squid-4.15-halfclosed.patch
rename to squid-6.1-crash-half-closed.patch
index 6a9fc59..901ece2 100644
--- a/SOURCES/squid-4.15-halfclosed.patch
+++ b/squid-6.1-crash-half-closed.patch
@@ -1,8 +1,8 @@
 diff --git a/src/client_side.cc b/src/client_side.cc
-index f57f3f7..ab393e4 100644
+index f488fc4..69586df 100644
 --- a/src/client_side.cc
 +++ b/src/client_side.cc
-@@ -906,7 +906,7 @@ ConnStateData::kick()
+@@ -932,7 +932,7 @@ ConnStateData::kick()
       * We are done with the response, and we are either still receiving request
       * body (early response!) or have already stopped receiving anything.
       *
@@ -11,7 +11,7 @@ index f57f3f7..ab393e4 100644
       * (XXX: but then we will call readNextRequest() which may succeed and
       * execute a smuggled request as we are not done with the current request).
       *
-@@ -926,28 +926,12 @@ ConnStateData::kick()
+@@ -952,28 +952,12 @@ ConnStateData::kick()
       * Attempt to parse a request from the request buffer.
       * If we've been fed a pipelined request it may already
       * be in our read buffer.
@@ -42,8 +42,8 @@ index f57f3f7..ab393e4 100644
  
      /** \par
       * At this point we either have a parsed request (which we've
-@@ -2058,16 +2042,11 @@ ConnStateData::receivedFirstByte()
-     commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
+@@ -1893,16 +1877,11 @@ ConnStateData::receivedFirstByte()
+     resetReadTimeout(Config.Timeout.request);
  }
  
 -/**
@@ -60,19 +60,19 @@ index f57f3f7..ab393e4 100644
  {
 -    bool parsed_req = false;
 -
-     debugs(33, 5, HERE << clientConnection << ": attempting to parse");
+     debugs(33, 5, clientConnection << ": attempting to parse");
  
      // Loop while we have read bytes that are not needed for producing the body
-@@ -2116,8 +2095,6 @@ ConnStateData::clientParseRequests()
+@@ -1947,8 +1926,6 @@ ConnStateData::clientParseRequests()
  
              processParsedRequest(context);
  
 -            parsed_req = true; // XXX: do we really need to parse everything right NOW ?
 -
              if (context->mayUseConnection()) {
-                 debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
+                 debugs(33, 3, "Not parsing new requests, as this request may need the connection");
                  break;
-@@ -2130,8 +2107,19 @@ ConnStateData::clientParseRequests()
+@@ -1961,8 +1938,19 @@ ConnStateData::clientParseRequests()
          }
      }
  
@@ -94,21 +94,16 @@ index f57f3f7..ab393e4 100644
  }
  
  void
-@@ -2148,23 +2136,7 @@ ConnStateData::afterClientRead()
+@@ -1979,18 +1967,7 @@ ConnStateData::afterClientRead()
      if (pipeline.empty())
          fd_note(clientConnection->fd, "Reading next request");
  
 -    if (!clientParseRequests()) {
 -        if (!isOpen())
 -            return;
--        /*
--         * If the client here is half closed and we failed
--         * to parse a request, close the connection.
--         * The above check with connFinishedWithConn() only
--         * succeeds _if_ the buffer is empty which it won't
--         * be if we have an incomplete request.
--         * XXX: This duplicates ConnStateData::kick
--         */
+-        // We may get here if the client half-closed after sending a partial
+-        // request. See doClientRead() and shouldCloseOnEof().
+-        // XXX: This partially duplicates ConnStateData::kick().
 -        if (pipeline.empty() && commIsHalfClosed(clientConnection->fd)) {
 -            debugs(33, 5, clientConnection << ": half-closed connection, no completed request parsed, connection closing.");
 -            clientConnection->close();
@@ -119,7 +114,7 @@ index f57f3f7..ab393e4 100644
  
      if (!isOpen())
          return;
-@@ -3945,7 +3917,7 @@ ConnStateData::notePinnedConnectionBecameIdle(PinnedIdleContext pic)
+@@ -3775,7 +3752,7 @@ ConnStateData::notePinnedConnectionBecameIdle(PinnedIdleContext pic)
      startPinnedConnectionMonitoring();
  
      if (pipeline.empty())
@@ -129,27 +124,27 @@ index f57f3f7..ab393e4 100644
  
  /// Forward future client requests using the given server connection.
 diff --git a/src/client_side.h b/src/client_side.h
-index 9fe8463..dfb4d8e 100644
+index 6027b31..60b99b1 100644
 --- a/src/client_side.h
 +++ b/src/client_side.h
-@@ -85,7 +85,6 @@ public:
-     virtual void doneWithControlMsg();
+@@ -98,7 +98,6 @@ public:
+     void doneWithControlMsg() override;
  
      /// Traffic parsing
 -    bool clientParseRequests();
      void readNextRequest();
  
      /// try to make progress on a transaction or read more I/O
-@@ -373,6 +372,7 @@ private:
-     virtual bool connFinishedWithConn(int size);
-     virtual void checkLogging();
+@@ -443,6 +442,7 @@ private:
+ 
+     void checkLogging();
  
 +    void parseRequests();
      void clientAfterReadingRequests();
      bool concurrentRequestQueueFilled() const;
  
 diff --git a/src/tests/stub_client_side.cc b/src/tests/stub_client_side.cc
-index d7efb0f..655ed83 100644
+index 8c160e5..f49d5dc 100644
 --- a/src/tests/stub_client_side.cc
 +++ b/src/tests/stub_client_side.cc
 @@ -14,7 +14,7 @@
diff --git a/SOURCES/squid-4.11-location.patch b/squid-6.1-location.patch
similarity index 74%
rename from SOURCES/squid-4.11-location.patch
rename to squid-6.1-location.patch
index e33e9a0..4abd519 100644
--- a/SOURCES/squid-4.11-location.patch
+++ b/squid-6.1-location.patch
@@ -1,8 +1,7 @@
-diff --git a/QUICKSTART b/QUICKSTART
-index e5299b4..a243437 100644
---- a/QUICKSTART
-+++ b/QUICKSTART
-@@ -10,10 +10,9 @@ After you retrieved, compiled and installed the Squid software (see
+diff -up squid-3.1.0.9/QUICKSTART.location squid-3.1.0.9/QUICKSTART
+--- squid-3.1.0.9/QUICKSTART.location	2009-06-26 12:35:27.000000000 +0200
++++ squid-3.1.0.9/QUICKSTART	2009-07-17 14:03:10.000000000 +0200
+@@ -10,10 +10,9 @@ After you retrieved, compiled and instal
  INSTALL in the same directory), you have to configure the squid.conf
  file. This is the list of the values you *need* to change, because no
  sensible defaults could be defined. Do not touch the other variables
@@ -15,7 +14,7 @@ index e5299b4..a243437 100644
  
  ==============================================================================
  
-@@ -80,12 +79,12 @@ After editing squid.conf to your liking, run Squid from the command
+@@ -82,12 +81,12 @@ After editing squid.conf to your liking,
  line TWICE:
  
  To create any disk cache_dir configured:
diff --git a/SOURCES/squid-4.11-perlpath.patch b/squid-6.1-perlpath.patch
similarity index 69%
rename from SOURCES/squid-4.11-perlpath.patch
rename to squid-6.1-perlpath.patch
index 9e7fbbc..fe37759 100644
--- a/SOURCES/squid-4.11-perlpath.patch
+++ b/squid-6.1-perlpath.patch
@@ -1,10 +1,10 @@
 diff --git a/contrib/url-normalizer.pl b/contrib/url-normalizer.pl
-index 90ac6a4..8dbed90 100755
+index e965e9e..ed5ffcb 100755
 --- a/contrib/url-normalizer.pl
 +++ b/contrib/url-normalizer.pl
 @@ -1,4 +1,4 @@
 -#!/usr/local/bin/perl -Tw
 +#!/usr/bin/perl -Tw
  #
- # * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
+ # * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
  # *
diff --git a/squid-6.1-symlink-lang-err.patch b/squid-6.1-symlink-lang-err.patch
new file mode 100644
index 0000000..a29274b
--- /dev/null
+++ b/squid-6.1-symlink-lang-err.patch
@@ -0,0 +1,26 @@
+diff --git a/errors/aliases b/errors/aliases
+index c256106..38c123a 100644
+--- a/errors/aliases
++++ b/errors/aliases
+@@ -14,8 +14,7 @@ da	da-dk
+ de	de-at de-ch de-de de-li de-lu
+ el	el-gr
+ en	en-au en-bz en-ca en-cn en-gb en-ie en-in en-jm en-nz en-ph en-sg en-tt en-uk en-us en-za en-zw
+-es	es-ar es-bo es-cl es-cu es-co es-do es-ec es-es es-pe es-pr es-py es-us es-uy es-ve es-xl spq
+-es-mx	es-bz es-cr es-gt es-hn es-ni es-pa es-sv
++es	es-ar es-bo es-cl es-co es-cr es-do es-ec es-es es-gt es-hn es-mx es-ni es-pa es-pe es-pr es-py es-sv es-us es-uy es-ve es-xl
+ et	et-ee
+ fa	fa-fa fa-ir
+ fi	fi-fi
+diff --git a/errors/language.am b/errors/language.am
+index a437d17..f2fe463 100644
+--- a/errors/language.am
++++ b/errors/language.am
+@@ -19,7 +19,6 @@ LANGUAGE_FILES = \
+ 	de.lang \
+ 	el.lang \
+ 	en.lang \
+-	es-mx.lang \
+ 	es.lang \
+ 	et.lang \
+ 	fa.lang \
diff --git a/squid-6.10.tar.xz.asc b/squid-6.10.tar.xz.asc
new file mode 100644
index 0000000..0539d48
--- /dev/null
+++ b/squid-6.10.tar.xz.asc
@@ -0,0 +1,17 @@
+File: squid-6.10.tar.xz
+Date: Sat Jun  8 02:53:29 PM UTC 2024
+Size: 2558208
+MD5 : 86deefa7282c4388be95260aa4d4cf6a
+SHA1: 70e90865df0e4e9ba7765b622da40bda9bb8fc5d
+Key : 29B4B1F7CE03D1B1DED22F3028F85029FEF6E865 
+            29B4 B1F7 CE03 D1B1 DED2  2F30 28F8 5029 FEF6 E865
+sub   cv25519 2021-05-15 [E]
+      keyring = http://www.squid-cache.org/pgp.asc
+      keyserver = pool.sks-keyservers.net
+-----BEGIN PGP SIGNATURE-----
+
+iHUEABYKAB0WIQQptLH3zgPRsd7SLzAo+FAp/vboZQUCZmRwewAKCRAo+FAp/vbo
+ZZV0AP0WDdXJFarEEYCSXSv/zT1l0FrI8jLQCT3Rsp6nTbWxfwD/VYmUMDetPLPJ
+GYHJNrRm7OceMQcsqhQIz6X71SR9AQs=
+=4HPC
+-----END PGP SIGNATURE-----
diff --git a/SOURCES/squid.logrotate b/squid.logrotate
similarity index 78%
rename from SOURCES/squid.logrotate
rename to squid.logrotate
index 4a0406f..c88da04 100644
--- a/SOURCES/squid.logrotate
+++ b/squid.logrotate
@@ -2,6 +2,7 @@
     weekly
     rotate 5
     compress
+    delaycompress
     notifempty
     missingok
     nocreate
@@ -10,7 +11,5 @@
       # Asks squid to reopen its logs. (logfile_rotate 0 is set in squid.conf)
       # errors redirected to make it silent if squid is not running
       /usr/sbin/squid -k rotate 2>/dev/null
-      # Wait a little to allow Squid to catch up before the logs is compressed
-      sleep 1
     endscript
 }
diff --git a/SOURCES/squid.nm b/squid.nm
similarity index 55%
rename from SOURCES/squid.nm
rename to squid.nm
index 5e40f76..1f317da 100755
--- a/SOURCES/squid.nm
+++ b/squid.nm
@@ -2,6 +2,6 @@
 
 case "$2" in
         up|down|vpn-up|vpn-down)
-                /bin/systemctl -q reload squid.service || :
+                /usr/bin/systemctl -q reload squid.service || :
                 ;;
 esac
diff --git a/SOURCES/squid.pam b/squid.pam
similarity index 100%
rename from SOURCES/squid.pam
rename to squid.pam
diff --git a/SOURCES/squid.service b/squid.service
similarity index 100%
rename from SOURCES/squid.service
rename to squid.service
diff --git a/SPECS/squid.spec b/squid.spec
similarity index 81%
rename from SPECS/squid.spec
rename to squid.spec
index 8d93173..ecf3f0a 100644
--- a/SPECS/squid.spec
+++ b/squid.spec
@@ -1,86 +1,51 @@
 %define __perl_requires %{SOURCE98}
 
 Name:     squid
-Version:  4.15
-Release:  10%{?dist}.3
+Version:  6.10
+Release:  1%{?dist}
 Summary:  The Squid proxy caching server
 Epoch:    7
 # See CREDITS for breakdown of non GPLv2+ code
-License:  GPLv2+ and (LGPLv2+ and MIT and BSD and Public Domain)
+License:  GPL-2.0-or-later AND (LGPL-2.0-or-later AND MIT AND BSD-2-Clause AND BSD-3-Clause AND BSD-4-Clause AND BSD-4-Clause-UC AND LicenseRef-Fedora-Public-Domain AND Beerware)
 URL:      http://www.squid-cache.org
-Source0:  http://www.squid-cache.org/Versions/v4/squid-%{version}.tar.xz
-Source1:  http://www.squid-cache.org/Versions/v4/squid-%{version}.tar.xz.asc
-Source2:  squid.logrotate
-Source3:  squid.sysconfig
-Source4:  squid.pam
-Source5:  squid.nm
-Source6:  squid.service
-Source7:  cache_swap.sh
+
+Source0:  http://www.squid-cache.org/Versions/v6/squid-%{version}.tar.xz
+Source1:  http://www.squid-cache.org/Versions/v6/squid-%{version}.tar.xz.asc
+Source2:  http://www.squid-cache.org/pgp.asc
+Source3:  squid.logrotate
+Source4:  squid.sysconfig
+Source5:  squid.pam
+Source6:  squid.nm
+Source7:  squid.service
+Source8:  cache_swap.sh
+Source9:  squid.sysusers
 
 Source98: perl-requires-squid.sh
 
 # Upstream patches
 
 # Backported patches
-Patch101: squid-4.15-ip-bind-address-no-port.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2072988
+# Patch101: patch
 
 # Local patches
 # Applying upstream patches first makes it less likely that local patches
 # will break upstream ones.
-Patch201: squid-4.11-config.patch
-Patch202: squid-4.11-location.patch
-Patch203: squid-4.11-perlpath.patch
-Patch204: squid-4.11-include-guards.patch
-Patch205: squid-4.11-large-acl.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=980511
-Patch206: squid-4.11-active-ftp.patch
-Patch208: squid-4.11-convert-ipv4.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2006121
-Patch209: squid-4.15-ftp-filename-extraction.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2076717
-Patch210: squid-4.15-halfclosed.patch
+Patch201: squid-6.1-config.patch
+Patch202: squid-6.1-location.patch
+Patch203: squid-6.1-perlpath.patch
+# revert this upstream patch - https://bugzilla.redhat.com/show_bug.cgi?id=1936422
+# workaround for #1934919
+Patch204: squid-6.1-symlink-lang-err.patch
+# Upstream PR: https://github.com/squid-cache/squid/pull/1442
+Patch205: squid-6.1-crash-half-closed.patch
 
-# Security fixes
-# https://bugzilla.redhat.com/show_bug.cgi?id=1941506
-Patch300: squid-4.15-CVE-2021-28116.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2100721
-Patch301: squid-4.15-CVE-2021-46784.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2129771
-Patch302: squid-4.15-CVE-2022-41318.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2245910
-# +backported: https://github.com/squid-cache/squid/commit/417da4006cf5c97d44e74431b816fc58fec9e270
-Patch303: squid-4.15-CVE-2023-46846.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2245916
-Patch304: squid-4.15-CVE-2023-46847.patch
-# https://issues.redhat.com/browse/RHEL-14792
-Patch305: squid-4.15-CVE-2023-5824.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2248521
-Patch306: squid-4.15-CVE-2023-46728.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2247567
-Patch307: squid-4.15-CVE-2023-46724.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2252926
-Patch308: squid-4.15-CVE-2023-49285.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2252923
-Patch309: squid-4.15-CVE-2023-49286.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2254663
-Patch310: squid-4.15-CVE-2023-50269.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2264309
-Patch311: squid-4.15-CVE-2024-25617.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2268366
-Patch312: squid-4.15-CVE-2024-25111.patch
-# Regression caused by squid-4.15-CVE-2023-46846.patch
-# Upstream PR: https://github.com/squid-cache/squid/pull/1914
-Patch313: squid-4.15-ignore-wsp-after-chunk-size.patch
-# https://bugzilla.redhat.com/show_bug.cgi?id=2260051
-Patch314: squid-4.15-CVE-2024-23638.patch
+# cache_swap.sh
+Requires: bash gawk
+# for httpd conf file - cachemgr script alias
+Requires: httpd-filesystem
 
-Requires: bash >= 2.0
-Requires(pre): shadow-utils
-Requires(post): systemd
-Requires(preun): systemd
-Requires(postun): systemd
 # squid_ldap_auth and other LDAP helpers require OpenLDAP
+BuildRequires: make
 BuildRequires: openldap-devel
 # squid_pam_auth requires PAM development libs
 BuildRequires: pam-devel
@@ -88,8 +53,10 @@ BuildRequires: pam-devel
 BuildRequires: openssl-devel
 # squid_kerb_aut requires Kerberos development libs
 BuildRequires: krb5-devel
-# time_quota requires DB
-BuildRequires: libdb-devel
+# time_quota requires TrivialDB
+BuildRequires: libtdb-devel
+# ESI support requires Expat & libxml2
+BuildRequires: expat-devel libxml2-devel
 # TPROXY requires libcap, and also increases security somewhat
 BuildRequires: libcap-devel
 # eCAP support
@@ -100,13 +67,23 @@ BuildRequires: libtool libtool-ltdl-devel
 BuildRequires: perl-generators
 # For test suite
 BuildRequires: pkgconfig(cppunit)
-BuildRequires: autoconf
+# For verifying downloded src tarball
+BuildRequires: gnupg2
+# for _tmpfilesdir and _unitdir macro
+# see https://docs.fedoraproject.org/en-US/packaging-guidelines/Systemd/#_packaging
+BuildRequires: systemd-rpm-macros
 # systemd notify
 BuildRequires: systemd-devel
 
+%{?systemd_requires}
+%{?sysusers_requires_compat}
+
+# Old NetworkManager expects the dispatcher scripts in a different place
+Conflicts: NetworkManager < 1.20
+
 %description
 Squid is a high-performance proxy caching server for Web clients,
-supporting FTP, gopher, and HTTP data objects. Unlike traditional
+supporting FTP and HTTP data objects. Unlike traditional
 caching software, Squid handles all requests in a single,
 non-blocking, I/O-driven process. Squid keeps meta data and especially
 hot objects cached in RAM, caches DNS lookups, supports non-blocking
@@ -117,48 +94,15 @@ lookup program (dnsserver), a program for retrieving FTP data
 (ftpget), and some management and client tools.
 
 %prep
-%setup -q
+%{gpgverify} --keyring='%{SOURCE2}' --signature='%{SOURCE1}' --data='%{SOURCE0}'
 
-# Upstream patches
-
-# Backported patches
-%patch101 -p1 -b .ip-bind-address-no-port
-
-# Local patches
-%patch201 -p1 -b .config
-%patch202 -p1 -b .location
-%patch203 -p1 -b .perlpath
-%patch204 -p1 -b .include-guards
-%patch205 -p1 -b .large_acl
-%patch206 -p1 -b .active-ftp
-%patch208 -p1 -b .convert-ipv4
-%patch209 -p1 -b .ftp-fn-extraction
-%patch210 -p1 -b .halfclosed
-
-# Security patches
-%patch300 -p1 -b .CVE-2021-28116
-%patch301 -p1 -b .CVE-2021-46784
-%patch302 -p1 -b .CVE-2022-41318
-%patch303 -p1 -b .CVE-2023-46846
-%patch304 -p1 -b .CVE-2023-46847
-%patch305 -p1 -b .CVE-2023-5824
-%patch306 -p1 -b .CVE-2023-46728
-%patch307 -p1 -b .CVE-2023-46724
-%patch308 -p1 -b .CVE-2023-49285
-%patch309 -p1 -b .CVE-2023-49286
-%patch310 -p1 -b .CVE-2023-50269
-%patch311 -p1 -b .CVE-2024-25617
-%patch312 -p1 -b .CVE-2024-25111
-%patch313 -p1 -b .ignore-wsp-chunk-sz
-%patch314 -p1 -b .CVE-2024-23638
+%autosetup -p1
 
 # https://bugzilla.redhat.com/show_bug.cgi?id=1679526
 # Patch in the vendor documentation and used different location for documentation
 sed -i 's|@SYSCONFDIR@/squid.conf.documented|%{_pkgdocdir}/squid.conf.documented|' src/squid.8.in
 
 %build
-# cppunit-config patch changes configure.ac
-autoconf
 
 # NIS helper has been removed because of the following bug
 # https://bugzilla.redhat.com/show_bug.cgi?id=1531540
@@ -167,7 +111,7 @@ autoconf
    --datadir=%{_datadir}/squid \
    --sysconfdir=%{_sysconfdir}/squid \
    --with-logdir='%{_localstatedir}/log/squid' \
-   --with-pidfile='%{_localstatedir}/run/squid.pid' \
+   --with-pidfile='/run/squid.pid' \
    --disable-dependency-tracking \
    --enable-eui \
    --enable-follow-x-forwarded-for \
@@ -195,7 +139,7 @@ autoconf
    --enable-storeio="aufs,diskd,ufs,rock" \
    --enable-diskio \
    --enable-wccpv2 \
-   --disable-esi \
+   --enable-esi \
    --enable-ecap \
    --with-aio \
    --with-default-user="squid" \
@@ -204,13 +148,20 @@ autoconf
    --with-pthreads \
    --disable-arch-native \
    --disable-security-cert-validators \
-   --with-swapdir=%{_localstatedir}/spool/squid
+   --disable-strict-error-checking \
+   --with-swapdir=%{_localstatedir}/spool/squid \
+   --enable-translation
+
+# workaround to build squid v5
+mkdir -p src/icmp/tests
+mkdir -p tools/squidclient/tests
+mkdir -p tools/tests
 
 %make_build
 
 %check
 make check
-	
+
 %install
 %make_install
 
@@ -233,22 +184,20 @@ mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d
 mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig
 mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/pam.d
 mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/httpd/conf.d/
-mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/NetworkManager/dispatcher.d
+mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/NetworkManager/dispatcher.d
 mkdir -p $RPM_BUILD_ROOT%{_unitdir}
 mkdir -p $RPM_BUILD_ROOT%{_libexecdir}/squid
-install -m 644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/squid
-install -m 644 %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/squid
-install -m 644 %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/squid
-install -m 644 %{SOURCE6} $RPM_BUILD_ROOT%{_unitdir}
-install -m 755 %{SOURCE7} $RPM_BUILD_ROOT%{_libexecdir}/squid
+install -m 644 %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/squid
+install -m 644 %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/squid
+install -m 644 %{SOURCE5} $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/squid
+install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_unitdir}
+install -m 755 %{SOURCE8} $RPM_BUILD_ROOT%{_libexecdir}/squid
 install -m 644 $RPM_BUILD_ROOT/squid.httpd.tmp $RPM_BUILD_ROOT%{_sysconfdir}/httpd/conf.d/squid.conf
-install -m 644 %{SOURCE5} $RPM_BUILD_ROOT%{_sysconfdir}/NetworkManager/dispatcher.d/20-squid
+install -m 755 %{SOURCE6} $RPM_BUILD_ROOT%{_prefix}/lib/NetworkManager/dispatcher.d/20-squid
 mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/squid
 mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/spool/squid
-mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/squid
+mkdir -p $RPM_BUILD_ROOT/run/squid
 chmod 644 contrib/url-normalizer.pl contrib/user-agents.pl
-iconv -f ISO88591 -t UTF8 ChangeLog -o ChangeLog.tmp
-mv -f ChangeLog.tmp ChangeLog
 
 # install /usr/lib/tmpfiles.d/squid.conf
 mkdir -p ${RPM_BUILD_ROOT}%{_tmpfilesdir}
@@ -266,11 +215,12 @@ mv $RPM_BUILD_ROOT/usr/share/squid/mib.txt $RPM_BUILD_ROOT/usr/share/snmp/mibs/S
 rm -f $RPM_BUILD_ROOT%{_sysconfdir}/squid/squid.conf.documented
 
 # remove unpackaged files from the buildroot
-rm -f $RPM_BUILD_ROOT%{_bindir}/{RunAccel,RunCache}
 rm -f $RPM_BUILD_ROOT/squid.httpd.tmp
 
+# sysusers.d
+install -p -D -m 0644 %{SOURCE9} %{buildroot}%{_sysusersdir}/squid.conf
+
 %files
-%defattr(-,root,root,-)
 %license COPYING 
 %doc CONTRIBUTORS README ChangeLog QUICKSTART src/squid.conf.documented
 %doc contrib/url-normalizer.pl contrib/user-agents.pl
@@ -282,7 +232,7 @@ rm -f $RPM_BUILD_ROOT/squid.httpd.tmp
 %attr(755,root,root) %dir %{_libdir}/squid
 %attr(770,squid,root) %dir %{_localstatedir}/log/squid
 %attr(750,squid,squid) %dir %{_localstatedir}/spool/squid
-%attr(755,squid,squid) %dir %{_localstatedir}/run/squid
+%attr(755,squid,squid) %dir /run/squid
 
 %config(noreplace) %attr(644,root,root) %{_sysconfdir}/httpd/conf.d/squid.conf
 %config(noreplace) %attr(640,root,squid) %{_sysconfdir}/squid/squid.conf
@@ -300,7 +250,7 @@ rm -f $RPM_BUILD_ROOT/squid.httpd.tmp
 
 %dir %{_datadir}/squid
 %attr(-,root,root) %{_datadir}/squid/errors
-%attr(755,root,root) %{_sysconfdir}/NetworkManager/dispatcher.d/20-squid
+%{_prefix}/lib/NetworkManager
 %{_datadir}/squid/icons
 %{_sbindir}/squid
 %{_bindir}/squidclient
@@ -310,15 +260,10 @@ rm -f $RPM_BUILD_ROOT/squid.httpd.tmp
 %{_libdir}/squid/*
 %{_datadir}/snmp/mibs/SQUID-MIB.txt
 %{_tmpfilesdir}/squid.conf
+%{_sysusersdir}/squid.conf
 
 %pre
-if ! getent group squid >/dev/null 2>&1; then
-  /usr/sbin/groupadd -g 23 squid
-fi
-
-if ! getent passwd squid >/dev/null 2>&1 ; then
-  /usr/sbin/useradd -g 23 -u 23 -d /var/spool/squid -r -s /sbin/nologin squid >/dev/null 2>&1 || exit 1 
-fi
+%sysusers_create_compat %{SOURCE9}
 
 for i in /var/log/squid /var/spool/squid ; do
         if [ -d $i ] ; then
@@ -331,6 +276,18 @@ done
 exit 0
 
 %pretrans -p 
+-- temporarilly commented until https://bugzilla.redhat.com/show_bug.cgi?id=1936422 is resolved
+--
+-- previously /usr/share/squid/errors/es-mx was symlink, now it is directory since squid v5
+-- see https://docs.fedoraproject.org/en-US/packaging-guidelines/Directory_Replacement/
+-- Define the path to the symlink being replaced below.
+--
+-- path = "/usr/share/squid/errors/es-mx"
+-- st = posix.stat(path)
+-- if st and st.type == "link" then
+--   os.remove(path)
+-- end
+
 -- Due to a bug #447156
 paths = {"/usr/share/squid/errors/zh-cn", "/usr/share/squid/errors/zh-tw"}
 for key,path in ipairs(paths)
@@ -367,182 +324,238 @@ fi
 
 
 %changelog
-* Wed Nov 13 2024 Luboš Uhliarik  - 7:4.15-10.3
-- Resolves: RHEL-22593 - CVE-2024-23638 squid:4/squid: vulnerable to
-  a Denial of Service attack against Cache Manager error responses
+* Mon Jul 01 2024 Luboš Uhliarik  - 7:6.10-1
+- new version 6.10
+- Resolves: RHEL-45048 - squid: Out-of-bounds write error may lead to Denial of
+  Service (CVE-2024-37894)
 
-* Thu Nov 07 2024 Luboš Uhliarik  - 7:4.15-10.2
-- Disable ESI support
-- Resolves: RHEL-65075 - CVE-2024-45802 squid:4/squid: Denial of Service
-  processing ESI response content
+* Mon Jun 24 2024 Troy Dawson  - 7:6.7-2
+- Bump release for June 2024 mass rebuild
 
-* Mon Oct 14 2024 Luboš Uhliarik  - 7:4.15-10.1
-- Resolves: RHEL-56024 - (Regression) Transfer-encoding:chunked data is not sent
-  to the client in its complementary
+* Mon Feb 12 2024 Luboš Uhliarik  - 7:6.7-1
+- new version 6.7
+- switch to autosetup
+- fix FTBFS when using gcc14
 
-* Tue Mar 19 2024 Luboš Uhliarik  - 7:4.15-10
-- Resolves: RHEL-28529 - squid:4/squid: Denial of Service in HTTP Chunked
-  Decoding (CVE-2024-25111)
-- Resolves: RHEL-26088 - squid:4/squid: denial of service in HTTP header
-  parser (CVE-2024-25617)
+* Sat Jan 27 2024 Fedora Release Engineering  - 7:6.6-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
 
-* Fri Feb 02 2024 Luboš Uhliarik  - 7:4.15-9
-- Resolves: RHEL-19552 - squid:4/squid: denial of service in HTTP request 
-  parsing (CVE-2023-50269)
+* Wed Dec 13 2023 Yaakov Selkowitz  - 7:6.6-1
+- new version 6.6
 
-* Fri Feb 02 2024 Luboš Uhliarik  - 7:4.15-8
-- Resolves: RHEL-18351 - squid:4/squid: Buffer over-read in the HTTP Message
-  processing feature (CVE-2023-49285)
-- Resolves: RHEL-18342 - squid:4/squid: Incorrect Check of Function Return
-  Value In Helper Process management (CVE-2023-49286)
-- Resolves: RHEL-18230 - squid:4/squid: Denial of Service in SSL Certificate
-  validation (CVE-2023-46724)
-- Resolves: RHEL-15911 - squid:4/squid: NULL pointer dereference in the gopher
-  protocol code (CVE-2023-46728)
-- Resolves: RHEL-18251 - squid crashes in assertion when a parent peer exists
-- Resolves: RHEL-14794 - squid: squid multiple issues in HTTP response caching
-  (CVE-2023-5824)
-- Resolves: RHEL-14803 - squid: squid: Denial of Service in HTTP Digest
-  Authentication (CVE-2023-46847)
-- Resolves: RHEL-14777 - squid: squid: Request/Response smuggling in HTTP/1.1
-  and ICAP (CVE-2023-46846)
+* Tue Nov 07 2023 Luboš Uhliarik  - 7:6.5-1
+- new version 6.5
 
-* Wed Aug 16 2023 Luboš Uhliarik  - 7:4.15-7
-- Resolves: #2076717 - Crash with half_closed_client on
+* Tue Oct 24 2023 Luboš Uhliarik  - 7:6.4-1
+- new version 6.4
 
-* Thu Dec 08 2022 Tomas Korbar  - 4.15-6
-- Resolves: #2072988 - [RFE] Add the "IP_BIND_ADDRESS_NO_PORT"
-  flag to sockets created for outgoing connections in the squid source code.
+* Thu Sep 14 2023 Luboš Uhliarik  - 7:6.3-2
+- SPDX migration
 
-* Wed Sep 28 2022 Luboš Uhliarik  - 7:4.15-5
-- Resolves: #2130260 - CVE-2022-41318 squid:4/squid: buffer-over-read in SSPI and SMB
-  authentication
+* Tue Sep 05 2023 Luboš Uhliarik  - 7:6.3-1
+- new version 6.3
 
-* Tue Jun 28 2022 Luboš Uhliarik  - 7:4.15-4
-- Resolves: #2100783 - CVE-2021-46784 squid:4/squid: DoS when processing gopher
-  server responses
+* Wed Aug 16 2023 Luboš Uhliarik  - 7:6.2-1
+- new version 6.2
 
-* Wed Feb 09 2022 Luboš Uhliarik  - 7:4.15-3
-- Resolves: #1941506 - CVE-2021-28116 squid:4/squid: out-of-bounds read in WCCP
-  protocol data may lead to information disclosure
+* Fri Aug 04 2023 Luboš Uhliarik  - 7:6.1-3
+- Fix "!commHasHalfClosedMonitor(fd)" assertion
 
-* Tue Jan 25 2022 Luboš Uhliarik  - 7:4.15-2
-- Resolves: #2006121 - SQUID shortens FTP Link wrong that contains a semi-colon
-  and as a result is not able to download zip file.CODE 404 TO CLIENT)
+* Sat Jul 22 2023 Fedora Release Engineering  - 7:6.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
 
-* Fri Jun 18 2021 Luboš Uhliarik  - 7:4.15-1
-- new version 4.15
-- Resolves: #1964384 - squid:4 rebase to 4.15
+* Tue Jul 11 2023 Luboš Uhliarik  - 7:6.1-1
+- new version 6.1
 
-* Wed Mar 31 2021 Lubos Uhliarik  - 7:4.11-5
-- Resolves: #1944261 - CVE-2020-25097 squid:4/squid: improper input validation
-  may allow a trusted client to perform HTTP Request Smuggling
+* Tue May 09 2023 Luboš Uhliarik  - 7:5.9-1
+- new version 5.9
 
-* Mon Oct 26 2020 Lubos Uhliarik  - 7:4.11-4
-- Resolves: #1890606 - Fix for CVE 2019-13345 breaks authentication in
-  cachemgr.cgi
+* Tue Feb 28 2023 Luboš Uhliarik  - 7:5.8-1
+- new version 5.8
 
-* Wed Aug 26 2020 Lubos Uhliarik  - 7:4.11-3
-- Resolves: #1871705 - CVE-2020-24606 squid: Improper Input Validation could
-  result in a DoS
-- Resolves: #1871702 - CVE-2020-15811 squid: HTTP Request Splitting could result
-  in cache poisoning
-- Resolves: #1871700 - CVE-2020-15810 squid: HTTP Request Smuggling could result
-  in cache poisoning
+* Sat Jan 21 2023 Fedora Release Engineering  - 7:5.7-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
 
-* Thu Jul 02 2020 Lubos Uhliarik  - 7:4.11-2
-- Resolves: #1853130 - CVE-2020-15049 squid:4/squid: request smuggling and
-  poisoning attack against the HTTP cache
-- Resolves: #1853136 - CVE-2020-14058 squid:4/squid: DoS in TLS handshake
+* Mon Dec 05 2022 Tomas Korbar  - 7:5.7-3
+- Backport adding IP_BIND_ADDRESS_NO_PORT flag to outgoing connections
+
+* Wed Oct 12 2022 Luboš Uhliarik  - 7:5.7-2
+- Provide a sysusers.d file to get user() and group() provides (#2134071)
+
+* Tue Sep 06 2022 Luboš Uhliarik  - 7:5.7-1
+- new version 5.7
+
+* Sat Jul 23 2022 Fedora Release Engineering  - 7:5.6-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
+
+* Mon Jun 27 2022 Luboš Uhliarik  - 7:5.6-1
+- new version 5.6
+
+* Wed Apr 20 2022 Luboš Uhliarik  - 7:5.5-1
+- new version 5.5
+- Resolves: #2053799 - squid-5.5 is available
+
+* Wed Feb 09 2022 Luboš Uhliarik  - 7:5.4-1
+- new version 5.4
+
+* Sat Jan 22 2022 Fedora Release Engineering  - 7:5.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
+
+* Tue Oct 05 2021 Luboš Uhliarik  - 7:5.2-1
+- new version 5.2 (#2010109)
+- Resolves: #1934559 - squid: out-of-bounds read in WCCP protocol
+
+* Tue Sep 14 2021 Sahana Prasad  - 7:5.1-2
+- Rebuilt with OpenSSL 3.0.0
+
+* Thu Aug 05 2021 Luboš Uhliarik  - 7:5.1-1
+- new version 5.1
+
+* Fri Jul 23 2021 Fedora Release Engineering  - 7:5.0.6-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
+
+* Mon May 17 2021 Lubos Uhliarik  - 7:5.0.6-1
+- new version 5.0.6
+
+* Fri Apr 23 2021 Lubos Uhliarik  - 7:5.0.5-4
+- Related: #1934919 - squid update attempts fail with file conflicts
+
+* Fri Mar 05 2021 Lubos Uhliarik  - 7:5.0.5-3
+- Resolves: #1934919 - squid update attempts fail with file conflicts
+
+* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek  - 7:5.0.5-2
+- Rebuilt for updated systemd-rpm-macros
+  See https://pagure.io/fesco/issue/2583.
+
+* Wed Feb 10 2021 Lubos Uhliarik  - 7:5.0.5-1
+- new version 5.0.5
+
+* Wed Jan 27 2021 Fedora Release Engineering  - 7:4.13-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Sat Oct 17 2020 Jeff Law  - 7:4.13-2
+- Fix missing #includes for gcc-11
+
+* Tue Aug 25 2020 Lubos Uhliarik  - 7:4.13-1
+- new version 4.13
+
+* Fri Aug 07 2020 Jeff law  - 7:4.12-4
+- Disable LTO
+
+* Sat Aug 01 2020 Fedora Release Engineering  - 7:4.12-3
+- Second attempt - Rebuilt for
+  https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Wed Jul 29 2020 Fedora Release Engineering  - 7:4.12-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Mon Jun 15 2020 Lubos Uhliarik  - 7:4.12-1
+- new version 4.12
 
 * Thu May 07 2020 Lubos Uhliarik  - 7:4.11-1
 - new version 4.11
 - libsystemd integration
-- Resolves: #1829467 - squid:4 rebase
-- Resolves: #1828378 - CVE-2019-12521 squid:4/squid: off-by-one error in
-  addStackElement allows for a heap buffer overflow and a crash
-- Resolves: #1828377 - CVE-2019-12520 squid:4/squid: improper input validation
-  in request allows for proxy manipulation
-- Resolves: #1828375 - CVE-2019-12524 squid:4/squid: improper access restriction
-  in url_regex may lead to security bypass
-- Resolves: #1820664 - CVE-2019-18860 squid: mishandles HTML in the host
-  parameter to cachemgr.cgi which could result in squid behaving in unsecure way
-- Resolves: #1802514 - CVE-2020-8449 squid:4/squid: Improper input validation
-  issues in HTTP Request processing
-- Resolves: #1802513 - CVE-2020-8450 squid:4/squid: Buffer overflow in a Squid
-  acting as reverse-proxy
-- Resolves: #1802512 - CVE-2019-12528 squid:4/squid: Information Disclosure
-  issue in FTP Gateway
-- Resolves: #1771288 - CVE-2019-18678 squid:4/squid: HTTP Request Splitting
-  issue in HTTP message processing
-- Resolves: #1771283 - CVE-2019-18679 squid:4/squid: Information Disclosure
-  issue in HTTP Digest Authentication
-- Resolves: #1771280 - CVE-2019-18677 squid:4/squid: Cross-Site Request Forgery
-  issue in HTTP Request processing
-- Resolves: #1771275 - CVE-2019-12523 squid:4/squid: Improper input validation
-  in URI processor
-- Resolves: #1771272 - CVE-2019-18676 squid:4/squid: Buffer overflow in URI
-  processor
-- Resolves: #1771264 - CVE-2019-12526 squid:4/squid: Heap overflow issue in URN
-  processing
-- Resolves: #1738581 - CVE-2019-12529 squid: OOB read in Proxy-Authorization
-  header causes DoS
-
-* Tue Apr 28 2020 Lubos Uhliarik  - 7:4.4-9
-- Resolves: #1738583 - CVE-2019-12525 squid:4/squid: parsing of header
-  Proxy-Authentication leads to memory corruption
-- Resolves: #1828369 - CVE-2020-11945 squid: improper access restriction upon
+- Resolves: #1827564 - CVE-2020-11945 squid: improper access restriction upon
   Digest Authentication nonce replay could lead to remote code execution
-- Resolves: #1828370 - CVE-2019-12519 squid: improper check for new member in
-  ESIExpression::Evaluate allows for stack buffer overflow
 
-* Fri Aug 23 2019 Lubos Uhliarik  - 7:4.4-8
-- Resolves: # 1738485 - CVE-2019-12527 squid:4/squid: heap-based buffer overflow
-  in HttpHeader::getAuth
+* Thu Mar 26 2020 Lubos Uhliarik  - 7:4.10-4
+- Resolves: #1817208 - More cache_swap.sh optimizations
 
-* Wed Jul 31 2019 Lubos Uhliarik  - 7:4.4-7
-- Resolves: #1729436 - CVE-2019-13345 squid: XSS via user_name or auth parameter
-  in cachemgr.cgi
+* Wed Mar 25 2020 Lubos Uhliarik  - 7:4.10-3
+- Resolves: #1786485 - squid.service: use ${SQUID_CONF} rather than $SQUID_CONF
+- Resolves: #1798535 - CVE-2019-12528 squid: Information Disclosure issue in
+  FTP Gateway
+- Resolves: #1798554 - CVE-2020-8450 squid: Buffer overflow in a Squid acting
+  as reverse-proxy
+- Resolves: #1798541 - CVE-2020-8449 squid: Improper input validation issues 
+  in HTTP Request processing
 
-* Fri Jun 21 2019 Lubos Uhliarik  - 7:4.4-6
-- Resolves: #1679526 - Missing detailed configuration file
-- Resolves: #1703117 - RHEL 7 to 8 fails with squid installed because dirs
-  changed to symlinks
-- Resolves: #1691741 - Squid cache_peer DNS lookup failed when not all lower
-  case
-- Resolves: #1683527 - "Reloading" message on a fresh reboot after enabling
-  squid
+* Tue Jan 28 2020 Lubos Uhliarik  - 7:4.10-1
+- new version 4.10
 
-* Tue Dec 11 2018 Lubos Uhliarik  - 7:4.4-4
-- Resolves: #1612524 - Man page scan results for squid 
+* Tue Dec 17 2019 Lubos Uhliarik  - 7:4.9-3
+- Resolves: #1784383 - Add BuildRequires: systemd-rpm-macros
+- Resolves: #1783757 - Build with ./configure --with-pidfile=/run/squid.pid
+- Resolves: #1783768 - Optimize cache_swap.sh cache_dir search
 
-* Tue Dec 11 2018 Lubos Uhliarik  - 7:4.4-3
-- Resolves: #1642384 - squid doesn't work with active ftp
+* Mon Nov 11 2019 Lubos Uhliarik  - 7:4.9-2
+- new version 4.9
+- verify src taball signature by default in prep section
 
-* Tue Dec 11 2018 Lubos Uhliarik  - 7:4.4-2
-- Resolves: #1657847 - Unable to start Squid in Selinux Enforcing mode
+* Tue Oct 08 2019 Lubos Uhliarik  - 7:4.8-6
+- Resolves: #1741342 - Do not call autoconf at build time
+
+* Tue Oct 08 2019 Lubos Uhliarik  - 7:4.8-5
+- Resolves: #1716950 - Drop "sleep 1" from logrotate fragment
+
+* Thu Aug 22 2019 Lubomir Rintel  - 7:4.8-4
+- Move the NetworkManager dispatcher script out of /etc
+
+* Mon Aug 05 2019 Lubos Uhliarik  - 7:4.8-3
+- Resolves: #1737030 - depend on httpd-filesystem
+
+* Sat Jul 27 2019 Fedora Release Engineering  - 7:4.8-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Wed Jul 10 2019 Lubos Uhliarik  - 7:4.8-1
+- new version 4.8
+- Resolves: #1727745 - squid: CVe-2019-13345 squid: XSS via user_name or auth
+  parameter in cachemgr.cgi
+
+* Tue Jul 02 2019 Lubos Uhliarik  - 7:4.7-6
+- fix filepath to squid.conf.documented in squid's manpage
+- fix path to systemctl in nm script
+
+* Wed May 22 2019 Lubos Uhliarik  - 7:4.7-5
+- Related: #1709299 - Use upstream squid.service
+
+* Fri May 17 2019 Luboš Uhliarik  - 7:4.7-1
+- new version 4.7
+
+* Fri May 17 2019 Luboš Uhliarik  - 7:4.6-3
+- Resolves: #1709299 - Use upstream squid.service
+
+* Mon Apr 29 2019 Lubos Uhliarik  - 7:4.6-2
+- Resolves: #1599074 - squid: 3 coredumps every day
+
+* Wed Apr 24 2019 Lubos Uhliarik  - 7:4.6-1
+- new version 4.6
+- disabled strict checking due to gcc warnings
+
+* Sun Feb 03 2019 Fedora Release Engineering  - 7:4.4-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Mon Jan 14 2019 Björn Esser  - 7:4.4-2
+- Rebuilt for libcrypt.so.2 (#1666033)
 
 * Mon Dec 10 2018 Lubos Uhliarik  - 7:4.4-1
-- Resolves: #1656871 - squid rebase to 4.4
-- Resolves: #1645148 - CVE-2018-19131 squid: Cross-Site Scripting when
-  generating HTTPS response messages about TLS errors
-- Resolves: #1645156 - CVE-2018-19132 squid: Memory leak in SNMP query
-  rejection code
+- new version 4.4
 
-* Mon Aug 06 2018 Lubos Uhliarik  - 7:4.2-1
+* Sun Oct 14 2018 Peter Robinson  7:4.2-3
+- Drop obsolete legacy sys-v remanents
+
+* Mon Aug 20 2018 Luboš Uhliarik  - 7:4.2-2
+- Resolves: #1618790 - SELinux 'dac_override' denial for cache_swap.sh
+
+* Mon Aug 06 2018 Luboš Uhliarik  - 7:4.2-1
 - new version 4.2
 - enable back strict error checking
 
 * Wed Aug 01 2018 Luboš Uhliarik  - 7:4.1-1
 - new version 4.1
 
-* Mon Jun 04 2018 Luboš Uhliarik  - 7:4.0.23-5
-- Resolves: #1585617 - Build against libdb only instead of libdb4
-- disabled strict checking for now (squid can not be built with GCC8)
+* Sat Jul 14 2018 Fedora Release Engineering  - 7:4.0.25-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
 
-* Mon Apr 16 2018 Luboš Uhliarik  - 7:4.0.23-4
-- Resolves: #1566055 - module squid cannot be installed due to missing
-  perl(Crypt::OpenSSL::X509)
+* Thu Jun 28 2018 Luboš Uhliarik  - 7:4.0.25-1
+- new version 4.0.25
+
+* Mon Jun 04 2018 Luboš Uhliarik  - 7:4.0.24-2
+- removed obsolete BuildRequires (libdb4-devel)
+
+* Thu Mar 08 2018 Luboš Uhliarik  - 7:4.0.24-1
+- new version 4.0.24
+- disabled strict checking (removed -Werror)
 
 * Fri Feb 09 2018 Fedora Release Engineering  - 7:4.0.23-3
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
diff --git a/squid.sysconfig b/squid.sysconfig
new file mode 100644
index 0000000..f01b6e3
--- /dev/null
+++ b/squid.sysconfig
@@ -0,0 +1,5 @@
+# default squid options
+SQUID_OPTS=""
+
+# default squid conf file
+SQUID_CONF="/etc/squid/squid.conf"
diff --git a/squid.sysusers b/squid.sysusers
new file mode 100644
index 0000000..f9cc56b
--- /dev/null
+++ b/squid.sysusers
@@ -0,0 +1,2 @@
+g squid 23 -
+u squid 23 "Squid proxy user" /var/spool/squid /sbin/nologin