3 * $Id: HttpHeaderTools.cc,v 1.62 2007/08/13 17:20:51 hno Exp $
5 * DEBUG: section 66 HTTP Header Tools
6 * AUTHOR: Alex Rousskov
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
37 #include "HttpHeader.h"
38 #include "HttpHdrContRange.h"
39 #include "ACLChecklist.h"
42 static void httpHeaderPutStrvf(HttpHeader
* hdr
, http_hdr_type id
, const char *fmt
, va_list vargs
);
46 httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs
* attrs
, int count
)
49 HttpHeaderFieldInfo
*table
= NULL
;
50 assert(attrs
&& count
);
53 table
= new HttpHeaderFieldInfo
[count
];
55 for (i
= 0; i
< count
; ++i
) {
56 const http_hdr_type id
= attrs
[i
].id
;
57 HttpHeaderFieldInfo
*info
= table
+ id
;
59 assert(id
>= 0 && id
< count
);
60 assert(attrs
[i
].name
);
61 assert(info
->id
== HDR_ACCEPT
&& info
->type
== ftInvalid
); /* was not set before */
62 /* copy and init fields */
64 info
->type
= attrs
[i
].type
;
65 info
->name
= attrs
[i
].name
;
66 assert(info
->name
.size());
73 httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo
* table
, int count
)
77 for (i
= 0; i
< count
; ++i
)
78 table
[i
].name
.clean();
84 httpHeaderMaskInit(HttpHeaderMask
* mask
, int value
)
86 memset(mask
, value
, sizeof(*mask
));
89 /* calculates a bit mask of a given array; does not reset mask! */
91 httpHeaderCalcMask(HttpHeaderMask
* mask
, http_hdr_type http_hdr_type_enums
[], size_t count
)
94 const int * enums
= (const int *) http_hdr_type_enums
;
95 assert(mask
&& enums
);
96 assert(count
< sizeof(*mask
) * 8); /* check for overflow */
98 for (i
= 0; i
< count
; ++i
) {
99 assert(!CBIT_TEST(*mask
, enums
[i
])); /* check for duplicates */
100 CBIT_SET(*mask
, enums
[i
]);
104 /* same as httpHeaderPutStr, but formats the string using snprintf first */
107 httpHeaderPutStrf(HttpHeader
* hdr
, http_hdr_type id
, const char *fmt
,...)
109 httpHeaderPutStrf(va_alist
)
119 HttpHeader
*hdr
= NULL
;
120 http_hdr_type id
= HDR_ENUM_END
;
121 const char *fmt
= NULL
;
123 hdr
= va_arg(args
, HttpHeader
*);
124 id
= va_arg(args
, http_hdr_type
);
125 fmt
= va_arg(args
, char *);
128 httpHeaderPutStrvf(hdr
, id
, fmt
, args
);
132 /* used by httpHeaderPutStrf */
134 httpHeaderPutStrvf(HttpHeader
* hdr
, http_hdr_type id
, const char *fmt
, va_list vargs
)
138 mb
.vPrintf(fmt
, vargs
);
139 hdr
->putStr(id
, mb
.buf
);
144 /* wrapper arrounf PutContRange */
146 httpHeaderAddContRange(HttpHeader
* hdr
, HttpHdrRangeSpec spec
, int64_t ent_len
)
148 HttpHdrContRange
*cr
= httpHdrContRangeCreate();
149 assert(hdr
&& ent_len
>= 0);
150 httpHdrContRangeSet(cr
, spec
, ent_len
);
151 hdr
->putContRange(cr
);
152 httpHdrContRangeDestroy(cr
);
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.
162 httpHeaderHasConnDir(const HttpHeader
* hdr
, const char *directive
)
167 /* what type of header do we have? */
169 if (hdr
->has(HDR_PROXY_CONNECTION
))
170 ht
= HDR_PROXY_CONNECTION
;
171 else if (hdr
->has(HDR_CONNECTION
))
176 list
= hdr
->getList(ht
);
178 res
= strListIsMember(&list
, directive
, ',');
185 /* returns true iff "m" is a member of the list */
187 strListIsMember(const String
* list
, const char *m
, char del
)
189 const char *pos
= NULL
;
196 while (strListGetItem(list
, del
, &item
, &ilen
, &pos
)) {
197 if (mlen
== ilen
&& !strncasecmp(item
, m
, ilen
))
204 /* returns true iff "s" is a substring of a member of the list */
206 strListIsSubstr(const String
* list
, const char *s
, char del
)
209 return list
->pos(s
) != 0;
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.
220 /* appends an item to the list */
222 strListAdd(String
* str
, const char *item
, char del
)
234 str
->append(item
, strlen(item
));
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
242 * returns true if next item is found.
243 * init pos with NULL to start iteration.
246 strListGetItem(const String
* str
, char del
, const char **item
, int *ilen
, const char **pos
)
249 static char delim
[2][3] = {
253 assert(str
&& item
&& pos
);
258 if (!**pos
) /* end of string */
269 /* skip leading ws (ltrim) */
270 *pos
+= xcountws(*pos
);
272 *item
= *pos
; /* remember item's start */
274 /* find next delimiter */
276 *pos
+= strcspn(*pos
, delim
[quoted
]);
286 if (quoted
&& **pos
== '\\') {
294 len
= *pos
- *item
; /* *pos points to del or '\0' */
297 while (len
> 0 && xisspace((*item
)[len
- 1]))
306 /* handy to printf prefixes of potentially very long buffers */
308 getStringPrefix(const char *str
, const char *end
)
310 #define SHORT_PREFIX_SIZE 512
311 LOCAL_ARRAY(char, buf
, SHORT_PREFIX_SIZE
);
312 const int sz
= 1 + (end
? end
- str
: strlen(str
));
313 xstrncpy(buf
, str
, (sz
> SHORT_PREFIX_SIZE
) ? SHORT_PREFIX_SIZE
: sz
);
318 * parses an int field, complains if soemthing went wrong, returns true on
322 httpHeaderParseInt(const char *start
, int *value
)
325 *value
= atoi(start
);
327 if (!*value
&& !xisdigit(*start
)) {
328 debugs(66, 2, "failed to parse an int header field near '" << start
<< "'");
336 httpHeaderParseSize(const char *start
, ssize_t
* value
)
339 const int res
= httpHeaderParseInt(start
, &v
);
341 *value
= res
? v
: 0;
346 httpHeaderParseOffset(const char *start
, int64_t * value
)
348 int64_t res
= strtoll(start
, NULL
, 10);
349 if (!res
&& EINVAL
== errno
) /* maybe not portable? */
356 /* Parses a quoted-string field (RFC 2616 section 2.2), complains if
357 * something went wrong, returns non-zero on success.
358 * start should point at the first ".
359 * RC TODO: This is too looose. We should honour the BNF and exclude CTL's
362 httpHeaderParseQuotedString (const char *start
, String
*val
)
364 const char *end
, *pos
;
366 assert (*start
== '"');
370 if (!(end
= index (pos
,'"'))) {
371 debugs(66, 2, "failed to parse a quoted-string header field near '" << start
<< "'");
375 /* check for quoted-chars */
376 if (*(end
- 1) != '\\') {
378 val
->append(start
+ 1, end
-start
-1);
382 /* try for the end again */
388 * httpHdrMangle checks the anonymizer (header_access) configuration.
389 * Returns 1 if the header is allowed.
392 httpHdrMangle(HttpHeaderEntry
* e
, HttpRequest
* request
, int req_or_rep
)
396 /* check with anonymizer tables */
398 ACLChecklist
*checklist
;
401 if (ROR_REQUEST
== req_or_rep
) {
402 hm
= &Config
.request_header_access
[e
->id
];
403 } else if (ROR_REPLY
== req_or_rep
) {
404 hm
= &Config
.reply_header_access
[e
->id
];
406 /* error. But let's call it "request". */
407 hm
= &Config
.request_header_access
[e
->id
];
410 checklist
= aclChecklistCreate(hm
->access_list
, request
, NULL
);
412 if (1 == checklist
->fastCheck()) {
413 /* aclCheckFast returns 1 for allow. */
415 } else if (NULL
== hm
->replacement
) {
416 /* It was denied, and we don't have any replacement */
419 /* It was denied, but we have a replacement. Replace the
420 * header on the fly, and return that the new header
423 e
->value
= hm
->replacement
;
431 /* Mangles headers for a list of headers. */
433 httpHdrMangleList(HttpHeader
* l
, HttpRequest
* request
, int req_or_rep
)
436 HttpHeaderPos p
= HttpHeaderInitPos
;
438 int headers_deleted
= 0;
439 while ((e
= l
->getEntry(&p
)))
440 if (0 == httpHdrMangle(e
, request
, req_or_rep
))
441 l
->delAt(p
, headers_deleted
);
448 * return 1 if manglers are configured. Used to set a flag
449 * for optimization during request forwarding.
452 httpReqHdrManglersConfigured()
454 for (int i
= 0; i
< HDR_ENUM_END
; i
++) {
455 if (NULL
!= Config
.request_header_access
[i
].access_list
)