You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2012/05/18 18:58:28 UTC
git commit: TS-475: Support for efficient processing of single range
requests.
Updated Branches:
refs/heads/master 9c5bac58a -> e80215f4e
TS-475: Support for efficient processing of single range requests.
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/e80215f4
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/e80215f4
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/e80215f4
Branch: refs/heads/master
Commit: e80215f4e4ab4e3505b74652c31025708ce0e076
Parents: 9c5bac5
Author: Alan M. Carroll <am...@network-geographics.com>
Authored: Fri May 18 11:56:06 2012 -0500
Committer: Alan M. Carroll <am...@network-geographics.com>
Committed: Fri May 18 11:56:06 2012 -0500
----------------------------------------------------------------------
CHANGES | 3 +
iocore/cache/Cache.cc | 12 ++
iocore/cache/CacheRead.cc | 90 ++++++++++---
iocore/cache/CacheWrite.cc | 33 ++++-
iocore/cache/P_CacheInternal.h | 17 +++-
proxy/Transform.cc | 249 ++++-------------------------------
proxy/Transform.h | 37 +++++-
proxy/TransformInternal.h | 32 +----
proxy/http/HttpSM.cc | 234 +++++++++++++++++++++++++++++---
proxy/http/HttpSM.h | 7 +-
proxy/http/HttpTransact.cc | 112 +++++++++++++---
proxy/http/HttpTransact.h | 34 ++++--
proxy/http/HttpTunnel.cc | 25 +++-
13 files changed, 546 insertions(+), 339 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index b4371e9..27893f1 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,8 @@
-*- coding: utf-8 -*-
Changes with Apache Traffic Server 3.1.4
+ *) [TS-475] Accelerated single range requests.
+ Based on initial work by jumby, with help from bwyatt.
+
*) [TS-1236] HTTP Accept filters do not work on Illumos.
*) [TS-1075] Workarounds for linux auto-port issues in transparent
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/iocore/cache/Cache.cc
----------------------------------------------------------------------
diff --git a/iocore/cache/Cache.cc b/iocore/cache/Cache.cc
index 587f749..864918a 100644
--- a/iocore/cache/Cache.cc
+++ b/iocore/cache/Cache.cc
@@ -1874,6 +1874,18 @@ CacheVC::handleReadDone(int event, Event *e) {
}
#endif
Doc *doc = (Doc *) buf->data();
+
+ if (is_debug_tag_set("cache_read")) {
+ char xt[33];
+ Debug("cache_read"
+ , "Read fragment %s Length %d/%d/%"PRId64"[pre=%d] vc=%s doc=%s %d frags"
+ , doc->key.toHexStr(xt), doc->data_len(), doc->len, doc->total_len, doc->prefix_len()
+ , f.single_fragment ? "single" : "multi"
+ , doc->single_fragment() ? "single" : "multi"
+ , doc->nfrags()
+ );
+ }
+
// put into ram cache?
if (io.ok() &&
((doc->first_key == *read_key) || (doc->key == *read_key) || STORE_COLLISION) && doc->magic == DOC_MAGIC) {
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/iocore/cache/CacheRead.cc
----------------------------------------------------------------------
diff --git a/iocore/cache/CacheRead.cc b/iocore/cache/CacheRead.cc
index d10f851..b23a13c 100644
--- a/iocore/cache/CacheRead.cc
+++ b/iocore/cache/CacheRead.cc
@@ -624,43 +624,81 @@ CacheVC::openReadMain(int event, Event * e)
int64_t bytes = doc->len - doc_pos;
IOBufferBlock *b = NULL;
if (seek_to) { // handle do_io_pread
- if (seek_to >= (int64_t)doc_len) {
+ if (seek_to >= doc_len) {
vio.ndone = doc_len;
return calluser(VC_EVENT_EOS);
}
Doc *first_doc = (Doc*)first_buf->data();
- Frag *first_frag = first_doc->frags();
- if (!f.single_fragment) {
- // find the target fragment
- int i = 0;
- for (; i < (int)first_doc->nfrags(); i++)
- if (seek_to < (int64_t)first_frag[i].offset) break;
- if (i >= (int)first_doc->nfrags()) {
- Warning("bad fragment header");
- return calluser(VC_EVENT_ERROR);
+ Frag *frags = first_doc->frags();
+ if (is_debug_tag_set("cache_seek")) {
+ char b[33], c[33];
+ Debug("cache_seek", "Seek @ %"PRId64" in %s from #%d @ %"PRId64"/%d:%s",
+ seek_to, first_key.toHexStr(b), fragment, doc_pos, doc->len, doc->key.toHexStr(c));
+ }
+ /* Because single fragment objects can migrate to hang off an alt vector
+ they can appear to the VC as multi-fragment when they are not really.
+ The essential difference is the existence of a fragment table. All
+ fragments past the header fragment have the same value for this check
+ and it's consistent with the existence of a frag table in first_doc.
+ f.single_fragment is not (it can be false when this check is true).
+ */
+ if (!doc->single_fragment()) {
+ int target = 0;
+ uint64_t next_off = frags[target].offset;
+ int lfi = static_cast<int>(first_doc->nfrags()) - 1;
+ ink_debug_assert(lfi >= 0); // because it's not a single frag doc.
+
+ /* Note: frag[i].offset is the offset of the first byte past the
+ i'th fragment. So frag[0].offset is the offset of the first
+ byte of fragment 1. In addition the # of fragments is one
+ more than the fragment table length, the start of the last
+ fragment being the last offset in the table.
+ */
+ if (fragment == 0 ||
+ seek_to < frags[fragment-1].offset ||
+ (fragment <= lfi && frags[fragment].offset <= seek_to)
+ ) {
+ // search from frag 0 on to find the proper frag
+ while (seek_to >= next_off && target < lfi) {
+ next_off = frags[++target].offset;
+ }
+ if (target == lfi && seek_to >= next_off) ++target;
+ } else { // shortcut if we are in the fragment already
+ target = fragment;
}
- // fragment is the current fragment
- // key is the next key (fragment + 1)
- if (i != fragment) {
- i--; // read will increment the key and fragment
- while (i > fragment) {
+ if (target != fragment) {
+ // Lread will read the next fragment always, so if that
+ // is the one we want, we don't need to do anything
+ int cfi = fragment;
+ --target;
+ while (target > fragment) {
next_CacheKey(&key, &key);
- fragment++;
+ ++fragment;
}
- while (i < fragment) {
+ while (target < fragment) {
prev_CacheKey(&key, &key);
- fragment--;
+ --fragment;
+ }
+
+ if (is_debug_tag_set("cache_seek")) {
+ char target_key_str[33];
+ key.toHexStr(target_key_str);
+ Debug("cache_seek", "Seek #%d @ %"PRId64" -> #%d @ %"PRId64":%s", cfi, doc_pos, target, seek_to, target_key_str);
}
goto Lread;
}
}
- if (fragment)
- doc_pos = doc->prefix_len() + seek_to - (int64_t)first_frag[fragment-1].offset;
- else
- doc_pos = doc->prefix_len() + seek_to;
+ doc_pos = doc->prefix_len() + seek_to;
+ if (fragment) doc_pos -= static_cast<int64_t>(frags[fragment-1].offset);
vio.ndone = 0;
seek_to = 0;
ntodo = vio.ntodo();
+ bytes = doc->len - doc_pos;
+ if (is_debug_tag_set("cache_seek")) {
+ char target_key_str[33];
+ key.toHexStr(target_key_str);
+ Debug("cache_seek", "Read # %d @ %"PRId64"/%d for %"PRId64, fragment, doc_pos, doc->len, bytes);
+ }
}
if (ntodo <= 0)
return EVENT_CONT;
@@ -1055,6 +1093,14 @@ CacheVC::openReadStartHead(int event, Event * e)
doc_pos = doc->prefix_len();
doc_len = doc->total_len;
}
+
+ if (is_debug_tag_set("cache_read")) { // amc debug
+ char xt[33],yt[33];
+ Debug("cache_rad", "CacheReadStartHead - read %s target %s - %s %d of %"PRId64" bytes, %d fragments",
+ doc->key.toHexStr(xt), key.toHexStr(yt),
+ f.single_fragment ? "single" : "multi",
+ doc->len, doc->total_len, doc->nfrags());
+ }
// the first fragment might have been gc'ed. Make sure the first
// fragment is there before returning CACHE_EVENT_OPEN_READ
if (!f.single_fragment)
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/iocore/cache/CacheWrite.cc
----------------------------------------------------------------------
diff --git a/iocore/cache/CacheWrite.cc b/iocore/cache/CacheWrite.cc
index df18b6c..a75b5fb 100644
--- a/iocore/cache/CacheWrite.cc
+++ b/iocore/cache/CacheWrite.cc
@@ -179,10 +179,24 @@ CacheVC::handleWrite(int event, Event *e)
// plain write case
ink_assert(!trigger);
- if (f.use_first_key && fragment) {
- frag_len = (fragment-1) * sizeof(Frag);
- } else
- frag_len = 0;
+ frag_len = 0;
+ if (f.use_first_key) { // Writing the header fragment.
+ if (fragment) { // Multi-fragment from cache fill.
+ frag_len = (fragment-1) * sizeof(Frag);
+ } else if (first_buf) {
+ Doc* old_header = reinterpret_cast<Doc*>(first_buf->data());
+ if (!frag && old_header->flen && !f.single_fragment) {
+ // There's a fragment table we need to preserve.
+ frag_len = old_header->flen;
+ if (is_debug_tag_set("cache_update")) {
+ char x[33];
+ Debug("cache_update", "Preserving fragment table (%d in %d bytes) for %s",
+ old_header->nfrags(), frag_len, first_key.toHexStr(x));
+ }
+ }
+ }
+ }
+
set_agg_write_in_progress();
POP_HANDLER;
agg_len = vol->round_to_approx_size(write_len + header_len + frag_len + sizeofDoc);
@@ -784,8 +798,13 @@ agg_copy(char *p, CacheVC *vc)
doc->key = vc->key;
dir_set_head(&vc->dir, !vc->fragment);
}
- if (doc->flen)
- memcpy(doc->frags(), &vc->frag[0], doc->flen);
+ if (doc->flen) {
+ // There's a fragment table to write.
+ // If this was a cache fill, the frag table is internal.
+ // If it's a header update (e.g. 304 NOT MODIFIED return on
+ // stale object) then the frag table is in the first_buf.
+ memcpy(doc->frags(), vc->get_frag_table(), doc->flen);
+ }
#ifdef HTTP_CACHE
if (vc->f.rewrite_resident_alt) {
@@ -1507,7 +1526,7 @@ CacheVC::openWriteStartDone(int event, Event *e)
}
/* INKqa07123.
- A directory entry which is nolonger valid may have been overwritten.
+ A directory entry which is no longer valid may have been overwritten.
We need to start afresh from the beginning by setting last_collision
to NULL.
*/
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/iocore/cache/P_CacheInternal.h
----------------------------------------------------------------------
diff --git a/iocore/cache/P_CacheInternal.h b/iocore/cache/P_CacheInternal.h
index 260edea..69e298f 100644
--- a/iocore/cache/P_CacheInternal.h
+++ b/iocore/cache/P_CacheInternal.h
@@ -361,6 +361,12 @@ struct CacheVC: public CacheVConnection
virtual bool set_disk_io_priority(int priority);
virtual int get_disk_io_priority();
+ /** Get the fragment table.
+ @return The address of the start of the fragment table,
+ or @c NULL if there is no fragment table.
+ */
+ virtual Frag* get_frag_table();
+
// offsets from the base stat
#define CACHE_STAT_ACTIVE 0
#define CACHE_STAT_SUCCESS 1
@@ -436,7 +442,7 @@ struct CacheVC: public CacheVConnection
int base_stat;
int recursive;
int closed;
- int64_t seek_to; // pread offset
+ uint64_t seek_to; // pread offset
int64_t offset; // offset into 'blocks' of data to write
int64_t writer_offset; // offset of the writer for reading from a writer
int64_t length; // length of data available to write
@@ -740,6 +746,15 @@ CacheVC::writer_done()
return false;
}
+TS_INLINE Frag*
+CacheVC::get_frag_table() {
+ if (frag)
+ return frag;
+ else if (first_buf)
+ return reinterpret_cast<Doc*>(first_buf->data())->frags();
+ return 0;
+}
+
TS_INLINE int
Vol::close_write(CacheVC *cont)
{
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/proxy/Transform.cc
----------------------------------------------------------------------
diff --git a/proxy/Transform.cc b/proxy/Transform.cc
index 34f819e..8a71878 100644
--- a/proxy/Transform.cc
+++ b/proxy/Transform.cc
@@ -115,17 +115,9 @@ TransformProcessor::null_transform(ProxyMutex *mutex)
-------------------------------------------------------------------------*/
INKVConnInternal *
-TransformProcessor::range_transform(ProxyMutex *mut, MIMEField *range_field, HTTPInfo *cache_obj, HTTPHdr *transform_resp, bool & b)
+TransformProcessor::range_transform(ProxyMutex *mut, RangeRecord *ranges, bool unsatisfiable, int num_fields, HTTPHdr *transform_resp, const char * content_type, int content_type_len, int64_t content_length)
{
- RangeTransform *range_transform = NEW(new RangeTransform(mut, range_field, cache_obj, transform_resp));
-
- b = range_transform->is_range_unsatisfiable();
-
- if (b || range_transform->is_this_range_not_handled()) {
- delete range_transform;
- return NULL;
- }
-
+ RangeTransform *range_transform = NEW(new RangeTransform(mut, ranges, unsatisfiable, num_fields, transform_resp, content_type, content_type_len, content_length));
return range_transform;
}
@@ -735,56 +727,24 @@ TransformTest::run()
-///////////////////////////////////////////////////////////////////
-/// RangeTransform implementation
-/// handling Range requests from clients
-///////////////////////////////////////////////////////////////////
-
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
-inline static int
-num_chars_for_int(int64_t i)
-{
- int k = 1;
-
- if (i < 0)
- return 0;
-
- while ((i /= 10) != 0)
- ++k;
-
- return k;
-}
-
-
-/*-------------------------------------------------------------------------
- -------------------------------------------------------------------------*/
-
-RangeTransform::RangeTransform(ProxyMutex *mut, MIMEField *range_field, HTTPInfo *cache_obj, HTTPHdr *transform_resp)
+RangeTransform::RangeTransform(ProxyMutex *mut, RangeRecord *ranges,bool unsatisfiable, int num_fields, HTTPHdr * transform_resp, const char * content_type, int content_type_len, int64_t content_length)
: INKVConnInternal(NULL, reinterpret_cast<TSMutex>(mut)),
- m_output_buf(NULL),
- m_output_reader(NULL),
- m_range_field(range_field),
- m_transform_resp(transform_resp),
- m_output_vio(NULL),
- m_unsatisfiable_range(true),
- m_not_handle_range(false),
- m_num_range_fields(0),
- m_current_range(0), m_content_type(NULL), m_content_type_len(0), m_ranges(NULL), m_output_cl(0), m_done(0)
+ m_output_buf(NULL),
+ m_output_reader(NULL),
+ m_transform_resp(transform_resp),
+ m_output_vio(NULL),
+ m_unsatisfiable_range(unsatisfiable),
+ m_range_content_length(0),
+ m_num_range_fields(num_fields),
+ m_current_range(0), m_content_type(content_type), m_content_type_len(content_type_len), m_ranges(ranges), m_output_cl(content_length), m_done(0)
{
SET_HANDLER(&RangeTransform::handle_event);
- m_content_type = cache_obj->
- response_get()->value_get(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, &m_content_type_len);
-
- m_content_length = cache_obj->object_size_get();
- m_num_chars_for_cl = num_chars_for_int(m_content_length);
-
- parse_range_and_compare();
- calculate_output_cl();
-
- Debug("transform_range", "RangeTransform creation finishes");
+ m_num_chars_for_cl = num_chars_for_int(m_range_content_length);
+ Debug("http_trans", "RangeTransform creation finishes");
}
@@ -793,8 +753,6 @@ RangeTransform::RangeTransform(ProxyMutex *mut, MIMEField *range_field, HTTPInfo
RangeTransform::~RangeTransform()
{
- if (m_ranges)
- delete[]m_ranges;
if (m_output_buf)
free_MIOBuffer(m_output_buf);
}
@@ -803,119 +761,6 @@ RangeTransform::~RangeTransform()
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
-void
-RangeTransform::parse_range_and_compare()
-{
- // note: unsatisfiable_range is initialized to true in constructor
- int prev_good_range, i;
- const char *value;
- int value_len;
- HdrCsvIter csv;
- const char *s, *e;
-
- if (m_content_length <= 0)
- return;
-
- ink_assert(m_range_field != NULL);
-
- m_num_range_fields = 0;
- value = csv.get_first(m_range_field, &value_len);
-
- while (value) {
- m_num_range_fields++;
- value = csv.get_next(&value_len);
- }
-
- if (m_num_range_fields <= 0)
- return;
-
- m_ranges = NEW(new RangeRecord[m_num_range_fields]);
-
- value = csv.get_first(m_range_field, &value_len);
-
- i = 0;
- prev_good_range = -1;
- // Currently HTTP/1.1 only defines bytes Range
- if (ptr_len_ncmp(value, value_len, "bytes=", 6) == 0) {
- while (value) {
- // If delimiter '-' is missing
- if (!(e = (const char *) memchr(value, '-', value_len))) {
- m_unsatisfiable_range = true;
- return;
- }
-
- if ( memcmp(value,"bytes=",6) == 0 ) {
- s = value + 6;
- }
- else {
- s = value;
- }
-
- m_ranges[i]._start = ((s==e)?-1:mime_parse_int64(s, e));
-
- e++;
- s = e;
- e = value + value_len;
- if ( e && *(e-1) == '-') { //open-ended Range: bytes=10-\r\n\r\n should be supported
- m_ranges[i]._end = -1;
- }
- else {
- m_ranges[i]._end = mime_parse_int64(s, e);
- }
-
- // check and change if necessary whether this is a right entry
- // the last _end bytes are required
- if (m_ranges[i]._start == -1 && m_ranges[i]._end > 0) {
- if (m_ranges[i]._end > m_content_length)
- m_ranges[i]._end = m_content_length;
-
- m_ranges[i]._start = m_content_length - m_ranges[i]._end;
- m_ranges[i]._end = m_content_length - 1;
- }
- // open start
- else if (m_ranges[i]._start >= 0 && m_ranges[i]._end == -1) {
- if (m_ranges[i]._start >= m_content_length)
- m_ranges[i]._start = -1;
- else
- m_ranges[i]._end = m_content_length - 1;
- }
- // "normal" Range - could be wrong if _end<_start
- else if (m_ranges[i]._start >= 0 && m_ranges[i]._end >= 0) {
- if (m_ranges[i]._start > m_ranges[i]._end || m_ranges[i]._start >= m_content_length)
- m_ranges[i]._start = m_ranges[i]._end = -1;
- else if (m_ranges[i]._end >= m_content_length)
- m_ranges[i]._end = m_content_length - 1;
- }
-
- else
- m_ranges[i]._start = m_ranges[i]._end = -1;
-
- // this is a good Range entry
- if (m_ranges[i]._start != -1) {
- if (m_unsatisfiable_range) {
- m_unsatisfiable_range = false;
- // initialize m_current_range to the first good Range
- m_current_range = i;
- }
- // currently we don't handle out-of-order Range entry
- else if (prev_good_range >= 0 && m_ranges[i]._start <= m_ranges[prev_good_range]._end) {
- m_not_handle_range = true;
- break;
- }
-
- prev_good_range = i;
- }
-
- value = csv.get_next(&value_len);
- i++;
- }
- }
-}
-
-
-/*-------------------------------------------------------------------------
- -------------------------------------------------------------------------*/
-
int
RangeTransform::handle_event(int event, void *edata)
{
@@ -923,7 +768,7 @@ RangeTransform::handle_event(int event, void *edata)
if (m_closed) {
if (m_deletable) {
- Debug("transform_range", "RangeTransform destroy: %p", this);
+ Debug("http_trans", "RangeTransform destroy: %p ndone=%"PRId64, this, m_output_vio ? m_output_vio->ndone : 0);
delete this;
}
} else {
@@ -1035,7 +880,7 @@ RangeTransform::transform_to_range()
add_boundary(true);
}
- Debug("transform_range", "total bytes of Range response body is %"PRId64"", m_done);
+ Debug("http_trans", "total bytes of Range response body is %"PRId64, m_done);
m_output_vio->nbytes = m_done;
m_output_vio->reenable();
@@ -1093,8 +938,6 @@ static char bound[] = "RANGE_SEPARATOR";
static char range_type[] = "multipart/byteranges; boundary=RANGE_SEPARATOR";
static char cont_type[] = "Content-type: ";
static char cont_range[] = "Content-range: bytes ";
-static int sub_header_size = sizeof(cont_type) - 1 + 2 + sizeof(cont_range) - 1 + 4;
-static int boundary_size = 2 + sizeof(bound) - 1 + 2;
void
RangeTransform::add_boundary(bool end)
@@ -1127,7 +970,7 @@ RangeTransform::add_sub_header(int index)
m_done += m_output_buf->write("\r\n", 2);
m_done += m_output_buf->write(cont_range, sizeof(cont_range) - 1);
- snprintf(numbers, sizeof(numbers), "%" PRId64 "d-%" PRId64 "d/%" PRId64 "d", m_ranges[index]._start, m_ranges[index]._end, m_content_length);
+ snprintf(numbers, sizeof(numbers), "%" PRId64 "d-%" PRId64 "d/%" PRId64 "d", m_ranges[index]._start, m_ranges[index]._end, m_output_cl);
len = strlen(numbers);
if (len < RANGE_NUMBERS_LENGTH)
m_done += m_output_buf->write(numbers, len);
@@ -1148,8 +991,9 @@ RangeTransform::change_response_header()
MIMEField *field;
char *reason_phrase;
HTTPStatus status_code;
-
- ink_assert(m_transform_resp->field_find(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE) == NULL);
+
+ ink_release_assert(m_transform_resp);
+ ink_release_assert(m_transform_resp->field_find(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE) == NULL);
status_code = HTTP_STATUS_PARTIAL_CONTENT;
m_transform_resp->status_set(status_code);
@@ -1157,62 +1001,19 @@ RangeTransform::change_response_header()
m_transform_resp->reason_set(reason_phrase, strlen(reason_phrase));
// set the right Content-Type for multiple entry Range
- if (m_num_range_fields > 1) {
- field = m_transform_resp->field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+ field = m_transform_resp->field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
- if (field != NULL)
- m_transform_resp->field_delete(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+ if (field != NULL)
+ m_transform_resp->field_delete(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
- field = m_transform_resp->field_create(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
- field->value_append(m_transform_resp->m_heap, m_transform_resp->m_mime, range_type, sizeof(range_type) - 1);
+ field = m_transform_resp->field_create(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+ field->value_append(m_transform_resp->m_heap, m_transform_resp->m_mime, range_type, sizeof(range_type) - 1);
- m_transform_resp->field_attach(field);
- }
+ m_transform_resp->field_attach(field);
- else {
- char numbers[RANGE_NUMBERS_LENGTH];
-
- field = m_transform_resp->field_create(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE);
- snprintf(numbers, sizeof(numbers), "bytes %" PRId64 "-%" PRId64 "/%" PRId64 "", m_ranges[0]._start, m_ranges[0]._end, m_content_length);
- field->value_append(m_transform_resp->m_heap, m_transform_resp->m_mime, numbers, strlen(numbers));
- m_transform_resp->field_attach(field);
- }
}
#undef RANGE_NUMBERS_LENGTH
-
-/*-------------------------------------------------------------------------
- -------------------------------------------------------------------------*/
-
-void
-RangeTransform::calculate_output_cl()
-{
- int i;
-
- if (m_unsatisfiable_range)
- return;
-
- if (m_num_range_fields == 1)
- m_output_cl = m_ranges[0]._end - m_ranges[0]._start + 1;
-
- else {
- for (i = 0; i < m_num_range_fields; i++) {
- if (m_ranges[i]._start >= 0) {
- m_output_cl += boundary_size;
- m_output_cl += sub_header_size + m_content_type_len;
- m_output_cl += num_chars_for_int(m_ranges[i]._start)
- + num_chars_for_int(m_ranges[i]._end) + m_num_chars_for_cl + 2;
- m_output_cl += m_ranges[i]._end - m_ranges[i]._start + 1;
- m_output_cl += 2;
- }
- }
-
- m_output_cl += boundary_size + 2;
- }
-
- Debug("transform_range", "Pre-calculated Content-Length for Range response is %"PRId64"", m_output_cl);
-}
-
#endif // TS_NO_TRANSFORM
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/proxy/Transform.h
----------------------------------------------------------------------
diff --git a/proxy/Transform.h b/proxy/Transform.h
index a9e95c7..af27870 100644
--- a/proxy/Transform.h
+++ b/proxy/Transform.h
@@ -25,13 +25,21 @@
#define __TRANSFORM_H__
#include "P_EventSystem.h"
-
#include "HTTP.h"
#include "InkAPIInternal.h"
-
#define TRANSFORM_READ_READY (TRANSFORM_EVENTS_START + 0)
+typedef struct _RangeRecord
+{
+ _RangeRecord() :
+ _start(-1), _end(-1), _done_byte(-1)
+ { }
+
+ int64_t _start;
+ int64_t _end;
+ int64_t _done_byte;
+} RangeRecord;
class TransformProcessor
{
@@ -41,11 +49,9 @@ public:
public:
VConnection * open(Continuation * cont, APIHook * hooks);
INKVConnInternal *null_transform(ProxyMutex * mutex);
- INKVConnInternal *range_transform(ProxyMutex * mutex, MIMEField * range_field, HTTPInfo * cache_obj,
- HTTPHdr * transform_resp, bool & b);
+ INKVConnInternal *range_transform(ProxyMutex * mutex, RangeRecord * ranges, bool, int, HTTPHdr *, const char * content_type, int content_type_len, int64_t content_length);
};
-
#ifdef TS_HAS_TESTS
class TransformTest
{
@@ -54,6 +60,27 @@ public:
};
#endif
+///////////////////////////////////////////////////////////////////
+/// RangeTransform implementation
+/// handling Range requests from clients
+///////////////////////////////////////////////////////////////////
+
+/*-------------------------------------------------------------------------
+ -------------------------------------------------------------------------*/
+
+inline static int
+num_chars_for_int(int64_t i)
+{
+ int k = 1;
+
+ if (i < 0)
+ return 0;
+
+ while ((i /= 10) != 0)
+ ++k;
+
+ return k;
+}
extern TransformProcessor transformProcessor;
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/proxy/TransformInternal.h
----------------------------------------------------------------------
diff --git a/proxy/TransformInternal.h b/proxy/TransformInternal.h
index 641868d..45781a7 100644
--- a/proxy/TransformInternal.h
+++ b/proxy/TransformInternal.h
@@ -115,10 +115,10 @@ public:
class RangeTransform:public INKVConnInternal
{
public:
- RangeTransform(ProxyMutex * mutex, MIMEField * range_field, HTTPInfo * cache_obj, HTTPHdr * transform_resp);
+ RangeTransform(ProxyMutex * mutex, RangeRecord * ranges, bool, int, HTTPHdr *transform_resp, const char * content_type, int content_type_len, int64_t content_length);
~RangeTransform();
- void parse_range_and_compare();
+ // void parse_range_and_compare();
int handle_event(int event, void *edata);
void transform_to_range();
@@ -126,35 +126,18 @@ public:
void add_sub_header(int index);
void change_response_header();
void calculate_output_cl();
- bool is_this_range_not_handled()
- {
- return m_not_handle_range;
- }
- bool is_range_unsatisfiable()
- {
- return m_unsatisfiable_range;
- }
-
- typedef struct _RangeRecord
- {
- _RangeRecord() :
- _start(-1), _end(-1), _done_byte(-1)
- { }
-
- int64_t _start;
- int64_t _end;
- int64_t _done_byte;
- } RangeRecord;
public:
MIOBuffer * m_output_buf;
IOBufferReader *m_output_reader;
- MIMEField *m_range_field;
+
+
+ // MIMEField *m_range_field;
HTTPHdr *m_transform_resp;
VIO *m_output_vio;
bool m_unsatisfiable_range;
- bool m_not_handle_range;
- int64_t m_content_length;
+ // bool m_not_handle_range;
+ int64_t m_range_content_length;
int m_num_chars_for_cl;
int m_num_range_fields;
int m_current_range;
@@ -163,6 +146,7 @@ public:
RangeRecord *m_ranges;
int64_t m_output_cl;
int64_t m_done;
+
};
#define PREFETCH
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/proxy/http/HttpSM.cc
----------------------------------------------------------------------
diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc
index f456181..18b2aab 100644
--- a/proxy/http/HttpSM.cc
+++ b/proxy/http/HttpSM.cc
@@ -37,6 +37,7 @@
#include "PluginVC.h"
#include "ReverseProxy.h"
#include "RemapProcessor.h"
+#include "Transform.h"
#include "HttpPages.h"
@@ -87,6 +88,12 @@ static uint32_t val[MAX_SCATTER_LEN];
static uint16_t to[MAX_SCATTER_LEN];
static int scat_count = 0;
+static char bound[] = "RANGE_SEPARATOR";
+//
+static char cont_type[] = "Content-type: ";
+static char cont_range[] = "Content-range: bytes ";
+static int sub_header_size = sizeof(cont_type) - 1 + 2 + sizeof(cont_range) - 1 + 4;
+static int boundary_size = 2 + sizeof(bound) - 1 + 2;
/**
* Takes two milestones and returns the difference.
@@ -3024,7 +3031,7 @@ HttpSM::tunnel_handler_ua(int event, HttpTunnelConsumer * c)
//
HttpTransact::Source_t original_source = t_state.source;
if (HttpTransact::SOURCE_TRANSFORM == original_source &&
- t_state.range_setup == HttpTransact::RANGE_TRANSFORM) {
+ t_state.range_setup != HttpTransact::RANGE_NONE) {
original_source = t_state.pre_transform_source;
}
@@ -3571,7 +3578,7 @@ HttpSM::tunnel_handler_transform_write(int event, HttpTunnelConsumer * c)
// all other transforms are plugin driven and the difference between
// source data and final data should represent the transformation delta
//
- if (t_state.range_setup != HttpTransact::RANGE_TRANSFORM) {
+ if (t_state.range_setup == HttpTransact::RANGE_NONE) {
switch (t_state.pre_transform_source) {
case HttpTransact::SOURCE_HTTP_ORIGIN_SERVER:
server_response_body_bytes = client_response_body_bytes;
@@ -3878,37 +3885,220 @@ HttpSM::do_hostdb_update_if_necessary()
return;
}
+void
+HttpSM::parse_range_and_compare(MIMEField *field, int64_t content_length)
+{
+ // note: unsatisfiable_range is initialized to true in constructor
+ int prev_good_range, i;
+ const char *value;
+ int value_len;
+ HdrCsvIter csv;
+ const char *s, *e;
+
+ if (content_length <= 0)
+ return;
-// this function first checks if cached response has Accept-Ranges and
-// Content-Length header and is HTTP/1.1. && There is no other plugins
-// hooked to TS_HTTP_RESPONSE_TRANSFORM_HOOK.
-// Then setup Range transformation if necessary
+ ink_assert(field != NULL);
+
+ t_state.num_range_fields = 0;
+ value = csv.get_first(field, &value_len);
+
+ while (value) {
+ t_state.num_range_fields++;
+ value = csv.get_next(&value_len);
+ }
+
+ if (t_state.num_range_fields <= 0)
+ return;
+
+ t_state.ranges = NEW(new RangeRecord[t_state.num_range_fields]);
+
+ value = csv.get_first(field, &value_len);
+
+ i = 0;
+ prev_good_range = -1;
+ // Currently HTTP/1.1 only defines bytes Range
+ if (ptr_len_ncmp(value, value_len, "bytes=", 6) == 0) {
+ while (value) {
+ // If delimiter '-' is missing
+ if (!(e = (const char *) memchr(value, '-', value_len))) {
+ t_state.range_setup = HttpTransact::RANGE_NOT_SATISFIABLE;
+ t_state.num_range_fields = -1;
+ return;
+ }
+
+ if ( memcmp(value,"bytes=",6) == 0 ) {
+ s = value + 6;
+ }
+ else {
+ s = value;
+ }
+ t_state.ranges[i]._start = ((s==e)?-1:mime_parse_int64(s, e));
+
+ e++;
+ s = e;
+ e = value + value_len;
+ if ( e && *(e-1) == '-') { //open-ended Range: bytes=10-\r\n\r\n should be supported
+ t_state.ranges[i]._end = -1;
+ }
+ else {
+ t_state.ranges[i]._end = mime_parse_int64(s, e);
+ }
+
+ // check and change if necessary whether this is a right entry
+ // the last _end bytes are required
+ if (t_state.ranges[i]._start == -1 && t_state.ranges[i]._end > 0) {
+ if (t_state.ranges[i]._end > content_length)
+ t_state.ranges[i]._end = content_length;
+
+ t_state.ranges[i]._start = content_length - t_state.ranges[i]._end;
+ t_state.ranges[i]._end = content_length - 1;
+ }
+ // open start
+ else if (t_state.ranges[i]._start >= 0 && t_state.ranges[i]._end == -1) {
+ if (t_state.ranges[i]._start >= content_length)
+ t_state.ranges[i]._start = -1;
+ else
+ t_state.ranges[i]._end = content_length - 1;
+ }
+ // "normal" Range - could be wrong if _end<_start
+ else if (t_state.ranges[i]._start >= 0 && t_state.ranges[i]._end >= 0) {
+ if (t_state.ranges[i]._start > t_state.ranges[i]._end || t_state.ranges[i]._start >= content_length)
+ t_state.ranges[i]._start = t_state.ranges[i]._end = -1;
+ else if (t_state.ranges[i]._end >= content_length)
+ t_state.ranges[i]._end = content_length - 1;
+ }
+
+ else
+ t_state.ranges[i]._start = t_state.ranges[i]._end = -1;
+
+ // this is a good Range entry
+ if (t_state.ranges[i]._start != -1) {
+ if (t_state.unsatisfiable_range) {
+ t_state.unsatisfiable_range = false;
+ // initialize t_state.current_range to the first good Range
+ t_state.current_range = i;
+ }
+ // currently we don't handle out-of-order Range entry
+ else if (prev_good_range >= 0 && t_state.ranges[i]._start <= t_state.ranges[prev_good_range]._end) {
+ t_state.not_handle_range = true;
+ break;
+ }
+
+ prev_good_range = i;
+ }
+
+ value = csv.get_next(&value_len);
+ i++;
+ }
+ }
+}
+
+void
+HttpSM::calculate_output_cl(int64_t content_length, int64_t num_chars)
+{
+ int i;
+
+ if (t_state.unsatisfiable_range)
+ return;
+
+ if (t_state.num_range_fields == 1) {
+ t_state.range_output_cl = t_state.ranges[0]._end - t_state.ranges[0]._start + 1;
+ }
+ else {
+ for (i = 0; i < t_state.num_range_fields; i++) {
+ if (t_state.ranges[i]._start >= 0) {
+ t_state.range_output_cl += boundary_size;
+ t_state.range_output_cl += sub_header_size + content_length;
+ t_state.range_output_cl += num_chars_for_int(t_state.ranges[i]._start)
+ + num_chars_for_int(t_state.ranges[i]._end) + num_chars + 2;
+ t_state.range_output_cl += t_state.ranges[i]._end - t_state.ranges[i]._start + 1;
+ t_state.range_output_cl += 2;
+ }
+ }
+
+ t_state.range_output_cl += boundary_size + 2;
+ }
+
+ Debug("http_range", "Pre-calculated Content-Length for Range response is %"PRId64, t_state.range_output_cl);
+}
+
+void
+HttpSM::do_range_parse(MIMEField *range_field)
+{
+
+ //bool res = false;
+
+ int64_t content_length = t_state.cache_info.object_read->object_size_get();
+ int64_t num_chars_for_cl = num_chars_for_int(content_length);
+
+ parse_range_and_compare(range_field, content_length);
+ calculate_output_cl(content_length, num_chars_for_cl);
+
+ if (t_state.unsatisfiable_range) {
+ t_state.range_setup = HttpTransact::RANGE_NOT_SATISFIABLE;
+ return;
+ }
+
+ if (t_state.not_handle_range) {
+ t_state.range_setup = HttpTransact::RANGE_NOT_HANDLED;
+ return;
+ }
+
+ t_state.range_setup = HttpTransact::RANGE_REQUESTED;
+}
+
+// this function looks for any Range: headers, parses them and either
+// sets up a transform processor to handle the request OR defers to the
+// HttpTunnel
void
HttpSM::do_range_setup_if_necessary()
{
MIMEField *field;
INKVConnInternal *range_trans;
- bool res = false;
-
+ int field_content_type_len = -1;
+ const char * content_type;
+
ink_assert(t_state.cache_info.object_read != NULL);
-
+
field = t_state.hdr_info.client_request.field_find(MIME_FIELD_RANGE, MIME_LEN_RANGE);
ink_assert(field != NULL);
-
+
t_state.range_setup = HttpTransact::RANGE_NONE;
if (t_state.method == HTTP_WKSIDX_GET && t_state.hdr_info.client_request.version_get() == HTTPVersion(1, 1)) {
- if (api_hooks.get(TS_HTTP_RESPONSE_TRANSFORM_HOOK) == NULL) {
- // We may still not do Range if it is out of order Range.
- range_trans = transformProcessor.range_transform(mutex, field,
- t_state.cache_info.object_read,
- &t_state.hdr_info.transform_response, res);
- if (range_trans != NULL) {
- api_hooks.append(TS_HTTP_RESPONSE_TRANSFORM_HOOK, range_trans);
- t_state.range_setup = HttpTransact::RANGE_TRANSFORM;
- } else if (res)
- t_state.range_setup = HttpTransact::RANGE_NOT_SATISFIABLE;
- else
- t_state.range_setup = HttpTransact::RANGE_NOT_HANDLED;
+ do_range_parse(field);
+
+ if (t_state.range_setup == HttpTransact::RANGE_REQUESTED &&
+ t_state.num_range_fields > 1 &&
+ api_hooks.get(TS_HTTP_RESPONSE_TRANSFORM_HOOK) == NULL
+ )
+ {
+ Debug("http_trans", "Handling multiple Range: requests");
+ content_type = t_state.cache_info.object_read->response_get()->value_get(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, &field_content_type_len);
+ //create a Range: transform processor for requests of type Range: bytes=1-2,4-5,10-100 (eg. multiple ranges)
+ range_trans = transformProcessor.range_transform(mutex,
+ t_state.ranges,
+ t_state.unsatisfiable_range,
+ t_state.num_range_fields,
+ &t_state.hdr_info.transform_response,
+ content_type,
+ field_content_type_len,
+ t_state.cache_info.object_read->object_size_get()
+ );
+ if (range_trans != NULL) {
+ api_hooks.append(TS_HTTP_RESPONSE_TRANSFORM_HOOK, range_trans);
+ t_state.range_setup = HttpTransact::RANGE_REQUESTED;
+ }
+ else { //we couldnt append the transform to our API hooks so bailing
+ t_state.range_setup = HttpTransact::RANGE_NOT_SATISFIABLE;
+ }
+ }
+ else if (t_state.range_setup == HttpTransact::RANGE_REQUESTED && t_state.num_range_fields == 1) {
+ Debug("http_trans", "Handling single Range: request");
+ //no op, we will handle this later in the HttpTunnel
+ }
+ else {
+ t_state.range_setup = HttpTransact::RANGE_NOT_SATISFIABLE;
}
}
}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/proxy/http/HttpSM.h
----------------------------------------------------------------------
diff --git a/proxy/http/HttpSM.h b/proxy/http/HttpSM.h
index d2eb74c..07aeaad 100644
--- a/proxy/http/HttpSM.h
+++ b/proxy/http/HttpSM.h
@@ -41,6 +41,7 @@
#include "InkAPIInternal.h"
#include "StatSystem.h"
#include "HttpClientSession.h"
+#include "HdrUtils.h"
//#include "AuthHttpAdapter.h"
/* Enable LAZY_BUF_ALLOC to delay allocation of buffers until they
@@ -218,7 +219,11 @@ public:
// setup Range transfomration if so.
// return true when the Range is unsatisfiable
void do_range_setup_if_necessary();
-
+
+ void do_range_parse(MIMEField *range_field);
+ void calculate_output_cl(int64_t, int64_t);
+ void parse_range_and_compare(MIMEField*, int64_t);
+
// Called by transact to prevent reset problems
// failed PUSH requests
void set_ua_half_close_flag();
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/proxy/http/HttpTransact.cc
----------------------------------------------------------------------
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index 5d60c6c..68e7540 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -50,6 +50,8 @@
#include "IPAllow.h"
static const char *URL_MSG = "Unable to process requested URL.\n";
+static char range_type[] = "multipart/byteranges; boundary=RANGE_SEPARATOR";
+#define RANGE_NUMBERS_LENGTH 60
#define HTTP_INCREMENT_TRANS_STAT(X) update_stat(s, X, 1);
#define HTTP_SUM_TRANS_STAT(X,S) update_stat(s, X, (ink_statval_t) S);
@@ -2447,7 +2449,8 @@ HttpTransact::HandleCacheOpenReadHit(State* s)
}
ink_assert(s->cache_lookup_result == CACHE_LOOKUP_HIT_FRESH ||
- s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING || s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE);
+ s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING ||
+ s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE);
if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE &&
s->api_update_cached_object != HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
needs_revalidate = true;
@@ -2481,9 +2484,6 @@ HttpTransact::HandleCacheOpenReadHit(State* s)
s->www_auth_content = send_revalidate ? CACHE_AUTH_STALE : CACHE_AUTH_FRESH;
send_revalidate = true;
}
- if (send_revalidate && s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE)) {
- s->range_setup = RANGE_REVALIDATE;
- }
DebugTxn("http_trans", "CacheOpenRead --- needs_auth = %d", needs_authenticate);
DebugTxn("http_trans", "CacheOpenRead --- needs_revalidate = %d", needs_revalidate);
@@ -2564,9 +2564,29 @@ HttpTransact::HandleCacheOpenReadHit(State* s)
return;
}
}
+
build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
- issue_revalidate(s);
+ /* We can't handle revalidate requests with ranges. If it comes
+ back unmodified that's fine but if not the OS will either
+ provide the entire object which is difficult to handle and
+ potentially very inefficient, or we'll get back just the
+ range and we can't cache that anyway. So just forward the
+ request.
+
+ Note: We're here only if the cache is stale so we don't want
+ to serve it.
+ */
+ if (s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE)) {
+ Debug("http_trans", "[CacheOpenReadHit] Forwarding range request for stale cache object.");
+ s->cache_info.action = CACHE_DO_NO_ACTION;
+ s->cache_info.object_read = NULL;
+ } else {
+ // If it's not a range tweak the request to be a revalidate.
+ // Note this doesn't actually issue the request, it simply
+ // adjusts the headers for later.
+ issue_revalidate(s);
+ }
// this can not be anything but a simple origin server connection.
// in other words, we would not have looked up the cache for a
@@ -2706,16 +2726,16 @@ HttpTransact::build_response_from_cache(State* s, HTTPWarningCode warning_code)
// only if the cached response is a 200 OK
if (client_response_code == HTTP_STATUS_OK && client_request->presence(MIME_PRESENCE_RANGE)) {
s->state_machine->do_range_setup_if_necessary();
- if (s->range_setup == RANGE_NOT_SATISFIABLE && s->http_config_param->reverse_proxy_enabled) {
+ if (s->range_setup == RANGE_NOT_SATISFIABLE) {
build_error_response(s, HTTP_STATUS_RANGE_NOT_SATISFIABLE, "Requested Range Not Satisfiable","","");
s->cache_info.action = CACHE_DO_NO_ACTION;
s->next_action = PROXY_INTERNAL_CACHE_NOOP;
break;
- } else if (s->range_setup == RANGE_NOT_SATISFIABLE || s->range_setup == RANGE_NOT_HANDLED) {
- // we switch to tunneing for Range requests either
- // 1. we need to revalidate or
- // 2. out-of-order Range requests
- DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] Out-oforder Range request - tunneling");
+ } else if (s->range_setup == RANGE_NOT_HANDLED) {
+ // we switch to tunneling for Range requests if it is out of order.
+ // In that case we fetch the entire source so it's OK to switch
+ // this late.
+ DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] Out-of-order Range request - tunneling");
s->cache_info.action = CACHE_DO_NO_ACTION;
if (s->force_dns) {
HandleCacheOpenReadMiss(s); // DNS is already completed no need of doing DNS
@@ -4528,6 +4548,11 @@ HttpTransact::handle_no_cache_operation_on_forward_server_response(State* s)
s->next_action = how_to_open_connection(s);
}
return;
+ case HTTP_STATUS_PARTIAL_CONTENT:
+ // If we get this back we should be just passing it through.
+ ink_debug_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
+ s->next_action = SERVER_READ;
+ break;
default:
DebugTxn("http_trans", "[hncoofsr] server sent back something other than 100,304,200");
/* Default behavior is to pass-through response to the client */
@@ -6085,7 +6110,7 @@ HttpTransact::is_response_cacheable(State* s, HTTPHdr* request, HTTPHdr* respons
int req_method = request->method_get_wksidx();
if (!(HttpTransactHeaders::is_method_cacheable(req_method)) && s->api_req_cacheable == false) {
DebugTxn("http_trans", "[is_response_cacheable] " "only GET, and some HEAD and POST are cachable");
- return (false);
+ return false;
}
// DebugTxn("http_trans", "[is_response_cacheable] method is cacheable");
// If the request was not looked up in the cache, the response
@@ -6097,7 +6122,7 @@ HttpTransact::is_response_cacheable(State* s, HTTPHdr* request, HTTPHdr* respons
}
// already has a fresh copy in the cache
if (s->range_setup == RANGE_NOT_HANDLED)
- return (false);
+ return false;
// Check whether the response is cachable based on its cookie
// If there are cookies in response but a ttl is set, allow caching
@@ -6121,7 +6146,7 @@ HttpTransact::is_response_cacheable(State* s, HTTPHdr* request, HTTPHdr* respons
}
// DebugTxn("http_trans", "[is_response_cacheable] server permits storing");
- // does config explicitly forbit storing?
+ // does config explicitly forbid storing?
// ttl overides other config parameters
if ((!s->cache_info.directives.does_config_permit_storing &&
!s->cache_control.ignore_server_no_cache &&
@@ -6132,7 +6157,7 @@ HttpTransact::is_response_cacheable(State* s, HTTPHdr* request, HTTPHdr* respons
}
// DebugTxn("http_trans", "[is_response_cacheable] config permits storing");
- // does client explicitly forbit storing?
+ // does client explicitly forbid storing?
if (!s->cache_info.directives.does_client_permit_storing && !s->cache_control.ignore_client_no_cache) {
DebugTxn("http_trans", "[is_response_cacheable] client does not permit storing, "
"and cache control does not say to ignore client no-cache");
@@ -6687,13 +6712,21 @@ HttpTransact::handle_content_length_header(State* s, HTTPHdr* header, HTTPHdr* b
switch (s->source) {
case SOURCE_CACHE:
+
+ // if we are doing a single Range: request, calculate the new
+ // C-L: header
+ if (s->range_setup == HttpTransact::RANGE_REQUESTED && s->num_range_fields == 1) {
+ Debug("http_trans", "Partial content requested, re-calculating content-length");
+ change_response_header_because_of_range_request(s,header);
+ s->hdr_info.trust_response_cl = true;
+ }
////////////////////////////////////////////////
// Make sure that the cache's object size //
// agrees with the Content-Length //
// Otherwise, set the state's machine view //
// of c-l to undefined to turn off K-A //
////////////////////////////////////////////////
- if ((int64_t) s->cache_info.object_read->object_size_get() == cl) {
+ else if ((int64_t) s->cache_info.object_read->object_size_get() == cl) {
s->hdr_info.trust_response_cl = true;
} else {
DebugTxn("http_trans", "Content Length header and cache object size mismatch." "Disabling keep-alive");
@@ -6719,6 +6752,7 @@ HttpTransact::handle_content_length_header(State* s, HTTPHdr* header, HTTPHdr* b
header->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
s->hdr_info.trust_response_cl = false;
}
+ Debug("http_trans", "[handle_content_length_header] RESPONSE cont len in hdr is %"PRId64, header->get_content_length());
} else {
// No content length header
if (s->source == SOURCE_CACHE) {
@@ -6732,7 +6766,15 @@ HttpTransact::handle_content_length_header(State* s, HTTPHdr* header, HTTPHdr* b
header->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
s->hdr_info.trust_response_cl = false;
s->hdr_info.request_content_length = HTTP_UNDEFINED_CL;
- } else {
+ }
+ else if (s->range_setup == HttpTransact::RANGE_REQUESTED && s->num_range_fields == 1) {
+ // if we are doing a single Range: request, calculate the new
+ // C-L: header
+ Debug("http_trans", "Partial content requested, re-calculating content-length");
+ change_response_header_because_of_range_request(s,header);
+ s->hdr_info.trust_response_cl = true;
+ }
+ else {
header->set_content_length(cl);
s->hdr_info.trust_response_cl = true;
}
@@ -8852,3 +8894,39 @@ HttpTransact::delete_warning_value(HTTPHdr* to_warn, HTTPWarningCode warning_cod
}
}
}
+
+void
+HttpTransact::change_response_header_because_of_range_request(State *s, HTTPHdr * header)
+{
+ MIMEField *field;
+ char *reason_phrase;
+
+ ink_assert(header->field_find(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE) == NULL);
+
+ header->status_set(HTTP_STATUS_PARTIAL_CONTENT);
+ reason_phrase = (char *) (http_hdr_reason_lookup(HTTP_STATUS_PARTIAL_CONTENT));
+ header->reason_set(reason_phrase, strlen(reason_phrase));
+
+ // set the right Content-Type for multiple entry Range
+ if (s->num_range_fields > 1) {
+ field = header->field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+
+ if (field != NULL)
+ header->field_delete(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+
+ field = header->field_create(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+ field->value_append(header->m_heap, header->m_mime, range_type, sizeof(range_type) - 1);
+
+ header->field_attach(field);
+ header->set_content_length(s->range_output_cl);
+ }
+ else {
+ char numbers[RANGE_NUMBERS_LENGTH];
+
+ field = header->field_create(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE);
+ snprintf(numbers, sizeof(numbers), "bytes %"PRId64"-%"PRId64"/%"PRId64, s->ranges[0]._start, s->ranges[0]._end, s->cache_info.object_read->object_size_get());
+ field->value_append(header->m_heap, header->m_mime, numbers, strlen(numbers));
+ header->field_attach(field);
+ header->set_content_length(s->range_output_cl);
+ }
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/proxy/http/HttpTransact.h
----------------------------------------------------------------------
diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h
index 694ac04..e70d1b2 100644
--- a/proxy/http/HttpTransact.h
+++ b/proxy/http/HttpTransact.h
@@ -651,13 +651,12 @@ public:
enum RangeSetup_t
{
- RANGE_NONE,
- RANGE_TRANSFORM,
+ RANGE_NONE = 0,
+ RANGE_REQUESTED,
RANGE_NOT_SATISFIABLE,
RANGE_NOT_HANDLED,
- RANGE_REVALIDATE
};
-
+
enum CacheAuth_t
{
CACHE_AUTH_NONE = 0,
@@ -982,9 +981,6 @@ public:
StatBlock first_stats;
StatBlock *current_stats;
- // for Range: to avoid write transfomed Range response into cache
- RangeSetup_t range_setup;
-
// for negative caching
bool negative_caching;
@@ -1052,7 +1048,16 @@ public:
URL pristine_url; // pristine url is the url before remap
bool api_skip_all_remapping;
-
+
+ // Http Range: related variables
+ RangeSetup_t range_setup;
+ bool unsatisfiable_range;
+ bool not_handle_range;
+ int64_t num_range_fields;
+ int64_t range_output_cl;
+ int64_t current_range;
+ RangeRecord *ranges;
+
OverridableHttpConfigParams *txn_conf;
OverridableHttpConfigParams my_txn_conf; // Storage for plugins, to avoid malloc
@@ -1103,7 +1108,6 @@ public:
state_machine_id(0),
first_stats(),
current_stats(NULL),
- range_setup(RANGE_NONE),
negative_caching(false),
www_auth_content(CACHE_AUTH_NONE),
client_connection_enabled(true),
@@ -1144,6 +1148,13 @@ public:
reverse_proxy(false), url_remap_success(false), remap_redirect(NULL), filter_mask(0), already_downgraded(false),
pristine_url(),
api_skip_all_remapping(false),
+ range_setup(RANGE_NONE),
+ unsatisfiable_range(false),
+ not_handle_range(false),
+ num_range_fields(0),
+ range_output_cl(0),
+ current_range(-1),
+ ranges(NULL),
txn_conf(NULL)
{
int i;
@@ -1232,6 +1243,10 @@ public:
url_map.clear();
arena.reset();
pristine_url.clear();
+
+ delete[] ranges; ranges = NULL;
+ range_setup = RANGE_NONE;
+ unsatisfiable_range = true;
return;
}
@@ -1366,6 +1381,7 @@ public:
static void build_response_copy(State* s, HTTPHdr* base_response, HTTPHdr* outgoing_response, HTTPVersion outgoing_version);
static void handle_content_length_header(State* s, HTTPHdr* header, HTTPHdr* base);
+ static void change_response_header_because_of_range_request(State* s, HTTPHdr* header);
static void handle_request_keep_alive_headers(State *s, HTTPVersion ver, HTTPHdr *heads);
static void handle_response_keep_alive_headers(State *s, HTTPVersion ver, HTTPHdr *heads);
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e80215f4/proxy/http/HttpTunnel.cc
----------------------------------------------------------------------
diff --git a/proxy/http/HttpTunnel.cc b/proxy/http/HttpTunnel.cc
index f10c84c..d9f1a62 100644
--- a/proxy/http/HttpTunnel.cc
+++ b/proxy/http/HttpTunnel.cc
@@ -784,7 +784,12 @@ HttpTunnel::producer_run(HttpTunnelProducer * p)
}
}
- if (p->nbytes >= 0) {
+ int64_t read_start_pos = 0;
+ if (p->vc_type == HT_CACHE_READ && sm->t_state.range_setup == HttpTransact::RANGE_REQUESTED && sm->t_state.num_range_fields == 1) {
+ read_start_pos = sm->t_state.ranges[0]._start;
+ producer_n = (sm->t_state.ranges[0]._end - sm->t_state.ranges[0]._start)+1;
+ consumer_n = (producer_n + sm->client_response_hdr_bytes);
+ } else if (p->nbytes >= 0) {
consumer_n = p->nbytes;
producer_n = p->ntodo;
} else {
@@ -925,16 +930,22 @@ HttpTunnel::producer_run(HttpTunnelProducer * p)
Debug("http_tunnel", "[%" PRId64 "] [tunnel_run] producer already done", sm->sm_id);
producer_handler(HTTP_TUNNEL_EVENT_PRECOMPLETE, p);
} else {
- p->read_vio = p->vc->do_io_read(this, producer_n, p->read_buffer);
+ if (read_start_pos > 0) {
+ p->read_vio = ((CacheVC*)p->vc)->do_io_pread(this, producer_n, p->read_buffer, read_start_pos);
+ }
+ else {
+ p->read_vio = p->vc->do_io_read(this, producer_n, p->read_buffer);
+ }
}
-
- // Now that the tunnel has started, we must remove producer's reader so
- // that it doesn't act like a buffer guard
- p->read_buffer->dealloc_reader(p->buffer_start);
- p->buffer_start = NULL;
}
+
+ // Now that the tunnel has started, we must remove producer's reader so
+ // that it doesn't act like a buffer guard
+ p->read_buffer->dealloc_reader(p->buffer_start);
+ p->buffer_start = NULL;
}
+
int
HttpTunnel::producer_handler_dechunked(int event, HttpTunnelProducer * p)
{