This patch replaces String with SBuf for storage of header names in HttpHeaderEntry.

In the process we gain SBuf methods for delByName and hasNamed. Deprecating the char* versions.




Amos

=== modified file 'src/HttpHeader.cc'
--- src/HttpHeader.cc	2017-03-23 12:55:36 +0000
+++ src/HttpHeader.cc	2017-04-23 19:40:31 +0000
@@ -1,31 +1,32 @@
 /*
  * Copyright (C) 1996-2017 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 55    HTTP Header */
 
 #include "squid.h"
+#include "base/CharacterSet.h"
 #include "base/EnumIterator.h"
 #include "base64.h"
 #include "globals.h"
 #include "http/ContentLengthInterpreter.h"
 #include "HttpHdrCc.h"
 #include "HttpHdrContRange.h"
 #include "HttpHdrScTarget.h" // also includes HttpHdrSc.h
 #include "HttpHeader.h"
 #include "HttpHeaderFieldInfo.h"
 #include "HttpHeaderStat.h"
 #include "HttpHeaderTools.h"
 #include "MemBuf.h"
 #include "mgr/Registration.h"
 #include "mime_header.h"
 #include "profiler/Profiler.h"
 #include "rfc1123.h"
 #include "sbuf/StringConvert.h"
 #include "SquidConfig.h"
 #include "StatHist.h"
 #include "Store.h"
@@ -231,43 +232,42 @@
 HttpHeader::append(const HttpHeader * src)
 {
     assert(src);
     assert(src != this);
     debugs(55, 7, "appending hdr: " << this << " += " << src);
 
     for (auto e : src->entries) {
         if (e)
             addEntry(e->clone());
     }
 }
 
 /// check whether the fresh header has any new/changed updatable fields
 bool
 HttpHeader::needUpdate(HttpHeader const *fresh) const
 {
     for (const auto e: fresh->entries) {
         if (!e || skipUpdateHeader(e->id))
             continue;
         String value;
-        const char *name = e->name.termedBuf();
-        if (!hasNamed(name, strlen(name), &value) ||
-                (value != fresh->getByName(name)))
+        if (!hasNamed(e->name, &value) ||
+                (value != fresh->getByName(e->name)))
             return true;
     }
     return false;
 }
 
 void
 HttpHeader::updateWarnings()
 {
     int count = 0;
     HttpHeaderPos pos = HttpHeaderInitPos;
 
     // RFC 7234, section 4.3.4: delete 1xx warnings and retain 2xx warnings
     while (HttpHeaderEntry *e = getEntry(&pos)) {
         if (e->id == Http::HdrType::WARNING && (e->getInt()/100 == 1) )
             delAt(pos, count);
     }
 }
 
 bool
 HttpHeader::skipUpdateHeader(const Http::HdrType id) const
