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

bneradt 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 799c4cc89a TSHttpAltInfoQualitySet: add an autest (#13391)
799c4cc89a is described below

commit 799c4cc89a64972d93716e23ddba9ede099b50f5
Author: Brian Neradt <[email protected]>
AuthorDate: Thu Jul 30 09:04:49 2026 -0500

    TSHttpAltInfoQualitySet: add an autest (#13391)
    
    This adds an AuTest to verify the TSHttpAltInfoQualitySet plugin API.
    The test verifies alternate creation and selection through observable
    cached responses.
    
    Closes: #7205
---
 tests/gold_tests/cache/alternate-caching.test.py   |   8 +-
 .../cache/replay/alternate-caching-quality.yaml    | 142 +++++++++++++++++++++
 tests/tools/plugins/CMakeLists.txt                 |   1 +
 tests/tools/plugins/http_alt_info_quality.cc       | 101 +++++++++++++++
 4 files changed, 251 insertions(+), 1 deletion(-)

diff --git a/tests/gold_tests/cache/alternate-caching.test.py 
b/tests/gold_tests/cache/alternate-caching.test.py
index b6ec135083..7f3b698c67 100644
--- a/tests/gold_tests/cache/alternate-caching.test.py
+++ b/tests/gold_tests/cache/alternate-caching.test.py
@@ -17,9 +17,15 @@ Test the alternate caching features
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
+import os
+
 Test.Summary = '''
 Test the alternate caching feature.
 '''
 
-# Verify disabled negative revalidating behavior.
+# Verify cache alternate replacement behavior.
 Test.ATSReplayTest(replay_file="replay/alternate-caching-update-size.yaml")
+
+# Verify TSHttpAltInfoQualitySet affects alternate selection.
+tr = Test.ATSReplayTest(replay_file="replay/alternate-caching-quality.yaml")
+Test.PrepareTestPlugin(os.path.join(Test.Variables.AtsTestPluginsDir, 
'http_alt_info_quality.so'), tr.Processes.ts_alt_quality)
diff --git a/tests/gold_tests/cache/replay/alternate-caching-quality.yaml 
b/tests/gold_tests/cache/replay/alternate-caching-quality.yaml
new file mode 100644
index 0000000000..06915997fd
--- /dev/null
+++ b/tests/gold_tests/cache/replay/alternate-caching-quality.yaml
@@ -0,0 +1,142 @@
+#  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.
+
+meta:
+  version: "1.0"
+
+autest:
+  description: 'Verify TSHttpAltInfoQualitySet affects alternate selection'
+
+  dns:
+    name: 'dns_alt_quality'
+
+  server:
+    name: 'server_alt_quality'
+
+  client:
+    name: 'client_alt_quality'
+
+  ats:
+    name: 'ts_alt_quality'
+    process_config:
+      enable_cache: true
+
+    records_config:
+      proxy.config.cache.select_alternate: 1
+      proxy.config.cache.limits.http.max_alts: 4
+      proxy.config.http.cache.ignore_query: 1
+
+    plugin_config:
+      - "xdebug.so --enable=x-cache"
+
+    remap_config:
+      - from: "http://example.com/";
+        to: "http://backend.example.com:{SERVER_HTTP_PORT}/";
+
+sessions:
+- transactions:
+  # Store the English response as the first alternate.
+  - all: { headers: { fields: [[ uuid, 1 ]]}}
+    client-request:
+      method: "GET"
+      version: "1.1"
+      scheme: "http"
+      url: /path/request?variant=English
+      delay: 100ms
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ Accept-Language, English ]
+        - [ X-Debug, X-Cache ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Content-Length, 0 ]
+        - [ Cache-Control, max-age=60 ]
+        - [ Content-Language, English ]
+        - [ X-Response-Variant, English ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response-Variant, { value: English, as: equal } ]
+        - [ X-Cache, { value: miss, as: equal } ]
+
+  # The language mismatch prevents reuse of the English alternate, allowing
+  # the French response to be stored as another alternate.
+  - all: { headers: { fields: [[ uuid, 2 ]]}}
+    client-request:
+      method: "GET"
+      version: "1.1"
+      scheme: "http"
+      url: /path/request?variant=French
+      delay: 100ms
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ Accept-Language, French ]
+        - [ X-Debug, X-Cache ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Content-Length, 0 ]
+        - [ Cache-Control, max-age=60 ]
+        - [ Content-Language, French ]
+        - [ X-Response-Variant, French ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response-Variant, { value: French, as: equal } ]
+        - [ X-Cache, { value: miss, as: equal } ]
+
+  # Both languages are acceptable, so the plugin's query-based quality
+  # selects the older English alternate without contacting the origin.
+  - all: { headers: { fields: [[ uuid, 3 ]]}}
+    client-request:
+      method: "GET"
+      version: "1.1"
+      scheme: "http"
+      url: /path/request?variant=English
+      delay: 100ms
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ Accept-Language, "English, French" ]
+        - [ X-Debug, X-Cache ]
+
+    proxy-request:
+      expect: absent
+
+    server-response:
+      status: 500
+      reason: NOT USED
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response-Variant, { value: English, as: equal } ]
+        - [ X-Cache, { value: hit-fresh, as: equal } ]
diff --git a/tests/tools/plugins/CMakeLists.txt 
b/tests/tools/plugins/CMakeLists.txt
index a909cf59d1..b7f18109ef 100644
--- a/tests/tools/plugins/CMakeLists.txt
+++ b/tests/tools/plugins/CMakeLists.txt
@@ -24,6 +24,7 @@ add_autest_plugin(delay_txn_start delay_txn_start.cc)
 add_autest_plugin(emergency_shutdown emergency_shutdown.cc)
 add_autest_plugin(fatal_shutdown fatal_shutdown.cc)
 add_autest_plugin(hook_add_plugin hook_add_plugin.cc)
