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