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

Reply via email to