+add_autest_plugin(http_alt_info_quality http_alt_info_quality.cc)
 add_autest_plugin(missing_mangled_definition missing_mangled_definition_c.c 
missing_mangled_definition_cpp.cc)
 add_autest_plugin(missing_ts_plugin_init missing_ts_plugin_init.cc)
 add_autest_plugin(server_packet_mark server_packet_mark.cc 
packet_mark_common.cc)
diff --git a/tests/tools/plugins/http_alt_info_quality.cc 
b/tests/tools/plugins/http_alt_info_quality.cc
new file mode 100644
index 0000000000..dcd180d4a5
--- /dev/null
+++ b/tests/tools/plugins/http_alt_info_quality.cc
@@ -0,0 +1,101 @@
+/** @file
+
+  Select cache alternates by matching request query strings.
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ */
+
+#include <ts/ts.h>
+
+#include <string>
+
+namespace
+{
+constexpr char PLUGIN_NAME[] = "http_alt_info_quality";
+
+std::string
+get_query(TSMBuffer buffer, TSMLoc header)
+{
+  TSMLoc url;
+  int    length = 0;
+
+  if (TSHttpHdrUrlGet(buffer, header, &url) != TS_SUCCESS) {
+    return {};
+  }
+
+  const char *query = TSUrlHttpQueryGet(buffer, url, &length);
+  std::string result;
+
+  if (query != nullptr) {
+    result.assign(query, static_cast<size_t>(length));
+  }
+
+  TSHandleMLocRelease(buffer, header, url);
+  return result;
+}
+
+int
+select_alternate(TSCont /* contp ATS_UNUSED */, TSEvent event, void *edata)
+{
+  TSAssert(event == TS_EVENT_HTTP_SELECT_ALT);
+
+  auto      alt_info = static_cast<TSHttpAltInfo>(edata);
+  TSMBuffer client_buffer;
+  TSMBuffer cached_buffer;
+  TSMLoc    client_header;
+  TSMLoc    cached_header;
+
+  if (TSHttpAltInfoClientReqGet(alt_info, &client_buffer, &client_header) != 
TS_SUCCESS) {
+    TSHttpAltInfoQualitySet(alt_info, 0.0F);
+    return 0;
+  }
+  if (TSHttpAltInfoCachedReqGet(alt_info, &cached_buffer, &cached_header) != 
TS_SUCCESS) {
+    TSHttpAltInfoQualitySet(alt_info, 0.0F);
+    TSHandleMLocRelease(client_buffer, TS_NULL_MLOC, client_header);
+    return 0;
+  }
+
+  std::string client_query = get_query(client_buffer, client_header);
+  std::string cached_query = get_query(cached_buffer, cached_header);
+
+  TSHttpAltInfoQualitySet(alt_info, client_query == cached_query ? 1.0F : 
0.0F);
+
+  TSHandleMLocRelease(client_buffer, TS_NULL_MLOC, client_header);
+  TSHandleMLocRelease(cached_buffer, TS_NULL_MLOC, cached_header);
+  return 0;
+}
+} // namespace
+
+void
+TSPluginInit(int /* argc ATS_UNUSED */, const char ** /* argv ATS_UNUSED */)
+{
+  TSPluginRegistrationInfo info;
+
+  info.plugin_name   = PLUGIN_NAME;
+  info.vendor_name   = "Apache Software Foundation";
+  info.support_email = "[email protected]";
+
+  if (TSPluginRegister(&info) != TS_SUCCESS) {
+    TSError("[%s] plugin registration failed", PLUGIN_NAME);
+    return;
+  }
+
+  TSHttpHookAdd(TS_HTTP_SELECT_ALT_HOOK, TSContCreate(select_alternate, 
nullptr));
+}

Reply via email to