From: rousskov <> Date: Thu, 12 Mar 1998 05:18:42 +0000 (+0000) Subject: - Attempted to fix a problem when Squid ignores all header fields it cannot X-Git-Tag: SQUID_3_0_PRE1~3862 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=de336bbe153efb272a1024f07392b3fb7b43a120;p=thirdparty%2Fsquid.git - Attempted to fix a problem when Squid ignores all header fields it cannot parse. Now we support a two-level scheme where raw header fields are preserved regardless of our parsing/interpretation abilities. Each header field has also a "cache" where we store parsed value of the field (if any). - The only opearation on raw headers left is "joining" of appropriate headers together. - Fixed a lot of minor problems and added new header fields: Server, X-Cache, and other. --- diff --git a/include/Stack.h b/include/Stack.h index 213ae1439c..e313fe6190 100644 --- a/include/Stack.h +++ b/include/Stack.h @@ -1,5 +1,5 @@ /* - * $Id: Stack.h,v 1.4 1998/03/03 00:30:55 rousskov Exp $ + * $Id: Stack.h,v 1.5 1998/03/11 22:18:42 rousskov Exp $ * * AUTHOR: Alex Rousskov * @@ -37,7 +37,7 @@ struct _Stack { size_t capacity; /* protected, do not use these, use interface functions instead */ - size_t count; + int count; void **items; }; diff --git a/include/util.h b/include/util.h index 4a9a14d3fe..87762b0571 100644 --- a/include/util.h +++ b/include/util.h @@ -1,5 +1,5 @@ /* - * $Id: util.h,v 1.44 1998/03/07 23:42:55 rousskov Exp $ + * $Id: util.h,v 1.45 1998/03/11 22:18:42 rousskov Exp $ * * AUTHOR: Harvest Derived * @@ -196,5 +196,6 @@ extern double xpercent(double part, double whole); extern int xpercentInt(double part, double whole); extern double xdiv(double nom, double denom); +extern const char *xitoa(int num); #endif /* ndef _UTIL_H_ */ diff --git a/lib/util.c b/lib/util.c index 2a914e1574..5df5eb0368 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,6 +1,6 @@ /* - * $Id: util.c,v 1.56 1998/03/08 04:42:25 wessels Exp $ + * $Id: util.c,v 1.57 1998/03/11 22:18:43 rousskov Exp $ * * DEBUG: * AUTHOR: Harvest Derived @@ -752,10 +752,18 @@ xpercentInt(double part, double whole) return (int) rint(xpercent(part, whole)); } - /* somewhat safer division */ double xdiv(double nom, double denom) { return (denom != 0.0) ? nom / denom : -1.0; } + +/* integer to string */ +const char * +xitoa(int num) +{ + static char buf[24]; /* 2^64 = 18446744073709551616 */ + snprintf(buf, sizeof(buf), "%d", num); + return buf; +} diff --git a/src/HttpHdrCc.cc b/src/HttpHdrCc.cc index bf946125be..c1accab386 100644 --- a/src/HttpHdrCc.cc +++ b/src/HttpHdrCc.cc @@ -1,6 +1,6 @@ /* - * $Id: HttpHdrCc.cc,v 1.5 1998/03/08 21:02:07 rousskov Exp $ + * $Id: HttpHdrCc.cc,v 1.6 1998/03/11 22:18:43 rousskov Exp $ * * DEBUG: section 65 HTTP Cache Control Header * AUTHOR: Alex Rousskov @@ -31,8 +31,8 @@ #include "squid.h" -/* this table is used for parsing server cache control header */ -static field_attrs_t CcAttrs[CC_ENUM_END] = +/* this table is used for parsing cache control header */ +static const HttpHeaderFieldAttrs CcAttrs[CC_ENUM_END] = { {"public", CC_PUBLIC}, {"private", CC_PRIVATE}, @@ -43,7 +43,7 @@ static field_attrs_t CcAttrs[CC_ENUM_END] = {"proxy-revalidate", CC_PROXY_REVALIDATE}, {"max-age", CC_MAX_AGE} }; - +HttpHeaderFieldInfo *CcFieldsInfo = NULL; /* counters */ static int CcParsedCount = 0; @@ -57,7 +57,14 @@ static int httpHdrCcParseInit(HttpHdrCc * cc, const char *str); void httpHdrCcInitModule() { - httpHeaderInitAttrTable((field_attrs_t *) CcAttrs, CC_ENUM_END); + CcFieldsInfo = httpHeaderBuildFieldsInfo(CcAttrs, CC_ENUM_END); +} + +void +httpHdrCcCleanModule() +{ + httpHeaderDestroyFieldsInfo(CcFieldsInfo, CC_ENUM_END); + CcFieldsInfo = NULL; } /* implementation */ @@ -101,14 +108,14 @@ httpHdrCcParseInit(HttpHdrCc * cc, const char *str) ilen = p++ - item; /* find type */ type = httpHeaderIdByName(item, ilen, - CcAttrs, CC_ENUM_END, -1); + CcFieldsInfo, CC_ENUM_END, -1); if (type < 0) { debug(65, 2) ("hdr cc: unknown cache-directive: near '%s' in '%s'\n", item, str); continue; } if (EBIT_TEST(cc->mask, type)) { debug(65, 2) ("hdr cc: ignoring duplicate cache-directive: near '%s' in '%s'\n", item, str); - CcAttrs[type].stat.repCount++; + CcFieldsInfo[type].stat.repCount++; continue; } /* update mask */ @@ -162,7 +169,7 @@ httpHdrCcPackInto(const HttpHdrCc * cc, Packer * p) } for (flag = 0; flag < CC_ENUM_END; flag++) { if (EBIT_TEST(cc->mask, flag)) { - packerPrintf(p, pcount ? ", %s" : "%s", CcAttrs[flag].name); + packerPrintf(p, pcount ? ", %s" : "%s", CcFieldsInfo[flag].name); pcount++; } } @@ -192,7 +199,7 @@ httpHdrCcStatDumper(StoreEntry * sentry, int idx, double val, double size, int c { const int id = (int) val; const int valid_id = id >= 0 && id < CC_ENUM_END; - const char *name = valid_id ? CcAttrs[id].name : "INVALID"; + const char *name = valid_id ? strBuf(CcFieldsInfo[id].name) : "INVALID"; if (count || valid_id) storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", id, name, count, xdiv(count, CcParsedCount)); diff --git a/src/HttpHdrContRange.cc b/src/HttpHdrContRange.cc index 0d9676897c..01706534ea 100644 --- a/src/HttpHdrContRange.cc +++ b/src/HttpHdrContRange.cc @@ -1,6 +1,6 @@ /* - * $Id: HttpHdrContRange.cc,v 1.2 1998/03/08 21:40:20 rousskov Exp $ + * $Id: HttpHdrContRange.cc,v 1.3 1998/03/11 22:18:44 rousskov Exp $ * * DEBUG: section 68 HTTP Content-Range Header * AUTHOR: Alex Rousskov @@ -65,14 +65,14 @@ httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec *spec, const char *field, int fle return 0; /* is spec given ? */ if (*field == '*') - return 0; + return 1; /* check format, must be %d-%d */ if (!((p = strchr(field, '-')) && (p-field < flen))) { - debug(68, 2) ("invalid resp-range-spec near: '%s'\n", field); + debug(68, 2) ("invalid (no '-') resp-range-spec near: '%s'\n", field); return 0; } /* parse offset */ - if (!httpHeaderParseSize(field+1, &spec->offset)) + if (!httpHeaderParseSize(field, &spec->offset)) return 0; p++; /* do we have last-pos ? */ @@ -80,11 +80,12 @@ httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec *spec, const char *field, int fle size_t last_pos; if (!httpHeaderParseSize(p, &last_pos)) return 0; - spec->length = size_diff(last_pos, spec->offset); + spec->length = size_diff(last_pos+1, spec->offset); } /* we managed to parse, check if the result makes sence */ if (known_spec(spec->length) && !spec->length) { - debug(68, 2) ("invalid resp-range-spec near: '%s'\n", field); + debug(68, 2) ("invalid range (%d += %d) in resp-range-spec near: '%s'\n", + spec->offset, spec->length, field); return 0; } return 1; @@ -130,6 +131,11 @@ httpHdrContRangeParseInit(HttpHdrContRange *range, const char *str) { const char *p; assert(range && str); + debug(68, 8) ("parsing content-range field: '%s'\n", str); + /* check range type */ + if (strncasecmp(str, "bytes ", 6)) + return 0; + str += 6; /* split */ if (!(p = strchr(str, '/'))) return 0; @@ -144,6 +150,9 @@ httpHdrContRangeParseInit(HttpHdrContRange *range, const char *str) else if (!httpHeaderParseSize(p, &range->elength)) return 0; + debug(68, 8) ("parsed content-range field: %d-%d / %d\n", + range->spec.offset, range->spec.offset+range->spec.length-1, + range->elength); return 1; } diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc index c984d6803a..80b2592e50 100644 --- a/src/HttpHeader.cc +++ b/src/HttpHeader.cc @@ -1,6 +1,6 @@ /* - * $Id: HttpHeader.cc,v 1.22 1998/03/08 21:26:29 rousskov Exp $ + * $Id: HttpHeader.cc,v 1.23 1998/03/11 22:18:45 rousskov Exp $ * * DEBUG: section 55 HTTP Header * AUTHOR: Alex Rousskov @@ -58,10 +58,12 @@ */ /* - * HttpHeader entry (type of entry.field is Headers[id].type) + * HttpHeader entry (type of cached value is Headers[id].type) */ struct _HttpHeaderEntry { - field_store field; + String name; + String value; + field_store cache; short int id; }; @@ -75,8 +77,6 @@ typedef struct { } HttpHeaderStat; -/* use HttpHeaderPos as opaque type, do not interpret */ -typedef ssize_t HttpHeaderPos; /* use this and only this to initialize HttpHeaderPos */ #define HttpHeaderInitPos (-1) @@ -90,9 +90,10 @@ typedef ssize_t HttpHeaderPos; * We calculate name lengths and reorganize this array on start up. * After reorganization, field id can be used as an index to the table. */ -static field_attrs_t Headers[] = +static const HttpHeaderFieldAttrs HeadersAttrs[] = { {"Accept", HDR_ACCEPT, ftStr}, + {"Accept-Ranges", HDR_ACCEPT_RANGES, ftStr}, {"Age", HDR_AGE, ftInt}, {"Cache-Control", HDR_CACHE_CONTROL, ftPCc}, {"Connection", HDR_CONNECTION, ftStr}, /* for now */ @@ -114,12 +115,15 @@ static field_attrs_t Headers[] = {"Public", HDR_PUBLIC, ftStr}, {"Range", HDR_RANGE, ftPRange}, {"Retry-After", HDR_RETRY_AFTER, ftStr}, /* for now */ + {"Server", HDR_SERVER, ftStr}, {"Set-Cookie", HDR_SET_COOKIE, ftStr}, {"Upgrade", HDR_UPGRADE, ftStr}, /* for now */ {"Warning", HDR_WARNING, ftStr}, /* for now */ {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr}, + {"X-Cache", HDR_X_CACHE, ftStr}, {"Other:", HDR_OTHER, ftPExtField} /* ':' will not allow matches */ }; +static HttpHeaderFieldInfo *Headers = NULL; /* * headers with field values defined as #(values) in HTTP/1.1 @@ -128,18 +132,17 @@ static field_attrs_t Headers[] = * draft-ietf-http-v11-spec-rev-01.txt. Headers that are currently not * recognized, are commented out. */ -static int ListHeadersMask = 0; /* set run-time using ListHeaders */ -static http_hdr_type ListHeaders[] = +static int ListHeadersMask = 0; /* set run-time using ListHeadersArr */ +static http_hdr_type ListHeadersArr[] = { HDR_ACCEPT, /* HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE, */ - /* HDR_ACCEPT_RANGES, */ + HDR_ACCEPT_RANGES, /* HDR_ALLOW, */ HDR_CACHE_CONTROL, HDR_CONNECTION, HDR_CONTENT_ENCODING, /* HDR_CONTENT_LANGUAGE, */ /* HDR_IF_MATCH, HDR_IF_NONE_MATCH, HDR_PRAGMA, */ - HDR_RANGE, /* HDR_TRANSFER_ENCODING, */ HDR_UPGRADE, /* HDR_VARY, */ /* HDR_VIA, HDR_WARNING, */ @@ -147,19 +150,27 @@ static http_hdr_type ListHeaders[] = /* HDR_EXPECT, HDR_TE, HDR_TRAILER */ }; +/* for these headers duplicates are left; list headers do not belong here */ +static int DupHeadersMask = 0; /* set run-time */ +static http_hdr_type DupHeadersArr[] = { + HDR_SET_COOKIE, HDR_X_CACHE, HDR_OTHER +}; + static int ReplyHeadersMask = 0; /* set run-time using ReplyHeaders */ -static http_hdr_type ReplyHeaders[] = +static http_hdr_type ReplyHeadersArr[] = { - HDR_ACCEPT, HDR_AGE, HDR_CACHE_CONTROL, HDR_CONTENT_LENGTH, - HDR_CONTENT_MD5, HDR_CONTENT_TYPE, HDR_DATE, HDR_ETAG, HDR_EXPIRES, + HDR_RANGE, + + HDR_ACCEPT, HDR_ACCEPT_RANGES, HDR_AGE, HDR_CACHE_CONTROL, HDR_CONTENT_LENGTH, + HDR_CONTENT_MD5, HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_DATE, HDR_ETAG, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_LOCATION, HDR_MAX_FORWARDS, HDR_PUBLIC, HDR_RETRY_AFTER, - HDR_SET_COOKIE, HDR_UPGRADE, HDR_WARNING, HDR_PROXY_KEEPALIVE, HDR_OTHER + HDR_SERVER, HDR_SET_COOKIE, HDR_UPGRADE, HDR_WARNING, HDR_PROXY_KEEPALIVE, HDR_X_CACHE, HDR_OTHER }; static int RequestHeadersMask = 0; /* set run-time using RequestHeaders */ -static http_hdr_type RequestHeaders[] = +static http_hdr_type RequestHeadersArr[] = { - HDR_CONTENT_RANGE, HDR_OTHER + HDR_RANGE, HDR_OTHER }; /* when first field is added, this is how much entries we allocate */ @@ -187,25 +198,34 @@ static int HeaderEntryParsedCount = 0; #define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END) static HttpHeaderEntry *httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos); -static void httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos); +static void httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos, int auto_sync); +static void httpHeaderDelById(HttpHeader * hdr, http_hdr_type id); static void httpHeaderAddParsedEntry(HttpHeader * hdr, HttpHeaderEntry * e); static void httpHeaderAddNewEntry(HttpHeader * hdr, const HttpHeaderEntry * e); -static field_store httpHeaderGet(const HttpHeader * hdr, http_hdr_type id); -static void httpHeaderSet(HttpHeader * hdr, http_hdr_type id, const field_store value); +static field_store httpHeaderGetCache(const HttpHeader * hdr, http_hdr_type id); +static void httpHeaderSet(HttpHeader * hdr, HttpHeaderEntry *e); static void httpHeaderSyncMasks(HttpHeader * hdr, const HttpHeaderEntry * e, int add); static void httpHeaderGrow(HttpHeader * hdr); -static void httpHeaderEntryInit(HttpHeaderEntry * e, http_hdr_type id, field_store field); +static void httpHeaderEntryInit(HttpHeaderEntry * e, http_hdr_type id, const char *value, field_store cache); +static void httpHeaderEntryExtInit(HttpHeaderEntry * e, const char *name, const char *value); static void httpHeaderEntryClean(HttpHeaderEntry * e); static int httpHeaderEntryParseInit(HttpHeaderEntry * e, const char *field_start, const char *field_end, int mask); +static int httpHeaderEntryParse(HttpHeaderEntry * e, const char *field_start, const char *field_end); +static void httpHeaderEntrySyncCache(HttpHeaderEntry * e); +static void httpHeaderEntrySyncCacheByType(HttpHeaderEntry * e); +/* static int httpHeaderEntryParseExtFieldInit(HttpHeaderEntry * e, int id, const HttpHdrExtField * f); static int httpHeaderEntryParseByTypeInit(HttpHeaderEntry * e, int id, const HttpHdrExtField * f); +*/ static HttpHeaderEntry httpHeaderEntryClone(const HttpHeaderEntry * e); -static void httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p); +/* static void httpHeaderEntryPackByType(const HttpHeaderEntry * e, Packer * p); +*/ static void httpHeaderEntryJoinWith(HttpHeaderEntry * e, const HttpHeaderEntry * newe); -static int httpHeaderEntryIsValid(const HttpHeaderEntry * e); +/* static const char *httpHeaderEntryName(const HttpHeaderEntry * e); +*/ static void httpHeaderFieldInit(field_store * field); static field_store httpHeaderFieldDup(field_type type, field_store value); @@ -219,7 +239,7 @@ static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e); /* * some compilers do not want to convert a type into a union which that type - * belongs to + * belongs to so we have to do it manualy */ static field_store intField(int n) @@ -246,7 +266,7 @@ static field_store ptrField(void *p) { field_store f; - f.v_pefield = (HttpHdrExtField *) p; + f.v_pcc = p; return f; } @@ -261,12 +281,14 @@ httpHeaderInitModule() int i; /* paranoid check if smbd put a big object into field_store */ assert(sizeof(field_store) == sizeof(String)); - /* have to force removal of const here */ - httpHeaderInitAttrTable((field_attrs_t *) Headers, countof(Headers)); + Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); /* create masks */ - ListHeadersMask = httpHeaderCalcMask((const int *) ListHeaders, countof(ListHeaders)); - ReplyHeadersMask = httpHeaderCalcMask((const int *) ReplyHeaders, countof(ReplyHeaders)); - RequestHeadersMask = httpHeaderCalcMask((const int *) RequestHeaders, countof(RequestHeaders)); + ListHeadersMask = httpHeaderCalcMask((const int *) ListHeadersArr, countof(ListHeadersArr)); + DupHeadersMask = httpHeaderCalcMask((const int *) DupHeadersArr, countof(DupHeadersArr)); + /* dup-headers cannot be joined */ + assert(!(ListHeadersMask & DupHeadersMask)); + ReplyHeadersMask = httpHeaderCalcMask((const int *) ReplyHeadersArr, countof(ReplyHeadersArr)); + RequestHeadersMask = httpHeaderCalcMask((const int *) RequestHeadersArr, countof(RequestHeadersArr)); /* init header stats */ for (i = 0; i < HttpHeaderStatCount; i++) httpHeaderStatInit(HttpHeaderStats + i, HttpHeaderStats[i].label); @@ -278,6 +300,9 @@ httpHeaderInitModule() void httpHeaderCleanModule() { + httpHeaderDestroyFieldsInfo(Headers, HDR_ENUM_END); + Headers = NULL; + httpHdrCcCleanModule(); } static void @@ -296,16 +321,6 @@ httpHeaderStatInit(HttpHeaderStat * hs, const char *label) */ -HttpHeader * -httpHeaderCreate() -{ - HttpHeader *hdr = xmalloc(sizeof(HttpHeader)); - httpHeaderInit(hdr); - return hdr; -} - - -/* "create" for non-alloc objects; also used by real Create */ void httpHeaderInit(HttpHeader * hdr) { @@ -329,8 +344,8 @@ httpHeaderClean(HttpHeader * hdr) /* fix this (for cc too) for req headers @?@ */ statHistCount(&HttpHeaderStats[0].fieldTypeDistr, e->id); if (e->id == HDR_CACHE_CONTROL) - httpHdrCcUpdateStats(e->field.v_pcc, &HttpHeaderStats[0].ccTypeDistr); - httpHeaderDelAt(hdr, pos); + httpHdrCcUpdateStats(e->cache.v_pcc, &HttpHeaderStats[0].ccTypeDistr); + httpHeaderEntryClean(e); /* yes, this leaves us in incosistent state */ } xfree(hdr->entries); hdr->emask = 0; @@ -339,28 +354,17 @@ httpHeaderClean(HttpHeader * hdr) } void -httpHeaderDestroy(HttpHeader * hdr) -{ - httpHeaderClean(hdr); - xfree(hdr); -} - -/* create a copy of self */ -HttpHeader * -httpHeaderClone(HttpHeader * hdr) +httpHeaderCopy(HttpHeader *dest, const HttpHeader *src) { - HttpHeader *clone = httpHeaderCreate(); HttpHeaderEntry *e; HttpHeaderPos pos = HttpHeaderInitPos; + assert(dest && src); + debug(55, 7) ("copying hdr: %p <- %p\n", dest, src); - debug(55, 7) ("cloning hdr: %p -> %p\n", hdr, clone); - - while ((e = httpHeaderGetEntry(hdr, &pos))) { + while ((e = httpHeaderGetEntry(src, &pos))) { HttpHeaderEntry e_clone = httpHeaderEntryClone(e); - httpHeaderAddNewEntry(clone, &e_clone); + httpHeaderAddNewEntry(dest, &e_clone); } - - return clone; } /* just handy in parsing: resets and returns false */ @@ -372,15 +376,6 @@ httpHeaderReset(HttpHeader * hdr) return 0; } -/* - * Note: currently, in most cases, we discard a field if we cannot parse it. We - * also truncate some field values (e.g. content-type). Thus, we may not - * forward exactly what was received. However, Squid keeps a copy of "raw" - * headers anyway, so we are safe until that changes. A possible alternative - * would be to store any buggy field as HDR_OTHER, but that still leaves a - * problem with truncated fields. The later one requires a better parser and - * additional storage, I guess. - */ int httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end) { @@ -398,12 +393,12 @@ httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_e while (field_start < header_end) { const char *field_end = field_start + strcspn(field_start, "\r\n"); /*tmp_debug(here) ("found end of field: %d\n", (int)*field_end); */ - if (!*field_end) + if (!*field_end || field_end > header_end) return httpHeaderReset(hdr); /* missing */ /* - * If we fail to parse a field, we ignore that field. We also could - * claim that the whole header is invalid. The latter is safer, but less - * robust. Note that we should be able to parse any commonn format field + * If we fail to parse a field, we ignore it. We also could claim that + * the whole header is invalid. The latter is safer, but less robust. + * Note that we should be able to parse any commonn format field. */ if (!httpHeaderEntryParseInit(&e, field_start, field_end, mask)) debug(55, 2) ("warning: ignoring unparseable http header field near '%s'\n", @@ -411,8 +406,8 @@ httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_e else httpHeaderAddParsedEntry(hdr, &e); /* - * Note that we init() e, bit never clean() it which is equivalent to * - * creating a fresh entry on each loop iteration; thus, it is safe to * + * Note that we init() "e", bit never clean() it which is equivalent to + * creating a fresh entry on each loop iteration; thus, it is safe to * add e without dup()-ing it. */ field_start = field_end; @@ -422,7 +417,7 @@ httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_e if (*field_start == '\n') field_start++; } - return 1; /* even if no fields where found, they could be optional! */ + return 1; /* even if no fields where found, it is a valid header */ } /* @@ -451,9 +446,9 @@ httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos) debug(55, 8) ("searching next e in hdr %p from %d\n", hdr, *pos); for ((*pos)++; *pos < hdr->ucount; (*pos)++) { HttpHeaderEntry *e = hdr->entries + *pos; - if (httpHeaderEntryIsValid(e)) { + if (e->id >= 0) { debug(55, 8) ("%p returning entry: %s at %d\n", - hdr, httpHeaderEntryName(e), *pos); + hdr, strBuf(e->name), *pos); return e; } } @@ -464,32 +459,32 @@ httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos) /* * returns a pointer to a specified entry and updates pos; * note that we search from the very begining so it does not make much sense to - * ask for HDR_OTHER entries since there could be more than one. + * ask for headers that maybe repeated. */ -static HttpHeaderEntry * +HttpHeaderEntry * httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id, HttpHeaderPos * pos) { HttpHeaderPos p; HttpHeaderEntry *e; - int is_absent; assert(hdr); assert_eid(id); - assert(id != HDR_OTHER); + assert(!EBIT_TEST(DupHeadersMask, id)); debug(55, 8) ("finding entry %d in hdr %p\n", id, hdr); - /* check mask first @?@ @?@ remove double checking and asserts when done */ - is_absent = (id != HDR_OTHER && !EBIT_TEST(hdr->emask, id)); + /* check mask first */ + if (!EBIT_TEST(hdr->emask, id)) + return NULL; + /* looks like we must have it, do linear search */ if (!pos) pos = &p; *pos = HttpHeaderInitPos; while ((e = httpHeaderGetEntry(hdr, pos))) { - if (e->id == id) { - assert(!is_absent); + if (e->id == id) return e; - } } - assert(!EBIT_TEST(hdr->emask, id)); - return NULL; + /* hm.. we thought it was there, but it was not found */ + assert(0); + return NULL; /* not reached */ } /* @@ -500,16 +495,19 @@ int httpHeaderDelFields(HttpHeader * hdr, const char *name) { int count = 0; + int mask = 0; HttpHeaderPos pos = HttpHeaderInitPos; HttpHeaderEntry *e; debug(55, 7) ("deleting '%s' fields in hdr %p\n", name, hdr); while ((e = httpHeaderGetEntry(hdr, &pos))) { - if (!strcmp(httpHeaderEntryName(e), name)) { - httpHeaderDelAt(hdr, pos); + if (!strCmp(e->name, name)) { + httpHeaderDelAt(hdr, pos, 0); count++; - } + } else + EBIT_SET(mask, e->id); } + hdr->emask = mask; return count; } @@ -518,7 +516,7 @@ httpHeaderDelFields(HttpHeader * hdr, const char *name) * possible to iterate(search) and delete fields at the same time */ static void -httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos) +httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos, int auto_sync) { HttpHeaderEntry *e; assert(hdr); @@ -527,10 +525,11 @@ httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos) debug(55, 7) ("%p deling entry at %d: id: %d (%p:%p)\n", hdr, pos, e->id, hdr->entries, e); /* sync masks */ - httpHeaderSyncMasks(hdr, e, 0); + if (auto_sync) { + assert(!EBIT_TEST(DupHeadersMask, e->id)); + httpHeaderSyncMasks(hdr, e, 0); + } httpHeaderEntryClean(e); - if (pos == hdr->ucount) - hdr->ucount--; } /* @@ -548,22 +547,19 @@ httpHeaderAddParsedEntry(HttpHeader * hdr, HttpHeaderEntry * e) debug(55, 7) ("%p adding parsed entry %d\n", hdr, e->id); - /* there is no good reason to add invalid entries */ - if (!httpHeaderEntryIsValid(e)) - return; - olde = (e->id == HDR_OTHER) ? NULL : httpHeaderFindEntry(hdr, e->id, NULL); + if (EBIT_TEST(hdr->emask, e->id)) + Headers[e->id].stat.repCount++; + olde = EBIT_TEST(DupHeadersMask, e->id) ? NULL : httpHeaderFindEntry(hdr, e->id, NULL); if (olde) { if (EBIT_TEST(ListHeadersMask, e->id)) httpHeaderEntryJoinWith(olde, e); - else { - debug(55, 2) ("ignoring duplicate header: %s\n", httpHeaderEntryName(e)); - Headers[e->id].stat.repCount++; - } + else + debug(55, 3) ("ignoring duplicate header: %s\n", strBuf(e->name)); httpHeaderEntryClean(e); } else { /* actual add */ httpHeaderAddNewEntry(hdr, e); - debug(55, 6) ("%p done adding parsed entry %d (%s)\n", hdr, e->id, httpHeaderEntryName(e)); + debug(55, 6) ("%p done adding parsed entry %d (%s)\n", hdr, e->id, strBuf(e->name)); } } @@ -572,7 +568,7 @@ httpHeaderAddParsedEntry(HttpHeader * hdr, HttpHeaderEntry * e) * copy e value, thus, e can point to a tmp variable (but e->field is not dupped!) */ static void -httpHeaderAddNewEntry(HttpHeader * hdr, const HttpHeaderEntry * e) +httpHeaderAddNewEntry(HttpHeader * hdr, const HttpHeaderEntry *e) { assert(hdr && e); debug(55, 8) ("%p adding entry: %d at %d, (%p:%p)\n", @@ -582,14 +578,13 @@ httpHeaderAddNewEntry(HttpHeader * hdr, const HttpHeaderEntry * e) httpHeaderGrow(hdr); hdr->entries[hdr->ucount++] = *e; /* sync masks */ - httpHeaderSyncMasks(hdr, e, 1); + if (EBIT_TEST(DupHeadersMask, e->id)) + EBIT_SET(DupHeadersMask, e->id); + else + httpHeaderSyncMasks(hdr, e, 1); } -/* - * Global (user level) routines - */ - /* test if a field is present */ int httpHeaderHas(const HttpHeader * hdr, http_hdr_type id) @@ -601,70 +596,64 @@ httpHeaderHas(const HttpHeader * hdr, http_hdr_type id) return EBIT_TEST(hdr->emask, id); } -/* delete a field if any */ -void -httpHeaderDel(HttpHeader * hdr, http_hdr_type id) +/* delete a field if any; see httpHeaderFindEntry for restrictions */ +static void +httpHeaderDelById(HttpHeader * hdr, http_hdr_type id) { HttpHeaderPos pos = HttpHeaderInitPos; - assert(id != HDR_OTHER); debug(55, 8) ("%p del-by-id %d\n", hdr, id); - if (httpHeaderFindEntry(hdr, id, &pos)) { - httpHeaderDelAt(hdr, pos); - } + if (httpHeaderFindEntry(hdr, id, &pos)) + httpHeaderDelAt(hdr, pos, 1); } /* * set a field - * setting an invaid value is equivalent to deleting a field - * (if field is not present, it is added; otherwise, old content is destroyed). + * old content, if any, is destroyed. */ static void -httpHeaderSet(HttpHeader * hdr, http_hdr_type id, const field_store value) +httpHeaderSet(HttpHeader * hdr, HttpHeaderEntry *e) { - HttpHeaderPos pos; - HttpHeaderEntry e; assert(hdr); - assert_eid(id); - - debug(55, 7) ("%p sets entry with id: %d\n", hdr, id); - if (httpHeaderFindEntry(hdr, id, &pos)) /* delete old entry */ - httpHeaderDelAt(hdr, pos); + assert_eid(e->id); - httpHeaderEntryInit(&e, id, httpHeaderFieldDup(Headers[id].type, value)); - if (httpHeaderEntryIsValid(&e)) - httpHeaderAddNewEntry(hdr, &e); - else - httpHeaderEntryClean(&e); + debug(55, 7) ("%p sets entry with id: %d\n", hdr, e->id); + httpHeaderDelById(hdr, e->id); /* delete old entry if any */ + httpHeaderAddNewEntry(hdr, e); } void httpHeaderSetInt(HttpHeader * hdr, http_hdr_type id, int number) { - field_store value; + HttpHeaderEntry e; assert_eid(id); assert(Headers[id].type == ftInt); /* must be of an appropriatre type */ - value.v_int = number; - httpHeaderSet(hdr, id, value); + assert(number >= 0); + httpHeaderEntryInit(&e, id, xitoa(number), intField(number)); + httpHeaderSet(hdr, &e); } void httpHeaderSetTime(HttpHeader * hdr, http_hdr_type id, time_t time) { - field_store value; + HttpHeaderEntry e; assert_eid(id); assert(Headers[id].type == ftDate_1123); /* must be of an appropriatre type */ - value.v_time = time; - httpHeaderSet(hdr, id, value); + if (time >= 0) { + httpHeaderEntryInit(&e, id, mkrfc1123(time), timeField(time)); + httpHeaderSet(hdr, &e); + } else + httpHeaderDelById(hdr, id); } void httpHeaderSetStr(HttpHeader * hdr, http_hdr_type id, const char *str) { - field_store value; + HttpHeaderEntry e; assert_eid(id); assert(Headers[id].type == ftStr); /* must be of a string type */ - stringInit(&value.v_str, str); - httpHeaderSet(hdr, id, value); + assert(str); + httpHeaderEntryInit(&e, id, str, strField(StringNull)); + httpHeaderSet(hdr, &e); } void @@ -682,25 +671,22 @@ httpHeaderSetAuth(HttpHeader * hdr, const char *authScheme, const char *realm) void httpHeaderAddExt(HttpHeader * hdr, const char *name, const char *value) { - HttpHdrExtField *ext = httpHdrExtFieldCreate(name, value); HttpHeaderEntry e; - - debug(55, 8) ("%p adds ext entry '%s:%s'\n", hdr, name, value); - httpHeaderEntryInit(&e, HDR_OTHER, ptrField(ext)); + assert(name && value); + debug(55, 8) ("%p adds ext entry '%s: %s'\n", hdr, name, value); + httpHeaderEntryExtInit(&e, name, value); httpHeaderAddNewEntry(hdr, &e); } -/* get a value of a field (not lvalue though) */ +/* get a ["right"] cached value of a field, see httpHeaderFindEntry for restrictions */ static field_store -httpHeaderGet(const HttpHeader * hdr, http_hdr_type id) +httpHeaderGetCache(const HttpHeader * hdr, http_hdr_type id) { HttpHeaderEntry *e; assert_eid(id); - assert(id != HDR_OTHER); /* there is no single value for HDR_OTHER */ - debug(55, 7) ("%p get for id %d\n", hdr, id); if ((e = httpHeaderFindEntry(hdr, id, NULL))) - return e->field; + return e->cache; else return httpHeaderFieldBadValue(Headers[id].type); } @@ -709,56 +695,55 @@ int httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id) { assert_eid(id); - assert(Headers[id].type == ftInt); /* must be of an apropriate type */ - return httpHeaderGet(hdr, id).v_int; + assert(Headers[id].type == ftInt); /* must be of an appropriate type */ + return httpHeaderGetCache(hdr, id).v_int; } -const char * -httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id) +time_t +httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id) { assert_eid(id); - assert(Headers[id].type == ftStr); /* must be of an apropriate type */ - return strBuf(httpHeaderGet(hdr, id).v_str); + assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */ + return httpHeaderGetCache(hdr, id).v_time; } -time_t -httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id) +const char * +httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id) { + HttpHeaderEntry *e; assert_eid(id); - assert(Headers[id].type == ftDate_1123); /* must be of an apropriate type */ - return httpHeaderGet(hdr, id).v_time; + assert(Headers[id].type == ftStr); /* must be of an appropriate type */ + if ((e = httpHeaderFindEntry(hdr, id, NULL))) + if (strBuf(e->cache.v_str)) + return strBuf(e->cache.v_str); + else /* use real value if no cached one */ + return strBuf(e->value); + return NULL; } HttpHdrCc * httpHeaderGetCc(const HttpHeader * hdr) { - return httpHeaderGet(hdr, HDR_CACHE_CONTROL).v_pcc; + return httpHeaderGetCache(hdr, HDR_CACHE_CONTROL).v_pcc; } HttpHdrRange * httpHeaderGetRange(const HttpHeader * hdr) { - return httpHeaderGet(hdr, HDR_RANGE).v_prange; + return httpHeaderGetCache(hdr, HDR_RANGE).v_prange; } HttpHdrContRange * httpHeaderGetContRange(const HttpHeader * hdr) { - return httpHeaderGet(hdr, HDR_CONTENT_RANGE).v_pcont_range; + return httpHeaderGetCache(hdr, HDR_CONTENT_RANGE).v_pcont_range; } /* updates header masks */ static void httpHeaderSyncMasks(HttpHeader * hdr, const HttpHeaderEntry * e, int add) { - int isSet; - assert(hdr && e); - assert_eid(e->id); - - /* we cannot mask HDR_OTHER because it may not be unique */ - if (e->id == HDR_OTHER) - return; - isSet = EBIT_TEST(hdr->emask, e->id) != 0; + const int isSet = EBIT_TEST(hdr->emask, e->id) != 0; add = add != 0; assert(isSet ^ add); add ? EBIT_SET(hdr->emask, e->id) : EBIT_CLR(hdr->emask, e->id); @@ -788,15 +773,32 @@ httpHeaderGrow(HttpHeader * hdr) */ static void -httpHeaderEntryInit(HttpHeaderEntry * e, http_hdr_type id, field_store field) +httpHeaderEntryDoInit(HttpHeaderEntry * e, http_hdr_type id, const char *name, const char *value, field_store cache) { assert(e); assert_eid(id); e->id = id; - e->field = field; + if (id != HDR_OTHER) + e->name = Headers[id].name; + else + stringInit(&e->name, name); + stringInit(&e->value, value); + e->cache = cache; Headers[id].stat.aliveCount++; } +static void +httpHeaderEntryInit(HttpHeaderEntry * e, http_hdr_type id, const char *value, field_store cache) +{ + httpHeaderEntryDoInit(e, id, NULL, value, cache); +} + +static void +httpHeaderEntryExtInit(HttpHeaderEntry * e, const char *name, const char *value) +{ + httpHeaderEntryDoInit(e, HDR_OTHER, name, value, strField(StringNull)); +} + static void httpHeaderEntryClean(HttpHeaderEntry * e) { @@ -807,172 +809,172 @@ httpHeaderEntryClean(HttpHeaderEntry * e) case ftInvalid: case ftInt: case ftDate_1123: + case ftPExtField: /* no special cleaning is necessary */ break; case ftStr: - stringClean(&e->field.v_str); + stringClean(&e->cache.v_str); break; case ftPCc: - if (e->field.v_pcc) - httpHdrCcDestroy(e->field.v_pcc); + if (e->cache.v_pcc) + httpHdrCcDestroy(e->cache.v_pcc); break; case ftPRange: - if (e->field.v_prange) - httpHdrRangeDestroy(e->field.v_prange); + if (e->cache.v_prange) + httpHdrRangeDestroy(e->cache.v_prange); break; case ftPContRange: - if (e->field.v_pcont_range) - httpHdrContRangeDestroy(e->field.v_pcont_range); - break; - case ftPExtField: - if (e->field.v_pefield) - httpHdrExtFieldDestroy(e->field.v_pefield); + if (e->cache.v_pcont_range) + httpHdrContRangeDestroy(e->cache.v_pcont_range); break; default: assert(0); /* somebody added a new type? */ } + /* clean name if needed */ + if (e->id == HDR_OTHER) + stringClean(&e->name); + stringClean(&e->value); Headers[e->id].stat.aliveCount--; /* we have to do that so entry will be _invlaid_ */ e->id = -1; - memset(&e->field, 0, sizeof(e->field)); + httpHeaderFieldInit(&e->cache); } /* parses and inits header entry, returns true on success */ static int httpHeaderEntryParseInit(HttpHeaderEntry * e, const char *field_start, const char *field_end, int mask) { - HttpHdrExtField *f; - int id; - int result; - HeaderEntryParsedCount++; /* paranoid reset */ + memset(e, 0, sizeof(*e)); e->id = -1; - memset(&e->field, 0, sizeof(e->field)); - /* first assume it is just an extension field */ - f = httpHdrExtFieldParseCreate(field_start, field_end); - if (!f) /* total parsing failure */ - return 0; - id = httpHeaderIdByName(strBuf(f->name), -1, Headers, countof(Headers), mask); - if (id < 0) - id = HDR_OTHER; - Headers[id].stat.parsCount++; - if (id == HDR_OTHER) { - /* hm.. it is an extension field indeed */ - httpHeaderEntryInit(e, id, ptrField(f)); - return 1; + if (!httpHeaderEntryParse(e, field_start, field_end)) + return 0; /* total parsing failure */ + e->id = httpHeaderIdByName(strBuf(e->name), -1, Headers, HDR_ENUM_END, mask); + debug(55, 8) ("EntryParseInit: '%s'.id = %d\n", strBuf(e->name), e->id); + if (e->id < 0) + e->id = HDR_OTHER; + Headers[e->id].stat.parsCount++; + Headers[e->id].stat.aliveCount++; + if (e->id != HDR_OTHER) { + /* we got something interesting, parse and cache the value */ + httpHeaderEntrySyncCache(e); } - /* ok, we got something interesting, parse it further */ - result = httpHeaderEntryParseExtFieldInit(e, id, f); - /* do not need it anymore */ - httpHdrExtFieldDestroy(f); - return result; + return 1; } static int -httpHeaderEntryParseExtFieldInit(HttpHeaderEntry * e, int id, const HttpHdrExtField * f) +httpHeaderEntryParse(HttpHeaderEntry * e, const char *field_start, const char *field_end) { - assert(e && f); - assert_eid(id); - e->id = -1; + /* note: name_start == field_start */ + const char *name_end = strchr(field_start, ':'); + const char *value_start; + /* note: value_end == field_end */ + + if (!name_end || name_end <= field_start || name_end > field_end) + return 0; + + value_start = name_end + 1; /* skip ':' */ + /* skip white space */ + while (value_start < field_end && isspace(*value_start)) + value_start++; + + stringLimitInit(&e->name, field_start, name_end - field_start); + stringLimitInit(&e->value, value_start, field_end - value_start); + return 1; +} + +/* tries to parse field value further and cache the result */ +static void +httpHeaderEntrySyncCache(HttpHeaderEntry * e) +{ + assert(e); + assert_eid(e->id); + debug(55, 9) ("httpHeaderEntrySyncCache: start with %s: %s\n", + strBuf(e->name), strBuf(e->value)); + httpHeaderFieldInit(&e->cache); /* * check for exceptions first (parsing is not determined by value type) * then parse using value type if needed */ - switch (id) { + switch (e->id) { case HDR_PROXY_KEEPALIVE: /* we treat Proxy-Connection as "keep alive" only if it says so */ - httpHeaderEntryInit(e, id, intField(!strcasecmp(strBuf(f->value), "Keep-Alive"))); + e->cache = intField(!strcasecmp(strBuf(e->value), "Keep-Alive")); + break; + case HDR_CONTENT_TYPE: + /* strip content type params */ + stringLimitInit(&e->cache.v_str, strBuf(e->value), + strcspn(strBuf(e->value), ";\t ")); break; default: /* if we got here, it is something that can be parsed based on value type */ - if (!httpHeaderEntryParseByTypeInit(e, id, f)) - return 0; + httpHeaderEntrySyncCacheByType(e); } - /* parsing was successful, post-processing maybe required */ - switch (id) { - case HDR_CONTENT_TYPE: { - /* cut off "; parameter" from Content-Type @?@ why? */ - const int l = strcspn(strBuf(e->field.v_str), ";\t "); - if (l > 0) - strCut(e->field.v_str, l); - break; - } + /* post-processing */ + switch (e->id) { case HDR_EXPIRES: /* * The HTTP/1.0 specs says that robust implementations should * consider bad or malformed Expires header as equivalent to * "expires immediately." */ - if (!httpHeaderEntryIsValid(e)) - e->field.v_time = squid_curtime; + if (e->cache.v_time <= 0) + e->cache.v_time = squid_curtime; /* - * real expiration value also depends on max-age too, but it is not - * of our business (HttpReply should handle it) + * real expiration value also depends on max-age too, + * HttpReply should handle that */ break; } - return 1; } -static int -httpHeaderEntryParseByTypeInit(HttpHeaderEntry * e, int id, const HttpHdrExtField * f) +static void +httpHeaderEntrySyncCacheByType(HttpHeaderEntry * e) { const char *err_entry_descr = NULL; - int type; - field_store field; - assert(e && f); - assert_eid(id); - type = Headers[id].type; + const field_type type = Headers[e->id].type; - httpHeaderFieldInit(&field); + debug(55, 8) ("httpHeaderEntrySyncCacheByType: id: %d type: %d\n", e->id, type); switch (type) { case ftInt: - if (!httpHeaderParseInt(strBuf(f->value), &field.v_int)) + if (!httpHeaderParseInt(strBuf(e->value), &e->cache.v_int)) err_entry_descr = "integer field"; break; case ftStr: - field.v_str = stringDup(&f->value); + /* we do not cache string values to avoid duplicating e->value */ break; case ftDate_1123: - field.v_time = parse_rfc1123(strBuf(f->value)); - if (field.v_time <= 0) - Headers[id].stat.errCount++; - /* - * if parse_rfc1123 fails we fall through anyway so upper levels - * will notice invalid date rather than unparsible header - */ + e->cache.v_time = parse_rfc1123(strBuf(e->value)); + if (e->cache.v_time <= 0) + err_entry_descr = "date field"; break; case ftPCc: - field.v_pcc = httpHdrCcParseCreate(strBuf(f->value)); - if (!field.v_pcc) + e->cache.v_pcc = httpHdrCcParseCreate(strBuf(e->value)); + if (!e->cache.v_pcc) err_entry_descr = "cache control hdr"; break; case ftPRange: - field.v_prange = httpHdrRangeParseCreate(strBuf(f->value)); - if (!field.v_prange) + e->cache.v_prange = httpHdrRangeParseCreate(strBuf(e->value)); + if (!e->cache.v_prange) err_entry_descr = "range hdr"; break; case ftPContRange: - field.v_pcont_range = httpHdrContRangeParseCreate(strBuf(f->value)); - if (!field.v_pcont_range) + e->cache.v_pcont_range = httpHdrContRangeParseCreate(strBuf(e->value)); + if (!e->cache.v_pcont_range) err_entry_descr = "content range hdr"; break; default: debug(55, 2) ("something went wrong with hdr field type analysis: id: %d, type: %d, field: '%s: %s'\n", - id, type, strBuf(f->name), strBuf(f->value)); + e->id, type, strBuf(e->name), strBuf(e->value)); assert(0); } - /* failure ? */ + /* notify of failure if any */ if (err_entry_descr) { debug(55, 2) ("failed to parse %s: id: %d, field: '%s: %s'\n", - err_entry_descr, id, strBuf(f->name), strBuf(f->value)); - Headers[id].stat.errCount++; - return 0; + err_entry_descr, e->id, strBuf(e->name), strBuf(e->value)); + Headers[e->id].stat.errCount++; } - /* success, do actual init */ - httpHeaderEntryInit(e, id, field); - return 1; } static HttpHeaderEntry @@ -981,67 +983,22 @@ httpHeaderEntryClone(const HttpHeaderEntry * e) HttpHeaderEntry clone; assert(e); assert_eid(e->id); - httpHeaderEntryInit(&clone, e->id, - httpHeaderFieldDup(Headers[e->id].type, e->field)); + if (e->id == HDR_OTHER) + httpHeaderEntryExtInit(&clone, strBuf(e->name), strBuf(e->value)); + else + httpHeaderEntryInit(&clone, e->id, strBuf(e->value), + httpHeaderFieldDup(Headers[e->id].type, e->cache)); return clone; } -static void +void httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p) { assert(e && p); - - /* pack the field_name: */ - packerPrintf(p, "%s: ", httpHeaderEntryName(e)); - /* - * pack the value - * check for exceptions (packing is not determined by value type) - * then swap using value type - */ - switch (e->id) { - case HDR_PROXY_KEEPALIVE: - packerPrintf(p, "%s", "Keep-Alive"); - break; - default: - /* if we got here, it is something that can be swap based on value type */ - httpHeaderEntryPackByType(e, p); - } - /* add CRLF */ - packerPrintf(p, "%s", "\r\n"); -} - -static void -httpHeaderEntryPackByType(const HttpHeaderEntry * e, Packer * p) -{ - field_type type; - assert(e && p); - assert_eid(e->id); - type = Headers[e->id].type; - switch (type) { - case ftInt: - packerPrintf(p, "%d", e->field.v_int); - break; - case ftStr: - packerPrintf(p, "%s", strBuf(e->field.v_str)); - break; - case ftDate_1123: - packerPrintf(p, "%s", mkrfc1123(e->field.v_time)); - break; - case ftPCc: - httpHdrCcPackInto(e->field.v_pcc, p); - break; - case ftPRange: - httpHdrRangePackInto(e->field.v_prange, p); - break; - case ftPContRange: - httpHdrContRangePackInto(e->field.v_pcont_range, p); - break; - case ftPExtField: - packerPrintf(p, "%s", strBuf(e->field.v_pefield->value)); - break; - default: - assert(0 && type); /* pack for invalid/unknown type */ - } + packerAppend(p, strBuf(e->name), strLen(e->name)); + packerAppend(p, ": ", 2); + packerAppend(p, strBuf(e->value), strLen(e->value)); + packerAppend(p, "\r\n", 2); } static void @@ -1053,18 +1010,17 @@ httpHeaderEntryJoinWith(HttpHeaderEntry * e, const HttpHeaderEntry * newe) assert(e->id == newe->id); debug(55, 6) ("joining entry (%p) with (%p)\n", e, newe); + /* append value */ + stringAppend(&e->value, ",", 1); + stringAppend(&e->value, strBuf(newe->value), strLen(newe->value)); /* type-based join */ type = Headers[e->id].type; switch (type) { case ftStr: - stringAppend(&e->field.v_str, ",", 1); - stringAppend(&e->field.v_str, strBuf(newe->field.v_str), strLen(newe->field.v_str)); + assert(!strBuf(e->cache.v_str)); /* currently others should not be join-able */ break; case ftPCc: - httpHdrCcJoinWith(e->field.v_pcc, newe->field.v_pcc); - break; - case ftPRange: - httpHdrRangeJoinWith(e->field.v_prange, newe->field.v_prange); + httpHdrCcJoinWith(e->cache.v_pcc, newe->cache.v_pcc); break; default: debug(55, 0) ("join for invalid/unknown type: id: %d, type: %d\n", e->id, type); @@ -1073,46 +1029,32 @@ httpHeaderEntryJoinWith(HttpHeaderEntry * e, const HttpHeaderEntry * newe) } +#if OLD_CODE static int -httpHeaderEntryIsValid(const HttpHeaderEntry * e) +httpHeaderFieldIsValid(field_type type, const HttpHeaderEntry * e) { - assert(e); - if (e->id == -1) - return 0; - assert_eid(e->id); /* type-based analysis */ - switch (Headers[e->id].type) { + switch (type) { case ftInvalid: return 0; case ftInt: - return e->field.v_int >= 0; + return e->cache.v_int >= 0; case ftStr: - return strBuf(e->field.v_str) != NULL; + return strBuf(e->cache.v_str) != NULL; case ftDate_1123: - return e->field.v_time >= 0; + return e->cache.v_time >= 0; case ftPCc: - return e->field.v_pcc != NULL; + return e->cache.v_pcc != NULL; case ftPRange: - return e->field.v_prange != NULL; + return e->cache.v_prange != NULL; case ftPContRange: - return e->field.v_pcont_range != NULL; - case ftPExtField: - return e->field.v_pefield != NULL; + return e->cache.v_pcont_range != NULL; default: assert(0); /* query for invalid/unknown type */ } return 0; /* not reached */ } - -static const char * -httpHeaderEntryName(const HttpHeaderEntry * e) -{ - assert(e); - assert_eid(e->id); - - return (e->id == HDR_OTHER) ? - strBuf(e->field.v_pefield->name) : Headers[e->id].name; -} +#endif /* * HttpHeaderField @@ -1141,8 +1083,6 @@ httpHeaderFieldDup(field_type type, field_store value) return ptrField(httpHdrRangeDup(value.v_prange)); case ftPContRange: return ptrField(httpHdrContRangeDup(value.v_pcont_range)); - case ftPExtField: - return ptrField(httpHdrExtFieldDup(value.v_pefield)); default: assert(0); /* dup of invalid/unknown type */ } @@ -1166,7 +1106,6 @@ httpHeaderFieldBadValue(field_type type) case ftPCc: case ftPRange: case ftPContRange: - case ftPExtField: return ptrField(NULL); case ftInvalid: default: @@ -1184,7 +1123,7 @@ httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, { const int id = (int) val; const int valid_id = id >= 0 && id < HDR_ENUM_END; - const char *name = valid_id ? Headers[id].name : "INVALID"; + const char *name = valid_id ? strBuf(Headers[id].name) : "INVALID"; if (count || valid_id) storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", id, name, count, xdiv(count, NonEmptyHeaderDestroyedCount)); @@ -1239,9 +1178,9 @@ httpHeaderStoreReport(StoreEntry * e) storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n", "id", "name", "#alive", "%err", "%repeat"); for (ht = 0; ht < HDR_ENUM_END; ht++) { - field_attrs_t *f = Headers + ht; + HttpHeaderFieldInfo *f = Headers + ht; storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n", - f->id, f->name, f->stat.aliveCount, + f->id, strBuf(f->name), f->stat.aliveCount, xpercent(f->stat.errCount, f->stat.parsCount), xpercent(f->stat.repCount, f->stat.parsCount)); } diff --git a/src/HttpHeaderTools.cc b/src/HttpHeaderTools.cc index 1747dd8c37..f25d05836b 100644 --- a/src/HttpHeaderTools.cc +++ b/src/HttpHeaderTools.cc @@ -1,5 +1,5 @@ /* - * $Id: HttpHeaderTools.cc,v 1.2 1998/03/05 20:55:56 rousskov Exp $ + * $Id: HttpHeaderTools.cc,v 1.3 1998/03/11 22:18:45 rousskov Exp $ * * DEBUG: section 66 HTTP Header Tools * AUTHOR: Alex Rousskov @@ -30,36 +30,44 @@ #include "squid.h" -void -httpHeaderInitAttrTable(field_attrs_t * table, int count) +static int httpHeaderStrCmp(const char *h1, const char *h2, int len); + + +HttpHeaderFieldInfo * +httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs *attrs, int count) { int i; - assert(table); - assert(count > 1); /* to protect from buggy "countof" implementations */ - - /* reorder so that .id becomes an index */ - for (i = 0; i < count;) { - const int id = table[i].id; - assert(id >= 0 && id < count); /* sanity check */ - assert(id >= i); /* entries prior to i have been indexed already */ - if (id != i) { /* out of order */ - const field_attrs_t fa = table[id]; - assert(fa.id != id); /* avoid endless loops */ - table[id] = table[i]; /* swap */ - table[i] = fa; - } else - i++; /* make progress */ - } + HttpHeaderFieldInfo *table = NULL; + assert(attrs && count); + + /* allocate space */ + table = xcalloc(count, sizeof(HttpHeaderFieldInfo)); - /* calculate name lengths and init stats */ for (i = 0; i < count; ++i) { - assert(table[i].name); - table[i].name_len = strlen(table[i].name); - debug(66, 5) ("hdr table entry[%d]: %s (%d)\n", i, table[i].name, table[i].name_len); - assert(table[i].name_len); + const int id = attrs[i].id; + HttpHeaderFieldInfo *info = table + id; + /* sanity checks */ + assert(id >= 0 && id < count); + assert(attrs[i].name); + assert(info->id == 0 && info->type == 0); /* was not set before */ + /* copy and init fields */ + info->id = id; + info->type = attrs[i].type; + stringInit(&info->name, attrs[i].name); + assert(strLen(info->name)); /* init stats */ - memset(&table[i].stat, 0, sizeof(table[i].stat)); + memset(&info->stat, 0, sizeof(info->stat)); } + return table; +} + +void +httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo *table, int count) +{ + int i; + for (i = 0; i < count; ++i) + stringClean(&table[i].name); + xfree(table); } /* calculates a bit mask of a given array */ @@ -81,15 +89,15 @@ httpHeaderCalcMask(const int *enums, int count) int -httpHeaderIdByName(const char *name, int name_len, const field_attrs_t * attrs, int end, int mask) +httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo *info, int end, int mask) { int i; for (i = 0; i < end; ++i) { if (mask < 0 || EBIT_TEST(mask, i)) { - if (name_len >= 0 && name_len != attrs[i].name_len) + if (name_len >= 0 && name_len != strLen(info[i].name)) continue; - if (!strncasecmp(name, attrs[i].name, - name_len < 0 ? attrs[i].name_len + 1 : name_len)) + if (!strncasecmp(name, strBuf(info[i].name), + name_len < 0 ? strLen(info[i].name) + 1 : name_len)) return i; } } @@ -168,3 +176,86 @@ httpHeaderParseSize(const char *start, size_t *value) *value = res ? v : 0; return res; } + + +/* + * parses a given string then packs compiled headers and compares the result + * with the original, reports discrepancies + */ +void httpHeaderTestParser(const char *hstr) +{ + static int bug_count = 0; + int hstr_len; + int parse_success; + HttpHeader hdr; + int pos; + Packer p; + MemBuf mb; + assert(hstr); + /* do not print too much, kludge */ + if (bug_count > 100 && (lrand48() % bug_count) > 50L) + return; + /* skip start line if any */ + if (!strncasecmp(hstr, "HTTP/", 5)) { + const char *p = strchr(hstr, '\n'); + if (p) + hstr = p+1; + } + /* skip invalid first line if any */ + if (isspace(*hstr)) { + const char *p = strchr(hstr, '\n'); + if (p) + hstr = p+1; + } + hstr_len = strlen(hstr); + /* skip terminator if any */ + if (strstr(hstr, "\n\r\n")) + hstr_len -= 2; + else + if (strstr(hstr, "\n\n")) + hstr_len -= 1; + httpHeaderInit(&hdr); + /* debugLevels[55] = 8; */ + parse_success = httpHeaderParse(&hdr, hstr, hstr+hstr_len); + /* debugLevels[55] = 2; */ + if (!parse_success) { + debug(66, 2) ("TEST: failed to parsed a header: {\n%s}\n", hstr); + return; + } + /* we think that we parsed it, veryfy */ + memBufDefInit(&mb); + packerToMemInit(&p, &mb); + httpHeaderPackInto(&hdr, &p); + if ((pos = abs(httpHeaderStrCmp(hstr, mb.buf, hstr_len)))) { + bug_count++; + debug(66, 2) ("TEST: hdr parsing bug (pos: %d near '%s'): expected: {\n%s} got: {\n%s}\n", + pos, hstr+pos, hstr, mb.buf); + } + httpHeaderClean(&hdr); + packerClean(&p); + memBufClean(&mb); +} + + +/* like strncasecmp but ignores ws characters */ +static int +httpHeaderStrCmp(const char *h1, const char *h2, int len) +{ + int len1 = 0; + int len2 = 0; + assert(h1 && h2); + /* fast check first */ + if (!strncasecmp(h1, h2, len)) + return 0; + while (1) { + const char c1 = toupper(h1[len1 += xcountws(h1 + len1)]); + const char c2 = toupper(h2[len2 += xcountws(h2 + len2)]); + if (c1 < c2) return -len1; + if (c1 > c2) return +len1; + if (!c1 && !c2) + return 0; + if (c1) len1++; + if (c2) len2++; + } + return 0; +} diff --git a/src/HttpReply.cc b/src/HttpReply.cc index 8aa23ce586..585ad09749 100644 --- a/src/HttpReply.cc +++ b/src/HttpReply.cc @@ -1,6 +1,6 @@ /* - * $Id: HttpReply.cc,v 1.11 1998/03/05 00:42:43 wessels Exp $ + * $Id: HttpReply.cc,v 1.12 1998/03/11 22:18:46 rousskov Exp $ * * DEBUG: section 58 HTTP Reply (Response) * AUTHOR: Alex Rousskov @@ -173,33 +173,21 @@ httpPackedReply(double ver, http_status status, const char *ctype, MemBuf httpPacked304Reply(const HttpReply * rep) { + static const http_hdr_type ImsEntries[] = { HDR_DATE, HDR_CONTENT_LENGTH, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER }; + http_hdr_type t; MemBuf mb; + Packer p; + HttpHeaderEntry *e; assert(rep); memBufDefInit(&mb); + packerToMemInit(&p, &mb); memBufPrintf(&mb, "%s", "HTTP/1.0 304 Not Modified\r\n"); - - if (httpHeaderHas(&rep->hdr, HDR_DATE)) - memBufPrintf(&mb, "Date: %s\r\n", mkrfc1123( - httpHeaderGetTime(&rep->hdr, HDR_DATE))); - - if (httpHeaderHas(&rep->hdr, HDR_CONTENT_TYPE)) - memBufPrintf(&mb, "Content-type: %s\r\n", - httpHeaderGetStr(&rep->hdr, HDR_CONTENT_TYPE)); - - if (httpHeaderHas(&rep->hdr, HDR_CONTENT_LENGTH)) - memBufPrintf(&mb, "Content-Length: %d\r\n", - httpReplyContentLen(rep)); - - if (httpHeaderHas(&rep->hdr, HDR_EXPIRES)) - memBufPrintf(&mb, "Expires: %s\r\n", mkrfc1123( - httpHeaderGetTime(&rep->hdr, HDR_EXPIRES))); - - if (httpHeaderHas(&rep->hdr, HDR_LAST_MODIFIED)) - memBufPrintf(&mb, "Last-modified: %s\r\n", mkrfc1123( - httpHeaderGetTime(&rep->hdr, HDR_LAST_MODIFIED))); - + for (t = 0; ImsEntries[t] != HDR_OTHER; ++t) + if ((e = httpHeaderFindEntry(&rep->hdr, ImsEntries[t], NULL))) + httpHeaderEntryPackInto(e, &p); memBufAppend(&mb, "\r\n", 2); + packerClean(&p); return mb; } @@ -216,7 +204,7 @@ httpReplySetHeaders(HttpReply * reply, double ver, http_status status, const cha httpHeaderSetTime(hdr, HDR_DATE, squid_curtime); if (ctype) httpHeaderSetStr(hdr, HDR_CONTENT_TYPE, ctype); - if (clen > 0) + if (clen >= 0) httpHeaderSetInt(hdr, HDR_CONTENT_LENGTH, clen); if (expires >= 0) httpHeaderSetTime(hdr, HDR_EXPIRES, expires); @@ -233,8 +221,8 @@ httpReplySetHeaders(HttpReply * reply, double ver, http_status status, const cha * if you, for example, assume that HDR_EXPIRES contains expire info * * if you think about it, in most cases, you are not looking for the information - * in the header, but rather for current state of the reply, which may or maynot - * depend on headers. + * in the header, but rather for current state of the reply, which may or may + * not depend on headers. * * For example, the _real_ question is * "when does this object expire?" @@ -245,7 +233,7 @@ httpReplySetHeaders(HttpReply * reply, double ver, http_status status, const cha void httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply * freshRep) { -#if 0 /* this is what we want: */ +#if 0 /* this is what we want: */ rep->cache_control = freshRep->cache_control; rep->misc_headers = freshRep->misc_headers; if (freshRep->date > -1) @@ -265,8 +253,9 @@ httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply * freshRep) lmt = httpHeaderGetTime(&rep->hdr, HDR_LAST_MODIFIED); /* clean old headers */ httpHeaderClean(&rep->hdr); - /* clone */ - rep->hdr = *httpHeaderClone(&freshRep->hdr); + httpHeaderInit(&rep->hdr); + /* copy */ + httpHeaderCopy(&rep->hdr, &freshRep->hdr); /* restore missing info if needed */ if (!httpHeaderHas(&rep->hdr, HDR_DATE)) httpHeaderSetTime(&rep->hdr, HDR_DATE, date); @@ -291,7 +280,6 @@ httpReplyContentType(const HttpReply * rep) return httpHeaderGetStr(&rep->hdr, HDR_CONTENT_TYPE); } -/* does it make sense to cache these computations ? @?@ */ time_t httpReplyExpires(const HttpReply * rep) { @@ -370,8 +358,6 @@ httpReplyParseStep(HttpReply * rep, const char *buf, int atEnd) rep->hdr_sz = *parse_end_ptr - buf; rep->pstate++; } - /* could check here for a _small_ body that we could parse right away?? @?@ */ - return 1; } diff --git a/src/client_side.cc b/src/client_side.cc index ede57c8a8b..484bfc4c1a 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -1,6 +1,6 @@ /* - * $Id: client_side.cc,v 1.223 1998/03/07 23:43:03 rousskov Exp $ + * $Id: client_side.cc,v 1.224 1998/03/11 22:18:47 rousskov Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels @@ -856,6 +856,12 @@ clientBuildReplyHeader(clientHttpRequest * http, debug(33, 3) ("clientBuildReplyHeader: OUTPUT:\n%s\n", hdr_out); memFree(MEM_4K_BUF, xbuf); memFree(MEM_4K_BUF, ybuf); + /* temporary kludge to test headers, remove it @?@ @?@ */ + { + extern void httpHeaderTestParser(const char *hstr); + httpHeaderTestParser(hdr_out); + } + /* end of kludge */ return len; } @@ -1517,6 +1523,12 @@ parseHttpRequest(ConnStateData * conn, method_t * method_p, int *status, *(*headers_p + header_sz) = '\0'; debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n", *headers_p); + /* temporary kludge to test headers, remove it @?@ @?@ */ + { + extern void httpHeaderTestParser(const char *hstr); + httpHeaderTestParser(*headers_p); + } + /* end of kludge */ /* Assign http->uri */ if ((t = strchr(url, '\n'))) /* remove NL */ diff --git a/src/defines.h b/src/defines.h index d0c9fb3fcd..50cb12349a 100644 --- a/src/defines.h +++ b/src/defines.h @@ -134,8 +134,8 @@ #define SM_PAGE_SIZE 4096 #define DISK_PAGE_SIZE 8192 -#define EBIT_SET(flag, bit) ((flag) |= ((1<