This is an automated email from the ASF dual-hosted git repository.
wkaras pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 0d8a6aced9 Add set-custom-body config item to header_rewrite (#11472)
0d8a6aced9 is described below
commit 0d8a6aced92f5c8dc5cef3113e34268cc99dfc01
Author: Jasmine Emanouel <[email protected]>
AuthorDate: Mon Jul 15 16:03:12 2024 +0100
Add set-custom-body config item to header_rewrite (#11472)
* Add set-body-custom to header_rewrite options
(cherry picked from commit 5bd999953b2605898f15714453250cb7e5e403f9)
(cherry picked from commit 08c614ef0089b175a5b6cee205b748416efb87cd)
* Update set-custom-body response body to exclude headers (#780)
* Update response body to exclude headers
* Update tests to check both response with headers and response body only
* Update header_rewrite_custom_body.test.py
* Fix tests to check headers and body
(cherry picked from commit cb552b63d6755a554edc5b67721f35678b38163b)
(cherry picked from commit e12f4798d0d46bd90c810f8f3a7a067ea3b2c76f)
* Remove debug check due to known issue
(cherry picked from commit 964b12cc21a9a7c3f9d3fd50286e4cd2d2757bcc)
* Fix formatting
Remove whitespaces
doc formatting fix
Doc formatting fix
* Rename to set-body-from
* Add test for failed second call
* Add more test cases
* Clarify test cases
* Update tests
* Update header_rewrite.en.rst
* Fix flakey test
---------
Co-authored-by: Jasmine Emanouel <[email protected]>
---
doc/admin-guide/plugins/header_rewrite.en.rst | 25 ++++
plugins/header_rewrite/factory.cc | 3 +-
plugins/header_rewrite/operators.cc | 142 ++++++++++++++++++
plugins/header_rewrite/operators.h | 21 +++
.../gold/header_rewrite-set_body_from_200.gold | 1 +
.../header_rewrite-set_body_from_conn_fail.gold | 17 +++
.../header_rewrite-set_body_from_remap_fail.gold | 15 ++
.../gold/header_rewrite-set_body_from_success.gold | 1 +
.../header_rewrite_set_body_from.test.py | 161 +++++++++++++++++++++
.../rules/rule_set_body_from_plugin.conf | 26 ++++
.../rules/rule_set_body_from_remap.conf | 24 +++
11 files changed, 435 insertions(+), 1 deletion(-)
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst
b/doc/admin-guide/plugins/header_rewrite.en.rst
index a5128b698f..7d971a0f4d 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -704,6 +704,31 @@ set-body
Sets the body to ``<text>``. Can also be used to delete a body with ``""``.
This is only useful when overriding the origin status, i.e.
intercepting/pre-empting a request so that you can override the body from the
body-factory with your own.
+set-body-from
+~~~~~~~~~~~~~
+::
+
+ set-body-from <URL>
+
+Will call ``<URL>`` (see URL in `URL Parts`_) to retrieve a custom error
response
+and set the body with the result. Triggering this rule on an OK transaction
will
+send a 500 status code to the client with the desired response. If this is
triggered
+on any error status code, that original status code will be sent to the client.
+**Note**: This config should only be set using READ_RESPONSE_HDR_HOOK
+
+An example config would look like
+
+ cond %{READ_RESPONSE_HDR_HOOK}
+ set-body-from http://www.example.com/second
+
+Where ``http://www.example.com/second`` is the destination to retrieve the
custom response from.
+This can be enabled per-mapping or globally.
+Ensure there is a remap rule for the second endpoint as well!
+An example remap config would look like
+
+ map /first http://www.example.com/first @plugin=header_rewrite.so
@pparam=cond1.conf
+ map /second http://www.example.com/second
+
set-config
~~~~~~~~~~
::
diff --git a/plugins/header_rewrite/factory.cc
b/plugins/header_rewrite/factory.cc
index 531f390f24..346265f893 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -77,7 +77,8 @@ operator_factory(const std::string &op)
o = new OperatorSetHttpCntl();
} else if (op == "run-plugin") {
o = new OperatorRunPlugin();
-
+ } else if (op == "set-body-from") {
+ o = new OperatorSetBodyFrom();
} else {
TSError("[%s] Unknown operator: %s", PLUGIN_NAME, op.c_str());
return nullptr;
diff --git a/plugins/header_rewrite/operators.cc
b/plugins/header_rewrite/operators.cc
index b22f04253a..0bb87f89c3 100644
--- a/plugins/header_rewrite/operators.cc
+++ b/plugins/header_rewrite/operators.cc
@@ -30,6 +30,91 @@
#include "operators.h"
#include "ts/apidefs.h"
+namespace
+{
+const unsigned int LOCAL_IP_ADDRESS = 0x0100007f;
+const unsigned int MAX_SIZE = 256;
+const int LOCAL_PORT = 8080;
+
+int
+handleFetchEvents(TSCont cont, TSEvent event, void *edata)
+{
+ TSHttpTxn http_txn = static_cast<TSHttpTxn>(TSContDataGet(cont));
+
+ switch (static_cast<int>(event)) {
+ case OperatorSetBodyFrom::TS_EVENT_FETCHSM_SUCCESS: {
+ TSHttpTxn fetchsm_txn = static_cast<TSHttpTxn>(edata);
+ int data_len;
+ const char *data_start = TSFetchRespGet(fetchsm_txn, &data_len);
+ if (data_start && (data_len > 0)) {
+ const char *data_end = data_start + data_len;
+ TSHttpParser parser = TSHttpParserCreate();
+ TSMBuffer hdr_buf = TSMBufferCreate();
+ TSMLoc hdr_loc = TSHttpHdrCreate(hdr_buf);
+ TSHttpHdrTypeSet(hdr_buf, hdr_loc, TS_HTTP_TYPE_RESPONSE);
+ if (TSHttpHdrParseResp(parser, hdr_buf, hdr_loc, &data_start, data_end)
== TS_PARSE_DONE) {
+ TSHttpTxnErrorBodySet(http_txn, TSstrdup(data_start), (data_end -
data_start), nullptr);
+ } else {
+ TSWarning("[%s] Unable to parse set-custom-body fetch response",
__FUNCTION__);
+ }
+ TSHttpParserDestroy(parser);
+ TSHandleMLocRelease(hdr_buf, nullptr, hdr_loc);
+ TSMBufferDestroy(hdr_buf);
+ } else {
+ TSWarning("[%s] Successful set-custom-body fetch did not result in any
content", __FUNCTION__);
+ }
+ TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_ERROR);
+ } break;
+ case OperatorSetBodyFrom::TS_EVENT_FETCHSM_FAILURE: {
+ Dbg(pi_dbg_ctl, "OperatorSetBodyFrom: Error getting custom body");
+ TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_CONTINUE);
+ } break;
+ case OperatorSetBodyFrom::TS_EVENT_FETCHSM_TIMEOUT: {
+ Dbg(pi_dbg_ctl, "OperatorSetBodyFrom: Timeout getting custom body");
+ TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_CONTINUE);
+ } break;
+ case TS_EVENT_HTTP_TXN_CLOSE: {
+ TSContDestroy(cont);
+ TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_CONTINUE);
+ } break;
+ case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
+ // Do nothing
+ // The transaction is reenabled with the FetchSM transaction
+ break;
+ default:
+ TSError("[%s] handleFetchEvents got unknown event: %d", PLUGIN_NAME,
event);
+ break;
+ }
+ return 0;
+}
+
+TSReturnCode
+createRequestString(const std::string_view &value, char (&req_buf)[MAX_SIZE],
int *req_buf_size)
+{
+ const char *start = value.data();
+ const char *end = start + value.size();
+ TSMLoc url_loc;
+ TSMBuffer url_buf = TSMBufferCreate();
+ int host_len, url_len = 0;
+
+ if (TSUrlCreate(url_buf, &url_loc) == TS_SUCCESS && TSUrlParse(url_buf,
url_loc, &start, end) == TS_PARSE_DONE) {
+ const char *host = TSUrlHostGet(url_buf, url_loc, &host_len);
+ const char *url = TSUrlStringGet(url_buf, url_loc, &url_len);
+
+ *req_buf_size = snprintf(req_buf, MAX_SIZE, "GET %.*s HTTP/1.1\r\nHost:
%.*s\r\n\r\n", url_len, url, host_len, host);
+
+ TSMBufferDestroy(url_buf);
+
+ return TS_SUCCESS;
+ } else {
+ Dbg(pi_dbg_ctl, "Failed to parse url %s", start);
+ TSMBufferDestroy(url_buf);
+ return TS_ERROR;
+ }
+}
+
+} // namespace
+
// OperatorConfig
void
OperatorSetConfig::initialize(Parser &p)
@@ -1219,3 +1304,60 @@ OperatorRunPlugin::exec(const Resources &res) const
_plugin->doRemap(res.txnp, res._rri);
}
}
+
+// OperatorSetBody
+void
+OperatorSetBodyFrom::initialize(Parser &p)
+{
+ Operator::initialize(p);
+ // we want the arg since body only takes one value
+ _value.set_value(p.get_arg());
+ require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+ require_resources(RSRC_RESPONSE_STATUS);
+}
+
+void
+OperatorSetBodyFrom::initialize_hooks()
+{
+ add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+}
+
+void
+OperatorSetBodyFrom::exec(const Resources &res) const
+{
+ if (TSHttpTxnIsInternal(res.txnp)) {
+ // If this is triggered by an internal transaction, a infinte loop may
occur
+ // It should only be triggered by the original transaction sent by the
client
+ Dbg(pi_dbg_ctl, "OperatorSetBodyFrom triggered by an internal
transaction");
+ return;
+ }
+
+ char req_buf[MAX_SIZE];
+ int req_buf_size = 0;
+ if (createRequestString(_value.get_value(), req_buf, &req_buf_size) ==
TS_SUCCESS) {
+ TSCont fetchCont = TSContCreate(handleFetchEvents, TSMutexCreate());
+ TSContDataSet(fetchCont, static_cast<void *>(res.txnp));
+
+ TSHttpTxnHookAdd(res.txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, fetchCont);
+ TSHttpTxnHookAdd(res.txnp, TS_HTTP_TXN_CLOSE_HOOK, fetchCont);
+
+ TSFetchEvent event_ids;
+ event_ids.success_event_id = TS_EVENT_FETCHSM_SUCCESS;
+ event_ids.failure_event_id = TS_EVENT_FETCHSM_FAILURE;
+ event_ids.timeout_event_id = TS_EVENT_FETCHSM_TIMEOUT;
+
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = LOCAL_IP_ADDRESS;
+ addr.sin_port = LOCAL_PORT;
+ TSFetchUrl(static_cast<const char *>(req_buf), req_buf_size,
reinterpret_cast<struct sockaddr const *>(&addr), fetchCont,
+ AFTER_BODY, event_ids);
+
+ // Forces original status code in event TSHttpTxnErrorBodySet changed
+ // the code or another condition was set conflicting with this one.
+ // Set here because res is the only structure that contains the original
status code.
+ TSHttpTxnStatusSet(res.txnp, res.resp_status);
+ } else {
+ TSError(PLUGIN_NAME, "OperatorSetBodyFrom:exec:: Could not create
request");
+ }
+}
diff --git a/plugins/header_rewrite/operators.h
b/plugins/header_rewrite/operators.h
index dc7b19ee4a..2c4712a67b 100644
--- a/plugins/header_rewrite/operators.h
+++ b/plugins/header_rewrite/operators.h
@@ -478,3 +478,24 @@ protected:
private:
RemapPluginInst *_plugin = nullptr;
};
+
+class OperatorSetBodyFrom : public Operator
+{
+public:
+ OperatorSetBodyFrom() { Dbg(pi_dbg_ctl, "Calling CTOR for
OperatorSetBodyFrom"); }
+
+ // noncopyable
+ OperatorSetBodyFrom(const OperatorSetBodyFrom &) = delete;
+ void operator=(const OperatorSetBodyFrom &) = delete;
+
+ void initialize(Parser &p) override;
+
+ enum { TS_EVENT_FETCHSM_SUCCESS = 70000, TS_EVENT_FETCHSM_FAILURE = 70001,
TS_EVENT_FETCHSM_TIMEOUT = 70002 };
+
+protected:
+ void initialize_hooks() override;
+ void exec(const Resources &res) const override;
+
+private:
+ Value _value;
+};
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_200.gold
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_200.gold
new file mode 100644
index 0000000000..3e37495f65
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_200.gold
@@ -0,0 +1 @@
+Custom body found
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_conn_fail.gold
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_conn_fail.gold
new file mode 100644
index 0000000000..70d40bddd1
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_conn_fail.gold
@@ -0,0 +1,17 @@
+<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>
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_remap_fail.gold
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_remap_fail.gold
new file mode 100644
index 0000000000..8f069904f1
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_remap_fail.gold
@@ -0,0 +1,15 @@
+<HTML>
+<HEAD>
+<TITLE>Not Found on Accelerator</TITLE>
+</HEAD>
+
+<BODY BGCOLOR="white" FGCOLOR="black">
+<H1>Not Found on Accelerator</H1>
+<HR>
+
+<FONT FACE="Helvetica,Arial"><B>
+Description: Your request on the specified host was not found.
+Check the location and try again.
+</B></FONT>
+<HR>
+</BODY>
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_success.gold
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_success.gold
new file mode 100644
index 0000000000..3e37495f65
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-set_body_from_success.gold
@@ -0,0 +1 @@
+Custom body found
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_set_body_from.test.py
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_set_body_from.test.py
new file mode 100644
index 0000000000..925458d5d8
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_set_body_from.test.py
@@ -0,0 +1,161 @@
+# 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 for successful response manipulation using set-body-from
+'''
+Test.ContinueOnFail = True
+
+
+class HeaderRewriteSetBodyFromTest:
+
+ def __init__(self):
+ self.setUpOriginServer()
+ self.setUpTS()
+
+ def setUpOriginServer(self):
+ self.server = Test.MakeOriginServer("server")
+
+ # Response for original transaction
+ response_header = {"headers": "HTTP/1.1 404 Not Found\r\nConnection:
close\r\n\r\n", "body": "404 Not Found"}
+
+ # Request/response for original transaction where transaction returns
a 200 status code
+ remap_success_request_header = {"headers": "GET /200 HTTP/1.1\r\nHost:
www.example.com\r\n\r\n"}
+ ooo = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
"body": "200 OK"}
+
+ self.server.addResponse("sessionfile.log",
remap_success_request_header, ooo)
+
+ # Request/response for original transaction with failed second
tranasaction
+ remap_fail_1_request_header = {"headers": "GET /remap_fail
HTTP/1.1\r\nHost: www.example.com\r\n\r\n"}
+ self.server.addResponse("sessionfile.log",
remap_fail_1_request_header, response_header)
+
+ plugin_fail_1_request_header = {"headers": "GET /plugin_fail
HTTP/1.1\r\nHost: www.example.com\r\n\r\n"}
+ self.server.addResponse("sessionfile.log",
plugin_fail_1_request_header, response_header)
+
+ # Request/response for original successful transaction with successful
second tranasaction
+ remap_success_1_request_header = {"headers": "GET /remap_success
HTTP/1.1\r\nHost: www.example.com\r\n\r\n"}
+ self.server.addResponse("sessionfile.log",
remap_success_1_request_header, response_header)
+
+ plugin_success_1_request_header = {"headers": "GET /plugin_success
HTTP/1.1\r\nHost: www.example.com\r\n\r\n"}
+ self.server.addResponse("sessionfile.log",
plugin_success_1_request_header, response_header)
+
+ # Request/response for custom body transaction that successfully
retrieves body
+ success_2_request_header = {"headers": "GET /404.html
HTTP/1.1\r\nHost: www.example.com\r\n\r\n"}
+ success_2_response_header = {"headers": "HTTP/1.1 200
OK\r\nConnection: close\r\n\r\n", "body": "Custom body found\n"}
+ self.server.addResponse("sessionfile.log", success_2_request_header,
success_2_response_header)
+
+ def setUpTS(self):
+ self.ts = Test.MakeATSProcess("ts")
+
+ # Set header rewrite rules
+ self.ts.Setup.CopyAs('rules/rule_set_body_from_remap.conf',
Test.RunDirectory)
+ self.ts.Setup.CopyAs('rules/rule_set_body_from_plugin.conf',
Test.RunDirectory)
+
+ self.ts.Disk.remap_config.AddLine(
+ """\
+ map http://www.example.com/remap_success
http://127.0.0.1:{0}/remap_success @plugin=header_rewrite.so
@pparam={1}/rule_set_body_from_remap.conf
+ map http://www.example.com/200 http://127.0.0.1:{0}/200
@plugin=header_rewrite.so @pparam={1}/rule_set_body_from_remap.conf
+ map http://www.example.com/remap_fail
http://127.0.0.1:{0}/remap_fail @plugin=header_rewrite.so
@pparam={1}/rule_set_body_from_remap.conf
+ map http://www.example.com/plugin_success
http://127.0.0.1:{0}/plugin_success
+ map http://www.example.com/plugin_fail
http://127.0.0.1:{0}/plugin_fail
+ map http://www.example.com/404.html http://127.0.0.1:{0}/404.html
+ map http://www.example.com/plugin_no_server
http://127.0.0.1::{2}/plugin_no_server
+ """.format(self.server.Variables.Port, Test.RunDirectory,
Test.GetTcpPort("bad_port")))
+ self.ts.Disk.plugin_config.AddLine('header_rewrite.so
{0}/rule_set_body_from_plugin.conf'.format(Test.RunDirectory))
+
+ def test_setBodyFromFails_remap(self):
+ '''
+ Test where set-body-from request fails
+ Triggered from remap file
+ This uses the case where no remap rule is provided
+ '''
+ tr = Test.AddTestRun()
+ tr.Processes.Default.Command = (
+ 'curl -s -v --proxy 127.0.0.1:{0}
"http://www.example.com/remap_fail"'.format(self.ts.Variables.port))
+ tr.Processes.Default.ReturnCode = 0
+ tr.Processes.Default.StartBefore(self.server)
+ tr.Processes.Default.StartBefore(self.ts)
+ tr.Processes.Default.Streams.stdout =
"gold/header_rewrite-set_body_from_remap_fail.gold"
+ tr.Processes.Default.Streams.stderr.Content =
Testers.ContainsExpression("404 Not Found", "Expected 404 response")
+ tr.StillRunningAfter = self.server
+
+ def test_setBodyFromSucceeds_remap(self):
+ '''
+ Test where set-body-from request succeeds
+ Triggered from remap file
+ '''
+ tr = Test.AddTestRun()
+ tr.Processes.Default.Command = (
+ 'curl -s -v --proxy 127.0.0.1:{0}
"http://www.example.com/remap_success"'.format(self.ts.Variables.port))
+ tr.Processes.Default.ReturnCode = 0
+ tr.Processes.Default.Streams.stdout =
"gold/header_rewrite-set_body_from_success.gold"
+ tr.Processes.Default.Streams.stderr.Content =
Testers.ContainsExpression("404 Not Found", "Expected 404 response")
+ tr.StillRunningAfter = self.server
+
+ def test_setBodyFromSucceeds_plugin(self):
+ '''
+ Test where set-body-from request succeeds
+ Triggered from plugin file
+ '''
+ tr = Test.AddTestRun()
+ tr.Processes.Default.Command = (
+ 'curl -s -v --proxy 127.0.0.1:{0}
"http://www.example.com/plugin_success"'.format(self.ts.Variables.port))
+ tr.Processes.Default.ReturnCode = 0
+ tr.Processes.Default.Streams.stdout =
"gold/header_rewrite-set_body_from_success.gold"
+ tr.Processes.Default.Streams.stderr.Content =
Testers.ContainsExpression("404 Not Found", "Expected 404 response")
+ tr.StillRunningAfter = self.server
+
+ def test_setBodyFromFails_plugin(self):
+ '''
+ Test where set-body-from request fails
+ This uses the case where the second endpoint cannot connect to the
requested server
+ Triggered from plugin file
+ '''
+ tr = Test.AddTestRun()
+ tr.Processes.Default.Command = (
+ 'curl -s -v --proxy 127.0.0.1:{0}
"http://www.example.com/plugin_fail"'.format(self.ts.Variables.port))
+ tr.Processes.Default.ReturnCode = 0
+ tr.Processes.Default.Streams.stdout =
"gold/header_rewrite-set_body_from_conn_fail.gold"
+ tr.Processes.Default.Streams.stderr.Content =
Testers.ContainsExpression("404 Not Found", "Expected 404 response")
+ tr.StillRunningAfter = self.server
+
+ def test_setBodyFromSucceeds_200(self):
+ '''
+ Test where set-body-from request succeeds and returns 200 OK
+ Triggered from remap file
+ This is tested because right now, TSHttpTxnErrorBodySet will change OK
status codes to 500 INKApi Error
+ Ideally, this would not occur.
+ '''
+ tr = Test.AddTestRun()
+ tr.Processes.Default.Command = (
+ 'curl -s -v --proxy 127.0.0.1:{0}
"http://www.example.com/200"'.format(self.ts.Variables.port))
+ tr.Processes.Default.ReturnCode = 0
+ tr.Processes.Default.Streams.stdout =
"gold/header_rewrite-set_body_from_200.gold"
+ tr.Processes.Default.Streams.stderr.Content =
Testers.ContainsExpression("500 INKApi Error", "Expected 500 response")
+ tr.StillRunningAfter = self.server
+
+ def runTraffic(self):
+ self.test_setBodyFromFails_remap()
+ self.test_setBodyFromSucceeds_remap()
+ self.test_setBodyFromSucceeds_plugin()
+ self.test_setBodyFromFails_plugin()
+ self.test_setBodyFromSucceeds_200()
+
+ def run(self):
+ self.runTraffic()
+
+
+HeaderRewriteSetBodyFromTest().run()
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.conf
b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.conf
new file mode 100644
index 0000000000..c4c83abb17
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.conf
@@ -0,0 +1,26 @@
+#
+# 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 uses cond %{CLIENT-URL:PATH} to differentiate tests
+# It is not needed to make set-body-from work
+cond %{READ_RESPONSE_HDR_HOOK}
+cond %{CLIENT-URL:PATH} = "plugin_success"
+set-body-from http://www.example.com/404.html
+
+cond %{READ_RESPONSE_HDR_HOOK}
+cond %{CLIENT-URL:PATH} = "plugin_fail"
+set-body-from http://www.example.com/plugin_no_server
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.conf
b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.conf
new file mode 100644
index 0000000000..351e17f7b8
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.conf
@@ -0,0 +1,24 @@
+#
+# 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.
+cond %{READ_RESPONSE_HDR_HOOK}
+cond %{CLIENT-URL:PATH} = "remap_success" [OR]
+cond %{CLIENT-URL:PATH} = "200"
+set-body-from http://www.example.com/404.html
+
+cond %{READ_RESPONSE_HDR_HOOK}
+cond %{CLIENT-URL:PATH} = "remap_fail"
+set-body-from http://www.example.com/fail