@@ -284,41 +284,41 @@
 
     // Optimization: Finding whether a header field changed is expensive
     // and probably not worth it except for collapsed revalidation needs.
     if (Config.onoff.collapsed_forwarding && !needUpdate(fresh))
         return false;
 
     updateWarnings();
 
     const HttpHeaderEntry *e;
     HttpHeaderPos pos = HttpHeaderInitPos;
 
     while ((e = fresh->getEntry(&pos))) {
         /* deny bad guys (ok to check for Http::HdrType::OTHER) here */
 
         if (skipUpdateHeader(e->id))
             continue;
 
         if (e->id != Http::HdrType::OTHER)
             delById(e->id);
         else
-            delByName(e->name.termedBuf());
+            delByName(e->name);
     }
 
     pos = HttpHeaderInitPos;
     while ((e = fresh->getEntry(&pos))) {
         /* deny bad guys (ok to check for Http::HdrType::OTHER) here */
 
         if (skipUpdateHeader(e->id))
             continue;
 
         debugs(55, 7, "Updating header '" << Http::HeaderLookupTable.lookup(e->id).name << "' in cached entry");
 
         addEntry(e->clone());
     }
     return true;
 }
 
 bool
 HttpHeader::Isolate(const char **parse_start, size_t l, const char **blk_start, const char **blk_end)
 {
     /*
@@ -475,41 +475,43 @@
 
             if (Config.onoff.relaxed_header_parser)
                 continue;
 
             PROF_stop(HttpHeaderParse);
             clean();
             return 0;
         }
 
         if (e->id == Http::HdrType::CONTENT_LENGTH && !clen.checkField(e->value)) {
             delete e;
 
             if (Config.onoff.relaxed_header_parser)
                 continue; // clen has printed any necessary warnings
 
             PROF_stop(HttpHeaderParse);
             clean();
             return 0;
         }
 
-        if (e->id == Http::HdrType::OTHER && stringHasWhitespace(e->name.termedBuf())) {
+        // TODO: this is probably incorrect, for now it should match w_space
+        static CharacterSet wsp = (CharacterSet::WSP + CharacterSet::CR + CharacterSet::LF);
+        if (e->id == Http::HdrType::OTHER && e->name.findFirstOf(wsp) != SBuf::npos) {
             debugs(55, warnOnError, "WARNING: found whitespace in HTTP header name {" <<
                    getStringPrefix(field_start, field_end-field_start) << "}");
 
             if (!Config.onoff.relaxed_header_parser) {
                 delete e;
                 PROF_stop(HttpHeaderParse);
                 clean();
                 return 0;
             }
         }
 
         addEntry(e);
     }
 
     if (clen.headerWideProblem) {
         debugs(55, warnOnError, "WARNING: " << clen.headerWideProblem <<
                " Content-Length field values in" <<
                Raw("header", header_start, hdrLen));
     }
 
@@ -551,41 +553,41 @@
             e->packInto(p);
             continue;
         }
 
         bool maskThisEntry = false;
         switch (e->id) {
         case Http::HdrType::AUTHORIZATION:
         case Http::HdrType::PROXY_AUTHORIZATION:
             maskThisEntry = true;
             break;
 
         case Http::HdrType::FTP_ARGUMENTS:
             if (const HttpHeaderEntry *cmd = findEntry(Http::HdrType::FTP_COMMAND))
                 maskThisEntry = (cmd->value == "PASS");
             break;
 
         default:
             break;
         }
         if (maskThisEntry) {
-            p->append(e->name.rawBuf(), e->name.size());
+            p->append(e->name.rawContent(), e->name.length());
             p->append(": ** NOT DISPLAYED **\r\n", 23);
         } else {
             e->packInto(p);
         }
 
     }
     /* Pack in the "special" entries */
 
     /* Cache-Control */
 }
 
 /* returns next valid entry */
 HttpHeaderEntry *
 HttpHeader::getEntry(HttpHeaderPos * pos) const
 {
     assert(pos);
     assert(*pos >= HttpHeaderInitPos && *pos < static_cast<ssize_t>(entries.size()));
 
     for (++(*pos); *pos < static_cast<ssize_t>(entries.size()); ++(*pos)) {
         if (entries[*pos])
@@ -628,53 +630,49 @@
 HttpHeaderEntry *
 HttpHeader::findLastEntry(Http::HdrType id) const
 {
     assert(any_registered_header(id));
     assert(!Http::HeaderLookupTable.lookup(id).list);
 
     /* check mask first */
     if (!CBIT_TEST(mask, id))
         return NULL;
 
     for (auto e = entries.rbegin(); e != entries.rend(); ++e) {
         if (*e && (*e)->id == id)
             return *e;
     }
 
     /* hm.. we thought it was there, but it was not found */
     assert(false);
     return nullptr; /* not reached */
 }
 
-/*
- * deletes all fields with a given name if any, returns #fields deleted;
- */
 int
-HttpHeader::delByName(const char *name)
+HttpHeader::delByName(const SBuf &name)
 {
     int count = 0;
     HttpHeaderPos pos = HttpHeaderInitPos;
-    HttpHeaderEntry *e;
     httpHeaderMaskInit(&mask, 0);   /* temporal inconsistency */
     debugs(55, 9, "deleting '" << name << "' fields in hdr " << this);
 
-    while ((e = getEntry(&pos))) {
+    while (const HttpHeaderEntry *e = getEntry(&pos)) {
         if (!e->name.caseCmp(name))
             delAt(pos, count);
         else
             CBIT_SET(mask, e->id);
     }
 
     return count;
 }
 
 /* deletes all entries with a given id, returns the #entries deleted */
 int
 HttpHeader::delById(Http::HdrType id)
 {
     debugs(55, 8, this << " del-by-id " << id);
     assert(any_registered_header(id));
 
     if (!CBIT_TEST(mask, id))
         return 0;
 
     int count = 0;
@@ -687,119 +685,119 @@
 
     CBIT_CLR(mask, id);
     assert(count);
     return count;
 }
 
 /*
  * deletes an entry at pos and leaves a gap; leaving a gap makes it
  * possible to iterate(search) and delete fields at the same time
  * NOTE: Does not update the header mask. Caller must follow up with
  * a call to refreshMask() if headers_deleted was incremented.
  */
 void
 HttpHeader::delAt(HttpHeaderPos pos, int &headers_deleted)
 {
     HttpHeaderEntry *e;
     assert(pos >= HttpHeaderInitPos && pos < static_cast<ssize_t>(entries.size()));
     e = static_cast<HttpHeaderEntry*>(entries[pos]);
     entries[pos] = NULL;
     /* decrement header length, allow for ": " and crlf */
-    len -= e->name.size() + 2 + e->value.size() + 2;
+    len -= e->name.length() + 2 + e->value.size() + 2;
     assert(len >= 0);
     delete e;
     ++headers_deleted;
 }
 
 /*
  * Compacts the header storage
  */
 void
 HttpHeader::compact()
 {
     // TODO: optimize removal, or possibly make it so that's not needed.
     entries.erase( std::remove(entries.begin(), entries.end(), nullptr),
                    entries.end());
 }
 
 /*
  * Refreshes the header mask. Required after delAt() calls.
  */
 void
 HttpHeader::refreshMask()
 {
     httpHeaderMaskInit(&mask, 0);
     debugs(55, 7, "refreshing the mask in hdr " << this);
     for (auto e : entries) {
         if (e)
             CBIT_SET(mask, e->id);
     }
 }
 
 /* appends an entry;
  * does not call e->clone() so one should not reuse "*e"
  */
 void
 HttpHeader::addEntry(HttpHeaderEntry * e)
 {
     assert(e);
     assert(any_HdrType_enum_value(e->id));
-    assert(e->name.size());
+    assert(e->name.length());
 
     debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size());
 
     if (e->id != Http::HdrType::BAD_HDR) {
         if (CBIT_TEST(mask, e->id)) {
             ++ headerStatsTable[e->id].repCount;
         } else {
             CBIT_SET(mask, e->id);
         }
     }
 
     entries.push_back(e);
 
     /* increment header length, allow for ": " and crlf */
-    len += e->name.size() + 2 + e->value.size() + 2;
+    len += e->name.length() + 2 + e->value.size() + 2;
 }
 
 /* inserts an entry;
  * does not call e->clone() so one should not reuse "*e"
  */
 void
 HttpHeader::insertEntry(HttpHeaderEntry * e)
 {
     assert(e);
     assert(any_valid_header(e->id));
 
     debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size());
 
     // Http::HdrType::BAD_HDR is filtered out by assert_any_valid_header
     if (CBIT_TEST(mask, e->id)) {
         ++ headerStatsTable[e->id].repCount;
     } else {
         CBIT_SET(mask, e->id);
     }
 
     entries.insert(entries.begin(),e);
 
     /* increment header length, allow for ": " and crlf */
-    len += e->name.size() + 2 + e->value.size() + 2;
+    len += e->name.length() + 2 + e->value.size() + 2;
 }
 
 bool
 HttpHeader::getList(Http::HdrType id, String *s) const
 {
     debugs(55, 9, this << " joining for id " << id);
     /* only fields from ListHeaders array can be "listed" */
     assert(Http::HeaderLookupTable.lookup(id).list);
 
     if (!CBIT_TEST(mask, id))
         return false;
 
     for (auto e: entries) {
         if (e && e->id == id)
             strListAdd(s, e->value.termedBuf(), ',');
     }
 
     /*
      * note: we might get an empty (size==0) string if there was an "empty"
      * header. This results in an empty length String, which may have a NULL
@@ -894,60 +892,60 @@
 
 bool
 HttpHeader::hasNamed(const SBuf &s, String *result) const
 {
     return hasNamed(s.rawContent(), s.length(), result);
 }
 
 bool
 HttpHeader::getByIdIfPresent(Http::HdrType id, String *result) const
 {
     if (id == Http::HdrType::BAD_HDR)
         return false;
     if (!has(id))
         return false;
     if (result)
         *result = getStrOrList(id);
     return true;
 }
 
 bool
-HttpHeader::hasNamed(const char *name, int namelen, String *result) const
+HttpHeader::hasNamed(const char *name, unsigned int namelen, String *result) const
 {
     Http::HdrType id;
     HttpHeaderPos pos = HttpHeaderInitPos;
     HttpHeaderEntry *e;
 
     assert(name);
 
     /* First try the quick path */
     id = Http::HeaderLookupTable.lookup(name,namelen).id;
 
     if (id != Http::HdrType::BAD_HDR) {
         if (getByIdIfPresent(id, result))
             return true;
     }
 
     /* Sorry, an unknown header name. Do linear search */
     bool found = false;
     while ((e = getEntry(&pos))) {
-        if (e->id == Http::HdrType::OTHER && e->name.size() == static_cast<String::size_type>(namelen) && e->name.caseCmp(name, namelen) == 0) {
+        if (e->id == Http::HdrType::OTHER && e->name.length() == namelen && e->name.caseCmp(name, namelen) == 0) {
             found = true;
             if (!result)
                 break;
             strListAdd(result, e->value.termedBuf(), ',');
         }
     }
 
     return found;
 }
 
 /*
  * Returns a the value of the specified list member, if any.
  */
 String
 HttpHeader::getByNameListMember(const char *name, const char *member, const char separator) const
 {
     String header;
     const char *pos = NULL;
     const char *item;
     int ilen;
@@ -1021,156 +1019,156 @@
             buf.appendf("%s/", AnyP::ProtocolType_str[ver.protocol]);
         buf.appendf("%d.%d %s", ver.major, ver.minor, ThisCache);
         const HttpHeader *hdr = from ? from : this;
         SBuf strVia = StringToSBuf(hdr->getList(Http::HdrType::VIA));
         if (!strVia.isEmpty())
             strVia.append(", ", 2);
         strVia.append(buf);
         // XXX: putStr() still suffers from String size limits
         Must(strVia.length() < String::SizeMaxXXX());
         delById(Http::HdrType::VIA);
         putStr(Http::HdrType::VIA, strVia.c_str());
     }
 }
 
 void
 HttpHeader::putInt(Http::HdrType id, int number)
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt);  /* must be of an appropriate type */
     assert(number >= 0);
