This is an automated email from the ASF dual-hosted git repository.

bnolsen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new c0da45fcda slice plugin: simplify self healing by switching to ident 
header (#12408)
c0da45fcda is described below

commit c0da45fcda4b1b27bad02bdaea28b98e1ec69cc5
Author: Brian Olsen <[email protected]>
AuthorDate: Wed Aug 20 12:54:04 2025 -0600

    slice plugin: simplify self healing by switching to ident header (#12408)
    
    * cache_range_requests: remove IMS header support and expand ident header
    * slice plugin initial code to remove IMS header and rely on Ident header 
to simplify self healing
    * get crr autest working again
    * crr_ident: add fresh to stale test
    * add Stale ident directive in case we're in reference slice refetch mode 
and there aren't any ident headers
    * update crr documentation
    * refactor crr plugin identifier/validator check logic and autest, update 
docs
---
 .../plugins/cache_range_requests.en.rst            |  59 ++--
 doc/admin-guide/plugins/slice.en.rst               |  54 +--
 .../cache_range_requests/cache_range_requests.cc   | 365 ++++++++++++---------
 plugins/slice/Config.cc                            |  12 +-
 plugins/slice/Config.h                             |   1 -
 plugins/slice/Data.h                               |  15 +-
 plugins/slice/server.cc                            | 104 ++----
 plugins/slice/util.cc                              |  49 +--
 ...range_requests_cache_complete_responses.test.py |   2 -
 .../cache_range_requests_ident.test.py             | 254 +++++++++++---
 .../cache_range_requests_ims.test.py               | 133 --------
 .../pluginTest/slice/gold/slice_crr_ident.gold     |   8 +-
 .../pluginTest/slice/gold/slice_ident.gold         |   8 +-
 .../pluginTest/slice/slice_ident.test.py           |   6 +-
 .../pluginTest/slice/slice_selfhealing.test.py     |   9 +-
 15 files changed, 536 insertions(+), 543 deletions(-)

diff --git a/doc/admin-guide/plugins/cache_range_requests.en.rst 
b/doc/admin-guide/plugins/cache_range_requests.en.rst
index e7fd928437..416c18856d 100644
--- a/doc/admin-guide/plugins/cache_range_requests.en.rst
+++ b/doc/admin-guide/plugins/cache_range_requests.en.rst
@@ -112,37 +112,6 @@ which includes information about the partial content 
range.  In this mode,
 all requests (include partial content) will use consistent hashing method
 for parent selection.
 
-X-Crr-Ims header support
-------------------------
-
-.. option:: --consider-ims
-.. option:: -c
-.. option:: --ims-header=[header name] (default: X-Crr-Ims)
-.. option:: -i
-
-To support slice plugin self healing an option to force revalidation
-after cache lookup complete was added.  This option is triggered by a
-special header:
-
-.. code::
-
-    X-Crr-Ims: Tue, 19 Nov 2019 13:26:45 GMT
-
-When this header is provided and a `cache hit fresh` is encountered the
-``Date`` header of the object in cache is compared to this header date
-value.  If the cache date is *less* than this IMS date then the object
-is marked as STALE and an appropriate If-Modified-Since or If-Match
-request along with this X-Crr-Ims header is passed up to the parent.
-
-In order for this to properly work in a CDN each cache in the
-chain *SHOULD* also contain a remap rule with the
-:program:`cache_range_requests` plugin with this option set.
-
-When used with the :program:`slice` plugin its `--crr-ims-header`
-option must have the same value (or not be defined) in order to work.
-
-Presence of the `--ims-header` automatically sets the `--consider-ims` option.
-
 X-Crr-Ident header support
 --------------------------
 
@@ -158,13 +127,29 @@ to this header.
 
 .. code::
 
-    X-Crr-Ident: Etag: "foo"
-    X-Crr-Ident: Last-Modified: Tue, 19 Nov 2019 13:26:45 GMT
+    X-Crr-Ident: Etag "foo"
+    X-Crr-Ident: Last-Modified Tue, 19 Nov 2019 13:26:45 GMT
+    X-Crr-Ident: Stale
+
+During the cache lookup hook the identifer is used in the following ways:
+
+If a range request cache lookup is considered STALE the identifier from
+this header will be compared to the stale cache identifier. If the values
+match the response will be changed to FRESH, preventing the transaction
+from contacting a parent.
+
+If a range request cache lookup is considered FRESH the identifier from
+this header will be compared to the stale cache identifier. If the values
+do not match the transaction will be changed to STALE, resulting in an
+IMS check being sent to the parent cache.
+
+A FRESH to STALE cache lookup state may be forced by the "Stale"
+identifier tag.  This is used by the slice plugin when an interior
+range request returns a 404 indicating that the asset has been removed
+at the origin.
 
-During the cache lookup hook if a range request is considered STALE
-the identifier from this header will be compared to the stale cache
-identifier. If the values match the response will be changed to FRESH,
-preventing the transaction from contacting a parent.
+Based on RFC7232 and ATS internals if present the ETag header is evaluated
+first, followed by the Last-Modified header.
 
 When used with the :program:`slice` plugin its `--crr-ident-header`
 option must have the same value (or not be defined) in order to work.
diff --git a/doc/admin-guide/plugins/slice.en.rst 
b/doc/admin-guide/plugins/slice.en.rst
index d0dce11b20..40d606129c 100644
--- a/doc/admin-guide/plugins/slice.en.rst
+++ b/doc/admin-guide/plugins/slice.en.rst
@@ -132,21 +132,13 @@ The slice plugin supports the following options::
         to indicate that the slice plugin should be skipped.
         -s for short
 
-    --crr-ims-header=<header name> (default: X-Crr-Ims)
-        Header name used by the slice plugin to tell the
-        `cache_range_requests` plugin that a request should
-        be marked as STALE.  Used for self healing.
-        This must match the `--ims-header` option used by the
-        `cache_range_requests` plugin.
-        -i for short
-
     --crr-ident-header=<header name> (default: X-Crr-Ident)
         Header name used by the slice plugin to tell the
         `cache_range_requests` plugin the identifier of the
         first/reference slice fetched.  First Etag is preferred
         followed by Last-Modified. The `cache_range_requests`
-        can use this identifier to flip a STALE asset back to
-        FRESH in order to limit unnecessary IMS requests.
+                               plugin uses this header to flip cache lookup 
status
+                               to STALE or FRESH depending on the header.
 
     --prefetch-count=<int> (optional)
         Default is 0
@@ -309,31 +301,39 @@ Self Healing
 ------------
 
 The slice plugin uses the very first slice as a reference slice which
-uses content-length and last-modified and/or etags to ensure assembled
+uses content-length and etag or last-modified headers to ensure assembled
 blocks come from the same asset.  In the case where a slice from a parent
 is fetched which indicates that the asset has changed, the slice plugin
 will attempt to self heal the asset.  The `cache_range_requests` plugin
-must be configured with the `--consider-ims` parameter in order for
+must be configured with the `--consider-ident` parameter in order for
 this to work.
 
 Example `remap.config` configuration::
 
   map http://slice/ http://parent/ @plugin=slice.so 
@pparam=--remap-host=cache_range_requests
-  map http://cache_range_requests/ http://parent/ 
@plugin=cache_range_requests.so @pparam=--consider-ims
-
-When a request is served, the slice plugin uses reference slice 0 to
-build a response to the client.  When subsequent slices are fetched they
-are checked against this reference slice.  If a mismatch occurs an IMS
-request for the offending slice is made through the `cache_range_requests`
-plugin using an X-Crr-Ims header.  If the refetched slice still mismatches
-then the client connection is aborted a crr IMS request is made for
-the reference slice in an attempt to refetch it.
-
-Optionally (but not recommended) the plugin may be configured to use
-the first slice in the request as the reference slice.  This option
-is faster since it does not visit any slices outside those needed to
-fulfill a request.  However this may still cause problems if the
-requested range was calculated from a newer version of the asset.
+  map http://cache_range_requests/ http://parent/ 
@plugin=cache_range_requests.so @pparam=--consider-ident
+
+When a request is served, the slice plugin uses the header from slice 0
+requested range build a response to the client. When subsequent slices
+are requested from the parent the X-Crr-Ident header is populated with
+the reference identifier (etag or last-modified) and the request is made
+through the `cache_range_requests` plugin.  The `cache_range_requests`
+plugin will then decide whether to send back the current cached slice
+(if the identifier matches) or attempt to refetch or "heal" that slice
+by marking it STALE.
+
+If the slice returned by the cache_range_requests plugin still
+doesn't match the reference slice then client side of the transaction
+is aborted.  The plugin will then attempt to "heal" the reference
+slice.  The X-Crr-Ident header is populated with the new identifer
+and the reference slice is re-requested with the intent of having the
+`cache_range_requests` plugin "heal" the reference slice.
+
+The plugin may be configured to use the first slice of the request
+as the reference slice instead of the asset slice 0.  This option is
+faster as it does not visit any slices outside those needed to fulfill
+a request. This option may cause serious out of sync issues as range
+requests may end up being served from temporally different assets.
 
 Purge Requests
 --------------
diff --git a/plugins/cache_range_requests/cache_range_requests.cc 
b/plugins/cache_range_requests/cache_range_requests.cc
index c2c149ee1b..750c735b8e 100644
--- a/plugins/cache_range_requests/cache_range_requests.cc
+++ b/plugins/cache_range_requests/cache_range_requests.cc
@@ -41,6 +41,25 @@
 #define DEBUG_LOG(fmt, ...) Dbg(dbg_ctl, fmt, ##__VA_ARGS__)
 #define ERROR_LOG(fmt, ...) TSError("[%s:%d] %s(): " fmt, __FILE__, __LINE__, 
__func__, ##__VA_ARGS__)
 
+// The following allows for scope based defer.
+#ifndef defer
+struct defer_dummy {
+};
+template <class F> struct deferrer {
+  F f;
+  ~deferrer() { f(); }
+};
+template <class F>
+deferrer<F>
+operator*(defer_dummy, F f)
+{
+  return {f};
+}
+#define DEFER_(LINE) zz_defer##LINE
+#define DEFER(LINE)  DEFER_(LINE)
+#define defer        auto DEFER(__LINE__) = defer_dummy{} *[&]()
+#endif // defer
+
 namespace
 {
 DbgCtl dbg_ctl{PLUGIN_NAME};
@@ -50,7 +69,6 @@ enum parent_select_mode_t {
   PS_CACHEKEY_URL, // Set parent selection url to cache_key url
 };
 
-constexpr std::string_view DefaultImsHeader   = {"X-Crr-Ims"};
 constexpr std::string_view DefaultIdentHeader = {"X-Crr-Ident"};
 constexpr std::string_view SLICE_CRR_HEADER   = {"Slice-Crr-Status"};
 constexpr std::string_view SLICE_CRR_VAL      = "1";
@@ -58,15 +76,14 @@ constexpr std::string_view SKIP_CRR_HDR_NAME  = 
{"X-Skip-Crr"};
 
 std::string_view const Etag(TS_MIME_FIELD_ETAG, TS_MIME_LEN_ETAG);
 std::string_view const LastModified(TS_MIME_FIELD_LAST_MODIFIED, 
TS_MIME_LEN_LAST_MODIFIED);
+std::string_view const Stale{"Stale"};
 
 struct pluginconfig {
   parent_select_mode_t ps_mode{PS_DEFAULT};
-  bool                 consider_ims_header{false};
   bool                 consider_ident_header{false};
   bool                 modify_cache_key{true};
   bool                 verify_cacheability{false};
   bool                 cache_complete_responses{false};
-  std::string          ims_header;
   std::string          ident_header;
 };
 
@@ -74,7 +91,6 @@ struct txndata {
   pluginconfig const *config{nullptr};
   std::string         range_value;
   TSHttpStatus        origin_status{TS_HTTP_STATUS_NONE};
-  time_t              ims_time{0};
   bool                ident_check{false};
   bool                verify_cacheability{false};
   bool                cache_complete_responses{false};
@@ -118,9 +134,7 @@ create_pluginconfig(int argc, char *const argv[])
   }
 
   static const struct option longopts[] = {
-    {const_cast<char *>("consider-ims"),             no_argument,       
nullptr, 'c'},
     {const_cast<char *>("consider-ident"),           no_argument,       
nullptr, 'd'},
-    {const_cast<char *>("ims-header"),               required_argument, 
nullptr, 'i'},
     {const_cast<char *>("ident-header"),             required_argument, 
nullptr, 'j'},
     {const_cast<char *>("no-modify-cachekey"),       no_argument,       
nullptr, 'n'},
     {const_cast<char *>("ps-cachekey"),              no_argument,       
nullptr, 'p'},
@@ -134,25 +148,16 @@ create_pluginconfig(int argc, char *const argv[])
   --argv;
 
   for (;;) {
-    int const opt = getopt_long(argc, argv, "i:", longopts, nullptr);
+    int const opt = getopt_long(argc, argv, "j:", longopts, nullptr);
     if (-1 == opt) {
       break;
     }
 
     switch (opt) {
-    case 'c': {
-      DEBUG_LOG("Plugin considers the ims header");
-      pc->consider_ims_header = true;
-    } break;
     case 'd': {
       DEBUG_LOG("Plugin considers the ident header");
       pc->consider_ident_header = true;
     } break;
-    case 'i': {
-      DEBUG_LOG("Plugin uses custom ims header: %s", optarg);
-      pc->ims_header.assign(optarg);
-      pc->consider_ims_header = true;
-    } break;
     case 'j': {
       DEBUG_LOG("Plugin uses custom ident header: %s", optarg);
       pc->ident_header.assign(optarg);
@@ -185,11 +190,6 @@ create_pluginconfig(int argc, char *const argv[])
     pc->ps_mode = PS_CACHEKEY_URL;
   }
 
-  if (pc->consider_ims_header && pc->ims_header.empty()) {
-    pc->ims_header = DefaultImsHeader;
-    DEBUG_LOG("Plugin uses default ims header: %s", pc->ims_header.c_str());
-  }
-
   if (pc->consider_ident_header && pc->ident_header.empty()) {
     pc->ident_header = DefaultIdentHeader;
     DEBUG_LOG("Plugin uses default ident header: %s", 
pc->ident_header.c_str());
@@ -261,8 +261,16 @@ range_header_check(TSHttpTxn txnp, pluginconfig *const pc)
   TSMLoc    hdr_loc = TS_NULL_MLOC;
 
   if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &hdr_buf, &hdr_loc)) {
+    defer
+    {
+      TSHandleMLocRelease(hdr_buf, TS_NULL_MLOC, hdr_loc);
+    };
     TSMLoc const range_loc = TSMimeHdrFieldFind(hdr_buf, hdr_loc, 
TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE);
     if (TS_NULL_MLOC != range_loc) {
+      defer
+      {
+        TSHandleMLocRelease(hdr_buf, hdr_loc, range_loc);
+      };
       int               len       = 0;
       char const *const hdr_value = TSMimeHdrFieldValueStringGet(hdr_buf, 
hdr_loc, range_loc, 0, &len);
 
@@ -308,7 +316,8 @@ range_header_check(TSHttpTxn txnp, pluginconfig *const pc)
             const char *start  = cache_key_url;
             const char *end    = cache_key_url + cache_key_url_len;
             if (TS_SUCCESS == TSUrlCreate(hdr_buf, &ps_loc)) {
-              if (TS_PARSE_DONE == TSUrlParse(hdr_buf, ps_loc, &start, end) && 
// This should always succeed.
+              // This should always succeed.
+              if (TS_PARSE_DONE == TSUrlParse(hdr_buf, ps_loc, &start, end) &&
                   TS_SUCCESS == TSHttpTxnParentSelectionUrlSet(txnp, hdr_buf, 
ps_loc)) {
                 DEBUG_LOG("Setting Parent Selection URL to '%.*s'", 
cache_key_url_len, cache_key_url);
               }
@@ -316,28 +325,13 @@ range_header_check(TSHttpTxn txnp, pluginconfig *const pc)
             }
           }
 
-          // optionally consider an ims header
-          bool ims_active = false;
-          if (pc->consider_ims_header) {
-            TSMLoc const imsloc = TSMimeHdrFieldFind(hdr_buf, hdr_loc, 
pc->ims_header.data(), pc->ims_header.size());
-            if (TS_NULL_MLOC != imsloc) {
-              time_t const itime = TSMimeHdrFieldValueDateGet(hdr_buf, 
hdr_loc, imsloc);
-              DEBUG_LOG("Servicing the '%s' header", pc->ims_header.c_str());
-              TSHandleMLocRelease(hdr_buf, hdr_loc, imsloc);
-              if (0 < itime) {
-                txn_state->ims_time = itime;
-                ims_active          = true;
-              }
-            }
-          }
-
           // If not revalidating then consider the identity header
-          if (!ims_active && pc->consider_ident_header) {
+          if (pc->consider_ident_header) {
             TSMLoc const identloc = TSMimeHdrFieldFind(hdr_buf, hdr_loc, 
pc->ident_header.data(), pc->ident_header.size());
             if (TS_NULL_MLOC != identloc) {
               DEBUG_LOG("Servicing the '%s' header", pc->ident_header.c_str());
-              TSHandleMLocRelease(hdr_buf, hdr_loc, identloc);
               txn_state->ident_check = true;
+              TSHandleMLocRelease(hdr_buf, hdr_loc, identloc);
             };
           }
 
@@ -358,7 +352,7 @@ range_header_check(TSHttpTxn txnp, pluginconfig *const pc)
         TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp);
         DEBUG_LOG("Added TS_HTTP_SEND_REQUEST_HDR_HOOK, 
TS_HTTP_SEND_RESPONSE_HDR_HOOK, and TS_HTTP_TXN_CLOSE_HOOK");
 
-        if (0 < txn_state->ims_time || txn_state->ident_check) {
+        if (txn_state->ident_check) {
           TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, 
txn_contp);
           DEBUG_LOG("Also Added TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK");
         }
@@ -370,11 +364,9 @@ range_header_check(TSHttpTxn txnp, pluginconfig *const pc)
           txn_state->slice_request = true;
         }
       }
-      TSHandleMLocRelease(hdr_buf, hdr_loc, range_loc);
     } else {
       DEBUG_LOG("No range request header.");
     }
-    TSHandleMLocRelease(hdr_buf, TS_NULL_MLOC, hdr_loc);
   }
 }
 
@@ -394,13 +386,16 @@ handle_send_origin_request(TSCont contp, TSHttpTxn txnp, 
txndata *const txn_stat
     return;
   }
 
-  if (TS_SUCCESS == TSHttpTxnServerReqGet(txnp, &hdr_buf, &hdr_loc) && 
!rv.empty()) {
-    if (set_header(hdr_buf, hdr_loc, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE, 
rv.data(), rv.size())) {
+  if (TS_SUCCESS == TSHttpTxnServerReqGet(txnp, &hdr_buf, &hdr_loc)) {
+    defer
+    {
+      TSHandleMLocRelease(hdr_buf, TS_NULL_MLOC, hdr_loc);
+    };
+    if (!rv.empty() && set_header(hdr_buf, hdr_loc, TS_MIME_FIELD_RANGE, 
TS_MIME_LEN_RANGE, rv.data(), rv.size())) {
       DEBUG_LOG("Added range header: %s", rv.c_str());
       TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
     }
   }
-  TSHandleMLocRelease(hdr_buf, TS_NULL_MLOC, hdr_loc);
 }
 
 /**
@@ -416,15 +411,21 @@ handle_client_send_response(TSHttpTxn txnp, txndata 
*const txn_state)
   TSMBuffer resp_buf = nullptr;
   TSMLoc    resp_loc = TS_NULL_MLOC;
   if (TS_SUCCESS == TSHttpTxnClientRespGet(txnp, &resp_buf, &resp_loc)) {
+    defer
+    {
+      TSHandleMLocRelease(resp_buf, TS_NULL_MLOC, resp_loc);
+    };
+
     TSHttpStatus const status = TSHttpHdrStatusGet(resp_buf, resp_loc);
     // a cached status will be 200 with expected parent response status of 206
     if (TS_HTTP_STATUS_OK == status) {
       if (txn_state->origin_status == TS_HTTP_STATUS_NONE ||
           txn_state->origin_status == TS_HTTP_STATUS_NOT_MODIFIED) { // cache 
hit or revalidation
         // status is always TS_HTTP_STATUS_NONE on cache hit; its value is 
only set during handle_server_read_response()
-        TSMLoc content_range_loc = TSMimeHdrFieldFind(resp_buf, resp_loc, 
TS_MIME_FIELD_CONTENT_RANGE, TS_MIME_LEN_CONTENT_RANGE);
+        TSMLoc const content_range_loc =
+          TSMimeHdrFieldFind(resp_buf, resp_loc, TS_MIME_FIELD_CONTENT_RANGE, 
TS_MIME_LEN_CONTENT_RANGE);
 
-        if (content_range_loc) {
+        if (TS_NULL_MLOC != content_range_loc) {
           DEBUG_LOG("Got TS_HTTP_STATUS_OK on cache hit or revalidation and 
Content-Range header present in response");
           partial_content_reason = true;
           TSHandleMLocRelease(resp_buf, resp_loc, content_range_loc);
@@ -452,7 +453,6 @@ handle_client_send_response(TSHttpTxn txnp, txndata *const 
txn_state)
     } else {
       DEBUG_LOG("Ignoring status code %d; txn_state->origin_status=%d", 
status, txn_state->origin_status);
     }
-    TSHandleMLocRelease(resp_buf, TS_NULL_MLOC, resp_loc);
   }
 
   if (partial_content_reason) {
@@ -485,44 +485,49 @@ handle_server_read_response(TSHttpTxn txnp, txndata 
*const txn_state)
   TSMLoc    resp_loc = TS_NULL_MLOC;
   int       cache_lookup;
 
-  if (TS_SUCCESS == TSHttpTxnServerRespGet(txnp, &resp_buf, &resp_loc)) {
-    TSHttpStatus const status = TSHttpHdrStatusGet(resp_buf, resp_loc);
-    txn_state->origin_status  = status;
-    if (TS_HTTP_STATUS_PARTIAL_CONTENT == status) {
-      DEBUG_LOG("Got TS_HTTP_STATUS_PARTIAL_CONTENT.");
-      // changing the status code from 206 to 200 forces the object into cache
-      TSHttpHdrStatusSet(resp_buf, resp_loc, TS_HTTP_STATUS_OK);
-      DEBUG_LOG("Set response header to TS_HTTP_STATUS_OK.");
-
-      if (txn_state->verify_cacheability && !TSHttpTxnIsCacheable(txnp, 
nullptr, resp_buf)) {
-        DEBUG_LOG("transaction is not cacheable; resetting status code to 
206");
-        TSHttpHdrStatusSet(resp_buf, resp_loc, TS_HTTP_STATUS_PARTIAL_CONTENT);
-      }
-    } else if (TS_HTTP_STATUS_OK == status) {
-      bool cacheable = txn_state->cache_complete_responses;
+  if (TS_SUCCESS != TSHttpTxnServerRespGet(txnp, &resp_buf, &resp_loc)) {
+    return;
+  }
 
-      if (cacheable && txn_state->verify_cacheability) {
-        DEBUG_LOG("Received a cacheable complete response from the origin; 
verifying cacheability");
-        cacheable = TSHttpTxnIsCacheable(txnp, nullptr, resp_buf);
-      }
+  defer
+  {
+    TSHandleMLocRelease(resp_buf, TS_NULL_MLOC, resp_loc);
+  };
 
-      // 200s are cached by default; only cache if configured to do so
-      if (!cacheable && TS_SUCCESS == TSHttpTxnCntlSet(txnp, 
TS_HTTP_CNTL_SERVER_NO_STORE, true)) {
-        DEBUG_LOG("Cache write has been disabled for this transaction.");
-      } else {
-        DEBUG_LOG("Allowing object to be cached.");
-      }
+  TSHttpStatus const status = TSHttpHdrStatusGet(resp_buf, resp_loc);
+  txn_state->origin_status  = status;
+  if (TS_HTTP_STATUS_PARTIAL_CONTENT == status) {
+    DEBUG_LOG("Got TS_HTTP_STATUS_PARTIAL_CONTENT.");
+    // changing the status code from 206 to 200 forces the object into cache
+    TSHttpHdrStatusSet(resp_buf, resp_loc, TS_HTTP_STATUS_OK);
+    DEBUG_LOG("Set response header to TS_HTTP_STATUS_OK.");
+
+    if (txn_state->verify_cacheability && !TSHttpTxnIsCacheable(txnp, nullptr, 
resp_buf)) {
+      DEBUG_LOG("transaction is not cacheable; resetting status code to 206");
+      TSHttpHdrStatusSet(resp_buf, resp_loc, TS_HTTP_STATUS_PARTIAL_CONTENT);
     }
+  } else if (TS_HTTP_STATUS_OK == status) {
+    bool cacheable = txn_state->cache_complete_responses;
 
-    // slice requesting cache lookup status and cacheability (only on miss or 
validation)
-    if ((txn_state->origin_status == TS_HTTP_STATUS_PARTIAL_CONTENT || 
txn_state->origin_status == TS_HTTP_STATUS_NOT_MODIFIED) &&
-        txn_state->slice_request && TSHttpTxnIsCacheable(txnp, nullptr, 
resp_buf) &&
-        TSHttpTxnCacheLookupStatusGet(txnp, &cache_lookup) == TS_SUCCESS &&
-        (cache_lookup == TS_CACHE_LOOKUP_MISS || cache_lookup == 
TS_CACHE_LOOKUP_HIT_STALE)) {
-      txn_state->slice_response = true;
+    if (cacheable && txn_state->verify_cacheability) {
+      DEBUG_LOG("Received a cacheable complete response from the origin; 
verifying cacheability");
+      cacheable = TSHttpTxnIsCacheable(txnp, nullptr, resp_buf);
     }
 
-    TSHandleMLocRelease(resp_buf, TS_NULL_MLOC, resp_loc);
+    // 200s are cached by default; only cache if configured to do so
+    if (!cacheable && TS_SUCCESS == TSHttpTxnCntlSet(txnp, 
TS_HTTP_CNTL_SERVER_NO_STORE, true)) {
+      DEBUG_LOG("Cache write has been disabled for this transaction.");
+    } else {
+      DEBUG_LOG("Allowing object to be cached.");
+    }
+  }
+
+  // slice requesting cache lookup status and cacheability (only on miss or 
validation)
+  if ((txn_state->origin_status == TS_HTTP_STATUS_PARTIAL_CONTENT || 
txn_state->origin_status == TS_HTTP_STATUS_NOT_MODIFIED) &&
+      txn_state->slice_request && TSHttpTxnIsCacheable(txnp, nullptr, 
resp_buf) &&
+      TSHttpTxnCacheLookupStatusGet(txnp, &cache_lookup) == TS_SUCCESS &&
+      (cache_lookup == TS_CACHE_LOOKUP_MISS || cache_lookup == 
TS_CACHE_LOOKUP_HIT_STALE)) {
+    txn_state->slice_response = true;
   }
 }
 
@@ -540,7 +545,6 @@ remove_header(TSMBuffer buf, TSMLoc hdr_loc, const char 
*header, int len)
 
   while (TS_NULL_MLOC != field) {
     TSMLoc const tmp = TSMimeHdrFieldNextDup(buf, hdr_loc, field);
-
     ++cnt;
     TSMimeHdrFieldDestroy(buf, hdr_loc, field);
     TSHandleMLocRelease(buf, hdr_loc, field);
@@ -598,104 +602,143 @@ set_header(TSMBuffer buf, TSMLoc hdr_loc, const char 
*header, int len, const cha
   return ret;
 }
 
-time_t
-get_date_from_cached_hdr(TSHttpTxn txn)
+void
+debugTxnUrl(TSHttpTxn const txnp, std::string_view const desc, std::string 
const &rangeval)
 {
-  TSMBuffer buf     = nullptr;
-  TSMLoc    hdr_loc = TS_NULL_MLOC;
-  time_t    date    = 0;
-
-  if (TSHttpTxnCachedRespGet(txn, &buf, &hdr_loc) == TS_SUCCESS) {
-    TSMLoc const date_loc = TSMimeHdrFieldFind(buf, hdr_loc, 
TS_MIME_FIELD_DATE, TS_MIME_LEN_DATE);
-    if (TS_NULL_MLOC != date_loc) {
-      date = TSMimeHdrFieldValueDateGet(buf, hdr_loc, date_loc);
-      TSHandleMLocRelease(buf, hdr_loc, date_loc);
-    }
-    TSHandleMLocRelease(buf, TS_NULL_MLOC, hdr_loc);
+  int         url_len = 0;
+  char *const req_url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_len);
+  if (nullptr != req_url) {
+    DEBUG_LOG("%.*s: %.*s-%s", (int)desc.length(), desc.data(), url_len, 
req_url, rangeval.c_str());
+    TSfree(req_url);
   }
-
-  return date;
 }
 
 /**
- * Handle a special IMS request or identity check on stale asset
+ * Handle the identity/validator check.
+ * Based on RFC7232 and 
HttpTransactCache::match_response_to_request_conditionals
+ * Compare ETAG if provided.
+ * else Compare Last-Modified if provided.
  */
 void
 handle_cache_lookup_complete(TSHttpTxn txnp, txndata *const txn_state)
 {
+  TSAssert(txn_state->ident_check);
   int cachestat;
   if (TS_SUCCESS == TSHttpTxnCacheLookupStatusGet(txnp, &cachestat)) {
-    if (TS_CACHE_LOOKUP_HIT_FRESH == cachestat) {
-      time_t const ch_time = get_date_from_cached_hdr(txnp);
-      DEBUG_LOG("IMS Cached header time %jd vs IMS %jd", 
static_cast<intmax_t>(ch_time),
-                static_cast<intmax_t>(txn_state->ims_time));
-      if (ch_time < txn_state->ims_time) {
+    if (TS_CACHE_LOOKUP_HIT_FRESH != cachestat && TS_CACHE_LOOKUP_HIT_STALE != 
cachestat) {
+      return;
+    }
+
+    // ensure the cached response is a 200
+    TSMBuffer cbuf  = nullptr;
+    TSMLoc    chloc = TS_NULL_MLOC;
+    if (TSHttpTxnCachedRespGet(txnp, &cbuf, &chloc) != TS_SUCCESS) {
+      return;
+    }
+
+    defer
+    {
+      TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chloc);
+    };
+
+    if (TS_HTTP_STATUS_OK != TSHttpHdrStatusGet(cbuf, chloc)) {
+      return;
+    }
+
+    pluginconfig const *const pc = txn_state->config;
+
+    // request identifier/validator
+    TSMBuffer rbuf  = nullptr;
+    TSMLoc    rhloc = TS_NULL_MLOC;
+    if (TS_SUCCESS != TSHttpTxnClientReqGet(txnp, &rbuf, &rhloc)) {
+      return;
+    }
+
+    defer
+    {
+      TSHandleMLocRelease(rbuf, TS_NULL_MLOC, rhloc);
+    };
+
+    // No directive found (should not happen)
+    TSMLoc const riloc = TSMimeHdrFieldFind(rbuf, rhloc, 
pc->ident_header.data(), pc->ident_header.size());
+    if (TS_NULL_MLOC == riloc) {
+      return;
+    }
+    defer
+    {
+      TSHandleMLocRelease(rbuf, rhloc, riloc);
+    };
+
+    int               rilen = 0;
+    char const *const ristr = TSMimeHdrFieldValueStringGet(rbuf, rhloc, riloc, 
-1, &rilen);
+    if (0 == rilen) {
+      return;
+    }
+
+    // pull the identifier from the request header
+    std::string_view rident(ristr, rilen);
+    std::string_view rtag;
+    if (rident.substr(0, Etag.length()) == Etag) {
+      DEBUG_LOG("Etag identifier provided in '%.*s'", rilen, ristr);
+      rtag   = Etag;
+      rident = rident.substr(Etag.length() + 1);
+    } else if (rident.substr(0, LastModified.length()) == LastModified) {
+      DEBUG_LOG("Last-Modified indentifier provided in '%.*s'", rilen, ristr);
+      rtag   = LastModified;
+      rident = rident.substr(LastModified.length() + 1);
+    } else if (rident.substr(0, Stale.length()) == Stale) {
+      // note tag isn't set here
+      if (TS_CACHE_LOOKUP_HIT_FRESH == cachestat) {
         TSHttpTxnCacheLookupStatusSet(txnp, TS_CACHE_LOOKUP_HIT_STALE);
         if (dbg_ctl.on()) {
-          int         url_len = 0;
-          char *const req_url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_len);
-          if (nullptr != req_url) {
-            std::string const &rv = txn_state->range_value;
-            DEBUG_LOG("Forced revalidate %.*s-%s", url_len, req_url, 
rv.c_str());
-
-            TSfree(req_url);
-          }
+          debugTxnUrl(txnp, "force to stale", txn_state->range_value);
         }
       }
-    } else if (TS_CACHE_LOOKUP_HIT_STALE == cachestat && 
txn_state->ident_check) {
-      pluginconfig const *const pc = txn_state->config;
-      DEBUG_LOG("Stale asset ident check");
-
-      TSMBuffer resp_buf = nullptr;
-      TSMLoc    resp_loc = TS_NULL_MLOC;
-
-      if (TS_SUCCESS == TSHttpTxnCachedRespGet(txnp, &resp_buf, &resp_loc)) {
-        if (TS_HTTP_STATUS_OK == TSHttpHdrStatusGet(resp_buf, resp_loc)) {
-          // get the request identifier
-          TSMBuffer req_buf = nullptr;
-          TSMLoc    req_loc = TS_NULL_MLOC;
-          if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &req_buf, &req_loc)) {
-            TSMLoc const ident_loc = TSMimeHdrFieldFind(req_buf, req_loc, 
pc->ident_header.data(), pc->ident_header.size());
-            if (TS_NULL_MLOC != ident_loc) {
-              DEBUG_LOG("Checking identifier against the '%s' header", 
pc->ident_header.c_str());
-
-              int               len = 0;
-              char const *const str = TSMimeHdrFieldValueStringGet(req_buf, 
req_loc, ident_loc, -1, &len);
-
-              // determine which identifier has been provided
-              std::string_view const svreq(str, len);
-              std::string_view       tag;
-              if (svreq.substr(0, Etag.length()) == Etag) {
-                DEBUG_LOG("Etag identifier provided in '%.*s'", len, str);
-                tag = Etag;
-              } else if (svreq.substr(0, LastModified.length()) == 
LastModified) {
-                DEBUG_LOG("Last-Modified indentifier provided in '%.*s'", len, 
str);
-                tag = LastModified;
-              }
+      return;
+    } else {
+      DEBUG_LOG("Unknown indentifier provided in '%.*s'", rilen, ristr);
+      return;
+    }
 
-              if (!tag.empty()) {
-                TSMLoc const id_loc = TSMimeHdrFieldFind(resp_buf, resp_loc, 
tag.data(), tag.size());
-                if (TS_NULL_MLOC != id_loc) {
-                  int                    len = 0;
-                  char const *const      str = 
TSMimeHdrFieldValueStringGet(resp_buf, resp_loc, id_loc, 0, &len);
-                  std::string_view const sv(str, len);
-
-                  DEBUG_LOG("Checking cached '%.*s' against request '%.*s'", 
len, str, (int)svreq.size(), svreq.data());
-
-                  if (std::string_view::npos != svreq.rfind(sv)) {
-                    DEBUG_LOG("Flipping cache lookup status from STALE to 
FRESH");
-                    TSHttpTxnCacheLookupStatusSet(txnp, 
TS_CACHE_LOOKUP_HIT_FRESH);
-                  }
-                  TSHandleMLocRelease(resp_buf, resp_loc, id_loc);
-                }
-              }
+    // pull the identifier from the cached response
+    // first priority is etag
+    std::string_view ctag  = Etag;
+    TSMLoc           ciloc = TSMimeHdrFieldFind(cbuf, chloc, ctag.data(), 
ctag.length());
+    if (TS_NULL_MLOC == ciloc) {
+      ctag  = LastModified;
+      ciloc = TSMimeHdrFieldFind(cbuf, chloc, ctag.data(), ctag.length());
+      if (TS_NULL_MLOC == ciloc) {
+        return;
+      }
+    }
 
-              TSHandleMLocRelease(req_buf, req_loc, ident_loc);
-            }
-            TSHandleMLocRelease(req_buf, TS_NULL_MLOC, req_loc);
-          }
+    defer
+    {
+      TSHandleMLocRelease(cbuf, chloc, ciloc);
+    };
+
+    int               cilen = 0;
+    char const *const cistr = TSMimeHdrFieldValueStringGet(cbuf, chloc, ciloc, 
-1, &cilen);
+    if (0 == cilen) {
+      return;
+    }
+    std::string_view cident(cistr, cilen);
+
+    // fresh to stale with ident mismatch
+    if (TS_CACHE_LOOKUP_HIT_FRESH == cachestat) {
+      if (rtag != ctag || rident != cident) {
+        TSHttpTxnCacheLookupStatusSet(txnp, TS_CACHE_LOOKUP_HIT_STALE);
+        if (dbg_ctl.on()) {
+          debugTxnUrl(txnp, "flip to stale", txn_state->range_value);
+        }
+      }
+      // stale to fresh with ident match
+    } else if (TS_CACHE_LOOKUP_HIT_STALE == cachestat) {
+      if (rtag == ctag && rident == cident) {
+        TSHttpTxnCacheLookupStatusSet(txnp, TS_CACHE_LOOKUP_HIT_FRESH);
+        if (dbg_ctl.on()) {
+          debugTxnUrl(txnp, "flip to fresh", txn_state->range_value);
         }
-        TSHandleMLocRelease(resp_buf, TS_NULL_MLOC, resp_loc);
       }
     }
   }
diff --git a/plugins/slice/Config.cc b/plugins/slice/Config.cc
index 98da5bd410..aa007cccb8 100644
--- a/plugins/slice/Config.cc
+++ b/plugins/slice/Config.cc
@@ -28,7 +28,6 @@
 namespace
 {
 constexpr std::string_view DefaultSliceSkipHeader = {"X-Slicer-Info"};
-constexpr std::string_view DefaultCrrImsHeader    = {"X-Crr-Ims"};
 constexpr std::string_view DefaultCrrIdentHeader  = {"X-Crr-Ident"};
 } // namespace
 
@@ -114,7 +113,6 @@ Config::fromArgs(int const argc, char const *const argv[])
   // standard parsing
   constexpr struct option longopts[] = {
     {const_cast<char *>("blockbytes"),           required_argument, nullptr, 
'b'},
-    {const_cast<char *>("crr-ims-header"),       required_argument, nullptr, 
'c'},
     {const_cast<char *>("disable-errorlog"),     no_argument,       nullptr, 
'd'},
     {const_cast<char *>("exclude-regex"),        required_argument, nullptr, 
'e'},
     {const_cast<char *>("crr-ident-header"),     required_argument, nullptr, 
'g'},
@@ -135,7 +133,7 @@ Config::fromArgs(int const argc, char const *const argv[])
   // getopt assumes args start at '1' so this hack is needed
   char *const *argvp = (const_cast<char *const *>(argv) - 1);
   for (;;) {
-    int const opt = getopt_long(argc + 1, argvp, "b:dc:e:g:i:lm:p:r:s:t:x:z:", 
longopts, nullptr);
+    int const opt = getopt_long(argc + 1, argvp, "b:de:g:i:lm:p:r:s:t:x:z:", 
longopts, nullptr);
     if (-1 == opt) {
       break;
     }
@@ -152,10 +150,6 @@ Config::fromArgs(int const argc, char const *const argv[])
         ERROR_LOG("Invalid blockbytes: %s", optarg);
       }
     } break;
-    case 'c': {
-      m_crr_ims_header.assign(optarg);
-      DEBUG_LOG("Using override crr ims header %s", optarg);
-    } break;
     case 'd': {
       m_paceerrsecs = -1;
     } break;
@@ -280,10 +274,6 @@ Config::fromArgs(int const argc, char const *const argv[])
   } else {
     DEBUG_LOG("Block stitching error logs at most every %d sec(s)", 
m_paceerrsecs);
   }
-  if (m_crr_ims_header.empty()) {
-    m_crr_ims_header = DefaultCrrImsHeader;
-    DEBUG_LOG("Using default crr ims header %s", m_crr_ims_header.c_str());
-  }
   if (m_crr_ident_header.empty()) {
     m_crr_ident_header = DefaultCrrIdentHeader;
     DEBUG_LOG("Using default crr ident header %s", m_crr_ident_header.c_str());
diff --git a/plugins/slice/Config.h b/plugins/slice/Config.h
index 824ffddd40..27a99ccb74 100644
--- a/plugins/slice/Config.h
+++ b/plugins/slice/Config.h
@@ -51,7 +51,6 @@ struct Config {
   uint64_t m_min_size_to_slice{0};    // Only strip objects larger than this
 
   std::string m_skip_header;
-  std::string m_crr_ims_header;
   std::string m_crr_ident_header;
 
   // Convert optarg to bytes
diff --git a/plugins/slice/Data.h b/plugins/slice/Data.h
index 754a2c331e..85f335d4c1 100644
--- a/plugins/slice/Data.h
+++ b/plugins/slice/Data.h
@@ -30,12 +30,10 @@
 struct Config;
 
 enum BlockState {
-  Pending,
-  PendingInt, // Pending internal refectch
-  PendingRef, // Pending reference refetch
-  Active,
-  ActiveInt, // Active internal refetch
-  ActiveRef, // Active reference refetch
+  Pending,    // waiting for response
+  Active,     // processing current block
+  PendingRef, // waiting for reference refetch response
+  ActiveRef,  // processing current reference refetch
   Done,
   Passthru, // non 206 response passthru
   Fail,
@@ -59,9 +57,7 @@ struct Data {
   char m_hostname[8192];
   int  m_hostlen{0};
 
-  // read from slice block 0
-  char m_date[33];
-  int  m_datelen{0};
+  // best identifier headers, initially from reference slice
   char m_etag[8192];
   int  m_etaglen{0};
   char m_lastmodified[33];
@@ -104,7 +100,6 @@ struct Data {
 
   explicit Data(Config *const config) : m_config(config)
   {
-    m_date[0]         = '\0';
     m_hostname[0]     = '\0';
     m_etag[0]         = '\0';
     m_lastmodified[0] = '\0';
diff --git a/plugins/slice/server.cc b/plugins/slice/server.cc
index bad9947994..f723af38d0 100644
--- a/plugins/slice/server.cc
+++ b/plugins/slice/server.cc
@@ -113,6 +113,8 @@ update_object_size(TSHttpTxn txnp, int64_t size, Config 
&config)
   }
 }
 
+// This sets up the reference data with the content length and any
+// strong/weak identifiers for comparing against subsequent slices.
 HeaderState
 handleFirstServerHeader(Data *const data, TSCont const contp)
 {
@@ -201,10 +203,6 @@ handleFirstServerHeader(Data *const data, TSCont const 
contp)
     return HeaderState::Fail;
   }
 
-  // save data header string
-  data->m_datelen = sizeof(data->m_date);
-  header.valueForKey(TS_MIME_FIELD_DATE, TS_MIME_LEN_DATE, data->m_date, 
&data->m_datelen);
-
   // save weak cache header identifiers (rfc7232 section 2)
   data->m_etaglen = sizeof(data->m_etag);
   header.valueForKey(TS_MIME_FIELD_ETAG, TS_MIME_LEN_ETAG, data->m_etag, 
&data->m_etaglen);
@@ -387,6 +385,7 @@ handleNextServerHeader(Data *const data)
     DEBUG_LOG("Next Header:\n%s", header.toString().c_str());
   }
 
+  // assume the next block header is from the same asset until proven false
   bool same = true;
 
   switch (header.status()) {
@@ -394,7 +393,7 @@ handleNextServerHeader(Data *const data)
     if (data->onlyHeader()) {
       return false;
     }
-    // need to reissue reference slice
+    // asset is gone, reissue the reference block
     logSliceError("404 internal block response (asset gone)", data, header);
     same = false;
     break;
@@ -412,6 +411,7 @@ handleNextServerHeader(Data *const data)
   // can't parse the content range header, abort -- might be too strict
   ContentRange blockcr;
 
+  // check the content range (offset and size)
   if (same) {
     blockcr = contentRangeFrom(header);
     if (!blockcr.isValid() || blockcr.m_length != data->m_contentlen) {
@@ -420,10 +420,16 @@ handleNextServerHeader(Data *const data)
     }
   }
 
+  // identifiers.  Use them
+  char etag[8192];
+  int  etaglen = 0;
+  char lastmodified[33];
+  int  lastmodifiedlen = 0;
+
+  // check the identifiers
   if (same) {
-    // prefer the etag but use Last-Modified if we must.
-    char etag[8192];
-    int  etaglen = sizeof(etag);
+    // prefer the etag
+    etaglen = sizeof(etag);
     header.valueForKey(TS_MIME_FIELD_ETAG, TS_MIME_LEN_ETAG, etag, &etaglen);
 
     if (0 < data->m_etaglen || 0 < etaglen) {
@@ -432,8 +438,8 @@ handleNextServerHeader(Data *const data)
         logSliceError("Mismatch block Etag", data, header);
       }
     } else {
-      char lastmodified[33];
-      int  lastmodifiedlen = sizeof(lastmodified);
+      // use Last-Modified if we must
+      lastmodifiedlen = sizeof(lastmodified);
       header.valueForKey(TS_MIME_FIELD_LAST_MODIFIED, 
TS_MIME_LEN_LAST_MODIFIED, lastmodified, &lastmodifiedlen);
       if (0 < data->m_lastmodifiedlen || 0 < lastmodifiedlen) {
         same = data->m_lastmodifiedlen == lastmodifiedlen && 0 == 
strncmp(lastmodified, data->m_lastmodified, lastmodifiedlen);
@@ -445,59 +451,29 @@ handleNextServerHeader(Data *const data)
   }
 
   // Header mismatch
-  if (same) {
-    // If we were in reference block refetch mode and the headers
-    // still match there is a problem
-    if (BlockState::ActiveRef == data->m_blockstate) {
-      ERROR_LOG("Reference block refetched, got the same block back again");
-      return false;
-    }
-  } else {
-    switch (data->m_blockstate) {
-    case BlockState::Active: {
+  if (!same) {
+    if (data->m_blockstate == BlockState::Active) {
       data->m_upstream.abort();
 
-      // Refetch the current interior slice
-      data->m_blockstate = BlockState::PendingInt;
-
-      time_t date = 0;
-      header.timeForKey(TS_MIME_FIELD_DATE, TS_MIME_LEN_DATE, &date);
-
-      // Ask for any slice newer than the cached one
-      time_t const dateims = date + 1;
-
-      DEBUG_LOG("Attempting to reissue interior slice block request with IMS 
header time: %jd", static_cast<intmax_t>(dateims));
-
-      // add special CRR IMS header to the request
-      HttpHeader          headerreq(data->m_req_hdrmgr.m_buffer, 
data->m_req_hdrmgr.m_lochdr);
-      Config const *const conf = data->m_config;
-      if (!headerreq.setKeyTime(conf->m_crr_ims_header.data(), 
conf->m_crr_ims_header.size(), dateims)) {
-        ERROR_LOG("Failed setting '%s'", conf->m_crr_ims_header.c_str());
-        return false;
-      }
-
-    } break;
-    case BlockState::ActiveInt: {
-      data->m_upstream.abort();
+      DEBUG_LOG("Starting refetch of reference block");
 
-      // New interior slice still mismatches, refetch the reference slice
+      // Interior slice doesn't match reference slice, refetch reference
+      // In this case we've given up but are trying to fix the reference
+      // for next time
       data->m_blockstate = BlockState::PendingRef;
 
-      // convert reference date header to time_t
-      time_t const date = TSMimeParseDate(data->m_date, data->m_datelen);
-
-      // Ask for any slice newer than the cached one
-      time_t const dateims = date + 1;
-
-      DEBUG_LOG("Attempting to reissue reference slice block request with IMS 
header time: %jd", static_cast<intmax_t>(dateims));
-
-      // add special CRR IMS header to the request
-      HttpHeader          headerreq(data->m_req_hdrmgr.m_buffer, 
data->m_req_hdrmgr.m_lochdr);
-      Config const *const conf = data->m_config;
-      if (!headerreq.setKeyTime(conf->m_crr_ims_header.data(), 
conf->m_crr_ims_header.size(), dateims)) {
-        ERROR_LOG("Failed setting '%s'", conf->m_crr_ims_header.c_str());
-        return false;
+      // interior headers for new identifier reference
+      data->m_etaglen = etaglen;
+      if (0 < etaglen) {
+        strncpy(data->m_etag, etag, etaglen);
       }
+      data->m_lastmodifiedlen = lastmodifiedlen;
+      if (0 < lastmodifiedlen) {
+        strncpy(data->m_lastmodified, lastmodified, lastmodifiedlen);
+      }
+
+      // potentially new content length
+      data->m_contentlen = blockcr.m_length;
 
       // Reset for first block
       if (Config::RefType::First == data->m_config->m_reftype) {
@@ -507,17 +483,6 @@ handleNextServerHeader(Data *const data)
       }
 
       return true;
-
-    } break;
-      // Refetch the reference slice
-    case BlockState::ActiveRef: {
-      // In this state the reference changed otherwise the asset is toast
-      // reset the content length (if content length drove the mismatch)
-      data->m_contentlen = blockcr.m_length;
-      return true;
-    } break;
-    default:
-      break;
     }
   }
 
@@ -603,8 +568,7 @@ handle_server_resp(TSCont contp, TSEvent event, Data *const 
data)
 
       // header may have been successfully parsed but with caveats
       switch (data->m_blockstate) {
-        // request new version of current internal slice
-      case BlockState::PendingInt:
+        // request new version of reference slice
       case BlockState::PendingRef: {
         if (!request_block(contp, data)) {
           data->m_blockstate = BlockState::Fail;
diff --git a/plugins/slice/util.cc b/plugins/slice/util.cc
index 7967185b81..579aac447a 100644
--- a/plugins/slice/util.cc
+++ b/plugins/slice/util.cc
@@ -57,7 +57,6 @@ request_block(TSCont contp, Data *const data)
 
   switch (data->m_blockstate) {
   case BlockState::Pending:
-  case BlockState::PendingInt:
   case BlockState::PendingRef:
     break;
   default:
@@ -76,7 +75,7 @@ request_block(TSCont contp, Data *const data)
 
   DEBUG_LOG("requestBlock: %s", rangestr);
 
-  // reuse the incoming client header, just change the range
+  // reuse the original client request header, just change the range
   HttpHeader header(data->m_req_hdrmgr.m_buffer, data->m_req_hdrmgr.m_lochdr);
 
   // if configured, remove range header from head requests
@@ -99,22 +98,31 @@ request_block(TSCont contp, Data *const data)
   }
 
   // Attach the identifier header
+  // The cache_range_requests plugin will:
+  //   mark a fresh block stale if the identifiers mismatch
+  //   mark a stale block fresh if the identifiers match.
   Config const *const cfg = data->m_config;
-  if (!cfg->m_crr_ident_header.empty() && 
!header.hasKey(cfg->m_crr_ident_header.data(), cfg->m_crr_ident_header.size())) 
{
-    swoc::LocalBufferWriter<8192> idbuf;
-    if (0 < data->m_etaglen) {
-      idbuf.write(TS_MIME_FIELD_ETAG, TS_MIME_LEN_ETAG);
-      idbuf.write(": ");
-      idbuf.write(data->m_etag, data->m_etaglen);
-    } else if (0 < data->m_lastmodifiedlen) {
-      idbuf.write(TS_MIME_FIELD_LAST_MODIFIED, TS_MIME_LEN_LAST_MODIFIED);
-      idbuf.write(": ");
-      idbuf.write(data->m_lastmodified, data->m_lastmodifiedlen);
-    }
+  if (!cfg->m_crr_ident_header.empty()) {
+    // Set ident header if header not set or if refetch reference slice
+    if (BlockState::PendingRef == data->m_blockstate ||
+        !header.hasKey(cfg->m_crr_ident_header.data(), 
cfg->m_crr_ident_header.size())) {
+      swoc::LocalBufferWriter<8192> idbuf;
+      if (0 < data->m_etaglen) {
+        idbuf.write(TS_MIME_FIELD_ETAG, TS_MIME_LEN_ETAG);
+        idbuf.write(" ");
+        idbuf.write(data->m_etag, data->m_etaglen);
+      } else if (0 < data->m_lastmodifiedlen) {
+        idbuf.write(TS_MIME_FIELD_LAST_MODIFIED, TS_MIME_LEN_LAST_MODIFIED);
+        idbuf.write(" ");
+        idbuf.write(data->m_lastmodified, data->m_lastmodifiedlen);
+      } else if (BlockState::PendingRef == data->m_blockstate) {
+        idbuf.write("Stale");
+      }
 
-    if (0 < idbuf.size()) {
-      DEBUG_LOG("Adding identity '%.*s'", (int)idbuf.size(), idbuf.data());
-      header.setKeyVal(cfg->m_crr_ident_header.data(), 
cfg->m_crr_ident_header.size(), idbuf.data(), idbuf.size());
+      if (0 < idbuf.size()) {
+        DEBUG_LOG("Adding identity '%.*s'", (int)idbuf.size(), idbuf.data());
+        header.setKeyVal(cfg->m_crr_ident_header.data(), 
cfg->m_crr_ident_header.size(), idbuf.data(), idbuf.size());
+      }
     }
   }
 
@@ -174,15 +182,8 @@ request_block(TSCont contp, Data *const data)
   case BlockState::Pending:
     data->m_blockstate = BlockState::Active;
     break;
-  case BlockState::PendingInt: {
-    data->m_blockstate       = BlockState::ActiveInt;
-    Config const *const conf = data->m_config;
-    header.removeKey(conf->m_crr_ims_header.c_str(), 
conf->m_crr_ims_header.size());
-  } break;
   case BlockState::PendingRef: {
-    data->m_blockstate       = BlockState::ActiveRef;
-    Config const *const conf = data->m_config;
-    header.removeKey(conf->m_crr_ims_header.c_str(), 
conf->m_crr_ims_header.size());
+    data->m_blockstate = BlockState::ActiveRef;
   } break;
   default:
     ERROR_LOG("Invalid blockstate");
diff --git 
a/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_cache_complete_responses.test.py
 
b/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_cache_complete_responses.test.py
index e36f4f2d73..faa3be859e 100644
--- 
a/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_cache_complete_responses.test.py
+++ 
b/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_cache_complete_responses.test.py
@@ -16,8 +16,6 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-import time
-
 Test.Summary = '''
 cache_range_requests cache-complete-responses test
 '''
diff --git 
a/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_ident.test.py
 
b/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_ident.test.py
index e08ffa4d45..eb3ae9a923 100644
--- 
a/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_ident.test.py
+++ 
b/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_ident.test.py
@@ -16,7 +16,9 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
+import datetime
 import os
+import time
 
 Test.Summary = '''
 cache_range_requests X-Crr-Ident plugin test
@@ -30,13 +32,9 @@ Test.SkipUnless(
     Condition.PluginExists('cache_range_requests.so'),
     Condition.PluginExists('xdebug.so'),
 )
-#Test.ContinueOnFail = False
-Test.ContinueOnFail = True
+Test.ContinueOnFail = False
 Test.testName = "cache_range_requests_ident"
 
-# Define and configure ATS
-ts = Test.MakeATSProcess("ts")
-
 # Define and configure origin server
 server = Test.MakeOriginServer("server")
 
@@ -54,16 +52,17 @@ server.addResponse("sessionlog.json", req_chk, res_chk)
 body = "lets go surfin now"
 bodylen = len(body)
 
-req_full = {
-    "headers": "GET /path HTTP/1.1\r\n" + "Host: www.example.com\r\n" + 
"Accept: */*\r\n" + "Range: bytes=0-\r\n" + "\r\n",
+# baseline for testing
+last_modified = "Fri, 07 Mar 2025 18:06:58 GMT"
+etag = '"772102f4-56f4bc1e6d417"'
+
+req_both = {
+    "headers": "GET /both HTTP/1.1\r\n" + "Host: www.example.com\r\n" + 
"Accept: */*\r\n" + "Range: bytes=0-\r\n" + "\r\n",
     "timestamp": "1469733493.993",
     "body": ""
 }
 
-last_modified = "Fri, 07 Mar 2025 18:06:58 GMT"
-etag = '"772102f4-56f4bc1e6d417"'
-
-res_full = {
+res_both = {
     "headers":
         "HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + 
"Cache-Control: max-age=1\r\n" +
         "Content-Range: bytes 0-{0}/{0}\r\n".format(bodylen) + "Connection: 
close\r\n" + 'Etag: ' + etag + '\r\n' +
@@ -72,10 +71,44 @@ res_full = {
     "body": body
 }
 
-server.addResponse("sessionlog.json", req_full, res_full)
+server.addResponse("sessionlog.json", req_both, res_both)
+
+req_etag = {
+    "headers": "GET /etag HTTP/1.1\r\n" + "Host: www.example.com\r\n" + 
"Accept: */*\r\n" + "Range: bytes=0-\r\n" + "\r\n",
+    "timestamp": "1469733493.993",
+    "body": ""
+}
+
+res_etag = {
+    "headers":
+        "HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + 
"Cache-Control: max-age=1\r\n" +
+        "Content-Range: bytes 0-{0}/{0}\r\n".format(bodylen) + "Connection: 
close\r\n" + 'Etag: ' + etag + '\r\n' + "\r\n",
+    "timestamp": "1469733493.993",
+    "body": body
+}
+
+server.addResponse("sessionlog.json", req_etag, res_etag)
+
+req_lm = {
+    "headers": "GET /lm HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Accept: 
*/*\r\n" + "Range: bytes=0-\r\n" + "\r\n",
+    "timestamp": "1469733493.993",
+    "body": ""
+}
 
+res_lm = {
+    "headers":
+        "HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + 
"Cache-Control: max-age=1\r\n" +
+        "Content-Range: bytes 0-{0}/{0}\r\n".format(bodylen) + "Connection: 
close\r\n" + 'Last-Modified: ' + last_modified +
+        '\r\n' + "\r\n",
+    "timestamp": "1469733493.993",
+    "body": body
+}
+
+server.addResponse("sessionlog.json", req_lm, res_lm)
+
+# test for custom Ident header
 req_custom = {
-    "headers": "GET /pathheader HTTP/1.1\r\n" + "Host: www.example.com\r\n" + 
"Accept: */*\r\n" + "Range: bytes=0-\r\n" + "\r\n",
+    "headers": "GET /custom HTTP/1.1\r\n" + "Host: www.example.com\r\n" + 
"Accept: */*\r\n" + "Range: bytes=0-\r\n" + "\r\n",
     "timestamp": "1469733493.993",
     "body": ""
 }
@@ -92,6 +125,29 @@ res_custom = {
 
 server.addResponse("sessionlog.json", req_custom, res_custom)
 
+# Long lived asset for FRESH to STALE testing
+req_fresh = {
+    "headers": "GET /fresh HTTP/1.1\r\n" + "Host: www.example.com\r\n" + 
"Accept: */*\r\n" + "Range: bytes=0-\r\n" + "\r\n",
+    "timestamp": "1469733493.993",
+    "body": ""
+}
+
+etag_fresh = 'fresh'
+
+res_fresh = {
+    "headers":
+        "HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + 
"Cache-Control: max-age=3600\r\n" +
+        "Content-Range: bytes 0-{0}/{0}\r\n".format(bodylen) + "Connection: 
close\r\n" + 'Etag: ' + etag_fresh + '\r\n' + '\r\n' +
+        'Last-Modified: ' + last_modified + '\r\n' + "\r\n",
+    "timestamp": "1469733493.993",
+    "body": body
+}
+
+server.addResponse("sessionlog.json", req_fresh, res_fresh)
+
+# Define and configure ATS
+ts = Test.MakeATSProcess("ts")
+
 # cache range requests plugin remap
 ts.Disk.remap_config.AddLines(
     [
@@ -111,85 +167,181 @@ ts.Disk.records_config.update({
 
 curl_and_args = '-s -D /dev/stdout -o /dev/stderr -x localhost:{} -H "x-debug: 
x-cache"'.format(ts.Variables.port)
 
-# 0 Test - Fetch asset into cache
+##
+## Stale to Fresh testing
+##
+
+## Fetch short lived assets into cache
+
+# 0 Test - Fetch both asset into cache
 tr = Test.AddTestRun("0- range cache load")
 ps = tr.Processes.Default
 ps.StartBefore(server, ready=When.PortOpen(server.Variables.Port))
 ps.StartBefore(Test.Processes.ts)
-tr.MakeCurlCommand(curl_and_args + ' http://ident/path -r 0-', ts=ts)
+tr.MakeCurlCommand(curl_and_args + ' http://ident/both -r 0-', ts=ts)
 ps.ReturnCode = 0
 ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: miss", 
"expected cache miss for load")
 tr.StillRunningAfter = ts
 
-# 1 Test - Fetch asset into cache
-tr = Test.AddTestRun("0- range cache load")
+# 1 Test - Fetch etag asset into cache
+tr = Test.AddTestRun("0- etag cache load")
 ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + ' http://identheader/pathheader -r 0-', 
ts=ts)
+tr.MakeCurlCommand(curl_and_args + ' http://ident/etag -r 0-', ts=ts)
 ps.ReturnCode = 0
 ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: miss", 
"expected cache miss for load")
 tr.StillRunningAfter = ts
 
-# 2 Test - Ensure range is fetched
-tr = Test.AddTestRun("0- cache hit check")
+# 2 Test - Fetch lm asset into cache
+tr = Test.AddTestRun("0- lm cache load")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + ' http://ident/lm -r 0-', ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: miss", 
"expected cache miss for load")
+tr.StillRunningAfter = ts
+
+## both tests
+
+# 3 Test - Ensure Etag header match results in hit-fresh
+tr = Test.AddTestRun("0- both Etag check")
+tr.DelayStart = 2  # Time to ensure stale
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + f" http://ident/both -r 0- -H 'X-Crr-Ident: 
Etag {etag}'", ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-fresh", 
"expected cache hit-fresh")
+tr.StillRunningAfter = ts
+
+# 4 Test - Plugin expects Etag even if Last-Modified matches - hit-stale
+tr = Test.AddTestRun("0- both Last-Modified check")
 ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + ' http://ident/path -r 0-', ts=ts)
+tr.MakeCurlCommand(curl_and_args + f' http://ident/both -r 0- -H "X-Crr-Ident: 
Last-Modified {last_modified}"', ts=ts)
 ps.ReturnCode = 0
-ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit", 
"expected cache hit")
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
 tr.StillRunningAfter = ts
 
-# 3 Test - Ensure range is fetched
-tr = Test.AddTestRun("0- cache hit check")
+# 5 Test - Bad etag stays stale
+tr = Test.AddTestRun("0- both bad Etag check")
 ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + ' http://identheader/pathheader -r 0-', 
ts=ts)
+tr.MakeCurlCommand(curl_and_args + f" http://ident/both -r 0- -H 'X-Crr-Ident: 
Etag no_match'", ts=ts)
 ps.ReturnCode = 0
-ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit", 
"expected cache hit")
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
 tr.StillRunningAfter = ts
 
-# These requests should flip from STALE to FRESH
+## etag only supplied
 
-# 4 Test - Ensure X-Crr-Ident Etag header results in hit-fresh
-tr = Test.AddTestRun("0- range X-Crr-Ident Etag check")
-tr.DelayStart = 2
+# 6 Test - Ensure Etag header match results in hit-fresh
+tr = Test.AddTestRun("0- etag Etag check")
 ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + f" http://ident/path -r 0- -H 'X-Crr-Ident: 
Etag: {etag}'", ts=ts)
+tr.MakeCurlCommand(curl_and_args + f" http://ident/etag -r 0- -H 'X-Crr-Ident: 
Etag {etag}'", ts=ts)
 ps.ReturnCode = 0
 ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-fresh", 
"expected cache hit-fresh")
 tr.StillRunningAfter = ts
 
-# 5 Test - Ensure X-Crr-Ident Etag header results in hit-fresh, custom header
-tr = Test.AddTestRun("0- range CrrIdent Etag check")
-tr.DelayStart = 2
+# 7 Test - Last modified will result in stale
+tr = Test.AddTestRun("0- etag lm cache stale check")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + f' http://ident/etag -r 0- -H "X-Crr-Ident: 
Last-Modified {last_modified}"', ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache stale")
+tr.StillRunningAfter = ts
+
+# 8 Test - Bad etag stays stale
+tr = Test.AddTestRun("0- etag bad Etag check")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + f" http://ident/etag -r 0- -H 'X-Crr-Ident: 
Etag no_match'", ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
+tr.StillRunningAfter = ts
+
+## last modified
+
+# 9 Test - Last modified will result in fresh
+tr = Test.AddTestRun("0- lm lm fresh check")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + f' http://ident/lm -r 0- -H "X-Crr-Ident: 
Last-Modified {last_modified}"', ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-fresh", 
"expected cache fresh")
+tr.StillRunningAfter = ts
+
+# 10 Test - Ensure Etag header match results in hit-fresh
+tr = Test.AddTestRun("0- lm Etag check")
 ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + f" http://identheader/pathheader -r 0- -H 
'CrrIdent: Etag: {etag_custom}'", ts=ts)
+tr.MakeCurlCommand(curl_and_args + f" http://ident/lm -r 0- -H 'X-Crr-Ident: 
Etag {etag}'", ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
+tr.StillRunningAfter = ts
+
+## Fresh to stale testing
+
+# 11 Test - Fetch "fresh" into cache
+tr = Test.AddTestRun("0- fresh range cache load")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + ' http://ident/fresh -r 0-', ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: miss", 
"expected cache miss for load")
+tr.StillRunningAfter = ts
+
+# 12 Test - Ensure "fresh" is in cache
+tr = Test.AddTestRun("0- fresh range cache check")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + ' http://ident/fresh -r 0-', ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-fresh", 
"expected cache fresh")
+tr.StillRunningAfter = ts
+
+# 13 request with different etag and ensure it goes stale
+tr = Test.AddTestRun("0- fresh range to stale")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + " http://ident/fresh -r 0- -H 'X-Crr-Ident: 
Etag not_the_same'", ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
+tr.StillRunningAfter = ts
+
+# 14 request with Last-Modified ensure it goes stale (expected etag)
+tr = Test.AddTestRun("0- etag fresh range to stale")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + f' http://ident/fresh -r 0- -H 
"X-Crr-Ident: Last-Modified {last_modified}"', ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
+tr.StillRunningAfter = ts
+
+# 15 Test - Ensure Etag header match results in hit-fresh
+tr = Test.AddTestRun("0- fresh ensure Etag check")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + f" http://ident/fresh -r 0- -H 
'X-Crr-Ident: Etag {etag_fresh}'", ts=ts)
 ps.ReturnCode = 0
 ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-fresh", 
"expected cache hit-fresh")
 tr.StillRunningAfter = ts
 
-# 6 Test - Ensure X-Crr-Ident Last-Modified header results in hit-fresh
-tr = Test.AddTestRun("0- range X-Crr-Ident Last-Modified check")
+# 16 hit test asset to ensure its still fresh
+tr = Test.AddTestRun("0- plain request path again")
 ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + f' http://ident/path -r 0- -H "X-Crr-Ident: 
Last-Modified: {last_modified}"', ts=ts)
+tr.MakeCurlCommand(curl_and_args + ' http://ident/fresh -r 0-', ts=ts)
 ps.ReturnCode = 0
 ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-fresh", 
"expected cache hit-fresh")
 tr.StillRunningAfter = ts
 
-# 7 Test - Provide a mismatch Etag force IMS request
-tr = Test.AddTestRun("0- range X-Crr-Ident check")
+# 17 request with force stale
+tr = Test.AddTestRun("0- force stale")
 ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + f' http://ident/path -r 0- -H "X-Crr-Ident: 
Last-Modified: foo"', ts=ts)
+tr.MakeCurlCommand(curl_and_args + " http://ident/fresh -r 0- -H 'X-Crr-Ident: 
Stale'", ts=ts)
 ps.ReturnCode = 0
 ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
 tr.StillRunningAfter = ts
 
-# post checks for traffic.out
+## custom header
 
-ts.Disk.traffic_out.Content = Testers.ContainsExpression(
-    """Checking cached '"772102f4-56f4bc1e6d417"' against request 'Etag: 
"772102f4-56f4bc1e6d417"'""",
-    "Etag is correctly considered")
-
-ts.Disk.traffic_out.Content = Testers.ContainsExpression(
-    """Checking cached 'foo' against request 'Etag: foo'""", "Etag custom 
header is correctly considered")
+# 18 Test - Fetch custom asset into cache
+tr = Test.AddTestRun("0- custom range cache load")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + ' http://identheader/custom -r 0-', ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: miss", 
"expected cache miss for load")
+tr.StillRunningAfter = ts
 
-ts.Disk.traffic_out.Content = Testers.ContainsExpression(
-    """Checking cached 'Fri, 07 Mar 2025 18:06:58 GMT' against request 
'Last-Modified: Fri, 07 Mar 2025 18:06:58 GMT'""",
-    "Last-Modified is correctly considered")
+# 19 Test - Ensure CrrIdent Etag header results in hit-fresh, custom header
+tr = Test.AddTestRun("0- fresh CrrIdent Etag check")
+ps = tr.Processes.Default
+tr.MakeCurlCommand(curl_and_args + f" http://identheader/custom -r 0- -H 
'CrrIdent: Etag {etag_custom}'", ts=ts)
+ps.ReturnCode = 0
+ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-fresh", 
"expected cache hit-fresh")
+tr.StillRunningAfter = ts
diff --git 
a/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_ims.test.py
 
b/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_ims.test.py
deleted file mode 100644
index bb8e4cdd8b..0000000000
--- 
a/tests/gold_tests/pluginTest/cache_range_requests/cache_range_requests_ims.test.py
+++ /dev/null
@@ -1,133 +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.
-
-import time
-
-Test.Summary = '''
-cache_range_requests X-CRR-IMS plugin test
-'''
-
-# Test description:
-# Preload the cache with the entire asset to be range requested.
-# Reload remap rule with cache_range_requests plugin
-# Request content through the cache_range_requests plugin
-
-Test.SkipUnless(
-    Condition.PluginExists('cache_range_requests.so'),
-    Condition.PluginExists('xdebug.so'),
-)
-Test.ContinueOnFail = False
-Test.testName = "cache_range_requests_ims"
-
-# Define and configure ATS
-ts = Test.MakeATSProcess("ts")
-
-# Define and configure origin server
-server = Test.MakeOriginServer("server")
-
-# default root
-req_chk = {
-    "headers": "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "uuid: 
none\r\n" + "\r\n",
-    "timestamp": "1469733493.993",
-    "body": ""
-}
-
-res_chk = {"headers": "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + 
"\r\n", "timestamp": "1469733493.993", "body": ""}
-
-server.addResponse("sessionlog.json", req_chk, res_chk)
-
-body = "lets go surfin now"
-bodylen = len(body)
-
-req_full = {
-    "headers": "GET /path HTTP/1.1\r\n" + "Host: www.example.com\r\n" + 
"Accept: */*\r\n" + "Range: bytes=0-\r\n" + "\r\n",
-    "timestamp": "1469733493.993",
-    "body": ""
-}
-
-res_full = {
-    "headers":
-        "HTTP/1.1 206 Partial Content\r\n" + "Accept-Ranges: bytes\r\n" + 
"Cache-Control: max-age=500\r\n" +
-        "Content-Range: bytes 0-{0}/{0}\r\n".format(bodylen) + "Connection: 
close\r\n" + 'Etag: "772102f4-56f4bc1e6d417"\r\n' +
-        "\r\n",
-    "timestamp": "1469733493.993",
-    "body": body
-}
-
-server.addResponse("sessionlog.json", req_full, res_full)
-
-# cache range requests plugin remap
-ts.Disk.remap_config.AddLines(
-    [
-        f'map http://ims http://127.0.0.1:{server.Variables.Port}' + ' 
@plugin=cache_range_requests.so @pparam=--consider-ims',
-        f'map http://imsheader http://127.0.0.1:{server.Variables.Port}' +
-        ' @plugin=cache_range_requests.so @pparam=--consider-ims' + ' 
@pparam=--ims-header=CrrIms',
-    ])
-
-# cache debug
-ts.Disk.plugin_config.AddLine('xdebug.so --enable=x-cache')
-
-# minimal configuration
-ts.Disk.records_config.update({
-    'proxy.config.diags.debug.enabled': 1,
-    'proxy.config.diags.debug.tags': 'cache_range_requests',
-})
-
-curl_and_args = '-s -D /dev/stdout -o /dev/stderr -x localhost:{} -H "x-debug: 
x-cache"'.format(ts.Variables.port)
-
-# 0 Test - Fetch whole asset into cache
-tr = Test.AddTestRun("0- range cache load")
-ps = tr.Processes.Default
-ps.StartBefore(server, ready=When.PortOpen(server.Variables.Port))
-ps.StartBefore(Test.Processes.ts)
-tr.MakeCurlCommand(curl_and_args + ' http://ims/path -r 0-', ts=ts)
-ps.ReturnCode = 0
-ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: miss", 
"expected cache miss for load")
-tr.StillRunningAfter = ts
-
-# test inner range
-# 1 Test - Fetch range into cache
-tr = Test.AddTestRun("0- cache hit check")
-ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + ' http://ims/path -r 0-', ts=ts)
-ps.ReturnCode = 0
-ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit", 
"expected cache hit")
-tr.StillRunningAfter = ts
-
-# set up the IMS date field (go in the future) RFC 2616
-futuretime = time.time() + 100  # seconds
-futurestr = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(futuretime))
-
-# 2 Test - Ensure X-CRR-IMS header results in hit-stale
-tr = Test.AddTestRun("0- range X-CRR-IMS check")
-ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + ' http://ims/path -r 0- -H "X-CRR-IMS: 
{}"'.format(futurestr), ts=ts)
-ps.ReturnCode = 0
-ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
-tr.StillRunningAfter = ts
-
-futuretime += 10  # seconds
-futurestr = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(futuretime))
-
-# 3 Test - Ensure CrrIms header results in hit-stale
-tr = Test.AddTestRun("0- range CrrIms check, override header")
-ps = tr.Processes.Default
-tr.MakeCurlCommand(curl_and_args + ' http://imsheader/path -r 0- -H "CrrIms: 
{}"'.format(futurestr), ts=ts)
-ps.ReturnCode = 0
-ps.Streams.stdout.Content = Testers.ContainsExpression("X-Cache: hit-stale", 
"expected cache hit-stale")
-tr.StillRunningAfter = ts
diff --git a/tests/gold_tests/pluginTest/slice/gold/slice_crr_ident.gold 
b/tests/gold_tests/pluginTest/slice/gold/slice_crr_ident.gold
index f81ffa57f8..8cb833638f 100644
--- a/tests/gold_tests/pluginTest/slice/gold/slice_crr_ident.gold
+++ b/tests/gold_tests/pluginTest/slice/gold/slice_crr_ident.gold
@@ -1,13 +1,13 @@
 cpuup=/plain sssc=200 pssc=206 phr=DIRECT range=::bytes=0-2:: 
x-crr-ident=::-:: uid=::plain 0:: crc=TCP_MISS
-cpuup=/plain sssc=200 pssc=206 phr=DIRECT range=::bytes=3-5:: 
x-crr-ident=::Etag: "plain":: uid=::plain 1:: crc=TCP_MISS
+cpuup=/plain sssc=200 pssc=206 phr=DIRECT range=::bytes=3-5:: 
x-crr-ident=::Etag "plain":: uid=::plain 1:: crc=TCP_MISS
 cpuup=/plain sssc=200 pssc=200 phr=DIRECT range=::-:: x-crr-ident=::-:: 
uid=::plain:: crc=TCP_MISS
 cpuup=/plain sssc=200 pssc=206 phr=DIRECT range=::bytes=0-2:: 
x-crr-ident=::-:: uid=::plain 0:: crc=TCP_REFRESH_MISS
-cpuup=/plain sssc=000 pssc=206 phr=NONE range=::bytes=3-5:: 
x-crr-ident=::Etag: "plain":: uid=::-:: crc=TCP_HIT
+cpuup=/plain sssc=000 pssc=206 phr=NONE range=::bytes=3-5:: x-crr-ident=::Etag 
"plain":: uid=::-:: crc=TCP_HIT
 cpuup=/plain sssc=200 pssc=200 phr=DIRECT range=::-:: x-crr-ident=::-:: 
uid=::plain:: crc=TCP_MISS
 cpuup=/plain sssc=200 pssc=206 phr=DIRECT range=::bytes=0-2:: 
x-crr-ident=::-:: uid=::chg 0:: crc=TCP_REFRESH_MISS
-cpuup=/plain sssc=200 pssc=206 phr=DIRECT range=::bytes=3-5:: 
x-crr-ident=::Etag: "chg":: uid=::chg 1:: crc=TCP_REFRESH_MISS
+cpuup=/plain sssc=200 pssc=206 phr=DIRECT range=::bytes=3-5:: 
x-crr-ident=::Etag "chg":: uid=::chg 1:: crc=TCP_REFRESH_MISS
 cpuup=/plain sssc=200 pssc=200 phr=DIRECT range=::-:: x-crr-ident=::-:: 
uid=::chg:: crc=TCP_MISS
 cpuup=/plain sssc=000 pssc=206 phr=NONE range=::bytes=0-2:: x-crr-ident=::-:: 
uid=::-:: crc=TCP_HIT
-cpuup=/plain sssc=000 pssc=206 phr=NONE range=::bytes=3-5:: 
x-crr-ident=::Etag: "chg":: uid=::-:: crc=TCP_HIT
+cpuup=/plain sssc=000 pssc=206 phr=NONE range=::bytes=3-5:: x-crr-ident=::Etag 
"chg":: uid=::-:: crc=TCP_HIT
 cpuup=/plain sssc=200 pssc=200 phr=DIRECT range=::-:: x-crr-ident=::-:: 
uid=::chg:: crc=TCP_MISS
 cpuup=/404.txt sssc=404 pssc=404 phr=DIRECT range=::-:: x-crr-ident=::-:: 
uid=::-:: crc=TCP_MISS
diff --git a/tests/gold_tests/pluginTest/slice/gold/slice_ident.gold 
b/tests/gold_tests/pluginTest/slice/gold/slice_ident.gold
index ed44f114db..c32629f0fc 100644
--- a/tests/gold_tests/pluginTest/slice/gold/slice_ident.gold
+++ b/tests/gold_tests/pluginTest/slice/gold/slice_ident.gold
@@ -1,15 +1,15 @@
 /etag 200 200 range=::-:: x-crr-ident=::-:: crrident=::-::
 /lm 200 200 range=::-:: x-crr-ident=::-:: crrident=::-::
 /etag 000 206 range=::bytes=0-10:: x-crr-ident=::-:: crrident=::-::
-/etag 000 206 range=::bytes=11-21:: x-crr-ident=::Etag: "foo":: crrident=::-::
+/etag 000 206 range=::bytes=11-21:: x-crr-ident=::Etag "foo":: crrident=::-::
 /etag 200 200 range=::-:: x-crr-ident=::-:: crrident=::-::
 /etag 000 206 range=::bytes=0-10:: x-crr-ident=::-:: crrident=::-::
-/etag 000 206 range=::bytes=11-21:: x-crr-ident=::-:: crrident=::Etag: "foo"::
+/etag 000 206 range=::bytes=11-21:: x-crr-ident=::-:: crrident=::Etag "foo"::
 /etag 200 200 range=::-:: x-crr-ident=::-:: crrident=::-::
 /lm 000 206 range=::bytes=0-10:: x-crr-ident=::-:: crrident=::-::
-/lm 000 206 range=::bytes=11-21:: x-crr-ident=::Last-Modified: Fri, 07 Mar 
2025 18:06:58 GMT:: crrident=::-::
+/lm 000 206 range=::bytes=11-21:: x-crr-ident=::Last-Modified Fri, 07 Mar 2025 
18:06:58 GMT:: crrident=::-::
 /lm 200 200 range=::-:: x-crr-ident=::-:: crrident=::-::
 /lm 000 206 range=::bytes=0-10:: x-crr-ident=::-:: crrident=::-::
-/lm 000 206 range=::bytes=11-21:: x-crr-ident=::-:: crrident=::Last-Modified: 
Fri, 07 Mar 2025 18:06:58 GMT::
+/lm 000 206 range=::bytes=11-21:: x-crr-ident=::-:: crrident=::Last-Modified 
Fri, 07 Mar 2025 18:06:58 GMT::
 /lm 200 200 range=::-:: x-crr-ident=::-:: crrident=::-::
 /404.txt 000 404 range=::-:: x-crr-ident=::-:: crrident=::-::
diff --git a/tests/gold_tests/pluginTest/slice/slice_ident.test.py 
b/tests/gold_tests/pluginTest/slice/slice_ident.test.py
index 7c7784a27d..7babe58ea3 100644
--- a/tests/gold_tests/pluginTest/slice/slice_ident.test.py
+++ b/tests/gold_tests/pluginTest/slice/slice_ident.test.py
@@ -169,19 +169,19 @@ ps.Streams.stdout.Content = 
Testers.ContainsExpression("200 OK", "expected 200 O
 tr.StillRunningAfter = ts
 
 # 6 Test - add token to transaction log
-tr = Test.AddTestRun("Fetch last-modified asset")
+tr = Test.AddTestRun("Add 404 token to transaction log")
 ps = tr.Processes.Default
 tr.MakeCurlCommand(curl_and_args + ' http://prefetch/404.txt', ts=ts)
 ps.ReturnCode = 0
 ps.Streams.stdout.Content = Testers.ContainsExpression("404", "expected 404 
Not Found response")
 tr.StillRunningAfter = ts
 
+# 7 - wait for logs
 condwaitpath = os.path.join(Test.Variables.AtsTestToolsDir, 'condwait')
-
 tslog = os.path.join(ts.Variables.LOGDIR, 'transaction.log')
 Test.AddAwaitFileContainsTestRun('Await ts transactions to finish logging.', 
tslog, '404.txt')
 
-# 6 Check logs
+# 8 Check logs
 tr = Test.AddTestRun()
 tr.Processes.Default.Command = (f"cat {tslog}")
 tr.Streams.stdout = "gold/slice_ident.gold"
diff --git a/tests/gold_tests/pluginTest/slice/slice_selfhealing.test.py 
b/tests/gold_tests/pluginTest/slice/slice_selfhealing.test.py
index 2bced732d8..5f2ef1a70b 100644
--- a/tests/gold_tests/pluginTest/slice/slice_selfhealing.test.py
+++ b/tests/gold_tests/pluginTest/slice/slice_selfhealing.test.py
@@ -68,12 +68,11 @@ ts.Disk.remap_config.AddLines(
     [
         f'map http://slice/ http://127.0.0.1:{server.Variables.Port}/' +
         ' @plugin=slice.so @pparam=--blockbytes-test=3 
@pparam=--remap-host=crr',
-        f'map http://crr/ http://127.0.0.1:{server.Variables.Port}/' +
-        '  @plugin=cache_range_requests.so @pparam=--consider-ims 
@pparam=--consider-ident',
+        f'map http://crr/ http://127.0.0.1:{server.Variables.Port}/' + '  
@plugin=cache_range_requests.so @pparam=--consider-ident',
         f'map http://slicehdr/ http://127.0.0.1:{server.Variables.Port}/' + ' 
@plugin=slice.so @pparam=--blockbytes-test=3' +
-        ' @pparam=--remap-host=crrhdr @pparam=--crr-ims-header=crr-foo',
+        ' @pparam=--remap-host=crrhdr @pparam=--crr-ident-header=crr-foo',
         f'map http://crrhdr/ http://127.0.0.1:{server.Variables.Port}/'
-        '  @plugin=cache_range_requests.so @pparam=--ims-header=crr-foo',
+        '  @plugin=cache_range_requests.so @pparam=--ident-header=crr-foo',
     ])
 
 ts.Disk.plugin_config.AddLine('xdebug.so --enable=x-cache')
@@ -239,7 +238,7 @@ ps.Streams.stderr = "gold/aaa.gold"
 ps.Streams.stdout.Content = Testers.ContainsExpression("etagold", "expected 
etagold")
 tr.StillRunningAfter = ts
 
-# 5 Test - Preload reference etagnew-1
+# 5 Test - Preload slice 1 etagnew-1
 tr = Test.AddTestRun("Preload slice etagnew-1")
 ps = tr.Processes.Default
 tr.MakeCurlCommand(curl_and_args + ' http://crr/reference -r 3-5 -H "uuid: 
etagnew-1"', ts=ts)

Reply via email to