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