]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrRange.cc
gindent
[thirdparty/squid.git] / src / HttpHdrRange.cc
1
2 /*
3 * $Id: HttpHdrRange.cc,v 1.6 1998/04/06 22:32:07 wessels Exp $
4 *
5 * DEBUG: section 64 HTTP Range Header
6 * AUTHOR: Alex Rousskov
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
31
32 #include "squid.h"
33
34 /*
35 * Currently only byte ranges are supported
36 *
37 * Essentially, there are three types of byte ranges:
38 *
39 * 1) first-byte-pos "-" last-byte-pos // range
40 * 2) first-byte-pos "-" // trailer
41 * 3) "-" suffix-length // suffix (last length bytes)
42 *
43 *
44 * When Range field is parsed, we have no clue about the content
45 * length of the document. Thus, we simply code an "absent" part
46 * using range_spec_unknown constant.
47 *
48 * Note: when response length becomes known, we convert any range
49 * spec into type one above. (Canonization process).
50 */
51
52
53 /* local constants */
54 #define range_spec_unknown ((size_t)-1)
55
56 /* local routines */
57 #define known_spec(s) ((s) != range_spec_unknown)
58 #define size_min(a,b) ((a) <= (b) ? (a) : (b))
59 #define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0)
60 static HttpHdrRangeSpec *httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec);
61 static int httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen);
62 static void httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p);
63
64 /* globals */
65 static int RangeParsedCount = 0;
66
67 /*
68 * Range-Spec
69 */
70
71 static HttpHdrRangeSpec *
72 httpHdrRangeSpecCreate()
73 {
74 return memAllocate(MEM_HTTP_HDR_RANGE_SPEC);
75 }
76
77 /* parses range-spec and returns new object on success */
78 static HttpHdrRangeSpec *
79 httpHdrRangeSpecParseCreate(const char *field, int flen)
80 {
81 HttpHdrRangeSpec spec =
82 {range_spec_unknown, range_spec_unknown};
83 const char *p;
84 if (flen < 2)
85 return NULL;
86 /* is it a suffix-byte-range-spec ? */
87 if (*field == '-') {
88 if (!httpHeaderParseSize(field + 1, &spec.length))
89 return NULL;
90 } else
91 /* must have a '-' somewhere in _this_ field */
92 if (!((p = strchr(field, '-')) || (p - field >= flen))) {
93 debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'\n", field);
94 return NULL;
95 } else {
96 if (!httpHeaderParseSize(field, &spec.offset))
97 return NULL;
98 p++;
99 /* do we have last-pos ? */
100 if (p - field < flen) {
101 size_t last_pos;
102 if (!httpHeaderParseSize(p, &last_pos))
103 return NULL;
104 spec.length = size_diff(last_pos + 1, spec.offset);
105 }
106 }
107 /* we managed to parse, check if the result makes sence */
108 if (known_spec(spec.length) && !spec.length) {
109 debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'\n", field);
110 return NULL;
111 }
112 return httpHdrRangeSpecDup(&spec);
113 }
114
115 static void
116 httpHdrRangeSpecDestroy(HttpHdrRangeSpec * spec)
117 {
118 memFree(MEM_HTTP_HDR_RANGE_SPEC, spec);
119 }
120
121
122 static HttpHdrRangeSpec *
123 httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec)
124 {
125 HttpHdrRangeSpec *dup = httpHdrRangeSpecCreate();
126 dup->offset = spec->offset;
127 dup->length = spec->length;
128 return dup;
129 }
130
131 static void
132 httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p)
133 {
134 if (!known_spec(spec->offset)) /* suffix */
135 packerPrintf(p, "-%d", spec->length);
136 else if (!known_spec(spec->length)) /* trailer */
137 packerPrintf(p, "%d-", spec->offset);
138 else /* range */
139 packerPrintf(p, "%d-%d",
140 spec->offset, spec->offset + spec->length - 1);
141 }
142
143 /* fills "absent" positions in range specification based on response body size
144 * returns true if the range is still valid
145 * range is valid if its intersection with [0,length-1] is not empty
146 */
147 static int
148 httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen)
149 {
150 if (!known_spec(spec->offset)) /* suffix */
151 spec->offset = size_diff(clen, spec->length);
152 else if (!known_spec(spec->length)) /* trailer */
153 spec->length = size_diff(clen, spec->offset);
154 /* we have a "range" now, adjust length if needed */
155 assert(known_spec(spec->length));
156 assert(known_spec(spec->offset));
157 spec->length = size_min(size_diff(clen, spec->offset), spec->length);
158 /* check range validity */
159 return spec->length > 0;
160 }
161
162 /*
163 * Range
164 */
165
166 HttpHdrRange *
167 httpHdrRangeCreate()
168 {
169 HttpHdrRange *r = memAllocate(MEM_HTTP_HDR_RANGE);
170 stackInit(&r->specs);
171 return r;
172 }
173
174 HttpHdrRange *
175 httpHdrRangeParseCreate(const char *str)
176 {
177 HttpHdrRange *r = httpHdrRangeCreate();
178 if (!httpHdrRangeParseInit(r, str)) {
179 httpHdrRangeDestroy(r);
180 r = NULL;
181 }
182 return r;
183 }
184
185 /* returns true if ranges are valid; inits HttpHdrRange */
186 int
187 httpHdrRangeParseInit(HttpHdrRange * range, const char *str)
188 {
189 const char *item;
190 const char *pos = NULL;
191 int ilen;
192 assert(range && str);
193 RangeParsedCount++;
194 debug(64, 8) ("parsing range field: '%s'\n", str);
195 /* check range type */
196 if (strncasecmp(str, "bytes=", 6))
197 return 0;
198 str += 6;
199 /* iterate through comma separated list */
200 while (strListGetItem(str, ',', &item, &ilen, &pos)) {
201 HttpHdrRangeSpec *spec = httpHdrRangeSpecParseCreate(item, ilen);
202 /*
203 * HTTP/1.1 draft says we must ignore the whole header field if one spec
204 * is invalid. However, RFC 2068 just says that we must ignore that spec.
205 */
206 if (spec)
207 stackPush(&range->specs, spec);
208 }
209 debug(68, 8) ("parsed range range count: %d\n", range->specs.count);
210 return range->specs.count;
211 }
212
213 void
214 httpHdrRangeDestroy(HttpHdrRange * range)
215 {
216 assert(range);
217 while (range->specs.count)
218 httpHdrRangeSpecDestroy(stackPop(&range->specs));
219 stackClean(&range->specs);
220 memFree(MEM_HTTP_HDR_RANGE, range);
221 }
222
223 HttpHdrRange *
224 httpHdrRangeDup(const HttpHdrRange * range)
225 {
226 HttpHdrRange *dup;
227 int i;
228 assert(range);
229 dup = httpHdrRangeCreate();
230 stackPrePush(&dup->specs, range->specs.count);
231 for (i = 0; i < range->specs.count; i++)
232 stackPush(&dup->specs, httpHdrRangeSpecDup(range->specs.items[i]));
233 assert(range->specs.count == dup->specs.count);
234 return dup;
235 }
236
237 void
238 httpHdrRangePackInto(const HttpHdrRange * range, Packer * p)
239 {
240 HttpHdrRangePos pos = HttpHdrRangeInitPos;
241 HttpHdrRangeSpec spec;
242 assert(range);
243 while (httpHdrRangeGetSpec(range, &spec, &pos)) {
244 if (pos != HttpHdrRangeInitPos)
245 packerAppend(p, ",", 1);
246 httpHdrRangeSpecPackInto(&spec, p);
247 }
248 }
249
250 /*
251 * canonizes all range specs within a set preserving the order
252 * returns true if the set is valid after canonization;
253 * the set is valid if
254 * - all range specs are valid and
255 * - there is at least one range spec
256 */
257 int
258 httpHdrRangeCanonize(HttpHdrRange * range, size_t clen)
259 {
260 int i;
261 assert(range);
262 for (i = 0; i < range->specs.count; i++)
263 if (!httpHdrRangeSpecCanonize(range->specs.items[i], clen))
264 return 0;
265 return range->specs.count;
266 }
267
268 /* searches for next range, returns true if found */
269 int
270 httpHdrRangeGetSpec(const HttpHdrRange * range, HttpHdrRangeSpec * spec, int *pos)
271 {
272 assert(range && spec);
273 assert(pos && *pos >= -1 && *pos < range->specs.count);
274 (*pos)++;
275 if (*pos < range->specs.count) {
276 *spec = *(HttpHdrRangeSpec *) range->specs.items[*pos];
277 return 1;
278 }
279 spec->offset = spec->length = 0;
280 return 0;
281 }