HttpHeader.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:33k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: HttpHeader.c,v 1.61.2.1 1999/02/12 19:38:18 wessels Exp $
  3.  *
  4.  * DEBUG: section 55    HTTP Header
  5.  * AUTHOR: Alex Rousskov
  6.  *
  7.  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
  8.  * ----------------------------------------------------------
  9.  *
  10.  *  Squid is the result of efforts by numerous individuals from the
  11.  *  Internet community.  Development is led by Duane Wessels of the
  12.  *  National Laboratory for Applied Network Research and funded by the
  13.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  14.  *  Duane Wessels and the University of California San Diego.  Please
  15.  *  see the COPYRIGHT file for full details.  Squid incorporates
  16.  *  software developed and/or copyrighted by other sources.  Please see
  17.  *  the CREDITS file for full details.
  18.  *
  19.  *  This program is free software; you can redistribute it and/or modify
  20.  *  it under the terms of the GNU General Public License as published by
  21.  *  the Free Software Foundation; either version 2 of the License, or
  22.  *  (at your option) any later version.
  23.  *  
  24.  *  This program is distributed in the hope that it will be useful,
  25.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *  GNU General Public License for more details.
  28.  *  
  29.  *  You should have received a copy of the GNU General Public License
  30.  *  along with this program; if not, write to the Free Software
  31.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  32.  *
  33.  */
  34. #include "squid.h"
  35. /*
  36.  * On naming conventions:
  37.  * 
  38.  * HTTP/1.1 defines message-header as 
  39.  * 
  40.  * message-header = field-name ":" [ field-value ] CRLF
  41.  * field-name     = token
  42.  * field-value    = *( field-content | LWS )
  43.  * 
  44.  * HTTP/1.1 does not give a name name a group of all message-headers in a message.
  45.  * Squid 1.1 seems to refer to that group _plus_ start-line as "headers".
  46.  * 
  47.  * HttpHeader is an object that represents all message-headers in a message.
  48.  * HttpHeader does not manage start-line.
  49.  * 
  50.  * HttpHeader is implemented as a collection of header "entries".
  51.  * An entry is a (field_id, field_name, field_value) triplet.
  52.  */
  53. /*
  54.  * local constants and vars
  55.  */
  56. /*
  57.  * A table with major attributes for every known field. 
  58.  * We calculate name lengths and reorganize this array on start up. 
  59.  * After reorganization, field id can be used as an index to the table.
  60.  */
  61. static const HttpHeaderFieldAttrs HeadersAttrs[] =
  62. {
  63.     {"Accept", HDR_ACCEPT, ftStr},
  64.     {"Accept-Charset", HDR_ACCEPT_CHARSET, ftStr},
  65.     {"Accept-Encoding", HDR_ACCEPT_ENCODING, ftStr},
  66.     {"Accept-Language", HDR_ACCEPT_LANGUAGE, ftStr},
  67.     {"Accept-Ranges", HDR_ACCEPT_RANGES, ftStr},
  68.     {"Age", HDR_AGE, ftInt},
  69.     {"Allow", HDR_ALLOW, ftStr},
  70.     {"Authorization", HDR_AUTHORIZATION, ftStr}, /* for now */
  71.     {"Cache-Control", HDR_CACHE_CONTROL, ftPCc},
  72.     {"Connection", HDR_CONNECTION, ftStr},
  73.     {"Content-Base", HDR_CONTENT_BASE, ftStr},
  74.     {"Content-Encoding", HDR_CONTENT_ENCODING, ftStr},
  75.     {"Content-Language", HDR_CONTENT_LANGUAGE, ftStr},
  76.     {"Content-Length", HDR_CONTENT_LENGTH, ftInt},
  77.     {"Content-Location", HDR_CONTENT_LOCATION, ftStr},
  78.     {"Content-MD5", HDR_CONTENT_MD5, ftStr}, /* for now */
  79.     {"Content-Range", HDR_CONTENT_RANGE, ftPContRange},
  80.     {"Content-Type", HDR_CONTENT_TYPE, ftStr},
  81.     {"Date", HDR_DATE, ftDate_1123},
  82.     {"ETag", HDR_ETAG, ftETag},
  83.     {"Expires", HDR_EXPIRES, ftDate_1123},
  84.     {"From", HDR_FROM, ftStr},
  85.     {"Host", HDR_HOST, ftStr},
  86.     {"If-Match", HDR_IF_MATCH, ftStr}, /* for now */
  87.     {"If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123},
  88.     {"If-None-Match", HDR_IF_NONE_MATCH, ftStr}, /* for now */
  89.     {"If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag},
  90.     {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123},
  91.     {"Link", HDR_LINK, ftStr},
  92.     {"Location", HDR_LOCATION, ftStr},
  93.     {"Max-Forwards", HDR_MAX_FORWARDS, ftInt},
  94.     {"Mime-Version", HDR_MIME_VERSION, ftStr}, /* for now */
  95.     {"Pragma", HDR_PRAGMA, ftStr},
  96.     {"Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftStr},
  97.     {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr},
  98.     {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr},
  99.     {"Public", HDR_PUBLIC, ftStr},
  100.     {"Range", HDR_RANGE, ftPRange},
  101.     {"Referer", HDR_REFERER, ftStr},
  102.     {"Request-Range", HDR_REQUEST_RANGE, ftPRange}, /* usually matches HDR_RANGE */
  103.     {"Retry-After", HDR_RETRY_AFTER, ftStr}, /* for now (ftDate_1123 or ftInt!) */
  104.     {"Server", HDR_SERVER, ftStr},
  105.     {"Set-Cookie", HDR_SET_COOKIE, ftStr},
  106.     {"Title", HDR_TITLE, ftStr},
  107.     {"Upgrade", HDR_UPGRADE, ftStr}, /* for now */
  108.     {"User-Agent", HDR_USER_AGENT, ftStr},
  109.     {"Vary", HDR_VARY, ftStr}, /* for now */
  110.     {"Via", HDR_VIA, ftStr}, /* for now */
  111.     {"Warning", HDR_WARNING, ftStr}, /* for now */
  112.     {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr},
  113.     {"X-Cache", HDR_X_CACHE, ftStr},
  114.     {"X-Cache-Lookup", HDR_X_CACHE_LOOKUP, ftStr},
  115.     {"X-Forwarded-For", HDR_X_FORWARDED_FOR, ftStr},
  116.     {"X-Request-URI", HDR_X_REQUEST_URI, ftStr},
  117.     {"X-Squid-Error", HDR_X_SQUID_ERROR, ftStr},
  118.     {"Other:", HDR_OTHER, ftStr} /* ':' will not allow matches */
  119. };
  120. static HttpHeaderFieldInfo *Headers = NULL;
  121. /*
  122.  * headers with field values defined as #(values) in HTTP/1.1
  123.  * Headers that are currently not recognized, are commented out.
  124.  */
  125. static HttpHeaderMask ListHeadersMask; /* set run-time using  ListHeadersArr */
  126. static http_hdr_type ListHeadersArr[] =
  127. {
  128.     HDR_ACCEPT,
  129.     HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE,
  130.     HDR_ACCEPT_RANGES, HDR_ALLOW,
  131.     HDR_CACHE_CONTROL,
  132.     HDR_CONTENT_ENCODING,
  133.     HDR_CONTENT_LANGUAGE,
  134.     HDR_CONNECTION,
  135.     HDR_IF_MATCH, HDR_IF_NONE_MATCH,
  136.     HDR_LINK, HDR_PRAGMA,
  137.     /* HDR_TRANSFER_ENCODING, */
  138.     HDR_UPGRADE,
  139.     HDR_VARY,
  140.     HDR_VIA,
  141.     /* HDR_WARNING, */
  142.     HDR_WWW_AUTHENTICATE,
  143.     /* HDR_EXPECT, HDR_TE, HDR_TRAILER */
  144.     HDR_X_FORWARDED_FOR
  145. };
  146. /* general-headers */
  147. static http_hdr_type GeneralHeadersArr[] =
  148. {
  149.     HDR_CACHE_CONTROL, HDR_CONNECTION, HDR_DATE, HDR_PRAGMA,
  150.     /* HDR_TRANSFER_ENCODING, */
  151.     HDR_UPGRADE,
  152.     /* HDR_TRAILER, */
  153.     HDR_VIA
  154. };
  155. /* entity-headers */
  156. static http_hdr_type EntityHeadersArr[] =
  157. {
  158.     HDR_ALLOW, HDR_CONTENT_BASE, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE,
  159.     HDR_CONTENT_LENGTH, HDR_CONTENT_LOCATION, HDR_CONTENT_MD5,
  160.     HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_ETAG, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_LINK,
  161.     HDR_OTHER
  162. };
  163. static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */
  164. static http_hdr_type ReplyHeadersArr[] =
  165. {
  166.     HDR_ACCEPT, HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE,
  167.     HDR_ACCEPT_RANGES, HDR_AGE,
  168.     HDR_LOCATION, HDR_MAX_FORWARDS,
  169.     HDR_MIME_VERSION, HDR_PUBLIC, HDR_RETRY_AFTER, HDR_SERVER, HDR_SET_COOKIE,
  170.     HDR_VARY,
  171.     HDR_WARNING, HDR_PROXY_CONNECTION, HDR_X_CACHE,
  172.     HDR_X_CACHE_LOOKUP,
  173.     HDR_X_REQUEST_URI,
  174.     HDR_X_SQUID_ERROR
  175. };
  176. static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */
  177. static http_hdr_type RequestHeadersArr[] =
  178. {
  179.     HDR_AUTHORIZATION, HDR_FROM, HDR_HOST,
  180.     HDR_IF_MATCH, HDR_IF_MODIFIED_SINCE, HDR_IF_NONE_MATCH,
  181.     HDR_IF_RANGE, HDR_MAX_FORWARDS, HDR_PROXY_CONNECTION,
  182.     HDR_PROXY_AUTHORIZATION, HDR_RANGE, HDR_REFERER, HDR_REQUEST_RANGE,
  183.     HDR_USER_AGENT, HDR_X_FORWARDED_FOR
  184. };
  185. /* header accounting */
  186. static HttpHeaderStat HttpHeaderStats[] =
  187. {
  188.     {"all"},
  189. #if USE_HTCP
  190.     {"HTCP reply"},
  191. #endif
  192.     {"request"},
  193.     {"reply"}
  194. };
  195. static int HttpHeaderStatCount = countof(HttpHeaderStats);
  196. static int HeaderEntryParsedCount = 0;
  197. /*
  198.  * local routines
  199.  */
  200. #define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END)
  201. static HttpHeaderEntry *httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value);
  202. static void httpHeaderEntryDestroy(HttpHeaderEntry * e);
  203. static HttpHeaderEntry *httpHeaderEntryParseCreate(const char *field_start, const char *field_end);
  204. static void httpHeaderNoteParsedEntry(http_hdr_type id, String value, int error);
  205. static void httpHeaderStatInit(HttpHeaderStat * hs, const char *label);
  206. static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);
  207. /*
  208.  * Module initialization routines
  209.  */
  210. void
  211. httpHeaderInitModule()
  212. {
  213.     int i;
  214.     /* check that we have enough space for masks */
  215.     assert(8 * sizeof(HttpHeaderMask) >= HDR_ENUM_END);
  216.     /* all headers must be described */
  217.     assert(countof(HeadersAttrs) == HDR_ENUM_END);
  218.     if (!Headers)
  219. Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
  220.     /* create masks */
  221.     httpHeaderMaskInit(&ListHeadersMask, 0);
  222.     httpHeaderCalcMask(&ListHeadersMask, (const int *) ListHeadersArr, countof(ListHeadersArr));
  223.     httpHeaderMaskInit(&ReplyHeadersMask, 0);
  224.     httpHeaderCalcMask(&ReplyHeadersMask, (const int *) ReplyHeadersArr, countof(ReplyHeadersArr));
  225.     httpHeaderCalcMask(&ReplyHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr));
  226.     httpHeaderCalcMask(&ReplyHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr));
  227.     httpHeaderMaskInit(&RequestHeadersMask, 0);
  228.     httpHeaderCalcMask(&RequestHeadersMask, (const int *) RequestHeadersArr, countof(RequestHeadersArr));
  229.     httpHeaderCalcMask(&RequestHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr));
  230.     httpHeaderCalcMask(&RequestHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr));
  231.     /* init header stats */
  232.     assert(HttpHeaderStatCount == hoReply + 1);
  233.     for (i = 0; i < HttpHeaderStatCount; i++)
  234. httpHeaderStatInit(HttpHeaderStats + i, HttpHeaderStats[i].label);
  235.     HttpHeaderStats[hoRequest].owner_mask = &RequestHeadersMask;
  236.     HttpHeaderStats[hoReply].owner_mask = &ReplyHeadersMask;
  237. #if USE_HTCP
  238.     HttpHeaderStats[hoHtcpReply].owner_mask = &ReplyHeadersMask;
  239. #endif
  240.     /* init dependent modules */
  241.     httpHdrCcInitModule();
  242.     /* register with cache manager */
  243.     cachemgrRegister("http_headers",
  244. "HTTP Header Statistics", httpHeaderStoreReport, 0, 1);
  245. }
  246. void
  247. httpHeaderCleanModule()
  248. {
  249.     httpHeaderDestroyFieldsInfo(Headers, HDR_ENUM_END);
  250.     Headers = NULL;
  251.     httpHdrCcCleanModule();
  252. }
  253. static void
  254. httpHeaderStatInit(HttpHeaderStat * hs, const char *label)
  255. {
  256.     assert(hs);
  257.     assert(label);
  258.     memset(hs, 0, sizeof(HttpHeaderStat));
  259.     hs->label = label;
  260.     statHistEnumInit(&hs->hdrUCountDistr, 32); /* not a real enum */
  261.     statHistEnumInit(&hs->fieldTypeDistr, HDR_ENUM_END);
  262.     statHistEnumInit(&hs->ccTypeDistr, CC_ENUM_END);
  263. }
  264. /*
  265.  * HttpHeader Implementation
  266.  */
  267. void
  268. httpHeaderInit(HttpHeader * hdr, http_hdr_owner_type owner)
  269. {
  270.     assert(hdr);
  271.     assert(owner > hoNone && owner <= hoReply);
  272.     debug(55, 7) ("init-ing hdr: %p owner: %dn", hdr, owner);
  273.     memset(hdr, 0, sizeof(*hdr));
  274.     hdr->owner = owner;
  275.     arrayInit(&hdr->entries);
  276. }
  277. void
  278. httpHeaderClean(HttpHeader * hdr)
  279. {
  280.     HttpHeaderPos pos = HttpHeaderInitPos;
  281.     HttpHeaderEntry *e;
  282.     assert(hdr);
  283.     assert(hdr->owner > hoNone && hdr->owner <= hoReply);
  284.     debug(55, 7) ("cleaning hdr: %p owner: %dn", hdr, hdr->owner);
  285.     statHistCount(&HttpHeaderStats[hdr->owner].hdrUCountDistr, hdr->entries.count);
  286.     HttpHeaderStats[hdr->owner].destroyedCount++;
  287.     HttpHeaderStats[hdr->owner].busyDestroyedCount += hdr->entries.count > 0;
  288.     while ((e = httpHeaderGetEntry(hdr, &pos))) {
  289. /* tmp hack to try to avoid coredumps */
  290. if (e->id < 0 || e->id >= HDR_ENUM_END) {
  291.     debug(55, 0) ("httpHeaderClean BUG: entry[%d] is invalid (%d). Ignored.n",
  292. pos, e->id);
  293. } else {
  294.     statHistCount(&HttpHeaderStats[hdr->owner].fieldTypeDistr, e->id);
  295.     /* yes, this destroy() leaves us in an incosistent state */
  296.     httpHeaderEntryDestroy(e);
  297. }
  298.     }
  299.     arrayClean(&hdr->entries);
  300. }
  301. /* append entries (also see httpHeaderUpdate) */
  302. void
  303. httpHeaderAppend(HttpHeader * dest, const HttpHeader * src)
  304. {
  305.     const HttpHeaderEntry *e;
  306.     HttpHeaderPos pos = HttpHeaderInitPos;
  307.     assert(src && dest);
  308.     assert(src != dest);
  309.     debug(55, 7) ("appending hdr: %p += %pn", dest, src);
  310.     while ((e = httpHeaderGetEntry(src, &pos))) {
  311. httpHeaderAddEntry(dest, httpHeaderEntryClone(e));
  312.     }
  313. }
  314. /* use fresh entries to replace old ones */
  315. void
  316. httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
  317. {
  318.     const HttpHeaderEntry *e;
  319.     HttpHeaderPos pos = HttpHeaderInitPos;
  320.     assert(old && fresh);
  321.     assert(old != fresh);
  322.     debug(55, 7) ("updating hdr: %p <- %pn", old, fresh);
  323.     while ((e = httpHeaderGetEntry(fresh, &pos))) {
  324. /* deny bad guys (ok to check for HDR_OTHER) here */
  325. if (denied_mask && CBIT_TEST(*denied_mask, e->id))
  326.     continue;
  327. httpHeaderDelByName(old, strBuf(e->name));
  328. httpHeaderAddEntry(old, httpHeaderEntryClone(e));
  329.     }
  330. }
  331. /* just handy in parsing: resets and returns false */
  332. int
  333. httpHeaderReset(HttpHeader * hdr)
  334. {
  335.     http_hdr_owner_type ho = hdr->owner;
  336.     assert(hdr);
  337.     ho = hdr->owner;
  338.     httpHeaderClean(hdr);
  339.     httpHeaderInit(hdr, ho);
  340.     return 0;
  341. }
  342. int
  343. httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end)
  344. {
  345.     const char *field_start = header_start;
  346.     HttpHeaderEntry *e;
  347.     assert(hdr);
  348.     assert(header_start && header_end);
  349.     debug(55, 7) ("parsing hdr: (%p)n%sn", hdr, getStringPrefix(header_start, header_end));
  350.     HttpHeaderStats[hdr->owner].parsedCount++;
  351.     /* commonn format headers are "<name>:[ws]<value>" lines delimited by <CRLF> */
  352.     while (field_start < header_end) {
  353. const char *field_end = field_start + strcspn(field_start, "rn");
  354. if (!*field_end || field_end > header_end)
  355.     return httpHeaderReset(hdr); /* missing <CRLF> */
  356. e = httpHeaderEntryParseCreate(field_start, field_end);
  357. if (e != NULL)
  358.     httpHeaderAddEntry(hdr, e);
  359. else
  360.     debug(55, 2) ("warning: ignoring unparseable http header field near '%s'n",
  361. getStringPrefix(field_start, field_end));
  362. field_start = field_end;
  363. /* skip CRLF */
  364. if (*field_start == 'r')
  365.     field_start++;
  366. if (*field_start == 'n')
  367.     field_start++;
  368.     }
  369.     return 1; /* even if no fields where found, it is a valid header */
  370. }
  371. /* packs all the entries using supplied packer */
  372. void
  373. httpHeaderPackInto(const HttpHeader * hdr, Packer * p)
  374. {
  375.     HttpHeaderPos pos = HttpHeaderInitPos;
  376.     const HttpHeaderEntry *e;
  377.     assert(hdr && p);
  378.     debug(55, 7) ("packing hdr: (%p)n", hdr);
  379.     /* pack all entries one by one */
  380.     while ((e = httpHeaderGetEntry(hdr, &pos)))
  381. httpHeaderEntryPackInto(e, p);
  382. }
  383. /* returns next valid entry */
  384. HttpHeaderEntry *
  385. httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos)
  386. {
  387.     assert(hdr && pos);
  388.     assert(*pos >= HttpHeaderInitPos && *pos < hdr->entries.count);
  389.     for ((*pos)++; *pos < hdr->entries.count; (*pos)++) {
  390. if (hdr->entries.items[*pos])
  391.     return hdr->entries.items[*pos];
  392.     }
  393.     return NULL;
  394. }
  395. /*
  396.  * returns a pointer to a specified entry if any 
  397.  * note that we return one entry so it does not make much sense to ask for
  398.  * "list" headers
  399.  */
  400. HttpHeaderEntry *
  401. httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id)
  402. {
  403.     HttpHeaderPos pos = HttpHeaderInitPos;
  404.     HttpHeaderEntry *e;
  405.     assert(hdr);
  406.     assert_eid(id);
  407.     assert(!CBIT_TEST(ListHeadersMask, id));
  408.     /* check mask first */
  409.     if (!CBIT_TEST(hdr->mask, id))
  410. return NULL;
  411.     /* looks like we must have it, do linear search */
  412.     while ((e = httpHeaderGetEntry(hdr, &pos))) {
  413. if (e->id == id)
  414.     return e;
  415.     }
  416.     /* hm.. we thought it was there, but it was not found */
  417.     assert(0);
  418.     return NULL; /* not reached */
  419. }
  420. /*
  421.  * same as httpHeaderFindEntry
  422.  */
  423. static HttpHeaderEntry *
  424. httpHeaderFindLastEntry(const HttpHeader * hdr, http_hdr_type id)
  425. {
  426.     HttpHeaderPos pos = HttpHeaderInitPos;
  427.     HttpHeaderEntry *e;
  428.     HttpHeaderEntry *result = NULL;
  429.     assert(hdr);
  430.     assert_eid(id);
  431.     assert(!CBIT_TEST(ListHeadersMask, id));
  432.     /* check mask first */
  433.     if (!CBIT_TEST(hdr->mask, id))
  434. return NULL;
  435.     /* looks like we must have it, do linear search */
  436.     while ((e = httpHeaderGetEntry(hdr, &pos))) {
  437. if (e->id == id)
  438.     result = e;
  439.     }
  440.     assert(result); /* must be there! */
  441.     return result;
  442. }
  443. /*
  444.  * deletes all fields with a given name if any, returns #fields deleted; 
  445.  */
  446. int
  447. httpHeaderDelByName(HttpHeader * hdr, const char *name)
  448. {
  449.     int count = 0;
  450.     HttpHeaderPos pos = HttpHeaderInitPos;
  451.     HttpHeaderEntry *e;
  452.     httpHeaderMaskInit(&hdr->mask, 0); /* temporal inconsistency */
  453.     debug(55, 7) ("deleting '%s' fields in hdr %pn", name, hdr);
  454.     while ((e = httpHeaderGetEntry(hdr, &pos))) {
  455. if (!strCaseCmp(e->name, name)) {
  456.     httpHeaderDelAt(hdr, pos);
  457.     count++;
  458. } else
  459.     CBIT_SET(hdr->mask, e->id);
  460.     }
  461.     return count;
  462. }
  463. /* deletes all entries with a given id, returns the #entries deleted */
  464. int
  465. httpHeaderDelById(HttpHeader * hdr, http_hdr_type id)
  466. {
  467.     int count = 0;
  468.     HttpHeaderPos pos = HttpHeaderInitPos;
  469.     HttpHeaderEntry *e;
  470.     debug(55, 8) ("%p del-by-id %dn", hdr, id);
  471.     assert(hdr);
  472.     assert_eid(id);
  473.     assert_eid(id != HDR_OTHER); /* does not make sense */
  474.     if (!CBIT_TEST(hdr->mask, id))
  475. return 0;
  476.     while ((e = httpHeaderGetEntry(hdr, &pos))) {
  477. if (e->id == id) {
  478.     httpHeaderDelAt(hdr, pos);
  479.     count++;
  480. }
  481.     }
  482.     CBIT_CLR(hdr->mask, id);
  483.     assert(count);
  484.     return count;
  485. }
  486. /*
  487.  * deletes an entry at pos and leaves a gap; leaving a gap makes it
  488.  * possible to iterate(search) and delete fields at the same time
  489.  */
  490. void
  491. httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos)
  492. {
  493.     HttpHeaderEntry *e;
  494.     assert(pos >= HttpHeaderInitPos && pos < hdr->entries.count);
  495.     e = hdr->entries.items[pos];
  496.     hdr->entries.items[pos] = NULL;
  497.     /* decrement header length, allow for ": " and crlf */
  498.     hdr->len -= strLen(e->name) + 2 + strLen(e->value) + 2;
  499.     assert(hdr->len >= 0);
  500.     httpHeaderEntryDestroy(e);
  501. }
  502. /* appends an entry; 
  503.  * does not call httpHeaderEntryClone() so one should not reuse "*e"
  504.  */
  505. void
  506. httpHeaderAddEntry(HttpHeader * hdr, HttpHeaderEntry * e)
  507. {
  508.     assert(hdr && e);
  509.     assert_eid(e->id);
  510.     debug(55, 7) ("%p adding entry: %d at %dn",
  511. hdr, e->id, hdr->entries.count);
  512.     if (CBIT_TEST(hdr->mask, e->id))
  513. Headers[e->id].stat.repCount++;
  514.     else
  515. CBIT_SET(hdr->mask, e->id);
  516.     arrayAppend(&hdr->entries, e);
  517.     /* increment header length, allow for ": " and crlf */
  518.     hdr->len += strLen(e->name) + 2 + strLen(e->value) + 2;
  519. }
  520. /* return a list of entries with the same id separated by ',' and ws */
  521. String
  522. httpHeaderGetList(const HttpHeader * hdr, http_hdr_type id)
  523. {
  524.     String s = StringNull;
  525.     HttpHeaderEntry *e;
  526.     HttpHeaderPos pos = HttpHeaderInitPos;
  527.     debug(55, 6) ("%p: joining for id %dn", hdr, id);
  528.     /* only fields from ListHeaders array can be "listed" */
  529.     assert(CBIT_TEST(ListHeadersMask, id));
  530.     if (!CBIT_TEST(hdr->mask, id))
  531. return s;
  532.     while ((e = httpHeaderGetEntry(hdr, &pos))) {
  533. if (e->id == id)
  534.     strListAdd(&s, strBuf(e->value), ',');
  535.     }
  536.     /*
  537.      * note: we might get an empty (len==0) string if there was an "empty"
  538.      * header; we must not get a NULL string though.
  539.      */
  540.     assert(strBuf(s));
  541.     /* temporary warning: remove it! @?@ @?@ @?@ */
  542.     if (!strLen(s))
  543. debug(55, 3) ("empty list header: %s (%d)n", strBuf(Headers[id].name), id);
  544.     debug(55, 6) ("%p: joined for id %d: %sn", hdr, id, strBuf(s));
  545.     return s;
  546. }
  547. /* test if a field is present */
  548. int
  549. httpHeaderHas(const HttpHeader * hdr, http_hdr_type id)
  550. {
  551.     assert(hdr);
  552.     assert_eid(id);
  553.     assert(id != HDR_OTHER);
  554.     debug(55, 7) ("%p lookup for %dn", hdr, id);
  555.     return CBIT_TEST(hdr->mask, id);
  556. }
  557. void
  558. httpHeaderPutInt(HttpHeader * hdr, http_hdr_type id, int number)
  559. {
  560.     assert_eid(id);
  561.     assert(Headers[id].type == ftInt); /* must be of an appropriate type */
  562.     assert(number >= 0);
  563.     httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, xitoa(number)));
  564. }
  565. void
  566. httpHeaderPutTime(HttpHeader * hdr, http_hdr_type id, time_t time)
  567. {
  568.     assert_eid(id);
  569.     assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
  570.     assert(time >= 0);
  571.     httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(time)));
  572. }
  573. void
  574. httpHeaderPutStr(HttpHeader * hdr, http_hdr_type id, const char *str)
  575. {
  576.     assert_eid(id);
  577.     assert(Headers[id].type == ftStr); /* must be of an appropriate type */
  578.     assert(str);
  579.     httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, str));
  580. }
  581. void
  582. httpHeaderPutAuth(HttpHeader * hdr, const char *authScheme, const char *realm)
  583. {
  584.     assert(hdr && authScheme && realm);
  585.     httpHeaderPutStrf(hdr, HDR_WWW_AUTHENTICATE, "%s realm="%s"", authScheme, realm);
  586. }
  587. void
  588. httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc)
  589. {
  590.     MemBuf mb;
  591.     Packer p;
  592.     assert(hdr && cc);
  593.     /* remove old directives if any */
  594.     httpHeaderDelById(hdr, HDR_CACHE_CONTROL);
  595.     /* pack into mb */
  596.     memBufDefInit(&mb);
  597.     packerToMemInit(&p, &mb);
  598.     httpHdrCcPackInto(cc, &p);
  599.     /* put */
  600.     httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CACHE_CONTROL, NULL, mb.buf));
  601.     /* cleanup */
  602.     packerClean(&p);
  603.     memBufClean(&mb);
  604. }
  605. void
  606. httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr)
  607. {
  608.     MemBuf mb;
  609.     Packer p;
  610.     assert(hdr && cr);
  611.     /* remove old directives if any */
  612.     httpHeaderDelById(hdr, HDR_CONTENT_RANGE);
  613.     /* pack into mb */
  614.     memBufDefInit(&mb);
  615.     packerToMemInit(&p, &mb);
  616.     httpHdrContRangePackInto(cr, &p);
  617.     /* put */
  618.     httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CONTENT_RANGE, NULL, mb.buf));
  619.     /* cleanup */
  620.     packerClean(&p);
  621.     memBufClean(&mb);
  622. }
  623. void
  624. httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range)
  625. {
  626.     MemBuf mb;
  627.     Packer p;
  628.     assert(hdr && range);
  629.     /* remove old directives if any */
  630.     httpHeaderDelById(hdr, HDR_RANGE);
  631.     /* pack into mb */
  632.     memBufDefInit(&mb);
  633.     packerToMemInit(&p, &mb);
  634.     httpHdrRangePackInto(range, &p);
  635.     /* put */
  636.     httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_RANGE, NULL, mb.buf));
  637.     /* cleanup */
  638.     packerClean(&p);
  639.     memBufClean(&mb);
  640. }
  641. /* add extension header (these fields are not parsed/analyzed/joined, etc.) */
  642. void
  643. httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value)
  644. {
  645.     assert(name && value);
  646.     debug(55, 8) ("%p adds ext entry '%s: %s'n", hdr, name, value);
  647.     httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_OTHER, name, value));
  648. }
  649. int
  650. httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id)
  651. {
  652.     HttpHeaderEntry *e;
  653.     int value = -1;
  654.     int ok;
  655.     assert_eid(id);
  656.     assert(Headers[id].type == ftInt); /* must be of an appropriate type */
  657.     if ((e = httpHeaderFindEntry(hdr, id))) {
  658. ok = httpHeaderParseInt(strBuf(e->value), &value);
  659. httpHeaderNoteParsedEntry(e->id, e->value, !ok);
  660.     }
  661.     return value;
  662. }
  663. time_t
  664. httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id)
  665. {
  666.     HttpHeaderEntry *e;
  667.     time_t value = -1;
  668.     assert_eid(id);
  669.     assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
  670.     if ((e = httpHeaderFindEntry(hdr, id))) {
  671. value = parse_rfc1123(strBuf(e->value));
  672. httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
  673.     }
  674.     return value;
  675. }
  676. /* sync with httpHeaderGetLastStr */
  677. const char *
  678. httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id)
  679. {
  680.     HttpHeaderEntry *e;
  681.     assert_eid(id);
  682.     assert(Headers[id].type == ftStr); /* must be of an appropriate type */
  683.     if ((e = httpHeaderFindEntry(hdr, id))) {
  684. httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
  685. return strBuf(e->value);
  686.     }
  687.     return NULL;
  688. }
  689. /* unusual */
  690. const char *
  691. httpHeaderGetLastStr(const HttpHeader * hdr, http_hdr_type id)
  692. {
  693.     HttpHeaderEntry *e;
  694.     assert_eid(id);
  695.     assert(Headers[id].type == ftStr); /* must be of an appropriate type */
  696.     if ((e = httpHeaderFindLastEntry(hdr, id))) {
  697. httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
  698. return strBuf(e->value);
  699.     }
  700.     return NULL;
  701. }
  702. HttpHdrCc *
  703. httpHeaderGetCc(const HttpHeader * hdr)
  704. {
  705.     HttpHdrCc *cc;
  706.     String s;
  707.     if (!CBIT_TEST(hdr->mask, HDR_CACHE_CONTROL))
  708. return NULL;
  709.     s = httpHeaderGetList(hdr, HDR_CACHE_CONTROL);
  710.     cc = httpHdrCcParseCreate(&s);
  711.     HttpHeaderStats[hdr->owner].ccParsedCount++;
  712.     if (cc)
  713. httpHdrCcUpdateStats(cc, &HttpHeaderStats[hdr->owner].ccTypeDistr);
  714.     httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc);
  715.     stringClean(&s);
  716.     return cc;
  717. }
  718. HttpHdrRange *
  719. httpHeaderGetRange(const HttpHeader * hdr)
  720. {
  721.     HttpHdrRange *r = NULL;
  722.     HttpHeaderEntry *e;
  723.     /* some clients will send "Request-Range" _and_ *matching* "Range"
  724.      * who knows, some clients might send Request-Range only;
  725.      * this "if" should work correctly in both cases;
  726.      * hopefully no clients send mismatched headers! */
  727.     if ((e = httpHeaderFindEntry(hdr, HDR_RANGE)) ||
  728. (e = httpHeaderFindEntry(hdr, HDR_REQUEST_RANGE))) {
  729. r = httpHdrRangeParseCreate(&e->value);
  730. httpHeaderNoteParsedEntry(e->id, e->value, !r);
  731.     }
  732.     return r;
  733. }
  734. HttpHdrContRange *
  735. httpHeaderGetContRange(const HttpHeader * hdr)
  736. {
  737.     HttpHdrContRange *cr = NULL;
  738.     HttpHeaderEntry *e;
  739.     if ((e = httpHeaderFindEntry(hdr, HDR_CONTENT_RANGE))) {
  740. cr = httpHdrContRangeParseCreate(strBuf(e->value));
  741. httpHeaderNoteParsedEntry(e->id, e->value, !cr);
  742.     }
  743.     return cr;
  744. }
  745. const char *
  746. httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *authScheme)
  747. {
  748.     const char *field;
  749.     int l;
  750.     assert(hdr && authScheme);
  751.     field = httpHeaderGetStr(hdr, id);
  752.     if (!field) /* no authorization field */
  753. return NULL;
  754.     l = strlen(authScheme);
  755.     if (!l || strncasecmp(field, authScheme, l)) /* wrong scheme */
  756. return NULL;
  757.     field += l;
  758.     if (!xisspace(*field)) /* wrong scheme */
  759. return NULL;
  760.     /* skip white space */
  761.     field += xcountws(field);
  762.     if (!*field) /* no authorization cookie */
  763. return NULL;
  764.     return base64_decode(field);
  765. }
  766. ETag
  767. httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id)
  768. {
  769.     ETag etag =
  770.     {NULL, -1};
  771.     HttpHeaderEntry *e;
  772.     assert(Headers[id].type == ftETag); /* must be of an appropriate type */
  773.     if ((e = httpHeaderFindEntry(hdr, id)))
  774. etagParseInit(&etag, strBuf(e->value));
  775.     return etag;
  776. }
  777. TimeOrTag
  778. httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id)
  779. {
  780.     TimeOrTag tot;
  781.     HttpHeaderEntry *e;
  782.     assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */
  783.     memset(&tot, 0, sizeof(tot));
  784.     if ((e = httpHeaderFindEntry(hdr, id))) {
  785. const char *str = strBuf(e->value);
  786. /* try as an ETag */
  787. if (etagParseInit(&tot.tag, str)) {
  788.     tot.valid = tot.tag.str != NULL;
  789.     tot.time = -1;
  790. } else {
  791.     /* or maybe it is time? */
  792.     tot.time = parse_rfc1123(str);
  793.     tot.valid = tot.time >= 0;
  794.     tot.tag.str = NULL;
  795. }
  796.     }
  797.     assert(tot.time < 0 || !tot.tag.str); /* paranoid */
  798.     return tot;
  799. }
  800. /*
  801.  * HttpHeaderEntry
  802.  */
  803. static HttpHeaderEntry *
  804. httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value)
  805. {
  806.     HttpHeaderEntry *e;
  807.     assert_eid(id);
  808.     e = memAllocate(MEM_HTTP_HDR_ENTRY);
  809.     e->id = id;
  810.     if (id != HDR_OTHER)
  811. e->name = Headers[id].name;
  812.     else
  813. stringInit(&e->name, name);
  814.     stringInit(&e->value, value);
  815.     Headers[id].stat.aliveCount++;
  816.     debug(55, 9) ("created entry %p: '%s: %s'n", e, strBuf(e->name), strBuf(e->value));
  817.     return e;
  818. }
  819. static void
  820. httpHeaderEntryDestroy(HttpHeaderEntry * e)
  821. {
  822.     assert(e);
  823.     assert_eid(e->id);
  824.     debug(55, 9) ("destroying entry %p: '%s: %s'n", e, strBuf(e->name), strBuf(e->value));
  825.     /* clean name if needed */
  826.     if (e->id == HDR_OTHER)
  827. stringClean(&e->name);
  828.     stringClean(&e->value);
  829.     assert(Headers[e->id].stat.aliveCount);
  830.     Headers[e->id].stat.aliveCount--;
  831.     e->id = -1;
  832.     memFree(e, MEM_HTTP_HDR_ENTRY);
  833. }
  834. /* parses and inits header entry, returns new entry on success */
  835. static HttpHeaderEntry *
  836. httpHeaderEntryParseCreate(const char *field_start, const char *field_end)
  837. {
  838.     HttpHeaderEntry *e;
  839.     int id;
  840.     /* note: name_start == field_start */
  841.     const char *name_end = strchr(field_start, ':');
  842.     const int name_len = name_end ? name_end - field_start : 0;
  843.     const char *value_start = field_start + name_len + 1; /* skip ':' */
  844.     /* note: value_end == field_end */
  845.     HeaderEntryParsedCount++;
  846.     /* do we have a valid field name within this field? */
  847.     if (!name_len || name_end > field_end)
  848. return NULL;
  849.     /* now we know we can parse it */
  850.     e = memAllocate(MEM_HTTP_HDR_ENTRY);
  851.     debug(55, 9) ("creating entry %p: near '%s'n", e, getStringPrefix(field_start, field_end));
  852.     /* is it a "known" field? */
  853.     id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END);
  854.     if (id < 0)
  855. id = HDR_OTHER;
  856.     assert_eid(id);
  857.     e->id = id;
  858.     /* set field name */
  859.     if (id == HDR_OTHER)
  860. stringLimitInit(&e->name, field_start, name_len);
  861.     else
  862. e->name = Headers[id].name;
  863.     /* trim field value */
  864.     while (value_start < field_end && xisspace(*value_start))
  865. value_start++;
  866.     /* set field value */
  867.     stringLimitInit(&e->value, value_start, field_end - value_start);
  868.     Headers[id].stat.seenCount++;
  869.     Headers[id].stat.aliveCount++;
  870.     debug(55, 9) ("created entry %p: '%s: %s'n", e, strBuf(e->name), strBuf(e->value));
  871.     return e;
  872. }
  873. HttpHeaderEntry *
  874. httpHeaderEntryClone(const HttpHeaderEntry * e)
  875. {
  876.     return httpHeaderEntryCreate(e->id, strBuf(e->name), strBuf(e->value));
  877. }
  878. void
  879. httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p)
  880. {
  881.     assert(e && p);
  882.     packerAppend(p, strBuf(e->name), strLen(e->name));
  883.     packerAppend(p, ": ", 2);
  884.     packerAppend(p, strBuf(e->value), strLen(e->value));
  885.     packerAppend(p, "rn", 2);
  886. }
  887. static void
  888. httpHeaderNoteParsedEntry(http_hdr_type id, String context, int error)
  889. {
  890.     Headers[id].stat.parsCount++;
  891.     if (error) {
  892. Headers[id].stat.errCount++;
  893. debug(55, 2) ("cannot parse hdr field: '%s: %s'n",
  894.     strBuf(Headers[id].name), strBuf(context));
  895.     }
  896. }
  897. /*
  898.  * Reports
  899.  */
  900. /* tmp variable used to pass stat info to dumpers */
  901. extern const HttpHeaderStat *dump_stat; /* argh! */
  902. const HttpHeaderStat *dump_stat = NULL;
  903. static void
  904. httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
  905. {
  906.     const int id = (int) val;
  907.     const int valid_id = id >= 0 && id < HDR_ENUM_END;
  908.     const char *name = valid_id ? strBuf(Headers[id].name) : "INVALID";
  909.     int visible = count > 0;
  910.     /* for entries with zero count, list only those that belong to current type of message */
  911.     if (!visible && valid_id && dump_stat->owner_mask)
  912. visible = CBIT_TEST(*dump_stat->owner_mask, id);
  913.     if (visible)
  914. storeAppendPrintf(sentry, "%2dt %-20st %5dt %6.2fn",
  915.     id, name, count, xdiv(count, dump_stat->busyDestroyedCount));
  916. }
  917. static void
  918. httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count)
  919. {
  920.     if (count)
  921. storeAppendPrintf(sentry, "%2dt %5dt %5dt %6.2fn",
  922.     idx, (int) val, count,
  923.     xpercent(count, dump_stat->destroyedCount));
  924. }
  925. static void
  926. httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
  927. {
  928.     assert(hs && e);
  929.     dump_stat = hs;
  930.     storeAppendPrintf(e, "nHeader Stats: %sn", hs->label);
  931.     storeAppendPrintf(e, "nField type distributionn");
  932.     storeAppendPrintf(e, "%2st %-20st %5st %6sn",
  933. "id", "name", "count", "#/header");
  934.     statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper);
  935.     storeAppendPrintf(e, "nCache-control directives distributionn");
  936.     storeAppendPrintf(e, "%2st %-20st %5st %6sn",
  937. "id", "name", "count", "#/cc_field");
  938.     statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper);
  939.     storeAppendPrintf(e, "nNumber of fields per header distributionn");
  940.     storeAppendPrintf(e, "%2st %-5st %5st %6sn",
  941. "id", "#flds", "count", "%total");
  942.     statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper);
  943.     dump_stat = NULL;
  944. }
  945. void
  946. httpHeaderStoreReport(StoreEntry * e)
  947. {
  948.     int i;
  949.     http_hdr_type ht;
  950.     assert(e);
  951.     HttpHeaderStats[0].parsedCount =
  952. HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
  953.     HttpHeaderStats[0].ccParsedCount =
  954. HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
  955.     HttpHeaderStats[0].destroyedCount =
  956. HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
  957.     HttpHeaderStats[0].busyDestroyedCount =
  958. HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
  959.     for (i = 1; i < HttpHeaderStatCount; i++) {
  960. httpHeaderStatDump(HttpHeaderStats + i, e);
  961. storeAppendPrintf(e, "%sn", "<br>");
  962.     }
  963.     /* field stats for all messages */
  964.     storeAppendPrintf(e, "nHttp Fields Stats (replies and requests)n");
  965.     storeAppendPrintf(e, "%2st %-20st %5st %6st %6sn",
  966. "id", "name", "#alive", "%err", "%repeat");
  967.     for (ht = 0; ht < HDR_ENUM_END; ht++) {
  968. HttpHeaderFieldInfo *f = Headers + ht;
  969. storeAppendPrintf(e, "%2dt %-20st %5dt %6.3ft %6.3fn",
  970.     f->id, strBuf(f->name), f->stat.aliveCount,
  971.     xpercent(f->stat.errCount, f->stat.parsCount),
  972.     xpercent(f->stat.repCount, f->stat.seenCount));
  973.     }
  974.     storeAppendPrintf(e, "Headers Parsed: %d + %d = %dn",
  975. HttpHeaderStats[hoRequest].parsedCount,
  976. HttpHeaderStats[hoReply].parsedCount,
  977. HttpHeaderStats[0].parsedCount);
  978.     storeAppendPrintf(e, "Hdr Fields Parsed: %dn", HeaderEntryParsedCount);
  979. }
  980. int
  981. httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end)
  982. {
  983.     int i;
  984.     for (i = 0; i < end; ++i) {
  985. if (name_len >= 0 && name_len != strLen(info[i].name))
  986.     continue;
  987. if (!strncasecmp(name, strBuf(info[i].name),
  988. name_len < 0 ? strLen(info[i].name) + 1 : name_len))
  989.     return i;
  990.     }
  991.     return -1;
  992. }
  993. int
  994. httpHeaderIdByNameDef(const char *name, int name_len)
  995. {
  996.     if (!Headers)
  997. Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
  998.     return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END);
  999. }