Index: squid-3.3.13/src/http.cc
===================================================================
--- squid-3.3.13.orig/src/http.cc
+++ squid-3.3.13/src/http.cc
@@ -173,6 +173,7 @@ void
 HttpStateData::httpStateConnClosed(const CommCloseCbParams &params)
 {
     debugs(11, 5, "httpStateFree: FD " << params.fd << ", httpState=" << params.data);
+    doneWithFwd = "httpStateConnClosed()"; // assume FwdState is monitoring too
     mustStop("HttpStateData::httpStateConnClosed");
 }
 
@@ -744,8 +745,7 @@ HttpStateData::processReplyHeader()
             flags.headers_parsed = true;
             newrep->sline.version = HttpVersion(1,1);
             newrep->sline.status = error;
-            HttpReply *vrep = setVirginReply(newrep);
-            entry->replaceHttpReply(vrep);
+            setVirginReply(newrep);
             ctx_exit(ctx);
             return;
         }
@@ -1769,7 +1769,8 @@ HttpStateData::httpBuildRequestHeader(Ht
 
         String strFwd = hdr_in->getList(HDR_X_FORWARDED_FOR);
 
-        if (strFwd.size() > 65536/2) {
+        // if we cannot double strFwd size, then it grew past 50% of the limit
+        if (!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?
@@ -2414,20 +2415,10 @@ HttpStateData::sentRequestBody(const Com
     ServerStateData::sentRequestBody(io);
 }
 
-// Quickly abort the transaction
-// TODO: destruction should be sufficient as the destructor should cleanup,
-// including canceling close handlers
 void
 HttpStateData::abortTransaction(const char *reason)
 {
     debugs(11,5, HERE << "aborting transaction for " << reason <<
            "; " << serverConnection << ", this " << this);
-
-    if (Comm::IsConnOpen(serverConnection)) {
-        serverConnection->close();
-        return;
-    }
-
-    fwd->handleUnregisteredServerEnd();
-    mustStop("HttpStateData::abortTransaction");
+    mustStop(reason);
 }
Index: squid-3.3.13/src/SquidString.h
===================================================================
--- squid-3.3.13.orig/src/SquidString.h
+++ squid-3.3.13/src/SquidString.h
@@ -146,6 +146,13 @@ public:
     _SQUID_INLINE_ int caseCmp(char const *, size_type count) const;
     _SQUID_INLINE_ int caseCmp(String const &) const;
 
+    /// Whether creating a totalLen-character string is safe (i.e., unlikely to assert).
+    /// Optional extras can be used for overflow-safe length addition.
+    /// Implementation has to add 1 because many String allocation methods do.
+    static bool CanGrowTo(size_type totalLen, const size_type extras = 0) { return SafeAdd(totalLen, extras) && SafeAdd(totalLen, 1); }
+    /// whether appending growthLen characters is safe (i.e., unlikely to assert)
+    bool canGrowBy(const size_type growthLen) const { return CanGrowTo(size(), growthLen); }
+
     String substr(size_type from, size_type to) const;
 
     _SQUID_INLINE_ void cut(size_type newLength);
@@ -162,10 +169,20 @@ private:
     _SQUID_INLINE_ bool nilCmp(bool, bool, int &) const;
 
     /* never reference these directly! */
-    size_type size_; /* buffer size; 64K limit */
+    size_type size_; /* buffer size; limited by SizeMax_ */
 
     size_type len_;  /* current length  */
 
+    static const size_type SizeMax_ = 65535; ///< 64K limit protects some fixed-size buffers
+    /// 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;
+    }
+
     char *buf_;
 
     _SQUID_INLINE_ void set(char const *loc, char const ch);
Index: squid-3.3.13/src/StrList.cc
===================================================================
--- squid-3.3.13.orig/src/StrList.cc
+++ squid-3.3.13/src/StrList.cc
@@ -33,20 +33,24 @@
 #include "squid.h"
 #include "SquidString.h"
 #include "StrList.h"
+#include "base/TextException.h"
 
 /** appends an item to the list */
 void
 strListAdd(String * str, const char *item, char del)
 {
     assert(str && item);
+    const String::size_type itemSize = strlen(item);
     if (str->size()) {
         char buf[3];
         buf[0] = del;
         buf[1] = ' ';
         buf[2] = '\0';
+        Must(str->canGrowBy(2));
         str->append(buf, 2);
     }
-    str->append(item, strlen(item));
+    Must(str->canGrowBy(itemSize));
+    str->append(item, itemSize);
 }
 
 /** returns true iff "m" is a member of the list */
Index: squid-3.3.13/src/String.cc
===================================================================
--- squid-3.3.13.orig/src/String.cc
+++ squid-3.3.13/src/String.cc
@@ -67,7 +67,7 @@ void
 String::setBuffer(char *aBuf, String::size_type aSize)
 {
     assert(undefined());
-    assert(aSize < 65536);
+    assert(aSize < SizeMax_);
     buf_ = aBuf;
     size_ = aSize;
 }
@@ -198,7 +198,7 @@ String::append( char const *str, int len
     } else {
         // Create a temporary string and absorb it later.
         String snew;
-        assert(len_ + len < 65536); // otherwise snew.len_ overflows below
+        assert(canGrowBy(len)); // otherwise snew.len_ overflows below
         snew.len_ = len_ + len;
         snew.allocBuffer(snew.len_ + 1);
 
Index: squid-3.3.13/src/Server.cc
===================================================================
--- squid-3.3.13.orig/src/Server.cc
+++ squid-3.3.13/src/Server.cc
@@ -68,6 +68,7 @@ ServerStateData::ServerStateData(FwdStat
         startedAdaptation(false),
 #endif
         receivedWholeRequestBody(false),
+        doneWithFwd(NULL),
         theVirginReply(NULL),
         theFinalReply(NULL)
 {
@@ -94,8 +95,6 @@ ServerStateData::~ServerStateData()
     HTTPMSGUNLOCK(theVirginReply);
     HTTPMSGUNLOCK(theFinalReply);
 
-    fwd = NULL; // refcounted
-
     if (responseBodyBuffer != NULL) {
         delete responseBodyBuffer;
         responseBodyBuffer = NULL;
@@ -113,6 +112,14 @@ ServerStateData::swanSong()
     cleanAdaptation();
 #endif
 
+    if (!doneWithServer())
+        closeServer();
+
+    if (!doneWithFwd) {
+        doneWithFwd = "swanSong()";
+        fwd->handleUnregisteredServerEnd();
+    }
+
     BodyConsumer::swanSong();
 #if USE_ADAPTATION
     Initiator::swanSong();
@@ -235,6 +242,7 @@ ServerStateData::completeForwarding()
 {
     debugs(11,5, HERE << "completing forwarding for "  << fwd);
     assert(fwd != NULL);
+    doneWithFwd = "completeForwarding()";
     fwd->complete();
 }
 
Index: squid-3.3.13/src/Server.h
===================================================================
--- squid-3.3.13.orig/src/Server.h
+++ squid-3.3.13/src/Server.h
@@ -192,6 +192,10 @@ protected:
 #endif
     bool receivedWholeRequestBody; ///< handleRequestBodyProductionEnded called
 
+    /// whether we should not be talking to FwdState; XXX: clear fwd instead
+    /// points to a string literal which is used only for debugging
+    const char *doneWithFwd;
+
 private:
     void sendBodyIsTooLargeError();
     void maybePurgeOthers();
Index: squid-3.3.13/src/ftp.cc
===================================================================
--- squid-3.3.13.orig/src/ftp.cc
+++ squid-3.3.13/src/ftp.cc
@@ -466,6 +466,7 @@ FtpStateData::ctrlClosed(const CommClose
 {
     debugs(9, 4, HERE);
     ctrl.clear();
+    doneWithFwd = "ctrlClosed()"; // assume FwdState is monitoring too
     mustStop("FtpStateData::ctrlClosed");
 }
 
@@ -3884,24 +3885,12 @@ FtpStateData::haveControlChannel(const c
     return true;
 }
 
-/**
- * Quickly abort the transaction
- *
- \todo destruction should be sufficient as the destructor should cleanup,
- *	including canceling close handlers
- */
 void
 FtpStateData::abortTransaction(const char *reason)
 {
     debugs(9, 3, HERE << "aborting transaction for " << reason <<
            "; FD " << (ctrl.conn!=NULL?ctrl.conn->fd:-1) << ", Data FD " << (data.conn!=NULL?data.conn->fd:-1) << ", this " << this);
-    if (Comm::IsConnOpen(ctrl.conn)) {
-        ctrl.conn->close();
-        return;
-    }
-
-    fwd->handleUnregisteredServerEnd();
-    mustStop("FtpStateData::abortTransaction");
+    mustStop(reason);
 }
 
 /// creates a data channel Comm close callback
Index: squid-3.3.13/src/esi/CustomParser.cc
===================================================================
--- squid-3.3.13.orig/src/esi/CustomParser.cc
+++ squid-3.3.13/src/esi/CustomParser.cc
@@ -110,9 +110,11 @@ ESICustomParser::parse(char const *dataT
     }
 
     size_t openESITags (0);
-    //erring on the safe side. Probably rawBuf would be ok too
-    char const *currentPos = content.termedBuf();
-    size_t remainingCount = content.size();
+    // TODO: convert to Tokenizer parse
+    // erring on the safe side for now. Probably rawContent would be ok too
+    // note that operations below do *X='\0' ... altering the 'const' buffer content.
+    char const *currentPos = content.c_str();
+    SBuf::size_type remainingCount = content.length();
     char const *tag = NULL;
 
     while ((tag = findTag(currentPos, remainingCount))) {
Index: squid-3.3.13/src/esi/CustomParser.h
===================================================================
--- squid-3.3.13.orig/src/esi/CustomParser.h
+++ squid-3.3.13/src/esi/CustomParser.h
@@ -35,7 +35,7 @@ class Trie;
 /* inherits from */
 #include "esi/Parser.h"
 
-/* for String variables */
+#include "SBuf.h"
 #include "SquidString.h"
 
 /**
@@ -67,7 +67,7 @@ private:
     ESIParserClient *theClient;
     String error;
     /* cheap n dirty - buffer it all */
-    String content;
+    SBuf content;
     /* TODO: make a class of this type code */
     ESITAG_t lastTag;
 };
Index: squid-3.3.13/src/SBuf.cc
===================================================================
--- /dev/null
+++ squid-3.3.13/src/SBuf.cc
@@ -0,0 +1,966 @@
+/*
+ * Copyright (C) 1996-2016 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/CharacterSet.h"
+#include "RefCount.h"
+#include "Debug.h"
+#include "OutOfBoundsException.h"
+#include "SBuf.h"
+#include "SBufDetailedStats.h"
+#include "SBufExceptions.h"
+#include "util.h"
+
+#include <cstring>
+#include <iostream>
+#include <sstream>
+
+#ifdef VA_COPY
+#undef VA_COPY
+#endif
+#if defined HAVE_VA_COPY
+#define VA_COPY va_copy
+#elif defined HAVE___VA_COPY
+#define VA_COPY __va_copy
+#endif
+
+InstanceIdDefinitions(SBuf, "SBuf");
+
+SBufStats SBuf::stats;
+const SBuf::size_type SBuf::npos;
+const SBuf::size_type SBuf::maxSize;
+
+SBufStats::SBufStats()
+    : alloc(0), allocCopy(0), allocFromString(0), allocFromCString(0),
+      assignFast(0), clear(0), append(0), toStream(0), setChar(0),
+      getChar(0), compareSlow(0), compareFast(0), copyOut(0),
+      rawAccess(0), nulTerminate(0), chop(0), trim(0), find(0), scanf(0),
+      caseChange(0), cowFast(0), cowSlow(0), live(0)
+{}
+
+SBufStats&
+SBufStats::operator +=(const SBufStats& ss)
+{
+    alloc += ss.alloc;
+    allocCopy += ss.allocCopy;
+    allocFromString += ss.allocFromString;
+    allocFromCString += ss.allocFromCString;
+    assignFast += ss.assignFast;
+    clear += ss.clear;
+    append += ss.append;
+    toStream += ss.toStream;
+    setChar += ss.setChar;
+    getChar += ss.getChar;
+    compareSlow += ss.compareSlow;
+    compareFast += ss.compareFast;
+    copyOut += ss.copyOut;
+    rawAccess += ss.rawAccess;
+    nulTerminate += ss.nulTerminate;
+    chop += ss.chop;
+    trim += ss.trim;
+    find += ss.find;
+    scanf += ss.scanf;
+    caseChange += ss.caseChange;
+    cowFast += ss.cowFast;
+    cowSlow += ss.cowSlow;
+    live += ss.live;
+
+    return *this;
+}
+
+SBuf::SBuf()
+    : store_(GetStorePrototype()), off_(0), len_(0)
+{
+    debugs(24, 8, id << " created");
+    ++stats.alloc;
+    ++stats.live;
+}
+
+SBuf::SBuf(const SBuf &S)
+    : store_(S.store_), off_(S.off_), len_(S.len_)
+{
+    debugs(24, 8, id << " created from id " << S.id);
+    ++stats.alloc;
+    ++stats.allocCopy;
+    ++stats.live;
+}
+
+SBuf::SBuf(const String &S)
+    : store_(GetStorePrototype()), off_(0), len_(0)
+{
+    debugs(24, 8, id << " created from string");
+    assign(S.rawBuf(), S.size());
+    ++stats.alloc;
+    ++stats.allocFromString;
+    ++stats.live;
+}
+
+SBuf::SBuf(const std::string &s)
+    : store_(GetStorePrototype()), off_(0), len_(0)
+{
+    debugs(24, 8, id << " created from std::string");
+    lowAppend(s.data(),s.length());
+    ++stats.alloc;
+    ++stats.allocFromString;
+    ++stats.live;
+}
+
+SBuf::SBuf(const char *S, size_type n)
+    : store_(GetStorePrototype()), off_(0), len_(0)
+{
+    append(S,n);
+    ++stats.alloc;
+    ++stats.allocFromCString;
+    ++stats.live;
+}
+
+SBuf::~SBuf()
+{
+    debugs(24, 8, id << " destructed");
+    --stats.live;
+    recordSBufSizeAtDestruct(len_);
+}
+
+MemBlob::Pointer
+SBuf::GetStorePrototype()
+{
+    static MemBlob::Pointer InitialStore = new MemBlob(0);
+    return InitialStore;
+}
+
+SBuf&
+SBuf::assign(const SBuf &S)
+{
+    debugs(24, 7, "assigning " << id << " from " <<  S.id);
+    if (&S == this) //assignment to self. Noop.
+        return *this;
+    ++stats.assignFast;
+    store_ = S.store_;
+    off_ = S.off_;
+    len_ = S.len_;
+    return *this;
+}
+
+SBuf&
+SBuf::assign(const char *S, size_type n)
+{
+    const Locker blobKeeper(this, S);
+    debugs(24, 6, id << " from c-string, n=" << n << ")");
+    clear();
+    return append(S, n); //bounds checked in append()
+}
+
+void
+SBuf::reserveCapacity(size_type minCapacity)
+{
+    Must(minCapacity <= maxSize);
+    cow(minCapacity);
+}
+
+SBuf::size_type
+SBuf::reserve(const SBufReservationRequirements &req)
+{
+    debugs(24, 8, id << " was: " << off_ << '+' << len_ << '+' << spaceSize() <<
+           '=' << store_->capacity);
+
+    const bool mustRealloc = !req.allowShared && store_->LockCount() > 1;
+
+    if (!mustRealloc && spaceSize() >= req.minSpace)
+        return spaceSize(); // the caller is content with what we have
+
+    /* only reallocation can make the caller happy */
+
+    if (!mustRealloc && len_ >= req.maxCapacity)
+        return spaceSize(); // but we cannot reallocate
+
+    const size_type newSpace = std::min(req.idealSpace, maxSize - len_);
+    reserveCapacity(std::min(len_ + newSpace, req.maxCapacity));
+    debugs(24, 7, id << " now: " << off_ << '+' << len_ << '+' << spaceSize() <<
+           '=' << store_->capacity);
+    return spaceSize(); // reallocated and probably reserved enough space
+}
+
+char *
+SBuf::rawSpace(size_type minSpace)
+{
+    Must(length() <= maxSize - minSpace);
+    debugs(24, 7, "reserving " << minSpace << " for " << id);
+    ++stats.rawAccess;
+    // we're not concerned about RefCounts here,
+    // the store knows the last-used portion. If
+    // it's available, we're effectively claiming ownership
+    // of it. If it's not, we need to go away (realloc)
+    if (store_->canAppend(off_+len_, minSpace)) {
+        debugs(24, 7, id << " not growing");
+        return bufEnd();
+    }
+    // TODO: we may try to memmove before realloc'ing in order to avoid
+    //   one allocation operation, if we're the sole owners of a MemBlob.
+    //   Maybe some heuristic on off_ and length()?
+    cow(minSpace+length());
+    return bufEnd();
+}
+
+void
+SBuf::clear()
+{
+#if 0
+    //enabling this code path, the store will be freed and reinitialized
+    store_ = GetStorePrototype(); //uncomment to actually free storage upon clear()
+#else
+    //enabling this code path, we try to release the store without deallocating it.
+    // will be lazily reallocated if needed.
+    if (store_->LockCount() == 1)
+        store_->clear();
+#endif
+    len_ = 0;
+    off_ = 0;
+    ++stats.clear;
+}
+
+SBuf&
+SBuf::append(const SBuf &S)
+{
+    const Locker blobKeeper(this, S.buf());
+    return lowAppend(S.buf(), S.length());
+}
+
+SBuf &
+SBuf::append(const char * S, size_type Ssize)
+{
+    const Locker blobKeeper(this, S);
+    if (S == NULL)
+        return *this;
+    if (Ssize == SBuf::npos)
+        Ssize = strlen(S);
+    debugs(24, 7, "from c-string to id " << id);
+    // coverity[access_dbuff_in_call]
+    return lowAppend(S, Ssize);
+}
+
+SBuf &
+SBuf::append(const char c)
+{
+    return lowAppend(&c, 1);
+}
+
+SBuf&
+SBuf::Printf(const char *fmt, ...)
+{
+    // with printf() the fmt or an arg might be a dangerous char*
+    // NP: cant rely on vappendf() Locker because of clear()
+    const Locker blobKeeper(this, buf());
+
+    va_list args;
+    va_start(args, fmt);
+    clear();
+    vappendf(fmt, args);
+    va_end(args);
+    return *this;
+}
+
+SBuf&
+SBuf::appendf(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    vappendf(fmt, args);
+    va_end(args);
+    return *this;
+}
+
+SBuf&
+SBuf::vappendf(const char *fmt, va_list vargs)
+{
+    // with (v)appendf() the fmt or an arg might be a dangerous char*
+    const Locker blobKeeper(this, buf());
+
+    Must(fmt != NULL);
+    int sz = 0;
+    //reserve twice the format-string size, it's a likely heuristic
+    size_type requiredSpaceEstimate = strlen(fmt)*2;
+
+    char *space = rawSpace(requiredSpaceEstimate);
+#ifdef VA_COPY
+    va_list ap;
+    VA_COPY(ap, vargs);
+    sz = vsnprintf(space, spaceSize(), fmt, ap);
+    va_end(ap);
+#else
+    sz = vsnprintf(space, spaceSize(), fmt, vargs);
+#endif
+
+    /* check for possible overflow */
+    /* snprintf on Linux returns -1 on output errors, or the size
+     * that would have been written if enough space had been available */
+    /* vsnprintf is standard in C99 */
+
+    if (sz >= static_cast<int>(spaceSize())) {
+        // not enough space on the first go, we now know how much we need
+        requiredSpaceEstimate = sz*2; // TODO: tune heuristics
+        space = rawSpace(requiredSpaceEstimate);
+        sz = vsnprintf(space, spaceSize(), fmt, vargs);
+        if (sz < 0) // output error in vsnprintf
+            throw TextException("output error in second-go vsnprintf",__FILE__,
+                                __LINE__);
+    }
+
+    if (sz < 0) // output error in either vsnprintf
+        throw TextException("output error in vsnprintf",__FILE__, __LINE__);
+
+    // data was appended, update internal state
+    len_ += sz;
+
+    /* C99 specifies that the final '\0' is not counted in vsnprintf's
+     * return value. Older compilers/libraries might instead count it */
+    /* check whether '\0' was appended and counted */
+    static bool snPrintfTerminatorChecked = false;
+    static bool snPrintfTerminatorCounted = false;
+    if (!snPrintfTerminatorChecked) {
+        char testbuf[16];
+        snPrintfTerminatorCounted = snprintf(testbuf, sizeof(testbuf),
+                                             "%s", "1") == 2;
+        snPrintfTerminatorChecked = true;
+    }
+    if (snPrintfTerminatorCounted) {
+        --sz;
+        --len_;
+    }
+
+    store_->size += sz;
+    ++stats.append;
+
+    return *this;
+}
+
+std::ostream&
+SBuf::print(std::ostream &os) const
+{
+    os.write(buf(), length());
+    ++stats.toStream;
+    return os;
+}
+
+std::ostream&
+SBuf::dump(std::ostream &os) const
+{
+    os << id
+       << ": ";
+    store_->dump(os);
+    os << ", offset:" << off_
+       << ", len:" << len_
+       << ") : '";
+    print(os);
+    os << '\'' << std::endl;
+    return os;
+# if 0
+    // alternate implementation, based on Raw() API.
+    os << Raw("SBuf", buf(), length()) <<
+       ". id: " << id <<
+       ", offset:" << off_ <<
+       ", len:" << len_ <<
+       ", store: ";
+    store_->dump(os);
+    os << std::endl;
+    return os;
+#endif
+}
+
+void
+SBuf::setAt(size_type pos, char toset)
+{
+    checkAccessBounds(pos);
+    cow();
+    store_->mem[off_+pos] = toset;
+    ++stats.setChar;
+}
+
+static int
+memcasecmp(const char *b1, const char *b2, SBuf::size_type len)
+{
+    int rv=0;
+    while (len > 0) {
+        rv = tolower(*b1)-tolower(*b2);
+        if (rv != 0)
+            return rv;
+        ++b1;
+        ++b2;
+        --len;
+    }
+    return rv;
+}
+
+int
+SBuf::compare(const SBuf &S, const SBufCaseSensitive isCaseSensitive, const size_type n) const
+{
+    if (n != npos)
+        return substr(0,n).compare(S.substr(0,n),isCaseSensitive);
+
+    const size_type byteCompareLen = min(S.length(), length());
+    ++stats.compareSlow;
+    int rv = 0;
+    if (isCaseSensitive == caseSensitive) {
+        rv = memcmp(buf(), S.buf(), byteCompareLen);
+    } else {
+        rv = memcasecmp(buf(), S.buf(), byteCompareLen);
+    }
+    if (rv != 0)
+        return rv;
+    if (length() == S.length())
+        return 0;
+    if (length() > S.length())
+        return 1;
+    return -1;
+}
+
+int
+SBuf::compare(const char *s, const SBufCaseSensitive isCaseSensitive, const size_type n) const
+{
+    // 0-length comparison is always true regardless of buffer states
+    if (!n) {
+        ++stats.compareFast;
+        return 0;
+    }
+
+    // N-length compare MUST provide a non-NULL C-string pointer
+    assert(s);
+
+    // when this is a 0-length string, no need for any complexity.
+    if (!length()) {
+        ++stats.compareFast;
+        return '\0' - *s;
+    }
+
+    // brute-force scan in order to avoid ever needing strlen() on a c-string.
+    ++stats.compareSlow;
+    const char *left = buf();
+    const char *right = s;
+    int rv = 0;
+    // what area to scan.
+    // n may be npos, but we treat that as a huge positive value
+    size_type byteCount = min(length(), n);
+
+    // loop until we find a difference, a '\0', or reach the end of area to scan
+    if (isCaseSensitive == caseSensitive) {
+        while ((rv = *left - *right++) == 0) {
+            if (*left++ == '\0' || --byteCount == 0)
+                break;
+        }
+    } else {
+        while ((rv = tolower(*left) - tolower(*right++)) == 0) {
+            if (*left++ == '\0' || --byteCount == 0)
+                break;
+        }
+    }
+
+    // If we stopped scanning because we reached the end
+    //  of buf() before we reached the end of s,
+    // pretend we have a 0-terminator there to compare.
+    // NP: the loop already incremented "right" ready for this comparison
+    if (!byteCount && length() < n)
+        return '\0' - *right;
+
+    // If we found a difference within the scan area,
+    // or we found a '\0',
+    // or all n characters were identical (and none was \0).
+    return rv;
+}
+
+bool
+SBuf::startsWith(const SBuf &S, const SBufCaseSensitive isCaseSensitive) const
+{
+    debugs(24, 8, id << " startsWith " << S.id << ", caseSensitive: " <<
+           isCaseSensitive);
+    if (length() < S.length()) {
+        debugs(24, 8, "no, too short");
+        ++stats.compareFast;
+        return false;
+    }
+    return (compare(S, isCaseSensitive, S.length()) == 0);
+}
+
+bool
+SBuf::operator ==(const SBuf & S) const
+{
+    debugs(24, 8, id << " == " << S.id);
+    if (length() != S.length()) {
+        debugs(24, 8, "no, different lengths");
+        ++stats.compareFast;
+        return false; //shortcut: must be equal length
+    }
+    if (store_ == S.store_ && off_ == S.off_) {
+        debugs(24, 8, "yes, same length and backing store");
+        ++stats.compareFast;
+        return true;  //shortcut: same store, offset and length
+    }
+    ++stats.compareSlow;
+    const bool rv = (0 == memcmp(buf(), S.buf(), length()));
+    debugs(24, 8, "returning " << rv);
+    return rv;
+}
+
+bool
+SBuf::operator !=(const SBuf & S) const
+{
+    return !(*this == S);
+}
+
+SBuf
+SBuf::consume(size_type n)
+{
+    if (n == npos)
+        n = length();
+    else
+        n = min(n, length());
+    debugs(24, 8, id << " consume " << n);
+    SBuf rv(substr(0, n));
+    chop(n);
+    return rv;
+}
+
+const
+SBufStats& SBuf::GetStats()
+{
+    return stats;
+}
+
+SBuf::size_type
+SBuf::copy(char *dest, size_type n) const
+{
+    size_type toexport = min(n,length());
+    memcpy(dest, buf(), toexport);
+    ++stats.copyOut;
+    return toexport;
+}
+
+const char*
+SBuf::rawContent() const
+{
+    ++stats.rawAccess;
+    return buf();
+}
+
+void
+SBuf::forceSize(size_type newSize)
+{
+    debugs(24, 8, id << " force " << (newSize > length() ? "grow" : "shrink") << " to length=" << newSize);
+
+    Must(store_->LockCount() == 1);
+    if (newSize > min(maxSize,store_->capacity-off_))
+        throw SBufTooBigException(__FILE__,__LINE__);
+    len_ = newSize;
+    store_->size = newSize;
+}
+
+const char*
+SBuf::c_str()
+{
+    ++stats.rawAccess;
+    /* null-terminate the current buffer, by hand-appending a \0 at its tail but
+     * without increasing its length. May COW, the side-effect is to guarantee that
+     * the MemBlob's tail is availabe for us to use */
+    *rawSpace(1) = '\0';
+    ++store_->size;
+    ++stats.setChar;
+    ++stats.nulTerminate;
+    return buf();
+}
+
+SBuf&
+SBuf::chop(size_type pos, size_type n)
+{
+    if (pos == npos || pos > length())
+        pos = length();
+
+    if (n == npos || (pos+n) > length())
+        n = length() - pos;
+
+    // if there will be nothing left, reset the buffer while we can
+    if (pos == length() || n == 0) {
+        clear();
+        return *this;
+    }
+
+    ++stats.chop;
+    off_ += pos;
+    len_ = n;
+    return *this;
+}
+
+SBuf&
+SBuf::trim(const SBuf &toRemove, bool atBeginning, bool atEnd)
+{
+    ++stats.trim;
+    if (atEnd) {
+        const char *p = bufEnd()-1;
+        while (!isEmpty() && memchr(toRemove.buf(), *p, toRemove.length()) != NULL) {
+            //current end-of-buf is in the searched set
+            --len_;
+            --p;
+        }
+    }
+    if (atBeginning) {
+        const char *p = buf();
+        while (!isEmpty() && memchr(toRemove.buf(), *p, toRemove.length()) != NULL) {
+            --len_;
+            ++off_;
+            ++p;
+        }
+    }
+    if (isEmpty())
+        clear();
+    return *this;
+}
+
+SBuf
+SBuf::substr(size_type pos, size_type n) const
+{
+    SBuf rv(*this);
+    rv.chop(pos, n); //stats handled by callee
+    return rv;
+}
+
+SBuf::size_type
+SBuf::find(char c, size_type startPos) const
+{
+    ++stats.find;
+
+    if (startPos == npos) // can't find anything if we look past end of SBuf
+        return npos;
+
+    // std::string returns npos if needle is outside hay
+    if (startPos > length())
+        return npos;
+
+    const void *i = memchr(buf()+startPos, (int)c, (size_type)length()-startPos);
+
+    if (i == NULL)
+        return npos;
+
+    return (static_cast<const char *>(i)-buf());
+}
+
+SBuf::size_type
+SBuf::find(const SBuf &needle, size_type startPos) const
+{
+    if (startPos == npos) { // can't find anything if we look past end of SBuf
+        ++stats.find;
+        return npos;
+    }
+
+    // std::string allows needle to overhang hay but not start outside
+    if (startPos > length()) {
+        ++stats.find;
+        return npos;
+    }
+
+    // for empty needle std::string returns startPos
+    if (needle.length() == 0) {
+        ++stats.find;
+        return startPos;
+    }
+
+    // if needle length is 1 use the char search
+    if (needle.length() == 1)
+        return find(needle[0], startPos);
+
+    ++stats.find;
+
+    char *begin = buf()+startPos;
+    char *lastPossible = buf()+length()-needle.length()+1;
+    char needleBegin = needle[0];
+
+    debugs(24, 7, "looking for " << needle << "starting at " << startPos <<
+           " in id " << id);
+    while (begin < lastPossible) {
+        char *tmp;
+        debugs(24, 8, " begin=" << (void *) begin <<
+               ", lastPossible=" << (void*) lastPossible );
+        tmp = static_cast<char *>(memchr(begin, needleBegin, lastPossible-begin));
+        if (tmp == NULL) {
+            debugs(24, 8 , "First byte not found");
+            return npos;
+        }
+        // lastPossible guarrantees no out-of-bounds with memcmp()
+        if (0 == memcmp(needle.buf(), tmp, needle.length())) {
+            debugs(24, 8, "Found at " << (tmp-buf()));
+            return (tmp-buf());
+        }
+        begin = tmp+1;
+    }
+    debugs(24, 8, "not found");
+    return npos;
+}
+
+SBuf::size_type
+SBuf::rfind(const SBuf &needle, SBuf::size_type endPos) const
+{
+    // when the needle is 1 char, use the 1-char rfind()
+    if (needle.length() == 1)
+        return rfind(needle[0], endPos);
+
+    ++stats.find;
+
+    // needle is bigger than haystack, impossible find
+    if (length() < needle.length())
+        return npos;
+
+    // if startPos is npos, std::string scans from the end of hay
+    if (endPos == npos || endPos > length()-needle.length())
+        endPos = length()-needle.length();
+
+    // an empty needle found at the end of the haystack
+    if (needle.length() == 0)
+        return endPos;
+
+    char *bufBegin = buf();
+    char *cur = bufBegin+endPos;
+    const char needleBegin = needle[0];
+    while (cur >= bufBegin) {
+        if (*cur == needleBegin) {
+            if (0 == memcmp(needle.buf(), cur, needle.length())) {
+                // found
+                return (cur-buf());
+            }
+        }
+        --cur;
+    }
+    return npos;
+}
+
+SBuf::size_type
+SBuf::rfind(char c, SBuf::size_type endPos) const
+{
+    ++stats.find;
+
+    // shortcut: haystack is empty, can't find anything by definition
+    if (length() == 0)
+        return npos;
+
+    // on npos input std::string compares last octet of hay
+    if (endPos == npos || endPos >= length()) {
+        endPos = length();
+    } else {
+        // NP: off-by-one weirdness:
+        // endPos is an offset ... 0-based
+        // length() is a count ... 1-based
+        // memrhr() requires a 1-based count of space to scan.
+        ++endPos;
+    }
+
+    if (length() == 0)
+        return endPos;
+
+    const void *i = memrchr(buf(), (int)c, (size_type)endPos);
+
+    if (i == NULL)
+        return npos;
+
+    return (static_cast<const char *>(i)-buf());
+}
+
+SBuf::size_type
+SBuf::findFirstOf(const CharacterSet &set, size_type startPos) const
+{
+    ++stats.find;
+
+    if (startPos == npos)
+        return npos;
+
+    if (startPos >= length())
+        return npos;
+
+    debugs(24, 7, "first of characterset " << set.name << " in id " << id);
+    char *cur = buf()+startPos;
+    const char *end = bufEnd();
+    while (cur < end) {
+        if (set[*cur])
+            return cur-buf();
+        ++cur;
+    }
+    debugs(24, 7, "not found");
+    return npos;
+}
+
+SBuf::size_type
+SBuf::findFirstNotOf(const CharacterSet &set, size_type startPos) const
+{
+    ++stats.find;
+
+    if (startPos == npos)
+        return npos;
+
+    if (startPos >= length())
+        return npos;
+
+    debugs(24, 7, "first not of characterset " << set.name << " in id " << id);
+    char *cur = buf()+startPos;
+    const char *end = bufEnd();
+    while (cur < end) {
+        if (!set[*cur])
+            return cur-buf();
+        ++cur;
+    }
+    debugs(24, 7, "not found");
+    return npos;
+}
+
+/*
+ * TODO: borrow a sscanf implementation from Linux or similar?
+ * we'd really need a vsnscanf(3)... ? As an alternative, a
+ * light-regexp-like domain-specific syntax might be an idea.
+ */
+int
+SBuf::scanf(const char *format, ...)
+{
+    // with the format or an arg might be a dangerous char*
+    // that gets invalidated by c_str()
+    const Locker blobKeeper(this, buf());
+
+    va_list arg;
+    int rv;
+    ++stats.scanf;
+    va_start(arg, format);
+    rv = vsscanf(c_str(), format, arg);
+    va_end(arg);
+    return rv;
+}
+
+std::ostream &
+SBufStats::dump(std::ostream& os) const
+{
+    MemBlobStats ststats = MemBlob::GetStats();
+    os <<
+       "SBuf stats:\nnumber of allocations: " << alloc <<
+       "\ncopy-allocations: " << allocCopy <<
+       "\ncopy-allocations from SquidString: " << allocFromString <<
+       "\ncopy-allocations from C String: " << allocFromCString <<
+       "\nlive references: " << live <<
+       "\nno-copy assignments: " << assignFast <<
+       "\nclearing operations: " << clear <<
+       "\nappend operations: " << append <<
+       "\ndump-to-ostream: " << toStream <<
+       "\nset-char: " << setChar <<
+       "\nget-char: " << getChar <<
+       "\ncomparisons with data-scan: " << compareSlow <<
+       "\ncomparisons not requiring data-scan: " << compareFast <<
+       "\ncopy-out ops: " << copyOut <<
+       "\nraw access to memory: " << rawAccess <<
+       "\nNULL terminate C string: " << nulTerminate <<
+       "\nchop operations: " << chop <<
+       "\ntrim operations: " << trim <<
+       "\nfind: " << find <<
+       "\nscanf: " << scanf <<
+       "\ncase-change ops: " << caseChange <<
+       "\nCOW not actually requiring a copy: " << cowFast <<
+       "\nCOW: " << cowSlow <<
+       "\naverage store share factor: " <<
+       (ststats.live != 0 ? static_cast<float>(live)/ststats.live : 0) <<
+       std::endl;
+    return os;
+}
+
+void
+SBuf::toLower()
+{
+    debugs(24, 8, "\"" << *this << "\"");
+    for (size_type j = 0; j < length(); ++j) {
+        const int c = (*this)[j];
+        if (isupper(c))
+            setAt(j, tolower(c));
+    }
+    debugs(24, 8, "result: \"" << *this << "\"");
+    ++stats.caseChange;
+}
+
+void
+SBuf::toUpper()
+{
+    debugs(24, 8, "\"" << *this << "\"");
+    for (size_type j = 0; j < length(); ++j) {
+        const int c = (*this)[j];
+        if (islower(c))
+            setAt(j, toupper(c));
+    }
+    debugs(24, 8, "result: \"" << *this << "\"");
+    ++stats.caseChange;
+}
+
+/**
+ * checks whether the requested 'pos' is within the bounds of the SBuf
+ * \throw OutOfBoundsException if access is out of bounds
+ */
+void
+SBuf::checkAccessBounds(size_type pos) const
+{
+    if (pos >= length())
+        throw OutOfBoundsException(*this, pos, __FILE__, __LINE__);
+}
+
+String
+SBuf::toString() const
+{
+    String rv;
+    rv.limitInit(buf(), length());
+    ++stats.copyOut;
+    return rv;
+}
+
+/** re-allocate the backing store of the SBuf.
+ *
+ * If there are contents in the SBuf, they will be copied over.
+ * NO verifications are made on the size parameters, it's up to the caller to
+ * make sure that the new size is big enough to hold the copied contents.
+ * The re-allocated storage MAY be bigger than the requested size due to size-chunking
+ * algorithms in MemBlock, it is guarranteed NOT to be smaller.
+ */
+void
+SBuf::reAlloc(size_type newsize)
+{
+    debugs(24, 8, id << " new size: " << newsize);
+    if (newsize > maxSize)
+        throw SBufTooBigException(__FILE__, __LINE__);
+    MemBlob::Pointer newbuf = new MemBlob(newsize);
+    if (length() > 0)
+        newbuf->append(buf(), length());
+    store_ = newbuf;
+    off_ = 0;
+    ++stats.cowSlow;
+    debugs(24, 7, id << " new store capacity: " << store_->capacity);
+}
+
+SBuf&
+SBuf::lowAppend(const char * memArea, size_type areaSize)
+{
+    rawSpace(areaSize); //called method also checks n <= maxSize()
+    store_->append(memArea, areaSize);
+    len_ += areaSize;
+    ++stats.append;
+    return *this;
+}
+
+/**
+ * copy-on-write: make sure that we are the only holder of the backing store.
+ * If not, reallocate. If a new size is specified, and it is greater than the
+ * current length, the backing store will be extended as needed
+ */
+void
+SBuf::cow(SBuf::size_type newsize)
+{
+    debugs(24, 8, id << " new size:" << newsize);
+    if (newsize == npos || newsize < length())
+        newsize = length();
+
+    if (store_->LockCount() == 1 && newsize == length()) {
+        debugs(24, 8, id << " no cow needed");
+        ++stats.cowFast;
+        return;
+    }
+    reAlloc(newsize);
+}
+
Index: squid-3.3.13/src/SBuf.h
===================================================================
--- /dev/null
+++ squid-3.3.13/src/SBuf.h
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 1996-2016 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_SBUF_H
+#define SQUID_SBUF_H
+
+#include "base/InstanceId.h"
+#include "MemBlob.h"
+#include "SBufExceptions.h"
+#include "SquidString.h"
+
+#include <climits>
+#include <cstdarg>
+#include <iosfwd>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* SBuf placeholder for printf */
+#ifndef SQUIDSBUFPH
+#define SQUIDSBUFPH "%.*s"
+#define SQUIDSBUFPRINT(s) (s).plength(),(s).rawContent()
+#endif /* SQUIDSBUFPH */
+
+// TODO: move within SBuf and rename
+typedef enum {
+    caseSensitive,
+    caseInsensitive
+} SBufCaseSensitive;
+
+/**
+ * Container for various SBuf class-wide statistics.
+ *
+ * The stats are not completely accurate; they're mostly meant to
+ * understand whether Squid is leaking resources
+ * and whether SBuf is paying off the expected gains.
+ */
+class SBufStats
+{
+public:
+    uint64_t alloc; ///<number of calls to SBuf constructors
+    uint64_t allocCopy; ///<number of calls to SBuf copy-constructor
+    uint64_t allocFromString; ///<number of copy-allocations from Strings
+    uint64_t allocFromCString; ///<number of copy-allocations from c-strings
+    uint64_t assignFast; ///<number of no-copy assignment operations
+    uint64_t clear; ///<number of clear operations
+    uint64_t append; ///<number of append operations
+    uint64_t toStream;  ///<number of write operations to ostreams
+    uint64_t setChar; ///<number of calls to setAt
+    uint64_t getChar; ///<number of calls to at() and operator[]
+    uint64_t compareSlow; ///<number of comparison operations requiring data scan
+    uint64_t compareFast; ///<number of comparison operations not requiring data scan
+    uint64_t copyOut; ///<number of data-copies to other forms of buffers
+    uint64_t rawAccess; ///<number of accesses to raw contents
+    uint64_t nulTerminate; ///<number of c_str() terminations
+    uint64_t chop;  ///<number of chop operations
+    uint64_t trim;  ///<number of trim operations
+    uint64_t find;  ///<number of find operations
+    uint64_t scanf;  ///<number of scanf operations
+    uint64_t caseChange; ///<number of toUpper and toLower operations
+    uint64_t cowFast; ///<number of cow operations not actually requiring a copy
+    uint64_t cowSlow; ///<number of cow operations requiring a copy
+    uint64_t live;  ///<number of currently-allocated SBuf
+
+    ///Dump statistics to an ostream.
+    std::ostream& dump(std::ostream &os) const;
+    SBufStats();
+
+    SBufStats& operator +=(const SBufStats&);
+};
+
+class CharacterSet;
+class SBufReservationRequirements;
+
+/**
+ * A String or Buffer.
+ * Features: refcounted backing store, cheap copy and sub-stringing
+ * operations, copy-on-write to isolate change operations to each instance.
+ * Where possible, we're trying to mimic std::string's interface.
+ */
+class SBuf
+{
+public:
+    typedef MemBlob::size_type size_type;
+    static const size_type npos = 0xffffffff; // max(uint32_t)
+
+    /// Maximum size of a SBuf. By design it MUST be < MAX(size_type)/2. Currently 256Mb.
+    static const size_type maxSize = 0xfffffff;
+
+    /// create an empty (zero-size) SBuf
+    SBuf();
+    SBuf(const SBuf &S);
+
+    /** Constructor: import c-style string
+     *
+     * Create a new SBuf containing a COPY of the contents of the
+     * c-string
+     * \param S the c string to be copied
+     * \param n how many bytes to import into the SBuf. If it is npos
+     *              or unspecified, imports to end-of-cstring
+     * \note it is the caller's responsibility not to go out of bounds
+     * \note bounds is 0 <= pos < length(); caller must pay attention to signedness
+     */
+    explicit SBuf(const char *S, size_type n = npos);
+
+    /** Constructor: import SquidString, copying contents.
+     *
+     * This method will be removed once SquidString has gone.
+     */
+    explicit SBuf(const String &S);
+
+    /// Constructor: import std::string. Contents are copied.
+    explicit SBuf(const std::string &s);
+
+    ~SBuf();
+
+    /** Explicit assignment.
+     *
+     * Current SBuf will share backing store with the assigned one.
+     */
+    SBuf& assign(const SBuf &S);
+
+    /** Assignment operator.
+     *
+     * Current SBuf will share backing store with the assigned one.
+     */
+    SBuf& operator =(const SBuf & S) {return assign(S);}
+
+    /** Import a c-string into a SBuf, copying the data.
+     *
+     * It is the caller's duty to free the imported string, if needed.
+     * \param S the c string to be copied
+     * \param n how many bytes to import into the SBuf. If it is npos
+     *              or unspecified, imports to end-of-cstring
+     * \note it is the caller's responsibility not to go out of bounds
+     * \note to assign a std::string use the pattern:
+     *    assign(stdstr.data(), stdstd.length())
+     */
+    SBuf& assign(const char *S, size_type n = npos);
+
+    /** Assignment operator. Copy a NULL-terminated c-style string into a SBuf.
+     *
+     * Copy a c-style string into a SBuf. Shortcut for SBuf.assign(S)
+     * It is the caller's duty to free the imported string, if needed.
+     * \note not \0-clean
+     */
+    SBuf& operator =(const char *S) {return assign(S);}
+
+    /** reset the SBuf as if it was just created.
+     *
+     * Resets the SBuf to empty, memory is freed lazily.
+     */
+    void clear();
+
+    /** Append operation
+     *
+     * Append the supplied SBuf to the current one; extend storage as needed.
+     */
+    SBuf& append(const SBuf & S);
+
+    /// Append a single character. The character may be NUL (\0).
+    SBuf& append(const char c);
+
+    /** Append operation for C-style strings.
+     *
+     * Append the supplied c-string to the SBuf; extend storage
+     * as needed.
+     *
+     * \param S the c string to be copied. Can be NULL.
+     * \param Ssize how many bytes to import into the SBuf. If it is npos
+     *              or unspecified, imports to end-of-cstring. If S is NULL,
+     *              Ssize is ignored.
+     * \note to append a std::string use the pattern
+     *     cstr_append(stdstr.data(), stdstd.length())
+     */
+    SBuf& append(const char * S, size_type Ssize = npos);
+
+    /** Assignment operation with printf(3)-style definition
+     * \note arguments may be evaluated more than once, be careful
+     *       of side-effects
+     */
+    SBuf& Printf(const char *fmt, ...);
+
+    /** Append operation with printf-style arguments
+     * \note arguments may be evaluated more than once, be careful
+     *       of side-effects
+     */
+    SBuf& appendf(const char *fmt, ...);
+
+    /** Append operation, with vsprintf(3)-style arguments.
+     * \note arguments may be evaluated more than once, be careful
+     *       of side-effects
+     */
+    SBuf& vappendf(const char *fmt, va_list vargs);
+
+    /// print the SBuf contents to the supplied ostream
+    std::ostream& print(std::ostream &os) const;
+
+    /** print SBuf contents and debug information about the SBuf to an ostream
+     *
+     * Debug function, dumps to a stream informations on the current SBuf,
+     * including low-level details and statistics.
+     */
+    std::ostream& dump(std::ostream &os) const;
+
+    /** random-access read to any char within the SBuf
+     *
+     * does not check access bounds. If you need that, use at()
+     */
+    char operator [](size_type pos) const {++stats.getChar; return store_->mem[off_+pos];}
+
+    /** random-access read to any char within the SBuf.
+     *
+     * \throw OutOfBoundsException when access is out of bounds
+     * \note bounds is 0 <= pos < length(); caller must pay attention to signedness
+     */
+    char at(size_type pos) const {checkAccessBounds(pos); return operator[](pos);}
+
+    /** direct-access set a byte at a specified operation.
+     *
+     * \param pos the position to be overwritten
+     * \param toset the value to be written
+     * \throw OutOfBoundsException when pos is of bounds
+     * \note bounds is 0 <= pos < length(); caller must pay attention to signedness
+     * \note performs a copy-on-write if needed.
+     */
+    void setAt(size_type pos, char toset);
+
+    /** compare to other SBuf, str(case)cmp-style
+     *
+     * \param isCaseSensitive one of caseSensitive or caseInsensitive
+     * \param n compare up to this many bytes. if npos (default), compare whole SBufs
+     * \retval >0 argument of the call is greater than called SBuf
+     * \retval <0 argument of the call is smaller than called SBuf
+     * \retval 0  argument of the call has the same contents of called SBuf
+     */
+    int compare(const SBuf &S, const SBufCaseSensitive isCaseSensitive, const size_type n = npos) const;
+
+    /// shorthand version for compare()
+    inline int cmp(const SBuf &S, const size_type n = npos) const {
+        return compare(S,caseSensitive,n);
+    }
+
+    /// shorthand version for case-insensitive compare()
+    inline int caseCmp(const SBuf &S, const size_type n = npos) const {
+        return compare(S,caseInsensitive,n);
+    }
+
+    /// Comparison with a C-string.
+    int compare(const char *s, const SBufCaseSensitive isCaseSensitive, const size_type n = npos) const;
+
+    /// Shorthand version for C-string compare().
+    inline int cmp(const char *S, const size_type n = npos) const {
+        return compare(S,caseSensitive,n);
+    }
+
+    /// Shorthand version for case-insensitive C-string compare().
+    inline int caseCmp(const char *S, const size_type n = npos) const {
+        return compare(S,caseInsensitive,n);
+    }
+
+    /** check whether the entire supplied argument is a prefix of the SBuf.
+     *  \param S the prefix to match against
+     *  \param isCaseSensitive one of caseSensitive or caseInsensitive
+     *  \retval true argument is a prefix of the SBuf
+     */
+    bool startsWith(const SBuf &S, const SBufCaseSensitive isCaseSensitive = caseSensitive) const;
+
+    bool operator ==(const SBuf & S) const;
+    bool operator !=(const SBuf & S) const;
+    bool operator <(const SBuf &S) const {return (cmp(S) < 0);}
+    bool operator >(const SBuf &S) const {return (cmp(S) > 0);}
+    bool operator <=(const SBuf &S) const {return (cmp(S) <= 0);}
+    bool operator >=(const SBuf &S) const {return (cmp(S) >= 0);}
+
+    /** Consume bytes at the head of the SBuf
+     *
+     * Consume N chars at SBuf head, or to SBuf's end,
+     * whichever is shorter. If more bytes are consumed than available,
+     * the SBuf is emptied
+     * \param n how many bytes to remove; could be zero.
+     *     npos (or no argument) means 'to the end of SBuf'
+     * \return a new SBuf containing the consumed bytes.
+     */
+    SBuf consume(size_type n = npos);
+
+    /// gets global statistic informations
+    static const SBufStats& GetStats();
+
+    /** Copy SBuf contents into user-supplied C buffer.
+     *
+     * Export a copy of the SBuf's contents into the user-supplied
+     * buffer, up to the user-supplied-length. No zero-termination is performed
+     * \return num the number of actually-copied chars.
+     */
+    size_type copy(char *dest, size_type n) const;
+
+    /** exports a pointer to the SBuf internal storage.
+     * \warning ACCESSING RAW STORAGE IS DANGEROUS!
+     *
+     * Returns a ead-only pointer to SBuf's content. No terminating null
+     * character is appended (use c_str() for that).
+     * The returned value points to an internal location whose contents
+     * are guaranteed to remain unchanged only until the next call
+     * to a non-constant member function of the SBuf object. Such a
+     * call may be implicit (e.g., when SBuf is destroyed
+     * upon leaving the current context).
+     * This is a very UNSAFE way of accessing the data.
+     * This call never returns NULL.
+     * \see c_str
+     * \note the memory management system guarantees that the exported region
+     *    of memory will remain valid if the caller keeps holding
+     *    a valid reference to the SBuf object and does not write or append to
+     *    it. For example:
+     * \code
+     * SBuf foo("some string");
+     * const char *bar = foo.rawContent();
+     * doSomething(bar); //safe
+     * foo.append(" other string");
+     * doSomething(bar); //unsafe
+     * \endcode
+     */
+    const char* rawContent() const;
+
+    /** Exports a writable pointer to the SBuf internal storage.
+     * \warning Use with EXTREME caution, this is a dangerous operation.
+     *
+     * Returns a pointer to the first unused byte in the SBuf's storage,
+     * which can be be used for appending. At least minSize bytes will
+     * be available for writing.
+     * The returned pointer must not be stored by the caller, as it will
+     * be invalidated by the first call to a non-const method call
+     * on the SBuf.
+     * This call guarantees to never return NULL.
+     * \see reserveSpace
+     * \note Unlike reserveSpace(), this method does not guarantee exclusive
+     *       buffer ownership. It is instead optimized for a one writer
+     *       (appender), many readers scenario by avoiding unnecessary
+     *       copying and allocations.
+     * \throw SBufTooBigException if the user tries to allocate too big a SBuf
+     */
+    char *rawSpace(size_type minSize);
+
+    /** Obtain how much free space is available in the backing store.
+     *
+     * \note: unless the client just cow()ed, it is not guaranteed that
+     *        the free space can be used.
+     */
+    size_type spaceSize() const { return store_->spaceSize(); }
+
+    /** Force a SBuf's size
+     * \warning use with EXTREME caution, this is a dangerous operation
+     *
+     * Adapt the SBuf internal state after external interference
+     * such as writing into it via rawSpace.
+     * \throw TextException if SBuf doesn't have exclusive ownership of store
+     * \throw SBufTooBigException if new size is bigger than available store space
+     */
+    void forceSize(size_type newSize);
+
+    /** exports a null-terminated reference to the SBuf internal storage.
+     * \warning ACCESSING RAW STORAGE IS DANGEROUS! DO NOT EVER USE
+     *  THE RETURNED POINTER FOR WRITING
+     *
+     * The returned value points to an internal location whose contents
+     * are guaranteed to remain unchanged only until the next call
+     * to a non-constant member function of the SBuf object. Such a
+     * call may be implicit (e.g., when SBuf is destroyed
+     * upon leaving the current context).
+     * This is a very UNSAFE way of accessing the data.
+     * This call never returns NULL.
+     * \see rawContent
+     * \note the memory management system guarantees that the exported region
+     *    of memory will remain valid will remain valid only if the
+     *    caller keeps holding a valid reference to the SBuf object and
+     *    does not write or append to it
+     */
+    const char* c_str();
+
+    /// Returns the number of bytes stored in SBuf.
+    size_type length() const {return len_;}
+
+    /** Get the length of the SBuf, as a signed integer
+     *
+     * Compatibility function for printf(3) which requires a signed int
+     * \throw SBufTooBigException if the SBuf is too big for a signed integer
+     */
+    int plength() const {
+        if (length()>INT_MAX)
+            throw SBufTooBigException(__FILE__, __LINE__);
+        return static_cast<int>(length());
+    }
+
+    /** Check whether the SBuf is empty
+     *
+     * \return true if length() == 0
+     */
+    bool isEmpty() const {return (len_==0);}
+
+    /** Request to guarantee the SBuf's free store space.
+     *
+     * After the reserveSpace request, the SBuf is guaranteed to have at
+     * least minSpace bytes of unused backing store following the currently
+     * used portion and single ownership of the backing store.
+     * \throw SBufTooBigException if the user tries to allocate too big a SBuf
+     */
+    void reserveSpace(size_type minSpace) {
+        Must(minSpace <= maxSize);
+        Must(length() <= maxSize - minSpace);
+        reserveCapacity(length()+minSpace);
+    }
+
+    /** Request to guarantee the SBuf's store capacity
+     *
+     * After this method is called, the SBuf is guaranteed to have at least
+     * minCapacity bytes of total buffer size, including the currently-used
+     * portion; it is also guaranteed that after this call this SBuf
+     * has unique ownership of the underlying memory store.
+     * \throw SBufTooBigException if the user tries to allocate too big a SBuf
+     */
+    void reserveCapacity(size_type minCapacity);
+
+    /** Accommodate caller's requirements regarding SBuf's storage if possible.
+     *
+     * \return spaceSize(), which may be zero
+     */
+    size_type reserve(const SBufReservationRequirements &requirements);
+
+    /** slicing method
+     *
+     * Removes SBuf prefix and suffix, leaving a sequence of 'n'
+     * bytes starting from position 'pos', first byte is at pos 0.
+     * It is an in-place-modifying version of substr.
+     * \param pos start sub-stringing from this byte. If it is
+     *      npos or it is greater than the SBuf length, the SBuf is cleared and
+     *      an empty SBuf is returned.
+     * \param n maximum number of bytes of the resulting SBuf.
+     *     npos means "to end of SBuf".
+     *     if it is 0, the SBuf is cleared and an empty SBuf is returned.
+     *     if it overflows the end of the SBuf, it is capped to the end of SBuf
+     * \see substr, trim
+     */
+    SBuf& chop(size_type pos, size_type n = npos);
+
+    /** Remove characters in the toremove set at the beginning, end or both
+     *
+     * \param toremove characters to be removed. Stops chomping at the first
+     *        found char not in the set
+     * \param atBeginning if true (default), strips at the beginning of the SBuf
+     * \param atEnd if true (default), strips at the end of the SBuf
+     */
+    SBuf& trim(const SBuf &toRemove, bool atBeginning = true, bool atEnd = true);
+
+    /** Extract a part of the current SBuf.
+     *
+     * Return a fresh a fresh copy of a portion the current SBuf, which is
+     * left untouched. The same parameter convetions apply as for chop.
+     * \see trim, chop
+     */
+    SBuf substr(size_type pos, size_type n = npos) const;
+
+    /** Find first occurrence of character in SBuf
+     *
+     * Returns the index in the SBuf of the first occurrence of char c.
+     * \return npos if the char was not found
+     * \param startPos if specified, ignore any occurrences before that position
+     *     if startPos is npos or greater than length() npos is always returned
+     *     if startPos is less than zero, it is ignored
+     */
+    size_type find(char c, size_type startPos = 0) const;
+
+    /** Find first occurrence of SBuf in SBuf.
+     *
+     * Returns the index in the SBuf of the first occurrence of the
+     * sequence contained in the str argument.
+     * \param startPos if specified, ignore any occurrences before that position
+     *     if startPos is npos or greater than length() npos is always returned
+     * \return npos if the SBuf was not found
+     */
+    size_type find(const SBuf & str, size_type startPos = 0) const;
+
+    /** Find last occurrence of character in SBuf
+     *
+     * Returns the index in the SBuf of the last occurrence of char c.
+     * \return npos if the char was not found
+     * \param endPos if specified, ignore any occurrences after that position.
+     *   if npos or greater than length(), the whole SBuf is considered
+     */
+    size_type rfind(char c, size_type endPos = npos) const;
+
+    /** Find last occurrence of SBuf in SBuf
+     *
+     * Returns the index in the SBuf of the last occurrence of the
+     * sequence contained in the str argument.
+     * \return npos if the sequence  was not found
+     * \param endPos if specified, ignore any occurrences after that position
+     *   if npos or greater than length(), the whole SBuf is considered
+     */
+    size_type rfind(const SBuf &str, size_type endPos = npos) const;
+
+    /** Find first occurrence of character of set in SBuf
+     *
+     * Finds the first occurrence of ANY of the characters in the supplied set in
+     * the SBuf.
+     * \return npos if no character in the set could be found
+     * \param startPos if specified, ignore any occurrences before that position
+     *   if npos, then npos is always returned
+     *
+     * TODO: rename to camelCase
+     */
+    size_type findFirstOf(const CharacterSet &set, size_type startPos = 0) const;
+
+    /** Find first occurrence character NOT in character set
+     *
+     * \return npos if all characters in the SBuf are from set
+     * \param startPos if specified, ignore any occurrences before that position
+     *   if npos, then npos is always returned
+     *
+     * TODO: rename to camelCase
+     */
+    size_type findFirstNotOf(const CharacterSet &set, size_type startPos = 0) const;
+
+    /** sscanf-alike
+     *
+     * sscanf re-implementation. Non-const, and not \0-clean.
+     * \return same as sscanf
+     * \see man sscanf(3)
+     */
+    int scanf(const char *format, ...);
+
+    /// converts all characters to lower case; \see man tolower(3)
+    void toLower();
+
+    /// converts all characters to upper case; \see man toupper(3)
+    void toUpper();
+
+    /** String export function
+     * converts the SBuf to a legacy String, by copy.
+     * \deprecated
+     */
+    String toString() const;
+
+    /// std::string export function
+    std::string toStdString() const { return std::string(buf(),length()); }
+
+    // TODO: possibly implement erase() similar to std::string's erase
+    // TODO: possibly implement a replace() call
+private:
+
+    /**
+     * Keeps SBuf's MemBlob alive in a blob-destroying context where
+     * a seemingly unrelated memory pointer may belong to the same blob.
+     * For [an extreme] example, consider: a.append(a).
+     * Compared to an SBuf temporary, this class is optimized to
+     * preserve blobs only if needed and to reduce debugging noise.
+     */
+    class Locker
+    {
+    public:
+        Locker(SBuf *parent, const char *otherBuffer) {
+            // lock if otherBuffer intersects the parents buffer area
+            const MemBlob *blob = parent->store_.getRaw();
+            if (blob->mem <= otherBuffer && otherBuffer < (blob->mem + blob->capacity))
+                locket = blob;
+        }
+    private:
+        MemBlob::Pointer locket;
+    };
+    friend class Locker;
+
+    MemBlob::Pointer store_; ///< memory block, possibly shared with other SBufs
+    size_type off_; ///< our content start offset from the beginning of shared store_
+    size_type len_; ///< number of our content bytes in shared store_
+    static SBufStats stats; ///< class-wide statistics
+
+    /// SBuf object identifier; does not change when contents do,
+    ///   including during assignment
+    const InstanceId<SBuf> id;
+
+    /** obtain prototype store
+     *
+     * Just-created SBufs all share to the same MemBlob.
+     * This call instantiates and returns it.
+     */
+    static MemBlob::Pointer GetStorePrototype();
+
+    /**
+     * obtains a char* to the beginning of this SBuf in memory.
+     * \note the obtained string is NOT null-terminated.
+     */
+    char * buf() const {return (store_->mem+off_);}
+
+    /** returns the pointer to the first char after this SBuf end
+     *
+     *  No checks are made that the space returned is safe, checking that is
+     *  up to the caller.
+     */
+    char * bufEnd() const {return (store_->mem+off_+len_);}
+
+    /**
+     * Try to guesstimate how big a MemBlob to allocate.
+     * The result is guarranteed to be to be at least the desired size.
+     */
+    size_type estimateCapacity(size_type desired) const {return (2*desired);}
+
+    void reAlloc(size_type newsize);
+
+    void cow(size_type minsize = npos);
+
+    void checkAccessBounds(size_type pos) const;
+
+    /** Low-level append operation
+     *
+     * Takes as input a contiguous area of memory and appends its contents
+     * to the SBuf, taking care of memory management. Does no bounds checking
+     * on the supplied memory buffer, it is the duty of the caller to ensure
+     * that the supplied area is valid.
+     */
+    SBuf& lowAppend(const char * memArea, size_type areaSize);
+};
+
+/// Named SBuf::reserve() parameters. Defaults ask for and restrict nothing.
+class SBufReservationRequirements
+{
+public:
+    typedef SBuf::size_type size_type;
+
+    SBufReservationRequirements() : idealSpace(0), minSpace(0), maxCapacity(SBuf::maxSize), allowShared(true) {}
+
+    /*
+     * Parameters are listed in the reverse order of importance: Satisfaction of
+     * the lower-listed requirements may violate the higher-listed requirements.
+     */
+    size_type idealSpace; ///< if allocating anyway, provide this much space
+    size_type minSpace; ///< allocate if spaceSize() is smaller
+    size_type maxCapacity; ///< do not allocate more than this
+    bool allowShared; ///< whether sharing our storage with others is OK
+};
+
+/// ostream output operator
+inline std::ostream &
+operator <<(std::ostream& os, const SBuf& S)
+{
+    return S.print(os);
+}
+
+/// Returns a lower-cased copy of its parameter.
+inline SBuf
+ToUpper(SBuf buf)
+{
+    buf.toUpper();
+    return buf;
+}
+
+/// Returns an upper-cased copy of its parameter.
+inline SBuf
+ToLower(SBuf buf)
+{
+    buf.toLower();
+    return buf;
+}
+
+#endif /* SQUID_SBUF_H */
+
Index: squid-3.3.13/src/SBufDetailedStats.cc
===================================================================
--- /dev/null
+++ squid-3.3.13/src/SBufDetailedStats.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 1996-2016 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 "SBufDetailedStats.h"
+#include "StatHist.h"
+
+/*
+ * Implementation note: the purpose of this construct is to avoid adding
+ * external dependencies to the SBuf code
+ */
+
+static StatHist *
+newStatHist() {
+    StatHist *stats = new StatHist;
+    stats->logInit(100, 30.0, 128000.0);
+    return stats;
+}
+
+StatHist &
+collectSBufDestructTimeStats()
+{
+    static StatHist *stats = newStatHist();
+    return *stats;
+}
+
+StatHist &
+collectMemBlobDestructTimeStats()
+{
+    static StatHist *stats = newStatHist();
+    return *stats;
+}
+
+void
+recordSBufSizeAtDestruct(SBuf::size_type sz)
+{
+    collectSBufDestructTimeStats().count(static_cast<double>(sz));
+}
+
+void
+recordMemBlobSizeAtDestruct(SBuf::size_type sz)
+{
+    collectMemBlobDestructTimeStats().count(static_cast<double>(sz));
+}
+
Index: squid-3.3.13/src/SBufDetailedStats.h
===================================================================
--- /dev/null
+++ squid-3.3.13/src/SBufDetailedStats.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 1996-2016 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_SBUFDETAILEDSTATS_H
+#define SQUID_SBUFDETAILEDSTATS_H
+
+#include "SBuf.h"
+
+class StatHist;
+
+/// Record the size a SBuf had when it was destructed
+void recordSBufSizeAtDestruct(SBuf::size_type sz);
+
+/// the SBuf size-at-destruct-time histogram
+StatHist &collectSBufDestructTimeStats();
+
+/// Record the size a MemBlob had when it was destructed
+void recordMemBlobSizeAtDestruct(MemBlob::size_type sz);
+
+/// the MemBlob size-at-destruct-time histogram
+StatHist &collectMemBlobDestructTimeStats();
+
+#endif /* SQUID_SBUFDETAILEDSTATS_H */
+
Index: squid-3.3.13/src/SBufExceptions.cc
===================================================================
--- /dev/null
+++ squid-3.3.13/src/SBufExceptions.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 1996-2016 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 "OutOfBoundsException.h"
+#include "SBuf.h"
+#include "SBufExceptions.h"
+
+OutOfBoundsException::OutOfBoundsException(const SBuf &throwingBuf,
+        SBuf::size_type &pos,
+        const char *aFileName, int aLineNo)
+    : TextException(NULL, aFileName, aLineNo),
+      theThrowingBuf(throwingBuf),
+      accessedPosition(pos)
+{
+    SBuf explanatoryText("OutOfBoundsException");
+    if (aLineNo != -1)
+        explanatoryText.appendf(" at line %d", aLineNo);
+    if (aFileName != NULL)
+        explanatoryText.appendf(" in file %s", aFileName);
+    explanatoryText.appendf(" while accessing position %d in a SBuf long %d",
+                            pos, throwingBuf.length());
+    // we can safely alias c_str as both are local to the object
+    //  and will not further manipulated.
+    message = xstrndup(explanatoryText.c_str(),explanatoryText.length());
+}
+
+OutOfBoundsException::~OutOfBoundsException() throw()
+{ }
+
+InvalidParamException::InvalidParamException(const char *aFilename, int aLineNo)
+    : TextException("Invalid parameter", aFilename, aLineNo)
+{ }
+
+SBufTooBigException::SBufTooBigException(const char *aFilename, int aLineNo)
+    : TextException("Trying to create an oversize SBuf", aFilename, aLineNo)
+{ }
+
Index: squid-3.3.13/src/SBufExceptions.h
===================================================================
--- /dev/null
+++ squid-3.3.13/src/SBufExceptions.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 1996-2016 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_SBUFEXCEPTIONS_H
+#define SQUID_SBUFEXCEPTIONS_H
+
+#include "base/TextException.h"
+
+/**
+ * Exception raised when call parameters are not valid
+ * \todo move to an Exceptions.h?
+ */
+class InvalidParamException : public TextException
+{
+public:
+    explicit InvalidParamException(const char *aFilename = 0, int aLineNo = -1);
+};
+
+/**
+ * Exception raised when an attempt to resize a SBuf would cause it to reserve too big
+ */
+class SBufTooBigException : public TextException
+{
+public:
+    explicit SBufTooBigException(const char *aFilename = 0, int aLineNo = -1);
+};
+
+#endif /* SQUID_SBUFEXCEPTIONS_H */
+
Index: squid-3.3.13/src/Makefile.am
===================================================================
--- squid-3.3.13.orig/src/Makefile.am
+++ squid-3.3.13/src/Makefile.am
@@ -22,9 +22,14 @@ DNSSOURCE += \
 	DnsLookupDetails.cc
 
 SBUF_SOURCE= \
+	base/CharacterSet.h \
 	base/InstanceId.h \
 	MemBlob.h \
-	MemBlob.cc
+	MemBlob.cc \
+	SBuf.h \
+	SBuf.cc \
+	SBufExceptions.h \
+	SBufExceptions.cc
 
 LOADABLE_MODULES_SOURCES = \
 	LoadableModule.h \
@@ -468,6 +473,8 @@ squid_SOURCES = \
 	send-announce.h \
 	send-announce.cc \
 	$(SBUF_SOURCE) \
+	SBufDetailedStats.h \
+	SBufDetailedStats.cc \
 	$(SNMP_SOURCE) \
 	SquidMath.h \
 	SquidMath.cc \
Index: squid-3.3.13/src/base/CharacterSet.cc
===================================================================
--- /dev/null
+++ squid-3.3.13/src/base/CharacterSet.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 1996-2016 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 "CharacterSet.h"
+
+#include <algorithm>
+#include <functional>
+
+CharacterSet &
+CharacterSet::operator +=(const CharacterSet &src)
+{
+    Storage::const_iterator s = src.chars_.begin();
+    const Storage::const_iterator e = src.chars_.end();
+    Storage::iterator d = chars_.begin();
+    while (s != e) {
+        if (*s)
+            *d = 1;
+        ++s;
+        ++d;
+    }
+    return *this;
+}
+
+CharacterSet
+CharacterSet::operator +(const CharacterSet &src) const
+{
+    CharacterSet rv(*this);
+    rv += src;
+    return rv;
+}
+
+CharacterSet &
+CharacterSet::add(const unsigned char c)
+{
+    chars_[static_cast<uint8_t>(c)] = 1;
+    return *this;
+}
+
+CharacterSet &
+CharacterSet::addRange(unsigned char low, unsigned char high)
+{
+    //manual loop splitting is needed to cover case where high is 255
+    // otherwise low will wrap, resulting in infinite loop
+    while (low < high) {
+        chars_[static_cast<uint8_t>(low)] = 1;
+        ++low;
+    }
+    chars_[static_cast<uint8_t>(high)] = 1;
+    return *this;
+}
+
+CharacterSet
+CharacterSet::complement(const char *label) const
+{
+    CharacterSet result((label ? label : "complement_of_some_other_set"), "");
+    // negate each of our elements and add them to the result storage
+    std::transform(chars_.begin(), chars_.end(), result.chars_.begin(),
+                   std::logical_not<Storage::value_type>());
+    return result;
+}
+
+CharacterSet::CharacterSet(const char *label, const char * const c) :
+    name(label == NULL ? "anonymous" : label),
+    chars_(Storage(256,0))
+{
+    const size_t clen = strlen(c);
+    for (size_t i = 0; i < clen; ++i)
+        add(c[i]);
+}
+
+CharacterSet::CharacterSet(const char *label, unsigned char low, unsigned char high) :
+    name(label == NULL ? "anonymous" : label),
+    chars_(Storage(256,0))
+{
+    addRange(low,high);
+}
+
+const CharacterSet
+// RFC 5234
+CharacterSet::ALPHA("ALPHA", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),
+             CharacterSet::BIT("BIT","01"),
+             CharacterSet::CR("CR","\r"),
+#if __cplusplus == 201103L
+//CharacterSet::CTL("CTL",{{0x01,0x1f},{0x7f,0x7f}}),
+#endif
+             CharacterSet::DIGIT("DIGIT","0123456789"),
+             CharacterSet::DQUOTE("DQUOTE","\""),
+             CharacterSet::HEXDIG("HEXDIG","0123456789aAbBcCdDeEfF"),
+             CharacterSet::HTAB("HTAB","\t"),
+             CharacterSet::LF("LF","\n"),
+             CharacterSet::SP("SP"," "),
+             CharacterSet::VCHAR("VCHAR", 0x21, 0x7e),
+// RFC 7230
+             CharacterSet::WSP("WSP"," \t"),
+#if __cplusplus == 201103L
+//CharacterSet::CTEXT("ctext",{{0x09,0x09},{0x20,0x20},{0x2a,0x5b},{0x5d,0x7e},{0x80,0xff}}),
+#endif
+             CharacterSet::TCHAR("TCHAR","!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),
+             CharacterSet::SPECIAL("SPECIAL","()<>@,;:\\\"/[]?={}"),
+#if __cplusplus == 201103L
+//CharacterSet::QDTEXT("QDTEXT",{{0x09,0x09},{0x20,0x21},{0x23,0x5b},{0x5d,0x7e},{0x80,0xff}}),
+#endif
+             CharacterSet::OBSTEXT("OBSTEXT",0x80,0xff),
+// RFC 7232
+#if __cplusplus == 201103L
+//CharacterSet::ETAGC("ETAGC",{{0x21,0x21},{0x23,0x7e},{0x80,0xff}}),
+#endif
+// RFC 7235
+             CharacterSet::TOKEN68C("TOKEN68C","-._~+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+             ;
+
Index: squid-3.3.13/src/base/CharacterSet.h
===================================================================
--- /dev/null
+++ squid-3.3.13/src/base/CharacterSet.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 1996-2016 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_PARSER_CHARACTERSET_H
+#define _SQUID_SRC_PARSER_CHARACTERSET_H
+
+#include <vector>
+
+/// optimized set of C chars, with quick membership test and merge support
+class CharacterSet
+{
+public:
+    typedef std::vector<uint8_t> Storage;
+
+    /// define a character set with the given label ("anonymous" if NULL)
+    ///  with specified initial contents
+    CharacterSet(const char *label, const char * const initial);
+
+    /// define a character set with the given label ("anonymous" if NULL)
+    ///  containing characters defined in the supplied ranges
+    /// \see addRange
+    CharacterSet(const char *label, unsigned char low, unsigned char high);
+
+    /// whether a given character exists in the set
+    bool operator[](unsigned char c) const {return chars_[static_cast<uint8_t>(c)] != 0;}
+
+    /// add a given character to the character set
+    CharacterSet & add(const unsigned char c);
+
+    /// add a list of character ranges, expressed as pairs [low,high], including both ends
+    CharacterSet & addRange(unsigned char low, unsigned char high);
+
+    /// add all characters from the given CharacterSet to this one
+    CharacterSet &operator +=(const CharacterSet &src);
+
+    /// return a new CharacterSet containing the union of two sets
+    CharacterSet operator +(const CharacterSet &src) const;
+
+    /// return a new CharacterSet containing characters not in this set
+    CharacterSet complement(const char *complementLabel = NULL) const;
+
+    /// change name; handy in const declarations that use operators
+    CharacterSet &rename(const char *label) { name = label; return *this; }
+
+    /// optional set label for debugging (default: "anonymous")
+    const char * name;
+
+    // common character sets, RFC 5234
+    // A-Za-z
+    static const CharacterSet ALPHA;
+    // 0-1
+    static const CharacterSet BIT;
+    // carriage return
+    static const CharacterSet CR;
+    // controls
+#if __cplusplus == 201103L
+    // ready but disabled as needs C++11 constructor
+    //static const CharacterSet CTL;
+#endif
+    // 0-9
+    static const CharacterSet DIGIT;
+    // double quote
+    static const CharacterSet DQUOTE;
+    // 0-9aAbBcCdDeEfF
+    static const CharacterSet HEXDIG;
+    // horizontal tab
+    static const CharacterSet HTAB;
+    // line feed
+    static const CharacterSet LF;
+    // white space
+    static const CharacterSet SP;
+    // visible (printable) characters
+    static const CharacterSet VCHAR;
+    // <space><tab>
+    static const CharacterSet WSP;
+
+    // HTTP character sets, RFC 7230
+    // ctext
+#if __cplusplus == 201103L
+    // ready but disabled as needs C++11 constructor
+    //static const CharacterSet CTEXT;
+#endif
+    // XXX: maybe field-vchar = VCHAR / obs-text
+    // any VCHAR except for SPECIAL
+    static const CharacterSet TCHAR;
+    // special VCHARs
+    static const CharacterSet SPECIAL;
+    // qdtext
+#if __cplusplus == 201103L
+    // ready but disabled as needs C++11 constructor
+    //static const CharacterSet QDTEXT;
+#endif
+    // obs-text
+    static const CharacterSet OBSTEXT;
+
+    // HTTP character sets, RFC 7232
+    // etagc
+#if __cplusplus == 201103L
+    // ready but disabled as needs C++11 constructor
+    //static const CharacterSet ETAGC;
+#endif
+
+    // HTTP character sets, RFC 7235
+    // token68 (internal charaters only, excludes '=' terminator)
+    static const CharacterSet TOKEN68C;
+
+private:
+    /** index of characters in this set
+     *
+     * \note guaranteed to be always 256 slots big, as forced in the
+     *  constructor. This assumption is relied upon in operator[], add,
+     *  operator+=
+     */
+    Storage chars_;
+};
+
+#endif /* _SQUID_SRC_PARSER_CHARACTERSET_H */
+
Index: squid-3.3.13/src/base/Makefile.am
===================================================================
--- squid-3.3.13.orig/src/base/Makefile.am
+++ squid-3.3.13/src/base/Makefile.am
@@ -14,6 +14,8 @@ libbase_la_SOURCES = \
 	AsyncCallQueue.cc \
 	AsyncCallQueue.h \
 	TidyPointer.h \
+	CharacterSet.h \
+	CharacterSet.cc \
 	CbcPointer.h \
 	InstanceId.h \
 	RunnersRegistry.cc \
Index: squid-3.3.13/src/OutOfBoundsException.h
===================================================================
--- /dev/null
+++ squid-3.3.13/src/OutOfBoundsException.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 1996-2016 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_OUTOFBOUNDSEXCEPTION_H
+#define _SQUID_SRC_OUTOFBOUNDSEXCEPTION_H
+
+#include "base/TextException.h"
+#include "SBuf.h"
+
+/**
+ * Exception raised when the user is going out of bounds when accessing
+ * a char within the SBuf
+ */
+class OutOfBoundsException : public TextException
+{
+public:
+    OutOfBoundsException(const SBuf &buf, SBuf::size_type &pos, const char *aFileName = 0, int aLineNo = -1);
+    virtual ~OutOfBoundsException() throw();
+
+protected:
+    SBuf theThrowingBuf;
+    SBuf::size_type accessedPosition;
+};
+
+#endif /* _SQUID_SRC_OUTOFBOUNDSEXCEPTION_H */
+
Index: squid-3.3.13/include/RefCount.h
===================================================================
--- squid-3.3.13.orig/include/RefCount.h
+++ squid-3.3.13/include/RefCount.h
@@ -117,7 +117,7 @@ struct RefCountable_ {
         ++count_;
     }
 
-    unsigned RefCountDereference() const {
+    uint32_t RefCountDereference() const {
 #if REFCOUNT_DEBUG
         old_debug(0,1)("Decrementing this %p from count %u\n",this,count_);
 #endif
@@ -125,10 +125,11 @@ struct RefCountable_ {
         return --count_;
     }
 
-    unsigned RefCountCount() const { return count_; } // for debugging only
+    uint32_t RefCountCount() const { return count_; } // for debugging only
+    uint32_t LockCount() const { return count_; }
 
 private:
-    mutable unsigned count_;
+    mutable uint32_t count_;
 };
 
 #define RefCountable virtual RefCountable_
