]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/HttpHdrRange.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / HttpHdrRange.cc
index 7c173230dbbdf081fc423fd2c54faf9f8b80a5aa..78e59bbd0df6c6635297e7ad2bb87f61c4fc9b9a 100644 (file)
@@ -1,32 +1,32 @@
 
 /*
- * $Id: HttpHdrRange.cc,v 1.20 1999/10/04 05:04:54 wessels Exp $
+ * $Id$
  *
  * DEBUG: section 64    HTTP Range Header
  * AUTHOR: Alex Rousskov
  *
- * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
  * ----------------------------------------------------------
  *
- *  Squid is the result of efforts by numerous individuals from the
- *  Internet community.  Development is led by Duane Wessels of the
- *  National Laboratory for Applied Network Research and funded by the
- *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
- *  Duane Wessels and the University of California San Diego.  Please
- *  see the COPYRIGHT file for full details.  Squid incorporates
- *  software developed and/or copyrighted by other sources.  Please see
- *  the CREDITS file for full details.
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  */
 
 #include "squid.h"
+#include "Store.h"
+#include "HttpHeaderRange.h"
+#include "client_side_request.h"
+#include "HttpReply.h"
 
 /*
  *    Currently only byte ranges are supported
  *
  *    When Range field is parsed, we have no clue about the content
  *    length of the document. Thus, we simply code an "absent" part
- *    using range_spec_unknown constant.
+ *    using HttpHdrRangeSpec::UnknownPosition constant.
  *
  *    Note: when response length becomes known, we convert any range
  *    spec into type one above. (Canonization process).
  */
 
 
-/* local constants */
-#define range_spec_unknown ((ssize_t)-1)
-
 /* local routines */
-#define known_spec(s) ((s) != range_spec_unknown)
-#define size_min(a,b) ((a) <= (b) ? (a) : (b))
-#define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0)
-static HttpHdrRangeSpec *httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec);
-static int httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen);
-static void httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p);
+#define known_spec(s) ((s) > HttpHdrRangeSpec::UnknownPosition)
 
 /* globals */
-static int RangeParsedCount = 0;
+size_t HttpHdrRange::ParsedCount = 0;
+int64_t const HttpHdrRangeSpec::UnknownPosition = -1;
 
 /*
  * Range-Spec
  */
 
-static HttpHdrRangeSpec *
-httpHdrRangeSpecCreate(void)
+HttpHdrRangeSpec::HttpHdrRangeSpec() : offset(UnknownPosition), length(UnknownPosition) {}
+
+/* parses range-spec and returns new object on success */
+HttpHdrRangeSpec *
+HttpHdrRangeSpec::Create(const char *field, int flen)
 {
-    return memAllocate(MEM_HTTP_HDR_RANGE_SPEC);
+    HttpHdrRangeSpec spec;
+
+    if (!spec.parseInit(field, flen))
+        return NULL;
+
+    return new HttpHdrRangeSpec(spec);
 }
 
