moonchen commented on code in PR #13603:
URL: https://github.com/apache/trafficserver/pull/13603#discussion_r3936547687


##########
src/proxy/hdrs/HTTP.cc:
##########
@@ -2262,6 +2306,8 @@ HTTPInfo::unmarshal(char *buf, int len, RefCountObj 
*block_ref)
     alt->m_response_hdr.m_mime = hh->m_fields_impl;
   }
 
+  recompute_alt_wks_indices(alt);

Review Comment:
   Could we avoid doing this unconditionally when the object was written 
against the same WKS table? A compact WKS compatibility identity persisted with 
newly written cache metadata would let the reader rebuild for pre-24.3 objects 
and for an identity mismatch, while trusting the stored indexes on a match. 
That preserves the flexibility this PR is aiming for without paying the field 
walk and tokenization cost in the common case.



##########
src/proxy/hdrs/unit_tests/test_Hdrs.cc:
##########
@@ -2902,6 +2902,198 @@ TEST_CASE("HTTPInfo::unmarshal_v24_1 frag bounds 
checks", "[proxy][hdrtest][unma
   }
 }
 
+// ---------------------------------------------------------------------------
+// Well-known string index rebuilding.
+//
+// A cached object stores indexes into the well-known string table next to the 
strings those
+// indexes stand for. A build whose table differs from the writer's would read 
those indexes as
+// different strings, so HTTPInfo::unmarshal() rebuilds them from the strings.
+//
+// There is only one table in a process, so these tests stand in for a 
differing table by rotating
+// every stored index and clearing the presence bits and slot accelerators 
derived from them, the
+// way a build that lacked some of these strings would leave them.
+// ---------------------------------------------------------------------------
+namespace
+{
+int16_t
+rotate_wks_idx(int16_t wks_idx)
+{
+  return wks_idx < 0 ? wks_idx : static_cast<int16_t>((wks_idx + 7) % 
hdrtoken_num_wks);
+}
+
+/// Rotate every well-known string index in one marshalled header heap.
+void
+scramble_marshalled_heap(HdrHeap *heap)
+{
+  char *obj_data = reinterpret_cast<char *>(heap) + sizeof(HdrHeap);
+  char *heap_end = reinterpret_cast<char *>(heap) + heap->m_size;
+
+  // Objects start at the marshalled heap's data offset, which marshal() sets 
to the header size.
+  obj_data = reinterpret_cast<char *>(heap) + 
reinterpret_cast<intptr_t>(heap->m_data_start);
+
+  while (obj_data < heap_end) {
+    HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(obj_data);
+
+    REQUIRE(obj->m_length > 0);
+    switch (static_cast<HdrHeapObjType>(obj->m_type)) {
+    case HdrHeapObjType::URL: {
+      URLImpl *url          = reinterpret_cast<URLImpl *>(obj);
+      url->m_scheme_wks_idx = rotate_wks_idx(url->m_scheme_wks_idx);
+      break;
+    }
+    case HdrHeapObjType::HTTP_HEADER: {
+      HTTPHdrImpl *hh = reinterpret_cast<HTTPHdrImpl *>(obj);
+      if (hh->m_polarity == HTTPType::REQUEST) {
+        hh->u.req.m_method_wks_idx = 
rotate_wks_idx(hh->u.req.m_method_wks_idx);
+      }
+      break;
+    }
+    case HdrHeapObjType::FIELD_BLOCK: {
+      MIMEFieldBlockImpl *fblock = reinterpret_cast<MIMEFieldBlockImpl *>(obj);
+      for (uint32_t i = 0; i < fblock->m_freetop; ++i) {
+        MIMEField &field = fblock->m_field_slots[i];
+        if (field.is_live()) {
+          field.m_wks_idx = rotate_wks_idx(field.m_wks_idx);
+        }
+      }
+      break;
+    }
+    case HdrHeapObjType::MIME_HEADER: {
+      MIMEHdrImpl *mh            = reinterpret_cast<MIMEHdrImpl *>(obj);
+      mh->m_presence_bits        = MIME_PRESENCE_NONE;
+      mh->m_slot_accelerators[0] = 0xFFFFFFFF;
+      mh->m_slot_accelerators[1] = 0xFFFFFFFF;
+      mh->m_slot_accelerators[2] = 0xFFFFFFFF;
+      mh->m_slot_accelerators[3] = 0xFFFFFFFF;

Review Comment:
   Same inline-block gap in the unit-test scrambler: with these test headers, 
the `FIELD_BLOCK` case never fires. Please rotate the live fields in 
`mh->m_first_fblock` so the serialized-data test verifies field-index 
reconstruction rather than only method/scheme indexes and the aggregate 
metadata.
   
   ```suggestion
         mh->m_slot_accelerators[3] = 0xFFFFFFFF;
         for (uint32_t i = 0; i < mh->m_first_fblock.m_freetop; ++i) {
           MIMEField &field = mh->m_first_fblock.m_field_slots[i];
           if (field.is_live()) {
             field.m_wks_idx = rotate_wks_idx(field.m_wks_idx);
           }
         }
   ```



##########
src/proxy/hdrs/HdrHeap.cc:
##########
@@ -49,6 +52,95 @@ namespace
 {
 DbgCtl dbg_ctl_http{"http"};
 
+#if TS_HAS_TESTS
+// Test hook: how far to rotate the well-known string indexes written into a 
marshalled heap.
+// Zero, the default, leaves marshalling alone.
+int const test_wks_idx_shift = []() -> int {
+  char const *const value = std::getenv("ATS_TEST_WKS_IDX_SHIFT");
+
+  return value != nullptr ? atoi(value) : 0;
+}();
+
+int16_t
+test_shift_wks_idx(int16_t wks_idx)
+{
+  if (wks_idx < 0) {
+    return wks_idx;
+  }
+  // Fold the configured shift into [0, hdrtoken_num_wks) here rather than 
where it is read:
+  // hdrtoken_num_wks is initialized in another translation unit, so it is not 
dependable during
+  // this one's static initialization. Folding also keeps a negative or 
oversized environment value
+  // from producing an index that is not in the table.
+  int const shift = ((test_wks_idx_shift % hdrtoken_num_wks) + 
hdrtoken_num_wks) % hdrtoken_num_wks;
+
+  return static_cast<int16_t>((wks_idx + shift) % hdrtoken_num_wks);
+}
+
+/** Make a marshalled heap look like one written by a build with a different 
well-known string
+ * table: rotate every stored index, and drop the presence bits and slot 
accelerators that a build
+ * lacking some of this build's strings would never have set.
+ *
+ * There is no way to run two well-known string tables in one process now that 
the table is built at
+ * compile time, so this stands in for the case the reader has to survive. 
Reading such a heap back
+ * has to reproduce the header the writer had, because 
HTTPHdrImpl::recompute_wks_indices() rebuilds
+ * all of it from the header strings the heap also carries. See the 
ATS_TEST_WKS_IDX_SHIFT autest.
+ */
+void
+test_shift_marshalled_wks_indices(HdrHeap *marshal_hdr)
+{
+  if (test_wks_idx_shift == 0) {
+    return;
+  }
+
+  char *obj_data = reinterpret_cast<char *>(marshal_hdr) + HDR_HEAP_HDR_SIZE;
+  char *heap_end = reinterpret_cast<char *>(marshal_hdr) + marshal_hdr->m_size;
+
+  while (obj_data < heap_end) {
+    HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(obj_data);
+
+    switch (static_cast<HdrHeapObjType>(obj->m_type)) {
+    case HdrHeapObjType::URL: {
+      URLImpl *url          = reinterpret_cast<URLImpl *>(obj);
+      url->m_scheme_wks_idx = test_shift_wks_idx(url->m_scheme_wks_idx);
+      break;
+    }
+    case HdrHeapObjType::HTTP_HEADER: {
+      HTTPHdrImpl *hh = reinterpret_cast<HTTPHdrImpl *>(obj);
+      if (hh->m_polarity == HTTPType::REQUEST) {
+        hh->u.req.m_method_wks_idx = 
test_shift_wks_idx(hh->u.req.m_method_wks_idx);
+      }
+      break;
+    }
+    case HdrHeapObjType::FIELD_BLOCK: {
+      MIMEFieldBlockImpl *fblock = reinterpret_cast<MIMEFieldBlockImpl *>(obj);
+      for (uint32_t i = 0; i < fblock->m_freetop; ++i) {
+        MIMEField &field = fblock->m_field_slots[i];
+        if (field.is_live()) {
+          field.m_wks_idx = test_shift_wks_idx(field.m_wks_idx);
+        }
+      }
+      break;
+    }
+    case HdrHeapObjType::MIME_HEADER: {
+      MIMEHdrImpl *mh     = reinterpret_cast<MIMEHdrImpl *>(obj);
+      mh->m_presence_bits = MIME_PRESENCE_NONE;
+      for (uint32_t &accelerator : mh->m_slot_accelerators) {
+        accelerator = 0xFFFFFFFF;
+      }

Review Comment:
   `m_first_fblock` is inline in `MIMEHdrImpl`, so this walk only reaches 
fields that overflowed into a separate `FIELD_BLOCK` object, which takes more 
than 16 fields. Every header in the autest fits in the first block, so no field 
index is actually rotated; only the presence bits and accelerators are cleared. 
Checked by dumping the marshalled heap with `ATS_TEST_WKS_IDX_SHIFT=7`: the 
method index moves 117 -> 124 while `Host`/`Accept-Encoding` stay at 30/1. 
Please rotate the live fields in `mh->m_first_fblock` here as well.
   
   ```suggestion
         for (uint32_t &accelerator : mh->m_slot_accelerators) {
           accelerator = 0xFFFFFFFF;
         }
         // The first field block is inline in the MIMEHdrImpl, so the walk 
never sees it as a FIELD_BLOCK.
         for (uint32_t i = 0; i < mh->m_first_fblock.m_freetop; ++i) {
           MIMEField &field = mh->m_first_fblock.m_field_slots[i];
           if (field.is_live()) {
             field.m_wks_idx = test_shift_wks_idx(field.m_wks_idx);
           }
         }
   ```
   
   With this (and the same in `scramble_marshalled_heap`) the autest and both 
unit tests still pass here.



##########
include/iocore/cache/CacheDefs.h:
##########
@@ -37,13 +37,35 @@ enum class CacheInitState : int {
 #define CACHE_ALT_INDEX_DEFAULT -1
 #define CACHE_ALT_REMOVED       -2
 
+// Bumping the minor version does not clear anyone's cache: stripe validation 
looks only at the
+// major version, and this build still reads every object written at an older 
minor version. What
+// it does mean is that an ATS older than this treats the objects this build 
writes as corrupt and
+// refetches them, so bump it whenever an object gains a shape an older ATS 
would misread.
+//
+// 24.2 marshalled the fragment offset table in full; see 
CACHE_DB_FRAG_OFFSET_TABLE_VERSION below.
+// 24.3 stopped trusting the well-known string indexes stored in an object and 
started rebuilding
+// them from the header strings stored alongside them, in 
HTTPHdrImpl::recompute_wks_indices().
+// That is what frees the well-known string table in proxy/hdrs/HdrToken.cc to 
change: any ATS at
+// 24.3 or newer reads objects written against any table, and anything older 
refuses them outright
+// rather than resolving their indexes against the wrong table.
 static const uint8_t CACHE_DB_MAJOR_VERSION = 24;
-static const uint8_t CACHE_DB_MINOR_VERSION = 2;
+static const uint8_t CACHE_DB_MINOR_VERSION = 3;

Review Comment:
   `traffic_cache_tool` still defines its cache version as 24.1, and 
`validateMeta()` rejects stripe minor versions above 2. As a result it refuses 
a stripe written by this 24.3 code before reaching the WKS-safe URL scan added 
here. Please update the tool's accepted version handling and cover inspection 
of a 24.3 stripe.



##########
src/proxy/hdrs/unit_tests/test_Hdrs.cc:
##########
@@ -2902,6 +2902,198 @@ TEST_CASE("HTTPInfo::unmarshal_v24_1 frag bounds 
checks", "[proxy][hdrtest][unma
   }
 }
 
+// ---------------------------------------------------------------------------
+// Well-known string index rebuilding.
+//
+// A cached object stores indexes into the well-known string table next to the 
strings those
+// indexes stand for. A build whose table differs from the writer's would read 
those indexes as
+// different strings, so HTTPInfo::unmarshal() rebuilds them from the strings.
+//
+// There is only one table in a process, so these tests stand in for a 
differing table by rotating
+// every stored index and clearing the presence bits and slot accelerators 
derived from them, the
+// way a build that lacked some of these strings would leave them.
+// ---------------------------------------------------------------------------
+namespace
+{
+int16_t
+rotate_wks_idx(int16_t wks_idx)
+{
+  return wks_idx < 0 ? wks_idx : static_cast<int16_t>((wks_idx + 7) % 
hdrtoken_num_wks);
+}
+
+/// Rotate every well-known string index in one marshalled header heap.
+void
+scramble_marshalled_heap(HdrHeap *heap)
+{
+  char *obj_data = reinterpret_cast<char *>(heap) + sizeof(HdrHeap);
+  char *heap_end = reinterpret_cast<char *>(heap) + heap->m_size;
+
+  // Objects start at the marshalled heap's data offset, which marshal() sets 
to the header size.
+  obj_data = reinterpret_cast<char *>(heap) + 
reinterpret_cast<intptr_t>(heap->m_data_start);

Review Comment:
   The first `obj_data` initializer is dead because it is overwritten below. 
This can be a single initialization from `heap->m_data_start`.



##########
doc/developer-guide/cache-architecture/architecture.en.rst:
##########
@@ -483,6 +483,47 @@ default). Objects which are in use when the write cursor 
is near use the same
 underlying evacuation mechanism but are handled automatically and not via the
 explicit ``pinned`` bit in :cpp:class:`Dir`.
 
+Object Versioning
+-----------------
+
+Every ``Doc`` records the cache format version that wrote it, in its 
``v_major``
+and ``v_minor`` fields, taken from ``CACHE_DB_MAJOR_VERSION`` and
+``CACHE_DB_MINOR_VERSION`` in ``iocore/cache/CacheDefs.h``.
+
+Bumping the minor version does not clear the cache. Stripe validation looks 
only
+at the major version, and the current reader still reads every object written 
at
+an older minor version. What the bump buys is protection in the other 
direction:
+a reader rejects any object newer than itself and refetches it, rather than
+misreading a shape it does not understand.
+
+Reading an older object sometimes needs work that reading a current one does
+not. Compare against **the fixed version at which that part of the format
+changed**, never against ``CACHE_DB_VERSION``. The latter silently changes
+meaning at the next bump, and sends every object the previous release wrote 
down
+the wrong path. ``CACHE_DB_FRAG_OFFSET_TABLE_VERSION`` is such a fixed point.
+
+Well-Known Strings
+------------------
+
+A marshalled header stores indexes into the well-known string table
+(``proxy/hdrs/HdrToken.cc``) beside the strings those indexes stand for: the
+index of every MIME field name, of the request method, and of the request URL
+scheme, plus the presence bits and slot accelerators derived from them. Change
+the table and every stored index denotes a different string.
+
+The strings are in the object too, so the indexes are only a cache over them.
+``HTTPInfo::unmarshal()`` rebuilds all of it through
+``HTTPHdrImpl::recompute_wks_indices()`` before anything reads the header,
+unconditionally rather than on a version test, since an object written by a
+same-version build with a different table needs the same treatment as an older
+one. The ``CacheAltMagic`` check keeps this to once per marshalled buffer, on a
+read that already paid for disk I/O or a RAM-cache decompression.
+
+The table is therefore free to change without invalidating anyone's cache. The

Review Comment:
   The index/cardinality changes motivating this PR are covered, but 
`m_cooked_stuff` is marshalled as-is. Reordering or reusing WKS IDs does not 
affect it; changing which Cache-Control directives are recognized, or changing 
their cooked masks, does. Could this qualify the statement that the table is 
free to change so it does not imply that those Cache-Control semantics are also 
reconstructed?



##########
src/proxy/hdrs/unit_tests/test_Hdrs.cc:
##########
@@ -2902,6 +2902,198 @@ TEST_CASE("HTTPInfo::unmarshal_v24_1 frag bounds 
checks", "[proxy][hdrtest][unma
   }
 }
 
+// ---------------------------------------------------------------------------
+// Well-known string index rebuilding.
+//
+// A cached object stores indexes into the well-known string table next to the 
strings those
+// indexes stand for. A build whose table differs from the writer's would read 
those indexes as
+// different strings, so HTTPInfo::unmarshal() rebuilds them from the strings.
+//
+// There is only one table in a process, so these tests stand in for a 
differing table by rotating
+// every stored index and clearing the presence bits and slot accelerators 
derived from them, the
+// way a build that lacked some of these strings would leave them.
+// ---------------------------------------------------------------------------
+namespace
+{
+int16_t
+rotate_wks_idx(int16_t wks_idx)
+{
+  return wks_idx < 0 ? wks_idx : static_cast<int16_t>((wks_idx + 7) % 
hdrtoken_num_wks);
+}
+
+/// Rotate every well-known string index in one marshalled header heap.
+void
+scramble_marshalled_heap(HdrHeap *heap)
+{
+  char *obj_data = reinterpret_cast<char *>(heap) + sizeof(HdrHeap);
+  char *heap_end = reinterpret_cast<char *>(heap) + heap->m_size;
+
+  // Objects start at the marshalled heap's data offset, which marshal() sets 
to the header size.
+  obj_data = reinterpret_cast<char *>(heap) + 
reinterpret_cast<intptr_t>(heap->m_data_start);
+
+  while (obj_data < heap_end) {
+    HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(obj_data);
+
+    REQUIRE(obj->m_length > 0);
+    switch (static_cast<HdrHeapObjType>(obj->m_type)) {
+    case HdrHeapObjType::URL: {
+      URLImpl *url          = reinterpret_cast<URLImpl *>(obj);
+      url->m_scheme_wks_idx = rotate_wks_idx(url->m_scheme_wks_idx);
+      break;
+    }
+    case HdrHeapObjType::HTTP_HEADER: {
+      HTTPHdrImpl *hh = reinterpret_cast<HTTPHdrImpl *>(obj);
+      if (hh->m_polarity == HTTPType::REQUEST) {
+        hh->u.req.m_method_wks_idx = 
rotate_wks_idx(hh->u.req.m_method_wks_idx);
+      }
+      break;
+    }
+    case HdrHeapObjType::FIELD_BLOCK: {
+      MIMEFieldBlockImpl *fblock = reinterpret_cast<MIMEFieldBlockImpl *>(obj);
+      for (uint32_t i = 0; i < fblock->m_freetop; ++i) {
+        MIMEField &field = fblock->m_field_slots[i];
+        if (field.is_live()) {
+          field.m_wks_idx = rotate_wks_idx(field.m_wks_idx);
+        }
+      }
+      break;
+    }
+    case HdrHeapObjType::MIME_HEADER: {
+      MIMEHdrImpl *mh            = reinterpret_cast<MIMEHdrImpl *>(obj);
+      mh->m_presence_bits        = MIME_PRESENCE_NONE;
+      mh->m_slot_accelerators[0] = 0xFFFFFFFF;
+      mh->m_slot_accelerators[1] = 0xFFFFFFFF;
+      mh->m_slot_accelerators[2] = 0xFFFFFFFF;
+      mh->m_slot_accelerators[3] = 0xFFFFFFFF;
+      break;
+    }
+    default:
+      break;
+    }
+    obj_data += obj->m_length;
+  }
+}
+
+void
+parse_request(HTTPHdr &hdr, std::string_view text)
+{
+  HTTPParser parser;
+
+  http_parser_init(&parser);
+  hdr.create(HTTPType::REQUEST);
+
+  char const *start = text.data();
+  char const *end   = text.data() + text.length();
+
+  REQUIRE(hdr.parse_req(&parser, &start, end, true) == ParseResult::DONE);
+  http_parser_clear(&parser);
+}
+
+void
+parse_response(HTTPHdr &hdr, std::string_view text)
+{
+  HTTPParser parser;
+
+  http_parser_init(&parser);
+  hdr.create(HTTPType::RESPONSE);
+
+  char const *start = text.data();
+  char const *end   = text.data() + text.length();
+
+  REQUIRE(hdr.parse_resp(&parser, &start, end, true) == ParseResult::DONE);
+  http_parser_clear(&parser);
+}
+} // anonymous namespace
+
+TEST_CASE("HTTPHdrImpl::recompute_wks_indices rebuilds from the stored 
strings", "[proxy][hdrtest][wks]")
+{
+  HTTPHdr req;
+  parse_request(req, "GET /a HTTP/1.1\r\nHost: example.com\r\nCache-Control: 
no-cache\r\nAccept: */*\r\n\r\n"sv);
+  req.url_get()->scheme_set(static_cast<std::string_view>(URL_SCHEME_HTTP));
+
+  // Everything the header derives from the table is now wrong, as it would be 
had it come from a
+  // build whose table differed.
+  req.m_http->u.req.m_method_wks_idx             = 
rotate_wks_idx(req.m_http->u.req.m_method_wks_idx);
+  req.m_http->u.req.m_url_impl->m_scheme_wks_idx = 
rotate_wks_idx(req.m_http->u.req.m_url_impl->m_scheme_wks_idx);
+  req.m_mime->m_presence_bits                    = MIME_PRESENCE_NONE;
+  for (MIMEFieldBlockImpl *fblock = &req.m_mime->m_first_fblock; fblock != 
nullptr; fblock = fblock->m_next) {
+    for (uint32_t i = 0; i < fblock->m_freetop; ++i) {
+      MIMEField &field = fblock->m_field_slots[i];
+      if (field.is_live()) {
+        field.m_wks_idx = rotate_wks_idx(field.m_wks_idx);
+      }
+    }
+  }
+  CHECK(req.method_get() != "GET"sv);
+  CHECK(req.presence(MIME_PRESENCE_CACHE_CONTROL) == 0);
+
+  req.m_http->recompute_wks_indices();
+
+  CHECK(req.method_get() == "GET"sv);
+  CHECK(req.method_get_wksidx() == HTTP_WKSIDX_GET);
+  CHECK(req.url_get()->scheme_get() == 
static_cast<std::string_view>(URL_SCHEME_HTTP));
+  CHECK(req.presence(MIME_PRESENCE_CACHE_CONTROL) != 0);
+  CHECK(req.value_get(static_cast<std::string_view>(MIME_FIELD_CACHE_CONTROL)) 
== "no-cache"sv);
+  CHECK(req.value_get(static_cast<std::string_view>(MIME_FIELD_HOST)) == 
"example.com"sv);
+
+  req.destroy();
+}
+
+TEST_CASE("HTTPInfo::unmarshal rebuilds well-known string indices", 
"[proxy][hdrtest][wks]")
+{
+  HTTPHdr req;
+  HTTPHdr resp;
+
+  parse_request(req, "GET /a HTTP/1.1\r\nHost: example.com\r\nAccept-Encoding: 
gzip\r\n\r\n"sv);
+  req.url_get()->scheme_set(static_cast<std::string_view>(URL_SCHEME_HTTP));
+  parse_response(resp,
+                 "HTTP/1.1 200 OK\r\nCache-Control: 
max-age=300\r\nContent-Type: text/plain\r\nVary: Accept-Encoding\r\n\r\n"sv);
+
+  HTTPInfo info;
+  info.create();
+  info.request_set(&req);
+  info.response_set(&resp);
+
+  int const len = info.marshal_length();
+  // uint64_t elements so the buffer meets the alignment marshal() asserts on.
+  std::vector<uint64_t> storage((len + sizeof(uint64_t) - 1) / 
sizeof(uint64_t), 0);
+  char *const           buf = reinterpret_cast<char *>(storage.data());
+
+  REQUIRE(info.marshal(buf, len) <= len);
+
+  HTTPCacheAlt *marshalled = reinterpret_cast<HTTPCacheAlt *>(buf);
+  REQUIRE(marshalled->m_request_hdr.m_heap != nullptr);
+  REQUIRE(marshalled->m_response_hdr.m_heap != nullptr);
+  scramble_marshalled_heap(reinterpret_cast<HdrHeap *>(buf + 
reinterpret_cast<intptr_t>(marshalled->m_request_hdr.m_heap)));
+  scramble_marshalled_heap(reinterpret_cast<HdrHeap *>(buf + 
reinterpret_cast<intptr_t>(marshalled->m_response_hdr.m_heap)));
+
+  REQUIRE(HTTPInfo::unmarshal(buf, len, nullptr) > 0);
+
+  HTTPInfo got;
+  REQUIRE(got.get_handle(buf, len) > 0);
+
+  HTTPHdr *got_req  = got.request_get();
+  HTTPHdr *got_resp = got.response_get();
+
+  CHECK(got_req->method_get() == "GET"sv);
+  CHECK(got_req->method_get_wksidx() == HTTP_WKSIDX_GET);
+  CHECK(got_req->url_get()->scheme_get() == 
static_cast<std::string_view>(URL_SCHEME_HTTP));
+  
CHECK(got_req->value_get(static_cast<std::string_view>(MIME_FIELD_ACCEPT_ENCODING))
 == "gzip"sv);
+  CHECK(got_req->presence(MIME_PRESENCE_HOST) != 0);
+
+  
CHECK(got_resp->value_get(static_cast<std::string_view>(MIME_FIELD_CACHE_CONTROL))
 == "max-age=300"sv);
+  CHECK(got_resp->value_get(static_cast<std::string_view>(MIME_FIELD_VARY)) == 
"Accept-Encoding"sv);
+  CHECK(got_resp->presence(MIME_PRESENCE_CACHE_CONTROL) != 0);
+  CHECK(got_resp->presence(MIME_PRESENCE_VARY) != 0);
+  // The cooked Cache-Control cache is keyed by directive name, so it survives 
independently, but
+  // it has to still agree with the rebuilt indices.
+  CHECK(got_resp->get_cooked_cc_mask() & MIME_COOKED_MASK_CC_MAX_AGE);
+  CHECK(got_resp->get_cooked_cc_max_age() == 300);
+
+  req.destroy();
+  resp.destroy();

Review Comment:
   `info` still owns the alternate allocated by `create()`; please add 
`info.destroy()` before leaving the test.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to