You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2022/07/13 20:18:47 UTC

[trafficserver] branch 9.2.x updated: ESI processing when origin returns 304 response (#8563)

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

zwoop pushed a commit to branch 9.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/9.2.x by this push:
     new f8a900f0d ESI processing when origin returns 304 response (#8563)
f8a900f0d is described below

commit f8a900f0d6e8b0ca9fa8eabbbafcbb8a571b7122
Author: Kit Chan <ki...@apache.org>
AuthorDate: Tue Jan 18 16:43:00 2022 -0800

    ESI processing when origin returns 304 response (#8563)
    
    * ESI processing when origin returns 304 response
    
    * Fix gold tests
    
    (cherry picked from commit c0e3ddb985689ae2f71ca486421caf67cc83cfc3)
---
 plugins/esi/esi.cc                                 |  14 ++
 tests/gold_tests/pluginTest/esi/esi_304.test.py    | 210 +++++++++++++++++++++
 .../pluginTest/esi/gold/esi_private_headers.gold   |   7 +
 3 files changed, 231 insertions(+)

diff --git a/plugins/esi/esi.cc b/plugins/esi/esi.cc
index 9d223b5f9..3b29f83ce 100644
--- a/plugins/esi/esi.cc
+++ b/plugins/esi/esi.cc
@@ -1292,6 +1292,20 @@ isTxnTransformable(TSHttpTxn txnp, bool is_cache_txn, bool *intercept_header, bo
     return false;
   }
 
+  // if origin returns status 304, check cached response instead
+  int response_status;
+  if (is_cache_txn == false) {
+    response_status = TSHttpHdrStatusGet(bufp, hdr_loc);
+    if (response_status == TS_HTTP_STATUS_NOT_MODIFIED) {
+      TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+      header_obtained = TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc);
+      if (header_obtained != TS_SUCCESS) {
+        TSError("[esi][%s] Couldn't get txn cache response header", __FUNCTION__);
+        return false;
+      }
+    }
+  }
+
   do {
     *intercept_header = checkHeaderValue(bufp, hdr_loc, SERVER_INTERCEPT_HEADER, SERVER_INTERCEPT_HEADER_LEN);
     if (*intercept_header) {
diff --git a/tests/gold_tests/pluginTest/esi/esi_304.test.py b/tests/gold_tests/pluginTest/esi/esi_304.test.py
new file mode 100644
index 000000000..a078f84da
--- /dev/null
+++ b/tests/gold_tests/pluginTest/esi/esi_304.test.py
@@ -0,0 +1,210 @@
+'''
+Test the ESI plugin when origin returns 304 response.
+'''
+#  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 os
+
+Test.Summary = '''
+Test the ESI plugin when origin returns 304 response.
+'''
+
+Test.SkipUnless(
+    Condition.PluginExists('esi.so'),
+)
+
+
+class EsiTest():
+    """
+    A class that encapsulates the configuration and execution of a set of ESI
+    test cases.
+    """
+
+    """ static: The same server Process is used across all tests. """
+    _server = None
+
+    """ static: A counter to keep the ATS process names unique across tests. """
+    _ts_counter = 0
+
+    """ static: A counter to keep any output file names unique across tests. """
+    _output_counter = 0
+
+    """ The ATS process for this set of test cases. """
+    _ts = None
+
+    def __init__(self, plugin_config):
+        """
+        Args:
+            plugin_config (str): The config line to place in plugin.config for
+                the ATS process.
+        """
+        if EsiTest._server is None:
+            EsiTest._server = EsiTest._create_server()
+
+        self._ts = EsiTest._create_ats(self, plugin_config)
+
+    @staticmethod
+    def _create_server():
+        """
+        Create and start a server process.
+        """
+        # Configure our server.
+        server = Test.MakeOriginServer("server", lookup_key="{%uuid}")
+
+        # Generate the set of ESI responses.
+        request_header = {
+            "headers":
+            "GET /esi_etag.php HTTP/1.1\r\n" +
+            "Host: www.example.com\r\n" +
+            "uuid: first\r\n" +
+            "Content-Length: 0\r\n\r\n",
+            "timestamp": "1469733493.993",
+            "body": ""
+        }
+        esi_body = r'''<html>
+<body>
+Hello, ESI 304 test
+</body>
+</html>
+'''
+        response_header = {
+            "headers":
+            "HTTP/1.1 200 OK\r\n" +
+            "X-Esi: 1\r\n" +
+            "Cache-Control: public, max-age=0\r\n" +
+            'Etag: "esi_304_test"\r\n' +
+            "Content-Type: text/html\r\n" +
+            "Connection: close\r\n" +
+            "Content-Length: {}\r\n".format(len(esi_body)) +
+            "\r\n",
+            "timestamp": "1469733493.993",
+            "body": esi_body
+        }
+        server.addResponse("sessionfile.log", request_header, response_header)
+
+        request_header = {
+            "headers":
+            "GET /esi_etag.php HTTP/1.1\r\n" +
+            "Host: www.example.com\r\n" +
+            "uuid: second\r\n" +
+            'If-None-Match: "esi_304_test"\r\n' +
+            "Content-Length: 0\r\n\r\n",
+            "timestamp": "1469733493.993",
+            "body": ""
+        }
+        response_header = {
+            "headers":
+            "HTTP/1.1 304 Not Modified\r\n" +
+            "Content-Type: text/html\r\n" +
+            "Connection: close\r\n" +
+            "Content-Length: 0\r\n" +
+            "\r\n",
+            "timestamp": "1469733493.993",
+            "body": ""
+        }
+        server.addResponse("sessionfile.log", request_header, response_header)
+
+        request_header = {
+            "headers":
+            "GET /date.php HTTP/1.1\r\n" +
+            "Host: www.example.com\r\n" +
+            "uuid: date\r\n" +
+            "Content-Length: 0\r\n\r\n",
+            "timestamp": "1469733493.993",
+            "body": ""
+        }
+        date_body = r'''ESI 304 test
+No Date
+'''
+        response_header = {
+            "headers":
+            "HTTP/1.1 200 OK\r\n" +
+            "Content-Type: text/html\r\n" +
+            "Connection: close\r\n" +
+            "Content-Length: {}\r\n".format(len(date_body)) +
+            "\r\n",
+            "timestamp": "1469733493.993",
+            "body": date_body
+        }
+        server.addResponse("sessionfile.log", request_header, response_header)
+
+        # Create a run to start the server.
+        tr = Test.AddTestRun("Start the server.")
+        tr.Processes.Default.StartBefore(server)
+        tr.Processes.Default.Command = "echo starting the server"
+        tr.Processes.Default.ReturnCode = 0
+        tr.StillRunningAfter = server
+
+        return server
+
+    @staticmethod
+    def _create_ats(self, plugin_config):
+        """
+        Create and start an ATS process.
+        """
+        EsiTest._ts_counter += 1
+
+        # Configure ATS with a vanilla ESI plugin configuration.
+        ts = Test.MakeATSProcess("ts{}".format(EsiTest._ts_counter))
+        ts.Disk.records_config.update({
+            'proxy.config.diags.debug.enabled': 1,
+            'proxy.config.diags.debug.tags': 'http|plugin_esi',
+        })
+        ts.Disk.remap_config.AddLine(
+            'map http://www.example.com/ http://127.0.0.1:{0}'.format(EsiTest._server.Variables.Port)
+        )
+        ts.Disk.plugin_config.AddLine(plugin_config)
+
+        # Create a run to start the ATS process.
+        tr = Test.AddTestRun("Start the ATS process.")
+        tr.Processes.Default.StartBefore(ts)
+        tr.Processes.Default.Command = "echo starting ATS"
+        tr.Processes.Default.ReturnCode = 0
+        tr.StillRunningAfter = ts
+        return ts
+
+    def run_cases(self):
+        # Test 1: Verify basic ESI functionality.
+        tr = Test.AddTestRun("First request for esi_etag.php: not cached")
+        tr.Processes.Default.Command = \
+            ('curl http://127.0.0.1:{0}/esi_etag.php -H"Host: www.example.com" '
+             '-H"Accept: */*" -H"uuid: first" --verbose -o /dev/stderr'.format(
+                 self._ts.Variables.port))
+        tr.Processes.Default.ReturnCode = 0
+        tr.Processes.Default.Streams.stderr = "gold/esi_private_headers.gold"
+        tr.StillRunningAfter = self._server
+        tr.StillRunningAfter = self._ts
+
+        # Test 2: Repeat the above, origin should now be returning 304 response.
+        tr = Test.AddTestRun("Second request for esi_etag.php: will be cached")
+        tr.Processes.Default.Command = \
+            ('curl http://127.0.0.1:{0}/esi_etag.php -H"Host: www.example.com" '
+             '-H"Accept: */*" -H"uuid: second" --verbose -o /dev/stderr'.format(
+                 self._ts.Variables.port))
+        tr.Processes.Default.ReturnCode = 0
+        tr.Processes.Default.Streams.stderr = "gold/esi_private_headers.gold"
+        tr.StillRunningAfter = self._server
+        tr.StillRunningAfter = self._ts
+
+
+#
+# Configure and run the test cases.
+#
+
+# Run the tests with ESI configured with private response.
+private_response_test = EsiTest(plugin_config='esi.so --private-response')
+private_response_test.run_cases()
diff --git a/tests/gold_tests/pluginTest/esi/gold/esi_private_headers.gold b/tests/gold_tests/pluginTest/esi/gold/esi_private_headers.gold
new file mode 100644
index 000000000..caa77ec0d
--- /dev/null
+++ b/tests/gold_tests/pluginTest/esi/gold/esi_private_headers.gold
@@ -0,0 +1,7 @@
+``
+> GET /esi_etag.php HTTP/1.1
+``
+< HTTP/1.1 200 OK
+``
+< Cache-Control: max-age=0, private
+``