-    addEntry(new HttpHeaderEntry(id, NULL, xitoa(number)));
+    addEntry(new HttpHeaderEntry(id, SBuf(), xitoa(number)));
 }
 
 void
 HttpHeader::putInt64(Http::HdrType id, int64_t number)
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt64);    /* must be of an appropriate type */
     assert(number >= 0);
-    addEntry(new HttpHeaderEntry(id, NULL, xint64toa(number)));
+    addEntry(new HttpHeaderEntry(id, SBuf(), xint64toa(number)));
 }
 
 void
 HttpHeader::putTime(Http::HdrType id, time_t htime)
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftDate_1123);    /* must be of an appropriate type */
     assert(htime >= 0);
-    addEntry(new HttpHeaderEntry(id, NULL, mkrfc1123(htime)));
+    addEntry(new HttpHeaderEntry(id, SBuf(), mkrfc1123(htime)));
 }
 
 void
 HttpHeader::putStr(Http::HdrType id, const char *str)
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftStr);  /* must be of an appropriate type */
     assert(str);
-    addEntry(new HttpHeaderEntry(id, NULL, str));
+    addEntry(new HttpHeaderEntry(id, SBuf(), str));
 }
 
 void
 HttpHeader::putAuth(const char *auth_scheme, const char *realm)
 {
     assert(auth_scheme && realm);
     httpHeaderPutStrf(this, Http::HdrType::WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
 }
 
 void
 HttpHeader::putCc(const HttpHdrCc * cc)
 {
     assert(cc);
     /* remove old directives if any */
     delById(Http::HdrType::CACHE_CONTROL);
     /* pack into mb */
     MemBuf mb;
     mb.init();
     cc->packInto(&mb);
     /* put */
-    addEntry(new HttpHeaderEntry(Http::HdrType::CACHE_CONTROL, NULL, mb.buf));
+    addEntry(new HttpHeaderEntry(Http::HdrType::CACHE_CONTROL, SBuf(), mb.buf));
     /* cleanup */
     mb.clean();
 }
 
 void
 HttpHeader::putContRange(const HttpHdrContRange * cr)
 {
     assert(cr);
     /* remove old directives if any */
     delById(Http::HdrType::CONTENT_RANGE);
     /* pack into mb */
     MemBuf mb;
     mb.init();
     httpHdrContRangePackInto(cr, &mb);
     /* put */
-    addEntry(new HttpHeaderEntry(Http::HdrType::CONTENT_RANGE, NULL, mb.buf));
+    addEntry(new HttpHeaderEntry(Http::HdrType::CONTENT_RANGE, SBuf(), mb.buf));
     /* cleanup */
     mb.clean();
 }
 
 void
 HttpHeader::putRange(const HttpHdrRange * range)
 {
     assert(range);
     /* remove old directives if any */
     delById(Http::HdrType::RANGE);
     /* pack into mb */
     MemBuf mb;
     mb.init();
     range->packInto(&mb);
     /* put */
-    addEntry(new HttpHeaderEntry(Http::HdrType::RANGE, NULL, mb.buf));
+    addEntry(new HttpHeaderEntry(Http::HdrType::RANGE, SBuf(), mb.buf));
     /* cleanup */
     mb.clean();
 }
 
 void
 HttpHeader::putSc(HttpHdrSc *sc)
 {
     assert(sc);
     /* remove old directives if any */
     delById(Http::HdrType::SURROGATE_CONTROL);
     /* pack into mb */
     MemBuf mb;
     mb.init();
     sc->packInto(&mb);
     /* put */
-    addEntry(new HttpHeaderEntry(Http::HdrType::SURROGATE_CONTROL, NULL, mb.buf));
+    addEntry(new HttpHeaderEntry(Http::HdrType::SURROGATE_CONTROL, SBuf(), mb.buf));
     /* cleanup */
     mb.clean();
 }
 
 void
 HttpHeader::putWarning(const int code, const char *const text)
 {
     char buf[512];
     snprintf(buf, sizeof(buf), "%i %s \"%s\"", code, visible_appname_string, text);
     putStr(Http::HdrType::WARNING, buf);
 }
 
 /* add extension header (these fields are not parsed/analyzed/joined, etc.) */
 void
 HttpHeader::putExt(const char *name, const char *value)
 {
     assert(name && value);
     debugs(55, 8, this << " adds ext entry " << name << " : " << value);
-    addEntry(new HttpHeaderEntry(Http::HdrType::OTHER, name, value));
+    addEntry(new HttpHeaderEntry(Http::HdrType::OTHER, SBuf(name), value));
 }
 
 int
 HttpHeader::getInt(Http::HdrType id) const
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt);  /* must be of an appropriate type */
     HttpHeaderEntry *e;
 
     if ((e = findEntry(id)))
         return e->getInt();
 
     return -1;
 }
 
 int64_t
 HttpHeader::getInt64(Http::HdrType id) const
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt64);    /* must be of an appropriate type */
@@ -1380,41 +1378,41 @@
 
         if (etagParseInit(&tot.tag, str)) {
             tot.valid = tot.tag.str != NULL;
             tot.time = -1;
         } else {
             /* or maybe it is time? */
             tot.time = parse_rfc1123(str);
             tot.valid = tot.time >= 0;
             tot.tag.str = NULL;
         }
     }
 
     assert(tot.time < 0 || !tot.tag.str);   /* paranoid */
     return tot;
 }
 
 /*
  * HttpHeaderEntry
  */
 
