You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2014/03/27 18:10:40 UTC

[1/2] git commit: Added TS-2554 to CHANGES

Repository: trafficserver
Updated Branches:
  refs/heads/master 0eb46cf95 -> 9c84680a0


Added TS-2554 to CHANGES


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/9c84680a
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/9c84680a
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/9c84680a

Branch: refs/heads/master
Commit: 9c84680a050ecefb8d57e26531f3047f547420d8
Parents: 07ebb01
Author: Leif Hedstrom <zw...@apache.org>
Authored: Thu Mar 27 11:10:07 2014 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Thu Mar 27 11:10:18 2014 -0600

----------------------------------------------------------------------
 CHANGES | 6 ++++++
 1 file changed, 6 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9c84680a/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index f0e000d..a60ef74 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,12 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 5.0.0
 
+  *) [TS-2554] New plugin: background_fetch, which under certain conditions
+   will kick off a background fetch when it detects Range request and
+   responses. This allows for the cache to start populating objects that would
+   otherwise not be cacheable. This is ideally used together with the
+   read-while-writer feature as well.
+
   *) [TS-2671] Restore missing .useflt remap directive.
 
   *) [TS-2654] Crash in Range requests with read-while-writer.


[2/2] git commit: TS-2554 New plugin to performance background fetch on Range responses

Posted by zw...@apache.org.
TS-2554 New plugin to performance background fetch on Range responses


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/07ebb012
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/07ebb012
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/07ebb012

Branch: refs/heads/master
Commit: 07ebb0125de8e7ea2827239c19a459c0f47bea4a
Parents: 0eb46cf
Author: Leif Hedstrom <zw...@apache.org>
Authored: Mon Mar 24 16:19:16 2014 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Thu Mar 27 11:10:18 2014 -0600

