]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrRange.cc
3 * $Id: HttpHdrRange.cc,v 1.14 1998/09/24 20:16:24 rousskov Exp $
5 * DEBUG: section 64 HTTP Range Header
6 * AUTHOR: Alex Rousskov
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
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 the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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.
39 * Currently only byte ranges are supported
41 * Essentially, there are three types of byte ranges:
43 * 1) first-byte-pos "-" last-byte-pos // range
44 * 2) first-byte-pos "-" // trailer
45 * 3) "-" suffix-length // suffix (last length bytes)
48 * When Range field is parsed, we have no clue about the content
49 * length of the document. Thus, we simply code an "absent" part
50 * using range_spec_unknown constant.
52 * Note: when response length becomes known, we convert any range
53 * spec into type one above. (Canonization process).
58 #define range_spec_unknown ((size_t)-1)
61 #define known_spec(s) ((s) != range_spec_unknown)
62 #define size_min(a,b) ((a) <= (b) ? (a) : (b))
63 #define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0)
64 static HttpHdrRangeSpec
*httpHdrRangeSpecDup(const HttpHdrRangeSpec
* spec
);
65 static int httpHdrRangeSpecCanonize(HttpHdrRangeSpec
* spec
, size_t clen
);
66 static void httpHdrRangeSpecPackInto(const HttpHdrRangeSpec
* spec
, Packer
* p
);
69 static int RangeParsedCount
= 0;
75 static HttpHdrRangeSpec
*
76 httpHdrRangeSpecCreate()
78 return memAllocate(MEM_HTTP_HDR_RANGE_SPEC
);
81 /* parses range-spec and returns new object on success */
82 static HttpHdrRangeSpec
*
83 httpHdrRangeSpecParseCreate(const char *field
, int flen
)
85 HttpHdrRangeSpec spec
=
86 {range_spec_unknown
, range_spec_unknown
};
90 /* is it a suffix-byte-range-spec ? */
92 if (!httpHeaderParseSize(field
+ 1, &spec
.length
))
95 /* must have a '-' somewhere in _this_ field */
96 if (!((p
= strchr(field
, '-')) || (p
- field
>= flen
))) {
97 debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'\n", field
);
100 if (!httpHeaderParseSize(field
, &spec
.offset
))
103 /* do we have last-pos ? */
104 if (p
- field
< flen
) {
106 if (!httpHeaderParseSize(p
, &last_pos
))
108 spec
.length
= size_diff(last_pos
+ 1, spec
.offset
);
111 /* we managed to parse, check if the result makes sence */
112 if (known_spec(spec
.length
) && !spec
.length
) {
113 debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'\n", field
);
116 return httpHdrRangeSpecDup(&spec
);
120 httpHdrRangeSpecDestroy(HttpHdrRangeSpec
* spec
)
122 memFree(MEM_HTTP_HDR_RANGE_SPEC
, spec
);
126 static HttpHdrRangeSpec
*
127 httpHdrRangeSpecDup(const HttpHdrRangeSpec
* spec
)
129 HttpHdrRangeSpec
*dup
= httpHdrRangeSpecCreate();
130 dup
->offset
= spec
->offset
;
131 dup
->length
= spec
->length
;
136 httpHdrRangeSpecPackInto(const HttpHdrRangeSpec
* spec
, Packer
* p
)
138 if (!known_spec(spec
->offset
)) /* suffix */
139 packerPrintf(p
, "-%d", spec
->length
);
140 else if (!known_spec(spec
->length
)) /* trailer */
141 packerPrintf(p
, "%d-", spec
->offset
);
143 packerPrintf(p
, "%d-%d",
144 spec
->offset
, spec
->offset
+ spec
->length
- 1);
147 /* fills "absent" positions in range specification based on response body size
148 * returns true if the range is still valid
149 * range is valid if its intersection with [0,length-1] is not empty
152 httpHdrRangeSpecCanonize(HttpHdrRangeSpec
* spec
, size_t clen
)
154 debug(64, 5) ("httpHdrRangeSpecCanonize: have: [%d, %d) len: %d\n",
155 spec
->offset
, spec
->offset
+ spec
->length
, spec
->length
);
156 if (!known_spec(spec
->offset
)) /* suffix */
157 spec
->offset
= size_diff(clen
, spec
->length
);
158 else if (!known_spec(spec
->length
)) /* trailer */
159 spec
->length
= size_diff(clen
, spec
->offset
);
160 /* we have a "range" now, adjust length if needed */
161 assert(known_spec(spec
->length
));
162 assert(known_spec(spec
->offset
));
163 spec
->length
= size_min(size_diff(clen
, spec
->offset
), spec
->length
);
164 /* check range validity */
165 debug(64, 5) ("httpHdrRangeSpecCanonize: done: [%d, %d) len: %d\n",
166 spec
->offset
, spec
->offset
+ spec
->length
, spec
->length
);
167 return spec
->length
> 0;
170 /* merges recepient with donor if possible; returns true on success
171 * both specs must be canonized prior to merger, of course */
173 httpHdrRangeSpecMergeWith(HttpHdrRangeSpec
* recep
, const HttpHdrRangeSpec
* donor
)
176 #if MERGING_BREAKS_NOTHING
177 /* Note: this code works, but some clients may not like its effects */
178 size_t rhs
= recep
->offset
+ recep
->length
; /* no -1 ! */
179 const size_t donor_rhs
= donor
->offset
+ donor
->length
; /* no -1 ! */
180 assert(known_spec(recep
->offset
));
181 assert(known_spec(donor
->offset
));
182 assert(recep
->length
> 0);
183 assert(donor
->length
> 0);
184 /* do we have a left hand side overlap? */
185 if (donor
->offset
< recep
->offset
&& recep
->offset
<= donor_rhs
) {
186 recep
->offset
= donor
->offset
; /* decrease left offset */
189 /* do we have a right hand side overlap? */
190 if (donor
->offset
<= rhs
&& rhs
< donor_rhs
) {
191 rhs
= donor_rhs
; /* increase right offset */
194 /* adjust length if offsets have been changed */
196 assert(rhs
> recep
->offset
);
197 recep
->length
= rhs
- recep
->offset
;
199 /* does recepient contain donor? */
201 recep
->offset
<= donor
->offset
&& donor
->offset
< rhs
;
214 HttpHdrRange
*r
= memAllocate(MEM_HTTP_HDR_RANGE
);
215 stackInit(&r
->specs
);
220 httpHdrRangeParseCreate(const String
* str
)
222 HttpHdrRange
*r
= httpHdrRangeCreate();
223 if (!httpHdrRangeParseInit(r
, str
)) {
224 httpHdrRangeDestroy(r
);
230 /* returns true if ranges are valid; inits HttpHdrRange */
232 httpHdrRangeParseInit(HttpHdrRange
* range
, const String
* str
)
235 const char *pos
= NULL
;
238 assert(range
&& str
);
240 debug(64, 8) ("parsing range field: '%s'\n", strBuf(*str
));
241 /* check range type */
242 if (strNCaseCmp(*str
, "bytes=", 6))
244 /* skip "bytes="; hack! */
245 pos
= strBuf(*str
) + 5;
246 /* iterate through comma separated list */
247 while (strListGetItem(str
, ',', &item
, &ilen
, &pos
)) {
248 HttpHdrRangeSpec
*spec
= httpHdrRangeSpecParseCreate(item
, ilen
);
250 * HTTP/1.1 draft says we must ignore the whole header field if one spec
251 * is invalid. However, RFC 2068 just says that we must ignore that spec.
254 stackPush(&range
->specs
, spec
);
257 debug(64, 8) ("parsed range range count: %d\n", range
->specs
.count
);
258 return range
->specs
.count
;
262 httpHdrRangeDestroy(HttpHdrRange
* range
)
265 while (range
->specs
.count
)
266 httpHdrRangeSpecDestroy(stackPop(&range
->specs
));
267 stackClean(&range
->specs
);
268 memFree(MEM_HTTP_HDR_RANGE
, range
);
272 httpHdrRangeDup(const HttpHdrRange
* range
)
277 dup
= httpHdrRangeCreate();
278 stackPrePush(&dup
->specs
, range
->specs
.count
);
279 for (i
= 0; i
< range
->specs
.count
; i
++)
280 stackPush(&dup
->specs
, httpHdrRangeSpecDup(range
->specs
.items
[i
]));
281 assert(range
->specs
.count
== dup
->specs
.count
);
286 httpHdrRangePackInto(const HttpHdrRange
* range
, Packer
* p
)
288 HttpHdrRangePos pos
= HttpHdrRangeInitPos
;
289 const HttpHdrRangeSpec
*spec
;
291 while ((spec
= httpHdrRangeGetSpec(range
, &pos
))) {
292 if (pos
!= HttpHdrRangeInitPos
)
293 packerAppend(p
, ",", 1);
294 httpHdrRangeSpecPackInto(spec
, p
);
299 * canonizes all range specs within a set preserving the order
300 * returns true if the set is valid after canonization;
301 * the set is valid if
302 * - all range specs are valid and
303 * - there is at least one range spec
306 httpHdrRangeCanonize(HttpHdrRange
* range
, size_t clen
)
309 HttpHdrRangeSpec
*spec
;
310 HttpHdrRangePos pos
= HttpHdrRangeInitPos
;
315 debug(64, 3) ("httpHdrRangeCanonize: started with %d specs, clen: %d\n", range
->specs
.count
, clen
);
317 /* canonize each entry and destroy bad ones if any */
318 while ((spec
= httpHdrRangeGetSpec(range
, &pos
))) {
319 if (httpHdrRangeSpecCanonize(spec
, clen
))
320 stackPush(&goods
, spec
);
322 httpHdrRangeSpecDestroy(spec
);
324 debug(64, 3) ("httpHdrRangeCanonize: found %d bad specs\n",
325 range
->specs
.count
- goods
.count
);
326 /* reset old array */
327 stackClean(&range
->specs
);
328 stackInit(&range
->specs
);
331 * take one spec from "goods" and merge it with specs from
332 * "range->specs" (if any) until there is no overlap */
333 for (i
= 0; i
< goods
.count
;) {
334 HttpHdrRangeSpec
*prev_spec
= stackTop(&range
->specs
);
335 spec
= goods
.items
[i
];
337 if (httpHdrRangeSpecMergeWith(spec
, prev_spec
)) {
338 /* merged with current so get rid of the prev one */
339 assert(prev_spec
== stackPop(&range
->specs
));
340 httpHdrRangeSpecDestroy(prev_spec
);
341 continue; /* re-iterate */
344 stackPush(&range
->specs
, spec
);
348 if (spec
) /* last "merge" may not be pushed yet */
349 stackPush(&range
->specs
, spec
);
350 debug(64, 3) ("httpHdrRangeCanonize: had %d specs, merged %d specs\n",
351 goods
.count
, goods
.count
- range
->specs
.count
);
352 debug(64, 3) ("httpHdrRangeCanonize: finished with %d specs\n",
355 return range
->specs
.count
> 0;
358 /* searches for next range, returns true if found */
360 httpHdrRangeGetSpec(const HttpHdrRange
* range
, HttpHdrRangePos
* pos
)
363 assert(pos
&& *pos
>= -1 && *pos
< range
->specs
.count
);
365 if (*pos
< range
->specs
.count
)
366 return (HttpHdrRangeSpec
*) range
->specs
.items
[*pos
];
371 /* hack: returns true if range specs are too "complex" for Squid to handle */
372 /* requires that specs are "canonized" first! */
374 httpHdrRangeIsComplex(const HttpHdrRange
* range
)
376 HttpHdrRangePos pos
= HttpHdrRangeInitPos
;
377 const HttpHdrRangeSpec
*spec
;
380 /* check that all rangers are in "strong" order */
381 while ((spec
= httpHdrRangeGetSpec(range
, &pos
))) {
382 if (spec
->offset
< offset
)
384 offset
= spec
->offset
+ spec
->length
;
389 /* hack: returns true if range specs may be too "complex" when "canonized" */
390 /* see also: httpHdrRangeIsComplex */
392 httpHdrRangeWillBeComplex(const HttpHdrRange
* range
)
394 HttpHdrRangePos pos
= HttpHdrRangeInitPos
;
395 const HttpHdrRangeSpec
*spec
;
398 /* check that all rangers are in "strong" order, */
399 /* as far as we can tell without the content length */
400 while ((spec
= httpHdrRangeGetSpec(range
, &pos
))) {
401 if (!known_spec(spec
->offset
)) /* ignore unknowns */
403 if (spec
->offset
< offset
)
405 offset
= spec
->offset
;
406 if (known_spec(spec
->length
)) /* avoid unknowns */
407 offset
+= spec
->length
;
412 /* generates a "unique" boundary string for multipart responses
413 * the caller is responsible for cleaning the string */
415 httpHdrRangeBoundaryStr(clientHttpRequest
* http
)
418 String b
= StringNull
;
420 stringAppend(&b
, full_appname_string
, strlen(full_appname_string
));
421 stringAppend(&b
, ":", 1);
422 key
= storeKeyText(http
->entry
->key
);
423 stringAppend(&b
, key
, strlen(key
));