]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeaderTools.cc
Bug #2016 fix: Prevent BodyPipe async calls from getting seemingly
[thirdparty/squid.git] / src / HttpHeaderTools.cc
CommitLineData
e3dd531e 1
7faf2bdb 2/*
30abd221 3 * $Id: HttpHeaderTools.cc,v 1.61 2007/05/29 13:31:37 amosjeffries Exp $
7faf2bdb 4 *
5 * DEBUG: section 66 HTTP Header Tools
6 * AUTHOR: Alex Rousskov
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
7faf2bdb 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
7faf2bdb 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 *
7faf2bdb 34 */
35
36#include "squid.h"
e6ccf245 37#include "HttpHeader.h"
528b2c61 38#include "HttpHdrContRange.h"
8000a965 39#include "ACLChecklist.h"
0eb49b6d 40#include "MemBuf.h"
7faf2bdb 41
2246b732 42static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs);
de336bbe 43
44
45HttpHeaderFieldInfo *
b644367b 46httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count)
7faf2bdb 47{
48 int i;
de336bbe 49 HttpHeaderFieldInfo *table = NULL;
50 assert(attrs && count);
51
52 /* allocate space */
0353e724 53 table = new HttpHeaderFieldInfo[count];
7faf2bdb 54
7faf2bdb 55 for (i = 0; i < count; ++i) {
62e76326 56 const http_hdr_type id = attrs[i].id;
57 HttpHeaderFieldInfo *info = table + id;
58 /* sanity checks */
59 assert(id >= 0 && id < count);
60 assert(attrs[i].name);
0353e724 61 assert(info->id == HDR_ACCEPT && info->type == ftInvalid); /* was not set before */
62e76326 62 /* copy and init fields */
63 info->id = id;
64 info->type = attrs[i].type;
65 info->name = attrs[i].name;
66 assert(info->name.size());
7faf2bdb 67 }
62e76326 68
de336bbe 69 return table;
70}
71
72void
b644367b 73httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count)
de336bbe 74{
75 int i;
62e76326 76
de336bbe 77 for (i = 0; i < count; ++i)
30abd221 78 table[i].name.clean();
62e76326 79
7f4c8dec 80 delete [] table;
7faf2bdb 81}
82
d8b249ef 83void
97474590 84httpHeaderMaskInit(HttpHeaderMask * mask, int value)
d8b249ef 85{
97474590 86 memset(mask, value, sizeof(*mask));
d8b249ef 87}
88
99edd1c3 89/* calculates a bit mask of a given array; does not reset mask! */
d8b249ef 90void
8abf232c 91httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count)
7faf2bdb 92{
e6ccf245 93 size_t i;
8abf232c 94 const int * enums = (const int *) http_hdr_type_enums;
d8b249ef 95 assert(mask && enums);
99edd1c3 96 assert(count < sizeof(*mask) * 8); /* check for overflow */
7faf2bdb 97
98 for (i = 0; i < count; ++i) {
62e76326 99 assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */
100 CBIT_SET(*mask, enums[i]);
7faf2bdb 101 }
7faf2bdb 102}
103
2246b732 104/* same as httpHeaderPutStr, but formats the string using snprintf first */
2246b732 105void
9bc73deb 106#if STDC_HEADERS
eeb423fb 107httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...)
2246b732 108#else
2246b732 109httpHeaderPutStrf(va_alist)
62e76326 110va_dcl
9bc73deb 111#endif
2246b732 112{
9bc73deb 113#if STDC_HEADERS
114 va_list args;
115 va_start(args, fmt);
116#else
62e76326 117
2246b732 118 va_list args;
119 HttpHeader *hdr = NULL;
120 http_hdr_type id = HDR_ENUM_END;
121 const char *fmt = NULL;
122 va_start(args);
123 hdr = va_arg(args, HttpHeader *);
124 id = va_arg(args, http_hdr_type);
125 fmt = va_arg(args, char *);
126#endif
62e76326 127
2246b732 128 httpHeaderPutStrvf(hdr, id, fmt, args);
129 va_end(args);
130}
131
132/* used by httpHeaderPutStrf */
133static void
134httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs)
135{
2246b732 136 MemBuf mb;
2fe7eff9 137 mb.init();
138 mb.vPrintf(fmt, vargs);
a9925b40 139 hdr->putStr(id, mb.buf);
2fe7eff9 140 mb.clean();
2246b732 141}
142
143
d192d11f 144/* wrapper arrounf PutContRange */
145void
9bc73deb 146httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, ssize_t ent_len)
d192d11f 147{
148 HttpHdrContRange *cr = httpHdrContRangeCreate();
149 assert(hdr && ent_len >= 0);
150 httpHdrContRangeSet(cr, spec, ent_len);
a9925b40 151 hdr->putContRange(cr);
d192d11f 152 httpHdrContRangeDestroy(cr);
153}
154
a9771e51 155
99edd1c3 156/*
9bc73deb 157 * return true if a given directive is found in at least one of
158 * the "connection" header-fields note: if HDR_PROXY_CONNECTION is
159 * present we ignore HDR_CONNECTION.
99edd1c3 160 */
161int
5999b776 162httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive)
99edd1c3 163{
30abd221 164 String list;
9bc73deb 165 http_hdr_type ht;
166 int res;
167 /* what type of header do we have? */
62e76326 168
a9925b40 169 if (hdr->has(HDR_PROXY_CONNECTION))
62e76326 170 ht = HDR_PROXY_CONNECTION;
a9925b40 171 else if (hdr->has(HDR_CONNECTION))
62e76326 172 ht = HDR_CONNECTION;
9bc73deb 173 else
62e76326 174 return 0;
9bc73deb 175
a9925b40 176 list = hdr->getList(ht);
62e76326 177
9bc73deb 178 res = strListIsMember(&list, directive, ',');
62e76326 179
30abd221 180 list.clean();
181
9bc73deb 182 return res;
99edd1c3 183}
184
185/* returns true iff "m" is a member of the list */
186int
30abd221 187strListIsMember(const String * list, const char *m, char del)
99edd1c3 188{
189 const char *pos = NULL;
190 const char *item;
9bc73deb 191 int ilen = 0;
192 int mlen;
99edd1c3 193 assert(list && m);
9bc73deb 194 mlen = strlen(m);
62e76326 195
9bc73deb 196 while (strListGetItem(list, del, &item, &ilen, &pos)) {
62e76326 197 if (mlen == ilen && !strncasecmp(item, m, ilen))
198 return 1;
99edd1c3 199 }
62e76326 200
99edd1c3 201 return 0;
202}
203
edf3ffdc 204/* returns true iff "s" is a substring of a member of the list */
205int
30abd221 206strListIsSubstr(const String * list, const char *s, char del)
edf3ffdc 207{
9bc73deb 208 assert(list && del);
650c4b88 209 return list->pos(s) != 0;
9bc73deb 210
211 /*
212 * Note: the original code with a loop is broken because it uses strstr()
213 * instead of strnstr(). If 's' contains a 'del', strListIsSubstr() may
214 * return true when it should not. If 's' does not contain a 'del', the
215 * implementaion is equavalent to strstr()! Thus, we replace the loop with
216 * strstr() above until strnstr() is available.
217 */
edf3ffdc 218}
219
99edd1c3 220/* appends an item to the list */
221void
30abd221 222strListAdd(String * str, const char *item, char del)
99edd1c3 223{
224 assert(str && item);
62e76326 225
528b2c61 226 if (str->size()) {
62e76326 227 char buf[3];
228 buf[0] = del;
229 buf[1] = ' ';
230 buf[2] = '\0';
231 str->append(buf, 2);
2246b732 232 }
62e76326 233
528b2c61 234 str->append(item, strlen(item));
99edd1c3 235}
236
7faf2bdb 237/*
238 * iterates through a 0-terminated string of items separated by 'del's.
239 * white space around 'del' is considered to be a part of 'del'
240 * like strtok, but preserves the source, and can iterate several strings at once
241 *
242 * returns true if next item is found.
243 * init pos with NULL to start iteration.
244 */
245int
30abd221 246strListGetItem(const String * str, char del, const char **item, int *ilen, const char **pos)
7faf2bdb 247{
248 size_t len;
f7573ac0 249 static char delim[2][3] = {
250 { '"', '?', 0},
251 { '"', '\\', 0}};
252 int quoted = 0;
7faf2bdb 253 assert(str && item && pos);
62e76326 254
f7573ac0 255 delim[0][1] = del;
256
a3f9588e 257 if (*pos) {
62e76326 258 if (!**pos) /* end of string */
259 return 0;
260 else
261 (*pos)++;
a3f9588e 262 } else {
30abd221 263 *pos = str->buf();
62e76326 264
265 if (!*pos)
266 return 0;
a3f9588e 267 }
7faf2bdb 268
269 /* skip leading ws (ltrim) */
270 *pos += xcountws(*pos);
62e76326 271
7faf2bdb 272 *item = *pos; /* remember item's start */
62e76326 273
7faf2bdb 274 /* find next delimiter */
f7573ac0 275 do {
276 *pos += strcspn(*pos, delim[quoted]);
277
278 if (**pos == del)
279 break;
280
281 if (**pos == '"') {
282 quoted = !quoted;
283 *pos += 1;
284 }
285
286 if (quoted && **pos == '\\') {
287 *pos += 1;
62e76326 288
f7573ac0 289 if (**pos)
290 *pos += 1;
291 }
292 } while (**pos);
62e76326 293
7faf2bdb 294 len = *pos - *item; /* *pos points to del or '\0' */
62e76326 295
7faf2bdb 296 /* rtrim */
b6a2f15e 297 while (len > 0 && xisspace((*item)[len - 1]))
62e76326 298 len--;
299
7faf2bdb 300 if (ilen)
62e76326 301 *ilen = len;
302
7faf2bdb 303 return len > 0;
304}
305
306/* handy to printf prefixes of potentially very long buffers */
307const char *
d8b249ef 308getStringPrefix(const char *str, const char *end)
7faf2bdb 309{
d8b249ef 310#define SHORT_PREFIX_SIZE 512
7faf2bdb 311 LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE);
b644367b 312 const int sz = 1 + (end ? end - str : strlen(str));
d8b249ef 313 xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz);
7faf2bdb 314 return buf;
315}
b5107edb 316
317/*
318 * parses an int field, complains if soemthing went wrong, returns true on
319 * success
320 */
321int
322httpHeaderParseInt(const char *start, int *value)
323{
324 assert(value);
325 *value = atoi(start);
62e76326 326
b6a2f15e 327 if (!*value && !xisdigit(*start)) {
bf8fe701 328 debugs(66, 2, "failed to parse an int header field near '" << start << "'");
62e76326 329 return 0;
b5107edb 330 }
62e76326 331
b5107edb 332 return 1;
333}
334
335int
9bc73deb 336httpHeaderParseSize(const char *start, ssize_t * value)
b5107edb 337{
338 int v;
339 const int res = httpHeaderParseInt(start, &v);
340 assert(value);
341 *value = res ? v : 0;
342 return res;
343}
de336bbe 344
345
43ae1d95 346/* Parses a quoted-string field (RFC 2616 section 2.2), complains if
347 * something went wrong, returns non-zero on success.
348 * start should point at the first ".
349 * RC TODO: This is too looose. We should honour the BNF and exclude CTL's
350 */
351int
30abd221 352httpHeaderParseQuotedString (const char *start, String *val)
43ae1d95 353{
354 const char *end, *pos;
30abd221 355 val->clean();
43ae1d95 356 assert (*start == '"');
357 pos = start + 1;
358
359 while (1) {
360 if (!(end = index (pos,'"'))) {
bf8fe701 361 debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'");
43ae1d95 362 return 0;
363 }
364
365 /* check for quoted-chars */
366 if (*(end - 1) != '\\') {
367 /* done */
368 val->append(start + 1, end-start-1);
369 return 1;
370 }
371
372 /* try for the end again */
373 pos = end + 1;
374 }
375}
376
6bccf575 377/*
378 * httpHdrMangle checks the anonymizer (header_access) configuration.
379 * Returns 1 if the header is allowed.
380 */
2d72d4fd 381static int
8c01ada0 382httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep)
6bccf575 383{
e9b1e111 384 int retval;
385
6bccf575 386 /* check with anonymizer tables */
387 header_mangler *hm;
4fb35c3c 388 ACLChecklist *checklist;
6bccf575 389 assert(e);
8c01ada0 390
391 if (ROR_REQUEST == req_or_rep) {
392 hm = &Config.request_header_access[e->id];
393 } else if (ROR_REPLY == req_or_rep) {
394 hm = &Config.reply_header_access[e->id];
395 } else {
396 /* error. But let's call it "request". */
397 hm = &Config.request_header_access[e->id];
398 }
399
e9b1e111 400 checklist = aclChecklistCreate(hm->access_list, request, NULL);
62e76326 401
b448c119 402 if (1 == checklist->fastCheck()) {
62e76326 403 /* aclCheckFast returns 1 for allow. */
404 retval = 1;
a453662f 405 } else if (NULL == hm->replacement) {
62e76326 406 /* It was denied, and we don't have any replacement */
407 retval = 0;
a453662f 408 } else {
62e76326 409 /* It was denied, but we have a replacement. Replace the
410 * header on the fly, and return that the new header
411 * is allowed.
412 */
413 e->value = hm->replacement;
414 retval = 1;
a453662f 415 }
e9b1e111 416
8000a965 417 delete checklist;
e9b1e111 418 return retval;
6bccf575 419}
420
421/* Mangles headers for a list of headers. */
422void
8c01ada0 423httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep)
6bccf575 424{
425 HttpHeaderEntry *e;
426 HttpHeaderPos p = HttpHeaderInitPos;
62e76326 427
ba9fb01d 428 int headers_deleted = 0;
a9925b40 429 while ((e = l->getEntry(&p)))
8c01ada0 430 if (0 == httpHdrMangle(e, request, req_or_rep))
ba9fb01d 431 l->delAt(p, headers_deleted);
432
433 if (headers_deleted)
434 l->refreshMask();
6bccf575 435}
5967c0bf 436
437/*
438 * return 1 if manglers are configured. Used to set a flag
439 * for optimization during request forwarding.
440 */
441int
4f56514c 442httpReqHdrManglersConfigured()
5967c0bf 443{
444 for (int i = 0; i < HDR_ENUM_END; i++) {
445 if (NULL != Config.request_header_access[i].access_list)
446 return 1;
447 }
448
449 return 0;
450}