Hi, the hard part is IMO done, should be enough for a first round of review; I've left the TODO list I discussed with Amos on IRC in to keep track of what's been done. There are two things left that I can think of: getting rid of the masks (ListHeadersMask etc) in favor of bools or bitfields in the headersTable (I'd go for bools for speed, I believe we can afford to waste 100-ish bytes per flag), and possibly switching some enums to c++11 strongly-typed enums (enum class foo) in some places.
Code has been casually run-tested, seems to be OK. On Thu, Jul 30, 2015 at 9:44 PM, Kinkie <gkin...@gmail.com> wrote: > > > On Thu, Jul 30, 2015 at 6:42 PM, Alex Rousskov < > rouss...@measurement-factory.com> wrote: > >> On 07/30/2015 01:44 AM, Kinkie wrote: >> >> > headerDescription: a std::vector keyed by header ID containing header >> type >> >> HeaderDescriptions (plural) then. >> > > Ok, will change. > > >> > template <typename EnumType> >> > typedef struct LookupTableRecord >> > { >> > const char *name; >> > EnumType id; >> > } >> >> Please avoid "struct" whenever possible. Typing "public:" once is better >> that always remembering whether to search for and forward-declare a >> "struct Foo" or a "class Foo". >> > > Sure. > > >> > template<typename Enumtype, typename RecordType = >> > LookupTableRecord<EnumType> > >> > class LookupTable >> > { >> > //... >> > } >> > >> > This would make it so that someone could define a custom Record type, >> > and as long as that record type matches the signature of >> > LookupTableRecord, LookupTable won't care. >> >> Do not add that template parameter until you actually need to customize >> the record type. If you end up using this design, just typedef Record to >> be LookupTableRecord. >> > > Need is already present; it's done exactly as you say. > Thanks! > > -- > Francesco > -- Francesco
# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kin...@squid-cache.org-20150731193737-firdyshwvzs546v7 # target_branch: ../trunk # testament_sha1: 2d0c92ab3a9b65da79ba165993fa47a61b13a869 # timestamp: 2015-07-31 21:38:29 +0200 # base_revision_id: squ...@treenet.co.nz-20150731062638-\ # 7524t81yy3nyarog # # Begin patch === modified file 'src/HttpHeader.cc' --- src/HttpHeader.cc 2015-03-04 16:02:53 +0000 +++ src/HttpHeader.cc 2015-07-31 15:05:22 +0000 @@ -9,6 +9,7 @@ /* DEBUG: section 55 HTTP Header */ #include "squid.h" +#include "base/LookupTable.h" #include "base64.h" #include "globals.h" #include "HttpHdrCc.h" @@ -59,114 +60,20 @@ * local constants and vars */ -/* - * A table with major attributes for every known field. - * We calculate name lengths and reorganize this array on start up. - * After reorganization, field id can be used as an index to the table. +/* TODO: + * DONE 1. shift HDR_BAD_HDR to end of enum + * DONE 2. shift headers data array to http/RegistredHeaders.cc + * DONE 3. creatign LookupTable object from teh enum and array + * (with HDR_BAD_HDR as invalid value) + * DONE 4. replacing httpHeaderIdByName() uses with the lookup table + * 5. merge HDR_BAD_HDR and HDR_ENUM_END into one thing + * DONE 6. replacing httpHeaderNameById with direct array lookups + * 7. being looking at the other arrays removal */ -static const HttpHeaderFieldAttrs HeadersAttrs[] = { - HttpHeaderFieldAttrs("Accept", HDR_ACCEPT, ftStr), - - HttpHeaderFieldAttrs("Accept-Charset", HDR_ACCEPT_CHARSET, ftStr), - HttpHeaderFieldAttrs("Accept-Encoding", HDR_ACCEPT_ENCODING, ftStr), - HttpHeaderFieldAttrs("Accept-Language", HDR_ACCEPT_LANGUAGE, ftStr), - HttpHeaderFieldAttrs("Accept-Ranges", HDR_ACCEPT_RANGES, ftStr), - HttpHeaderFieldAttrs("Age", HDR_AGE, ftInt), - HttpHeaderFieldAttrs("Allow", HDR_ALLOW, ftStr), - HttpHeaderFieldAttrs("Alternate-Protocol", HDR_ALTERNATE_PROTOCOL, ftStr), - HttpHeaderFieldAttrs("Authorization", HDR_AUTHORIZATION, ftStr), /* for now */ - HttpHeaderFieldAttrs("Cache-Control", HDR_CACHE_CONTROL, ftPCc), - HttpHeaderFieldAttrs("Connection", HDR_CONNECTION, ftStr), - HttpHeaderFieldAttrs("Content-Base", HDR_CONTENT_BASE, ftStr), - HttpHeaderFieldAttrs("Content-Disposition", HDR_CONTENT_DISPOSITION, ftStr), /* for now */ - HttpHeaderFieldAttrs("Content-Encoding", HDR_CONTENT_ENCODING, ftStr), - HttpHeaderFieldAttrs("Content-Language", HDR_CONTENT_LANGUAGE, ftStr), - HttpHeaderFieldAttrs("Content-Length", HDR_CONTENT_LENGTH, ftInt64), - HttpHeaderFieldAttrs("Content-Location", HDR_CONTENT_LOCATION, ftStr), - HttpHeaderFieldAttrs("Content-MD5", HDR_CONTENT_MD5, ftStr), /* for now */ - HttpHeaderFieldAttrs("Content-Range", HDR_CONTENT_RANGE, ftPContRange), - HttpHeaderFieldAttrs("Content-Type", HDR_CONTENT_TYPE, ftStr), - HttpHeaderFieldAttrs("Cookie", HDR_COOKIE, ftStr), - HttpHeaderFieldAttrs("Cookie2", HDR_COOKIE2, ftStr), - HttpHeaderFieldAttrs("Date", HDR_DATE, ftDate_1123), - HttpHeaderFieldAttrs("ETag", HDR_ETAG, ftETag), - HttpHeaderFieldAttrs("Expect", HDR_EXPECT, ftStr), - HttpHeaderFieldAttrs("Expires", HDR_EXPIRES, ftDate_1123), - HttpHeaderFieldAttrs("Forwarded", HDR_FORWARDED, ftStr), - HttpHeaderFieldAttrs("From", HDR_FROM, ftStr), - HttpHeaderFieldAttrs("Host", HDR_HOST, ftStr), - HttpHeaderFieldAttrs("HTTP2-Settings", HDR_HTTP2_SETTINGS, ftStr), /* for now */ - HttpHeaderFieldAttrs("If-Match", HDR_IF_MATCH, ftStr), /* for now */ - HttpHeaderFieldAttrs("If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123), - HttpHeaderFieldAttrs("If-None-Match", HDR_IF_NONE_MATCH, ftStr), /* for now */ - HttpHeaderFieldAttrs("If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag), - HttpHeaderFieldAttrs("If-Unmodified-Since", HDR_IF_UNMODIFIED_SINCE, ftDate_1123), - HttpHeaderFieldAttrs("Keep-Alive", HDR_KEEP_ALIVE, ftStr), - HttpHeaderFieldAttrs("Key", HDR_KEY, ftStr), - HttpHeaderFieldAttrs("Last-Modified", HDR_LAST_MODIFIED, ftDate_1123), - HttpHeaderFieldAttrs("Link", HDR_LINK, ftStr), - HttpHeaderFieldAttrs("Location", HDR_LOCATION, ftStr), - HttpHeaderFieldAttrs("Max-Forwards", HDR_MAX_FORWARDS, ftInt64), - HttpHeaderFieldAttrs("Mime-Version", HDR_MIME_VERSION, ftStr), /* for now */ - HttpHeaderFieldAttrs("Negotiate", HDR_NEGOTIATE, ftStr), - HttpHeaderFieldAttrs("Origin", HDR_ORIGIN, ftStr), - HttpHeaderFieldAttrs("Pragma", HDR_PRAGMA, ftStr), - HttpHeaderFieldAttrs("Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftStr), - HttpHeaderFieldAttrs("Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr), - HttpHeaderFieldAttrs("Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr), - HttpHeaderFieldAttrs("Proxy-Connection", HDR_PROXY_CONNECTION, ftStr), - HttpHeaderFieldAttrs("Proxy-support", HDR_PROXY_SUPPORT, ftStr), - HttpHeaderFieldAttrs("Public", HDR_PUBLIC, ftStr), - HttpHeaderFieldAttrs("Range", HDR_RANGE, ftPRange), - HttpHeaderFieldAttrs("Referer", HDR_REFERER, ftStr), - HttpHeaderFieldAttrs("Request-Range", HDR_REQUEST_RANGE, ftPRange), /* usually matches HDR_RANGE */ - HttpHeaderFieldAttrs("Retry-After", HDR_RETRY_AFTER, ftStr), /* for now (ftDate_1123 or ftInt!) */ - HttpHeaderFieldAttrs("Server", HDR_SERVER, ftStr), - HttpHeaderFieldAttrs("Set-Cookie", HDR_SET_COOKIE, ftStr), - HttpHeaderFieldAttrs("Set-Cookie2", HDR_SET_COOKIE2, ftStr), - HttpHeaderFieldAttrs("TE", HDR_TE, ftStr), - HttpHeaderFieldAttrs("Title", HDR_TITLE, ftStr), - HttpHeaderFieldAttrs("Trailer", HDR_TRAILER, ftStr), - HttpHeaderFieldAttrs("Transfer-Encoding", HDR_TRANSFER_ENCODING, ftStr), - HttpHeaderFieldAttrs("Translate", HDR_TRANSLATE, ftStr), /* for now. may need to crop */ - HttpHeaderFieldAttrs("Unless-Modified-Since", HDR_UNLESS_MODIFIED_SINCE, ftStr), /* for now ignore. may need to crop */ - HttpHeaderFieldAttrs("Upgrade", HDR_UPGRADE, ftStr), /* for now */ - HttpHeaderFieldAttrs("User-Agent", HDR_USER_AGENT, ftStr), - HttpHeaderFieldAttrs("Vary", HDR_VARY, ftStr), /* for now */ - HttpHeaderFieldAttrs("Via", HDR_VIA, ftStr), /* for now */ - HttpHeaderFieldAttrs("Warning", HDR_WARNING, ftStr), /* for now */ - HttpHeaderFieldAttrs("WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr), - HttpHeaderFieldAttrs("Authentication-Info", HDR_AUTHENTICATION_INFO, ftStr), - HttpHeaderFieldAttrs("X-Cache", HDR_X_CACHE, ftStr), - HttpHeaderFieldAttrs("X-Cache-Lookup", HDR_X_CACHE_LOOKUP, ftStr), - HttpHeaderFieldAttrs("X-Forwarded-For", HDR_X_FORWARDED_FOR, ftStr), - HttpHeaderFieldAttrs("X-Request-URI", HDR_X_REQUEST_URI, ftStr), - HttpHeaderFieldAttrs("X-Squid-Error", HDR_X_SQUID_ERROR, ftStr), -#if X_ACCELERATOR_VARY - HttpHeaderFieldAttrs("X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr), -#endif -#if USE_ADAPTATION - HttpHeaderFieldAttrs("X-Next-Services", HDR_X_NEXT_SERVICES, ftStr), -#endif - HttpHeaderFieldAttrs("Surrogate-Capability", HDR_SURROGATE_CAPABILITY, ftStr), - HttpHeaderFieldAttrs("Surrogate-Control", HDR_SURROGATE_CONTROL, ftPSc), - HttpHeaderFieldAttrs("Front-End-Https", HDR_FRONT_END_HTTPS, ftStr), - HttpHeaderFieldAttrs("FTP-Command", HDR_FTP_COMMAND, ftStr), - HttpHeaderFieldAttrs("FTP-Arguments", HDR_FTP_ARGUMENTS, ftStr), - HttpHeaderFieldAttrs("FTP-Pre", HDR_FTP_PRE, ftStr), - HttpHeaderFieldAttrs("FTP-Status", HDR_FTP_STATUS, ftInt), - HttpHeaderFieldAttrs("FTP-Reason", HDR_FTP_REASON, ftStr), - HttpHeaderFieldAttrs("Other:", HDR_OTHER, ftStr) /* ':' will not allow matches */ -}; - -static HttpHeaderFieldInfo *Headers = NULL; - -http_hdr_type &operator++ (http_hdr_type &aHeader) -{ - int tmp = (int)aHeader; - aHeader = (http_hdr_type)(++tmp); - return aHeader; -} + + +LookupTable<http_hdr_type, HeaderTableRecord> headerLookupTable(HDR_BAD_HDR, headerTable); +std::vector<HttpHeaderFieldStat> headerStatsTable(HDR_OTHER+1); /* * headers with field values defined as #(values) in HTTP/1.1 @@ -370,11 +277,12 @@ { /* check that we have enough space for masks */ assert(8 * sizeof(HttpHeaderMask) >= HDR_ENUM_END); - /* all headers must be described */ - assert(countof(HeadersAttrs) == HDR_ENUM_END); - - if (!Headers) - Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); + + // check invariant: for each index in headerTable, (int)headerTable[index] = index + for (int i = 0; headerTable[i].name; ++i) + assert(headerTable[i].id == i); + + // use headerLookupTable in place of Headers /* create masks */ httpHeaderMaskInit(&ListHeadersMask, 0); @@ -406,8 +314,6 @@ void httpHeaderCleanModule(void) { - httpHeaderDestroyFieldsInfo(Headers, HDR_ENUM_END); - Headers = NULL; httpHdrCcCleanModule(); httpHdrScCleanModule(); } @@ -549,7 +455,7 @@ if (denied_mask && CBIT_TEST(*denied_mask, e->id)) continue; - debugs(55, 7, "Updating header '" << HeadersAttrs[e->id].name << "' in cached entry"); + debugs(55, 7, "Updating header '" << headerTable[e->id].name << "' in cached entry"); addEntry(e->clone()); } @@ -953,10 +859,11 @@ debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size()); - if (CBIT_TEST(mask, e->id)) - ++ Headers[e->id].stat.repCount; - else + if (CBIT_TEST(mask, e->id)) { + ++ headerStatsTable[e->id].repCount; + } else { CBIT_SET(mask, e->id); + } entries.push_back(e); @@ -975,10 +882,11 @@ debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size()); - if (CBIT_TEST(mask, e->id)) - ++ Headers[e->id].stat.repCount; - else + if (CBIT_TEST(mask, e->id)) { + ++ headerStatsTable[e->id].repCount; //TODO: use operator[] ? + } else { CBIT_SET(mask, e->id); + } entries.insert(entries.begin(),e); @@ -1010,7 +918,7 @@ */ /* temporary warning: remove it? (Is it useful for diagnostics ?) */ if (!s->size()) - debugs(55, 3, "empty list header: " << Headers[id].name << "(" << id << ")"); + debugs(55, 3, "empty list header: " << headerTable[id].name << "(" << id << ")"); else debugs(55, 6, this << ": joined for id " << id << ": " << s); @@ -1044,7 +952,7 @@ */ /* temporary warning: remove it? (Is it useful for diagnostics ?) */ if (!s.size()) - debugs(55, 3, "empty list header: " << Headers[id].name << "(" << id << ")"); + debugs(55, 3, "empty list header: " << headerTable[id].name << "(" << id << ")"); else debugs(55, 6, this << ": joined for id " << id << ": " << s); @@ -1088,7 +996,7 @@ assert(name); /* First try the quick path */ - id = httpHeaderIdByNameDef(name, strlen(name)); + id = httpHeaderIdByNameDef(SBuf(name)); if (id != -1) { if (!has(id)) @@ -1179,7 +1087,7 @@ HttpHeader::putInt(http_hdr_type id, int number) { assert_eid(id); - assert(Headers[id].type == ftInt); /* must be of an appropriate type */ + assert(headerTable[id].type == ftInt); /* must be of an appropriate type */ assert(number >= 0); addEntry(new HttpHeaderEntry(id, NULL, xitoa(number))); } @@ -1188,7 +1096,7 @@ HttpHeader::putInt64(http_hdr_type id, int64_t number) { assert_eid(id); - assert(Headers[id].type == ftInt64); /* must be of an appropriate type */ + assert(headerTable[id].type == ftInt64); /* must be of an appropriate type */ assert(number >= 0); addEntry(new HttpHeaderEntry(id, NULL, xint64toa(number))); } @@ -1197,7 +1105,7 @@ HttpHeader::putTime(http_hdr_type id, time_t htime) { assert_eid(id); - assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */ + assert(headerTable[id].type == ftDate_1123); /* must be of an appropriate type */ assert(htime >= 0); addEntry(new HttpHeaderEntry(id, NULL, mkrfc1123(htime))); } @@ -1206,7 +1114,7 @@ HttpHeader::insertTime(http_hdr_type id, time_t htime) { assert_eid(id); - assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */ + assert(headerTable[id].type == ftDate_1123); /* must be of an appropriate type */ assert(htime >= 0); insertEntry(new HttpHeaderEntry(id, NULL, mkrfc1123(htime))); } @@ -1215,7 +1123,7 @@ HttpHeader::putStr(http_hdr_type id, const char *str) { assert_eid(id); - assert(Headers[id].type == ftStr); /* must be of an appropriate type */ + assert(headerTable[id].type == ftStr); /* must be of an appropriate type */ assert(str); addEntry(new HttpHeaderEntry(id, NULL, str)); } @@ -1312,7 +1220,7 @@ HttpHeader::getInt(http_hdr_type id) const { assert_eid(id); - assert(Headers[id].type == ftInt); /* must be of an appropriate type */ + assert(headerTable[id].type == ftInt); /* must be of an appropriate type */ HttpHeaderEntry *e; if ((e = findEntry(id))) @@ -1325,7 +1233,7 @@ HttpHeader::getInt64(http_hdr_type id) const { assert_eid(id); - assert(Headers[id].type == ftInt64); /* must be of an appropriate type */ + assert(headerTable[id].type == ftInt64); /* must be of an appropriate type */ HttpHeaderEntry *e; if ((e = findEntry(id))) @@ -1340,7 +1248,7 @@ HttpHeaderEntry *e; time_t value = -1; assert_eid(id); - assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */ + assert(headerTable[id].type == ftDate_1123); /* must be of an appropriate type */ if ((e = findEntry(id))) { value = parse_rfc1123(e->value.termedBuf()); @@ -1356,7 +1264,7 @@ { HttpHeaderEntry *e; assert_eid(id); - assert(Headers[id].type == ftStr); /* must be of an appropriate type */ + assert(headerTable[id].type == ftStr); /* must be of an appropriate type */ if ((e = findEntry(id))) { httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */ @@ -1372,7 +1280,7 @@ { HttpHeaderEntry *e; assert_eid(id); - assert(Headers[id].type == ftStr); /* must be of an appropriate type */ + assert(headerTable[id].type == ftStr); /* must be of an appropriate type */ if ((e = findLastEntry(id))) { httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */ @@ -1510,7 +1418,7 @@ { ETag etag = {NULL, -1}; HttpHeaderEntry *e; - assert(Headers[id].type == ftETag); /* must be of an appropriate type */ + assert(headerTable[id].type == ftETag); /* must be of an appropriate type */ if ((e = findEntry(id))) etagParseInit(&etag, e->value.termedBuf()); @@ -1523,7 +1431,7 @@ { TimeOrTag tot; HttpHeaderEntry *e; - assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */ + assert(headerTable[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */ memset(&tot, 0, sizeof(tot)); if ((e = findEntry(id))) { @@ -1555,13 +1463,13 @@ id = anId; if (id != HDR_OTHER) - name = Headers[id].name; + name = headerTable[id].name; else name = aName; value = aValue; - ++ Headers[id].stat.aliveCount; + ++ headerStatsTable[id].aliveCount; debugs(55, 9, "created HttpHeaderEntry " << this << ": '" << name << " : " << value ); } @@ -1570,16 +1478,10 @@ { assert_eid(id); debugs(55, 9, "destroying entry " << this << ": '" << name << ": " << value << "'"); - /* clean name if needed */ - - if (id == HDR_OTHER) - name.clean(); - - value.clean(); - - assert(Headers[id].stat.aliveCount); - - -- Headers[id].stat.aliveCount; + + assert(headerStatsTable[id].aliveCount); // is this really needed? + + -- headerStatsTable[id].aliveCount; id = HDR_BAD_HDR; } @@ -1623,7 +1525,8 @@ debugs(55, 9, "parsing HttpHeaderEntry: near '" << getStringPrefix(field_start, field_end-field_start) << "'"); /* is it a "known" field? */ - http_hdr_type id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END); + http_hdr_type id = headerLookupTable.lookup(SBuf(field_start,name_len)); + debugs(55, 9, "got hdr id hdr: " << id); String name; @@ -1638,7 +1541,7 @@ if (id == HDR_OTHER) name.limitInit(field_start, name_len); else - name = Headers[id].name; + name = headerTable[id].name; /* trim field value */ while (value_start < field_end && xisspace(*value_start)) @@ -1660,7 +1563,7 @@ /* set field value */ value.limitInit(value_start, field_end - value_start); - ++ Headers[id].stat.seenCount; + ++ headerStatsTable[id].seenCount; debugs(55, 9, "parsed HttpHeaderEntry: '" << name << ": " << value << "'"); @@ -1687,7 +1590,6 @@ HttpHeaderEntry::getInt() const { assert_eid (id); - assert (Headers[id].type == ftInt); int val = -1; int ok = httpHeaderParseInt(value.termedBuf(), &val); httpHeaderNoteParsedEntry(id, value, !ok); @@ -1701,7 +1603,6 @@ HttpHeaderEntry::getInt64() const { assert_eid (id); - assert (Headers[id].type == ftInt64); int64_t val = -1; int ok = httpHeaderParseOffset(value.termedBuf(), &val); httpHeaderNoteParsedEntry(id, value, !ok); @@ -1714,11 +1615,11 @@ static void httpHeaderNoteParsedEntry(http_hdr_type id, String const &context, int error) { - ++ Headers[id].stat.parsCount; + ++ headerStatsTable[id].parsCount; if (error) { - ++ Headers[id].stat.errCount; - debugs(55, 2, "cannot parse hdr field: '" << Headers[id].name << ": " << context << "'"); + ++ headerStatsTable[id].errCount; + debugs(55, 2, "cannot parse hdr field: '" << headerTable[id].name << ": " << context << "'"); } } @@ -1735,7 +1636,7 @@ { const int id = (int) val; const int valid_id = id >= 0 && id < HDR_ENUM_END; - const char *name = valid_id ? Headers[id].name.termedBuf() : "INVALID"; + const char *name = valid_id ? headerTable[id].name : "INVALID"; int visible = count > 0; /* for entries with zero count, list only those that belong to current type of message */ @@ -1787,7 +1688,7 @@ httpHeaderStoreReport(StoreEntry * e) { int i; - http_hdr_type ht; +// http_hdr_type ht; assert(e); HttpHeaderStats[0].parsedCount = @@ -1809,12 +1710,13 @@ storeAppendPrintf(e, "%2s\t %-25s\t %5s\t %6s\t %6s\n", "id", "name", "#alive", "%err", "%repeat"); - for (ht = (http_hdr_type)0; ht < HDR_ENUM_END; ++ht) { - HttpHeaderFieldInfo *f = Headers + ht; + // scan heaaderTable and output + for (int j = 0; headerTable[j].name != nullptr; ++j) { + auto stats = headerStatsTable[j]; storeAppendPrintf(e, "%2d\t %-25s\t %5d\t %6.3f\t %6.3f\n", - f->id, f->name.termedBuf(), f->stat.aliveCount, - xpercent(f->stat.errCount, f->stat.parsCount), - xpercent(f->stat.repCount, f->stat.seenCount)); + headerTable[j].id, headerTable[j].name, stats.aliveCount, + xpercent(stats.errCount, stats.parsCount), + xpercent(stats.repCount, stats.seenCount)); } storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n", @@ -1824,6 +1726,7 @@ storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount); } +// (ab)used by other modules. http_hdr_type httpHeaderIdByName(const char *name, size_t name_len, const HttpHeaderFieldInfo * info, int end) { @@ -1841,23 +1744,22 @@ } http_hdr_type +httpHeaderIdByNameDef(const SBuf &name) +{ + return headerLookupTable.lookup(name); +} + +http_hdr_type httpHeaderIdByNameDef(const char *name, int name_len) { - if (!Headers) - Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); - - return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END); + return headerLookupTable.lookup(SBuf(name,name_len)); } const char * httpHeaderNameById(int id) { - if (!Headers) - Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); - assert(id >= 0 && id < HDR_ENUM_END); - - return Headers[id].name.termedBuf(); + return headerTable[id].name; } int === modified file 'src/HttpHeader.h' --- src/HttpHeader.h 2015-03-03 11:43:26 +0000 +++ src/HttpHeader.h 2015-07-31 14:42:48 +0000 @@ -26,21 +26,6 @@ class StoreEntry; class SBuf; -/** possible types for http header fields */ -typedef enum { - ftInvalid = HDR_ENUM_END, /**< to catch nasty errors with hdr_id<->fld_type clashes */ - ftInt, - ftInt64, - ftStr, - ftDate_1123, - ftETag, - ftPCc, - ftPContRange, - ftPRange, - ftPSc, - ftDate_1123_or_ETag -} field_type; - /** Possible owners of http header */ typedef enum { hoNone =0, === modified file 'src/HttpHeaderTools.cc' --- src/HttpHeaderTools.cc 2015-03-03 16:51:03 +0000 +++ src/HttpHeaderTools.cc 2015-07-31 14:14:34 +0000 @@ -456,7 +456,7 @@ headerMangler * HeaderManglers::track(const char *name) { - int id = httpHeaderIdByNameDef(name, strlen(name)); + int id = httpHeaderIdByNameDef(SBuf(name)); if (id == HDR_BAD_HDR) { // special keyword or a custom header if (strcmp(name, "All") == 0) === modified file 'src/HttpHeaderTools.h' --- src/HttpHeaderTools.h 2015-01-13 07:25:36 +0000 +++ src/HttpHeaderTools.h 2015-07-31 10:28:59 +0000 @@ -118,6 +118,7 @@ HttpHeaderFieldInfo *httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count); void httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * info, int count); http_hdr_type httpHeaderIdByName(const char *name, size_t name_len, const HttpHeaderFieldInfo * attrs, int end); +http_hdr_type httpHeaderIdByNameDef(const SBuf &name); http_hdr_type httpHeaderIdByNameDef(const char *name, int name_len); const char *httpHeaderNameById(int id); int httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive); === modified file 'src/acl/HttpHeaderData.cc' --- src/acl/HttpHeaderData.cc 2015-07-27 12:51:43 +0000 +++ src/acl/HttpHeaderData.cc 2015-07-31 10:28:59 +0000 @@ -76,7 +76,7 @@ char* t = ConfigParser::strtokFile(); assert (t != NULL); hdrName = t; - hdrId = httpHeaderIdByNameDef(hdrName.rawBuf(), hdrName.size()); + hdrId = httpHeaderIdByNameDef(SBuf(hdrName)); regex_rule->parse(); } === modified file 'src/base/LookupTable.h' --- src/base/LookupTable.h 2015-07-28 12:12:10 +0000 +++ src/base/LookupTable.h 2015-07-30 12:35:58 +0000 @@ -14,11 +14,27 @@ #include <map> /** - * SBuf -> enum lookup table + * a record in the initializer list for a LookupTable + * + * In case it is wished to extend the structure of a LookupTable's initializer + * list, it can be done by using a custom struct which must match + * LookupTableRecord's signature plus any extra custom fields the user may + * wish to add; the extended record type must then be passed as RecordType + * template parameter to LookupTable. + */ +template <typename EnumType> +struct LookupTableRecord +{ + const char *name; + EnumType id; +}; + +/** + * SBuf -> case-insensitive enum lookup table * * How to use: * enum enum_type { ... }; - * static const LookupTable<enum_type>::Record initializerTable[] { + * static const LookupTable<enum_type>::Record initializerTable[] = { * {"key1", ENUM_1}, {"key2", ENUM_2}, ... {nullptr, ENUM_INVALID_VALUE} * }; * LookupTable<enum_type> lookupTableInstance(ENUM_INVALID_VALUE, initializerTable); @@ -29,15 +45,18 @@ * if (item != ENUM_INVALID_VALUE) { // do stuff } * */ -template<typename EnumType> + +struct SBufCaseInsensitiveLess : public std::binary_function<SBuf, SBuf, bool> { + bool operator() (const SBuf &x, const SBuf &y) const { + return x.caseCmp(y) < 0; + } +}; +template<typename EnumType, typename RecordType = LookupTableRecord<EnumType> > class LookupTable { public: /// element of the lookup table initialization list - typedef struct { - const char *name; - EnumType id; - } Record; + typedef RecordType Record; LookupTable(const EnumType theInvalid, const Record data[]) : invalidValue(theInvalid) @@ -46,6 +65,7 @@ lookupTable[SBuf(data[i].name)] = data[i].id; } } + EnumType lookup(const SBuf &key) const { auto r = lookupTable.find(key); if (r == lookupTable.end()) @@ -54,7 +74,7 @@ } private: - typedef std::map<const SBuf, EnumType> lookupTable_t; + typedef std::map<const SBuf, EnumType, SBufCaseInsensitiveLess> lookupTable_t; lookupTable_t lookupTable; EnumType invalidValue; }; === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2015-07-29 07:11:17 +0000 +++ src/cache_cf.cc 2015-07-31 14:14:34 +0000 @@ -4608,7 +4608,7 @@ } HeaderWithAcl hwa; hwa.fieldName = fn; - hwa.fieldId = httpHeaderIdByNameDef(fn, strlen(fn)); + hwa.fieldId = httpHeaderIdByNameDef(SBuf(fn)); if (hwa.fieldId == HDR_BAD_HDR) hwa.fieldId = HDR_OTHER; === modified file 'src/external_acl.cc' --- src/external_acl.cc 2015-07-21 00:12:11 +0000 +++ src/external_acl.cc 2015-07-31 14:14:34 +0000 @@ -244,7 +244,7 @@ } format->header = xstrdup(header); - format->header_id = httpHeaderIdByNameDef(header, strlen(header)); + format->header_id = httpHeaderIdByNameDef(SBuf(header)); } void === modified file 'src/http/Makefile.am' --- src/http/Makefile.am 2015-01-13 07:25:36 +0000 +++ src/http/Makefile.am 2015-07-31 14:42:48 +0000 @@ -21,6 +21,7 @@ MethodType.h \ ProtocolVersion.h \ RegisteredHeaders.h \ + RegisteredHeaders.cc \ RequestMethod.cc \ RequestMethod.h \ StatusCode.cc \ === added file 'src/http/RegisteredHeaders.cc' --- src/http/RegisteredHeaders.cc 1970-01-01 00:00:00 +0000 +++ src/http/RegisteredHeaders.cc 2015-07-31 19:37:37 +0000 @@ -0,0 +1,111 @@ +/* + * Copyright (C) 1996-2015 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 "RegisteredHeaders.h" + +/* + * A table with major attributes for every known field. + * + * Invariant on this table: + * for each index in headerTable, (int)headerTable[index] = index + */ +const HeaderTableRecord headerTable[] = { + {"Accept", HDR_ACCEPT, ftStr}, + {"Accept-Charset", HDR_ACCEPT_CHARSET, ftStr}, + {"Accept-Encoding", HDR_ACCEPT_ENCODING, ftStr}, + {"Accept-Language", HDR_ACCEPT_LANGUAGE, ftStr}, + {"Accept-Ranges", HDR_ACCEPT_RANGES, ftStr}, + {"Age", HDR_AGE, ftInt}, + {"Allow", HDR_ALLOW, ftStr}, + {"Alternate-Protocol", HDR_ALTERNATE_PROTOCOL, ftStr}, + {"Authentication-Info", HDR_AUTHENTICATION_INFO, ftStr}, + {"Authorization", HDR_AUTHORIZATION, ftStr}, /* for now */ + {"Cache-Control", HDR_CACHE_CONTROL, ftPCc}, + {"Connection", HDR_CONNECTION, ftStr}, + {"Content-Base", HDR_CONTENT_BASE, ftStr}, + {"Content-Disposition", HDR_CONTENT_DISPOSITION, ftStr}, /* for now */ + {"Content-Encoding", HDR_CONTENT_ENCODING, ftStr}, + {"Content-Language", HDR_CONTENT_LANGUAGE, ftStr}, + {"Content-Length", HDR_CONTENT_LENGTH, ftInt64}, + {"Content-Location", HDR_CONTENT_LOCATION, ftStr}, + {"Content-MD5", HDR_CONTENT_MD5, ftStr}, /* for now */ + {"Content-Range", HDR_CONTENT_RANGE, ftPContRange}, + {"Content-Type", HDR_CONTENT_TYPE, ftStr}, + {"Cookie", HDR_COOKIE, ftStr}, + {"Cookie2", HDR_COOKIE2, ftStr}, + {"Date", HDR_DATE, ftDate_1123}, + {"ETag", HDR_ETAG, ftETag}, + {"Expect", HDR_EXPECT, ftStr}, + {"Expires", HDR_EXPIRES, ftDate_1123}, + {"Forwarded", HDR_FORWARDED, ftStr}, + {"From", HDR_FROM, ftStr}, + {"Host", HDR_HOST, ftStr}, + {"HTTP2-Settings", HDR_HTTP2_SETTINGS, ftStr}, /* for now */ + {"If-Match", HDR_IF_MATCH, ftStr}, /* for now */ + {"If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123}, + {"If-None-Match", HDR_IF_NONE_MATCH, ftStr}, /* for now */ + {"If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag}, + {"If-Unmodified-Since", HDR_IF_UNMODIFIED_SINCE, ftDate_1123}, + {"Keep-Alive", HDR_KEEP_ALIVE, ftStr}, + {"Key", HDR_KEY, ftStr}, + {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123}, + {"Link", HDR_LINK, ftStr}, + {"Location", HDR_LOCATION, ftStr}, + {"Max-Forwards", HDR_MAX_FORWARDS, ftInt64}, + {"Mime-Version", HDR_MIME_VERSION, ftStr}, /* for now */ + {"Negotiate", HDR_NEGOTIATE, ftStr}, + {"Origin", HDR_ORIGIN, ftStr}, + {"Pragma", HDR_PRAGMA, ftStr}, + {"Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftStr}, + {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr}, + {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr}, + {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr}, + {"Proxy-support", HDR_PROXY_SUPPORT, ftStr}, + {"Public", HDR_PUBLIC, ftStr}, + {"Range", HDR_RANGE, ftPRange}, + {"Referer", HDR_REFERER, ftStr}, + {"Request-Range", HDR_REQUEST_RANGE, ftPRange}, /* usually matches HDR_RANGE */ + {"Retry-After", HDR_RETRY_AFTER, ftStr}, /* for now (ftDate_1123 or ftInt!} */ + {"Server", HDR_SERVER, ftStr}, + {"Set-Cookie", HDR_SET_COOKIE, ftStr}, + {"Set-Cookie2", HDR_SET_COOKIE2, ftStr}, + {"TE", HDR_TE, ftStr}, + {"Title", HDR_TITLE, ftStr}, + {"Trailer", HDR_TRAILER, ftStr}, + {"Transfer-Encoding", HDR_TRANSFER_ENCODING, ftStr}, + {"Translate", HDR_TRANSLATE, ftStr}, /* for now. may need to crop */ + {"Unless-Modified-Since", HDR_UNLESS_MODIFIED_SINCE, ftStr}, /* for now ignore. may need to crop */ + {"Upgrade", HDR_UPGRADE, ftStr}, /* for now */ + {"User-Agent", HDR_USER_AGENT, ftStr}, + {"Vary", HDR_VARY, ftStr}, /* for now */ + {"Via", HDR_VIA, ftStr}, /* for now */ + {"Warning", HDR_WARNING, ftStr}, /* for now */ + {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr}, + {"X-Cache", HDR_X_CACHE, ftStr}, + {"X-Cache-Lookup", HDR_X_CACHE_LOOKUP, ftStr}, + {"X-Forwarded-For", HDR_X_FORWARDED_FOR, ftStr}, + {"X-Request-URI", HDR_X_REQUEST_URI, ftStr}, + {"X-Squid-Error", HDR_X_SQUID_ERROR, ftStr}, +#if X_ACCELERATOR_VARY + {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr}, +#endif +#if USE_ADAPTATION + {"X-Next-Services", HDR_X_NEXT_SERVICES, ftStr}, +#endif + {"Surrogate-Capability", HDR_SURROGATE_CAPABILITY, ftStr}, + {"Surrogate-Control", HDR_SURROGATE_CONTROL, ftPSc}, + {"Front-End-Https", HDR_FRONT_END_HTTPS, ftStr}, + {"FTP-Command", HDR_FTP_COMMAND, ftStr}, + {"FTP-Arguments", HDR_FTP_ARGUMENTS, ftStr}, + {"FTP-Pre", HDR_FTP_PRE, ftStr}, + {"FTP-Status", HDR_FTP_STATUS, ftInt}, + {"FTP-Reason", HDR_FTP_REASON, ftStr}, + {"Other:", HDR_OTHER, ftStr}, /* ':' will not allow matches */ + {nullptr, HDR_BAD_HDR} /* end of table */ +}; === modified file 'src/http/RegisteredHeaders.h' --- src/http/RegisteredHeaders.h 2015-05-15 00:38:20 +0000 +++ src/http/RegisteredHeaders.h 2015-07-31 15:05:22 +0000 @@ -12,7 +12,6 @@ /// recognized or "known" header fields; and the RFC which defines them (or not) /// http://www.iana.org/assignments/message-headers/message-headers.xhtml typedef enum { - HDR_BAD_HDR = -1, HDR_ACCEPT = 0, /**< RFC 7231 */ HDR_ACCEPT_CHARSET, /**< RFC 7231 */ HDR_ACCEPT_ENCODING, /**< RFC 7231 */ @@ -116,8 +115,35 @@ HDR_FTP_STATUS, /**< Internal header for FTP reply status */ HDR_FTP_REASON, /**< Internal header for FTP reply reason */ HDR_OTHER, /**< internal tag value for "unknown" headers */ - HDR_ENUM_END + HDR_ENUM_END, + HDR_BAD_HDR = -1 } http_hdr_type; +/** possible types for http header fields */ +//TODO: move to strongly-typed enums? (enum class) +typedef enum { + ftInvalid = HDR_ENUM_END, /**< to catch nasty errors with hdr_id<->fld_type clashes */ + ftInt, + ftInt64, + ftStr, + ftDate_1123, + ftETag, + ftPCc, + ftPContRange, + ftPRange, + ftPSc, + ftDate_1123_or_ETag +} field_type; + +/* POD for headerTable */ +class HeaderTableRecord { +public: + const char *name; + http_hdr_type id; + field_type type; +}; + +extern const HeaderTableRecord headerTable[]; + #endif /* SQUID_HTTP_REGISTEREDHEADERS_H */ # Begin bundle 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
_______________________________________________ squid-dev mailing list squid-dev@lists.squid-cache.org http://lists.squid-cache.org/listinfo/squid-dev