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 */
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
->pos(s
) != 0;
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 static char delim
[3][8] = {
238 assert(str
&& item
&& pos
);
244 *pos
= str
->termedBuf();
250 /* skip leading ws and delimiters */
251 *pos
+= strspn(*pos
, delim
[2]);
253 *item
= *pos
; /* remember item's start */
255 /* find next delimiter */
257 *pos
+= strcspn(*pos
, delim
[quoted
]);
267 if (quoted
&& **pos
== '\\') {
275 len
= *pos
- *item
; /* *pos points to del or '\0' */
278 while (len
> 0 && xisspace((*item
)[len
- 1]))
287 /** handy to printf prefixes of potentially very long buffers */
289 getStringPrefix(const char *str
, const char *end
)
291 #define SHORT_PREFIX_SIZE 512
292 LOCAL_ARRAY(char, buf
, SHORT_PREFIX_SIZE
);
293 const int sz
= 1 + (end
? end
- str
: strlen(str
));
294 xstrncpy(buf
, str
, (sz
> SHORT_PREFIX_SIZE
) ? SHORT_PREFIX_SIZE
: sz
);
299 * parses an int field, complains if soemthing went wrong, returns true on
303 httpHeaderParseInt(const char *start
, int *value
)
306 *value
= atoi(start
);
308 if (!*value
&& !xisdigit(*start
)) {
309 debugs(66, 2, "failed to parse an int header field near '" << start
<< "'");
317 httpHeaderParseOffset(const char *start
, int64_t * value
)
320 int64_t res
= strtoll(start
, NULL
, 10);
321 if (!res
&& EINVAL
== errno
) /* maybe not portable? */
329 * Parses a quoted-string field (RFC 2616 section 2.2), complains if
330 * something went wrong, returns non-zero on success.
331 * start should point at the first ".
332 * RC TODO: This is too looose. We should honour the BNF and exclude CTL's
335 httpHeaderParseQuotedString (const char *start
, String
*val
)
337 const char *end
, *pos
;
339 assert (*start
== '"');
343 if (!(end
= index (pos
,'"'))) {
344 debugs(66, 2, "failed to parse a quoted-string header field near '" << start
<< "'");
348 /* check for quoted-chars */
349 if (*(end
- 1) != '\\') {
351 val
->append(start
+ 1, end
-start
-1);
355 /* try for the end again */
361 * Checks the anonymizer (header_access) configuration.
363 * \retval 0 Header is explicitly blocked for removal
364 * \retval 1 Header is explicitly allowed
365 * \retval 1 Header has been replaced, the current version can be used.
366 * \retval 1 Header has no access controls to test
369 httpHdrMangle(HttpHeaderEntry
* e
, HttpRequest
* request
, int req_or_rep
)
373 /* check with anonymizer tables */
375 ACLChecklist
*checklist
;
378 if (ROR_REQUEST
== req_or_rep
) {
379 hm
= &Config
.request_header_access
[e
->id
];
380 } else if (ROR_REPLY
== req_or_rep
) {
381 hm
= &Config
.reply_header_access
[e
->id
];
383 /* error. But let's call it "request". */
384 hm
= &Config
.request_header_access
[e
->id
];
387 /* mangler or checklist went away. default allow */
388 if(!hm
|| !hm
->access_list
) {
392 checklist
= aclChecklistCreate(hm
->access_list
, request
, NULL
);
394 if (checklist
->fastCheck()) {
395 /* aclCheckFast returns true for allow. */
397 } else if (NULL
== hm
->replacement
) {
398 /* It was denied, and we don't have any replacement */
401 /* It was denied, but we have a replacement. Replace the
402 * header on the fly, and return that the new header
405 e
->value
= hm
->replacement
;
413 /** Mangles headers for a list of headers. */
415 httpHdrMangleList(HttpHeader
* l
, HttpRequest
* request
, int req_or_rep
)
418 HttpHeaderPos p
= HttpHeaderInitPos
;
420 int headers_deleted
= 0;
421 while ((e
= l
->getEntry(&p
)))
422 if (0 == httpHdrMangle(e
, request
, req_or_rep
))
423 l
->delAt(p
, headers_deleted
);
430 * return 1 if manglers are configured. Used to set a flag
431 * for optimization during request forwarding.
434 httpReqHdrManglersConfigured()
436 for (int i
= 0; i
< HDR_ENUM_END
; i
++) {
437 if (NULL
!= Config
.request_header_access
[i
].access_list
)