diff --git a/plugins/xdebug/xdebug.cc b/plugins/xdebug/xdebug.cc index f0b1d307a48..9d34655d942 100644 --- a/plugins/xdebug/xdebug.cc +++ b/plugins/xdebug/xdebug.cc @@ -83,6 +83,7 @@ enum { XHEADER_X_PROBE_HEADERS = 1u << 9, XHEADER_X_PSELECT_KEY = 1u << 10, XHEADER_X_CACHE_INFO = 1u << 11, + XHEADER_X_EFFECTIVE_URL = 1u << 12, }; static TSCont XInjectHeadersCont = nullptr; @@ -376,6 +377,29 @@ InjectRemapHeader(TSHttpTxn txn, TSMBuffer buffer, TSMLoc hdr) } } +static void +InjectEffectiveURLHeader(TSHttpTxn txn, TSMBuffer buffer, TSMLoc hdr) +{ + struct { + char *ptr; + int len; + } strval = {nullptr, 0}; + + TSDebug("xdebug", "attempting to inject X-Effective-URL header"); + + strval.ptr = TSHttpTxnEffectiveUrlStringGet(txn, &strval.len); + + if (strval.ptr != nullptr && strval.len > 0) { + TSMLoc dst = FindOrMakeHdrField(buffer, hdr, "X-Effective-URL", lengthof("X-Effective-URL")); + if (dst != TS_NULL_MLOC) { + TSReleaseAssert(TSMimeHdrFieldValueStringInsert(buffer, hdr, dst, -1 /* idx */, strval.ptr, strval.len) == TS_SUCCESS); + TSHandleMLocRelease(buffer, hdr, dst); + } + } + + TSfree(strval.ptr); +} + static void InjectTxnUuidHeader(TSHttpTxn txn, TSMBuffer buffer, TSMLoc hdr) { @@ -484,6 +508,10 @@ XInjectResponseHeaders(TSCont /* contp */, TSEvent event, void *edata) InjectRemapHeader(txn, buffer, hdr); } + if (xheaders & XHEADER_X_EFFECTIVE_URL) { + InjectEffectiveURLHeader(txn, buffer, hdr); + } + // intentionally placed after all injected headers. if (xheaders & XHEADER_X_DUMP_HEADERS) { @@ -649,6 +677,8 @@ XScanRequestHeaders(TSCont /* contp */, TSEvent event, void *edata) snprintf(newVal, sizeof(newVal), "fwd=%" PRIiMAX, fwdCnt - 1); TSMimeHdrFieldValueStringSet(buffer, hdr, field, i, newVal, std::strlen(newVal)); } + } else if (header_field_eq("x-effective-url", value, vsize)) { + xheaders |= XHEADER_X_EFFECTIVE_URL; } else { TSDebug("xdebug", "ignoring unrecognized debug tag '%.*s'", vsize, value); } diff --git a/tests/gold_tests/pluginTest/xdebug/x_effective_url/none.in b/tests/gold_tests/pluginTest/xdebug/x_effective_url/none.in new file mode 100644 index 00000000000..e07b30662f5 --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_effective_url/none.in @@ -0,0 +1,5 @@ +GET /argh HTTP/1.1 +Host: none +X-Debug: X-Effective-URL +Connection: close + diff --git a/tests/gold_tests/pluginTest/xdebug/x_effective_url/one.in b/tests/gold_tests/pluginTest/xdebug/x_effective_url/one.in new file mode 100644 index 00000000000..d9223256128 --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_effective_url/one.in @@ -0,0 +1,5 @@ +GET /argh HTTP/1.1 +Host: one +X-Debug: x-effective-url +Connection: close + diff --git a/tests/gold_tests/pluginTest/xdebug/x_effective_url/out.gold b/tests/gold_tests/pluginTest/xdebug/x_effective_url/out.gold new file mode 100644 index 00000000000..7af772ad449 --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_effective_url/out.gold @@ -0,0 +1,61 @@ +HTTP/1.1 500 Cannot find server. +Date: `` +Connection: close +Server: ATS/`` +Cache-Control: no-store +Content-Type: text/html +Content-Language: en +X-Effective-URL: http://none/argh +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: close +Server: ATS/`` +X-Effective-URL: http://127.0.0.1:SERVER_PORT/argh + +0 + +====== +HTTP/1.1 200 OK +Date: `` +Age: `` +Transfer-Encoding: chunked +Connection: close +Server: ATS/`` +X-Effective-URL: http://127.0.0.1:SERVER_PORT/two/argh + +0 + +====== +HTTP/1.1 200 OK +Date: `` +Age: `` +Transfer-Encoding: chunked +Connection: close +Server: ATS/`` +X-Effective-URL: http://127.0.0.1:SERVER_PORT/argh + +0 + +====== diff --git a/tests/gold_tests/pluginTest/xdebug/x_effective_url/three.in b/tests/gold_tests/pluginTest/xdebug/x_effective_url/three.in new file mode 100644 index 00000000000..7076c1a249c --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_effective_url/three.in @@ -0,0 +1,5 @@ +GET /argh HTTP/1.1 +Host: three123 +X-Debug: X-effective-url +Connection: close + diff --git a/tests/gold_tests/pluginTest/xdebug/x_effective_url/two.in b/tests/gold_tests/pluginTest/xdebug/x_effective_url/two.in new file mode 100644 index 00000000000..52ded33b4f0 --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_effective_url/two.in @@ -0,0 +1,5 @@ +GET /argh HTTP/1.1 +Host: two +X-Debug: X-EFFECTIVE-URL +Connection: close + diff --git a/tests/gold_tests/pluginTest/xdebug/x_effective_url/x_effective_url.test.py b/tests/gold_tests/pluginTest/xdebug/x_effective_url/x_effective_url.test.py new file mode 100644 index 00000000000..ed1c3b54300 --- /dev/null +++ b/tests/gold_tests/pluginTest/xdebug/x_effective_url/x_effective_url.test.py @@ -0,0 +1,81 @@ +# 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-Effective header +''' + +server = Test.MakeOriginServer("server") + +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) + +request_header_two = { + "headers": "GET /two/argh HTTP/1.1\r\nHost: doesnotmatter\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header_two, response_header) + +ts = Test.MakeATSProcess("ts") + +ts.Disk.records_config.update({ + 'proxy.config.url_remap.remap_required': 0, + 'proxy.config.diags.debug.enabled': 0, +}) + +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) + "/two" +) +ts.Disk.remap_config.AddLine( + "regex_map http://three[0-9]+ http://127.0.0.1:{0}".format(server.Variables.Port) +) + +tr = Test.AddTestRun() +tr.Processes.Default.StartBefore(Test.Processes.ts) +tr.Processes.Default.StartBefore(Test.Processes.server) +tr.Processes.Default.Command = "cp {}/tcp_client.py {}/tcp_client.py".format( + Test.Variables.AtsTestToolsDir, Test.RunDirectory) +tr.Processes.Default.ReturnCode = 0 + + +def sendMsg(msgFile): + + tr = Test.AddTestRun() + tr.Processes.Default.Command = ( + "( python3 {}/tcp_client.py 127.0.0.1 {} {}/{}.in".format( + Test.RunDirectory, ts.Variables.port, Test.TestDirectory, msgFile) + + " ; echo '======' ) | sed 's/:{}/:SERVER_PORT/' >> {}/out.log 2>&1 ".format( + server.Variables.Port, Test.RunDirectory) + ) + tr.Processes.Default.ReturnCode = 0 + + +sendMsg('none') +sendMsg('one') +sendMsg('two') +sendMsg('three') + +tr = Test.AddTestRun() +tr.Processes.Default.Command = "echo test out.gold" +tr.Processes.Default.ReturnCode = 0 +f = tr.Disk.File("out.log") +f.Content = "out.gold"