]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
- Attempted to fix a problem when Squid ignores all header fields it cannot
authorrousskov <>
Thu, 12 Mar 1998 05:18:42 +0000 (05:18 +0000)
committerrousskov <>
Thu, 12 Mar 1998 05:18:42 +0000 (05:18 +0000)
  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.

14 files changed:
include/Stack.h
include/util.h
lib/util.c
src/HttpHdrCc.cc
src/HttpHdrContRange.cc
src/HttpHeader.cc
src/HttpHeaderTools.cc
src/HttpReply.cc
src/client_side.cc
src/defines.h
src/enums.h
src/protos.h
src/structs.h
src/typedefs.h

index 213ae1439cd5beb4d160edda3c90df002ea0edb2..e313fe6190a0ee8cdbf46ea22a84faf0e18aba6b 100644 (file)
@@ -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;
 };
 
index 4a9a14d3fe1c5f559d5f77d02182fdbca85174c0..87762b0571c0b085bb2c39e96a36c078df6afd05 100644 (file)
@@ -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_ */
index 2a914e1574c7b2fd28aee93fd87872ec5fc1d658..5df5eb03684bfb18a8cbe377b2446771955a68a3 100644 (file)
@@ -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;
+}
index bf946125bec5a84b9bb20d447c9d8e3ec880dfe1..c1accab38621f940e138aee563c329d4fae7364b 100644 (file)
@@ -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));
index 0d9676897cfd17a729075c2740401f949cf6f4c1..01706534ea5031d68e328563edb41d87c09665c7 100644 (file)
@@ -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;
 }
 
index c984d6803a2f073a8b9e6742fd9fe00db69a0efc..80b2592e5035adac1c6425758ac5092290cb2c76 100644 (file)
@@ -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
  */
 
 /*
- * 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 <CRLF> */
        /*
-        * 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));
     }
index 1747dd8c376351960e01bd5218a7e25a100cff58..f25d05836b530fa0f58da2ea504b2a753fd2be40 100644 (file)
@@ -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
 
 #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;
+}
index 8aa23ce586c6257482e06618e70fa5907f6b7129..585ad09749e0bd10eda9b681b0359fc0de287d1e 100644 (file)
@@ -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;
 }
 
index ede57c8a8b3c481d48934fa6ef3f0692332cfcac..484bfc4c1acf3cf391bf998099b1958725f60995 100644 (file)
@@ -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 */
index d0c9fb3fcd84adec31c5b57e5f8c1d9b5adae543..50cb12349ae19fa0228887b1f56229900d90059d 100644 (file)
 #define SM_PAGE_SIZE 4096
 #define DISK_PAGE_SIZE  8192
 
-#define EBIT_SET(flag, bit)    ((flag) |= ((1<<bit)))
-#define EBIT_CLR(flag, bit)    ((flag) &= ~((1<<bit)))
+#define EBIT_SET(flag, bit)    ((void)((flag) |= ((1<<bit))))
+#define EBIT_CLR(flag, bit)    ((void)((flag) &= ~((1<<bit))))
 #define EBIT_TEST(flag, bit)   ((flag) & ((1<<bit)))
 
 #define MAX_FILES_PER_DIR (1<<20)
index 0475644534279ccdde2269a94c38c719c35c2630..16524fc7a217db95148f329d59891f20aaabc705 100644 (file)
@@ -168,6 +168,7 @@ typedef enum {
 /* recognized or "known" header fields; @?@ add more! */
 typedef enum {
     HDR_ACCEPT,
+    HDR_ACCEPT_RANGES,
     HDR_AGE,
     HDR_CACHE_CONTROL,
     HDR_CONNECTION,
@@ -188,10 +189,12 @@ typedef enum {
     HDR_PUBLIC,
     HDR_RANGE,
     HDR_RETRY_AFTER,
+    HDR_SERVER,
     HDR_SET_COOKIE,
     HDR_UPGRADE,
     HDR_WARNING,
     HDR_WWW_AUTHENTICATE,
+    HDR_X_CACHE,
     HDR_PROXY_KEEPALIVE,
     HDR_OTHER,
     HDR_ENUM_END
index 78b7cd919754388afbd143ca257f4cd0794899d2..402f4a7cb243d7312fe14b65634bde6871155aff 100644 (file)
@@ -269,6 +269,7 @@ extern HttpHdrExtField *httpHdrExtFieldDup(HttpHdrExtField * f);
 
 /* Http Cache Control Header Field */
 extern void httpHdrCcInitModule();
+extern void httpHdrCcCleanModule();
 extern HttpHdrCc *httpHdrCcCreate();
 extern HttpHdrCc *httpHdrCcParseCreate(const char *str);
 extern void httpHdrCcDestroy(HttpHdrCc * cc);
@@ -285,7 +286,6 @@ extern int httpHdrRangeParseInit(HttpHdrRange *range, const char *range_spec);
 extern void httpHdrRangeDestroy(HttpHdrRange *range);
 extern HttpHdrRange *httpHdrRangeDup(const HttpHdrRange * range);
 extern void httpHdrRangePackInto(const HttpHdrRange * range, Packer * p);
-extern void httpHdrRangeJoinWith(HttpHdrRange * range, const HttpHdrRange * new_range);
 /* iterate through specs */
 extern int httpHdrRangeGetSpec(const HttpHdrRange *range, HttpHdrRangeSpec *spec, int *pos);
 
@@ -298,8 +298,9 @@ extern HttpHdrContRange *httpHdrContRangeDup(const HttpHdrContRange * crange);
 extern void httpHdrContRangePackInto(const HttpHdrContRange * crange, Packer * p);
 
 /* Http Header Tools */
-extern int httpHeaderIdByName(const char *name, int name_len, const field_attrs_t * attrs, int end, int mask);
-extern void httpHeaderInitAttrTable(field_attrs_t * table, int count);
+extern HttpHeaderFieldInfo *httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count);
+extern void httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo *info, int count);
+extern int httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo* attrs, int end, int mask);
 extern int httpHeaderCalcMask(const int *enums, int count);
 extern int strListGetItem(const char *str, char del, const char **item, int *ilen, const char **pos);
 extern const char *getStringPrefix(const char *str);
