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