----------------------------------------------------------------------
 configure.ac                                    |   1 +
 doc/reference/plugins/background_fetch.en.rst   |  73 +++
 doc/reference/plugins/index.en.rst              |   1 +
 plugins/experimental/Makefile.am                |   5 +-
 .../experimental/background_fetch/Makefile.am   |  21 +
 .../background_fetch/background_fetch.cc        | 521 +++++++++++++++++++
 6 files changed, 620 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/07ebb012/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 6883cb6..79da351 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1978,6 +1978,7 @@ AC_CONFIG_FILES([
   plugins/stats_over_http/Makefile
   plugins/experimental/Makefile
   plugins/experimental/authproxy/Makefile
+  plugins/experimental/background_fetch/Makefile
   plugins/experimental/balancer/Makefile
   plugins/experimental/buffer_upload/Makefile
   plugins/experimental/channel_stats/Makefile

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/07ebb012/doc/reference/plugins/background_fetch.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/plugins/background_fetch.en.rst b/doc/reference/plugins/background_fetch.en.rst
new file mode 100644
index 0000000..3df7d3c
--- /dev/null
+++ b/doc/reference/plugins/background_fetch.en.rst
@@ -0,0 +1,73 @@
+.. _background-fetch-plugin:
+
+Background Fetch Plugin
+***********************
+
+.. Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+
+
+This is a plugin for Apache Traffic Server that allows you to proactively
+fetch content from Origin in a way that it will fill the object into
+cache. This is particularly useful when all (or most) of your client requests
+are of the byte-Range type. The underlying problem being that Traffic Server
+is not able to cache request / responses with byte ranges.
+
+Using the plugin
+----------------
+
+This plugin currently only functions as a global plugin, and it takes not
+arguments or parameters. In :file:`plugin.config`, simply add::
+
+  background_fetch.so
+
+
+Functionality
+-------------
+
+Examining the responses from origin, we decide to trigger a background fetch
+of the original (Client) request under these conditions:
+
+- The request is a ``GET`` request (we only support these right now)
+- The response is a ``206`` response
+- The original client request, and the Origin server response, is clearly
+  indicating that the response is cacheable. This uses the new API
+  c:func:`TSHttpTxnIsCacheable()`, which also implies honoring current
+  Traffic Server configurations.
+
+
+Once deemed a good candidate to performance a background fetch, we'll replay
+the original client request through the Traffic Server proxy again, except
+this time eliminating the ``Range`` header. This is transparent to the
+original client request, which continues as normal.
+
+Only one background fetch per URL is ever performed, making sure we do not
+accidentally put pressure on the origin servers.
+
+
+
+Future additions
+----------------
+
+The infrastructure is in place for providing global and per-remap
+configurations. This could include:
+
+- Limiting the background fetches to certain Content-Types
+- Limiting the background fetches to content of certain sizes
+
+
+None of this is currently not implemented.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/07ebb012/doc/reference/plugins/index.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/plugins/index.en.rst b/doc/reference/plugins/index.en.rst
index 275411d..669b8d2 100644
--- a/doc/reference/plugins/index.en.rst
+++ b/doc/reference/plugins/index.en.rst
@@ -63,6 +63,7 @@ directory of the Apache Traffic Server source tree. Exmperimental plugins can be
   :maxdepth: 1
 
   authproxy.en
+  background_fetch.en
   balancer.en
   buffer_upload.en
   combo_handler.en

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/07ebb012/plugins/experimental/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am
index d961002..a2c5a37 100644
--- a/plugins/experimental/Makefile.am
+++ b/plugins/experimental/Makefile.am
@@ -18,16 +18,17 @@ if BUILD_EXPERIMENTAL_PLUGINS
 
 SUBDIRS = \
  authproxy \
+ background_fetch \
  balancer \
- escalate \
  buffer_upload \
  channel_stats \
  custom_redirect \
+ escalate \
  esi \
  geoip_acl \
  healthchecks \
- lua \
  hipes \
+ lua \
  metalink \
  remap_stats \
  rfc5861 \

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/07ebb012/plugins/experimental/background_fetch/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/background_fetch/Makefile.am b/plugins/experimental/background_fetch/Makefile.am
new file mode 100644
index 0000000..fef8d44
--- /dev/null
+++ b/plugins/experimental/background_fetch/Makefile.am
@@ -0,0 +1,21 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+include $(top_srcdir)/build/plugins.mk
+
+pkglib_LTLIBRARIES = background_fetch.la
+background_fetch_la_SOURCES = background_fetch.cc
+background_fetch_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/07ebb012/plugins/experimental/background_fetch/background_fetch.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/background_fetch/background_fetch.cc b/plugins/experimental/background_fetch/background_fetch.cc
new file mode 100644
index 0000000..dc895f3
--- /dev/null
+++ b/plugins/experimental/background_fetch/background_fetch.cc
@@ -0,0 +1,521 @@
+/** @file
+
+    Plugin to perform background fetches of certain content that would
+    otherwise not be cached. For example, Range: requests / responses.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+*/
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <string>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "ts/ts.h"
+#include "ts/remap.h"
+#include "ink_defs.h"
+
+
+// Some wonkiness around compiler version and the unordered map (hash)
+#if HAVE_UNORDERED_MAP
+#  include <unordered_map>
+   typedef std::unordered_map<std::string, bool> OutstandingRequests;
+#else
+#  include <map>
+   typedef std::map<std::string, bool> OutstandingRequests;
+#endif
+
+// Constants
+const char PLUGIN_NAME[] = "background_fetch";
+
+
+///////////////////////////////////////////////////////////////////////////
+// Remove a header (fully) from an TSMLoc / TSMBuffer. Return the number
+// of fields (header values) we removed.
+int
+remove_header(TSMBuffer bufp, TSMLoc hdr_loc, const char* header, int len)
+{
+  TSMLoc field = TSMimeHdrFieldFind(bufp, hdr_loc, header, len);
+  int c = 0;
+
+  while (field) {
+    ++c;
+    TSMLoc tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field);
+
+    TSMimeHdrFieldDestroy(bufp, hdr_loc, field);
+    TSHandleMLocRelease(bufp, hdr_loc, field);
+    field = tmp;
+  }
+
+  return c;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Set a header to a specific value. This will avoid going to through a
+// remove / add sequence in case of an existing header.
+// but clean.
+bool
+set_header(TSMBuffer bufp, TSMLoc hdr_loc, const char* header, int len, const char* val, int val_len)
+{
+  if (!bufp || !hdr_loc || !header || len <= 0 || !val || val_len <= 0) {
+    return false;
+  }
+
+  bool ret = false;
+  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, header, len);
+
+  if (!field_loc) {
+    // No existing header, so create one
+    if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(bufp, hdr_loc, header, len, &field_loc)) {
+      if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, val, val_len)) {
+        TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
+        ret = true;
+      }
+      TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+    }
+  } else {
+    TSMLoc tmp = NULL;
+    bool first = true;
+
+    while (field_loc) {
+      if (first) {
+        first = false;
+        if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, val, val_len)) {
+          ret = true;
+        }
+      } else {
+        TSMimeHdrFieldDestroy(bufp, hdr_loc, field_loc);
+      }
+      tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field_loc);
+      TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+      field_loc = tmp;
+    }
+  }
+
+  return ret;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Dump a header on stderr, useful together with TSDebug().
+void
+dump_headers(TSMBuffer bufp, TSMLoc hdr_loc)
+{
+  TSIOBuffer output_buffer;
+  TSIOBufferReader reader;
+  TSIOBufferBlock block;
+  const char* block_start;
+  int64_t block_avail;
+
+  output_buffer = TSIOBufferCreate();
+  reader = TSIOBufferReaderAlloc(output_buffer);
+
+  /* This will print  just MIMEFields and not the http request line */
+  TSMimeHdrPrint(bufp, hdr_loc, output_buffer);
+
+  /* We need to loop over all the buffer blocks, there can be more than 1 */
+  block = TSIOBufferReaderStart(reader);
+  do {
+    block_start = TSIOBufferBlockReadStart(block, reader, &block_avail);
+    fwrite(block_start, block_avail, 1, stderr);
+    TSIOBufferReaderConsume(reader, block_avail);
+    block = TSIOBufferReaderStart(reader);
+  } while (block && block_avail != 0);
+
+  /* Free up the TSIOBuffer that we used to print out the header */
+  TSIOBufferReaderFree(reader);
+  TSIOBufferDestroy(output_buffer);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Struct to hold configurations and state. This can be global, or per
+// remap rule. This also holds the list of currently outstanding URLs,
+// such that we can avoid sending more than one background fill per URL at
+// any given time.
+class BGFetchConfig {
+public:
+  BGFetchConfig()
+  {
+    _lock = TSMutexCreate();
+  }
+
+  ~BGFetchConfig()
+  {
+    // ToDo: Destroy mutex ? TS-1432
+  }
+
+  bool acquire(const std::string &url)
+  {
+    bool ret;
+
+    TSMutexLock(_lock);
+    if (_urls.end() == _urls.find(url)) {
+      _urls[url] = true;
+      ret = true;
+    } else {
+      ret = false;
+    }
+    TSMutexUnlock(_lock);
+
+    return ret;
+  }
+
+  bool release(const std::string &url)
+  {
+    bool ret;
+
+    TSMutexLock(_lock);
+    if (_urls.end() == _urls.find(url)) {
+      ret = false;
+    } else {
+      _urls.erase(url);
+      ret = true;
+    }
+    TSMutexUnlock(_lock);
+
+    return ret;
+  }
+
+private:
+  OutstandingRequests _urls;
+  TSMutex _lock;
+};
+
+BGFetchConfig gConfig;
+
+//////////////////////////////////////////////////////////////////////////////
+// Hold and manage some state for the background fetch continuation
+// This is necessary, because the TXN is likely to not be available
+// during the time we fetch from origin.
+static int bg_fetch_cont(TSCont contp, TSEvent event, void* edata);
+
+struct BGFetchData
+{
+  BGFetchData(BGFetchConfig* cfg=&gConfig)
+    : hdr_loc(TS_NULL_MLOC), url_loc(TS_NULL_MLOC), _config(cfg)
+  {
+    mbuf = TSMBufferCreate();
+  }
+
+  ~BGFetchData()
+  {
+    release_url();
+
+    TSHandleMLocRelease(mbuf, TS_NULL_MLOC, hdr_loc);
+    TSHandleMLocRelease(mbuf, TS_NULL_MLOC, url_loc);
+
+    TSMBufferDestroy(mbuf);
+
+    // If we got schedule, also clean that up
+    if (_cont) {
+      TSContDestroy(_cont);
+
+      TSIOBufferReaderFree(req_io_buf_reader);
+      TSIOBufferDestroy(req_io_buf);
+      TSIOBufferReaderFree(resp_io_buf_reader);
+      TSIOBufferDestroy(resp_io_buf);
+    }
+  }
+
+  bool acquire_url() const { return _config->acquire(_url); }
+  bool release_url() const { return _config->release(_url); }
+
+  const char* get_url() const { return _url.c_str(); }
+
+  bool initialize(TSMBuffer request, TSMLoc req_hdr, TSHttpTxn txnp);
+  void schedule();
+
+  TSMBuffer mbuf;
+  TSMLoc hdr_loc;
+  TSMLoc url_loc;
+  struct sockaddr_storage client_ip;
+
+  // This is for the actual background fetch / NetVC
+  TSVConn vc;
+  TSIOBuffer req_io_buf, resp_io_buf;
+  TSIOBufferReader req_io_buf_reader, resp_io_buf_reader;
+  TSVIO r_vio, w_vio;
+
+private:
+  std::string _url;
+  TSCont _cont;
+  BGFetchConfig* _config;
+};
+
+
+// This sets up the data and continuation properly, this is done outside
+// of the CTor, since this can actually fail. If we fail, the data is
+// useless, and should be delete'd.
+//
+// This needs the txnp temporarily, so it can copy the pristine request
+// URL. The txnp is not used once initialize() returns.
+//
+// Upon succesful completion, the struct should be ready to start a
+// background fetch.
+bool
+BGFetchData::initialize(TSMBuffer request, TSMLoc req_hdr, TSHttpTxn txnp)
+{
+  TSReleaseAssert(TS_NULL_MLOC == hdr_loc);
+  TSReleaseAssert(TS_NULL_MLOC == url_loc);
+  struct sockaddr const* ip = TSHttpTxnClientAddrGet(txnp);
+
+  if (ip) {
+    if (ip->sa_family == AF_INET) {
+      memcpy(&client_ip, ip, sizeof(sockaddr_in));
+    } else if (ip->sa_family == AF_INET6) {
+      memcpy(&client_ip, ip, sizeof(sockaddr_in6));
+    } else {
+      TSError("%s: Unknown address family %d", PLUGIN_NAME, ip->sa_family);
+    }
+  } else {
+    TSError("%s: failed to get client host info", PLUGIN_NAME);
+    return false;
+  }
+
+  hdr_loc = TSHttpHdrCreate(mbuf);
+  if (TS_SUCCESS == TSHttpHdrCopy(mbuf, hdr_loc, request, req_hdr)) {
+    TSMLoc purl;
+    int len;
+
+    // Now copy the pristine request URL into our MBuf
+    if ((TS_SUCCESS == TSHttpTxnPristineUrlGet(txnp, &request, &purl)) &&
+        (TS_SUCCESS == TSUrlClone(mbuf, request, purl, &url_loc))) {
+      char* url = TSUrlStringGet(mbuf, url_loc, &len);
+
+      _url.append(url, len); // Save away the URL for later use when acquiring lock
+      TSfree(static_cast<void*>(url));
+
+      if (TS_SUCCESS == TSHttpHdrUrlSet(mbuf, hdr_loc, url_loc)) {
+        // Make sure we have the correct Host: header for this request.
+        const char *hostp = TSUrlHostGet(mbuf, url_loc, &len);
+
+        if (set_header(mbuf, hdr_loc, TS_MIME_FIELD_HOST, TS_MIME_LEN_HOST, hostp, len)) {
+          TSDebug(PLUGIN_NAME, "Set header Host: %.*s", len, hostp);
+        }
+
+        // Next, remove any Range: headers from our request.
+        if (remove_header(mbuf, hdr_loc, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE) > 0) {
+          TSDebug(PLUGIN_NAME, "Removed the Range: header from request");
+        }
+
+        return true;
+      }
+    }
+  }
+
+  // Something failed.
+  return false;
+}
+
+
+// Create, setup and schedule the background fetch continuation.
+void
+BGFetchData::schedule()
+{
+  // Setup the continuation
+  _cont = TSContCreate(bg_fetch_cont, NULL);
+  TSContDataSet(_cont, static_cast<void*>(this));
+
+  // Initialize the VIO stuff (for the fetch)
+  req_io_buf = TSIOBufferCreate();
+  req_io_buf_reader = TSIOBufferReaderAlloc(req_io_buf);
+  resp_io_buf = TSIOBufferCreate();
+  resp_io_buf_reader = TSIOBufferReaderAlloc(resp_io_buf);
+
+  // Schedule
+  TSContSchedule(_cont, 0, TS_THREAD_POOL_NET);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Continuation to perform a background fill of a URL. This is pretty
+// expensive (memory allocations etc.), we could eliminate maybe the
+// std::string, but I think it's fine for now.
+static int
+bg_fetch_cont(TSCont contp, TSEvent event, void* /* edata ATS_UNUSED */)
+{
+  BGFetchData* data = static_cast<BGFetchData*>(TSContDataGet(contp));
+  int64_t avail;
+
+  switch (event) {
+  case TS_EVENT_IMMEDIATE:
+  case TS_EVENT_TIMEOUT:
+    // Debug info for this particular bg fetch (put all debug in here please)
+    if (TSIsDebugTagSet(PLUGIN_NAME)) {
+      char buf[INET6_ADDRSTRLEN];
+      const sockaddr* sockaddress = (const sockaddr*)&data->client_ip;
+
+      switch (sockaddress->sa_family) {
+      case AF_INET:
+        inet_ntop(AF_INET, &(((struct sockaddr_in *) sockaddress)->sin_addr), buf, INET_ADDRSTRLEN);
+        TSDebug(PLUGIN_NAME, "Client IPv4 = %s", buf);
+        break;
+      case AF_INET6:
+        inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) sockaddress)->sin6_addr), buf, INET6_ADDRSTRLEN);
+        TSDebug(PLUGIN_NAME, "Client IPv6 = %s", buf);
+        break;
+      default:
+        TSError("%s: Unknown address family %d", PLUGIN_NAME, sockaddress->sa_family);
+        break;
+      }
+      TSDebug(PLUGIN_NAME, "Starting bg fetch on: %s", data->get_url());
+      dump_headers(data->mbuf, data->hdr_loc);
+    }
+
+    // Setup the NetVC for background fetch
+    if ((data->vc = TSHttpConnect((sockaddr*)&data->client_ip)) != NULL) {
+      TSHttpHdrPrint(data->mbuf, data->hdr_loc, data->req_io_buf);
+      // We never send a body with the request. ToDo: Do we ever need to support that ?
+      TSIOBufferWrite(data->req_io_buf, "\r\n", 2);
+
+      data->r_vio = TSVConnRead(data->vc, contp, data->resp_io_buf, INT64_MAX);
+      data->w_vio = TSVConnWrite(data->vc, contp, data->req_io_buf_reader, TSIOBufferReaderAvail(data->req_io_buf_reader));
+    } else {
+      delete data;
+      TSError("%s: failed to connect to internal process, major malfunction", PLUGIN_NAME);
+    }
+
+  case TS_EVENT_VCONN_WRITE_COMPLETE:
+    TSDebug(PLUGIN_NAME, "Write Complete");
+    break;
+
+  case TS_EVENT_VCONN_READ_READY:
+    avail = TSIOBufferReaderAvail(data->resp_io_buf_reader);
+    TSIOBufferReaderConsume(data->resp_io_buf_reader, avail);
+    TSVIONDoneSet(data->r_vio, TSVIONDoneGet(data->r_vio) + avail);
+    TSVIOReenable(data->r_vio);
+    break;
+
+  case TS_EVENT_VCONN_READ_COMPLETE:
+  case TS_EVENT_VCONN_EOS:
+  case TS_EVENT_VCONN_INACTIVITY_TIMEOUT:
+    if (event == TS_EVENT_VCONN_INACTIVITY_TIMEOUT) {
+      TSDebug(PLUGIN_NAME, "Encountered Inactivity Timeout");
+      TSVConnAbort(data->vc, TS_VC_CLOSE_ABORT);
+    } else {
+      TSVConnClose(data->vc);
+    }
+
+    // ToDo: Is this really necessary to do here for all 3 cases?
+    TSDebug(PLUGIN_NAME, "Closing down background transaction, event=%d", event);
+    avail = TSIOBufferReaderAvail(data->resp_io_buf_reader);
+    TSIOBufferReaderConsume(data->resp_io_buf_reader, avail);
+    TSVIONDoneSet(data->r_vio, TSVIONDoneGet(data->r_vio) + avail);
+
+    // Release and Cleanup
+    delete data;
+    break;
+
+  default:
+    TSDebug(PLUGIN_NAME, "Unhandled event: %d", event); // ToDo: use new API in v5.0.0
+    break;
+  }
+
+  return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Main "plugin".
+//
+static int
+cont_handle_response(TSCont /* contp ATS_UNUSED */, TSEvent /* event ATS_UNUSED */, void* edata)
+{
+  // ToDo: If we want to support per-remap configurations, we have to pass along the data here
+  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
+
+  TSDebug(PLUGIN_NAME, "Testing: request is internal?");
+  if (TSHttpIsInternalRequest(txnp) != TS_SUCCESS) {
+    TSMBuffer request;
+    TSMLoc req_hdr;
+
+    if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &request, &req_hdr)) {
+      int method_len;
+      const char* method = TSHttpHdrMethodGet(request, req_hdr, &method_len);
+
+      // Make sure it's not an internal request first, and then examine the Origin server response
+      TSDebug(PLUGIN_NAME, "Testing: request is a GET?");
+      if (TS_HTTP_METHOD_GET == method) {
+        TSMBuffer response;
+        TSMLoc resp_hdr;
+
+        if (TS_SUCCESS == TSHttpTxnServerRespGet(txnp, &response, &resp_hdr)) {
+          // ToDo: Check the MIME type first, to see if it's a type we care about.
+          // ToDo: Such MIME types should probably be per remap rule.
+
+          // Only deal with 206 responses on a GET request (partial content), anything else is irrelevant
+          TSDebug(PLUGIN_NAME, "Testing: response is 206?");
+          if (TS_HTTP_STATUS_PARTIAL_CONTENT == TSHttpHdrStatusGet(response, resp_hdr)) {
+            // Temporarily change the response status to 200 OK, so we can reevaluate cacheability.
+            TSHttpHdrStatusSet(response, resp_hdr, TS_HTTP_STATUS_OK);
+            bool cacheable = TSHttpTxnIsCacheable(txnp, NULL, response);
+            TSHttpHdrStatusSet(response, resp_hdr, TS_HTTP_STATUS_PARTIAL_CONTENT);
+
+            TSDebug(PLUGIN_NAME, "Testing: request / response is cacheable?");
+            if (cacheable) {
+              BGFetchData* data = new BGFetchData();
+
+              // Initialize the data structure (can fail) and acquire a privileged lock on the URL
+              if (data->initialize(request, req_hdr, txnp) && data->acquire_url()) {
+                // We schedule this in about 200ms, that gives another request / response
+                // a chance to start before us.
+                data->schedule();
+              } else {
+                delete data;
+              }
+            }
+          }
+          // Release the response MLoc
+          TSHandleMLocRelease(response, TS_NULL_MLOC, resp_hdr);
+        }
+      }
+      // Release the request MLoc
+      TSHandleMLocRelease(request, TS_NULL_MLOC, req_hdr);
+    }
+  }
+
+  // Reenable and continue with the state machine.
+  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+  return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Setup global hooks
+void
+TSPluginInit(int /* argc ATS_UNUSED */, const char* /* argv ATS_UNUSED */[])
+{
+  TSPluginRegistrationInfo info;
+
+  info.plugin_name = (char*)PLUGIN_NAME;
+  info.vendor_name = (char*)"Apache Software Foundation";
+  info.support_email = (char*)"dev@trafficserver.apache.org";
+
+  if (TS_SUCCESS != TSPluginRegister(TS_SDK_VERSION_3_0 , &info)) {
+    TSError("%s: plugin registration failed.\n", PLUGIN_NAME);
+  }
+
+  TSDebug(PLUGIN_NAME, "Initialized");
+  TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, TSContCreate(cont_handle_response, NULL));
+}