-/* parses range-spec and returns new object on success */
-static HttpHdrRangeSpec *
-httpHdrRangeSpecParseCreate(const char *field, int flen)
+bool
+HttpHdrRangeSpec::parseInit(const char *field, int flen)
 {
-    HttpHdrRangeSpec spec =
-    {range_spec_unknown, range_spec_unknown};
     const char *p;
+
     if (flen < 2)
-       return NULL;
+        return false;
+
     /* is it a suffix-byte-range-spec ? */
     if (*field == '-') {
-       if (!httpHeaderParseSize(field + 1, &spec.length))
-           return NULL;
+        if (!httpHeaderParseOffset(field + 1, &length))
+            return false;
     } else
-       /* must have a '-' somewhere in _this_ field */
-    if (!((p = strchr(field, '-')) || (p - field >= flen))) {
-       debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'\n", field);
-       return NULL;
-    } else {
-       if (!httpHeaderParseSize(field, &spec.offset))
-           return NULL;
-       p++;
-       /* do we have last-pos ? */
-       if (p - field < flen) {
-           ssize_t last_pos;
-           if (!httpHeaderParseSize(p, &last_pos))
-               return NULL;
-           spec.length = size_diff(last_pos + 1, spec.offset);
-       }
-    }
+        /* must have a '-' somewhere in _this_ field */
+        if (!((p = strchr(field, '-')) || (p - field >= flen))) {
+            debugs(64, 2, "ignoring invalid (missing '-') range-spec near: '" << field << "'");
+            return false;
+        } else {
+            if (!httpHeaderParseOffset(field, &offset))
+                return false;
+
+            p++;
+
+            /* do we have last-pos ? */
+            if (p - field < flen) {
+                int64_t last_pos;
+
+                if (!httpHeaderParseOffset(p, &last_pos))
+                    return false;
+
+                HttpHdrRangeSpec::HttpRange aSpec (offset, last_pos + 1);
+
+                length = aSpec.size();
+            }
+        }
+
     /* we managed to parse, check if the result makes sence */
-    if (known_spec(spec.length) && !spec.length) {
-       debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'\n", field);
-       return NULL;
+    if (length == 0) {
+        debugs(64, 2, "ignoring invalid (zero length) range-spec near: '" << field << "'");
+        return false;
     }
-    return httpHdrRangeSpecDup(&spec);
-}
 
-static void
-httpHdrRangeSpecDestroy(HttpHdrRangeSpec * spec)
-{
-    memFree(spec, MEM_HTTP_HDR_RANGE_SPEC);
+    return true;
 }
 
-
-static HttpHdrRangeSpec *
-httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec)
+void
+HttpHdrRangeSpec::packInto(Packer * packer) const
 {
-    HttpHdrRangeSpec *dup = httpHdrRangeSpecCreate();
-    dup->offset = spec->offset;
-    dup->length = spec->length;
-    return dup;
+    if (!known_spec(offset))   /* suffix */
+        packerPrintf(packer, "-%"PRId64,  length);
+    else if (!known_spec(length))              /* trailer */
+        packerPrintf(packer, "%"PRId64"-", offset);
+    else                       /* range */
+        packerPrintf(packer, "%"PRId64"-%"PRId64,
+                     offset, offset + length - 1);
 }
 
-static void
-httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p)
+void
+HttpHdrRangeSpec::outputInfo( char const *note) const
 {
-    if (!known_spec(spec->offset))     /* suffix */
-       packerPrintf(p, "-%d", spec->length);
-    else if (!known_spec(spec->length))                /* trailer */
-       packerPrintf(p, "%d-", spec->offset);
-    else                       /* range */
-       packerPrintf(p, "%d-%d",
-           spec->offset, spec->offset + spec->length - 1);
+    debugs(64, 5, "HttpHdrRangeSpec::canonize: " << note << ": [" <<
+           offset << ", " << offset + length <<
+           ") len: " << length);
 }
 
-/* fills "absent" positions in range specification based on response body size 
+/* fills "absent" positions in range specification based on response body size
  * returns true if the range is still valid
  * range is valid if its intersection with [0,length-1] is not empty
  */
-static int
-httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen)
+int
+HttpHdrRangeSpec::canonize(int64_t clen)
 {
-    debug(64, 5) ("httpHdrRangeSpecCanonize: have: [%d, %d) len: %d\n",
-       spec->offset, spec->offset + spec->length, spec->length);
-    if (!known_spec(spec->offset))     /* suffix */
-       spec->offset = size_diff(clen, spec->length);
-    else if (!known_spec(spec->length))                /* trailer */
-       spec->length = size_diff(clen, spec->offset);
+    outputInfo ("have");
+    HttpRange object(0, clen);
+
+    if (!known_spec(offset)) { /* suffix */
+        assert(known_spec(length));
+        offset = object.intersection(HttpRange (clen - length, clen)).start;
+    } else if (!known_spec(length)) {  /* trailer */
+        assert(known_spec(offset));
+        HttpRange newRange = object.intersection(HttpRange (offset, clen));
+        length = newRange.size();
+    }
     /* we have a "range" now, adjust length if needed */
-    assert(known_spec(spec->length));
-    assert(known_spec(spec->offset));
-    spec->length = size_min(size_diff(clen, spec->offset), spec->length);
-    /* check range validity */
-    debug(64, 5) ("httpHdrRangeSpecCanonize: done: [%d, %d) len: %d\n",
-       spec->offset, spec->offset + spec->length, spec->length);
-    return spec->length > 0;
+    assert(known_spec(length));
+
+    assert(known_spec(offset));
+
+    HttpRange newRange = object.intersection (HttpRange (offset, offset + length));
+
+    length = newRange.size();
+
+    outputInfo ("done");
+
+    return length > 0;
 }
 