-HttpHeaderEntry::HttpHeaderEntry(Http::HdrType anId, const char *aName, const char *aValue)
+HttpHeaderEntry::HttpHeaderEntry(Http::HdrType anId, const SBuf &aName, const char *aValue)
 {
     assert(any_HdrType_enum_value(anId));
     id = anId;
 
     if (id != Http::HdrType::OTHER)
         name = Http::HeaderLookupTable.lookup(id).name;
     else
         name = aName;
 
     value = aValue;
 
     if (id != Http::HdrType::BAD_HDR)
         ++ headerStatsTable[id].aliveCount;
 
     debugs(55, 9, "created HttpHeaderEntry " << this << ": '" << name << " : " << value );
 }
 
 HttpHeaderEntry::~HttpHeaderEntry()
 {
     debugs(55, 9, "destroying entry " << this << ": '" << name << ": " << value << "'");
@@ -1452,92 +1450,88 @@
 
     if (Config.onoff.relaxed_header_parser && xisspace(field_start[name_len - 1])) {
         debugs(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2,
                "NOTICE: Whitespace after header name in '" << getStringPrefix(field_start, field_end-field_start) << "'");
 
         while (name_len > 0 && xisspace(field_start[name_len - 1]))
             --name_len;
 
         if (!name_len)
             return NULL;
     }
 
     /* now we know we can parse it */
 
     debugs(55, 9, "parsing HttpHeaderEntry: near '" <<  getStringPrefix(field_start, field_end-field_start) << "'");
 
     /* is it a "known" field? */
     Http::HdrType id = Http::HeaderLookupTable.lookup(field_start,name_len).id;
     debugs(55, 9, "got hdr-id=" << id);
 
-    String name;
+    SBuf theName;
 
     String value;
 
     if (id == Http::HdrType::BAD_HDR)
         id = Http::HdrType::OTHER;
 
     /* set field name */
     if (id == Http::HdrType::OTHER)
-        name.limitInit(field_start, name_len);
+        theName.append(field_start, name_len);
     else
-        name = Http::HeaderLookupTable.lookup(id).name;
+        theName = Http::HeaderLookupTable.lookup(id).name;
 
     /* trim field value */
     while (value_start < field_end && xisspace(*value_start))
         ++value_start;
 
     while (value_start < field_end && xisspace(field_end[-1]))
         --field_end;
 
     if (field_end - value_start > 65534) {
         /* String must be LESS THAN 64K and it adds a terminating NULL */
-        debugs(55, DBG_IMPORTANT, "WARNING: ignoring '" << name << "' header of " << (field_end - value_start) << " bytes");
-
-        if (id == Http::HdrType::OTHER)
-            name.clean();
-
+        debugs(55, DBG_IMPORTANT, "WARNING: ignoring '" << theName << "' header of " << (field_end - value_start) << " bytes");
         return NULL;
     }
 
     /* set field value */
     value.limitInit(value_start, field_end - value_start);
 
     if (id != Http::HdrType::BAD_HDR)
         ++ headerStatsTable[id].seenCount;
 
-    debugs(55, 9, "parsed HttpHeaderEntry: '" << name << ": " << value << "'");
+    debugs(55, 9, "parsed HttpHeaderEntry: '" << theName << ": " << value << "'");
 
-    return new HttpHeaderEntry(id, name.termedBuf(), value.termedBuf());
+    return new HttpHeaderEntry(id, theName, value.termedBuf());
 }
 
 HttpHeaderEntry *
 HttpHeaderEntry::clone() const
 {
-    return new HttpHeaderEntry(id, name.termedBuf(), value.termedBuf());
+    return new HttpHeaderEntry(id, name, value.termedBuf());
 }
 
 void
 HttpHeaderEntry::packInto(Packable * p) const
 {
     assert(p);
-    p->append(name.rawBuf(), name.size());
+    p->append(name.rawContent(), name.length());
     p->append(": ", 2);
     p->append(value.rawBuf(), value.size());
     p->append("\r\n", 2);
 }
 
 int
 HttpHeaderEntry::getInt() const
 {
     int val = -1;
     int ok = httpHeaderParseInt(value.termedBuf(), &val);
     httpHeaderNoteParsedEntry(id, value, ok == 0);
     /* XXX: Should we check ok - ie
      * return ok ? -1 : value;
      */
     return val;
 }
 
 int64_t
 HttpHeaderEntry::getInt64() const
 {
@@ -1734,28 +1728,28 @@
 HttpHeader::removeConnectionHeaderEntries()
 {
     if (has(Http::HdrType::CONNECTION)) {
         /* anything that matches Connection list member will be deleted */
         String strConnection;
 
         (void) getList(Http::HdrType::CONNECTION, &strConnection);
         const HttpHeaderEntry *e;
         HttpHeaderPos pos = HttpHeaderInitPos;
         /*
          * think: on-average-best nesting of the two loops (hdrEntry
          * and strListItem) @?@
          */
         /*
          * maybe we should delete standard stuff ("keep-alive","close")
          * from strConnection first?
          */
 
         int headers_deleted = 0;
         while ((e = getEntry(&pos))) {
-            if (strListIsMember(&strConnection, e->name.termedBuf(), ','))
+            if (strListIsMember(&strConnection, e->name, ','))
                 delAt(pos, headers_deleted);
         }
         if (headers_deleted)
             refreshMask();
     }
 }
 

=== modified file 'src/HttpHeader.h'
--- src/HttpHeader.h	2017-03-23 12:55:36 +0000
+++ src/HttpHeader.h	2017-04-22 18:28:11 +0000
@@ -35,101 +35,106 @@
 #endif
     hoRequest,
     hoReply,
 #if USE_OPENSSL
     hoErrorDetail,
 #endif
     hoEnd
 } http_hdr_owner_type;
 
 /** Iteration for headers; use HttpHeaderPos as opaque type, do not interpret */
 typedef ssize_t HttpHeaderPos;
 
 /* use this and only this to initialize HttpHeaderPos */
 #define HttpHeaderInitPos (-1)
 
 class HttpHeaderEntry
 {
     MEMPROXY_CLASS(HttpHeaderEntry);
 
 public:
-    HttpHeaderEntry(Http::HdrType id, const char *name, const char *value);
+    HttpHeaderEntry(Http::HdrType id, const SBuf &name, const char *value);
     ~HttpHeaderEntry();
     static HttpHeaderEntry *parse(const char *field_start, const char *field_end);
     HttpHeaderEntry *clone() const;
     void packInto(Packable *p) const;
     int getInt() const;
     int64_t getInt64() const;
 
     Http::HdrType id;
-    String name;
+    SBuf name;
     String value;
 };
 
 class ETag;
 class TimeOrTag;
 
 class HttpHeader
 {
 
 public:
     HttpHeader();
     explicit HttpHeader(const http_hdr_owner_type owner);
     HttpHeader(const HttpHeader &other);
     ~HttpHeader();
 
     HttpHeader &operator =(const HttpHeader &other);
 
     /* Interface functions */
     void clean();
     void append(const HttpHeader * src);
     bool update(HttpHeader const *fresh);
     void compact();
     int parse(const char *header_start, size_t len);
     /// Parses headers stored in a buffer.
     /// \returns 1 and sets hdr_sz on success
     /// \returns 0 when needs more data
     /// \returns -1 on error
     int parse(const char *buf, size_t buf_len, bool atEnd, size_t &hdr_sz);
     void packInto(Packable * p, bool mask_sensitive_info=false) const;
     HttpHeaderEntry *getEntry(HttpHeaderPos * pos) const;
     HttpHeaderEntry *findEntry(Http::HdrType id) const;
-    int delByName(const char *name);
+    /// deletes all fields with a given name, if any.
+    /// \return #fields deleted
+    int delByName(const SBuf &name);
+    /// \deprecated use SBuf method instead. performance regression: reallocates
+    int delByName(const char *name) { return delByName(SBuf(name)); }
     int delById(Http::HdrType id);
     void delAt(HttpHeaderPos pos, int &headers_deleted);
     void refreshMask();
     void addEntry(HttpHeaderEntry * e);
     void insertEntry(HttpHeaderEntry * e);
     String getList(Http::HdrType id) const;
     bool getList(Http::HdrType id, String *s) const;
     bool conflictingContentLength() const { return conflictingContentLength_; }
     String getStrOrList(Http::HdrType id) const;
     String getByName(const SBuf &name) const;
     String getByName(const char *name) const;
     String getById(Http::HdrType id) const;
     /// returns true iff a [possibly empty] field identified by id is there
     /// when returning true, also sets the `result` parameter (if it is not nil)
     bool getByIdIfPresent(Http::HdrType id, String *result) const;
     /// returns true iff a [possibly empty] named field is there
     /// when returning true, also sets the `value` parameter (if it is not nil)
     bool hasNamed(const SBuf &s, String *value = 0) const;
-    bool hasNamed(const char *name, int namelen, String *value = 0) const;
+    /// \deprecated use SBuf method instead.
+    bool hasNamed(const char *name, unsigned int namelen, String *value = 0) const;
     String getByNameListMember(const char *name, const char *member, const char separator) const;
     String getListMember(Http::HdrType id, const char *member, const char separator) const;
     int has(Http::HdrType id) const;
     /// Appends "this cache" information to VIA header field.
     /// Takes the initial VIA value from "from" parameter, if provided.
     void addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from = 0);
     void putInt(Http::HdrType id, int number);
     void putInt64(Http::HdrType id, int64_t number);
     void putTime(Http::HdrType id, time_t htime);
     void putStr(Http::HdrType id, const char *str);
     void putAuth(const char *auth_scheme, const char *realm);
     void putCc(const HttpHdrCc * cc);
     void putContRange(const HttpHdrContRange * cr);
     void putRange(const HttpHdrRange * range);
     void putSc(HttpHdrSc *sc);
     void putWarning(const int code, const char *const text); ///< add a Warning header
     void putExt(const char *name, const char *value);
     int getInt(Http::HdrType id) const;
     int64_t getInt64(Http::HdrType id) const;
     time_t getTime(Http::HdrType id) const;

