You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by so...@apache.org on 2014/05/22 21:11:43 UTC

git commit: TS-2737: Rename rfc5861 plugin to stale_while_revalidate

Repository: trafficserver
Updated Branches:
  refs/heads/master 78aa134ca -> e49d84773


TS-2737: Rename rfc5861 plugin to stale_while_revalidate


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

Branch: refs/heads/master
Commit: e49d8477385123d186ecbf96737d79fc6ef1234d
Parents: 78aa134
Author: Phil Sorber <so...@apache.org>
Authored: Thu May 22 13:09:47 2014 -0600
Committer: Phil Sorber <so...@apache.org>
Committed: Thu May 22 13:11:24 2014 -0600

----------------------------------------------------------------------
 CHANGES                                         |   2 +
 NOTICE                                          |   2 +-
 configure.ac                                    |   2 +-
 plugins/experimental/Makefile.am                |   2 +-
 plugins/experimental/rfc5861/Makefile.am        |  21 -
 plugins/experimental/rfc5861/Makefile.tsxs      |  26 -
 plugins/experimental/rfc5861/README             |  26 -
 plugins/experimental/rfc5861/rfc5861.c          | 744 -------------------
 plugins/experimental/rfc5861/test_server.js     |  42 --
 .../stale_while_revalidate/Makefile.am          |  21 +
 .../stale_while_revalidate/Makefile.tsxs        |  27 +
 .../experimental/stale_while_revalidate/README  |  26 +
 .../stale_while_revalidate.c                    | 744 +++++++++++++++++++
 .../stale_while_revalidate/test_server.js       |  42 ++
 14 files changed, 865 insertions(+), 862 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 254cece..9a9e8e8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 5.0.0
 
+  *) [TS-2737] Rename rfc5861 plugin to stale_while_revalidate.
+
   *) [TS-2400] Our default SSL cipher-suite advocates speed over security
 
   *) [TS-2818] TSHttpTxnServerAddrSet() doesn't update the server port

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/NOTICE
----------------------------------------------------------------------
diff --git a/NOTICE b/NOTICE
index dacd4a7..3083af3 100644
--- a/NOTICE
+++ b/NOTICE
@@ -39,7 +39,7 @@ Copyright (C) 2011 Google Inc.
 
 ~~~
 
