]>
Commit | Line | Data |
---|---|---|
7faf2bdb | 1 | /* |
99edd1c3 | 2 | * $Id: HttpHeaderTools.cc,v 1.11 1998/05/11 18:44:27 rousskov Exp $ |
7faf2bdb | 3 | * |
4 | * DEBUG: section 66 HTTP Header Tools | |
5 | * AUTHOR: Alex Rousskov | |
6 | * | |
7 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
8 | * -------------------------------------------------------- | |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from the | |
11 | * Internet community. Development is led by Duane Wessels of the | |
12 | * National Laboratory for Applied Network Research and funded by | |
13 | * the National Science Foundation. | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License as published by | |
17 | * the Free Software Foundation; either version 2 of the License, or | |
18 | * (at your option) any later version. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
26 | * along with this program; if not, write to the Free Software | |
27 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
28 | * | |
29 | */ | |
30 | ||
31 | #include "squid.h" | |
32 | ||
de336bbe | 33 | static int httpHeaderStrCmp(const char *h1, const char *h2, int len); |
34 | ||
35 | ||
36 | HttpHeaderFieldInfo * | |
b644367b | 37 | httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count) |
7faf2bdb | 38 | { |
39 | int i; | |
de336bbe | 40 | HttpHeaderFieldInfo *table = NULL; |
41 | assert(attrs && count); | |
42 | ||
43 | /* allocate space */ | |
44 | table = xcalloc(count, sizeof(HttpHeaderFieldInfo)); | |
7faf2bdb | 45 | |
7faf2bdb | 46 | for (i = 0; i < count; ++i) { |
de336bbe | 47 | const int id = attrs[i].id; |
48 | HttpHeaderFieldInfo *info = table + id; | |
49 | /* sanity checks */ | |
50 | assert(id >= 0 && id < count); | |
51 | assert(attrs[i].name); | |
b644367b | 52 | assert(info->id == 0 && info->type == 0); /* was not set before */ |
de336bbe | 53 | /* copy and init fields */ |
54 | info->id = id; | |
55 | info->type = attrs[i].type; | |
56 | stringInit(&info->name, attrs[i].name); | |
57 | assert(strLen(info->name)); | |
7faf2bdb | 58 | /* init stats */ |
de336bbe | 59 | memset(&info->stat, 0, sizeof(info->stat)); |
7faf2bdb | 60 | } |
de336bbe | 61 | return table; |
62 | } | |
63 | ||
64 | void | |
b644367b | 65 | httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count) |
de336bbe | 66 | { |
67 | int i; | |
68 | for (i = 0; i < count; ++i) | |
69 | stringClean(&table[i].name); | |
70 | xfree(table); | |
7faf2bdb | 71 | } |
72 | ||
d8b249ef | 73 | void |
b644367b | 74 | httpHeaderMaskInit(HttpHeaderMask * mask) |
d8b249ef | 75 | { |
76 | memset(mask, 0, sizeof(*mask)); | |
77 | } | |
78 | ||
99edd1c3 | 79 | /* calculates a bit mask of a given array; does not reset mask! */ |
d8b249ef | 80 | void |
b644367b | 81 | httpHeaderCalcMask(HttpHeaderMask * mask, const int *enums, int count) |
7faf2bdb | 82 | { |
83 | int i; | |
d8b249ef | 84 | assert(mask && enums); |
99edd1c3 | 85 | assert(count < sizeof(*mask) * 8); /* check for overflow */ |
7faf2bdb | 86 | |
87 | for (i = 0; i < count; ++i) { | |
d8b249ef | 88 | assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */ |
89 | CBIT_SET(*mask, enums[i]); | |
7faf2bdb | 90 | } |
7faf2bdb | 91 | } |
92 | ||
93 | ||
94 | int | |
b644367b | 95 | httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end) |
7faf2bdb | 96 | { |
97 | int i; | |
98 | for (i = 0; i < end; ++i) { | |
d8b249ef | 99 | if (name_len >= 0 && name_len != strLen(info[i].name)) |
100 | continue; | |
101 | if (!strncasecmp(name, strBuf(info[i].name), | |
b644367b | 102 | name_len < 0 ? strLen(info[i].name) + 1 : name_len)) |
103 | return i; | |
7faf2bdb | 104 | } |
105 | return -1; | |
106 | } | |
107 | ||
99edd1c3 | 108 | /* |
109 | * return true if a given directive is found in at least one of the "connection" header-fields | |
110 | * note: if HDR_PROXY_CONNECTION is present we ignore HDR_CONNECTION | |
111 | */ | |
112 | int | |
113 | httpHeaderHasConnDir(const HttpHeader *hdr, const char *directive) | |
114 | { | |
115 | if (httpHeaderHas(hdr, HDR_PROXY_CONNECTION)) { | |
116 | const char *str = httpHeaderGetStr(hdr, HDR_PROXY_CONNECTION); | |
117 | return str && !strcasecmp(str, directive); | |
118 | } | |
119 | if (httpHeaderHas(hdr, HDR_CONNECTION)) { | |
120 | String str = httpHeaderGetList(hdr, HDR_CONNECTION); | |
121 | const int res = strListIsMember(&str, directive, ','); | |
122 | stringClean(&str); | |
123 | return res; | |
124 | } | |
125 | return 0; | |
126 | } | |
127 | ||
128 | /* returns true iff "m" is a member of the list */ | |
129 | int | |
130 | strListIsMember(const String *list, const char *m, char del) | |
131 | { | |
132 | const char *pos = NULL; | |
133 | const char *item; | |
134 | assert(list && m); | |
135 | while (strListGetItem(list, del, &item, NULL, &pos)) { | |
136 | if (!strcasecmp(item, m)) | |
137 | return 1; | |
138 | } | |
139 | return 0; | |
140 | } | |
141 | ||
142 | /* appends an item to the list */ | |
143 | void | |
144 | strListAdd(String *str, const char *item, char del) | |
145 | { | |
146 | assert(str && item); | |
147 | if (strLen(*str)) | |
148 | stringAppend(str, &del, 1); | |
149 | stringAppend(str, item, strlen(item)); | |
150 | } | |
151 | ||
7faf2bdb | 152 | /* |
153 | * iterates through a 0-terminated string of items separated by 'del's. | |
154 | * white space around 'del' is considered to be a part of 'del' | |
155 | * like strtok, but preserves the source, and can iterate several strings at once | |
156 | * | |
157 | * returns true if next item is found. | |
158 | * init pos with NULL to start iteration. | |
159 | */ | |
160 | int | |
99edd1c3 | 161 | strListGetItem(const String *str, char del, const char **item, int *ilen, const char **pos) |
7faf2bdb | 162 | { |
163 | size_t len; | |
164 | assert(str && item && pos); | |
a3f9588e | 165 | if (*pos) { |
7faf2bdb | 166 | if (!**pos) /* end of string */ |
167 | return 0; | |
168 | else | |
169 | (*pos)++; | |
a3f9588e | 170 | } else { |
99edd1c3 | 171 | *pos = strBuf(*str); |
172 | if (!*pos) | |
173 | return 0; | |
a3f9588e | 174 | } |
7faf2bdb | 175 | |
176 | /* skip leading ws (ltrim) */ | |
177 | *pos += xcountws(*pos); | |
178 | *item = *pos; /* remember item's start */ | |
179 | /* find next delimiter */ | |
180 | *pos = strchr(*item, del); | |
181 | if (!*pos) /* last item */ | |
182 | *pos = *item + strlen(*item); | |
183 | len = *pos - *item; /* *pos points to del or '\0' */ | |
184 | /* rtrim */ | |
185 | while (len > 0 && isspace((*item)[len - 1])) | |
186 | len--; | |
187 | if (ilen) | |
188 | *ilen = len; | |
189 | return len > 0; | |
190 | } | |
191 | ||
192 | /* handy to printf prefixes of potentially very long buffers */ | |
193 | const char * | |
d8b249ef | 194 | getStringPrefix(const char *str, const char *end) |
7faf2bdb | 195 | { |
d8b249ef | 196 | #define SHORT_PREFIX_SIZE 512 |
7faf2bdb | 197 | LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE); |
b644367b | 198 | const int sz = 1 + (end ? end - str : strlen(str)); |
d8b249ef | 199 | xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz); |
7faf2bdb | 200 | return buf; |
201 | } | |
b5107edb | 202 | |
203 | /* | |
204 | * parses an int field, complains if soemthing went wrong, returns true on | |
205 | * success | |
206 | */ | |
207 | int | |
208 | httpHeaderParseInt(const char *start, int *value) | |
209 | { | |
210 | assert(value); | |
211 | *value = atoi(start); | |
212 | if (!*value && !isdigit(*start)) { | |
213 | debug(66, 2) ("failed to parse an int header field near '%s'\n", start); | |
b644367b | 214 | return 0; |
b5107edb | 215 | } |
216 | return 1; | |
217 | } | |
218 | ||
219 | int | |
b644367b | 220 | httpHeaderParseSize(const char *start, size_t * value) |
b5107edb | 221 | { |
222 | int v; | |
223 | const int res = httpHeaderParseInt(start, &v); | |
224 | assert(value); | |
225 | *value = res ? v : 0; | |
226 | return res; | |
227 | } | |
de336bbe | 228 | |
229 | ||
230 | /* | |
231 | * parses a given string then packs compiled headers and compares the result | |
232 | * with the original, reports discrepancies | |
233 | */ | |
4b4cd312 | 234 | void |
b644367b | 235 | httpHeaderTestParser(const char *hstr) |
de336bbe | 236 | { |
237 | static int bug_count = 0; | |
238 | int hstr_len; | |
239 | int parse_success; | |
240 | HttpHeader hdr; | |
241 | int pos; | |
242 | Packer p; | |
243 | MemBuf mb; | |
244 | assert(hstr); | |
de336bbe | 245 | /* skip start line if any */ |
246 | if (!strncasecmp(hstr, "HTTP/", 5)) { | |
247 | const char *p = strchr(hstr, '\n'); | |
248 | if (p) | |
b644367b | 249 | hstr = p + 1; |
de336bbe | 250 | } |
251 | /* skip invalid first line if any */ | |
252 | if (isspace(*hstr)) { | |
253 | const char *p = strchr(hstr, '\n'); | |
254 | if (p) | |
b644367b | 255 | hstr = p + 1; |
de336bbe | 256 | } |
257 | hstr_len = strlen(hstr); | |
258 | /* skip terminator if any */ | |
259 | if (strstr(hstr, "\n\r\n")) | |
260 | hstr_len -= 2; | |
b644367b | 261 | else if (strstr(hstr, "\n\n")) |
de336bbe | 262 | hstr_len -= 1; |
263 | httpHeaderInit(&hdr); | |
264 | /* debugLevels[55] = 8; */ | |
b644367b | 265 | parse_success = httpHeaderParse(&hdr, hstr, hstr + hstr_len); |
de336bbe | 266 | /* debugLevels[55] = 2; */ |
267 | if (!parse_success) { | |
d8b249ef | 268 | debug(66, 2) ("TEST (%d): failed to parsed a header: {\n%s}\n", bug_count, hstr); |
de336bbe | 269 | return; |
270 | } | |
271 | /* we think that we parsed it, veryfy */ | |
272 | memBufDefInit(&mb); | |
273 | packerToMemInit(&p, &mb); | |
274 | httpHeaderPackInto(&hdr, &p); | |
275 | if ((pos = abs(httpHeaderStrCmp(hstr, mb.buf, hstr_len)))) { | |
276 | bug_count++; | |
d8b249ef | 277 | debug(66, 2) ("TEST (%d): hdr parsing bug (pos: %d near '%s'): expected: {\n%s} got: {\n%s}\n", |
b644367b | 278 | bug_count, pos, hstr + pos, hstr, mb.buf); |
de336bbe | 279 | } |
280 | httpHeaderClean(&hdr); | |
281 | packerClean(&p); | |
282 | memBufClean(&mb); | |
283 | } | |
284 | ||
285 | ||
b644367b | 286 | /* like strncasecmp but ignores ws characters */ |
de336bbe | 287 | static int |
288 | httpHeaderStrCmp(const char *h1, const char *h2, int len) | |
289 | { | |
290 | int len1 = 0; | |
291 | int len2 = 0; | |
292 | assert(h1 && h2); | |
293 | /* fast check first */ | |
294 | if (!strncasecmp(h1, h2, len)) | |
295 | return 0; | |
296 | while (1) { | |
297 | const char c1 = toupper(h1[len1 += xcountws(h1 + len1)]); | |
298 | const char c2 = toupper(h2[len2 += xcountws(h2 + len2)]); | |
b644367b | 299 | if (c1 < c2) |
300 | return -len1; | |
301 | if (c1 > c2) | |
302 | return +len1; | |
de336bbe | 303 | if (!c1 && !c2) |
304 | return 0; | |
b644367b | 305 | if (c1) |
306 | len1++; | |
307 | if (c2) | |
308 | len2++; | |
de336bbe | 309 | } |
310 | return 0; | |
311 | } |