=== modified file 'src/HttpHeaderTools.cc'
--- src/HttpHeaderTools.cc	2017-01-01 00:12:22 +0000
+++ src/HttpHeaderTools.cc	2017-04-22 19:30:24 +0000
@@ -70,41 +70,41 @@
     mb.clean();
 }
 
 /** wrapper arrounf PutContRange */
 void
 httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, int64_t ent_len)
 {
     HttpHdrContRange *cr = httpHdrContRangeCreate();
     assert(hdr && ent_len >= 0);
     httpHdrContRangeSet(cr, spec, ent_len);
     hdr->putContRange(cr);
     delete cr;
 }
 
 /**
  * \return true if a given directive is found in the Connection header field-value.
  *
  * \note if no Connection header exists we may check the Proxy-Connection header
  */
 bool
-httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive)
+httpHeaderHasConnDir(const HttpHeader * hdr, const SBuf &directive)
 {
     String list;
 
     /* what type of header do we have? */
     if (hdr->getList(Http::HdrType::CONNECTION, &list))
         return strListIsMember(&list, directive, ',') != 0;
 
 #if USE_HTTP_VIOLATIONS
     if (hdr->getList(Http::HdrType::PROXY_CONNECTION, &list))
         return strListIsMember(&list, directive, ',') != 0;
 #endif
 
     // else, no connection header for it to exist in
     return false;
 }
 
 /** handy to printf prefixes of potentially very long buffers */
 const char *
 getStringPrefix(const char *str, size_t sz)
 {
@@ -439,65 +439,65 @@
     // for backword compatibility, we allow replacements to be configured
     // for headers w/o access rules, but such replacements are ignored
     headerMangler *m = track(name);
 
     safe_free(m->replacement); // overwrite old value if any
     m->replacement = xstrdup(value);
 }
 
 const headerMangler *
 HeaderManglers::find(const HttpHeaderEntry &e) const
 {
     // a known header with a configured ACL list
     if (e.id != Http::HdrType::OTHER && Http::any_HdrType_enum_value(e.id) &&
             known[e.id].access_list)
         return &known[e.id];
 
     // a custom header
     if (e.id == Http::HdrType::OTHER) {
         // does it have an ACL list configured?
         // Optimize: use a name type that we do not need to convert to here
-        const ManglersByName::const_iterator i = custom.find(e.name.termedBuf());
+        SBuf tmp(e.name); // XXX: performance regression. c_str() reallocates
+        const ManglersByName::const_iterator i = custom.find(tmp.c_str());
         if (i != custom.end())
             return &i->second;
     }
 
     // Next-to-last resort: "Other" rules match any custom header
     if (e.id == Http::HdrType::OTHER && known[Http::HdrType::OTHER].access_list)
         return &known[Http::HdrType::OTHER];
 
     // Last resort: "All" rules match any header
     if (all.access_list)
         return &all;
 
     return NULL;
 }
 
 void
 httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd)
 {
     ACLFilledChecklist checklist(NULL, request, NULL);
 
     for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) {
         if (!hwa->aclList || checklist.fastCheck(hwa->aclList) == ACCESS_ALLOWED) {
             const char *fieldValue = NULL;
             MemBuf mb;
             if (hwa->quoted) {
                 if (al != NULL) {
                     mb.init();
                     hwa->valueFormat->assemble(mb, al, 0);
                     fieldValue = mb.content();
                 }
             } else {
                 fieldValue = hwa->fieldValue.c_str();
             }
 
             if (!fieldValue || fieldValue[0] == '\0')
                 fieldValue = "-";
 
-            HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, hwa->fieldName.c_str(),
-                    fieldValue);
+            HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, SBuf(hwa->fieldName), fieldValue);
             heads->addEntry(e);
         }
     }
 }
 

=== modified file 'src/HttpHeaderTools.h'
--- src/HttpHeaderTools.h	2017-01-01 00:12:22 +0000
+++ src/HttpHeaderTools.h	2017-04-22 18:03:08 +0000
@@ -1,48 +1,48 @@
 /*
  * Copyright (C) 1996-2017 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_HTTPHEADERTOOLS_H
 #define SQUID_HTTPHEADERTOOLS_H
 
 #include "acl/forward.h"
 #include "format/Format.h"
 #include "HttpHeader.h"
+#include "sbuf/forward.h"
 
 #include <functional>
 #include <list>
 #include <map>
 #include <string>
 #if HAVE_STRINGS_H
 #include <strings.h>
 #endif
 
 class HeaderWithAcl;
 class HttpHeader;
 class HttpRequest;
 class StoreEntry;
-class String;
 
 typedef std::list<HeaderWithAcl> HeaderWithAclList;
 
 /* Distinguish between Request and Reply (for header mangling) */
 typedef enum {
     ROR_REQUEST,
     ROR_REPLY
 } req_or_rep_t;
 
 // Currently a POD
 class headerMangler
 {
 public:
     acl_access *access_list;
     char *replacement;
 };
 
 /// A collection of headerMangler objects for a given message kind.
 class HeaderManglers
 {
@@ -108,30 +108,30 @@
     ACLList *aclList;
 
     /// compiled HTTP header field value (no macros)
     Format::Format *valueFormat;
 
     /// internal ID for "known" headers or HDR_OTHER
     Http::HdrType fieldId;
 
     /// whether fieldValue may contain macros
     bool quoted;
 };
 
 /// A strtoll(10) wrapper that checks for strtoll() failures and other problems.
 /// XXX: This function is not fully compatible with some HTTP syntax rules.
 /// Just like strtoll(), allows whitespace prefix, a sign, and _any_ suffix.
 /// Requires at least one digit to be present.
 /// Sets "off" and "end" arguments if and only if no problems were found.
 /// \return true if and only if no problems were found.
 bool httpHeaderParseOffset(const char *start, int64_t *offPtr, char **endPtr = nullptr);
 
