Skip to content
43 changes: 43 additions & 0 deletions plugins/xdebug/xdebug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <unistd.h>

#include <ts/ts.h>
#include "ts/experimental.h"
#include "tscore/ink_defs.h"
#include "tscpp/util/PostScript.h"
#include "tscpp/util/TextView.h"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)) {
Expand Down
8 changes: 7 additions & 1 deletion proxy/http/HttpCacheSM.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions tests/gold_tests/pluginTest/xdebug/x_cache_info/none.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
GET /argh HTTP/1.1
Host: none
X-Debug: X-Cache-Info

4 changes: 4 additions & 0 deletions tests/gold_tests/pluginTest/xdebug/x_cache_info/one.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
GET /argh HTTP/1.1
Host: one
X-Debug: X-Cache-Info

60 changes: 60 additions & 0 deletions tests/gold_tests/pluginTest/xdebug/x_cache_info/out.gold
Original file line number Diff line number Diff line change
@@ -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

<HTML>
<HEAD>
<TITLE>Unknown Host</TITLE>
</HEAD>

<BODY BGCOLOR="white" FGCOLOR="black">
<H1>Unknown Host</H1>
<HR>

<FONT FACE="Helvetica,Arial"><B>
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.
</B></FONT>
<HR>
</BODY>
======
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

======
4 changes: 4 additions & 0 deletions tests/gold_tests/pluginTest/xdebug/x_cache_info/three.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
GET /argh HTTP/1.1
Host: three123
X-Debug: x-cACHE-iNFO

4 changes: 4 additions & 0 deletions tests/gold_tests/pluginTest/xdebug/x_cache_info/two.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
GET /argh HTTP/1.1
Host: two
X-Debug: x-cache-info

Original file line number Diff line number Diff line change
@@ -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"