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 60c09eab3 Allow HEAD req to be served from cached GET (#9066)
60c09eab3 is described below

commit 60c09eab32457498e68b57ef35ef0056ac873f1b
Author: Serris Lew <[email protected]>
AuthorDate: Tue Aug 30 16:40:13 2022 -0600

    Allow HEAD req to be served from cached GET (#9066)
    
    (cherry picked from commit 405875ad11ef07d5c9d79454bc33d556442f89c6)
---
 proxy/http/HttpTransact.cc                         |   6 +-
 .../gold_tests/cache/cache-request-method.test.py  |  17 ++
 .../cache/replay/head_with_get_cached.replay.yaml  | 285 +++++++++++++++++++++
 3 files changed, 306 insertions(+), 2 deletions(-)

diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index b5bbec45b..20b3e680f 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -5990,10 +5990,12 @@ HttpTransact::is_cache_response_returnable(State *s)
   // sure that our cached resource has a method that matches the incoming
   // requests's method. If not, then we cannot reply with the cached resource.
   // That is, we cannot reply to an incoming GET request with a response to a
-  // previous POST request.
+  // previous POST request. The only exception is replying a HEAD request with
+  // a cached GET request as neither are destructive
   int const client_request_method = 
s->hdr_info.client_request.method_get_wksidx();
   int const cached_request_method = 
s->cache_info.object_read->request_get()->method_get_wksidx();
-  if (client_request_method != cached_request_method) {
+  if (client_request_method != cached_request_method &&
+      (client_request_method != HTTP_WKSIDX_HEAD || cached_request_method != 
HTTP_WKSIDX_GET)) {
     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_NOT_ACCEPTABLE);
     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
     return false;
diff --git a/tests/gold_tests/cache/cache-request-method.test.py 
b/tests/gold_tests/cache/cache-request-method.test.py
index 5715e812a..3e444b2bd 100644
--- a/tests/gold_tests/cache/cache-request-method.test.py
+++ b/tests/gold_tests/cache/cache-request-method.test.py
@@ -61,3 +61,20 @@ tr = Test.AddTestRun("Verify correct with POST response 
caching enabled.")
 tr.Processes.Default.StartBefore(server)
 tr.Processes.Default.StartBefore(ts)
 tr.AddVerifierClientProcess("client1", replay_file, 
http_ports=[ts.Variables.port])
+
+# Test 2: Verify correct HEAD response handling with cached GET response
+ts = Test.MakeATSProcess("ts-cache-head")
+replay_file = "replay/head_with_get_cached.replay.yaml"
+server = Test.MakeVerifierServerProcess("server2", replay_file)
+ts.Disk.records_config.update({
+    'proxy.config.diags.debug.enabled': 1,
+    'proxy.config.diags.debug.tags': 'http.*|cache.*',
+    'proxy.config.http.insert_age_in_response': 0,
+})
+ts.Disk.remap_config.AddLine(
+    'map / http://127.0.0.1:{0}'.format(server.Variables.http_port)
+)
+tr = Test.AddTestRun("Verify correct with HEAD response.")
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(ts)
+tr.AddVerifierClientProcess("client2", replay_file, 
http_ports=[ts.Variables.port])
diff --git a/tests/gold_tests/cache/replay/head_with_get_cached.replay.yaml 
b/tests/gold_tests/cache/replay/head_with_get_cached.replay.yaml
new file mode 100644
index 000000000..e55b12a7e
--- /dev/null
+++ b/tests/gold_tests/cache/replay/head_with_get_cached.replay.yaml
@@ -0,0 +1,285 @@
+#  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"
+
+
+sessions:
+- transactions:
+
+  # Populate the cache with a response to a GET request.
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /some/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 1 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Content-Length, 16 ]
+        - [ Cache-Control, max-age=300 ]
+        - [ X-Response, first_get_response ]
+
+    proxy-response:
+      status: 200
+
+  # Verify that we reply to the request out of the cache.
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /some/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 2 ]
+
+      # Add a delay so ATS has time to finish any caching IO for the previous
+      # transaction.
+      delay: 100ms
+
+    # The request should be served out of cache, so this 403 should not be
+    # received.
+    server-response:
+      status: 403
+      reason: Forbidden
+      headers:
+        fields:
+        - [ Content-Length, 8 ]
+
+    # ATS should serve the cached 200 response.
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response, { value: first_get_response, as: equal} ]
+
+  # Verify that we reply to the request out of the cache with HEAD requests.
+  - client-request:
+      method: "HEAD"
+      version: "1.1"
+      url: /some/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 3 ]
+
+      # Add a delay so ATS has time to finish any caching IO for the previous
+      # transaction.
+      delay: 100ms
+
+    # The request should be served out of cache, so this 403 should not be
+    # received.
+    server-response:
+      status: 403
+      reason: Forbidden
+      headers:
+        fields:
+        - [ Content-Length, 8 ]
+
+    # ATS should serve the cached 200 response.
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response, { value: first_get_response, as: equal} ]
+
+  # Since the HEAD response was served from cache and didn't invalidate object,
+  # the cached response for the GET method should still be valid.
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /some/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 4 ]
+
+      # Add a delay so ATS has time to finish any caching IO for the previous
+      # transaction.
+      delay: 100ms
+
+    # The server should not receive this request because ATS should reply out
+    # of the cache.
+    server-response:
+      status: 500
+      reason: Internal Server Error
+      headers:
+        fields:
+        - [ Content-Length, 8 ]
+        - [ X-Response, internal_server_error ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response, { value: first_get_response, as: equal} ]
+
+  # Test 2: Verify HEAD doesn't cache and only served from cache when it's 
from GET
+  - client-request:
+      method: "HEAD"
+      version: "1.1"
+      url: /some/head/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 5 ]
+
+      # Add a delay so ATS has time to finish any caching IO for the previous
+      # transaction.
+      delay: 100ms
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Content-Length, 8 ]
+        - [ Cache-Control, max-age=300 ]
+        - [ X-Response, first_head_response ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response, { value: first_head_response, as: equal} ]
+
+  # Repeat the request again, it should not be served from cache.
+  - client-request:
+      method: "HEAD"
+      version: "1.1"
+      url: /some/head/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 6 ]
+
+      # Add a delay so ATS has time to finish any caching IO for the previous
+      # transaction.
+      delay: 100ms
+
+    # This should not be served out of cache
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Content-Length, 8 ]
+        - [ Cache-Control, max-age=300 ]
+        - [ X-Response, second_head_response ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response, { value: second_head_response, as: equal} ]
+
+  # GET request should also be from server and this time it will be cached
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /some/head/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 7 ]
+
+      # Add a delay so ATS has time to finish any caching IO for the previous
+      # transaction.
+      delay: 100ms
+
+    # The server should not receive this request because ATS should reply out
+    # of the cache.
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Content-Length, 8 ]
+        - [ Cache-Control, max-age=300 ]
+        - [ X-Response, second_get_response ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response, { value: second_get_response, as: equal} ]
+
+  # Verify that we reply to the request out of the cache.
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /some/head/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 8 ]
+
+      # Add a delay so ATS has time to finish any caching IO for the previous
+      # transaction.
+      delay: 100ms
+
+    # The request should be served out of cache, so this 403 should not be
+    # received.
+    server-response:
+      status: 403
+      reason: Forbidden
+      headers:
+        fields:
+        - [ Content-Length, 8 ]
+
+    # ATS should serve the cached 200 response.
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response, { value: second_get_response, as: equal} ]
+
+  # Verify that we reply to the request out of the cache with HEAD requests.
+  - client-request:
+      method: "HEAD"
+      version: "1.1"
+      url: /some/head/path
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 9 ]
+
+      # Add a delay so ATS has time to finish any caching IO for the previous
+      # transaction.
+      delay: 100ms
+
+    # The request should be served out of cache, so this 403 should not be
+    # received.
+    server-response:
+      status: 403
+      reason: Forbidden
+      headers:
+        fields:
+        - [ Content-Length, 8 ]
+
+    # ATS should serve the cached 200 response.
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Response, { value: second_get_response, as: equal} ]
\ No newline at end of file

Reply via email to