-bool httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive);
+bool httpHeaderHasConnDir(const HttpHeader * hdr, const SBuf &directive);
 int httpHeaderParseInt(const char *start, int *val);
 void httpHeaderPutStrf(HttpHeader * hdr, Http::HdrType id, const char *fmt,...) PRINTF_FORMAT_ARG3;
 
 const char *getStringPrefix(const char *str, size_t len);
 
 void httpHdrMangleList(HttpHeader *, HttpRequest *, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep);
 
 #endif
 

=== modified file 'src/HttpReply.cc'
--- src/HttpReply.cc	2017-02-20 04:56:00 +0000
+++ src/HttpReply.cc	2017-04-22 18:27:42 +0000
@@ -557,41 +557,41 @@
 HttpReply::inheritProperties(const Http::Message *aMsg)
 {
     const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg);
     if (!aRep)
         return false;
     keep_alive = aRep->keep_alive;
     sources = aRep->sources;
     return true;
 }
 
 void HttpReply::removeStaleWarnings()
 {
     String warning;
     if (header.getList(Http::HdrType::WARNING, &warning)) {
         const String newWarning = removeStaleWarningValues(warning);
         if (warning.size() && warning.size() == newWarning.size())
             return; // some warnings are there and none changed
         header.delById(Http::HdrType::WARNING);
         if (newWarning.size()) { // some warnings left
             HttpHeaderEntry *const e =
-                new HttpHeaderEntry(Http::HdrType::WARNING, NULL, newWarning.termedBuf());
+                new HttpHeaderEntry(Http::HdrType::WARNING, SBuf(), newWarning.termedBuf());
             header.addEntry(e);
         }
     }
 }
 
 /**
  * Remove warning-values with warn-date different from Date value from
  * a single header entry. Returns a string with all valid warning-values.
  */
 String HttpReply::removeStaleWarningValues(const String &value)
 {
     String newValue;
     const char *item = 0;
     int len = 0;
     const char *pos = 0;
     while (strListGetItem(&value, ',', &item, &len, &pos)) {
         bool keep = true;
         // Does warning-value have warn-date (which contains quoted date)?
         // We scan backwards, looking for two quoted strings.
         // warning-value = warn-code SP warn-agent SP warn-text [SP warn-date]

=== modified file 'src/StrList.cc'
--- src/StrList.cc	2017-01-01 00:12:22 +0000
+++ src/StrList.cc	2017-04-22 19:33:11 +0000
@@ -1,66 +1,66 @@
 /*
  * Copyright (C) 1996-2017 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 66    HTTP Header Tools */
 
 #include "squid.h"
 #include "base/TextException.h"
+#include "sbuf/SBuf.h"
 #include "SquidString.h"
 #include "StrList.h"
 
 /** appends an item to the list */
 void
 strListAdd(String * str, const char *item, char del)
 {
     assert(str && item);
     const auto 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);
     }
     Must(str->canGrowBy(itemSize));
     str->append(item, itemSize);
 }
 
 /** returns true iff "m" is a member of the list */
 int
-strListIsMember(const String * list, const char *m, char del)
+strListIsMember(const String * list, const SBuf &m, char del)
 {
     const char *pos = NULL;
     const char *item;
     int ilen = 0;
-    int mlen;
 
-    assert(list && m);
-    mlen = strlen(m);
+    assert(list);
+    int mlen = m.plength();
     while (strListGetItem(list, del, &item, &ilen, &pos)) {
-        if (mlen == ilen && !strncasecmp(item, m, ilen))
+        if (mlen == ilen && m.caseCmp(item, ilen) == 0)
             return 1;
     }
     return 0;
 }
 
 /** returns true iff "s" is a substring of a member of the list */
 int
 strListIsSubstr(const String * list, const char *s, char del)
 {
     assert(list && del);
     return (list->find(s) != String::npos);
 
     /** \note
      * Note: the original code with a loop is broken because it uses strstr()
      * instead of strnstr(). If 's' contains a 'del', strListIsSubstr() may
      * return true when it should not. If 's' does not contain a 'del', the
      * implementaion is equavalent to strstr()! Thus, we replace the loop with
      * strstr() above until strnstr() is available.
      */
 }

=== modified file 'src/StrList.h'
--- src/StrList.h	2017-01-01 00:12:22 +0000
+++ src/StrList.h	2017-04-22 18:10:21 +0000
@@ -1,22 +1,24 @@
 /*
  * Copyright (C) 1996-2017 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 66    HTTP Header Tools */
 
 #ifndef SQUID_STRLIST_H_
 #define SQUID_STRLIST_H_
 
+#include "sbuf/forward.h"
+
 class String;
 
 void strListAdd(String * str, const char *item, char del);
-int strListIsMember(const String * str, const char *item, char del);
+int strListIsMember(const String * str, const SBuf &item, char del);
 int strListIsSubstr(const String * list, const char *s, char del);
 int strListGetItem(const String * str, char del, const char **item, int *ilen, const char **pos);
 
 #endif /* SQUID_STRLIST_H_ */
 