-rfc5861 Plugin developed by OmniTI on behalf of Oregon Health & Science University
+stale_while_revalidate Plugin developed by OmniTI on behalf of Oregon Health & Science University
 Copyright (C) 2012 Oregon Health & Science University
 
 ~~~

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index ab39b06..41ff592 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1919,8 +1919,8 @@ AC_CONFIG_FILES([
   plugins/experimental/hipes/Makefile
   plugins/experimental/metalink/Makefile
   plugins/experimental/remap_stats/Makefile
-  plugins/experimental/rfc5861/Makefile
   plugins/experimental/s3_auth/Makefile
+  plugins/experimental/stale_while_revalidate/Makefile
   plugins/experimental/ts_lua/Makefile
   plugins/experimental/url_sig/Makefile
   plugins/experimental/xdebug/Makefile

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am
index 621c40c..6192dfb 100644
--- a/plugins/experimental/Makefile.am
+++ b/plugins/experimental/Makefile.am
@@ -30,8 +30,8 @@ SUBDIRS = \
  hipes \
  metalink \
  remap_stats \
- rfc5861 \
  s3_auth \
+ stale_while_revalidate \
  ts_lua \
  url_sig \
  xdebug

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/rfc5861/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/rfc5861/Makefile.am b/plugins/experimental/rfc5861/Makefile.am
deleted file mode 100644
index d878858..0000000
--- a/plugins/experimental/rfc5861/Makefile.am
+++ /dev/null
@@ -1,21 +0,0 @@
-#  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 = rfc5861.la
-rfc5861_la_SOURCES = rfc5861.c
-rfc5861_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/rfc5861/Makefile.tsxs
----------------------------------------------------------------------
diff --git a/plugins/experimental/rfc5861/Makefile.tsxs b/plugins/experimental/rfc5861/Makefile.tsxs
deleted file mode 100644
index 0b312d4..0000000
--- a/plugins/experimental/rfc5861/Makefile.tsxs
+++ /dev/null
@@ -1,26 +0,0 @@
-#  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.
-
-TSXS?=tsxs
-
-all:	rfc5861.c
-	$(TSXS) -v -o rfc5861.so $? 
-
-install:
-	$(TSXS) -i -o rfc5861.so
-
-clean:
-	rm -f *.lo *.so

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/rfc5861/README
----------------------------------------------------------------------
diff --git a/plugins/experimental/rfc5861/README b/plugins/experimental/rfc5861/README
deleted file mode 100644
index aafba62..0000000
--- a/plugins/experimental/rfc5861/README
+++ /dev/null
@@ -1,26 +0,0 @@
-This plugin was sponsored by Oregon Health & Science University.
-
-Quick install:
-
-Make sure devel packages for traffic-server are installed.
-Make sure that 'tsxs' is in your path.
-
-	make -f Makefile.tsxs
-	make -f Makefile.tsxs install
-
-Add 'rfc5861.so' to plugin.config.
-
-Restart traffic-server.
-
-test_server.js is a test server written in JavaScript and
-meant to be run under node.js.
-
-Logging:
-
-You can enable logging by adding parameters to the plugin.config
-line for the plugin.
-
---log-all                       enable all logging
---log-stale-while-revalidate    enable logging of stale-while-revalidate
---log-stale-if-error            enable logging of stale-if-error
---log-filename <name>           set the filename to log to (<name>.log)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/rfc5861/rfc5861.c
----------------------------------------------------------------------
diff --git a/plugins/experimental/rfc5861/rfc5861.c b/plugins/experimental/rfc5861/rfc5861.c
deleted file mode 100644
index f1f6c5d..0000000
--- a/plugins/experimental/rfc5861/rfc5861.c
+++ /dev/null
@@ -1,744 +0,0 @@
-/** @file
-
-  Implements RFC 5861 (HTTP Cache-Control Extensions for Stale Content)
-
-  @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 <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <string.h>
-#include <stdbool.h>
-#include <search.h>
-#include <getopt.h>
-#include <arpa/inet.h>
-
-#include "ink_defs.h"
-#include "ts/ts.h"
-#include "ts/experimental.h"
-
-#define PLUGIN_NAME "rfc5861"
-
-static const char HTTP_VALUE_STALE_WHILE_REVALIDATE[] = "stale-while-revalidate";
-static const char HTTP_VALUE_STALE_IF_ERROR[] = "stale-if-error";
-static const char HTTP_VALUE_STALE_WARNING[] = "110 Response is stale";
-
-typedef struct
-{
-    TSTextLogObject object;
-    bool all, stale_if_error, stale_while_revalidate;
-    char* filename;
-} log_info_t;
-
-typedef struct
-{
-    void *troot;
-    TSMutex troot_mutex;
-    int txn_slot;
-    time_t stale_if_error_override;
-    log_info_t log_info;
-} config_t;
-
-typedef struct
-{
-    time_t date, stale_while_revalidate, stale_on_error, max_age;
-} CachedHeaderInfo;
-
-typedef struct
-{
-    char *effective_url;
-    TSMBuffer buf;
-    TSMLoc http_hdr_loc;
-    struct sockaddr *client_addr;
-} RequestInfo;
-
-typedef struct
-{
-    TSMBuffer buf;
-    TSMLoc http_hdr_loc;
-    TSHttpParser parser;
-    bool parsed;
-    TSHttpStatus status;
-} ResponseInfo;
-
-typedef struct
-{
-    TSHttpTxn txn;
-    TSCont main_cont;
-    bool async_req;
-    TSIOBuffer req_io_buf, resp_io_buf;
-    TSIOBufferReader req_io_buf_reader, resp_io_buf_reader;
-    TSVIO r_vio, w_vio;
-    TSVConn vconn;
-    RequestInfo *req_info;
-    ResponseInfo *resp_info;
-    time_t txn_start;
-    config_t *plugin_config;
-} StateInfo;
-
-static ResponseInfo*
-create_response_info(void)
-{
-    ResponseInfo *resp_info;
-
-    resp_info = (ResponseInfo *) TSmalloc(sizeof(ResponseInfo));
-
-    resp_info->buf = TSMBufferCreate();
-    resp_info->http_hdr_loc = TSHttpHdrCreate(resp_info->buf);
-    resp_info->parser = TSHttpParserCreate();
-    resp_info->parsed = false;
-
-    return resp_info;
-}
-
-static void
-free_response_info(ResponseInfo *resp_info)
-{
-    TSHandleMLocRelease(resp_info->buf, TS_NULL_MLOC, resp_info->http_hdr_loc);
-    TSMBufferDestroy(resp_info->buf);
-    TSHttpParserDestroy(resp_info->parser);
-    TSfree(resp_info);
-}
-
-static RequestInfo*
-create_request_info(TSHttpTxn txn)
-{
-    RequestInfo *req_info;
-    char *url;
-    int url_len;
-    TSMBuffer buf;
-    TSMLoc loc;
-
-    req_info = (RequestInfo *) TSmalloc(sizeof(RequestInfo));
-
-    url = TSHttpTxnEffectiveUrlStringGet(txn, &url_len);
-    req_info->effective_url = TSstrndup(url, url_len);
-    TSfree(url);
-
-    TSHttpTxnClientReqGet(txn, &buf, &loc);
-    req_info->buf = TSMBufferCreate();
-    TSHttpHdrClone(req_info->buf, buf, loc, &(req_info->http_hdr_loc));
-    TSHandleMLocRelease(buf, TS_NULL_MLOC, loc);
-
-    req_info->client_addr = TSmalloc(sizeof(struct sockaddr));
-    memmove((void *) req_info->client_addr, (void *) TSHttpTxnClientAddrGet(txn), sizeof(struct sockaddr));
-
-    return req_info;
-}
-
-static void
-free_request_info(RequestInfo *req_info)
-{
-    TSfree(req_info->effective_url);
-    TSHandleMLocRelease(req_info->buf, TS_NULL_MLOC, req_info->http_hdr_loc);
-    TSMBufferDestroy(req_info->buf);
-    TSfree(req_info->client_addr);
-    TSfree(req_info);
-}
-
-static CachedHeaderInfo*
-get_cached_header_info(TSHttpTxn txn)
-{
-    CachedHeaderInfo* chi;
-    TSMBuffer cr_buf;
-    TSMLoc cr_hdr_loc, cr_date_loc, cr_cache_control_loc, cr_cache_control_dup_loc;
-    int cr_cache_control_count, val_len, i;
-    char *value, *ptr;
-
-    chi = (CachedHeaderInfo *) TSmalloc(sizeof(CachedHeaderInfo));
-    chi->date = 0;
-    chi->max_age = 0;
-    chi->stale_while_revalidate = 0;
-    chi->stale_on_error = 0;
-
-    if (TSHttpTxnCachedRespGet(txn, &cr_buf, &cr_hdr_loc) == TS_SUCCESS)
-    {
-        cr_date_loc = TSMimeHdrFieldFind(cr_buf, cr_hdr_loc, TS_MIME_FIELD_DATE, TS_MIME_LEN_DATE);
-        if (cr_date_loc != TS_NULL_MLOC)
-        {
-            TSDebug(PLUGIN_NAME, "Found a date");
-            chi->date = TSMimeHdrFieldValueDateGet(cr_buf, cr_hdr_loc, cr_date_loc);
-            TSHandleMLocRelease(cr_buf, cr_hdr_loc, cr_date_loc);
-        }
-
-        cr_cache_control_loc = TSMimeHdrFieldFind(cr_buf, cr_hdr_loc, TS_MIME_FIELD_CACHE_CONTROL, TS_MIME_LEN_CACHE_CONTROL);
-
-        while(cr_cache_control_loc != TS_NULL_MLOC)
-        {
-            TSDebug(PLUGIN_NAME, "Found cache-control");
-            cr_cache_control_count = TSMimeHdrFieldValuesCount(cr_buf, cr_hdr_loc, cr_cache_control_loc);
-
-            for (i = 0; i < cr_cache_control_count; i++)
-            {
-                value = (char *) TSMimeHdrFieldValueStringGet(cr_buf, cr_hdr_loc, cr_cache_control_loc, i, &val_len);
-                ptr = value;
-
-                if (strncmp(value, TS_HTTP_VALUE_MAX_AGE, TS_HTTP_LEN_MAX_AGE) == 0)
-                {
-                    TSDebug(PLUGIN_NAME, "Found max-age");
-                    ptr += TS_HTTP_LEN_MAX_AGE;
-                    if (*ptr == '=')
-                    {
-                        ptr++;
-                        chi->max_age = atol(ptr);
-                    }
-                    else
-                    {
-                        ptr = TSstrndup(value, TS_HTTP_LEN_MAX_AGE + 2);
-                        TSDebug(PLUGIN_NAME, "This is what I found: %s", ptr);
-                        TSfree(ptr);
-                    }
-                }
-                else if (strncmp(value, HTTP_VALUE_STALE_WHILE_REVALIDATE, strlen(HTTP_VALUE_STALE_WHILE_REVALIDATE)) == 0)
-                {
-                    TSDebug(PLUGIN_NAME, "Found stale-while-revalidate");
-                    ptr += strlen(HTTP_VALUE_STALE_WHILE_REVALIDATE);
-                    if (*ptr == '=')
-                    {
-                        ptr++;
-                        chi->stale_while_revalidate = atol(ptr);
-                    }
-                }
-                else if (strncmp(value, HTTP_VALUE_STALE_IF_ERROR, strlen(HTTP_VALUE_STALE_IF_ERROR)) == 0)
-                {
-                    TSDebug(PLUGIN_NAME, "Found stale-on-error");
-                    ptr += strlen(HTTP_VALUE_STALE_IF_ERROR);
-                    if (*ptr == '=')
-                    {
-                        ptr++;
-                        chi->stale_on_error = atol(ptr);
-                    }
-                }
-                else
-                {
-                    TSDebug(PLUGIN_NAME, "Unknown field value");
-                }
-            }
-
-            cr_cache_control_dup_loc = TSMimeHdrFieldNextDup(cr_buf, cr_hdr_loc, cr_cache_control_loc);
-            TSHandleMLocRelease(cr_buf, cr_hdr_loc, cr_cache_control_loc);
-            cr_cache_control_loc = cr_cache_control_dup_loc;
-        }
-        TSHandleMLocRelease(cr_buf, TS_NULL_MLOC, cr_hdr_loc);
-    }
-
-    return chi;
-}
-
-static int
-xstrcmp(const void *a, const void *b)
-{
-    return strcmp((const char *) a, (const char *) b);
-}
-
-static void
-parse_response(StateInfo *state)
-{
-    TSIOBufferBlock block;
-    TSParseResult pr = TS_PARSE_CONT;
-    int64_t avail;
-    char *start;
-
-    block = TSIOBufferReaderStart(state->resp_io_buf_reader);
-
-    while ((pr == TS_PARSE_CONT) && (block != NULL))
-    {
-        start = (char *) TSIOBufferBlockReadStart(block, state->resp_io_buf_reader, &avail);
-        if (avail > 0)
-        {
-            pr = TSHttpHdrParseResp(state->resp_info->parser, state->resp_info->buf, state->resp_info->http_hdr_loc, (const char **) &start, (const char *) (start + avail));
-        }
-        block = TSIOBufferBlockNext(block);
-    }
-
-    if (pr != TS_PARSE_CONT)
-    {
-        state->resp_info->status = TSHttpHdrStatusGet(state->resp_info->buf, state->resp_info->http_hdr_loc);
-        state->resp_info->parsed = true;
-        TSDebug(PLUGIN_NAME, "HTTP Status: %d", state->resp_info->status);
-    }
-}
-
-static int
-consume_resource(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-{
-    StateInfo *state;
-    int64_t avail;
-    TSVConn vconn;
-    TSMLoc url_loc;
-    int lookup_count;
-
-    vconn = (TSVConn) edata;
-    state = (StateInfo *) TSContDataGet(cont);
-
-    switch (event)
-    {
-        case TS_EVENT_VCONN_WRITE_READY:
-            // We shouldn't get here because we specify the exact size of the buffer.
-            TSDebug(PLUGIN_NAME, "Write Ready");
-        case TS_EVENT_VCONN_WRITE_COMPLETE:
-            TSDebug(PLUGIN_NAME, "Write Complete");
-            //TSDebug(PLUGIN_NAME, "TSVConnShutdown()");
-            //TSVConnShutdown(state->vconn, 0, 1);
-            //TSVIOReenable(state->w_vio);
-            break;
-        case TS_EVENT_VCONN_READ_READY:
-            TSDebug(PLUGIN_NAME, "Read Ready");
-
-            avail = TSIOBufferReaderAvail(state->resp_io_buf_reader);
-
-            if ((state->resp_info) && !state->resp_info->parsed)
-            {
-                parse_response(state);
-            }
-
-            // Consume data
-            avail = TSIOBufferReaderAvail(state->resp_io_buf_reader);
-            TSIOBufferReaderConsume(state->resp_io_buf_reader, avail);
-            TSVIONDoneSet(state->r_vio, TSVIONDoneGet(state->r_vio) + avail);
-            TSVIOReenable(state->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, "Inactivity Timeout");
-                TSVConnAbort(vconn, TS_VC_CLOSE_ABORT);
-            }
-            else
-            {
-                if (event == TS_EVENT_VCONN_READ_COMPLETE)
-                {
-                    TSDebug(PLUGIN_NAME, "Read Complete");
-                }
-                else if (event == TS_EVENT_VCONN_EOS)
-                {
-                    TSDebug(PLUGIN_NAME, "EOS");
-                }
-                TSVConnClose(state->vconn);
-            }
-
-            avail = TSIOBufferReaderAvail(state->resp_io_buf_reader);
-
-            if ((state->resp_info) && !state->resp_info->parsed)
-            {
-                parse_response(state);
-            }
-
-            // Consume data
-            avail = TSIOBufferReaderAvail(state->resp_io_buf_reader);
-            TSIOBufferReaderConsume(state->resp_io_buf_reader, avail);
-            TSVIONDoneSet(state->r_vio, TSVIONDoneGet(state->r_vio) + avail);
-            if (state->async_req)
-            {
-                TSDebug(PLUGIN_NAME, "Unlock URL");
-                TSMutexLock(state->plugin_config->troot_mutex);
-                tdelete(state->req_info->effective_url, &(state->plugin_config->troot), xstrcmp);
-                TSMutexUnlock(state->plugin_config->troot_mutex);
-            }
-            else
-            {
-                TSDebug(PLUGIN_NAME, "In sync path. setting fresh and re-enabling");
-                TSHttpTxnCacheLookupCountGet(state->txn, &lookup_count);
-                if ((state->resp_info->status == 500) || ((state->resp_info->status >= 502) && (state->resp_info->status <= 504)) || lookup_count > 2)
-                {
-                    TSDebug(PLUGIN_NAME, "Sending stale data as fresh");
-                    if (state->plugin_config->log_info.object && (state->plugin_config->log_info.all || state->plugin_config->log_info.stale_if_error))
-                    {
-                        CachedHeaderInfo *chi = get_cached_header_info(state->txn);
-                        TSTextLogObjectWrite(state->plugin_config->log_info.object, "stale-if-error: %d - %d < %d + %d %s", (int) state->txn_start, (int) chi->date, (int) chi->max_age, (int) chi->stale_on_error, state->req_info->effective_url);
-                        TSfree(chi);
-                    }
-                    TSHttpTxnHookAdd(state->txn, TS_HTTP_SEND_RESPONSE_HDR_HOOK, state->main_cont);
-                    TSHttpTxnCacheLookupStatusSet(state->txn, TS_CACHE_LOOKUP_HIT_FRESH);
-                }
-                else
-                {
-                    TSDebug(PLUGIN_NAME, "Attempting new cache lookup");
-                    TSHttpHdrUrlGet(state->req_info->buf, state->req_info->http_hdr_loc, &url_loc);
-                    TSHttpTxnNewCacheLookupDo(state->txn, state->req_info->buf, url_loc);
-                    TSHandleMLocRelease(state->req_info->buf, state->req_info->http_hdr_loc, url_loc);
-                    // TODO add txn translation hook and pass result along, maybe inside continuation?
-                    //TSHttpTxnHookAdd(state->txn, TS_HTTP_RESPONSE_TRANSFORM_HOOK, TSTransformCreate(replace_transform, state->txn));
-                }
-                TSHttpTxnReenable(state->txn, TS_EVENT_HTTP_CONTINUE);
-            }
-            free_request_info(state->req_info);
-            if (state->resp_info)
-            {
-                free_response_info(state->resp_info);
-            }
-            TSIOBufferReaderFree(state->req_io_buf_reader);
-            TSIOBufferDestroy(state->req_io_buf);
-            TSIOBufferReaderFree(state->resp_io_buf_reader);
-            TSIOBufferDestroy(state->resp_io_buf);
-            TSfree(state);
-            TSContDestroy(cont);
-            break;
-        default:
-            TSError("Unknown event %d.", event);
-            break;
-    }
-
-    return 0;
-}
-
-static int
-fetch_resource(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-{
-    StateInfo *state;
-    TSCont consume_cont;
-    //struct sockaddr_in client_addr;
-    TSMLoc connection_hdr_loc, connection_hdr_dup_loc;
-
-    state = (StateInfo *) TSContDataGet(cont);
-
-    //li = (RequestInfo *) edata;
-    if (state->async_req)
-    {
-        TSMutexLock(state->plugin_config->troot_mutex);
-        // If already doing async lookup lets just close shop and go home
-        if (tfind(state->req_info->effective_url, &(state->plugin_config->troot), xstrcmp) != NULL)
-        {
-            TSDebug(PLUGIN_NAME, "Looks like an async is already in progress");
-            TSMutexUnlock(state->plugin_config->troot_mutex);
-            free_request_info(state->req_info);
-            TSfree(state);
-        }
-        // Otherwise lets do the lookup!
-        else
-        {
-            // Lock in tree
-            TSDebug(PLUGIN_NAME, "Locking URL");
-            tsearch(state->req_info->effective_url, &(state->plugin_config->troot), xstrcmp);
-            TSMutexUnlock(state->plugin_config->troot_mutex);
-        }
-    }
-
-    if (state)
-    {
-        TSDebug(PLUGIN_NAME, "Lets do the lookup");
-        consume_cont = TSContCreate(consume_resource, NULL);
-        TSContDataSet(consume_cont, (void *) state);
-
-        if (state->async_req)
-        {
-            state->resp_info = NULL;
-        }
-        else
-        {
-            state->resp_info = create_response_info();
-        }
-
-        TSDebug(PLUGIN_NAME, "Set Connection: close");
-        connection_hdr_loc = TSMimeHdrFieldFind(state->req_info->buf, state->req_info->http_hdr_loc, TS_MIME_FIELD_CONNECTION, TS_MIME_LEN_CONNECTION);
-
-        while(connection_hdr_loc != TS_NULL_MLOC)
-        {
-            TSDebug(PLUGIN_NAME, "Found old Connection hdr");
-
-            connection_hdr_dup_loc = TSMimeHdrFieldNextDup(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
-            TSMimeHdrFieldRemove(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
-            TSMimeHdrFieldDestroy(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
-            TSHandleMLocRelease(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
-            connection_hdr_loc = connection_hdr_dup_loc;
-        }
-
-        // This seems to have little effect
-        TSDebug(PLUGIN_NAME, "Creating Connection hdr");
-        TSMimeHdrFieldCreateNamed(state->req_info->buf, state->req_info->http_hdr_loc, TS_MIME_FIELD_CONNECTION, TS_MIME_LEN_CONNECTION, &connection_hdr_loc);
-        TSMimeHdrFieldValueStringInsert(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc, -1, TS_HTTP_VALUE_CLOSE, TS_HTTP_LEN_CLOSE);
-        TSMimeHdrFieldAppend(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
-        TSHandleMLocRelease(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
-
-        /*
-        TSDebug(PLUGIN_NAME, "Creating @RFC5861 header");
-        TSMimeHdrFieldCreateNamed(state->req_info->buf, state->req_info->http_hdr_loc, TS_MIME_FIELD_CONNECTION, TS_MIME_LEN_CONNECTION, &connection_hdr_loc);
-        TSMimeHdrFieldValueStringInsert(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc, -1, TS_HTTP_VALUE_CLOSE, TS_HTTP_LEN_CLOSE);
-        TSMimeHdrFieldAppend(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
-        TSHandleMLocRelease(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
-        */
-
-        TSDebug(PLUGIN_NAME, "Create Buffers");
-        state->req_io_buf = TSIOBufferCreate();
-        state->req_io_buf_reader = TSIOBufferReaderAlloc(state->req_io_buf);
-        state->resp_io_buf = TSIOBufferCreate();
-        state->resp_io_buf_reader = TSIOBufferReaderAlloc(state->resp_io_buf);
-
-        TSHttpHdrPrint(state->req_info->buf, state->req_info->http_hdr_loc, state->req_io_buf);
-        TSIOBufferWrite(state->req_io_buf, "\r\n", 2);
-
-        //memmove((void *) &client_addr, (void *) state->req_info->client_addr, sizeof(struct sockaddr));
-        //TSDebug(PLUGIN_NAME, "client_addr: %s:%d", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
-        state->vconn = TSHttpConnect((struct sockaddr const *) state->req_info->client_addr);
-
-        state->r_vio = TSVConnRead(state->vconn, consume_cont, state->resp_io_buf, INT64_MAX);
-        state->w_vio = TSVConnWrite(state->vconn, consume_cont, state->req_io_buf_reader, TSIOBufferReaderAvail(state->req_io_buf_reader));
-    }
-
-    TSContDestroy(cont);
-
-    return 0;
-}
-
-static int
-rfc5861_plugin(TSCont cont, TSEvent event, void *edata)
-{
-    TSHttpTxn txn = (TSHttpTxn) edata;
-    int status, lookup_count;
-    CachedHeaderInfo *chi;
-    TSCont fetch_cont;
-    StateInfo *state;
-    TSMBuffer buf;
-    TSMLoc loc,warn_loc;
-    TSHttpStatus http_status;
-    config_t *plugin_config;
-
-    switch (event)
-    {
-        // Is this the proper event?
-        case TS_EVENT_HTTP_READ_REQUEST_HDR:
-
-            if (TSHttpIsInternalRequest(txn) != TS_SUCCESS)
-            {
-                TSDebug(PLUGIN_NAME, "External Request");
-                plugin_config = (config_t *) TSContDataGet(cont);
-                state = TSmalloc(sizeof(StateInfo));
-                state->plugin_config = plugin_config;
-                time(&state->txn_start);
-                state->req_info = create_request_info(txn);
-                TSHttpTxnArgSet(txn, state->plugin_config->txn_slot, (void *) state);
-                TSHttpTxnHookAdd(txn, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, cont);
-            }
-            else
-            {
-                TSDebug(PLUGIN_NAME, "Internal Request"); // This is insufficient if there are other plugins using TSHttpConnect
-                TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_SHARE_SERVER_SESSIONS, 1);
-                //TSHttpTxnHookAdd(txn, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, cont);
-                TSHttpTxnHookAdd(txn, TS_HTTP_READ_RESPONSE_HDR_HOOK, cont);
-                // This might be needed in 3.2.0 to fix a timeout issue
-                //TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_TRANSACTION_NO_ACTIVITY_TIMEOUT_IN, 5);
-                //TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_TRANSACTION_NO_ACTIVITY_TIMEOUT_OUT, 5);
-            }
-
-            TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-            break;
-        case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
-            plugin_config = (config_t *) TSContDataGet(cont);
-            state = (StateInfo *) TSHttpTxnArgGet(txn, plugin_config->txn_slot);
-            TSHttpTxnCacheLookupCountGet(txn, &lookup_count);
-            if (TSHttpTxnCacheLookupStatusGet(txn, &status) == TS_SUCCESS)
-            {
-                // Are we stale?
-                if (status == TS_CACHE_LOOKUP_HIT_STALE)
-                {
-                    TSDebug(PLUGIN_NAME, "CacheLookupStatus is STALE");
-                    // Get headers
-                    chi = get_cached_header_info(txn);
-
-                    if (state->plugin_config->stale_if_error_override > chi->stale_on_error)
-                        chi->stale_on_error = state->plugin_config->stale_if_error_override;
-
-                    if ((state->txn_start - chi->date) < (chi->max_age + chi->stale_while_revalidate))
-                    {
-                        TSDebug(PLUGIN_NAME, "Looks like we can return fresh info and validate in the background");
-                        if (state->plugin_config->log_info.object && (state->plugin_config->log_info.all || state->plugin_config->log_info.stale_while_revalidate))
-                            TSTextLogObjectWrite(state->plugin_config->log_info.object, "stale-while-revalidate: %d - %d < %d + %d %s", (int) state->txn_start, (int) chi->date, (int) chi->max_age, (int) chi->stale_while_revalidate, state->req_info->effective_url);
-                        // lookup async
-
-#if (TS_VERSION_NUMBER >= 3003000)
-                        TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_INSERT_AGE_IN_RESPONSE, 1);
-#endif
-                        // Set warning header
-                        TSHttpTxnHookAdd(txn, TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
-
-                        TSDebug(PLUGIN_NAME, "set state as async");
-                        state->async_req = true;
-                        TSHttpTxnCacheLookupStatusSet(txn, TS_CACHE_LOOKUP_HIT_FRESH);
-                        //TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-                        fetch_cont = TSContCreate(fetch_resource, NULL);
-                        TSContDataSet(fetch_cont, (void *) state);
-                        TSContSchedule(fetch_cont, 0, TS_THREAD_POOL_TASK);
-                        TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-                    }
-                    else if ((state->txn_start - chi->date) < (chi->max_age + chi->stale_on_error))
-                    {
-                        TSDebug(PLUGIN_NAME, "Looks like we can return fresh data on 500 error");
-#if (TS_VERSION_NUMBER >= 3003000)
-                        TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_INSERT_AGE_IN_RESPONSE, 1);
-#endif
-                        //lookup sync
-                        state->async_req = false;
-                        state->txn = txn;
-                        state->main_cont = cont; // we need this for the warning header callback. not sure i like it, but it works.
-                        fetch_cont = TSContCreate(fetch_resource, NULL);
-                        TSContDataSet(fetch_cont, (void *) state);
-                        TSContSchedule(fetch_cont, 0, TS_THREAD_POOL_NET);
-                    }
-                    else
-                    {
-                        TSDebug(PLUGIN_NAME, "No love? now: %d date: %d max-age: %d swr: %d soe: %d", (int) state->txn_start, (int) chi->date, (int) chi->max_age, (int) chi->stale_while_revalidate, (int) chi->stale_on_error);
-                        if (lookup_count == 1)
-                        {
-                            free_request_info(state->req_info);
-                            TSfree(state);
-                        }
-                        TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-                    }
-
-                    TSfree(chi);
-                }
-                else
-                {
-                    TSDebug(PLUGIN_NAME, "Not Stale!");
-                    if (lookup_count == 1)
-                    {
-                        free_request_info(state->req_info);
-                        TSfree(state);
-                    }
-                    TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-                }
-            }
-            else
-            {
-                TSDebug(PLUGIN_NAME, "Could not get CacheLookupStatus");
-                if (lookup_count == 1)
-                {
-                    free_request_info(state->req_info);
-                    TSfree(state);
-                }
-                TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-            }
-            break;
-        case TS_EVENT_HTTP_READ_RESPONSE_HDR:
-            TSHttpTxnServerRespGet(txn, &buf, &loc);
-            http_status = TSHttpHdrStatusGet(buf, loc);
-            if ((http_status == 500) || ((http_status >= 502) && (http_status <= 504))) // 500, 502, 503, or 504
-            {
-                TSDebug(PLUGIN_NAME, "Set non-cachable");
-#if (TS_VERSION_NUMBER >= 3003000)
-                TSHttpTxnServerRespNoStoreSet(txn,1);
-#else
-                TSHttpTxnServerRespNoStore(txn);
-#endif
-            }
-            TSHandleMLocRelease(buf, TS_NULL_MLOC, loc);
-            TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-            break;
-        case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
-            TSDebug(PLUGIN_NAME, "set warning header");
-            TSHttpTxnClientRespGet(txn, &buf, &loc);
-            TSMimeHdrFieldCreateNamed(buf, loc, TS_MIME_FIELD_WARNING, TS_MIME_LEN_WARNING, &warn_loc);
-            TSMimeHdrFieldValueStringInsert(buf, loc, warn_loc, -1, HTTP_VALUE_STALE_WARNING, strlen(HTTP_VALUE_STALE_WARNING));
-            TSMimeHdrFieldAppend(buf, loc, warn_loc);
-            TSHandleMLocRelease(buf, loc, warn_loc);
-            TSHandleMLocRelease(buf, TS_NULL_MLOC, loc);
-            TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-            break;
-        default:
-            TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-            break;
-    }
-
-    return 0;
-}
-
-void
-TSPluginInit (int argc, const char *argv[])
-{
-    config_t *plugin_config;
-    TSPluginRegistrationInfo info;
-    TSCont main_cont;
-
-    info.plugin_name = PLUGIN_NAME;
-    info.vendor_name = "Apache Software Foundation";
-    info.support_email = "dev@trafficserver.apache.org";
-
-    if (TSPluginRegister(TS_SDK_VERSION_3_0 , &info) != TS_SUCCESS)
-    {
-        TSError("Plugin registration failed.\n");
-        return;
-    }
-    else
-    {
-        TSDebug(PLUGIN_NAME, "Plugin registration succeeded.\n");
-    }
-
-    plugin_config = TSmalloc(sizeof(config_t));
-
-    plugin_config->troot = NULL;
-    plugin_config->troot_mutex = TSMutexCreate();
-    plugin_config->stale_if_error_override = 0;
-    plugin_config->log_info.object = NULL;
-    plugin_config->log_info.all = false;
-    plugin_config->log_info.stale_if_error = false;
-    plugin_config->log_info.stale_while_revalidate = false;
-    plugin_config->log_info.filename = PLUGIN_NAME;
-
-    if (argc > 1)
-    {
-        int c;
-        optind = 1;
-        static const struct option longopts[] = {
-                { "log-all", no_argument, NULL, 'a' },
-                { "log-stale-while-revalidate", no_argument, NULL, 'r' },
-                { "log-stale-if-error", no_argument, NULL, 'e' },
-                { "log-filename", required_argument, NULL, 'f' },
-                { "force-stale-if-error", required_argument, NULL, 'E' },
-                { NULL, 0, NULL, 0 }
-            };
-
-        while ((c = getopt_long(argc, (char * const*) argv, "aref:E:", longopts, NULL)) != -1)
-        {
-            switch (c)
-            {
-                case 'a':
-                    plugin_config->log_info.all = true;
-                    break;
-                case 'r':
-                    plugin_config->log_info.stale_while_revalidate = true;
-                    break;
-                case 'e':
-                    plugin_config->log_info.stale_if_error = true;
-                    break;
-                case 'f':
-                    plugin_config->log_info.filename = strdup(optarg);
-                    break;
-                case 'E':
-                    plugin_config->stale_if_error_override = atoi(optarg);
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        if (plugin_config->log_info.all || plugin_config->log_info.stale_while_revalidate || plugin_config->log_info.stale_if_error)
-            TSTextLogObjectCreate(plugin_config->log_info.filename, TS_LOG_MODE_ADD_TIMESTAMP, &(plugin_config->log_info.object));
-    }
-
-    // proxy.config.http.insert_age_in_response
-    TSHttpArgIndexReserve(PLUGIN_NAME, "txn state info", &(plugin_config->txn_slot));
-    main_cont = TSContCreate(rfc5861_plugin, NULL);
-    TSContDataSet(main_cont, (void *) plugin_config);
-    TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, main_cont);
-
-    TSDebug(PLUGIN_NAME, "Plugin Init Complete.\n");
-}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/rfc5861/test_server.js
----------------------------------------------------------------------
diff --git a/plugins/experimental/rfc5861/test_server.js b/plugins/experimental/rfc5861/test_server.js
deleted file mode 100755
index 5225a08..0000000
--- a/plugins/experimental/rfc5861/test_server.js
+++ /dev/null
@@ -1,42 +0,0 @@
-#! /usr/bin/env node
-
-/** @file
-
-  Implements Simple HTTP test server for the rfc5861 plugin
-
-  @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.
- */
-
-var http = require('http');
-var url  = require('url');
-
-http.createServer(function (request, response) {
-  console.log(Date());
-  setTimeout( function (req, res) {
-    console.log(req.headers);
-    console.log(url.parse(req.url));
-    res.writeHead(200, {'Content-Type': 'text/plain', 'Cache-Control': 'max-age=5, stale-while-revalidate=55'});
-    //res.writeHead(500, {'Content-Type': 'text/plain', 'Cache-Control': 'max-age=5, stale-if-error=555'});
-    res.end(Date() + '\n');
-    console.log(Date() + '\n');
-  }, 50, request, response);
-}).listen(8081, '127.0.0.1');
-
-console.log('Proxy running at http://127.0.0.1:8080/');
-console.log('Server running at http://127.0.0.1:8081/');

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/stale_while_revalidate/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/stale_while_revalidate/Makefile.am b/plugins/experimental/stale_while_revalidate/Makefile.am
new file mode 100644
index 0000000..5bff627
--- /dev/null
+++ b/plugins/experimental/stale_while_revalidate/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 = stale_while_revalidate.la
+stale_while_revalidate_la_SOURCES = stale_while_revalidate.c
+stale_while_revalidate_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/stale_while_revalidate/Makefile.tsxs
----------------------------------------------------------------------
diff --git a/plugins/experimental/stale_while_revalidate/Makefile.tsxs b/plugins/experimental/stale_while_revalidate/Makefile.tsxs
new file mode 100644
index 0000000..30e651e
--- /dev/null
+++ b/plugins/experimental/stale_while_revalidate/Makefile.tsxs
@@ -0,0 +1,27 @@
+#  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.
+
+PLUGIN=stale_while_revalidate
+TSXS?=tsxs
+
+all:	${PLUGIN}.c
+	$(TSXS) -v -o ${PLUGIN}.so $? 
+
+install:
+	$(TSXS) -i -o ${PLUGIN}.so
+
+clean:
+	rm -f *.lo *.so

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/stale_while_revalidate/README
----------------------------------------------------------------------
diff --git a/plugins/experimental/stale_while_revalidate/README b/plugins/experimental/stale_while_revalidate/README
new file mode 100644
index 0000000..ef9e5aa
--- /dev/null
+++ b/plugins/experimental/stale_while_revalidate/README
@@ -0,0 +1,26 @@
+This plugin was sponsored by Oregon Health & Science University.
+
+Quick install:
+
+Make sure devel packages for traffic-server are installed.
+Make sure that 'tsxs' is in your path.
+
+	make -f Makefile.tsxs
+	make -f Makefile.tsxs install
+
+Add 'stale_while_revalidate.so' to plugin.config.
+
+Restart traffic-server.
+
+test_server.js is a test server written in JavaScript and
+meant to be run under node.js.
+
+Logging:
+
+You can enable logging by adding parameters to the plugin.config
+line for the plugin.
+
+--log-all                       enable all logging
+--log-stale-while-revalidate    enable logging of stale-while-revalidate
+--log-stale-if-error            enable logging of stale-if-error
+--log-filename <name>           set the filename to log to (<name>.log)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
----------------------------------------------------------------------
diff --git a/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c b/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
new file mode 100644
index 0000000..48c9cb8
--- /dev/null
+++ b/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
@@ -0,0 +1,744 @@
+/** @file
+
+  Implements RFC 5861 (HTTP Cache-Control Extensions for Stale Content)
+
+  @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 <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <stdbool.h>
+#include <search.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+
+#include "ink_defs.h"
+#include "ts/ts.h"
+#include "ts/experimental.h"
+
+#define PLUGIN_NAME "stale_while_revalidate"
+
+static const char HTTP_VALUE_STALE_WHILE_REVALIDATE[] = "stale-while-revalidate";
+static const char HTTP_VALUE_STALE_IF_ERROR[] = "stale-if-error";
+static const char HTTP_VALUE_STALE_WARNING[] = "110 Response is stale";
+
+typedef struct
+{
+    TSTextLogObject object;
+    bool all, stale_if_error, stale_while_revalidate;
+    char* filename;
+} log_info_t;
+
+typedef struct
+{
+    void *troot;
+    TSMutex troot_mutex;
+    int txn_slot;
+    time_t stale_if_error_override;
+    log_info_t log_info;
+} config_t;
+
+typedef struct
+{
+    time_t date, stale_while_revalidate, stale_on_error, max_age;
+} CachedHeaderInfo;
+
+typedef struct
+{
+    char *effective_url;
+    TSMBuffer buf;
+    TSMLoc http_hdr_loc;
+    struct sockaddr *client_addr;
+} RequestInfo;
+
+typedef struct
+{
+    TSMBuffer buf;
+    TSMLoc http_hdr_loc;
+    TSHttpParser parser;
+    bool parsed;
+    TSHttpStatus status;
+} ResponseInfo;
+
+typedef struct
+{
+    TSHttpTxn txn;
+    TSCont main_cont;
+    bool async_req;
+    TSIOBuffer req_io_buf, resp_io_buf;
+    TSIOBufferReader req_io_buf_reader, resp_io_buf_reader;
+    TSVIO r_vio, w_vio;
+    TSVConn vconn;
+    RequestInfo *req_info;
+    ResponseInfo *resp_info;
+    time_t txn_start;
+    config_t *plugin_config;
+} StateInfo;
+
+static ResponseInfo*
+create_response_info(void)
+{
+    ResponseInfo *resp_info;
+
+    resp_info = (ResponseInfo *) TSmalloc(sizeof(ResponseInfo));
+
+    resp_info->buf = TSMBufferCreate();
+    resp_info->http_hdr_loc = TSHttpHdrCreate(resp_info->buf);
+    resp_info->parser = TSHttpParserCreate();
+    resp_info->parsed = false;
+
+    return resp_info;
+}
+
+static void
+free_response_info(ResponseInfo *resp_info)
+{
+    TSHandleMLocRelease(resp_info->buf, TS_NULL_MLOC, resp_info->http_hdr_loc);
+    TSMBufferDestroy(resp_info->buf);
+    TSHttpParserDestroy(resp_info->parser);
+    TSfree(resp_info);
+}
+
+static RequestInfo*
+create_request_info(TSHttpTxn txn)
+{
+    RequestInfo *req_info;
+    char *url;
+    int url_len;
+    TSMBuffer buf;
+    TSMLoc loc;
+
+    req_info = (RequestInfo *) TSmalloc(sizeof(RequestInfo));
+
+    url = TSHttpTxnEffectiveUrlStringGet(txn, &url_len);
+    req_info->effective_url = TSstrndup(url, url_len);
+    TSfree(url);
+
+    TSHttpTxnClientReqGet(txn, &buf, &loc);
+    req_info->buf = TSMBufferCreate();
+    TSHttpHdrClone(req_info->buf, buf, loc, &(req_info->http_hdr_loc));
+    TSHandleMLocRelease(buf, TS_NULL_MLOC, loc);
+
+    req_info->client_addr = TSmalloc(sizeof(struct sockaddr));
+    memmove((void *) req_info->client_addr, (void *) TSHttpTxnClientAddrGet(txn), sizeof(struct sockaddr));
+
+    return req_info;
+}
+
+static void
+free_request_info(RequestInfo *req_info)
+{
+    TSfree(req_info->effective_url);
+    TSHandleMLocRelease(req_info->buf, TS_NULL_MLOC, req_info->http_hdr_loc);
+    TSMBufferDestroy(req_info->buf);
+    TSfree(req_info->client_addr);
+    TSfree(req_info);
+}
+
+static CachedHeaderInfo*
+get_cached_header_info(TSHttpTxn txn)
+{
+    CachedHeaderInfo* chi;
+    TSMBuffer cr_buf;
+    TSMLoc cr_hdr_loc, cr_date_loc, cr_cache_control_loc, cr_cache_control_dup_loc;
+    int cr_cache_control_count, val_len, i;
+    char *value, *ptr;
+
+    chi = (CachedHeaderInfo *) TSmalloc(sizeof(CachedHeaderInfo));
+    chi->date = 0;
+    chi->max_age = 0;
+    chi->stale_while_revalidate = 0;
+    chi->stale_on_error = 0;
+
+    if (TSHttpTxnCachedRespGet(txn, &cr_buf, &cr_hdr_loc) == TS_SUCCESS)
+    {
+        cr_date_loc = TSMimeHdrFieldFind(cr_buf, cr_hdr_loc, TS_MIME_FIELD_DATE, TS_MIME_LEN_DATE);
+        if (cr_date_loc != TS_NULL_MLOC)
+        {
+            TSDebug(PLUGIN_NAME, "Found a date");
+            chi->date = TSMimeHdrFieldValueDateGet(cr_buf, cr_hdr_loc, cr_date_loc);
+            TSHandleMLocRelease(cr_buf, cr_hdr_loc, cr_date_loc);
+        }
+
+        cr_cache_control_loc = TSMimeHdrFieldFind(cr_buf, cr_hdr_loc, TS_MIME_FIELD_CACHE_CONTROL, TS_MIME_LEN_CACHE_CONTROL);
+
+        while(cr_cache_control_loc != TS_NULL_MLOC)
+        {
+            TSDebug(PLUGIN_NAME, "Found cache-control");
+            cr_cache_control_count = TSMimeHdrFieldValuesCount(cr_buf, cr_hdr_loc, cr_cache_control_loc);
+
+            for (i = 0; i < cr_cache_control_count; i++)
+            {
+                value = (char *) TSMimeHdrFieldValueStringGet(cr_buf, cr_hdr_loc, cr_cache_control_loc, i, &val_len);
+                ptr = value;
+
+                if (strncmp(value, TS_HTTP_VALUE_MAX_AGE, TS_HTTP_LEN_MAX_AGE) == 0)
+                {
+                    TSDebug(PLUGIN_NAME, "Found max-age");
+                    ptr += TS_HTTP_LEN_MAX_AGE;
+                    if (*ptr == '=')
+                    {
+                        ptr++;
+                        chi->max_age = atol(ptr);
+                    }
+                    else
+                    {
+                        ptr = TSstrndup(value, TS_HTTP_LEN_MAX_AGE + 2);
+                        TSDebug(PLUGIN_NAME, "This is what I found: %s", ptr);
+                        TSfree(ptr);
+                    }
+                }
+                else if (strncmp(value, HTTP_VALUE_STALE_WHILE_REVALIDATE, strlen(HTTP_VALUE_STALE_WHILE_REVALIDATE)) == 0)
+                {
+                    TSDebug(PLUGIN_NAME, "Found stale-while-revalidate");
+                    ptr += strlen(HTTP_VALUE_STALE_WHILE_REVALIDATE);
+                    if (*ptr == '=')
+                    {
+                        ptr++;
+                        chi->stale_while_revalidate = atol(ptr);
+                    }
+                }
+                else if (strncmp(value, HTTP_VALUE_STALE_IF_ERROR, strlen(HTTP_VALUE_STALE_IF_ERROR)) == 0)
+                {
+                    TSDebug(PLUGIN_NAME, "Found stale-on-error");
+                    ptr += strlen(HTTP_VALUE_STALE_IF_ERROR);
+                    if (*ptr == '=')
+                    {
+                        ptr++;
+                        chi->stale_on_error = atol(ptr);
+                    }
+                }
+                else
+                {
+                    TSDebug(PLUGIN_NAME, "Unknown field value");
+                }
+            }
+
+            cr_cache_control_dup_loc = TSMimeHdrFieldNextDup(cr_buf, cr_hdr_loc, cr_cache_control_loc);
+            TSHandleMLocRelease(cr_buf, cr_hdr_loc, cr_cache_control_loc);
+            cr_cache_control_loc = cr_cache_control_dup_loc;
+        }
+        TSHandleMLocRelease(cr_buf, TS_NULL_MLOC, cr_hdr_loc);
+    }
+
+    return chi;
+}
+
+static int
+xstrcmp(const void *a, const void *b)
+{
+    return strcmp((const char *) a, (const char *) b);
+}
+
+static void
+parse_response(StateInfo *state)
+{
+    TSIOBufferBlock block;
+    TSParseResult pr = TS_PARSE_CONT;
+    int64_t avail;
+    char *start;
+
+    block = TSIOBufferReaderStart(state->resp_io_buf_reader);
+
+    while ((pr == TS_PARSE_CONT) && (block != NULL))
+    {
+        start = (char *) TSIOBufferBlockReadStart(block, state->resp_io_buf_reader, &avail);
+        if (avail > 0)
+        {
+            pr = TSHttpHdrParseResp(state->resp_info->parser, state->resp_info->buf, state->resp_info->http_hdr_loc, (const char **) &start, (const char *) (start + avail));
+        }
+        block = TSIOBufferBlockNext(block);
+    }
+
+    if (pr != TS_PARSE_CONT)
+    {
+        state->resp_info->status = TSHttpHdrStatusGet(state->resp_info->buf, state->resp_info->http_hdr_loc);
+        state->resp_info->parsed = true;
+        TSDebug(PLUGIN_NAME, "HTTP Status: %d", state->resp_info->status);
+    }
+}
+
+static int
+consume_resource(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
+{
+    StateInfo *state;
+    int64_t avail;
+    TSVConn vconn;
+    TSMLoc url_loc;
+    int lookup_count;
+
+    vconn = (TSVConn) edata;
+    state = (StateInfo *) TSContDataGet(cont);
+
+    switch (event)
+    {
+        case TS_EVENT_VCONN_WRITE_READY:
+            // We shouldn't get here because we specify the exact size of the buffer.
+            TSDebug(PLUGIN_NAME, "Write Ready");
+        case TS_EVENT_VCONN_WRITE_COMPLETE:
+            TSDebug(PLUGIN_NAME, "Write Complete");
+            //TSDebug(PLUGIN_NAME, "TSVConnShutdown()");
+            //TSVConnShutdown(state->vconn, 0, 1);
+            //TSVIOReenable(state->w_vio);
+            break;
+        case TS_EVENT_VCONN_READ_READY:
+            TSDebug(PLUGIN_NAME, "Read Ready");
+
+            avail = TSIOBufferReaderAvail(state->resp_io_buf_reader);
+
+            if ((state->resp_info) && !state->resp_info->parsed)
+            {
+                parse_response(state);
+            }
+
+            // Consume data
+            avail = TSIOBufferReaderAvail(state->resp_io_buf_reader);
+            TSIOBufferReaderConsume(state->resp_io_buf_reader, avail);
+            TSVIONDoneSet(state->r_vio, TSVIONDoneGet(state->r_vio) + avail);
+            TSVIOReenable(state->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, "Inactivity Timeout");
+                TSVConnAbort(vconn, TS_VC_CLOSE_ABORT);
+            }
+            else
+            {
+                if (event == TS_EVENT_VCONN_READ_COMPLETE)
+                {
+                    TSDebug(PLUGIN_NAME, "Read Complete");
+                }
+                else if (event == TS_EVENT_VCONN_EOS)
+                {
+                    TSDebug(PLUGIN_NAME, "EOS");
+                }
+                TSVConnClose(state->vconn);
+            }
+
+            avail = TSIOBufferReaderAvail(state->resp_io_buf_reader);
+
+            if ((state->resp_info) && !state->resp_info->parsed)
+            {
+                parse_response(state);
+            }
+
+            // Consume data
+            avail = TSIOBufferReaderAvail(state->resp_io_buf_reader);
+            TSIOBufferReaderConsume(state->resp_io_buf_reader, avail);
+            TSVIONDoneSet(state->r_vio, TSVIONDoneGet(state->r_vio) + avail);
+            if (state->async_req)
+            {
+                TSDebug(PLUGIN_NAME, "Unlock URL");
+                TSMutexLock(state->plugin_config->troot_mutex);
+                tdelete(state->req_info->effective_url, &(state->plugin_config->troot), xstrcmp);
+                TSMutexUnlock(state->plugin_config->troot_mutex);
+            }
+            else
+            {
+                TSDebug(PLUGIN_NAME, "In sync path. setting fresh and re-enabling");
+                TSHttpTxnCacheLookupCountGet(state->txn, &lookup_count);
+                if ((state->resp_info->status == 500) || ((state->resp_info->status >= 502) && (state->resp_info->status <= 504)) || lookup_count > 2)
+                {
+                    TSDebug(PLUGIN_NAME, "Sending stale data as fresh");
+                    if (state->plugin_config->log_info.object && (state->plugin_config->log_info.all || state->plugin_config->log_info.stale_if_error))
+                    {
+                        CachedHeaderInfo *chi = get_cached_header_info(state->txn);
+                        TSTextLogObjectWrite(state->plugin_config->log_info.object, "stale-if-error: %d - %d < %d + %d %s", (int) state->txn_start, (int) chi->date, (int) chi->max_age, (int) chi->stale_on_error, state->req_info->effective_url);
+                        TSfree(chi);
+                    }
+                    TSHttpTxnHookAdd(state->txn, TS_HTTP_SEND_RESPONSE_HDR_HOOK, state->main_cont);
+                    TSHttpTxnCacheLookupStatusSet(state->txn, TS_CACHE_LOOKUP_HIT_FRESH);
+                }
+                else
+                {
+                    TSDebug(PLUGIN_NAME, "Attempting new cache lookup");
+                    TSHttpHdrUrlGet(state->req_info->buf, state->req_info->http_hdr_loc, &url_loc);
+                    TSHttpTxnNewCacheLookupDo(state->txn, state->req_info->buf, url_loc);
+                    TSHandleMLocRelease(state->req_info->buf, state->req_info->http_hdr_loc, url_loc);
+                    // TODO add txn translation hook and pass result along, maybe inside continuation?
+                    //TSHttpTxnHookAdd(state->txn, TS_HTTP_RESPONSE_TRANSFORM_HOOK, TSTransformCreate(replace_transform, state->txn));
+                }
+                TSHttpTxnReenable(state->txn, TS_EVENT_HTTP_CONTINUE);
+            }
+            free_request_info(state->req_info);
+            if (state->resp_info)
+            {
+                free_response_info(state->resp_info);
+            }
+            TSIOBufferReaderFree(state->req_io_buf_reader);
+            TSIOBufferDestroy(state->req_io_buf);
+            TSIOBufferReaderFree(state->resp_io_buf_reader);
+            TSIOBufferDestroy(state->resp_io_buf);
+            TSfree(state);
+            TSContDestroy(cont);
+            break;
+        default:
+            TSError("Unknown event %d.", event);
+            break;
+    }
+
+    return 0;
+}
+
+static int
+fetch_resource(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
+{
+    StateInfo *state;
+    TSCont consume_cont;
+    //struct sockaddr_in client_addr;
+    TSMLoc connection_hdr_loc, connection_hdr_dup_loc;
+
+    state = (StateInfo *) TSContDataGet(cont);
+
+    //li = (RequestInfo *) edata;
+    if (state->async_req)
+    {
+        TSMutexLock(state->plugin_config->troot_mutex);
+        // If already doing async lookup lets just close shop and go home
+        if (tfind(state->req_info->effective_url, &(state->plugin_config->troot), xstrcmp) != NULL)
+        {
+            TSDebug(PLUGIN_NAME, "Looks like an async is already in progress");
+            TSMutexUnlock(state->plugin_config->troot_mutex);
+            free_request_info(state->req_info);
+            TSfree(state);
+        }
+        // Otherwise lets do the lookup!
+        else
+        {
+            // Lock in tree
+            TSDebug(PLUGIN_NAME, "Locking URL");
+            tsearch(state->req_info->effective_url, &(state->plugin_config->troot), xstrcmp);
+            TSMutexUnlock(state->plugin_config->troot_mutex);
+        }
+    }
+
+    if (state)
+    {
+        TSDebug(PLUGIN_NAME, "Lets do the lookup");
+        consume_cont = TSContCreate(consume_resource, NULL);
+        TSContDataSet(consume_cont, (void *) state);
+
+        if (state->async_req)
+        {
+            state->resp_info = NULL;
+        }
+        else
+        {
+            state->resp_info = create_response_info();
+        }
+
+        TSDebug(PLUGIN_NAME, "Set Connection: close");
+        connection_hdr_loc = TSMimeHdrFieldFind(state->req_info->buf, state->req_info->http_hdr_loc, TS_MIME_FIELD_CONNECTION, TS_MIME_LEN_CONNECTION);
+
+        while(connection_hdr_loc != TS_NULL_MLOC)
+        {
+            TSDebug(PLUGIN_NAME, "Found old Connection hdr");
+
+            connection_hdr_dup_loc = TSMimeHdrFieldNextDup(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
+            TSMimeHdrFieldRemove(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
+            TSMimeHdrFieldDestroy(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
+            TSHandleMLocRelease(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
+            connection_hdr_loc = connection_hdr_dup_loc;
+        }
+
+        // This seems to have little effect
+        TSDebug(PLUGIN_NAME, "Creating Connection hdr");
+        TSMimeHdrFieldCreateNamed(state->req_info->buf, state->req_info->http_hdr_loc, TS_MIME_FIELD_CONNECTION, TS_MIME_LEN_CONNECTION, &connection_hdr_loc);
+        TSMimeHdrFieldValueStringInsert(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc, -1, TS_HTTP_VALUE_CLOSE, TS_HTTP_LEN_CLOSE);
+        TSMimeHdrFieldAppend(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
+        TSHandleMLocRelease(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
+
+        /*
+        TSDebug(PLUGIN_NAME, "Creating @stale_while_revalidate header");
+        TSMimeHdrFieldCreateNamed(state->req_info->buf, state->req_info->http_hdr_loc, TS_MIME_FIELD_CONNECTION, TS_MIME_LEN_CONNECTION, &connection_hdr_loc);
+        TSMimeHdrFieldValueStringInsert(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc, -1, TS_HTTP_VALUE_CLOSE, TS_HTTP_LEN_CLOSE);
+        TSMimeHdrFieldAppend(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
+        TSHandleMLocRelease(state->req_info->buf, state->req_info->http_hdr_loc, connection_hdr_loc);
+        */
+
+        TSDebug(PLUGIN_NAME, "Create Buffers");
+        state->req_io_buf = TSIOBufferCreate();
+        state->req_io_buf_reader = TSIOBufferReaderAlloc(state->req_io_buf);
+        state->resp_io_buf = TSIOBufferCreate();
+        state->resp_io_buf_reader = TSIOBufferReaderAlloc(state->resp_io_buf);
+
+        TSHttpHdrPrint(state->req_info->buf, state->req_info->http_hdr_loc, state->req_io_buf);
+        TSIOBufferWrite(state->req_io_buf, "\r\n", 2);
+
+        //memmove((void *) &client_addr, (void *) state->req_info->client_addr, sizeof(struct sockaddr));
+        //TSDebug(PLUGIN_NAME, "client_addr: %s:%d", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
+        state->vconn = TSHttpConnect((struct sockaddr const *) state->req_info->client_addr);
+
+        state->r_vio = TSVConnRead(state->vconn, consume_cont, state->resp_io_buf, INT64_MAX);
+        state->w_vio = TSVConnWrite(state->vconn, consume_cont, state->req_io_buf_reader, TSIOBufferReaderAvail(state->req_io_buf_reader));
+    }
+
+    TSContDestroy(cont);
+
+    return 0;
+}
+
+static int
+main_plugin(TSCont cont, TSEvent event, void *edata)
+{
+    TSHttpTxn txn = (TSHttpTxn) edata;
+    int status, lookup_count;
+    CachedHeaderInfo *chi;
+    TSCont fetch_cont;
+    StateInfo *state;
+    TSMBuffer buf;
+    TSMLoc loc,warn_loc;
+    TSHttpStatus http_status;
+    config_t *plugin_config;
+
+    switch (event)
+    {
+        // Is this the proper event?
+        case TS_EVENT_HTTP_READ_REQUEST_HDR:
+
+            if (TSHttpIsInternalRequest(txn) != TS_SUCCESS)
+            {
+                TSDebug(PLUGIN_NAME, "External Request");
+                plugin_config = (config_t *) TSContDataGet(cont);
+                state = TSmalloc(sizeof(StateInfo));
+                state->plugin_config = plugin_config;
+                time(&state->txn_start);
+                state->req_info = create_request_info(txn);
+                TSHttpTxnArgSet(txn, state->plugin_config->txn_slot, (void *) state);
+                TSHttpTxnHookAdd(txn, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, cont);
+            }
+            else
+            {
+                TSDebug(PLUGIN_NAME, "Internal Request"); // This is insufficient if there are other plugins using TSHttpConnect
+                TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_SHARE_SERVER_SESSIONS, 1);
+                //TSHttpTxnHookAdd(txn, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, cont);
+                TSHttpTxnHookAdd(txn, TS_HTTP_READ_RESPONSE_HDR_HOOK, cont);
+                // This might be needed in 3.2.0 to fix a timeout issue
+                //TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_TRANSACTION_NO_ACTIVITY_TIMEOUT_IN, 5);
+                //TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_TRANSACTION_NO_ACTIVITY_TIMEOUT_OUT, 5);
+            }
+
+            TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+            break;
+        case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
+            plugin_config = (config_t *) TSContDataGet(cont);
+            state = (StateInfo *) TSHttpTxnArgGet(txn, plugin_config->txn_slot);
+            TSHttpTxnCacheLookupCountGet(txn, &lookup_count);
+            if (TSHttpTxnCacheLookupStatusGet(txn, &status) == TS_SUCCESS)
+            {
+                // Are we stale?
+                if (status == TS_CACHE_LOOKUP_HIT_STALE)
+                {
+                    TSDebug(PLUGIN_NAME, "CacheLookupStatus is STALE");
+                    // Get headers
+                    chi = get_cached_header_info(txn);
+
+                    if (state->plugin_config->stale_if_error_override > chi->stale_on_error)
+                        chi->stale_on_error = state->plugin_config->stale_if_error_override;
+
+                    if ((state->txn_start - chi->date) < (chi->max_age + chi->stale_while_revalidate))
+                    {
+                        TSDebug(PLUGIN_NAME, "Looks like we can return fresh info and validate in the background");
+                        if (state->plugin_config->log_info.object && (state->plugin_config->log_info.all || state->plugin_config->log_info.stale_while_revalidate))
+                            TSTextLogObjectWrite(state->plugin_config->log_info.object, "stale-while-revalidate: %d - %d < %d + %d %s", (int) state->txn_start, (int) chi->date, (int) chi->max_age, (int) chi->stale_while_revalidate, state->req_info->effective_url);
+                        // lookup async
+
+#if (TS_VERSION_NUMBER >= 3003000)
+                        TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_INSERT_AGE_IN_RESPONSE, 1);
+#endif
+                        // Set warning header
+                        TSHttpTxnHookAdd(txn, TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
+
+                        TSDebug(PLUGIN_NAME, "set state as async");
+                        state->async_req = true;
+                        TSHttpTxnCacheLookupStatusSet(txn, TS_CACHE_LOOKUP_HIT_FRESH);
+                        //TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+                        fetch_cont = TSContCreate(fetch_resource, NULL);
+                        TSContDataSet(fetch_cont, (void *) state);
+                        TSContSchedule(fetch_cont, 0, TS_THREAD_POOL_TASK);
+                        TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+                    }
+                    else if ((state->txn_start - chi->date) < (chi->max_age + chi->stale_on_error))
+                    {
+                        TSDebug(PLUGIN_NAME, "Looks like we can return fresh data on 500 error");
+#if (TS_VERSION_NUMBER >= 3003000)
+                        TSHttpTxnConfigIntSet(txn, TS_CONFIG_HTTP_INSERT_AGE_IN_RESPONSE, 1);
+#endif
+                        //lookup sync
+                        state->async_req = false;
+                        state->txn = txn;
+                        state->main_cont = cont; // we need this for the warning header callback. not sure i like it, but it works.
+                        fetch_cont = TSContCreate(fetch_resource, NULL);
+                        TSContDataSet(fetch_cont, (void *) state);
+                        TSContSchedule(fetch_cont, 0, TS_THREAD_POOL_NET);
+                    }
+                    else
+                    {
+                        TSDebug(PLUGIN_NAME, "No love? now: %d date: %d max-age: %d swr: %d soe: %d", (int) state->txn_start, (int) chi->date, (int) chi->max_age, (int) chi->stale_while_revalidate, (int) chi->stale_on_error);
+                        if (lookup_count == 1)
+                        {
+                            free_request_info(state->req_info);
+                            TSfree(state);
+                        }
+                        TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+                    }
+
+                    TSfree(chi);
+                }
+                else
+                {
+                    TSDebug(PLUGIN_NAME, "Not Stale!");
+                    if (lookup_count == 1)
+                    {
+                        free_request_info(state->req_info);
+                        TSfree(state);
+                    }
+                    TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+                }
+            }
+            else
+            {
+                TSDebug(PLUGIN_NAME, "Could not get CacheLookupStatus");
+                if (lookup_count == 1)
+                {
+                    free_request_info(state->req_info);
+                    TSfree(state);
+                }
+                TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+            }
+            break;
+        case TS_EVENT_HTTP_READ_RESPONSE_HDR:
+            TSHttpTxnServerRespGet(txn, &buf, &loc);
+            http_status = TSHttpHdrStatusGet(buf, loc);
+            if ((http_status == 500) || ((http_status >= 502) && (http_status <= 504))) // 500, 502, 503, or 504
+            {
+                TSDebug(PLUGIN_NAME, "Set non-cachable");
+#if (TS_VERSION_NUMBER >= 3003000)
+                TSHttpTxnServerRespNoStoreSet(txn,1);
+#else
+                TSHttpTxnServerRespNoStore(txn);
+#endif
+            }
+            TSHandleMLocRelease(buf, TS_NULL_MLOC, loc);
+            TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+            break;
+        case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
+            TSDebug(PLUGIN_NAME, "set warning header");
+            TSHttpTxnClientRespGet(txn, &buf, &loc);
+            TSMimeHdrFieldCreateNamed(buf, loc, TS_MIME_FIELD_WARNING, TS_MIME_LEN_WARNING, &warn_loc);
+            TSMimeHdrFieldValueStringInsert(buf, loc, warn_loc, -1, HTTP_VALUE_STALE_WARNING, strlen(HTTP_VALUE_STALE_WARNING));
+            TSMimeHdrFieldAppend(buf, loc, warn_loc);
+            TSHandleMLocRelease(buf, loc, warn_loc);
+            TSHandleMLocRelease(buf, TS_NULL_MLOC, loc);
+            TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+            break;
+        default:
+            TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+            break;
+    }
+
+    return 0;
+}
+
+void
+TSPluginInit (int argc, const char *argv[])
+{
+    config_t *plugin_config;
+    TSPluginRegistrationInfo info;
+    TSCont main_cont;
+
+    info.plugin_name = PLUGIN_NAME;
+    info.vendor_name = "Apache Software Foundation";
+    info.support_email = "dev@trafficserver.apache.org";
+
+    if (TSPluginRegister(TS_SDK_VERSION_3_0 , &info) != TS_SUCCESS)
+    {
+        TSError("Plugin registration failed.\n");
+        return;
+    }
+    else
+    {
+        TSDebug(PLUGIN_NAME, "Plugin registration succeeded.\n");
+    }
+
+    plugin_config = TSmalloc(sizeof(config_t));
+
+    plugin_config->troot = NULL;
+    plugin_config->troot_mutex = TSMutexCreate();
+    plugin_config->stale_if_error_override = 0;
+    plugin_config->log_info.object = NULL;
+    plugin_config->log_info.all = false;
+    plugin_config->log_info.stale_if_error = false;
+    plugin_config->log_info.stale_while_revalidate = false;
+    plugin_config->log_info.filename = PLUGIN_NAME;
+
+    if (argc > 1)
+    {
+        int c;
+        optind = 1;
+        static const struct option longopts[] = {
+                { "log-all", no_argument, NULL, 'a' },
+                { "log-stale-while-revalidate", no_argument, NULL, 'r' },
+                { "log-stale-if-error", no_argument, NULL, 'e' },
+                { "log-filename", required_argument, NULL, 'f' },
+                { "force-stale-if-error", required_argument, NULL, 'E' },
+                { NULL, 0, NULL, 0 }
+            };
+
+        while ((c = getopt_long(argc, (char * const*) argv, "aref:E:", longopts, NULL)) != -1)
+        {
+            switch (c)
+            {
+                case 'a':
+                    plugin_config->log_info.all = true;
+                    break;
+                case 'r':
+                    plugin_config->log_info.stale_while_revalidate = true;
+                    break;
+                case 'e':
+                    plugin_config->log_info.stale_if_error = true;
+                    break;
+                case 'f':
+                    plugin_config->log_info.filename = strdup(optarg);
+                    break;
+                case 'E':
+                    plugin_config->stale_if_error_override = atoi(optarg);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        if (plugin_config->log_info.all || plugin_config->log_info.stale_while_revalidate || plugin_config->log_info.stale_if_error)
+            TSTextLogObjectCreate(plugin_config->log_info.filename, TS_LOG_MODE_ADD_TIMESTAMP, &(plugin_config->log_info.object));
+    }
+
+    // proxy.config.http.insert_age_in_response
+    TSHttpArgIndexReserve(PLUGIN_NAME, "txn state info", &(plugin_config->txn_slot));
+    main_cont = TSContCreate(main_plugin, NULL);
+    TSContDataSet(main_cont, (void *) plugin_config);
+    TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, main_cont);
+
+    TSDebug(PLUGIN_NAME, "Plugin Init Complete.\n");
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e49d8477/plugins/experimental/stale_while_revalidate/test_server.js
----------------------------------------------------------------------
diff --git a/plugins/experimental/stale_while_revalidate/test_server.js b/plugins/experimental/stale_while_revalidate/test_server.js
new file mode 100755
index 0000000..6aebc27
--- /dev/null
+++ b/plugins/experimental/stale_while_revalidate/test_server.js
@@ -0,0 +1,42 @@
+#! /usr/bin/env node
+
+/** @file
+
+  Implements Simple HTTP test server for the stale_while_revalidate plugin
+
+  @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.
+ */
+
+var http = require('http');
+var url  = require('url');
+
+http.createServer(function (request, response) {
+  console.log(Date());
+  setTimeout( function (req, res) {
+    console.log(req.headers);
+    console.log(url.parse(req.url));
+    res.writeHead(200, {'Content-Type': 'text/plain', 'Cache-Control': 'max-age=5, stale-while-revalidate=55'});
+    //res.writeHead(500, {'Content-Type': 'text/plain', 'Cache-Control': 'max-age=5, stale-if-error=555'});
+    res.end(Date() + '\n');
+    console.log(Date() + '\n');
+  }, 50, request, response);
+}).listen(8081, '127.0.0.1');
+
+console.log('Proxy running at http://127.0.0.1:8080/');
+console.log('Server running at http://127.0.0.1:8081/');