]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrRange.cc
3 * $Id: HttpHdrRange.cc,v 1.32 2003/03/06 06:21:36 robertc Exp $
5 * DEBUG: section 64 HTTP Range Header
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.
38 #include "HttpHeaderRange.h"
39 #include "client_side_request.h"
42 * Currently only byte ranges are supported
44 * Essentially, there are three types of byte ranges:
46 * 1) first-byte-pos "-" last-byte-pos // range
47 * 2) first-byte-pos "-" // trailer
48 * 3) "-" suffix-length // suffix (last length bytes)
51 * When Range field is parsed, we have no clue about the content
52 * length of the document. Thus, we simply code an "absent" part
53 * using HttpHdrRangeSpec::UnknownPosition constant.
55 * Note: when response length becomes known, we convert any range
56 * spec into type one above. (Canonization process).
61 #define known_spec(s) ((s) > HttpHdrRangeSpec::UnknownPosition)
64 size_t HttpHdrRange::ParsedCount
= 0;
65 ssize_t
const HttpHdrRangeSpec::UnknownPosition
= -1;
71 MemPool
*HttpHdrRangeSpec::Pool
= NULL
;
74 HttpHdrRangeSpec::operator new(size_t size
)
76 assert (size
== sizeof (HttpHdrRangeSpec
));
79 Pool
= memPoolCreate ("HttpHdrRangeSpec", sizeof (HttpHdrRangeSpec
));
81 return memPoolAlloc(Pool
);
85 HttpHdrRangeSpec::operator delete (void *spec
)
87 memPoolFree(Pool
, spec
);
91 HttpHdrRangeSpec::deleteSelf() const
96 HttpHdrRangeSpec::HttpHdrRangeSpec() : offset(UnknownPosition
), length(UnknownPosition
){}
98 /* parses range-spec and returns new object on success */
100 HttpHdrRangeSpec::Create(const char *field
, int flen
)
102 HttpHdrRangeSpec spec
;
104 if (!spec
.parseInit(field
, flen
))
107 return new HttpHdrRangeSpec(spec
);
111 HttpHdrRangeSpec::parseInit(const char *field
, int flen
)
118 /* is it a suffix-byte-range-spec ? */
120 if (!httpHeaderParseSize(field
+ 1, &length
))
123 /* must have a '-' somewhere in _this_ field */
124 if (!((p
= strchr(field
, '-')) || (p
- field
>= flen
))) {
125 debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'\n", field
);
128 if (!httpHeaderParseSize(field
, &offset
))
133 /* do we have last-pos ? */
134 if (p
- field
< flen
) {
137 if (!httpHeaderParseSize(p
, &last_pos
))
140 HttpHdrRangeSpec::HttpRange
aSpec (offset
, last_pos
+ 1);
142 length
= aSpec
.size();
146 /* we managed to parse, check if the result makes sence */
148 debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'\n", field
);
156 HttpHdrRangeSpec::packInto(Packer
* packer
) const
158 if (!known_spec(offset
)) /* suffix */
159 packerPrintf(packer
, "-%ld", (long int) length
);
160 else if (!known_spec(length
)) /* trailer */
161 packerPrintf(packer
, "%ld-", (long int) offset
);
163 packerPrintf(packer
, "%ld-%ld",
164 (long int) offset
, (long int) offset
+ length
- 1);
168 HttpHdrRangeSpec::outputInfo( char const *note
) const
170 debug(64, 5) ("HttpHdrRangeSpec::canonize: %s: [%ld, %ld) len: %ld\n",
171 note
, (long int) offset
, (long int) offset
+ length
, (long int) length
);
174 /* fills "absent" positions in range specification based on response body size
175 * returns true if the range is still valid
176 * range is valid if its intersection with [0,length-1] is not empty
179 HttpHdrRangeSpec::canonize(size_t clen
)
182 HttpRange
object(0, clen
);
184 if (!known_spec(offset
)) /* suffix */
186 assert(known_spec(length
));
187 offset
= object
.intersection(HttpRange (clen
- length
, clen
)).start
;
188 } else if (!known_spec(length
)) /* trailer */
190 assert(known_spec(offset
));
191 HttpRange newRange
= object
.intersection(HttpRange (offset
, clen
));
192 length
= newRange
.size();
194 /* we have a "range" now, adjust length if needed */
195 assert(known_spec(length
));
197 assert(known_spec(offset
));
199 HttpRange newRange
= object
.intersection (HttpRange (offset
, offset
+ length
));
201 length
= newRange
.size();
208 /* merges recepient with donor if possible; returns true on success
209 * both specs must be canonized prior to merger, of course */
211 HttpHdrRangeSpec::mergeWith(const HttpHdrRangeSpec
* donor
)
214 #if MERGING_BREAKS_NOTHING
215 /* Note: this code works, but some clients may not like its effects */
216 size_t rhs
= offset
+ length
; /* no -1 ! */
217 const size_t donor_rhs
= donor
->offset
+ donor
->length
; /* no -1 ! */
218 assert(known_spec(offset
));
219 assert(known_spec(donor
->offset
));
221 assert(donor
->length
> 0);
222 /* do we have a left hand side overlap? */
224 if (donor
->offset
< offset
&& offset
<= donor_rhs
) {
225 offset
= donor
->offset
; /* decrease left offset */
229 /* do we have a right hand side overlap? */
230 if (donor
->offset
<= rhs
&& rhs
< donor_rhs
) {
231 rhs
= donor_rhs
; /* increase right offset */
235 /* adjust length if offsets have been changed */
237 assert(rhs
> offset
);
238 length
= rhs
- offset
;
240 /* does recepient contain donor? */
242 offset
<= donor
->offset
&& donor
->offset
< rhs
;
253 MemPool
*HttpHdrRange::Pool
= NULL
;
256 HttpHdrRange::operator new(size_t size
)
258 assert (size
== sizeof (HttpHdrRange
));
261 Pool
= memPoolCreate ("HttpHdrRange", sizeof (HttpHdrRange
));
263 return memPoolAlloc(Pool
);
267 HttpHdrRange::operator delete (void *address
)
269 memPoolFree(Pool
, address
);
273 HttpHdrRange::deleteSelf() const
278 HttpHdrRange::HttpHdrRange () : clen (HttpHdrRangeSpec::UnknownPosition
)
282 HttpHdrRange::ParseCreate(const String
* range_spec
)
284 HttpHdrRange
*r
= new HttpHdrRange
;
286 if (!r
->parseInit(range_spec
)) {
294 /* returns true if ranges are valid; inits HttpHdrRange */
296 HttpHdrRange::parseInit(const String
* range_spec
)
299 const char *pos
= NULL
;
302 assert(this && range_spec
);
304 debug(64, 8) ("parsing range field: '%s'\n", range_spec
->buf());
305 /* check range type */
307 if (range_spec
->caseCmp("bytes=", 6))
310 /* skip "bytes="; hack! */
311 pos
= range_spec
->buf() + 5;
313 /* iterate through comma separated list */
314 while (strListGetItem(range_spec
, ',', &item
, &ilen
, &pos
)) {
315 HttpHdrRangeSpec
*spec
= HttpHdrRangeSpec::Create(item
, ilen
);
317 * HTTP/1.1 draft says we must ignore the whole header field if one spec
318 * is invalid. However, RFC 2068 just says that we must ignore that spec.
322 specs
.push_back(spec
);
327 debug(64, 8) ("parsed range range count: %d, kept %d\n", count
, specs
.size());
328 return specs
.count
!= 0;
331 HttpHdrRange::~HttpHdrRange()
334 specs
.pop_back()->deleteSelf();
337 HttpHdrRange::HttpHdrRange(HttpHdrRange
const &old
) : specs()
339 specs
.reserve(old
.specs
.size());
341 for (const_iterator i
= old
.begin(); i
!= old
.end(); ++i
)
342 specs
.push_back(new HttpHdrRangeSpec ( **i
));
344 assert(old
.specs
.size() == specs
.size());
347 HttpHdrRange::iterator
348 HttpHdrRange::begin()
350 return specs
.begin();
353 HttpHdrRange::iterator
359 HttpHdrRange::const_iterator
360 HttpHdrRange::begin() const
362 return specs
.begin();
365 HttpHdrRange::const_iterator
366 HttpHdrRange::end() const
372 HttpHdrRange::packInto(Packer
* packer
) const
374 const_iterator pos
= begin();
377 while (pos
!= end()) {
379 packerAppend(packer
, ",", 1);
381 (*pos
)->packInto(packer
);
388 HttpHdrRange::merge (Vector
<HttpHdrRangeSpec
*> &basis
)
390 /* reset old array */
393 * take one spec from "goods" and merge it with specs from
394 * "specs" (if any) until there is no overlap */
395 iterator i
= basis
.begin();
397 while (i
!= basis
.end()) {
398 if (specs
.size() && (*i
)->mergeWith(specs
.back())) {
399 /* merged with current so get rid of the prev one */
400 specs
.pop_back()->deleteSelf();
401 continue; /* re-iterate */
404 specs
.push_back (*i
);
408 debug(64, 3) ("HttpHdrRange::merge: had %d specs, merged %d specs\n",
409 basis
.size(), basis
.size() - specs
.size());
414 HttpHdrRange::getCanonizedSpecs (Vector
<HttpHdrRangeSpec
*> ©
)
416 /* canonize each entry and destroy bad ones if any */
418 for (iterator
pos (begin()); pos
!= end(); ++pos
) {
419 if ((*pos
)->canonize(clen
))
420 copy
.push_back (*pos
);
422 (*pos
)->deleteSelf();
425 debug(64, 3) ("HttpHdrRange::getCanonizedSpecs: found %d bad specs\n",
426 specs
.size() - copy
.size());
429 #include "HttpHdrContRange.h"
432 * canonizes all range specs within a set preserving the order
433 * returns true if the set is valid after canonization;
434 * the set is valid if
435 * - all range specs are valid and
436 * - there is at least one range spec
439 HttpHdrRange::canonize(HttpReply
*rep
)
443 if (rep
->content_range
)
444 clen
= rep
->content_range
->elength
;
446 clen
= rep
->content_length
;
448 return canonize (clen
);
452 HttpHdrRange::canonize (size_t newClen
)
455 debug(64, 3) ("HttpHdrRange::canonize: started with %d specs, clen: %ld\n", specs
.count
, (long int) clen
);
456 Vector
<HttpHdrRangeSpec
*> goods
;
457 getCanonizedSpecs(goods
);
459 debug(64, 3) ("HttpHdrRange::canonize: finished with %d specs\n",
461 return specs
.count
> 0;
464 /* hack: returns true if range specs are too "complex" for Squid to handle */
465 /* requires that specs are "canonized" first! */
467 HttpHdrRange::isComplex() const
471 /* check that all rangers are in "strong" order */
472 const_iterator
pos (begin());
474 while (pos
!= end()) {
475 /* Ensure typecasts is safe */
476 assert ((*pos
)->offset
>= 0);
478 if ((unsigned int)(*pos
)->offset
< offset
)
481 offset
= (*pos
)->offset
+ (*pos
)->length
;
490 * hack: returns true if range specs may be too "complex" when "canonized".
491 * see also: HttpHdrRange::isComplex.
494 HttpHdrRange::willBeComplex() const
497 /* check that all rangers are in "strong" order, */
498 /* as far as we can tell without the content length */
501 for (const_iterator
pos (begin()); pos
!= end(); ++pos
) {
502 if (!known_spec((*pos
)->offset
)) /* ignore unknowns */
505 /* Ensure typecasts is safe */
506 assert ((*pos
)->offset
>= 0);
508 if ((size_t) (*pos
)->offset
< offset
)
511 offset
= (*pos
)->offset
;
513 if (known_spec((*pos
)->length
)) /* avoid unknowns */
514 offset
+= (*pos
)->length
;
521 * Returns lowest known offset in range spec(s),
522 * or HttpHdrRangeSpec::UnknownPosition
523 * this is used for size limiting
526 HttpHdrRange::firstOffset() const
528 ssize_t offset
= HttpHdrRangeSpec::UnknownPosition
;
530 const_iterator pos
= begin();
532 while (pos
!= end()) {
533 if ((*pos
)->offset
< offset
|| !known_spec(offset
))
534 offset
= (*pos
)->offset
;
543 * Returns lowest offset in range spec(s), 0 if unknown.
544 * This is used for finding out where we need to start if all
545 * ranges are combined into one, for example FTP REST.
546 * Use 0 for size if unknown
549 HttpHdrRange::lowestOffset(ssize_t size
) const
551 ssize_t offset
= HttpHdrRangeSpec::UnknownPosition
;
552 const_iterator pos
= begin();
555 while (pos
!= end()) {
556 ssize_t current
= (*pos
)->offset
;
558 if (!known_spec(current
)) {
559 if ((*pos
)->length
> size
|| !known_spec((*pos
)->length
))
560 return 0; /* Unknown. Assume start of file */
562 current
= size
- (*pos
)->length
;
565 if (current
< offset
|| !known_spec(offset
))
571 return known_spec(offset
) ? offset
: 0;
575 * Return true if the first range offset is larger than the configured
579 HttpHdrRange::offsetLimitExceeded() const
582 /* not a range request */
585 if (-1 == (ssize_t
)Config
.rangeOffsetLimit
)
589 if ((ssize_t
)Config
.rangeOffsetLimit
>= firstOffset())
590 /* below the limit */
596 const HttpHdrRangeSpec
*
597 HttpHdrRangeIter::currentSpec() const
599 if (pos
.incrementable())
606 HttpHdrRangeIter::updateSpec()
608 assert (debt_size
== 0);
610 if (pos
.incrementable()) {
611 debt(currentSpec()->length
);
616 HttpHdrRangeIter::debt() const
618 debug(64, 3) ("HttpHdrRangeIter::debt: debt is %d\n",
623 void HttpHdrRangeIter::debt(ssize_t newDebt
)
625 debug(64, 3) ("HttpHdrRangeIter::debt: was %d now %d\n",
626 (int)debt_size
, (int)newDebt
);