=== modified file 'src/adaptation/ecap/MessageRep.cc'
--- src/adaptation/ecap/MessageRep.cc	2017-02-16 11:51:56 +0000
+++ src/adaptation/ecap/MessageRep.cc	2017-04-22 18:27:27 +0000
@@ -36,41 +36,41 @@
     return squidId == Http::HdrType::OTHER ?
            theHeader.hasNamed(name.image().c_str(), name.image().size()) :
            static_cast<bool>(theHeader.has(squidId));
 }
 
 Adaptation::Ecap::HeaderRep::Value
 Adaptation::Ecap::HeaderRep::value(const Name &name) const
 {
     const Http::HdrType squidId = TranslateHeaderId(name);
     const String value = squidId == Http::HdrType::OTHER ?
                          theHeader.getByName(name.image().c_str()) :
                          theHeader.getStrOrList(squidId);
     return value.size() > 0 ?
            Value::FromTempString(value.termedBuf()) : Value();
 }
 
 void
 Adaptation::Ecap::HeaderRep::add(const Name &name, const Value &value)
 {
     const Http::HdrType squidId = TranslateHeaderId(name); // Http::HdrType::OTHER OK
-    HttpHeaderEntry *e = new HttpHeaderEntry(squidId, name.image().c_str(),
+    HttpHeaderEntry *e = new HttpHeaderEntry(squidId, name.image(),
             value.toString().c_str());
     theHeader.addEntry(e);
 
     if (squidId == Http::HdrType::CONTENT_LENGTH)
         theMessage.content_length = theHeader.getInt64(Http::HdrType::CONTENT_LENGTH);
 }
 
 void
 Adaptation::Ecap::HeaderRep::removeAny(const Name &name)
 {
     const Http::HdrType squidId = TranslateHeaderId(name);
     if (squidId == Http::HdrType::OTHER)
         theHeader.delByName(name.image().c_str());
     else
         theHeader.delById(squidId);
 
     if (squidId == Http::HdrType::CONTENT_LENGTH)
         theMessage.content_length = theHeader.getInt64(Http::HdrType::CONTENT_LENGTH);
 }
 

=== modified file 'src/adaptation/icap/ModXact.cc'
--- src/adaptation/icap/ModXact.cc	2017-02-23 10:02:10 +0000
+++ src/adaptation/icap/ModXact.cc	2017-04-22 18:04:19 +0000
@@ -780,41 +780,42 @@
         // of 206 responses too, where we do not want to update Http::Message::sources
         // flag. However even for 206 responses the state.sending is
         // not set yet to sendingVirgin. This is done in later step
         // after the parseBody method called.
         updateSources();
     }
 }
 
 void Adaptation::Icap::ModXact::parseIcapHead()
 {
     Must(state.sending == State::sendingUndecided);
 
     if (!parseHead(icapReply.getRaw()))
         return;
 
     if (expectIcapTrailers()) {
         Must(!trailerParser);
         trailerParser = new TrailerParser;
     }
 
-    if (httpHeaderHasConnDir(&icapReply->header, "close")) {
+    static SBuf close("close", 5);
+    if (httpHeaderHasConnDir(&icapReply->header, close)) {
         debugs(93, 5, HERE << "found connection close");
         reuseConnection = false;
     }
 
     switch (icapReply->sline.status()) {
 
     case Http::scContinue:
         handle100Continue();
         break;
 
     case Http::scOkay:
     case Http::scCreated: // Symantec Scan Engine 5.0 and later when modifying HTTP msg
 
         if (!validate200Ok()) {
             throw TexcHere("Invalid ICAP Response");
         } else {
             handle200Ok();
         }
 
         break;

=== modified file 'src/adaptation/icap/OptXact.cc'
--- src/adaptation/icap/OptXact.cc	2017-02-16 11:51:56 +0000
+++ src/adaptation/icap/OptXact.cc	2017-04-22 18:35:46 +0000
@@ -94,41 +94,42 @@
         setOutcome(xoOpt);
         sendAnswer(Answer::Forward(icapReply.getRaw()));
         Must(done()); // there should be nothing else to do
         return;
     }
 
     scheduleRead();
 }
 
 bool Adaptation::Icap::OptXact::parseResponse()
 {
     debugs(93, 5, "have " << readBuf.length() << " bytes to parse" << status());
     debugs(93, DBG_DATA, "\n" << readBuf);
 
     HttpReply::Pointer r(new HttpReply);
     r->protoPrefix = "ICAP/"; // TODO: make an IcapReply class?
 
     if (!parseHttpMsg(r.getRaw())) // throws on errors
         return false;
 
-    if (httpHeaderHasConnDir(&r->header, "close"))
+    static SBuf close("close", 5);
+    if (httpHeaderHasConnDir(&r->header, close))
         reuseConnection = false;
 
     icapReply = r;
     return true;
 }
 
 void Adaptation::Icap::OptXact::swanSong()
 {
     Adaptation::Icap::Xaction::swanSong();
 }
 
 void Adaptation::Icap::OptXact::finalizeLogInfo()
 {
     //    al.cache.caddr = 0;
     al.icap.reqMethod = Adaptation::methodOptions;
 
     if (icapReply != NULL && al.icap.bytesRead > icapReply->hdr_sz)
         al.icap.bodyBytesRead = al.icap.bytesRead - icapReply->hdr_sz;
 
     Adaptation::Icap::Xaction::finalizeLogInfo();

=== modified file 'src/format/Format.cc'
--- src/format/Format.cc	2017-03-03 12:12:01 +0000
+++ src/format/Format.cc	2017-04-22 14:23:10 +0000
@@ -730,71 +730,71 @@
         case LFT_ICAP_REQ_HEADER:
             if (al->icap.request) {
                 sb = StringToSBuf(al->icap.request->header.getByName(fmt->data.header.header));
                 out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_REQ_HEADER_ELEM:
             if (al->icap.request) {
                 sb = StringToSBuf(al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator));
                 out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_REQ_ALL_HEADERS:
             if (al->icap.request) {
                 HttpHeaderPos pos = HttpHeaderInitPos;
                 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
-                    sb.append(StringToSBuf(e->name));
+                    sb.append(e->name);
                     sb.append(": ");
                     sb.append(StringToSBuf(e->value));
                     sb.append("\r\n");
                 }
                 out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_REP_HEADER:
             if (al->icap.reply) {
                 sb = StringToSBuf(al->icap.reply->header.getByName(fmt->data.header.header));
                 out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_REP_HEADER_ELEM:
             if (al->icap.reply) {
                 sb = StringToSBuf(al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator));
                 out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_REP_ALL_HEADERS:
             if (al->icap.reply) {
                 HttpHeaderPos pos = HttpHeaderInitPos;
                 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
-                    sb.append(StringToSBuf(e->name));
+                    sb.append(e->name);
                     sb.append(": ");
                     sb.append(StringToSBuf(e->value));
                     sb.append("\r\n");
                 }
                 out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_TR_RESPONSE_TIME:
             outtv = al->icap.trTime;
             doMsec = 1;
             break;
 
         case LFT_ICAP_IO_TIME:
             outtv = al->icap.ioTime;
             doMsec = 1;
             break;
 
         case LFT_ICAP_STATUS_CODE:

=== modified file 'src/http.cc'
--- src/http.cc	2017-04-02 12:06:21 +0000
+++ src/http.cc	2017-04-22 18:27:20 +0000
@@ -1024,41 +1024,42 @@
                 EBIT_SET(entry->flags, ENTRY_REVALIDATE_ALWAYS);
         }
 #endif
     }
 
 #if HEADERS_LOG
     headersLog(1, 0, request->method, rep);
 
 #endif
 
     ctx_exit(ctx);
 }
 
 HttpStateData::ConnectionStatus
 HttpStateData::statusIfComplete() const
 {
     const HttpReply *rep = virginReply();
     /** \par
      * If the reply wants to close the connection, it takes precedence */
 
-    if (httpHeaderHasConnDir(&rep->header, "close"))
+    static SBuf close("close", 5);
+    if (httpHeaderHasConnDir(&rep->header, close))
         return COMPLETE_NONPERSISTENT_MSG;
 
     /** \par
      * If we didn't send a keep-alive request header, then this
      * can not be a persistent connection.
      */
     if (!flags.keepalive)
         return COMPLETE_NONPERSISTENT_MSG;
 
     /** \par
      * If we haven't sent the whole request then this can not be a persistent
      * connection.
      */
     if (!flags.request_sent) {
         debugs(11, 2, "Request not yet fully sent " << request->method << ' ' << entry->url());
         return COMPLETE_NONPERSISTENT_MSG;
     }
 
     /** \par
      * What does the reply have to say about keep-alive?
@@ -1756,41 +1757,41 @@
                                       const AccessLogEntryPointer &al,
                                       HttpHeader * hdr_out,
                                       const Http::StateFlags &flags)
 {
     /* building buffer for complex strings */
 #define BBUF_SZ (MAX_URL+32)
     LOCAL_ARRAY(char, bbuf, BBUF_SZ);
     LOCAL_ARRAY(char, ntoabuf, MAX_IPSTRLEN);
     const HttpHeader *hdr_in = &request->header;
     const HttpHeaderEntry *e = NULL;
     HttpHeaderPos pos = HttpHeaderInitPos;
     assert (hdr_out->owner == hoRequest);
 
     /* use our IMS header if the cached entry has Last-Modified time */
     if (request->lastmod > -1)
         hdr_out->putTime(Http::HdrType::IF_MODIFIED_SINCE, request->lastmod);
 
     // Add our own If-None-Match field if the cached entry has a strong ETag.
     // copyOneHeaderFromClientsideRequestToUpstreamRequest() adds client ones.
     if (request->etag.size() > 0) {
-        hdr_out->addEntry(new HttpHeaderEntry(Http::HdrType::IF_NONE_MATCH, NULL,
+        hdr_out->addEntry(new HttpHeaderEntry(Http::HdrType::IF_NONE_MATCH, SBuf(),
                                               request->etag.termedBuf()));
     }
 
     bool we_do_ranges = decideIfWeDoRanges (request);
 
     String strConnection (hdr_in->getList(Http::HdrType::CONNECTION));
 
     while ((e = hdr_in->getEntry(&pos)))
         copyOneHeaderFromClientsideRequestToUpstreamRequest(e, strConnection, request, hdr_out, we_do_ranges, flags);
 
     /* Abstraction break: We should interpret multipart/byterange responses
      * into offset-length data, and this works around our inability to do so.
      */
     if (!we_do_ranges && request->multipartRangeRequest()) {
         /* don't cache the result */
         request->flags.cachable = false;
         /* pretend it's not a range request */
         request->ignoreRange("want to request the whole object");
         request->flags.isRanged = false;
     }
@@ -2082,42 +2083,41 @@
     case Http::HdrType::X_FORWARDED_FOR:
 
     case Http::HdrType::CACHE_CONTROL:
         /** \par X-Forwarded-For:, Cache-Control:
          * handled specially by Squid, so leave off for now.
          * append these after the loop if needed */
         break;
 
     case Http::HdrType::FRONT_END_HTTPS:
         /** \par Front-End-Https:
          * Pass thru only if peer is configured with front-end-https */
         if (!flags.front_end_https)
             hdr_out->addEntry(e->clone());
 
         break;
 
     default:
         /** \par default.
          * pass on all other header fields
          * which are NOT listed by the special Connection: header. */
-
-        if (strConnection.size()>0 && strListIsMember(&strConnection, e->name.termedBuf(), ',')) {
+        if (strConnection.size()>0 && strListIsMember(&strConnection, e->name, ',')) {
             debugs(11, 2, "'" << e->name << "' header cropped by Connection: definition");
             return;
         }
 
         hdr_out->addEntry(e->clone());
     }
 }
 
 bool
 HttpStateData::decideIfWeDoRanges (HttpRequest * request)
 {
     bool result = true;
     /* decide if we want to do Ranges ourselves
      * and fetch the whole object now)
      * We want to handle Ranges ourselves iff
      *    - we can actually parse client Range specs
      *    - the specs are expected to be simple enough (e.g. no out-of-order ranges)
      *    - reply will be cachable
      * (If the reply will be uncachable we have to throw it away after
      *  serving this request, so it is better to forward ranges to

=== modified file 'src/http/Message.cc'
--- src/http/Message.cc	2017-02-20 04:56:00 +0000
+++ src/http/Message.cc	2017-04-22 18:05:16 +0000
@@ -244,44 +244,46 @@
     reset();
     return -1;
 }
 
 void
 Http::Message::setContentLength(int64_t clen)
 {
     header.delById(Http::HdrType::CONTENT_LENGTH); // if any
     header.putInt64(Http::HdrType::CONTENT_LENGTH, clen);
     content_length = clen;
 }
 
 bool
 Http::Message::persistent() const
 {
     if (http_ver > Http::ProtocolVersion(1,0)) {
         /*
          * for modern versions of HTTP: persistent unless there is
          * a "Connection: close" header.
          */
-        return !httpHeaderHasConnDir(&header, "close");
+        static SBuf close("close", 5);
+        return !httpHeaderHasConnDir(&header, close);
     } else {
         /* for old versions of HTTP: persistent if has "keep-alive" */
-        return httpHeaderHasConnDir(&header, "keep-alive");
+        static SBuf keepAlive("keep-alive", 10);
+        return httpHeaderHasConnDir(&header, keepAlive);
     }
 }
 
 void
 Http::Message::packInto(Packable *p, bool full_uri) const
 {
     packFirstLineInto(p, full_uri);
     header.packInto(p);
     p->append("\r\n", 2);
 }
 
 void
 Http::Message::hdrCacheInit()
 {
     content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
     assert(NULL == cache_control);
     cache_control = header.getCc();
 }
 
 /// useful for debugging

=== modified file 'src/store.cc'
--- src/store.cc	2017-03-03 22:15:10 +0000
+++ src/store.cc	2017-04-22 19:35:07 +0000
@@ -1987,42 +1987,44 @@
 {
     const String reqETags = request.header.getList(Http::HdrType::IF_MATCH);
     return hasOneOfEtags(reqETags, false);
 }
 
 bool
 StoreEntry::hasIfNoneMatchEtag(const HttpRequest &request) const
 {
     const String reqETags = request.header.getList(Http::HdrType::IF_NONE_MATCH);
     // weak comparison is allowed only for HEAD or full-body GET requests
     const bool allowWeakMatch = !request.flags.isRanged &&
                                 (request.method == Http::METHOD_GET || request.method == Http::METHOD_HEAD);
     return hasOneOfEtags(reqETags, allowWeakMatch);
 }
 
 /// whether at least one of the request ETags matches entity ETag
 bool
 StoreEntry::hasOneOfEtags(const String &reqETags, const bool allowWeakMatch) const
 {
     const ETag repETag = getReply()->header.getETag(Http::HdrType::ETAG);
-    if (!repETag.str)
-        return strListIsMember(&reqETags, "*", ',');
+    if (!repETag.str) {
+        static SBuf asterisk("*", 1);
+        return strListIsMember(&reqETags, asterisk, ',');
+    }
 
     bool matched = false;
     const char *pos = NULL;
     const char *item;
     int ilen;
     while (!matched && strListGetItem(&reqETags, ',', &item, &ilen, &pos)) {
         if (!strncmp(item, "*", ilen))
             matched = true;
         else {
             String str;
             str.append(item, ilen);
             ETag reqETag;
             if (etagParseInit(&reqETag, str.termedBuf())) {
                 matched = allowWeakMatch ? etagIsWeakEqual(repETag, reqETag) :
                           etagIsStrongEqual(repETag, reqETag);
             }
         }
     }
     return matched;
 }

_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to