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));
+}