4 * DEBUG: section 66 HTTP Header Tools
5 * AUTHOR: Alex Rousskov
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36 #include "acl/FilledChecklist.h"
37 #include "compat/strtoll.h"
38 #include "HttpHdrContRange.h"
39 #include "HttpHeader.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 */
106 httpHeaderPutStrf(HttpHeader
* hdr
, http_hdr_type id
, const char *fmt
,...)
111 httpHeaderPutStrvf(hdr
, id
, fmt
, args
);
115 /* used by httpHeaderPutStrf */
117 httpHeaderPutStrvf(HttpHeader
* hdr
, http_hdr_type id
, const char *fmt
, va_list vargs
)
121 mb
.vPrintf(fmt
, vargs
);
122 hdr
->putStr(id
, mb
.buf
);
127 /** wrapper arrounf PutContRange */
129 httpHeaderAddContRange(HttpHeader
* hdr
, HttpHdrRangeSpec spec
, int64_t ent_len
)
131 HttpHdrContRange
*cr
= httpHdrContRangeCreate();
132 assert(hdr
&& ent_len
>= 0);
133 httpHdrContRangeSet(cr
, spec
, ent_len
);
134 hdr
->putContRange(cr
);
135 httpHdrContRangeDestroy(cr
);
140 * return true if a given directive is found in at least one of
141 * the "connection" header-fields note: if HDR_PROXY_CONNECTION is
142 * present we ignore HDR_CONNECTION.
145 httpHeaderHasConnDir(const HttpHeader
* hdr
, const char *directive
)
150 /* what type of header do we have? */
152 if (hdr
->has(HDR_PROXY_CONNECTION
))
153 ht
= HDR_PROXY_CONNECTION
;
154 else if (hdr
->has(HDR_CONNECTION
))
159 list
= hdr
->getList(ht
);
161 res
= strListIsMember(&list
, directive
, ',');
168 /** returns true iff "m" is a member of the list */
170 strListIsMember(const String
* list
, const char *m
, char del
)
172 const char *pos
= NULL
;
179 while (strListGetItem(list
, del
, &item
, &ilen
, &pos
)) {
180 if (mlen
== ilen
&& !strncasecmp(item
, m
, ilen
))
187 /** returns true iff "s" is a substring of a member of the list */
189 strListIsSubstr(const String
* list
, const char *s
, char del
)
192 return (list
->find(s
) != String::npos
);
195 * Note: the original code with a loop is broken because it uses strstr()
196 * instead of strnstr(). If 's' contains a 'del', strListIsSubstr() may
197 * return true when it should not. If 's' does not contain a 'del', the
198 * implementaion is equavalent to strstr()! Thus, we replace the loop with
199 * strstr() above until strnstr() is available.
203 /** appends an item to the list */
205 strListAdd(String
* str
, const char *item
, char del
)
217 str
->append(item
, strlen(item
));
221 * iterates through a 0-terminated string of items separated by 'del's.
222 * white space around 'del' is considered to be a part of 'del'
223 * like strtok, but preserves the source, and can iterate several strings at once
225 * returns true if next item is found.
226 * init pos with NULL to start iteration.
229 strListGetItem(const String
* str
, char del
, const char **item
, int *ilen
, const char **pos
)
232 /* ',' is always enabled as field delimiter as this is required for
233 * processing merged header values properly, even if Cookie normally
234 * uses ';' as delimiter.
236 static char delim
[3][8] = {
242 assert(str
&& item
&& pos
);
248 *pos
= str
->termedBuf();
254 /* skip leading whitespace and delimiters */
255 *pos
+= strspn(*pos
, delim
[2]);
257 *item
= *pos
; /* remember item's start */
259 /* find next delimiter */
261 *pos
+= strcspn(*pos
, delim
[quoted
]);
265 } else if (quoted
&& **pos
== '\\') {
270 break; /* Delimiter found, marking the end of this value */
274 len
= *pos
- *item
; /* *pos points to del or '\0' */
277 while (len
> 0 && xisspace((*item
)[len
- 1]))
286 /** handy to printf prefixes of potentially very long buffers */
288 getStringPrefix(const char *str
, const char *end
)
290 #define SHORT_PREFIX_SIZE 512
291 LOCAL_ARRAY(char, buf
, SHORT_PREFIX_SIZE
);
292 const int sz
= 1 + (end
? end
- str
: strlen(str
));
293 xstrncpy(buf
, str
, (sz
> SHORT_PREFIX_SIZE
) ? SHORT_PREFIX_SIZE
: sz
);
298 * parses an int field, complains if soemthing went wrong, returns true on
302 httpHeaderParseInt(const char *start
, int *value
)
305 *value
= atoi(start
);
307 if (!*value
&& !xisdigit(*start
)) {
308 debugs(66, 2, "failed to parse an int header field near '" << start
<< "'");
316 httpHeaderParseOffset(const char *start
, int64_t * value
)
319 int64_t res
= strtoll(start
, NULL
, 10);
320 if (!res
&& EINVAL
== errno
) /* maybe not portable? */
328 * Parses a quoted-string field (RFC 2616 section 2.2), complains if
329 * something went wrong, returns non-zero on success.
330 * start should point at the first double-quote.
331 * RC TODO: This is too looose. We should honour the BNF and exclude CTL's
334 httpHeaderParseQuotedString(const char *start
, String
*val
)
336 const char *end
, *pos
;
339 debugs(66, 2, "failed to parse a quoted-string header field near '" << start
<< "'");
344 while (*pos
!= '"') {
345 bool quoted
= (*pos
== '\\');
349 debugs(66, 2, "failed to parse a quoted-string header field near '" << start
<< "'");
353 end
= pos
+ strcspn(pos
+ quoted
, "\"\\") + quoted
;
354 val
->append(pos
, end
-pos
);
357 /* Make sure it's defined even if empty "" */
359 val
->limitInit("", 0);
364 * Checks the anonymizer (header_access) configuration.
366 * \retval 0 Header is explicitly blocked for removal
367 * \retval 1 Header is explicitly allowed
368 * \retval 1 Header has been replaced, the current version can be used.
369 * \retval 1 Header has no access controls to test
372 httpHdrMangle(HttpHeaderEntry
* e
, HttpRequest
* request
, int req_or_rep
)
376 /* check with anonymizer tables */
380 if (ROR_REQUEST
== req_or_rep
) {
381 hm
= &Config
.request_header_access
[e
->id
];
382 } else if (ROR_REPLY
== req_or_rep
) {
383 hm
= &Config
.reply_header_access
[e
->id
];
385 /* error. But let's call it "request". */
386 hm
= &Config
.request_header_access
[e
->id
];
389 /* mangler or checklist went away. default allow */
390 if (!hm
|| !hm
->access_list
) {
394 ACLFilledChecklist
checklist(hm
->access_list
, request
, NULL
);
396 if (checklist
.fastCheck()) {
397 /* aclCheckFast returns true for allow. */
399 } else if (NULL
== hm
->replacement
) {
400 /* It was denied, and we don't have any replacement */
403 /* It was denied, but we have a replacement. Replace the
404 * header on the fly, and return that the new header
407 e
->value
= hm
->replacement
;
414 /** Mangles headers for a list of headers. */
416 httpHdrMangleList(HttpHeader
* l
, HttpRequest
* request
, int req_or_rep
)
419 HttpHeaderPos p
= HttpHeaderInitPos
;
421 int headers_deleted
= 0;
422 while ((e
= l
->getEntry(&p
)))
423 if (0 == httpHdrMangle(e
, request
, req_or_rep
))
424 l
->delAt(p
, headers_deleted
);
431 * return 1 if manglers are configured. Used to set a flag
432 * for optimization during request forwarding.
435 httpReqHdrManglersConfigured()
437 for (int i
= 0; i
< HDR_ENUM_END
; i
++) {
438 if (NULL
!= Config
.request_header_access
[i
].access_list
)