diff --git a/plugins/xdebug/xdebug.cc b/plugins/xdebug/xdebug.cc index 30a1d108c58..f0b1d307a48 100644 --- a/plugins/xdebug/xdebug.cc +++ b/plugins/xdebug/xdebug.cc @@ -31,6 +31,7 @@ #include #include +#include "ts/experimental.h" #include "tscore/ink_defs.h" #include "tscpp/util/PostScript.h" #include "tscpp/util/TextView.h" @@ -81,6 +82,7 @@ enum { XHEADER_X_REMAP = 1u << 8, XHEADER_X_PROBE_HEADERS = 1u << 9, XHEADER_X_PSELECT_KEY = 1u << 10, + XHEADER_X_CACHE_INFO = 1u << 11, }; static TSCont XInjectHeadersCont = nullptr; @@ -174,6 +176,41 @@ InjectCacheKeyHeader(TSHttpTxn txn, TSMBuffer buffer, TSMLoc hdr) TSfree(strval.ptr); } +static void +InjectCacheInfoHeader(TSHttpTxn txn, TSMBuffer buffer, TSMLoc hdr) +{ + TSMLoc dst = TS_NULL_MLOC; + TSMgmtInt volume; + const char *path; + + TSDebug("xdebug", "attempting to inject X-Cache-Info header"); + + if ((path = TSHttpTxnCacheDiskPathGet(txn, nullptr)) == nullptr) { + goto done; + } + + if (TSHttpTxnInfoIntGet(txn, TS_TXN_INFO_CACHE_VOLUME, &volume) != TS_SUCCESS) { + goto done; + } + + // Create a new response header field. + dst = FindOrMakeHdrField(buffer, hdr, "X-Cache-Info", lengthof("X-Cache-Info")); + if (dst == TS_NULL_MLOC) { + goto done; + } + + char value[1024]; + snprintf(value, sizeof(value), "path=%s; volume=%" PRId64, path, volume); + + // Now copy the CacheDisk info into the response header. + TSReleaseAssert(TSMimeHdrFieldValueStringInsert(buffer, hdr, dst, -1 /* idx */, value, std::strlen(value)) == TS_SUCCESS); + +done: + if (dst != TS_NULL_MLOC) { + TSHandleMLocRelease(buffer, hdr, dst); + } +} + static void InjectCacheHeader(TSHttpTxn txn, TSMBuffer buffer, TSMLoc hdr) { @@ -423,6 +460,10 @@ XInjectResponseHeaders(TSCont /* contp */, TSEvent event, void *edata) InjectCacheKeyHeader(txn, buffer, hdr); } + if (xheaders & XHEADER_X_CACHE_INFO) { + InjectCacheInfoHeader(txn, buffer, hdr); + } + if (xheaders & XHEADER_X_CACHE) { InjectCacheHeader(txn, buffer, hdr); } @@ -558,6 +599,8 @@ XScanRequestHeaders(TSCont /* contp */, TSEvent event, void *edata) if (header_field_eq("x-cache-key", value, vsize)) { xheaders |= XHEADER_X_CACHE_KEY; + } else if (header_field_eq("x-cache-info", value, vsize)) { + xheaders |= XHEADER_X_CACHE_INFO; } else if (header_field_eq("x-milestones", value, vsize)) { xheaders |= XHEADER_X_MILESTONES; } else if (header_field_eq("x-cache", value, vsize)) { diff --git a/proxy/http/HttpCacheSM.h b/proxy/http/HttpCacheSM.h index 2fc0c0ec2aa..64de7850759 100644 --- a/proxy/http/HttpCacheSM.h +++ b/proxy/http/HttpCacheSM.h @@ -135,7 +135,13 @@ class HttpCacheSM : public Continuation int get_volume_number() { - return cache_read_vc ? (cache_read_vc->get_volume_number()) : -1; + if (cache_read_vc) { + return cache_read_vc->get_volume_number(); + } else if (cache_write_vc) { + return cache_write_vc->get_volume_number(); + } + + return -1; } inline void diff --git a/tests/gold_tests/pluginTest/xdebug/x_cache_info/none.in b/tests/gold_tests/pluginTest/xdebug/x_cache_info/none.in new file mode 100644 index 00000000000..1d9b7f59a4f --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_cache_info/none.in @@ -0,0 +1,4 @@ +GET /argh HTTP/1.1 +Host: none +X-Debug: X-Cache-Info + diff --git a/tests/gold_tests/pluginTest/xdebug/x_cache_info/one.in b/tests/gold_tests/pluginTest/xdebug/x_cache_info/one.in new file mode 100644 index 00000000000..e751d8ddad3 --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_cache_info/one.in @@ -0,0 +1,4 @@ +GET /argh HTTP/1.1 +Host: one +X-Debug: X-Cache-Info + diff --git a/tests/gold_tests/pluginTest/xdebug/x_cache_info/out.gold b/tests/gold_tests/pluginTest/xdebug/x_cache_info/out.gold new file mode 100644 index 00000000000..94dffe672cc --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_cache_info/out.gold @@ -0,0 +1,60 @@ +HTTP/1.1 500 Cannot find server. +Date:`` +Connection: keep-alive +Server:`` +Cache-Control: no-store +Content-Type: text/html +Content-Language: en +Content-Length: 391 + + + +Unknown Host + + + +