-/* merges recepient with donor if possible; returns true on success 
+/* merges recepient with donor if possible; returns true on success
  * both specs must be canonized prior to merger, of course */
-static int
-httpHdrRangeSpecMergeWith(HttpHdrRangeSpec * recep, const HttpHdrRangeSpec * donor)
+bool
+HttpHdrRangeSpec::mergeWith(const HttpHdrRangeSpec * donor)
 {
-    int merged = 0;
+    bool merged (false);
 #if MERGING_BREAKS_NOTHING
     /* Note: this code works, but some clients may not like its effects */
-    size_t rhs = recep->offset + recep->length;                /* no -1 ! */
-    const size_t donor_rhs = donor->offset + donor->length;    /* no -1 ! */
-    assert(known_spec(recep->offset));
+    int64_t rhs = offset + length;             /* no -1 ! */
+    const int64_t donor_rhs = donor->offset + donor->length;   /* no -1 ! */
+    assert(known_spec(offset));
     assert(known_spec(donor->offset));
-    assert(recep->length > 0);
+    assert(length > 0);
     assert(donor->length > 0);
     /* do we have a left hand side overlap? */
-    if (donor->offset < recep->offset && recep->offset <= donor_rhs) {
-       recep->offset = donor->offset;  /* decrease left offset */
-       merged = 1;
+
+    if (donor->offset < offset && offset <= donor_rhs) {
+        offset = donor->offset;        /* decrease left offset */
+        merged = 1;
     }
+
     /* do we have a right hand side overlap? */
     if (donor->offset <= rhs && rhs < donor_rhs) {
-       rhs = donor_rhs;        /* increase right offset */
-       merged = 1;
+        rhs = donor_rhs;       /* increase right offset */
+        merged = 1;
     }
+
     /* adjust length if offsets have been changed */
     if (merged) {
-       assert(rhs > recep->offset);
-       recep->length = rhs - recep->offset;
+        assert(rhs > offset);
+        length = rhs - offset;
     } else {
-       /* does recepient contain donor? */
-       merged =
-           recep->offset <= donor->offset && donor->offset < rhs;
+        /* does recepient contain donor? */
+        merged =
+            offset <= donor->offset && donor->offset < rhs;
     }
+
 #endif
     return merged;
 }
@@ -208,224 +225,269 @@ httpHdrRangeSpecMergeWith(HttpHdrRangeSpec * recep, const HttpHdrRangeSpec * don
  * Range
  */
 
-HttpHdrRange *
-httpHdrRangeCreate(void)
-{
-    HttpHdrRange *r = memAllocate(MEM_HTTP_HDR_RANGE);
-    stackInit(&r->specs);
-    return r;
-}
+HttpHdrRange::HttpHdrRange () : clen (HttpHdrRangeSpec::UnknownPosition)
+{}
 
 HttpHdrRange *
-httpHdrRangeParseCreate(const String * str)
+HttpHdrRange::ParseCreate(const String * range_spec)
 {
-    HttpHdrRange *r = httpHdrRangeCreate();
-    if (!httpHdrRangeParseInit(r, str)) {
-       httpHdrRangeDestroy(r);
-       r = NULL;
+    HttpHdrRange *r = new HttpHdrRange;
+
+    if (!r->parseInit(range_spec)) {
+        delete r;
+        r = NULL;
     }
+
     return r;
 }
 
 /* returns true if ranges are valid; inits HttpHdrRange */
-int
-httpHdrRangeParseInit(HttpHdrRange * range, const String * str)
+bool
+HttpHdrRange::parseInit(const String * range_spec)
 {
     const char *item;
     const char *pos = NULL;
     int ilen;
     int count = 0;
-    assert(range && str);
-    RangeParsedCount++;
-    debug(64, 8) ("parsing range field: '%s'\n", strBuf(*str));
+    assert(this && range_spec);
+    ++ParsedCount;
+    debugs(64, 8, "parsing range field: '" << range_spec->buf() << "'");
     /* check range type */
-    if (strNCaseCmp(*str, "bytes=", 6))
-       return 0;
+
+    if (range_spec->caseCmp("bytes=", 6))
+        return 0;
+
     /* skip "bytes="; hack! */
-    pos = strBuf(*str) + 5;
+    pos = range_spec->buf() + 6;
+
     /* iterate through comma separated list */
-    while (strListGetItem(str, ',', &item, &ilen, &pos)) {
-       HttpHdrRangeSpec *spec = httpHdrRangeSpecParseCreate(item, ilen);
-       /*
-        * HTTP/1.1 draft says we must ignore the whole header field if one spec
-        * is invalid. However, RFC 2068 just says that we must ignore that spec.
-        */
-       if (spec)
-           stackPush(&range->specs, spec);
-       count++;
+    while (strListGetItem(range_spec, ',', &item, &ilen, &pos)) {
+        HttpHdrRangeSpec *spec = HttpHdrRangeSpec::Create(item, ilen);
+        /*
+         * HTTP/1.1 draft says we must ignore the whole header field if one spec
+         * is invalid. However, RFC 2068 just says that we must ignore that spec.
+         */
+
+        if (spec)
+            specs.push_back(spec);
+
+        ++count;
     }
-    debug(64, 8) ("parsed range range count: %d\n", range->specs.count);
-    return range->specs.count;
+
+    debugs(64, 8, "parsed range range count: " << count << ", kept " <<
+           specs.size());
+    return specs.count != 0;
+}
+
+HttpHdrRange::~HttpHdrRange()
+{
+    while (specs.size())
+        delete specs.pop_back();
+}
+
+HttpHdrRange::HttpHdrRange(HttpHdrRange const &old) : specs()
+{
+    specs.reserve(old.specs.size());
+
+    for (const_iterator i = old.begin(); i != old.end(); ++i)
+        specs.push_back(new HttpHdrRangeSpec ( **i));
+
+    assert(old.specs.size() == specs.size());
+}
+
+HttpHdrRange::iterator
+HttpHdrRange::begin()
+{
+    return specs.begin();
+}
+
+HttpHdrRange::iterator
+HttpHdrRange::end()
+{
+    return specs.end();
+}
+
+HttpHdrRange::const_iterator
+HttpHdrRange::begin() const
+{
+    return specs.begin();
+}
+
+HttpHdrRange::const_iterator
+HttpHdrRange::end() const
+{
+    return specs.end();
 }
 
 void
-httpHdrRangeDestroy(HttpHdrRange * range)
+HttpHdrRange::packInto(Packer * packer) const
 {
-    assert(range);
-    while (range->specs.count)
-       httpHdrRangeSpecDestroy(stackPop(&range->specs));
-    stackClean(&range->specs);
-    memFree(range, MEM_HTTP_HDR_RANGE);
+    const_iterator pos = begin();
+    assert(this);
+
+    while (pos != end()) {
+        if (pos != begin())
+            packerAppend(packer, ",", 1);
+
+        (*pos)->packInto(packer);
+
+        ++pos;
+    }
 }
 
-HttpHdrRange *
-httpHdrRangeDup(const HttpHdrRange * range)
+void
+HttpHdrRange::merge (Vector<HttpHdrRangeSpec *> &basis)
 {
-    HttpHdrRange *dup;
-    int i;
-    assert(range);
-    dup = httpHdrRangeCreate();
-    stackPrePush(&dup->specs, range->specs.count);
-    for (i = 0; i < range->specs.count; i++)
-       stackPush(&dup->specs, httpHdrRangeSpecDup(range->specs.items[i]));
-    assert(range->specs.count == dup->specs.count);
-    return dup;
+    /* reset old array */
+    specs.clean();
+    /* merge specs:
+     * take one spec from "goods" and merge it with specs from
+     * "specs" (if any) until there is no overlap */
+    iterator i = basis.begin();
+
+    while (i != basis.end()) {
+        if (specs.size() && (*i)->mergeWith(specs.back())) {
+            /* merged with current so get rid of the prev one */
+            delete specs.pop_back();
+            continue;  /* re-iterate */
+        }
+
+        specs.push_back (*i);
+        ++i;                   /* progress */
+    }
+
+    debugs(64, 3, "HttpHdrRange::merge: had " << basis.size() <<
+           " specs, merged " << basis.size() - specs.size() << " specs");
 }
 
+
 void
-httpHdrRangePackInto(const HttpHdrRange * range, Packer * p)
+HttpHdrRange::getCanonizedSpecs (Vector<HttpHdrRangeSpec *> &copy)
 {
-    HttpHdrRangePos pos = HttpHdrRangeInitPos;
-    const HttpHdrRangeSpec *spec;
-    assert(range);
-    while ((spec = httpHdrRangeGetSpec(range, &pos))) {
-       if (pos != HttpHdrRangeInitPos)
-           packerAppend(p, ",", 1);
-       httpHdrRangeSpecPackInto(spec, p);
+    /* canonize each entry and destroy bad ones if any */
+
+    for (iterator pos (begin()); pos != end(); ++pos) {
+        if ((*pos)->canonize(clen))
+            copy.push_back (*pos);
+        else
+            delete (*pos);
     }
+
+    debugs(64, 3, "HttpHdrRange::getCanonizedSpecs: found " <<
+           specs.size() - copy.size() << " bad specs");
 }
 
+#include "HttpHdrContRange.h"
+
 /*
  * canonizes all range specs within a set preserving the order
- * returns true if the set is valid after canonization; 
- * the set is valid if 
- *   - all range specs are valid and 
+ * returns true if the set is valid after canonization;
+ * the set is valid if
+ *   - all range specs are valid and
  *   - there is at least one range spec
  */
 int
-httpHdrRangeCanonize(HttpHdrRange * range, ssize_t clen)
+HttpHdrRange::canonize(HttpReply *rep)
 {
-    int i;
-    HttpHdrRangeSpec *spec;
-    HttpHdrRangePos pos = HttpHdrRangeInitPos;
-    Stack goods;
-    assert(range);
-    assert(clen >= 0);
-    stackInit(&goods);
-    debug(64, 3) ("httpHdrRangeCanonize: started with %d specs, clen: %d\n", range->specs.count, clen);
+    assert(this && rep);
 
-    /* canonize each entry and destroy bad ones if any */
-    while ((spec = httpHdrRangeGetSpec(range, &pos))) {
-       if (httpHdrRangeSpecCanonize(spec, clen))
-           stackPush(&goods, spec);
-       else
-           httpHdrRangeSpecDestroy(spec);
-    }
-    debug(64, 3) ("httpHdrRangeCanonize: found %d bad specs\n",
-       range->specs.count - goods.count);
-    /* reset old array */
-    stackClean(&range->specs);
-    stackInit(&range->specs);
-    spec = NULL;
-    /* merge specs:
-     * take one spec from "goods" and merge it with specs from 
-     * "range->specs" (if any) until there is no overlap */
-    for (i = 0; i < goods.count;) {
-       HttpHdrRangeSpec *prev_spec = stackTop(&range->specs);
-       spec = goods.items[i];
-       if (prev_spec) {
-           if (httpHdrRangeSpecMergeWith(spec, prev_spec)) {
-               /* merged with current so get rid of the prev one */
-               assert(prev_spec == stackPop(&range->specs));
-               httpHdrRangeSpecDestroy(prev_spec);
-               continue;       /* re-iterate */
-           }
-       }
-       stackPush(&range->specs, spec);
-       spec = NULL;
-       i++;                    /* progress */
-    }
-    if (spec)                  /* last "merge" may not be pushed yet */
-       stackPush(&range->specs, spec);
-    debug(64, 3) ("httpHdrRangeCanonize: had %d specs, merged %d specs\n",
-       goods.count, goods.count - range->specs.count);
-    debug(64, 3) ("httpHdrRangeCanonize: finished with %d specs\n",
-       range->specs.count);
-    stackClean(&goods);
-    return range->specs.count > 0;
+    if (rep->content_range)
+        clen = rep->content_range->elength;
+    else
+        clen = rep->content_length;
+
+    return canonize (clen);
 }
 
-/* searches for next range, returns true if found */
-HttpHdrRangeSpec *
-httpHdrRangeGetSpec(const HttpHdrRange * range, HttpHdrRangePos * pos)
+int
+HttpHdrRange::canonize (int64_t newClen)
 {
-    assert(range);
-    assert(pos && *pos >= -1 && *pos < range->specs.count);
-    (*pos)++;
-    if (*pos < range->specs.count)
-       return (HttpHdrRangeSpec *) range->specs.items[*pos];
-    else
-       return NULL;
+    clen = newClen;
+    debugs(64, 3, "HttpHdrRange::canonize: started with " << specs.count <<
+           " specs, clen: " << clen);
+    Vector<HttpHdrRangeSpec*> goods;
+    getCanonizedSpecs(goods);
+    merge (goods);
+    debugs(64, 3, "HttpHdrRange::canonize: finished with " << specs.count <<
+           " specs");
+    return specs.count > 0;
 }
 
 /* hack: returns true if range specs are too "complex" for Squid to handle */
 /* requires that specs are "canonized" first! */
-int
-httpHdrRangeIsComplex(const HttpHdrRange * range)
+bool
+HttpHdrRange::isComplex() const
 {
-    HttpHdrRangePos pos = HttpHdrRangeInitPos;
-    const HttpHdrRangeSpec *spec;
-    size_t offset = 0;
-    assert(range);
+    int64_t offset = 0;
+    assert(this);
     /* check that all rangers are in "strong" order */
-    while ((spec = httpHdrRangeGetSpec(range, &pos))) {
-       if (spec->offset < offset)
-           return 1;
-       offset = spec->offset + spec->length;
+    const_iterator pos (begin());
+
+    while (pos != end()) {
+        /* Ensure typecasts is safe */
+        assert ((*pos)->offset >= 0);
+
+        if ((*pos)->offset < offset)
+            return 1;
+
+        offset = (*pos)->offset + (*pos)->length;
+
+        ++pos;
     }
+
     return 0;
 }
 
 /*
  * hack: returns true if range specs may be too "complex" when "canonized".
- * see also: httpHdrRangeIsComplex.
+ * see also: HttpHdrRange::isComplex.
  */
-int
-httpHdrRangeWillBeComplex(const HttpHdrRange * range)
+bool
+HttpHdrRange::willBeComplex() const
 {
-    HttpHdrRangePos pos = HttpHdrRangeInitPos;
-    const HttpHdrRangeSpec *spec;
-    size_t offset = 0;
-    assert(range);
+    assert(this);
     /* check that all rangers are in "strong" order, */
     /* as far as we can tell without the content length */
-    while ((spec = httpHdrRangeGetSpec(range, &pos))) {
-       if (!known_spec(spec->offset))  /* ignore unknowns */
-           continue;
-       if (spec->offset < offset)
-           return 1;
-       offset = spec->offset;
-       if (known_spec(spec->length))   /* avoid  unknowns */
-           offset += spec->length;
+    int64_t offset = 0;
+
+    for (const_iterator pos (begin()); pos != end(); ++pos) {
+        if (!known_spec((*pos)->offset))       /* ignore unknowns */
+            continue;
+
+        /* Ensure typecasts is safe */
+        assert ((*pos)->offset >= 0);
+
+        if ((*pos)->offset < offset)
+            return true;
+
+        offset = (*pos)->offset;
+
+        if (known_spec((*pos)->length))        /* avoid  unknowns */
+            offset += (*pos)->length;
     }
-    return 0;
+
+    return false;
 }
 
 /*
- * Returns lowest known offset in range spec(s), or range_spec_unknown
+ * Returns lowest known offset in range spec(s),
+ * or HttpHdrRangeSpec::UnknownPosition
  * this is used for size limiting
  */
-ssize_t
-httpHdrRangeFirstOffset(const HttpHdrRange * range)
+int64_t
+HttpHdrRange::firstOffset() const
 {
-    ssize_t offset = range_spec_unknown;
-    HttpHdrRangePos pos = HttpHdrRangeInitPos;
-    const HttpHdrRangeSpec *spec;
-    assert(range);
-    while ((spec = httpHdrRangeGetSpec(range, &pos))) {
-       if (spec->offset < offset || !known_spec(offset))
-           offset = spec->offset;
+    int64_t offset = HttpHdrRangeSpec::UnknownPosition;
+    assert(this);
+    const_iterator pos = begin();
+
+    while (pos != end()) {
+        if ((*pos)->offset < offset || !known_spec(offset))
+            offset = (*pos)->offset;
+
+        ++pos;
     }
+
     return offset;
 }
 
@@ -435,39 +497,110 @@ httpHdrRangeFirstOffset(const HttpHdrRange * range)
  * ranges are combined into one, for example FTP REST.
  * Use 0 for size if unknown
  */
-ssize_t
-httpHdrRangeLowestOffset(const HttpHdrRange * range, ssize_t size)
+int64_t
+HttpHdrRange::lowestOffset(int64_t size) const
 {
-    ssize_t offset = range_spec_unknown;
-    ssize_t current;
-    HttpHdrRangePos pos = HttpHdrRangeInitPos;
-    const HttpHdrRangeSpec *spec;
-    assert(range);
-    while ((spec = httpHdrRangeGetSpec(range, &pos))) {
-       current = spec->offset;
-       if (!known_spec(current)) {
-           if (spec->length > size || !known_spec(spec->length))
-               return 0;       /* Unknown. Assume start of file */
-           current = size - spec->length;
-       }
-       if (current < offset || !known_spec(offset))
-           offset = current;
+    int64_t offset = HttpHdrRangeSpec::UnknownPosition;
+    const_iterator pos = begin();
+    assert(this);
+
+    while (pos != end()) {
+        int64_t current = (*pos)->offset;
+
+        if (!known_spec(current)) {
+            if ((*pos)->length > size || !known_spec((*pos)->length))
+                return 0;      /* Unknown. Assume start of file */
+
+            current = size - (*pos)->length;
+        }
+
+        if (current < offset || !known_spec(offset))
+            offset = current;
+
+        ++pos;
     }
+
     return known_spec(offset) ? offset : 0;
 }
 
+/*
+ * Return true if the first range offset is larger than the configured
+ * limit.
+ * Note that exceeding the limit (returning true) results in only
+ * grabbing the needed range elements from the origin.
+ */
+bool
+HttpHdrRange::offsetLimitExceeded() const
+{
+    if (NULL == this)
+        /* not a range request */
+        return false;
+
+    if (Config.rangeOffsetLimit == 0)
+        /* disabled */
+        return true;
+
+    if (-1 == Config.rangeOffsetLimit)
+        /* forced */
+        return false;
+
+    if (firstOffset() == -1)
+        /* tail request */
+        return true;
+
+    if (Config.rangeOffsetLimit >= firstOffset())
+        /* below the limit */
+        return false;
+
+    return true;
+}
+
+bool
+HttpHdrRange::contains(HttpHdrRangeSpec& r) const
+{
+    assert(r.length >= 0);
+    HttpHdrRangeSpec::HttpRange rrange(r.offset, r.offset + r.length);
+
+    for (const_iterator i = begin(); i != end(); ++i) {
+        HttpHdrRangeSpec::HttpRange irange((*i)->offset, (*i)->offset + (*i)->length);
+        HttpHdrRangeSpec::HttpRange intersection = rrange.intersection(irange);
+
+        if (intersection.start == irange.start && intersection.size() == irange.size())
+            return true;
+    }
+
+    return false;
+}
+
+const HttpHdrRangeSpec *
+HttpHdrRangeIter::currentSpec() const
+{
+    if (pos.incrementable())
+        return *pos;
+
+    return NULL;
+}
+
+void
+HttpHdrRangeIter::updateSpec()
+{
+    assert (debt_size == 0);
+    assert (valid);
+
+    if (pos.incrementable()) {
+        debt(currentSpec()->length);
+    }
+}
+
+int64_t
+HttpHdrRangeIter::debt() const
+{
+    debugs(64, 3, "HttpHdrRangeIter::debt: debt is " << debt_size);
+    return debt_size;
+}
 
-/* generates a "unique" boundary string for multipart responses
- * the caller is responsible for cleaning the string */
-String
-httpHdrRangeBoundaryStr(clientHttpRequest * http)
+void HttpHdrRangeIter::debt(int64_t newDebt)
 {
-    const char *key;
-    String b = StringNull;
-    assert(http);
-    stringAppend(&b, full_appname_string, strlen(full_appname_string));
-    stringAppend(&b, ":", 1);
-    key = storeKeyText(http->entry->key);
-    stringAppend(&b, key, strlen(key));
-    return b;
+    debugs(64, 3, "HttpHdrRangeIter::debt: was " << debt_size << " now " << newDebt);
+    debt_size = newDebt;
 }