@@ -315,13 +316,13 @@ extern void httpHeaderInit(HttpHeader * hdr);
 extern void httpHeaderClean(HttpHeader * hdr);
 extern void httpHeaderDestroy(HttpHeader * hdr);
 /* clone */
-HttpHeader *httpHeaderClone(HttpHeader * hdr);
+void httpHeaderCopy(HttpHeader *dest, const HttpHeader *src);
 /* parse/pack */
 extern int httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end);
 extern void httpHeaderPackInto(const HttpHeader * hdr, Packer * p);
 /* field manipulation */
 extern int httpHeaderHas(const HttpHeader * hdr, http_hdr_type type);
-extern void httpHeaderDel(HttpHeader * hdr, http_hdr_type id);
+extern HttpHeaderEntry *httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id, HttpHeaderPos * pos);
 extern void httpHeaderSetInt(HttpHeader * hdr, http_hdr_type type, int number);
 extern void httpHeaderSetTime(HttpHeader * hdr, http_hdr_type type, time_t time);
 extern void httpHeaderSetStr(HttpHeader * hdr, http_hdr_type type, const char *str);
@@ -333,6 +334,7 @@ extern HttpHdrCc *httpHeaderGetCc(const HttpHeader * hdr);
 extern HttpHdrRange *httpHeaderGetRange(const HttpHeader * hdr);
 extern HttpHdrContRange *httpHeaderGetContRange(const HttpHeader * hdr);
 extern const char *httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id);
+extern void httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p);
 int httpHeaderDelFields(HttpHeader * hdr, const char *name);
 /* store report about current header usage and other stats */
 extern void httpHeaderStoreReport(StoreEntry * e);
index 6d8474b82895b03498f55cdd23fc5dcc967e82c1..04e9cd80111111d84e930420088e1fcd6b7ad4a2 100644 (file)
@@ -513,10 +513,10 @@ union _field_store {
     int v_int;
     time_t v_time;
     String v_str;
+    const String *v_cpstr;
     HttpHdrCc *v_pcc;
     HttpHdrRange *v_prange;
     HttpHdrContRange *v_pcont_range;
-    HttpHdrExtField *v_pefield;
 };
 
 /* per field statistics */
@@ -528,11 +528,17 @@ struct _HttpHeaderFieldStat {
 };
 
 /* constant attributes of http header fields */
-struct _field_attrs_t {
+struct _HttpHeaderFieldAttrs {
     const char *name;
     http_hdr_type id;
     field_type type;
-    int name_len;
+};
+
+/* compiled version HttpHeaderFieldAttrs plus stats */
+struct _HttpHeaderFieldInfo {
+    http_hdr_type id;
+    String name;
+    field_type type;
     HttpHeaderFieldStat stat;
 };
 
@@ -554,7 +560,7 @@ struct _HttpReply {
     /* public, readable */
     HttpMsgParseState pstate;  /* the current parsing state */
 
-    /* public, writable, but use interfaces below when possible */
+    /* public, writable, but use httpReply* interfaces when possible */
     HttpStatusLine sline;
     HttpHeader hdr;
     HttpBody body;             /* used for small constant memory-resident text bodies only */
index b1f3b53dba3b9550075599fcbec43bc3a418e665..3a92952980ba82fe09687fe56c0342c54d4b6cf6 100644 (file)
@@ -51,7 +51,8 @@ typedef struct _hash_link hash_link;
 typedef struct _hash_table hash_table;
 typedef struct _HttpReply http_reply;
 typedef struct _HttpStatusLine HttpStatusLine;
-typedef struct _field_attrs_t field_attrs_t;
+typedef struct _HttpHeaderFieldAttrs HttpHeaderFieldAttrs;
+typedef struct _HttpHeaderFieldInfo HttpHeaderFieldInfo;
 typedef struct _HttpHeader HttpHeader;
 typedef struct _HttpHdrCc HttpHdrCc;
 typedef struct _HttpHdrRangeSpec HttpHdrRangeSpec;
@@ -164,3 +165,6 @@ typedef size_t mb_size_t;
 
 /* iteration for HttpHdrRange */
 typedef int HttpHdrRangePos;
+
+/*iteration for headers; use HttpHeaderPos as opaque type, do not interpret */
+typedef ssize_t HttpHeaderPos;