Unknown Host

+
+ + +Description: Unable to locate the server requested --- +the server does not have a DNS entry. Perhaps there is a misspelling +in the server name, or the server no longer exists. Double-check the +name and try again. + +
+ +====== +HTTP/1.1 200 OK +Date:`` +Age:`` +Transfer-Encoding: chunked +Connection: keep-alive +Server:`` +X-Cache-Info: path=``/x_cache_info/ts/storage/cache.db; volume=0 + +0 + +====== +HTTP/1.1 200 OK +Date:`` +Age:`` +Transfer-Encoding: chunked +Connection: keep-alive +Server:`` +X-Cache-Info: path=``/x_cache_info/ts/storage/cache.db; volume=0 + +0 + +====== +HTTP/1.1 200 OK +Date:`` +Age:`` +Transfer-Encoding: chunked +Connection: keep-alive +Server:`` +X-Cache-Info: path=``/x_cache_info/ts/storage/cache.db; volume=0 + +0 + +====== diff --git a/tests/gold_tests/pluginTest/xdebug/x_cache_info/three.in b/tests/gold_tests/pluginTest/xdebug/x_cache_info/three.in new file mode 100644 index 00000000000..d0f63c0d440 --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_cache_info/three.in @@ -0,0 +1,4 @@ +GET /argh HTTP/1.1 +Host: three123 +X-Debug: x-cACHE-iNFO + diff --git a/tests/gold_tests/pluginTest/xdebug/x_cache_info/two.in b/tests/gold_tests/pluginTest/xdebug/x_cache_info/two.in new file mode 100644 index 00000000000..55786b0aebf --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_cache_info/two.in @@ -0,0 +1,4 @@ +GET /argh HTTP/1.1 +Host: two +X-Debug: x-cache-info + diff --git a/tests/gold_tests/pluginTest/xdebug/x_cache_info/x_cache_info.test.py b/tests/gold_tests/pluginTest/xdebug/x_cache_info/x_cache_info.test.py new file mode 100644 index 00000000000..892297bb6dd --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_cache_info/x_cache_info.test.py @@ -0,0 +1,76 @@ +# 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. + +Test.Summary = ''' +Test xdebug plugin X-Cache-Info header +''' + +server = Test.MakeOriginServer("server") +started = False + +request_header = { + "headers": "GET /argh HTTP/1.1\r\nHost: doesnotmatter\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header, response_header) + +ts = Test.MakeATSProcess("ts") + +ts.Disk.records_config.update({ + 'proxy.config.url_remap.remap_required': 0, + 'proxy.config.diags.debug.enabled': 0, + 'proxy.config.diags.debug.tags': 'http' +}) + +ts.Disk.plugin_config.AddLine('xdebug.so') + +ts.Disk.remap_config.AddLine( + "map http://one http://127.0.0.1:{0}".format(server.Variables.Port) +) +ts.Disk.remap_config.AddLine( + "map http://two http://127.0.0.1:{0}".format(server.Variables.Port) +) +ts.Disk.remap_config.AddLine( + "regex_map http://three[0-9]+ http://127.0.0.1:{0}".format(server.Variables.Port) +) + +Test.Setup.Copy(f'{Test.Variables.AtsTestToolsDir}') + +files = ["none", "one", "two", "three"] + +for file in files: + Test.Setup.Copy(f'{Test.TestDirectory}/{file}.in') + + +def sendMsg(msgFile): + global started + tr = Test.AddTestRun() + tr.Processes.Default.Command = f"( python3 tools/tcp_client.py 127.0.0.1 {ts.Variables.port} {msgFile}.in ; echo '======' ) | sed 's/:{server.Variables.Port}/:SERVER_PORT/' >> out.log 2>&1" + tr.Processes.Default.ReturnCode = 0 + + if not started: + tr.Processes.Default.StartBefore(Test.Processes.ts) + tr.Processes.Default.StartBefore(Test.Processes.server) + started = True + + +for file in files: + sendMsg(file) + +tr = Test.AddTestRun() +tr.Processes.Default.Command = "echo test gold" +tr.Processes.Default.ReturnCode = 0 +f = tr.Disk.File("out.log") +f.Content = "out.gold"