before seek change.
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/ee708499 Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/ee708499 Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/ee708499 Branch: refs/heads/ts-974-multi-range-read Commit: ee7084990274939458314e35157a23abb13f3edc Parents: 50f37f3 Author: Alan M. Carroll <solidwallofc...@yahoo-inc.com> Authored: Wed Nov 26 09:04:13 2014 -0600 Committer: Alan M. Carroll <solidwallofc...@yahoo-inc.com> Committed: Sat Dec 6 11:56:03 2014 -0600 ---------------------------------------------------------------------- iocore/cache/CacheHttp.cc | 39 ++++++++++++++--- iocore/cache/CacheRead.cc | 22 ++++++---- iocore/cache/I_CacheDefs.h | 87 ++++++++++++++++++++++++++++++++++--- iocore/cache/P_CacheInternal.h | 27 ++++++++++++ lib/ts/InkErrno.h | 1 + 5 files changed, 155 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee708499/iocore/cache/CacheHttp.cc ---------------------------------------------------------------------- diff --git a/iocore/cache/CacheHttp.cc b/iocore/cache/CacheHttp.cc index e58e42e..024f5ca 100644 --- a/iocore/cache/CacheHttp.cc +++ b/iocore/cache/CacheHttp.cc @@ -329,20 +329,47 @@ RangeSpec::parse(char const* v, int len) RangeSpec& RangeSpec::add(uint64_t low, uint64_t high) { - if (EMPTY == _state || INVALID == _state) { + if (MULTI == _state) { + _ranges.push_back(Range(low, high)); + } else if (SINGLE == _state) { + _ranges.push_back(_single); + _ranges.push_back(Range(low,high)); + _state = MULTI; + } else { _single._min = low; _single._max = high; _state = SINGLE; - } else { - _ranges.push_back(Range(low, high)); } return *this; } -size_t -RangeSpec::count() const +bool +RangeSpec::finalize(uint64_t len) { - return (EMPTY == _state || INVALID == _state) ? 0 : 1 + (_ranges.size()); + if (INVALID == _state || EMPTY == _state) { + // nothing but simplifying later logic. + } else if (0 == len) { + /* Must special case zero length content + - suffix ranges are OK but other ranges are not. + - SM must return a 200 (not 206 or 416) for a valid range on zero length content. + (this is what Apache HTTPD does and seems the least bad thing) + - Therefore we don't bother actually adjusting the ranges as values don't matter. + */ + if (!_single.isSuffix()) _state = INVALID; + if (MULTI == _state) { + for ( RangeBox::iterator spot = _ranges.begin(), limit = _ranges.end() ; spot != limit && MULTI == _state ; ++spot ) { + if (!spot->isSuffix()) _state = INVALID; + } + } + } else { // len > 0 + if (!_single.finalize(len)) _state = INVALID; + if (MULTI == _state) { + for ( RangeBox::iterator spot = _ranges.begin(), limit = _ranges.end() ; spot != limit && MULTI == _state; ++spot ) { + if (!spot->finalize(len)) _state = INVALID; + } + } + } + return INVALID != _state; } /*------------------------------------------------------------------------- -------------------------------------------------------------------------*/ http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee708499/iocore/cache/CacheRead.cc ---------------------------------------------------------------------- diff --git a/iocore/cache/CacheRead.cc b/iocore/cache/CacheRead.cc index 944cb37..c2caab7 100644 --- a/iocore/cache/CacheRead.cc +++ b/iocore/cache/CacheRead.cc @@ -106,18 +106,11 @@ Cache::open_read(Continuation * cont, CacheKey * key, CacheHTTPHdr * request, OpenDirEntry *od = NULL; CacheVC *c = NULL; - MIMEField* range_field = request->field_find(MIME_FIELD_RANGE, MIME_LEN_RANGE); - if (range_field) { - RangeSpec rs; - char const* value; - int len; - value = range_field->value_get(&len); - rs.parse(value, len); - } - { CACHE_TRY_LOCK(lock, vol->mutex, mutex->thread_holding); if (!lock.is_locked() || (od = vol->open_read(key)) || dir_probe(key, vol, &result, &last_collision)) { + MIMEField* range_field = request->field_find(MIME_FIELD_RANGE, MIME_LEN_RANGE); + c = new_CacheVC(cont); c->first_key = c->key = c->earliest_key = *key; c->vol = vol; @@ -128,6 +121,12 @@ Cache::open_read(Continuation * cont, CacheKey * key, CacheHTTPHdr * request, c->frag_type = CACHE_FRAG_TYPE_HTTP; c->params = params; c->od = od; + if (range_field) { + char const* value; + int len; + value = range_field->value_get(&len); + c->req_rs.parse(value, len); + } } if (!lock.is_locked()) { SET_CONTINUATION_HANDLER(c, &CacheVC::openReadStartHead); @@ -1157,6 +1156,11 @@ CacheVC::openReadStartHead(int event, Event * e) doc_len = doc->total_len; } + if (!req_rs.finalize(doc_len)) { + err = ECACHE_BAD_REQUEST_RANGE; + goto Ldone; + } + if (is_debug_tag_set("cache_read")) { // amc debug char xt[33],yt[33]; Debug("cache_read", "CacheReadStartHead - read %s target %s - %s %d of %" PRId64" bytes, %d fragments", http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee708499/iocore/cache/I_CacheDefs.h ---------------------------------------------------------------------- diff --git a/iocore/cache/I_CacheDefs.h b/iocore/cache/I_CacheDefs.h index 2c5caa4..df430f4 100644 --- a/iocore/cache/I_CacheDefs.h +++ b/iocore/cache/I_CacheDefs.h @@ -154,15 +154,22 @@ struct RangeSpec { /// Construct as the range ( @a low .. @a high ) Range(uint64_t low, uint64_t high) : _min(low), _max(high) {} - bool isValid() const { return _min <= _max || (0 == _max && 0 < _min); } + /// Test if this range is a trailing (terminal) range. + bool isSuffix() const; + /// Test if this range is a valid range. + bool isValid() const; + /// Adjust the range values based on content size @a len. + bool finalize(uint64_t len); + /// Force the range to an invalid state. + Range& invalidate(); }; /// Current state of the overall specification. /// @internal We can distinguish between @c SINGLE and @c MULTI by looking at the /// size of @a _ranges but we need this to mark @c EMPTY vs. not. enum State { - INVALID, ///< Range parsing failed. EMPTY, ///< No range. + INVALID, ///< Range parsing failed. SINGLE, ///< Single range. MULTI, ///< Multiple ranges. } _state; @@ -171,21 +178,89 @@ struct RangeSpec { /// By separating this out we can avoid allocation in the case of a single /// range value, which is by far the most common ( > 99% in my experience). Range _single; - /// Storage for range values past the first one. - std::vector<Range> _ranges; + /// Storage for range values. + typedef std::vector<Range> RangeBox; + /// The first range is copied here if there is more than one (to simplify). + RangeBox _ranges; - RangeSpec() : _state(EMPTY) - {} + /// Default constructor - invalid range + RangeSpec(); /** Parse a range field and update @a this with the results. @return @c true if @a v was a valid range specifier, @c false otherwise. */ bool parse(char const* v, int len); + /** Validate and convert for a specific content @a length. + + @return @c true if the range is satisfiable per the HTTP spec, @c false otherwise. + Note a range spec with no ranges is always satisfiable. + */ + bool finalize(uint64_t length); + + /** Number of distinct ranges. + @return Number of ranges. + */ size_t count() const; + /// If this is a valid, single range specification. + bool isSingle() const; + protected: self& add(uint64_t low, uint64_t high); }; +inline +RangeSpec::RangeSpec() : _state(EMPTY) +{ +} + +inline bool +RangeSpec::isSingle() const +{ + return SINGLE == _state; +} + +inline size_t +RangeSpec::count() const +{ + return SINGLE == _state ? 1 : _ranges.size(); +} + +inline RangeSpec::Range& +RangeSpec::Range::invalidate() +{ + _min = UINT64_MAX; + _max = 1; + return *this; +} + +inline bool +RangeSpec::Range::isSuffix() const +{ + return 0 == _max && _min > 0; +} + +inline bool +RangeSpec::Range::isValid() const +{ + return _min <= _max || this->isSuffix(); +} + +inline bool +RangeSpec::Range::finalize(uint64_t len) +{ + ink_assert(len > 0); + bool zret = true; // is this range satisfiable for @a len? + if (this->isSuffix()) { + _max = len - 1; + _min = _min > len ? 0 : len - _min; + } else if (_min < len) { + _max = MIN(_max,len); + } else { + this->invalidate(); + zret = false; + } + return zret; +} #endif // __CACHE_DEFS_H__ http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee708499/iocore/cache/P_CacheInternal.h ---------------------------------------------------------------------- diff --git a/iocore/cache/P_CacheInternal.h b/iocore/cache/P_CacheInternal.h index 57c5b0b..5a0b8fc 100644 --- a/iocore/cache/P_CacheInternal.h +++ b/iocore/cache/P_CacheInternal.h @@ -232,6 +232,25 @@ extern int cache_config_mutex_retry_delay; #if TS_USE_INTERIM_CACHE == 1 extern int good_interim_disks; #endif + +/** Range operation tracking. + + This holds a range specification and tracks where in the range the current + cache operation is. +*/ +class CacheRange : public RangeSpec +{ + public: + typedef CacheRange self; ///< Self reference type. + + /// Default constructor + CacheRange() : _offset(0), _idx(-1) { } + + protected: + uint64_t _offset; ///< Current offset into the range. + int _idx; ///< Current range index. (< 0 -> not in a range) +}; + // CacheVC struct CacheVC: public CacheVConnection { @@ -462,6 +481,12 @@ struct CacheVC: public CacheVConnection int header_to_write_len; void *header_to_write; short writer_lock_retry; + /* Range specs for range based operations. + @a req_rng is the range spec in the request from the User Agent. + @a rsp_rng is the range spec sent to the origin server. + */ + CacheRange req_rs; + CacheRange rsp_rs; #if TS_USE_INTERIM_CACHE == 1 InterimCacheVol *interim_vol; MigrateToInterimCache *mts; @@ -628,6 +653,8 @@ free_CacheVC(CacheVC *cont) cont->alternate_index = CACHE_ALT_INDEX_DEFAULT; if (cont->scan_vol_map) ats_free(cont->scan_vol_map); + cont->req_rs.~CacheRange(); + cont->rsp_rs.~CacheRange(); memset((char *) &cont->vio, 0, cont->size_to_init); #ifdef CACHE_STAT_PAGES ink_assert(!cont->stat_link.next && !cont->stat_link.prev); http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ee708499/lib/ts/InkErrno.h ---------------------------------------------------------------------- diff --git a/lib/ts/InkErrno.h b/lib/ts/InkErrno.h index 7f8676e..bb9bfb1 100644 --- a/lib/ts/InkErrno.h +++ b/lib/ts/InkErrno.h @@ -66,6 +66,7 @@ #define ECACHE_NOT_READY (CACHE_ERRNO+7) #define ECACHE_ALT_MISS (CACHE_ERRNO+8) #define ECACHE_BAD_READ_REQUEST (CACHE_ERRNO+9) +#define ECACHE_BAD_REQUEST_RANGE (CACHE_ERRNO+10) #define EHTTP_ERROR (HTTP_ERRNO+0)